てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] 「つまずき Ansible 【Part28】ping 系モジュールを試す」ふりかえり

はじめに

2021/01/30 に、YouTube Live で「つまずき Ansible 【Part28】ping 系モジュールを試す」という配信をしました。

実際に作業しながら(ときには)エラーと戦って進めるシリーズです。

今回は、pingios_ping や win_ping などの ping 系モジュールの違いを確認しながら、試しました。

tekunabe.connpass.com

  • 環境
    • Ansible 2.10.6

参考: 【Ansible】ping 系モジュールまとめ(ping/win_ping/ios_ping/nxos_ping/net_ping) - てくなべ (tekunabe)


動画

youtu.be

■ やったこと

ping モジュール

pingモジュールは、ターゲットノードへのAnsible的な接続や認証の正常性を確認する。

ICMP ではなく、通常は SSH を利用する。

[A] --- SSH ---> [linux]
  • 実行ログ
$  ansible -i inventory.ini linux  -m ping
linux01 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
  • Playbook の場合
---
- hosts: linux
  gather_facts: false

  tasks:
    - ping:

win_ping モジュール

win_ping モジュールは、Windows ターゲットのターゲットノードへのAnsible的な接続や認証の正常性を確認する。

通常は、WinRM を利用する。

[A] -- WinRM --> [Windows]
  • 実行ログ
$ ansible -i inventory.ini win -m win_ping
win01 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

おまけ: うっかり Windows 相手に ping モジュールを使った場合のエラー

$ ansible -i inventory.ini win -m ping
[WARNING]: No python interpreters found for host win01 (tried ['/usr/bin/python', 'python3.7', 'python3.6', 'python3.5',
'python2.7', 'python2.6', '/usr/libexec/platform-python', '/usr/bin/python3', 'python'])
win01 | FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "module_stderr": "Exception calling \"Create\" with \"1\" argument(s): \"At line:4 char:21\r\n+ def _ansiballz_main():\r\n+                     ~\r\nAn expression was expected after '('.\r\nAt line:13 char:27\r\n+     except (AttributeError, OSError):\r\n+                           ~\r\nMissing argument in parameter list.\r\nAt line:15 char:29\r\n+     excludes = set(('', '.', scriptdir))\r\n+                             ~\r\nMissing expression after ','.\r\nAt line:15 char:30\r\n+     excludes = set(('', '.', scriptdir))\r\n+                              ~~~~~~~~~\r\nUnexpected token 'scriptdir' in expression or statement.\r\nAt line:15 char:29\r\n+     excludes = set(('', '.', scriptdir))\r\n+                             ~\r\nMissing closing ')' in expression.\r\nAt line:15 char:39\r\n+     excludes = set(('', '.', scriptdir))\r\n+                                       ~\r\nUnexpected token ')' in expression or statement.\r\nAt line:15 char:40\r\n+     excludes =

ios_ping モジュール

ios_ping モジュールは、Cisco IOS 機器から指定した宛先への疎通確認をする。

ターゲットノード「へ」ではなく「から」。

[A] ------------ [ios]--- ICMP ---> [dest先]
  • 実行ログ
$ ansible -i inventory.ini ios -m ios_ping -a "dest=192.168.1.1"
ios01 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "commands": [
        "ping 192.168.1.1"
    ],
    "packet_loss": "0%",
    "packets_rx": 5,
    "packets_tx": 5,
    "rtt": {
        "avg": 1,
        "max": 2,
        "min": 1
    }
}
  • Playbook の場合
---
# ios
- hosts: ios
  gather_facts: no

  tasks:
    # ansible -i inventory.ini ios -m ios_ping -a "dest=192.168.1.1"
    - name: ping for ios
      ios_ping:
        dest: 192.168.1.1

net_ping モジュール

ios_ping モジュールは、ios_pingjunos_ping モジュールなどを抽象化したもの。

  • Playbook
---
# ios
- hosts: ios
  gather_facts: no

  tasks:
    - name: ping for ios
      net_ping:
        dest: 192.168.1.1

番外編1: ネットワーク機器に ping モジュールを使ったら?

配信後にご質問をいただきました。

はい、SUCCESS になってしまいます。(たしか Ansible 2.9 頃からの仕様)

  • 認証情報が誤っていてもSUCCESS
$ ansible -i inventory.ini ios -m ping -e ansible_user=tekitoutekiyou
ios01 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
  • 接続先が誤っていても SUCCESS
$ ansible -i inventory.ini ios -m ping -e ansible_host=tekitoudesuuuuuuu
ios01 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

番外編2:ICMP による ping

[A] ---- ICMP ---> [dest先]

command モジュール使うくらいしか思い浮かばす・・。

$ ansible -i inventory.ini linux -m command -a "ping {{ ansible_host }} -c 4" -c local
linux01 | CHANGED | rc=0 >>
PING 192.168.1.145 (192.168.1.145) 56(84) bytes of data.
64 bytes from 192.168.1.145: icmp_seq=1 ttl=64 time=0.176 ms
64 bytes from 192.168.1.145: icmp_seq=2 ttl=64 time=0.212 ms
64 bytes from 192.168.1.145: icmp_seq=3 ttl=64 time=0.198 ms
64 bytes from 192.168.1.145: icmp_seq=4 ttl=64 time=0.264 ms

