Skip to content

Commit

Permalink
Alphabetic inventory hostname patterns.
Browse files Browse the repository at this point in the history
  - Code, docs, tests.
  - Also added test of large range 000-142 to verify alpha range did not
    break this.
  • Loading branch information
njharman committed Dec 11, 2012
1 parent 959a461 commit 6603737
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 37 deletions.
13 changes: 7 additions & 6 deletions docsite/rst/patterns.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,17 @@ Adding a lot of hosts? In 0.6 and later, if you have a lot of hosts following s

[webservers]
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
+++++++++++++++++

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>

Such as::

ansible webservers -m service -a "name=httpd state=restarted"
Expand All @@ -70,15 +71,15 @@ This is done by designating particular host names or groups of hosts.
The following patterns target all hosts in the inventory file::

all
*
*

Basically 'all' is an alias for '*'. It is also possible to address a specific host or hosts::

one.example.com
one.example.com:two.example.com
192.168.1.50
192.168.1.*

The following patterns address one or more groups, which are denoted
with the aforementioned bracket headers in the inventory file::

Expand All @@ -105,7 +106,7 @@ Host Variables
++++++++++++++

It is easy to assign variables to hosts that will be used later in playbooks::

[atlanta]
host1 http_port=80 maxRequestsPerChild=808
host2 http_port=303 maxRequestsPerChild=909
Expand Down Expand Up @@ -188,7 +189,7 @@ the 'raleigh' group might look like::

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.

.. versionadded:: 0.5
Expand Down
29 changes: 18 additions & 11 deletions lib/ansible/inventory/expand_hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
'''
This module is for enhancing ansible's inventory parsing capability such
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
defaults to 0.
form of [beg:end], example: [1:5], [a:c], [D:G]. If beg is not specified,
it defaults to 0.
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
Expand All @@ -30,6 +30,7 @@
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.
'''
import string

from ansible import errors

Expand Down Expand Up @@ -81,17 +82,23 @@ def expand_hostname_range(line = None):
raise errors.AnsibleError("host range end value missing")
if beg[0] == '0' and len(beg) > 1:
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:
rlen = None
if rlen > 1 and rlen != len(end):
raise errors.AnsibleError("host range format incorrectly specified!")
fill = str

for _ in range(int(beg), int(end)+1):
if rlen:
rseq = str(_).zfill(rlen) # range sequence
else:
rseq = str(_)
hname = ''.join((head, rseq, tail))
try:
i_beg = string.ascii_letters.index(beg)
i_end = string.ascii_letters.index(end)
if i_beg > i_end:
raise errors.AnsibleError("host range format incorrectly specified!")
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)

return all_hosts
44 changes: 24 additions & 20 deletions test/TestInventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ def setUp(self):
self.cwd = os.getcwd()
self.test_dir = os.path.join(self.cwd, 'test')

self.inventory_file = os.path.join(self.test_dir, 'simple_hosts')
self.complex_inventory_file = os.path.join(self.test_dir, 'complex_hosts')
self.inventory_script = os.path.join(self.test_dir, 'inventory_api.py')
self.inventory_file = os.path.join(self.test_dir, 'simple_hosts')
self.large_range_inventory_file = os.path.join(self.test_dir, 'large_range')
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)

Expand All @@ -29,38 +30,36 @@ def compare(self, left, right, sort=True):
def simple_inventory(self):
return Inventory(self.inventory_file)

def large_range_inventory(self):
return Inventory(self.large_range_inventory_file)

def script_inventory(self):
return Inventory(self.inventory_script)

def complex_inventory(self):
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

def test_simple(self):
inventory = self.simple_inventory()
hosts = inventory.list_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)
self.assertEqual(sorted(hosts), sorted(self.all_simple_hosts))

def test_simple_all(self):
inventory = self.simple_inventory()
hosts = inventory.list_hosts('all')

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)
self.assertEqual(sorted(hosts), sorted(self.all_simple_hosts))

def test_simple_norse(self):
inventory = self.simple_inventory()
Expand Down Expand Up @@ -132,6 +131,11 @@ def test_simple_port(self):
print 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

Expand Down Expand Up @@ -252,7 +256,7 @@ def test_script_multiple_groups(self):
vars = inventory.get_variables('zeus')

print "VARS=%s" % vars

assert vars == {'inventory_hostname': 'zeus',
'inventory_hostname_short': 'zeus',
'group_names': ['greek', 'major-god']}
1 change: 1 addition & 0 deletions test/large_range
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bob[000:142]
4 changes: 4 additions & 0 deletions test/simple_hosts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ cottus[99:100]
thor
odin
loki

[egyptian]
Hotep-[a:c]
Bast[C:D]

0 comments on commit 6603737

Please sign in to comment.