てくなべ (tekunabe)

ansible / network / automation / StackStorm

[Ansible] フィルターを手軽に試行錯誤したいときの ansible-console コマンドの使い方

はじめに

Anislbe には、値を変換したり取り出したりする、さまざまなフィルター機能があります。 しかし、Playbook を書かく時、なかなか意図通りにフィルターできないこともあるのではないしょうか。 このようなときは、毎回 Playbook を実行するのも手間になり「とにかく手軽にフィルターを試行錯誤したい」と思うこともあると思います(私はあります)。

そんな時に便利な ansible-console コマンドの使い方をご紹介します。

なお、本来は ansible-console コマンドはフィルターの確認だけでなく、Plyabook と同じようにモジュールを実行できますが、この記事では特定用途に限って紹介をします。

  • 動作確認環境: Ansible 2.8.5

ansible-console コマンドの使い方

以下、一連の流れです。

$ ansible-console -i localhost,  # 接続先は問わないのでここでは localhost のみ
Welcome to the ansible console.
Type help or ? to list commands.

test@all (1)[f:5]$ debug msg={{ 10 | random }}  # random フィルター確認
localhost | SUCCESS => {
    "msg": "6"
}
test@all (1)[f:5]$ debug msg={{ 10 | random }}  # 再度確認(↑キーで履歴呼び出し)
localhost | SUCCESS => {
    "msg": "2"
}
test@all (1)[f:5]$ debug msg={{ [3, 4, 2] | max }}  # max フィルター確認
localhost | SUCCESS => {
    "msg": "4"
}
test@all (1)[f:5]$ debug msg={{ [3, 4, 2] | min }}  # min フィルター確認
localhost | SUCCESS => {
    "msg": "2"
}
test@all (1)[f:5]$ exit     # 終了

[test@6175d1755f33 ~]$ 

より雰囲気をお伝えするため、同じ内容を動画にしたものを載せます。

www.youtube.com

参考

その他、多少環境の準備は必要ですが試行錯誤しやすい点では、Jupyter Notebook の Ansible カーネルである ansible-jupyter-kernel も便利です。

[Ansible] 変数 ansible_network_os に指定できる値 (eos/ios/nxos/junosなど)

変数 ansible_network_os とは

Ansible のネットワークモジュールでは、多くの場合ansible_network_os という変数に、どのプラットフォーム(Cisco IOS、Arista EOS など)を対象とするか指定する必要あります。

サンプルの Playbook でも見かけることは多いのではないでしょうか。

指定できる値

具体的にどのプラットフォームを対象とするときに、どのような値をしていすればよいかは、公式ドキュメントのSettings by Platform にまとまっています。

Network OS ansible_network_os
Arista EOS eos
Cisco ASA asa
Cisco IOS ios
Cisco IOS XR iosxr
Cisco NX-OS nxos
Dell OS6 dellos6
Dell OS9 dellos9
Dell OS10 dellos10
Extreme EXOS exos
Extreme IronWare ironware
Extreme NOS nos
Extreme SLX-OS slxos
Extreme VOSS voss
F5 BIG-IP (不要)
F5 BIG-IQ (不要)
Junos OS junos
Lenovo CNOS cnos
Lenovo ENOS enos
MikroTik RouterOS routeros
Nokia SR OS sros
Pluribus Netvisor netvisor
VyOS vyos

※ Ansible 2.8 現在の Settings by Platform から

具体例

以下は、変数定義ファイルで ios を指定する例です。

---
ansible_connection: network_cli
ansible_network_os: ios             # ここ
ansible_user: dummyuser
ansible_password: dummypassword

各プラットフォームごとに必要な変数については、Platform Options を参照してください。

[Ansible] 小ネタ: 順番が違うだけで読みにくい Playbook

はじめに

頭の体操なので、実運用で利用するのはおすすめしませんシリーズです。

Playbook は YAML で書きます。ディクショナリの順番には処理上の意味はありません。ところが、慣例上は以下のように hosts から書くことが書くことが多いのでしょうか。

- hosts: localhost
  gather_facts: no
  
  # ...略...

