3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Ansibleのplaybookのjunos commandモジュールで実行した結果に応じてtry-catch的に後続タスクを続けたい

3
Last updated at Posted at 2020-06-11

0.はじめに

こんにちは。都内でエンジニアをしている、@gkzvoiceです。
今回は、AnsibleのplaybookでAnsibleっぽい?プログラマブルなエラーハンドリングを知ったので、早速アウトプットしていきます。

1.やりたかったこと

  • 実行結果に応じてどのタスクを行うか決めたい
  • 具体的には、直前に行われたタスクの成否問わず、必ず実行結果をslackに通知する
    • Ansibleでは直前のタスクが失敗した場合、後続タスクは実行されないですが、これは避けたい!!
  • 直前のタスクが失敗した場合、実行結果を通知する際@here をつけて、try-catch的にエラーキャッチしたい

こんなかんじ!

  • 異常時のみ@hereとグレーがかったFailedがついている
    show_ver.png

2. 環境/バージョン情報

  • Ansible実行環境(ローカル)
    • ubuntu20.04
    • ansible 2.9.6
    • python 3.8.2 
  • Ansibleでコマンドを流す対象機器
    • Junos 15.1X53-D63.9

※Juniper社のvlabsを使って検証した。
Juniper Vlabsホームページ

3. playbookで実行するコマンドを実行した様子とそのコマンドの成否判定

  • コマンドの出力結果のサンプル
jcluser@Template-vQFX-Light> show version | no-more                             
fpc0:
--------------------------------------------------------------------------
Hostname: Template-vQFX-Light
Model: vqfx-10000
Junos: 15.1X53-D63.9
JUNOS Base OS boot [15.1X53-D63.9]
JUNOS Base OS Software Suite [15.1X53-D63.9]
JUNOS Online Documentation [15.1X53-D63.9]
JUNOS Crypto Software Suite [15.1X53-D63.9]
JUNOS Packet Forwarding Engine Support (qfx-10-f) [15.1X53-D63.9]
JUNOS Kernel Software Suite [15.1X53-D63.9]
JUNOS Web Management [15.1X53-D63.9]
JUNOS Enterprise Software Suite [15.1X53-D63.9]
JUNOS SDN Software Suite [15.1X53-D63.9]
JUNOS Routing Software Suite [15.1X53-D63.9]
JUNOS py-base-i386 [15.1X53-D63.9]

{master:0}
jcluser@Template-vQFX-Light> 
  • コマンドの成否判定
    • "Junos: 15.1|Junos: 15.2 "であれば成功
    • 異なる場合、失敗とする
jcluser@Template-vQFX-Light> show version | match "Junos: 15.1|Junos: 15.2 "    
Junos: 15.1X53-D63.9

{master:0}
jcluser@Template-vQFX-Light> 

4.学んだこと

  • 4-1. コマンドの結果を判定するときに使うのはwhenではなくfailed_when: ${OR条件}
  • 4-2. ${コマンドの実行結果}.stdoutは文字列ではなくリスト型
  • 4-3. エラーキャッチをするならBlock/Rescue/Alwaysディレクティブ
    • 今回はBlock/Rescueを使用

4-1. コマンドの実行実行結果を判定するときに使うのはwhenではなくfailed_when: ${OR条件}

※resultはコマンドの実行結果を格納した変数

- when: "{{ result is search("xxx|aaa") }}"
+ failed_when: result.stdout[0] is not search("xxx|aaa")

@akira6592 さんに教えていただきました。
ありがとうございます。

when は、そのタスクを実行するかの条件なので、まだ register: result が評価されておらず、result が未定義になっている、ということだと思います。
when ではなく、failed_when でいかがでしょうか。

出所:Ansibleユーザーグループのslack #qa

whenは、変数の値確認のときに使う


vars:
  url: "http://example.com/users/foo/resources/bar"

tasks:
    - debug:
        msg: "matched pattern 1"
      when: url is match("http://example.com/users/.*/resources/.*")

    - debug:
        msg: "matched pattern 2"
      when: url is search("/users/.*/resources/.*")

    - debug:
        msg: "matched pattern 3"
      when: url is search("/users/")

    - debug:
        msg: "matched pattern 4"
      when: url is regex("example.com/\w+/foo")

出所:Tests — Ansible Documentation

4-2. ${コマンドの実行結果}.stdoutは文字列ではなくリスト型

- {{ result.stdout }}"
- {{ result.stdout_lines }}"
+ {{ result.stdout[0] }}"

こちらも@akira6592 さんに教えていただきました。
ありがとうございます。

result.stdout の値は文字列ではなくリストなので、うまく解釈できなかったのかもしれません。

参考:junos commandモジュールで実行した結果をslackに通知出来ない

Ansibleの公式ドキュメントにもリスト形式で返却と書いてありました。

Screenshot from 2020-06-11 23-15-31.png

出所:junos_command – Run arbitrary commands on an Juniper JUNOS device — Ansible Documentation

※ ↑で使っているslackモジュールや"{{ result.stdout[0] | default('None', true)}}" の書き方については、手前味噌ですが、こちらの記事をご参照ください。
Ansibleのslackモジュールを使う際に必要な"token"でハマった話他[token/トークンの取り扱いと変数のdefault値など]

