From 9041adddaa327aff0cdd7f2f6368012e8893da20 Mon Sep 17 00:00:00 2001 From: Michael DeHaan Date: Sat, 11 Aug 2012 13:49:18 -0400 Subject: [PATCH] Add support for ranged patterns like webservers[0-49] for hitting the first 50 webservers. --- CHANGELOG.md | 1 + lib/ansible/inventory/__init__.py | 27 ++++++++++++++++++++++----- test/TestInventory.py | 31 +++++++++++++++++++++++++------ test/complex_hosts | 2 +- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e02bbce2cc2..bb8dd5041af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ Ansible Changes By Release * setup module now detects interfaces with aliases * new 'ansible_all_ipv4_addresses' and 'ansible_all_ipv6_addresses' facts -- which are simple lists * better handling of VM guest type detection in setup module +* adds ranged patterns like dbservers[0-49] for usage with patterns or --limit 0.6 "Cabo" -- August 6, 2012 diff --git a/lib/ansible/inventory/__init__.py b/lib/ansible/inventory/__init__.py index a6e3b5e03c5..019fb60d847 100644 --- a/lib/ansible/inventory/__init__.py +++ b/lib/ansible/inventory/__init__.py @@ -123,7 +123,7 @@ class Inventory(object): if self._restriction is not None: hosts = [ h for h in hosts if h.name in self._restriction ] - return hosts + return sorted(hosts, key=lambda x: x.name) def _get_hosts(self, patterns): """ @@ -134,7 +134,9 @@ class Inventory(object): by_pattern = {} for p in patterns: (name, enumeration_details) = self._enumeration_info(p) - by_pattern[p] = self._hosts_in_unenumerated_pattern(name) + hpat = self._hosts_in_unenumerated_pattern(name) + hpat = sorted(hpat, key=lambda x: x.name) + by_pattern[p] = hpat ranged = {} for (pat, hosts) in by_pattern.iteritems(): @@ -156,17 +158,32 @@ class Inventory(object): if not "[" in pattern: return (pattern, None) (first, rest) = pattern.split("[") - rest.replace("]","") + rest = 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): + """ + given a pattern like foo, that matches hosts, return all of hosts + given a pattern like foo[0:5], where foo matches hosts, return the first 6 hosts + """ + (loose_pattern, limits) = self._enumeration_info(pat) if not limits: return hosts - raise Exception("ranges are not yet supported") + + (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 # TODO: cache this logic so if called a second time the result is not recalculated def _hosts_in_unenumerated_pattern(self, pattern): @@ -252,7 +269,7 @@ class Inventory(object): return [ h.name for h in self.get_hosts(pattern) ] def list_groups(self): - return [ g.name for g in self.groups ] + return sorted([ g.name for g in self.groups ], key=lambda x: x.name) def get_restriction(self): return self._restriction diff --git a/test/TestInventory.py b/test/TestInventory.py index 89917f37cef..ec71f54bc10 100644 --- a/test/TestInventory.py +++ b/test/TestInventory.py @@ -18,14 +18,14 @@ class TestInventory(unittest.TestCase): def tearDown(self): os.chmod(self.inventory_script, 0644) - def compare(self, left, right): - left = sorted(left) - right = sorted(right) + def compare(self, left, right, sort=True): + if sort: + left = sorted(left) + right = sorted(right) print left print right assert left == right - def simple_inventory(self): return Inventory(self.inventory_file) @@ -152,13 +152,32 @@ class TestInventory(unittest.TestCase): def test_complex_exclude(self): inventory = self.complex_inventory() - hosts = inventory.list_hosts("nc:florida:!triangle:!orlando") - expected_hosts = ['miami', 'rtb_c', 'rtp_a', 'rtp_b'] + expected_hosts = ['miami', 'rtp_a', 'rtp_b', 'rtp_c'] print "HOSTS=%s" % sorted(hosts) print "EXPECTED=%s" % sorted(expected_hosts) assert sorted(hosts) == sorted(expected_hosts) + 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'] + + inventory = self.complex_inventory() + print "ALL NC=%s" % inventory.list_hosts("nc") + hosts = inventory.list_hosts("nc[0-1]") + self.compare(hosts, expected1, sort=False) + hosts = inventory.list_hosts("nc[2-3]") + self.compare(hosts, expected2, sort=False) + hosts = inventory.list_hosts("nc[1-99999]") + self.compare(hosts, expected3, sort=False) + hosts = inventory.list_hosts("nc[2-3]:florida[1-2]") + self.compare(hosts, expected4, sort=False) + + ################################################### ### Inventory API tests diff --git a/test/complex_hosts b/test/complex_hosts index 18b95b207c6..8c2c726f3be 100644 --- a/test/complex_hosts +++ b/test/complex_hosts @@ -38,7 +38,7 @@ d=10002 [rtp] rtp_a rtp_b -rtb_c +rtp_c [rtp:vars] a=1