今回はこれを、処理に影響を与えない範囲で読みにくく崩してみます。

読みにくい Playbook

- gather_facts: false
  tasks:
    - when: 1 == 1
      debug:
        msg: "hello!"
      name: debug test
  connection: local
  hosts: localhost

ディクショナリの順番を変えただけですが、読みにくくなったと思います。 gather_factstasksconnectionhosts は 同じ階層のディクショナリのキーなので、どういう順番でも処理上は関係ないわけです。

ちゃんと実行できます。

$ ansible-playbook -i localhost, 00.yml 

PLAY [localhost] ********************************************************************************

TASK [debug test] ********************************************************************************
ok: [localhost] => {
    "msg": "hello!"
}

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

よく見かける Playbook

よく見かけるのは以下のような書き方ではないでしょうか。

- hosts: localhost
  gather_facts: false
  connection: local

  tasks:
    - name: debug test
      debug:
        msg: "hello!"
      when: 1 == 1
  • 各プレイでは hosts が最初、gather_facts などを続ける
  • 各タスクでは name が最初、when が最後の方

しっくりきますね。はやり、よく見かける順番で書くのがいいと思います。

ただ、when については最後の方に書く週間が不思議に感じられる方もいらっしゃるようです。プログラムでいうと if に相当するので、タスクの最初の方に定義するのが自然ではという考え方も分かる気がします。

参考

もっとひどいのはこちら tekunabe.hatenablog.jp

[Ansible] restconf_config モジュールで Cisco IOS XE のSyslogサーバー設定追加・削除してみる

■ はじめに

Ansible 2.8 では、RESTCONF でネットワーク機器の情報を取得したり、設定を変更したりできる RESTCONF モジュールが導入されました。

以前、本ブログでは、モジュールを利用してIOS-XE へ RESTCONF でアクセスしてインターフェース情報の取得をためした記事を投稿しました。

今回は、情報の取得ではなく、設定追加・削除を試します。restconf_config モジュールを利用し、送信先 Syslog サーバーの追加、削除をしてみます。

環境


■ 準備

Playook 実行に必要なファイルを作成していきます。

インベントリファイル

ホスト情報を示すインベントリファイルを作成します。

  • inventory
[ios]
iosao1 ansible_host=ios-xe-mgmt.cisco.com

変数定義ファイル

インベントリファイルで定義したグループ ios が利用する変数を定義するファイルを作成します。

  • group_vars/ios.yml
ansible_connection: httpapi
ansible_httpapi_port: 9443
ansible_network_os: restconf      # ios ではないので注意
ansible_user: root
ansible_password: p@ss9999
ansible_httpapi_use_ssl: yes
ansible_httpapi_validate_certs: no

各変数の意味は、前回の記事か、公式ドキュメントの httpapi コネクションプラグインのページを参照してください。


■ 追加(POST)する Playbook

追加 Playbook 作成

追加(POST)する Playbook を作成します。

Playbook 実行前は、ネットワーク機器側に送信先 Syslog サーバーの設定が一つもない状態です。 10.0.0.110.0.0.2 の2つ設定します。

  • config.yml
- hosts: ios
  gather_facts: no

  tasks:
    - name: restconf test
      restconf_config:
        path: /data/Cisco-IOS-XE-native:native/logging/host
        method: post
        content: "{{ content_data | to_json }}"
      vars:
        content_data:
            Cisco-IOS-XE-native:ipv4-host-list:
              - ipv4-host: 10.0.0.1
              - ipv4-host: 10.0.0.2

設定を追加したいので、method オプションには post を指定しています。

content オプションには、json データを指定します。restconf_config モジュールの Examples のよいうに、YAML 上に JSON を定義してもよいのですが、あまり好みではないため、普通に YAML の変数で定義したものを to_json フィルターに通しています。

追加 Playnook 実行

追加(POST)する Playbook を実行します。

$ ansible-playbook -i inventory config.yml 

PLAY [ios] ****************************************************************************************

TASK [restconf test] ******************************************************************************
changed: [iosao1]

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

