Commit 8fc46a3a by Michael DeHaan

Return inventory objects in the order they are presented. Additionally, fix…

Return inventory objects in the order they are presented.  Additionally, fix host slicing such that it works on Python terms with
zero indexed lists and a non-inclusive final element.
parent 19386c43
......@@ -14,7 +14,7 @@ Highlighted new features:
New modules:
* cloud:ec2_eip -- manage AWS elastic IP's
* cloud:ec2_eip -- manage AWS elastic IPs
* cloud:rax_clb -- manage Rackspace cloud load balancers
* system: firewalld -- manage the firewalld configuration
* system: host -- manage `/etc/hosts` file entries
......@@ -28,9 +28,11 @@ Misc changes:
* Added `ansible_env` to the list of facts returned by the setup module.
* Added `state=touch` to the file module, which functions similarly to the command-line version of `touch`.
* Added a -vvvv level, which will show SSH client debugging information in the event of a failure.
* Includes now support the more standard syntax, similar to that of role includes and dependencies. It is no longer necessary to specify a special "vas" field for the variables passed to the include.
* Includes now support the more standard syntax, similar to that of role includes and dependencies. It is no longer necessary to specify a special "vars" field for the variables passed to the include.
* Changed the `user:` parameter on plays to `remote_user:` to prevent confusion with the module of the same name. Still backwards compatible on play parameters.
* Added parameter to allow the fetch module to skip the md5 validation step ('validate_md5=false'). This is usefull when fetching files that are actively being written to, such as live log files.
* Inventory hosts are used in the order they appear in the inventory.
* in hosts: foo[2-5] type syntax, the iterators now are zero indexed and the last index is non-inclusive, to match Python standards.
1.3 "Top of the World" - September 13th, 2013
......
......@@ -20,8 +20,8 @@
import fnmatch
import os
import re
import subprocess
import ansible.constants as C
from ansible.inventory.ini import InventoryParser
from ansible.inventory.script import InventoryScript
......@@ -132,7 +132,11 @@ class Inventory(object):
# exclude hosts not in a subset, if defined
if self._subset:
subset = self._get_hosts(self._subset)
hosts.intersection_update(subset)
new_hosts = []
for h in hosts:
if h in subset and h not in new_hosts:
new_hosts.append(h)
hosts = new_hosts
# exclude hosts mentioned in any restriction (ex: failed hosts)
if self._restriction is not None:
......@@ -140,7 +144,7 @@ class Inventory(object):
if self._also_restriction is not None:
hosts = [ h for h in hosts if h.name in self._also_restriction ]
return sorted(hosts, key=lambda x: x.name)
return hosts
def _get_hosts(self, patterns):
"""
......@@ -169,17 +173,19 @@ class Inventory(object):
# first, then the &s, then the !s.
patterns = pattern_regular + pattern_intersection + pattern_exclude
hosts = set()
hosts = []
for p in patterns:
that = self.__get_hosts(p)
if p.startswith("!"):
# Discard excluded hosts
hosts.difference_update(self.__get_hosts(p))
hosts = [ h for h in hosts if h not in that ]
elif p.startswith("&"):
# Only leave the intersected hosts
hosts.intersection_update(self.__get_hosts(p))
hosts = [ h for h in hosts if h in that ]
else:
# Get all hosts from both patterns
hosts.update(self.__get_hosts(p))
for h in that:
if h not in hosts:
hosts.append(h)
return hosts
def __get_hosts(self, pattern):
......@@ -190,9 +196,7 @@ class Inventory(object):
(name, enumeration_details) = self._enumeration_info(pattern)
hpat = self._hosts_in_unenumerated_pattern(name)
hpat = sorted(hpat, key=lambda x: x.name)
return set(self._apply_ranges(pattern, hpat))
return self._apply_ranges(pattern, hpat)
def _enumeration_info(self, pattern):
"""
......@@ -205,9 +209,18 @@ class Inventory(object):
return (pattern, None)
(first, rest) = pattern.split("[")
rest = rest.replace("]","")
try:
# support selectors like webservers[0]
x = int(rest)
return (first, (x,x))
except:
pass
if "-" in rest:
(left, right) = rest.split("-",1)
return (first, (left, right))
elif ":" in rest:
(left, right) = rest.split(":",1)
return (first, (left, right))
else:
return (first, (rest, rest))
......@@ -222,30 +235,34 @@ class Inventory(object):
return hosts
(left, right) = limits
enumerated = enumerate(hosts)
if left == '':
left = 0
if right == '':
right = 0
left=int(left)
right=int(right)
enumerated = [ h for (i,h) in enumerated if i>=left and i<=right ]
return enumerated
if left != right:
return hosts[left:right]
else:
return [ hosts[left] ]
# TODO: cache this logic so if called a second time the result is not recalculated
def _hosts_in_unenumerated_pattern(self, pattern):
""" Get all host names matching the pattern """
hosts = {}
hosts = []
# ignore any negative checks here, this is handled elsewhere
pattern = pattern.replace("!","").replace("&", "")
results = []
groups = self.get_groups()
for group in groups:
for host in group.get_hosts():
if pattern == 'all' or self._match(group.name, pattern) or self._match(host.name, pattern):
hosts[host.name] = host
return sorted(hosts.values(), key=lambda x: x.name)
if host not in results:
results.append(host)
return results
def groups_for_host(self, host):
results = []
......
......@@ -53,11 +53,16 @@ class Group(object):
def get_hosts(self):
hosts = set()
hosts = []
for kid in self.child_groups:
hosts.update(kid.get_hosts())
hosts.update(self.hosts)
return list(hosts)
kid_hosts = kid.get_hosts()
for kk in kid_hosts:
if kk not in hosts:
hosts.append(kk)
for mine in self.hosts:
if mine not in hosts:
hosts.append(mine)
return hosts
def get_variables(self):
return self.vars.copy()
......
......@@ -126,15 +126,11 @@ class TestInventory(unittest.TestCase):
inventory.restrict_to(restricted_hosts)
hosts = inventory.list_hosts("norse:greek")
print "Hosts=%s" % hosts
print "Restricted=%s" % restricted_hosts
assert sorted(hosts) == sorted(restricted_hosts)
inventory.lift_restriction()
hosts = inventory.list_hosts("norse:greek")
print hosts
print expected_hosts
assert sorted(hosts) == sorted(expected_hosts)
def test_simple_string_ipv4(self):
......@@ -171,7 +167,6 @@ class TestInventory(unittest.TestCase):
inventory = self.simple_inventory()
vars = inventory.get_variables('thor')
print vars
assert vars == {'group_names': ['norse'],
'inventory_hostname': 'thor',
'inventory_hostname_short': 'thor'}
......@@ -180,12 +175,10 @@ class TestInventory(unittest.TestCase):
inventory = self.simple_inventory()
vars = inventory.get_variables('hera')
print vars
expected = { 'ansible_ssh_port': 3000,
'group_names': ['greek'],
'inventory_hostname': 'hera',
'inventory_hostname_short': 'hera' }
print expected
assert vars == expected
def test_large_range(self):
......@@ -257,21 +250,19 @@ class TestInventory(unittest.TestCase):
def test_complex_enumeration(self):
expected1 = ['rtp_a', 'rtp_b']
expected2 = ['rtp_c', 'tri_a']
expected3 = ['rtp_b', 'rtp_c', 'tri_a', 'tri_b', 'tri_c']
expected4 = ['orlando', 'rtp_c', 'tri_a']
expected1 = ['rtp_b']
expected2 = ['rtp_a', 'rtp_b']
expected3 = ['rtp_a', 'rtp_b', 'rtp_c', 'tri_a', 'tri_b', 'tri_c']
expected4 = ['rtp_b', 'orlando' ]
inventory = self.complex_inventory()
print "ALL NC=%s" % inventory.list_hosts("nc")
# use "-1" instead of 0-1 to test the syntax, on purpose
hosts = inventory.list_hosts("nc[-1]")
hosts = inventory.list_hosts("nc[1]")
self.compare(hosts, expected1, sort=False)
hosts = inventory.list_hosts("nc[2-3]")
hosts = inventory.list_hosts("nc[0-2]")
self.compare(hosts, expected2, sort=False)
hosts = inventory.list_hosts("nc[1-99999]")
hosts = inventory.list_hosts("nc[0-99999]")
self.compare(hosts, expected3, sort=False)
hosts = inventory.list_hosts("nc[2-3]:florida[1-2]")
hosts = inventory.list_hosts("nc[1-2]:florida[0-1]")
self.compare(hosts, expected4, sort=False)
def test_complex_intersect(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment