Automate the VLAN creation process using Python & Netmiko. Note: This article is about a script designed to be executable across all Cisco devices that offer SSH support, generally devices introduced from the year 2010 and onward. It is specifically tailored for Cisco devices operating: -IOS, -IOS-XE, -IOS-XR, -NX-OS -and SG300. The script proves particularly valuable in scenarios where switches lack compatibility with Cisco DNA (Example 2960 or SG300), serving as an effective alternative for streamlined and efficient management. My tests are built on IOS and IOS-XE devices. Prerequisites: Before you proceed with learning about using Netmiko for VLAN automation on Cisco devices, it's essential to have a foundational understanding of the following: -Basic Networking Concepts -Cisco Device Configuration -Python Programming Basics -SSH Access to Cisco Devices Automating VLAN Creation on Cisco devices: Streamlining Network Management with Python and Netmiko In the dynamic world of networking, managing VLANs (Virtual Local Area Networks) on many devices can be a time-consuming and error-prone task. Manually configuring VLANs on multiple Cisco devices can quickly become tedious and inefficient. Fortunately, automation can significantly Mitigate this burden, saving you time and effort while enhancing the overall efficiency of your network management operations. Why do we sometimes need to automate the VLAN creation process? The VLAN creation process on a Cisco switch is easy and straightforward, we only need to use some commands to get it done: Switch(config)# vlan 150 (press enter) Switch(config-vlan)# name Main-VLAN (press enter) And then if we are using a trunk port with a limited number of VLANs, we need to add our new created VLAN to that Trunk port. That process is easy and needs no more than a few seconds to be done, but what if we have many switches that need to have the same VLAN in our network?! Then we need a way to automate this process and get it done as fast as possible What is Netmiko anyway? Netmiko https://pynet.twb-tech.com/blog/netmiko-python-library.html is an open-source Python library that allows you to interact with networking devices using SSH. It provides a high-level interface for configuring and managing network devices, making it easier to automate network tasks. Here are some of the key features of Netmiko: • Supports a wide variety of network devices including Cisco, Juniper, Arista, Huawei, and more. • Easy to use: Netmiko is easy to use and has a simple API that is easy to learn. • Efficient: Netmiko is efficient and can be used to automate complex network tasks. • Secure: Netmiko supports SSH authentication and encryption, ensuring that your network devices are secure. Setting Up the Environment: -Install Ubuntu (bare metal or VM) or have it installed on your Windows machine (Windows Subsystem for Linux WSL) To set up Ubuntu on Windows Subsystem for Linux (WSL), follow these steps: 1- Enable WSL: Open PowerShell as Administrator and run the following commands to enable WSL: wsl --install Restart-Computer 2- Open Microsoft Store and search for Ubuntu, get the latest version. When getting Ubuntu is done, Click on the app icon to launch it. 3- Set up your account: The first time you launch Ubuntu, you will be prompted to create a new user account and password. Follow the prompts to complete the setup process. 4- Update packages: Once the initial setup is complete, update the package lists and upgrade installed packages by running the following commands: sudo apt update sudo apt upgrade -On Ubuntu we need to install Python, Netmiko and Paramiko using these commands: sudo sudo pip3 pip3 apt install python3 apt install python3-pip install Netmiko install paramiko What is Paramiko anyway? Paramiko and Netmiko are both Python libraries used for network automation and managing network devices. to use Netmiko, you need to have Paramiko installed. Netmiko is built on top of Paramiko and relies on its SSH implementation for establishing connections and executing commands on network devices. You can choose to import Paramiko to your script or not if you are using Netmiko, because Netmiko already imports Paramiko internally. Sudo apt update: in Ubuntu “sudo apt update” is used to update the package lists for repositories configured on your system. It's important to run "sudo apt update" periodically to keep your package lists up to date. After running this command, you can use "sudo apt upgrade" to upgrade the installed packages to their latest versions, if any updates are available. Sudo apt install python3: This command installs Python 3 and its related packages on your Ubuntu system. Once installed, you'll have access to the Python 3 interpreter and the necessary tools to develop and run Python programs. Sudo apt install python3 pip: python3-pip installs pip for Python 3. Pip is a package manager used to install and manage Python packages from the Python Package Index (PyPI). Running a Netmiko script: To run a netmiko script, you will need two files: one file with a .py extension and another file with a .json extension. The .py file is the main script that contains the logic and the commands to be executed on the network devices. The .json file is a file that contains only a list of the IP addresses of the network devices, as a Python dictionary. The credentials of the network devices, such as username, password, and secret, will be collected by asking the user to enter them interactively. The advantage of using a .json file for the IP addresses is that it allows you to store the device information in a structured and readable format, and it also enables you to use a loop or a file to control multiple devices at once. You can also use different .json files for different groups of devices or different environments, such as production, testing, or development. Creating the .py and .json files in Ubuntu: To create these files we use Nano, which is a lightweight and user-friendly command-line text editor that allows you to create and modify text files directly from the terminal. To create the .py file run this command in Ubuntu: nano create-vlan.py (inside create-vlan.py paste the script text that you take from this article) Press Ctrl+O to save, and then press Ctrl+X to exit Your .py file should look something like this: To create the .json file run this command in Ubuntu: nano devices.json Your .json file should look something like this: Notice!! • Use your switches IP addresses! •You can add as many switches as you like, only be careful with the last one it does not need to have a comma!! With that been said, let’s look at the script: Our script will first prompt for the administrator username, the administrative password and then the Enable password of the device. Then, it will display two new prompts, one about the desired VLAN ID and the other about the desired VLAN-name that we want to create or check. If the VLAN already exists, the script will verify that it is properly configured on the relevant trunk ports, which are configured with the "switchport trunk allowed vlan add" command. If the VLAN does not exist, the script will create it and then configure it on the appropriate trunk ports. Here is the script: from netmiko import ConnectHandler from getpass import getpass import json import os import re import paramiko username = getpass ('Enter Your UserName:') password = getpass('Enter Your Password:') secret = getpass('Enter Your Enable Password:') which_vlan = input("wich VLAN do you want to check?: ") vlan_name = input("vlan name?: ") working_dir = os.getcwd() try: with open(working_dir + '/devices.json', 'r') as json_file: ip_list = json.load(json_file) except: print('No ip list found') for ip in ip_list.values(): device = { 'device_type': 'cisco_ios', 'ip': ip, 'username': username, 'password': password, 'secret': secret } net_connect = ConnectHandler(**device) net_connect.enable() show_run_hostname = net_connect.send_command('show run | include hostname', use_textfsm=True) match = re.search(r'^hostname\s+([^\n]*)', show_run_hostname, re.IGNORECASE) hostname = match.group(1) if match else "N/A" print('\033[32m' + '*'*60 + '\033[0m') print('\033[32m' + '*'*60 + '\033[0m') print(f'Connecting to {ip} {hostname}') #net_connect.enable() show_vlan_command = net_connect.send_command('show vlan b', use_textfsm=True) vlan_already_there = False for i in show_vlan_command: if i['vlan_id'] == which_vlan: print('The VLAN is already configured') vlan_already_there = True if not vlan_already_there: create_new_vlan = [ f'vlan {which_vlan}', f'name {vlan_name}' ] create_vlan = net_connect.send_config_set(create_new_vlan) show_vlan_command = net_connect.send_command(f'show vlan b | include {which_vlan}', use_textfsm=True) print(show_vlan_command) show_interface_command = net_connect.send_command('show interfaces status', use_textfsm=True) result = [] for d in show_interface_command: if d['vlan']=='trunk': result.append(d) y = 0 while y < len(result): a = result[y]['port'] print(f'{a} interface is a trunk') add_new_vlan_to_trunk = [ (f'interface {a}'), (f'switchport trunk allowed vlan add {which_vlan}'), ] show_run_command = net_connect.send_command(f'show run interface {a} | in allowed', use_textfsm=True) trunk_is_allowed = 0 if show_run_command == "": print("trunk is: switch port trunk") trunk_is_allowed = 1 vlan_in_trunk = False if show_run_command != "" and (f'{which_vlan}') in show_run_command: vlan_in_trunk = True print(f'For VLAN {which_vlan}, the configuration is already present on the trunk interface {a}') if not vlan_in_trunk and trunk_is_allowed < 1: add_vlan_to_trunk = net_connect.send_config_set(add_new_vlan_to_trunk) print(f'new vlan {which_vlan} has been allowed on trunk interface {a}') y += 1 ###End of the script Notice: You need to fix the indentation of the script before using it! I could not find a better way to paste it in this Word document. Here are screenshots of the script and how the indentation should look like: Here is the script with comments to explain it: # Import the netmiko library for network automation from netmiko import ConnectHandler # Import the getpass library for getting user input securely from getpass import getpass # Import the json library for parsing JSON files import json # Import the os library for interacting with the operating system import os # Import the re library for regular expressions import re # Import the paramiko library for SSH connections import paramiko # Prompt the user to enter their username, password, and enable password username = getpass ('Enter Your UserName:') password = getpass('Enter Your Password:') secret = getpass('Enter Your Enable Password:') # Prompt the user to enter the VLAN ID and name that they want to check or create which_vlan = input("wich VLAN do you want to check?: ") vlan_name = input("vlan name?: ") # Get the current working directory working_dir = os.getcwd() # Try to open and read the devices.json file that contains the IP addresses of the switches try: with open(working_dir + '/devices.json', 'r') as json_file: ip_list = json.load(json_file) # If the file is not found, print an error message except: print('No ip list found') # Loop through the IP addresses in the ip_list dictionary for ip in ip_list.values(): # Create a device dictionary with the device type, IP address, username, password, and secret device = { 'device_type': 'cisco_ios', 'ip': ip, 'username': username, 'password': password, 'secret': secret } # Establish a connection to the device using netmiko net_connect = ConnectHandler(**device) # Enter the enable mode net_connect.enable() # Send the command 'show run | include hostname' and parse the output using textfsm show_run_hostname = net_connect.send_command('show run | include hostname', use_textfsm=True) # Use a regular expression to extract the hostname from the output match = re.search(r'^hostname\s+([^\n]*)', show_run_hostname, re.IGNORECASE) # Assign the hostname to a variable, or "N/A" if not found hostname = match.group(1) if match else "N/A" # Print 60 asterisks in green color print('\033[32m' + '*'*60 + '\033[0m') # Print another 60 asterisks in green color print('\033[32m' + '*'*60 + '\033[0m') # Print the IP address and the hostname of the device print(f'Connecting to {ip} {hostname}') # Enter the enable mode again #net_connect.enable() # Send the command 'show vlan b' and parse the output using textfsm show_vlan_command = net_connect.send_command('show vlan b', use_textfsm=True) # Initialize a flag variable to indicate if the VLAN is already configured vlan_already_there = False # Loop through the output of the show vlan command for i in show_vlan_command: # If the VLAN ID matches the user input, print a message and set the flag to True if i['vlan_id'] == which_vlan: print('The VLAN is already configured') vlan_already_there = True # If the VLAN is not already configured, create a new VLAN with the user input if not vlan_already_there: # Create a list of commands to create the VLAN and assign a name create_new_vlan = [ f'vlan {which_vlan}', f'name {vlan_name}' ] # Send the commands to the device using netmiko create_vlan = net_connect.send_config_set(create_new_vlan) # Send the command 'show vlan b | include <VLAN ID>' and parse the output using textfsm show_vlan_command = net_connect.send_command(f'show vlan b | include {which_vlan}', use_textfsm=True) # Print the output of the show vlan command print(show_vlan_command) # Send the command 'show interfaces status' and parse the output using textfsm show_interface_command = net_connect.send_command('show interfaces status', use_textfsm=True) # Initialize an empty list to store the trunk interfaces result = [] # Loop through the output of the show interfaces command for d in show_interface_command: # If the VLAN is trunk, append the interface to the result list if d['vlan']=='trunk': result.append(d) # Initialize a counter variable y = 0 # Loop through the result list while y < len(result): # Assign the port name to a variable a = result[y]['port'] # Print the port name and indicate that it is a trunk interface print(f'{a} interface is a trunk') # Create a list of commands to add the new VLAN to the trunk interface add_new_vlan_to_trunk = [ (f'interface {a}'), (f'switchport trunk allowed vlan add {which_vlan}'), ] # Send the command 'show run interface <port> | in allowed' and parse the output using textfsm show_run_command = net_connect.send_command(f'show run interface {a} | in allowed', use_textfsm=True) # Initialize a flag variable to indicate if the trunk is allowed trunk_is_allowed = 0 # If the output is empty, print a message and set the flag to 1 if show_run_command == "": print("trunk is: switch port trunk") trunk_is_allowed = 1 # Initialize a flag variable to indicate if the VLAN is in the trunk vlan_in_trunk = False # If the output is not empty and contains the VLAN ID, print a message and set the flag to True if show_run_command != "" and (f'{which_vlan}') in show_run_command: vlan_in_trunk = True print(f'For VLAN {which_vlan}, the configuration is already present on the trunk interface {a}') # If the VLAN is not in the trunk and the trunk is not allowed, send the commands to add the VLAN to the trunk if not vlan_in_trunk and trunk_is_allowed < 1: add_vlan_to_trunk = net_connect.send_config_set(add_new_vlan_to_trunk) print(f'new vlan {which_vlan} has been allowed on trunk interface {a}') # Increment the counter variable y += 1 Testing and Verification: In this example I will create VLAN 113 and name it Test113: To run the script, we use this command on Ubuntu: Python3 create-vlan.py I got 3 switches to test my script on, only the last switch with IP address 172.22.1.130 has a trunk port configured with a limited number of VLANs on it. Trunk is: switch port trunk means that the trunk interface on that switch is configured using the command switchport mode trunk. New vlan 113 has been allowed on trunk interface Gi0/2 means that the interface Gi0/2 on that switch is a trunk interface configured with the command switchport trunk allowed vlan add command. 113 Test113 active means that the VLAN 113 is created and is active. So, what happens if we run the script again trying to create the same VLAN-ID 113? The VLAN is already configured means that the VLAN 113 is already created on the switch. For VLAN 113, the configuration is already present on the trunk interface Gi0/2 means that the VLAN 113 is already added to that trunk interface. Conclusion: In this article, I have shown how to use Python and Netmiko to automate the VLAN creation process on Cisco devices. By using these Python modules, I can easily connect to multiple switches, check if the VLAN already exists, create a new VLAN if needed, and add it to the trunk interfaces. This saves me time and effort and ensures consistency and accuracy across my network. I hope this article has been helpful for network administrators who want to use automation to make their life easier. Of course, this script is not perfect and can be improved in many ways. I welcome any feedback or suggestions from the readers. I am not a programmer, but a network administrator who loves to learn new things and share them with others. Reference • Python Official Documentation https://docs.python.org/3/ • Netmiko GitHub Repository https://github.com/ktbyers/netmiko • Youtube https://www.youtube.com/watch?v=5dahqpYiH4g https://www.youtube.com/watch?v=R7-2DAsPIbE https://www.youtube.com/watch?v=NSnrvVhbuy8&t=3s • Stack Overflow - Netmiko https://stackoverflow.com/questions/tagged/netmiko • Network Automation using Python 3: An Administrator's Handbook https://www.amazon.com/Network-Automation-using-PythonAdministrators/dp/B084DH69QV/ref=sr_1_1?crid=LA09Z5LXBOHF&keywords=Network+ Automation+using+Python+3%3A+An+Administrator%27s+Handbook&qid=1705937541&s prefix=network+automation+using+python+3+an+administrator%27s+handbook%2Caps%2 C151&sr=8-1