てくなべ (tekunabe)

ansible / network automation / 学習メモ

[AWX/React] はじめて AWX の UI を修正するまでにやったこと(React何もわからん状態から)

はじめに

AWX の UI (画面)で一部修正したい箇所があり、修正してPRを出したところマージしていただきました。

github.com

AWX の UI 修正は今回が初めてでした。特にフロントエンドは分からないことが多い状態からでしたが、どうにか無事にマージしていただきました。

この記事では、技術的な調査や修正、PR提出までのやったことをまとめます。

[2022/10/04 追記] バージョン 21.7.0 としてリリースされました( Changelog)。

修正したかった箇所

各画面の右上にベルアイコンがあり、ワークフロージョブテンプレートの実行中に承認ノード止まっている待ちの数を示すのですが、待ちが0のときと1以上のときで見た目の変化が少なく分かりにくいなと感じていました。

数字が変わるのみで、承認待ち有無が視覚的にはわかりにくく感じた(上段が承認待ちなし、下段が承認待ち1件)

なのでここを修正してみようと思いました。

最終的にはこうしました。

承認待ちがあるときは色も変わるようにした

React の調査

AWXの UI は React (17系)で作られていることが分かったので、まず React について調べました。そもそも JavaScript は、入力フォームのバリデーションで昔少し使ったことがある程度なので、まず最近の JavaScript の書きっぷりがわからない状態でした。

関連する書籍をいくつか読んでいくことにしました。

www.sbcr.jp

上記の本で、React の前提知識である最近の JavaScript の書きっぷり、コンポーネントの考え方、JSX、Props、State、副作用などのReact の基本が学習できました。

もうちょっと別の React の本を読んでみたいなと思って以下の本を読みました。

www.c-r.com

そもそもフロントエンドの全体像が分かっていないなと思ったので、次に以下の本を読みました。先にこれを読んでおけばよかったかなと思いました。

bookplus.nikkei.com

一番新しそうな本として以下の本も読みました。ただし、今までわからなかった部分の補足としてつまみ読みという感じです。AWX では Next.js を使っていないようなので Next.js のパートは、さーっと読むにとどめました。

booklog.jp

最後はこちら。関数型プログラミングのところはほぼ飛ばしてしまいました・・。

www.oreilly.co.jp

今回は本の読み方として、体系的に学びたいというよりは、修正に必要な知識を取り込みたい、というスタンスです。

AWX のコードもなんとなーく、少しだけですが読めるようになってきました。

ただ、JavaScript についてはこの記号、書き方なんだっけ?となり、React としては、これやるとどうなるんだっけ?と思うことは相変わらず多々ありました。

修正箇所の特定

AWX の UI のコードは、awx/ui ディレクトリにあるようです。この中から、画面右上のあのバッジを定義しているコンポーネントを探せば良いわけです。が、ちょっと自信がなかったので、React Developer Tools という Chrome 拡張を使って、コンポーネントの階層が確認できたのが助かりました。

AWX は PatternFlyというコンポーネント集を利用しています。現状のバッジは、ベルアイコンは BellIconというアイコン、数字部分は Badge というものを使っていることが分かりました。

PatternFlyのページはあとでも何回か見る機会がありました。

開発環境の構築

なんとなくコードが追えるようになったり、修正箇所が特定できても、ちょっといじって変化を見て・・という工程を繰り返さないと理解が深まらないことが多々あります。

なので、コードを修正するための環境をつくりました。詳細は別の記事にまとめています。

tekunabe.hatenablog.jp

修正作業

ようやく修正作業です。修正して確認して・・を繰り返しました。ホットリロード便利ですね。

テストを書くか迷ったんですが、既存のテストの粒度感をみて書きませんでした。書く練習もしてみれば良かったなとは思います。

PR提出

どきどきしながら PR を出しました。

薄々感じていたのですが、最初にやった修正でもちょっとまだ変化が分かりにくいかなと思っていました。それもあってか、別のコンポーネント Notification badge に差し替えたほうが良いとコメントいただきました。なるほど確かにと思って、再度修正しました。

今回始めての PR 提出だったのですが、このリポジトリの仕組みとしては、初めての場合はCIを走らせるのに承認が必要でした。いざ承認されて CI が走ったら失敗しました。今回の私の修正とは無関係なようでした。レビュアーの方に rebase を指示されたので rebase して force push しました。CONTRIBUTING.md にも merge ではなく rebase でという旨の記述がありました。

