Im vorherigen Artikel habe ich ein Beispiel für die automatische Generierung von Config aus der Portverwaltungstabelle vorgestellt. Ich habe versucht, die L2SW-Konfiguration automatisch aus der Portverwaltungstabelle + Parametertabelle + Jinja2-Vorlage zu generieren
In einigen Fällen möchten Sie möglicherweise eine Portverwaltungstabelle aus der erstellten Konfiguration erstellen. Dieses Mal habe ich versucht, mithilfe einer Python-Parser-Bibliothek namens TTP (Template Text Parser) automatisch eine Portverwaltungstabelle im CSV-Format aus der L2SW-Konfigurationsdatei zu generieren.
Um ein einfaches Beispiel für Schnellstart in das Dokument einzuführen, wird "(1) Config" in "(2) Template" geändert. Das Bild wird mit analysiert und der in {{~}} eingeschlossene Teil ist das durch "(3) Ausgabeergebnis (JSON- oder CSV-Format)" erzeugte Bild.
(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) Vorlage
interface {{ interface }}
ip address {{ ip }}/{{ mask }}
description {{ description }}
ip vrf {{ vrf }}
(3) Ausgabeergebnis (JSON-Format)
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"
}
]
]
Dieses Mal habe ich fast dieselbe Konfigurationsdatei verwendet wie der am Anfang erwähnte Artikel. Weitere Informationen finden Sie im GitHub-Repository unten. Konfigurationsdatei --config_hqaccess1.txt
Der allgemeine Fluss ist wie folgt.
Perth
Verwenden Sie parse_config ()
, um Analyseergebnisse im JSON-Format von Config und Template auszugeben.
Generieren Sie eine Portverwaltungstabelle Konvertieren Sie JSON in das Wörterbuchformat und konvertieren Sie das Parsergebnis der L2-Schnittstelle in die Portverwaltungstabelle im CSV-Format mit "write_dict_to_csv ()".
Wir werden jedes Ergebnis in den folgenden Punkten vorstellen.
portlist_generation.py
# -*- coding: utf-8 -*-
from ttp import ttp
import json
import csv
#Definieren Sie Pfade für verschiedene Dateien
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: #Für Windows Newline=''Ist notwendig
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()
Ich habe tatsächlich eine Datei erstellt (catalog2960_template_ttp.txt), aber zur Erklärung habe ich drei für jedes Einstellungselement erstellt. Es ist geteilt.
Da die schnittstellenbezogene Vorlage in (2) und (3) erstellt wurde, werden hier andere globale Einstellungen definiert.
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>
Die Einstellungselemente sind durch "
In der Gruppe wird der Teil, den Sie analysieren möchten, durch "{{~}}" in einer Form angegeben, die der Jinja2-Vorlage ähnelt. Das Ausgabeergebnis ist wie folgt.
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"
}
}
]
Ich habe die folgende Vorlage mit dem Gruppennamen "vlan_interfaces" erstellt.
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>
Zusätzlich zu (1) verwenden wir vier zusätzliche Funktionen.
Muster für reguläre Ausdrücke ORPHRASE Wenn die Beschreibung ein Wort enthält, geben Sie das Muster für reguläre Ausdrücke "WORD" an. Wenn mehrere Wörter durch Leerzeichen getrennt sind, z. B. "<< To hqdist1 Gi0 / 1 >>", können Sie "PHRASE" angeben, um zum Zeilenende zu gelangen. Wenn Sie beide von Fall zu Fall nehmen können, geben Sie wie in diesem Fall "ORPHRASE" an.
Geben Sie den Standardwert default ()
an
Da der Befehl nach dem Herunterfahren zum Parsen bestimmt ist, ist der Wert von "port_status" bei einem Befehl zum Herunterfahren "down" (blockierter Zustand). Wenn kein Befehl vorhanden ist, wird standardmäßig "up" (offener Zustand) verwendet.
Gruppenfunktion Makro Mit TTP können Sie Makros mithilfe von Python-Code festlegen. Erstellen Sie hier das Makro "check_port_status". Wenn der Wert von "port_status" "down" ist, geben Sie den Wert des neu erstellten Schlüssels "status" in "x" an. Verwenden Sie für "up" "o".
Gruppenfunktionen del Da für die Statusinformationen zum Öffnen / Blockieren nur "Status" erforderlich ist, wird "Portstatus" aus dem Ausgabeergebnis gelöscht.
Das Ausgabeergebnis ist wie folgt.
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"
}
]
}
]
Schließlich habe ich die folgende Vorlage mit dem Gruppennamen "l2_interfaces" erstellt.
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>
Grundsätzlich handelt es sich um eine Anwendung von (1) und (2), aber um das Ergebnis nur an die L2-Schnittstelle in die Portverwaltungstabelle auszugeben, werden die Vorlagen "ip {{ip_setting}}" und "no ip {{no_ip_setting}}" verwendet. Wenn es eine IP-Einstellung (= L3-Schnittstelleneinstellung) gibt, die dieser entspricht, verwenden Sie die Gruppenfunktion ausschließen. , Das Ergebnis der entsprechenden Schnittstelle wird ausgeschlossen.
Das Ausgabeergebnis ist wie folgt.
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"
}
]
}
]
Das Ausgabeergebnis (3) im vorherigen Abschnitt wird in das Wörterbuchformat konvertiert, und die Ergebnisausgabe in CSV lautet wie folgt.
[Letzte Portverwaltungstabelle](https://qiita.com/tech_kitara/items/b6ffd9790483b08b568b#%E3%83%9D%E3%83%BC%E3%83%88%E7%AE%A1%E7%90 Im Vergleich zu% 86% E8% A1% A8) gibt es Unterschiede zwischen den Zellen E10 und E11 aufgrund der Hinzufügung des Befehls "Switchport Trunk erlaubt vlan 100-101" zu Gi0 / 1 und Gi0 / 2, aber ansonsten grundsätzlich Das gleiche wurde erzeugt.
Bisher habe ich TextFSM und Genie Parser als Parser für NW-Geräte verwendet. Wenn Sie jedoch einen eigenen Parser erstellen möchten, der zu Ihrer Umgebung passt, ist TTP meiner Meinung nach auch eine leistungsstarke Option, wenn es um Funktionalität und Anpassbarkeit geht. Die diesmal eingeführten Dateien wurden auf GitHub --portlist_generator hochgeladen. Ich hoffe, Sie finden sie hilfreich.