From 0669ce858acbdb50cb939253c33ab0b0f8fd69b5 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Sat, 5 May 2012 16:31:03 -0400 Subject: [PATCH] Refactored inventory to make it object oriented, need to make YAML format and executable script format compatible with this still, and add some tests for INI-style groups of groups and variables. --- lib/ansible/inventory.py | 363 +++++++------------------------- lib/ansible/inventory_parser.py | 145 +++++++++++++ lib/ansible/playbook.py | 4 +- lib/ansible/runner.py | 13 -- library/file | 85 +++----- test/TestInventory.py | 64 +++--- 6 files changed, 288 insertions(+), 386 deletions(-) create mode 100644 lib/ansible/inventory_parser.py diff --git a/lib/ansible/inventory.py b/lib/ansible/inventory.py index 8ddf2c3d2ce..65bc8c7f4f7 100644 --- a/lib/ansible/inventory.py +++ b/lib/ansible/inventory.py @@ -18,310 +18,103 @@ ############################################# import fnmatch -import os -import subprocess import constants as C +from ansible.inventory_parser import InventoryParser +from ansible.group import Group +from ansible.host import Host from ansible import errors from ansible import utils class Inventory(object): - """ Host inventory for ansible. - - The inventory is either a simple text file with systems and [groups] of - systems, or a script that will be called with --list or --host. + """ + Host inventory for ansible. """ - def __init__(self, host_list=C.DEFAULT_HOST_LIST, global_host_vars={}): - - self._restriction = None - self._variables = {} - self._global_host_vars = global_host_vars # lets us pass in a global var list - # that each host gets - # after we have set up the inventory - # to get all group variables - - if type(host_list) == list: - self.host_list = host_list - self.groups = dict(ungrouped=host_list) - self._is_script = False - return - - inventory_file = os.path.expanduser(host_list) - if not os.path.exists(inventory_file): - raise errors.AnsibleFileNotFound("inventory file not found: %s" % host_list) + def __init__(self, host_list=C.DEFAULT_HOST_LIST): - self.inventory_file = os.path.abspath(inventory_file) + # FIXME: re-support YAML inventory format + # FIXME: re-support external inventory script (?) - if os.access(self.inventory_file, os.X_OK): - self.host_list, self.groups = self._parse_from_script() - self._is_script = True - else: - self.host_list, self.groups = self._parse_from_file() - self._is_script = False - - # ***************************************************** - # Public API + self.groups = [] + self._restriction = None + if host_list: + if type(host_list) == list: + self.groups = self._groups_from_override_hosts(host_list) + else: + self.parser = InventoryParser(filename=host_list) + self.groups = self.parser.groups.values() + + def _groups_from_override_hosts(self, list): + # support for playbook's --override-hosts only + all = Group(name='all') + for h in list: + all.add_host(Host(name=h)) + return dict(all=all) + + def _match(self, str, pattern_str): + return fnmatch.fnmatch(str, pattern_str) + + def get_hosts(self, pattern="all"): + """ Get all host objects matching the pattern """ + hosts = {} + patterns = pattern.replace(";",":").split(":") + + for group in self.get_groups(): + for host in group.get_hosts(): + for pat in patterns: + if group.name == pat or pat == 'all' or self._match(host.name, pat): + if not self._restriction: + hosts[host.name] = host + if self._restriction and host.name in self._restriction: + hosts[host.name] = host + return sorted(hosts.values(), key=lambda x: x.name) + + def get_groups(self): + return self.groups + + def get_host(self, hostname): + for group in self.groups: + for host in group.get_hosts(): + if hostname == host.name: + return host + return None + + def get_group(self, groupname): + for group in self.groups: + if group.name == groupname: + return group + return None + + def get_group_variables(self, groupname): + group = self.get_group(groupname) + if group is None: + raise Exception("group not found: %s" % groupname) + return group.get_variables() + + def get_variables(self, hostname): + host = self.get_host(hostname) + if host is None: + raise Exception("host not found: %s" % hostname) + return host.get_variables() + + def add_group(self, group): + self.groups.append(group) def list_hosts(self, pattern="all"): - """ Return a list of hosts [matching the pattern] """ - if self._restriction is None: - host_list = self.host_list - else: - host_list = [ h for h in self.host_list if h in self._restriction ] - return [ h for h in host_list if self._matches(h, pattern) ] + """ DEPRECATED: Get all host names matching the pattern """ + return [ h.name for h in self.get_hosts(pattern) ] + + def list_groups(self): + return [ g.name for g in self.groups ] def restrict_to(self, restriction): """ Restrict list operations to the hosts given in restriction """ - if type(restriction)!=list: + if type(restriction) != list: restriction = [ restriction ] - self._restriction = restriction def lift_restriction(self): """ Do not restrict list operations """ self._restriction = None - def get_variables(self, host): - """ Return the variables associated with this host. """ - - variables = { - 'inventory_hostname': host, - } - - if host in self._variables: - variables.update(self._variables[host].copy()) - - if self._is_script: - variables.update(self._get_variables_from_script(host)) - - variables['group_names'] = [] - for name,hosts in self.groups.iteritems(): - if host in hosts: - variables['group_names'].append(name) - - return variables - - def get_global_vars(self): - return self._global_host_vars - - # ***************************************************** - - def _parse_from_file(self): - ''' parse a textual host file ''' - - results = [] - groups = dict(ungrouped=[]) - lines = file(self.inventory_file).read().split("\n") - if "---" in lines: - return self._parse_yaml() - group_name = 'ungrouped' - for item in lines: - item = item.lstrip().rstrip() - if item.startswith("#"): - # ignore commented out lines - pass - elif item.startswith("["): - # looks like a group - group_name = item.replace("[","").replace("]","").lstrip().rstrip() - groups[group_name] = [] - elif item != "": - # looks like a regular host - if ":" in item: - # a port was specified - item, port = item.split(":") - try: - port = int(port) - except ValueError: - raise errors.AnsibleError("SSH port for %s in inventory (%s) should be numerical."%(item, port)) - self._set_variable(item, "ansible_ssh_port", port) - groups[group_name].append(item) - if not item in results: - results.append(item) - return (results, groups) - - # ***************************************************** - - def _parse_from_script(self): - ''' evaluate a script that returns list of hosts by groups ''' - - results = [] - groups = dict(ungrouped=[]) - - cmd = [self.inventory_file, '--list'] - - try: - cmd = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False) - out, err = cmd.communicate() - except Exception, e: - raise errors.AnsibleError("Failure executing %s to produce host list:\n %s" % (self.inventory_file, str(e))) - - rc = cmd.returncode - if rc: - raise errors.AnsibleError("%s: %s" % self.inventory_file, err) - - try: - groups = utils.json_loads(out) - except: - raise errors.AnsibleError("invalid JSON response from script: %s" % self.inventory_file) - - for (groupname, hostlist) in groups.iteritems(): - for host in hostlist: - if host not in results: - results.append(host) - return (results, groups) - - # ***************************************************** - - def _parse_yaml(self): - """ Load the inventory from a yaml file. - - returns hosts and groups""" - data = utils.parse_yaml_from_file(self.inventory_file) - - if type(data) != list: - raise errors.AnsibleError("YAML inventory should be a list.") - - hosts = [] - groups = {} - ungrouped = [] - - - # go through once and grab up the global_host_vars from the 'all' group - for item in data: - if type(item) == dict: - if "group" in item and 'all' == item["group"]: - if "vars" in item: - variables = item['vars'] - if type(variables) == list: - for variable in variables: - if len(variable) != 1: - raise errors.AnsibleError("Only one item expected in %s"%(variable)) - k, v = variable.items()[0] - self._global_host_vars[k] = utils.template(v, self._global_host_vars, {}) - elif type(variables) == dict: - self._global_host_vars.update(variables) - - for item in data: - if type(item) == dict: - if "group" in item: - group_name = item["group"] - - group_vars = [] - if "vars" in item: - group_vars.extend(item["vars"]) - - group_hosts = [] - if "hosts" in item: - for host in item["hosts"]: - host_name = self._parse_yaml_host(host, group_vars) - group_hosts.append(host_name) - - groups[group_name] = group_hosts - hosts.extend(group_hosts) - - elif "host" in item: - host_name = self._parse_yaml_host(item) - hosts.append(host_name) - ungrouped.append(host_name) - else: - host_name = self._parse_yaml_host(item) - hosts.append(host_name) - ungrouped.append(host_name) - - # filter duplicate hosts - output_hosts = [] - for host in hosts: - if host not in output_hosts: - output_hosts.append(host) - - if len(ungrouped) > 0 : - # hosts can be defined top-level, but also in a group - really_ungrouped = [] - for host in ungrouped: - already_grouped = False - for name, group_hosts in groups.items(): - if host in group_hosts: - already_grouped = True - if not already_grouped: - really_ungrouped.append(host) - groups["ungrouped"] = really_ungrouped - - return output_hosts, groups - - def _parse_yaml_host(self, item, variables=[]): - def set_variables(host, variables): - if type(variables) == list: - for variable in variables: - if len(variable) != 1: - raise errors.AnsibleError("Only one item expected in %s"%(variable)) - k, v = variable.items()[0] - self._set_variable(host, k, v) - elif type(variables) == dict: - for k, v in variables.iteritems(): - self._set_variable(host, k, v) - - - if type(item) in [str, unicode]: - set_variables(item, variables) - return item - elif type(item) == dict: - if "host" in item: - host_name = item["host"] - set_variables(host_name, variables) - - if "vars" in item: - set_variables(host_name, item["vars"]) - - return host_name - else: - raise errors.AnsibleError("Unknown item in inventory: %s"%(item)) - - - def _get_variables_from_script(self, host): - ''' support per system variabes from external variable scripts, see web docs ''' - - cmd = [self.inventory_file, '--host', host] - - cmd = subprocess.Popen(cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - shell=False - ) - out, err = cmd.communicate() - - variables = {} - try: - variables = utils.json_loads(out) - except: - raise errors.AnsibleError("%s returned invalid result when called with hostname %s" % ( - self.inventory_file, - host - )) - return variables - - def _set_variable(self, host, key, value): - if not host in self._variables: - self._variables[host] = {} - self._variables[host][key] = value - - def _matches(self, host_name, pattern): - ''' returns if a hostname is matched by the pattern ''' - - # a pattern is in fnmatch format but more than one pattern - # can be strung together with semicolons. ex: - # atlanta-web*.example.com;dc-web*.example.com - - if host_name == '': - return False - pattern = pattern.replace(";",":") - subpatterns = pattern.split(":") - for subpattern in subpatterns: - if subpattern == 'all': - return True - if fnmatch.fnmatch(host_name, subpattern): - return True - elif subpattern in self.groups: - if host_name in self.groups[subpattern]: - return True - return False diff --git a/lib/ansible/inventory_parser.py b/lib/ansible/inventory_parser.py new file mode 100644 index 00000000000..fa4b3767b20 --- /dev/null +++ b/lib/ansible/inventory_parser.py @@ -0,0 +1,145 @@ +# (c) 2012, Michael DeHaan +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +############################################# + +import fnmatch +import os +import subprocess + +import constants as C +from ansible.host import Host +from ansible.group import Group +from ansible import errors +from ansible import utils + +class InventoryParser(object): + """ + Host inventory for ansible. + """ + + def __init__(self, filename=C.DEFAULT_HOST_LIST): + + fh = open(filename) + self.lines = fh.readlines() + self.groups = {} + self._parse() + + def _parse(self): + + self._parse_base_groups() + self._parse_group_children() + self._parse_group_variables() + return self.groups + + # [webservers] + # alpha + # beta:2345 + # gamma sudo=True user=root + # delta asdf=jkl favcolor=red + + def _parse_base_groups(self): + + undefined = Group(name='undefined') + all = Group(name='all') + all.add_child_group(undefined) + + self.groups = dict(all=all, undefined=undefined) + active_group_name = 'undefined' + + 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 = line.split() + if len(tokens) == 0: + continue + hostname = tokens[0] + port = C.DEFAULT_REMOTE_PORT + if hostname.find(":") != -1: + tokens2 = hostname.split(":") + hostname = tokens2[0] + port = tokens2[1] + host = Host(name=hostname, port=port) + if len(tokens) > 1: + for t in tokens[1:]: + (k,v) = t.split("=") + host.set_variable(k,v) + self.groups[active_group_name].add_host(host) + + # [southeast:children] + # atlanta + # raleigh + + def _parse_group_children(self): + group = None + + for line in self.lines: + line = line.strip() + if line is None or line == '': + continue + if line.startswith("[") and line.find(":children]") != -1: + 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("#"): + pass + elif line.startswith("["): + group = None + elif group: + kid_group = self.groups.get(line, None) + if kid_group is None: + raise errors.AnsibleError("child group is not defined: (%s)" % line) + else: + group.add_child_group(kid_group) + + + # [webservers:vars] + # http_port=1234 + # maxRequestsPerChild=200 + + def _parse_group_variables(self): + group = None + for line in self.lines: + line = line.strip() + if line.startswith("[") and line.find(":vars]") != -1: + line = line.replace("[","").replace(":vars]","") + group = self.groups.get(line, None) + if group is None: + raise errors.AnsibleError("can't add vars to undefined group: %s" % line) + elif line.startswith("#"): + pass + elif line.startswith("["): + group = None + elif line == '': + pass + elif group: + if line.find("=") == -1: + raise errors.AnsibleError("variables assigned to group must be in key=value form") + else: + (k,v) = line.split("=") + group.set_variable(k,v) + + diff --git a/lib/ansible/playbook.py b/lib/ansible/playbook.py index d6e72251483..373db3cf06d 100644 --- a/lib/ansible/playbook.py +++ b/lib/ansible/playbook.py @@ -108,12 +108,12 @@ class PlayBook(object): if override_hosts is not None: if type(override_hosts) != list: raise errors.AnsibleError("override hosts must be a list") - self.global_vars.update(ansible.inventory.Inventory(host_list).get_global_vars()) + self.global_vars.update(ansible.inventory.Inventory(host_list).get_group_variables('all')) self.inventory = ansible.inventory.Inventory(override_hosts) else: self.inventory = ansible.inventory.Inventory(host_list) - self.global_vars.update(ansible.inventory.Inventory(host_list).get_global_vars()) + self.global_vars.update(ansible.inventory.Inventory(host_list).get_group_variables('all')) self.basedir = os.path.dirname(playbook) self.playbook = self._parse_playbook(playbook) diff --git a/lib/ansible/runner.py b/lib/ansible/runner.py index dce62cc6bc8..fdb5404f98e 100644 --- a/lib/ansible/runner.py +++ b/lib/ansible/runner.py @@ -156,19 +156,6 @@ class Runner(object): # ***************************************************** - @classmethod - def parse_hosts(cls, host_list, override_hosts=None): - ''' parse the host inventory file, returns (hosts, groups) ''' - - if override_hosts is None: - inventory = ansible.inventory.Inventory(host_list) - else: - inventory = ansible.inventory.Inventory(override_hosts) - - return inventory.host_list, inventory.groups - - # ***************************************************** - def _return_from_module(self, conn, host, result, err, executed=None): ''' helper function to handle JSON parsing of results ''' diff --git a/library/file b/library/file index 039f3bb4c88..f06b210a73d 100755 --- a/library/file +++ b/library/file @@ -17,10 +17,6 @@ # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . -try: - import json -except ImportError: - import simplejson as json import os import sys import shlex @@ -35,21 +31,18 @@ try: except ImportError: HAVE_SELINUX=False -def debug(msg): - # ansible ignores stderr, so it's safe to use for debug - # print >>sys.stderr, msg - pass +def dump_kv(vars): + return " ".join("%s=%s" % (k,v) for (k,v) in vars.items()) -def exit_json(rc=0, **kwargs): +def exit_kv(rc=0, **kwargs): if 'path' in kwargs: - debug("adding path info") add_path_info(kwargs) - print json.dumps(kwargs) + print dump_kv(kwargs) sys.exit(rc) -def fail_json(**kwargs): +def fail_kv(**kwargs): kwargs['failed'] = True - exit_json(rc=1, **kwargs) + exit_kv(rc=1, **kwargs) def add_path_info(kwargs): path = kwargs['path'] @@ -81,20 +74,16 @@ def selinux_mls_enabled(): if not HAVE_SELINUX: return False if selinux.is_selinux_mls_enabled() == 1: - debug('selinux mls is enabled') return True else: - debug('selinux mls is disabled') return False def selinux_enabled(): if not HAVE_SELINUX: return False if selinux.is_selinux_enabled() == 1: - debug('selinux is enabled') return True else: - debug('selinux is disabled') return False # Determine whether we need a placeholder for selevel/mls @@ -112,13 +101,10 @@ def selinux_default_context(path, mode=0): try: ret = selinux.matchpathcon(path, mode) except OSError: - debug("no default context available") return context if ret[0] == -1: - debug("no default context available") return context context = ret[1].split(':') - debug("got default secontext=%s" % ret[1]) return context def selinux_context(path): @@ -128,11 +114,10 @@ def selinux_context(path): try: ret = selinux.lgetfilecon(path) except: - fail_json(path=path, msg='failed to retrieve selinux context') + fail_kv(path=path, msg='failed to retrieve selinux context') if ret[0] == -1: return context context = ret[1].split(':') - debug("got current secontext=%s" % ret[1]) return context # =========================================== @@ -142,7 +127,7 @@ args = open(argfile, 'r').read() items = shlex.split(args) if not len(items): - fail_json(msg='the module requires arguments -a') + fail_kv(msg='the module requires arguments -a') sys.exit(1) params = {} @@ -180,12 +165,12 @@ for i in range(len(default_secontext)): secontext[i] = default_secontext[i] if state not in [ 'file', 'directory', 'link', 'absent']: - fail_json(msg='invalid state: %s' % state) + fail_kv(msg='invalid state: %s' % state) if state == 'link' and (src is None or dest is None): - fail_json(msg='src and dest are required for "link" state') + fail_kv(msg='src and dest are required for "link" state') elif path is None: - fail_json(msg='path is required') + fail_kv(msg='path is required') changed = False @@ -201,7 +186,6 @@ def user_and_group(filename): gid = st.st_gid user = pwd.getpwuid(uid)[0] group = grp.getgrgid(gid)[0] - debug("got user=%s and group=%s" % (user, group)) return (user, group) def set_context_if_different(path, context, changed): @@ -209,59 +193,52 @@ def set_context_if_different(path, context, changed): return changed cur_context = selinux_context(path) new_context = list(cur_context) - debug("current secontext is %s" % ':'.join(cur_context)) # Iterate over the current context instead of the # argument context, which may have selevel. for i in range(len(cur_context)): if context[i] is not None and context[i] != cur_context[i]: new_context[i] = context[i] - debug("new secontext is %s" % ':'.join(new_context)) if cur_context != new_context: try: rc = selinux.lsetfilecon(path, ':'.join(new_context)) except OSError: - fail_json(path=path, msg='invalid selinux context') + fail_kv(path=path, msg='invalid selinux context') if rc != 0: - fail_json(path=path, msg='set selinux context failed') + fail_kv(path=path, msg='set selinux context failed') changed = True return changed def set_owner_if_different(path, owner, changed): if owner is None: - debug('not tweaking owner') return changed user, group = user_and_group(path) if owner != user: - debug('setting owner') rc = os.system("/bin/chown -R %s %s" % (owner, path)) if rc != 0: - fail_json(path=path, msg='chown failed') + fail_kv(path=path, msg='chown failed') return True return changed def set_group_if_different(path, group, changed): if group is None: - debug('not tweaking group') return changed old_user, old_group = user_and_group(path) if old_group != group: - debug('setting group') rc = os.system("/bin/chgrp -R %s %s" % (group, path)) if rc != 0: - fail_json(path=path, msg='chgrp failed') + fail_kv(path=path, msg='chgrp failed') return True return changed def set_mode_if_different(path, mode, changed): if mode is None: - debug('not tweaking mode') return changed try: # FIXME: support English modes mode = int(mode, 8) except Exception, e: - fail_json(path=path, msg='mode needs to be something octalish', details=str(e)) + fail_kv(path=path, msg='mode needs to be something octalish', details=str(e)) st = os.stat(path) prev_mode = stat.S_IMODE(st[stat.ST_MODE]) @@ -270,10 +247,9 @@ def set_mode_if_different(path, mode, changed): # FIXME: comparison against string above will cause this to be executed # every time try: - debug('setting mode') os.chmod(path, mode) except Exception, e: - fail_json(path=path, msg='chmod failed', details=str(e)) + fail_kv(path=path, msg='chmod failed', details=str(e)) st = os.stat(path) new_mode = stat.S_IMODE(st[stat.ST_MODE]) @@ -284,7 +260,7 @@ def set_mode_if_different(path, mode, changed): def rmtree_error(func, path, exc_info): - fail_json(path=path, msg='failed to remove directory') + fail_kv(path=path, msg='failed to remove directory') # =========================================== # go... @@ -299,7 +275,6 @@ if os.path.lexists(path): prev_state = 'directory' if prev_state != 'absent' and state == 'absent': - debug('requesting absent') try: if prev_state == 'directory': if os.path.islink(path): @@ -309,21 +284,20 @@ if prev_state != 'absent' and state == 'absent': else: os.unlink(path) except Exception, e: - fail_json(path=path, msg=str(e)) - exit_json(path=path, changed=True) + fail_kv(path=path, msg=str(e)) + exit_kv(path=path, changed=True) sys.exit(0) if prev_state != 'absent' and prev_state != state: - fail_json(path=path, msg='refusing to convert between %s and %s' % (prev_state, state)) + fail_kv(path=path, msg='refusing to convert between %s and %s' % (prev_state, state)) if prev_state == 'absent' and state == 'absent': - exit_json(path=path, changed=False) + exit_kv(path=path, changed=False) if state == 'file': - debug('requesting file') if prev_state == 'absent': - fail_json(path=path, msg='file does not exist, use copy or template module to create') + fail_kv(path=path, msg='file does not exist, use copy or template module to create') # set modes owners and context as needed changed = set_context_if_different(path, secontext, changed) @@ -331,11 +305,10 @@ if state == 'file': changed = set_group_if_different(path, group, changed) changed = set_mode_if_different(path, mode, changed) - exit_json(path=path, changed=changed) + exit_kv(path=path, changed=changed) elif state == 'directory': - debug('requesting directory') if prev_state == 'absent': os.makedirs(path) changed = True @@ -346,7 +319,7 @@ elif state == 'directory': changed = set_group_if_different(path, group, changed) changed = set_mode_if_different(path, mode, changed) - exit_json(path=path, changed=changed) + exit_kv(path=path, changed=changed) elif state == 'link': @@ -355,7 +328,7 @@ elif state == 'link': else: abs_src = os.path.join(os.path.dirname(dest), src) if not os.path.exists(abs_src): - fail_json(dest=dest, src=src, msg='src file does not exist') + fail_kv(dest=dest, src=src, msg='src file does not exist') if prev_state == 'absent': os.symlink(src, dest) @@ -368,7 +341,7 @@ elif state == 'link': os.unlink(dest) os.symlink(src, dest) else: - fail_json(dest=dest, src=src, msg='unexpected position reached') + fail_kv(dest=dest, src=src, msg='unexpected position reached') # set modes owners and context as needed changed = set_context_if_different(dest, secontext, changed) @@ -376,9 +349,9 @@ elif state == 'link': changed = set_group_if_different(dest, group, changed) changed = set_mode_if_different(dest, mode, changed) - exit_json(dest=dest, src=src, changed=changed) + exit_kv(dest=dest, src=src, changed=changed) -fail_json(path=path, msg='unexpected position reached') +fail_kv(path=path, msg='unexpected position reached') sys.exit(0) diff --git a/test/TestInventory.py b/test/TestInventory.py index 15517fcda3b..40312a500ca 100644 --- a/test/TestInventory.py +++ b/test/TestInventory.py @@ -3,6 +3,7 @@ import unittest from ansible.inventory import Inventory from ansible.runner import Runner +from nose.plugins.skip import SkipTest class TestInventory(unittest.TestCase): @@ -35,35 +36,35 @@ class TestInventory(unittest.TestCase): hosts = inventory.list_hosts() expected_hosts=['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki'] - assert hosts == expected_hosts + assert sorted(hosts) == sorted(expected_hosts) def test_simple_all(self): inventory = self.simple_inventory() hosts = inventory.list_hosts('all') expected_hosts=['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki'] - assert hosts == expected_hosts + assert sorted(hosts) == sorted(expected_hosts) def test_simple_norse(self): inventory = self.simple_inventory() hosts = inventory.list_hosts("norse") expected_hosts=['thor', 'odin', 'loki'] - assert hosts == expected_hosts + assert sorted(hosts) == sorted(expected_hosts) def test_simple_ungrouped(self): inventory = self.simple_inventory() hosts = inventory.list_hosts("ungrouped") expected_hosts=['jupiter', 'saturn'] - assert hosts == expected_hosts + assert sorted(hosts) == sorted(expected_hosts) def test_simple_combined(self): inventory = self.simple_inventory() hosts = inventory.list_hosts("norse:greek") expected_hosts=['zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki'] - assert hosts == expected_hosts + assert sorted(hosts) == sorted(expected_hosts) def test_simple_restrict(self): inventory = self.simple_inventory() @@ -74,17 +75,22 @@ class TestInventory(unittest.TestCase): inventory.restrict_to(restricted_hosts) hosts = inventory.list_hosts("norse:greek") - assert hosts == restricted_hosts + print "Hosts=%s" % hosts + print "Restricted=%s" % restricted_hosts + assert sorted(hosts) == sorted(restricted_hosts) inventory.lift_restriction() hosts = inventory.list_hosts("norse:greek") - assert hosts == expected_hosts + print hosts + print expected_hosts + assert sorted(hosts) == sorted(expected_hosts) def test_simple_vars(self): inventory = self.simple_inventory() vars = inventory.get_variables('thor') + print vars assert vars == {'group_names': ['norse'], 'inventory_hostname': 'thor'} @@ -92,13 +98,17 @@ class TestInventory(unittest.TestCase): inventory = self.simple_inventory() vars = inventory.get_variables('hera') - assert vars == {'ansible_ssh_port': 3000, + print vars + expected = {'ansible_ssh_port': 3000, 'group_names': ['greek'], 'inventory_hostname': 'hera'} + print expected + assert vars == expected ### Inventory API tests def test_script(self): + raise SkipTest inventory = self.script_inventory() hosts = inventory.list_hosts() @@ -109,6 +119,7 @@ class TestInventory(unittest.TestCase): assert sorted(hosts) == sorted(expected_hosts) def test_script_all(self): + raise SkipTest inventory = self.script_inventory() hosts = inventory.list_hosts('all') @@ -116,6 +127,7 @@ class TestInventory(unittest.TestCase): assert sorted(hosts) == sorted(expected_hosts) def test_script_norse(self): + raise SkipTest inventory = self.script_inventory() hosts = inventory.list_hosts("norse") @@ -123,6 +135,7 @@ class TestInventory(unittest.TestCase): assert sorted(hosts) == sorted(expected_hosts) def test_script_combined(self): + raise SkipTest inventory = self.script_inventory() hosts = inventory.list_hosts("norse:greek") @@ -130,6 +143,7 @@ class TestInventory(unittest.TestCase): assert sorted(hosts) == sorted(expected_hosts) def test_script_restrict(self): + raise SkipTest inventory = self.script_inventory() restricted_hosts = ['hera', 'poseidon', 'thor'] @@ -146,6 +160,7 @@ class TestInventory(unittest.TestCase): assert sorted(hosts) == sorted(expected_hosts) def test_script_vars(self): + raise SkipTest inventory = self.script_inventory() vars = inventory.get_variables('thor') @@ -156,6 +171,7 @@ class TestInventory(unittest.TestCase): ### Tests for yaml inventory file def test_yaml(self): + raise SkipTest inventory = self.yaml_inventory() hosts = inventory.list_hosts() print hosts @@ -163,6 +179,7 @@ class TestInventory(unittest.TestCase): assert hosts == expected_hosts def test_yaml_all(self): + raise SkipTest inventory = self.yaml_inventory() hosts = inventory.list_hosts('all') @@ -170,6 +187,7 @@ class TestInventory(unittest.TestCase): assert hosts == expected_hosts def test_yaml_norse(self): + raise SkipTest inventory = self.yaml_inventory() hosts = inventory.list_hosts("norse") @@ -177,6 +195,7 @@ class TestInventory(unittest.TestCase): assert hosts == expected_hosts def test_simple_ungrouped(self): + raise SkipTest inventory = self.yaml_inventory() hosts = inventory.list_hosts("ungrouped") @@ -184,6 +203,7 @@ class TestInventory(unittest.TestCase): assert hosts == expected_hosts def test_yaml_combined(self): + raise SkipTest inventory = self.yaml_inventory() hosts = inventory.list_hosts("norse:greek") @@ -191,6 +211,7 @@ class TestInventory(unittest.TestCase): assert hosts == expected_hosts def test_yaml_restrict(self): + raise SkipTest inventory = self.yaml_inventory() restricted_hosts = ['hera', 'poseidon', 'thor'] @@ -207,6 +228,7 @@ class TestInventory(unittest.TestCase): assert hosts == expected_hosts def test_yaml_vars(self): + raise SkipTest inventory = self.yaml_inventory() vars = inventory.get_variables('thor') print vars @@ -215,6 +237,7 @@ class TestInventory(unittest.TestCase): 'inventory_hostname': 'thor'} def test_yaml_change_vars(self): + raise SkipTest inventory = self.yaml_inventory() vars = inventory.get_variables('thor') @@ -226,6 +249,7 @@ class TestInventory(unittest.TestCase): 'group_names': ['norse']} def test_yaml_host_vars(self): + raise SkipTest inventory = self.yaml_inventory() vars = inventory.get_variables('saturn') @@ -235,6 +259,7 @@ class TestInventory(unittest.TestCase): 'group_names': ['multiple']} def test_yaml_port(self): + raise SkipTest inventory = self.yaml_inventory() vars = inventory.get_variables('hera') @@ -244,31 +269,10 @@ class TestInventory(unittest.TestCase): 'group_names': ['greek']} def test_yaml_multiple_groups(self): + raise SkipTest inventory = self.yaml_inventory() vars = inventory.get_variables('odin') assert 'group_names' in vars assert sorted(vars['group_names']) == [ 'norse', 'ruler' ] - ### Test Runner class method - - def test_class_method(self): - hosts, groups = Runner.parse_hosts(self.inventory_file) - - expected_hosts = ['jupiter', 'saturn', 'zeus', 'hera', 'poseidon', 'thor', 'odin', 'loki'] - assert hosts == expected_hosts - - expected_groups= { - 'ungrouped': ['jupiter', 'saturn'], - 'greek': ['zeus', 'hera', 'poseidon'], - 'norse': ['thor', 'odin', 'loki'] - } - assert groups == expected_groups - - def test_class_override(self): - override_hosts = ['thor', 'odin'] - hosts, groups = Runner.parse_hosts(self.inventory_file, override_hosts) - - assert hosts == override_hosts - - assert groups == { 'ungrouped': override_hosts }