てくなべ (tekunabe)

ansible / network automation / 学習メモ

Ansible の napalm-ansible モジュール群でCisco IOS 機器の様々な情報を取得する

f:id:akira6592:20170527215846p:plain

■ はじめに

マルチベンダー対応のネットワーク機器制御ライブラリのNAPALMには、ansibleと連携するための napalm-ansible というモジュール群があります。 今回はその中の napalm_get_facts というモジュールを利用して Cisco IOS 機器の様々な情報を取得してみます。

[githubリポジトリ]

GitHub - napalm-automation/napalm-ansibleGitHub - napalm-automation/napalm-ansible

[参考情報(Aristaでの例)]

AnsibleとNAPALMの連携を試してみた – ネットワークエンジニアが日々の出来事を語る

なお、標準モジュールである ios_facts を利用する場合は、以下の記事をご参照ください。

tekunabe.hatenablog.jp


■ 準備

NAPALM のインストール

あらかじめ NAPALM をインストールします。

pip install napalm

napalm-ansible モジュール群のダウンロードと配置

git clone https://github.com/napalm-automation/napalm-ansible.git

napalm-automation ディレクトリ配下の library ディレクトリを、作成予定の playbook と同じディレクトリに配置します。


■ 基本情報の取得

まず、基本的な情報を取得してみます。

Playbook

以下のようなplaybook を作成します。ansible.cfg は特に作成していません。

---
- hosts: cisco   # イベントリファイルで定義済み
  gather_facts: no
  connection: local

  tasks:
    - name: get facts from device
      napalm_get_facts:
        hostname: "{{ inventory_hostname }}"
        provider: "{{ cli }}"
      register: result

    - name: debug
      debug:
        msg: "{{ result }}"

  vars:
    cli: # 認証情報
      username: "{{ ansible_user }}"          # ログインユーザー
      password: "{{ ansible_password }}"      # ログインパスワード
      dev_os: "ios"                           # デバイスのOS

なお、napalm_get_facts モジュールには filter オプションがあり、情報取得する対象を指定することができます。 デフォルトは facts となっており、ホスト名やシリアルナンバーなど基本的な情報を取得します。facts 以外については後述します。

実行結果

実行して結果を確認してみます。

[root@localhost ~]# ansible-playbook nf.yml

PLAY [cisco] **********************************************************************

TASK [get facts from device] ******************************************************
ok: [x.x.x.x]

TASK [debug] **********************************************************************
ok: [x.x.x.x] => {
    "changed": false,
    "msg": {
        "ansible_facts": {
            "facts": {
                "fqdn": "csr1.********.com",
                "hostname": "csr1",
                "interface_list": [
                    "GigabitEthernet1",
                    "GigabitEthernet2",
                    "GigabitEthernet3",
                    "GigabitEthernet4"
                ],
                "model": "CSR1000V",
                "os_version": "CSR1000V Software (X86_64_LINUX_IOSD-UNIVERSALK9-M),                                                                        SOFTWARE (fc3)",
                "serial_number": "XXXXXXXXXXX",
                "uptime": 1080,
                "vendor": "Cisco"
            }
        },
        "changed": false
    }
}

PLAY RECAP ************************************************************************
x.x.x.x             : ok=2    changed=0    unreachable=0    failed=0

基本的な情報が取得できました。


■ facts 以外の情報取得方法について

指定できる対象

前述のように filter オプションでは情報取得する対象を指定することができます。 指定できるものは Cisco IOS の場合は以下の通りです。

  • arp_table
  • bgp_neighbors
  • config
  • environment
  • facts
  • interfaces
  • interfaces_counters
  • interfaces_ip
  • lldp_neighbors
  • lldp_neighbors_detail
  • mac_address_table
  • ntp_servers
  • ntp_stats
  • optics
  • snmp_information

指定できる値は、napalm-automation 自身のドキュメントには記述されいませんが、 NAPALM本体の「Getters support matrix」のIOS(今回の場合)の列を見ると分かります。

Supported Devices — NAPALM 1 documentation

例) get_config に対応していれば filterconfig が指定でき、以下のように記述できる。

      napalm_get_facts:
        hostname: "{{ inventory_hostname }}"
        provider: "{{ cli }}"
        filter:
          - "config"

それぞれどのように取得できるのか試します。 ※あまり設定が入っていない機器で試したので情報が少ないです。

arp_table

今回の環境では特に通信していないため空となりました。

"arp_table": []

bgp_neighbors

今回の環境ではBGPは未設定のため空となりました。

"bgp_neighbors": {}

config

running-config が取得できました。

"config": {
                "candidate": "",
                "running": "Building configuration...\n\nCurrent configuration : 39iguration change at 11:25:09 UTC Sat May 27 2017\n!\nversion 16.3\n(略)
}

environment

ハードウェア状況の情報が取得できました。

