def _expand_hostpattern(self, hostpattern): ''' Takes a single host pattern and returns a list of hostnames and an optional port number that applies to all of them. ''' # Can the given hostpattern be parsed as a host with an optional port # specification? try: (pattern, port) = parse_address(hostpattern, allow_ranges=True) except: # not a recognizable host pattern pattern = hostpattern port = None # Once we have separated the pattern, we expand it into list of one or # more hostnames, depending on whether it contains any [x:y] ranges. if detect_range(pattern): hostnames = expand_hostname_range(pattern) else: hostnames = [pattern] return (hostnames, port)
def _expand_hostpattern(self, hostpattern): ''' Takes a single host pattern and returns a list of hostnames and an optional port number that applies to all of them. ''' # Is a port number specified? # # This may be a mandatory :NN suffix on any square-bracketed expression # (IPv6 address, IPv4 address, host name, host pattern), or an optional # :NN suffix on an IPv4 address, host name, or pattern. IPv6 addresses # must be in square brackets if a port is specified. port = None for type in ['bracketed_hostport', 'hostport']: m = self.patterns[type].match(hostpattern) if m: (hostpattern, port) = m.groups() continue # Now we're left with just the pattern, which results in a list of one # or more hostnames, depending on whether it contains any [x:y] ranges. # # FIXME: We could be more strict here about validation. if detect_range(hostpattern): hostnames = expand_hostname_range(hostpattern) else: hostnames = [hostpattern] return (hostnames, port)
def _parse_base_groups(self): # FIXME: refactor ungrouped = Group(name='ungrouped') all = Group(name='all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) active_group_name = 'ungrouped' for line in self.lines: if line.startswith("["): active_group_name = line.replace("[","").replace("]","").strip() if line.find(":vars") != -1 or line.find(":children") != -1: active_group_name = None else: new_group = self.groups[active_group_name] = Group(name=active_group_name) all.add_child_group(new_group) elif line.startswith("#") or line == '': pass elif active_group_name: tokens = shlex.split(line) if len(tokens) == 0: continue hostname = tokens[0] port = C.DEFAULT_REMOTE_PORT # Two cases to check: # 0. A hostname that contains a range pesudo-code and a port # 1. A hostname that contains just a port if (hostname.find("[") != -1 and hostname.find("]") != -1 and hostname.find(":") != -1 and (hostname.rindex("]") < hostname.rindex(":")) or (hostname.find("]") == -1 and hostname.find(":") != -1)): tokens2 = hostname.rsplit(":", 1) hostname = tokens2[0] port = tokens2[1] host = None _all_hosts = [] if hostname in self.hosts: host = self.hosts[hostname] _all_hosts.append(host) else: if detect_range(hostname): _hosts = expand_hostname_range(hostname) for _ in _hosts: host = Host(name=_, port=port) self.hosts[_] = host _all_hosts.append(host) else: host = Host(name=hostname, port=port) self.hosts[hostname] = host _all_hosts.append(host) if len(tokens) > 1: for t in tokens[1:]: (k,v) = t.split("=") host.set_variable(k,v) for _ in _all_hosts: self.groups[active_group_name].add_host(_)
def expand_hostpattern(hostpattern): """Expand pattern into list of hosts. Takes a single host pattern and returns a list of hostnames. :param hostpattern: a single host pattern :returns: list of hostnames """ # Can the given hostpattern be parsed as a host with an optional port # specification? try: # pylint: disable=unused-variable (pattern, port) = parse_address(hostpattern, allow_ranges=True) except: # noqa pylint: disable=bare-except # not a recognizable host pattern pattern = hostpattern # Once we have separated the pattern, we expand it into list of one or # more hostnames, depending on whether it contains any [x:y] ranges. if detect_range(pattern): hostnames = expand_hostname_range(pattern) else: hostnames = [pattern] return hostnames
def _expand_hostpattern(self, hostpattern): ''' Takes a single host pattern and returns a list of hostnames and an optional port number that applies to all of them. ''' # Can the given hostpattern be parsed as a host with an optional port # specification? (pattern, port) = parse_address(hostpattern, allow_ranges=True) if not pattern: self._raise_error("Can't parse '%s' as host[:port]" % hostpattern) # Once we have separated the pattern, we expand it into list of one or # more hostnames, depending on whether it contains any [x:y] ranges. if detect_range(pattern): hostnames = expand_hostname_range(pattern) else: hostnames = [pattern] return (hostnames, port)
def _parse_base_groups(self): # FIXME: refactor ungrouped = Group(name='ungrouped') all = Group(name='all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) active_group_name = 'ungrouped' for line in self.lines: line = utils.before_comment(line).strip() if line.startswith("[") and line.endswith("]"): active_group_name = line.replace("[","").replace("]","") if ":vars" in line or ":children" in line: active_group_name = active_group_name.rsplit(":", 1)[0] if active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group(name=active_group_name) all.add_child_group(new_group) active_group_name = None elif active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group(name=active_group_name) all.add_child_group(new_group) elif line.startswith(";") or line == '': pass elif active_group_name: tokens = shlex.split(line) if len(tokens) == 0: continue hostname = tokens[0] port = C.DEFAULT_REMOTE_PORT # Three cases to check: # 0. A hostname that contains a range pesudo-code and a port # 1. A hostname that contains just a port if hostname.count(":") > 1: # Possible an IPv6 address, or maybe a host line with multiple ranges # IPv6 with Port XXX:XXX::XXX.port # FQDN foo.example.com if hostname.count(".") == 1: (hostname, port) = hostname.rsplit(".", 1) elif ("[" in hostname and "]" in hostname and ":" in hostname and (hostname.rindex("]") < hostname.rindex(":")) or ("]" not in hostname and ":" in hostname)): (hostname, port) = hostname.rsplit(":", 1) hostnames = [] if detect_range(hostname): hostnames = expand_hostname_range(hostname) else: hostnames = [hostname] for hn in hostnames: host = None if hn in self.hosts: host = self.hosts[hn] else: host = Host(name=hn, port=port) self.hosts[hn] = host if len(tokens) > 1: for t in tokens[1:]: if t.startswith('#'): break try: (k,v) = t.split("=", 1) except ValueError, e: raise errors.AnsibleError("Invalid ini entry: %s - %s" % (t, str(e))) # If there is a hash in the value don't pass it through to ast at ast will split at the hash. if "#" in v: host.set_variable(k, v) else: try: host.set_variable(k,ast.literal_eval(v)) # Using explicit exceptions. # Likely a string that literal_eval does not like. We wil then just set it. except ValueError: # For some reason this was thought to be malformed. host.set_variable(k, v) except SyntaxError: # Is this a hash with an equals at the end? host.set_variable(k, v) self.groups[active_group_name].add_host(host)
def test_expand_hostname_range(self): for e in self.ranges_to_expand: r = self.ranges_to_expand[e] self.assertEqual(r, expand_hostname_range(e))
def _parse_base_groups(self): # FIXME: refactor ungrouped = Group(name='ungrouped') all = Group(name='all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) active_group_name = 'ungrouped' for line in self.lines: line = self._before_comment(line).strip() if line.startswith("[") and line.endswith("]"): active_group_name = line.replace("[","").replace("]","") if ":vars" in line or ":children" in line: active_group_name = active_group_name.rsplit(":", 1)[0] if active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group(name=active_group_name) active_group_name = None elif active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group(name=active_group_name) elif line.startswith(";") or line == '': pass elif active_group_name: tokens = shlex.split(line) if len(tokens) == 0: continue hostname = tokens[0] port = C.DEFAULT_REMOTE_PORT # Three cases to check: # 0. A hostname that contains a range pesudo-code and a port # 1. A hostname that contains just a port if hostname.count(":") > 1: # Possible an IPv6 address, or maybe a host line with multiple ranges # IPv6 with Port XXX:XXX::XXX.port # FQDN foo.example.com if hostname.count(".") == 1: (hostname, port) = hostname.rsplit(".", 1) elif ("[" in hostname and "]" in hostname and ":" in hostname and (hostname.rindex("]") < hostname.rindex(":")) or ("]" not in hostname and ":" in hostname)): (hostname, port) = hostname.rsplit(":", 1) hostnames = [] if detect_range(hostname): hostnames = expand_hostname_range(hostname) else: hostnames = [hostname] for hn in hostnames: host = None if hn in self.hosts: host = self.hosts[hn] else: host = Host(name=hn, port=port) self.hosts[hn] = host if len(tokens) > 1: for t in tokens[1:]: if t.startswith('#'): break try: (k,v) = t.split("=", 1) except ValueError, e: raise AnsibleError("Invalid ini entry in %s: %s - %s" % (self.filename, t, str(e))) if k == 'ansible_ssh_host': host.ipv4_address = self._parse_value(v) else: host.set_variable(k, self._parse_value(v)) self.groups[active_group_name].add_host(host)
def _parse_base_groups(self): # FIXME: refactor ungrouped = Group(name='ungrouped') all = Group(name='all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) active_group_name = 'ungrouped' for line in self.lines: line = line.split("#")[0].strip() if line.startswith("[") and line.endswith("]"): active_group_name = line.replace("[", "").replace("]", "") if line.find(":vars") != -1 or line.find(":children") != -1: active_group_name = active_group_name.rsplit(":", 1)[0] if active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group( name=active_group_name) all.add_child_group(new_group) active_group_name = None elif active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group( name=active_group_name) all.add_child_group(new_group) elif line.startswith(";") or line == '': pass elif active_group_name: tokens = shlex.split(line) if len(tokens) == 0: continue hostname = tokens[0] port = C.DEFAULT_REMOTE_PORT # Three cases to check: # 0. A hostname that contains a range pesudo-code and a port # 1. A hostname that contains just a port if hostname.count(":") > 1: # probably an IPv6 addresss, so check for the format # XXX:XXX::XXX.port, otherwise we'll just assume no # port is set if hostname.find(".") != -1: (hostname, port) = hostname.rsplit(".", 1) elif (hostname.find("[") != -1 and hostname.find("]") != -1 and hostname.find(":") != -1 and (hostname.rindex("]") < hostname.rindex(":")) or (hostname.find("]") == -1 and hostname.find(":") != -1)): (hostname, port) = hostname.rsplit(":", 1) hostnames = [] if detect_range(hostname): hostnames = expand_hostname_range(hostname) else: hostnames = [hostname] for hn in hostnames: host = None if hn in self.hosts: host = self.hosts[hn] else: host = Host(name=hn, port=port) self.hosts[hn] = host if len(tokens) > 1: for t in tokens[1:]: if t.startswith('#'): break try: (k, v) = t.split("=") except ValueError, e: raise errors.AnsibleError( "Invalid ini entry: %s - %s" % (t, str(e))) try: host.set_variable(k, ast.literal_eval(v)) except: # most likely a string that literal_eval # doesn't like, so just set it host.set_variable(k, v) self.groups[active_group_name].add_host(host)
def _parse_base_groups(self): # FIXME: refactor,貌似在后面的版本中该函数会被重构 # 这部分解析ini配置文件的代码却是写的很臃肿,急需要重构 # 基础group解析,除了已经定义组名的group以外,还有ungrouped表示未分组的组名,all表示所有组的总和 ungrouped = Group(name='ungrouped') all = Group(name='all') all.add_child_group(ungrouped) # 喜欢这种dict创建字典的方式,比用{}的方式好看多了 self.groups = dict(all=all, ungrouped=ungrouped) active_group_name = 'ungrouped' # 遍历self.lines,self.lines是文件内容, # ini文件的解析大多可以用python内置的conparser类,不过ansible的inventory所支持的语法比较复杂,这里作者自己处理了 for lineno in range(len(self.lines)): line = utils.before_comment(self.lines[lineno]).strip() # 如果某行以'['开头,以']'结尾则表名是一个section if line.startswith("[") and line.endswith("]"): # 将中括号replace掉获得组名 active_group_name = line.replace("[","").replace("]","") # 如果组名中有:vars 或:children,则进行二次处理,比如[southeast:vars],在通过冒号分割得到southeast if ":vars" in line or ":children" in line: active_group_name = active_group_name.rsplit(":", 1)[0] # rsplit(":", 1)表示右边开始以冒号为分隔符分割一次 # 如果组名未加到self.groups里面,则创建一个新的Group类,并添加到self.groups里面 # 如果组名已经存在与self.groups里面,跳过... if active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group(name=active_group_name) # 在这种情况下将active_group_name设置为None,用来表示这不是一个包含真正host的group active_group_name = None # 这部分是组名中没有冒号的处理方式,和上面一样。 elif active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group(name=active_group_name) elif line.startswith(";") or line == '': # 如果改行以分号开始或为空行则跳过。 pass elif active_group_name: # 这种情况表示当前行为非中括号打头的行,既包含真实host主机数据的行 # 在section中包含:vars/:children的段并不包含真实主机 # shlex模块实现了一个类来解析简单的类shell语法,可以用来编写领域特定的语言,或者解析加引号的字符串。 tokens = shlex.split(line) if len(tokens) == 0: continue hostname = tokens[0] # 获取主机名 port = C.DEFAULT_REMOTE_PORT # 使用默认的SSH端口号 # Three cases to check: # 0. A hostname that contains a range pesudo-code and a port,like badwol[a:f].example.com:5309 # 1. A hostname that contains just a port ,like badwolf.example.com:5309 # 对hostname需要进行以下的检测 if hostname.count(":") > 1: # IPV6格式的地址,端口号和hostname之间用"."表示。 # Possible an IPv6 address, or maybe a host line with multiple ranges # IPv6 with Port XXX:XXX::XXX.port # FQDN foo.example.com if hostname.count(".") == 1: (hostname, port) = hostname.rsplit(".", 1) elif ("[" in hostname and "]" in hostname and ":" in hostname and (hostname.rindex("]") < hostname.rindex(":")) or ("]" not in hostname and ":" in hostname)): # 如果冒号在中括号外面,表示这个冒号后面是端口号,因此通过通过rsplit按照冒号分割一次获取端口号和hostname (hostname, port) = hostname.rsplit(":", 1) hostnames = [] # 检测hostname是否是表示一个范围的hosts,如果是则将其扩展成一组host列表,否则加入空列表 if detect_range(hostname): hostnames = expand_hostname_range(hostname) else: hostnames = [hostname] # 遍历hostnames列表 for hn in hostnames: host = None if hn in self.hosts: host = self.hosts[hn] else: # 如果host不在self.hosts列表中,则创建一个Host基类 host = Host(name=hn, port=port) self.hosts[hn] = host # len(tokens) > 1表示该行拥有变量,如:jumper ansible_ssh_port=5555 ansible_ssh_host=192.168.1.50 if len(tokens) > 1: for t in tokens[1:]: if t.startswith('#'): # 如果是注释则退出,在ini文件中仍然可以使用#作为注释标识。 break try: (k,v) = t.split("=", 1) # kv变量解析 except ValueError, e: raise errors.AnsibleError("%s:%s: Invalid ini entry: %s - %s" % (self.filename, lineno + 1, t, str(e))) host.set_variable(k, self._parse_value(v)) # 将该行解析的变量设置到该host下 self.groups[active_group_name].add_host(host) # 将该host加入到对应的group中。
def _parse_base_groups(self): # FIXME: refactor ungrouped = Group(name='ungrouped') all = Group(name='all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) active_group_name = 'ungrouped' for line in self.lines: line = utils.before_comment(line).strip() if line.startswith("[") and line.endswith("]"): active_group_name = line.replace("[","").replace("]","") if line.find(":vars") != -1 or line.find(":children") != -1: active_group_name = active_group_name.rsplit(":", 1)[0] if active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group(name=active_group_name) all.add_child_group(new_group) active_group_name = None elif active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group(name=active_group_name) all.add_child_group(new_group) elif line.startswith(";") or line == '': pass elif active_group_name: tokens = shlex.split(line) if len(tokens) == 0: continue hostname = tokens[0] port = C.DEFAULT_REMOTE_PORT # Three cases to check: # 0. A hostname that contains a range pesudo-code and a port # 1. A hostname that contains just a port if hostname.count(":") > 1: # Possible an IPv6 address, or maybe a host line with multiple ranges # IPv6 with Port XXX:XXX::XXX.port # FQDN foo.example.com if hostname.count(".") == 1: (hostname, port) = hostname.rsplit(".", 1) elif (hostname.find("[") != -1 and hostname.find("]") != -1 and hostname.find(":") != -1 and (hostname.rindex("]") < hostname.rindex(":")) or (hostname.find("]") == -1 and hostname.find(":") != -1)): (hostname, port) = hostname.rsplit(":", 1) hostnames = [] if detect_range(hostname): hostnames = expand_hostname_range(hostname) else: hostnames = [hostname] for hn in hostnames: host = None if hn in self.hosts: host = self.hosts[hn] else: host = Host(name=hn, port=port) self.hosts[hn] = host if len(tokens) > 1: for t in tokens[1:]: if t.startswith('#'): break try: (k,v) = t.split("=", 1) except ValueError, e: raise errors.AnsibleError("Invalid ini entry: %s - %s" % (t, str(e))) # If there is a hash in the value don't pass it through to ast at ast will split at the hash. if "#" in v: host.set_variable(k, v) else: try: host.set_variable(k,ast.literal_eval(v)) # Using explicit exceptions. # Likely a string that literal_eval does not like. We wil then just set it. except ValueError: # For some reason this was thought to be malformed. host.set_variable(k, v) except SyntaxError: # Is this a hash with an equals at the end? host.set_variable(k, v) self.groups[active_group_name].add_host(host)
def _parse_base_groups(self): # FIXME: refactor ungrouped = Group(name='ungrouped') all = Group(name='all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) active_group_name = 'ungrouped' for line in self.lines: if line.startswith("["): active_group_name = line.split(" #")[0].replace("[","").replace("]","").strip() if line.find(":vars") != -1 or line.find(":children") != -1: active_group_name = active_group_name.rsplit(":", 1)[0] if active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group(name=active_group_name) all.add_child_group(new_group) active_group_name = None elif active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group(name=active_group_name) all.add_child_group(new_group) elif line.startswith("#") or line.startswith(";") or line == '': pass elif active_group_name: tokens = shlex.split(line.split(" #")[0]) if len(tokens) == 0: continue hostname = tokens[0] port = C.DEFAULT_REMOTE_PORT # Two cases to check: # 0. A hostname that contains a range pesudo-code and a port # 1. A hostname that contains just a port if (hostname.find("[") != -1 and hostname.find("]") != -1 and hostname.find(":") != -1 and (hostname.rindex("]") < hostname.rindex(":")) or (hostname.find("]") == -1 and hostname.find(":") != -1)): tokens2 = hostname.rsplit(":", 1) hostname = tokens2[0] port = tokens2[1] hostnames = [] if detect_range(hostname): hostnames = expand_hostname_range(hostname) else: hostnames = [hostname] for hn in hostnames: host = None if hn in self.hosts: host = self.hosts[hn] else: host = Host(name=hn, port=port) self.hosts[hn] = host if len(tokens) > 1: for t in tokens[1:]: if t.startswith('#'): break try: (k,v) = t.split("=") except ValueError, e: raise errors.AnsibleError("Invalid ini entry: %s - %s" % (t, str(e))) try: host.set_variable(k,ast.literal_eval(v)) except: # most likely a string that literal_eval # doesn't like, so just set it host.set_variable(k,v) self.groups[active_group_name].add_host(host)
def _parse_base_groups(self): # FIXME: refactor ungrouped = Group(name='ungrouped') all = Group(name='all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) active_group_name = 'ungrouped' for line in self.lines: line = utils.before_comment(line).strip() if line.startswith("[") and line.endswith("]"): active_group_name = line.replace("[", "").replace("]", "") if ":vars" in line or ":children" in line: active_group_name = active_group_name.rsplit(":", 1)[0] if active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group( name=active_group_name) active_group_name = None elif active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group( name=active_group_name) elif line.startswith(";") or line == '': pass elif active_group_name: tokens = shlex.split(line) if len(tokens) == 0: continue hostname = tokens[0] port = C.DEFAULT_REMOTE_PORT # Three cases to check: # 0. A hostname that contains a range pesudo-code and a port # 1. A hostname that contains just a port if hostname.count(":") > 1: # Possible an IPv6 address, or maybe a host line with multiple ranges # IPv6 with Port XXX:XXX::XXX.port # FQDN foo.example.com if hostname.count(".") == 1: (hostname, port) = hostname.rsplit(".", 1) elif ("[" in hostname and "]" in hostname and ":" in hostname and (hostname.rindex("]") < hostname.rindex(":")) or ("]" not in hostname and ":" in hostname)): (hostname, port) = hostname.rsplit(":", 1) hostnames = [] if detect_range(hostname): hostnames = expand_hostname_range(hostname) else: hostnames = [hostname] for hn in hostnames: host = None if hn in self.hosts: host = self.hosts[hn] else: host = Host(name=hn, port=port) self.hosts[hn] = host if len(tokens) > 1: for t in tokens[1:]: if t.startswith('#'): break try: (k, v) = t.split("=", 1) except ValueError, e: raise errors.AnsibleError( "Invalid ini entry: %s - %s" % (t, str(e))) host.set_variable(k, self._parse_value(v)) self.groups[active_group_name].add_host(host)
def _parse_base_groups(self): # FIXME: refactor ungrouped = Group(name='ungrouped') all = Group(name='all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) active_group_name = 'ungrouped' for line in self.lines: if line.startswith("["): active_group_name = line.split(" #")[0].replace( "[", "").replace("]", "").strip() if line.find(":vars") != -1 or line.find(":children") != -1: active_group_name = active_group_name.rsplit(":", 1)[0] if active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group( name=active_group_name) all.add_child_group(new_group) active_group_name = None elif active_group_name not in self.groups: new_group = self.groups[active_group_name] = Group( name=active_group_name) all.add_child_group(new_group) elif line.startswith("#") or line.startswith(";") or line == '': pass elif active_group_name: tokens = shlex.split(line.split(" #")[0]) if len(tokens) == 0: continue hostname = tokens[0] port = C.DEFAULT_REMOTE_PORT # Two cases to check: # 0. A hostname that contains a range pesudo-code and a port # 1. A hostname that contains just a port if (hostname.find("[") != -1 and hostname.find("]") != -1 and hostname.find(":") != -1 and (hostname.rindex("]") < hostname.rindex(":")) or (hostname.find("]") == -1 and hostname.find(":") != -1)): tokens2 = hostname.rsplit(":", 1) hostname = tokens2[0] port = tokens2[1] hostnames = [] if detect_range(hostname): hostnames = expand_hostname_range(hostname) else: hostnames = [hostname] for hn in hostnames: host = None if hn in self.hosts: host = self.hosts[hn] else: host = Host(name=hn, port=port) self.hosts[hn] = host if len(tokens) > 1: for t in tokens[1:]: if t.startswith('#'): break try: (k, v) = t.split("=") except ValueError, e: raise errors.AnsibleError( "Invalid ini entry: %s - %s" % (t, str(e))) host.set_variable(k, v) self.groups[active_group_name].add_host(host)