--- 192.168.1.145 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2999ms
rtt min/avg/max/mdev = 0.176/0.212/0.264/0.035 ms

回数を指定する -c を忘れると無限 ping になって、応答が返ってこない。

ありがとうございます!

参考: [Ansible] コントロールノードから ICMP ping を実行する(ただしcommandモジュール) - てくなべ (tekunabe)


おわりに

名前がよく似てても性質が結構違ったりするので、少し注意が必要でした。 公式ドキュメントに必要な情報は掲載されているので、やっぱりよく確認すべきですね。


Part29 にむけて

以下のネタを検討中です。気が向いたものをやります。 connpass申込時のアンケートでいただいたものも含めています。

  • Ansible 2.10 関連
  • connection: local ななにか
  • Jinja2、フィルター
  • Windows
  • ESXi で VM作成
  • モジュールのテスト
  • AWXとの共存を念頭に入れたDirectory構成

[Ansible] 「つまずき Ansible 【Part27】ネットワーク機器の状態確認をしたい」ふりかえり

はじめに

2021/01/23 に、YouTube Live で「つまずき Ansible 【Part27】ネットワーク機器の状態確認をしたい」という配信をしました。

実際に作業しながら(ときには)エラーと戦って進めるシリーズです。

今回は、以下の記事を参照しながら、ネットワーク機器の状態確認する処理を試しました。

www.ansible.com

tekunabe.connpass.com

  • 環境
    • Ansible 2.10.5
      • (配信中、記事の前提のバージョンが不明と言ってしまいましたが、実際は 2.9.15 と記載がありました。失礼しました。)


動画

youtu.be

  • 00:00 イントロダクション
  • 05:56 情報の取得とパース結果の表示
  • 21:55 インターフェース状態のバリデーション
  • 30:26 復旧
  • 32:46 BGP状態のバリデーション
  • 44:49 おわりに

■ やったこと

venv の作成と ansible 2.10.5 のインストール

まず、環境の準備として、新しい venv に ansible をインストール。

[admin@gitlab ~]$ python3 -m venv envs/a210
[admin@gitlab ~]$ source envs/a210/bin/activate
(a210) [admin@gitlab ~]$ pip install ansible
(略)
(a210) [admin@gitlab ~]$ ansible --version
ansible 2.10.5
(略)

表示用Playbook の実行

Playbook (公式ブログからの引用)を実行

---
- hosts: nxos
  connection: ansible.netcommon.network_cli
  gather_facts: false
  vars:
    ansible_network_os: cisco.nxos.nxos
    ansible_user: "changeme"
    ansible_password: "changeme"

  tasks:
  - name: "Fetch interface state and parse with pyats"
    ansible.utils.cli_parse:
      command: show interface
      parser:
        name: ansible.netcommon.pyats
    register: nxos_pyats_show_interface

  - name: print structured interface state data
    ansible.builtin.debug:
      msg: "{{ nxos_pyats_show_interface['parsed'] }}"

ansible.utils.cli_parse モジュール関連のエラー

ansible.utils.cli_parse モジュールが見つからないエラー。

ERROR! couldn't resolve module/action 'ansible.utils.cli_parse'. This often indicates a misspelling, missing collection, or incorrect module path.

上記エラーの対策として、ansible.utils collection をインストール。

(a210) [admin@gitlab stumble]$ ansible-galaxy collection install ansible.utils
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Installing 'ansible.utils:1.0.1' to '/home/admin/.ansible/collections/ansible_collections/ansible/utils'
Downloading https://galaxy.ansible.com/download/ansible-utils-1.0.1.tar.gz to /home/admin/.ansible/tmp/ansible-local-23876jjourm5u/tmppi521mhv
ansible.utils (1.0.1) was installed successfully

paramiko 関連のエラー

再度 Playbook 実行で、paramiko が見つからないエラー。

TASK [Fetch interface state and parse with pyats] *****************************************************************************
fatal: [nxos01]: FAILED! => {"changed": false, "msg": ["paramiko is not installed: No module named 'paramiko'"]}

上記エラーの対策として、paramiko をインストール。

pip install paramiko

pyats 関連のエラー

再度 Playbook 実行で、pyats が見つからないエラー。

TASK [Fetch interface state and parse with pyats] *****************************************************************************
fatal: [nxos01]: FAILED! => {"changed": false, "msg": "Failed to import the required Python library (genie) on gitlab's Python /home/admin/envs/a210/bin/python3. Please read the module documentation and install it in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter Failed to import the required Python library (pyats) on gitlab's Python /home/admin/envs/a210/bin/python3. Please read the module documentation and install it in the appropriate location. If the required library is installed, but Ansible is using the wrong Python interpreter, please consult the documentation on ansible_python_interpreter"}

上記エラーの対策として、pyats をインストール。

