てくなべ (tekunabe)

ansible / network / automation

[Ansible] OSPF 設定後にネイバーが張られるまで待つ(until の活用)

はじめに

Ansible には、指定した条件を満たすまでタスクを繰り返す until というループ機能があります。

until を利用すると、ネットワーク機器に設定を投入した後に、期待した状態になるまで待つ、といった処理を Playbook で実現できます。

この記事では、Cisco IOS の機器に OSPF の設定をして、ネイバーが張られるまで待つ Playbook をご紹介します。

検証環境


■ 想定環境

以下のように、2 台の Cisco IOS のネットワーク機器の環境を想定します。 2台はお互いに GigabitEthernet2 で接続されています。インターフェースの IP アドレスは設定済みですが、OSPF はまだ設定していません。

f:id:akira6592:20200209113500p:plain
2台のCisco IOS でOSPFを設定する


■ 環境の準備

ネイバーが張られるまで待つには、show ip ospf neighbor コマンドの結果を確認します。コマンドの結果を正規表現などでマッチさせる方法では少々危ういので、結果をパースして確認することにします。

パースするために TextFSM というライブラリをインストールします。

  • TextFSM のインストール
pip intall textfsm

また、TextFSM ではパースするために各コマンドごとにテンプレート準備する必要があります。 ありがたいことに、networktocode/ntc-templates というリポジトリで、様々なベンダーのコマンドのテンプレートが作られています。今回は、Cisco IOSshow ip ospf neighbor コマンドの結果をパースしたいので、cisco_ios_show_ip_ospf_neighbor.textfsm を利用します。ダウンロードして templates ディレクトリに配置します。

(もちろんnetworktocode/ntc-templatesリポジトリをまるごとcloneなどしても構いません。今回は上記の構成を前提に説明します。)

ファイル構成

ファイルの構成は以下のようにします。

.
├── configure_ospf.yml # Playbook (後述)
├── group_vars
│   └── ios.yml   # 接続情報を定義(詳細省略)
├── inventory.ini # 2台をグループ ios で定義(詳細省略)
└── templates
    └── cisco_ios_show_ip_ospf_neighbor.textfsm # 先程ダウンロードしたもの


■ サンプル Playbook の作成

処理を定義する Playbook を以下の通り作成します(説明は後述)。

- hosts: ios
  gather_facts: no

  tasks:
    - name: 1. configure OSPF
      ios_config:
        lines:
          - network 10.0.1.0 0.0.0.255 area 0
        parents:
          - router ospf 1

    - name: 2. check neighbor
      ios_command:
        commands:
          - show ip ospf neighbor
      register: result
      vars:
        parsed: "{{ result.stdout[0] | parse_cli_textfsm('./templates/cisco_ios_show_ip_ospf_neighbor.textfsm') }}" 
      until: "'FULL' in (parsed | selectattr('INTERFACE', '==', 'GigabitEthernet2') | list | first ).STATE"
      delay: 10
      retries: 6

    - name: 3. debug status
      debug:
        msg: "{{ result.stdout_lines[0] }}"

以下、各タスクについて説明します。

1. configure OSPF

ios_config モジュールを利用して、OSPF のコンフィグを投入します。

具体的には、parentslines オプションの指定により以下のコンフィグを投入します。

router ospf 1
  network 10.0.1.0 0.0.0.255 area 0

2. check neighbor

このタスクが一番のポイントです。ネイバーが張られるまで待ちます。少し複雑ですが、以下の流れで処理します。

(1) show コマンドの実行

ios_command モジュールを利用して、show ip ospf neighbor コマンドを実行し、結果を変数 result に格納します。 この時点での result.stdout[0] の内容は以下の通りです。(ios1 側の例)

Neighbor ID     Pri   State           Dead Time   Address         Interface
2.2.2.2           1   2WAY/DROTHER    00:00:39    10.0.1.2        GigabitEthernet2

(2) コマンド実行結果のパース

vars ディレクティブで、コマンド結果変数 result をパースして、変数 parsed に格納する。パースは、内部で TextFSM を呼び出す parse_cli_textfsm フィルター を利用します。対応するパーステンプレートファイルは、「環境の準備」でダウンロードしたものを指定します。