"environment": {
    "cpu": {
        "0": {
            "%usage": 0.0
        }
    },
    "fans": {
        "invalid": {
            "status": true
        }
    },
    "memory": {
        "available_ram": 1765843468,
        "used_ram": 333704364
    },
    "power": {
        "invalid": {
            "capacity": -1.0,
            "output": -1.0,
            "status": true
        }
    },
    "temperature": {
        "invalid": {
            "is_alert": false,
            "is_critical": false,
            "temperature": -1.0
        }
    }
}

facts

再掲になりますが、ホスト名やシリアルナンバーなどの基本的な情報が取得できました。

"facts": {
    "fqdn": "csr1.********.com",
    "hostname": "csr1",
    "interface_list": [
        "GigabitEthernet1",
        "GigabitEthernet2",
        "GigabitEthernet3",
        "GigabitEthernet4"
    ],
    "model": "CSR1000V",
    "os_version": "CSR1000V Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), SOFTWARE (fc3)",
    "serial_number": "XXXXXXXXXXX",
    "uptime": 1200,
    "vendor": "Cisco"
}

interfaces

インターフェース情報が取得できました。

"interfaces": {
    "GigabitEthernet1": {
        "description": "N/A",
        "is_enabled": true,
        "is_up": true,
        "last_flapped": -1.0,
        "mac_address": "2C:C2:60:XX:XX:XX",
        "speed": 1000
    },
    "GigabitEthernet2": {
        "description": "N/A",
        "is_enabled": true,
        "is_up": true,
        "last_flapped": -1.0,
        "mac_address": "2C:C2:60:XX:XX:XX",
        "speed": 1000
    },
    "GigabitEthernet3": {
        "description": "N/A",
        "is_enabled": true,
        "is_up": true,
        "last_flapped": -1.0,
        "mac_address": "2C:C2:60:XX:XX:XX",
        "speed": 1000
    },
    "GigabitEthernet4": {
        "description": "N/A",
        "is_enabled": true,
        "is_up": true,
        "last_flapped": -1.0,
        "mac_address": "2C:C2:60:XX:XX:XX",
        "speed": 1000
    }
}

interfaces_counters

インターフェースのカウンタ情報が取得できました。

"interfaces_counters": {
    "GigabitEthernet1": {
        "rx_broadcast_packets": 0,
        "rx_discards": 0,
        "rx_errors": 753,
        "rx_multicast_packets": 0,
        "rx_octets": 68538,
        "rx_unicast_packets": 980,
        "tx_broadcast_packets": -1,
        "tx_discards": 0,
        "tx_errors": 0,
        "tx_multicast_packets": -1,
        "tx_octets": 199698,
        "tx_unicast_packets": 1358
    },
    (省略)
    }
}

interfaces_ip

IPアドレスが設定されているインターフェースの情報が取得できました。

"interfaces_ip": {
    "GigabitEthernet1": {
        "ipv4": {
            "10.0.0.51": {
                "prefix_length": 24
            }
        }
    }
}

lldp_neighbors

今回の環境では未設定のため空となりました。

"lldp_neighbors": {}

lldp_neighbors_detail

今回の環境では未設定のため空となりました。

"lldp_neighbors_detail": {}

mac_address_table

今回の環境では未通信のため空となりました。

"mac_address_table": []

ntp_servers

今回の環境では未設定のため空となりました。

"ntp_servers": {}

ntp_stats

今回の環境では未設定のため空となりました。

"ntp_stats": []

optics

今回の環境ではSFP等のスロットはないため空となりました。

"optics": {}

snmp_information

今回の環境では未設定のため特に意味のない値となりました。

"snmp_information": {
    "chassis_id": "%SNMP agent not enabled",
    "community": {},
    "contact": "unknown",
    "location": "unknown"
}

■ さいごに

稼働中のネットワーク機器の情報を収集したいケースは割とよくあると思いますが、自作しなくてもここまでできますので、もし、取得したい情報がこのモジュールで取得できるのであれば、利用してみるのもよいのではないでしょうか。

なお、Ansible を利用せずに、pythonスクリプト中でなるべくパース処理の手間を省きたい場合は、TextFSMというライブラリと ntc-template というテンプレート集を利用するのが良いと思います。 github.com github.com

Ansible の ios_facts モジュールでCisco IOS 機器のシステム情報やインターフェース情報を取得する

f:id:akira6592:20170527151631p:plain

■ はじめに

Ansible には 2.2 から Cisco IOS の システム情報やインターフェース情報などを収集する ios_facts というモジュールが追加されています。 どのような結果になるのか確認するために試してみます。

[公式ドキュメント]

 ios_facts - Collect facts from remote devices running Cisco IOS — Ansible Documentation

[参考情報]

 AnsibleでCisco IOSの情報収集 - ios_facts - Qiita

