Skip to content

17 — OLT Management

Overview

An OLT (Optical Line Terminal) is the fiber network head-end device — the ISP-side equipment in an FTTH (Fiber to the Home) deployment. The OLT manages all ONTs (Optical Network Terminals) — the fiber modems at customer premises. PyroRadius integrates with OLT devices via SNMP to poll ONT status, track fiber signal levels (Rx power), provision new ONTs, and automatically associate fiber ports with customer records.

Important: PyroRadius polls OLTs via SNMP (snmpbulkwalk), not SSH or Telnet CLI. Do not configure SSH credentials on OLTs for PyroRadius — only the SNMP read community string is required.


Network Architecture

Internet / Core Network
   OLT Device (Huawei MA5600T)
        │ SNMP (UDP 161)
        │ ◄─────────── PyroRadius Server polls via snmpbulkwalk
        │  Optical splitter (1:16 or 1:32)
        ├──────────────────────────────┐
        │                              │
        ▼                              ▼
   ONT/ONU (customer 1)          ONT/ONU (customer 2)
   Rx power: -22 dBm             Rx power: -28 dBm
   PPPoE → MikroTik NAS          PPPoE → MikroTik NAS

olts Table Structure

Column Type Description
id bigint PK Auto-increment
name varchar Human-readable label (e.g. "HAJI-PARK-OLT")
alias varchar nullable Short alias
ip varchar OLT management IP address
location varchar nullable Physical location description
device_type varchar nullable e.g. "Huawei MA5600T"
snmp_community varchar SNMP read community string (default: public)
snmp_write_community varchar nullable SNMP write community (for ONT actions)
snmp_version varchar SNMP version: 2c or 3 (default: 2c)
enabled boolean Whether this OLT is included in polling
status varchar online / offline (set by last poll result)
poll_interval_min int Minutes between polls (default: 3, minimum: 1)
last_polled_at timestamp When the last successful poll completed
sys_name varchar nullable OLT sysName from SNMP
sys_descr varchar nullable OLT sysDescr
uptime_sec bigint nullable OLT uptime in seconds
temperature decimal nullable OLT board temperature
version varchar nullable OLT firmware version
created_at timestamp
updated_at timestamp

ont_cache Table Structure

Stores the most recent ONT data polled from each OLT. Upserted on every poll (not appended). Stale ONTs (deregistered from OLT) are deleted after each poll.

Column Type Description
id bigint PK
olt_id bigint FK Which OLT this ONT belongs to
snmp_index varchar Full SNMP index key: portIndex.ontId (e.g. 4194312192.0)
frame int GPON frame number (from ifIndex decode)
slot int GPON slot number
port int GPON port number
ont_id int ONT ID within the port (0-based)
description varchar ONT description — used to join with customer PPPoE username
sn_hex varchar Raw serial number as space-separated hex bytes
sn_decoded varchar Decoded SN (e.g. HWTC5443023E1E0C) — vendor prefix + hex
port_alias varchar nullable Port name (e.g. "3Rd Part TEST") from ifAlias
vendor varchar nullable First 4 bytes of SN decoded as ASCII (e.g. HWTC, FHTT, ZTEG)
oper_status int nullable ONT operational status code (1=Online, 2=Offline, etc.)
config_status int nullable ONT config state from OLT
rx_raw int nullable Raw Rx power (OLT-measured, ×100 — e.g. -2250 = -22.50 dBm)
deact_cause int nullable Last deactivation cause code
mac_count int nullable Number of MACs seen on this ONT
last_up_at timestamp nullable Last time ONT came online (parsed from SNMP DateAndTime)
last_down_at timestamp nullable Last time ONT went offline
created_at timestamp
updated_at timestamp When this row was last refreshed by a poll

rx_raw Scale

rx_raw = -2250  →  actual Rx power = -22.50 dBm  (strong)
rx_raw = -2750  →  actual Rx power = -27.50 dBm  (marginal)
rx_raw = -3100  →  actual Rx power = -31.00 dBm  (weak)
rx_raw = 2147483647  →  sentinel value — no optical module / not readable

Conversion: rx_power_dBm = rx_raw / 100


PollOltOnts Command

php artisan olt:poll-onts [--olt=ID]

Scheduled every 1 minute (scheduler level). Per-OLT skip logic prevents actual SNMP polling more often than poll_interval_min (default: 3 minutes). The --olt=ID flag forces an immediate poll of a single OLT bypassing the interval check.

SNMP OIDs Polled (Huawei MA5600T — enterprise OID 2011.6.128.1.1.2)

Key OID suffix Description
desc .43.1.9 ONT description (→ PPPoE username)
sn .43.1.3 ONT serial number (hex)
oper .46.1.15 Operational run state
cfg .47.1.1 Configuration state
deact .46.1.24 Last deactivation cause
mac .46.1.21 Connected device MAC count
up .46.1.22 Last up time (SNMP DateAndTime)
down .46.1.23 Last down time (SNMP DateAndTime)
rx .51.1.6 OLT-measured Rx power (primary)
rx_ddm .51.1.4 ONT DDM Rx (fallback if .51.1.6 = sentinel)
ifAlias 1.3.6.1.2.1.31.1.1.1.18 Port name (ifAlias)

Execution Strategy

1. Launch 8 snmpbulkwalk processes IN PARALLEL (-Cr125, timeout 10s):
   desc, sn, oper, cfg, deact, mac, up, down

