てくなべ (tekunabe)

ansible / network automation / 学習メモ

StackStorm 経由で Slack に投稿する(準備 ~ st2 run編)

■ はじめに

StackStorm には Slack と連携するための Pack が用意されています。 この記事では、環境の準備と、簡単な動作確認としてSlackへの手動投稿を試します。


■ 環境の準備

StackStorm 本体のインストール

今回は 以下の方法でインストールします。 tekunabe.hatenablog.jp 2.7.2 というバージョンがインストールされました。

Slack Pack のインストール

まず st2 login コマンドでログインします。

[ec2-user@ip-172-31-4-147 ~]$ st2 login testu
Password:
Logged in as testu

Note: You didn't use --write-password option so the password hasn't been stored in the client config and you will need to login again in 24 hours when the auth token expires.
As an alternative, you can run st2 login command with the "--write-password" flag, but keep it mind this will cause it to store the password in plain-text in the client config file (~/.st2/config).

続いて st2 pack install slack コマンドで Slack Pack をインストールします。

[ec2-user@ip-172-31-4-147 ~]$ st2 pack install slack

For the "slack" pack, the following content will be registered:

rules     |  0
sensors   |  1
triggers  |  0
actions   |  113
aliases   |  0

Installation may take a while for packs with many items.

        [ succeeded ] download pack
        [ succeeded ] make a prerun
        [ succeeded ] install pack dependencies
        [ succeeded ] register pack

+-------------+------------------------------------------------+
| Property    | Value                                          |
+-------------+------------------------------------------------+
| name        | slack                                          |
| description | st2 content pack containing slack integrations |
| version     | 0.10.0                                         |
| author      | StackStorm, Inc.                               |
+-------------+------------------------------------------------+

Slack Pack の内容確認

Slack Pack がインストールされたことを確認します。

[ec2-user@ip-172-31-4-147 ~]$ st2 pack show slack
+-------------+---------------------------------------------------------+
| Property    | Value                                                   |
+-------------+---------------------------------------------------------+
| name        | slack                                                   |
| description | st2 content pack containing slack integrations          |
| author      | StackStorm, Inc.                                        |
| content     | {                                                       |
|             |     "sensors": {                                        |
|             |         "count": 1,                                     |
|             |         "resources": [                                  |
|             |             "SlackSensor"                               |
|             |         ]                                               |
|             |     },                                                  |
|             |     "actions": {                                        |
|             |         "count": 113,                                   |
|             |         "resources": [                                  |
|             |             "dnd.endSnooze",                            |
(略)
|             |             "post_message",                             |
|             |             "dnd.info",                                 |
|             |             "team.billableInfo",                        |
|             |             "channels.replies",                         |
|             |             "groups.setPurpose",                        |
|             |             "channels.list",                            |
|             |             "groups.rename",                            |
|             |             "im.replies",                               |
|             |             "im.list",                                  |
(略)
|             |             "stars.add"                                 |
|             |         ]                                               |
|             |     }                                                   |
|             | }                                                       |
| email       | info@stackstorm.com                                     |
| keywords    | [                                                       |
|             |     "slack",                                            |
|             |     "chat",                                             |
|             |     "messaging",                                        |
|             |     "instant messaging"                                 |
|             | ]                                                       |
| ref         | slack                                                   |
| repo_url    | https://github.com/StackStorm-Exchange/stackstorm-slack |
| version     | 0.10.0                                                  |
+-------------+---------------------------------------------------------+

多くのActionがあるようです。

メッセージを投稿するには、 slack.post_message という Action を利用すればよさそうなので、slack.post_message のヘルプを参照します。

[ec2-user@ip-172-31-4-147 ~]$ st2 action get slack.post_message
+-------------+--------------------------------------------------------------+
| Property    | Value                                                        |
+-------------+--------------------------------------------------------------+
| id          | 5b0b5b446fb12304dc0e0199                                     |
| uid         | action:slack:post_message                                    |
| ref         | slack.post_message                                           |
| pack        | slack                                                        |
| name        | post_message                                                 |
| description | Post a message to the Slack channel.                         |
| enabled     | True                                                         |
| entry_point | post_message.py                                              |
| runner_type | python-script                                                |
| parameters  | {                                                            |
|             |     "username": {                                            |
|             |         "required": false,                                   |
|             |         "type": "string",                                    |
|             |         "description": "Bot username."                       |
|             |     },                                                       |
|             |     "disable_formatting": {                                  |
|             |         "default": false,                                    |
|             |         "required": false,                                   |
|             |         "type": "boolean",                                   |
|             |         "description": "Disable formatting, don't parse the  |
|             | message and treat it as raw text"                            |
|             |     },                                                       |
|             |     "icon_emoji": {                                          |
|             |         "required": false,                                   |
|             |         "type": "string",                                    |
|             |         "description": "Bot icon"                            |
|             |     },                                                       |
|             |     "webhook_url": {                                         |
|             |         "required": false,                                   |
|             |         "type": "string",                                    |
|             |         "description": "Optional Webhook url"                |
|             |     },                                                       |
|             |     "message": {                                             |
|             |         "required": true,                                    |
|             |         "type": "string",                                    |
|             |         "description": "Message to send."                    |
|             |     },                                                       |
|             |     "channel": {                                             |
|             |         "required": false,                                   |
|             |         "type": "string",                                    |
|             |         "description": "Optional channel to post to. Note    |
|             | channel must contain leading #"                              |
|             |     }                                                        |
|             | }                                                            |
| notify      |                                                              |
| tags        |                                                              |
+-------------+--------------------------------------------------------------+