pip install pyats

gccのエラー

    gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/home/admin/envs/a210/include -I/usr/include/python3.6m -c multidict/_multidict.c -o build/temp.linux-x86_64-3.6/multidict/_multidict.o -O2 -std=c99 -Wall -Wsign-compare -Wconversion -fno-strict-aliasing -pedantic
    unable to execute 'gcc': No such file or directory
    error: command 'gcc' failed with exit status 1
    
    ----------------------------------------
Command "/home/admin/envs/a210/bin/python3 -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-ptsvl0o2/multidict/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-eplnwr3p-record/install-record.txt --single-version-externally-managed --compile --install-headers /home/admin/envs/a210/include/site/python3.6/multidict" failed with error code 1 in /tmp/pip-build-ptsvl0o2/multidict/
You are using pip version 9.0.3, however version 20.3.3 is available.

上記の対策として、yum でインストール。(実際はもっと最小限にできたと思います)

yum groupinstall "Development Tools"
sudo yum install python3-* -y

その後 pip install "pyats[full]" 実行で無事インストール。

再度 Playbook 実行。無事に show interface 実行結果がパースされて表示される。

(a210) [admin@gitlab stumble]$ ansible-playbook -i inventory.ini debug.yml 

PLAY [nxos] *******************************************************************************************************************

TASK [Fetch interface state and parse with pyats] *****************************************************************************
ok: [nxos01]

