尝试将NSO与Ansible结合起来

首先

本文是由思科的同志作为“冒险日历”的一部分投稿的。

「网络自动化」这一概念已经被提出一段时间了,为此出现了许多解决方案。在进行「网络自动化」时,需要确定具体目标,并相应地组合所需解决方案,以实现更顺畅的自动化过程。

思科提供的解决方案之一是Network Services Orchestrator (NSO)。它是用作自动化设备配置和作为选择所需设备的编排器的框架。它经常被用作工作流管理工具或用于配置网络设备的工具。

希望您也可以参考之前的这篇文章,了解概述部分。思科网络服务编排器(NSO)能够实现的功能。

Ansible 是一個由 Redhat 公司擁有的開源自動化解決方案。在冪等性政策的基礎上,它使用了許多已登錄的模塊,可以進行遠程設備的配置。

本文介绍了如何将这两种方法结合起来使用。

NSO 和 Ansible 的不同之处

有人可能会想到为什么会有人将NSO和Ansible进行比较,因为它们本质上是不同的产品。然而,事实上,很多时候人们把NSO的“Orchestrator”部分剔除出来进行比较。虽然它们有不同的目的,但从配置远程设备的角度来比较一下。

NSO可以做到的事情,Ansible办不到。

在进行网络设备配置方面,两者都可以做类似的事情。例如,如果你想要将 Cisco IOS 路由器上的接口 Ethernet 1/1 的描述从 “hello” 改为 “cisco”,在 NSO 中你可以使用 Cisco IOS NED,在 Ansible 中你可以使用 ios_config 模块。

如果想要将设置的值恢复到原来的状态,该怎么办呢?在NSO中有一个回滚功能,可以自动生成像”描述 你好”这样的命令,但在Ansible中需要以某种方式保存原始值,并准备一个用于再次更改的Playbook。这是因为NSO会保存设备的状态信息,并且具有命令的模式信息,所以才可以实现这个功能。

只需要一个选项,用中文将以下内容重新表达:

用 Ansible 能做到的事情,NSO 做不到。

Ansible的服务器设置功能非常出色,并且NSO的NED没有特别针对它,因此在使用NSO的环境中使用Ansible也具有重要意义。例如,在Redhat Linux上安装Apache软件包,可以通过使用非常简单的Playbook来实现。

使用Ansible来操作NSO

其实,Ansible也有一个名为 nso 的模块。

    • nso_config

 

    • nso_action

 

    • nso_query

 

    • nso_show

 

    nso_verify

通过使用这些,可以从Ansible控制NSO。在内部,使用了REST,并且还可以扩展NSO提供的北向接口。

在这篇文章中,我想尝试使用Ansible来对NSO进行反向操作。

使用 Ansible 通过 NSO 来进行操作

NSO是一个Orchestrator,但通常与之配套使用的NED非常重要,这也是NSO的一个重要吸引点之一。它承担管理远程设备的角色,并成为与Southbound连接的接入点。

如果使用CLI NED,则使用内置的telnet/ssh客户端。如果使用NETCONF NED,则将NSO内部的NETCONF模块用于外部使用。(因此,CLI NED的SSH客户端和NETCONF NED的SSH客户端是不同的)

在中文中的某个选项:还有一个通用NED,这个NED不是用于telnet/ssh/netconf的Southbound连接。对于telnet/ssh/netconf NED,NSO会进行一定程度的管理,但对于通用NED,需要在NED内部管理所有内容,但可以支持各种Southbound连接。例如,为了连接到Cisco ACI的APIC,需要理解APIC的REST协议,但实现为通用NED。

通用NED的实现

数据模型

服务器的设置包括需要进行”设置”的内容以及不可改变的内容。同时,在服务器运行时还可能有数据被修改的情况。

– 配置 – config
– 不变数据 – 运行数据
– 动态数据 – 实时状态(运行数据)

需要对这些进行建模。

这次我已经制作了以下内容。

配置文件用于linux.yang$ cat linux/src/yang/linux.yang
模块 linux {

命名空间 “http://com/example/linux”;
前缀 linux;

导入 tailf-ncs {
前缀 ncs;
}
导入 tailf-common {
前缀 tailf;
}
导入 linux-gen {
前缀 family;
}

容器 dns {
叶列表 name-servers {
类型 string;
}
叶列表 search {
类型 string;
}

}

叶 hostname {
类型 string;
}
列表 packages {
键名 name;
叶 name {
类型 string;
}
叶 service-state {
类型 枚举 {
枚举值 started;
枚举值 stopped;
}
}
httpd-param {
当 “../name = ‘httpd'”;
列表 vserver {
键名 name;
叶 name {
类型 string;
}
}
}
容器 samba-param {
当 “../name = ‘samba'”;

}
tailf:动作 restart {
tailf:动作点 restart;
}
}
容器 firewall {
叶列表 open-port {
类型 string;
}
}
}

