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.
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.
Before you run the script, ensure the following are in place:
paramiko, which is used to establish SSH connections.To install it, run:
pip install paramiko
commands.json should exist in the same directory as the script, containing the list of switch IPs and their respective commands.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:
paramiko and invokes a shell session..txt file named after the switch IP.
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()
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
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.