なお、ところどころでモジュールのコードを確認していますが、今回は Ansible 2.3 の ios_facts.py を確認しています。

ansible/ios_facts.py at stable-2.3 · ansible/ansible · GitHub

この ios_facts という標準モジュールではなく、NAPALMと連携するモジュールを利用する場合は、以下の記事をご参照ください。

tekunabe.hatenablog.jp


■ 情報収集の対象範囲の指定について(gather_subset オプション)

gather_subset というオプションで情報収集する対象を指定できます。 デフォルトでは !config となっており、コンフィグが除外がされる形となります。

ドキュメントには、ほかのどのような指定ができるか記載されていませんが、モジュールのコードを読むと以下のオプションが指定できることが分かります。

  • all
  • config
  • hardware
  • interfaces

どの指定でどの情報が取得できるかは公式ドキュメントの「Return Values」に記載されています。

ios_facts - Collect facts from remote devices running Cisco IOS — Ansible Documentation


■ 全情報取得

今回は、gather_subset: all という指定をして全情報を取得してみます。

Playbook

以下のようなplaybook を作成します。ansible.cfg は特に作成していません。

- hosts: cisco          # インベントリファイルに定義済み
  gather_facts: no
  connection: local

  tasks:
    - name: facts
      ios_facts:
        gather_subset: all
        provider: "{{ cli }}"
      register: result
    - name: debug
      debug:
        msg: "{{ result }}"

  vars:
    cli: # 認証情報。各変数はインベントリファイルに定義済み
      host:     "{{ inventory_hostname }}"    # ホスト対象ホスト
      username: "{{ ansible_user }}"          # ログインユーザー
      password: "{{ ansible_password }}"      # ログインパスワード

実行結果

実行して結果を確認してみます。

[root@sv01 ansible]# ansible-playbook cf.yml

PLAY [cisco] **********************************************************************************************************************************

TASK [facts] **********************************************************************************************************************************
ok: [x.x.x.x]

TASK [debug] **********************************************************************************************************************************
ok: [x.x.x.x] => {
    "changed": false, 
    "msg": {
        "ansible_facts": {
"ansible_net_all_ipv4_addresses": [
    "10.0.0.51"
], 
            "ansible_net_all_ipv6_addresses": [], 
            "ansible_net_config": "Building configuration...\n\nCurrent configuration : 3940 bytes\n!\n! Last configuration change at 02:03:09 UTC Sat May 27 2017\n!\nversion 16.3\nservice timestamps debug datetime msec(略)\nend", 
"ansible_net_filesystems": [
    "bootflash:"
], 
"ansible_net_gather_subset": [
    "hardware", 
    "default", 
    "interfaces", 
    "config"
], 
            "ansible_net_hostname": "csr1", 
            "ansible_net_image": "bootflash:packages.conf", 
"ansible_net_interfaces": {
    "GigabitEthernet1": {
        "bandwidth": 1000000, 
        "description": null, 
        "duplex": "Full", 
        "ipv4": {
            "address": "10.0.0.51", 
            "masklen": 24
        }, 
        "lineprotocol": "up ", 
        "macaddress": "2cc2.60xx.xxxx", 
        "mediatype": "RJ45", 
        "mtu": 1500, 
        "operstatus": "up", 
        "type": "CSR vNIC"
    }, 
                "GigabitEthernet2": {
                    "bandwidth": 1000000, 
                    "description": null, 
                    "duplex": "Full", 
                    "ipv4": null, 
                    "lineprotocol": "up ", 
                    "macaddress": "2cc2.60xx.xxxx", 
                    "mediatype": "RJ45", 
                    "mtu": 1500, 
                    "operstatus": "up", 
                    "type": "CSR vNIC"
                }, 
                "GigabitEthernet3": {
                    "bandwidth": 1000000, 
                    "description": null, 
                    "duplex": "Full", 
                    "ipv4": null, 
                    "lineprotocol": "up ", 
                    "macaddress": "2cc2.60xx.xxxx", 
                    "mediatype": "RJ45", 
                    "mtu": 1500, 
                    "operstatus": "up", 
                    "type": "CSR vNIC"
                }, 
                "GigabitEthernet4": {
                    "bandwidth": 1000000, 
                    "description": null, 
                    "duplex": "Full", 
                    "ipv4": null, 
                    "lineprotocol": "up ", 
                    "macaddress": "2cc2.60xx.xxxx", 
                    "mediatype": "RJ45", 
                    "mtu": 1500, 
                    "operstatus": "up", 
                    "type": "CSR vNIC"
                }
            }, 
            "ansible_net_memfree_mb": 1723809, 
            "ansible_net_memtotal_mb": 2047264, 
            "ansible_net_model": null, 
"ansible_net_neighbors": {
    "null": [
        {
            "host": null, 
            "port": null
        }
    ]
}, 
            "ansible_net_serialnum": "9KXI0DXXXXX", 
            "ansible_net_version": "16.3.1"
        }, 
        "changed": false
    }
}

