You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 09:24:50