ansible笔记


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组, 可能是还需要一些其他的操作吧,继续翻源码,找到了InventoryDatareconcile_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"
    }))