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
    '''
    module = InventoryCustomModule()
    module.mine_parse(text)
    
    for _, group in module.inventory.groups.items():
        if group.name in ["all", "ungrouped"]:
            continue
        print(group, group.hosts)
        print(group.hosts[0].vars)
    

    结果:

    (MY-HOST, [MY_HOST-1, MY_HOST-2, MY_HOST-3, MY_HOST-4])
    {u'ansible_ssh_host': u'127.0.0.1', 'inventory_file': None, 'inventory_dir': None}
    

    但有一个问题, all组下的groups列表为空, 可能是还需要一些其他的操作吧

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"
    }))

There are comments.