2. Wait for all 8 to complete, collect output.

3. Run RX power walk SEQUENTIALLY (-Cr75, timeout 15s):
   OLT cannot handle 9 concurrent SNMP sessions — RX runs alone.
   ~34 PDUs for 2531 ONTs at -Cr75.

4. For any ONT where RX = sentinel (2147483647):
   Run a single snmpget for .51.1.4 to get DDM Rx (one round-trip for all sentinels).

5. Run snmpget for ifAlias of each unique port index (one PDU, all ports).

6. Decode each ONT's ifIndex → frame/slot/port.
   Decode SN hex → vendor prefix + full SN string.

7. Upsert all rows into ont_cache (chunks of 200).

8. Delete stale ONTs (in DB but not in this poll's result).

9. Update olts.last_polled_at = now(), status = 'online'.

Performance (HAJI-PARK-OLT, 2531 ONTs)

Setting Poll Duration
Before (−Cr20) ~490 seconds (~8 min)
After (−Cr75) ~146 seconds (~2.5 min)

Data freshness with poll_interval_min = 3: data is at most 5–6 minutes old (3 min wait + 2.5 min poll).


SyncOntAssignments Command

php artisan olt:sync-ont-assignments

Scheduled every 1 minute. Joins ont_cache with customers on PPPoE username = ONT description, then updates customer records with current signal data.

// Join logic
DB::table('ont_cache')
    ->join('customers', 'ont_cache.description', '=', 'customers.pppoe_username')
    ->whereNull('customers.deleted_at')
    ->select(['customers.id', 'customers.ont_serial', 'ont_cache.sn_decoded',
              'ont_cache.rx_raw', 'ont_cache.port_alias', 'customers.rx_power'])
    ->get();

// Only writes if value changed (IS DISTINCT FROM guard — prevents lock contention)
if ($changed) {
    Customer::where('id', $match->id)->update([
        'ont_serial'  => $newOntSerial,
        'rx_power'    => $match->rx_raw / 100,
        'rx_power_at' => now(),
    ]);
}

OltController Routes

Method Route Action
GET /ont-list GPON Management page (React SPA)
GET /ont-list/{olt}/data ONT list data for a specific OLT
GET /ont-list/{olt}/ports PON port list from ont_cache
GET /ont-list/{olt}/onts ONTs on a specific port
POST /ont-list/{olt}/reboot-ont Reboot an ONT via SNMP write
POST /ont-list/{olt}/ont-action Activate/Deactivate an ONT
POST /ont-list/{olt}/bulk-ont-action Bulk action on multiple ONTs
GET /ont-list/{olt}/autofind Scan for unregistered ONTs
GET /ont-list/{olt}/provision-meta Get provisioning metadata
POST /ont-list/{olt}/confirm-ont Register/provision an ONT
POST /ont-list/{olt}/delete-ont Remove an ONT from OLT
POST /ont-list/{olt}/change-profile Change ONT line profile
POST /ont-list/{olt}/change-service-vlan Change service VLAN
POST /ont-list/{olt}/set-pppoe Set PPPoE username on ONT
POST /ont-list/{olt}/set-ont-desc Update ONT description
GET /ont-list/{olt}/wlan-info Get Wi-Fi config from ONT
GET /ont-list/{olt}/find-ont Find ONT by serial number
GET /ont-list/unassigned-customers Customers without linked ONTs
POST /olts/{olt}/poll Force immediate SNMP poll

Rx Power Signal Quality Thresholds

Range Status Color Meaning
> −27 dBm Good Green Healthy signal
−27 to −30 dBm Warning Yellow Marginal; clean connectors
< −30 dBm Critical Red Poor signal; failing fiber/connector
No reading Unknown Grey ONT not seen in last poll

Adding a New OLT

Prerequisites

  • OLT must have SNMP v2c enabled with a read community string.
  • UDP port 161 on the OLT must be reachable from the PyroRadius server (your-server-ip).
  • For write operations (reboot, provision): also configure the SNMP write community.

Steps

  1. Navigate to Network → GPON Management.
  2. Click Add OLT.
  3. Fill in: Name, IP, SNMP Community, SNMP Version, Poll Interval.
  4. Save. The first automatic poll runs within poll_interval_min minutes.

ifIndex Decoding (Huawei MA5600T)

The Huawei MA5600T encodes the GPON port into ifIndex using a proprietary scheme:

ifIndex = 0xFA00_{frame_byte}{slot_port_byte}00

frame = (ifIndex >> 16) & 0xFF
slot  = ((ifIndex >> 8) & 0xFF) >> 5   (high 3 bits of byte 1)
port  = ((ifIndex >> 8) & 0xFF) & 0x0F (low 4 bits of byte 1)

Example: ifIndex = 4194312192 - hex = 0xFA010800 - frame = 0x01 = 1 - slot_port_byte = 0x08 → slot = 0, port = 8

ONT SNMP index format: portIndex.ontId (e.g. 4194312192.5 = frame 1, port 8, ONT 5)


Unregistered ONTs

ONTs that appear in the OLT's SNMP walk (connected to the fiber) but have no matching customer in customers (by PPPoE username in description) appear in the Unregistered ONT tab.

These are ONTs that are: - Physically plugged in but not provisioned in PyroRadius - Provisioned under a different username (needs manual match) - Test or spare ONTs

Use the Unregistered ONT tab to assign them to existing customers or provision new ones.