てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] cisco.ios collection 0.0.3 リリース、ACL や OSPF などのモジュールなどが追加

はじめに

Ansible は、Ansible 本体の標準モジュールの他に、Collection という形式でも配布されています。

例えば、ios_* モジュールは cisco.ios collection 経由で配布されてます。

先日 cisco.ios collection 0.0.3 がリリースされ、4つモジュールが追加されました。

この記事では追加されたモジュールについて、ごく簡単ではありますがご紹介します。


追加モジュール

いずれも、Netwrok Resource Module です。

cisco.ios.ios_acl_interfaces

cisco.ios/ios_acl_interfaces.py at 0.0.3 · ansible-collections/cisco.ios · GitHub

ACL をインターフェースに適用するモジュールです。

  • Example (
- name: Merge module attributes of given access-groups
  cisco.ios.ios_acl_interfaces:
    config:
    - name: GigabitEthernet0/1
      access_groups:
      - afi: ipv4
        acls:
        - name: 110
          direction: in
        - name: 123
          direction: out
      - afi: ipv6
        acls:
        - name: test_v6
          direction: out
        - name: temp_v6
          direction: in
    - name: GigabitEthernet0/2
      access_groups:
      - afi: ipv4
        acls:
        - name: 100
          direction: in
    state: merged

cisco.ios.ios_acls

cisco.ios/ios_acls.py at 0.0.3 · ansible-collections/cisco.ios · GitHub

ACL を定義するモジュールです。コマンドのオプションが多いのでモジュールのオプションも多いですね。

標準 ACL、拡張ACL に対応しているようです。

  • Example (
- name: Merge provided configuration with device configuration
  cisco.ios.ios_acls:
    config:
    - afi: ipv4
      acls:
      - name: std_acl
        acl_type: standard
        aces:
        - grant: deny
          source:
            address: 192.168.1.200
        - grant: deny
          source:
            address: 192.168.2.0
            wildcard_bits: 0.0.0.255
      - name: 110
        aces:
        - sequence: 10
          protocol_options:
            icmp:
              traceroute: true
        - grant: deny
          protocol_options:
            tcp:
              ack: true
          source:
            host: 198.51.100.0
          destination:
            host: 198.51.110.0
            port_protocol:
              eq: telnet
      - name: test
        acl_type: extended
        aces:
        - grant: deny
          protocol_options:
            tcp:
              fin: true
          source:
            address: 192.0.2.0
            wildcard_bits: 0.0.0.255
          destination:
            address: 192.0.3.0
            wildcard_bits: 0.0.0.255
            port_protocol:
              eq: www
          option:
            traceroute: true
          ttl:
            eq: 10
      - name: 123
        aces:
        - grant: deny
          protocol_options:
            tcp:
              ack: true
          source:
            address: 198.51.100.0
            wildcard_bits: 0.0.0.255
          destination:
            address: 198.51.101.0
            wildcard_bits: 0.0.0.255
            port_protocol:
              eq: telnet
          tos:
            service_value: 12
        - grant: deny
          protocol_options:
            tcp:
              ack: true
          source:
            address: 192.0.3.0
            wildcard_bits: 0.0.0.255
          destination:
            address: 192.0.4.0
            wildcard_bits: 0.0.0.255
            port_protocol:
              eq: www
          dscp: ef
          ttl:
            lt: 20
    - afi: ipv6
      acls:
      - name: R1_TRAFFIC
        aces:
        - grant: deny
          protocol_options:
            tcp:
              ack: true
          source:
            any: true
            port_protocol:
              eq: www
          destination:
            any: true
            port_protocol:
              eq: telnet
          dscp: af11
    state: merged

cisco.ios.ios_ospfv2

cisco.ios/ios_ospfv2.py at 0.0.3 · ansible-collections/cisco.ios · GitHub

OSPFv4 を設定するモジュールです。細かいオプションも指定できるようです。

  • Example (
- name: Merge provided OSPF V2 configuration
  cisco.ios.ios_ospfv2:
    config:
      processes:
      - process_id: 1
        max_metric:
          router_lsa: true
          on_startup:
            time: 110
        areas:
        - area_id: '5'
          capability: true
          authentication:
            enable: true
        - area_id: '10'
          authentication:
            message_digest: true
          nssa:
            default_information_originate:
              metric: 10
            translate: suppress-fa
          default_cost: 10
          filter_list:
          - name: test_prefix_in
            direction: in
          - name: test_prefix_out
            direction: out
        network:
          address: 198.51.100.0
          wildcard_bits: 0.0.0.255
          area: 5
        default_information:
          originate: true
      - process_id: 200
        vrf: blue
        domain_id:
          ip_address:
            address: 192.0.3.1
        max_metric:
          router_lsa: true
          on_startup:
            time: 100
        auto_cost:
          reference_bandwidth: 4
        areas:
        - area_id: '10'
          capability: true
        distribute_list:
          acls:
          - name: 10
            direction: out
          - name: 123
            direction: in
    state: merged

cisco.ios.ios_static_routes

cisco.ios/ios_static_routes.py at 0.0.3 · ansible-collections/cisco.ios · GitHub

スタティックルートを設定するモジュールです。

ios_static_route モジュール(末尾 s なし)の後継版です。

  • Example (
- name: Merge provided configuration with device configuration
  cisco.ios.ios_static_routes:
    config:
    - vrf: blue
      address_families:
      - afi: ipv4
        routes:
        - dest: 192.0.2.0/24
          next_hops:
          - forward_router_address: 192.0.2.1
            name: merged_blue
            tag: 50
            track: 150
    - address_families:
      - afi: ipv4
        routes:
        - dest: 198.51.100.0/24
          next_hops:
          - forward_router_address: 198.51.101.1
            name: merged_route_1
            distance_metric: 110
            tag: 40
            multicast: true
          - forward_router_address: 198.51.101.2
            name: merged_route_2
            distance_metric: 30
          - forward_router_address: 198.51.101.3
            name: merged_route_3
      - afi: ipv6
        routes:
        - dest: 2001:DB8:0:3::/64
          next_hops:
          - forward_router_address: 2001:DB8:0:3::2
            name: merged_v6
            tag: 105
    state: merged


これらモジュールを利用するには?

cisco.ios collection はAnsible 2.9.10 以上 2.11 未満で利用できるようです。

Ansible versions: >=2.9.10,<2.11

まず、 ansible-galaxy collection install cisco.ios でインストールします。

モジュール名を cisco.ios_acls のように指定して呼び出します。詳細は、公式ドキュメントをご参照ください。

Using collections — Ansible Documentation


おわりに

これまでは Ansible 本体のバージョンアップの際の changelog を見れば、この手の情報は追えたのですが、今は collection の情報をウォッチする必要があります。

気になる方は Collection もチェックすることをおすすめします。

Ansible Galaxy にログインし、対象の Collection のFollow Author ボタンを押してフォローすると、更新をメールや右上のアイコンで通知してくれます。

f:id:akira6592:20200621103006p:plain
Follow Author

[2020/06/23 追記] 1.0.0 がリリースされました。0.0.3 からモジュールは追加されていませんが、ドキュメントが整備されました。

[Ansible] 「つまずき Ansible 【Part6】Juniper Junos に何かしてみる」ふりかえり

はじめに

2020/06/20 に、YouTube Live で「[つまずき Ansible 【Part6】Juniper Junos に何かしてみる(https://www.youtube.com/watch?v=jJedVzaGDRU)」という配信をしました。 実際に作業しながらエラーと戦って進めるシリーズです。

tekunabe.connpass.com

今回は、Juniper Junos の機器への接続確認、show コマンド実行、インターフェース設定する Playbook を作りました。

環境は vLabs を利用しました。

つまずいたエラーと原因、対処をふりかえります。

動画

www.youtube.com


■ 疎通確認

ncclient が見つからないエラーが発生

以下のエラー。

(ansible) [vagrant@stumble stumble]$ ansible -i inventory.ini junos -m junos_facts
[WARNING]: Platform linux on host vsrx1 is using the discovered Python interpreter at /usr/bin/python, but future installation
of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
vsrx1 | FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "msg": "Failed to import the required Python library (ncclient) on stumble's Python /home/vagrant/envs/ansible/bin/python3. Please read module documentation and install in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter: No module named 'ncclient'"
}

原因

NETCONF 接続に必要な ncclient がインストールされていないため。

対処

以下のコマンドで nccliant をインストール。

$ pip install ncclient


■ インターフェースの description 設定スタティックルートの追加

lxml が見つからないエラーが発生

以下のエラー。

(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini junos_set.yml 

PLAY [junos] ********************************************************************************************************

TASK [set int] ******************************************************************************************************
[WARNING]: Platform linux on host vsrx1 is using the discovered Python interpreter at /usr/bin/python, but future
installation of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
fatal: [vsrx1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "msg": "lxml is not installed."}

PLAY RECAP **********************************************************************************************************
vsrx1                      : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

原因

Ansible をインストールした venv には lxml は入っているが、Playbook 実行に利用する Python 環境(今回の場合、デフォルト /usr/bin/python)には入っていなかった。

対処

PlyaPython インタープリタの指定を追加。

---
- hosts: junos
  gather_facts: false

  vars:    
    ansible_python_interpreter: ~/envs/ansible/bin/python    # 追加

network_cli が使えないエラーが発生

junos_interfaces モジュールnetwork の組み合わせで、以下のエラー。

(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini junos_set.yml  --diff

PLAY [junos] ********************************************************************************************************

TASK [set int] ******************************************************************************************************
fatal: [vsrx1]: FAILED! => {"changed": false, "msg": "Connection type 'network_cli' is not valid for 'junos_interfaces' module. Please see https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html"}

PLAY RECAP **********************************************************************************************************
vsrx1                      : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

原因

junos_interafaces モジュールは network_cli には対応しておらず、netconf のみ対応しているため。

参考: https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html:

対処

netconf を利用する。

もし、タスク個別にコネクションプラグインを指定する場合は、以下のようにタスク変数で指定する。

    - name: set int
      junos_interfaces:
        config:
          - name: ge-0/0/0
            description: funa
      vars:
        ansible_connection: netconf     # ここ
        ansible_port: 33001             # ここ

Configuration database is not open というエラーが発生

※ 配信時は放置しして終わりましたが、配信終了後に対処しました

以下のエラー。

(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini junos_set.yml  --diff

PLAY [junos] ********************************************************************************************************

TASK [set int] ******************************************************************************************************
fatal: [vsrx1]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "\n{\"msg\": \"b'\\\\nconfiguration database modified\\\\n'\", \"failed\": true, \"invocation\": {\"module_args\": {\"config\": [{\"name\": \"ge-0/0/0\", \"description\": \"funa\", \"enabled\": true, \"duplex\": null, \"hold_time\": null, \"mtu\": null, \"speed\": null}], \"state\": \"merged\"}}}\n\n{\"msg\": \"b'\\\\nConfiguration database is not open\\\\n'\", \"failed\": true, \"invocation\": {\"module_args\": {\"config\": [{\"name\": \"ge-0/0/0\", \"description\": \"funa\", \"enabled\": true, \"duplex\": null, \"hold_time\": null, \"mtu\": null, \"speed\": null}], \"state\": \"merged\"}}}\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

PLAY RECAP **********************************************************************************************************

原因

手作業で設定変更を試みた時に、未コミットの変更を残してしまったため。

[edit]
ansible@vSRX-addr-0# show | compare 
[edit interfaces ge-0/0/0]
-   description funa;
+   description funa2;

[edit]
ansible@vSRX-addr-0# exit 
The configuration has been changed but not committed
Exit with uncommitted changes? [yes,no] (yes) 

Exiting configuration mode

ansible@vSRX-addr-0>    # 未コミットの変更が残っている。

Ansible としては、安全のために設定変更を控えた。

(配信時はコンフィグ不整合を起こしたといってしまいましたが、正しくは不整合を起こさないために安全側に倒したものです)

対処

手作業で設定変更を試みたた未コミットの変更を破棄。

ansible@vSRX-addr-0> configure 
Entering configuration mode
The configuration has been changed but not committed

[edit]
ansible@vSRX-addr-0# 

[edit]
ansible@vSRX-addr-0# rollback 
load complete

[edit]
ansible@vSRX-addr-0# exit 
Exiting configuration mode

ansible@vSRX-addr-0> 


おまけ

閲覧したサイト

作った各種ファイル

インベントリ: inventory.ini

[ios]
rt01 ansible_host=192.168.1.11
rt02 ansible_host=192.168.1.12

[eos]
eos1 ansible_host=192.168.1.133

; 以下追加
[junos]
vsrx1 ansible_port=33001

変数定義ファイル: group_vars/junos.yml




Playbook: juos_show.yml

show version の実行と表示。

---
- hosts: junos
  gather_facts: false
  
  tasks:
    - name: show version
      junos_command:
        commands:
          - show version
      register: resgister_show_version
  
    - name: debug
      debug:
        msg: "{{ resgister_show_version.stdout_lines }}"

Playbook: juos_set.yml

インターフェースの description 設定。

---
- hosts: junos
  gather_facts: false

  vars:
    ansible_python_interpreter: ~/envs/ansible/bin/python   # 追加
  
  tasks:
    - name: set int
      junos_interfaces:
        config:
          - name: ge-0/0/0
            description: kingyo

全実行ログ

クリックして開く(長いです)

(ansible) [vagrant@stumble stumble]$ ansible -i inventory.ini junos -m junos_facts
[WARNING]: Platform linux on host vsrx1 is using the discovered Python interpreter at /usr/bin/python, but future installation
of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
vsrx1 | FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "msg": "Failed to import the required Python library (ncclient) on stumble's Python /home/vagrant/envs/ansible/bin/python3. Please read module documentation and install in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter: No module named 'ncclient'"
}
(ansible) [vagrant@stumble stumble]$ pip install ncclient
Collecting ncclient
  Downloading https://files.pythonhosted.org/packages/dd/7f/700ffea36c4c1c72d7581ef3dc3f40ec9756fd161816cd258cd303cd9f39/ncclient-0.6.7.tar.gz (605kB)
    100% |████████████████████████████████| 614kB 214kB/s 
Requirement already satisfied: setuptools>0.6 in /home/vagrant/envs/ansible/lib/python3.6/site-packages (from ncclient)
Requirement already satisfied: paramiko>=1.15.0 in /home/vagrant/envs/ansible/lib/python3.6/site-packages (from ncclient)
Collecting lxml>=3.3.0 (from ncclient)
(ansible) [vagrant@stumble stumble]$ ansible -i inventory.ini junos -m junos_facts
[WARNING]: default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards
[WARNING]: Platform linux on host vsrx1 is using the discovered Python interpreter at /usr/bin/python, but future installation
of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
vsrx1 | SUCCESS => {
    "ansible_facts": {
        "ansible_net_api": "netconf",
        "ansible_net_filesystems": [
            "/dev/gpt/junos",
            "tmpfs",
            "tmpfs",
            "host_corefiles",
            "host_volatile",
            "host_log",
            "host_traffic_log",
            "host_local",
            "host_aamwd",
            "host_geoip",
            "host_secinteld",
            "host_app_disk"
        ],
        "ansible_net_gather_network_resources": [],
        "ansible_net_gather_subset": [
            "hardware",
            "default",
            "interfaces"
        ],
        "ansible_net_has_2RE": false,
        "ansible_net_hostname": "vSRX-addr-0",
        "ansible_net_interfaces": {
            ".local.": {
                "admin-status": "up",
                "macaddress": "Unspecified",
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unlimited",
                "type": "Loopback"
            },
            "dsc": {
                "admin-status": "up",
                "macaddress": "Unspecified",
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unspecified",
                "type": "Software-Pseudo"
            },
            "em0": {
                "admin-status": "up",
                "macaddress": "02:00:00:00:00:04",
                "mtu": "1514",
                "oper-status": "up",
                "speed": "1000mbps",
                "type": "Ethernet"
            },
            "em1": {
                "admin-status": "up",
                "macaddress": "52:54:00:21:26:49",
                "mtu": "1514",
                "oper-status": "up",
                "speed": "1000mbps",
                "type": "Ethernet"
            },
            "em2": {
                "admin-status": "up",
                "macaddress": "52:54:00:d9:7c:01",
                "mtu": "1514",
                "oper-status": "down",
                "speed": "Unspecified",
                "type": "Ethernet"
            },
            "fti0": {
                "admin-status": "up",
                "macaddress": "Unspecified",
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unlimited",
                "type": "FTI"
            },
            "fxp0": {
                "admin-status": "up",
                "macaddress": "00:50:56:01:0c:00",
                "mtu": "1514",
                "oper-status": "up",
                "speed": "1000mbps",
                "type": "Ethernet"
            },
            "ge-0/0/0": {
                "admin-status": "up",
                "macaddress": "00:50:56:a2:d8:85",
                "mtu": "1514",
                "oper-status": "down",
                "speed": "1000mbps",
                "type": null
            },
            "ge-0/0/1": {
                "admin-status": "up",
                "macaddress": "00:50:56:a2:c6:23",
                "mtu": "1514",
                "oper-status": "down",
                "speed": "1000mbps",
                "type": null
            },
            "ge-0/0/2": {
                "admin-status": "up",
                "macaddress": "00:50:56:a2:f9:11",
                "mtu": "1514",
                "oper-status": "down",
                "speed": "1000mbps",
                "type": null
            },
            "ge-0/0/3": {
                "admin-status": "up",
                "macaddress": "00:50:56:a2:6f:db",
                "mtu": "1514",
                "oper-status": "down",
                "speed": "1000mbps",
                "type": null
            },
            "ge-0/0/4": {
                "admin-status": "up",
                "macaddress": "00:50:56:a2:04:91",
                "mtu": "1514",
                "oper-status": "down",
                "speed": "1000mbps",
                "type": null
            },
            "ge-0/0/5": {
                "admin-status": "up",
                "macaddress": "00:50:56:a2:d0:57",
                "mtu": "1514",
                "oper-status": "down",
                "speed": "1000mbps",
                "type": null
            },
            "ge-0/0/6": {
                "admin-status": "up",
                "macaddress": "00:50:56:a2:41:9d",
                "mtu": "1514",
                "oper-status": "down",
                "speed": "1000mbps",
                "type": null
            },
            "ge-0/0/7": {
                "admin-status": "up",
                "macaddress": "00:50:56:a2:da:ed",
                "mtu": "1514",
                "oper-status": "down",
                "speed": "1000mbps",
                "type": null
            },
            "gr-0/0/0": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "800mbps",
                "type": "GRE"
            },
            "gre": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unlimited",
                "type": "GRE"
            },
            "ip-0/0/0": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "800mbps",
                "type": "IPIP"
            },
            "ipip": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unlimited",
                "type": "IPIP"
            },
            "irb": {
                "admin-status": "up",
                "macaddress": "4c:96:14:c4:b1:70",
                "mtu": "1514",
                "oper-status": "up",
                "speed": "Unspecified",
                "type": "Ethernet"
            },
            "lo0": {
                "admin-status": "up",
                "macaddress": "Unspecified",
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unspecified",
                "type": "Loopback"
            },
            "lsi": {
                "admin-status": "up",
                "macaddress": "Unspecified",
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unlimited",
                "type": "Software-Pseudo"
            },
            "lsq-0/0/0": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "1504",
                "oper-status": "up",
                "speed": null,
                "type": null
            },
            "lt-0/0/0": {
                "admin-status": "up",
                "macaddress": "02:96:14:c4:b1:73",
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "800mbps",
                "type": "Logical-tunnel"
            },
            "mt-0/0/0": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "800mbps",
                "type": "Multicast-GRE"
            },
            "mtun": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unlimited",
                "type": "Multicast-GRE"
            },
            "pimd": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unlimited",
                "type": "PIMD"
            },
            "pime": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unlimited",
                "type": "PIME"
            },
            "pp0": {
                "admin-status": "up",
                "macaddress": "Unspecified",
                "mtu": "1532",
                "oper-status": "up",
                "speed": "Unspecified",
                "type": "PPPoE"
            },
            "ppd0": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "800mbps",
                "type": "PIMD"
            },
            "ppe0": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "800mbps",
                "type": "PIME"
            },
            "sp-0/0/0": {
                "admin-status": "up",
                "macaddress": "Unspecified",
                "mtu": "9192",
                "oper-status": "up",
                "speed": "800mbps",
                "type": "Adaptive-Services"
            },
            "st0": {
                "admin-status": "up",
                "macaddress": null,
                "mtu": "9192",
                "oper-status": "up",
                "speed": "Unspecified",
                "type": "Secure-Tunnel"
            },
            "tap": {
                "admin-status": "up",
                "macaddress": "Unspecified",
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unlimited",
                "type": "Software-Pseudo"
            },
            "vlan": {
                "admin-status": "up",
                "macaddress": "00:00:00:00:00:00",
                "mtu": "1518",
                "oper-status": "down",
                "speed": "1000mbps",
                "type": "VLAN"
            },
            "vtep": {
                "admin-status": "up",
                "macaddress": "Unspecified",
                "mtu": "Unlimited",
                "oper-status": "up",
                "speed": "Unlimited",
                "type": "Software-Pseudo"
            }
        },
        "ansible_net_memfree_mb": 27784,
        "ansible_net_memtotal_mb": 955320,
        "ansible_net_model": "vsrx",
        "ansible_net_modules": [
            {
                "description": "VSRX",
                "name": "Midplane",
                "part_number": "750-058562",
                "serial_number": "42227D10",
                "version": "REV 08"
            },
            {
                "name": "Pseudo CB 0"
            },
            {
                "description": "VSRX-S",
                "name": "Routing Engine 0",
                "part_number": "BUILTIN",
                "serial_number": "BUILTIN"
            },
            {
                "chassis_sub_module": null,
                "description": "FPC",
                "name": "FPC 0",
                "part_number": "611-049549",
                "serial_number": "RL3714040884",
                "version": "REV 07"
            }
        ],
        "ansible_net_python_version": "2.7.5",
        "ansible_net_routing_engines": {
            "null": {
                "cpu_background": "0",
                "cpu_background1": "0",
                "cpu_background2": "0",
                "cpu_background3": "0",
                "cpu_idle": "84",
                "cpu_idle1": "95",
                "cpu_idle2": "98",
                "cpu_idle3": "98",
                "cpu_interrupt": "2",
                "cpu_interrupt1": "0",
                "cpu_interrupt2": "0",
                "cpu_interrupt3": "0",
                "cpu_system": "14",
                "cpu_system1": "4",
                "cpu_system2": "1",
                "cpu_system3": "1",
                "cpu_temperature": "0 degrees C / 32 degrees F",
                "cpu_user": "1",
                "cpu_user1": "1",
                "cpu_user2": "1",
                "cpu_user3": "1",
                "last_reboot_reason": "0x4000:VJUNOS reboot",
                "load_average_fifteen": "0.47",
                "load_average_five": "0.57",
                "load_average_one": "0.74",
                "memory_buffer_utilization": "82",
                "memory_system_total": "933",
                "memory_system_total_used": "774",
                "memory_system_total_util": "83",
                "model": "VSRX-S",
                "serial_number": "BUILTIN",
                "slot": null,
                "start_time": "2020-06-20 09:54:25 UTC",
                "status": "OK",
                "temperature": "0 degrees C / 32 degrees F",
                "up_time": "1 hour, 15 minutes, 11 seconds"
            }
        },
        "ansible_net_serialnum": "XXX",
        "ansible_net_system": "junos",
        "ansible_net_version": "18.3R1.9",
        "ansible_network_resources": {},
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false
}
(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini junos_show.yml 

PLAY [junos] *********************************************************************************

TASK [show version] **************************************************************************
[WARNING]: Platform linux on host vsrx1 is using the discovered Python interpreter at
/usr/bin/python, but future installation of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more
information.
ok: [vsrx1]

TASK [debug] *********************************************************************************
ok: [vsrx1] => {
    "msg": [
        [
            "Hostname: vSRX-addr-0",
            "Model: vsrx",
            "Junos: 18.3R1.9",
            "JUNOS OS Kernel 64-bit  [20180816.8630ec5_builder_stable_11]",
            "JUNOS OS libs [20180816.8630ec5_builder_stable_11]",
            "JUNOS OS runtime [20180816.8630ec5_builder_stable_11]",
            "JUNOS OS time zone information [20180816.8630ec5_builder_stable_11]",
            "JUNOS OS libs compat32 [20180816.8630ec5_builder_stable_11]",
            "JUNOS OS 32-bit compatibility [20180816.8630ec5_builder_stable_11]",
            "JUNOS py extensions [20180920.185504_builder_junos_183_r1]",
            "JUNOS py base [20180920.185504_builder_junos_183_r1]",
            "JUNOS OS vmguest [20180816.8630ec5_builder_stable_11]",
            "JUNOS OS crypto [20180816.8630ec5_builder_stable_11]",
            "JUNOS network stack and utilities [20180920.185504_builder_junos_183_r1]",
            "JUNOS libs [20180920.185504_builder_junos_183_r1]",
            "JUNOS libs compat32 [20180920.185504_builder_junos_183_r1]",
            "JUNOS runtime [20180920.185504_builder_junos_183_r1]",
            "JUNOS Web Management Platform Package [20180920.185504_builder_junos_183_r1]",
            "JUNOS srx libs compat32 [20180920.185504_builder_junos_183_r1]",
            "JUNOS srx runtime [20180920.185504_builder_junos_183_r1]",
            "JUNOS srx platform support [20180920.185504_builder_junos_183_r1]",
            "JUNOS common platform support [20180920.185504_builder_junos_183_r1]",
            "JUNOS srxtvp runtime [20180920.185504_builder_junos_183_r1]",
            "JUNOS pppoe [20180920.185504_builder_junos_183_r1]",
            "JUNOS mtx network modules [20180920.185504_builder_junos_183_r1]",
            "JUNOS modules [20180920.185504_builder_junos_183_r1]",
            "JUNOS srxtvp modules [20180920.185504_builder_junos_183_r1]",
            "JUNOS srxtvp libs [20180920.185504_builder_junos_183_r1]",
            "JUNOS srx libs [20180920.185504_builder_junos_183_r1]",
            "JUNOS srx Data Plane Crypto Support [20180920.185504_builder_junos_183_r1]",
            "JUNOS daemons [20180920.185504_builder_junos_183_r1]",
            "JUNOS srx daemons [20180920.185504_builder_junos_183_r1]",
            "JUNOS SRX TVP AppQos Daemon [20180920.185504_builder_junos_183_r1]",
            "JUNOS High End AppQos Daemon [20180920.185504_builder_junos_183_r1]",
            "JUNOS Extension Toolkit [20180920.185504_builder_junos_183_r1]",
            "JUNOS Phone-home [20180920.185504_builder_junos_183_r1]",
            "JUNOS J-Insight [20180920.185504_builder_junos_183_r1]",
            "JUNOS Online Documentation [20180920.185504_builder_junos_183_r1]",
            "JUNOS jail runtime [20180816.8630ec5_builder_stable_11]",
            "JUNOS FIPS mode utilities [20180920.185504_builder_junos_183_r1]"
        ]
    ]
}

PLAY RECAP ***********************************************************************************
vsrx1                      : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ ansible-doc junos_interfaces
> JUNOS_INTERFACES    (/home/vagrant/envs/ansible/lib64/python3.6/site-packages/ansible/modules/network/junos/junos_i

        This module manages the interfaces on Juniper Junos OS network devices.

  * This module is maintained by The Ansible Network Team
OPTIONS (= is mandatory):

- config
        The provided configuration
        [Default: (null)]
        suboptions:
          description:
            description:
            - Interface description.
            type: str
          duplex:
            choices:
            - automatic
            - full-duplex
            - half-duplex
            description:
            - Interface link status. Applicable for Ethernet interfaces only, either in half
              duplex, full duplex or in automatic state which negotiates the duplex automatically.
            type: str
          enabled:
            default: true
            description:
            - Administrative state of the interface.
            - Set the value to `true' to administratively enabled the interface or `false'
              to disable it.
            type: bool
          hold_time:
            description:
            - The hold time for given interface name.
            suboptions:
              down:
                description:
                - The link down hold time in milliseconds.
                type: int
              up:
                description:
                - The link up hold time in milliseconds.
                type: int
            type: dict
          mtu:
            description:
            - MTU for a specific interface.
            - Applicable for Ethernet interfaces only.
            type: int
          name:
            description:
            - Full name of interface, e.g. ge-0/0/0.
            required: true
            type: str
          speed:
            description:
            - Interface link speed. Applicable for Ethernet interfaces only.
            type: int
        
        type: list

- state
        The state of the configuration after module completion
        (Choices: merged, replaced, overridden, deleted)[Default: merged]
        type: str


NOTES:
      * This module requires the netconf system service be enabled on the remote device
        being managed.
      * Tested against vSRX JUNOS version 18.4R1.
      * This module works with connection `netconf'. See L(the Junos OS Platform
        Options,../network/user_guide/platform_junos.html).


REQUIREMENTS:  ncclient (>=v0.6.4)

AUTHOR: Ganesh Nalawade (@ganeshrn)
        METADATA:
          status:
          - preview
          supported_by: network
        

EXAMPLES:

# Using deleted

# Before state:
# -------------
# user@junos01# show interfaces
# ge-0/0/1 {
#    description "Configured by Ansible-1";
#    speed 1g;
#    mtu 1800
# }
# ge-0/0/2 {
#    description "Configured by Ansible-2";
#    ether-options {
#        auto-negotiation;
#    }
# }

- name: "Delete given options for the interface (Note: This won't delete the interface itself if any other values are
  junos_interfaces:
    config:
      - name: ge-0/0/1
        description: 'Configured by Ansible-1'
        speed: 1g
        mtu: 1800
      - name: ge-0/0/2
        description: 'Configured by Ansible -2'
    state: deleted

# After state:
# ------------
# user@junos01# show interfaces
# ge-0/0/2 {
#    ether-options {
#        auto-negotiation;
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini junos_set.yml 

PLAY [junos] ********************************************************************************************************

TASK [set int] ******************************************************************************************************
[WARNING]: Platform linux on host vsrx1 is using the discovered Python interpreter at /usr/bin/python, but future
installation of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
fatal: [vsrx1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "msg": "lxml is not installed."}

PLAY RECAP **********************************************************************************************************
vsrx1                      : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ pip install lxml
Requirement already satisfied: lxml in /home/vagrant/envs/ansible/lib/python3.6/site-packages
You are using pip version 9.0.3, however version 20.1.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ python 
Python 3.6.8 (default, Apr  2 2020, 13:34:55) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import lxml
>>> 
>>> 
>>> exit
Use exit() or Ctrl-D (i.e. EOF) to exit
>>> 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ python python
python: can't open file 'python': [Errno 2] No such file or directory
(ansible) [vagrant@stumble stumble]$ which python
~/envs/ansible/bin/python
(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini junos_set.yml 

PLAY [junos] *****************************************************************************************************

TASK [set int] ***************************************************************************************************
changed: [vsrx1]

PLAY RECAP *******************************************************************************************************
vsrx1                      : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini junos_set.yml  --check --diff

PLAY [junos] ********************************************************************************************************

TASK [set int] ******************************************************************************************************
[edit interfaces ge-0/0/0]
-   description kingyo;
+   description funa;
changed: [vsrx1]

PLAY RECAP **********************************************************************************************************
vsrx1                      : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini junos_set.yml  --diff

PLAY [junos] ********************************************************************************************************

TASK [set int] ******************************************************************************************************
[edit interfaces ge-0/0/0]
-   description kingyo;
+   description funa;
changed: [vsrx1]

PLAY RECAP **********************************************************************************************************
vsrx1                      : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini junos_set.yml  --diff

PLAY [junos] ********************************************************************************************************

TASK [set int] ******************************************************************************************************
fatal: [vsrx1]: FAILED! => {"changed": false, "msg": "Connection type 'network_cli' is not valid for 'junos_interfaces' module. Please see https://docs.ansible.com/ansible/latest/network/user_guide/platform_junos.html"}

PLAY RECAP **********************************************************************************************************
vsrx1                      : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini junos_set.yml  --diff
^CTraceback (most recent call last):
  File "/home/vagrant/envs/ansible/bin/ansible-playbook", line 62, in <module>
    import ansible.constants as C
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/ansible/constants.py", line 174, in <module>
    config = ConfigManager()
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/ansible/config/manager.py", line 279, in __init__
    self._base_defs = self._read_config_yaml_file(defs_file or ('%s/base.yml' % os.path.dirname(__file__)))
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/ansible/config/manager.py", line 305, in _read_config_yaml_file
    return yaml_load(config_def, Loader=SafeLoader) or {}
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/__init__.py", line 114, in load
    return loader.get_single_data()
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/constructor.py", line 49, in get_single_data
    node = self.get_single_node()
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/composer.py", line 36, in get_single_node
    document = self.compose_document()
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/composer.py", line 55, in compose_document
    node = self.compose_node(None, None)
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/composer.py", line 84, in compose_node
    node = self.compose_mapping_node(anchor)
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/composer.py", line 133, in compose_mapping_node
    item_value = self.compose_node(node, item_key)
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/composer.py", line 84, in compose_node
    node = self.compose_mapping_node(anchor)
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/composer.py", line 133, in compose_mapping_node
    item_value = self.compose_node(node, item_key)
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/composer.py", line 82, in compose_node
    node = self.compose_sequence_node(anchor)
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/composer.py", line 110, in compose_sequence_node
    while not self.check_event(SequenceEndEvent):
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/parser.py", line 98, in check_event
    self.current_event = self.state()
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/parser.py", line 379, in parse_block_sequence_first_entry
    return self.parse_block_sequence_entry()
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/parser.py", line 382, in parse_block_sequence_entry
    if self.check_token(BlockEntryToken):
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/scanner.py", line 115, in check_token
    while self.need_more_tokens():
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/scanner.py", line 152, in need_more_tokens
    self.stale_possible_simple_keys()
  File "/home/vagrant/envs/ansible/lib64/python3.6/site-packages/yaml/scanner.py", line 286, in stale_possible_simple_keys
    for level in list(self.possible_simple_keys):
KeyboardInterrupt
(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini junos_set.yml  --diff

PLAY [junos] ********************************************************************************************************

TASK [set int] ******************************************************************************************************
fatal: [vsrx1]: FAILED! => {"changed": false, "module_stderr": "", "module_stdout": "\n{\"msg\": \"b'\\\\nconfiguration database modified\\\\n'\", \"failed\": true, \"invocation\": {\"module_args\": {\"config\": [{\"name\": \"ge-0/0/0\", \"description\": \"funa\", \"enabled\": true, \"duplex\": null, \"hold_time\": null, \"mtu\": null, \"speed\": null}], \"state\": \"merged\"}}}\n\n{\"msg\": \"b'\\\\nConfiguration database is not open\\\\n'\", \"failed\": true, \"invocation\": {\"module_args\": {\"config\": [{\"name\": \"ge-0/0/0\", \"description\": \"funa\", \"enabled\": true, \"duplex\": null, \"hold_time\": null, \"mtu\": null, \"speed\": null}], \"state\": \"merged\"}}}\n", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

PLAY RECAP **********************************************************************************************************
vsrx1                      : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ 

Part7 にむけて

リリースされたばかりの Ansible 2.10.0b1 で遊びます。

https://tekunabe.connpass.com/event/180268/

参加ブログ

参加ブログありがとうございます!

note.com

[Ansible] user01、user02・・のように数値を任意の桁数にゼロパディングする

はじめに

なにか連番を生成する際に、user1、user2・・user10 ではなく、user01、user02・・のように 特定の桁数でゼロパディングしたいことはないでしょうか。

Jinja2 の filter でフォーマットするのが便利です。

  • 動作確認環境
    • Ansible 2.9.9
    • Jinja2 2.11.2

サンプル1: 簡単な例(単一の値)

10進数を2桁で揃えたいときは %02d です。

- hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - debug:
        msg: "{{ 'user%02d' | format(1) }}"

実行結果

TASK [debug] **********************************************************************************
ok: [localhost] => {
    "msg": "user01"
}

サンプル2: 連番

- hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - debug:
        msg: "{{ 'user%02d' | format(item) }}"
      loop: "{{ range(1, 10 + 1 , 1)| list }}"

実行結果

TASK [debug] **********************************************************************************
ok: [localhost] => (item=1) => {
    "msg": "user01"
}
ok: [localhost] => (item=2) => {
    "msg": "user02"
}
ok: [localhost] => (item=3) => {
    "msg": "user03"
}
ok: [localhost] => (item=4) => {
    "msg": "user04"
}
ok: [localhost] => (item=5) => {
    "msg": "user05"
}
ok: [localhost] => (item=6) => {
    "msg": "user06"
}
ok: [localhost] => (item=7) => {
    "msg": "user07"
}
ok: [localhost] => (item=8) => {
    "msg": "user08"
}
ok: [localhost] => (item=9) => {
    "msg": "user09"
}
ok: [localhost] => (item=10) => {
    "msg": "user10"
}

CentOS 8.2 で dnf install python38 すると Python 3.8 がインストールできる

はじめに

Red Hat Enterprise Linux 8.2 リリースノートに、yum install python38Python 3.8 がするっとインストールできる旨の記述をみかけました。

access.redhat.com

新しいモジュール python38 が導入されました。

先日、CentOS 8.2 もリリースされたので、CentOS 8.2 で試してみます。

python38 のインストール

環境確認

[root@59e0611b5e49 /]# 
[root@59e0611b5e49 /]# cat /etc/redhat-release 
CentOS Linux release 8.2.2004 (Core) 
[root@59e0611b5e49 /]# 

インストール

[root@59e0611b5e49 /]# dnf install python38 -y
Failed to set locale, defaulting to C.UTF-8
Last metadata expiration check: 0:03:44 ago on Wed Jun 17 13:29:06 2020.
Dependencies resolved.
...(略)...

Installed:
  python38-3.8.0-6.module_el8.2.0+317+61fa6e7d.x86_64                                                                    
  python38-libs-3.8.0-6.module_el8.2.0+317+61fa6e7d.x86_64                                                               
  python38-pip-19.2.3-5.module_el8.2.0+317+61fa6e7d.noarch                                                               
  python38-pip-wheel-19.2.3-5.module_el8.2.0+317+61fa6e7d.noarch                                                         
  python38-setuptools-41.6.0-4.module_el8.2.0+317+61fa6e7d.noarch                                                        
  python38-setuptools-wheel-41.6.0-4.module_el8.2.0+317+61fa6e7d.noarch                                                  

Complete!
[root@59e0611b5e49 /]# 

確認

[root@59e0611b5e49 /]# python3
python3    python3.8  
[root@59e0611b5e49 /]# python3 --version
Python 3.8.0
[root@59e0611b5e49 /]# 
[root@59e0611b5e49 /]# python3.8 --version
Python 3.8.0
[root@59e0611b5e49 /]# 
[root@59e0611b5e49 /]# 

Python 3.8.0 です。

補足1: python3Python 3.6

従来からインストールできたパッケージ python3Python 3.6 です。

[root@59e0611b5e49 /]# dnf install python3 -y
Failed to set locale, defaulting to C.UTF-8
Last metadata expiration check: 0:07:45 ago on Wed Jun 17 13:29:06 2020.
Dependencies resolved.
...(略)...             
Installed:
  platform-python-pip-9.0.3-16.el8.noarch               python3-pip-9.0.3-16.el8.noarch                                  
  python3-setuptools-39.2.0-5.el8.noarch                python36-3.6.8-2.module_el8.1.0+245+c39af44f.x86_64   

Complete!
[root@59e0611b5e49 /]# 
[root@59e0611b5e49 /]# 
[root@59e0611b5e49 /]# python3
python3     python3.6   python3.6m  python3.8   
[root@59e0611b5e49 /]# python3
python3     python3.6   python3.6m  python3.8   
[root@59e0611b5e49 /]# python3 --version
Python 3.6.8
[root@59e0611b5e49 /]# 
[root@59e0611b5e49 /]# python3.6 --version
Python 3.6.8
[root@59e0611b5e49 /]# 

python38python3 の順番でインストールしたら、 python3 コマンドは Python 3.6 になりました。

補足2: CentOS 8.1 でも

CentOS 8.1 でも python38 インストールできました。

[Ansible] Network Resource Module で実際に実行されるコマンドを確認する方法

はじめに

Ansible のネットワークモジュールの中には、コンフィグを直接指定しないタイプのものがあります。

中でも Ansible 2.9 から追加された Network Resource Module の機能は強力です。

とはいえ、ネットワークエンジニアとしては「実際どういうコマンドが実行されたの?」が気になるのではないでしょうか。

この記事では、ios_interfaces モジュールを例に、実行コマンドを確認する方法をご紹介します。

サンプル Playbook

GigabitEthernet1GigabitEthernet2description を設定する Playbook です。

大まかな流れは、最初にチェックモードでタスクを実行し、戻り値内の commands で実行コマンドを表示して、実際に設定変更、です。

---
- hosts: rt01
  gather_facts: false

  vars:   # 設定内容を変数で定義
    config:
      - name: GigabitEthernet1
        description: desc1
        enabled: True
      - name: GigabitEthernet2
        description: desc2
        enabled: True

  tasks:
    - name: 1. check mode   # チェックモードで実行
      ios_interfaces:
        config: "{{ config }}"
      register: result
      check_mode: true      # タスク単位でチェックモードを有効
    
    - name: 2. debug execute commands   # 実行コマンドを確認
      debug:
        msg: "{{ result.commands }}"

    - name: 3. set         # 実際に設定変更
      ios_interfaces:
        config: "{{ config }}"
        state: merged

補足

このサンプルでは、実際に設定変更を行う処理の前に、投入コマンドを表示させる意図で 1. check mode のタスクをチェックモードに指定しています。本来、特にチェックモードにしなくても commands は返ってきますが、その場合は実際に設定変更したあとの事後報告となります。

チェックモード実行後にコマンドを確認して、人による処理の中止や継続の判断をはさみたい場合、2 と 3 の間に、pause モジュールを挟むという方法があります。


実行

事前のネットワーク機器側のコンフィグ(抜粋)は以下の状態とします。

interface GigabitEthernet1
 ip address 192.168.1.11 255.255.255.0
 negotiation auto
!
interface GigabitEthernet2
 description desc2
 ip address 10.0.0.1 255.255.255.0

Playbook で指定した、GigabitEthernet1 には description が設定されておらず、GigabitEthernet2 は Playbook で指定したものが設定されています。

この状態で、Playbook を実行します。

$ ansible-playbook -i inventory.ini ios_config.yml 

PLAY [rt01] ************************************************************************************

TASK [1. check mode] ***************************************************************************
changed: [rt01]

TASK [2. debug execute commands] ***************************************************************
ok: [rt01] => {
    "msg": [
        "interface GigabitEthernet1",
        "description desc1"
    ]
}

TASK [3. set] **********************************************************************************
changed: [rt01]

PLAY RECAP *************************************************************************************
rt01      : ok=3  changed=2  unreachable=0  failed=0  skipped=0  rescued=0  ignored=0   
 

2. debug execute commands のタスクで、実行コマンドが表示されたことが分かります。 GigabitEthernet2 には既に意図する description が設定されていたことを Ansible が認識したため、 GigabitEthernet2 への設定コマンドは含まれていません。

なお、事後はこうなります。無事に設定されました。

interface GigabitEthernet1
 description desc1
 ip address 192.168.1.11 255.255.255.0
!
interface GigabitEthernet2
 description desc2
 ip address 10.0.0.1 255.255.255.0

もう一度 Playbook を実行すると、実行するコマンドがないので、msg の表示は以下のようにあります。

ok: [rt01] => {
    "msg": []
}


[nornir] NETCONF で Cisco IOS XE 機器のコンフィグや状態の情報を取得する

はじめに

Python 製自動化フレームワーク nornir は、接続方法として netmikoや、NAPALM の他に、NETCONF にも対応しています。

この記事では NETCONF 経由で、Cisco IOS XE 機器の runnin-config、インターフェース、ルーティングテーブを取得するサンプルをご紹介します。

環境


■ 準備

Python スクリプト本体を作成する前の準備として、以下の2つのファイルを作成します。

config.yml    # 設定ファイル(インベントリの形式、ファイルのパスなどを定義)
inventory/
  hosts.yaml  # インベントリファイル(対象機器の情報を定義)

インベントリ

対象機器の情報を定義するインベントリファイルを作成します。

インベントリに利用できる形式は、Ansible や NetBox などがあるようですが、ここでは一番シンプルなインベントリである Simple を利用します。

  • inventory/hosts.yaml
---
ios01:
  hostname: ios-xe-mgmt-latest.cisco.com
  port: 10000       # netconf 利用時のデフォルトは 830
  username: developer
  password: dummy
  platform: ios     # netconf の場合はなんでも良い模様
  connection_options:
    netconf:
        extras:
            allow_agent: False
            hostkey_verify: False

コンフィグファイル

インベントリの形式やファイルのパスを指定します。

  • config.yaml
---
inventory:
  plugin: nornir.plugins.inventory.simple.SimpleInventory
  options:
      host_file: "inventory/hosts.yaml"  # ホストインベントリファイルのパス


■ サンプル1: running-config の取得と表示

netconf_get_config を利用して、running-confg を取得します。

コード

from nornir import InitNornir
from nornir.plugins.tasks.networking import netconf_get_config
from xml.dom import minidom   # 表示用

nr = InitNornir(config_file="config.yaml")  # 初期化
result = nr.run(task=netconf_get_config, source="running")  # source は running

result_xml = minidom.parseString(result["ios01"].result)
print(result_xml.toprettyxml())

実行結果

$ python get_confg.py 
<?xml version="1.0" ?>
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
        <native xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-native">
                <version>16.11</version>
                <!-- 略 -->
                <hostname>csr1000v-1</hostname>
                <!-- 略 -->
                <interface>
                        <GigabitEthernet>
                                <name>1</name>
                                <description>MANAGEMENT INTERFACE - DON'T TOUCH ME</description>
                                <ip>
                                        <address>
                                                <primary>
                                                        <address>10.10.20.48</address>
                                                        <mask>255.255.255.0</mask>
                                                </primary>
                                        </address>
                                </ip>
                                <mop>
                                        <enabled>false</enabled>
                                        <sysid>false</sysid>
                                </mop>
                                <negotiation xmlns="http://cisco.com/ns/yang/Cisco-IOS-XE-ethernet">
                                        <auto>true</auto>
                                </negotiation>
                        </GigabitEthernet>
                <!-- 略 -->
                </interface>
                <!-- 略 -->
</data>

$ 


■ サンプル2: インターフェース情報の取得と表示

netconf_get を利用して、インターフェース情報を取得します。

コード

from nornir import InitNornir
from nornir.plugins.tasks.networking import netconf_get  # netconf_get_config ではなく
from xml.dom import minidom   # 表示用

nr = InitNornir(config_file="config.yaml")  # 初期化
result = nr.run(task=netconf_get, path="/interfaces-state") # ポイント

result_xml = minidom.parseString(result["ios01"].result)
print(result_xml.toprettyxml())

実行結果

$ python get_if_state.py 
<?xml version="1.0" ?>
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
        <interfaces-state xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
                <interface>
                        <name>GigabitEthernet1</name>
                        <type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:ethernetCsmacd</type>
                        <admin-status>up</admin-status>
                        <oper-status>up</oper-status>
                        <last-change>2020-06-12T23:57:32.000772+00:00</last-change>
                        <if-index>1</if-index>
                        <phys-address>00:50:56:bb:e9:9c</phys-address>
                        <speed>1024000000</speed>
                        <statistics>
                                <discontinuity-time>2020-06-12T23:56:13.000936+00:00</discontinuity-time>
                                <in-octets>24546855591</in-octets>
                                <in-unicast-pkts>39304700</in-unicast-pkts>
                                <in-broadcast-pkts>0</in-broadcast-pkts>
                                <in-multicast-pkts>0</in-multicast-pkts>
                                <in-discards>0</in-discards>
                                <in-errors>0</in-errors>
                                <in-unknown-protos>0</in-unknown-protos>
                                <out-octets>172071875</out-octets>
                                <out-unicast-pkts>532219</out-unicast-pkts>
                                <out-broadcast-pkts>0</out-broadcast-pkts>
                                <out-multicast-pkts>0</out-multicast-pkts>
                                <out-discards>0</out-discards>
                                <out-errors>0</out-errors>
                        </statistics>
                </interface>
        <!-- 略 -->
        </interfaces-state>
</data>

$ 

subtree でも指定可能

path は、上記サンプルのような xpath の他にも subtree でも指定できます。

filter_type="subtree" を指定するのがポイントです。デフォルトは xpath です。

# 抜粋
query = """
  <interfaces-state xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
  </interfaces-state>
"""
result = nr.run(task=netconf_get, path=query, filter_type="subtree")


■ サンプル3: ルーティングテーブルの情報の取得と表示

サンプル2と同じく、netconf_get を利用して、ルーティングテーブルの情報を取得します。

違いは path のみです。

コード

from nornir import InitNornir
from nornir.plugins.tasks.networking import netconf_get  # netconf_get_config ではなく
from xml.dom import minidom   # 表示用

nr = InitNornir(config_file="config.yaml")  # 初期化
result = nr.run(task=netconf_get, path="/routing-state")  # サンプル2との違い

result_xml = minidom.parseString(result["ios01"].result)
print(result_xml.toprettyxml())

実行結果

$ python get_rouging.py 
<?xml version="1.0" ?>
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0">
        <routing-state xmlns="urn:ietf:params:xml:ns:yang:ietf-routing">
                <routing-instance>
                        <name>default</name>
                        <type>default-routing-instance</type>
                        <router-id>0.0.0.0</router-id>
                        <routing-protocols>
                                <routing-protocol>
                                        <type>direct</type>
                                        <name>0</name>
                                </routing-protocol>
                                <routing-protocol>
                                        <type>static</type>
                                        <name>0</name>
                                </routing-protocol>
                        </routing-protocols>
                        <ribs>
                                <rib>
                                        <name>ipv4-default</name>
                                        <address-family>ipv4</address-family>
                                        <default-rib>false</default-rib>
                                        <routes>
                                                <route>
                                                        <destination-prefix>0.0.0.0/0</destination-prefix>
                                                        <route-preference>1</route-preference>
                                                        <metric>1</metric>
                                                        <next-hop>
                                                                <outgoing-interface>GigabitEthernet1</outgoing-interface>
                                                                <next-hop-address>10.10.20.254</next-hop-address>
                                                        </next-hop>
        <!-- 略 -->
        </routing-state>
</data>

$ 

subtree 指定する場合は以下のようなかたちです。

# 抜粋
query = """
  <routing-state xmlns="urn:ietf:params:xml:ns:yang:ietf-routing">
  </routing-state>
"""
result = nr.run(task=netconf_get, path=query, filter_type="subtree")


おわりに

nornir で NETCONF 経由で情報取得するサンプルをご紹介しました。

今回はざっくりとした情報を取得しました。XML で返ってくるので、必要な情報を抽出しやすいと思います。

もちろアクセスが頻繁にならずに済むのであれば、あらかじめ path や filter で絞っても良いと思います。

参考

www.youtube.com

tekunabe.hatenablog.jp

[Ansible] 「つまずき Ansible 【Part5】Arista EOS に何かしてみる」ふりかえり

はじめに

2020/06/13 に、YouTube Live で「つまずき Ansible 【Part5】Arista EOS に何かしてみる」という配信をしました。 実際に作業しながらエラーと戦って進めるシリーズです。

tekunabe.connpass.com

今回は、Arista EOS の機器(cEOS-lab) への接続確認、show コマンド実行、スタティックルート設定する Playbook を作りました。

つまずいたエラーと原因、対処をふりかえります。

動画

www.youtube.com


■ 疎通確認

接続できないエラーが発生

以下のエラー。

(ansible) [vagrant@stumble stumble]$ ansible -i inventory.ini eos -m eos_facts
eos1 | FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "msg": "[Errno None] Unable to connect to port 22 on 192.168.1.133"
}

原因

コンテナで立てた cEOS-lab へは、-p 5022:22 でポートフォワーディングしているの対して、Ansible 側でポート指定していなかった。

対処

以下の変数で、ポートを指定。

ansible_port: 5022


■ スタティックルートの追加

mask オプションがない旨エラーが発生

以下のエラー。

fatal: [eos1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "msg": "Unsupported parameters for (eos_static_route) module: mask Supported parameters include: address, admin_distance, aggregate, auth_pass, authorize, host, next_hop, password, port, provider, ssh_keyfile, state, timeout, transport, use_ssl, username, validate_certs, vrf"}

原因

ios_static_route モジュールと同じ用に prefixmask オプションで指定したが、 eos_static_route モジュールには mask オプションがない。

対処

address オプションプレフィックス表記で指定する。

    - name: set route
      eos_static_route:
        address: 0.0.0.0/0
        next_hop: 192.168.1.1


■ eAPI 経由で show コマンド実行

接続不可のエラーが発生

以下のエラー。

 Could not connect to http://192.168.1.133:80/command-api: [Errno 111] Connection refused\

原因

ansible_connection: httpapi

を指定していたため、デフォルトでは80/TCPで接続しようとするが、環境の都合上別のポート 5080 を指定する必要があった。

さらに、httpapi の場合は、ポートの指定は ansible_port 変数ではなく、ansible_httpapi_port 変数 である。

対処

以下の変数定義を追加。

ansible_httpapi_port: 5080


おまけ

閲覧したサイト

各種ファイル

インベントリ: inventory.ini

[ios]
rt01 ansible_host=192.168.1.11
rt02 ansible_host=192.168.1.12

[eos]
eos1 ansible_host=192.168.1.133

変数定義ファイル: group_vars/eos.yml

---
ansible_network_os: eos

ansible_connection: network_cli
ansible_port: 5022
# ansible_connection: httpapi    # eAPI 利用時はこちら
# ansible_httpapi_port: 5080     # eAPI 利用時はこちら

ansible_user: ansible
ansible_password: p@ssword

# 以下、一般権限を利用する場合は以下の特権設定も必要
ansible_become: true 
ansible_become_method: enable
ansible_become_password: secret 

Playbook: eos_show.yml

---
- hosts: eos
  gather_facts: false
  
  tasks:
    - name: show version
      eos_command: 
        commands:
          - show ver
      register: result
    
    - name: debug
      debug:
        msg: "{{ result }}"

Playbook: eos_set.yml

---
- hosts: eos
  gather_facts: false

  tasks:
    - name: set route
      eos_static_route:
        address: 0.0.0.0/0
        next_hop: 192.168.1.1

    - name: save
      eos_config:
        save_when: modified

全実行ログ

クリックして開く

[vagrant@stumble stumble]$ source ~/envs/ansible/bin/activate
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ ansible -i inventory.ini eos -m eos_facts
eos1 | FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "msg": "[Errno None] Unable to connect to port 22 on 192.168.1.133"
}
(ansible) [vagrant@stumble stumble]$ ansible -i inventory.ini eos -m eos_facts
[WARNING]: default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards
eos1 | SUCCESS => {
    "ansible_facts": {
        "ansible_net_all_ipv4_addresses": [
            "0.0.0.0"
        ],
        "ansible_net_all_ipv6_addresses": [],
        "ansible_net_api": "cliconf",
        "ansible_net_filesystems": [
            "file:",
            "flash:",
            "system:"
        ],
        "ansible_net_fqdn": "ceos1",
        "ansible_net_gather_network_resources": [],
        "ansible_net_gather_subset": [
            "hardware",
            "default",
            "interfaces"
        ],
        "ansible_net_hostname": "ceos1",
        "ansible_net_interfaces": {
            "Ethernet1": {
                "bandwidth": 0,
                "description": "",
                "duplex": "duplexFull",
                "ipv4": {},
                "lineprotocol": "up",
                "macaddress": "02:42:ac:15:00:02",
                "mtu": 9214,
                "operstatus": "connected",
                "type": "bridged"
            },
            "Ethernet2": {
                "bandwidth": 0,
                "description": "",
                "duplex": "duplexFull",
                "ipv4": {
                    "address": "0.0.0.0",
                    "masklen": 0
                },
                "lineprotocol": "up",
                "macaddress": "02:42:ac:2a:8f:7d",
                "mtu": 1500,
                "operstatus": "connected",
                "type": "routed"
            },
            "Ethernet3": {
                "bandwidth": 0,
                "description": "",
                "duplex": "duplexFull",
                "ipv4": {},
                "lineprotocol": "up",
                "macaddress": "02:42:ac:17:00:02",
                "mtu": 9214,
                "operstatus": "connected",
                "type": "bridged"
            },
            "Ethernet4": {
                "bandwidth": 0,
                "description": "",
                "duplex": "duplexFull",
                "ipv4": {},
                "lineprotocol": "up",
                "macaddress": "02:42:ac:18:00:02",
                "mtu": 9214,
                "operstatus": "connected",
                "type": "bridged"
            }
        },
        "ansible_net_memfree_mb": 1234.15625,
        "ansible_net_memtotal_mb": 1991.46875,
        "ansible_net_model": "cEOSLab",
        "ansible_net_neighbors": {},
        "ansible_net_python_version": "2.7.5",
        "ansible_net_serialnum": "",
        "ansible_net_system": "eos",
        "ansible_net_version": "4.21.10M",
        "ansible_network_resources": {},
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false
}
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini eos_show.yml 

PLAY [eos] *****************************************************************************************

TASK [show version] ********************************************************************************
ok: [eos1]

TASK [debug] ***************************************************************************************
ok: [eos1] => {
    "msg": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        },
        "changed": false,
        "failed": false,
        "stdout": [
            "cEOSLab\nHardware version:    \nSerial number:       \nSystem MAC address:  0242.ac2a.8f7d\n\nSoftware image version: 4.21.10M\nArchitecture:           i386\nInternal build version: 4.21.10M-15347597.42110M\nInternal build ID:      9d960dea-a6da-424b-b373-2958c07c48c3\n\ncEOS tools version: 1.1\n\nUptime:                 0 weeks, 0 days, 3 hours and 40 minutes\nTotal memory:           2039264 kB\nFree memory:            1264168 kB"
        ],
        "stdout_lines": [
            [
                "cEOSLab",
                "Hardware version:    ",
                "Serial number:       ",
                "System MAC address:  0242.ac2a.8f7d",
                "",
                "Software image version: 4.21.10M",
                "Architecture:           i386",
                "Internal build version: 4.21.10M-15347597.42110M",
                "Internal build ID:      9d960dea-a6da-424b-b373-2958c07c48c3",
                "",
                "cEOS tools version: 1.1",
                "",
                "Uptime:                 0 weeks, 0 days, 3 hours and 40 minutes",
                "Total memory:           2039264 kB",
                "Free memory:            1264168 kB"
            ]
        ]
    }
}

PLAY RECAP *****************************************************************************************
eos1                       : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini eos_set.yml 

PLAY [eos] ***************************************************************************************************

TASK [set route] *********************************************************************************************
fatal: [eos1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "msg": "Unsupported parameters for (eos_static_route) module: mask Supported parameters include: address, admin_distance, aggregate, auth_pass, authorize, host, next_hop, password, port, provider, ssh_keyfile, state, timeout, transport, use_ssl, username, validate_certs, vrf"}

PLAY RECAP ***************************************************************************************************
eos1                       : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini eos_set.yml 

PLAY [eos] **********************************************************************************

TASK [set route] ****************************************************************************
changed: [eos1]

TASK [save] *********************************************************************************
changed: [eos1]

PLAY RECAP **********************************************************************************
eos1                       : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ ansible -i inventory.ini eos -m eos_eapi -a http=true
eos1 | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python",
        "eos_eapi_urls": {}
    },
    "changed": true,
    "commands": [
        "management api http-commands",
        "protocol http port 80",
        "no shutdown"
    ],
    "session_name": "ansible_1592047615"
}
(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini eos_show.yml 

PLAY [eos] **************************************************************************************************

TASK [show version] *****************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.module_utils.connection.ConnectionError: Could not connect to http://192.168.1.133:80/command-api: [Errno 111] Connection refused
fatal: [eos1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/home/vagrant/.ansible/tmp/ansible-local-401092_8yu3k/ansible-tmp-1592047746.7101004-4016-36587511630429/AnsiballZ_eos_command.py\", line 102, in <module>\n    _ansiballz_main()\n  File \"/home/vagrant/.ansible/tmp/ansible-local-401092_8yu3k/ansible-tmp-1592047746.7101004-4016-36587511630429/AnsiballZ_eos_command.py\", line 94, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/vagrant/.ansible/tmp/ansible-local-401092_8yu3k/ansible-tmp-1592047746.7101004-4016-36587511630429/AnsiballZ_eos_command.py\", line 40, in invoke_module\n    runpy.run_module(mod_name='ansible.modules.network.eos.eos_command', init_globals=None, run_name='__main__', alter_sys=True)\n  File \"/usr/lib64/python2.7/runpy.py\", line 176, in run_module\n    fname, loader, pkg_name)\n  File \"/usr/lib64/python2.7/runpy.py\", line 82, in _run_module_code\n    mod_name, mod_fname, mod_loader, pkg_name)\n  File \"/usr/lib64/python2.7/runpy.py\", line 72, in _run_code\n    exec code in run_globals\n  File \"/tmp/ansible_eos_command_payload_DavWF4/ansible_eos_command_payload.zip/ansible/modules/network/eos/eos_command.py\", line 248, in <module>\n  File \"/tmp/ansible_eos_command_payload_DavWF4/ansible_eos_command_payload.zip/ansible/modules/network/eos/eos_command.py\", line 219, in main\n  File \"/tmp/ansible_eos_command_payload_DavWF4/ansible_eos_command_payload.zip/ansible/module_utils/network/eos/eos.py\", line 637, in run_commands\n  File \"/tmp/ansible_eos_command_payload_DavWF4/ansible_eos_command_payload.zip/ansible/module_utils/network/eos/eos.py\", line 107, in get_connection\n  File \"/tmp/ansible_eos_command_payload_DavWF4/ansible_eos_command_payload.zip/ansible/module_utils/connection.py\", line 185, in __rpc__\nansible.module_utils.connection.ConnectionError: Could not connect to http://192.168.1.133:80/command-api: [Errno 111] Connection refused\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

PLAY RECAP **************************************************************************************************
eos1                       : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ 
(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini eos_show.yml 

PLAY [eos] **************************************************************************************************

TASK [show version] *****************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.module_utils.connection.ConnectionError: Could not connect to http://192.168.1.133:80/command-api: [Errno 111] Connection refused
fatal: [eos1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/home/vagrant/.ansible/tmp/ansible-local-40497utj2p8g/ansible-tmp-1592047800.735867-4055-74241524366622/AnsiballZ_eos_command.py\", line 102, in <module>\n    _ansiballz_main()\n  File \"/home/vagrant/.ansible/tmp/ansible-local-40497utj2p8g/ansible-tmp-1592047800.735867-4055-74241524366622/AnsiballZ_eos_command.py\", line 94, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/vagrant/.ansible/tmp/ansible-local-40497utj2p8g/ansible-tmp-1592047800.735867-4055-74241524366622/AnsiballZ_eos_command.py\", line 40, in invoke_module\n    runpy.run_module(mod_name='ansible.modules.network.eos.eos_command', init_globals=None, run_name='__main__', alter_sys=True)\n  File \"/usr/lib64/python2.7/runpy.py\", line 176, in run_module\n    fname, loader, pkg_name)\n  File \"/usr/lib64/python2.7/runpy.py\", line 82, in _run_module_code\n    mod_name, mod_fname, mod_loader, pkg_name)\n  File \"/usr/lib64/python2.7/runpy.py\", line 72, in _run_code\n    exec code in run_globals\n  File \"/tmp/ansible_eos_command_payload_EHXx7W/ansible_eos_command_payload.zip/ansible/modules/network/eos/eos_command.py\", line 248, in <module>\n  File \"/tmp/ansible_eos_command_payload_EHXx7W/ansible_eos_command_payload.zip/ansible/modules/network/eos/eos_command.py\", line 219, in main\n  File \"/tmp/ansible_eos_command_payload_EHXx7W/ansible_eos_command_payload.zip/ansible/module_utils/network/eos/eos.py\", line 637, in run_commands\n  File \"/tmp/ansible_eos_command_payload_EHXx7W/ansible_eos_command_payload.zip/ansible/module_utils/network/eos/eos.py\", line 107, in get_connection\n  File \"/tmp/ansible_eos_command_payload_EHXx7W/ansible_eos_command_payload.zip/ansible/module_utils/connection.py\", line 185, in __rpc__\nansible.module_utils.connection.ConnectionError: Could not connect to http://192.168.1.133:80/command-api: [Errno 111] Connection refused\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

PLAY RECAP **************************************************************************************************
eos1                       : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini eos_show.yml 

PLAY [eos] **************************************************************************************************

TASK [show version] *****************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.module_utils.connection.ConnectionError: Could not connect to http://192.168.1.133:80/command-api: [Errno 111] Connection refused
fatal: [eos1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/home/vagrant/.ansible/tmp/ansible-local-4088qmoqg6k4/ansible-tmp-1592047844.611491-4094-263218421081738/AnsiballZ_eos_command.py\", line 102, in <module>\n    _ansiballz_main()\n  File \"/home/vagrant/.ansible/tmp/ansible-local-4088qmoqg6k4/ansible-tmp-1592047844.611491-4094-263218421081738/AnsiballZ_eos_command.py\", line 94, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/vagrant/.ansible/tmp/ansible-local-4088qmoqg6k4/an
(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini eos_show.yml 

PLAY [eos] **************************************************************************************************

TASK [show version] *****************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.module_utils.connection.ConnectionError: Could not connect to http://192.168.1.133:80/command-api: [Errno 111] Connection refused
fatal: [eos1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/home/vagrant/.ansible/tmp/ansible-local-41274_remwl3/ansible-tmp-1592047863.403169-4133-45467802027754/AnsiballZ_eos_command.py\", line 102, in <module>\n    _ansiballz_main()\n  File \"/home/vagrant/.ansible/tmp/ansible-local-41274_remwl3/ansible-tmp-1592047863.403169-4133-45467802027754/AnsiballZ_eos_command.py\", line 94, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/vagrant/.ansible/tmp/ansible-local-41274_remwl3/ansi
(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini eos_show.yml 

PLAY [eos] **************************************************************************************************

TASK [show version] *****************************************************************************************
ok: [eos1]

TASK [debug] ************************************************************************************************
ok: [eos1] => {
    "msg": {
        "ansible_facts": {
            "discovered_interpreter_python": "/usr/bin/python"
        },
        "changed": false,
        "failed": false,
        "stdout": [
            "cEOSLab\nHardware version:    \nSerial number:       \nSystem MAC address:  0242.ac2a.8f7d\n\nSoftware image version: 4.21.10M\nArchitecture:           i386\nInternal build version: 4.21.10M-15347597.42110M\nInternal build ID:      9d960dea-a6da-424b-b373-2958c07c48c3\n\ncEOS tools version: 1.1\n\nUptime:                 0 weeks, 0 days, 3 hours and 56 minutes\nTotal memory:           2039264 kB\nFree memory:            1254684 kB"
        ],
        "stdout_lines": [
            [
                "cEOSLab",
                "Hardware version:    ",
                "Serial number:       ",
                "System MAC address:  0242.ac2a.8f7d",
                "",
                "Software image version: 4.21.10M",
                "Architecture:           i386",
                "Internal build version: 4.21.10M-15347597.42110M",
                "Internal build ID:      9d960dea-a6da-424b-b373-2958c07c48c3",
                "",
                "cEOS tools version: 1.1",
                "",
                "Uptime:                 0 weeks, 0 days, 3 hours and 56 minutes",
                "Total memory:           2039264 kB",
                "Free memory:            1254684 kB"
            ]
        ]
    }
}

PLAY RECAP **************************************************************************************************
eos1                       : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ ansible-playbook -i inventory.ini eos_show.yml 

PLAY [eos] **************************************************************************************************

TASK [show version] *****************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible.module_utils.connection.ConnectionError: incomplete token (at token 1: 'ver')
fatal: [eos1]: FAILED! => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/home/vagrant/.ansible/tmp/ansible-local-4211h2y17s33/ansible-tmp-1592047950.0063741-4217-264763540732757/AnsiballZ_eos_command.py\", line 102, in <module>\n    _ansiballz_main()\n  File \"/home/vagrant/.ansible/tmp/ansible-local-4211h2y17s33/ansible-tmp-1592047950.0063741-4217-264763540732757/AnsiballZ_eos_command.py\", line 94, in _ansiballz_main\n    invoke_module(zipped_mod, temp_path, ANSIBALLZ_PARAMS)\n  File \"/home/vagrant/.ansible/tmp/ansible-local-4211h2y17s33/ansible-tmp-1592047950.0063741-4217-264763540732757/AnsiballZ_eos_command.py\", line 40, in invoke_module\n    runpy.run_module(mod_name='ansible.modules.network.eos.eos_command', init_globals=None, run_name='__main__', alter_sys=True)\n  File \"/usr/lib64/python2.7/runpy.py\", line 176, in run_module\n    fname, loader, pkg_name)\n  File \"/usr/lib64/python2.7/runpy.py\", line 82, in _run_module_code\n    mod_name, mod_fname, mod_loader, pkg_name)\n  File \"/usr/lib64/python2.7/runpy.py\", line 72, in _run_code\n    exec code in run_globals\n  File \"/tmp/ansible_eos_command_payload_syvs3j/ansible_eos_command_payload.zip/ansible/modules/network/eos/eos_command.py\", line 248, in <module>\n  File \"/tmp/ansible_eos_command_payload_syvs3j/ansible_eos_command_payload.zip/ansible/modules/network/eos/eos_command.py\", line 219, in main\n  File \"/tmp/ansible_eos_command_payload_syvs3j/ansible_eos_command_payload.zip/ansible/module_utils/network/eos/eos.py\", line 638, in run_commands\n  File \"/tmp/ansible_eos_command_payload_syvs3j/ansible_eos_command_payload.zip/ansible/module_utils/network/eos/eos.py\", line 483, in run_commands\n  File \"/tmp/ansible_eos_command_payload_syvs3j/ansible_eos_command_payload.zip/ansible/module_utils/network/eos/eos.py\", line 451, in run_queue\n  File \"/tmp/ansible_eos_command_payload_syvs3j/ansible_eos_command_payload.zip/ansible/module_utils/connection.py\", line 185, in __rpc__\nansible.module_utils.connection.ConnectionError: incomplete token (at token 1: 'ver')\n", "module_stdout": "", "msg": "MODULE FAILURE\nSee stdout/stderr for the exact error", "rc": 1}

PLAY RECAP **************************************************************************************************
eos1                       : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

(ansible) [vagrant@stumble stumble]$ 

Part6 にむけて

もう少しネットワーク方面をやってみたいと思います。