Skip to main content

Paquete de Integración OLT Huawei MA56XXT con Python

Project description

README - jmq_olt_huawei

Paquete de integración con OLTs Huawei MA56XXT (como MA5603T) vía Telnet y SNMP asíncrono, diseñado para automatizar la recolección de información GPON desde Python.

🛰️ ¿Qué hace este paquete?

Permite conectarse a una OLT Huawei MA56XXT y ejecutar operaciones como:

  • Listar slots activos (display board 0)
  • Consultar puertos GPON por slot
  • Obtener ONTs conectadas a cada puerto, incluyendo:
    • Potencia TX (ptx) y RX (prx) vía SNMP asíncrono
  • Realizar un escaneo completo (slots → puertos → ONTs)
  • Manejar paginación, prompts dinámicos y errores comunes de sesión (bloqueo de usuario)

📦 Instalación

Requiere Python ≥ 3.7 y PySNMP ≥ 6.2 (para usar el HLAPI asíncrono).

pip install jmq_olt_huawei

o bien:

pip install git+https://github.com/juaquicar/jmq_olt_huawei.git

Para desarrollo en modo editable:

pip install -e .

📁 Estructura del paquete

jmq_olt_huawei/
│
├── ma56xxt.py          # Lógica principal de conexión Telnet y SNMP asíncrono
├── __init__.py         # Archivo de inicialización del paquete
├── Examples/           # Scripts de ejemplo (opcional)
├── tests/              # Pruebas automatizadas (pytest)
├── requirements.txt    # Dependencias de desarrollo / CI
├── pyproject.toml      # Configuración de build
├── LICENSE             # Licencia MIT
└── README.md           # Este archivo

🧪 Ejemplo de uso

A continuación se muestra un ejemplo mínimo de cómo usar APIMA56XXT con llamadas asíncronas para obtener la lista completa de ONTs y sus potencias SNMP.

import asyncio
from jmq_olt_huawei.ma56xxt import APIMA56XXT, UserBusyError
from pprint import pprint

async def main():
    api = APIMA56XXT(
        host='192.168.88.25',
        user='root',
        password='admin',
        prompt='MA5603T',
        snmp_ip='192.168.88.25',
        snmp_port=161,
        snmp_community='public',
        debug=True
    )

    try:
        # Conexión Telnet (síncrona)
        api.connect()
        # Escaneo completo (async)
        full = await api.scan_all()
        pprint(full)
    except UserBusyError as e:
        print(f"ERROR: {e}")
    finally:
        api.close()  # Cierre de Telnet

if __name__ == '__main__':
    asyncio.run(main())

Flujo de ejemplo

  1. api.connect() abre una sesión Telnet, hace login, entra en modo enable y config.

  2. await api.scan_all() recorre:

    • get_slots() (síncrono, Telnet)

    • Para cada slot tipo GPBD: get_ports(slot) (síncrono)

    • Para cada puerto con optical_state == 'Online': await get_onts(slot, port_id) (asíncrono)

      • Cada ONT “online” dispara consultas SNMP asíncronas a await _snmp_potencia_tx(...) y await _snmp_potencia_rx(...).
  3. Devuelve una lista jerárquica de slots → puertos → ONTs con campos SNMP (ptx, prx).


🔍 Manual de uso de los métodos de APIMA56XXT

1. __init__(host, user, password, prompt, snmp_ip, snmp_port, snmp_community, timeout=2.0, debug=False)

Inicializa la clase:

  • host (str): IP o hostname de la OLT.
  • user (str): Usuario Telnet.
  • password (str): Contraseña Telnet.
  • prompt (str): Prefijo del prompt (p.ej. "MA5603T").
  • snmp_ip (str): IP de la OLT para SNMP.
  • snmp_port (int): Puerto SNMP (por defecto 161).
  • snmp_community (str): Comunidad SNMP (p.ej. "public").
  • timeout (float): Segundos de espera en lecturas Telnet (por defecto 2.0).
  • debug (bool): Si True, imprime logs de depuración.
api = APIMA56XXT(
    host='192.168.88.25',
    user='root',
    password='admin',
    prompt='MA5603T',
    snmp_ip='192.168.88.25',
    snmp_port=161,
    snmp_community='public',
    timeout=2.0,
    debug=True
)

2. connect()

Abre sesión Telnet, hace login y entra en modo operativo:

api.connect()
# Si debug=True:
# [DEBUG] Conectando a 192.168.88.25
# Conectado en modo config
  • No devuelve valor.
  • Lanza excepción (EOFError, socket.error) si la conexión falla.
  • Después de llamar, se puede invocar get_slots(), get_ports(), etc.

3. disconnect()