PLAY RECAP ************************************************************************************************************************************
x.x.x.x              : ok=2    changed=0    unreachable=0    failed=0  

■ 実行されるshowコマンド

実行結果の雰囲気からおおよそ分かりますが、どのようなshowコマンドが実行されるのか、念のためコードを確認しました。以下のコマンドが仕込まれていました。

  • dir
  • show memory statistics
  • show version
  • show running-config
  • show interfaces
  • show ipv6 interface
  • show lldp
  • show lldp neighbors detail

■ さいごに

稼働中のネットワーク機器の情報を収集したいケースは割とよくあると思いますが、自作しなくてもここまでできますので、もし、取得したい情報がこのモジュールで取得できるのであれば、利用してみるのもよいのではないでしょうか。

なお、Ansible を利用せずに、pythonスクリプト中でなるべくパース処理の手間を省きたい場合は、TextFSMというライブラリと ntc-template というテンプレート集を利用するのが良いと思います。 github.com github.com

Ansible の標準lookup pluginの「dig」で名前解決する

f:id:akira6592:20170525222437p:plain

はじめに

Ansible には標準で「dig」という名前解決ができる lookup plugin があることを知りました。

Lookups — Ansible Documentation

基本的な書式は lookup('dig', 'example.com.') です。 公式ドキュメントに実行例がなかったので試してみることにします。

準備

内部で dnspython というパッケージを利用するのであらかじめインストールしておきます。

pip install dnspython

なお、dnspython をインストールしていない状態で playbook を実行すると以下のエラーが表示されます。

fatal: [localhost]: FAILED! => {"failed": true, "msg": "An unhandled exception occurred while running the lookup plugin 'dig'. Error was a <class 'ansible.errors.AnsibleError'>, original message: Can't LOOKUP(dig): module dns.resolver is not installed"}

■ Aレコードを解決

playbook

特にタイプの指定をしない場合はAレコードとなります。

---
- hosts: localhost
  gather_facts: no

  tasks:
    - debug: msg="The IPv4 address for example.com. is {{ lookup('dig', 'example.com.')}}"

実行結果

TASK [debug] *******************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "changed": false,
    "msg": "The IPv4 address for example.com. is 93.184.216.34"
}

■ TXTレコード

playbook(抜粋)

qtype でタイプを指定できます。

    - debug: msg="The TXT record for example.org. is {{ lookup('dig', 'example.org.', 'qtype=TXT') }}"

以下の書き方も同様の動作をします。

    - debug: msg="The TXT record for example.org. is {{ lookup('dig', 'example.org./TXT') }}"

実行結果(抜粋)

TASK [debug] *******************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "changed": false,
    "msg": "The TXT record for example.org. is v=spf1 -all"
}

■ MXレコードを解決

playbook(抜粋)

以下のように with_itemswantlist=True の組み合わせで、複数の値を利用したループ処理が行えます。

    - debug: msg="One of the MX records for gmail.com. is {{ item }}"
      with_items: "{{ lookup('dig', 'gmail.com./MX', wantlist=True) }}"

実行結果(抜粋)

TASK [debug] *******************************************************************************************************************************************************************************************************************
ok: [localhost] => (item=10 alt1.gmail-smtp-in.l.google.com.) => {
    "item": "10 alt1.gmail-smtp-in.l.google.com.",
    "msg": "One of the MX records for gmail.com. is 10 alt1.gmail-smtp-in.l.google.com."
}
ok: [localhost] => (item=5 gmail-smtp-in.l.google.com.) => {
    "item": "5 gmail-smtp-in.l.google.com.",
    "msg": "One of the MX records for gmail.com. is 5 gmail-smtp-in.l.google.com."
}
ok: [localhost] => (item=40 alt4.gmail-smtp-in.l.google.com.) => {
    "item": "40 alt4.gmail-smtp-in.l.google.com.",
    "msg": "One of the MX records for gmail.com. is 40 alt4.gmail-smtp-in.l.google.com."
}
ok: [localhost] => (item=20 alt2.gmail-smtp-in.l.google.com.) => {
    "item": "20 alt2.gmail-smtp-in.l.google.com.",
    "msg": "One of the MX records for gmail.com. is 20 alt2.gmail-smtp-in.l.google.com."
}
ok: [localhost] => (item=30 alt3.gmail-smtp-in.l.google.com.) => {
    "item": "30 alt3.gmail-smtp-in.l.google.com.",
    "msg": "One of the MX records for gmail.com. is 30 alt3.gmail-smtp-in.l.google.com."
}

■ PTRレコード(逆引き)を解決

逆引きもできます。

    - debug: msg="Reverse DNS for 198.41.0.4 is {{ lookup('dig', '198.41.0.4/PTR') }}"

