Skip to content

Latest commit

 

History

History
782 lines (630 loc) · 26.1 KB

File metadata and controls

782 lines (630 loc) · 26.1 KB

六、使用 Python 和 Jinja2 的配置生成器

本章向您介绍 YAML 格式,用于表示数据和从 Jinja2 语言创建的黄金模板生成配置。我们将在 Ansible 和 Python 中使用这两个概念为我们的配置创建一个数据模型存储。

本章将介绍以下主题:

  • YAML 是什么?
  • 使用 Jinja2 构建黄金配置模板

YAML 是什么?

YAML 不是标记语言YAML)通常被称为数据序列化语言。它的目的是让人可读,并将数据组织成结构化格式。编程语言可以理解 YAML 文件(通常具有.yml.yaml扩展名)的内容,并将其映射到内置数据类型。例如,当您使用 Python 脚本中的.yaml文件时,它会自动将内容转换为字典{}或列表[],因此您可以对其进行操作和迭代。

YAML 规则有助于构造可读文件,因此理解它们对于编写有效且格式良好的 YAML 文件非常重要。

YAML 文件格式

在开发 YAML 文件时,需要遵循一些规则。YAML 使用缩进(如 Python),它构建项目之间的关系:

  1. 因此,编写 YAML 文件时的第一条规则是使用空格或制表符使缩进保持一致,并且不要混合使用。
  2. 第二条规则是在创建包含键和值的字典时使用冒号:(有时在yaml中称为关联数组)。冒号左侧的项是键,冒号右侧的项是值。
  3. 第三条规则是在对列表中的项目进行分组时使用破折号"-" 。为了有效地描述数据,可以在 YAML 文件中混合使用字典和列表。左侧用作字典键,而右侧用作字典值。您可以创建任意数量的级别以包含结构化数据:

让我们举一个例子,应用这些规则:

有很多事情需要考虑。首先,文件有一个顶层my_datacenter,作为顶层键,其值由其后面的所有缩进行组成,即GWswitch1switch2。这些项目也用作键,并在其内部具有值,即eve_portdevice_templatehostnamemgmt_intmgmt_ipmgmt_subnet,同时用作 3 级键和 2 级值。

另一件需要注意的事情是enabled_ports,它是一个键,但有一个用作列表的值。我们之所以知道这一点,是因为下一级缩进是破折号。

Notice that all interfaces are sibling elements because they have the same level of indentation.

最后,不需要在字符串周围使用单引号或双引号。当我们将文件加载到 Python 中时,Python 将自动执行该操作,它还将根据缩进确定每个项的数据类型和位置。

现在,让我们开发一个 Python 脚本来读取这个 YAML 文件,并使用yaml模块将其转换为字典和列表:

在本例中,我们可以看到以下内容:

  • 为了处理 YAML 文件,我们在 Python 脚本中导入了yaml模块。此外,我们还导入了pprint函数来显示嵌套字典和列表的层次结构。
  • 然后,我们使用with子句和open()函数作为yaml_file打开yaml_example.yml文件。
  • 最后,我们使用load()函数将文件加载到yaml_data变量中。在这个阶段,Python 解释器将分析yaml文件的内容并建立项目之间的关系,然后将它们转换为标准数据类型。可使用pprint()功能在控制台上显示输出。

脚本输出

现在,使用标准 Python 方法访问任何信息都相当容易。例如,您可以使用my_datacenter后跟switch1键访问switch1配置,如下代码段所示:

pprint(yaml_data['my_datacenter']['switch1'])

{'device_template': 'vIOSL2_Template',
 'eve_port': 32769,
 'hostname': 'SW1',
 'mgmt_intf': 'gig0/0',
 'mgmt_ip': '10.10.88.111',
 'mgmt_subnet': '255.255.255.0'}    

此外,您还可以通过一个简单的for循环对键进行迭代,并打印任意级别的值:

for device in yaml_data['my_datacenter']:
    print device

