てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Ansible] モジュールのオプションのデフォルトを自分で指定できる module_defaults

■ はじめに

Network Features Coming Soon in Ansible Engine 2.9 を見ているときにたまたま知ったのですが、モジュールのオプションのデフォルトを自分で指定できる module_defaults というディレクティブがあります。

タスクごとに、毎回指定しているようなオプション(リージョン名や認証情報)は、この module_defaults を利用すると負担が経験されるかもしれません。

docs.ansible.com

module_defaults がどのバージョンから利用できるようになったのか、changelog ではうまく見つからなかったのですが(PRはおそらくこちら)、少なくとも、Ansible 2.6 のドキュメントには module_defaults の説明ページがあります。


■ 基本的な使い方

例えば以下のように、module_defaults として file モジュールのオプション(ここでは ownergroupmode)のデフォルト値を指定することで、tasks 内の全 file モジュールのデフォルト値として適用されます。

- hosts: localhost
  module_defaults:
    file:
      owner: root
      group: root
      mode: 0755
  tasks:
    - file:
        state: touch
        path: /tmp/file1
    - file:
        state: touch
        path: /tmp/file2
    - file:
        state: touch
        path: /tmp/file3


■ Play、Role、Block、Task に指定できる

Playbook Keywords によると、module_defaults は、Play、Role、Block、Task に指定できるようです。


クラウド系モジュールなどはまとめて指定できる

Ansible 2.7 以降は、プレビュー機能という位置づけで、モジュールをグループで指定できようになりました。

具体的には、Module defaults groups に書かれているように、以下の系統のモジュールについては、一つ一つのモジュールごとの指定だけでなく、 group/aws のようなモジュールグループごとの指定もできます。

例えば、AWS 系モジュールであれば、以下のように group/aws で各モジュールのデフォルト値をまとめて指定できます。

- hosts: localhost
  module_defaults:
    group/aws:
      region: us-west-1   # デフォルトとしてここだけにに指定

  tasks:
    ec2_vpc_net:
        # その他オプション
    ec2_vpc_subnet:
        # その他オプション
    ec2_vpc_route_table:
        # その他オプション

もし、module_defaults を指定しない場合は、以下のようにタスクごとに指定するかたちになります。

- hosts: localhost

  tasks:
    - ec2_vpc_net:
        region: us-east-1   # 毎回指定
        # その他オプション
    - ec2_vpc_subnet:
        region: us-east-1   # 毎回指定
        # その他オプション
    - ec2_vpc_route_table:
        region: us-east-1   # 毎回指定
        # その他オプション

なお、Ansible 2.7 から、グループ名と所属モジュールの関係は lib/ansible/config/module_defaults.ymlという定義ファイルにまとめられています。


VMware、Docker 系モジュールもグループとして追加予定

Ansible 2.10 では、VMware 系のモジュール、Docker 系モジュールもそれぞれグループ定義が追加され、group/vmwaregroup/docker という指定で、モジュールなどのオプションデフォルト値を指定できるようです。

■ 自分でグループ化定義もできる

(2019/11/02 追記) 自分の環境の lib/ansible/config/module_defaults.yml を修正することで、自分でグループ化定義もできます。

bigip_* モジュールをグループ化した例の記事をご紹介します。 qiita.com


■ まとめ

モジュールのオプションのデフォルトを自分で指定できる module_defaults ディレクティブについてまとめました。

同じことを繰り返し指定しなくても良くなる方法は、他にも YAML のアンカー、エイリアスといった機能もあります。 やや好みが分かれるところでもあるようなので、module_defaults とうまく使い分けるのがよさそうです。

参考

Ansible Module defaults 検証awsbloglink.wordpress.com

  • 調べ中のツイートたち

[2022/11/14 追記] 2022に書いた類似記事。

tekunabe.hatenablog.jp

ネットワーク自動化Pythonライブラリ netmiko、NAPALM が Python 2系サポートの最終バージョンをリリース

はじめに