以下2つの書き方も同様の動作をします。

    - debug: msg="Reverse DNS for 198.41.0.4 is {{ lookup('dig', '4.0.41.198.in-addr.arpa./PTR') }}"
    - debug: msg="Reverse DNS for 198.41.0.4 is {{ lookup('dig', '4.0.41.198.in-addr.arpa.', 'qtype=PTR') }}"

実行結果(抜粋)

TASK [debug] *******************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "changed": false,
    "msg": "Reverse DNS for 198.41.0.4 is a.root-servers.net."
}

■ ネームサーバー指定でAレコードを解決

playbook(抜粋)

デフォルトではシステムで指定されているネームサーバーを利用しますが、@8.8.8.8のようにすることで個別に指定することができます。

    - debug: msg="Querying 8.8.8.8 for IPv4 address for example.com. produces {{ lookup('dig', 'example.com', '@8.8.8.8') }}"

実行結果(抜粋)

TASK [debug] *******************************************************************************************************************************************************************************************************************
ok: [localhost] => {
    "changed": false,
    "msg": "Querying 8.8.8.8 for IPv4 address for example.com. produces 93.184.216.34"
}

 

■ その他のレコード

今回試したタイプ以外にもAAAAやMX、SRVなど様々なタイプに対応しています。 詳細は公式ドキュメントを参照してください。 Lookups — Ansible Documentation

■ さいごに

正直あまり活用方法が思い浮かびませんでしたが、何かの時に思い出していただければ幸いです。

書籍「[改訂第3版]Jenkins実践入門」を購入しました

本日5/24に、Jenkins実践入門の第3版が発売されましたので購入してきました。

gihyo.jp

まだ読んでいませんが、大きな特徴は Jenkins 2系に対応していることだと思います。
特に Jenkins 2.0 から正式に搭載された Pipeline については「第10章 Pipelineの設定」という章でで20ページ余りを割いて紹介されています。なお、Scripted Pipeline ではなく、2017年2月にGAとなったDeclarative Pipeline の解説となっています。

参考
www.kaizenprogrammer.com


少々おおざっぱではありますが、前の版(2015年6月発売)との見出しレベルの差分をとりました。
比較しやすくするため、表記ゆれは調整しています。
正確な見出しは以下のページをご参照のうえ、比較してください。


【見出し差分】

追加/変更 第2版 追加/変更 第3版
変更 Windows Server 2012にインストールする 変更 Windows Server 2016にインストールする
追加 Dockerにインストールする
変更 Jenkinsをインストールせずにクラウドで使えるDEV@cloud by CloudBees 変更 Jenkins in the Cloud
変更 JDK/Ant/Mavenを自動的にインストールする 変更 JDK/Ant/Maven/Gitを自動的にインストールする
追加 Jenkins1系から2系へのアップデート
変更 SubversionからJenkinsのビルドをトリガーする 変更 GitからJenkinsビルドをトリガーする
変更 Subversionにプロジェクトデータを登録する 変更 GitHubにプロジェクトデータを登録する
変更 trunk,branches,tagsとは 変更 git-flow/GitHub Flow
追加 Column 単体テストフレームワーク
追加 Column カバレッジ測定ツール
追加 Column 静的コード解析ツール
変更 ビルドパイプラインの設定 変更 パラメータを指定してビルドする
変更 Build Pipeline Pluginのインストールと設定 変更 パラメータビルドとは
変更 ビルドパイプラインの設定 変更 パラメータビルドの設定
変更 ビルドパイプラインの実行 変更 上流ジョブのパラメータを下流ジョブに引継ぐ
追加 ビルドの手動実行
追加 ビルドの昇格
追加 Workflow Plugin
追加 Slackに通知する
追加 ChatOps/Hubot
追加 Pipelineの設定
追加 Jenkins Pipelineとは
追加 Jenkinsを設定するときの問題点
追加 Jenkins Pipelineによるコードによる設定
追加 Jenkins Pipelineの構文
追加 Pipelineを実行する
追加 Pipelineのインストールとジョブ作成
追加 Pipelineを実行する
追加 Pipelineの高度な設定をする
追加 Snippet GeneratorによるPipelineスクリプトの自動生成
追加 Pipelineの中で定義済みのジョブを呼び出す
追加 Pipelineを利用した並列処理
追加 SCMからPipelineスクリプトを読み込む
追加 Multibranch Pipelineでビルドする
追加 Shared Libraryでコードを共有する
追加 Column Blue Ocean
変更 分散ソースコード管理システムとの連携 変更 さまざまなソースコード管理システムと連携する
追加 Subversionとの連携
追加 Team Foundation Version Controlとの連携
変更 Mercurialと連携させる 変更 おすすめプラグイン「URL SCM plugin」
追加 Gitと連携させる
追加 Column おすすめプラグイン「URL SCM Plugin」
変更 マスターとスレーブとは 変更 マスターとビルドエージェントとは
変更 マスターとスレーブの4つの設定方法 変更 マスターとビルドエージェントの設定方法
変更 スレーブサーバとしてWindowsを利用する 変更 ビルドエージェントとしてWindowsを利用する
変更 スレーブサーバとしてLinuxMac OSを利用する 変更 ビルドエージェントとしてLinuxmacOSを利用する
変更 おすすめプラグイン「Jenkins MSBuild Plugin」 変更 Column おすすめプラグインMSBuild Plugin」
追加 ビルドエージェントとしてDockerコンテナを利用する
追加 Column コンテナとは? ~Docker
追加 ビルドの実行環境を制御する
追加 Column おすすめプラグイン「TextFinder Plugin」
変更 パッケージリポジトリによるパッケージ管理 変更 パッケージリポジトリと連携する
追加 Tracと連携させる
追加 Backlogと連携させる
追加 コンテナ仮想化 ~Docker
変更 安定して利用するための5つの運用管理 変更 安定して利用するための6つの運用管理
変更 Backup Pluginでバックアップする 変更 ThinBackup Pluginでバックアップする
追加 Jenkinsのエキスパートになる ~Jenkins認定試験

 

