本人扎根数通行业多年,作为数通老油条,日常运维当中很有必要定时去备份网络设备配置命令,Python自动备份脚本结合了网络通信、文件操作、日志管理和配置管理等多个领域的知识。通过编写和使用这样的脚本,网络管理员能够自动化网络设备的维护工作,提高工作效率,同时减少人为错误。对于熟悉Python编程和网络设备操作的人员来说,这是一个非常实用的工具。
以下脚本总体来说就是:SSH登录交换机,执行dis cu 命令来查看交换机配置命令,过程中会模拟空格键使得能够获取完整的配置命令输出,将输出内容保存在本地指定路径。此方法100%有用。
脚本如下:
import paramiko
import pandas as pd
import os
import time
import datetime
# 读取交换机信息
switches_df = pd.read_excel('switches.xlsx', engine='openpyxl')
# 可自定义用于判断交换机输出结束的字符序列,默认为('\n', '\r\n'),可按需修改
END_OUTPUT_MARKER = ('\n', '\r\n')
def backup_switch_config(ip, username, password, command):
"""
通过SSH连接到交换机,执行指定命令获取配置信息并返回
"""
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
max_read_times = 100 # 设置最大读取次数
read_count = 0 # 记录当前读取次数
max_retries = 3 # 设置最大重试次数
retry_count = 0 # 记录当前重试次数
while retry_count < max_retries:
try:
print(f"正在尝试连接交换机 {ip}...")
client.connect(ip, username=username, password=password)
print(f"已成功连接到交换机 {ip},准备发送命令...")
# 获取一个交互式终端
shell = client.invoke_shell()
# 确保command是有效字符串类型且格式正确,进行更严谨处理
if pd.isnull(command) or str(command).strip() == "":
print(f"交换机 {ip} 的命令列值为空或格式不正确,将使用默认命令 'display current-configuration'")
command = 'display current-configuration'
else:
command = str(command).strip() # 去除两端可能的空白字符
# 发送命令
shell.send(command + '\n')
print(f"已向交换机 {ip} 发送命令: {command}")
time.sleep(1) # 延时等待交换机响应,确保传入的是合法数字类型,这里默认等待1秒
# 模拟长按空格键4秒,这里通过每秒发送10次空格来模拟(频率可按需调整)
space_press_duration = 10 # 模拟长按的时长(秒)
space_send_frequency = 10 # 每秒发送空格的次数
for _ in range(space_press_duration * space_send_frequency):
shell.send(' ')
time.sleep(1 / space_send_frequency)
output = ""
last_output_chunk = "" # 记录上一次读取到的输出块,用于更准确判断是否结束读取
while True:
print("正在尝试读取交换机输出...")
# 读取输出
if shell.recv_ready():
data = shell.recv(655350).decode()
output += data
print(f"已读取到部分输出,长度为: {len(data)} 字节,内容为: {data}")
read_count += 1 # 每次读取到数据,读取次数加1
last_output_chunk = data # 更新上一次输出块
# 判断是否出现分页提示,若是则发送回车键获取下一页内容
if '---- More ----' in data:
shell.send('\n')
time.sleep(1)
else:
print("交换机暂无输出,等待...")
# 检查是否可以结束输出读取,综合考虑最大读取次数、换行符及输出内容变化情况
if (read_count >= max_read_times or
any(marker in last_output_chunk and last_output_chunk == output[-len(last_output_chunk):] for marker in END_OUTPUT_MARKER)):
print("满足结束读取输出条件,结束读取。")
break
if not any(marker in output[-10:] for marker in END_OUTPUT_MARKER):
shell.send(' \r\n')
time.sleep(1) # 再次确保等待时间参数合法,默认等待1秒
# 简单校验获取到的配置信息是否完整(这里只是示例,需完善校验逻辑)
if is_config_complete(output):
return output
else:
print("检测到获取的配置信息可能不完整,准备重试...")
retry_count += 1
continue
except paramiko.AuthenticationException as e:
print(f"连接交换机 {ip} 时认证失败: {e},请检查用户名和密码是否正确。")
return ""
except paramiko.SSHException as e:
print(f"连接交换机 {ip} 出现SSH异常: {e},可能是SSH配置或网络问题,请检查相关设置。")
return ""
except Exception as e:
print(f"连接交换机 {ip} 出现其他未知异常: {e},请查看详细错误信息排查问题。")
return ""
finally:
client.close()
print(f"已关闭与交换机 {ip} 的连接")
print(f"经过 {max_retries} 次重试后,仍未能获取完整的交换机配置信息,请检查相关问题。")
return ""
def save_backup(device_name, config):
"""
使用时间戳生成文件名,将交换机配置保存到指定的绝对路径下的文件中
"""
backup_dir = "C:\\SwitchConfigs" # 设置保存备份文件的绝对路径,可按需修改
if not os.path.exists(backup_dir):
os.makedirs(backup_dir)
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
filename = os.path.join(backup_dir, f"{device_name}_backup_{timestamp}.txt")
print(f"准备保存备份文件,文件路径为: {filename}")
try:
with open(filename, 'w', encoding='utf-8') as file:
file.write(config)
print(f"Switch configuration backup saved as {filename}")
except IOError as e:
print(f"Error saving backup file {filename}: {e}")
def delete_old_backups(days=7):
"""
删除指定绝对路径下本地指定天数之前的交换机配置备份文件
"""
current_time = time.time()
backup_dir = "C:\\SwitchConfigs" # 对应保存备份文件的绝对路径
if not os.path.exists(backup_dir):
os.makedirs(backup_dir)
# 删除7天前的文件
for file in os.listdir(backup_dir):
file_path = os.path.join(backup_dir, file)
file_time = os.path.getmtime(file_path)
if (current_time - file_time) // (24 * 3600) >= days:
try:
os.remove(file_path)
print(f"Deleted old backup file: {file_path}")
except OSError as e:
print(f"Error deleting file {file_path}: {e}")
def is_config_complete(config):
"""
简单判断配置信息是否完整的函数,这里只是示例,需根据实际情况完善更合理的校验逻辑
比如可以计算配置内容的校验和,与完整配置的校验和对比等方式
"""
return len(config) > 0 # 暂时简单判断长度大于0就算完整,实际需改进
def main():
print(switches_df)
for index, row in switches_df.iterrows():
switch_name = str(row['Switch Name'])
ip_address = str(row['IP Address'])
username = str(row['Username'])
password = str(row['Password'])
command = str(row['Command']) if 'Command' in row else 'display current-configuration'
print(f"Backing up configuration of {switch_name} ({ip_address})...")
config = backup_switch_config(ip_address, username, password, command)
if config:
save_backup(switch_name, config)
delete_old_backups()
if __name__ == '__main__':
main()
switches.xlsx如下:
注意:本地保存目录需要先创建!!!!
表格信息参照实际网络设备信息更改!!!!
执行结果: