Ansibleのlookupプラグインの作り方

2018年8月13日

Python

AWS の ElasticIP の一覧を取得するプラグインを例として、Ansible の lookup プラグインの作り方を解説します。

目次

  1. lookup プラグインとは?
  2. ディレクトリ構成
  3. plugins/lookup/ec2_addresses.py
  4. ansible.cfg
  5. site.yml
  6. playbook の実行
  7. パラメータを渡す

lookup プラグインとは?

lookup プラグインとは、ファイルや API など外部からデータを取得して Ansible で利用するプラグインです。

  • lookup('プラグイン名') もしくは with_プラグイン名 のように使用する
  • よくある例では with_items が lookup プラグイン
  • プラグインに書かれているコードはローカルマシン上で実行される
  • 作り方は Lookups — Ansible Documentation を参照

Ansible 本体の lookup プラグイン ansible/lib/ansible/plugins/lookup にたくさんサンプルがあるので、真似しながら書くと良いです。

ディレクトリ構成

それでは AWS の ElasticIP を取得する lookup プラグインを書いてみましょう。ディレクトリ構成はこんな感じ。

├── ansible.cfg
├── plugins
│   └── lookup
│       └── ec2_addresses.py
└── site.yml

plugins/lookup/ec2_addresses.py

AWS の ElasticIP の一覧を取得するプラグインの最小のコードは以下の通りです。ansible/plugins/lookup/aws_service_ip_ranges.py を参考に実装しました。

from __future__ import absolute_import, division, print_function
import boto3
from ansible.plugins.lookup import LookupBase
class LookupModule(LookupBase):
    def run(self, terms, variables=None, **kwargs):
        ec2 = boto3.client('ec2')
        addresses = ec2.describe_addresses()
        return addresses['Addresses']

ansible.cfg

ansible.cfg に plugin の場所を指定します。

[defaults]
lookup_plugins = ./plugins/lookup

ansible.cfg の具体例は ansible/ansible.cfg を、それぞれの項目の詳細は Ansible Configuration Settings — Ansible Documentation から確認できます。

site.yml

プラグインの動作を確認する playbook です。プラグインから取得した値を ec2_addresses 変数に入れて、中身を for ループで回してデバッグ表示しています。

- hosts: localhost
  connection: local
  vars:
    ec2_addresses: "{{ lookup('ec2_addresses') }}"
  tasks:
  - name: "use list return option and iterate as a loop"
    debug: msg="{% for address in ec2_addresses %}{{ address }} {% endfor %}"

playbook の実行

playbook を実行すると、以下のように boto3 で取得した変数の内容が表示できます。

$ OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES ansible-playbook site.yml

TASK [use list return option and iterate as a loop] ****
ok: [localhost] => {
    "msg": "{'PublicIp': 'xxx.xxx.xxx.xxx', 'AllocationId': 'eipalloc-xxxxxxxx', ...}"
}

私の手元のマシン macOS High Sierra では debug モジュールを使用すると以下のエラーが出るので OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES の環境変数を指定しています。

objc[21237]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called.
objc[21237]: +[__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called. We cannot safely call it or ignore it in the fork() child process. Crashing instead. Set a breakpoint on objc_initializeAfterForkError to debug.

パラメータを渡す

lookup プラグインにはパラメータを渡すことができます。さきほどのプラグインにパラメータを渡せるようにするには ec2_addresses.py に変更を加えます。パラメータは run メソッドの terms 引数で渡されるので、受け取って適切に処理します。

from __future__ import absolute_import, division, print_function
import boto3
from ansible.plugins.lookup import LookupBase
class LookupModule(LookupBase):
    def run(self, terms, variables=None, **kwargs):
        ret = []
        if not terms:
            terms = [{}]
        for term in terms:
            if 'region' in term:
                ec2 = boto3.client('ec2', region_name=term['region'])
            else:
                ec2 = boto3.client('ec2')
            address = ec2.describe_addresses()
            ret.extend(address['Addresses'])
        return ret

site.yml で以下のようにパラメータを渡します。

- hosts: localhost
  connection: local
  vars:
    ec2_addresses: "{{ lookup('ec2_addresses', {region: 'ap-northeast-1'}) }}"
  tasks:
  - name: "use list return option and iterate as a loop"
    debug: msg="{% for address in ec2_addresses %}{{ address }} {% endfor %}"

パラメータを扱えるプラグインは with_* 形式も使えるようになります。

- hosts: localhost
  connection: local
  tasks:
  - name: "use list return option and iterate as a loop"
    debug: msg="{{ item }}"
    with_ec2_addresses:
      - { region: "ap-northeast-1" }

-技術ブログ
-,