なお、もう一度実行すると設定済みに POST することになるので、以下のエラーになります。

"msg": "resource '/data/Cisco-IOS-XE-native:native/logging/host' already exist"

普段使っているコマンドの感覚だとちょっと戸惑ってしまうかもしれません。

確認

設定されたことをネットワーク機器側で確認します。

csr1000v#sh run | inc logging host
logging host 10.0.0.1
logging host 10.0.0.2

無事に 10.0.0.110.0.0.2 が設定されました。 この時点では startup-config には保存されていません。

もちろん、restconf_get モジュールで情報取得して確認してもよいでしょう。

(補足)設定変更する場合は?

もし、設定済みの状態から設定変更する場合、は patchput メソッドを利用します。

    - name: restconf test
      restconf_config:
        path: /data/Cisco-IOS-XE-native:native/logging/host
        method: put
        content: "{{ content_data | to_json }}"
      vars:
        content_data:
          host:  # patch/put の場合はここから含める
            Cisco-IOS-XE-native:ipv4-host-list:
              - ipv4-host: 10.0.0.8
              - ipv4-host: 10.0.0.9

例えば、上記 の put する Playbook を実行すると、10.0.0.110.0.0.2消えて10.0.0.810.0.0.9 になります。no コマンドを意識しなくていいのは楽で良いです。(ただし毎回 chenged になる)


■ 削除(DELETE)する Playbook

削除 Playbook 作成

削除(DELETE)する Playbook を作成します。

Playbook 実行前は、さきほどの Playbook により、10.0.0.110.0.0.2 の2つが設定されている状態です。

  • config.yml
- hosts: ios
  gather_facts: no

  tasks:
    - name: restconf test
      restconf_config:
        path: /data/Cisco-IOS-XE-native:native/logging/host
        method: delete   # ポイント
        content: "{{ content_data | to_json }}"
      vars:
        content_data:
          Cisco-IOS-XE-native:ipv4-host-list:
            - ipv4-host: 10.0.0.1
            - ipv4-host: 10.0.0.2

追加(POST)の Playbook と違う点は、method オプションに delete を指定している点のみです。

削除 Playnook 実行

削除(DELETE)する Playbook を実行します。

$ ansible-playbook -i inventory config.yml 

PLAY [ios] ****************************************************************************************
TASK [restconf test] ******************************************************************************
changed: [iosao1]

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

もう一度実行すると chenged になりません。

確認

削除されたことをネットワーク機器側で確認します。

csr1000v#sh run | inc logging host
csr1000v#

無事に削除されました。


■ まとめ

restconf_config モジュールを利用して、Syslogサーバーの追加(POST)、削除(DELETE)をする簡単な例を試しました。

restconf_config モジュールmethod オプションには、他にも putpatch を指定することもできます。これらのメソッドにより、柔軟な設定変更ができます。RESTCONF なので、普段使っているコマンドとは勝手が違うものの、restconf_* モジュール は プラットフォームごとにモジュールを使い分ける必要がないというメリットもあります。

RESTCONF が利用できる機器では、設定変更の方法の選択肢になるのではないでしょうか。

show コマンド結果をパースする方法あれこれ(TextFSM / Genie Parser と Netmiko / Ansible の組み合わせ)

2019/09/05 に、ネットワークプログラマビリティ勉強会 #18で、「show コマンド結果をパースする方法あれこれ」という発表をさせていだきました。サンプルコードが中心だったため、コピペしやすいように、ブログ記事として書きおこします。



■ 1. はじめに

ネットワーク機器の通常の show コマンドの結果は、機械(プログラム)にとっては取り扱いにくくなりがちです。これを解決する、show コマンドの結果を構造化データ(JSONなど)にするパーサーをご紹介します。

具体的には、TextFSMGenie Parser という2つのパーサーです。また、自動化ツール NetmikoAnsible との組み合わせも、サンブルコードベースにご紹介します。すべて Python ベースのものです。

f:id:akira6592:20190906175638p:plain
パーサーと自動化ツールの関係

