initial commit
This commit is contained in:
commit
8de962eeb8
44 changed files with 4843 additions and 0 deletions
758
MCMS_API.md
Normal file
758
MCMS_API.md
Normal file
|
|
@ -0,0 +1,758 @@
|
|||
# Ciena MCMS 6.2 REST API — Implementer's Field Guide
|
||||
|
||||
Reference for building an HTTP client against a Ciena MicroClimate
|
||||
Management System (MCMS) 6.2 PON Manager. Distilled from the official
|
||||
dev guide (`323-1961-306`) plus empirical findings against a working
|
||||
deployment with GNXS / Tibit MicroPlug ONUs on Interos Everest 5.x
|
||||
firmware.
|
||||
|
||||
Language-agnostic — every quirk listed here was hit in production. If
|
||||
you skip the gotchas in §3, you will lose an afternoon to one of them.
|
||||
|
||||
---
|
||||
|
||||
## 1. Connection basics
|
||||
|
||||
- **Base URL.** The root of the PON Manager web UI (e.g.
|
||||
`https://mcms.example.net`). All REST paths are prefixed with `/api`.
|
||||
- **Transport.** HTTPS. Internal deployments commonly use self-signed
|
||||
certificates on private IPs — your client needs an opt-in to skip
|
||||
TLS verification for that case (do **not** disable globally).
|
||||
- **HTTP version.** HTTP/1.1 works. The server also negotiates HTTP/2
|
||||
but the dev-guide examples are 1.1 and the only behavioural
|
||||
difference observed is irrelevant to clients.
|
||||
- **Path normalisation.** The dev guide writes paths as
|
||||
`/v1/users/authenticate/` — the real wire path is
|
||||
`/api/v1/users/authenticate/`. Normalise once in your client.
|
||||
- **API versions.** Both `/api/v1/...` and `/api/v3/...` exist and serve
|
||||
different endpoints. **Use whichever the dev guide lists for that
|
||||
resource — they are not interchangeable.** See §4 endpoint table.
|
||||
- **Trailing slashes.** Required. `/api/v1/onus/configs/<id>/` works;
|
||||
`/api/v1/onus/configs/<id>` does not (Django's `APPEND_SLASH` is
|
||||
on, but a few endpoints 500 instead of redirecting).
|
||||
|
||||
---
|
||||
|
||||
## 2. Login & session
|
||||
|
||||
MCMS uses Django session cookies + a CSRF token. Sessions do **not**
|
||||
auto-expire on idle — log out explicitly when done.
|
||||
|
||||
### 2.1 Authenticate
|
||||
|
||||
```
|
||||
POST /api/v1/users/authenticate/
|
||||
Content-Type: application/json
|
||||
|
||||
{ "data": { "email": "user@example.net", "password": "..." } }
|
||||
```
|
||||
|
||||
Response on success (`HTTP 200`):
|
||||
|
||||
```json
|
||||
{ "status": "success" }
|
||||
```
|
||||
|
||||
Set-Cookie headers carry the session. On observed MCMS 6.2 builds:
|
||||
|
||||
```
|
||||
Set-Cookie: __Host-csrftoken=<token>; Path=/; Secure; SameSite=Lax
|
||||
Set-Cookie: __Host-sessionid=<id>; Path=/; Secure; HttpOnly; SameSite=Lax
|
||||
Set-Cookie: __Host-sessionexpire=<epoch>; Path=/; Secure; SameSite=Lax
|
||||
```
|
||||
|
||||
→ **The `__Host-` prefix is significant.** Some MCMS builds (older
|
||||
ones, mostly) use bare `csrftoken` / `sessionid`. Your cookie reader
|
||||
should accept either prefix.
|
||||
|
||||
→ Some build variants accept an unwrapped body too:
|
||||
`{"email": ..., "password": ...}` with no `data` envelope. If wrapped
|
||||
returns HTTP 400, retry unwrapped.
|
||||
|
||||
### 2.2 Subsequent requests
|
||||
|
||||
Every authenticated request must send:
|
||||
|
||||
```
|
||||
Cookie: __Host-csrftoken=<token>; __Host-sessionid=<id>; ...
|
||||
X-CSRFToken: <token> # the value from __Host-csrftoken
|
||||
Referer: <baseURL>/ # MCMS rejects POST/PUT/DELETE without this
|
||||
User-Agent: <anything> # see §3.1 — must not be empty
|
||||
Accept: application/json
|
||||
```
|
||||
|
||||
For PUT/POST/PATCH also:
|
||||
|
||||
```
|
||||
Content-Type: application/json
|
||||
Content-Length: <bytes>
|
||||
```
|
||||
|
||||
### 2.3 (Optional) Database selection
|
||||
|
||||
If the deployment manages multiple databases:
|
||||
|
||||
```
|
||||
PUT /api/v1/databases/selection/
|
||||
{ "data": "<database_id>" }
|
||||
```
|
||||
|
||||
Most deployments only have one and skip this step.
|
||||
|
||||
### 2.4 Logout
|
||||
|
||||
```
|
||||
GET /api/v1/users/logout/
|
||||
```
|
||||
|
||||
Best-effort — don't fail your client if this errors.
|
||||
|
||||
---
|
||||
|
||||
## 3. The gotchas (read this section twice)
|
||||
|
||||
### 3.1 User-Agent header is REQUIRED
|
||||
|
||||
If you don't send a `User-Agent`, some MCMS middleware dereferences
|
||||
`HTTP_USER_AGENT` without a null check and returns **HTTP 500** with
|
||||
body:
|
||||
|
||||
```json
|
||||
{"status": "fail", "details": {"message": "Internal server error. See server logs for details."}}
|
||||
```
|
||||
|
||||
Reproduce: `curl -A '' https://mcms/api/v3/onus/configs/`. Any non-empty
|
||||
string works (`PonFleetClient/1.0`, etc.).
|
||||
|
||||
**For iOS/Swift**: `URLSession` sets a default User-Agent automatically
|
||||
(e.g. `<bundle>/1 CFNetwork/... Darwin/...`), so this is unlikely to
|
||||
bite you there — but if you ever set a custom `URLRequest` with
|
||||
`setValue(nil, forHTTPHeaderField: "User-Agent")`, the 500s will
|
||||
return.
|
||||
|
||||
### 3.2 CSRF token field name
|
||||
|
||||
The token cookie is `__Host-csrftoken` on new builds, bare `csrftoken`
|
||||
on older. The header you send back is always `X-CSRFToken` (no
|
||||
prefix, capital `T` in `Token`). Both Django case-fold the header
|
||||
name, so casing is fine — but the body name is exact.
|
||||
|
||||
### 3.3 Pagination: use `next`, NOT `skip`
|
||||
|
||||
`/api/v3/onus/configs/?skip=N` has been observed returning HTTP 500.
|
||||
The working approach uses the last document's `_id` as a cursor:
|
||||
|
||||
```
|
||||
GET /api/v3/onus/configs/?limit=100
|
||||
→ returns 100 docs, the last with _id = "X"
|
||||
GET /api/v3/onus/configs/?limit=100&next=X
|
||||
→ returns the next 100, EXCLUDING all _ids <= X
|
||||
```
|
||||
|
||||
Loop until you get a short page (< limit) or an empty page. Most list
|
||||
endpoints accept this pattern.
|
||||
|
||||
### 3.4 Page size 100 is the safe default
|
||||
|
||||
`?limit=1000` against `/api/v3/onus/configs/` on a ~1500-ONU fleet
|
||||
times out at the Apache front-end before Django logs the request.
|
||||
Full `ONU-CFG` documents are ~30 KB each, so 1000 × 30 KB = 30 MB,
|
||||
which exceeds the proxy timeout. **100 per page keeps each response
|
||||
~3 MB** and well under any default proxy budget.
|
||||
|
||||
This applies to all bulk list endpoints (configs, states, OLT states).
|
||||
|
||||
### 3.5 Request body envelope: `{"data": payload}`
|
||||
|
||||
PUT/POST/PATCH bodies are wrapped:
|
||||
|
||||
```json
|
||||
{ "data": { "_id": "...", "ONU": { ... } } }
|
||||
```
|
||||
|
||||
Response envelope is similar:
|
||||
|
||||
```json
|
||||
{ "status": "success", "data": { ... } }
|
||||
{ "status": "fail", "details": { "message": "..." } }
|
||||
{ "status": "warning", "data": { ... }, "details": { ... } }
|
||||
```
|
||||
|
||||
`"warning"` is treated as success by MCMS (the write happened) but
|
||||
indicates a schema mismatch you should log.
|
||||
|
||||
### 3.6 PUT is full-document replace, NOT patch
|
||||
|
||||
`PUT /api/v1/onus/configs/<id>/` replaces the entire `ONU-CFG`
|
||||
document. You **must** GET the current doc, mutate fields, and PUT
|
||||
the entire result. There is no PATCH equivalent that works reliably
|
||||
on MCMS 6.2.
|
||||
|
||||
Forgetting this turns into "I PUT'd just the FW Bank fields and now
|
||||
the ONU has no service config" — the dev guide is explicit but easy
|
||||
to skim past.
|
||||
|
||||
### 3.7 Timestamp format
|
||||
|
||||
MCMS emits and accepts timestamps as:
|
||||
|
||||
```
|
||||
"YYYY-MM-DD HH:MM:SS" # second precision
|
||||
"YYYY-MM-DD HH:MM:SS.ffffff" # microsecond precision (for STATE.Time)
|
||||
```
|
||||
|
||||
Always **UTC**, always with a **literal space** between date and time
|
||||
(not ISO-8601 `T`). Used on:
|
||||
|
||||
- `ONU-STATE.Time`
|
||||
- `OLT-STATE.Time`
|
||||
- Alarm timestamps
|
||||
- `AUTO-TASK-CFG.Task["Scheduled Start Time"]`
|
||||
- `start-time` / `end-time` URL params on stats endpoints
|
||||
|
||||
**For Swift**: `ISO8601DateFormatter` will NOT parse this. Use a
|
||||
plain `DateFormatter` with:
|
||||
|
||||
```swift
|
||||
let fmt = DateFormatter()
|
||||
fmt.timeZone = TimeZone(identifier: "UTC")
|
||||
fmt.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSSSS" // or "yyyy-MM-dd HH:mm:ss"
|
||||
fmt.locale = Locale(identifier: "en_US_POSIX")
|
||||
```
|
||||
|
||||
The `en_US_POSIX` locale matters — without it, devices set to a
|
||||
24-hour-format locale will still parse fine but devices set to
|
||||
12-hour-format locales will fail. POSIX locale forces parse rules.
|
||||
|
||||
### 3.8 Server-side query/projection filters are brittle
|
||||
|
||||
The dev guide documents `query`, `projection`, `sort`, `limit`,
|
||||
`skip`, `next`, `distinct` URL parameters. In practice:
|
||||
|
||||
- **`query`** (Mongo-style filter) works for simple equality on
|
||||
bare field names. Anything with **spaces in field names**, quoted
|
||||
values with non-ASCII characters, or projection paths that don't
|
||||
exist on every document version is unreliable — you'll get either
|
||||
empty results or 400s.
|
||||
- **`projection`** is *very* picky about paths. Better to fetch full
|
||||
docs and project client-side.
|
||||
|
||||
**Recommended approach**: load the full fleet (paginated by `next`)
|
||||
and filter client-side. The data volume is fine for any device with
|
||||
enough RAM to hold a few thousand 30 KB documents.
|
||||
|
||||
### 3.9 URL encoding
|
||||
|
||||
Use percent-encoding (`encodeURIComponent` equivalent). MCMS does NOT
|
||||
accept `+` for spaces in URL parameters — use `%20`.
|
||||
|
||||
This matters for query params like `start-time` which carry literal
|
||||
spaces in the timestamp.
|
||||
|
||||
### 3.10 ONU IDs
|
||||
|
||||
The `_id` field of an ONU document is:
|
||||
|
||||
- The **serial number** for XGS-PON / GPON ONUs (e.g. `GNXS05057fee`)
|
||||
- The **MAC address** for 10G-EPON ONUs
|
||||
|
||||
Treat as opaque strings. They're URL-safe in observed deployments
|
||||
but always `encodeURIComponent` to be defensive.
|
||||
|
||||
### 3.11 Registration status lives on OLT-STATE, not ONU-STATE
|
||||
|
||||
This is non-obvious. The 9 registration buckets live as arrays on the
|
||||
OLT's state document:
|
||||
|
||||
```json
|
||||
{
|
||||
"_id": "<OLT MAC>",
|
||||
"ONU States": {
|
||||
"Registered": ["GNXS01", "GNXS02", ...],
|
||||
"Deregistered": ["GNXS09", ...],
|
||||
"Dying Gasp": [...],
|
||||
"Disabled": [...],
|
||||
"Disallowed Admin": [...],
|
||||
"Disallowed Error": [...],
|
||||
"Disallowed Reg ID": [...],
|
||||
"Unspecified": [...],
|
||||
"Unprovisioned": [...]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To resolve "what's the status of ONU X?", you walk every OLT-STATE
|
||||
doc, find which bucket contains X, and that's your answer. See
|
||||
Procedure 2 in the dev guide.
|
||||
|
||||
An ONU should appear in exactly one bucket on exactly one OLT. In the
|
||||
rare duplicate case, prefer the non-`Registered` bucket — that's the
|
||||
interesting signal.
|
||||
|
||||
### 3.12 FEC counters live on ONU-STATE.STATS, not on `/onus/stats/`
|
||||
|
||||
`GET /api/v1/onus/stats/<id>/?start-time=...` returns historical PM
|
||||
samples (time-series). For the *current* FEC counters and optical
|
||||
levels, read the live `ONU-STATE` document:
|
||||
|
||||
```
|
||||
GET /api/v3/onus/states/<id>/
|
||||
→ data.STATS["ONU-PON"]["RX Pre-FEC BER"]
|
||||
→ data.STATS["ONU-PON"]["RX Post-FEC BER"]
|
||||
→ data.STATS["ONU-PON"]["RX Optical Level"] (dBm)
|
||||
→ data.STATS["ONU-PON"]["TX Optical Level"] (dBm)
|
||||
→ data.STATS["OLT-PON"]["RX Pre-FEC BER"]
|
||||
→ data.STATS["OLT-PON"]["RX Post-FEC BER"]
|
||||
```
|
||||
|
||||
The MCMS Web UI's `/api/onu/summary` helper returns the same data
|
||||
under `data.state_collection.STATS.*` (wrapper shape).
|
||||
|
||||
### 3.13 Bulk endpoints don't return everything in one shot
|
||||
|
||||
Even on a small fleet, MCMS may silently truncate large list
|
||||
responses. Always paginate — don't trust `?limit=large_number` to be
|
||||
authoritative.
|
||||
|
||||
### 3.14 Verbose error logging matters
|
||||
|
||||
When MCMS 500s, the response body sometimes contains useful detail
|
||||
(missing required param, schema validation failure, etc.). Always log
|
||||
both the response headers and the FULL body on non-2xx during
|
||||
development — most "mysterious 500" cases resolve to a one-line hint
|
||||
in the body.
|
||||
|
||||
---
|
||||
|
||||
## 4. Endpoints used by the fleet-upgrade app
|
||||
|
||||
| Method | Path | Purpose |
|
||||
|---|---|---|
|
||||
| `POST` | `/api/v1/users/authenticate/` | Login |
|
||||
| `GET` | `/api/v1/users/logout/` | Logout |
|
||||
| `PUT` | `/api/v1/databases/selection/` | (Optional) DB selection |
|
||||
| `GET` | `/api/v3/onus/configs/` | List ONU configs (paginated) |
|
||||
| `GET` | `/api/v1/onus/configs/<id>/` | Get single ONU config |
|
||||
| `PUT` | `/api/v1/onus/configs/<id>/` | Replace ONU config (full doc) |
|
||||
| `DELETE` | `/api/v1/onus/configs/<id>/` | Delete ONU (Procedure 43) |
|
||||
| `GET` | `/api/v3/onus/states/` | List ONU states (paginated) |
|
||||
| `GET` | `/api/v3/onus/states/<id>/` | Get single ONU state |
|
||||
| `DELETE` | `/api/v1/onus/states/<id>/` | Delete ONU state (best-effort) |
|
||||
| `GET` | `/api/v3/olts/states/` | List OLT states (paginated; registration) |
|
||||
| `GET` | `/api/v1/files/onu-firmware/` | List uploaded firmware files |
|
||||
| `POST` | `/api/v1/files/onu-firmware/<name>/` | Upload firmware |
|
||||
| `PUT` | `/api/v3/tasks/configs/<id>/` | Create bulk upgrade task (Procedure 8) |
|
||||
| `GET` | `/api/v3/tasks/configs/<id>/` | Poll bulk task config |
|
||||
| `GET` | `/api/v1/onus/<id>/upgrade/status/` | (Build-dependent) per-ONU status |
|
||||
|
||||
### Other endpoints documented but not (yet) used
|
||||
|
||||
| Method | Path | Notes |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/v1/onus/stats/<id>/?start-time=...` | Time-series ONU PM data |
|
||||
| `GET` | `/api/v1/olts/stats/<id>/?start-time=...` | OLT PM data |
|
||||
| `GET` | `/api/v1/onus/logs/<id>/?start-time=...` | Per-ONU syslog |
|
||||
| `GET` | `/api/v1/olts/logs/<id>/?start-time=...` | Per-OLT syslog |
|
||||
| `GET` | `/api/v3/tasks/states/<id>/` | Bulk task progress (good follow-up) |
|
||||
| `GET` | `/api/v1/onus/automation/states/` | PON automation state |
|
||||
| `GET` | `/api/v1/onus/cpe-states/` | ONU CPE states (downstream devices) |
|
||||
|
||||
---
|
||||
|
||||
## 5. Data shapes (observed)
|
||||
|
||||
These are not formally specified in the dev guide for MCMS 6.2 — they
|
||||
were extracted from live traffic.
|
||||
|
||||
### 5.1 ONU-CFG
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"_id": "GNXS05057fee",
|
||||
"ONU": {
|
||||
"Name": "Operator-set label",
|
||||
"Address": "Street, city",
|
||||
"PON Mode": "GPON", // GPON | XGS-PON | 10G-EPON
|
||||
"Equipment ID": "FT-XGS2110",
|
||||
|
||||
"FW Bank Files": ["...bin", "...bin"], // [slot 0, slot 1]
|
||||
"FW Bank Versions": ["EV05110R", "EV05100R"],
|
||||
"FW Bank Ptr": 0, // 0 | 1 | 65535 (unset)
|
||||
|
||||
"ONU-ALARM-CFG": "Default",
|
||||
"Vendor": "GNXS",
|
||||
"Serial Number": "GNXS05057fee",
|
||||
"Host MAC Address": "58:00:32:05:7f:ee",
|
||||
// ...many more fields - preserve all of them when PUTing
|
||||
},
|
||||
"OLT-Service 0": { ... }, // service config slots
|
||||
"OLT-Service 1": { ... },
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Critical**: when mutating for an upgrade, only change `ONU["FW Bank
|
||||
Files"]`, `ONU["FW Bank Versions"]`, and `ONU["FW Bank Ptr"]`. Pass
|
||||
through everything else unchanged.
|
||||
|
||||
### 5.2 ONU-STATE
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"_id": "GNXS05057fee",
|
||||
"Time": "2026-05-04 21:51:57.394127", // UTC, last update
|
||||
"ONU": {
|
||||
"Equipment ID": "FT-XGS2110",
|
||||
"FW Bank Files": [...],
|
||||
"FW Bank Versions": [...],
|
||||
"FW Bank Ptr": 0,
|
||||
"FW Version": "EV05110R", // resolved active version
|
||||
"FW Upgrade Status": {
|
||||
"Bank": 0,
|
||||
"Status": "Success", // status string
|
||||
"Progress": 100, // percent
|
||||
"Upgrade Time": "2026-03-18 17:12:43.093893",
|
||||
"Total Blocks": 482915,
|
||||
"Sent Blocks": 482915,
|
||||
"Failures": 0,
|
||||
// ...
|
||||
},
|
||||
"PON Mode": "GPON",
|
||||
"Online Time": "2026-03-18 17:13:42.285965",
|
||||
// ...
|
||||
},
|
||||
"STATS": {
|
||||
"ONU-PON": {
|
||||
"RX Pre-FEC BER": 22388041291, // cumulative since reset
|
||||
"RX Post-FEC BER": 22379111198,
|
||||
"RX Optical Level": -17.544, // dBm
|
||||
"TX Optical Level": 5.532 // dBm
|
||||
},
|
||||
"OLT-PON": {
|
||||
"RX Pre-FEC BER": 0, // upstream view
|
||||
"RX Post-FEC BER": 0,
|
||||
"TX Optical Level": 5.6,
|
||||
"RX Optical Level": -18.3
|
||||
},
|
||||
"ONU-EnhancedFecPmHistData-32769": { // NOT the same as the BER fields above
|
||||
"uncorrectable_code_words": 1829, // (these are codeword counts,
|
||||
"corrected_bytes": 181, // not bit-error-rate ratios)
|
||||
"corrected_code_words": 181
|
||||
},
|
||||
// ...many more
|
||||
},
|
||||
"Alarm": {
|
||||
"0-EMERG": [], "1-ALERT": [], "2-CRIT": [],
|
||||
"3-ERROR": [], "4-WARNING": [], "5-NOTICE": [],
|
||||
"6-INFO": [], "7-DEBUG": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3 OLT-STATE (relevant subset)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"_id": "<OLT MAC>",
|
||||
"Time": "2026-05-04 21:51:57.394127",
|
||||
"ONU States": {
|
||||
"Registered": ["onu1", "onu2", ...],
|
||||
"Deregistered": [...],
|
||||
"Dying Gasp": [...],
|
||||
"Disabled": [...],
|
||||
"Disallowed Admin": [...],
|
||||
"Disallowed Error": [...],
|
||||
"Disallowed Reg ID": [...],
|
||||
"Unspecified": [...],
|
||||
"Unprovisioned": [...]
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 AUTO-TASK-CFG (bulk firmware task, Procedure 8)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"_id": "<your-task-id>", // unique, caller-generated
|
||||
"Task": {
|
||||
"Device Type": "ONU",
|
||||
"Operation": "Firmware Upgrade",
|
||||
"Scheduled Start Time": "2026-05-12 03:00:00" // UTC space format
|
||||
},
|
||||
"Task Details": {
|
||||
"Devices": ["GNXS01", "GNXS02", ...], // ONU IDs (serial / MAC)
|
||||
"ONU": {
|
||||
"FW Bank Ptr": 1, // target slot (single value!)
|
||||
"FW Bank Files": { "0": "old.bin", "1": "new.bin" },
|
||||
"FW Bank Versions": { "0": "EV05110R", "1": "EV05120R" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
PUT to `/api/v3/tasks/configs/<task-id>/`. Note: `FW Bank Files` and
|
||||
`FW Bank Versions` here are **objects keyed by slot index as strings**
|
||||
(`"0"`, `"1"`), not arrays. This is different from the `ONU-CFG`
|
||||
shape where the same fields are arrays — almost certainly a Mongo
|
||||
serialisation quirk. Match it as-is.
|
||||
|
||||
**Single bank per task.** Because `FW Bank Ptr` accepts one slot, if
|
||||
your selection spans ONUs with different active banks, bucket them
|
||||
by target slot and create one task per bucket.
|
||||
|
||||
### 5.5 Firmware file entry (from `/api/v1/files/onu-firmware/`)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"_id": "...",
|
||||
"filename": "Interos-Everest-5.12.0-R-EV05120R.bin",
|
||||
"metadata": {
|
||||
"Compatible Manufacturer": "TIBITCOM",
|
||||
"Compatible Model": ["MicroPlug ONU"],
|
||||
"Version": "EV05120R"
|
||||
},
|
||||
// GridFS metadata...
|
||||
}
|
||||
```
|
||||
|
||||
To upload:
|
||||
|
||||
```
|
||||
POST /api/v1/files/onu-firmware/<filename>/
|
||||
{
|
||||
"data": {
|
||||
"file": "<base64 binary contents>",
|
||||
"metadata": {
|
||||
"Compatible Manufacturer": "TIBITCOM",
|
||||
"Compatible Model": ["MicroPlug ONU"],
|
||||
"Version": "EV05120R"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Firmware upgrade flow
|
||||
|
||||
Two methods documented:
|
||||
|
||||
### 6.1 Procedure 7 — per-ONU PUT
|
||||
|
||||
For each ONU:
|
||||
|
||||
1. `GET /api/v1/onus/configs/<id>/` → full doc.
|
||||
2. Compute the inactive bank (see §6.3).
|
||||
3. Set:
|
||||
- `ONU["FW Bank Files"][writeSlot] = "<filename>"`
|
||||
- `ONU["FW Bank Versions"][writeSlot] = "<version>"`
|
||||
- `ONU["FW Bank Ptr"] = writeSlot` (this initiates the download)
|
||||
4. `PUT /api/v1/onus/configs/<id>/` with the entire mutated document.
|
||||
|
||||
Pros: see errors per ONU live. Cons: slow for large fleets, you own
|
||||
retries.
|
||||
|
||||
### 6.2 Procedure 8 — bulk task (recommended)
|
||||
|
||||
Build one `AUTO-TASK-CFG` per (target slot) bucket:
|
||||
|
||||
```
|
||||
PUT /api/v3/tasks/configs/<task-id>/
|
||||
(body shape in §5.4)
|
||||
```
|
||||
|
||||
MCMS owns the rollout: scheduled start, retries, pacing.
|
||||
Monitor progress (build permitting):
|
||||
|
||||
```
|
||||
GET /api/v3/tasks/states/<task-id>/
|
||||
```
|
||||
|
||||
Or just poll the ONU configs and watch `FW Bank Versions` /
|
||||
`FW Bank Ptr` flip.
|
||||
|
||||
### 6.3 Inactive-bank selection
|
||||
|
||||
`FW Bank Ptr` identifies the active bank (0, 1, or 65535 = unset).
|
||||
**Always write to the inactive bank** so you don't blow away the
|
||||
running image:
|
||||
|
||||
| Current `FW Bank Ptr` | Write slot | Why |
|
||||
|---|---|---|
|
||||
| 0 | 1 | preserve active |
|
||||
| 1 | 0 | preserve active |
|
||||
| 65535 (unset) | 1 | matches Procedure 7 example; slot 0 stays as factory fallback |
|
||||
|
||||
MCMS flips `FW Bank Ptr` to the new slot when the download completes.
|
||||
The ONU reboots into the new image on its next reboot.
|
||||
|
||||
---
|
||||
|
||||
## 7. FEC + optical interpretation
|
||||
|
||||
### 7.1 Four FEC counters
|
||||
|
||||
Per the user guide green-LED criteria (`323-1961-302` p149):
|
||||
|
||||
| Field | Path | Green | Red |
|
||||
|---|---|---|---|
|
||||
| ONU Pre-FEC BER | `STATS["ONU-PON"]["RX Pre-FEC BER"]` | 0 | (informational) |
|
||||
| ONU Post-FEC BER | `STATS["ONU-PON"]["RX Post-FEC BER"]` | 0 | > 0 |
|
||||
| OLT Pre-FEC BER | `STATS["OLT-PON"]["RX Pre-FEC BER"]` | 0 | (informational) |
|
||||
| OLT Post-FEC BER | `STATS["OLT-PON"]["RX Post-FEC BER"]` | 0 | > 0 |
|
||||
|
||||
Note: the field name is "BER" but the magnitudes are big integers
|
||||
(cumulative error byte counts since reset), not ratios in [0,1].
|
||||
Treat them as counts.
|
||||
|
||||
### 7.2 Health bucket rules
|
||||
|
||||
Suggested 4-state reduction:
|
||||
|
||||
- **OK**: all four counters present and == 0.
|
||||
- **Pre-FEC errors**: any pre > 0, all post == 0. (Marginal link;
|
||||
FEC is correcting.)
|
||||
- **Post-FEC errors**: any post > 0. (Uncorrected bits reaching the
|
||||
user.)
|
||||
- **Buggy (pre ≈ post)**: ONU pre > 0 AND post > 0 AND
|
||||
`post / pre >= 0.95`. Real FEC reduces errors; if post is within
|
||||
5% of pre, the firmware is mirroring the counter. Observed on
|
||||
Interos Everest ONUs.
|
||||
|
||||
### 7.3 Optical levels
|
||||
|
||||
```
|
||||
STATS["ONU-PON"]["RX Optical Level"] // ONU receive power, dBm
|
||||
STATS["ONU-PON"]["TX Optical Level"] // ONU transmit power, dBm
|
||||
```
|
||||
|
||||
User guide thresholds:
|
||||
|
||||
| | Green | Red |
|
||||
|---|---|---|
|
||||
| RX | ≥ −30 dBm | < −30 dBm |
|
||||
| TX | ≥ 3 dBm | < 3 dBm |
|
||||
|
||||
Operators usually want a margin warning band — e.g. yellow when RX
|
||||
is between −28 and −30 dBm.
|
||||
|
||||
---
|
||||
|
||||
## 8. iOS-specific notes
|
||||
|
||||
### 8.1 Library stack suggestions
|
||||
|
||||
- **HTTP**: `URLSession` is fine. The features you need are standard
|
||||
cookie storage, custom headers, and the option to bypass cert
|
||||
validation. Don't reach for Alamofire just for this.
|
||||
- **Cookies**: `HTTPCookieStorage` handles `__Host-` prefixes
|
||||
correctly. Get the CSRF value via:
|
||||
```swift
|
||||
HTTPCookieStorage.shared.cookies(for: baseURL)?
|
||||
.first { $0.name == "__Host-csrftoken" || $0.name == "csrftoken" }?.value
|
||||
```
|
||||
- **Self-signed certs**: Implement
|
||||
`URLSessionDelegate.urlSession(_:didReceive:completionHandler:)`
|
||||
and accept `serverTrust` only when the user has explicitly toggled
|
||||
"accept self-signed" for the configured host. Never default-on.
|
||||
- **JSON**: `JSONSerialization` is simpler than `Codable` for the
|
||||
envelope pattern (`{status, data, details}`) because the `data`
|
||||
payload is heterogeneous. `Codable` works if you model each
|
||||
endpoint individually.
|
||||
|
||||
### 8.2 Concurrency
|
||||
|
||||
The fleet load is N HTTPS round-trips for per-ONU state (the bulk
|
||||
endpoint is the optimisation but the per-ID fallback may also be
|
||||
needed). Use Swift Concurrency (`TaskGroup`) with a concurrency cap
|
||||
of 8–10 to avoid stampeding the server. The desktop client uses 5
|
||||
for deletes and 8 for state fetches.
|
||||
|
||||
### 8.3 Background uploads
|
||||
|
||||
Firmware `.bin` files are 10–50 MB base64-encoded. If you implement
|
||||
uploads:
|
||||
|
||||
- Don't block the main thread.
|
||||
- Consider `URLSession` background configuration so an upload can
|
||||
survive an app suspend.
|
||||
- The upload is a single POST; no chunking required.
|
||||
|
||||
### 8.4 Storing the session
|
||||
|
||||
For UX: persist the base URL and username in `UserDefaults`, never
|
||||
the password. Re-prompt for password on launch.
|
||||
|
||||
The session cookie itself goes in the per-session cookie storage
|
||||
and is automatically discarded — don't try to persist it.
|
||||
|
||||
### 8.5 Date handling
|
||||
|
||||
See §3.7. Default `DateFormatter` with `en_US_POSIX` locale + UTC
|
||||
timezone, format `"yyyy-MM-dd HH:mm:ss.SSSSSS"` (microseconds) or
|
||||
`"yyyy-MM-dd HH:mm:ss"` (seconds). Try the microsecond format
|
||||
first, fall back to second if it fails.
|
||||
|
||||
### 8.6 Cell-vs-Wi-Fi
|
||||
|
||||
MCMS APIs are typically reachable only from the operator's internal
|
||||
network. Test on cellular: many internal hosts won't be reachable,
|
||||
and your error UI needs to communicate that clearly.
|
||||
|
||||
### 8.7 Self-signed cert prompt
|
||||
|
||||
Make this a first-class onboarding step. The lab MCMS at
|
||||
`https://mcms.lab.svorka.net/` uses a self-signed cert, and any
|
||||
auto-rejection will silently break login.
|
||||
|
||||
---
|
||||
|
||||
## 9. Procedures referenced in the dev guide
|
||||
|
||||
| # | Title | Notes |
|
||||
|---|---|---|
|
||||
| 2 | Determining ONU registration status | OLT-STATE["ONU States"] buckets |
|
||||
| 7 | Per-ONU firmware upgrade | Full-doc PUT to ONU-CFG |
|
||||
| 8 | Bulk firmware upgrade | AUTO-TASK-CFG via PUT |
|
||||
| 38 | Resetting bit error rate values | Names the four FEC fields |
|
||||
| 43 | Deleting an ONU | DELETE on ONU-CFG |
|
||||
|
||||
(Page numbers vary by document revision; cite by procedure number.)
|
||||
|
||||
---
|
||||
|
||||
## 10. Quick implementation checklist for a new client
|
||||
|
||||
A correct first POST-login GET against `/api/v3/onus/configs/?limit=1`
|
||||
returning HTTP 200 means you've got the eight critical pieces right:
|
||||
|
||||
- [ ] HTTPS reachable (with cert exception if needed)
|
||||
- [ ] Path prefix `/api` added
|
||||
- [ ] User-Agent set to non-empty value
|
||||
- [ ] Cookie jar storing `__Host-sessionid` + `__Host-csrftoken`
|
||||
- [ ] `X-CSRFToken` header pulled from `__Host-csrftoken` cookie value
|
||||
- [ ] `Referer` header set to base URL
|
||||
- [ ] `Accept: application/json`
|
||||
- [ ] Trailing slash on the path
|
||||
|
||||
Add for write operations:
|
||||
|
||||
- [ ] `Content-Type: application/json`
|
||||
- [ ] Body wrapped: `{"data": ...}`
|
||||
- [ ] Full-document PUT (never partial)
|
||||
|
||||
Add for list endpoints:
|
||||
|
||||
- [ ] Page size ≤ 100
|
||||
- [ ] `next` cursor (not `skip`)
|
||||
- [ ] Loop until short page
|
||||
|
||||
Once those land, the API is comfortable to work with — the hard parts
|
||||
are upfront.
|
||||
|
||||
---
|
||||
|
||||
*Compiled from `323-1961-306` (REST API dev guide), `323-1961-302`
|
||||
(PON Manager User Guide), and empirical testing against MCMS 6.2 with
|
||||
Interos Everest 5.11/5.12 firmware on GNXS / Tibit MicroPlug ONUs.*
|
||||
Loading…
Add table
Add a link
Reference in a new issue