Fabric のホストリストにAnsibleのインベントリを使う(2017年版)

# 免責

超やっつけのコードなので、ざっくりとした動作しか見てないです

# 前提

こんな環境下での話ですよ。というメモ

```shell-session:バージョンチェック
$ ansible --version
ansible 2.2.0.0
  config file = /home/takei_kazuya/ansible/ansible.cfg
  configured module search path = Default w/o overrides
$ fab --version
Fabric 1.12.0
Paramiko 1.17.2
```

# TL;DR

最新バージョン向け「AnsibleのInventoryをFabricのホストリストに使う簡易レシピ」はこちら

```python:fabfile.py
# 前略

from ansible.inventory import Inventory
from ansible.parsing.dataloader import DataLoader
from ansible.vars import VariableManager


ansible_loader = DataLoader()
ansible_var_manager = VariableManager()
ansible_inventory = Inventory(ansible_loader, ansible_var_manager, '/etc/ansible/hosts')
env.roledefs = ansible_inventory.get_group_dict()

# 以下略
```

# 本題

構成管理に`Ansible`をデプロイ系に`Fabric`を採用するケースで、Ansibleのinventoryを使いたいというのは多分よくある話ではあって、Qiitaにも「[FabricからAnsibleのインベントリを美味しく参照する](http://qiita.com/shivaken/items/c679ae9d15ac1cbebd0b)」という記事もあります。

で、参照して（というかインベントリファイル以外コピペ）して実行すると、

```python
# 前略
from ansible.inventory import Inventory

inventory = Inventory('MY_INVENTORY')
env.roledefs = inventory.groups_list()

# 攻略
```

```pytb
Traceback (most recent call last):
  File "/usr/lib/python2.6/site-packages/fabric/main.py", line 666, in main
    docstring, callables, default = load_fabfile(fabfile)
  File "/usr/lib/python2.6/site-packages/fabric/main.py", line 167, in load_fabfile
    imported = importer(os.path.splitext(fabfile)[0])
  File "/home/attakei/infra/fabfile.py", line 17, in <module>
    ansible_inventory = Inventory(ansible_inventory_file)
TypeError: __init__() takes at least 3 arguments (2 given)
```

動かなかった。。。

以下、[最新のリポジトリ](https://github.com/ansible/ansible)を見ながら少しずつ直していく。


## Inventoryのinit時引数が違っている

上記のエラーの通り、どうやら`__init__`の引数が以前とは違っているっぽい？
[現状のInventory.__init__](https://github.com/ansible/ansible/blob/devel/lib/ansible/inventory/__init__.py#L58)を見ると、確かに引数は最低2個必要な上にinventoryファイルはオプションになっている模様。
じゃあ、最初の`loader`,`variable_manager`には何を渡せばいんんだろうか？

手っ取り早く[ansible-playbookの実体](https://github.com/ansible/ansible/blob/devel/lib/ansible/cli/playbook.py#L130)を見てみた。
`loader`には`ansible.parsing.dataloader.DataLoader`のインスタンス、 `variable_manager`には`ansible.vars.VariableManager`のインスタンスをそれぞれ渡せば良さげな気がする。

## groups_listがなくなっている

`Inventory()`実行時のエラーは上記で解消できるのだが、今度は`inventory.groups_list()`が失敗する。コードを見ると、このメソッドは今はない模様。
斜め読みした感じでは[Inventory.get_group_dict()](https://github.com/ansible/ansible/blob/devel/lib/ansible/inventory/__init__.py#L577)がそれっぽいので、代用する。


## 出来上がったのがこちら

```python:fabfile.py
# 前略

from ansible.inventory import Inventory
from ansible.parsing.dataloader import DataLoader
from ansible.vars import VariableManager


ansible_loader = DataLoader()
ansible_var_manager = VariableManager()
ansible_inventory = Inventory(ansible_loader, ansible_var_manager, '/etc/ansible/hosts')
env.roledefs = ansible_inventory.get_group_dict()

# 以下略
```

# 補足事項

上記のコードだとinventoryファイルを1個しか指定することができず、ちょっと使いづらいです。
この辺は`fabric`の側でもしかしたらスマートな手法があるかもしれないですが、自環境では暫定で環境変数にinventoryを突っ込む運用で間に合わせています。

```py:fabfile.py
from fabric.api import env
from ansible.inventory import Inventory
from ansible.parsing.dataloader import DataLoader
from ansible.vars import VariableManager

ansible_inventory_file = os.environ.get('INVENTORY', '/etc/ansible/hosts')
ansible_loader = DataLoader()
ansible_var_manager = VariableManager()
ansible_inventory = Inventory(ansible_loader, ansible_var_manager, ansible_inventory_file)
env.roledefs = ansible_inventory.get_group_dict()
```