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.
This commit is contained in:
Michael DeHaan 2012-05-05 16:31:03 -04:00
parent 39e791a1d3
commit 0669ce858a
6 changed files with 284 additions and 382 deletions

View file

@ -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={}):
def __init__(self, host_list=C.DEFAULT_HOST_LIST):
# FIXME: re-support YAML inventory format
# FIXME: re-support external inventory script (?)
self.groups = []
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
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)
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 _match(self, str, pattern_str):
return fnmatch.fnmatch(str, pattern_str)
self.inventory_file = os.path.abspath(inventory_file)
def get_hosts(self, pattern="all"):
""" Get all host objects matching the pattern """
hosts = {}
patterns = pattern.replace(";",":").split(":")
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
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)
# *****************************************************
# Public API
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

View file

@ -0,0 +1,145 @@
# (c) 2012, Michael DeHaan <michael.dehaan@gmail.com>
#
# 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 <http://www.gnu.org/licenses/>.
#############################################
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)

View file

@ -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)

View file

@ -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 '''

View file

@ -17,10 +17,6 @@
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
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)

View file

@ -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 }