Scrapli Automation - trunk ports

Läs gärna mitt tidigare inlägg om Scrapli: Scrapli#1

TLDR: Vill du inte läsa igenom hela posten kan du bläddra längst ner på sidan för att läsa scriptet.

Jag fortsätter mitt arbete genom att använda scrapli för att utföra upprepande övningar. Den här gången kommer jag lägga till VLAN i en PortChannel.

Följande topologi arbetar vi med. Den kommer skapa konfiguration på NX01 och NX02. image

Låt oss börja!

Vi börjar med att importera nödvändiga moduler. Den ena gör att vi kan använda Scrapli och den andra möjliggör integration med operativsystemet.

from scrapli.driver.core import IOSXEDriver
import os

Vi skapar en lista över den utrustning som scriptet ska arbeta mot:

"""Lista med enheter som ska konfigureras"""
devices = [
    "NX01",
    "NX02"
]

Definierar ett användarnamn och lösenord som importeras från en variable i operativsystemet.

"""Användarnamn och lösenord för inlogg mot utrustningen som ska konfigureras. 
Datat har sparas som ett systemvariable i OS"""
username = os.environ.get("USER")
password = os.environ.get("PASS")
credentials = {
    "auth_username": username,
    "auth_password": password,
    "auth_strict_key": False,
}
"""En liten output som påminner användaren vad skriptet 
genomför och på vilka enheter."""
print("This will add vlans to Nexuses:")
print("Nexus01\nNexus02")
print("PortChannel 200\nPortChannel 202\nTowards Fw Inside Interfaces\n\n---------------")

Skriptet är uppdelat i flera funktioner för att göra det enkelt att överblicka.

Jag har börjat med att skapa en main if-statement. Den frågar användaren vad för vlan som ska läggas till i Trunken. Matas ett felaktigt VLAN in kommer den att säga ifrån och sedan lägga till ett namn till vlanet.

if __name__ == "__main__":

    #while för att förebygga felaktiga tecken i vlanet
    newVlanBolean = False
    while newVlanBolean == False:
        newVlan = input("vlan: ")
        newVlanBolean = newVlan.isnumeric()
        if newVlanBolean == False:
            print("nope, enter a valid vlan id or exit out")

    #Skapar ett namn till det inmatade vlanet.
    newVlanName = input("vlan name: ")

    for device in devices:
        connection = credentials
        connection["host"] = device
        print(f"Connecting to switch {device}")

        """Kallar på funktionen checkVlansInDb och matar in två värden. 
        Anslutningsuppgifterna till enheten och vlanet. Ifall vlanet 
        finns kommer funktionen svara tillbaka med True. Finns 
        vlanet inte med i vlandatabasen svarar funktionen tillbaka med False."""

        if checkVlansInDb(connection, newVlan) == True:
            print(f"  vlan: {newVlan} exists in vlandb")

        """Ifall svaret är False kommer vi kalla på funktionen createVlan. 
        Vi skickar in anslutningsuppfigifterna, vlanet och vlan-namnet."""
        else:
            print("  Not in db")
            createVlan(connection, newVlan, newVlanName)
            print("  vlan created")

        """Nu har vi säkerställt att vlanet finns i switchen, 
        antingen från början eller så är det skapat. Nu behöver 
        # vi ta reda på om vlanet redan finns i trunken. Det gör 
        vi genom att kalla på funktionen checkVlansInPo200. Ifall 
        vlanet redan finns på interfacet kommer funktionen svara 
        tillbaka med True, finns vlanet inte kommer den svara 
        tillbaka med False"""

        if checkVlansInPo200(connection, newVlan) == True:
            print(f"  vlan: {newVlan} exists in Po200")

        """Finns inte vlanet på interfacet kallar vi på funktionen 
        addVlansPo200, matar in anslutningsuppgifterna, samt vlanid."""

        else:
            print(f"  vlan: {newVlan} does not exist in Po200")
            addVlansPo200(connection, newVlan)

        """Den kommer även kolla på ett till interface, det interface 
        som går till FW2 så båda trunkportarna är identiska. Därför 
        görs samma check som mot Po200 även här"""

        if checkVlansInPo202(connection, newVlan) == True:
            print(f"  vlan: {newVlan} exists in Po202")
        else:
            print(f"  vlan: {newVlan} does not exist in Po202")
            addVlansPo202(connection, newVlan)

checkVlansInDb

Funktionen veriferar att vi inte skapar om ett vlan som redan finns i vlan databasen. Vi nyttjar sedan genie för att få vår svar mer lämplig för att parsas. Genie dokumentation

def checkVlansInDb(connection: dict, vlanId: int, newVlanName: str):

    with NXOSDriver(**connection) as conn:
        output = conn.send_command(f'show vlan')
        """Genie hjälper oss att formatera datat som retuneras i ett 
        format som är enklare för en maskin att förstå, i det här 
        fallet får vi tillbaka en lista"""
        vlans = output.genie_parse_output()

        """if / else kolla om vlanet finns i databasen och retunerar 
        en boolean. True eller False"""

        if str(vlanId) in vlans['vlans']:
            return True
        else:
            return False