Sale de los modos CLI (config, enable) y cierra la sesión Telnet:

api.disconnect()
# Si debug=True imprime:
# [DEBUG] Desconectado
  • Protege contra llamadas dobles (si self.tn is None, no hace nada).

4. get_slots() → List[Tuple[str, str]]

Obtiene los slots en la OLT con display board 0:

slots = api.get_slots()
# Ejemplo:
# [('0', 'GPBD'), ('6', 'SCUN'), ('7', 'SCUN'), ('9', 'GICF')]
  • Ejecuta internamente:

    1. _send('display board 0')
    2. _read_until_prompt()
    3. Parsea cada línea para extraer (slot_id, tipo).
  • Es método síncrono (no necesita await).


5. get_ports(slot: str) → List[dict]

Obtiene la lista de puertos GPON de un slot específico:

ports = api.get_ports('0')
# Ejemplo de un diccionario en la lista:
# {
#   'id': 0,
#   'schema_fsp': '0/0/0',
#   'optical_state': 'Online',
#   'port_state': 'Offline',
#   'laser_state': 'Normal',
#   'bw': '1239040',
#   'temperature': '35',
#   'tx_bias': '12',
#   'voltage': '3.22',
#   'tx_power': '3.72',
#   'illegal_rogue_ont': 'Inexistent',
#   'max_distance': '40',
#   'wave_length': '1490',
#   'fiber_type': 'Single Mode',
#   'length': '-'
# }
  • Paso a paso:

    1. _send(f'interface gpon 0/{slot}')
    2. _read_until_prompt()
    3. _send('display port state all')
    4. _read_until_prompt()
    5. Parsea bloques con _parse_port_block.
    6. _send('quit') + _read_until_prompt()
  • Retorna lista de diccionarios, uno por puerto.

  • Método síncrono (no usa await).


6. async get_onts(slot: str, port_id: int) → List[dict]

Devuelve la lista de ONTs conectadas a un puerto GPON:

onts = await api.get_onts('0', 0)
# Ejemplo de un diccionario ONT:
# {
#   'id': 2,
#   'schema_fsp': '0/0/0',
#   'sn': '48575443E010C4AC',
#   'control_flag': 'active',
#   'run_state': 'online',
#   'config_state': 'normal',
#   'match_state': 'match',
#   'protect_side': 'no',
#   'description': 'Cliente01',
#   'ptx': 2.34,      # Potencia TX (mW), float o "" si no está disponible
#   'prx': -21.57     # Potencia RX (dBm), float o "" si no está disponible
# }
  • Internamente:

    1. _send(f'interface gpon 0/{slot}') (Telnet) + _read_until_prompt()

    2. _send(f'display ont info {port_id} all') + _read_until_prompt()

    3. _send('quit') + _read_until_prompt()

    4. Parsea sección principal y descripción con _parse_onts.

    5. Para cada ONT con run_state.lower() == 'online':

      • Llama a await _snmp_potencia_tx(slot, port_id, ont_id)
      • Llama a await _snmp_potencia_rx(slot, port_id, ont_id)
    6. Si SNMP falla, asigna "" a ptx y prx.

  • Debe invocarse con await.

  • Retorna lista de diccionarios ONT, incluyendo campos SNMP (ptx, prx).


7. async scan_all() → List[dict]

Realiza un escaneo completo de la OLT:

  1. Obtiene todos los slots vía get_slots() (síncrono).

  2. Para cada slot de tipo GPBD:

    • Llama a get_ports(slot) (síncrono).

    • Para cada puerto donde optical_state.lower() == 'online':

      • Llama a await get_onts(slot, port_id) (asíncrono).
    • Agrega un campo 'onts' a cada puerto (lista vacía si está offline).

  3. Devuelve lista de diccionarios, cada uno así:

{
  "id": "0",
  "tipo": "GPBD",
  "ports": [
    {
      "id": 0,
      "schema_fsp": "0/0/0",
      "optical_state": "Online",
      "port_state": "Online",
      "laser_state": "Normal",
      "bw": "1239040",
      "temperature": "35",
      "tx_bias": "12",
      "voltage": "3.22",
      "tx_power": "3.72",
      "illegal_rogue_ont": "Inexistent",
      "max_distance": "40",
      "wave_length": "1490",
      "fiber_type": "Single Mode",
      "length": "-",
      "onts": [
        {
          "id": 2,
          "schema_fsp": "0/0/0",
          "sn": "48575443E010C4AC",
          "control_flag": "active",
          "run_state": "online",
          "config_state": "normal",
          "match_state": "match",
          "protect_side": "no",
          "description": "Cliente01",
          "ptx": 2.34,
          "prx": -21.57
        }
      ]
    },
    {
      "id": 1,
      "schema_fsp": "0/0/1",
      "optical_state": "Offline",
      "port_state": "Offline",
      // …otros campos
      "onts": []
    }
    // … más puertos
  ]
}
  • Debe invocarse con await api.scan_all().
  • Incluye SNMP asíncrono para ONTs “online”.

