はじめに
molecule とは
molecule は、Ansible のロールをテストするフレームワークです。
これまで、バージョンアップするごとに結構インパクトの大きい変更があって、やや敬遠気味でした。バージョン 3.0 で、デフォルトの verifier(実際にテストを担うもの)が Ansible になったのをきっかけに重い腰を上げました。ちなみに少し前は デフォルトは testinfra
でした。
ネットワーク機器もテストできるらしい
「nw機器とかもテストできる(ansibleで触れるものはだいたいいける)」という情報を Ansible ユーザー会の slack の #molecule チャンネルで得ました。そこで、ネットワーク機器へ設定するロールのテスト自動化を molecule でやってみることにしました。
なお、テスト対象はネットワーク機器ですので、おそらく一番メジャーな molecule の使い方である「テスト用の docker コンテナを作って設定してテストして破棄する」という方法とは異なる点が多いです。(driverに delegate
を使うため)
(いまいち編、とは)
タイトルに「いまいち編」とつけているのは、molecule を触りはじめたばかりで、いろいろ至らない点がありそうだからです。なにかアドバイスございましたら、@akira6592までご連絡いただけると、とてもありがたいです。
動作確認環境
- molecule 3.0.3
- ansible 2.9.6
■ やりたいこと
ロールで行うこと
molecule 経由でテストすること
- ロール実行後、
show running-config
にntp server 10.0.0.123
があることを確認する
テストには様々な考え方があると思いますが、ここでは簡単なことを優先してこの内容にします。
シナリオ内のアクションでやること
default
シナリオを利用します。必要なアクションに絞ったシナリオを別途作ったほうがよいのでしょうが・・。
各アクションでの処理内容は以下のとおりです。
シナリオ内のアクション | 今回の処理内容 |
---|---|
dependency | なし |
lint | なし |
cleanup | NTPコンフィグの削除 |
destroy | なし |
syntax | なし |
create | なし |
prepare | なし |
converge | ロールの実行(NTPサーバー設定) |
idempotence | 冪等性確認 |
side_effect | なし |
verify | 意図したコンフィグ入ったか |
cleanup (再実行) | NTPコンフィグの削除 |
destroy (再実行) | なし |
コンテナを作って壊してと言う場合は、create
や destroy
を利用するようですが、今回のネットワーク機器はぽこぽこ作ったり壊したりできません。そのため、代わりに cleanup
アクションを利用します。
また、今回は扱いませんが、Arista の cEOS-LabのようなコンテナのネットワークOSを利用して、テスト用の環境を作って壊してする場合は、create
や destroy
が良いのだと思います。
■ 環境の準備
molecule のインストール
以下コマンドで molecule インストールします。
pip install molecule
docker は利用しませんので pip install docker
も不要です。
■ 各種実装
ロールやテストを実装していきます。
ロールディレクトリ一式の作成
molecule init
コマンドで、molecule が利用するディレクトリを含めた一式を作成します。ロール名は set_ntp
とします。
$ molecule init role set_ntp --> Initializing new role set_ntp... Initialized role in /Users/sakana/molecule/set_ntp successfully.
今回は使わないディレクトリが多いので削除します。
$ cd set_ntp $ rm -fr defaults/ handlers/ meta/ handlers/ tests/ vars/ files/ templates/
構成はこうなります。
$ tree . . ├── README.md ├── molecule │ └── default │ ├── INSTALL.rst │ ├── converge.yml │ ├── molecule.yml │ └── verify.yml └── tasks └── main.yml
ロール処理の実装
set_ntp
ロールのメイン処理である、tasks/main.yml
の内容を実装します。
Cisco IOS に対する NTP サーバーの設定なので、iso_ntp
モジュールを利用します。
tasks/main.yml
--- - name: set ntp server ios_ntp: server: 10.0.0.123 state: present
molecule の基本設定
molecule/default/molecule.yml
で molecule の全体的な基本設定をします。
driver に docker
ではなく、delegated
を利用する点が一番のポイントです。
molecule/default/molecule.yml
--- dependency: name: galaxy driver: name: delegated platforms: - name: ios1 provisioner: name: ansible inventory: hosts: all: children: ios: hosts: ios1: ansible_host: 10.0.0.254 # 対象のNW機器 vars: ansible_connection: network_cli ansible_network_os: ios ansible_user: user1 ansible_password: xxx_dummy_xxx ansible_python_interpreter: ~/envs/nwlab/bin/python verifier: name: ansible
provisioner.inventory
配下は、YAML でインベントリを書くときと同じ書式のようです。
今回は、検証用のネットワーク機器を動的プロビジョニングして設定、と言う流れではなく、すでにあるネットワーク機器へ設定します。そのため platforms も provisioner も同じネットワーク機器を指すことになります。
[2021/09/11 追記] 以下のように、既存のインベントリファイルや変数定義ファイルを指定することもできます。
provisioner: name: ansible inventory: links: hosts: ../../../inventory/hosts group_vars: ../../../inventory/group_vars/ host_vars: ../../../inventory/host_vars/
(公式ドキュメントより抜粋)
ロール呼び出しの実装: converge.yml
molecule init
コマンドで自動生成された molecule/default/converge.yml
は、set_ntp
ロールを呼び出して、設定変更を担当します。
これはこのままで大丈夫です。
molecule/default/converge.yml
--- - name: Converge hosts: all gather_facts: false tasks: - name: "Include set_ntp" include_role: name: "set_ntp"
テストの実装: verify.yml
molecule/default/verify.yml
も自動生成されます。が、中身はダミーの assert なので、実際にテストしたい内容に書き換えます。
ここでは、show running-config
に ntp server 10.0.0.123
があることを確認します。
molecule/default/verify.yml
--- - name: Verify hosts: all gather_facts: false tasks: - name: show run ios_command: commands: - show running-config register: resutl_show_run - name: assert ntp sever assert: that: - ntp_conf in resutl_show_run.stdout_lines[0] vars: ntp_conf: ntp server 10.0.0.123
cleanup の実装: cleaup.yml
cleanup は、default シナリオで test したときに、最初の方と最後の方に呼ばれるアクションです。今回はコンフィグの掃除をします。具体的には ntp server 10.0.0.123
を削除します。
このファイルは自動では作成されませんので、自分で molecule/default/cleaup.yml
として作成します。
molecule/default/cleaup.yml
--- - hosts: all gather_facts: false tasks: - name: delete ntp config ios_config: lines: - no ntp server 10.0.0.123
これで準備と各種実装は完了です。
■ テストの実行
いよいよテストの実行です。
一通りのアクションを実行してテストするため molecule test
コマンドを実行します。
実行する場所は ロールのトップです。
(少しログが長いですが、★
の箇所でコメントします)
$ pwd /Users/sakana/molecule/set_ntp $ molecule test --> Test matrix └── default ├── dependency ├── lint ├── cleanup ├── destroy ├── syntax ├── create ├── prepare ├── converge ├── idempotence ├── side_effect ├── verify ├── cleanup └── destroy --> Scenario: 'default' --> Action: 'dependency' Skipping, missing the requirements file. Skipping, missing the requirements file. --> Scenario: 'default' --> Action: 'lint' --> Lint is disabled. --> Scenario: 'default' --> Action: 'cleanup' ★ cleanup(NTPコンフィグの削除)の実行 PLAY [all] ********************************************************************* TASK [delete ntp config] ******************************************************* changed: [ios1] PLAY RECAP ********************************************************************* ios1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 --> Scenario: 'default' --> Action: 'destroy' Skipping, destroy action has no playbook. --> Scenario: 'default' --> Action: 'syntax' playbook: /Users/sakana/molecule/set_ntp/molecule/default/converge.yml --> Scenario: 'default' --> Action: 'create' Skipping, create action has no playbook. --> Scenario: 'default' --> Action: 'prepare' Skipping, prepare playbook not configured. --> Scenario: 'default' --> Action: 'converge' ★ converge(メイン処理、ntp server 10.0.0.123)の実行 PLAY [Converge] **************************************************************** TASK [Include set_ntp] ********************************************************* TASK [set_ntp : set ntp server] ************************************************ changed: [ios1] PLAY RECAP ********************************************************************* ios1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 --> Scenario: 'default' --> Action: 'idempotence' ★ 冪等性確認(もう一度実行してchangedにならないこと) Idempotence completed successfully. --> Scenario: 'default' --> Action: 'side_effect' Skipping, side effect playbook not configured. --> Scenario: 'default' --> Action: 'verify' ★ verify(テスト、コンフィグの確認)の実行 --> Running Ansible Verifier PLAY [Verify] ****************************************************************** TASK [show run] **************************************************************** ok: [ios1] TASK [assert ntp sever] ******************************************************** ok: [ios1] => { "changed": false, "msg": "All assertions passed" } PLAY RECAP ********************************************************************* ios1 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 Verifier completed successfully. --> Scenario: 'default' --> Action: 'cleanup' ★ cleanup(NTPコンフィグの削除)の再実行 PLAY [all] ********************************************************************* TASK [delete ntp config] ******************************************************* changed: [ios1] PLAY RECAP ********************************************************************* ios1 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 --> Scenario: 'default' --> Action: 'destroy' Skipping, destroy action has no playbook. --> Pruning extra files from scenario ephemeral directory
生成されるファイル
テストを実行すると ~/.cache/molecule/ロール名/default
配下に色々ファイルが生成されるようです。
参考までに載せておきます。
ansible.cfg
Playbook 実行時に利用される ansible.cfg のようです。
# Molecule managed [defaults] ansible_managed = Ansible managed: Do NOT edit this file manually! display_failed_stderr = True forks = 50 retry_files_enabled = False host_key_checking = False nocows = 1 interpreter_python = auto [ssh_connection] scp_if_ssh = True control_path = %(directory)s/%%h-%%p-%%r
内容は、molecule.yml
内の provisioner.config_options
でカスタマイズできるようです。
provisioner: name: ansible config_options: defaults: fact_caching: jsonfile ssh_connection: scp_if_ssh: True
inventory/ansible_inventory.yml
molecule.yml
内の platforms
の設定と関係がありそうです。
# Molecule managed --- all: hosts: ios1: &id001 {} vars: molecule_ephemeral_directory: '{{ lookup(''env'', ''MOLECULE_EPHEMERAL_DIRECTORY'') }}' molecule_file: '{{ lookup(''env'', ''MOLECULE_FILE'') }}' molecule_instance_config: '{{ lookup(''env'', ''MOLECULE_INSTANCE_CONFIG'') }}' molecule_no_log: '{{ lookup(''env'', ''MOLECULE_NO_LOG'') or not molecule_yml.provisioner.log|default(False) | bool }}' molecule_scenario_directory: '{{ lookup(''env'', ''MOLECULE_SCENARIO_DIRECTORY'') }}' molecule_yml: '{{ lookup(''file'', molecule_file) | molecule_from_yaml }}' ungrouped: hosts: ios1: *id001 vars: {}
state.yml
どのような用途なのかは不明です。
# Molecule managed --- converged: false created: false driver: null is_parallel: false prepared: null run_uuid: b4424680-8cbf-4d14-b073-757caba749b5
inventory/hosts
molecule.yml
内の provisioner.inventory.host
内の内容と同じでした。テスト実行後、削除されました。
molecule.yml
(自分で書いたほうの) molecule.yml
のデフォルト値を含めた内容でした。こちらもテスト実行後、削除されました。
■ おわりに
molecule を利用して、ネットワーク機器への設定のテストの自動化を試してみました。
私の理解が浅いのと、おそらくメジャーではない使い方という点で、少々ぎこちない実装があったり、molecule の機能を活用できていない点があるかもしれません。
また、今回レベルのテスト内容であれば、とくに molecule を利用するまでもない気もします。ですが、他のテストとやり方を合わせるという事情があるのであれば、molecule でやるならこう、というやり方をある程度抑えておくのもいいかなと思いました。
参考
公式ドキュメント molecule.readthedocs.io
delegated
の使い方の参考になりました
sky-joker.tech