Commit 259f2dc4 by Michael DeHaan

Various retooling of the internal code behind inventory pattern matching in…

Various retooling of the internal code behind inventory pattern matching in support of pending support
for host ranges.
parent 1c81ddf8
...@@ -89,31 +89,99 @@ class Inventory(object): ...@@ -89,31 +89,99 @@ class Inventory(object):
def _match(self, str, pattern_str): def _match(self, str, pattern_str):
return fnmatch.fnmatch(str, pattern_str) return fnmatch.fnmatch(str, pattern_str)
# TODO: cache this logic so if called a second time the result is not recalculated
def get_hosts(self, pattern="all"): def get_hosts(self, pattern="all"):
""" Get all host objects matching the pattern """ """
hosts = {} find all host names matching a pattern string, taking into account any inventory restrictions or
applied subsets.
"""
# process patterns
patterns = pattern.replace(";",":").split(":") patterns = pattern.replace(";",":").split(":")
positive_patterns = [ p for p in patterns if not p.startswith("!") ]
negative_patterns = [ p for p in patterns if p.startswith("!") ]
# find hosts matching positive patterns
hosts = self._get_hosts(positive_patterns)
# exclude hosts mentioned in a negative pattern
if len(negative_patterns):
exclude_hosts = self._get_hosts(negative_patterns)
print "EXCLUDING HOSTS: %s" % exclude_hosts
hosts = [ h for h in hosts if h not in exclude_hosts ]
# exclude hosts not in a subset, if defined
if self._subset:
positive_subsetp = [ p for p in self._subset if not p.startswith("!") ]
negative_subsetp = [ p for p in self._subset if p.startswith("!") ]
if len(positive_subsetp):
positive_subset = self._get_hosts(positive_subsetp)
hosts = [ h for h in hosts if (h in positive_subset) ]
if len(negative_subsetp):
negative_subset = self._get_hosts(negative_subsetp)
hosts = [ h for h in hosts if (h not in negative_subset)]
# exclude hosts mentioned in any restriction (ex: failed hosts)
if self._restriction is not None:
hosts = [ h for h in hosts if h.name in self._restriction ]
return hosts
def _get_hosts(self, patterns):
"""
finds hosts that postively match a particular list of patterns. Does not
take into account negative matches.
"""
by_pattern = {}
for p in patterns:
(name, enumeration_details) = self._enumeration_info(p)
by_pattern[p] = self._hosts_in_unenumerated_pattern(name)
ranged = {}
for (pat, hosts) in by_pattern.iteritems():
ranged[pat] = self._apply_ranges(pat, hosts)
results = []
for (pat, hosts) in ranged.iteritems():
results.extend(hosts)
return list(set(results))
def _enumeration_info(self, pattern):
"""
returns (pattern, limits) taking a regular pattern and finding out
which parts of it correspond to start/stop offsets. limits is
a tuple of (start, stop) or None
"""
if not "[" in pattern:
return (pattern, None)
(first, rest) = pattern.split("[")
rest.replace("]","")
if not "-" in rest:
raise errors.AnsibleError("invalid pattern: %s" % pattern)
(left, right) = rest.split("-",1)
return (first, (left, right))
def _apply_ranges(self, pat, hosts):
(loose_pattern, limits) = self._enumeration_info(pat)
if not limits:
return hosts
raise Exception("ranges are not yet supported")
# 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 = {}
# ignore any negative checks here, this is handled elsewhere
pattern = pattern.replace("!","")
groups = self.get_groups() groups = self.get_groups()
for pat in patterns: for group in groups:
if pat.startswith("!"): for host in group.get_hosts():
pat = pat[1:] if pattern == 'all' or self._match(group.name, pattern) or self._match(host.name, pattern):
inverted = True hosts[host.name] = host
else:
inverted = False
for group in groups:
for host in group.get_hosts():
if self._subset and host.name not in self._subset:
continue
if pat == 'all' or self._match(group.name, pat) or self._match(host.name, pat):
# must test explicitly for None because [] means no hosts allowed
if self._restriction==None or host.name in self._restriction:
if inverted:
if host.name in hosts:
del hosts[host.name]
else:
hosts[host.name] = host
return sorted(hosts.values(), key=lambda x: x.name) return sorted(hosts.values(), key=lambda x: x.name)
def get_groups(self): def get_groups(self):
...@@ -210,7 +278,7 @@ class Inventory(object): ...@@ -210,7 +278,7 @@ class Inventory(object):
if subset_pattern is None: if subset_pattern is None:
self._subset = None self._subset = None
else: else:
self._subset = self.list_hosts(subset_pattern) self._subset = subset_pattern.replace(";",":").split(":")
def lift_restriction(self): def lift_restriction(self):
""" Do not restrict list operations """ """ Do not restrict list operations """
......
...@@ -111,21 +111,6 @@ class TestInventory(unittest.TestCase): ...@@ -111,21 +111,6 @@ class TestInventory(unittest.TestCase):
print expected_hosts print expected_hosts
assert sorted(hosts) == sorted(expected_hosts) assert sorted(hosts) == sorted(expected_hosts)
def test_simple_exclude(self):
inventory = self.simple_inventory()
hosts = inventory.list_hosts("all:!greek")
expected_hosts=['jupiter', 'saturn', 'thor', 'odin', 'loki',
'thrudgelmir0', 'thrudgelmir1', 'thrudgelmir2',
'thrudgelmir3', 'thrudgelmir4', 'thrudgelmir5']
assert sorted(hosts) == sorted(expected_hosts)
hosts = inventory.list_hosts("all:!norse:!greek")
expected_hosts=['jupiter', 'saturn',
'thrudgelmir0', 'thrudgelmir1', 'thrudgelmir2',
'thrudgelmir3', 'thrudgelmir4', 'thrudgelmir5']
assert sorted(hosts) == sorted(expected_hosts)
def test_simple_vars(self): def test_simple_vars(self):
inventory = self.simple_inventory() inventory = self.simple_inventory()
vars = inventory.get_variables('thor') vars = inventory.get_variables('thor')
...@@ -168,12 +153,12 @@ class TestInventory(unittest.TestCase): ...@@ -168,12 +153,12 @@ class TestInventory(unittest.TestCase):
def test_complex_exclude(self): def test_complex_exclude(self):
inventory = self.complex_inventory() inventory = self.complex_inventory()
hosts = inventory.list_hosts("nc:!triangle:florida:!orlando") hosts = inventory.list_hosts("nc:florida:!triangle:!orlando")
expected_hosts=['rtp_a', 'rtp_b', 'rtb_c', 'miami'] expected_hosts = ['miami', 'rtb_c', 'rtp_a', 'rtp_b']
print "HOSTS=%s" % sorted(hosts)
print "EXPECTED=%s" % sorted(expected_hosts)
assert sorted(hosts) == sorted(expected_hosts) assert sorted(hosts) == sorted(expected_hosts)
################################################### ###################################################
### Inventory API tests ### Inventory API tests
......
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