操作データ用のlinux-oper.yangファイル$ cat linux/src/yang/linux-oper.yang
module linux-oper {
namespace “http://com/example/linux”;
prefix linux;

tailf-ncsをインポート {
prefix ncs;
}
tailf-commonをインポート {
prefix tailf;
}
linux-genをインポート {
prefix family;
}
ncs:devices/ncs:deviceを拡張 {
when “derived-from(./ncs:device-type/ncs:generic/ncs:ned-id,’family:linux-gen’)”;
linux-factsというコンテナー {
config false;
tailf:cdb-oper {
tailf:persistent true;
}
architectureというリーフ {
type string;
}
bios_dateというリーフ {
type string;
}
bios_versionというリーフ {
type string;
}
distributionというリーフ {
type string;
}
distribution_releaseというリーフ {
type string;
}
distribution_versionというリーフ {
type string;
}
tzというリーフ {
type string;
}
devicesというリスト {
nameをキーに持つ;
nameというリーフ {
type string;
}
}
}
}
}

在进行“list packages”时,我们会将包信息列成列表并进行设置。此外,各种不变数据都存储在操作数据中。

获取数据

获取服务器状态可以使用Ansible Facts。具体而言,可以通过使用 setup、packages_facts 和 services_facts 模块来获取所需的信息。

以下是主要需要进行的工作:
– 在NED的show方法中执行ansible命令,以json格式获取数据
– 将获取的数据映射到已创建的设备模型中。

即使在创建NED时,对于对CDB的路径操作,基本上与创建服务时一样。

用于数据获取的ansible命令。

通常使用Ansible时需要ansible.cfg和inventory文件。但是,如果使用NED,目标可能会变化,需要在执行命令前创建文件。它就像缓存文件一样,如果可能的话,不想创建它。
因此,决定通过Adhoc命令获取,并编写以下代码。
模块中包含setup,package_facts和service_facts。

ProcessBuilder pb = new ProcessBuilder(
        "ansible",
        "-i",
        host+",",
        "all",
        "-e",
        "ansible_user="+user+ " ansible_password="+password,
        "-b",
        "-m",
        module
        );
Map<String, String> env = pb.environment();
env.put("ANSIBLE_REMOTE_TMP", "/tmp");
env.put("ANSIBLE_STDOUT_CALLBACK", "json");
env.put("ANSIBLE_LOAD_CALLBACK_PLUGINS", "True");

Process p = pb.start();

如果想在Linux上实际运行,请尝试以下方法:
通过ANSIBLE_STDOUT_CALLBACK环境变量,可以将输出设置为JSON格式。要在Adhoc命令中更改它,请设置ANSIBLE_LOAD_CALLBACK_PLUGINS环境变量。

ANSIBLE_REMOTE_TMP=/tmp ANSIBLE_STDOUT_CALLBACK=json ANSIBLE_LOAD_CALLBACK_PLUGINS=True ansible -i 192.168.1.1, all  -e "ansible_user=ansible ansible_password=ansible123" -m setup -b

我使用了nanojson库来解析Json。由于可以直接使用从p.getInputStream()创建的输出,所以非常方便处理。

JsonObject ob = JsonParser.object().from(jsonOutputSb.toString());
obForHost = ob.getArray("plays")
    .getObject(0)
    .getArray("tasks")
    .getObject(0)
    .getObject("hosts").getObject(host).getObject("ansible_facts");

生成 Ansible playbook 以进行数据更改

如果在NSO中创建了一个包含“添加包”等差异(diff)的操作,将会根据这个操作创建相应的playbook。
使用ncs-make-packages命令创建模板时,将会生成与MOP相匹配的方法的代码。本次使用了生成的代码,所以NedEditOp.CREATED的结果如下所示。

