浅谈JSON数据格式用以Paramiko和Netmiko的案例

浅谈JSON数据格式用以Paramiko和Netmiko的案例

〇、致谢

非常感谢盛哥 @朱嘉盛 ,在知乎专栏 《网工手艺》 中对Python处理结构化数据方法的梳理和分享:

一、JSON数据格式

JSON全称 JavaScript Object Notation ,是一种结构化的数据格式。也是一种完全独立于编程语言之外的文本存储、交换格式。

1、JSON语法规范

(1)json对象由key/value对组成;

(2)json对象必须在一组大括号{}内书写;

(3)json对象,key必须是 字符串 ,value可以是字符串、数字、布尔值、数组[]、对象、null;

{
       "router_01": [
               "router_01",
               "admin",
               "cisco",
               "192.168.47.1"
       "router_02": [
               "router_02",
               "admin",
               "cisco",
               "192.168.47.2"
       "router_03": [
               "router_03",
               "admin",
               "cisco",
               "192.168.47.3"
}

(4)json对象是可以嵌套的,即value也可以由另一组对象嵌套组成:

{
   "name": "R1",
   "device_info": {
     "device_type": "cisco",
     "username": "admin",
     "password": "cisco",
     "ip": "192.168.47.1"
}

(5)json对象中,value如果由数组[]构成,那么数组[]的内部,不能包含key/value对;

(6)多个json对象,可以放到一个数组[]中。

2、JSON格式辨析

(1) 错误格式 ,因为“device_info”这个key对应的value使用了数组[],数组内部不能包含key/value对。

{
   "name": "R1",
   "device_info": [
     "device_type": "cisco",
     "username": "admin",
     "password": "cisco",
     "ip": "192.168.47.1"
  ] # 错误格式,数组中[]不能放入key/value对
}

(2) 正确格式 ,因为“device_info”这个key对应的value使用了json对象嵌套(另一组key/value对)

{
 "name": "R1",
 "device_info":{
   "device_type": "cisco",
   "username": "admin",
   "password": "cisco",
   "ip": "192.168.47.1"
} # 正确格式,value可以使用JSON对象嵌套(key/value嵌套) 
}

(3) 错误格式 ,{}中,包含不正确的json对象(key/value对)

{
       "router_01": 
               "router_01",
               "admin",
               "cisco",
               "192.168.47.1"
} # 错误格式,JSON对象{}内部,必须是key/value对

(4) 正确格式 ,{}中,如果value有多个值,可以用数组收纳

{
       "router_01": [
               "router_01",
               "admin",
               "cisco",
               "192.168.47.1"
      ] # 正确格式,value可以用数组容纳多个值
}

(5) 错误格式 ,json对象{}中,不能再包含多个JSON对象{}

{
 "name": "R1",
 "device_info":{
   "device_type": "cisco",
   "username": "admin",
   "password": "cisco",
   "ip": "192.168.47.1"
 "name": "R2",
 "device_info":{
   "device_type": "cisco",
   "username": "admin",
   "password": "cisco",
   "ip": "192.168.47.1"
} # 错误格式,JSON对象{}中,不能再包含多个JSON对象{}

(6) 正确格式 ,多个json对象{},可以放到一个数组中

[
 "name": "R1",
 "device_info":{
   "device_type": "cisco",
   "username": "admin",
   "password": "cisco",
   "ip": "192.168.47.1"
 "name": "R2",
 "device_info":{
   "device_type": "cisco",
   "username": "admin",
   "password": "cisco",
   "ip": "192.168.47.1"
] # 正确格式,JSON的多个对像{},可以用数组容纳

(7)错误格式,{}中的json对象的key,必须是字符串

{
# 错误格式,router_01并不是字符串,需要用“”引起来当字符串
       router_01: [
               "router_01",
               "admin",
               "cisco",
               "192.168.47.1"
}

二、JSON用于Paramiko/Netmiko的简单案例

很多同行朋友,在使用Paramiko、Netmiko做自动化实验的时候,往往受制于非结构化数据。

比如,在使用Netmiko时,需要为ConnectHandler函数传一个 字典 参数,该字典包含了device_type、username、password、ip等数据。这就让部分同行朋友“恨如头醋” ,由于device_type的限制,部分同行朋友往往对Netmiko的“异构能力”产生“质疑”。

其实,只要掌握了结构化数据的知识,这样的烦恼就能迎刃而解。

1、Paramiko案例

熟悉DevNet的朋友可能都知道Python中大名鼎鼎的Paramiko库,如果要使用Paramiko库来操作网络设备,那么必须用到SSH登录名、SSH登录密码、SSH登录IP地址等数据。其实,就可以用结构化的数据格式来组织,比如,用JSON来组织。

sw_info.json

{
"router_01": [
"router_01",
"admin",
"cisco",
"192.168.47.1"
"router_02": [
"router_02",
"admin",
"cisco",
"192.168.47.2"
"router_03": [
"router_03",
"admin",
"cisco",
"192.168.47.3"
}

使用python自带的json库,来处理sw_info.json,Python自带的json库是很容易把json格式的文件转换成字典的。利用这个特性,就可以根据字典的特性来处理相关的数据。注意json.load和json.loads的区别。 盛哥 @朱嘉盛 在他的《 朱嘉盛:网络工程师Python数据存储(第2节,JSON文件) 》一文中,有过很精彩的助记总结:带s就是处理字符串的。不带s的就是处理文件的。

#! /usr/bin/env python3
# _*_ coding: utf-8 _*_
import json
with open('sw_info.json', encoding='utf-8') as f:
   template = f.read() # 读取sw_info.json为字符串
print(type(template))
print(template)
sw_date = json.loads(template) # 使用loads字符串读取成字典,注意这里使用的是带s的loads,处理的就是字符串
print(type(sw_date)) # 查看是否被处理成了字典
pprint(sw_date) # 漂亮打印,打印出字典的内容
print('\n')
# 遍历字典,把key读取出来
for key in sw_date.keys(): 
   # 利用key,取出value列表,利用列表进行多重歌值
   filename, username, password, ip = sw_date[key]
# 打印出多重赋值的变量内容
   print(filename)
   print(username)
   print(password)
   print(ip)
   print('\n')

运行结果

username@usernamedeMacBookPro1 Downloads % /usr/bin/env python3 "/Users/username/Downloads/json_test.py"
<class 'str'>
('{\n'
'\t"router_01": [\n'
'\t\t"router_01",\n'
'\t\t"admin",\n'
'\t\t"cisco",\n'
'\t\t"192.168.47.1"\n'
'\t],\n'
'\t"router_02": [\n'
'\t\t"router_02",\n'
'\t\t"admin",\n'
'\t\t"cisco",\n'
'\t\t"192.168.47.2"\n'
'\t],\n'
'\t"router_03": [\n'
'\t\t"router_03",\n'
'\t\t"admin",\n'
'\t\t"cisco",\n'
'\t\t"192.168.47.3"\n'
'\t]\n'
'}\n')
<class 'dict'>
{'router_01': ['router_01', 'admin', 'cisco', '192.168.47.1'],
'router_02': ['router_02', 'admin', 'cisco', '192.168.47.2'],
'router_03': ['router_03', 'admin', 'cisco', '192.168.47.3']}
# 多个设备的信息,都可以直接用字典value多重赋值
router_01
admin
cisco
192.168.47.1
router_02
admin
cisco
192.168.47.2
router_03
admin
cisco
192.168.47.3
​

2、Netmiko案例

dev_info.json

[
   "name": "R1",
   "device_info": {
     "device_type": "cisco_ios",
     "username": "admin",
     "password": "cisco",
     "ip": "192.168.47.10"
   "name": "R2",
   "device_info": {
     "device_type": "huawei",
     "username": "admin",
     "password": "huawei",
     "ip": "192.168.47.20"
]

依然使用python自带的json库,来处理这些数据,利用json.load函数,直接把json文件读取成列表,然后再进行处理。

#! /usr/bin/env python3
# _*_ coding: utf-8 _*_
import json
from pprint import pprint
with open('dev_info.json') as f:
   template = json.load(f) # 因为读取的是json文件,所以要使用不带s的load
print(type(template)) # 查看json.load读取的数据类型,注意,这是列表
pprint(template) # 漂亮打印出列表的内容
print('\n')
# 从列表中把字典遍历出来
for dict_info in template:
   for key in dict_info.keys():
       if 'device_info' == key:
           dict_netmiko_connecthandler = dict_info['device_info']
           pprint(dict_netmiko_connecthandler)
           print('\n')

运行结果

username@usernamedeMacBookPro1 Downloads % /usr/bin/env python3 "/Users/username/Downloads/tempCodeRunnerFile.py"
<class 'list'>
[{'device_info': {'device_type': 'cisco_ios',
                 'ip': '192.168.47.10',
                 'password': 'cisco',
                 'username': 'admin'},
 'name': 'R1'},
{'device_info': {'device_type': 'huawei',
                 'ip': '192.168.47.20',
                 'password': 'huawei',
                 'username': 'admin'},
 'name': 'R2'}]
# 以下字典,可以直接传给Netmiko的ConnectHandler函数,实现“异构”
{'device_type': 'cisco_ios',
'ip': '192.168.47.10',
'password': 'cisco',
'username': 'admin'}