TASK [print structured interface state data] **********************************************************************************
ok: [nxos01] => {
    "msg": {
        "Ethernet2/1": {
            "admin_state": "up",
            "auto_mdix": "off",
            "auto_negotiate": false,
            "bandwidth": 1000000,
            "beacon": "off",
            "counters": {
                "in_bad_etype_drop": 0,
                "in_broadcast_pkts": 0,
                "in_crc_errors": 0,
                "in_discard": 0,
                "in_errors": 0,
                "in_if_down_drop": 0,
                "in_ignored": 0,
                "in_jumbo_packets": 0,
                "in_mac_pause_frames": 0,
                "in_multicast_pkts": 0,
                "in_no_buffer": 0,
                "in_octets": 0,
                "in_overrun": 0,
                "in_oversize_frame": 0,
                "in_pkts": 0,
                "in_runts": 0,
                "in_short_frame": 0,
                "in_storm_suppression_packets": 0,
                "in_underrun": 0,
                "in_unicast_pkts": 0,
                "in_unknown_protos": 0,
                "in_watchdog": 0,
                "in_with_dribble": 0,
                "last_clear": "never",
                "out_babble": 0,
                "out_broadcast_pkts": 0,
                "out_collision": 0,
                "out_deferred": 0,
                "out_discard": 0,
                "out_errors": 0,
                "out_jumbo_packets": 0,
                "out_late_collision": 0,
                "out_lost_carrier": 0,
                "out_mac_pause_frames": 0,
                "out_multicast_pkts": 0,
                "out_no_carrier": 0,
                "out_octets": 0,
                "out_pkts": 0,
                "out_unicast_pkts": 0,
                "rate": {
                    "in_rate": 0,
                    "in_rate_bps": 0,
                    "in_rate_pkts": 0,
                    "in_rate_pps": 0,
                    "load_interval": 0,
                    "out_rate": 0,
                    "out_rate_bps": 0,
                    "out_rate_pkts": 0,
                    "out_rate_pps": 0
                },
                "rx": true,
                "tx": true
            },
            "dedicated_interface": true,
            "delay": 10,
            "duplex_mode": "full",
            "efficient_ethernet": "n/a",
            "enabled": true,
            "encapsulations": {
                "encapsulation": "arpa"
            },
            "ethertype": "0x8100",
            "flow_control": {
                "receive": false,
                "send": false
            },
            "interface_reset": 1,
            "last_link_flapped": "02:25:36",
            "link_state": "up",
            "mac_address": "0000.0000.002f",
            "medium": "broadcast",
            "mtu": 1500,
            "oper_status": "up",
            "phys_address": "5254.0003.xxxx",
            "port_channel": {
                "port_channel_member": false
            },
            "port_mode": "routed",
            "port_speed": "1000",
            "reliability": "255/255",
            "rxload": "1/255",
            "switchport_monitor": "off",
            "txload": "1/255",
            "types": "Ethernet"
        },
        "Ethernet2/10": {
            "admin_state": "down",
...(略)...

バリデーション用Playbook の実行以降

Validation 以降 については動画をご参照下さい。

(チャプター再掲)

  • 00:00 イントロダクション
  • 05:56 情報の取得とパース結果の表示
  • 21:55 インターフェース状態のバリデーション
  • 30:26 復旧
  • 32:46 BGP状態のバリデーション
  • 44:49 おわりに


おわりに

配信中はかなり雰囲気で試しましたが、使いこなすには、JSON Schema の書き方や、データの構造をよく理解したほうが良いと思いました。


Part28 にむけて

以下のネタを検討中です。気が向いたものをやります。

  • Ansible 2.10 関連
  • connection: local ななにか
  • Jinja2、フィルター
  • Windows
  • ESXi で VM作成

[Ansible] 「つまずき Ansible 【Part26】ansible.cfg を触る」ふりかえり

はじめに

2021/01/17 に、YouTube Live で「つまずき Ansible 【Part26】ansible.cfg を触る」という配信をしました。

実際に作業しながら(ときには)エラーと戦って進めるシリーズです。

今回は、設定ファイルである ansible.cfg で、何かを設定変更する試みをしました。

設定項目一覧 Ansible Configuration Settings — Ansible Documentation

tekunabe.connpass.com

  • 環境
    • Ansible 2.9.16


動画

youtu.be

  • 0:00 イントロダクション
  • 2:45 設定項目一覧
  • 4:15 優先順位
  • 8:12 コマンドでマニュアルを見る
  • 9:48 設定変更と確認方法(host_key_checking)
  • 19:09 stdout_callback
  • 21:39 gathering
  • 25:45 ask_vault_pass
  • 28:48 おわりに

■ やったこと

公式ドキュメントの確認

設定項目の一覧はこちら。

Ansible Configuration Settings — Ansible Documentation

ansible.cfg 以外にも環境変数などでも変更できるものもある。

section[defaults] で、Keyhost_key_checking なら、ansible.cfg の場合は、以下のように指定する、という見方。

[defaults]
host_key_checking=値

優先順位

公式ドキュメントの通りansible.cfg は、配置場所によって優先順位がある。

~/.ansible.cfg だけドット付きファイル名。

どの ansible.cfg を使っているかは、ansible --version で確認できる。

(a29) [admin@gitlab stumble]$ ansible --version
ansible 2.9.16
  config file = /home/admin/general/vagrant/nwlab/stumble/ansible.cfg
略

ansible-config の使いみち1: 設定変更したものだけ確認する

ansible.cfg を書き換えて、ちゃんと設定値として反映できたか確認するには、ansible-config dump --only-changed が便利。デフォルトから設定変更された項目だけが表示される。

(a29) [admin@gitlab stumble]$ ansible-config dump --only-changed
HOST_KEY_CHECKING(/home/admin/general/vagrant/nwlab/stumble/ansible.cfg) = False

ansible-config の使いみち2: マニュアル代わりに

ansible-config list で設定項目一覧が表示されれる。

(a29) [admin@gitlab stumble]$ ansible-config list
ACTION_WARNINGS:
  default: true
  description:
  - By default Ansible will issue a warning when received from a task action (module
    or action plugin)
  - These warnings can be silenced by adjusting this setting to False.
  env:
  - name: ANSIBLE_ACTION_WARNINGS
  ini:
  - key: action_warnings
    section: defaults
  name: Toggle action warnings
  type: boolean
  version_added: '2.5'

設定例

host_key_checking

接続先のフィンガープリングのチェック有無の指定。デフォルトは True

~/.ssh/known_host にない場合、サーバー相手だと以下のように問われる。

(a29) [admin@gitlab stumble]$ ansible-playbook -i inventory.ini sv.yml 

PLAY [sv] ******************************************************************************************************************

TASK [install] *************************************************************************************************************
The authenticity of host 'localhost (::1)' can't be established.
ECDSA key fingerprint is SHA256xxx.
ECDSA key fingerprint is MD5:xxx.
Are you sure you want to continue connecting (yes/no)?

(配信時に、デフォルトでもチェックされることなく接続できてしまったのは、ローカルに対するPlaybookでコネクションプラグインがデフォルトのままだったからのようです。connection: ssh を明示したところいかのようになりました。ゆんぐさん、ありがとうございました!

また、ネットワーク機器相手(試した限り IOS への ssh)だと、問われることなくエラーで終了する。

(a29) [admin@gitlab stumble]$ ansible-playbook -i inventory.ini  ios_show.yml 

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

TASK [show] *************************************************************************************************************************************************************************************
failed: [ios02] (item=show ip route) => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "ansible_loop_var": "item", "changed": false, "item": "show ip route", "msg": "paramiko: The authenticity of host '192.168.1.12' can't be established.\nThe ssh-rsa key fingerprint is b'yyy'."}
failed: [ios01] (item=show ip route) => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "ansible_loop_var": "item", "changed": false, "item": "show ip route", "msg": "paramiko: The authenticity of host '192.168.1.11' can't be established.\nThe ssh-rsa key fingerprint is b'xxx'."}

PLAY RECAP **************************************************************************************************************************************************************************************
ios01                      : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
ios02                      : ok=0    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   

stdout_callback

標準出力のコールバックプラグインの指定。デフォルトは defaultyamljson が便利。

gathering

ファクト収集の扱いの指定。デフォルトは implicit。デフォルトで無効にする場合は explicit にする。

ask_vault_pass

--ask-vault-pass 未指定時でもパスワードプロンプトを表示するかどうかの指定。デフォルトは False


Part27 にむけて

以下のネタを検討中です。気が向いたものをやります。

  • Ansible 2.10 関連
  • connection: local ななにか
  • Jinja2、フィルター
  • Windows
  • ESXi で VM作成
  • cli_parse モジュール(Part15 の続き)

[Ansible] UNIX タイムスタンプを任意の日付時刻フォーマットに変換する strftime

はじめに

1609948923 のような UNIX タイムスタンプを、2021/01/07 01:02:03 のようなフォーマットに変換する方法です。

これをやりたい機会があって調べてたのですが、思いの外目当ての情報ににたどり着けず・・。 結局ちゃんと公式ドキュメントに書いてありました。公式ドキュメントへの橋渡しを兼ねて記事にします。

  • 環境
    • Ansible 2.9.14
    • Jinja2 2.11.2

Playbook

strftime を利用します。

---
- hosts: localhost
  gather_facts: false

  tasks:
    - name: debug datetime format
      debug:
        msg: "{{ '%Y/%m/%d %H:%M:%S' | strftime(1609948923) }}"

利用できるフォーマットは、以下の Python のドキュメントに掲載されています。

time — Time access and conversions — Python 3.9.1 documentation

実行

Playbook を実行します。

$ ansible-playbook -i localhost, ts.yml

PLAY [localhost] **************************************************************************************************

TASK [debug datetime format] **************************************************************************************
ok: [localhost] => {
    "msg": "2021/01/07 01:02:03"
}

PLAY RECAP ********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 

"2021/01/07 01:02:03" と表示されました。

参考

公式ドキュメントはこちら。

docs.ansible.com

以下のサンプルが掲載されています。

# Use arbitrary epoch value
{{ '%Y-%m-%d' | strftime(0) }}          # => 1970-01-01
{{ '%Y-%m-%d' | strftime(1441357287) }} # => 2015-09-04

[Ansible] 「つまずき Ansible 【Part25】assert で期待値確認をしたい」ふりかえり

はじめに

2021/01/09 に、YouTube Live で「つまずき Ansible 【Part25】assert で期待値確認をしたい」という配信をしました。

実際に作業しながら(ときには)エラーと戦って進めるシリーズです。

今回は、条件式を与えて期待値のチェックなどができる、assert モジュールを扱いました。

tekunabe.connpass.com

  • 環境
    • Ansible 2.9.16


動画

youtu.be

  • 0:00 イントロダクション
  • 1:50 概要
  • 6:02 簡単な使い方
  • 11:37 少し応用(ネットワーク機器の状態確認)
  • 30:20 おわりに

■ やったこと

基本的な使い方

単一の条件

pass のパターン

    - name: assert
      assert:
        that:
          - 1 == 1

結果

TASK [assert] ********************************************
ok: [localhost] => {
    "changed": false,
    "msg": "All assertions passed"
}

fail のパターン

    - name: assert
      assert:
        that:
          - 1 == 0

結果

TASK [assert] *******************************************
fatal: [localhost]: FAILED! => {
    "assertion": "1 == 0",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}

AND 条件

that に複数書くと AND。

    - name: assert
      assert:
        that:
          - 1 == 0
          - 1 == 1

結果

TASK [assert] ******************************
fatal: [localhost]: FAILED! => {
    "assertion": "1 == 0",
    "changed": false,
    "evaluated_to": false,
    "msg": "Assertion failed"
}

OR 条件

or を指定する。

    - name: assert
      assert:
        that:
          - (1 == 0) or (1 == 1)

結果

TASK [assert] **************************************
ok: [localhost] => {
    "changed": false,
    "msg": "All assertions passed"
}

少し応用的な使い方

Cisco ISO のネットワーク機器から収集したファクト(インターフェースの状態やLLDPネイバー情報など)が期待したどおりかを assert でチェック。

ホスト変数

まず、期待値をホスト変数として定義。

  • ホスト変数ファイル ios01.yml
---
expected:
  interfaces:
    - name: GigabitEthernet0/1
      lineprotocol: up
  llpd_neighbors:
    - local_intf: GigabitEthernet0/1
      host: ios02.sakana.local
      port: Gi0/1
  • ホスト変数ファイル ios02.yml
---
expected:
  interfaces:
    - name: GigabitEthernet0/1
      lineprotocol: up
  llpd_neighbors:
    - local_intf: GigabitEthernet0/1
      host: ios01.sakana.local
      port: Gi0/1

Playbook

以下のように fail_msg を指定すると失敗したときのメッセージをカスタマイズできる。実際の値は何だったのか表示するときな喉に便利。success_msg オプションもある。

---
- hosts: ios

  tasks:

    - name: assert interface lineprotocol
      assert:
        that:
          -  ansible_facts.net_interfaces[item.name].lineprotocol == item.lineprotocol
        fail_msg: "実際は{{ ansible_facts.net_interfaces[item.name].lineprotocol }}"
      loop: "{{ expected.interfaces }}"

    - name: assert lldp neighbors
      assert:
        that:  # 0番目の neighbor しか見てないので荒い実装です 
          -  ansible_facts.net_neighbors[item.local_intf][0].host == item.host
          -  ansible_facts.net_neighbors[item.local_intf][0].port == item.port
        fail_msg: "実際は{{ ansible_facts.net_neighbors[item.local_intf][0] }}"
      loop: "{{ expected.llpd_neighbors }}"

実行(期待通りだったとき)

$ ansible-playbook -i inventory.ini  ios_assert.yml 

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

TASK [Gathering Facts] *************************************************************************************************
[WARNING]: Ignoring timeout(10) for ios_facts
[WARNING]: Ignoring timeout(10) for ios_facts
[WARNING]: default value for `gather_subset` will be changed to `min` from `!config` v2.11 onwards
ok: [ios01]
ok: [ios02]

TASK [assert interface lineprotocol] ***********************************************************************************
ok: [ios01] => (item={'name': 'GigabitEthernet0/1', 'lineprotocol': 'up'}) => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": {
        "lineprotocol": "up",
        "name": "GigabitEthernet0/1"
    },
    "msg": "All assertions passed"
}
ok: [ios02] => (item={'name': 'GigabitEthernet0/1', 'lineprotocol': 'up'}) => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": {
        "lineprotocol": "up",
        "name": "GigabitEthernet0/1"
    },
    "msg": "All assertions passed"
}