AnsiblePlaybook/Task 类是为了创建本次所需的 yaml 文件而创建的实用类。

    public void create(NedWorker worker, NedEditOp op, AnsiblePlaybook ap, StringBuilder dryRun)
        throws Exception  {

        ConfObject[] kp = getKP(op);
        ConfKey key = (ConfKey)kp[0];
        ConfTag tag = (ConfTag)kp[1];

        if(tag.getTag().equals("packages")){
            // new package is added
            Task yumTask = new Task("yum");
            ap.addTask(yumTask);
            yumTask.addParam("name", key.elementAt(0).toString());
            yumTask.addParam("state", "latest");
        }

同样,我们也会对NedEditOp.DELETED/NedEditOp.VALUE_SET进行实现。

完成品 – translated as “finished product” in English.

我将尝试使用创建的通用NED。使用该NED进行NSO配置与使用其他NED时没有区别。

admin@ncs# show running-config devices device webserver
devices device webserver
 address   192.168.1.1
 port      22
 authgroup webserver
 device-type generic ned-id linux-gen-1.0
 state admin-state unlocked
admin@ncs#

尝试进行同步

通过Ansible Adhoc命令获得的内容已经存入了CDB中。

admin@ncs# devices device webserver sync-from
result true
admin@ncs#
admin@ncs# show running-config devices device webserver config services
devices device webserver
 config
  services NetworkManager-dispatcher.service
   status enabled
  !
  services NetworkManager-wait-online.service
   status enabled
  !
  services NetworkManager.service
   status enabled
  !
...
admin@ncs# show running-config devices device webserver config packages
devices device webserver
 config
  packages NetworkManager
  !
  packages NetworkManager-libnm
  !
  packages NetworkManager-team
  !
  packages NetworkManager-tui
...

我们将一个不变的值作为操作数据。

admin@ncs# show devices device webserver linux-facts
linux-facts architecture x86_64
linux-facts bios_date 12/12/2018
linux-facts bios_version 6.00
linux-facts distribution CentOS
linux-facts distribution_release Core
linux-facts distribution_version 7.7
admin@ncs#

尝试安装或删除包。

根据NSO所创建的差异(diff),我们会制作一份适合ansible使用的合适playbook。

admin@ncs# conf
Entering configuration mode terminal
admin@ncs(config)# devices device webserver config
admin@ncs(config-config)# packages httpd
admin@ncs(config-packages-httpd)# packages firewalld
admin@ncs(config-packages-firewalld)# firewall open-port [ 80 443 ]
admin@ncs(config-config)# services httpd
admin@ncs(config-services-httpd)# status enabled
admin@ncs(config-services-httpd)# exit
admin@ncs(config-config)# no packages acl
admin@ncs(config-config)# commit dry-run outformat native
native {
    device {
        name webserver
        data - name: Configuration from NSO
               hosts: 192.168.1.1
               tasks:
                 - service:
                     name: httpd
                     state: started
                     enabled: enabled
                 - yum:
                     name: acl
                     state: absent
                 - yum:
                     name: httpd
                     state: latest
                 - firewalld:
                     port: 443
                     permanent: yes
                     state: enabled
                 - firewalld:
                     port: 80
                     permanent: yes
                     state: enabled
    }
}
admin@ncs(config-config)#

由于已经安装了firewalld,所以没有创建diff。因此,上述结果中不包括它。关于NSO的好处是,忽略了Ansible幂等性的优点(笑

在后端,以下等效的功能将被执行。不幸的是,为了执行ansible-playbook命令,必须创建一个yaml文件,因此放弃了完整的”一行命令”。

$ ANSIBLE_REMOTE_TMP=/tmp ANSIBLE_STDOUT_CALLBACK=json ANSIBLE_LOAD_CALLBACK_PLUGINS=True ansible-playbook -i 192.168.1.1, -e "ansible_user=root ansible_password=cisco123" test.yaml

回滚! (Huí !)

NSO的优点是能够创建rollback的差异,并能够自动生成相应的playbook。这对于独立使用Ansible来说可能很困难。

admin@ncs(config)# rollback configuration
admin@ncs(config)# commit dry-run
cli {
    local-node {
        data  devices {
                  device webserver {
                      config {
             +            packages acl {
             +            }
             -            packages httpd {
             -            }
             -            services httpd {
             -                status enabled;
             -            }
                          firewall {
             -                open-port [ 443 80 ];
                          }
                      }
                  }
              }
    }
}
admin@ncs(config)#
admin@ncs(config)# commit dry-run outformat native
native {
    device {
        name webserver
        data - name: Configuration from NSO
               hosts: 192.168.1.1
               tasks:
                 - service:
                     name: httpd
                     enabled: disabled
                 - yum:
                     name: acl
                     state: latest
                 - yum:
                     name: httpd
                     state: absent
                 - firewalld:
                     port: 443
                     permanent: yes
                     state: disabled
                 - firewalld:
                     port: 80
                     permanent: yes
                     state: disabled
    }
}
admin@ncs(config)#

由于首次更改,已删除了acl包,因此需要重新安装。

最后

这个例子似乎将 NSO 和 Ansible 结合起来使用,得到了一个很好的结果。由于目前 Cisco 还没有发布 Ansible Generic NED,所以用户自己尝试创建也是一个有趣的想法。

从现在开始,我们不再是NSO与Ansible的对立,而是要通过NSO与Ansible相结合,一同前进吧!

免责条款

在本网站和相关评论中表达的观点是发表者个人观点,不代表思科的观点。本网站的内容仅供信息提供,不旨在获得思科或其他相关人士的认可或陈述。每个用户通过在本网站上发布、链接或以其他方式上传的所有信息内容,承担全部责任,并同意解除思科与使用本网站所产生的任何责任。

广告
将在 10 秒后关闭
bannerAds