ネットワーク自動化Pythonライブラリである、netmikoNAPALMが、Python 2系 サポートの最終バージョンをリリースしました。それぞれ、netmiko 2.4.2、NAPALM 2.5.0 です。 次回のリリースからは Python 3.6 以降のみのサポートととしています。

バグ修正、セキュリティ対応、機能追加のなどの継続性の観点から、Python 3 での利用がよさそうです。

以下、ソースをまとめます。

■ netmiko

開発者によるお知らせ

netmiko 2.4.2リリースノート

github.com

Barring critical bugs this should be the last Netmiko release that will support PY2

[2020/01/16 追記] Netmiko 3.0.0 がリリースされました。予定通りPython 3.6 以降のみのサポートとなりました。 Release Netmiko 3.0.0 Release · ktbyers/netmiko · GitHub


■ NAPALM

NAPALM アカウントによるお知らせ

NAPALM 2.5.0 リリースノート

This is the last release supporting Python 2. Going forward, we will only support Python 3.6+

github.com

[2020/05/06 追記] NAPALM 3.0.0 がリリースされました。予定通りPython 3.6 以降のみのサポートとなりました。

https://github.com/napalm-automation/napalm/releases/tag/3.0.0:embed:titile

[Ansible] AWX を RHEL 8 にインストールする(HTTPS化もあり)

■ はじめに

Red Hat Enterprise Linux 8 (以下、RHEL 8)に、Ansible Tower のアップストリームである AWX をインスールする手順をまとめます。

  • 環境
    • RHEL 8.0 on AWS EC2 t2-medium (ami-0520e698dd500b1d1)
    • Python 3.6.8
    • デプロイ方法 docker-compose
    • AWX インストール Playbook 実行用 Ansible 2.8.5
    • AWX 7.0.0

公式の手順 をベースにして、要件や遭遇したエラーに応じて手順を加えています。

なお、AWX は開発スピードが早く、ここで示した手順がすぐに廃れてしまう可能性もありますのでご了承ください。また、とりあえず検証目的で構築する手順ですので、一部ワークアラウンド的になっています。パスワードのデフォルトです。


■ 各種準備

必要パッケージのインストール

あとで AWXのリポジトリから clone する手順があるため、予め git をインストールします。

# dnf install git -y

python3 環境の準備

デフォルトでシステムが利用する /usr/libexec/platform-python でははなく、別途 python3 をインストールします。これにより、python3/usr/local/python3pippip3 などを利用できるようになります。

# dnf install python3 -y

AWX のインストールには Ansible が必要です。そのための venv を専用に準備して有効化します。

# python3 -m venv venv
# source venv/bin/activate

インストール Playbook が利用する Python ライブラリをインストールします。

(venv)# pip install docker-compose selinux

補足: selinux と libselinux-python のエラーについて

SELinux が有効な環境では上記のように selinux という Python ライブラリをインストールする必要があります。インストールしない場合、AWX のインストール Playbook 実行時に、以下のような「SELinux が有効な環境では SELinuxバインディング libselinux-python が必要」というエラーが発生してしまいます。

TASK [local_docker : Create Docker Compose Configuration] ****************************************************************************************************
failed: [localhost] (item=environment.sh) => {"ansible_loop_var": "item", "changed": false, "checksum": "bcfaf5e471bde4210390d5231356841dbfcb22b2", "item": "environment.sh", "msg": "Aborting, target uses selinux but python bindings (libselinux-python) aren't installed!"}
failed: [localhost] (item=credentials.py) => {"ansible_loop_var": "item", "changed": false, "checksum": "9d8c48ec0e65f6cc2aa8d1e2720f826b0f65551a", "item": "credentials.py", "msg": "Aborting, target uses selinux but python bindings (libselinux-python) aren't installed!"}
failed: [localhost] (item=docker-compose.yml) => {"ansible_loop_var": "item", "changed": false, "checksum": "dd5b8e60cee28a19cc87e3f45866ef907ecbb6c3", "item": "docker-compose.yml", "msg": "Aborting, target uses selinux but python bindings (libselinux-python) aren't installed!"}