TASK [assert lldp neighbors] *******************************************************************************************
ok: [ios01] => (item={'local_intf': 'GigabitEthernet0/1', 'host': 'ios02.sakana.local', 'port': 'Gi0/1'}) => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": {
        "host": "ios02.sakana.local",
        "local_intf": "GigabitEthernet0/1",
        "port": "Gi0/1"
    },
    "msg": "All assertions passed"
}
ok: [ios02] => (item={'local_intf': 'GigabitEthernet0/1', 'host': 'ios01.sakana.local', 'port': 'Gi0/1'}) => {
    "ansible_loop_var": "item",
    "changed": false,
    "item": {
        "host": "ios01.sakana.local",
        "local_intf": "GigabitEthernet0/1",
        "port": "Gi0/1"
    },
    "msg": "All assertions passed"
}

PLAY RECAP *************************************************************************************************************
ios01                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
ios02                      : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   


実行(インターフェースの状態が期待通りでなかったときの抜粋)

failed: [ios01] (item={'name': 'GigabitEthernet0/1', 'lineprotocol': 'up'}) => {
    "ansible_loop_var": "item",
    "assertion": "ansible_facts.net_interfaces[item.name].lineprotocol == item.lineprotocol",
    "changed": false,
    "evaluated_to": false,
    "item": {
        "lineprotocol": "up",
        "name": "GigabitEthernet0/1"
    },
    "msg": "実際はdown"
}