「そもそもネットワーク器機が構造化データを出力してくれればいいのでは」と思われるとも思いますが、運用中の機器にはそういった機能なまだまだ無かったりすることも多いのではないでしょうか。そんなときに、これらのパーサーが役に立ちます。


■ 2. パーサーとは

パーサーとは、show コマンドの結果を機械(プログラム)が取り扱いやすい構造化データに変換するものです。(今回の記事の文脈上の説明)

例えば、以下のような show ip interface brief の結果

csr1000v#show ip interface brief
Interface          IP-Address      OK? Method Status                Protocol
GigabitEthernet1   10.10.20.48     YES NVRAM  up                    up      
GigabitEthernet2   unassigned      YES NVRAM  administratively down down    
GigabitEthernet3   unassigned      YES NVRAM  administratively down down    

を、以下のような機械が取り扱いやすいデータに変換します。

{
  "INTF": "GigabitEthernet1",
  "IPADDR": "10.10.20.48",
  "PROTO": "up",
  "STATUS": "up"
},
{
  "INTF": "GigabitEthernet2",
  "IPADDR": "unassigned",
  "PROTO": "down",
  "STATUS": "administratively down"
},
{
  "INTF": "GigabitEthernet3",
  "IPADDR": "unassigned",
  "PROTO": "down",
  "STATUS": "administratively down"
}

今回、この記事でパースする、サンプルの show コマンド結果はこちらです。 Cisco DevNet Sandbox CSRV1000V IOS-XE (16.11.01a) の環境を利用させていただきました。

csr1000v#show ip interface brief
Interface            IP-Address      OK? Method Status                Protocol
GigabitEthernet1     10.10.20.48     YES NVRAM  up                    up      
GigabitEthernet2     unassigned      YES NVRAM  administratively down down    
GigabitEthernet3     unassigned      YES NVRAM  administratively down down    
Loopback0            unassigned      YES unset  up                    up      
Loopback1            unassigned      YES unset  up                    up      
Loopback2            unassigned      YES unset  up                    up     


■ 3. TextFSM

3.1. TextFSM とは

TextFSM は、カスタマイズが容易なパーサーです。

  • ntc-templates のテンプレートを利用可
    • プラットフォーム数 23
    • 1コマンド数 290
    • (2019/09/03現在)
  • テンプレートは追加、カスタマイズ可能
  • pip install textfsm でインストール

テンプレートは以下のような形です。正規表現でパースする方法を定義します。

Value INTF (\S+)
Value IPADDR (\S+)
Value STATUS (up|down|administratively down)
Value PROTO (up|down)

Start
  ^${INTF}\s+${IPADDR}\s+\w+\s+\w+\s+${STATUS}\s+${PROTO} -> Record

3.2. TextFSM 単体の利用例

TextFSM 単体では、ネットワーク機器への接続機能はありません。そのためは何かしらの方法で取得したコマンド結果を用意しておきます。(コード内の raw_text_data に相当するデータ)

コード

import textfsm
from pprint import pprint

# テンプレートファイルの読み込み
template = open('./templates/cisco_ios_show_ip_interface_brief.template', 'r')
re_table = textfsm.TextFSM(template)

# raw_text_data は何かしらの方法で取得したコマンド結果
fsm_results = re_table.ParseText(raw_text_data)
 
results = list()
# ヘッダーとデータを結合
for item in fsm_results:
    results.append(dict(zip(re_table.header, item)))

# 表示
pprint(results)

なお、公式のサンプルはこちらです。

実行結果

以下のようにパースされます。ここまで、構造化されていれば、コードから取り扱いしやすいですね。

[{'INTF': 'GigabitEthernet1',
  'IPADDR': '10.10.20.48',
  'PROTO': 'up',
  'STATUS': 'up'},
 {'INTF': 'GigabitEthernet2',
  'IPADDR': 'unassigned',
  'PROTO': 'down',
  'STATUS': 'administratively down'},
 {'INTF': 'GigabitEthernet3',
  'IPADDR': 'unassigned',
  'PROTO': 'down',
  'STATUS': 'administratively down'},
 {'INTF': 'Loopback0', 'IPADDR': 'unassigned', 'PROTO': 'up', 'STATUS': 'up'},
 {'INTF': 'Loopback1', 'IPADDR': 'unassigned', 'PROTO': 'up', 'STATUS': 'up'},
 {'INTF': 'Loopback2', 'IPADDR': 'unassigned', 'PROTO': 'up', 'STATUS': 'up'}]

