Python Script to Execute Custom Commands on Multiple Cisco Devices

Introduction:

In many enterprise environments, network engineers often find themselves logging into multiple Cisco devices to perform repetitive tasks such as collecting configurations, checking logs, or verifying status. Doing this manually for each switch is time consuming.

To streamline this process, I’ve developed a Python script that automates SSH connections to multiple Cisco switches and executes specific CLI commands per switch. The output is saved into organized log files for documentation and further analysis.

Purpose:

Suppose we want to run a unique set of commands on multiple switches. This script allows us to do exactly that define separate commands per switch, connect via SSH, execute them, and save the results automatically.

Prerequisites:

Before you run the script, ensure the following are in place:

  • Python 3.x installed on your system.
  • You need to install the Python package paramiko, which is used to establish SSH connections.

To install it, run:

pip install paramiko
  • Ensure SSH access is enabled on all Cisco switches.
  • All switches should use the same local user credentials, or authentication via TACACS+/RADIUS should be enabled.
  • A file named commands.json should exist in the same directory as the script, containing the list of switch IPs and their respective commands.

Switch IP with Predefibed Commnands(commands.json)

The script reads from a JSON file named commands.json, which acts as a map of switch IPs to the commands that need to be executed on them. The structure looks like this:

{
  "192.168.0.245": [
    "terminal length 0",
    "enable",
    "show tech",
    "show version",
    "show running-config"
  ],
  "192.168.0.246": [
    "terminal length 0",
    "enable",
    "show tech",
    "show interface status",
    "show ip int brief"
  ],
  "192.168.0.244": [
    "terminal length 0",
    "enable",
    "show tech",
    "show logging",
    "show spanning-tree"
  ],
  "192.168.0.247": [
    "terminal length 0",
    "enable",
    "show tech",
    "show logging",
    "show spanning-tree"
  ]
}

Explanation:

  • Each IP address is a key.
  • The value is a list of Cisco CLI commands specific to that switch.
  • This allows for flexibility, as different switches can have different sets of commands.
  • Commands are executed in the order they appear in the list.

How the Script Works:

  1. Checks Directory: Validates whether the specified directory for output logs exists. If it doesn’t, you’ll be prompted to create it(Output will be save in this directory).
  2. Checks Reachability: Pings each switch to confirm it’s reachable before attempting a connection.
  3. Prompts for Credentials: Asks the user for SSH login credentials (username and password).
  4. Establishes SSH Connection: Logs into each switch using paramiko and invokes a shell session.
  5. Runs Commands: The list of commands specified for each switch is executed sequentially.
  6. Saves Output: The combined output of all commands is saved to a timestamped .txt file named after the switch IP.
  7. Displays Summary: At the end, a summary shows how many switches were processed successfully or failed.

Full Python Script:

import paramiko
import time
import getpass
import datetime
import os
import json
import subprocess  # For improved ping handling

def check_switch(ip):
    # Execute the ping command and capture the full output
    ping_result = subprocess.run(
        ["ping", "-n", "4", ip], capture_output=True, text=True
    )
    output = ping_result.stdout
    print(output)  # Show full ping output to console

    # Check for failure keywords in output
    failure_signs = ["unreachable", "timed out", "100% loss"]
    if any(sign.lower() in output.lower() for sign in failure_signs):
        return False

    return True

def ensure_directory_exists(directory):
    if not os.path.exists(directory):
        user_input = input(f"Directory '{directory}' does not exist. Do you want to create it? (y/n): ").strip().lower()
        if user_input == 'y':
            os.makedirs(directory)
            print(f"Directory '{directory}' created successfully.")
        else:
            print(f"Directory '{directory}' was not created.")
    else:
        print(f"Directory '{directory}' already exists.")

def load_commands(file_path):
    with open(file_path, "r") as file:
        return json.load(file)

def run_commands_on_switch(ip_address, commands, username, password, output_dir):
    try:
        ssh_client = paramiko.SSHClient()
        ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh_client.connect(hostname=ip_address, username=username, password=password)

        print(f"Successful connection to {ip_address}")
        remote_connection = ssh_client.invoke_shell()
        time.sleep(1)

        # Accumulate output for all commands
        output = []
        for command in commands:
            remote_connection.send(command + "\n")
            time.sleep(2)  # Adjust if needed, depending on the expected command execution time
            raw_output = remote_connection.recv(655350).decode("utf-8", errors="ignore")
            output.append(raw_output)

        # Save all accumulated output after all commands have been executed
        now = datetime.datetime.now()
        date_str = now.strftime("%d-%m-%Y")
        time_str = now.strftime("%H%M%S")
        fname = os.path.join(output_dir, f"{ip_address}_{date_str}_{time_str}.txt")
        
        with open(fname, "w") as saveoutput:
            saveoutput.write("".join(output))  # Write all accumulated output to file

        print(f"Output saved for {ip_address}: {fname}")
        ssh_client.close()
        return True
    except Exception as e:
        print(f"An error occurred while connecting to {ip_address}: {e}")
        return False

