学堂 学堂 学堂公众号手机端

网络自动化之Ansible模块的构建-1

lewis 1年前 (2024-03-24) 阅读数 5 #技术

重点介绍了如何安装Ansible,并描述了Ansible的主要构建模块,以及如何利用它们构建高级的Ansible playbook。


Ansible是一个非常受欢迎的自动化框架,它被用于自动化 IT运营较长时间。它简化了不同基础设施节点的管理,并将业务逻辑转换为定义良好的过程,以便实现该业务逻辑。Ansible是用Python编写的,它主要依靠SSH与基础设施节点通信,在节点上执行指令。它从Ansible 1.9开始支持网络设备,通过Ansible 2.9,它目前对网络设备的支持得到了广泛的发展。它可以使用SSH与网络设备交互,或通过API, 如果网络供应商在他们的设备上支持API。它还具有多种优点,包括以下几点:


简单的学习曲线:编写Ansible Playbook需要 YAML和Jinja2模板知识,它们很容易学习,而且它的描述性语言很容易理解。无代理:它不需要在远程管理设备上安装代理来控制该设备。可扩展:Ansible配备了多个模块,可以在托管节点上执行各种任务。它还支持编写自定义模块和插件来扩展Ansible的核心功能。幂等性:Ansible不会改变设备的状态,除非它需要改变设置的设置以达到所需的状态。一旦它在这个理想的状态,运行Ansible Playbooks对设备不会改变它的配置。

在这一章中,我们将介绍Ansible的主要组件,并概述Ansible支持的不同功能和选项。以下是将涉及的主要内容:

安装Ansible构建Ansible Invertory使用Ansible的变量构建Ansible Playbook使用Ansible的条件语句使用Ansible的循环语句使用Ansible Vault保护机密使用Jinja2与Ansible使用Ansible的过滤器使用Ansible标记定制Ansible的设置使用Ansible角色

本文的目的是对不同的Ansible组件有一个基本的了解,将在后面使用这些组件,与网络设备进行交互。因此,本文中的所有例子都不是针对网络设备的管理。相反,我们将专注于理解Ansible中的不同组件,以便在后面中有效地使用它们。


1.1 技术要求

下面是安装Ansible和运行我们所有的Ansible playbook的要求:

使用下列Linux虚拟机(VM)发行版之一:Ubuntu 18.04 or higherCentOS 7.0 or higher虚拟机连接Internet

设置Linux机器超出了本教程的范围。然而,设置任何操作系统版本的Linux VM最简单的方法是使用Vagrant创建和设置Ansible VM。


1.2 安装Ansible

我们安装Ansible的机器(这被称为Ansible控制机器)应该运行在任何Linux发行版上。在本节中,我们将概述如何安装 在Ubuntu Linux或CentOS机器上使用Ansible。

1.2.1 准备工作

为了安装Ansible,我们需要一个使用Ubuntu 18.04+或CentoS 7+操作系统的Linux虚拟机。 另外,这台机器需要有网络连接才能安装Ansible。

1.2.2 如何安装ansible

Ansible是用Python编写的,它的所有模块都需要Python来安装在Ansible控制机器上。我们的第一个任务是确保Python安装在Ansible控制机器上,如下面的步骤所述。

大多数Linux发行版默认安装了Python。但是,如果没有安装Python,下面是在Linux上安装它的步骤:在Ubuntu操作系统中,执行如下命令:

# Install python3
$sudo apt-get install python3
# validate python is installed
$python3 --version
Python 3.6.9
在CentOS操作系统下,执行如下命令:

# Install python
$sudo yum install pytho3
# validate python is installed
$python3 --version
Python 3.6.8
在我们确认Python已经安装之后,我们就可以开始安装Ansible了在Ubuntu操作系统中,执行如下命令:

# 我们需要使用ansible repository来安装最新版本的ansible
$ sudo apt-add-repository ppa:ansible/ansible
# 更新repo cach使用新的repo
$ sudo apt-get update
# 安装Ansible
$ sudo apt-get install ansible
在CentOS操作系统中执行如下命令:

# 我们需要使用最新的epel库来获取最新的ansible
$ sudo yum install epel-release
#安装ansible
$ sudo yum install ansible


1.2.3 如何使用ansible

安装Ansible最简单的方法是使用Linux发行版特有的包管理器。我们只需要确保我们已经启用了安装最新版本Ansible所需的存储库。在Ubuntu和CentOS中,我们需要启用额外的存储库来提供最新版本的Ansible。在CentOS中,我们需要安装并启用Enterprise Linux Repository (EPEL repo),它提供了额外的软件包,并有最新的CentOS Ansible软件包。


