百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

网络工程师的Python之路——浅谈CSV和Jinja2

toyiye 2024-06-21 11:55 21 浏览 0 评论

弈心:从事计算机网络工作11年(新加坡7年,沙特4年),2013年考取CCIE,在新加坡先后任职于AT&T,新加坡交通部,苹果,Equinix,苏格兰皇家银行等大型企业、银行和政府部门。 目前供职于“世界第一土豪大学“沙特阿卜杜拉国王科技大学(KAUST),担任Senior Network Engineer,为KAUST校史上第一位也是唯一一位华人IT部门高级职员。2019年6月在知乎发布了华语圈第一本专门为编程零基础的网络工程师量身打造的Python教程《网络工程师的Python之路》。

在平时的网络运维工作中,网工少不了要给新设备做批量配置。其中一些配置比如line vty下的transport input ssh以及AAA、SNMP之类的属于共有配置,所有此类命令在所有设备上是统一没有差异的,写一个Python脚本用Paramiko、Netmiko或者pexpect等SSH模块就能轻松搞定。但是针对那些有差异化的配置,比如每个端口下的description(比如用来描述每个端口下面所连接的服务器的名称,服务器的物理端口(网卡)号,以及服务器的用途等等),每个端口的功能(access port还是trunk port?如果是access port,access哪个VLAN?因为存在差异,你不能简单的用一个interface range来一次性统一完成所有端口的配置)以及在不同交换机上划分和创建不同的VLAN id等等,光靠上述SSH模块是很难搞定的,就算能搞定,写出来的脚本的代码量也是相当庞大,不便于维护的。这时就有必要用到Jinja2这个模板引擎来帮助我们完成这些量大又有差异化的配置。

本文将以一台思科的Nexus 9300交换机为例,讲解如何使用Jinja2来完成对该交换机12个端口的配置(12个端口用来做讲解和演示不多不少)。


1. CSV配置文件及csv模块在Python中的使用

在讲Jinja2之前,首先要知道怎么写配置文件。配置文件可以用YAML,也可以用CSV来写,但是鉴于后者的受众更多(会Excel的肯定并比会YAML的人多,你身边不懂编程的同事和同行更是如此),因此本文的示例配置文件将用CSV来写,并且会简单介绍如何使用Python内置的csv模块来导入我们用Excel写的csv文件的内容。

首先来看下面在Excel里写好的CSV文件,文件名为“switch ports.csv”:

该CSV文件包含了nexus 9300交换机的12个端口的端口号,其中Server,Link,Purpose下的内容对应每个端口的description配置,用来描述交换机每个端口下面所连接的服务器的名称,服务器的物理端口(网卡)号,以及服务器的用途,具体命令的格式为“description Link to 【Server】 port 【port】 for 【purpose】”,最后的VLAN将用来区别该端口是trunk port还是access port,方便我们配置“switchport mode access|trunk”,以及"switchport access vlan xxx"。

接下来看下如何在Python中使用csv模块来将上面的CSV文件里的内容导入Python中为我们所用(笔者所用Python版本为3.8.2),代码如下:

import csv

f = open('switch ports.csv')
reader = csv.DictReader(f)

for row in reader:
 print (row)
f.close()
  • csv是Python内置模块,无需安装即可导入直接使用。
  • 用open()函数打开上面提到的CSV文件,对其调用csv模块的DictReader()函数,并将它赋值给reader这个变量,该变量是一组特殊的包含“有序的字典”的可迭代对象(字典本身是无序的,但是csv的DictReader()函数返回的可迭代对象里的元素是一组有序的字典)。
  • 用for语句遍历reader里的内容然后将它们按顺序打印出来,最后用close()关闭刚才打开的CSV文件。

执行代码后的效果如下:

可以看到我们通过csv模块打印出了CSV文件下的所有内容,每行的内容格式为有序的字典,方便我们后续引用。

2. Jinja2配置模板

有了CSV写好的配置文件后,接下来我们要用Jinja2来写配置模板了。首先在“switch ports.csv”文件所在的同一目录下新建一个文本文件,用记事本将它打开,写入下面的配置模板内容后,点击记事本左上角的文件(F)-->另存为(A),将文件名设为“interface-template.j2”,将它的“保存类型”改为“所有文件”,最后点击“保存”,这样我们就得到了一个扩展名为'.j2'的Jinja2模板文件了