ここ最近、Jenkins の書籍は出版されていなかったので、買うのを控えていた方はこの機会に検討されてみてはいかがでしょうか。

Ansible でネットワーク機器のコマンド結果をパースしてくれるフィルタープラグイン「ansible_helpers」を試してみた

f:id:akira6592:20170521163508p:plain

■ はじめに

Ansible はCisco IOS や、Juniper JUNOSなど様々なネットワーク機器に対応するモジュールがあります。 show系のコマンドを実行して結果を取得することもできますが、取得した結果を良い感じにパースしてくれるフィルタープラグイン 「ansible_helpers」を見つけたので試してみます。

作者はnetmikoの作者でもある Kirk Byers (@ktbyers) さんです。

▼本プラグインを知るきっかけとなったツイート

 

■ 仕組み

google/textfsm というネットワーク機器のコマンドの結果をパースする python ライブラリと、 そのテンプレート集(networktocode/ntc-templates )を組み合わせているようです。

 

■ インストー

ktbyers/ansible_helpers にインストール手順が記載されていますので、その手順を参考にインストールします。

 

■ その1: show ip int brief で試す

まず、サンプルにあるように、Cisco IOSshow ip int brief コマンドを試してみます。

playbook

以下の playbook を用意します。

---
- name: parse sample
  hosts: cisco
  gather_facts: no
  connection: local


  vars:
    cli:   # 接続情報を辞書で定義(認証情報はインベントリファイルから取得)
      host:     "{{ inventory_hostname }}"    # ホスト対象ホスト
      username: "{{ ansible_user }}"          # ログインユーザー
      password: "{{ ansible_password }}"      # ログインパスワード

    platform: cisco_ios           # 対象機器プラットフォームの定義
    command: show ip int brief    # 実行コマンドの定義


  tasks:
    - ios_command:
        provider: "{{ cli }}"
        commands: "{{ command }}"
      register: output

    - name: parse output
      debug:
        msg: "{{ output | net_textfsm_parse(platform, command) }}"  # フィルター使用箇所

実行結果

以下のようにパースしてくれました。

[root@controller ~]# ansible-playbook cat_int.yml

PLAY [parse sample] ***************************

TASK [ios_command] *************************************************************
ok: [192.168.1.254]

TASK [parse output] ************************************************************
ok: [192.168.1.254] => {
    "msg": [
        {
            "intf": "GigabitEthernet1",
            "ipaddr": "10.0.0.51",
            "proto": "up",
            "status": "up"
        },
        {
            "intf": "GigabitEthernet2",
            "ipaddr": "10.0.2.1",
            "proto": "up",
            "status": "up"
        },
        {
            "intf": "GigabitEthernet3",
            "ipaddr": "unassigned",
            "proto": "up",
            "status": "up"
        },
        {
            "intf": "GigabitEthernet4",
            "ipaddr": "10.0.4.1",
            "proto": "up",
            "status": "up"
        }
    ]
}

PLAY RECAP *********************************************************************
192.168.1.254             : ok=2    changed=0    unreachable=0    failed=0

(参考) 実際のコマンド実行結果

ネットワーク機器自身でのコマンド実行結果は以下の通りです。

csr1#show ip int brief
Interface              IP-Address      OK? Method Status                Protocol
GigabitEthernet1       10.0.0.51       YES NVRAM  up                    up
GigabitEthernet2       10.0.2.1        YES NVRAM  up                    up
GigabitEthernet3       unassigned      YES NVRAM  up                    up
GigabitEthernet4       10.0.4.1        YES NVRAM  up                    up
csr1#

 

