# 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//` works; `/api/v1/onus/configs/` 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=; Path=/; Secure; SameSite=Lax Set-Cookie: __Host-sessionid=; Path=/; Secure; HttpOnly; SameSite=Lax Set-Cookie: __Host-sessionexpire=; 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=; __Host-sessionid=; ... X-CSRFToken: # the value from __Host-csrftoken Referer: / # MCMS rejects POST/PUT/DELETE without this User-Agent: # see §3.1 — must not be empty Accept: application/json ``` For PUT/POST/PATCH also: ``` Content-Type: application/json Content-Length: ``` ### 2.3 (Optional) Database selection If the deployment manages multiple databases: ``` PUT /api/v1/databases/selection/ { "data": "" } ``` 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. `/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//` 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": "", "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//?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// → 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//` | Get single ONU config | | `PUT` | `/api/v1/onus/configs//` | Replace ONU config (full doc) | | `DELETE` | `/api/v1/onus/configs//` | Delete ONU (Procedure 43) | | `GET` | `/api/v3/onus/states/` | List ONU states (paginated) | | `GET` | `/api/v3/onus/states//` | Get single ONU state | | `DELETE` | `/api/v1/onus/states//` | 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//` | Upload firmware | | `PUT` | `/api/v3/tasks/configs//` | Create bulk upgrade task (Procedure 8) | | `GET` | `/api/v3/tasks/configs//` | Poll bulk task config | | `GET` | `/api/v1/onus//upgrade/status/` | (Build-dependent) per-ONU status | ### Other endpoints documented but not (yet) used | Method | Path | Notes | |---|---|---| | `GET` | `/api/v1/onus/stats//?start-time=...` | Time-series ONU PM data | | `GET` | `/api/v1/olts/stats//?start-time=...` | OLT PM data | | `GET` | `/api/v1/onus/logs//?start-time=...` | Per-ONU syslog | | `GET` | `/api/v1/olts/logs//?start-time=...` | Per-OLT syslog | | `GET` | `/api/v3/tasks/states//` | 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": "", "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": "", // 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//`. 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// { "data": { "file": "", "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//` → full doc. 2. Compute the inactive bank (see §6.3). 3. Set: - `ONU["FW Bank Files"][writeSlot] = ""` - `ONU["FW Bank Versions"][writeSlot] = ""` - `ONU["FW Bank Ptr"] = writeSlot` (this initiates the download) 4. `PUT /api/v1/onus/configs//` 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// (body shape in §5.4) ``` MCMS owns the rollout: scheduled start, retries, pacing. Monitor progress (build permitting): ``` GET /api/v3/tasks/states// ``` 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**: NOT accepted in-app — both clients use system trust only, with no "accept self-signed" bypass. A self-signed appliance must have its CA installed on the device's OS trust store (MDM/configuration profile). Do not add a trust-bypass toggle. - **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 certs → install the CA There is no in-app "accept self-signed" prompt (removed for iOS/Android parity). The lab MCMS at `https://mcms.lab.svorka.net/` uses a self-signed cert, so it is unreachable until that CA is installed on the device's OS trust store (MDM/configuration profile) — or the box is given a CA-trusted cert. Surface the resulting trust failure clearly in the error UI so it doesn't look like a silent login break. --- ## 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.*