Slack Pack の 設定

予め 投稿したい先の Slack の着信WebフックのURLを控えておきます。 f:id:akira6592:20180528183246p:plain

続いて、 st2 pack config slack コマンドで Slack Pack の設定を行います。 いくつか対話形式で設定項目が聞かれます。

今回は以下の値にして、他はデフォルトのままにしました。

設定項目 備考
post_message_action.username akira6592 任意の名前
post_message_action.webhook_url (先ほど控えた着信WebフックのURL)
post_message_action.icon_emoji :smiley: 投稿時のアイコン。省略時は :panda:
admin.organization admin 任意の名前

途中、以下のように聞かれますのでエンターを押します。

Do you want to preview the config in an editor before saving? [y]:

設定内容がテキストとしてエディタで開かれるので、確認後エディタを終了します。

最後に

Do you want me to save it? [y]: 

と聞かれるのでエンターを押します。

設定が完了すると以下のように、設定内容が出力されます。

+----------+--------------------------------------------------------------+
| Property | Value                                                        |
+----------+--------------------------------------------------------------+
| id       | 5b0b5b466fb12304dc0e01c3                                     |
| pack     | slack                                                        |
| values   | {                                                            |
|          |     "action_token": "********",                              |
|          |     "admin": {                                               |
|          |         "organization": "admin",                             |
|          |         "attempts": 1,                                       |
|          |         "admin_token": "",                                   |
|          |         "auto_join_channels": null,                          |
|          |         "set_active": true                                   |
|          |     },                                                       |
|          |     "sensor": {                                              |
|          |         "strip_formatting": false,                           |
|          |         "token": ""                                          |
|          |     },                                                       |
|          |     "post_message_action": {                                 |
|          |         "username": "akira6592",                             |
|          |         "webhook_url": "https://hooks.slack.com/services/xxx |
|          | xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",                  |
|          |         "icon_emoji": ":smiley:",                            |
|          |         "channel": null                                      |
|          |     }                                                        |
|          | }                                                            |
+----------+--------------------------------------------------------------+

設定内容は /opt/stackstorm/configs/slack.yaml に保存されています。

[ec2-user@ip-172-31-4-147 ~]$ cat /opt/stackstorm/configs/slack.yaml
action_token: null
admin:
  admin_token: ''
  attempts: 1
  auto_join_channels: null
  organization: admin
  set_active: true
post_message_action:
  channel: null
  icon_emoji: ':smiley:'
  username: akira6592
  webhook_url: https://hooks.slack.com/services/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
sensor:
  strip_formatting: false
  token: ''


■ StackStorm から Slack への投稿

それでは実際に StackStorm から Slack へ投稿してみます。 手動での投稿は st2 run slack.post_message コマンドを利用し、 message オプションで投稿したいテキストを指定します。

[ec2-user@ip-172-31-4-147 ~]$ st2 run slack.post_message message="Hello, St2! :tada:"
.
id: 5b0bca1b6fb12304dc0e01d4
status: succeeded
parameters:
  message: 'Hello, St2! :tada:'
result:
  exit_code: 0
  result: true
  stderr: 'st2.actions.python.PostMessageAction: INFO     Message successfully posted
    '
  stdout: ''

無事、以下のようにSlackに投稿されました。 f:id:akira6592:20180528183318p:plain


■ まとめ

StackStorm と Slack を連携するための準備と、手動でSlackに投稿するところまで確認できました。 Notification先として便利そうです。 また、 slack.SlackSensor というセンサーも含まれているようなので、今度こちらも試してみたいと思います。

StackStorm の Ansible Pack で利用するPythonパッケージの追加インストール方法

はじめに

StackStorm の Ansible Pack を利用して Junos のルーターに接続しようとしたところ、 ncclient is not installed というエラーになってしまいました。

このエラー自身はAnsible 単体で利用した時にも見かけたことがあったので対処方法を知っていました。

しかし、Ansible Pack の実行環境は virtualenv で隔離されおり、パッケージのインストール方法に少しだけコツがあったので、対処した方法をご紹介します。

結論としては、以下のコマンドを利用しました。

sudo /opt/stackstorm/virtualenvs/ansible/bin/pip install ncclient jxmlease


■ やりたかったこと

以下のような Playbook で Junos のバージョン情報を表示させる処理を試そうとしました。

- hosts: junos
  gather_facts: no
  connection: netconf

  tasks:
    - name: show
      junos_command:
        commands:
          - show version
      register: result

    - name: debug
      debug:
        msg: "{{ result.stdio_lines[0] }}"

  vars:
    ansible_network_os: junos
    ansible_user: testuser
    ansible_ssh_pass: testpass

■ 遭遇したエラー

ところが、以下のようなエラーになりました。