■ その2: show ip route編 で試す

続けて、例にはない show ip route も試してみます。

playbook

以下の playbook を用意します。(command の定義以外は先ほどと同じです)

---
- name: parse sample
  hosts: cisco
  gather_facts: no
  connection: local


  vars:
    cli:   # 接続情報を辞書で定義(認証情報はインベントリファイルから取得)
      host:     "{{ inventory_hostname }}"    # ホスト対象ホスト
      username: "{{ ansible_user }}"          # ログインユーザー
      password: "{{ ansible_password }}"      # ログインパスワード

    platform: cisco_ios           # 対象機器プラットフォームの定義
    command: show ip route        # 実行コマンドの定義


  tasks:
    - ios_command:
        provider: "{{ cli }}"
        commands: "{{ command }}"
      register: output

    - name: parse output
      debug:
        msg: "{{ output | net_textfsm_parse(platform, command) }}"  # フィルター使用箇所

実行結果

以下のようにパースしてくれました。

[root@controller ~]# ansible-playbook cat_route.yml

PLAY [parse sample] ***************************

TASK [ios_command] *************************************************************

ok: [192.168.1.254]

TASK [parse output] ************************************************************
ok: [192.168.1.254] => {
    "msg": [
        {
            "distance": "",
            "mask": "/24",
            "metric": "",
            "network": "10.0.2.0",
            "nexthopif": "GigabitEthernet2",
            "nexthopip": "",
            "protocol": "C",
            "type": "",
            "uptime": ""
        },
        {
            "distance": "",
            "mask": "/32",
            "metric": "",
            "network": "10.0.2.1",
            "nexthopif": "GigabitEthernet2",
            "nexthopip": "",
            "protocol": "L",
            "type": "",
            "uptime": ""
        },
        {
            "distance": "110",
            "mask": "/24",
            "metric": "2",
            "network": "10.0.3.0",
            "nexthopif": "GigabitEthernet2",
            "nexthopip": "10.0.2.2",
            "protocol": "O",
            "type": "",
            "uptime": "00:37:41"
        },
        {
            "distance": "",
            "mask": "/24",
            "metric": "",
            "network": "10.0.4.0",
            "nexthopif": "GigabitEthernet4",
            "nexthopip": "",
            "protocol": "C",
            "type": "",
            "uptime": ""
        },
        {
            "distance": "",
            "mask": "/32",
            "metric": "",
            "network": "10.0.4.1",
            "nexthopif": "GigabitEthernet4",
            "nexthopip": "",
            "protocol": "L",
            "type": "",
            "uptime": ""
        },
        {
            "distance": "110",
            "mask": "/32",
            "metric": "3",
            "network": "10.10.10.10",
            "nexthopif": "GigabitEthernet2",
            "nexthopip": "10.0.2.2",
            "protocol": "O",
            "type": "",
            "uptime": "00:37:41"
        }
    ]
}

PLAY RECAP *********************************************************************
192.168.1.254             : ok=2    changed=0    unreachable=0    failed=0

(参考) 実際のコマンド実行結果

ネットワーク機器自身でのコマンド実行結果は以下の通りです。

csr1#sh ip route
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
       a - application route
       + - replicated route, % - next hop override, p - overrides from PfR

Gateway of last resort is not set

      10.0.0.0/8 is variably subnetted, 6 subnets, 2 masks
C        10.0.2.0/24 is directly connected, GigabitEthernet2
L        10.0.2.1/32 is directly connected, GigabitEthernet2
O        10.0.3.0/24 [110/2] via 10.0.2.2, 00:33:38, GigabitEthernet2
C        10.0.4.0/24 is directly connected, GigabitEthernet4
L        10.0.4.1/32 is directly connected, GigabitEthernet4
O        10.10.10.10/32 [110/3] via 10.0.2.2, 00:33:38, GigabitEthernet2

csr1#

■ まとめ

パース処理のキモとなる Pythonライブラリである TextFSM 自身は以前からもありましたが、 Python のプログラム内ではなく、Ansible と連携して使いたいという時には、 この ansible_helpers が便利なのではないでしょうか。

また、TextFSMのテンプレート集には、 Cisco IOSだけでも 30 以上あります。パース処理は自力で作るとなかなか大変ですので、ここにあるものを参考にするのも良いのではないでしょうか。

