DEV Community

Cover image for How to Automate Cisco Switch Configs with Python and SSH
Lars
Lars

Posted on

How to Automate Cisco Switch Configs with Python and SSH

Intro

As part of a school project where my team is developing a simple CLI tool to automatically deploy Minecraft servers, we need to have a fully functioning physical network to build on top of.

Rather than configuring Cisco Catalyst switches manually, I proposed a solution that uses Python and an appropriate SSH library to automate the configuration process.

While using REST APIs and controller-based networking solutions like Cisco DNA Center would be the modern approach, we’re working with limited hardware that doesn't support those advanced features. Still, I believe this is a great example of hands-on network automation, especially for anyone interested in both coding and networking.


Table of contents

  1. Application design
  2. Python venv setup
  3. Cisco Modeling Labs setup
  4. Coding
  5. Basic CML setup
  6. Connect and push a very simple configuration

1. Application design

Instead of treating Python purely as a scripting language, I decided to follow an object-oriented programming (OOP) approach. This makes the codebase more modular, decoupled, and maintainable.

Network Automation Class Diagram

As shown in the UML class diagram, I created three main classes: Switch, SSHConnection, and ConfigPusher. In the Main class, I instantiate these objects and orchestrate their interactions to achieve the goal of pushing configurations via SSH.

It's worth noting that the SSHConnection class maintains a relationship with an instance of paramiko.SSHClient, a class from the paramiko module that handles the underlying SSH functionality.


2. Python venv setup

Create venv

python3 -m venv myvenv
Enter fullscreen mode Exit fullscreen mode

Activate venv

source myvenv/bin/activate
Enter fullscreen mode Exit fullscreen mode

 Add requirements.txt

paramiko~=3.5.1
Enter fullscreen mode Exit fullscreen mode

Install the Paramiko dependency

pip3 install -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

3. Cisco Modeling Labs setup

CML Installation Guide


4. Coding

Switch class

This class serves as a blueprint for a switch. However, it's not the most modular or scalable approach. For example, if you wanted to add support for a router, it would be better to define a generic Device parent class and have both Switch and Router inherit from it. This would promote better code reuse and extensibility, making the design more in line with solid object-oriented principles.

class Switch:
    def __init__(self, hostname, port, username):
        self.hostname=hostname
        self.port=port
        self.username=username
Enter fullscreen mode Exit fullscreen mode

SSHConnection class

This class handles the SSH connection.

import paramiko
import getpass
import time

class SSHConnection:
    def __init__(self):
        self.ssh_client = paramiko.SSHClient()
        self.shell = None
        self.output = ''

    def __accept_fingerprint(self):
        self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    def __invoke_shell(self):
        self.shell = self.ssh_client.invoke_shell()

    def connect(self, device):
        self.__accept_fingerprint()
        self.ssh_client.connect(
            **device.__dict__,
            password=getpass.getpass('Enter password:'),
            look_for_keys=False, 
            allow_agent=False
        )
        self.__invoke_shell()

    def send(self, command):
        self.shell.send(command)
        time.sleep(1)

    def receive(self):
        return self.shell.recv(10_000).decode().strip()

    def disconnect(self):
        self.ssh_client.close()
Enter fullscreen mode Exit fullscreen mode

ConfigPusher class

This class is used to push the configuration file to the switch(es).

class ConfigPusher:
    def push(self, config, ssh_connection):
        with open(config, 'r') as file:
            for line in file:
                command = line.strip()
                if command:
                    ssh_connection.send(command + '\n')
Enter fullscreen mode Exit fullscreen mode

Main class

This is where all the objects are created and this file is then executed.

from dotenv import load_dotenv
import os
from switch import Switch
from ssh_connection import SSHConnection
from config_pusher import ConfigPusher

def main():
    load_dotenv()
    ip_address = os.getenv("IP_ADDRESS")
    port = os.getenv("PORT")
    ssh_user = os.getenv("SSH_USER")

    switch = Switch(ip_address, port, ssh_user)
    ssh_connection = SSHConnection()
    config_pusher = ConfigPusher()

    ssh_connection.connect(switch)
    config_pusher.push('cisco_configs/asw1.txt', ssh_connection)
    output = ssh_connection.receive()
    print(output)
    ssh_connection.disconnect()

if __name__ == "__main__": 
    main()
Enter fullscreen mode Exit fullscreen mode

5. Basic CML setup

External connector

Once CML is set up, you want to add an external connector to the canvas. Make sure that you select bridge0 under Config to bridge it to your network.

CML external connector

Add a switch

Next, add a switch and connect it to the external connector. Start the switch.

Configure the switch and SSH

For testing, I used a basic configuration on my virtual switch. I assigned the VLAN 1 interface an IP address via DHCP, provided by my home router. This works because my lab environment is bridged to my physical network, allowing the switch to receive an IP from the existing DHCP infrastructure.

Additionally, I created a new user called cisco, assigned it the highest privilege level (15), and set its password using Type 5 encryption, which is based on the MD5 hashing algorithm.

enable
configure terminal

int vlan 1
ip address dhcp

username cisco privilege 15 secret your_password

hostname your_hostname
ip domain name your_domain
crypto key generate rsa modulus 2048

line vty 0 4
login local
transport input ssh
exit
ip ssh version 2
Enter fullscreen mode Exit fullscreen mode

Ping my default gateway

CML ping default gateway

The VLAN 1 SVI has successfully received an IP address from my DHCP server and I'm able to ping my default gateway from the virtual switch.

Localhost connectivity works

As you can see, connectivity between my MacBook and the virtual switch in Cisco Modeling Labs (CML) is working perfectly. 😄

With the network in place, that’s all I need — I can now go ahead and run Main.py within my virtual environment.


Connect and push a very simple configuration

Prepare the config file

I prepared a simple config file that just sets the hostname of the Switch to PythonIsCool and sends three ping commands.

Config file to push

Command to run

python3 main.py
Enter fullscreen mode Exit fullscreen mode

Run the script

Enter the password

It prompts me for the password because getpass module.

Password prompt

Response

This is the message I get back from the switch.

Response from switch

 Verification

We can now see that the hostname has indeed been changed on the switch.

PythonIsCool new hostname verification


That's it

That's everything that I wanted to share with you in this post. Now obviously I left out some details but if you have any questions I'm happy to answer them.

Also, I'd appreciate any feedback and suggestions because I'm by no means an expert Python developer.

Top comments (0)