4-3.エラーキャッチをするならBlock/Rescue/Alwaysディレクティブ

  • try-catch的?に、エラーハンドリングをすることがBlock/Rescue/Alwaysディレクティブでは可能です。

Blocks also introduce the ability to handle errors in a way similar to exceptions in most programming languages.


tasks:
 - name: Handle the error
   block:
     - debug:
         msg: 'I execute normally'
     - name: i force a failure
       command: /bin/false
     - debug:
         msg: 'I never execute, due to the above task failing, :-('
   rescue:
     - debug:
         msg: 'I caught an error, can do stuff here to fix it, :-)'

出所:Blocks — Ansible Documentation

注意点

  • Block/Rescue/Alwaysディレクティブがエラーハンドリングできるタスクは、failedとなるタスクのみです。
  • 未定義なタスクや不到達なホストがあるためにエラーとなった場合、Block/Rescue/Alwaysディレクティブでもエラーハンドリングすることはできません。

Blocks only deal with ‘failed’ status of a task. A bad task definition or an unreachable host are not ‘rescuable’ errors.

出所:Blocks — Ansible Documentation

Block/Rescue/Alwaysディレクティブ v.s. try-catch

Block/Rescue/Always try-catch
Block try
rescue except
always finally

なお、Block/Rescue/Alwaysディレクティブについても @akira6592 さんに教えていただきました。
@akira6592 さんすごすぎです。
ありがとうございます!

五月雨ですみません。
block/rescue/always 使うのも Ansible っぽくていいかもしれません。
https://docs.ansible.com/ansible/latest/user_guide/playbooks_blocks.html#blocks-error-handling

出所:Ansibleユーザーグループのslack #qa

5.サンプルコードとplaybookの実行コマンド

  • invetntoryファイル各種とplaybookのサンプルコードです。

inventory


[junos]
***.***.***.***
↑対象機器のipアドレス
---
# file: group_vars/junos
ansible_connection: network_cli 
ansible_network_os: junos
ansible_port: <JUNOS_PORT>
ansible_ssh_user: <JUNOS_USERNAME>
ansible_ssh_pass: <JUNOS_PASSWORD>
playbook/show_version.yaml
---
- hosts: junos
  connection: local
  gather_facts: no
  become: yes

  vars:
    ptn: "Junos: 15.1|Junos: 15.2"
    #ptn: "Junos: 14.1|Junos: 14.2"

  tasks:
    - name: main
      block:
        - name: display version
          junos_command:
            commands:
              - show version | no-more
          when: ansible_network_os == "junos"
          register: result
          failed_when: result.stdout[0] is not search(ptn)
          changed_when: false
        - name: debug result.stdout_lines
          debug:
            msg: "{{ result.stdout_lines }}"
        
        - name: Use the attachments API
          slack:
            username: "{{ lookup('env', 'SLACKBOT_NAME') | default('ansibleBot')  }}"
            token: "{{ lookup('env', 'INCOMING_WEBHOOK_URL_SLACKFORMAT') }}"
            channel: "{{ lookup('env', 'DST_CHANNEL') }}"
            attachments:
            - title: display version [ " {{ ptn }} "]
              text: "Successful"
              color: "#ff00dd"
              fields:
              - title: Hostname
                value: "{{ inventory_hostname }}"
                short: False
              - title: "The output"
                value: "{{ result.stdout[0] | default('None', true)}}"
                short: False

      rescue:
        - name: Use the attachments API
          slack:
            username: "{{ lookup('env', 'SLACKBOT_NAME') | default('ansibleBot')  }}"
            token: "{{ lookup('env', 'INCOMING_WEBHOOK_URL_SLACKFORMAT') }}"
            channel: "{{ lookup('env', 'DST_CHANNEL') }}"
            attachments:
            - title: display current time [ " {{ ptn }} "]
              text: <!here> `Failed`
              color: "#ff00dd"
              fields:
              - title: Hostname
                value: "{{ inventory_hostname }}"
                short: False
              - title: "The output"
                value: "{{ result.stdout[0] | default('None', true)}}"
                short: False

※サンプルコードは、Githubでも公開しております。
ansible-sample

  • playbookの実行コマンド
$ ansible-playbook -i inventory/hosts playbook/show_varsion.yaml
  • デバッグしたい場合は-vオプション
  • vの数だけデバッグの量が増える点はsshの-vオプションと同じ
$ ansible-playbook -v -i inventory/hosts playbook/show_varsion.yaml

6. 要改善点

  • slackモジュールがblockディレクティブとrescueディレクティブの2箇所に書いてあるところ。

    • うまいことひとつにまとめられないか。
    • Ansibleがタスクの成否の結果によって、どのように通知するテキストを変えるか。
  • slackモジュールがblockディレクティブとrescueディレクティブの2箇所に書いてある理由
     - タスクが成功すれば、Successful、失敗すれば<!here> Failedとslackに通知したいので、blockディレクティブとrescueディレクティブそれぞれにテキストでベタ書きとした。

P.S. Twitterもやってるのでフォローしていただけると泣いて喜びます:)
@gkzvoice

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?