O conjunto de documentação deste produto faz o possível para usar uma linguagem imparcial. Para os fins deste conjunto de documentação, a imparcialidade é definida como uma linguagem que não implica em discriminação baseada em idade, deficiência, gênero, identidade racial, identidade étnica, orientação sexual, status socioeconômico e interseccionalidade. Pode haver exceções na documentação devido à linguagem codificada nas interfaces de usuário do software do produto, linguagem usada com base na documentação de RFP ou linguagem usada por um produto de terceiros referenciado. Saiba mais sobre como a Cisco está usando a linguagem inclusiva.
A Cisco traduziu este documento com a ajuda de tecnologias de tradução automática e humana para oferecer conteúdo de suporte aos seus usuários no seu próprio idioma, independentemente da localização. Observe que mesmo a melhor tradução automática não será tão precisa quanto as realizadas por um tradutor profissional. A Cisco Systems, Inc. não se responsabiliza pela precisão destas traduções e recomenda que o documento original em inglês (link fornecido) seja sempre consultado.
Este documento descreve como solucionar problemas de Notificações de EPNM quando a API REST é usada para acessar informações de falha do dispositivo.
O cliente que você implementar deverá ser capaz de manipular e assinar qualquer um dos dois mecanismos usados pelo Evolved Programmable Network Manager (EPNM) para enviar notificações.
As notificações alertam administradores e operadores de rede sobre eventos ou problemas importantes relacionados à rede. Essas notificações ajudam a garantir que possíveis problemas sejam detectados e resolvidos rapidamente, o que reduz o tempo de inatividade e melhora o desempenho geral da rede.
O EPNM pode lidar com diferentes métodos, como notificações via e-mail, traps do protocolo de gerenciamento de rede simples (SNMP - Simple Network Management Protocol) para receptores especificados ou mensagens de Syslog para servidores Syslog externos. Além desses métodos, o EPNM também fornece uma interface de programação de aplicativos de transferência de estado representacional (REST API) que pode ser usada para recuperar informações sobre inventário, alarmes, ativação de serviço, execução de modelo e alta disponibilidade.
Notificações baseadas em API são suportadas atualmente com o uso de dois mecanismos diferentes:
Todas as notificações compartilham o mesmo esquema e podem ser recuperadas nos formatos JSON ou XML.
Por padrão, as notificações de alarme e inventário são desativadas. Para habilitá-los, altere o restconf-config.properties
como indicado (não é necessário reiniciar o aplicativo EPNM):
/opt/CSCOlumos/conf/restconf/restconf-config.properties
epnm.restconf.inventory.notifications.enabled=true
epnm.restconf.alarm.notifications.enabled=true
Na figura, a máquina cliente executa um WebSocket e se inscreve no EPNM com um URL predefinido, com autenticação básica e através de um canal HTTPS seguro.
A biblioteca cliente WebSocket em Python pode ser usada para criar um WebSocket na máquina 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 um cliente WebSocket que assina o EPNM em wss://10.122.28.3/restconf/streams/v1/inventory.json.
Ele usa o Python WebSocket
para estabelecer a conexão e manipular mensagens de entrada e saída. A assinatura também pode ser (com base no tipo de notificação que você deseja assinar):
/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}
O on_message
, on_error
e on_close
As funções são funções de retorno de chamada que são chamadas quando a conexão WebSocket recebe uma mensagem, encontra um erro ou é fechada, respectivamente. O on_open
é um retorno de chamada que é chamado quando a conexão WebSocket é estabelecida e está pronta para uso.
O username
e password
as variáveis são definidas para as credenciais de login necessárias para acessar o servidor remoto. Essas credenciais são codificadas com o comando base64
e adicionado aos cabeçalhos da solicitação WebSocket.
O run_forever
é chamado no objeto WebSocket para iniciar a conexão, mantê-la aberta indefinidamente e ouvir as mensagens que vêm do servidor. O sslopt
é usado para configurar as opções SSL/TLS para a conexão. O CERT_NONE
flag desabilita a validação de certificação.
Execute o código para que o WebSocket esteja pronto para receber as notificações:
(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!
Você pode verificar as assinaturas de notificação para o servidor com esta consulta DB:
ade # ./sql_execution.sh "SELECT * from RstcnfNtfctnsSbscrptnMngr WHERE CONNECTIONTYPE = 'connection-oriented';" > /localdisk/sftp/conn-oriented.txt
Para melhor visualizar a conn-oriented.txt
(que é o resultado da consulta DB), você pode convertê-lo em HTML usando uma ferramenta como aha
(aqui seu uso é ilustrado em uma máquina Ubuntu):
devasc@labvm:~/tmp$ sudo apt-get install aha
devasc@labvm:~/tmp$ cat conn-oriented.txt | aha > conn-oriented.html
Em seguida, abra o conn-oriented.html
em um navegador:
A partir da documentação on-line do EPNM, uma vez estabelecida, a mesma conexão é mantida ativa durante todo o ciclo de vida do aplicativo:
Se, por algum motivo, for necessário excluir uma assinatura específica, você poderá enviar uma HTTP DELETE
com a SUBSCRIPTIONID
especificado na URL https://
. Por exemplo:
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'
Verificação de Mensagens, Entradas DEBUG, show log
, Nome de Arquivo Usado, Saídas SQL
Para solucionar o motivo pelo qual um cliente que usa um mecanismo orientado a conexão não recebe notificações corretamente, você pode executar a consulta DB indicada e verificar se a assinatura está presente ou não. Se ela não estiver presente, peça ao proprietário do cliente para garantir a emissão da assinatura.
Enquanto isso, você pode ativar o nível de DEPURAÇÃO em
com.cisco.nms.nbi.epnm.restconf.notifications.handler.NotificationsHandlerAdapter
para que você possa capturá-la sempre que a assinatura for enviada:
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 .
Após o envio da assinatura, você poderá verificar se uma entrada com o endereço IP do cliente WebSocket aparece na
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 novamente o DB (observe que o timestamp corresponde à entrada em
localhost_access_log.txt
).
O próximo registro mostra quando as solicitações POST para assinaturas são enviadas:
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
Enquanto a conexão for mantida ativa, uma notificação do tipo push-change-update será enviada do servidor EPN-M para todos os clientes que assinaram as notificações. O exemplo mostra uma das notificações enviadas pelo EPNM quando o nome de host de um NCS2k é alterado:
{ "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" } } } }
Notificações sem conexão
O próximo é o fluxo de trabalho no caso de
connectionless
notificações:
Executar um cliente Python de serviço Web REST
Espera-se que o usuário tenha um serviço Web REST que seja capaz de aceitar payloads XML e/ou JSON como uma solicitação POST. Esse serviço REST é o ponto final para o qual o EPNM da Ciscoa estrutura de notificações restconf publica notificações. Este é um exemplo de um serviço Web REST a ser instalado na máquina remota:
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)
Esta é uma aplicação Web Python Flask que define um único ponto final
/api/posts
que aceita HTTP POST
solicitações. O create_post()
função é chamada sempre que um HTTP POST
pedido é feito a /api/posts
.
Dentro do create_post()
função, os dados da solicitação recebidos são recuperados com o uso de request.get_json()
, que retorna um dicionário do payload JSON. A carga útil é então impressa com print(post_data)
para depuração. Depois disso, uma mensagem de resposta é criada com a tecla message
e valor Post created successfully
(em formato de dicionário). Essa mensagem de resposta é retornada ao cliente com um código de status HTTP 201 (criado).
O
if __name__ == '__main__':
block é uma construção padrão do Python que verifica se o script é executado como o programa principal, em vez de importado como um módulo. Se o script for executado como o programa principal, ele iniciará o aplicativo Flask e o executará no endereço IP e na porta especificados. O debug=True
O argumento ativa o modo de depuração, que fornece mensagens de erro detalhadas e o recarregamento automático do servidor quando são feitas alterações no código.
Execute o programa para iniciar o
REST
serviço 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
Assinatura de um cliente sem conexão
O usuário assina as notificações: o
REST
o ponto final de serviço é enviado junto com o tópico para se inscrever. Neste caso, o tópico é 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" > }'
A resposta esperada é uma resposta de 201, junto com os detalhes da assinatura no corpo da resposta:
{ "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" } }
É possível obter a lista de notificações em que o usuário está inscrito com uma solicitação 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'
A resposta obtida é:
{ "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" } ] } } }
Verificação de Mensagens, Entradas DEBUG, show log,
Nome de arquivo usado, Saídas SQL
Observe na resposta que há duas assinaturas: uma para
all ("push.topic": "all")
e um para inventário ("push.topic": "inventory")
. Você pode confirmá-la com uma consulta ao banco de dados (observe que o tipo de assinatura é 'sem conexão' e o SUBSCRIPTIONID
correspondem à saída do comando GET
como destacado em amarelo):
ade # ./sql_execution.sh "SELECT * from RstcnfNtfctnsSbscrptnMngr WHERE CONNECTIONTYPE = 'connection-less';" > /localdisk/sftp/connectionless.txt
Se precisar excluir uma assinatura sem conexão, você pode enviar uma
HTTP DELETE
com a ID de assinatura que deseja excluir. Suponha que você queira excluir 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'
Agora, se você consultar o BD novamente, verá apenas a assinatura com
SUBSCRIPTIONID
igual a 7969974728822328535
.
Quando ocorre uma alteração no inventário, o cliente imprime as notificações (que são do mesmo tipo que o
connection-oriented
as notificações vistas na seção sobre connected-oriented
clientes), seguido da resposta de 201:
(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 -
Conclusão
Neste documento, os dois tipos de notificações baseadas em API que podem ser configurados no EPNM (
connectionless
e connection-oriented
) são explicados e são dados exemplos dos respectivos clientes que podem ser utilizados como base para efeitos de simulação.
Informações Relacionadas
Revisão | Data de publicação | Comentários |
---|---|---|
1.0 |
10-Apr-2023 |
Versão inicial |