Merge pull request #1747 from njharman/alpharange

Alphabetic inventory hostname patterns.
This commit is contained in:
Michael DeHaan 2012-12-17 16:52:11 -08:00
commit 04195e202d
5 changed files with 54 additions and 37 deletions

View file

@ -48,8 +48,9 @@ Adding a lot of hosts? In 0.6 and later, if you have a lot of hosts following s
[webservers] [webservers]
www[01:50].example.com www[01:50].example.com
db-[a:f].example.com
Leading zeros can be included or removed, as desired, and the ranges are inclusive. For numeric patterns, leading zeros can be included or removed, as desired. Ranges are inclusive.
Selecting Targets Selecting Targets
+++++++++++++++++ +++++++++++++++++
@ -57,7 +58,7 @@ Selecting Targets
We'll go over how to use the command line in :doc:`examples` section, however, basically it looks like this:: We'll go over how to use the command line in :doc:`examples` section, however, basically it looks like this::
ansible <pattern_goes_here> -m <module_name> -a <arguments> ansible <pattern_goes_here> -m <module_name> -a <arguments>
Such as:: Such as::
ansible webservers -m service -a "name=httpd state=restarted" ansible webservers -m service -a "name=httpd state=restarted"
@ -70,7 +71,7 @@ This is done by designating particular host names or groups of hosts.
The following patterns target all hosts in the inventory file:: The following patterns target all hosts in the inventory file::
all all
* *
Basically 'all' is an alias for '*'. It is also possible to address a specific host or hosts:: Basically 'all' is an alias for '*'. It is also possible to address a specific host or hosts::
@ -78,7 +79,7 @@ Basically 'all' is an alias for '*'. It is also possible to address a specific
one.example.com:two.example.com one.example.com:two.example.com
192.168.1.50 192.168.1.50
192.168.1.* 192.168.1.*
The following patterns address one or more groups, which are denoted The following patterns address one or more groups, which are denoted
with the aforementioned bracket headers in the inventory file:: with the aforementioned bracket headers in the inventory file::
@ -105,7 +106,7 @@ Host Variables
++++++++++++++ ++++++++++++++
It is easy to assign variables to hosts that will be used later in playbooks:: It is easy to assign variables to hosts that will be used later in playbooks::
[atlanta] [atlanta]
host1 http_port=80 maxRequestsPerChild=808 host1 http_port=80 maxRequestsPerChild=808
host2 http_port=303 maxRequestsPerChild=909 host2 http_port=303 maxRequestsPerChild=909
@ -188,7 +189,7 @@ the 'raleigh' group might look like::
It is ok if these files do not exist, this is an optional feature. It is ok if these files do not exist, this is an optional feature.
Tip: Keeping your inventory file and variables in a git repo (or other version control) Tip: Keeping your inventory file and variables in a git repo (or other version control)
is an excellent way to track changes to your inventory and host variables. is an excellent way to track changes to your inventory and host variables.
.. versionadded:: 0.5 .. versionadded:: 0.5

View file

@ -20,8 +20,8 @@
''' '''
This module is for enhancing ansible's inventory parsing capability such This module is for enhancing ansible's inventory parsing capability such
that it can deal with hostnames specified using a simple pattern in the that it can deal with hostnames specified using a simple pattern in the
form of [beg:end], example: [1:5] where if beg is not specified, it form of [beg:end], example: [1:5], [a:c], [D:G]. If beg is not specified,
defaults to 0. it defaults to 0.
If beg is given and is left-zero-padded, e.g. '001', it is taken as a If beg is given and is left-zero-padded, e.g. '001', it is taken as a
formatting hint when the range is expanded. e.g. [001:010] is to be formatting hint when the range is expanded. e.g. [001:010] is to be
@ -30,6 +30,7 @@ expanded into 001, 002 ...009, 010.
Note that when beg is specified with left zero padding, then the length of Note that when beg is specified with left zero padding, then the length of
end must be the same as that of beg, else a exception is raised. end must be the same as that of beg, else a exception is raised.
''' '''
import string
from ansible import errors from ansible import errors
@ -81,17 +82,23 @@ def expand_hostname_range(line = None):
raise errors.AnsibleError("host range end value missing") raise errors.AnsibleError("host range end value missing")
if beg[0] == '0' and len(beg) > 1: if beg[0] == '0' and len(beg) > 1:
rlen = len(beg) # range length formatting hint rlen = len(beg) # range length formatting hint
if rlen != len(end):
raise errors.AnsibleError("host range format incorrectly specified!")
fill = lambda _: str(_).zfill(rlen) # range sequence
else: else:
rlen = None fill = str
if rlen > 1 and rlen != len(end):
raise errors.AnsibleError("host range format incorrectly specified!")
for _ in range(int(beg), int(end)+1): try:
if rlen: i_beg = string.ascii_letters.index(beg)
rseq = str(_).zfill(rlen) # range sequence i_end = string.ascii_letters.index(end)
else: if i_beg > i_end:
rseq = str(_) raise errors.AnsibleError("host range format incorrectly specified!")
hname = ''.join((head, rseq, tail)) seq = string.ascii_letters[i_beg:i_end+1]
except ValueError: # not a alpha range
seq = range(int(beg), int(end)+1)
for rseq in seq:
hname = ''.join((head, fill(rseq), tail))
all_hosts.append(hname) all_hosts.append(hname)
return all_hosts return all_hosts