def main():
    my_dir = "C:\\Python\\Backup\\Test2024"
    ensure_directory_exists(my_dir)

    command_file = "commands.json"
    command_mapping = load_commands(command_file)

    username = input("Enter Username: ")
    password = getpass.getpass("Enter Password: ")

    total_processed = 0
    successful = 0
    failed = 0

    for ip_address, commands in command_mapping.items():
        if not check_switch(ip_address):
            print(f"{ip_address} is not reachable. Skipping...\n")
            failed += 1
            continue

        print(f"Processing {ip_address} with commands: {commands}")
        total_processed += 1
        if run_commands_on_switch(ip_address, commands, username, password, my_dir):
            successful += 1
        else:
            failed += 1

    print("\n--- Summary ---")
    print(f"Total switches processed: {total_processed}")
    print(f"Successful connections: {successful}")
    print(f"Failed connections: {failed}")

if __name__ == "__main__":
    main()


Result:


D:\Python3>python.exe ciscotest.py
Directory 'C:\Python\Backup\Test2024' already exists.
Enter Username: admin
Enter Password:

Pinging 192.168.0.245 with 32 bytes of data:
Reply from 192.168.0.245: bytes=32 time=1ms TTL=255
Reply from 192.168.0.245: bytes=32 time=1ms TTL=255
Reply from 192.168.0.245: bytes=32 time=1ms TTL=255
Reply from 192.168.0.245: bytes=32 time=1ms TTL=255

Ping statistics for 192.168.0.245:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 1ms, Maximum = 1ms, Average = 1ms

Processing 192.168.0.245 with commands: ['terminal length 0', 'enable', 'show tech', 'show version', 'show running-config']
Successful connection to 192.168.0.245
Output saved for 192.168.0.245: C:\Python\Backup\Test2024\192.168.0.245_11-05-2025_201100.txt

Pinging 192.168.0.246 with 32 bytes of data:
Reply from 192.168.0.246: bytes=32 time=2ms TTL=255
Reply from 192.168.0.246: bytes=32 time=1ms TTL=255
Reply from 192.168.0.246: bytes=32 time=1ms TTL=255
Reply from 192.168.0.246: bytes=32 time=4ms TTL=255

Ping statistics for 192.168.0.246:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 1ms, Maximum = 4ms, Average = 2ms

Processing 192.168.0.246 with commands: ['terminal length 0', 'enable', 'show tech', 'show interface status', 'show ip int brief']
Successful connection to 192.168.0.246
Output saved for 192.168.0.246: C:\Python\Backup\Test2024\192.168.0.246_11-05-2025_201114.txt

Pinging 192.168.0.244 with 32 bytes of data:
Reply from 192.168.0.62: Destination host unreachable.
Reply from 192.168.0.62: Destination host unreachable.
Reply from 192.168.0.62: Destination host unreachable.
Reply from 192.168.0.62: Destination host unreachable.

Ping statistics for 192.168.0.244:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),

192.168.0.244 is not reachable. Skipping...


Pinging 192.168.0.247 with 32 bytes of data:
Reply from 192.168.0.247: bytes=32 time=2ms TTL=255
Reply from 192.168.0.247: bytes=32 time<1ms TTL=255
Reply from 192.168.0.247: bytes=32 time<1ms TTL=255
Reply from 192.168.0.247: bytes=32 time<1ms TTL=255

Ping statistics for 192.168.0.247:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 2ms, Average = 0ms

Processing 192.168.0.247 with commands: ['terminal length 0', 'enable', 'show tech', 'show logging', 'show spanning-tree']
Successful connection to 192.168.0.247
Output saved for 192.168.0.247: C:\Python\Backup\Test2024\192.168.0.247_11-05-2025_201139.txt

--- Summary ---
Total switches processed: 3
Successful connections: 3
Failed connections: 1

Output Logs:

Conclusion:

This Python script offers a simple and efficient way to manage multiple Cisco switches by automating command execution and saving outputs for future reference. One of its key advantages is the ability to run different sets of commands on different switches, based on what is defined in the commands.json file.

Post a Comment

Cookie Consent
We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.
Oops!
It seems there is something wrong with your internet connection. Please connect to the internet and start browsing again.
AdBlock Detected!
We have detected that you are using adblocking plugin in your browser.
The revenue we earn by the advertisements is used to manage this website, we request you to whitelist our website in your adblocking plugin.
Site is Blocked
Sorry! This site is not available in your country.