3.3. TextFMS + Netmiko 利用例

ネットワーク自動化ライブラリ Netmikoでは、Netmiko 2.0.0 から TextFSM が同梱されていて、簡単に連携できます。

なお、Netmiko インストール時に TextFSM も一緒にインストールされます。

コード

send_command の引数に、use_textfsm=True を付加するのがポイントです。

from netmiko import Netmiko
import os
from pprint import pprint

# 接続情報の定義
ios1 = {
  "host": "10.10.20.48",
  "device_type": "cisco_ios",
  "username": "testuser",
  "password": "testpass"
}

# テンプレートファイル格納ディレクトリを環境変数に指定
# デフォルトは ./ntc-templates/templates
os.environ["NET_TEXTFSM"] = "/home/npstudy/templates"

# 接続
net_connect = Netmiko(**ios1)
# コマンド実行、パース
result = net_connect.send_command("show ip interface brief", use_textfsm=True)
# 表示
pprint(result)

実行結果

以下のようにパースされます。

[{'intf': 'GigabitEthernet1',
  'ipaddr': '10.10.20.48',
  'proto': 'up',
  'status': 'up'},
 {'intf': 'GigabitEthernet2',
  'ipaddr': 'unassigned',
  'proto': 'down',
  'status': 'administratively down'},
 {'intf': 'GigabitEthernet3',
  'ipaddr': 'unassigned',
  'proto': 'down',
  'status': 'administratively down'},
 {'intf': 'Loopback0', 'ipaddr': 'unassigned', 'proto': 'up', 'status': 'up'},
 {'intf': 'Loopback1', 'ipaddr': 'unassigned', 'proto': 'up', 'status': 'up'},
 {'intf': 'Loopback2', 'ipaddr': 'unassigned', 'proto': 'up', 'status': 'up'}]

3.4. TextFMS + Ansible 利用例

今度は Ansible と組み合わせた利用例です。

Ansible から TextFSM のテンプレートを利用して、パースするには Ansible 2.4 で追加された parse_cli_textfsmフィルターを利用します。

このフィルターを利用するには、あらかじめ TextFSM をインストールしておく必要があります。

$ pip install textfsm

Playbook

- hosts: ios
  gather_facts: no
 
  vars:
    template_file: "./templates/cisco_ios_show_ip_interface_brief.template"
 
  tasks:
    - name: show
      ios_command:
        commands:
          - show ip interface brief
      register: result
 
    - name: show parsed result
      debug:
        msg: "{{ result.stdout[0] | parse_cli_textfsm(template_file) }}"

実行結果

以下のようにパースされます。

ok: [ios1] => {
  "msg": [
      {
          "INTF": "GigabitEthernet1",
          "IPADDR": "10.10.20.48",
          "PROTO": "up",
          "STATUS": "up"
      },
      {
          "INTF": "GigabitEthernet2",
          "IPADDR": "unassigned",
          "PROTO": "down",
          "STATUS": "administratively down"
      },
      {
          "INTF": "GigabitEthernet3",
          "IPADDR": "unassigned",
          "PROTO": "down",
          "STATUS": "administratively down"
      },
      {
          "INTF": "Loopback0",
          "IPADDR": "unassigned",
          "PROTO": "up",
          "STATUS": "up"
      },
      {
          "INTF": "Loopback1",
          "IPADDR": "unassigned",
          "PROTO": "up",
          "STATUS": "up"
      },
      {
          "INTF": "Loopback2",
          "IPADDR": "unassigned",
          "PROTO": "up",
          "STATUS": "up"
      }
  ]
}




■ 4. Genie Parser

4.1. Genie Parser とは

Genie Parser は、Cisco 機器に強いパーサーです。