GW
switch2
switch1

As a best practice, it's recommended you keep the key names consistent and change only the values while you describe your data. For example, the hostname, mgmt_intf, and mgmt_ip items exist on all devices with the same name, while they have different values in the .yaml file.

文本编辑器提示

正确的缩进对于 YAML 数据非常重要。建议使用高级文本编辑器,如升华文本或记事本++,因为它们具有将选项卡转换为特定数量的空白的选项。同时,您可以选择特定的制表符缩进大小为 2 或 4。因此,每当您单击选项卡按钮时,编辑器将该选项卡转换为静态数量的空白。最后,您可以选择在每个缩进处显示垂直线,以确保线缩进量相同。

Please note that Microsoft Windows Notepad doesn't have that option and this may result in a formatting error in your YAML file.

以下是一个名为 Sublime Text 的高级编辑器示例,可以使用上述选项进行配置:

屏幕截图显示了垂直线参考线,当您单击选项卡时,这些垂直线参考线确保同级项处于相同的缩进级别和空格数。

使用 Jinja2 构建黄金配置

大多数网络工程师都有一个文本文件作为特定设备配置的模板。此文件包含具有许多值的网络配置部分。当网络工程师想要配置新设备或更改其配置时,他们基本上会用另一个文件替换此文件中的特定值以生成新配置。