実際は、 libselinux-pythonPython 2 用であり、Python 3 用は python3-libselinux のはずです。この環境には python3-libselinux はインストール済みでした。にもかかわらず上記のエラーが出ていたのですが、結局は上記のようselinux という Python ライブラリをインストールすることでエラーは解消しました。

ansible のインストール

AWX インストール Playbook を実行するための ansible をインストールします。

(venv)# pip install ansible

Docker のインストール

今回は docker-compose でデプロイするため、docker をインストールします。

# dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
# dnf install --nobest docker-ce -y
# systemctl start docker
# systemctl enable docker

補足

dnf install コマンドに --nobest を付けているのは、以下のページの「NOTE!!」にああるエラーに遭遇したためです。 https://linuxconfig.org/how-to-install-docker-in-rhel-8

もう少しスマートな解決方法はあるかも知れません。


■ AWX のインストール

リソースの準備

AWX のリポジトリを clone して、インストーラーのあるディレクトリに異動します。

(venv)# git clone https://github.com/ansible/awx
(venv)# cd awx/installer/

HTTPS 化する場合

(HTTPのみで良い場合はスキップして「インストール Playbook の実行」へ)

デフォルトのままインストールすると、http のみの接続になります。https 化したい場合は、証明書を準備した上で、awx/installer/ 配下の inventory ファイルに以下の2つの変更をします。

変更1: dockerhub_base 変数定義のコメントアウト

dockerhub_base=ansible  

を、以下のようにコメントアウトします。削除でもかまいません。

# dockerhub_base=ansible  

現時点(2019/10/06)では、dockerhub_base の変数定義を外さないと、いくらあとで ssl_certificate 変数で証明書を指定しても、それを利用する nginx.conf にならない点が注意です。issue 内の以下のコメントがヒントになりました。 SSL · Issue #3751 · ansible/awx · GitHub

これにより、ビルド済みイメージは利用せず、手元でビルドする手順(Playbook内)になります(Official vs Building Images)。インストール Playbook の所要時間は長くなります。

[2019/10/19 追記] 上記のように対応をしなくても、ssl_certificate 変数が正しく反映されるようになるPRが出ていました。 Improve usage of ssl_certificate in local_docker by craph · Pull Request #5042 · ansible/awx · GitHub

変更2: ssl_certificate で証明書(秘密鍵含む)ファイルの指定

#ssl_certificate=

を、以下のように、証明書(秘密鍵含む)ファイルのパスを指定します。ここでは例として servcer.pem としています。

ssl_certificate=server.pem

証明書と秘密鍵を分けて準備した場合は、予め以下のように結合しておきます。

cat 証明書ファイル.cer 秘密鍵ファイル.key > server.pem

なお、公式の手順には、ssl_certificate 変数の以下の説明があります。

Optionally, provide the path to a file that contains a certificate and its private key.

