# REST Simulator API — barox Virtual Switch Simulator

Base URL: `http://<server>/api/v1/`

All endpoints require an API key:

```
Authorization: Bearer bxs_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

Create keys as an Administrator in the Web UI under **API Keys**. Keys are
shown once at creation and stored only as a SHA-256 hash server-side (§21).
Each key has a role (`administrator` / `operator` / `viewer` — same
permission model as Web UI users, §26) and an optional switch scope (if
scoped, the key only works for that one switch — requests for any other
switch id get `403`).

All responses are JSON. Errors: `{"error": "message"}` with an appropriate
HTTP status (400/401/403/404/405/500).

## Switches

### `GET /api/v1/switches`

```bash
curl -H "Authorization: Bearer $KEY" http://server/api/v1/switches
```

```json
{"switches":[{"id":1,"uuid":"...","name":"Demo RY-LGSP38-28","model":"RY-LGSP38-28",
  "management_ip":"127.0.0.1","status":"online","uptime_seconds":3600,
  "cpu_load":6.4,"memory_usage":31.8,"temperature":38.9, "...": "..."}]}
```

### `GET /api/v1/switches/{id}`

Same fields as above plus `system_name`, `system_location`, `system_contact`, `snmp`.

### `DELETE /api/v1/switches/{id}` — administrator only

### `POST /api/v1/switches/{id}/reboot` — administrator/operator

Simulates a reboot (§51): status becomes `rebooting` for the configured
delay, ports go down, counters reset, then startup configuration is
re-applied and status returns to `online`.

### `POST /api/v1/switches/{id}/save-config` — administrator/operator

Snapshots the current running configuration as startup configuration (§52).

### `POST /api/v1/switches/{id}/factory-reset` — administrator only

Restores factory defaults (§53): ports/PoE/system identity reset, dynamic
MAC entries removed.

## Ports

### `GET /api/v1/switches/{id}/ports`

### `GET /api/v1/switches/{id}/ports/{port}`

```bash
curl -H "Authorization: Bearer $KEY" http://server/api/v1/switches/1/ports/7
```

```json
{
  "port_number": 7, "port_name": "Port 7", "port_type": "copper",
  "admin_state": "enabled", "link_state": "up", "speed": 1000, "duplex": "full",
  "autoneg": true, "flow_control": false, "description": "", "connected_mac": null,
  "poe": {"capable": true, "enabled": true, "status": "deliveringPower", "class": 4,
          "priority": "low", "power_requested": 30.0, "power_allocated": 30.0,
          "power_used": 12.7, "current_ma": 265},
  "cable_diagnostics": {"status": "not_run", "length_m": null},
  "counters": {"rx_bytes": 128301, "tx_bytes": 20144, "...": "..."},
  "last_change": "2026-08-28 10:00:00"
}
```

### `PUT /api/v1/switches/{id}/ports/{port}` — administrator/operator

Body (all fields optional, only present ones are applied):

```json
{"link_state":"up","admin_state":"enabled","speed":1000,"duplex":"full",
 "autoneg":true,"flow_control":false,"description":"Camera-07",
 "traffic_profile":"camera_10mbps"}
```

```bash
curl -X PUT -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"link_state":"up","speed":1000,"duplex":"full"}' \
  http://server/api/v1/switches/1/ports/7
```

### `PUT /api/v1/switches/{id}/ports/{port}/poe` — administrator/operator

```json
{"enabled": true, "class": 4, "power_used": 12.7, "priority": "high"}
```

```bash
curl -X PUT -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"enabled":true,"class":4,"power_used":12.7}' \
  http://server/api/v1/switches/1/ports/7/poe
```

### `POST /api/v1/switches/{id}/ports/{port}/sfp` — administrator/operator

```json
{"vendor":"FS","part_number":"SFP-10G-LR","serial_number":"TEST123456",
 "wavelength":1310,"temperature":42.4,"tx_power":-2.1,"rx_power":-4.6}
```

```bash
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"vendor":"FS","part_number":"SFP-10G-LR","serial_number":"TEST123456","wavelength":1310}' \
  http://server/api/v1/switches/1/ports/25/sfp
```

### `DELETE /api/v1/switches/{id}/ports/{port}/sfp` — administrator/operator

Removes the module (forces link down, matching a real optic removal).

### `POST /api/v1/switches/{id}/ports/{port}/cable-diagnostics` — administrator/operator

Starts Cable Diagnostics (§10); result settles a few seconds later, poll
`GET .../ports/{port}` and read `cable_diagnostics.status`.

### `POST /api/v1/switches/{id}/ports/{port}/clear-counters` — administrator/operator

## MAC / LLDP / Events (read-only)

- `GET /api/v1/switches/{id}/mac` — MAC address table (§11)
- `GET /api/v1/switches/{id}/lldp` — LLDP neighbors (§12)
- `GET /api/v1/switches/{id}/events` — event log for this switch (§24)

## Scenarios

- `GET /api/v1/scenarios` — list built-in/custom scenarios (§22-23)
- `POST /api/v1/switches/{id}/scenarios/{scenarioId}/start` — administrator/operator

```bash
curl -X POST -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"port_number":7}' \
  http://server/api/v1/switches/1/scenarios/2/start
```

## Notes

- Every write here goes through the exact same `App\Services\*` classes as
  the Web UI and the SNMP agent (§49) — there is no separate REST state.
  A `PUT` here is visible immediately in the Web UI and via `snmpwalk`.
- Traffic counters are computed on read from a stored rate + timestamp
  (§13/§36), not incremented by a background loop — polling faster doesn't
  change the simulated rate, it only samples it more often.
