initial commit
This commit is contained in:
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
paramiko>=2.9.0
|
206
router_backup.py
Executable file
206
router_backup.py
Executable file
@ -0,0 +1,206 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
SSH Router Backup Script
|
||||||
|
Connects to routers via SSH and runs commands, saving output to local files.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import paramiko
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from datetime import datetime
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
class RouterBackup:
|
||||||
|
def __init__(self, hostname, username, password=None, key_file=None, port=22):
|
||||||
|
self.hostname = hostname
|
||||||
|
self.username = username
|
||||||
|
self.password = password
|
||||||
|
self.key_file = key_file
|
||||||
|
self.port = port
|
||||||
|
self.client = None
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
"""Establish SSH connection to the router"""
|
||||||
|
try:
|
||||||
|
self.client = paramiko.SSHClient()
|
||||||
|
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
|
||||||
|
if self.key_file:
|
||||||
|
self.client.connect(
|
||||||
|
hostname=self.hostname,
|
||||||
|
username=self.username,
|
||||||
|
key_filename=self.key_file,
|
||||||
|
port=self.port,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
elif os.environ.get('SSH_AUTH_SOCK'):
|
||||||
|
# Use SSH agent if available
|
||||||
|
self.client.connect(
|
||||||
|
hostname=self.hostname,
|
||||||
|
username=self.username,
|
||||||
|
port=self.port,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.client.connect(
|
||||||
|
hostname=self.hostname,
|
||||||
|
username=self.username,
|
||||||
|
password=self.password,
|
||||||
|
port=self.port,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
print(f"Successfully connected to {self.hostname}")
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to connect to {self.hostname}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def run_command(self, command):
|
||||||
|
"""Execute a command on the router and return the output"""
|
||||||
|
if not self.client:
|
||||||
|
print("No active connection")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
stdin, stdout, stderr = self.client.exec_command(command)
|
||||||
|
output = stdout.read().decode('utf-8')
|
||||||
|
error = stderr.read().decode('utf-8')
|
||||||
|
|
||||||
|
if error:
|
||||||
|
print(f"Error executing '{command}': {error}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return output
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to execute command '{command}': {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def backup_commands(self, commands, output_dir="backup"):
|
||||||
|
"""Run multiple commands and save outputs to files"""
|
||||||
|
if not os.path.exists(output_dir):
|
||||||
|
os.makedirs(output_dir)
|
||||||
|
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
backup_info = {
|
||||||
|
"hostname": self.hostname,
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"commands": {}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Truncate output file at start
|
||||||
|
filename = f"{self.hostname}.txt"
|
||||||
|
filepath = os.path.join(output_dir, filename)
|
||||||
|
open(filepath, 'w').close()
|
||||||
|
|
||||||
|
for i, command in enumerate(commands):
|
||||||
|
print(f"Running command {i+1}/{len(commands)}: {command}")
|
||||||
|
output = self.run_command(command)
|
||||||
|
|
||||||
|
if output:
|
||||||
|
# Use hostname as filename
|
||||||
|
filename = f"{self.hostname}.txt"
|
||||||
|
filepath = os.path.join(output_dir, filename)
|
||||||
|
|
||||||
|
with open(filepath, 'a') as f:
|
||||||
|
f.write(f"## COMMAND: {command}\n")
|
||||||
|
f.write(output)
|
||||||
|
|
||||||
|
backup_info["commands"][command] = {
|
||||||
|
"filename": filename,
|
||||||
|
"success": True,
|
||||||
|
"output_length": len(output)
|
||||||
|
}
|
||||||
|
print(f"Output saved to {filepath}")
|
||||||
|
else:
|
||||||
|
backup_info["commands"][command] = {
|
||||||
|
"filename": None,
|
||||||
|
"success": False,
|
||||||
|
"output_length": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save backup info
|
||||||
|
info_file = os.path.join(output_dir, f"{self.hostname}_{timestamp}_info.json")
|
||||||
|
with open(info_file, 'w') as f:
|
||||||
|
json.dump(backup_info, f, indent=2)
|
||||||
|
|
||||||
|
return backup_info
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
"""Close SSH connection"""
|
||||||
|
if self.client:
|
||||||
|
self.client.close()
|
||||||
|
print(f"Disconnected from {self.hostname}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='SSH Router Backup Tool')
|
||||||
|
parser.add_argument('--host', required=True, help='Router hostname or IP address')
|
||||||
|
parser.add_argument('--user', required=True, help='SSH username')
|
||||||
|
parser.add_argument('--password', help='SSH password')
|
||||||
|
parser.add_argument('--key-file', help='SSH private key file path')
|
||||||
|
parser.add_argument('--port', type=int, default=22, help='SSH port (default: 22)')
|
||||||
|
parser.add_argument('--output-dir', default='/tmp', help='Output directory for command output files (default: /tmp)')
|
||||||
|
parser.add_argument('--commands', nargs='+', help='Commands to run on the router')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Default commands for SR Linux routers
|
||||||
|
default_commands = [
|
||||||
|
"show version",
|
||||||
|
"show system information",
|
||||||
|
"show interface",
|
||||||
|
"show network-instance",
|
||||||
|
"show route-table"
|
||||||
|
]
|
||||||
|
|
||||||
|
commands = args.commands if args.commands else default_commands
|
||||||
|
|
||||||
|
# Use SSH key by default, fall back to password
|
||||||
|
if not args.password and not args.key_file:
|
||||||
|
# Check if SSH agent is available first
|
||||||
|
if os.environ.get('SSH_AUTH_SOCK'):
|
||||||
|
print("Using SSH agent for authentication")
|
||||||
|
else:
|
||||||
|
# Try default SSH key locations
|
||||||
|
default_keys = [
|
||||||
|
os.path.expanduser("~/.ssh/id_rsa"),
|
||||||
|
os.path.expanduser("~/.ssh/id_ed25519"),
|
||||||
|
os.path.expanduser("~/.ssh/id_ecdsa")
|
||||||
|
]
|
||||||
|
|
||||||
|
for key_path in default_keys:
|
||||||
|
if os.path.exists(key_path):
|
||||||
|
args.key_file = key_path
|
||||||
|
print(f"Using SSH key: {key_path}")
|
||||||
|
break
|
||||||
|
|
||||||
|
# If no key found and no SSH agent, prompt for password
|
||||||
|
if not args.key_file:
|
||||||
|
import getpass
|
||||||
|
args.password = getpass.getpass("No SSH key found. Enter SSH password: ")
|
||||||
|
|
||||||
|
# Create backup instance
|
||||||
|
backup = RouterBackup(
|
||||||
|
hostname=args.host,
|
||||||
|
username=args.user,
|
||||||
|
password=args.password,
|
||||||
|
key_file=args.key_file,
|
||||||
|
port=args.port
|
||||||
|
)
|
||||||
|
|
||||||
|
# Connect and backup
|
||||||
|
if backup.connect():
|
||||||
|
try:
|
||||||
|
backup_info = backup.backup_commands(commands, args.output_dir)
|
||||||
|
print(f"\nBackup completed. Files saved to '{args.output_dir}' directory")
|
||||||
|
print(f"Summary: {sum(1 for cmd in backup_info['commands'].values() if cmd['success'])}/{len(commands)} commands successful")
|
||||||
|
finally:
|
||||||
|
backup.disconnect()
|
||||||
|
else:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
Reference in New Issue
Block a user