[ec2-user@ip-172-31-4-147 ~]$ st2 run ansible.playbook playbook=/home/ec2-user/junos_show.yml inventory_file=/home/ec2-user/inventory
.
id: 5b0011b76fb12304eceb1b8b
status: failed
parameters:
  cwd: /opt/stackstorm/packs/ansible
  inventory_file: /home/ec2-user/inventory
  playbook: /home/ec2-user/junos_show.yml
result:
  failed: true
  return_code: 2
  stderr: Executed command "/opt/stackstorm/virtualenvs/ansible/bin/ansible-playbook --inventory-file=/home/ec2-user/inventory /home/ec2-user/junos_show.yml"
  stdout: "
PLAY [junos] *******************************************************************

TASK [show] ********************************************************************
fatal: [10.1.1.110]: FAILED! => {"msg": "ncclient is not installed"}
    to retry, use: --limit @/home/ec2-user/junos_show.retry

PLAY RECAP *********************************************************************
10.1.1.110               : ok=0    changed=0    unreachable=0    failed=1
"
  succeeded: false

ncclient というPythonパッケージが不足しているようです。


■ 対処

必要パッケージのインストール

Playbook 内で利用した、junos_commandモジュールのドキュメントを参照すると、ncclientjxmlease が必要とのことなのでこの2つをインストールすることにしました。

[ec2-user@ip-172-31-4-147 ~]$ sudo /opt/stackstorm/virtualenvs/ansible/bin/pip install ncclient jxmlease
(略)
Successfully installed jxmlease-1.0.1 ncclient-0.5.3

Ansible Pack を st2 pack install ansible でインストールした時に自動的に入った他のパッケージと site-packages 配下の権限を比較すると、以下のように異なります。

[ec2-user@ip-172-31-4-147 ~]$ ls -ls  /opt/stackstorm/virtualenvs/ansible/lib/python2.7/site-packages/
(略)
  4 drwxrwxr-x. 17 root st2packs   4096  5月 19 04:43 ansible
  0 drwxrwxr-x.  2 root st2packs    131  5月 19 04:43 ansible-2.5.3.dist-info
(略)
  0 drwxr-xr-x.  5 root root        243  5月 19 12:11 ncclient
  0 drwxr-xr-x.  2 root root        147  5月 19 12:11 ncclient-0.5.3.dist-info
(略)
  4 drwxrwxr-x.  2 root st2packs   4096  5月 19 04:43 paramiko
  0 drwxrwxr-x.  2 root st2packs    150  5月 19 04:43 paramiko-2.4.1.dist-info

補足

以下の方法でパッケージをインストールしても、Ansible Pack から ncclient は利用できませんでした。

# グローバル環境にインストールされ、Ansible Pack から利用できない
[ec2-user@ip-172-31-4-147 ~]$ pip install ncclient jxmlease
# 権限エラー
[ec2-user@ip-172-31-4-147 ~]$ source /opt/stackstorm/virtualenvs/ansible/bin/activate
(ansible) [ec2-user@ip-172-31-4-147 ~]$ pip install ncclient jxmlease
(略)
OSError: [Errno 13] 許可がありません: '/opt/stackstorm/virtualenvs/ansible/lib/python2.7/site-packages/ncclient'
# これもグローバル環境にインストールされてしまう
[ec2-user@ip-172-31-4-147 ~]$ source /opt/stackstorm/virtualenvs/ansible/bin/activate
(ansible) [ec2-user@ip-172-31-4-147 ~]$ sudo pip install ncclient jxmlease


■ 再実行

必要なパッケージをインストールしたので再実行します。

[ec2-user@ip-172-31-4-147 ~]$ st2 run ansible.playbook playbook=/home/ec2-user/junos_show.yml inventory_file=/home/ec2-user/inventory
........
id: 5b0014ee6fb12304eceb1b94
status: succeeded
parameters:
  cwd: /opt/stackstorm/packs/ansible
  inventory_file: /home/ec2-user/inventory
  playbook: /home/ec2-user/junos_show.yml
result:
  failed: false
  return_code: 0
  stderr: ''
  stdout: "
PLAY [junos] *******************************************************************

TASK [show] ********************************************************************
ok: [10.1.1.110]

TASK [debug] *******************************************************************
ok: [10.1.1.110] => {
    "msg": [
        "Hostname: ip-172-31-43-152",
        "Model: vmx",
        "Junos: 17.4R1-S2.2",
        "JUNOS OS Kernel 64-bit  [20180127.fdc8dfc_builder_stable_11]",
        "JUNOS OS libs [20180127.fdc8dfc_builder_stable_11]",
        "JUNOS OS runtime [20180127.fdc8dfc_builder_stable_11]",
(略)
    ]
}

PLAY RECAP *********************************************************************
10.1.1.110               : ok=2    changed=0    unreachable=0    failed=0
"
  succeeded: true

正常に実行でき、無事にJunosのバージョン情報が取得できました。


■ まとめ

StackStorm は Python で書かれているため、 virtualenv などのPythonの環境について意識する必要があると感じました。

公式ドキュメントに、この手の順が見つからなかったのですが、もしご存じの方がいらっしゃいましたら、コメントまたは twitter: @akira6592 へお知らせいただけるとうれしいです。

StackStorm と Ansible で Cisco IOS ルーターに設定を投入する(st2 run編)

■ はじめに