この時点での parsed の内容は以下の通りです。(ios1 側の例)

[
 {
     "ADDRESS": "10.0.1.2",
     "DEAD_TIME": "00:00:38",
     "INTERFACE": "GigabitEthernet2",
     "NEIGHBOR_ID": "2.2.2.2",
     "PRIORITY": "1",
     "STATE": "2WAY/DROTHER"
  }
]

なお、このタスクの vars ディレクティブで定義した変数 parsed のスコープは、あくまでこのタスクの範囲なのでご注意ください。

参考:

ネットワーク機器のコマンド結果をパースする parse_cli_textfsm フィルタープラグインを試す (Ansible 2.4新機能) - てくなべ (tekunabe)

(3) 繰り返し条件

until で、このタスクの終了条件を指定します。ここでは、パースしたコマンド実行結果である parsed 内の STATEFULL という文字列が含まれていたら終了とします。

今回の場合はネイバーは1つだけなので、以下のように「parsed の最初の要素の STATE」という指定方法でも構いません。

      until: "'FULL' in (parsed.0.STATE)"

ですが、拡張性を持たせるため、INTERFACEGigabitEthernet2 であるという条件にして、対象のネイバーを抽出します。

      until: "'FULL' in (parsed | selectattr('INTERFACE', '==', 'GigabitEthernet2') | list | first ).STATE"

until の動作を決めるリトライ間隔は delay 、リトライ回数を retries で指定します。終了条件を満たすまで(1)に戻って繰り返しこのタスクを実行します。一定回数繰り返しているうちにネイバーが張られ、STATEFULL/DRFULL/BDR などに変わります。そして、ループを終了し、次のタスクに移ります。

3. debug status

デバッグとして、show ip ospf neighbor コマンドの実行結果を表示します。

Playbook の各タスクの説明は以上です。次は Playbook の実行です。


■ Playbook 実行

Playbook を実行します。

$ ansible-playbook -i inventory.ini configure_ospf.yml 
PLAY [ios] *******************************************************************************************************

TASK [1. configure OSPF] *****************************************************************************************
changed: [ios1]
changed: [ios2]

TASK [2. check neighbor] *****************************************************************************************
FAILED - RETRYING: 2. check neighbor (6 retries left).  # 10秒ごとに繰り返しチェック中
FAILED - RETRYING: 2. check neighbor (6 retries left).
FAILED - RETRYING: 2. check neighbor (5 retries left).
FAILED - RETRYING: 2. check neighbor (5 retries left).
FAILED - RETRYING: 2. check neighbor (4 retries left).
FAILED - RETRYING: 2. check neighbor (4 retries left).
FAILED - RETRYING: 2. check neighbor (3 retries left).
FAILED - RETRYING: 2. check neighbor (3 retries left).
ok: [ios1]    # ネイバーが張れた
ok: [ios2]    # ネイバーが張れた

TASK [3. debug status] *******************************************************************************************
ok: [ios2] => {
    "msg": [
        "Neighbor ID     Pri   State           Dead Time   Address         Interface",
        "1.1.1.1           1   FULL/BDR        00:00:38    10.0.1.1        GigabitEthernet2"
    ]
}
ok: [ios1] => {
    "msg": [
        "Neighbor ID     Pri   State           Dead Time   Address         Interface",
        "2.2.2.2           1   FULL/DR         00:00:37    10.0.1.2        GigabitEthernet2"
    ]
}

PLAY RECAP *******************************************************************************************************
ios1                       : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
ios2                       : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

ネイバーが張られるまでタスク 2. check neighbor を繰り返し実行し、張られたら次のたタスク3. debug status に進んだことが分かります。

ネイバーがはれないと・・

何らかの問題があり、OSPF のネイバーが張れないと、タスク 2. check neighbor がエラーになって終了します。


■ さいごに

この記事では、Cisco IOS の機器に OSPF の設定をして、ネイバーが張られるまで待つ Playbook をご紹介しました。

このように、show コマンドを実効するモジュールと、until を組み合わせると、期待した状態まで待つ、といったことが Playbook で実現できます。

ネットワーク機器への作業は、OSPF の他にも設定してから待つ必要がある作業もあるので、until は有効ではないでしょうか。