使用这种方法,我们将安装Ansible和运行Ansible模块所需的所有必要的系统包。在Ubuntu和CentOS中,此方法也将安装Python 2并使用Python 2运行Ansible。通过运行下面的命令,我们可以验证Ansible已经安装,并且使用了哪个版本:

$ ansible --version
ansible 2.9
config file = /etc/ansible/ansible.cfg
configured module search path =
[u'/home/vagrant/.ansible/plugins/modules',
u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /usr/bin/ansible
python version = 2.7.5 (default, Aug 7 2019, 00:51:29) [GCC 4.8.520150623 (Red Hat 4.8.5-39)]

此外,我们可以检查Ansible是否按预期工作,尝试使用ping模块连接到本地机器,如图所示:

$ ansible -m ping localhost
localhost | SUCCESS => {
"changed": false,
"ping": "pong"
}

使用这种方法,我们可以看到它有以下几个问题:

它使用Python 2作为执行环境,但我们想使用Python 3。它更新安装在系统上的Python包,这可能不是我们想要的。它没有为我们提供选择使用哪个版本的Ansible所需的粒度。使用此方法,我们将始终安装最新版本ansible,这可能不是我们需要的。


1.2.4 在Python 3环境中安装Ansible

为了在Python 3环境中安装Ansible,并对部署的版本有更多的控制,我们将使用pip Python程序来安装Ansible,如下所示:

如果Python 3不存在,请安装Python 3,如下所示:

# Ubuntu
$ sudo apt-get install python3
# CentOS
sudo yum install python3
安装 python3-pip包

# Ubuntu
$ sudo apt-get install python3-pip
# CentOS
$ sudo yum install python3-pip
安装 Ansible

# Ubuntu and CentOS
# This will install ansible for the current user ONLY
$ pip3 install ansible==2.9 --user
# We Can install ansible on the System Level
$ sudo pip3 install ansible==2.9
我们可以验证Ansible已经成功安装,如图所示

$$ ansible --version
ansible 2.9
config file = None
configured module search path =
['/home/vagrant/.ansible/plugins/modules',
'/usr/share/ansible/plugins/modules']
ansible python module location =
/home/vagrant/.local/lib/python3.6/site-packages/ansible
executable location = /home/vagrant/.local/bin/ansible
python version = 3.6.8 (default, Aug 7 2019, 17:28:10) [GCC 4.8.5
20150623 (Red Hat 4.8.5-39)]

使用这种方法安装Ansible可以确保我们使用Python 3作为我们的执行环境,并允许我们控制安装哪个版本的Ansible,如示例所示。

我们将使用这个方法作为我们的Ansible安装方法,后续的所有章节都将基于这个安装过程。

提示:在第13章,Ansible的高级技术和最佳实践中,我们将概述另一种使用Python虚拟环境安装Ansible的方法。

1.2.5 另请参阅

关于Ansible安装的更多信息,请参考以下URL内容:

​​https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html​​


1.3 构建Ansible Inventory

在安装Ansible之后,我们需要定义Ansible的Inventory,这是一个文本文件,定义了Ansible将管理的节点。在这个节中,我们将概述如何创建和组织Ansible的Inventory文件。

1.3.1 准备工作

我们需要创建一个文件夹,其中包含我们将在本章中概述的所有代码。 我们创建了一个名为ch1_ansible的文件夹,如下所示:

mkdir ch1_ansible
$ cd ch1_ansible
1.3.2 创建Inventory文件

执行以下步骤创建Inventory文件:

创建一个名为hosts的文件:

touch hosts
使用任何文本编辑器,打开文件并添加以下内容:

$ cat hosts
[cisco]
csr1 ansible_host=172.10.1.2
csr2 ansible_host=172.10.1.3
[juniper]
mx1 ansible_host=172.20.1.2
mx2 ansible_host=172.20.1.3
[core]
mx1
mx2
[edge]
csr[1:2]
[network:children]
core
edge

提示:Ansible Inventory文件可以有任何名称。但是,作为最佳实践,我们将使用名称hosts来描述Inventory中的设备。


1.3.3 定义Ansible管理主机

Ansible Inventory文件定义了将由Ansible管理的主机(在前面的例子中,这是csr1-2和mx1-2),以及如何根据不同的标准将这些设备分组到自定义的组中。组用[ ]定义。这个分组帮助我们定义变量,简化设备之间的隔离,以及Ansible如何与它们交互。我们如何根据我们的用例对设备进行分组,所以我们可以根据供应商(cisco和juniper)或功能(core和edge)对它们进行分组。


我们还可以使用子元素为组构建层次结构,这在Inventory文件中进行了概述。下图显示了主机是如何分组的,以及组层次结构是如何构建的:


1.4 使用Ansible的变量

Ansible使用Ansible变量存储它管理的节点的信息。 Ansible变量可以在多个位置声明。然而,在观察Ansible的最佳实践时,我们将概述Ansible为清单文件中声明的节点寻找变量的两个主要部分。


为了遵循这个配方,Ansible库存文件必须按照前面的配方进行定义


在Inventory文件中,我们定义了主机,并将主机分组。现在我们定义了两个目录,用于Ansible搜索组变量和主机变量:

创建两个文件夹group_vars和host_vars:

$ cd ch1_ansible
$ mkdir group_vars host_vars
在group_vars文件夹中创建ios.yml和juos.yml文件

$ touch group_vars/cisco.yml group_vars/juniper.yml
在 host_vars文件夹中创建mx1.yml和csr1.yml文件

$ touch host_vars/csr1.yml host_vars/mx1.yml
在所有文件中填充变量,如下所示:

$echo 'hostname: core-mx1' >> host_vars/mx1.yml
$echo 'hostname: core-mx2' >> host_vars/mx2.yml
$echo 'hostname: edge-csr1' >> host_vars/csr1.yml
$echo 'hostname: edge-csr2' >> host_vars/csr2.yml
$echo 'os: ios' >> group_vars/cisco.yml
$echo 'os: junos' >> group_vars/juniper.yml


我们创建了如下结构的目录和文件来托管我们的变量,如下图所示:

目录group_vars中的所有文件都包含我们在Inventory中定义的组的组变量,它们应用于这个组中的所有主机。至于目录host_vars中的文件,它们包含每个主机的变量。使用这个结构,我们可以将来自多个主机的变量分组到一个特定的组文件中,而特定于主机的变量将放在一个特定于该主机的单独文件中。

除了host_vars和group_vars, Ansible还支持使用其他技术定义变量,包括以下技术:

在playbook中使用vars关键字指定多个变量使用vars_files在一个文件中定义变量,并让Ansible在运行剧本时从这个文件中读取这些变量使用-e选项在命令行指定变量


除了我们可以指定的用户定义变量之外,Ansible还有一些默认变量,它会为它的Inventory动态构建。下表捕捉了一些最常用的变量:

inventory_hostname

Inventory中定义的主机名称(例如:csr1 and mx1)

play_hosts

playbook中所有hosts的名单

group_names

一个特定主机所属的所有组的列表(例如,对于csr1,这将是[edge,cisco,network])


1.5 构建Ansible playbook

Ansible playbook是Ansible的基本元素,它声明了我们想要在托管主机上执行什么操作(在inventory中指定)。Ansible playbook是一个yaml格式的文件,它定义了将在我们管理的设备上执行的任务列表。在本节中,我们将概述如何编写Ansible playbook以及如何定义该playbook的目标主机。

1.5.1 准备工作

为了遵循此配置方法,必须定义Ansible Inventory文件,以及根据先前配置方法创建的所有特定于组和主机的变量文件。

1.5.2 编写和使用playbook创建一个名为playbook的新文件。然后在ch1_ansible文件夹中加入以下代码行:

$ cat playbook.yml
---
- name: Initial Playbook
hosts: all
gather_facts: no
tasks:
- name: Display Hostname
debug:
msg: "Router name is {{ hostname }}"
- name: Display OS
debug:
msg: "{{ hostname }} is running {{ os }}"
如下所示运行playbook:

$ ansible-playbook -i hosts playbook.yml
1.5.3 playbook是如何工作的

Ansible playbook是由一系列play组成的,每个play都针对特定的一组主机(在inventory文件中定义)。每个play都可以有一个或多个任务在该play的主机上执行。每个任务运行一个特定的Ansible模块,该模块有一些参数。下面的截图概述了该剧本的总体结构


在前面的playbook中,我们在{{}}括号中引用我们在前面配置方法中定义的变量。Ansible从group_vars或host_vars读取这些变量,我们在本playbook中使用的模块是调试模块,它将作为一个自定义消息显示在终端输出的msg参数中指定。playbook运行如下所示:

我们在Ansible -playbook命令中使用-i选项来指向Ansible Inventory文件,我们将把它用作构建Inventory的源文件。

在本playbook中,我使用了all关键字来指定inventory中的所有主机。这是一个众所周知的组名,Ansible会为inventory中的所有主机动态构造这个组名。


1.6 使用Ansible的条件语句

Ansible的核心功能之一是有条件的任务执行。这使我们能够根据指定的条件/测试来控制在给定主机上运行哪些任务。在本节中,我们将概述如何配置条件任务执行。

1.6.1 准备工作

为了遵循这个配置方法,Ansible Inventory文件必须按照前面的配置方法进行配置。此外,我们所有主机的Ansible变量应该按照前面的配置方法定义。

1.6.2 编写和使用Ansible条件语句模板在ch1_ansible文件夹中,创建一个名为ansible_cond.yml的新playbook。在新playbook中放置如下内容,如下所示:

---
- name: Using conditionals
hosts: all
gather_facts: no
tasks:
- name: Run for Edge nodes Onlay
debug:
msg: "Router name is {{hostname}}"
when: "'edge' in group_names"

- name: Run for Only Mx1 node
debug:
msg: "{{hostname}} is running {{os}}"
when:
- inventory_hostname == 'mx1'
如下所示运行playbook

$ ansible-playbook -i hosts ansible_cond.yml


1.6.3 Ansible条件语句是如何工作的

Ansible使用when语句为任务提供有条件的执行。当语句在任务级应用,如果when语句中的条件求值为true,则为给定的主机执行任务。如果为false,则跳过此主机的任务。运行上述playbook的结果如下所示:

when语句可以接受第一个任务中看到的单个条件,也可以接受第二个任务中看到的一系列条件。如果when是一个条件列表,那么为了执行任务,所有条件都必须为真。

在第一个任务中,when语句被括在""中,因为该语句以字符串开头。但是,在第二个语句中,我们使用普通的when语句,不带"",因为when语句以变量名开头。


1.6.4 另请参阅

更多关于Ansible条件的信息,请查看下面的URL:

​​https://docs.ansible.com/ansible/latest/user_guide/playbooks_conditionals.html​​


1.7 使用Ansible循环语句

在某些情况下,我们需要在Ansible playbook中运行一个任务来循环一些数据。 Ansible的循环允许我们对一个变量(字典或列表)进行多次循环,以实现这种行为。在本节中,我们将概述如何使用Ansible的循环语句。

1.7.1 准备工作

为了遵循这个配置方法,Ansible Inventory文件必须提供并配置,如前面的配方所述。

1.7.2 编写和使用Ansible循环语句模板在ch1_ansible文件夹中,创建一个名为ansible_loops.yml的新playbook。在group_vars/cisco.yml文件内包含以下内容:

snmp_servers:
- 10.1.1.1
- 10.2.1.1
在group_vars/juniper.yml文件内包含以下内容:

users:
admin: admin123
oper: oper123
在ansible_loops.yml文件,包含以下内容

---
- name: Ansible Loop over a list
hosts: cisco
gather_facts: no
tasks:
- name: Loop over SNMP Servers
debug:
msg: "Router {{ hostname }} with snmp server {{ item }}"
loop: "{{ snmp_servers}}"

- name: Ansible loop over a Dictionary
hosts: juniper
gather_facts: no
tasks:
- name: Loop over Username and Passwords
debug:
msg: "Router {{ hostname }} with user {{ item.key }} password {{ item.value }}"
with_dict: "{{ users }}"
运行playbook如下所示:

$ ansible-playbook ansible_loops.yml -i hosts


1.7.3 Ansible循环语句是如何工作的

Ansible支持循环使用两个主要的可迭代数据结构:列表和字典。当我们需要遍历列表(snmp_servers是一个列表数据结构)时,我们使用loops关键字;当我们遍历字典时,我们使用with_dicts (users是一个字典数据结构,其中用户名是键,密码是值)。在这两种情况下,我们都使用item关键字来指定当前迭代中的值。在with_dicts的情况下,我们使用item.key来获取key值, 使用item.value来获取value值。

前面playbook运行的输出如下所示:


1.7.5 另请参阅

关于不同的Ansible循环结构的更多信息,请参考以下URL内容:

​​https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html​​



1.8 使用Ansible Vault保护机密

当我们处理需要在Ansible playbook中引用的敏感材料时,例如密码,我们不应该以纯文本的形式保存这些数据。Ansible Vault提供了一种加密数据的方法,因此在剧本运行时可以安全地解密和访问。在本节中,我们将概述如何使用Ansible Vault,以确保Ansible中的敏感信息安全。


1.8.1 如何使用Ansible Vault创建一个名为decrypt_passwd的新文件,如下所示:

$ echo 'strong_password' > decrypt_passwd
使用ansible-vault会创建一个名为secrets的新文件,如下所示:

$ ansible-vault create --vault-id=decrypt_passwd secrets
将以下变量添加到这个新的secrets文件中

ospf_password: ospf_P@ssw0rD
bgp_password: BGP_p@ssw0rd
创建一个名为ansible_vault.yml的新playbook,如图所示:

---
- name: Using Ansible Vault
hosts: all
gather_facts: no
vars_files:
- secrets
tasks:
- name: Output OSPF password
debug:
msg: "Router {{ hostname }} ospf Password {{ ospf_password }}"
when: inventory_hostname == 'csr1'

- name: Outpass BGP password
debug:
msg: "Router {{ hostname }} BGP Password {{ bgp_password }}"
when: inventory_hostname == 'mx2'
运行playbook如下所示:

$ ansible-playbook --vault-id=decrypt_passwd ansible_vault.yml -i hosts


1.8.2 Ansible Vault是如何工作的

我们使用ansible-vault命令创建一个新文件,该文件使用--vault-id指定的密钥进行加密。我们将这个密钥/密码放在另一个文件中(在我们的示例中称为decrypt_passwd),并将该文件作为参数传递给vault-id。 在这个文件中,我们可以放置任意多的变量。最后,我们使用vars_files将这个文件作为变量文件包含在剧本中。下面是这个秘密文件的内容,以防我们不解密读取它:

前面playbook运行的输出如下所示:


为了让Ansible解密这个文件,我们必须通过--vault-id选项提供解密密码(在这个例子中存储在decrypt_passwd文件中)。当我们运行ansible-playbook时,我们必须提供这个解密密码,否则ansible-playbook会失败,如下所示:

#运行Ansible playbook不带--vault-id参数
$ansible-playbook ansible_vault.yml -i hosts
ERROR! Attempting to decrypt but no vault secrets found


如果我们不想在文本文件中指定加密/解密密码,我们可以使用--ask-vault-pass和ansible-playbook命令,以便在运行playbook时输入密码,如下所示:

#运行Ansible playbook带--ask-vault-pass参数
$ansible-playbook ansible_vault.yml -i hosts --ask-vault-pass
Vault password:


1.9 使用Jinja2与Ansible

Jinja2是一个强大的Python模板引擎,由Ansible支持。它还用于生成任何基于文本的文件,如HTML、CSV或YAML。我们可以利用Jinja2和Ansible变量来为网络设备生成自定义配置文件。在本节中,我们将概述如何在Ansible中使用Jinja2模板。

1.9.1 准备工作

为了遵循这个配置方法,Ansible Inventory文件必须按照前面的配置方法进行配置。

1.9.2 编写Asnible变量和Jinja2模板在group_vars目录中创建一个名为network.yml的新文件

$ cat group_vars/network.yml
---
ntp_servers:
- 172.20.1.1
- 172.20.2.1
创建一个新的templates目录,并创建一个新的os_basic.j2文件,内容如下:

$ cat templates/ios_basic.j2
!
hostname {{ hostname}}
{% for server in ntp_servers %}
ntp {{ server}}
{% endfor %}
!
创建一个新的junos_basic.j2文件,包含以下内容:

$ cat templates/junos_basic.j2
set system host-name {{ hostname }}
{% for server in ntp_servers %}
set system ntp server {{ server }}
{% endfor %}
创建一个名为ansible_jinja2.yml的新playbook,内容如下:

---
- name: Generate Cisco config from Jinja2
hosts: localhost
gather_facts: no
tasks:
- name: Create Configs Directory
file: path=configs state=directory

- name: Generates Cisco config from Jinja2
hosts: cisco
gather_facts: no
tasks:
- name: Generate Cisco Basic Config
template:
src: "templates/ios_basic.j2"
dest: "configs/{{inventory_hostname}}.cfg"
delegate_to: localhost

- name: Generate Juniper config from Jinja2
hosts: juniper
gather_facts: no
tasks:
- name: Generate Juniper Basic Config
template:
src: "templates/junos_basic.j2"
dest: "configs/{{ inventory_hostname}}.cfg"
delegate_to: localhost

运行Ansible playbook,如下所示:

$ ansible-playbook -i hosts ansible_jinja2.yml

前面playbook运行的输出如下所示:


1.9.3 Ansible变量与Jinja2模板是如何工作的

我们创造了这个network.yml文件,以便将应用于该组下所有设备的所有变量分组。之后,我们创建了两个Jinja2文件,一个用于Cisco IOS设备,另一个用于Juniper设备。在每个jina2模板中,我们使用{{}}引用Ansible变量。我们还使用了for循环构造,{% for server in ntp_servers %},这是jina2模板引擎支持的,以便循环通过ntp_servers变量(它是一个列表)来访问列表中的每一项。

Ansible提供了一个template模块有两个参数:

src:它引用jina2模板文件。dest:指定将生成的输出文件。

在我们的例子中,我们使用{{inventory_hostname}}变量,以使清单中每个路由器的输出配置文件都是唯一的。

默认情况下,template模块在远程管理节点上创建输出文件。 但是,这在我们的例子中是不可能的,因为被管理的设备是网络节点。 因此,我们使用delegate_to: localhost选项来在Ansible控制机器上本地运行这个任务。

在playbook中的第一个play创建config目录来存储网络设备的配置文件。第二个play在Cisco设备上运行template模块,第三个平台在Juniper设备上运行template模块。

以下是Cisco设备其中一个配置文件:

[root@netdevops ch1_ansible]# cat configs/csr1.cfg
hostname edge-csr1
!
ntp 172.20.1.1
ntp 172.20.2.1
!

这是Juniper设备的配置文件:

[root@netdevops ch1_ansible]# cat configs/mx2.cfg
set system host-name core-mx2
set system ntp server 172.20.1.1
set system ntp server 172.20.2.1
1.9.4 另请参阅

关于Ansible template模块的更多信息,请参考以下URL内容:

​​https://docs.ansible.com/ansible/latest/collections/ansible/builtin/template_module.htm​​


1.10 使用Ansible过滤器

Ansible的过滤器主要来源于Jinja2过滤器,所有的Ansible过滤器都是用来转换和操作数据(Ansible的变量)。除了Jinja2过滤器,Ansible还实现了自己的过滤器来增强Jinja2过滤器,同时也允许用户定义自己的自定义过滤器。在本节中,我们将概述如何配置和使用Ansible过滤器来操作我们的输入数据。


1.10.1 编写和使用Ansible过滤器安装python3-pip和Python的netaddr库,因为我们将使用 Ansible IP过滤器,它需要Python的netaddr库。

#在ubuntu系统上
$ sudo apt-get install python3-pip
#在CentOS系统上
$ sudo yum install python3-pip
$ pip3 install netaddr
创建一个新的Ansible剧本,名字为ansible_filters.yml,如图所示:

---
- name: Ansible Filters
hosts: csr1
vars:
interfaces:
- { port: FastEthernet0/0, prefix: 10.1.1.0/24 }
- { port: FastEthernet0/1, prefix: 10.1.2.0/24 }
tasks:
- name: Generate Interface Config
blockinfile:
block: |
hostname {{ hostname | upper }}
{% for intf in interfaces %}
!
interface {{ intf.port}}
ip address {{ intf.prefix | ipv4(1) | inv4('address') }} {{ intf.prefix | ipv4('netmask') }}
!
{% endfor %}
dest: "configs/csr1_interfaces.cfg"
create: yes
delegrate_to: localhost


1.10.2 Ansible过滤器是如何工作的

首先,我们使用blockinfile模块创建一个新的配置文件 Ansible控制机器。这个模块非常类似于template模块。但是,我们可以在模块的block选项中直接编写Jinja2表达式。我们使用playbook中的vars参数定义一个名为interfaces的新变量。这个变量是一个列表数据结构,其中列表中的每一项都是一个字典数据结构。这个嵌套的数据结构指定了每个接口使用的IP前缀。

在Jinja2表达式中,我们可以看到我们使用了许多过滤器,如下所示:

{{hostname | upper}}: upper是一个jina2过滤器,它将输入字符串中的所有字母转换成大写。通过这种方式,我们将主机名变量的值传递给这个过滤器,输出将是该值的大写版本。{{intf.prefix| ipv4(1) | ipv4('address')}}:这里,我们使用 Ansible IP地址过滤器两次。ipv4(1)接受一个输入的IP地址前缀,输出该前缀中的第一个IP地址。然后我们使用另一个IP地址过滤器,ipv4('address'),为了只得到IP地址前缀的IP地址部分。 因此在本例中,我们使用10.1.1.0/24,并输出10.1.1.1作为第一个接口。{{intf.prefix | ipv4('netmask') }}: 这里,我们使用Ansible IP地址过滤器来获取IP地址前缀的netmask,所以在我们的例子中,我们得到/24子网,并将其转换为255.255.255.0。

运行这个playbook之后,csr1路由器的输出文件显示如下:

$ cat configs/csr1_interfaces.cfg
# BEGIN ANSIBLE MANAGED BLOCK
hostname EDGE-CSR1
!
interface FastEthernet0/0
ip address 10.1.1.1 255.255.255.0
!
!
interface FastEthernet1/0
ip address 10.1.2.1 255.255.255.0
!
# END ANSIBLE MANAGED BLOCK


1.11 使用Ansible标记

Ansible Tags是一个强大的工具,它允许我们在一个大的Ansible playbook中标记特定的任务,并为我们提供了基于我们指定的标签选择在给定playbook中运行哪些任务的灵活性。在本节中,我们将概述如何配置和使用Ansible Tags。

1.11.1 配置和使用Ansible标记创建一个新的Ansible playbook,名为ansible_tags.yml,如下所示:

---
- name: Using Ansible Tags
hosts: cisco
gather_facts: no
tasks:
- name: Print OSPF
debug:
msg: "Router {{ hostname }} will Run OSPF"
tags: [ospf, routing]

- name: Print BGP
debug:
msg: "Router {{ hostname }} will Run BGP"
tags:
- bgp
- routing

- name: Print NTP
debug:
msg: "Router {{ hostname }} will run NTP"
tags: ntp
运行playbook,如下所示:

$ ansible-playbook ansible_tags.yml -i hosts --tags routing
再次运行playbook,这次使用标签,如下所示:

$ ansible-playbook ansible_tags.yml -i hosts --tags bgp
$ ansible-playbook ansible_tags.yml -i hosts --tags ospf
$ ansible-playbook ansible_tags.yml -i hosts --tags routing
$ ansible-playbook ansible_tags.yml -i hosts --tags ntp


1.11.2 Ansible标记是如何工作的

我们可以使用tags去标识给定tag的任务和play,以便使用它来控制执行哪些tasks或plays。这在开发playbook时给予了我们更多的控制,以允许我们运行相同的playbook。但是,在每次运行时,我们都可以控制要部署的内容。在本节的示例playbook中,我们已经将任务标记为OSPF、BGP或NTP,并将routing标记应用于OSPF和BGP任务。这允许我们有选择地在我们的剧本中运行任务,如下所示:

没有指定标签,这将运行剧本中的所有任务而不改变行为,如下面的截图所示:

使用osp或bgpf标签,我们将只运行任何带有该标签的任务,如下所示:

使用routing标签,我们将运行所有带有该标签的任务,如下所示:


1.12 定制Ansible设置

Ansible有很多设置,可以使用一个名为Ansible .cfg的配置文件进行调整和控制。这个文件有多个选项来控制Ansible的许多方面,包括Ansible的外观以及它如何连接到被管理的设备。在本节中,我们将概述如何调整其中一些默认设置。

1.12.1 如何调整ansible默认设置创建一个新文件,名为ansible.cfg,如下所示:

[defaults]
inventory=hosts
vault_password_file=decryption_password
gathering=explicit


1.12.2 ansible设置是如何工作的

默认情况下,Ansible的设置是由/etc/ansible .cfg文件控制的。这是Ansible的默认配置文件,用来控制Ansible如何与托管节点交互。我们可以直接编辑这个文件。然而,这将影响我们在Ansible控制机器上使用的任何剧本,以及这台机器上的任何用户。一个更灵活和定制的选项是在项目目录中包含一个名为ansible.cfg的文件,它包含了所有需要从默认参数修改的选项。在前面的示例中,我们只概述了这些选项的一小部分,如下所示:

inventory:该选项修改Ansible搜索的默认目录文件(默认为/etc/ansible/hosts)。我们调整这个选项,以便让Ansible使用我们的inventory文件,并停止使用 -i操作符在每次playboook运行时指定我们的inventory文件vault_password_file:该选项设置有密文密码加密和Ansible-vault密文解密。当使用加密的变量时,,这个选项消除了使用——vault-id操作符运行Ansible playbook的需要。gather = explicit:默认情况下,Ansible会在playbook运行时运行一个setup模块来收集与托管节点相关的事实。此设置模块不兼容网络节点,因为此模块需要 托管节点上的Python解释器。通过将事实收集设置为explicit,我们禁用了这种默认行为。


1.12.3 另请参阅

有关Ansible的配置设置的更多信息,请参考以下URL内容:

​​https://docs.ansible.com/ansible/latest/reference_appendices/config.html​​


1.13 使用Ansible角色

Ansible Roles促进了代码的可重用性,并提供了一种简单的打包方法 Ansible代码,以一种简单的方式,可以共享和消费。Ansible角色是所有需要的Ansible任务、处理程序和jinj2模板的集合,它们被打包在一个特定的结构中。角色的设计应该是为了交付特定的功能/任务。在这本中,我们将概述如何创建Ansible角色,以及如何在我们的playbook中使用它。

1.13.1 创建Ansible角色在ch1_ansible文件夹中,创建一个名为roles的新文件夹,并创建一个名为basic_config的新角色,如下所示:

mkdir roles
$ cd roles
$ ansible-galaxy init basic_config
更新basic_config/vars/main.yml文件包含以下变量:

$ cat roles/basic_config/vars/main.yml
---
config_dir: basic_config
更新basic_config/tasks/main.yml文件包含以下任务:

---
- name: Create Configs Directory
file:
path: "{{ config_dir }}"
state: directory
run_once: yes

- name: Generate Cisco Basic Config
template:
src: "{{os}}.j2"
dest: "{{config_dir}}/{{inventory_hostname}}.cfg"
在basic_config/templates文件夹中,创建如下结构:

[root@netdevops ch1_ansible]# tree roles/basic_config/templates/
roles/basic_config/templates/
├── ios.j2
└── junos.j2

0 directories, 2 files


[root@netdevops ch1_ansible]# cat roles/basic_config/templates/ios.j2
hostname {{ hostname }}
!
{% for server in ntp_servers %}
ntp {{ server}}
{% endfor %}
!


[root@netdevops ch1_ansible]# cat roles/basic_config/templates/junos.j2
set system host-name {{ hostname }}
{% for server in ntp_servers %}
set system ntp server {{ server }}
{% endfor %}
创建一个新的playbook,名为pb_ansible_role.Yml,结合下面的内容来使用我们的角色:

---
- name: Build Basic Cnfig Using Roles
hosts: all
connection: local
roles:
- basic_config


1.13.2 Ansible角色是如何工作的

在本节中,我们首先在主文件夹中创建roles目录。默认情况下,当使用角色时,Ansible会按照这个顺序在以下位置查找角色:

当前工作目录中的roles文件夹/etc/ansible/roles

因此,我们在当前工作目录中创建了roles文件夹 (ch1_ansible)以托管我们将在这个文件夹中创建的所有角色。我们使用ansible-galaxy命令创建角色,该命令带有init选项和角色名 (basic_config),它将在角色文件夹中创建以下角色结构:

[root@netdevops ch1_ansible]# tree roles/
roles/
└── basic_config
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── README.md
├── tasks
│ └── main.yml
├── templates
│ ├── ios.j2
│ └── junos.j2
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml

从前面的输出可以看出,这个文件夹结构是使用ansible-galaxy命令创建的,该命令构建的角色与最佳实践角色布局保持一致。并不是所有这些文件夹都需要有我们可以使用的功能性角色,下面列出了常用的主要文件夹:

tasks文件夹:它包含main.yml文件,它列出了使用此角色时应该执行的所有任务。templates文件夹:它包含我们将作为这个角色的一部分使用的所有jina2模板。vars文件夹:它包含我们想要定义的所有变量,以及我们将在角色中使用的所有变量。在运行playbook时,对变量求值时,vars文件夹内的变量具有很高的优先级。handlers文件夹:该文件夹包含main.yml文件,其中包括应该作为该角色的一部分运行的所有处理程序。

我们创建的角色只有一个目的,那就是为我们的设备构建基本配置。为了完成这个任务,我们需要定义一些Ansible任务,并使用一些Jinja2模板来生成设备的基本配置。我们在tasks/main.yaml文件中列出所有需要运行的任务,我们在模板文件夹中包含了所有必要的jina2模板。我们在vars文件夹中定义将在我们的角色中使用的任何必要变量。


我们创建一个新的playbook,它将使用我们的新角色来生成设备的配置。我们将所有想要运行的角色作为剧本的一部分称为角色参数。在本例中,我们有一个想要运行的角色,即basic_config角色。


运行playbook之后,我们可以看到创建了一个名为basic_config的新目录,其中包含以下内容:

$ tree basic_config/
basic_config/
├── csr1.cfg
├── csr2.cfg
├── mx1.cfg
└── mx2.cfg


1.13.3 另请参阅

有关Ansible Roles的更多信息,请参考以下URL内容:

​​https://docs.ansible.com/ansible/latest/user_guide/playbooks_reuse_roles.html​​

版权声明

本文仅代表作者观点,不代表博信信息网立场。

热门