Dans l'article précédent, j'ai présenté un exemple de génération automatique de Config à partir de la table de gestion des ports. J'ai essayé de générer automatiquement L2SW Config à partir de la table de gestion des ports + table des paramètres + modèle Jinja2
Dans certains cas, vous souhaiterez peut-être créer une table de gestion des ports à partir du fichier Config. Cette fois, j'ai essayé de générer automatiquement une table de gestion des ports au format CSV à partir du fichier de configuration L2SW à l'aide d'une bibliothèque d'analyseur Python appelée TTP (Template Text Parser).
Pour introduire un exemple simple de Démarrage rapide dans le document tel quel, "(1) Config" est remplacé par "(2) Template". Il s'agit d'une image générée par "(3) Résultat de sortie (format JSON ou CSV)" qui est analysée à l'aide de et incluse dans {{~}}.
(1) Config
interface Loopback0
description Router-id-loopback
ip address 192.168.0.113/24
!
interface Vlan778
description CPE_Acces_Vlan
ip address 2002::fd37/124
ip vrf CPE1
!
(2) Modèle
interface {{ interface }}
ip address {{ ip }}/{{ mask }}
description {{ description }}
ip vrf {{ vrf }}
(3) Résultat de sortie (format JSON)
python
[
[
{
"description": "Router-id-loopback",
"interface": "Loopback0",
"ip": "192.168.0.113",
"mask": "24"
},
{
"description": "CPE_Acces_Vlan",
"interface": "Vlan778",
"ip": "2002::fd37",
"mask": "124",
"vrf": "CPE1"
}
]
]
Cette fois, j'ai utilisé presque le même fichier Config que l'article mentionné au début. Consultez le référentiel GitHub ci-dessous pour plus de détails. Fichier de configuration --config_hqaccess1.txt
Le flux général est le suivant.
Perth
Utilisez parse_config ()
pour afficher les résultats d'analyse au format JSON à partir de Config et du modèle.
Générer une table de gestion des ports
Convertissez JSON au format dictionnaire et convertissez le résultat de l'analyse de l'interface L2 en table de gestion des ports au format CSV avec write_dict_to_csv ()
.
Nous présenterons chaque résultat dans les éléments suivants.
portlist_generation.py
# -*- coding: utf-8 -*-
from ttp import ttp
import json
import csv
#Définir les chemins pour divers fichiers
TEMPLATE = './catalyst2960_template_ttp.txt'
PORT_LIST = './port_list_hqaccess1_ttp.csv'
CONFIG_FILENAME = './config_hqaccess1.txt'
CSV_COLUMNS = ['port_no', 'speed', 'duplex', 'mode', 'vlan', 'portfast', 'status', 'description']
def parse_config(template_file, config_filename):
with open(config_filename, 'rt') as fc:
data_to_parse = fc.read()
with open(template_file, 'rt') as ft:
ttp_template = ft.read()
# create parser object and parse data using template:
parser = ttp(data=data_to_parse, template=ttp_template)
parser.parse()
# print result in JSON format
results = parser.result(format='json')[0]
return results
def write_dict_to_csv(port_list, csv_columns, results):
with open(port_list, 'w', newline='') as csvfile: #Pour Windows, nouvelle ligne=''Est nécessaire
writer = csv.DictWriter(csvfile, fieldnames=csv_columns)
writer.writeheader()
for data in results:
writer.writerow(data)
return
def main():
results = parse_config(TEMPLATE, CONFIG_FILENAME)
print(results)
results_dict = json.loads(results)
write_dict_to_csv(PORT_LIST, CSV_COLUMNS, results_dict[0]['l2_interfaces'])
if __name__ == "__main__":
main()
J'ai en fait créé un fichier (catalyseur2960_template_ttp.txt), mais pour des raisons d'explication, j'en ai créé trois pour chaque paramètre. Il est divisé.
Puisque le modèle lié à l'interface a été créé dans (2) et (3), d'autres paramètres globaux sont définis ici.
part1_template
<group name="global_settings">
hostname {{ hostname }}
enable secret {{ secret }}
username {{ username }} privilege 15 password {{ password }}
ip domain-name {{ hostname }}
ip default-gateway {{ default_gw }}
ntp server {{ ntp_server }}
</group>
Les éléments de réglage sont divisés en groupes par «" global_settings ": {~}
.
Je pense que c'est pratique si vous souhaitez faciliter la visualisation et la séparation du traitement pour chaque élément de réglage.
De plus, dans le groupe, la partie que vous voulez analyser est spécifiée par {{~}}
sous une forme similaire au modèle Jinja2.
Le résultat de sortie est le suivant.
part1_output_json
[
{
"global_settings": {
"default_gw": "192.168.100.150",
"hostname": "hqaccess1",
"ntp_server": "192.168.100.44",
"password": "cisco",
"secret": "test",
"username": "test"
}
}
]
J'ai créé le modèle suivant avec le nom de groupe vlan_interfaces
.
part2_template
<macro>
def check_port_status(data):
if "down" in data["port_status"]:
data["status"] = "x"
else:
data["status"] = "o"
return data
</macro>
<group name="vlan_interfaces" macro="check_port_status" del="port_status">
interface Vlan{{ vlan_num }}
description {{ vlan_desc | ORPHRASE }}
ip address {{ ip_address }} {{ subnet }}
shut{{ port_status | default("up") }}
!{{ _end_ }}
</group>
En plus de (1), nous utilisons quatre fonctions supplémentaires.
Modèle d'expression régulière ORPHRASE
Si la description contient un mot, spécifiez le modèle d'expression régulière «WORD». S'il y a plusieurs mots séparés par des espaces, tels que << To hqdist1 Gi0 / 1 >>
, vous pouvez spécifier PHRASE
pour arriver à la fin de la ligne. Si vous pouvez prendre les deux au cas par cas, spécifiez ʻORPHRASE` comme dans ce cas.
Spécification de la valeur par défaut default ()
Puisque la commande suivant shutdown est ciblée pour l'analyse, s'il y a une commande shutdown, la valeur de port_status
sera down
(état bloqué). S'il n'y a pas de commande, la valeur par défaut est ʻup` (état ouvert).
Fonction de groupe macro
TTP vous permet de définir des macros à l'aide du code Python. Ici, créez la macro check_port_status
, et si la valeur de port_status
est down
, spécifiez la valeur de la clé nouvellement créée status
dans x
. Pour ʻup, utilisez ʻo
.
Fonctions de groupe del
Puisque seul status
est requis pour les informations de statut ouvert / bloqué, port_status
est supprimé du résultat de sortie.
Le résultat de sortie est le suivant.
part2_output_json
[
{
"vlan_interfaces": [
{
"status": "x",
"vlan_num": "1"
},
{
"ip_address": "192.168.100.47",
"status": "o",
"subnet": "255.255.255.0",
"vlan_desc": "<< Server Segment >>",
"vlan_num": "100"
}
]
}
]
Enfin, j'ai créé le modèle suivant avec le nom de groupe l2_interfaces
.
part3_template
<macro>
def check_port_status(data):
if "down" in data["port_status"]:
data["status"] = "x"
else:
data["status"] = "o"
return data
def check_stp_option(data):
if "portfast" in data["stp_option"]:
data["portfast"] = "o"
else:
data["portfast"] = "x"
return data
</macro>
<group name="l2_interfaces" exclude="ip_setting, no_ip_setting" macro="check_port_status, check_stp_option" del="port_status, stp_option">
interface {{ port_no }}
description {{ description | ORPHRASE }}
switchport access vlan {{ vlan | default("1") }}
switchport trunk allowed vlan {{ vlan }}
switchport mode {{ mode | default("access") }}
duplex {{ duplex | default("auto") }}
speed {{ speed | default("auto") }}
shut{{ port_status | default("up") }}
spanning-tree {{ stp_option | default("none") }}
ip {{ ip_setting | ORPHRASE }}
no ip {{ no_ip_setting | ORPHRASE }}
!{{ _end_ }}
</group>
Fondamentalement, c'est une application de (1) et (2), mais pour afficher le résultat dans la table de gestion des ports uniquement vers l'interface L2, les modèles ʻip {{ip_setting}} ʻand` no ip {{no_ip_setting}} ʻ sont utilisés. S'il y a un paramètre IP (= paramètre d'interface L3) qui correspond à cela, utilisez la fonction de groupe exclude. , Le résultat de l'interface correspondante est exclu.
Le résultat de sortie est le suivant.
part3_output_json
[
{
"l2_interfaces": [
{
"description": "<< To PC1 >>",
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/1",
"portfast": "o",
"speed": "auto",
"status": "o",
"vlan": "100"
},
{
"description": "<< To PC2 >>",
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/2",
"portfast": "o",
"speed": "auto",
"status": "o",
"vlan": "100"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/3",
"portfast": "o",
"speed": "auto",
"status": "x",
"vlan": "100"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/4",
"portfast": "o",
"speed": "auto",
"status": "x",
"vlan": "100"
},
{
"description": "<< To PC3 >>",
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/5",
"portfast": "o",
"speed": "auto",
"status": "o",
"vlan": "101"
},
{
"description": "<< To PC4 >>",
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/6",
"portfast": "o",
"speed": "auto",
"status": "o",
"vlan": "101"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/7",
"portfast": "o",
"speed": "auto",
"status": "x",
"vlan": "101"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/8",
"portfast": "o",
"speed": "auto",
"status": "x",
"vlan": "101"
},
{
"description": "<< To hqdist1 Gi0/1 >>",
"duplex": "full",
"mode": "trunk",
"port_no": "GigabitEthernet0/1",
"portfast": "x",
"speed": "1000",
"status": "o",
"vlan": "100-101"
},
{
"description": "<< To hqdist2 Gi0/1 >>",
"duplex": "full",
"mode": "trunk",
"port_no": "GigabitEthernet0/2",
"portfast": "x",
"speed": "1000",
"status": "o",
"vlan": "100-101"
}
]
}
]
Le résultat de la conversion du résultat de sortie (3) dans la section précédente au format dictionnaire et de sa sortie au format CSV est le suivant.
[Tableau de gestion du dernier port](https://qiita.com/tech_kitara/items/b6ffd9790483b08b568b#%E3%83%9D%E3%83%BC%E3%83%88%E7%AE%A1%E7%90 Par rapport à% 86% E8% A1% A8), il existe des différences entre les cellules E10 et E11 en raison de l'ajout de la commande "switchport trunk allowed vlan 100-101" à Gi0 / 1 et Gi0 / 2, mais sinon fondamentalement La même chose a été générée.
Jusqu'à présent, j'utilisais TextFSM et Genie Parser comme analyseurs pour les appareils NW, mais si vous souhaitez créer votre propre analyseur qui convient à votre environnement, je pense que TTP est également une option puissante en ce qui concerne la fonctionnalité et la personnalisation. L'ensemble de fichiers introduit cette fois a été téléchargé sur GitHub --portlist_generator, donc j'espère que vous le trouverez utile.