StackStorm と Ansible 連携を試すために、Cisco IOS ルーターに設定を投入する方法を確認しました。 この記事では、環境の準備と st2 run コマンドによる簡単な Ansible 連携の方法をご紹介します。


■ StackStorm側の準備

StackStorm 本体のインストール

今回は 以下の方法でインストールします。 tekunabe.hatenablog.jp

2.7.2 というバージョンがインストールされました。

Ansible Pack のインストール

まず st2 login コマンドでログインします。

[ec2-user@ip-172-31-4-147 ~]$ st2 login testu
Password:
Logged in as testu

Note: You didn't use --write-password option so the password hasn't been stored in the client config and you will need to login again in 24 hours when the auth token expires.
As an alternative, you can run st2 login command with the "--write-password" flag, but keep it mind this will cause it to store the password in plain-text in the client config file (~/.st2/config).

続いて、st2 pack install ansible コマンドで Ansible Pack をインストールします。

[ec2-user@ip-172-31-4-147 ~]$ st2 pack install ansible

For the "ansible" pack, the following content will be registered:

rules     |  0
sensors   |  0
triggers  |  0
actions   |  8
aliases   |  0

Installation may take a while for packs with many items.

        [ succeeded ] download pack
        [ succeeded ] make a prerun
        [ succeeded ] install pack dependencies
        [ succeeded ] register pack

+-------------+--------------------------------------------------+
| Property    | Value                                            |
+-------------+--------------------------------------------------+
| name        | ansible                                          |
| description | st2 content pack containing ansible integrations |
| version     | 0.5.4                                            |
| author      | StackStorm, Inc.                                 |
+-------------+--------------------------------------------------+

Ansible Pack がインストールされたことを確認します。

[ec2-user@ip-172-31-4-147 ~]$ st2 pack show ansible
+-------------+-----------------------------------------------------------+
| Property    | Value                                                     |
+-------------+-----------------------------------------------------------+
| name        | ansible                                                   |
| description | st2 content pack containing ansible integrations          |
| author      | StackStorm, Inc.                                          |
| content     | {                                                         |
|             |     "tests": {                                            |
|             |         "count": 1,                                       |
|             |         "resources": [                                    |
|             |             "test_actions_lib_ansiblebaserunner.py"       |
|             |         ]                                                 |
|             |     },                                                    |
|             |     "actions": {                                          |
|             |         "count": 8,                                       |
|             |         "resources": [                                    |
|             |             "playbook",                                   |
|             |             "command_local",                              |
|             |             "galaxy.list",                                |
|             |             "vault.encrypt",                              |
|             |             "galaxy.install",                             |
|             |             "command",                                    |
|             |             "vault.decrypt",                              |
|             |             "galaxy.remove"                               |
|             |         ]                                                 |
|             |     }                                                     |
|             | }                                                         |
| email       | info@stackstorm.com                                       |
| keywords    | [                                                         |
|             |     "ansible",                                            |
|             |     "cfg management",                                     |
|             |     "configuration management"                            |
|             | ]                                                         |
| ref         | ansible                                                   |
| repo_url    | https://github.com/StackStorm-Exchange/stackstorm-ansible |
| version     | 0.5.4                                                     |
+-------------+-----------------------------------------------------------+

Ansible Pack に、どのような Acition があるかを確認します。

[ec2-user@ip-172-31-4-147 ~]$ st2 action list --pack=ansible
+------------------------+---------+--------------------------------------+
| ref                    | pack    | description                          |
+------------------------+---------+--------------------------------------+
| ansible.command        | ansible | Run ad-hoc ansible command (module)  |
| ansible.command_local  | ansible | Run ad-hoc ansible command (module)  |
|                        |         | on local machine                     |
| ansible.galaxy.install | ansible | Download & Install role from ansible |
|                        |         | galaxy                               |
| ansible.galaxy.list    | ansible | Display a list of installed roles    |
|                        |         | from ansible galaxy                  |
| ansible.galaxy.remove  | ansible | Remove an installed from ansible     |
|                        |         | galaxy role                          |
| ansible.playbook       | ansible | Run ansible playbook                 |
| ansible.vault.decrypt  | ansible | Decrypt ansible data files           |
| ansible.vault.encrypt  | ansible | Encrypt ansible data files           |
+------------------------+---------+--------------------------------------+

Playbook を実行するには、 ansible.playbook という Action を利用すればよいことが分かりました。

ansible.playbook という Action の説明を参照します。