おわりに

assert モジュールは、molecule の verifier を Ansible にしたときにもよく利用することになると思います。

あるいみ単純なモジュールですが、手になじませておくと便利です。


Part26 にむけて

以下のネタを検討中です。気が向いたものをやります。

  • Ansible 2.10 関連
  • connection: local ななにか
  • ansible.cfg
  • Jinja2、フィルター
  • Windows
  • ESXi で VM作成
  • cli_parse モジュール(Part15 の続き)

[AWS] Transit Gateway とオンプレの VPN 接続を試してみた

はじめに

AWSVPCVPN で接続する方法自体は、以前から仮想プライベートゲートウェイ(VGW)経由の方法がありました。

2018年に Transit Gateway というものを経由する方法もできてたことを知りました。

Transit Gateway を利用する場合は、VPC ごとに VGW、VPN を作成しなくてよく、効率的なようです。

そこで、雰囲気を掴みたく、Transit Gateway とオンプレの VPN 接続を試してみました。

設定手順

基本的には、以下の記事を参考にさせていただきました。

dev.classmethod.jp

私の環境の場合は、接続したい VPC 配下のサブネットが利用しているルートテーブルに、作成した Transit Gateway に向けるルートを追加しました。 (うまくやれば自動反映されるのでしょうか・・?)(追記: ルートテーブルの伝搬を有効にすれば反映されたはず)

f:id:akira6592:20210103113706p:plain
Transit Gateway をターゲットとしたルートエントリ

VPC は2つ(10.0.0.0/16、10.2.0.0/16)アタッチしました。

なお、オンプレ側のルーターは、固定IPを振った RTX 1200 を利用しました。コンフィグは、AWS 側の画面で生成されてダウンロードしたものをベースにしました。


確認

AWS

VPN 接続では、2つのトンネルがアップしました。

f:id:akira6592:20210103113640p:plain
トンネルのアップ

Transit Gateway 接続では、アタッチした2つの VPC(10.0.0.0/16、10.2.0.0/16)と、オンプレのルーターから広報された2つの経路(192.168.1.0/24、192.168.100.0/24)が掲載されました。

f:id:akira6592:20210103113749p:plain
VPC 側とオンプレ側のルート

ルーター

tunnel 1tunnle 2 それぞれ Online