We like to keep our commit history clean, and will require resubmission of pull requests that contain merge commits. Use git pull --rebase, rather than git pull, and git rebase, rather than git merge.

Cypress によるテスト(これは手動でキックするようです)は失敗しましたが、これも無関係だったようで、マージしていただきました。

この修正を含むリリースは現時点はまだですが、21.7.0 とか 22.0.0 とかのバージョンに含まれるのかなと思います。

おわりに

はじめてでしたが、無事にマージされてよかったです。 この修正が Automation Controlelr にも入るのか、いつ入るのかは分かりませんが、入ると良いなぁと思っています。

[Ansible] VS Code の Ansible 拡張でファイル内容によって関連付けされるようになった

はじめに

以前の記事で、VS Code の Ansible 拡張で「ファイル名やモードライン」によって、ファイルが関連付けされるようになったことを書きました。

tekunabe.hatenablog.jp

その後のアップデートで、バージョン 0.14.0では、ファイルの内容までチェックして、関連付けするよる機能が付きました。Playbook的なキーワードがあると Ansible に関連付けしてくれます。

おためし

以前と同じディレクトリ、ファイル構成です。

test.yml に注目していただいきたいのですが、 開いていないのでまだ ansible に関連付けられていません。ファイル名による関連付け規則にマッチしないためです。

開く前は test.yml はまだ ansible に関連付けられていない

test.yml を開きます。開いた直後はまだ関連付けされていません。

まだ関連付けされていない

試す限り、ファイルの保存操作をすると関連付けされました。Playbook 的なキーワードがあると Playbook と判断してくれるようです。

上書き保存したら ansible に関連付けされた

便利でありがたいです。

[Ansible] netbox.netbox.nb_inventory インベントリプラグインの token でJinja2テンプレートが使えるようになった

はじめに

リリースされたばかりの netbox.netbox コレクション 3.8.0で、こんな changelog がありました。

Allow netbox api access token to be templated by @TWitzenrath in #806

パッと見では分からなかったのですが、元の PR #806を見ると、netbox.netbox.nv_inventory インベントリプラグインのことのようでした。

これまでは平文べた書きか ansible-vault による暗号化文字列を指定していましたが、これにより指定方法の選択肢が増えました。

元の PR #806 では、community.hashi_vault.hashi_vault lookup プラグインを使い、HashiCorp Vault を使って NetBox の token を取ってきて指定する、というユースケースでした。

token: "{{ lookup('community.hashi_vault.hashi_vault','secret=[...path...]/netbox:api_token')}}"

なるほどと思い、少し異なるパターンを試してみました。

  • 環境
    • ansible-core 2.13.1
    • netbox.netbox コレクション 3.8.0

おため

私の環境では env lookup プラグインを使い、MY_NETBOX_TOKEN という環境変数に仕込まれたtokenを利用してみます。

※なお、ドキュメントにもあるように環境変数 NETBOX_TOKEN または NETBOX_API_KEY に仕込んでおけば token オプション自体不要です。ここでは token オプションの挙動を検証するためにあえて別の環境変数を利用しています。

  • test_nv_inventory.yml
---
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.example.com
token: "{{ lookup('env', 'NETBOX_TOKEN') }}"

query_filters:
  - site: nagoya

ansible-inventory コマンドでうまくデバイスを取得できるか確認します。

ansible-inventory -i test_nv_inventory.yml --graph    # 環境変数 NETBOX_TOKEN は仕込み済み
@all:
  |--@ungrouped:
  |  |--nagoya-l2sw01
  |  |--nagoya-l2sw02
  |  |--nagoya-l2sw03

無事取得できました。

インベントリファイルに機密情報が入らなくなって良いなと思いました。

参考

tekunabe.hatenablog.jp

[Ansible] モジュールのオプションのデフォルト値をカスタマイズできる module_defaults

はじめに

Playbookではモジュールにオプションを指定してさまざまな処理を自動化していきます。

Playbookを書いていくうちに、このモジュール(またはコレクション単位)のこのオプションにはいつも同じ値を設定する、というときはないでしょうか。

たとえば ansible.builtin.uri モジュールをたくさん使っていて、毎回 force_basic_auth: true を指定しているようなケースです。

モジュールの仕様としてデフォルト値が設定されている場合もありますが、別のデフォルト値を設定したかったり、そもそもデフォルト値がないオプションにデフォルト値を設定したくなります。

こんなときに便利な module_defaults というキーワードがあります。

docs.ansible.com