为了方便读者复制粘贴,下面给出配置模板里的具体内容:

interface {{ interface }}
  description Link to {{ server }} port {{ port }} for {{ purpose }}
  switchport
  {% if vlan == "Trunk" -%}
  switchport mode trunk
  {% else -%}
  switchport mode access
  switchport access vlan {{ vlan }}
  spanning-tree port type edge
  {% endif -%}
  no shutdown
  • 在Jinja2中,我们用{{ 变量名 }}的格式来定义变量(注意变量名和左右两边的大括号之间分别隔了一个空格),很显然,这里我们定义的{{ interface }}, {{ server }}, {{ port }}, {{ purpose }},{{ vlan }}分别对应的是CSV配置文件里的A1栏至E1栏里的Interface,Server, Port, Purpose, Vlan。
interface {{ interface }}
  description Link to {{ server }} port {{ port }} for {{ purpose }}
switchport access vlan {{ vlan }}
  • 在Jinja2中,if、else语句的格式为{% if 条件 -%}和{% else -%},这里要表达的意思是:如果CSV配置文件里的Vlan(E栏)下对应的内容为Trunk,那么我们将该端口配置为trunk port,如果端口模式为非Trunk,那我们将端口配置为access port,并分配对应的vlan给它,最后将其生成树的端口模式改为edge。注意Jinja2中的if语句最后需要用{% endif -%}来收尾。
  {% if vlan == "Trunk" -%}
  switchport mode trunk
  {% else -%}
  switchport mode access
  switchport access vlan {{ vlan }}
  spanning-tree port type edge
  {% endif -%}
  • 最后不管端口类型是trunk port还是access port,我们都用no shutdown命令开启它(因为no shutdown写在{% endif -%}下面):
  no shutdown

这里介绍了如何使用Jinja2来创建配置模板以及Jinja2的部分语法,下面我们来看下如何在Python中使用Jinja2。

3. Jinja2在Python中的使用

前面讲到了我们在Jinja2的配置模板里定义了{{ interface }}, {{ server }}, {{ port }}, {{ purpose }},{{ vlan }}五个变量,来分别对应CSV配置文件里的A1栏至E1栏里的Interface,Server, Port, Purpose, Vlan。接下来要做的是将两者真正结合,生成最后我们需要的Nexus 9300交换机的的实际配置命令,这里我们需要借助Python来完成,代码如下:

from jinja2 import Template
import csv

csv_file = open('switch ports.csv')
template_file = open('interface-template.j2')

reader = csv.DictReader(csv_file)
interface_template = Template(template_file.read(), keep_trailing_newline=True)

interface_configs = ''

for row in reader:
    interface_config = interface_template.render(
    interface = row['Interface'],
    vlan = row['VLAN'],
    server = row['Server'],
    port = row['Port'],
    purpose = row['Purpose']
    )
    interface_configs += interface_config

print(interface_configs)
  • Jinja2从Python2.4起被引入为Python的内置模块,无需安装即可import导入使用,其中Template是jinja2模块下最常用的类,这里我们用from jinja2 import Template将其导入。随后我们用open()函数打开之前创建好的“switch ports.csv”和“interface-template.j2”两个文件,并分别赋值给csv_file和template_file两个变量。
from jinja2 import Template
import csv

csv_file = open('switch ports.csv')
template_file = open('interface-template.j2')
  • "reader = csv.DictReader(csv_file)"的作用在前面已经讲到了,不再赘述。这里我们用jinja2的Template()函数将j2配置模板文件模板化,并将它赋值给interface_template这个变量,因为这个模板要被反复使用12次(我们有12个交换机端口需要配置),Template()函数里的“keep_trailing_newline=True”这个参数的作用是在每使用完一次模板后,自动替我们在配置命令的最后加入一个换行符\n,避免下面这种命令格式问题的出现:

reader = csv.DictReader(csv_file)
interface_template = Template(template_file.read(), keep_trailing_newline=True)
  • 这里创建一个空的字符串,将其赋值给interface_configs这个变量,它的作用下面马上会讲到。
