Inventory init.py

说明

这个模块比较简单,类inventory的总管,去调其它的模块做事。 包含基础的group host类,也包含配置文件解析、文件夹解析以及动态inventory(script.py)类的解析 host_list参数的不同情况来做不同解析的事情,看看源码解析就知道,后面的一部分代码就是来用获取东西的,不写。

class Inventory(object):
    """
    Host inventory for ansible.
    """

    __slots__ = [ 'host_list', 'groups', '_restriction', '_also_restriction', '_subset',
                  'parser', '_vars_per_host', '_vars_per_group', '_hosts_cache', '_groups_list',
                  '_pattern_cache', '_vault_password', '_vars_plugins', '_playbook_basedir']

    def __init__(self, host_list=C.DEFAULT_HOST_LIST, vault_password=None):

        # the host file file, or script path, or list of hosts
        # if a list, inventory data will NOT be loaded
        # 就是这个host_list非常关键,我们要获取inventory按照格式给一个script就行
        # 注意加载中文的时候加一个.decode("utf-8")
        self.host_list = host_list
        self._vault_password=vault_password

        # caching to avoid repeated calculations, particularly with
        # external inventory scripts.
        # 这里做一些缓存,避免重复计算,但是指同一个实例。

        self._vars_per_host  = {}
        self._vars_per_group = {}
        self._hosts_cache    = {}
        self._groups_list    = {}
        self._pattern_cache  = {}

        # to be set by calling set_playbook_basedir by playbook code
        self._playbook_basedir = None

        # the inventory object holds a list of groups
        self.groups = []

        # a list of host(names) to contain current inquiries to
        self._restriction = None
        self._also_restriction = None
        self._subset = None
        
        # 条件1,如果是字符串并且,在字符串中,进行赋值
        # 注意这个if与下面的if不是一个逻辑,这是先行逻辑
        if isinstance(host_list, basestring):
            if "," in host_list:
                host_list = host_list.split(",")
                host_list = [ h for h in host_list if h and h.strip() ]
                # 注意这里会输出一个列表

        if host_list is None:
            self.parser = None
        # 接着上面的处理, 匹配IPV6,使用host类实例化主机名然后添加主机
        elif isinstance(host_list, list):
            self.parser = None
            all = Group('all')
            self.groups = [ all ]
            ipv6_re = re.compile('\[([a-f:A-F0-9]*[%[0-z]+]?)\](?::(\d+))?')
            for x in host_list:
                m = ipv6_re.match(x)
                if m:
                    all.add_host(Host(m.groups()[0], m.groups()[1]))
                else:
                    if ":" in x:
                        tokens = x.rsplit(":", 1)
                        # if there is ':' in the address, then this is an ipv6
                        if ':' in tokens[0]:
                            all.add_host(Host(x))
                        else:
                            all.add_host(Host(tokens[0], tokens[1]))
                    else:
                        all.add_host(Host(x))
        # 如果这个文件名存在,即走文件名方式,判断是否是目录,是目录调dir解析
        # 是文件就是脚本解析,即我们讲的动态inventory,走不通这个就走ini配置文件解析
        # 这里的判断点就是SheBang(#!)
        elif os.path.exists(host_list):
            if os.path.isdir(host_list):
                # Ensure basedir is inside the directory
                self.host_list = os.path.join(self.host_list, "")
                self.parser = InventoryDirectory(filename=host_list) # 目录解析
                self.groups = self.parser.groups.values()
            else: # 动态inventory
                # check to see if the specified file starts with a
                # shebang (#!/), so if an error is raised by the parser
                # class we can show a more apropos error
                shebang_present = False
                try:
                    inv_file = open(host_list)
                    first_line = inv_file.readlines()[0]
                    inv_file.close()
                    if first_line.startswith('#!'):
                        shebang_present = True
                except:
                    pass

                if utils.is_executable(host_list):
                    try:
                        self.parser = InventoryScript(filename=host_list) # 脚本解析
                        self.groups = self.parser.groups.values()
                    except:
                        if not shebang_present:
                            raise errors.AnsibleError("The file %s is marked as executable, but failed to execute correctly. " % host_list + \
                                                      "If this is not supposed to be an executable script, correct this with `chmod -x %s`." % host_list)
                        else:
                            raise
                else:
                    try:
                        self.parser = InventoryParser(filename=host_list) # 文件解析
                        self.groups = self.parser.groups.values()
                    except:
                        if shebang_present:
                            raise errors.AnsibleError("The file %s looks like it should be an executable inventory script, but is not marked executable. " % host_list + \
                                                      "Perhaps you want to correct this with `chmod +x %s`?" % host_list)
                        else:
                            raise

            utils.plugins.vars_loader.add_directory(self.basedir(), with_subdir=True)
        else:
            raise errors.AnsibleError("Unable to find an inventory file, specify one with -i ?")

        self._vars_plugins = [ x for x in utils.plugins.vars_loader.all(self) ]

        # get group vars from group_vars/ files and vars plugins
        for group in self.groups:
            group.vars = utils.combine_vars(group.vars, self.get_group_variables(group.name, vault_password=self._vault_password))

        # get host vars from host_vars/ files and vars plugins
        for host in self.get_hosts():
            host.vars = utils.combine_vars(host.vars, self.get_host_variables(host.name, vault_password=self._vault_password))