El conjunto de documentos para este producto aspira al uso de un lenguaje no discriminatorio. A los fines de esta documentación, "no discriminatorio" se refiere al lenguaje que no implica discriminación por motivos de edad, discapacidad, género, identidad de raza, identidad étnica, orientación sexual, nivel socioeconómico e interseccionalidad. Puede haber excepciones en la documentación debido al lenguaje que se encuentra ya en las interfaces de usuario del software del producto, el lenguaje utilizado en función de la documentación de la RFP o el lenguaje utilizado por un producto de terceros al que se hace referencia. Obtenga más información sobre cómo Cisco utiliza el lenguaje inclusivo.
Cisco ha traducido este documento combinando la traducción automática y los recursos humanos a fin de ofrecer a nuestros usuarios en todo el mundo contenido en su propio idioma. Tenga en cuenta que incluso la mejor traducción automática podría no ser tan precisa como la proporcionada por un traductor profesional. Cisco Systems, Inc. no asume ninguna responsabilidad por la precisión de estas traducciones y recomienda remitirse siempre al documento original escrito en inglés (insertar vínculo URL).
Este documento describe cómo resolver problemas de notificaciones EPNM cuando se utiliza la API REST para acceder a la información de fallas del dispositivo.
El cliente que implemente debe ser capaz de gestionar y suscribirse a cualquiera de los dos mecanismos que utiliza Evolved Programmable Network Manager (EPNM) para enviar notificaciones.
Las notificaciones alertan a los administradores y operadores de red sobre eventos o problemas importantes relacionados con la red. Estas notificaciones ayudan a garantizar que los posibles problemas se detectan y resuelven rápidamente, lo que reduce el tiempo de inactividad y mejora el rendimiento general de la red.
EPNM puede administrar diferentes métodos, como notificaciones por correo electrónico, capturas del protocolo simple de administración de red (SNMP) a receptores especificados o mensajes de Syslog a servidores Syslog externos. Además de estos métodos, EPNM también proporciona una interfaz de programación de aplicaciones de transferencia de estado representacional (API REST) que se puede utilizar para recuperar información sobre el inventario, alarmas, activación de servicios, ejecución de plantillas y alta disponibilidad.
Actualmente, las notificaciones basadas en API son compatibles con el uso de dos mecanismos diferentes:
Todas las notificaciones comparten el mismo esquema y se pueden recuperar en formatos JSON o XML.
De forma predeterminada, las notificaciones de alarma e inventario están desactivadas. Para habilitarlos, cambie el restconf-config.properties
como se indica (no es necesario reiniciar la aplicación EPNM):
/opt/CSCOlumos/conf/restconf/restconf-config.properties
epnm.restconf.inventory.notifications.enabled=true
epnm.restconf.alarm.notifications.enabled=true
En la imagen, el equipo cliente ejecuta un WebSocket y se suscribe al EPNM con una URL predefinida, con autenticación básica y a través de un canal HTTPS seguro.
La biblioteca de cliente WebSocket en Python se puede utilizar para crear un WebSocket en el equipo cliente.
import websocket
import time
import ssl
import base64
def on_message(ws, message):
print(message)
def on_error(ws, error):
print(error)
def on_close(ws, close_status_code, close_msg):
print("### closed \###")
def on_open(ws):
ws.send("Hello, Server!")
if __name__ == "__main__":
username = "username"
password = "password"
credentials = base64.b64encode(f"{username}:{password}".encode("utf-8")).decode("utf-8")
headers = {"Authorization": f"Basic {credentials}"}
websocket.enableTrace(True)
ws = websocket.WebSocketApp("wss://10.122.28.3/restconf/streams/v1/inventory.json",
on_message=on_message,
on_error=on_error,
on_close=on_close,
header=headers)
ws.on_open = on_open
ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
Este código configura un cliente WebSocket que se suscribe a EPNM en wss://10.122.28.3/restconf/streams/v1/inventory.json.
Utiliza el Python WebSocket
para establecer la conexión y manejar los mensajes de entrada y salida. La suscripción también puede ser (en función del tipo de notificación a la que desea suscribirse):
/restconf/streams/v1/alarm{.xml | .json}
/restconf/streams/v1/service-activation{.xml | .json}
/restconf/streams/v1/template-execution{.xml | .json}
/restconf/streams/v1/all{.xml | .json}
on_message
, on_error
y on_close
Las funciones son funciones de devolución de llamada a las que se llama cuando la conexión WebSocket recibe un mensaje, encuentra un error o está cerrada, respectivamente. on_open
función es una devolución de llamada que se llama cuando la conexión WebSocket está establecida y lista para utilizarse.
username
y password
se establecen en las credenciales de inicio de sesión necesarias para acceder al servidor remoto. Estas credenciales se codifican con el base64
y se agrega a los encabezados de la solicitud WebSocket.
run_forever
se llama a este método en el objeto WebSocket para iniciar la conexión, mantenerla abierta indefinidamente y escuchar los mensajes que provienen del servidor. sslopt
se utiliza para configurar las opciones SSL/TLS para la conexión. CERT_NONE
marca deshabilita la validación de certificación.
Ejecute el código Para tener WebSocket listo para recibir las notificaciones:
(env) devasc@labvm:~/epnm$ python conn-oriented.py
--- request header ---
GET /restconf/streams/v1/inventory.json HTTP/1.1
Upgrade: websocket
Host: 10.122.28.3
Origin: https://10.122.28.3
Sec-WebSocket-Key: YYYYYYYYYYY
Sec-WebSocket-Version: 13
Connection: Upgrade
Authorization: Basic XXXXXXXXXXXX
-----------------------
--- response header ---
HTTP/1.1 101
Set-Cookie: JSESSIONID=5BFB68B0126226A0A13ABE595DC63AC9; Path=/restconf; Secure; HttpOnly
Strict-Transport-Security: max-age=31536000;includeSubDomains
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: Ozns7PGgHjrXj0nAgnlhbyVKPjc=
Date: Thu, 30 Mar 2023 16:18:19 GMT
Server: Prime
-----------------------
Websocket connected
++Sent raw: b'\x81\x8es\x99ry;\xfc\x1e\x15\x1c\xb5R*\x16\xeb\x04\x1c\x01\xb8'
++Sent decoded: fin=1 opcode=1 data=b'Hello, Server!'
++Rcv raw: b'\x81\x0eHello, Server!'
++Rcv decoded: fin=1 opcode=1 data=b'Hello, Server!'
Hello, Server!
Puede verificar las suscripciones de notificación al servidor con esta consulta de base de datos:
ade # ./sql_execution.sh "SELECT * from RstcnfNtfctnsSbscrptnMngr WHERE CONNECTIONTYPE = 'connection-oriented';" > /localdisk/sftp/conn-oriented.txt
Con el fin de visualizar mejor el conn-oriented.txt
(que es el resultado de la consulta DB), puede convertirlo a HTML utilizando una herramienta como aha
(aquí se ilustra su uso en una máquina Ubuntu):
devasc@labvm:~/tmp$ sudo apt-get install aha
devasc@labvm:~/tmp$ cat conn-oriented.txt | aha > conn-oriented.html
A continuación, abra el conn-oriented.html
archivo en un explorador:
A partir de la documentación en línea de EPNM, una vez establecida, la misma conexión se mantiene activa durante todo el ciclo de vida de la aplicación:
Si, por alguna razón, necesita eliminar una suscripción específica, puede enviar una HTTP DELETE
solicitud con el SUBSCRIPTIONID
especificado en la URL https://
. Por ejemplo:
devasc@labvm:~/tmp$ curl --location --insecure --request DELETE 'https://10.122.28.3/restconf/data/v1/cisco-notifications:subscription/3648313822269611499' \ > --header 'Accept: application/json' \ > --header 'Content-Type: application/json' \ > --header 'Authorization: Basic XXXXXXXX'
Verificación de mensajes, entradas de DEBUG, show log
, Nombre de archivo utilizado, Salidas de SQL
Para resolver problemas por qué un cliente que utiliza un mecanismo orientado a la conexión no recibe notificaciones correctamente, puede ejecutar la consulta de base de datos indicada y verificar si la suscripción está presente o no. Si no está presente, pida al propietario del cliente que se asegure de emitir la suscripción.
Mientras tanto, puede habilitar el nivel DEBUG en
com.cisco.nms.nbi.epnm.restconf.notifications.handler.NotificationsHandlerAdapter
para poder captarla siempre que se envíe la suscripción:
ade # sudo /opt/CSCOlumos/bin/setLogLevel.sh com.cisco.nms.nbi.epnm.restconf.notifications.handler.NotificationsHandlerAdapter DEBUG 2>/dev/null Loglevel set to DEBUG for com.cisco.nms.nbi.epnm.restconf.notifications.handler.NotificationsHandlerAdapter .
Después de enviar la suscripción, puede comprobar si aparece una entrada con la dirección IP del cliente WebSocket en
localhost_access_log.txt
:
ade # zgrep -h '"GET /restconf/streams/.* HTTP/1.1" 101' $(ls -1t /opt/CSCOlumos/logs/localhost_access_log.txt*) 10.82.244.205 - - [28/Aug/2023:16:13:03 -0300] "GET /restconf/streams/v1/inventory.json HTTP/1.1" 101 - 10.82.244.205 - - [28/Aug/2023:22:17:05 -0300] "GET /restconf/streams/v1/inventory.json HTTP/1.1" 101 -
Finalmente, verifique nuevamente la BD (observe que la marca de tiempo coincide con la entrada en
localhost_access_log.txt
).
El siguiente registro muestra cuándo se envían las solicitudes POST de suscripciones:
ade # grep -Eh 'DEBUG com.cisco.nms.nbi.epnm.restconf.notifications.handler.NotificationsHandlerAdapter - (Successfully subscribed a connection-oriented|Requested resource uuid)' $(ls -1t /opt/CSCOlumos/logs/restconf-nbi.log*) 2023-08-28 22:17:06,221: DEBUG com.cisco.nms.nbi.epnm.restconf.notifications.handler.NotificationsHandlerAdapter - Successfully subscribed a connection-oriented subscription with user: root and topic: inventory 2023-08-28 22:17:06,221: DEBUG com.cisco.nms.nbi.epnm.restconf.notifications.handler.NotificationsHandlerAdapter - Successfully subscribed a connection-oriented subscription with user: root and topic: inventory 2023-08-28 22:17:06,221: DEBUG com.cisco.nms.nbi.epnm.restconf.notifications.handler.NotificationsHandlerAdapter - Requested resource uuid 852a674a-e3d0-4ecc-8ea0-787af30f1305 2023-08-28 22:17:06,221: DEBUG com.cisco.nms.nbi.epnm.restconf.notifications.handler.NotificationsHandlerAdapter - Requested resource uuid 852a674a-e3d0-4ecc-8ea0-787af30f1305
Mientras la conexión se mantenga activa, se envía una notificación de tipo push-change-update desde el servidor EPN-M a todos los clientes que se suscribieron para recibir notificaciones. El ejemplo muestra una de las notificaciones que envía el EPNM cuando se cambia el nombre de host de un NCS2k:
{ "push.push-change-update":{ "push.notification-id":2052931975556780123, "push.topic":"inventory", "push.time-of-update":"2023-03-31 13:50:36.608", "push.time-of-update-iso8601":"2023-03-31T13:50:39.681-03:00", "push.operation":"push:modify", "push.update-data":{ "nd.node":{ "nd.description":"SOFTWARE=ONS,IPADDR=10.10.1.222,IPMASK=255.255.255.0,DEFRTR=255.255.255.255,IPV6ENABLE=N,IIOPPORT=57790,NAME=\\"tcc222c\\",SWVER=11.1.23,LOAD=11.123-022-D2911-W,PROTSWVER=none,PROTLOAD=none,DEFDESC=\\"Factory Defaults\\",PLATFORM=NCS2KFS-M15,SECUMODE=NORMAL,SUPPRESSIP=NO,MODE=MULTISHELF,AUTOPM=NO,SERIALPORTECHO=N,OSIROUTINGMODE=IS1,OSIL1BUFSIZE=512,NET=39840F800000000000000000000E67AD8A01DE00,SYSTEMMODE=SONET,ALARMSUPPRESS=N,CVSTATUS=VERIFICATION_IDLE,DEGILTHR=1.5,FAILILTHR=4.0,LATITUDE=N381343,LONGITUDE=W1223808,LCDSETTING=ALLOW-CONFIGURATION,NODEID=AD8A01DE,NODECVSTATUS=TRUE,ENABLESOCKSPROXY=FALSE,PROXYPORT=1080,ALARMPROFILENAME=\\"Default\\",COOLINGPROFILECTRL=AUTO,MACADDR=0e-67-ffffffad-ffffff8a-01-ffffffde,SUBNETMASKLEN=24,FORWARDDHCPENABLE=N,UPTIME=\\"217days\/14hours\/40mins\/17secs\\",DISCARDOTDRALARM=YES,CVTIMEBTWRUN=360", "nd.equipment-list":"", "nd.fdn":"MD=CISCO_EPNM!ND=tcc222c", "nd.sys-up-time":"217 days, 14:40:170.00" } } } }
Notificaciones sin conexión
El siguiente es el flujo de trabajo en el caso de
connectionless
notificaciones:
Ejecutar un cliente de Python del servicio web REST
Se espera que el usuario tenga un servicio web REST que sea capaz de aceptar cargas XML y/o JSON como una solicitud POST. Este servicio REST es el terminal al que se conecta el Cisco EPNMel marco de notificaciones restconf publica notificaciones. Se trata de un ejemplo de un servicio web REST que se va a instalar en el equipo remoto:
from flask import Flask, request, jsonify app = Flask(__name__) @ app.route('/api/posts', methods=['POST']) def create_post(): post_data = request.get_json() response = {'message': 'Post created successfully'} print(post_data) return jsonify(response), 201 if __name__ == '__main__': app.run(debug=True, host='10.122.28.2', port=8080)
Se trata de una aplicación web de Python Flask que define un único terminal
/api/posts
que acepta HTTP POST
solicitudes. create_post()
función se llama siempre que un HTTP POST
la solicitud se realiza a /api/posts
.
Dentro de la create_post()
función, los datos de la solicitud que entra se recuperan con el uso de request.get_json()
, que devuelve un diccionario de la carga de JSON. La carga útil se imprime con print(post_data)
con fines de depuración. Después de esto, se crea un mensaje de respuesta con la clave message
y valor Post created successfully
(en formato de diccionario). Este mensaje de respuesta se devuelve al cliente con un código de estado HTTP 201 (creado).
if __name__ == '__main__':
es una construcción estándar de Python que verifica si el script se ejecuta como el programa principal, en lugar de importarse como un módulo. Si la secuencia de comandos se ejecuta como programa principal, inicia la aplicación Flask y la ejecuta en la dirección IP y el puerto especificados. debug=True
habilita el modo de depuración, que proporciona mensajes de error detallados y recarga automática del servidor cuando se realizan cambios en el código.
Ejecute el programa para iniciar el
REST
servicio web:
(venv) [apinelli@centos8_cxlabs_spo app]$ python connectionless.py * Serving Flask app 'connectionless' (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: on * Running on http://10.122.28.2:8080/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 117-025-064
Suscripción de un cliente sin conexión
El usuario se suscribe a las notificaciones:
REST
se envía el extremo de servicio junto con el tema para suscribirse a. En este caso, el tema es all
.
[apinelli@centos8_cxlabs_spo ~]$ curl --location -X POST --insecure 'https://10.122.28.3/restconf/data/v1/cisco-notifications:subscription' \ > --header 'Accept: application/json' \ > --header 'Content-Type: application-json' \ > --header 'Authorization: Basic XXXXXXXXX' \ > --data '{ > "push.endpoint-url":"http://10.122.28.2:8080/api/posts", > "push.topic":"all", > "push.format": "json" > }'
La respuesta esperada es una respuesta 201, junto con los detalles de la suscripción en el cuerpo de la respuesta:
{ "push.notification-subscription": { "push.subscription-id": 7969974728822328535, "push.subscribed-user": "root", "push.endpoint-url": "http:\/\/10.122.28.2:8080\/api\/posts", "push.topic": "all", "push.creation-time": "Tue Aug 29 10:02:05 BRT 2023", "push.creation-time-iso8601": "2023-08-29T10:02:05.887-03:00", "push.time-of-update": "Tue Aug 29 10:02:05 BRT 2023", "push.time-of-update-iso8601": "2023-08-29T10:02:05.887-03:00", "push.format": "json", "push.connection-type": "connection-less" } }
Es posible obtener la lista de notificaciones a las que está suscrito el usuario con una solicitud GET:
curl --location --insecure 'https://10.122.28.3/restconf/data/v1/cisco-notifications:subscription' \ --header 'Accept: application/json' \ --header 'Content-Type: application/json' \ --header 'Authorization: Basic XXXXXXXXXXX'
La respuesta obtenida fue la siguiente:
{ "com.response-message": { "com.header": { "com.firstIndex": 0, "com.lastIndex": 1 }, "com.data": { "push.notification-subscription": [ { "push.subscription-id": 2985507860170167151, "push.subscribed-user": "root", "push.endpoint-url": "http://10.122.28.2:8080/api/posts", "push.session-id": 337897630, "push.topic": "inventory", "push.creation-time": "Fri Mar 31 17:45:47 BRT 2023", "push.time-of-update": "Fri Mar 31 17:45:47 BRT 2023", "push.format": "json", "push.connection-type": "connection-less" }, { "push.subscription-id": 7969974728822328535, "push.subscribed-user": "root", "push.endpoint-url": "http://10.122.28.2:8080/api/posts", "push.session-id": 0, "push.topic": "all", "push.creation-time": "Tue Aug 29 10:02:05 BRT 2023", "push.time-of-update": "Tue Aug 29 10:02:05 BRT 2023", "push.format": "json", "push.connection-type": "connection-less" } ] } } }
Verificación de mensajes, entradas de DEBUG, show log,
Nombre de archivo utilizado, salidas de SQL
Observe en la respuesta que hay dos suscripciones: una para
all ("push.topic": "all")
y uno para el inventario ("push.topic": "inventory")
. Puede confirmarlo con una consulta a la base de datos (observe que el tipo de suscripción es 'sin conexión' y el SUBSCRIPTIONID
coinciden con el resultado de la GET
(resaltado en amarillo):
ade # ./sql_execution.sh "SELECT * from RstcnfNtfctnsSbscrptnMngr WHERE CONNECTIONTYPE = 'connection-less';" > /localdisk/sftp/connectionless.txt
Si necesita eliminar una suscripción sin conexión, puede enviar una
HTTP DELETE
con la ID de suscripción que desea eliminar. Suponga que desea eliminar subscription-id 2985507860170167151
:
curl --location --insecure --request DELETE 'https://10.122.28.3/restconf/data/v1/cisco-notifications:subscription/2985507860170167151' \ --header 'Accept: application/json' \ --header 'Content-Type: application-json' \ --header 'Authorization: Basic XXXXXXXXXX'
Ahora, si vuelve a consultar la base de datos, sólo verá la suscripción con
SUBSCRIPTIONID
igual a 7969974728822328535
.
Cuando se produce un cambio en el inventario, el cliente imprime las notificaciones (que son del mismo tipo que el
connection-oriented
las notificaciones que aparecen en la sección acerca de connected-oriented
clientes), seguido de la respuesta de 2011:
(venv) [apinelli@centos8_cxlabs_spo app]$ python connectionless.py * Serving Flask app 'connectionless' (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: on * Running on http://10.122.28.2:8080/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 117-025-064 {'push.push-change-update': {'push.notification-id': -2185938612268228828, 'push.topic': 'inventory', 'push.time-of-update': '2023-03-31 17:47:04.865', 'push.time-of-update-iso8601': '2023-03-31T17:47:10.846-03:00', 'push.operation': 'push:modify', 'push.update-data': {'nd.node': {'nd.collection-status': 'Synchronizing', 'nd.equipment-list': '', 'nd.fdn': 'MD=CISCO_EPNM!ND=tcc221'}}}} 10.122.28.3 - - [31/Mar/2023 16:47:23] "POST /api/posts HTTP/1.1" 201 - {'push.push-change-update': {'push.notification-id': -1634959052215805274, 'push.topic': 'inventory', 'push.time-of-update': '2023-03-31 17:47:12.786', 'push.time-of-update-iso8601': '2023-03-31T17:47:14.935-03:00', 'push.operation': 'push:modify', 'push.update-data': {'nd.node': {'nd.equipment-list': '', 'nd.fdn': 'MD=CISCO_EPNM!ND=tcc221c', 'nd.name': 'tcc221c'}}}} 10.122.28.3 - - [31/Mar/2023 16:47:27] "POST /api/posts HTTP/1.1" 201 -
Conclusión
En este documento, los dos tipos de notificaciones basadas en API que se pueden configurar en EPNM (
connectionless
y connection-oriented
) y se proporcionan ejemplos de los clientes respectivos que pueden utilizarse como base para fines de simulación.
Información Relacionada
Revisión | Fecha de publicación | Comentarios |
---|---|---|
1.0 |
10-Apr-2023 |
Versión inicial |