[ec2-user@ip-172-31-4-147 ~]$ st2 action get ansible.playbook
+-------------+--------------------------------------------------------------+
| Property    | Value                                                        |
+-------------+--------------------------------------------------------------+
| id          | 5affab616fb123179539c2a2                                     |
| uid         | action:ansible:playbook                                      |
| ref         | ansible.playbook                                             |
| pack        | ansible                                                      |
| name        | playbook                                                     |
| description | Run ansible playbook                                         |
| enabled     | True                                                         |
| entry_point | ansible_playbook.py                                          |
| runner_type | local-shell-script                                           |
| parameters  | {                                                            |
|             |     "help": {                                                |
|             |         "type": "boolean",                                   |
|             |         "description": "Show help message and exit [-h]"     |
|             |     },                                                       |
(略)
|             |     "playbook": {                                            |
|             |         "position": 0,                                       |
|             |         "type": "string",                                    |
|             |         "description": "Playbook file"                       |
|             |     },                                                       |
(略)
|             |     "inventory_file": {                                      |
|             |         "type": "string",                                    |
|             |         "description": "Inventory host file                  |
|             | (default=/etc/ansible/hosts) [-i]"                           |
|             |     },                                                       |
(略)
|             |     "verbose": {                                             |
|             |         "enum": [                                            |
|             |             "v",                                             |
|             |             "vv",                                            |
|             |             "vvv",                                           |
|             |             "vvvv"                                           |
|             |         ],                                                   |
(略)
|             | }                                                            |
| notify      |                                                              |
| tags        |                                                              |
+-------------+--------------------------------------------------------------+

上記は抜粋ですが、よく使いそうなパラメータとして以下のことが分かります。

  • Playbook の指定: playbook
  • インベントリファイルの指定: inventory_file
  • 詳細度の指定: verbose


■ Ansible 側の準備

Ansible 本体

Ansible自身は、Ansible Pack インストール時に virtualenv 配下にインストールされます。そのため別途インストールする必要はありません。

インストールされたバージョンを確認します。

[ec2-user@ip-172-31-4-147 ~]$ /opt/stackstorm/virtualenvs/ansible/bin/ansible --version
ansible 2.5.3
  config file = None
  configured module search path = [u'/home/ec2-user/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /opt/stackstorm/virtualenvs/ansible/lib/python2.7/site-packages/ansible
  executable location = /opt/stackstorm/virtualenvs/ansible/bin/ansible
  python version = 2.7.5 (default, May  3 2017, 07:55:04) [GCC 4.8.5 20150623 (Red Hat 4.8.5-14)]

Playbook の作成

Cisco IOS ルーターntp server 10.1.1.1 を投入する、以下の Playbook を作成します。

ファイル名: /home/ec2-user/ios_test.yml

- hosts: ios
  gather_facts: no
  connection: network_cli

  tasks:
    - name: config_test
      ios_config:
        lines:
          - ntp server 10.1.1.1

  vars:
    ansible_network_os: ios
    ansible_user: testadmin
    ansible_ssh_pass: testpass

インベントリファイル

以下のインベントリファイルを作成します。 ファイル名: /home/ec2-user/inventory

[ios]
10.1.1.67

ansible.cfg の作成

Ansibleが初めて接続する相手のホストキーが登録されていないとエラーになりますので、今回はホストキーのチェックを無視する設定ファイルを作成します。

ファイル名: /opt/stackstorm/packs/ansible/ansible.cfg

[defaults]
host_key_checking = False


■ 実行

それでは st2 run コマンドで Ansible の Playbook を実行します。

[ec2-user@ip-172-31-4-147 ~]$ st2 run ansible.playbook playbook=/home/ec2-user/ios_test.yml inventory_file=/home/ec2-user/inventory
..
id: 5affbf066fb123179539c2e2
status: succeeded
parameters:
  cwd: /opt/stackstorm/packs/ansible
  inventory_file: /home/ec2-user/inventory
  playbook: /home/ec2-user/ios_test.yml
result:
  failed: false
  return_code: 0
  stderr: ''
  stdout: "
PLAY [ios] *********************************************************************

TASK [config_test] *************************************************************
changed: [10.1.1.67]

PLAY RECAP *********************************************************************
10.1.1.67               : ok=1    changed=1    unreachable=0    failed=0
"
  succeeded: true

正常に完了しました。

ルーター側で、設定が実際に反映されているか確認します。

Route101#sh run | inc ntp
ntp server 10.1.1.1

無事に設定が入りました。


■ まとめ

StackStorm の st2 run コマンドから Ansible 経由で Cisco IOS ルーターに設定投入できたことを確認しました。普通にAnsible 単体で利用するときと環境が別なので、たとえば、 ansible.cfg の置き場には注意が必要です。

今回は、Workflow に組み込むのではなく st2 run コマンドによる単発の実行でしたが、Ansible と連携できるのは魅力的に感じました。

Ansible 2.5.3 で ios_config モジュールのコンフィグバックアップ時に defaults オプションを参照するようになった

■ はじめに

ios_config モジュールには、コンフィグのバックアップファイルを生成する際に利用するコマンドを show running-config にするか、show running-config all または full (以降 show run all) にするかを決める、defaults というオプションがあります。 今までこのオプションは、backup: yes による、コンフィグバックアップの際は参照されませんでしたが、Ansible 2.5.3 で参照されるように仕様変更されました。

先にまとめると、backup: yes による コンフィグバックアップに利用されるコマンドは以下の様にります。

 Ansible バージョン defaults: no (デフォルト) defaults: yes
2.4.4 show run show run
2.5.2 show run all show run all
2.5.3 show run show run all

この記事ではこの仕様変更について簡単に動作検証を行います。 上記表内の 2.4.4 のログは割愛します。

  • 関連issue

Module ios_config ingores parameter defaults in combination with config backup · Issue #39724 · ansible/ansible · GitHub


■ ネットワーク機器側の事前確認

コンフィグバックアップの結果が show run の結果なのか show run all の結果なのかをあとの動産検証で判断するために、事前にネットワーク機器側で手動で両コマンドの結果を確認しておきます。

  • show run の結果
ip-172-31-38-162#sh running-config
Building configuration...

Current configuration : 3852 bytes
!
! Last configuration change at 05:37:12 UTC Fri May 18 2018 by ec2-user
!
version 16.7
service timestamps debug datetime msec
(略)
  • show run all の結果
ip-172-31-38-162#sh running-config all
Building configuration...

Current configuration with default configurations exposed : 350668 bytes
!
! Last configuration change at 05:37:12 UTC Fri May 18 2018 by ec2-user
!
version 16.7
downward-compatible-config 16.7
no service log backtrace
no service config
no service exec-callback
no service nagle
service slave-log
no service slave-coredump
no service pad to-xot
no service pad from-xot
no service pad cmns
service pad
no service telnet-zeroidle
no service tcp-keepalives-in
no service tcp-keepalives-out
service timestamps debug datetime mse
(略)

上記は抜粋となりますが、 show run allversion のあとに数々の no から始まるココンフィグが表示されるという特徴があることが分かりました。


■ 動作検証(Ansible 2.5.2)

仕様変更前の Ansible 2.5.2 で defaults: no と、 defaults: yes それぞれのケースで試します。

2.5.2 / defaults: no

Playbook

以下のように defaults: no (デフォルトのため指定なしと同じ) の Playbook を用意します。

- hosts: ios
  gather_facts: no
  connection: network_cli
    
  tasks:
    - name: config backup
      ios_config:
        defaults: no    # here
        backup: yes

コンフィグバックアップ結果

以下のコンフィグファイルが生成されした。

Building configuration...

Current configuration with default configurations exposed : 350668 bytes
!
! Last configuration change at 05:37:12 UTC Fri May 18 2018 by ec2-user
!
version 16.7
downward-compatible-config 16.7
no service log backtrace
no service config
no service exec-callback
no service nagle
(略)

このように、 show run の結果となりました。

2.5.2 / defaults: yes

Playbook

以下のように defaults: yes の Playbook を用意します。

- hosts: ios
  gather_facts: no
  connection: network_cli
    
  tasks:
    - name: config backup
      ios_config:
        defaults: yes    # here
        backup: yes

コンフィグバックアップ結果

以下のコンフィグファイルが生成されした。

`` Building configuration...

Current configuration with default configurations exposed : 350668 bytes ! ! Last configuration change at 05:37:12 UTC Fri May 18 2018 by ec2-user ! version 16.7 downward-compatible-config 16.7 no service log backtrace no service config no service exec-callback no service nagle (略) ``

このように、先ほどと同じく show run all の結果となりました。Ansible 2.5.2 では defaults オプションが参照されていないことが分かります。


■ 動作検証(Ansible 2.5.3)

次に、仕様変更前の Ansible 2.5.3 で defaults: nodefaults: yes それぞれのケースで試します。

2.5.3 / defaults: no

Playbook

以下のように defaults: no (デフォルトのため指定なしと同じ) の Playbook を用意します。

- hosts: ios
  gather_facts: no
  connection: network_cli
    
  tasks:
    - name: config backup
      ios_config:
        defaults: no    # here
        backup: yes

コンフィグバックアップ結果

以下のコンフィグファイルが生成されした。

Building configuration...

Current configuration : 3852 bytes
!
! Last configuration change at 05:37:12 UTC Fri May 18 2018 by ec2-user
!
version 16.7
service timestamps debug datetime msec
service timestamps log datetime msec
platform qfp utilization monitor load 80
no platform punt-keepalive disable-kernel-core
platform console virtual
(略)

このように、 show run の結果となりました。

2.5.3 / defaults: yes

Playbook

以下のように defaults: yes の Playbook を用意します。

- hosts: ios
  gather_facts: no
  connection: network_cli
    
  tasks:
    - name: config backup
      ios_config:
        defaults: yes    # here
        backup: yes

コンフィグバックアップ結果

以下のコンフィグファイルが生成されした。

Building configuration...

Current configuration with default configurations exposed : 350668 bytes
!
! Last configuration change at 05:37:12 UTC Fri May 18 2018 by ec2-user
!
version 16.7
downward-compatible-config 16.7
no service log backtrace
no service config
no service exec-callback
no service nagle
(略)

このように、 show run all の結果となりました。 Ansible 2.5.3 では defaults オプションが参照されていることが分かります。


■ まとめ

Ansible 2.5.3 で ios_config モジュールのコンフィグバックアップ時に defaults オプションを参照するようになったこと確認しました。 もし、backup: yesdefaults: yes を併用されている場合は、挙動の変化にご注意いただければと思います。

参考 Ansible 2.5.3 CANGELOG

ansible/CHANGELOG-v2.5.rst at stable-2.5 · ansible/ansible · GitHub

[Ansible] ios_config モジュールで意図せず chaneged になってしまうバグが 2.5.3 で修正された

はじめに

Ansible 2.5.0 - 2.5.2 の ios_configモジュールには、コンフィグが変更されていないにもかかわらず意図せず changed になってしまうバグがありました。2018/05/18 にリリースされた Ansible 2.5.3 で修正されましたので、簡単な動作確認をします。

バグの詳細

Ansible 2.5.0 - 2.5.2 の ios_config では、lines オプションで指定したコンフィグが、すでに入っているかどうかを比較する際に、デフォルトコンフィグも明示的に表示させるる、show running-config all または、 show running-config full コマンド(以降 show run all)の結果を取得していました。

本来は、 defaults: yes なら show run all の結果と、 defaults: no なら show run の結果と比較するのですが、defaults オプションが無視され、常にshow run all の結果と比較する挙動となっていました。

問題(意図せず changed)になってしまうのは、show run で表示したときと show run all で表示したときとで、表記が変わるコマンドを投入しようとした場合です。

例えば、snmp-server community XXXXX RO というコンフィグを投入すると、

  • show run では snmp-server community XXXXX RO
  • show run all では snmp-server community XXXXXv1default RO

のように、コンフィグ表記が異なります。(私の環境調べ)

このため、実際はすでに設定されている snmp-server community XXXXX RO (show run上の表記) を ios_config モジュールで投入しようとする際、 snmp-server community XXXXX ROshow run all の結果に含まれない(snmp-server community XXXXXv1default ROとは一致しない)ため、snmp-server community XXXXX RO を再度を投入します。 結果としては、コンフィグの実質的な内容には変更ありません。

関連issue/PR

ios_config module: Always "changed" will come out. "server community" and "snmp-server community" · Issue #37550 · ansible/ansible · GitHub

Fetch ios default config is defaults is enabled by ganeshrn · Pull Request #39741 · ansible/ansible · GitHub

Fix fetching ios default running config by ganeshrn · Pull Request #39475 · ansible/ansible · GitHub

Ansible 2.5.3 CHANGELOG

ansible/CHANGELOG-v2.5.rst at stable-2.5 · ansible/ansible · GitHub


■ バグの再現(Ansible 2.5.2)

Ansible 2.5.2 でバグの再現を試みます。

事前状態確認

ネットワーク機器側の事前状態としては、show run の結果に snmp-server community XXXXX RO がすでに含まれる状態です。

csr1# show run | inc snmp-server
snmp-server community XXXXX RO

なお、show run all だと以下のように、以下のような表記になります。

csr1# show run all | inc snmp-server 
snmp-server community XXXXXv1default RO

Playbook

以下のPlaybookを用意します。

- hosts: ios
  gather_facts: no

  tasks:
    - name: config test
      ios_config:
        lines:
          - snmp-server community XXXXX RO

実行結果

(ansible252) [vagrant@centos7 vagrant]$ ansible-playbook -i inventory ios_test.yml

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

TASK [config_test] ***********************************************************
changed: [10.0.8.232]

PLAY RECAP *******************************************************************
10.0.8.232               : ok=1    changed=1    unreachable=0    failed=0

このように、show run の結果に snmp-server community XXXXX RO がすでに含まれているのに changed となってしまいました。 前述のように、show run ではなく show run all の結果と比較してしまうためです。


■ バグ修正の確認(Ansible 2.5.3)

本バグが修正されたことを確認します。

Playbook

以下のPlaybookを用意します。(先ほどの 2.4.2 のときと同じです。)

- hosts: ios
  gather_facts: no

  tasks:
    - name: config test
      ios_config:
        lines:
          - snmp-server community XXXXX RO

実行結果

(ansible253) [vagrant@centos7 vagrant]$ ansible-playbook -i inventory ios_test.yml

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

TASK [config_test] ***********************************************************
ok: [10.0.8.232]

PLAY RECAP *******************************************************************
10.0.8.232               : ok=1    changed=0    unreachable=0    failed=0

今度は changed になりませんでした。意図通りの結果です。


■ まとめ

ios_config モジュールで意図せず chaneged になってしまうバグの再現と修正確認を行いました。 Ansible 2.5.0 - 2.5.2 をお使いでこのような現象でお困りの場合、2.5.3 へのアップデートを検討してみてはいかがでしょうか。

devel ブランチの Ansible を pip でインストールする方法

devel ブランチで開発中のコードで動作検証が必要な場合、pip では以下のコマンドでインストールします。

pip install git+https://github.com/ansible/ansible.git@devel

・引用元

Installation Guide — Ansible Documentation

Ansible でNW機器のインターフェースが意図通りのVLANに所属しているかチェックする(associated_interfaces)

■ はじめに

Ansible 2.4 から、ネットワークモジュールに、設定だけでなく「意図した状態か」チェックする Declarative Intent という機能が追加されました。

参考 https://www.ansible.com/blog/networking-features-in-ansible-2-4

Ansible 2.4 時点では、 例えば ios_interfaeや、junos_interfaceモジュールなどの、以下のオプションによりチェックすることができます。

  • neighbors: CDP/LLDPのホスト、ポート情報
  • state: インターフェースの状態(presentabsentupdown
  • rx_rate: 受信レート(bps)がとの程度か
  • tx_rate: 送信レート(bps)がとの程度か

あくまで、チェックするためのオプションであるため、指定した通りに設定するという機能ではりません。

この記事では、Ansible 2.5 でios_vlanモジュールに追加された、associated_interfaces オプションを利用し、想定したインターフェースが特定の VLAN に所属されているかチェックする方法をご紹介します。


■ ネットワーク機器側の状態

ネットワーク機器側の VLAN と インターフェースの所属関係は以下の状態とします。

Switch13(config)#do sh vlan

VLAN Name                             Status    Ports
---- -------------------------------- --------- -------------------------------
1    default                          active    Gi1/0/1, Gi1/0/2, Gi1/0/3, Gi1/0/4
                                                Gi1/0/5, Gi1/0/6, Gi1/0/7, Gi1/0/8
                                                Gi1/0/9, Gi1/0/12, Gi1/0/14
                                                Gi1/0/15, Gi1/0/16, Gi1/0/17
                                                Gi1/0/18, Gi1/0/19, Gi1/0/20
                                                Gi1/0/21, Gi1/0/22, Gi1/0/23
13   VLAN0013                         active    Gi1/0/13
99   VLAN0099                         active    Gi1/0/10, Gi1/0/11
192  VLAN0192                         active    Gi1/0/24

(コンフィグ抜粋)

interface GigabitEthernet1/0/9
!
interface GigabitEthernet1/0/10
 switchport access vlan 99
 switchport mode access
!
interface GigabitEthernet1/0/11
 switchport access vlan 99
 switchport mode access
!
interface GigabitEthernet1/0/12
!

今回は、vlan99(Gi1/0/10, Gi1/0/11)をチェック対象とします。


■ 意図通りの状態の場合

チェックが成功するような Playbook で試します。

Playbook

先程ネットワーク機器側で確認したように、vlan99 に GigabitEthernet1/0/10GigabitEthernet1/0/11 が所属していることをチェックします。 所属インターフェースの指定には、 associated_interfaces オプションを利用します。

- hosts: ios
  gather_facts: no

    - name: check vlan
      ios_vlan:
        vlan_id: 99
        associated_interfaces:
            - GigabitEthernet1/0/10
            - GigabitEthernet1/0/11

実行結果

以下のように、ok となります。

(ansible25) [vagrant@centos7 vagrant]$ ansible-playbook -i inventory ios_test.yml

PLAY [ios] **************************************************************************************************************
TASK [check vlan] *******************************************************************************************************
ok: [192.168.1.13]

PLAY RECAP **************************************************************************************************************
192.168.1.13               : ok=1    changed=0    unreachable=0    failed=0


■ 意図していない状態の場合

今度は、チェックが失敗するような Playbook で試します。

Playbook

先程とは異なり、 GigabitEthernet1/0/12 という vlan99 に所属しないインターフェースを追加で指定してみます。

- hosts: ios
  gather_facts: no

    - name: check vlan
      ios_vlan:
        vlan_id: 99
        associated_interfaces:
            - GigabitEthernet1/0/10
            - GigabitEthernet1/0/11
            - GigabitEthernet1/0/12    # not vlan99

実行結果

今度は、failed となります。"Interface GigabitEthernet1/0/12 not configured on vlan 99" というていねいなエラーメッセージが確認できます。

(ansible25) [vagrant@centos7 vagrant]$ ansible-playbook -i inventory ios_test.yml

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

TASK [check vlan] *******************************************************************************************************
fatal: [192.168.1.13]: FAILED! => {"changed": false, "msg": "Interface GigabitEthernet1/0/12 not configured on vlan 99"}
        to retry, use: --limit @/vagrant/ios_test.retry

PLAY RECAP **************************************************************************************************************
192.168.1.13               : ok=0    changed=0    unreachable=0    failed=1


■ 注意点

2点ほど注意点をあげます。

インターフェース名の指定はコンフィグ上の表記通りに

associated_interfaces オプションで指定するインターフェース名は、コンフィグ上の表記通りにする必要があります。

  • 正しい表記の指定例
        associated_interfaces:
            - GigabitEthernet1/0/10
            - GigabitEthernet1/0/11
  • 誤った表記の指定例(インターフェース名を省略してしまっている)
        associated_interfaces:
            - Gi1/0/10
            - Gi1/0/11

誤った表記の指定の仕方をすると、正しくチェックができませんので注意が必要です。

インターフェース名の指定が不足していても ok となる

例えば、vlan99に GigabitEthernet1/0/10GigabitEthernet1/0/11 が所属している状態で、以下のように associated_interfaces オプションで GigabitEthernet1/0/10 のみを指定した場合でも、チェック結果は ok となります。

    - name: check vlan
      ios_vlan:
        vlan_id: 99
        associated_interfaces:
            - GigabitEthernet1/0/10

そのため、チェックの意味合いとしては、

  • associated_interfaces オプションで指定したインターフェースが vlan_id オプションで指定した vlan に所属していること

になります。

  • vlan_id オプションで指定した vlan に associated_interfaces オプションで指定したインターフェースが過不足なく所属していること

ではないので注意が必要です。


■ まとめと補足

現段階ではチェックできるオプションは多くないですが、今後増えてくるかもしれません。 チェックしたいオプションがある場合は、利用を検討してみてはいかがでしょうか。

また、チェック(テスト)するという観点では、napalm_validate というサードパーティモジュールを利用する方法もあります。詳細は、AnsibleとNAPALMでネットワークをテストする をご参照下さい。