JSON输入下Junos配置备份脚本设备连接异常排查
问题:Junos备份脚本JSON输入导致设备IP与主机名绑定错误
原本正常运行的Junos配置备份脚本,修改为从JSON文件读取主机名和IP地址后出现异常:真实存在的路由器**TACOSPLEASE-R1(10.255.221.11)连接失败,测试用的不存在的路由器TACOSPLEASE-R2(10.255.221.12)**却显示连接成功并完成备份。使用纯IP文本输入时脚本运行正常,怀疑JSON键值配对混乱导致设备信息绑定错误。
运行结果
brian@tacosplease-scripts:~/scripts$ python3 junos-backups.py Config backups started at: 20250912-15:35:41 Connecting to device: TACOSPLEASE-R1 Error connecting to TACOSPLEASE-R1: Either ip or host must be set Connecting to device: TACOSPLEASE-R2 Connected to TACOSPLEASE-R2 Configuration backed up to /home/brian/backups/TACOSPLEASE-R2-Config-Backup-20250912-15:35:41.txt Disconnected from device. Config backups completed at: 20250912-15:35:41 brian@tacosplease-scripts:~/scripts$
JSON设备文件
{ "hosts": [ { "hostname": "TACOSPLEASE-R1", "address": "10.255.221.11" }, { "hostname": "TACOSPLEASE-R2", "address": "10.255.221.12" } ] }
脚本代码
from netmiko import ConnectHandler from datetime import datetime from os import getenv import json # Open json file to read password with open('/home/brian/script-variables/passwords.json', 'r') as pwd_file: pwd_data = json.load(pwd_file) juniper_password = pwd_data['password'] # netmiko variables for juniper juniper_device = { 'device_type': 'juniper_junos', 'username': 'root', 'password': juniper_password, } # Get the current date/time and format it for our filenames current_datetime = datetime.now() formatted_datetime = current_datetime.strftime("%Y%m%d-%H:%M:%S") # Print the start time print("Config backups started at:", formatted_datetime) # Open file to read IP addreses from with open('/home/brian/script-variables/devices.json', 'r') as dvc_file: device_file = json.load(dvc_file) json_device_file = device_file["hosts"] # Loop through the devices json file so that we can connect to every device for value in json_device_file: print(f"\nConnecting to device: {value['hostname']}") # Create a dictionary for the current device's connection current_device = juniper_device.copy() juniper_device["host"] = value['address'] try: net_connect = ConnectHandler(**current_device) print(f"Connected to {value['hostname']}") # Pull the configuration in 'set' format config_output = net_connect.send_command('show configuration | display set', use_textfsm=False) # Set the backup file name backup_filename = f"/home/brian/backups/{value['hostname']}-Config-Backup-{formatted_datetime}.txt" # Save the configuration to a file with open(backup_filename, 'w') as device_file: device_file.write(config_output) print(f"Configuration backed up to {backup_filename}") except Exception as e: print(f"Error connecting to {value['hostname']}: {e}") finally: if 'net_connect' in locals() and net_connect.is_alive(): net_connect.disconnect() print("Disconnected from device.") # Print the finish time print("Config backups completed at:", formatted_datetime)
问题根源
脚本中存在字典赋值逻辑错误:
- 复制基础配置到
current_device后,错误地将IP地址赋值给原始的juniper_device字典的host键,而非复制后的current_device。 - 第一次循环时,
current_device缺少host键,导致TACOSPLEASE-R1连接失败;第二次循环时,juniper_device已被第一次循环赋值了IP,current_device复制后带有该IP,因此错误地用TACOSPLEASE-R1的IP发起连接,却显示TACOSPLEASE-R2的主机名。
修复方案
修改循环内的赋值逻辑,将IP地址赋值给复制后的current_device,而非原始字典:
# 原错误代码 current_device = juniper_device.copy() juniper_device["host"] = value['address'] # 修改为 current_device = juniper_device.copy() current_device["host"] = value['address']
修复后的完整循环代码:
for value in json_device_file: print(f"\nConnecting to device: {value['hostname']}") # Create a dictionary for the current device's connection current_device = juniper_device.copy() current_device["host"] = value['address'] # 修正此处赋值对象 try: net_connect = ConnectHandler(**current_device) print(f"Connected to {value['hostname']}") # Pull the configuration in 'set' format config_output = net_connect.send_command('show configuration | display set', use_textfsm=False) # Set the backup file name backup_filename = f"/home/brian/backups/{value['hostname']}-Config-Backup-{formatted_datetime}.txt" # Save the configuration to a file with open(backup_filename, 'w') as device_file: device_file.write(config_output) print(f"Configuration backed up to {backup_filename}") except Exception as e: print(f"Error connecting to {value['hostname']}: {e}") finally: if 'net_connect' in locals() and net_connect.is_alive(): net_connect.disconnect() print("Disconnected from device.")
内容的提问来源于stack exchange,提问作者Brian
相关产品推荐
相关产品推荐