createVlan

Blir utfallet att vlanet inte finns i vlan-databasen så behöver vi skapa vlanet. Det gör vi med hjälp av funktionen createVlan.

def createVlan(connection: dict, vlanId: int):
    with NXOSDriver(**connection) as conn:
        """Den nya konfigurationen skickas till switchen med hjälpa 
        av send_configs som är en del av scrapli. En print för att 
        se att vlanet har skapats"""
        
        conn.send_configs([f"vlan {vlanId}", f"name {newVlanName}"])
        print(f"vlan {vlanId} created")

checkVlansInPo200

När vlanet väl är säkerställt att det finns i vlandatabasen är det dags att först säkerställa så det inte redan existerar i trunken. Det gör vi med funktionen checkVlansInPo200. Här kollar vi konfigurationen i running-config på det specifika interfacet. Med hjälp av genie omvanldar vi datat som kommer tillbaka till en läsbar lista som datorn enklare kan parsa.

def checkVlansInPo200(connection: dict, vlanId: int):
    with NXOSDriver(**connection) as conn:

        outputShowRunInterfacePo200 = conn.send_command(f'show running-config interface port-channel200')
        outputShowRunInterfacePo200Parsed = outputShowRunInterfacePo200.genie_parse_output()

        vlanListPo200 = outputShowRunInterfacePo200Parsed["interface"]["port-channel200"]["trunk_vlans"].split(",")

        """Följande for loop säkerställen att vi även får med dom 
        vlan som ofta presenteras som en range i en port konfiguration, 
        till exempel 100-200. For loopen identiferar ett sådant exempel 
        genom att kolla om interfacets vlan innehåller "-". Vid ett 
        sådant tillfälle kommer for loopen att ta bort det värdet och 
        ersätta det med samtliga vlan som existerar i rangen och addera 
        dom till vlanListPo200."""

        for vlan in vlanListPo200:
            if "-" in vlan:
                vlanSplit = vlan.split("-")
                vlanListPo200.remove(vlan)
                vlanStart = int(vlanSplit[0])
                vlanStop = int(vlanSplit[1])
                for i in range(vlanStart, vlanStop+1):
                    vlanListPo200.append(str(i))
        """Här undersöker vi om listan vlanListPo200 inneåller vlanet 
        vi vill konfigurera. Om det är sant retuneras True, är det 
        falskt retuneras False."""

        if vlanId in vlanListPo200:
            return True
        else:
            return False

checkVlansInPo202

checkVlansInPo202 gör samma sak som checkVlansInPo200 ovan, men för det andra trunk interfacet.

def checkVlansInPo202(connection: dict, vlanId: int):
    with NXOSDriver(**connection) as conn:

        outputShowRunInterfacePo202 = conn.send_command(f'show running-config interface port-channel202')
        outputShowRunInterfacePo202Parsed = outputShowRunInterfacePo202.genie_parse_output()

        vlanListPo202 = outputShowRunInterfacePo202Parsed["interface"]["port-channel202"]["trunk_vlans"].split(",")

        for vlan in vlanListPo202:
            if "-" in vlan:
                vlanSplit = vlan.split("-")
                vlanListPo202.remove(vlan)
                vlanStart = int(vlanSplit[0])
                vlanStop = int(vlanSplit[1])
                for i in range(vlanStart, vlanStop+1):
                    vlanListPo202.append(str(i))

        if vlanId in vlanListPo202:
            return True
        else:
            return False

addVlansPo200

addVlansPo200 adderar vlanet till trunkporten med kommandot switchport trunk allowed vlan add vlanId

def addVlansPo200(connection: dict, vlanId: int):
    with NXOSDriver(**connection) as conn:
        print(f"  adding vlan: {vlanId}, on port po200")
        conn.send_configs([f"interface po200", f"switchport trunk allowed vlan add {vlanId}"])

addVlansPo202

addVlansPo202 gör samma sak som addVlansPo200 fast på den andra trunk porten.

def addVlansPo202(connection: dict, vlanId: int):
    with NXOSDriver(**connection) as conn:
        print(f"  adding vlan: {vlanId}, on port po202")
        conn.send_configs([f"interface po202", f"switchport trunk allowed vlan add {vlanId}"])

Hela scriptet i sin helhet.

from scrapli import Scrapli
from scrapli.driver.core import NXOSDriver
import os

#Lista med enheter som ska konfigureras
devices = [
    "NX01",
    "NX02"
]

username = os.environ.get("USER")
password = os.environ.get("PASS")

credentials = {
   "auth_username": username,
   "auth_password": password,
   "auth_strict_key": False
}

print("This will add vlans Nexuses:")
print("Nexus01\nNexus02")
print("PortChannel 200\nPortChannel 202\nTowards FW Inside Interfaces\n\n---------------")


def checkVlansInDb(connection: dict, vlanId: int, newVlanName: str):

    with NXOSDriver(**connection) as conn:
        output = conn.send_command(f'show vlan')
        vlans = output.genie_parse_output()

        #if / else kolla om vlanet finns i databaset.
        if str(vlanId) in vlans['vlans']:
            return True
        else:
            return False

def createVlan(connection: dict, vlanId: int):
    with NXOSDriver(**connection) as conn:
        conn.send_configs([f"vlan {vlanId}", f"name {newVlanName}"])
        print(f"vlan {vlanId} created")

def checkVlansInPo200(connection: dict, vlanId: int):
    with NXOSDriver(**connection) as conn:

        outputShowRunInterfacePo200 = conn.send_command(f'show running-config interface port-channel200')
        outputShowRunInterfacePo200Parsed = outputShowRunInterfacePo200.genie_parse_output()

        vlanListPo200 = outputShowRunInterfacePo200Parsed["interface"]["port-channel200"]["trunk_vlans"].split(",")

        for vlan in vlanListPo200:
            if "-" in vlan:
                vlanSplit = vlan.split("-")
                vlanListPo200.remove(vlan)
                vlanStart = int(vlanSplit[0])
                vlanStop = int(vlanSplit[1])
                for i in range(vlanStart, vlanStop+1):
                    vlanListPo200.append(str(i))

        if vlanId in vlanListPo200:
            return True
        else:
            return False

def checkVlansInPo202(connection: dict, vlanId: int):
    with NXOSDriver(**connection) as conn:

        outputShowRunInterfacePo202 = conn.send_command(f'show running-config interface port-channel202')
        outputShowRunInterfacePo202Parsed = outputShowRunInterfacePo202.genie_parse_output()

        vlanListPo202 = outputShowRunInterfacePo202Parsed["interface"]["port-channel202"]["trunk_vlans"].split(",")

        for vlan in vlanListPo202:
            if "-" in vlan:
                vlanSplit = vlan.split("-")
                vlanListPo202.remove(vlan)
                vlanStart = int(vlanSplit[0])
                vlanStop = int(vlanSplit[1])
                for i in range(vlanStart, vlanStop+1):
                    vlanListPo202.append(str(i))

        if vlanId in vlanListPo202:
            return True
        else:
            return False

def addVlansPo200(connection: dict, vlanId: int):
    with NXOSDriver(**connection) as conn:
        print(f"  adding vlan: {vlanId}, on port po200")
        conn.send_configs([f"interface po200", f"switchport trunk allowed vlan add {vlanId}"])

def addVlansPo202(connection: dict, vlanId: int):
    with NXOSDriver(**connection) as conn:
        print(f"  adding vlan: {vlanId}, on port po202")
        conn.send_configs([f"interface po202", f"switchport trunk allowed vlan add {vlanId}"])
                

if __name__ == "__main__":

#while för att förebygga felaktiga tecken i vlanet
    newVlanBolean = False
    while newVlanBolean == False:
        newVlan = input("vlan: ")
        newVlanBolean = newVlan.isnumeric()
        if newVlanBolean == False:
            print("nope, enter a valid vlan id or exit out")

    newVlanName = input("vlan name: ")

    for device in devices:
        connection = credentials
        connection["host"] = device
        print(f"Connecting to switch {device}")

        if checkVlansInDb(connection, newVlan, newVlanName) == True:
            print(f"  vlan: {newVlan} exists in vlandb")
        else:
            print("  Not in db")
            createVlan(connection, newVlan, newVlanName)
            print("  vlan created")

        if checkVlansInPo200(connection, newVlan) == True:
            print(f"  vlan: {newVlan} exists in Po200")
        else:
            print(f"  vlan: {newVlan} does not exist in Po200")
            addVlansPo200(connection, newVlan)

        if checkVlansInPo202(connection, newVlan) == True:
            print(f"  vlan: {newVlan} exists in Po202")
        else:
            print(f"  vlan: {newVlan} does not exist in Po202")
            addVlansPo202(connection, newVlan)

Consultant


2025

Acebit expanderar

1 minute read

Acebit expanderar – automationsess från Dalarna öppnar i Stockholm

Back to Top ↑

2024

Acebit i Falu-Kuriren

less than 1 minute read

Även Falu-Kuriren har intresserat sig för Acebit och besökt oss i Falun!

Back to Top ↑

2023

Meraki API & Mgmt interface

4 minute read

Nyligen stötte jag på ett scenario som innebar att ca 200 Meraki-enheter behövde byta inställning från DHCP till statisk IP-adressering. Istället för att gör...

Arbeta med VPC Del3 - Konfiguration

3 minute read

I mina tidigare inlägg om virtuella port-channels kikade vi på vad det är och vilka delar de utgör. Det här avsnittet kommer beröra hur en grundkonfiguration...

Få upp farten med concurrency

3 minute read

Bakgrund Idag tänkt jag skriva några rader om att skriva concurrent kod, eller “samtidighet” om vi prompt ska översätta det till svenska. För att enklare för...

Back to Top ↑

2022

Back to Top ↑

2021

Back to Top ↑