rtx# show status tunnel 1
TUNNEL[1]: 
Description: 
  Interface type: IPsec
  Current status is Online.
  from 2021/01/02 20:38:10.
  15 hours 3 minutes 43 seconds  connection.
  Received:    (IPv4) 12378 packets [782025 octets]
               (IPv6) 0 packet [0 octet]
  Transmitted: (IPv4) 12091 packets [740159 octets]
               (IPv6) 0 packet [0 octet]
rtx# 
rtx# show status tunnel 2
TUNNEL[2]: 
Description: 
  Interface type: IPsec
  Current status is Online.
  from 2021/01/02 20:34:25.
  15 hours 7 minutes 38 seconds  connection.
  Received:    (IPv4) 14017 packets [942791 octets]
               (IPv6) 0 packet [0 octet]
  Transmitted: (IPv4) 16045 packets [1147258 octets]
               (IPv6) 0 packet [0 octet]

2つの BGP ネイバーは Established

rtx# show status bgp neighbor 
BGP neighbor is 169.254.140.1, remote AS 64512, local AS 65000, external link
  BGP version 4, remote router ID 169.254.140.1
  BGP state = Established, up for 00:46:42
  Last read 00:00:01, hold time is 30, keepalive interval is 10 seconds
  Received 283 messages, 0 notifications, 0 in queue
  Sent 285 messages, 0 notifications, 0 in queue
  Connection established 3; dropped 2
  Last reset 01:31:55
Local host: 169.254.140.2, Local port: 179
Foreign host: 169.254.140.1, Foreign port: 39273

BGP neighbor is 169.254.65.129, remote AS 64512, local AS 65000, external link
  BGP version 4, remote router ID 169.254.65.129
  BGP state = Established, up for 04:36:03
  Last read 00:00:03, hold time is 30, keepalive interval is 10 seconds
  Received 1658 messages, 0 notifications, 0 in queue
  Sent 1661 messages, 0 notifications, 0 in queue
  Connection established 2; dropped 1
  Last reset 05:21:17
Local host: 169.254.65.130, Local port: 179
Foreign host: 169.254.65.129, Foreign port: 42405

2つの VPC の分を受信し、オンプレから 2つの経路を広報。

rtx# show status bgp neighbor 169.254.140.1 received-routes 
Total routes: 2
*: valid route
  Network            Next Hop        Metric LocPrf Path
  10.0.0.0/16        169.254.140.1      100        64512 IGP
  10.2.0.0/16        169.254.140.1      100        64512 IGP
rtx# 
rtx# show status bgp neighbor 169.254.140.1 advertised-routes 
Total routes: 2
*: valid route
  Network            Next Hop        Metric LocPrf Path
* 192.168.1.0/24     169.254.140.2                 65000 IGP
* 192.168.100.0/24   169.254.140.2                 65000 IGP

rtx# show status bgp neighbor 169.254.65.129 received-routes 
Total routes: 2
*: valid route
  Network            Next Hop        Metric LocPrf Path
* 10.0.0.0/16        169.254.65.129     100        64512 IGP
* 10.2.0.0/16        169.254.65.129     100        64512 IGP
rtx# 
rtx# show status bgp neighbor 169.254.65.129 advertised-routes 
Total routes: 2
*: valid route
  Network            Next Hop        Metric LocPrf Path
* 192.168.1.0/24     169.254.65.130                65000 IGP
* 192.168.100.0/24   169.254.65.130                65000 IGP

ルーティングテーブルには tunnel 2 に向く経路が載っています。

rtx# show ip route 
Destination         Gateway          Interface       Kind  Additional Info.
default             -                    PP[01]    static  
10.0.0.0/16         169.254.65.129    TUNNEL[2]       BGP  path=64512
10.2.0.0/16         169.254.65.129    TUNNEL[2]       BGP  path=64512
169.254.65.128/30   -                 TUNNEL[2]  implicit  
169.254.140.0/30    -                 TUNNEL[1]  implicit  
...(略)...

エンドツーエンド

オンプレ側のマシン(192.168.100.2)から、各 VPC 内のインスタンスへの ping が無事通過。

[root@awx-try ~]# ping 10.0.1.10
PING 10.0.1.10 (10.0.1.10) 56(84) bytes of data.
64 bytes from 10.0.1.10: icmp_seq=1 ttl=252 time=9.76 ms
64 bytes from 10.0.1.10: icmp_seq=2 ttl=252 time=9.45 ms
64 bytes from 10.0.1.10: icmp_seq=3 ttl=252 time=9.45 ms
64 bytes from 10.0.1.10: icmp_seq=4 ttl=252 time=8.87 ms
[root@testsv ~]# 
[root@testsv ~]# ping 10.2.1.10
PING 10.2.1.10 (10.2.1.10) 56(84) bytes of data.
64 bytes from 10.2.1.10: icmp_seq=1 ttl=252 time=8.44 ms
64 bytes from 10.2.1.10: icmp_seq=2 ttl=252 time=10.1 ms
64 bytes from 10.2.1.10: icmp_seq=3 ttl=252 time=8.29 ms
64 bytes from 10.2.1.10: icmp_seq=4 ttl=252 time=8.16 ms


参考: awscli での設定手順