4.2. Genie Parser 単体の利用例

Genie にはネットワーク機器への接続機能があります。testdeb と呼ばれる接続情報を YMAL で定義し、コードから呼び出します。

コード

  • testbed (接続情報)ファイル
testbed: 
  name: "testbed1"
devices:
  csr1000v:
      type: catalyst
      platform: iosxe
      os: "iosxe"
      alias: "ios1"
      tacacs:
          login_prompt: "login:"
          password_prompt: "Password:"
          username: testuser
      passwords:
          tacacs: testpass
      # enable: testpass
      # line: testpass
      connections:
          ssh:
              protocol: ssh
              ip: "10.10.20.48"
  • コード
from genie.conf import Genie
from pprint import pprint

# testbed (接続情報)を読み込む
testbed = Genie.init("./testbed.yml") 
device = testbed.devices["csr1000v"]

# 機器への接続
device.connect()

# コマンド実行、パース
output = device.parse("show ip interface brief")

# 表示
pprint(output)

実行結果

以下のようにパースされます。最初はログインや ter len 0 などのログが流れます。(device.connect(log_stdout=False) とすることで非表示することも可能。@ccieojisanさん、情報ありがとうございます)

[2019-09-04 15:30:20,523] +++ csr1000v logfile /tmp/csr1000v-cli-20190904T153020523.log +++
[2019-09-04 15:30:20,524] +++ Unicon plugin iosxe +++
Password: 
[2019-09-04 15:30:28,022] +++ connection to spawn: ssh -l developer 10.10.20.48, id: 4458345528 +++
[2019-09-04 15:30:28,023] connection to csr1000v

...(略)...

{'interface': {'GigabitEthernet1': {'interface_is_ok': 'YES',
                                    'ip_address': '10.10.20.48',
                                    'method': 'NVRAM',
                                    'protocol': 'up',
                                    'status': 'up'},
               'GigabitEthernet2': {'interface_is_ok': 'YES',
                                    'ip_address': 'unassigned',
                                    'method': 'NVRAM',
                                    'protocol': 'down',
                                    'status': 'administratively down'},
               'GigabitEthernet3': {'interface_is_ok': 'YES',
                                    'ip_address': 'unassigned',
                                    'method': 'NVRAM',
                                    'protocol': 'down',
                                    'status': 'administratively down'},
               'Loopback0': {'interface_is_ok': 'YES',
                             'ip_address': 'unassigned',
                             'method': 'unset',
                             'protocol': 'up',
                             'status': 'up'},
               'Loopback1': {'interface_is_ok': 'YES',
                             'ip_address': 'unassigned',
                             'method': 'unset',
                             'protocol': 'up',
                             'status': 'up'},
               'Loopback2': {'interface_is_ok': 'YES',
                             'ip_address': 'unassigned',
                             'method': 'unset',
                             'protocol': 'up',
                             'status': 'up'}}}

4.3. Genie Parser + Netmiko 利用例

Netmiko 2.4.1 で Genie Parser との連携機能が追加されました。

Netmiko から Genie Parser を利用するには、あらかじめンストールしておく必要があります。

$ pip install genie

コード

send_command の引数に、use_genie=True を付加するのがポイントです。

from netmiko import Netmiko
import os
from pprint import pprint

# 接続情報の定義
ios1 = {
  "host": "10.10.20.48",
  "device_type": "cisco_ios",
  "username": "testuser",
  "password": "testpass"
}

# 接続
net_connect = Netmiko(**ios1)

# コマンド実行、パース
result = net_connect.send_command("show ip interface brief", use_genie=True)

# 表示
pprint(result)

実行結果

以下のようにパースされます。