🛠️ Métodos y privados útiles

  • _read_until_prompt() Lee líneas desde Telnet hasta detectar el prompt dinámico. Atiende paginación (More, ---- More) y errores de login acumulado (UserBusyError).

  • _parse_port_block(slot, bloque)dict | None Dado un bloque de texto de display port state all, extrae campos como id, optical_state, laser_state, bw, etc.

  • _parse_onts(raw, slot, port_id)List[dict] Dada la salida de display ont info {port_id} all, extrae la tabla de ONTs y sus descripciones. Devuelve lista de dicts con id, sn, run_state, etc.

  • _calcular_codigo(slot, pon)str Calcula la parte numérica base del OID SNMP:

    base = 4194304000
    código = base + pon_int * 256 + slot_int * 8192
    devuelve "<código>."
    
  • “Helpers” SNMP asíncronos (async def)

    • _consultagetsnmp(codigo_oid: str) → int | None Crea un UdpTransportTarget con await UdpTransportTarget.create((ip, port), timeout=1, retries=0), luego hace await get_cmd(...).
    • _snmp_potencia_tx(slot, port, ont) → float | "" Llama a await _consultagetsnmp(...) con OID para potencia TX y procesa la respuesta (mW).
    • _snmp_potencia_rx(slot, port, ont) → float | "" Similar para potencia RX (dBm).

✅ Tests

Para ejecutar pruebas unitarias:

pytest -s tests/test_ma56xxt.py
  • Asegúrate de tener un entorno de testing adecuado o mocks para Telnet/SNMP.
  • Los tests deben validar parsing de puertos, ONTs y comportamiento SNMP asíncrono.

🧩 Funcionalidades destacadas

  • Prompt dinámico configurable Detecta automáticamente prompts como MA5603T>, MA5603T(config)#, etc.
  • Manejo de paginación Envía \n si detecta líneas con More, ---- More o Press 'Q'.
  • Bloqueo de usuario Si la OLT informa “Reenter times have reached the upper limit”, lanza UserBusyError.
  • SNMP asíncrono (PySNMP ≥ 6.2) Obtiene potencias TX/RX de cada ONT “online” de forma concurrente sin bloquear la lectura Telnet.
  • Escaneo jerárquico (slots → puertos → ONTs) Permite obtener toda la información en una sola llamada a await scan_all().

📢 Contribuyendo

  1. Haz un fork del repositorio.

  2. Crea una rama:

    git checkout -b feature/nombre-de-funcionalidad
    
  3. Realiza tus cambios, añade tests y actualiza este README si es necesario.

  4. Haz commit y push a tu rama.

  5. Abre un Pull Request describiendo los cambios propuestos.


⚖️ Licencia

MIT © Juanma Quijada


🔗 Enlaces

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jmq_olt_huawei-1.0.0.tar.gz (15.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jmq_olt_huawei-1.0.0-py3-none-any.whl (10.8 kB view details)

Uploaded Python 3

File details

Details for the file jmq_olt_huawei-1.0.0.tar.gz.

File metadata

  • Download URL: jmq_olt_huawei-1.0.0.tar.gz
  • Upload date:
  • Size: 15.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for jmq_olt_huawei-1.0.0.tar.gz
Algorithm Hash digest
SHA256 56ba27b67c81b643392b783c51d052e841c3d9a34bef92ab12c0734debe42994
MD5 e32888b387852ffc6fe4c0ccad23a2d0
BLAKE2b-256 e668ff6d62305b247e7cdaf278d93002db1d9af8e72da7ebb57fbddd490bda22

See more details on using hashes here.

Provenance

The following attestation bundles were made for jmq_olt_huawei-1.0.0.tar.gz:

Publisher: python-publish.yml on juaquicar/jmq_olt_huawei

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jmq_olt_huawei-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: jmq_olt_huawei-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 10.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for jmq_olt_huawei-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6ee00686b5e847e06303b73138c6f4a20df9b81f69dea207525b5c4f4286aeff
MD5 f929365bf49b77e383cd1663f922228b
BLAKE2b-256 fce3a4b77e9190f3fe5e52d28c7e3eee08d09860ee765588192e9259598706b4

See more details on using hashes here.

Provenance

The following attestation bundles were made for jmq_olt_huawei-1.0.0-py3-none-any.whl:

Publisher: python-publish.yml on juaquicar/jmq_olt_huawei

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page