てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] ios_config モジュールの src で指定するファイルの内容には正しいインデントが必要


これは Ansible Blogger 2018 (sponsored by Red Hat) Advent Calendar 2018 の22日目の記事です。

■ はじめに

Ansible には、 Cisco IOS ネットワーク機器に対して設定変更コマンドを実行できる ios_config モジュールがあります。

ios_config モジュールには、Playbook 内に実行したいコマンドを直接指定する lines オプションのほかに、src オプションがあります。 src には、別ファイルにコマンドを書いて(以下、コマンドファイル)、そのファイル名を指定します。 コマンドファイルはただコマンドを列挙するだけでなく、階層を考慮して適切にインデントする必要があります。

f:id:akira6592:20181222130211p:plain
正しいインデントが必要

この記事では、どのようにインデントすればよいのか、なぜインデントが必要なのか、についてご説明します。


■ まとめ

少し長くなるので、はじめにまとめます。

  • ios_config モジュールの src で指定するコマンドファイルは階層を示すためにインデントが必要
  • インデントの形式はネットワーク機器側の show run で表示される形式と同じ
  • インデントがない場合、すべてグローバルコンフィギュレーションモードで実行しようとしてしまう
  • インデントがある場合、コンフィグの階層を解釈したうえで正しく実行する


■ そもそも src オプションとは

はじめにでもご紹介した通り、コマンドファイルを指定するオプションです。

  • Playbook
- hosts: iosprg
  gather_facts: no

  tasks:
    - name: config test
      ios_config:
        src: config.txt    # ファイル名を指定
  • config.txt
interface GigabitEthernet3
 description TEST
 ip address 172.16.0.1 255.255.255.0

今回は、このコマンドファイルのインデントが重要というお話です。


■ どのようにインデントすればよいのか

以下の点だけおさえておけば大丈夫です。

ネットワーク機器側の show running-config で表示される形式と同じインデントをする

例えば、具体的には以下のような形式です。見慣れていると形式かと思います。

  • インデントあり
interface GigabitEthernet3
 description TEST
 ip address 172.16.0.1 255.255.255.0

コンフィグの階層をインデントで表したような形式です。

interface GigabitEthernet3 が、グローバルコンフィギュレーションモードで実行したいコマンドです。 スペースによってインデントされている description TESTip address 172.16.0.1 255.255.255.0 が、インターフェースコンフィギュレーションモードで実行したいコマンドです。


■ だめな書き方

一方、コンフィグの階層を作らずにインデントなしで以下のように書いてしまうと、Playbook 実行時にエラーが起こるなどの不都合が起きてしまいます。

  • インデントなし
interface GigabitEthernet3
description TEST
ip address 172.16.0.1 255.255.255.0

手作業でターミナルへコピペする分には、上記のようにインデントなしでも問題ありません。 すべてのコマンドを実行し、1行ごとにネットワーク機器側が必要に応じてモードを切り替えるためです。 (もちろん手作業の場合でも、見さすさの観点でインデントを入れるケースもよくあると思います。)

しかし、Ansible の ios_config モジュールの src で指定するコマンドファイルにおいては、コマンドの階層を考慮して適切にインデントする必要があります。

その理由についてご説明します。


■ インデントが必要な理由

ios_config モジュールは、コマンド実行前に、そのコマンドがネットワーク機器側に設定済みかどうか確認し、設定済みであれば実行しない、という仕組みです。設定済みかどうか確認する際に、コンフィグの階層まで含めて比較、確認します。

f:id:akira6592:20181217133821p:plain
階層を解釈して比較し差分を生成

階層まで含めて正しく比較するために、コマンドファイルのインデントが利用されます。

具体的な例で見ていきましょう。

インデントがないとコンフィグ階層が解釈されない