{'interface': {'GigabitEthernet1': {'interface_is_ok': 'YES',
                                    'ip_address': '10.10.20.48',
                                    'method': 'NVRAM',
                                    'protocol': 'up',
                                    'status': 'up'},
               'GigabitEthernet2': {'interface_is_ok': 'YES',
                                    'ip_address': 'unassigned',
                                    'method': 'NVRAM',
                                    'protocol': 'down',
                                    'status': 'administratively down'},
               'GigabitEthernet3': {'interface_is_ok': 'YES',
                                    'ip_address': 'unassigned',
                                    'method': 'NVRAM',
                                    'protocol': 'down',
                                    'status': 'administratively down'},
               'Loopback0': {'interface_is_ok': 'YES',
                             'ip_address': 'unassigned',
                             'method': 'unset',
                             'protocol': 'up',
                             'status': 'up'},
               'Loopback1': {'interface_is_ok': 'YES',
                             'ip_address': 'unassigned',
                             'method': 'unset',
                             'protocol': 'up',
                             'status': 'up'},
               'Loopback2': {'interface_is_ok': 'YES',
                             'ip_address': 'unassigned',
                             'method': 'unset',
                             'protocol': 'up',
                             'status': 'up'}}}

4.4. Genie Parser + Ansible 利用例

今度は Ansible と組み合わせた利用例です。

Ansible から Genie Parser を利用するには、clay584.parse_genie というロールが必要です。Genie Parser に加えて、本ロールもあらかじめインストールしておきます。

$ pip install geneie
$ ansible-galaxy install clay584.parse_genie

なお、本ロールは Ansible 2.7 以上が必要です。

Playbook

- hosts: ios
  gather_facts: no
  vars:  # 実行 show コマンドの定義
    show: show ip interface brief

  tasks:
    - name: Read in parse_genie role
      include_role:         # clay584.parse_genie ロールを利用
        name: clay584.parse_genie
        
     - name: ios command test
      ios_command:          # コマンド実行
        commands:
          - "{{ show }}"
      register: result      # 実行結果が変数 result に入る

     - name: parsed result  # parse_genie フィルターでパース
      debug:
        msg: "{{ result.stdout[0] | parse_genie(command=show, os='iosxe') }}"

実行結果

以下のようにパースされます。

ok: [ios1] => {
    "msg": {interface”: {
            "GigabitEthernet1": {
                "interface_is_ok": "YES",
                "ip_address": "10.10.20.48",
                "method": "NVRAM",
                "protocol": "up",
                "status": "up"
            },
            "GigabitEthernet2": {
                "interface_is_ok": "YES",
                "ip_address": "unassigned",
                "method": "NVRAM",
                "protocol": "down",
                "status": "administratively down"
            },
            "GigabitEthernet3": {
                "interface_is_ok": "YES",
                "ip_address": "unassigned",
                "method": "NVRAM",
                "protocol": "down",
                "status": "administratively down"
            },
            "Loopback0": {
                "interface_is_ok": "YES",
                "ip_address": "unassigned",
                "method": "unset",
                "protocol": "up",
                "status": "up"
            },
            "Loopback1": {
                "interface_is_ok": "YES",
                "ip_address": "unassigned",
                "method": "unset",
                "protocol": "up",
                "status": "up"
            },
            "Loopback2": {
                "interface_is_ok": "YES",
                "ip_address": "unassigned",
                "method": "unset",
                "protocol": "up",
                "status": "up"
            }
        }
    }
}


■ 5. まとめ


参考資料

[Ansible] ipaddr フィルターで特定の範囲のIPアドレスを抽出する

■ はじめに

Ansible には、IP アドレスに対してさまざまフィルターをかける ipaddrというフィルタープラグインがあります。

過去記事参考: [ipaddr フィルターでプレフィックス表記とネットマスク表記を変換する)

このフィルターを使って、IPアドレスのリストの中から、特定の範囲のIPアドレスだけを抽出する方法を試します。

  • 動作確認環境: Ansible 2.8.4
  • 要 netaddr (pip install netaddr でインストール)

■ サンプル

簡単なサンプルで説明します。

Playbook

検証する Playbook は以下の通りです。

target_list に定義されているIPアドレスの中で ip_range (192.168.12.0/24) に含まれているものだけを debug モジュールで出力します。

