ini.py是對配置文件進行解析的模塊,主要處理了組,主機,組的變量,子組等關係。一個inventory中飲含 groups hosts 這兩個很是重要的屬性,其中有兩個死的組all ungrouped組。nginx
看看源碼再理解他的配置就會很好理解,有點意思的地方是做者居然用索引號去取值,而不是傳統的for,有點搞。web
這裏的邏輯是shell
cat /etc/ansible/hosts [web] 10.1.1.2 10.1.1.3 ansible_ssh_user=zwhset ansible_ssh_port=22 [web:vars] group=web name=zwhset age=18 [web:children] nginx tomcat apache [nginx] 10.1.1.4 [tomcat] 10.1.1.5 [apache] 10.1.1.6
In [27]: from ansible.inventory.ini import InventoryParser In [28]: inventory = InventoryParser() In [29]: inventory.filename Out[29]: '/etc/ansible/hosts' In [30]: inventory.groups # 查看全部的組 Out[30]: {'all': <ansible.inventory.group.Group at 0x1079cca10>, 'apache': <ansible.inventory.group.Group at 0x1079ccc80>, 'nginx': <ansible.inventory.group.Group at 0x1079ccb48>, 'tomcat': <ansible.inventory.group.Group at 0x1079ccc18>, 'ungrouped': <ansible.inventory.group.Group at 0x107959f58>, 'web': <ansible.inventory.group.Group at 0x1079ccae0>} In [31]: inventory.hosts # 查看全部的主機 Out[31]: {'10.1.1.2': <ansible.inventory.host.Host at 0x1078e9950>, '10.1.1.3': <ansible.inventory.host.Host at 0x10795b440>, '10.1.1.4': <ansible.inventory.host.Host at 0x1078feea8>, '10.1.1.5': <ansible.inventory.host.Host at 0x106e70950>, '10.1.1.6': <ansible.inventory.host.Host at 0x1078fc368>} In [32]: # 查看一下web的子組 In [33]: web = inventory.groups["web"] # 查看web的子組 In [36]: for g in web.child_groups: ...: print g.name ...: nginx tomcat apache # 查看web組子組的父組 In [38]: for g in web.child_groups: ...: for kg in g.parent_groups: # 查看子組的父組 ...: print kg.name ...: ...: web web web # 查看web子組的主機 In [39]: for g in web.child_groups: ...: print g.hosts ...: [<ansible.inventory.host.Host object at 0x1078feea8>] [<ansible.inventory.host.Host object at 0x106e70950>] [<ansible.inventory.host.Host object at 0x1078fc368>] # 查看web子組的主機變量,前面沒設 In [41]: for g in web.child_groups: ...: for h in g.hosts: ...: print h.vars ...: {} {} {} # 惟一的一個組變量,在這裏被成功解析 In [42]: for h in web.hosts: ...: print h.vars ...: {} {'ansible_ssh_port': 22, 'ansible_ssh_user': 'zwhset'}
import ansible.constants as C from ansible.inventory.host import Host from ansible.inventory.group import Group from ansible.inventory.expand_hosts import detect_range from ansible.inventory.expand_hosts import expand_hostname_range from ansible import errors from ansible import utils import shlex import re import ast class InventoryParser(object): """ Host inventory for ansible. """ # 解析配置文件 def __init__(self, filename=C.DEFAULT_HOST_LIST): # 獲取一個文件對象 with open(filename) as fh: self.filename = filename # 全部行的記錄,一個列表,將要對這個列表進行解析,也就是配置文件的每一行 self.lines = fh.readlines() self.groups = {} self.hosts = {} # 實例化的時候會角化_parse方法 self._parse() # 執行一堆函數,而後返回groups def _parse(self): # 對配置文件進行一個解析,最後獲得實例化的全部東西 一個all一個ungroupd # 這是處理基礎的主機以及變量,並無對組關係進行處理 self._parse_base_groups() # 1234 再來一次,處理子組 self._parse_group_children() # 把深度爲0而且不是all組的添加進all組 self._add_allgroup_children() # 解析組的變量 self._parse_group_variables() return self.groups @staticmethod def _parse_value(v): # 變量的value不包含# if "#" not in v: try: # 安全值的檢查, ret = ast.literal_eval(v) # 符點轉換 if not isinstance(ret, float): # Do not trim floats. Eg: "1.20" to 1.2 return ret # 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. pass except SyntaxError: # Is this a hash with an equals at the end? pass return v # [webservers] # alpha # beta:2345 # gamma sudo=True user=root # delta asdf=jkl favcolor=red def _add_allgroup_children(self): # 獲取groups的全部的值 for group in self.groups.values(): # 若是深度爲0 而且組名不等於all的,添加到all組 # 那麼深度不爲0的呢,不知道深度的能夠看一下group的方法 if group.depth == 0 and group.name != 'all': self.groups['all'].add_child_group(group) def _parse_base_groups(self): # FIXME: refactor # 定義ungrouped all組名, 並在all裏添加一個ungrouped組 ungrouped = Group(name='ungrouped') all = Group(name='all') all.add_child_group(ungrouped) self.groups = dict(all=all, ungrouped=ungrouped) active_group_name = 'ungrouped' # 活動的組,沒啥好說的 # 這裏沒用啥黑科技,使用range + len獲取的其實就是文件的索引號 for lineno in range(len(self.lines)): # 取 #號以前的字符串,而後消除兩邊的空白 line = utils.before_comment(self.lines[lineno]).strip() # 若是字符串開始是[*]這種形式代表就是一種組 if line.startswith("[") and line.endswith("]"): # 把[]去除掉,拿中間的* active_group_name = line.replace("[","").replace("]","") # 若是是變量或或是子組的方式 if ":vars" in line or ":children" in line: # 組名取:vars 左側分割的,即 [webs:vars] [web:children]取web 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, 這裏針對的是不以[]就不是組的 elif active_group_name: # 一個處理類shell的解析方式 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 # 若是主機名中包含:大於1,即爲IPV6的地址, XXX:XXX::XXX.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 = [] # 這裏是處理這種[a-z] [1-3]這種主機名,會返回匹配關係的主機名的 if detect_range(hostname): hostnames = expand_hostname_range(hostname) else: hostnames = [hostname] # 遍歷一下 for hn in hostnames: host = None # 判斷是否已經存在, 存在更新變量host,這裏是以已經存在的爲準 if hn in self.hosts: host = self.hosts[hn] else: # 一個實例化Host,更新一下實例的hosts列表 host = Host(name=hn, port=port) self.hosts[hn] = host # 環境變量,即配置文件裏面定義什麼密碼賬號之類的玩意兒 if len(tokens) > 1: for t in tokens[1:]: # 帶#號就跳出 if t.startswith('#'): break try: # kv型式,在後面設置主機的變量 (k,v) = t.split("=", 1) 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)) # 添加主機在 ungrouped裏面,因爲指針的關係,因此all裏面就會有 self.groups[active_group_name].add_host(host) # [southeast:children] # atlanta # raleigh def _parse_group_children(self): group = None #再來一次,注意用的是索引號,有更好的方式 for lineno in range(len(self.lines)): # 利用索引取到值,這寫的就尷尬,並去除兩邊的空格 line = self.lines[lineno].strip() # 空行跳到下一次 if line is None or line == '': continue # 注意這裏,是專門處理[:children]子組的往下看 if line.startswith("[") and ":children]" in line: # 一樣清空 [ :children] 留下的就是組名 line = line.replace("[","").replace(":children]","") #判斷一下組名是否在組裏,沒有就加唄 group = self.groups.get(line, None) if group is None: group = self.groups[line] = Group(name=line) # 同理 elif line.startswith("#") or line.startswith(";"): pass elif line.startswith("["): group = None # 若是匹配第一個if是子組,第二次循環group就爲真了 elif group: # 就是添加子組 kid_group = self.groups.get(line, None) if kid_group is None: raise errors.AnsibleError("%s:%d: child group is not defined: (%s)" % (self.filename, lineno + 1, line)) else: group.add_child_group(kid_group) # [webservers:vars] # http_port=1234 # maxRequestsPerChild=200 def _parse_group_variables(self): group = None for lineno in range(len(self.lines)): line = self.lines[lineno].strip() # [web:vars]這種形式,由於前面添加過組,所組應該是存在的,若是不存在就報錯唄 if line.startswith("[") and ":vars]" in line: line = line.replace("[","").replace(":vars]","") group = self.groups.get(line, None) if group is None: raise errors.AnsibleError("%s:%d: can't add vars to undefined group: %s" % (self.filename, lineno + 1, line)) # 跳過 elif line.startswith("#") or line.startswith(";"): pass # [開頭的前面處理過了這裏不處理,只處理組的變量 elif line.startswith("["): group = None elif line == '': pass # 若是匹配到了即表明這是組的變量設置 elif group: # 必須是用=號來進行賦值的 if "=" not in line: raise errors.AnsibleError("%s:%d: variables assigned to group must be in key=value form" % (self.filename, lineno + 1)) else: # 走K value (k, v) = [e.strip() for e in line.split("=", 1)] # 這裏用到組的方法設置一個字典其實就是一個字典 # 在這裏會檢查一下值是否是一些不安全的東西 group.set_variable(k, self._parse_value(v)) def get_host_variables(self, host): return {}