GUI であちこち設定画面を行き来するのが少しだけ手間だったので、 あとで、awscli でも同様のことができるか試しました。

Transit Gatetway の作成

まずは、Transit Gatetway を作成します。

aws ec2 create-transit-gateway --options="AmazonSideAsn=64512" --tag-specifications "ResourceType=transit-gateway,Tags=[{Key=Name,Value=tgw-test99}]"

戻り値の TransitGatewayId の値を控えておきます。

Customer Gateway の作成

GUI では、Create Transit Gateway Attachment 画面で、Customer Gateway の作成と Transit Gatetway との接続を同時に設定しました。

一方、awscli まず Customer Gateway を作成します。

aws ec2 create-customer-gateway --bgp-asn 65000 --public-ip ${カスタマーゲートウェイ側のIP}  --type ipsec.1 --tag-specifications "ResourceType=customer-gateway,Tags=[{Key=Name,Value=cgw-test99}]"

戻り値の CustomerGatewayId の値を控えておきます。

VPN 接続の作成

続いて VPN 接続を作成します。このとき、最初に作成した Transit Gateway の ID を --transit-gateway-id オプションで指定します。

aws ec2 create-vpn-connection  --type ipsec.1  --customer-gateway-id ${控えておいたCustomerGatewayId}  --transit-gateway-id ${控えておいたTransitGatewayId} --tag-specifications "ResourceType=vpn-connection,Tags=[{Key=Name,Value=vpn-test99}]"

これを実行したあと、VPN の設定画面の「設定のダウンロード」でダウンロードした、コンフィグをルーターに流し込みます。

(設定のダウンロードは自動化できるのでしょうか・・?戻り値の CustomerGatewayConfiguration 内の値と何かしらのテンプレートで生成できそうすが)

Transit Gatetway と VPC のアタッチ

Transit Gatetway と VPC にアタッチします。

aws ec2 create-transit-gateway-vpc-attachment  --vpc-id ${対象の VPC ID} --transit-gateway-id ${控えておいたTransitGatewayId} --subnet-ids ${対象のサブネットID} --tag-specifications "ResourceType=transit-gateway-attachment,Tags=[{Key=Name,Value=tgw-attach-vpc-test99}]"

ルートテーブルに Transit Gateway 追加

最後に、対象サブネットが利用するルートテーブルに、Transit Gateway をターゲットとするルートを追加します。ここではデフォゲとして設定します。

aws ec2 create-route --route-table-id ${対象のルートテーブルID} --destination-cidr-block  "0.0.0.0/0" --transit-gateway-id ${控えておいたTransitGatewayId}


おわりに

Transit Gateway を経由して、VPC とオンプレの間を VPN 接続で接続できることを試しました。

Transit Gateway をハブとした構成で接続できるのは便利なのだろうなと思いました。

[AWS] VPC 関連の個人的メモ

はじめに

引き続きVPC 周りを学習中です。 用語や特性、どのような関連があるのかを整理するために、個人的なメモをまとめます。

VPC

  • 仮想ネットワーク
  • CIDR は一度決めたらあとから変更できないため、余裕をもった設計にしておく

サブネット

  • VPC 内に作る
  • 例えば VPC10.0.0.0/16 であれば、サブネットは 10.0.1.0/24 のように
  • サブネットごとにアベイラビリティゾーンを指定できる
  • パブリック IPv4 アドレスを自動割り当てはい にすると、所属インスタンスに設定を引き継げる
  • 後述のインターネットゲートウェイ経由でインターネットと通信するサブネットをブリックサブネット、そうでないものをいプライベートサブネット呼ぶ

インターネットゲートウェイ

  • インターネットとの接続に必要なもの
  • インターネット側からもアクセスするために使う(中→外のにであれば後述の NAT ゲートウェイ
  • 所属サブネットのルートテーブルのデフォルトゲートウェイをインターネットゲートウェイに向けると、インターネットに出ていける
  • 特に設定はないが、VPC にアタッチさせる

NAT ゲートウェイ

  • 中からインターネットへの接続のみ必要なときに利用する
  • パブリックサブネットに配置して、EIP を付ける

ルートテーブル

  • いわゆるルーティングテーブル
  • VPC 作成時に、ローカル(VPC内)のみルーティングするデフォルトのルートテーブルができる(メイン: はい のもの)
  • デフォルトのルートテーブルは各サブネット共通なので影響が大きいためそのままにしておく
  • ルートテーブルを変更シたい場合は、別途ルートテーブルを作って関連付けし直す

セキュリティグループ

ネットワークACL

  • サブネットに適用
  • 許可の拒否を指定
  • ステートレス

EC2 の IP アドレス

  • インスタンス側の設定としては DHCP クライアントにする
  • プライマリプライベート IP アドレスは一度決まると変更されない
  • 自動で割り当てられたパブリック IP アドレスは再起動では変更されないが、停止→開始では変更される
  • 停止→開始でも固定化したい場合は EIP を割り当てる

参考

www.nikkeibp.co.jp book.impress.co.jp tekunabe.hatenablog.jp