module_defaults を使うと、都度都度モジュールごとに毎回同じオプションを指定しなくて良くなります。

module_defaults は、モジュール単位で指定する方法と、「グループ」によって複数モジュールにまるごと指定する方法があります。

指定方法1: モジュール単位で指定

公式ドキュメントのサンプルを引用します。

- hosts: localhost
  module_defaults:
    ansible.builtin.uri:
      force_basic_auth: true
      user: some_user
      password: some_password
  tasks:
    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever1

    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever2

    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever3

冒頭の module_defaults のところで、ansible.builtin.uri モジュールに対して3つのオプションのデフォルト値を設定しています。

ansible.builtin.uri モジュールの仕様としては、force_basic_auth オプションのデフォルト値は yesuserpassword オプションはデフォルト値なしです。これらのデフォルト値を上書き、または新たに設定している、ということです。

これを、仮に module_defautls を使わずタスクごとに指定する場合は、以下のような Playbook になります。少々ノイジーになってしまうかなと思います。

- hosts: localhost
  tasks:
    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever1
        force_basic_auth: true    # タスクごとに指定
        user: some_user           # タスクごとに指定
        password: some_password   # タスクごとに指定

    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever2
        force_basic_auth: true    # タスクごとに指定
        user: some_user           # タスクごとに指定
        password: some_password   # タスクごとに指定

    - name: Interact with a web service
      ansible.builtin.uri:
        url: http://some.api.host/v1/whatever3
        force_basic_auth: true    # タスクごとに指定
        user: some_user           # タスクごとに指定
        password: some_password   # タスクごとに指定

オプションの値は変数にすれば統一管理できますが、タスクごとにオプションを指定すること自体は避けられません。module_defaults を使うとこのような重複を避けられて便利です。

指定方法2: グループによる複数モジュールまとめた指定

モジュールの中には、共通のオプションをもったものがあります。こういうときのために、これらを「グループ」で定義してまとめてデフォルト値を設定する Module defaults groupsという機能があります。

以前からも、aws や、azuregcp など向けのモジュールのいくつかのグループが、Ansible本体に定義されていました

module_defaults でグループまとめて指定する場合は、以下のようにします。

  module_defaults:
    group/グループ名:
      オプション名1: 値1
      オプション名2: 値2

ansible-core 2.12 からは、コレクション側でグループを定義できるようになりました。これが後押しになったのか、グループ化されたモジュールも増えてきました。これはかなり便利だなと思っています。

グループ名は各コレクションの meta/runtime.yml ファイル内の、action_group キー配下で確認できます。

meta/runtime.yml は、コレクションを管理しているリポジトリ側でも確認できます(利用するバージョンと同じものを確認する)し、もちろんインストールした手元の環境でも確認できます。

以下、一つのコレクションをもとに説明します。

netbox.netbox コレクションの場合