ネットワークプログラマビリティ勉強会(#npstudy) #12 参加メモ

2017/04/21 にIIJさんで行われたネットワークプログラマビリティ勉強会 #12に行ってきました。ちょっとしたことですがメモを記録しておきます。


オープンソースコンテナネットワークプラグインContiv

感想

  • GUIがついていることに驚きました。

ネットワークエンジニアがニューラルネットワークやってみた

メモ

感想

  • 資料の中でも出てきましたが、ネットワーク的にはこの手の技術で障害予測ができると良いなと思いました。すでに事例があるかもしれません。

Microsoft Technology for ネットワークプログラマ

メモ

  • Bash on Windows の anniversary update で bash -c "ifconfig"のような形で直接bash側のコマンドを呼び出せる
    • 管理者権限なしで ping も打てるようになった
  • Visual Studio Code はクロスプラットフォーム対応の多機能エディタ
  • Power BI は ExcelAccess の間のようなツール
    • ログを取り込んで分析してきれいなダッシュボードで見せるといったことができる
    • ある程度のことは無料でできる。Office 365 のライセンスで、より高度な機能が利用できる

感想

  • Visual Studio Code は私もよく使っています。
  • Atomと比較すると拡張(パッケージ)は少ないですが、デフォルトである程度のことができて、軽いという印象があります。
  • Power BI はダッシュボードが魅力的に見えました。

Ansible for Network Engineer

メモ

  • Ansible の基礎、ARISTAの機器へのコンフィグ投入例、Ansibleハッカソンレポート
  • Ansible は、Chef/Puppetとは異なりエージェントレス
  • Playbookと呼ばれる設定構成ファイルをYAMLで記述する
  • Playbookはサッカーで例えると監督のようなもの(指示出し)
  • ARISTAの機器向けには、eos_commandeos_config などのモジュールが標準である
  • 2017/02/09 - 11 に沖縄オープンラボでAnsibleハッカソンが行われた
    • 失敗談やトラブルの共有は貴重なナレッジとなる
    • ansible-vault はパスワードを暗号化したい時に有効

感想

  • 私もAnsibleをネットワーク機器向けに使えるか色々試しているのですが、まだまだ情報が少ないので、このように発表してくださる方がいらっしゃって助かります。

「自動化と画面」を考えてみました

メモ

感想

  • 作りたいものがあるとき、機能を絞って簡単に試して捨てれるもの、かつ自分でも理解できるレベルのもの、といったポリシーが紹介されていたのが印象に残りました。

タブレットPCでルータを設定してみた with 二次元バーコードリーダ

メモ

  • コマンド類を二次元バーコード化して印刷した紙の手順をリーダで読んでOSPFを設定するデモ動画

感想

  • 発想力に脱帽です。「紙」の可能性の広がりを感じます。

全体を通して

今回初参加だったのですが、思ったよりもテーマが広くて自分にも身近に感じる内容がありました。 人気があって、参加枠に入るのが難しいのですが、次回もぜひ参加したいと思います。

会場提供や、運営、発表をしてくださった皆様、ありがとうございました。

オライリー「Infrastructure as Code」で印象に残ったポイント

先日、オライリー「Infrastructure as Code」の日本語版が発売されました。 www.oreilly.co.jp

副題にある通り「原則とプラクティス」をメインに書かれていて、特定のツールに依存しない内容になっています。 特に1章「課題と原則」は何度も読み直したくなる内容でした。(さらに言うと"1.2.1 Infrastructure as Code の目標" は壁に貼っておきたいような内容)

全体としては 「 予防より復旧 」が重要という印象をうけました。

一通り読んでみましたので、個人的に印象に残ったポイントをまとめます。

1. 大きな変更より小さな変更を日常的にする

  • 小さな変更のほうが失敗したときに問題を特定しやすく、戻しやすい。
  • 小さな変更のほうが確実に改善でき、モチベーションも保たれる。

2. システムを絶えず変更し、改良しているチームの方が大小の障害に対処できる力を持っている

  • "1.6 アンチフラジャイル:「堅牢」を超えて" からの引用。
  • 人体と同じように負荷、変更を避けると弱くなる。負荷、変更によって強化される。

3. 正しい設計は継続的に変化していく

  • "15.1 発展的なアーキテクチャ" からの引用。
  • 完全で「最終的な」設計にたどり着くことはない

4. 高速フィードバック

  • 誤りは早く検出、フィードバックさせることが品質を向上させる。
  • そのためにはモニタリングが重要。

5. 従来のインフラからマイグレーション時の注意点

  • 従来のパターンを当てはめようとすると、かえって複雑になる可能性があるため、今までの概念を破る必要がある。

6. おまけ:初めて聞いた言葉

「構成ドリフト」

  • 設定差異のこと。差異があること自体は悪くないが、管理できていなくてはならない。管理できていない差異はオートメーション恐怖症を生み出す。

スノーフレークサーバー」

  • 他と異なるサーバーのこと。なぜ異なるのか分からなかったり、再構築できない状態は問題である。

「システム疲労

  • 時間の経過でインフラが壊れていくこと。要因としては、脆弱性、ストレージの残容量など。

「フェニックス交換」

  • ブルー - グリーン 交換の進化形で、Immutable Infrastructure の基礎。

Infrastructure as Code の考え方を知るにとても良い本だと感じました。