Ceci est la publication du 20e jour de NetOpsCoding AdventCalender. Je suis désolé d'être en retard.
J'ai écrit cet article il y a deux semaines. Essayez de passer du logiciel au routeur avec SSH (Exscript)
L'un des défis de la configuration automatique d'un routeur est que le processus d'analyse syntaxique de l'interface de ligne de commande du routeur est très difficile. Étant donné que le résultat de sortie de la commande de contrôle d'état dans l'interface de ligne de commande du routeur est effectué en supposant qu'il est vérifié visuellement par une personne, il est subtil lorsque vous essayez d'extraire automatiquement des informations à l'aide du package SSH ou Telnel fourni dans le langage de programmation. C'est très difficile car il est nécessaire de gérer de manière flexible les sauts de ligne, les espaces, les caractères spéciaux, l'affichage des erreurs, etc.
L'histoire ici est également présentée dans le blog de @ yuyarin, alors veuillez vous y référer. Ce qui est requis de l'équipement réseau pour l'automatisation des opérations
Afin de se débarrasser du problème du traitement de l'analyse CLI / traitement des exceptions par SSH et Telnet, je voudrais cette fois créer un logiciel utilisant NETCONF, qui attire l'attention dans l'automatisation. Pour être honnête, l'état de mise en œuvre varie en fonction du fabricant, donc je ne suis pas sûr qu'il puisse être utilisé immédiatement au travail, mais cette fois je vais l'essayer à titre d'étude.
NETCONF est un protocole standardisé pour la configuration des périphériques réseau et l'acquisition d'informations. NETCONF est apparu comme une alternative au SNMP, mais il devrait être utilisé dans les périphériques réseau de plusieurs fournisseurs et a attiré l'attention en combinaison avec l'automatisation récente et le boom du SDN.
Tous les paramètres de configuration et la sortie d'état dans NETCONF sont définis au format XML-RPC, et le traitement d'analyse de syntaxe mentionné ci-dessus peut être effectué avec le minimum nécessaire.
Quand je l'ai vérifié il y a environ un an, il semblait que NETCONF était presque implémenté dans le dernier système d'exploitation des principaux fabricants. Cependant, l'état de mise en œuvre et les fonctions prises en charge diffèrent considérablement selon la version du système d'exploitation, il est donc recommandé de vérifier si NETCONF est pris en charge par votre version.
Manufactor | OS | Supported API |
---|---|---|
Cisco | IOS or IOS XE | OnePK, NETCONF |
IOS XR | OnePK, NETCONF | |
NX OS | OnePK, NX-API, NETCONF | |
Juniper | JUNOS | JUNOS XML Protocol, REST API, NETCONF |
Brocade | NetIron | REST API, NETCONF |
Network OS | REST API, NETCONF | |
Arista | EOS | eAPI (REST API) |
Pour avoir un aperçu de NETCONF, @codeout a une explication détaillée, vous pouvez donc vous référer à cet article.
NETCONF je ne sais pas Essayons NETCONF
Le contexte et les tendances de normalisation de l'apparence des modèles NETCONF et YANG sont présentés dans les documents de présentation suivants. JANOG36 NETCONF/YANG
Si vous souhaitez en savoir plus, veuillez lire la RFC. RFC6241 Network Configuration Protocol (NETCONF) RFC6020 YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF)
Maintenant, utilisons NETCONF pour configurer le routeur. Les requêtes et réponses sont échangées entre le client NETCONF et le routeur à l'aide de XML-RPC.
Le flux de mise en œuvre était le suivant.
Installez ncclient sur le serveur sur lequel vous écrivez le programme Python.
sudo pip install ncclient
Entrez les paramètres pour autoriser netconf à l'avance dans le routeur JUNOS.
set system services netconf ssh
Le format XML-RPC du routeur est publié sur la page de support de chaque fournisseur.
NETCONF XML Management Protocol Developer Guide
Dans JUNOS, il est très pratique d'ajouter «| display xml rpc» à la fin de la commande dans l'interface de ligne de commande du routeur car le contenu de la configuration et la commande show peuvent être sortis au format de requête RPC.
La partie entourée de \
user1@router> show interfaces xe-0/0/0 terse | display xml rpc
<rpc-reply xmlns:junos="http://xml.juniper.net/junos/xxxxx/junos">
<rpc>
<get-interface-information>
<terse/>
<interface-name>xe-0/0/0</interface-name>
</get-interface-information>
</rpc>
<cli>
<banner></banner>
</cli>
</rpc-reply>
En utilisant la fonction de commande dans ncclient, vous pouvez obtenir les informations en utilisant la commande show dans l'interface de ligne de commande du routeur telle quelle sans utiliser le schéma XML-RPC. Ici, exécutez la commande pour obtenir l'état de l'interface xe-0 / 0/0.
show_junos.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-
from ncclient import manager
username = 'user1'
password = 'pass1'
ipv4 = '192.168.0.1'
port = 22
connection = manager.connect(host = ipv4, port = port, username = username, password = password, timeout = 20, device_params={'name':'junos'}, hostkey_verify=False )
print '1. run show command'
print '='*40
print connection.command('show interfaces xe-0/0/0 terse')
Voici le résultat de l'exécution. Bien que l'espace soit légèrement vide, vous pouvez voir que vous pouvez vérifier l'état de l'interface avec \
Résultat d'exécution
$ python show_junos.py
1. run show command
========================================
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<interface-information style="terse">
<physical-interface>
<name>
xe-0/0/0
</name>
<admin-status>
down
</admin-status>
<oper-status>
down
</oper-status>
</physical-interface>
</interface-information>
</rpc-reply>
Si l'interface spécifiée n'existe pas, une erreur sera générée avec la balise \
Résultat d'exécution
[t-tsuchiya@dev set_router_netconf]$ python show_junos.py
1. run show command
========================================
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<interface-information style="terse">
<rpc-error>
<error-type>protocol</error-type>
<error-tag>operation-failed</error-tag>
<error-severity>error</error-severity>
<source-daemon>
ifinfo
</source-daemon>
<error-message>
device xe-0/0/0 not found
</error-message>
</rpc-error>
</interface-information>
</rpc-reply>
À propos, avec ncclient, il est également possible de générer en option un format de texte facile à voir pour les humains (= similaire au résultat CLI sur le routeur).
show_junos.py
(réduction)
# print connection.command('show interfaces terse xe-0/0/0')
print connection.command('show interfaces terse xe-0/0/0', format='text')
Résultat d'exécution
$ python show_junos.py
1. run show command
========================================
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<output>
Interface Admin Link Proto Local Remote
xe-0/0/0 down down
</output>
</rpc-reply>
Il est préférable d'utiliser le format XML-RPC pour le traitement par un logiciel d'automatisation et le format texte pour la confirmation visuelle par l'opérateur.
Dans ncclient, vous pouvez obtenir les informations de configuration définies dans le routeur actuel avec la fonction get_config et la fonction get. Dans l'exemple ci-dessous, toutes les informations de configuration définies dans le routeur sont acquises.
show_junos.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-
from ncclient import manager
username = 'user1'
password = 'pass1'
ipv4 = '192.168.0.1'
port = 22
connection = manager.connect(host = ipv4, port = port, username = username, password = password, timeout = 20, device_params={'name':'junos'}, hostkey_verify=False )
print connection.get_config(source='running')
Résultat d'exécution
$ python show_junos.py
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<data>
<configuration changed-seconds="1450593048" changed-localtime="2015-12-20 15:30:48 JST">
<version>xx.xx.xx</version>
<system>
<host-name>router</host-name>
<time-zone>Asia/Tokyo</time-zone>
(Omis ci-dessous)
Si vous voulez obtenir la configuration partiellement, par exemple lorsque vous voulez voir les paramètres de l'interface, vous pouvez l'obtenir en ajoutant l'option de filtre à la fonction get_config. Cependant, étant donné que les informations sur la structure XML-RPC sont requises à ce stade, il est nécessaire de vérifier le schéma XML-RPC à l'avance avec la commande 'how configration interaface xe-0 / 0/0 | display xml' etc. sur le routeur. ..
show_junos.py
(réduction)
request_config_interface = """
<configuration>
<interfaces>
<interface>
<name>xe-0/0/0</name>
</interface>
</interfaces>
</configuration>"""
# print connection.get_config(source='running')
print connection.get_config(source='running', filter=('subtree', request_config_interface) )
Résultat d'exécution
$ python show_junos.py
1. run show command
========================================
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<data>
<configuration commit-seconds="1450593048" commit-localtime="2015-12-20 15:30:48 JST" commit-user="user1">
<interfaces>
<interface>
<name>xe-0/0/0</name>
<disable/>
</interface>
</interfaces>
</configuration>
</data>
</rpc-reply>
À ce stade, xe-0 / 0/0 contient uniquement le paramètre "désactiver".
Résultat de la confirmation CLI sur le routeur
user1@router> show configuration interfaces xe-0/0/0
disable;
Créons maintenant un programme qui entre les paramètres de l'interface. Le but ici est de définir l'adresse IP indiquée ci-dessous et d'activer l'interface.
Paramètres que vous souhaitez exécuter
delete interfaces xe-0/0/0 disable
set interfaces xe-0/0/0 unit 0 family inet address 10.0.0.1/30
Pour définir la configuration du routeur, utilisez la fonction edit_config de ncclient. Après avoir déposé les paramètres ci-dessus dans le schéma XML-RPC, spécifiez-le dans l'argument de edit_config et exécutez-le.
Décrivez le programme créé. Le déroulement du programme est le suivant. --Étape 1. Vérifiez au préalable la configuration du routeur de la partie interface --Étape 2. Définissez la configuration du routeur en utilisant la fonction edit_config comme configuration candidate. --Étape 3. Vérifiez la normalité de la configuration candidate --Etape 4. Affichage de la configuration candidate --Étape 5. Mise en œuvre de Commit (les gens prennent des décisions ici)
set_junos.py
#! /usr/bin/env python
# -*- coding: utf-8 -*-
from ncclient import manager
username = 'user1'
password = 'pass1'
ipv4 = '192.168.0.1'
port = 22
connection = manager.connect(host = ipv4, port = port, username = username, password = password, timeout = 20, device_params={'name':'junos'}, hostkey_verify=False )
print '='*40
print 'Step 1. show running-config before commit'
print '='*40
request_config_interface = """
<configuration>
<interfaces>
<interface>
<name>xe-0/0/0</name>
</interface>
</interfaces>
</configuration>"""
print connection.get_config(source='running', filter=('subtree', request_config_interface) )
print '='*40
print 'Step 2. set config on candidate-config'
print '='*40
request_set_config_interface = """
<config>
<configuration>
<interfaces>
<interface>
<name>xe-0/0/0</name>
<enable/>
<unit>
<name>0</name>
<family>
<inet>
<address>
<name>10.0.0.1/30</name>
</address>
</inet>
</family>
</unit>
</interface>
</interfaces>
</configuration>
</config>
"""
print connection.edit_config(target='candidate', config=request_set_config_interface)
print '='*40
print 'Step 3. validate candidate-config'
print '='*40
print connection.validate(source='candidate')
print '='*40
print 'Step 4. show config on candicate-config'
print '='*40
print connection.get_config(source='candidate', filter=('subtree', request_config_interface) )
print '='*40
print 'Step 5. commit'
print '='*40
print 'Do you commit? y/n'
choice = raw_input().lower()
if choice == 'y':
connection.commit()
else:
connection.discard_changes()
print '='*40
print 'Step 6. show running-config after commit'
print '='*40
print connection.get_config(source='running', filter=('subtree', request_config_interface) )
if connection:
connection.close_session()
Une fois exécuté, il fonctionne comme suit.
Résultat d'exécution
$ python set_junos.py
========================================
Step 1. show running-config before commit
========================================
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<data>
<configuration commit-seconds="1450688416" commit-localtime="2015-12-21 18:00:16 JST" commit-user="user1">
<interfaces>
<interface>
<name>xe-0/0/0</name>
<disable/>
</interface>
</interfaces>
</configuration>
</data>
</rpc-reply>
========================================
Step 2. set config on candidate-config
========================================
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<ok/>
</rpc-reply>
========================================
Step 3. validate candidate-config
========================================
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<commit-results>
</commit-results>
<ok/>
</rpc-reply>
========================================
Step 4. show config on candicate-config
========================================
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<data>
<configuration changed-seconds="1450688512" changed-localtime="2015-12-21 18:01:52 JST">
<interfaces>
<interface>
<name>xe-0/0/0</name>
<undocumented>
<enable/>
</undocumented>
<unit>
<name>0</name>
<family>
<inet>
<address>
<name>10.0.0.1/30</name>
</address>
</inet>
</family>
</unit>
</interface>
</interfaces>
</configuration>
</data>
</rpc-reply>
========================================
Step 5. commit
========================================
Do you commit? y/n
y
========================================
Step 6. show running-config after commit
========================================
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<data>
<configuration commit-seconds="1450688535" commit-localtime="2015-12-21 18:02:15 JST" commit-user="user1">
<interfaces>
<interface>
<name>xe-0/0/0</name>
<undocumented>
<enable/>
</undocumented>
<unit>
<name>0</name>
<family>
<inet>
<address>
<name>10.0.0.1/30</name>
</address>
</inet>
</family>
</unit>
</interface>
</interfaces>
</configuration>
</data>
</rpc-reply>
J'ai pu confirmer que les paramètres étaient corrects même avec la CLI du routeur.
Résultat de la confirmation CLI sur le routeur
user1@router> show configuration interfaces xe-0/0/0
enable;
unit 0 {
family inet {
address 10.0.0.1/30;
}
}
Les paramètres de configuration sont effectués avec la fonction edit_config, et en cas de succès, le résultat est affiché comme suit.
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<ok/>
</rpc-reply>
Si le paramètre de configuration échoue, les détails de l'erreur seront affichés. (L'exemple ci-dessous est lorsque vous essayez de réécrire un paramètre qui ne peut pas être modifié)
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<rpc-error>
<error-type>protocol</error-type>
<error-tag>operation-failed</error-tag>
<error-severity>error</error-severity>
<error-message>
could not set enab_disab
</error-message>
</rpc-error>
<ok/>
</rpc-reply>
Ceci est très utile lors du développement de programmes automatisés. Si le logiciel utilise l'interface de ligne de commande du routeur, il est gênant de dire "Détecter le message d'erreur-> Exécuter la commande show pour trouver la partie problématique, analyse syntaxique-> Exécuter une autre commande show, analyse syntaxique-> ..." Il était nécessaire d'implémenter le logiciel, mais il est possible de passer au processus suivant en extrayant le message d'erreur en ne spécifiant que la partie nécessaire avec la balise XML, et le nombre de programmes peut être considérablement réduit. pouvez.
J'ai eu du mal à implémenter la commande de suppression lors de la création du programme ci-dessus.
delete interfaces xe-0/0/0 disable;
À l'origine, dans le schéma XML-RPC, désactiver; est stocké comme suit.
<interfaces>
<interface>
<name>xe-0/0/0</name>
<disable/>
</interface>
</interfaces>
Il était difficile de supprimer ce \ <désactiver />. Cela peut être un problème avec l'implémentation de ncclient, mais "\
set_junos.py
(réduction)
request_delete_config_interface = """
<delete-config>
<interfaces>
<interface>
<name>xe-0/0/0</name>
<disable/>
</interface>
</interfaces>
</delete-config>
"""
print connection.edit_config(target='candidate', config=request_delete_config_interface)
Résultat d'exécution
Traceback (most recent call last):
File "set_junos.py", line 62, in <module>
print connection.edit_config(target='candidate', config=request_delete_config_interface)
File "/usr/lib/python2.7/site-packages/ncclient/manager.py", line 157, in wrapper
return self.execute(op_cls, *args, **kwds)
File "/usr/lib/python2.7/site-packages/ncclient/manager.py", line 227, in execute
raise_mode=self._raise_mode).request(*args, **kwds)
File "/usr/lib/python2.7/site-packages/ncclient/operations/edit.py", line 62, in request
node.append(validated_element(config, ("config", qualify("config"))))
File "/usr/lib/python2.7/site-packages/ncclient/xml_.py", line 117, in validated_element
raise XMLError("Element [%s] does not meet requirement" % ele.tag)
ncclient.xml_.XMLError: Element [delete-config] does not meet requirement
Ensuite, reportez-vous à ncclient sample code et reportez-vous à "delete =" the XML tag delete ". J'ai aussi essayé la description "", mais bien que cela fonctionne, il semble préférable de l'éviter car elle renvoie une réponse d'erreur RPC.
set_junos.py
(réduction)
request_delete_config_interface = """
<config>
<configuration>
<interfaces>
<interface delete="delete">
<name>xe-1/3/0</name>
<disable/>
</interface>
</interfaces>
</configuration>
</config>
"""
print connection.edit_config(target='candidate', config=request_delete_config_interface)
Résultat d'exécution
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<rpc-error>
<error-type>protocol</error-type>
<error-tag>operation-failed</error-tag>
<error-severity>error</error-severity>
<error-message>
could not set enab_disab
</error-message>
</rpc-error>
<ok/>
</rpc-reply>
Enfin, cette fois, j'ai évité (?) En définissant "set enable;" au lieu de "delete disable;". Je ne pense pas que ce soit une solution fondamentale, mais pour le moment, je peux maintenant la définir sans aucune erreur.
#### **`set_junos.py`**
```py
(réduction)
request_delete_config_interface = """
<config>
<configuration>
<interfaces>
<interface>
<name>xe-1/3/0</name>
<enable/>
</interface>
</interfaces>
</configuration>
</config>
"""
print connection.edit_config(target='candidate', config=request_delete_config_interface)
Résultat d'exécution
<rpc-reply message-id="urn:uuid:xxxxxx-xxxxxx">
<ok/>
</rpc-reply>
XML avec "activer" défini-En regardant le RPC, "<undocumented>..<undocumented>Les détails sont inconnus car il est entouré par la balise, mais XML-Il peut s'agir d'un paramètre qui n'est pas défini dans RPC.
<interfaces>
<interface>
<name>xe-1/3/0</name>
<undocumented>
<enable/>
</undocumented>
</interface>
</interfaces>
Je ne l'ai pas vérifié moi-même, mais pour le moment, il semble qu'il y ait des choses qui ne peuvent pas être définies même avec NETCONF, il semble donc préférable de vérifier à l'avance si la fonction réseau que vous souhaitez automatiser peut être définie avec NETCONF. ..
##XML par fabricant-Le schéma RPC est différent et difficile à trouver À l'origine, j'avais l'intention d'écrire un programme pour JUNOS et IOSXR dans ce blog, mais j'ai abandonné car il a fallu trop de temps pour examiner chaque schéma XML.
JANOG36 NETCONF/Matériel de présentation YANGAutant que je puisse voir, IOS XR semble écrire comme suit.
IOSXR_XML-RPC
<Configuration>
<InterfaceConfigurationTable>
<InterfaceConfiguration>
<Naming>
<Active>act</Active>
<InterfaceName Match="Loopback0"/>
</Naming>
<InterfaceVirtual>true</InterfaceVirtual>
<IPV4Network>
<Addresses>
<Primary>
<Address>172.16.255.1</Address>
<Netmask>255.255.255.255</Netmask>
</Primary>
</Addresses>
</IPV4Network>
</InterfaceConfiguration>
</InterfaceConfigurationTable>
</Configuration>
Même si vous le comparez avec le schéma XML JUNOS utilisé cette fois, vous pouvez voir qu'il est assez différent.
JUNOS_XML-RPC
<configuration>
<interfaces>
<interface>
<name>xe-0/0/0</name>
<undocumented>
<enable/>
</undocumented>
<unit>
<name>0</name>
<family>
<inet>
<address>
<name>10.0.0.1/30</name>
</address>
</inet>
</family>
</unit>
</interface>
</interfaces>
</configuration>
Dans le cas de JUNOS, c'est assez simple car il y a une option "display xml rpc", mais c'est un peu pénible car il est implémenté en regardant le document pour examiner la structure XML des autres fabricants.
Je suis heureux que le protocole standard de l'industrie appelé NETCONF soit sorti, mais à l'heure actuelle, il est encore nécessaire d'écrire un programme dédié pour chaque OS de routeur. Le modèle de données YANG est actuellement activement discuté et mis en œuvre à l'IETF, et la structure de données des routeurs pourrait être unifiée dans un proche avenir.
#Résumé de l'utilisation de NETCONF J'ai résumé les avantages et les inconvénients de l'utilisation de NETCONF pour le moment. ##avantage -Les résultats de la vérification de l'état et les résultats des erreurs sont au format XML-Comme il est structuré par RPC, la quantité de code pour l'analyse syntaxique et la gestion des exceptions peut être considérablement réduite lors de la création de logiciels automatisés.
##Désavantage -La structure XML diffère selon le fabricant du périphérique réseau, et un programme dédié sera nécessaire pour chaque système d'exploitation de routeur pendant un certain temps. -XML pour chaque fabricant-Il est difficile de vérifier RPC -Y a-t-il des fonctions qui ne peuvent pas être définies dans NETCONF pour le moment? -Je ne l'ai pas trouvé moi-même, mais j'ai entendu dire que LACP et d'autres paramètres ne pouvaient pas être définis.
#finalement C'était une très bonne étude pour moi d'écrire cet article. Lors de l'écriture d'un programme NETCONF, plus que l'écriture d'un programme, XML publié par le fabricant-Vous devez lire de nombreuses informations sur les schémas RPC et la documentation des bibliothèques que vous utilisez. Pour le moment, il existe peu de codes échantillons, donc cela peut être difficile.
Utilisateur(=Opérateur de réseau, développeur de logiciels)XML pour chaque fabricant-RPCスキーマを意識させないUtilisateurフレンドリなラッパーAPIが登場すると実装の敷居がかなり下がるので、ネットワーク運用自動化が一気に進むように思います。 Je l'ai déjà présenté sur mon blogNAPALMSi une bibliothèque d'API permettant aux appareils de plusieurs fournisseurs d'être configurés avec la même fonction, telle que, apparaîtra à l'avenir, il sera plus facile d'implémenter le logiciel.
Article précédent: J'ai essayé de toucher la bibliothèque d'API de contrôle de routeur NAPALM (En passant, dans NAPALM, seul JUNOS est NETCONF, et le reste est SSH, API du fournisseur, et il semble que diverses implémentations soient utilisées.)
C'est tout pour cet article. Nous espérons que vous le trouverez utile lorsque vous démarrez NETCONF. Merci pour votre relation.