この2つの変更によって、

  • ssl_certificate 変数で指定した、証明書(秘密鍵含む)が awx_web コンテナ内の /etc/nginx/awxweb.pem としてマウントされる。
  • /etc/nginx/nginx.confが以下の定義になる。
    server {
                listen 8053 ssl;
                listen 8053 ssl;

        ssl_certificate /etc/nginx/awxweb.pem;
        ssl_certificate_key /etc/nginx/awxweb.pem;

インストール Playbook の実行

いよいよ インストール Playbook を実行します。

(venv)# ansible-playbook -i inventory install.yml

PLAY [Build and deploy AWX] *********************************************************************************************************************************************************************

(...略....)

PLAY RECAP **************************************************************************************************************************************************************************************
localhost                  : ok=14   changed=6    unreachable=0    failed=0    skipped=83   rescued=0    ignored=0


■ 確認

インストール後の状態を確認します。

docker コンテナ状態の確認

5つのコンテナが起動しているか確認します。

(venv)# docker ps
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                                                 NAMES
9e4403874de7        awx_task:7.0.0               "/tini -- /bin/sh -c…"   2 hours ago         Up 12 minutes       8052/tcp                                              awx_task
9407173599d0        awx_web:7.0.0                "/tini -- /bin/sh -c…"   2 hours ago         Up 12 minutes       0.0.0.0:80->8052/tcp                                  awx_web
f44e40be0f7c        postgres:10                  "docker-entrypoint.s…"   2 hours ago         Up 12 minutes       5432/tcp                                              awx_postgres
292bdc804e98        ansible/awx_rabbitmq:3.7.4   "docker-entrypoint.s…"   2 hours ago         Up 12 minutes       4369/tcp, 5671-5672/tcp, 15671-15672/tcp, 25672/tcp   awx_rabbitmq
605c832db064        memcached:alpine             "docker-entrypoint.s…"   2 hours ago         Up 12 minutes       11211/tcp                                             awx_memcached

HTTPS 化している場合は awx_webPORTS

0.0.0.0:80->8052/tcp, 0.0.0.0:443->8053/tcp になります。

ブラウザから確認

http://localhost

f:id:akira6592:20191006162709p:plain
ログイン画面
デフォルトのユーザー/パスワードである、admin/password でログインします。

f:id:akira6592:20191006162742p:plain
ログイン後直後のダッシュボード画面

HTTPS 化している場合は、https:// で確認します。

https://localhost

f:id:akira6592:20191006164206p:plain
手元ビルド版のログイン画面

inventory 内の dockerhub_baseコメントアウトし、手元でイメージをビルドする手順にしたことにより、ロゴがジャガイモになります。


■ まとめ

AWX を RHEL 8 にインストールする手順をまとめました。 大きなところでは、Ansible、docker が必要という点がポイントだと思います。

おまけ

HTTPS 化 するのに一番苦戦しました。色々調べている段階のツイートは以下。

コピペ用(HTTPのみ)

dnf install git -y
dnf install python3 -y
python3 -m venv venv
source venv/bin/activate
pip install docker-compose selinux
pip install ansible
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
dnf install --nobest docker-ce -y
systemctl start docker
systemctl enable docker
git clone https://github.com/ansible/awx
cd awx/installer/
ansible-playbook -i inventory install.yml
 

他の方による HTTPS 化方法

[Ansible] CentOS 8 に Ansible をインストールする(Python 3 + venv + pip)

■ はじめに

CentOS 8 では、システムで利用する PythonPython 3 になったり、デフォルトでは python コマンドが利用できなかったりと、CentOS 7 とは Python 事情が異なります。そのため、Ansible を pip でインストールする場合も、CentOS 7 とは少し手順が異なります(楽になったと思います)。

この記事では、以下の方針で CentOS 8 に Ansible をインストールする手段をまとめます。

Ansible インストール方針

OSインストール方法によっては、パッケージの過不足があるかもしれません。

なお、私の試した限り sudo dnf install ansiblesudo yum install ansible では Ansible をインストールできませんでした。

コマンドまとめ

コピペしやすいように、先にコアなコマンドだけまとめます。手順の説明は次項から。

sudo dnf install python3 -y
python3 -m venv ansible-py3
source ansible-py3/bin/activate
pip3 install ansible 


■ 手順

Python 3 のインストール

システムが利用している /usr/libexec/platform-python とは別に、ユーザが利用する環境として別途 Python 3 をインストールします。

$ sudo dnf install python3 -y

...(略)...

Installed:
  python36-3.6.8-2.module_el8.0.0+33+0a10c0e1.x86_64            python3-pip-9.0.3-13.el8.noarch            python3-setuptools-39.2.0-4.el8.noarch           

Complete!
  • 確認
$ python3 --version
Python 3.6.8

python3 コマンドは /usr/bin/python3 です。 バージョンなしのコマンド python/usr/bin/python はありません。

venv の準備

Python 環境を分離したいため、専用 の venv を用意して有効にします。

$ python3 -m venv ansible-py3
$ source ansible-py3/bin/activate
(ansible-py3)$ 

venv を有効にした地点で、バージョンなしのコマンド python(venv)/bin/python として利用できるようになります。

Ansible のインストール

venv を有効にした状態で、pip で Ansible をインストールします。

(ansible-py3)$ pip3 install ansible 
Collecting ansible
  Downloading https://files.pythonhosted.org/packages/04/25/48fee5f8048360d9375e01846fcf395dda58242ed1f25a2106b6794452eb/ansible-2.8.5.tar.gz (14.4MB)
    100% |████████████████████████████████| 14.4MB 96kB/s 
...(略)...
  • 確認
(ansible-py3)$ ansible --version
ansible 2.8.5
  config file = None
  configured module search path = ['/home/ansible/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/ansible/ansible-py3/lib64/python3.6/site-packages/ansible
  executable location = /home/ansible/ansible-py3/bin/ansible
  python version = 3.6.8 (default, May 21 2019, 23:51:36) [GCC 8.2.1 20180905 (Red Hat 8.2.1-3)]

Ansible 動作確認

自分自身に local コネクションプラグインを使って ping モジュールを利用する、というかんたんな動作確認をします。

(ansible-py3)$ ansible -i localhost, all -m ping -c local
localhost | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

discovered_interpreter_python とは?

上記の実行結果に discovered_interpreter_python の値が表示されているのは、Ansible 2.8 から導入された Python インタープリタを自動で探す機能によるものです。

特に ansible_python_interpreter 変数でパスを指定していない場合は、/usr/bin/python (バージョンなしコマンド)を利用しようとしますが、見つからない場合は一定の法則で探してきます。CentOS8/RHEL8の場合は、最初に /usr/libexec/platform-python を調べます。この記事の作業では、以下の条件により、インタープリタを自動で探しています。

  • /usr/bin/python3 はあるが /usr/bin/python はない
  • ansible_python_interpreter 変数は指定していない

なお、ansible_python_interpreter を明示的に指定するとこうなります。

$ ansible -i localhost, all -m ping -c local -e "ansible_python_interpreter=/home/ansible/ansible-py3/bin/python"
localhost | SUCCESS => {
    "changed": false,
    "ping": "pong"
}


■ まとめ

CentOS 8 に、Python 3 venv 環境に、Ansible を pip でインストールする方法をまとめました。

ふりかえると特に特別なことはしていませんが、CentOS 8 の Python 事情を意識しないと、ちょっとしたことでつまずいてしまうかもしれないので、残しておきます。

[Ansible] 利用する Python をタスクごとに切り替える方法(ansible_python_interpreter をタスク変数で指定)

■ はじめに

Ansible では、ターゲットノード(SSH接続先)で利用する Pythonansible_python_interpreter という変数で明示的に指定できます。

この変数は、インベントリ変数や host_vars の他にも、タスク変数としても指定できます。そのため、タスクごとにターゲットノードで利用する Python を切り替えられます。

この記事では、かんたんなサンプルでご紹介します。

  • 動作確認環境
    • Ansible 2.8.4
    • CentosOS 7.6 (コントロールノード、ターゲットノード共通)
    • Python 3 未インストール(ターゲットノード)
      • /usr/bin/pyrhon は Python 2


■ 検証サンプル

ここでは、 yum モジュールが Python 2 のみの対応していること利用して検証します。

(いい方法が思いつきませんでした・・・)

Playbook

- hosts: testsv
  gather_facts: no
  become: yes
 
  tasks:
    # デフォルのまま Python2 で実行
    - name: task1 
      yum:
        list: curl  

    # Python3 を使おうとしてエラー(そもそも未インストールのため)
    - name: task2 
      yum:
        list: curl
      ignore_errors: yes # エラーになるが検証を続けるため無視
      vars:
        ansible_python_interpreter: /usr/bin/python3

    ###########################
    # python3 のインストール
    - name: task3
      yum:
        name: python3

    # Python3 をインストールしたが、まだ Python2
    - name: task4
      yum:
        list: curl

    # Python3 を使う(インストール済み、指定済みなので今度はOK])
    - name: task5
      yum:
        list: curl
      vars:
        ansible_python_interpreter: /usr/bin/python3
      ignore_errors: yes # エラーになるが検証を続けるため無視

実行

$ ansible-playbook -i inventory.ini test.yml -v
Using /vagrant/ansible.cfg as config file

PLAY [testsv] *********************************************************************************

TASK [task1] **********************************************************************************
ok: [testsv] => {"ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}, "changed": false, "results": [{"arch": "x86_64", "envra": "0:curl-7.29.0-51.el7.x86_64", "epoch": "0", "name": "curl", "release": "51.el7", "repo": "installed", "version": "7.29.0", "yumstate": "installed"}, {"arch": "x86_64", "envra": "0:curl-7.29.0-54.el7.x86_64", "epoch": "0", "name": "curl", "release": "54.el7", "repo": "base", "version": "7.29.0", "yumstate": "available"}]}

TASK [task2] **********************************************************************************
fatal: [testsv]: FAILED! => {"changed": false, "msg": ["Could not detect which major revision of yum is in use, which is required to determine module backend.", "You can manually specify use_backend to tell the module whether to use the yum (yum3) or dnf (yum4) backend})"]}
...ignoring

TASK [task3] **********************************************************************************
changed: [testsv] => {"ansible_facts": {"pkg_mgr": "yum"}, "changed": true, "changes": {"installed": ["python3"]}, "msg": "", "rc": 0, "results": ["Loaded plugins: fastestmirror\nLoading mirror speeds from cached hostfile\n * base: ty1.mirror.newmediaexpress.com\n * extras: ty1.mirror.newmediaexpress.com\n * updates: ty1.mirror.newmediaexpress.com\nResolving Dependencies\n--> Running transaction check\n---> Package python3.x86_64 0:3.6.8-10.el7 will be installed\n--> Processing Dependency: python3-libs(x86-64) = 3.6.8-10.el7 for package: python3-3.6.8-10.el7.x86_64\n--> Processing Dependency: python3-setuptools for package: python3-3.6.8-10.el7.x86_64\n--> Processing Dependency: python3-pip for package: python3-3.6.8-10.el7.x86_64\n--> Processing Dependency: libpython3.6m.so.1.0()(64bit) for package: python3-3.6.8-10.el7.x86_64\n--> Running transaction check\n---> Package python3-libs.x86_64 0:3.6.8-10.el7 will be installed\n---> Package python3-pip.noarch 0:9.0.3-5.el7 will be installed\n---> Package python3-setuptools.noarch 0:39.2.0-10.el7 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package                   Arch          Version              Repository   Size\n================================================================================\nInstalling:\n python3                   x86_64        3.6.8-10.el7         base         69 k\nInstalling for dependencies:\n python3-libs              x86_64        3.6.8-10.el7         base        7.0 M\n python3-pip               noarch        9.0.3-5.el7          base        1.8 M\n python3-setuptools        noarch        39.2.0-10.el7        base        629 k\n\nTransaction Summary\n================================================================================\nInstall  1 Package (+3 Dependent packages)\n\nTotal download size: 9.4 M\nInstalled size: 48 M\nDownloading packages:\n--------------------------------------------------------------------------------\nTotal                                               30 kB/s | 9.4 MB  05:20     \nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n  Installing : python3-libs-3.6.8-10.el7.x86_64                             1/4 \n  Installing : python3-3.6.8-10.el7.x86_64                                  2/4 \n  Installing : python3-setuptools-39.2.0-10.el7.noarch                      3/4 \n  Installing : python3-pip-9.0.3-5.el7.noarch                               4/4 \n  Verifying  : python3-setuptools-39.2.0-10.el7.noarch                      1/4 \n  Verifying  : python3-libs-3.6.8-10.el7.x86_64                             2/4 \n  Verifying  : python3-3.6.8-10.el7.x86_64                                  3/4 \n  Verifying  : python3-pip-9.0.3-5.el7.noarch                               4/4 \n\nInstalled:\n  python3.x86_64 0:3.6.8-10.el7                                                 \n\nDependency Installed:\n  python3-libs.x86_64 0:3.6.8-10.el7          python3-pip.noarch 0:9.0.3-5.el7  \n  python3-setuptools.noarch 0:39.2.0-10.el7  \n\nComplete!\n"]}

TASK [task4] **********************************************************************************
ok: [testsv] => {"changed": false, "results": [{"arch": "x86_64", "envra": "0:curl-7.29.0-51.el7.x86_64", "epoch": "0", "name": "curl", "release": "51.el7", "repo": "installed", "version": "7.29.0", "yumstate": "installed"}, {"arch": "x86_64", "envra": "0:curl-7.29.0-54.el7.x86_64", "epoch": "0", "name": "curl", "release": "54.el7", "repo": "base", "version": "7.29.0", "yumstate": "available"}]}

TASK [task5] **********************************************************************************
fatal: [testsv]: FAILED! => {"changed": false, "msg": "The Python 2 bindings for rpm are needed for this module. If you require Python 3 support use the `dnf` Ansible module instead.. The Python 2 yum module is needed for this module. If you require Python 3 support use the `dnf` Ansible module instead."}
...ignoring

PLAY RECAP ************************************************************************************
testsv                     : ok=5    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=2   

各タスクの説明

  • task1
    • 利用する Python を指定していないため、デフォルトの /usr/bin/python で実行。そのため Python 2。結果、yum モジュールは正常実行。
    • yum モジュールが使用できるかどうか確認するのが目的のため、システムに変更を及ばさない適当な内容にしている。以下同じ
  • task2
    • ansible_python_interpreter/usr/bin/python3Python 3 を利用しようとしているが、未インストールのためエラー。タスクを続行させるために ignore_errors: yes
    • CentOS 7 系の python 3 インストールに関する記事はこちら
  • task3
    • ターゲットノードに python3 をインストールする。このタスク自体は Python 2 で実行。結果、yum モジュールは正常実行。
  • task4

    • Python 3 をインストールしたが、ansible_python_interpreter を指定していないため、まだ Python 2 で実行。結果、yum モジュールは正常実行。
  • task5

    • ansible_python_interpreter/usr/bin/python3 を指定し、Python 3 もインストール済みなので、Python 3 で実行。結果、 yum モジュールは Python 3 に対応していないためエラー。


■ まとめ

タスク変数で ansible_python_interpreter を指定することにより、タスクごとにターゲットノードで利用する Python を切り替えらることをご紹介しました。

今回は、基本的には Python 2 で実行し、例外的に Python 3 で実行するサンプルでしたが、もちろん逆もできます。ただし、例外をあまり乱発すると混乱のもとになるので、特別な事情がある場合のみの利用にとどめたほうが良さそうです。

[Ansible] Cisco ACI モジュールの REST API ログイン処理の流れ

はじめに

Ansible には Cisco ACI 対応のモジュールが多数用意されています。

ACI の REST API を利用しているのだろうな、という予想はすぐついたのですが、実際のログインの処理を覗いてみたくなりました。

ACI モジュール共通だと思いますが、ここでは代表して aci_epg モジュールのログイン処理を追った時の話をまとめます。

先にまとめますと、以下の流れのようでした。

なお、細かく精査したり、実際に実行しながら確認したわけでない点はご了承ください。


■ aci_epg モジュール の main()

aci_epg モジュールは lib/ansible/modules/network/aci/aci_epg.pymain() から追っていきます。

この中に ACIModuleインスタンス化しているコードがあります。

    aci = ACIModule(module)

このクラスは、lib/ansible/module_utils/network/aci/aci.py 内で定義されています。

__init__ 内を見てていくと、self.login()を呼び出しています。

この先にログイン処理がありそうです。 いきなり処理を投げのではなく、事前にログイン処理をするということのようです。


■ ログイン

実際のログイン処理はここにありました。

https://github.com/ansible/ansible/blob/v2.8.5/lib/ansible/module_utils/network/aci/aci.py#L184

        resp, auth = fetch_url(self.module, url,
                               data=json.dumps(payload),
                               method='POST',
                               timeout=self.params['timeout'],
                               use_proxy=self.params['use_proxy'])

Playbook 内の ACI モジュールの usernamepassword などの認証情報のオプションを、ログインするリクエストの組み立てに利用しています。

リクエストの形式は、ACI REST API ドキュメントの「API セッションの認証と維持」にあるのと同じです。

(例の引用)

POST https://192.0.20.123/api/aaaLogin.json

{
  "aaaUser" : {
    "attributes" : {
      "name" : "georgewa",
      "pwd" : "paSSword1"
    }
  }
}

そして認証が正常であれば、

        self.headers['Cookie'] = resp.headers['Set-Cookie']

Cookie を保存しています。 https://github.com/ansible/ansible/blob/v2.8.5/lib/ansible/module_utils/network/aci/aci.py#L212

実際の処理を投げる際は、この認証済みの Cookie を利用するのでしょう。次のリクエストも見てみます。


■ 次のリクエス

ACIModule クラスのインスタス化の過程でログインを済ませ、Cookie も保存したあとは、また lib/ansible/modules/network/aci/aci_epg.py に戻ります。

次に、ACI REST API にリクエストしているのは

    aci.get_existing()

のようです。 https://github.com/ansible/ansible/blob/v2.8.5/lib/ansible/modules/network/aci/aci_epg.py#L352

get_existing の定義はこちら https://github.com/ansible/ansible/blob/v2.8.5/lib/ansible/module_utils/network/aci/aci.py#L786

こちらでも ACI REST API へのリクエストが行われています。処理する前の状態を調べるためのようです。

        resp, info = fetch_url(self.module, uri,
                               headers=self.headers,
                               method='GET',
                               timeout=self.params['timeout'],
                               use_proxy=self.params['use_proxy'])

ここで headers=self.headers とあるので、先程保存した Cookie self.headers['Cookie'] も渡るはずです。

このようにして、事前にログインした情報を引き継いで、次のリクエストに渡しているようです。

更に次のリクエスト処理である、aci.post_config() でも同様でした。


■ 注意点: パスワード認証は HTTP 503 になる可能性がある

Ansible 公式ドキュメントの Cisco ACI Guideには、以下の記載があります。

Password-based authentication also may trigger anti-DoS measures in ACI v3.1+ that causes session throttling and results in HTTP 503 errors and login failures.

ACI v3.1 以上では、DOS対策として、パスワードベースの認証にたいして HTTP 503 のエラーを返すことがあるそうです。

代わりに、署名方式の認証方法の仕方が紹介されています。

Signature-based authentication using certificates


■ まとめ

ACI モジュールの APIログイン処理の流れを追った時の話をまとめました。

当初は、処理のリクエストとログイン処理は同じリクエストだと予想していましたが、実際はことなりました。 一つのタスク内で、何度か ACI REST API へアクセスする都合上、2回目以降のオーバーヘッドを低減するためにこのようにしているのだと思います。

Visual Studio Code 上のコードをパワポにコピペするとシンタックスハイライトしたままペーストできる

はじめに

タイトルのままですが、 Visual Studio Code(以下、VS Code) 上のコードをパワポにコピペすると、シンタックスハイライトしたままペーストできます。

パワポで資料を作る際、サンプルコードを貼る場合に便利です。

シンタックスハイライトしないで貼ると、やや見にくいですし、かといってシンタックスハイライトされたVS Code のエディタ画面のキャプチャですと、コードを修正できません。

見やすく、かつ修正できるのがこの方法です。

方法

f:id:akira6592:20190924214958p:plain
(1) 普通にコピー

f:id:akira6592:20190924222135p:plain
(2) 書式を保ってペースト

f:id:akira6592:20190924215318p:plain
(3) シンタックスハイライトされたまま文字列としてペーストされる

デモ動画

補足

なお、VSCode 1.37.1 までは、ペースとした際にインデントがなくなってしまう事象がありましたが、1.38.1 での確認では正常にインデントを保ってくれるようになりました。