使用 Python 和 Ansible,在本书的后面,我们将使用 Jinja2 模板语言(高效地自动化此过程 http://jinja.pocoo.org )。开发 Jinja2 的核心概念和驱动程序是对特定网络/系统配置的所有模板文件使用统一的语法,并将数据与实际配置分开。这允许我们多次使用同一模板,但使用不同的数据集。此外,如 Jinja2 网页所示,它具有一些独特的功能,使其从其他模板语言中脱颖而出。

以下是官方网站上提到的一些功能:

  • 强大的自动 HTML 转义系统,用于防止跨站点脚本编写。
  • 高性能,即时编译为 Python 字节码。Jinja2 将在第一次加载时将模板源代码转换为 Python 字节码,以获得最佳运行时性能。
  • 可选的提前编译。
  • 易于使用调试系统进行调试,该系统将模板编译和运行时错误集成到标准 Python 回溯系统中。
  • 可配置语法:例如,您可以重新配置 Jinja2 以更好地适应输出格式,如 LaTeX 或 JavaScript。
  • 模板设计器助手:Jinja2 附带了大量有用的小助手,帮助解决模板中的常见任务,例如将项目序列分解为多列。

另一个重要的 Jinja 功能是模板继承,通过它我们可以创建一个基本/父模板,该模板定义了我们系统的基本结构或所有设备的第 0 天初始配置。此初始配置将是基本配置,并包含常见部分,如用户名、管理子网、默认路由和 SNMP 社区。其他子模板扩展基础模板并继承它。

The terms Jinja and Jinja2 are used interchangeably throughout this chapter.

让我们举几个例子,在深入研究 JICAN2 语言提供的更多特性之前,先创建模板:

  1. 首先,我们需要使用以下命令确保 Jinja2 安装在您的系统中:
pip install jinja2 

该软件包将从 PyPi 下载,然后安装在站点软件包上。

  1. 现在,打开您喜爱的文本编辑器并编写以下模板,该模板表示第 2 层交换机的简单第 0 天(初始)配置,该交换机配置设备主机名、一些aaa参数、每个交换机上应存在的默认 VLAN 以及 IP 地址管理:
hostname {{ hostname }}

aaa new-model
aaa session-id unique
aaa authentication login default local
aaa authorization exec default local none
vtp mode transparent
vlan 10,20,30,40,50,60,70,80,90,100,200

int {{ mgmt_intf }}
no switchport
no shut
ip address {{ mgmt_ip }} {{ mgmt_subnet }}

Some text editors (such as Sublime Text and Notepad++) provide support for Jinja2 and can do syntax highlighting and auto-completion for you, either by natively supporting it or through extension.

注意,在前面的模板中,变量是用双大括号{{ }}编写的。因此,当 Python 脚本加载模板时,它将用所需的值替换这些变量:

#!/usr/bin/python

from jinja2 import Template
template = Template('''
hostname {{hostname}}

aaa new-model
aaa session-id unique
aaa authentication login default local
aaa authorization exec default local none
vtp mode transparent
vlan 10,20,30,40,50,60,70,80,90,100,200

int {{mgmt_intf}}
 no switchport
 no shut
 ip address {{mgmt_ip}} {{mgmt_subnet}}
''')

sw1 = {'hostname': 'switch1', 'mgmt_intf': 'gig0/0', 'mgmt_ip': '10.10.88.111', 'mgmt_subnet': '255.255.255.0'}
print(template.render(sw1))

在本例中,我们可以看到以下内容:

  • 首先,我们从jinja2模块导入Template类。此类将验证并解析 Jinja2 文件。
  • 然后,我们定义了一个变量sw1,它是一个字典,其中键的名称等于模板中的变量。字典值将是呈现模板的数据。
  • 最后,我们在模板内部使用了render()方法,将sw1作为输入,将 Jinja2 模板与渲染值连接起来,并打印配置。

脚本输出

现在,让我们增强脚本并使用 YAML 来呈现模板,而不是在字典中硬编码值。概念很简单:我们将在 YAML 文件中为我们的实验室建模day0配置,然后使用yaml.load()将该文件加载到我们的 Python 脚本中,并使用输出为 Jinja2 模板提供信息,这将导致为每个设备生成day0配置文件:

首先,我们将扩展上次开发的 YAML 文件,并向其中添加其他设备,同时保持每个节点的层次结构相同:

---
dc1:
  GW:
    eve_port: 32773
    device_template: vIOSL3_Template
    hostname: R1
    mgmt_intf: gig0/0
    mgmt_ip: 10.10.88.110
    mgmt_subnet: 255.255.255.0

  switch1:
    eve_port: 32769
    device_template: vIOSL2_Template
    hostname: SW1
    mgmt_intf: gig0/0
    mgmt_ip: 10.10.88.111
    mgmt_subnet: 255.255.255.0

  switch2:
    eve_port: 32770
    device_template: vIOSL2_Template
    hostname: SW2
    mgmt_intf: gig0/0
    mgmt_ip: 10.10.88.112
    mgmt_subnet: 255.255.255.0

  switch3:
    eve_port: 32769
    device_template: vIOSL2_Template
    hostname: SW3
    mgmt_intf: gig0/0
    mgmt_ip: 10.10.88.113
    mgmt_subnet: 255.255.255.0

  switch4:
    eve_port: 32770
    device_template: vIOSL2_Template
    hostname: SW4
    mgmt_intf: gig0/0
    mgmt_ip: 10.10.88.114
    mgmt_subnet: 255.255.255.0

以下是 Python 脚本:

#!/usr/bin/python
__author__ = "Bassim Aly"
__EMAIL__ = "basim.alyy@gmail.com"

import yaml
from jinja2 import Template

with open('/media/bassim/DATA/GoogleDrive/Packt/EnterpriseAutomationProject/Chapter6_Configuration_generator_with_python_and_jinja2/network_dc.yml', 'r') as yaml_file:
    yaml_data = yaml.load(yaml_file)

router_day0_template = Template("""
hostname {{hostname}}
int {{mgmt_intf}}
  no shutdown
  ip add {{mgmt_ip}} {{mgmt_subnet}}

lldp run

ip domain-name EnterpriseAutomation.net
ip ssh version 2
ip scp server enable
crypto key generate rsa general-keys modulus 1024

snmp-server community public RW
snmp-server trap link ietf
snmp-server enable traps snmp linkdown linkup
snmp-server enable traps syslog
snmp-server manager

logging history debugging
logging snmp-trap emergencies
logging snmp-trap alerts
logging snmp-trap critical
logging snmp-trap errors
logging snmp-trap warnings
logging snmp-trap notifications
logging snmp-trap informational
logging snmp-trap debugging

""")

switch_day0_template = Template("""
hostname {{hostname}}

aaa new-model
aaa session-id unique
aaa authentication login default local
aaa authorization exec default local none
vtp mode transparent
vlan 10,20,30,40,50,60,70,80,90,100,200

int {{mgmt_intf}}
 no switchport
 no shut
 ip address {{mgmt_ip}} {{mgmt_subnet}}

snmp-server community public RW
snmp-server trap link ietf
snmp-server enable traps snmp linkdown linkup
snmp-server enable traps syslog
snmp-server manager

logging history debugging
logging snmp-trap emergencies
logging snmp-trap alerts
logging snmp-trap critical
logging snmp-trap errors
logging snmp-trap warnings
logging snmp-trap notifications
logging snmp-trap informational
logging snmp-trap debugging

""")

for device,config in yaml_data['dc1'].iteritems():
    if config['device_template'] == "vIOSL2_Template":
        device_template = switch_day0_template
    elif config['device_template'] == "vIOSL3_Template":
        device_template = router_day0_template

    print("rendering now device {0}" .format(device))
    Day0_device_config = device_template.render(config)

    print Day0_device_config
    print "=" * 30

在本例中,我们可以看到以下内容:

  • 我们照常进口了yamlJinja2模块
  • 然后,我们指示脚本将yaml文件加载到yaml_data变量中,该变量将其转换为一系列字典和列表
  • 路由器和交换机配置的两个模板分别定义为router_day0_templateswitch_day0_template
  • for循环将迭代dc1的设备并检查device_template,然后呈现每个设备的配置

脚本输出

以下是路由器配置(输出省略):

以下是开关 1 的配置(输出省略):

从文件系统读取模板

Python 开发人员的一种常见方法是将静态、硬编码的值和模板移到 Python 脚本之外,而只保留脚本内部的逻辑。这种方法使您的程序保持干净和可伸缩性,同时允许其他对 Python 不太了解的团队成员通过更改输入获得所需的输出,Jinja2 也不例外。您可以使用 Jinja2 模块中的FileSystemLoader()类从操作系统目录加载模板。我们将修改代码,将脚本中的router_day0_templateswitch_day0_template内容移动到文本文件中,然后将它们加载到脚本中。

Python 代码

import yaml
from jinja2 import FileSystemLoader, Environment

with open('/media/bassim/DATA/GoogleDrive/Packt/EnterpriseAutomationProject/Chapter6_Configuration_generator_with_python_and_jinja2/network_dc.yml', 'r') as yaml_file:
    yaml_data = yaml.load(yaml_file)

template_dir = "/media/bassim/DATA/GoogleDrive/Packt/EnterpriseAutomationProject/Chapter6_Configuration_generator_with_python_and_jinja2"

template_env = Environment(loader=FileSystemLoader(template_dir),
                           trim_blocks=True,
                           lstrip_blocks= True
                           )

for device,config in yaml_data['dc1'].iteritems():
    if config['device_template'] == "vIOSL2_Template":
        device_template = template_env.get_template("switch_day1_template.j2")
    elif config['device_template'] == "vIOSL3_Template":
        device_template = template_env.get_template("router_day1_template.j2")

    print("rendering now device {0}" .format(device))
    Day0_device_config = device_template.render(config)

    print Day0_device_config
    print "=" * 30

在本例中,我们将导入用于从特定操作系统目录中读取 Jinja2 文件的Environment()FileSystemLoader(),而不是像以前那样从 Jinja2 模块加载Template()类,方法是为它们提供存储模板的template_dir。然后,我们将使用创建的template_env对象和get_template()方法获取模板名称,并将其与配置一起呈现。

Make sure your template file has a .j2 extension at the end. This will make PyCharm recognize the text inside the file as a Jinja2 template and hence provide syntax highlighting and better code completion.

使用 Jinja2 循环和条件

Jinja2 中的循环和条件用于增强我们的模板并向其添加更多功能。我们将首先了解如何在模板中添加for循环,以便迭代 YAML 传递的值。例如,我们可能需要在每个接口下添加交换机配置,例如使用 switchport 模式并配置将在访问端口下配置的 VLAN ID,或者在中继端口的情况下配置允许的 VLAN 范围。

另一方面,我们可能需要在路由器中启用一些接口,并向其添加自定义配置,例如 MTU、速度和双工。因此,我们将使用for循环。

请注意,我们的部分脚本逻辑现在将从 Python 移动到 Jinja2 模板。Python 脚本只需从操作系统外部或通过脚本中的Template()类读取模板,然后使用 YAML 文件中解析的值呈现模板。

金甲 2 号内部for回路的基本结构如下:

{% for key, value in var1.iteritems() %}
configuration snippets
{% endfor %}

Notice the use of {% %} to define logic inside the Jinja2 file.

此外,iteritems()的功能与迭代 Python 字典相同,即迭代键和值对。循环将返回var1字典中每个元素的键和值。

此外,我们可以有一个if条件来验证特定的条件,如果它是真的,那么配置片段将被添加到呈现的文件中。基本的if结构如下所示:

{% if enabled_ports %}
configuration snippet goes here and added to template if the condition is true
{% endif %}

现在,我们将修改描述数据中心设备的.yaml文件,并为每个设备添加接口配置和启用的端口:

---
dc1:
  GW:
    eve_port: 32773
    device_template: vIOSL3_Template
    hostname: R1
    mgmt_intf: gig0/0
    mgmt_ip: 10.10.88.110
    mgmt_subnet: 255.255.255.0
    enabled_ports:
      - gig0/0
      - gig0/1
      - gig0/2

  switch1:
    eve_port: 32769
    device_template: vIOSL2_Template
    hostname: SW1
    mgmt_intf: gig0/0
    mgmt_ip: 10.10.88.111
    mgmt_subnet: 255.255.255.0
    interfaces:
      gig0/1:
        vlan: [1,10,20,200]
        description: TO_DSW2_1
        mode: trunk
      gig0/2:
        vlan: [1,10,20,200]
        description: TO_DSW2_2
        mode: trunk
      gig0/3:
        vlan: [1,10,20,200]
        description: TO_ASW3
        mode: trunk
      gig1/0:
        vlan: [1,10,20,200]
        description: TO_ASW4
        mode: trunk
    enabled_ports:
      - gig0/0
      - gig1/1

  switch2:
    eve_port: 32770
    device_template: vIOSL2_Template
    hostname: SW2
    mgmt_intf: gig0/0
    mgmt_ip: 10.10.88.112
    mgmt_subnet: 255.255.255.0
    interfaces:
      gig0/1:
        vlan: [1,10,20,200]
        description: TO_DSW1_1
        mode: trunk
      gig0/2:
        vlan: [1,10,20,200]
        description: TO_DSW1_2
        mode: trunk
      gig0/3:
        vlan: [1,10,20,200]
        description: TO_ASW3
        mode: trunk
      gig1/0:
        vlan: [1,10,20,200]
        description: TO_ASW4
        mode: trunk
    enabled_ports:
      - gig0/0
      - gig1/1

  switch3:
    eve_port: 32769
    device_template: vIOSL2_Template
    hostname: SW3
    mgmt_intf: gig0/0
    mgmt_ip: 10.10.88.113
    mgmt_subnet: 255.255.255.0
    interfaces:
      gig0/1:
        vlan: [1,10,20,200]
        description: TO_DSW1
        mode: trunk
      gig0/2:
        vlan: [1,10,20,200]
        description: TO_DSW2
        mode: trunk
      gig1/0:
        vlan: 10
        description: TO_Client1
        mode: access
      gig1/1:
        vlan: 20
        description: TO_Client2
        mode: access
    enabled_ports:
      - gig0/0

  switch4:
    eve_port: 32770
    device_template: vIOSL2_Template
    hostname: SW4
    mgmt_intf: gig0/0
    mgmt_ip: 10.10.88.114
    mgmt_subnet: 255.255.255.0
    interfaces:
      gig0/1:
        vlan: [1,10,20,200]
        description: TO_DSW2
        mode: trunk
      gig0/2:
        vlan: [1,10,20,200]
        description: TO_DSW1
        mode: trunk
      gig1/0:
        vlan: 10
        description: TO_Client1
        mode: access
      gig1/1:
        vlan: 20
        description: TO_Client2
        mode: access
    enabled_ports:
      - gig0/0

Notice, that we categorized the switch ports to either trunk port or access port, and also added the vlans for each one.

根据yaml文件,使用 switchport 访问模式进入接口的数据包将被标记为 VLAN。对于 switchport 模式中继,如果传入的数据包具有属于已配置列表的 vlan ID,则允许该数据包。

现在,我们将为设备第 1 天(操作)配置创建两个附加模板。第一个模板将是router_day1_template,第二个模板将是switch_day1_template,它们都将继承我们之前开发的相应 day0 模板:

路由器\u 第 1 天\u 模板:

{% include 'router_day0_template.j2' %}

{% if enabled_ports %}
    {% for port in enabled_ports %}
interface {{ port }}
    no switchport
    no shutdown
    mtu 1520
    duplex auto
    speed auto
    {% endfor %}

{% endif %}

开关\u 第 1 天\u 模板:

{% include 'switch_day0_template.j2' %}

{% if enabled_ports %}
    {% for port in enabled_ports %}
interface {{ port }}
    no switchport
    no shutdown
    mtu 1520
    duplex auto
    speed auto

    {% endfor %}
{% endif %}

{% if interfaces %}
    {% for intf,intf_config in interfaces.items() %}
interface {{ intf }}
 description "{{intf_config['description']}}"
 no shutdown
 duplex full
        {% if intf_config['mode'] %}
            {% if intf_config['mode'] == "access" %}
 switchport mode {{intf_config['mode']}}
 switchport access vlan {{intf_config['vlan']}}

            {% elif intf_config['mode'] == "trunk" %}
 switchport {{intf_config['mode']}} encapsulation dot1q
 switchport mode trunk
 switchport trunk allowed vlan {{intf_config['vlan']|join(',')}}

            {% endif %}
        {% endif %}
    {% endfor %}
{% endif %}

Notice the use of the {% include <template_name.j2> %} tag, which refers to the day0 template of the device.

首先呈现此模板,并用 YAML 传递的值填充,然后填充下一部分。

The Jinja2 language inherits many writing styles and features from the Python language. Although it's not mandatory to follow the indentation rule when developing the template and inserting the tags, the author prefers to have it in a readable Jinja2 template.

脚本输出:

rendering now device GW
hostname R1
int gig0/0
  no shutdown
  ip add 10.10.88.110 255.255.255.0
lldp run
ip domain-name EnterpriseAutomation.net
ip ssh version 2
ip scp server enable
crypto key generate rsa general-keys modulus 1024
snmp-server community public RW
snmp-server trap link ietf
snmp-server enable traps snmp linkdown linkup
snmp-server enable traps syslog
snmp-server manager
logging history debugging
logging snmp-trap emergencies
logging snmp-trap alerts
logging snmp-trap critical
logging snmp-trap errors
logging snmp-trap warnings
logging snmp-trap notifications
logging snmp-trap informational
logging snmp-trap debugging
interface gig0/0
    no switchport
    no shutdown
    mtu 1520
    duplex auto
    speed auto
interface gig0/1
    no switchport
    no shutdown
    mtu 1520
    duplex auto
    speed auto
interface gig0/2
    no switchport
    no shutdown
    mtu 1520
    duplex auto
    speed auto
==============================
rendering now device switch1
hostname SW1
aaa new-model
aaa session-id unique
aaa authentication login default local
aaa authorization exec default local none
vtp mode transparent
vlan 10,20,30,40,50,60,70,80,90,100,200
int gig0/0
 no switchport
 no shut
 ip address 10.10.88.111 255.255.255.0
snmp-server community public RW
snmp-server trap link ietf
snmp-server enable traps snmp linkdown linkup
snmp-server enable traps syslog
snmp-server manager
logging history debugging
logging snmp-trap emergencies
logging snmp-trap alerts
logging snmp-trap critical
logging snmp-trap errors
logging snmp-trap warnings
logging snmp-trap notifications
logging snmp-trap informational
logging snmp-trap debugging
interface gig0/0
    no switchport
    no shutdown
    mtu 1520
    duplex auto
    speed auto
interface gig1/1
    no switchport
    no shutdown
    mtu 1520
    duplex auto
    speed auto
interface gig0/2
 description "TO_DSW2_2"
 no shutdown
 duplex full
 switchport trunk encapsulation dot1q
 switchport mode trunk
 switchport trunk allowed vlan 1,10,20,200
interface gig0/3
 description "TO_ASW3"
 no shutdown
 duplex full
 switchport trunk encapsulation dot1q
 switchport mode trunk
 switchport trunk allowed vlan 1,10,20,200
interface gig0/1
 description "TO_DSW2_1"
 no shutdown
 duplex full
 switchport trunk encapsulation dot1q
 switchport mode trunk
 switchport trunk allowed vlan 1,10,20,200
interface gig1/0
 description "TO_ASW4"
 no shutdown
 duplex full
 switchport trunk encapsulation dot1q
 switchport mode trunk
 switchport trunk allowed vlan 1,10,20,200
==============================

<switch2 output omitted>

==============================
rendering now device switch3
hostname SW3
aaa new-model
aaa session-id unique
aaa authentication login default local
aaa authorization exec default local none
vtp mode transparent
vlan 10,20,30,40,50,60,70,80,90,100,200
int gig0/0
 no switchport
 no shut
 ip address 10.10.88.113 255.255.255.0
snmp-server community public RW
snmp-server trap link ietf
snmp-server enable traps snmp linkdown linkup
snmp-server enable traps syslog
snmp-server manager
logging history debugging
logging snmp-trap emergencies
logging snmp-trap alerts
logging snmp-trap critical
logging snmp-trap errors
logging snmp-trap warnings
logging snmp-trap notifications
logging snmp-trap informational
logging snmp-trap debugging
interface gig0/0
    no switchport
    no shutdown
    mtu 1520
    duplex auto
    speed auto
interface gig0/2
 description "TO_DSW2"
 no shutdown
 duplex full
 switchport trunk encapsulation dot1q
 switchport mode trunk
 switchport trunk allowed vlan 1,10,20,200
interface gig1/1
 description "TO_Client2"
 no shutdown
 duplex full
 switchport mode access
 switchport access vlan 20
interface gig1/0
 description "TO_Client1"
 no shutdown
 duplex full
 switchport mode access
 switchport access vlan 10
interface gig0/1
 description "TO_DSW1"
 no shutdown
 duplex full
 switchport trunk encapsulation dot1q
 switchport mode trunk
 switchport trunk allowed vlan 1,10,20,200
==============================
<switch4 output omitted>

总结

在本章中,我们学习了 YAML 及其格式,以及如何使用文本编辑器。我们还了解了 Jinja2 及其配置。然后,我们探索了在 Jinja2 中使用循环和条件的方法。

在下一章中,我们将学习如何使用多处理并行实例化和执行 Python 代码。