View file

@ -9,9 +9,10 @@ class TestInventory(unittest.TestCase):
self.cwd = os.getcwd() self.cwd = os.getcwd()
self.test_dir = os.path.join(self.cwd, 'test') self.test_dir = os.path.join(self.cwd, 'test')
self.inventory_file = os.path.join(self.test_dir, 'simple_hosts') self.inventory_file = os.path.join(self.test_dir, 'simple_hosts')
self.complex_inventory_file = os.path.join(self.test_dir, 'complex_hosts') self.large_range_inventory_file = os.path.join(self.test_dir, 'large_range')
self.inventory_script = os.path.join(self.test_dir, 'inventory_api.py') self.complex_inventory_file = os.path.join(self.test_dir, 'complex_hosts')
self.inventory_script = os.path.join(self.test_dir, 'inventory_api.py')
os.chmod(self.inventory_script, 0755) os.chmod(self.inventory_script, 0755)
@ -29,38 +30,36 @@ class TestInventory(unittest.TestCase):
def simple_inventory(self): def simple_inventory(self):
return Inventory(self.inventory_file) return Inventory(self.inventory_file)
def large_range_inventory(self):
return Inventory(self.large_range_inventory_file)
def script_inventory(self): def script_inventory(self):
return Inventory(self.inventory_script) return Inventory(self.inventory_script)
def complex_inventory(self): def complex_inventory(self):
return Inventory(self.complex_inventory_file) return Inventory(self.complex_inventory_file)
all_simple_hosts=['jupiter', 'saturn', 'zeus', 'hera',
'cerberus001','cerberus002','cerberus003',
'cottus99', 'cottus100',
'poseidon', 'thor', 'odin', 'loki',
'thrudgelmir0', 'thrudgelmir1', 'thrudgelmir2',
'thrudgelmir3', 'thrudgelmir4', 'thrudgelmir5',
'Hotep-a', 'Hotep-b', 'Hotep-c',
'BastC', 'BastD', ]
##################################### #####################################
### Simple inventory format tests ### Simple inventory format tests
def test_simple(self): def test_simple(self):
inventory = self.simple_inventory() inventory = self.simple_inventory()
hosts = inventory.list_hosts() hosts = inventory.list_hosts()
self.assertEqual(sorted(hosts), sorted(self.all_simple_hosts))
expected_hosts=['jupiter', 'saturn', 'zeus', 'hera',
'cerberus001','cerberus002','cerberus003',
'cottus99', 'cottus100',
'poseidon', 'thor', 'odin', 'loki',
'thrudgelmir0', 'thrudgelmir1', 'thrudgelmir2',
'thrudgelmir3', 'thrudgelmir4', 'thrudgelmir5']
assert sorted(hosts) == sorted(expected_hosts)
def test_simple_all(self): def test_simple_all(self):
inventory = self.simple_inventory() inventory = self.simple_inventory()
hosts = inventory.list_hosts('all') hosts = inventory.list_hosts('all')
self.assertEqual(sorted(hosts), sorted(self.all_simple_hosts))
expected_hosts=['jupiter', 'saturn', 'zeus', 'hera',
'cerberus001','cerberus002','cerberus003',
'cottus99', 'cottus100',
'poseidon', 'thor', 'odin', 'loki',
'thrudgelmir0', 'thrudgelmir1', 'thrudgelmir2',
'thrudgelmir3', 'thrudgelmir4', 'thrudgelmir5']
assert sorted(hosts) == sorted(expected_hosts)
def test_simple_norse(self): def test_simple_norse(self):
inventory = self.simple_inventory() inventory = self.simple_inventory()
@ -132,6 +131,11 @@ class TestInventory(unittest.TestCase):
print expected print expected
assert vars == expected assert vars == expected
def test_large_range(self):
inventory = self.large_range_inventory()
hosts = inventory.list_hosts()
self.assertEqual(sorted(hosts), sorted('bob%03i' %i for i in range(0, 143)))
################################################### ###################################################
### INI file advanced tests ### INI file advanced tests
@ -280,7 +284,7 @@ class TestInventory(unittest.TestCase):
vars = inventory.get_variables('zeus') vars = inventory.get_variables('zeus')
print "VARS=%s" % vars print "VARS=%s" % vars
assert vars == {'inventory_hostname': 'zeus', assert vars == {'inventory_hostname': 'zeus',
'inventory_hostname_short': 'zeus', 'inventory_hostname_short': 'zeus',
'group_names': ['greek', 'major-god']} 'group_names': ['greek', 'major-god']}

1
test/large_range Normal file
View file

@ -0,0 +1 @@
bob[000:142]

View file

@ -13,3 +13,7 @@ cottus[99:100]
thor thor
odin odin
loki loki
[egyptian]
Hotep-[a:c]
Bast[C:D]