interface_configs = ''
  • 前面讲到了csv的DictReader()函数返回的是可迭代的对象,这个可迭代对象里的元素是一组一组有序的字典,这里我们使用for语句来遍历reader这个DictReader()函数返回的可迭代对象,并调用Template()下的render()函数对配置模板做渲染,这样配置文件和配置模板就被结合起来了。每调用一次配置文件和配置模板后,我们就能生成一个接口下的配置命令(从Eth1/1开始直到Eth1/12),每个接口配置命令的数据格式都为字符串。最后我们用"interface_configs += interface_config"将12个接口的配置(字符串)全部汇总进上面创建的interface_configs这个空字符串变量,然后通过print (interface_configs)来验证其内容。
interface_configs = ''

for row in reader:
    interface_config = interface_template.render(
    interface = row['Interface'],
    vlan = row['VLAN'],
    server = row['Server'],
    port = row['Port'],
    purpose = row['Purpose']
    )
    interface_configs += interface_config

print(interface_configs)

执行代码后的效果如下:

可以看到运行脚本后,代码将jinja2配置模板渲染成了我们实际需要的12个端口的配置命令。

4. 将生成的配置命令上传给交换机并执行

用Jinja2配置模板配合CSV配置文件生成了我们想要的实际配置命令后,最后一步自然是将该配置命令上传给交换机并执行,这里我们使用netmiko来完成, 在上面的代码中添加一些内容,如下:

from jinja2 import Template
import csv
from netmiko import ConnectHandler

csv_file = open('switch ports.csv')
template_file = open('interface-template.j2')

reader = csv.DictReader(csv_file)
interface_template = Template(template_file.read(), keep_trailing_newline=True)

interface_configs = ''

for row in reader:
    interface_config = interface_template.render(
    interface = row['Interface'],
    vlan = row['VLAN'],
    server = row['Server'],
    link = row['Port'],
    purpose = row['Purpose']
    )
    interface_configs += interface_config

config_set = interface_configs.split('\n')

SW = {
 'device_type': 'cisco_nxos',
 'ip': '10.1.1.1',
 'username': 'admin',
 'password': 'pass',
    }

connect = ConnectHandler(**SW)
print ('Connected to switch')

output = connect.send_config_set(config_set, cmd_verify=False)
print(output)

Netmiko没什么好讲的,只需要注意两点:

  • 1. 这里我们用"config_set = interface_configs.split('\n')"将字符串的配置命令用split('\n')隔行拆解成一个列表,方便我们配合Netmiko的send_config_set()函数来对交换机做配置。
  • 2. 知道"output = connect.send_config_set(config_set, cmd_verify=False)"中的"cmd_verify=False"这个参数意义的人不多,这里讲下它的作用:同Netmiko 2不一样,Netmiko 3中默认要等到输入的命令在屏幕上打印出来后才会执行后面的命令(因为Netmiko 3默认将send_config_set()里的"cmd_verify"参数设为True),像我们这种一次性对交换机输入多达60多条命令的情况(12个端口要配置),经常会出现网络延迟的问题导致在执行脚本时Netmiko会返回“netmiko.sshexception.NemikoTimeoutException:Time-out reading channel, data not available”这个异常(我们写的代码本身没有问题,这是Netmiko 3自身的一个"bug"),如下:

我使用的是Netmiko 3.1.1版本,因此必须将"cmd_verify"参数设为False,写成"output = connect.send_config_set(config_set, cmd_verify=False)",如果你使用的是Netmiko 2则没有这个顾虑(可以通过pip freeze来查看你的Netmiko版本)。

最后执行脚本前,我们先验证Nexus 9300交换机上现有的配置,这里我们选取Eth1/1, Eth1/2, Eth1/9, Eth1/11查看,可以发现目前这些端口下没有任何配置,并且"show interface description"也可以看到前面12个端口没有任何description配置(除了Eth1/5当前有一个“L3 LINK”的description,但是不影响我们的配置和验证)


执行脚本看效果:

配置完成后再次验证交换机的端口配置,并用"show interface description"进一步验证:

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码