Kennst du schon pyATS - ein Muss für jeden Netzwerkentwickler! pyATS steht für Python Automated Test System und ist ein Python-Framework von Cisco. Ursprünglich für interne Tests entwickelt, wurde es 2017 für alle zugänglich gemacht So können auch Netzwerkentwickler das Framework nutzen und wiederkehrende Aufgaben wie Geräte-Tests automatisieren. Dafür besteht pyATS aus mehreren Komponenten. Zum einen der eigentliche pyATS Kern für die Abbildung der Tests. Ergänzend dazu ist Genie eine Python-Bibliothek, welche passende Parser für die Entwicklung von Testcases bereitstellt (mehr dazu später). Die perfekte Ergänzung findet dann durch Tools wie Expresso zur “Job” Verwaltung statt. Das sind viele neue Begriffe, die wir besser Schritt für Schritt einordnen:
Die Bausteine
pyATS ist das eigentliche Framework, Genie ist die Werkzeugkiste und ist die Komponente welche Bibliotheken für Parser und APIs bereithält. Installieren könnt ihr das Framework ganz einfach mit pip install pyats[full] Zum Starten sollte ein Testbed bzw. eine Testbed-Datei im YAML-Format angelegt werden. Hier stehen die Geräte drin, welche für einen Test benutzt werden sollen. Dabei werden Parameter, wie das IOS und auch Interface angegeben. Das ist vergleichbar mit dem Inventory-File von Ansible. Wichtig ist, dass der Hostname exakt mit dem konfigurierten Hostname auf den Geräten matched.
Testbed anlegen und pyATS installieren
Haben wir unser Testbed angelegt, können wir direkt über die Shell unsere erste Abfrage mit pyATS starten.
Mit folgendem Befehl startest du eine Abfrage auf allen Geräten, welche wir zuvor in dem Testbed definiert haben:
pyats learn interfaces --testbed-file mein-testbed.yaml
Learning '['interface']' on devices '['test-switch']'
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:14<00:00, 14.79s/it]
+==============================================================================+
| Genie Learn Summary for device sw-0019 |
+==============================================================================+
| Connected to test-switch |
| - Log: ./connection_test-switch.txt |
|------------------------------------------------------------------------------|
| Learnt feature 'interface' |
| - Ops structure: ./interface_iosxe_test-switch_ops.txt |
| - Device Console: ./interface_iosxe_test-switch_console.txt |
|==============================================================================|
Der eigentliche Vorteil besteht hierbei in learn interfaces ein Parser, welcher in der Genie-Bibliothek beschrieben ist. Als Ausgabe erhalten wir nämlich zwei Dateien.
- interface_iosxe_test-switch_ops.txt
- interface_iosxe_test-switch_console.txt
In der ersten Datei haben wir alle Informationen über Interface im JSON-Format. Dabei wird die Ausgabe aus mehreren Befehlen zusammen erstellt. Das heißt, die Ausgabe beinhaltet Informationen aus mehreren show-Befehlen wie
show interfaceaber auchshow ip interfaceodershow vrf detail. Wir können also entspannt mit den Daten weiterarbeiten und müssen nicht selbst mit komplexen Regex-Befehlen Ausgaben zurechtschneiden! Für die Suche von Parser bzw Models gibt es den Genie-Feature-Browser. Die Parser sind teilweise auch für andere Hersteller verwendbar, trotzdem bleibt die Ausgabe gleich. Ihr könnt also mit dem gleichen Script Cisco wie auch Juniper Geräte testen!
Jobs
Als Nächstes können wir nun unseren ersten Job schreiben. Ein Job ist nichts anderes als ein Python-Script mit den Tests und API-Abfragen, die wir selber erstellen, um zum Beispiel ein Interface auf CRC-Errors zu überprüfen. Der Job wird nur dann von pyATS ausgeführt und kann entsprechend verarbeitet werden, sodass man am Ende ein optisch ansprechendes Resultat erhält. Dazu aber später mehr. Wer sich bereits mit Unittests auskennt, wird sich mit pyATS vertraut fühlen. Hier werden auch über Dekoratoren (prüfen, bei Unittest fangen die test mit test_ an) die Tests und Subtests definiert und pyATS übernimmt die Erstellung der einzelnen Objekte. Zusätzlich sollten wir jeden Job in drei Sektionen unterteilen:
- Common Setup Definition der Testgeräte
- Testsection Eigentliche Definition unserer Tests
- Common Cleanup Aufräumen, sauberer Disconnect von Devices.
Um nicht zu sehr auszuschweifen, ist hier ein einfaches Beispiel für das Auslesen von CRC-Fehlern, dabei lesen wir im obigen Beispiel zunächst die Interface mit dem Parser von Genie aus.
import json
import logging
from pyats import aetest
from rich.table import Table
from rich.console import Console
from genie.testbed import load
from genie.utils.diff import Diff
from pyats.log.utils import banner
from unicon.core.errors import ConnectionError
log = logging.getLogger(__name__)
# ---- Setup Bereich ---- #
class common_setup(aetest.CommonSetup):
@aetest.subsection
def connect_to_devices(self, testbed):
try:
testbed.connect(log_stdout=False)
except:
log.error("Unable to connect to all devices")
# -- Mit dieser Schleife werden alle Tests auf allen Geräten in der YAML Datei durchgeführt
@aetest.subsection
def loop_mark(self, testbed):
aetest.loop.mark(Test_Cisco_IOS_XE_Interfaces, device_name=testbed.devices)
class Test_Cisco_IOS_XE_Interfaces(aetest.Testcase):
@aetest.test
def setup(self, testbed, device_name):
#Setzt das Gerät für den aktuellen Schleifendurchlauf
self.device = testbed.devices[device_name]
@aetest.test
def get_interface(self):
#Hier lesen wir mit "learn" die Interface des Switches aus
parsed_interfaces = self.device.learn("interface")
self.interfaces=parsed_interfaces.info
@aetest.test
def create_json_file(self):
# Zum veranschaulichen legen wir hier noch ein JSON file ab.
with open(f'{self.device.alias}_Learn_Interface.json', 'w') as f:
f.write(json.dumps(self.interfaces, indent=4, sort_keys=True))
@aetest.test
def test_input_errors(self):
# Hier der eigntliche Test, es wird jedes Interface auf Errors geprüft.
# Zusätzlich geben wir hier immer eine einfache Print-Ausgabe aus.
input_errors_threshold = 0
self.failed_interface = {}
for intf, value in self.interfaces.items():
if 'counters' in value:
counter = value['counters']['in_errors']
if int(counter) > input_errors_threshold:
print(f'Interface {intf}, CRC-Counter: {str(counter)} = Failed')
self.failed_interface = int(counter)
else:
print(f'Interface: {intf}, CRC-Counter: {str(counter)} = Passed')
if self.failed_interface:
self.failed()
else:
self.passed()
# --- CleanUp Bereich --- #
class common_cleanup(aetest.CommonCleanup):
@aetest.subsection
def disconnect_from_devices(self, testbed):
testbed.disconnect()
if __name__ == '__main__':
aetest.main()
Diesen Job kann ich nun gegen ein Gerät laufen lassen. In meinem Fall ein Cisco Switch der 9000er Serie. In der Ausgabe sehe ich, dass der Test bestanden ist!
Tipps für den Einstieg
Bis hierhin seht ihr schon, dass pyATS sehr umfänglich ist und das komplette Framework sehr viel abdecken kann. Von Gerätetests bis tägliche Healthchecks ist alles möglich. Aus eigener Erfahrung kann ich daher nur raten, sich Schritt für Schritt an ein Ziel anzunähern. Fangt mit kleinen Teilaufgaben und Beispielen aus dem Internet an und lernt so die Umgebung kennen.
Mock Devices
Ein weiterer heißer Tipp sind Mock-Devices. Solltet ihr keinen Zugriff auf echte Geräte haben, können diese bis zu einem bestimmten Grad einen Einstieg für die ersten Tests ermöglichen. Mock Devices bilden als Code (in JSON) eine Instanz eines Gerätes ab, man kann diese mit Genie von einem bestehenden Switch erstellen und abspeichern. Oder eben ein von jemand anderem kopieren. Am Ende des Artikels habe ich euch ein Mock-Device angehängt. Ein weiterer Vorteil ist allgemein das offline Arbeiten. Sitzt ihr im Zug und habt nur begrenzt Netzwerkzugang, könnt ihr so weiter experimentieren.
Fazit
Man merkt vlt schon beim Lesen des Artikels, dass ich derzeit von dem Framework sehr begeistert bin. Leider bin ich erst recht spät auf pyATS gestoßen. Zukünftig möchte ich das Framework aber auch in meiner Arbeit deutlich mehr nutzen und sehe es als meine erste Wahl, wenn es um Automatisierung von Switch-Tests geht. Den Aufbau finde ich sehr verständlich, es gibt viele Erweiterungs- und Einbindungsmöglichkeiten wie in das Robotferamework. Ich persönlich arbeite lieber mit Programmiersprachen wie Python als mit Automatisierungs-Frameworks wie Ansible Wenn man sich etwas mit Programmierung befasst hat, kann man so auch jedes Tool auf die eigenen Bedürfnisse anpassen. Auch hier ist pyATS sehr offen und ermöglicht das Entwickeln von eigenen Parsern etc.
Template Mock-Device
In dem folgendem Repository ist ein Link zu einem Workshop von einer Cisco Live. In dem Ordner Replay sind Mockdevices. Letztlich braucht man nur die YAML-Datei (z.b. csr1000v-1.yaml). Auf diese müssen wir dann in unserem Testbed verweisen:
devices:
csr1000v-1:
type: router
os: iosxe
connections:
console: # Den Pfad entsprechen anpassen #
command: mock_device_cli --os iosxe --mock_data_dir mock-devices/ --state execute
protocol: telnet
ip: 172.25.192.90
port: 17001