Table of Contents
ansible动态解析inventory
ansible调用inventory模块时总会调用一个文件或脚本来进行处理, 但我想要动态的解析inventory, 即直接传入一个字符串而不是文件, 直接调用ansible的接口来进行解析(不同格式的inventory也可以手动解析,比如yaml格式可以使用pyyaml解析,不过直接使用ansible接口会更方便一些)
但问题是ansible没有直接可供调用的接口, 不过可以直接查看ansible源码,找到相应的解析函数,封装一下即可
查找源码, 根据 InventoryManager
传递的source变量找到parse_sources
这个函数
class InventoryManager(object): def parse_sources(self, cache=False): ''' iterate over inventory sources and parse each one to populate it''' self._setup_inventory_plugins() ...
然后再根据
def _setup_inventory_plugins(self): ''' sets up loaded inventory plugins for usage ''' inventory_loader = PluginLoader('InventoryModule', 'ansible.plugins.inventory', C.DEFAULT_INVENTORY_PLUGIN_PATH, 'inventory_plugins') ...
找到对应的解析plugin, 我使用的是ini格式的inventory, 所以自定义一下ansible.plugins.inventory.ini.InventoryModule
这个模块即可
from ansible.plugins.inventory.ini import InventoryModule from ansible.inventory.data import InventoryData from ansible.parsing.dataloader import DataLoader from ansible.module_utils._text import to_text from ansible.template import Templar class InventoryCustomModule(InventoryModule): def mine_parse(self, b_data): self.loader = DataLoader() self.inventory = InventoryData() self.templar = Templar(loader=self.loader) try: data = to_text(b_data, errors='surrogate_or_strict').splitlines() except UnicodeError: data = [] for line in b_data.splitlines(): if line and line[0] in self.b_COMMENT_MARKERS: data.append(u'') else: data.append(to_text(line, errors='surrogate_or_strict')) return self._parse("", data)
- 如何使用:
text = '''\ [MY-HOST] MY_HOST-1 ansible_ssh_host=127.0.0.1 MY_HOST-2 ansible_ssh_host=127.0.0.2 MY_HOST-3 ansible_ssh_host=127.0.0.3 MY_HOST-4 ansible_ssh_host=127.0.0.4 [MY-HOST:vars] vip=127.0.0.10 ppp=test [MY-HOST1] MY_HOST1-1 ansible_ssh_host=127.0.0.11 MY_HOST1-2 ansible_ssh_host=127.0.0.12 [MY-HOST:children] MY-HOST1 ''' module = InventoryCustomModule() module.mine_parse(text) for _, group in module.inventory.groups.items(): print(group, group.child_groups, group.vars) for host in group.hosts: print(host, host.vars)
结果:
(ungrouped, [], {}) (all, [ungrouped], {}) (MY-HOST, [MY-HOST1], {u'vip': u'127.0.0.10', u'ppp': u'test'}) (MY_HOST-1, {u'ansible_ssh_host': u'127.0.0.1', 'inventory_file': None, 'inventory_dir': None}) (MY_HOST-2, {u'ansible_ssh_host': u'127.0.0.2', 'inventory_file': None, 'inventory_dir': None}) (MY_HOST-3, {u'ansible_ssh_host': u'127.0.0.3', 'inventory_file': None, 'inventory_dir': None}) (MY_HOST-4, {u'ansible_ssh_host': u'127.0.0.4', 'inventory_file': None, 'inventory_dir': None}) (MY-HOST1, [], {}) (MY_HOST1-1, {u'ansible_ssh_host': u'127.0.0.11', 'inventory_file': None, 'inventory_dir': None}) (MY_HOST1-2, {u'ansible_ssh_host': u'127.0.0.12', 'inventory_file': None, 'inventory_dir': None})
可以看出已经没什么大的问题了, 但有一个点, all组下的groups列表只有
ungrouped
, 正常情况下MY-HOST
组也应该继承all
组,可能是还需要一些其他的操作吧,继续翻源码,找到了InventoryData
的reconcile_inventory
方法, 修改一下即可module = InventoryCustomModule() module.mine_parse(text) module.inventory.reconcile_inventory()
ansible自定义模块传递list变量会变成字符串
我自定义了一个模块,需要传入一个list变量group_names
from ansible.module_utils.basic import AnsibleModule def main(): module = AnsibleModule( argument_spec=dict(group_names={ "required": True })) i = module.params.get('group_names') msg = {"group_names": i, "type": str(type(i))} module.fail_json(changed=False, msg=msg) if __name__ == "__main__": main()
但发现传入的变量最后变成的str type
FAILED! => {"changed": false, "failed": true, "msg": {"group_names": "['test']", "type": "<type 'str'>"}}
最后查找资料后才知道, 传递的变量需要增加type
参数, 否则都是str
module = AnsibleModule( argument_spec=dict(group_names={ "required": True, "type": "list" }))