インデントがないと、ios_config モジュールはすべてグローバルコンフィグレーションモードで実行しようとします。その際、ネットワーク機器側のグローバルコンフィギュレーションレベルで設定済みかどうかを確認します。そして、設定済みのコンフィグはスキップされ実行しません。

例えば、もし

interface GigabitEthernet3
 no ip address

という状態のネットワーク機器に対して、

interface GigabitEthernet3
description TEST
ip address 172.16.0.1 255.255.255.0

というインデントがないコマンドファイルを src に指定して実行すると、 interface GigabitEthernet3 がスキップされて実行されません。 続いて、グローバルコンフィギュレーションモードのまま description TEST を実行しようとしてエラーになります。グローバルコンフィギュレーションモードでは desctiption コマンドが利用できないためです。

f:id:akira6592:20181217120626p:plain
インデントがない場合すべてグローバルレベルで比較

  • ansible-paybook コマンド実行時エラー抜粋
"module_stderr": "Traceback (...略...)ansible.module_utils.connection.ConnectionError: description TEST\r\n                   ^\r\n% Invalid input detected at '^' marker.\r\n\r\ncsr1000v(config)#\n",
  • ログを元にしたコマンド実行の再現イメージ(エラー)
csr1000v(config)#description TEST
                   ^
% Invalid input detected at '^' marker.
ncsr1000v(config)#

なお、これらの動作は、src の代わりに lines で以下のように指定した場合と同じです。このタスクでは親階層を示すコマンドである inteface GigabitEthernet3 含めてすべて lines オプションに指定しているため、正しく動作しません。

- name: config test
  ios_config:
    lines:
      - inteface GigabitEthernet3  # 正しくは parents オプションに指定すべき
      - description TEST
      - ip address 172.16.0.1 255.255.255.0

インデントがあるとコンフィグ階層が正しく解釈される

インデントがあれば、ios_config モジュールはコンフィグの階層を解釈します。解釈したうえで、ネットワーク機器側で設定済みかどうかを確認します。

例えば、もし

interface GigabitEthernet3
 no ip address

という状態のネットワーク機器(先ほどと同じ)に対して、

interface GigabitEthernet3
 description TEST
 ip address 172.16.0.1 255.255.255.0

というインデントがあるコマンドファイルを src に指定して実行すると、description TESTip address 172.16.0.1 255.255.255.0 コマンドが、interface GigabitEthernet3 配下である、という階層に解釈されます。そのため、interface GigabitEthernet3 は、親階層のコマンドとして必ず実行されます。

そして、差分コンフィグ生成の際、ネットワーク機器側の interface GigabitEthernet3description TESTip address 172.16.0.1 255.255.255.0 コマンドがないため、(コンフィグ階層を保ったまま)実行する必要があると判断されます。

その結果、以下の 3行がすべて実行されます。

interface GigabitEthernet3
 description TEST
 ip address 172.16.0.1 255.255.255.0

f:id:akira6592:20181217120807p:plain
インデントがあると階層を解釈して比較

  • コマンド実行イメージ(正常)
csr1000v(config)#interface GigabitEthernet3
csr1000v(config-if)#description TEST
csr1000v(config-if)#ip address 172.16.0.1 255.255.255.0

なお、これらの動作は、src の代わりに linesparents で以下のように指定した場合と同じです。このタスクでは親階層を示すコマンドである inteface GigabitEthernet3lines ではなくparents に指定しているため、正しく動作します。

- name: config test
  ios_config:
    parents:
      - inteface GigabitEthernet3     # 正しい指定
    lines:
      - description TEST
      - ip address 172.16.0.1 255.255.255.0


■ まとめ(再掲)

  • ios_config モジュールの src で指定するコマンドファイルは階層を示すためにインデントが必要
  • インデントの形式はネットワーク機器側の show run で表示される形式と同じ
  • インデントがない場合、すべてグローバルコンフィギュレーションモードで実行しようとしてしまう
  • インデントがある場合、コンフィグの階層を解釈したうえで正しく実行する