---
- hosts: localhost
  connection: local
  gather_facts: no

  # 各種変数の定義
  vars: 
    ip_range: 192.168.12.0/24     # この範囲のIPアドレスだけ抽出したい
    target_list:                  # 調べたいIPアドレスのリスト
        - 192.168.12.0            # 範囲内
        - 192.168.12.1            # 範囲内
        - 192.168.12.254          # 範囲内
        - 192.168.12.255          # 範囲内
        - 192.168.15.0
        - 192.168.15.1
        - 192.168.15.254
        - 192.168.15.255

  # タスクの定義
  tasks:
    - name: net range test
      debug:
        msg: "{{ item }} は {{ ip_range }} の範囲内です"
      loop: "{{ target_list | ipaddr(ip_range) }}" # フィルター後にループ

実行結果

Playbook を実行します。

$ ansible-playbook -i localhost, test.yml 

PLAY [localhost] **************************************************************************************************

TASK [net range test] *********************************************************************************************
ok: [localhost] => (item=192.168.12.0) => {
    "msg": "192.168.12.0 は 192.168.12.0/24 の範囲内です"
}
ok: [localhost] => (item=192.168.12.1) => {
    "msg": "192.168.12.1 は 192.168.12.0/24 の範囲内です"
}
ok: [localhost] => (item=192.168.12.254) => {
    "msg": "192.168.12.254 は 192.168.12.0/24 の範囲内です"
}
ok: [localhost] => (item=192.168.12.255) => {
    "msg": "192.168.12.255 は 192.168.12.0/24 の範囲内です"
}

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

192.168.12.0/24 の範囲内の IP アドレスだけ抽出されました。ループ前に抽出されるので、 そもそも 192.168.15.0 などはループの対象にはならず、skipped としても表示されません。

もしループ後に抽出したい場合は、以下の記事のように when で判断するのが良いと思います。このあたりはお好みで。

qiita.com

なお、192.168.12.0/24 ではなく 192.168.12.0/255.255.255.0 のようなマスク表記でも正常に抽出されました。 192.168.12.0/24192.168.13.0/24 のような範囲同士の比較、抽出もできるようようです。

■ まとめ

ipaddr フィルターで特定の範囲のIPアドレスを抽出する方法をご紹介しました。

ipaddr フィルタープラグインには、他にもさまざまな機能がありますので、IP アドレスに対してなにか加工したい場合は、このフィルターを調べてみると良いかもしれません。

[Ansible] TRANSFORM_INVALID_GROUP_CHARS を ignore に設定したときの表示が Ansible 2.8.2 から変更された

はじめに

Ansible 2.8.0 から、グループ名に利用できる文字が厳格化されました。 たとえば、ハイフンが入っていると不正とみなされ、WARNING が表示されます。

https://image.slidesharecdn.com/ansiblenight201905ansible2-190531065357/95/ansible-28-21-638.jpg?cb=1559302122

https://www.slideshare.net/akira6592/ansible28update/21

この挙動は、TRANSFORM_INVALID_GROUP_CHARS という設定項目で変更できます。

これを never に設定した時の WARNING の出方が、Ansible 2.8.2 で変わりました。

ansible.cfg で設定変更する場合は、以下のようにします。

[defaults]
force_valid_group_names=ignore

デフォルト(never)で表示される WARNING

デフォルトでは以下の表示になります。これは 2.8.0 でも 2.8.2 以降でも同じです。

[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, but still be user 
configurable on deprecation. This feature will be removed in version 2.10. Deprecation warnings can be disabled by setting deprecation_warnings=False in 
ansible.cfg.
 [WARNING]: Invalid characters were found in group names but not replaced, use -vvvv to see details

never 設定時

設定確認

$ ansible-config dump --only-changed
TRANSFORM_INVALID_GROUP_CHARS(/home/akira/ansible/ansible.cfg) = ignore

2.8.0 - 2.8.1 での表示

1つのめの WARNING は表示されたままです。

[DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to allow bad characters in group names by default, this will change, but still be user 
configurable on deprecation. This feature will be removed in version 2.10. Deprecation warnings can be disabled by setting deprecation_warnings=False in 
ansible.cfg.

2.8.2 以降では WARNING なし

WARNINGは表示されなくなります。