netbox.netbox コレクション 3.8.0 の場合は、meta/runtime.ymlに以下の定義があります。(この定義は 3.8.0 からできました

action_groups:
  netbox:
    - netbox_aggregate
    - netbox_cable
    - netbox_circuit
#...(略)...

これは、netbox_aggregatenetbox_cable などモジュールなどを netbox というグループでまとめてるよ、という意味です。

なので、module_defaults の指定の仕方は以下のようになります。

  module_defaults:
    group/netbox.netbox.netbox:
      オプション名1: 値1
      オプション名2: 値2
#...(略)...

グループ名が netbox.netbox.netbox と、わけがわからない感じになってますが、netbox.netbox コレクション内の netbox グループ、という指定です。最後の netboxmeta/runtime.ymlに定義されているグループ名です。

[2022/11/14 追記]

netbox.netbox コレクション 3.9.0 の段階で、公式ドキュメントにも module_defaults に関する記述が追記されました。

netbox-ansible-collection.readthedocs.io

おためし

netbox.netbox コレクション内のモジュールでで試した結果をまとめます

  • 環境
    • ansible-core 2.13.1
    • netbox.netbox コレクション 3.8.0 (これ未満のバージョンにはグループ名の定義がないため機能しません)

netbox.netbox コレクションの各モジュールは、netbox_urlnetbox_token など接続関連のオプションを共通で持っています。netbox_urlnetbox_token オプションは必須なので、通常であれば各タスクに都度指定する必要があります。

module_defaults を利用すると、各タスクに都度指定する必要がなくなります。

以下は、module_defaults を利用したPlaybook例です。タスク側には netbox_urlnetbox_token を指定していません。

---
- hosts: netbox01
  gather_facts: false

  module_defaults:
    group/netbox.netbox.netbox:
      netbox_url: "{{ netbox_url }}"      # Playごとに指定
      netbox_token: "{{ netbox_token }}"  # Playごとに指定

  tasks:
    - name: Create a site
      netbox.netbox.netbox_site:
        data:
          name: Nagoya
        state: present

    - name: Create a device
      netbox.netbox.netbox_device:
        data:
          name: Test Device
          device_type: C9200-48P
          device_role: Access Switch
          site: Nagoya
        state: present

ホスト変数定義ファイル、host_vars/netbox.01.yml には、以下のように netbox_urlnetbox_token 変数を定義しておきます(機密情報は ansible-vault などで暗号化推奨)。

---
ansible_connection: local
netbox_url: https://netbox.example.com
netbox_token: xxxxxxxxxxxxx_dummy_xxxxxxxxxxxx
ansible_python_interpreter: "{{ ansible_playbook_python }}"

以下、Playbook 実行例です。

 % ansible-playbook -i inventory.ini netbox.yml    

PLAY [netbox01] ****************************************************************************************************

TASK [Create a site] ***********************************************************************************************
changed: [netbox01]

TASK [Create a device] *********************************************************************************************
changed: [netbox01]

PLAY RECAP *********************************************************************************************************
netbox01                   : ok=2    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

無事に実行できました。

なお、仮にこのタスクの定義方法で modulue_defautls の定義をしなかった場合は、missing required arguments: netbox_token, netbox_url というエラーになります。今回正常に実行できるのは、netbox_tokennetbox_url オプションは必須のオプションでタスク側には定義がないけど、modulue_defautls で定義してあるから大丈夫ということです。

NetBox 側の結果です。

site Nagoya が作成できた

Test Device が作成できた

(おためしには NetBox のデモサイト https://demo.netbox.dev/ を利用させていただきました。便利です。)

他のコレクションのグループ名

他のコレクションのグループ名の定義も、現時点の最新バージョンのものを少し覗いてみます。

コレクションに含まれる全モジュールをグループ化している傾向にあるようです。

コレクション名前 調査バージョン meta/runtime.yml グループ名(FQCN
awx.awx 21.6.0 meta/runtime.yml awx.awx.controller
community.grafana 1.5.2 meta/runtime.yml community.grafana.grafana
communityhashi_vault 3.2.0 meta/runtime.yml communityhashi_vault.vault

他、もともと Ansible 本体で定義されていたグループは、コレクション側の定義に紐づけるような仕組みになったようです。

補足: module_defaults キーワードを指定できる場所

今回は Play 単位で、module_defaults キーワードを指定しましたが、他にも block や role 、 task 単位でも指定で来ます。

キーワード一覧 Playbook Keywords — Ansible Documentation

[Ansible] ansible-galaxy collection install コマンドは複数のコレクションを指定できる

はじめに

コレクションをインストールするには ansible-galaxy collection install コレクション名やアーカイブファイル名前 という書式が一番シンプルかなと思います。

docs.ansible.com

複数のコレクションをいっぺんにインストールしたい場合は、]requirements.yml というファイルに定義して -r オプションで指定する](https://docs.ansible.com/ansible/latest/user_guide/collections_using.html#install-multiple-collections-with-a-requirements-file)方法もあります。

ですが、ファイルに書くほどでもないけど、いますぐ、手っ取り早く複数のコレクションを指定するには、

ansible-galaxy collection install コレクション1 コレクション2 コレクション3

のような指定の仕方もできます。

  • 環境
    • ansible-core 2.12.

おためし

ansible.utilsnetbox.netbox という2つのコレクションを一度にインストールしてみます。

$ ansible-galaxy collection install ansible.utils netbox.netbox
Starting galaxy collection install process
Process install dependency map
Starting collection install process
Downloading https://galaxy.ansible.com/download/ansible-utils-2.6.1.tar.gz to /Users/sakana/.ansible/tmp/ansible-local-34764sxzv2emb/tmp850gohw3/ansible-utils-2.6.1-bf75vsy5
Installing 'ansible.utils:2.6.1' to '/Users/sakana/.ansible/collections/ansible_collections/ansible/utils'
Downloading https://galaxy.ansible.com/download/netbox-netbox-3.7.1.tar.gz to /Users/sakana/.ansible/tmp/ansible-local-34764sxzv2emb/tmp850gohw3/netbox-netbox-3.7.1-15y3wkfb
ansible.utils:2.6.1 was installed successfully
Installing 'netbox.netbox:3.7.1' to '/Users/sakana/.ansible/collections/ansible_collections/netbox/netbox'
netbox.netbox:3.7.1 was installed successfully

以下の通り無事にインストールできました。

$ ansible-galaxy collection list

# /Users/sakana/.ansible/collections/ansible_collections
Collection    Version
------------- -------
ansible.utils 2.6.1  
netbox.netbox 3.7.1 

補足

コマンドのヘルプを見ると The collection(s) name とあります。

$ ansible-galaxy collection install -h
usage: ansible-galaxy collection install [-h] [-s API_SERVER] [--token API_KEY] [-c] [-v] [-f] [--clear-response-cache] [--no-cache] [-i] [-n | --force-with-deps] [-p COLLECTIONS_PATH]
                                         [-r REQUIREMENTS] [--pre] [-U]
                                         [collection_name ...]

positional arguments:
  collection_name       The collection(s) name or path/url to a tar.gz collection artifact. This is mutually exclusive with --requirements-file.
...(略)...

おまけ

実はというわけでもないですが、複数指定できるものといえば ansible-playbook コマンドも Playbook を複数指定できます。

tekunabe.hatenablog.jp

[Ansible] VS Code の Ansible 拡張でファイル名やモードラインで関連付けされるようになった

はじめに

VS Code で Ansible の Playbook を作成するときに便利な、Ansible という拡張があります。モジュール名やキーワードを自動補完してくたり、ansible-lint と連携してくれたり、とても便利な拡張です。

marketplace.visualstudio.com

この拡張を有効にするには、言語モードを Ansible として開く必要があります。デフォルトでは YAML として開くので、手動で Ansible に指定し直すか、settings.jsonfiles.associations で関連付けを明示的に指定する必要があります。

この点は、最初のつまずきポイントかもしれません。そのためか、本拡張機能バグレポートのテンプレートにも、言語選択が Ansible になっているか確認する項目が設けられています。

本拡張の v0.13.0 で、特定の条件にマッチするファイルの言語選択が Ansible になる関連付けが、デフォルトで組み込まれました。

関連付けのルール

どんなファイル名が Ansible に関連付けられるのかは、package.json のこのあたりを見るとわかります。

        "extensions": [
          ".ansible.yml",
          ".ansible.yaml"
        ],
        "filenamePatterns": [
          "**/playbooks/*.yml",
          "**/playbooks/*.yaml",
          "**/*playbook*.yml",
          "**/*playbook*.yaml"
        ],
        "filenames": [
          "site.yml",
          "site.yaml"
        ],

ほか、PR #600を見ると、ファイルの先頭に

# code: language=ansible

を仕込んでおくと、関連付けされることが分かります。

関連付けを試す

ユーザー の settings.json も、ワークスペースsettings.jsonfiles.associations は空の状態で試します。

いくつかのパターンのファイルを用意しました。ファイル名に Ansible のアイコンになっているのが自動で Ansible に関連付けられたファイルです。

関連付けのお試し

個人的にポイントかなと思ったのは以下の点です。

  • ディレクトリは playbook ではなく、playbooks である必要がある
  • ロール内のファイルは関連付け対象外

おわりに

拡張側の設定として関連付けがされるものを試しました。

あとは、お好みに応じて settings.json で、

{
  // ...(略)...
  "files.associations": {
    "sakana.yml": "ansible",
    "ugui.yml": "ansible"
  }
  // ...(略)...
}

のように追加設定すれば良いかなと思います。

[Ansible] どのバージョンの Python に対応しているかまとめた表ができた

Ansible のどのバージョンで、どのバージョンの Python をサポートしているか、ぱっと出てきますでしょうか。それはマネージドノード側の話でしょうか、コントロールノード側の話でしょうか。

私は、ansible-core 2.11 をよく触ってる時期があって、Python 3.6 で使ってると「次(ansible-core 2.12)からコントロールノード上で Python 3.8 以上が必要になるよ」という警告を目にすることがあったので、この点はやけに覚えています。ですが、それ以外は覚えていません。Ansible のバージョンを横断して調べるとなると少し苦労するかもしれません。

ありがたいことに最近、公式ドキュメントに一通りまとまった表が作成されました(現状 develブランチのみ)。

とてもわかり易いです。

Pythonバージョン対応表

docs.ansible.com

[2024/01/14 追記] 今は以下のページの「ansible-core support matrix」が良いです。

docs.ansible.com