diff --git a/docsite/rst/intro_patterns.rst b/docsite/rst/intro_patterns.rst index e98f9195ca8..4e4b8ab6604 100644 --- a/docsite/rst/intro_patterns.rst +++ b/docsite/rst/intro_patterns.rst @@ -79,8 +79,9 @@ You can refer to hosts within the group by adding a subscript to the group name: webservers[0] # == cobweb webservers[-1] # == weber - webservers[0:1] # == webservers[0]:webservers[1] - # == cobweb:webbing + webservers[0:1] # == webservers[0],webservers[1] + # == cobweb,webbing + webservers[1:] # == webbing,weber Most people don't specify patterns as regular expressions, but you can. Just start the pattern with a '~':: diff --git a/lib/ansible/inventory/__init__.py b/lib/ansible/inventory/__init__.py index 99e6729b899..cd787e6876b 100644 --- a/lib/ansible/inventory/__init__.py +++ b/lib/ansible/inventory/__init__.py @@ -342,9 +342,9 @@ class Inventory(object): r'''^ (.+) # A pattern expression ending with... \[(?: # A [subscript] expression comprising: - (-?[0-9]+) # A single positive or negative number - | # Or a numeric range - ([0-9]+)([:-])([0-9]+) + (-?[0-9]+)| # A single positive or negative number + ([0-9]+)([:-]) # Or an x:y or x: range. + ([0-9]*) )\] $ ''', re.X @@ -357,6 +357,8 @@ class Inventory(object): if idx: subscript = (int(idx), None) else: + if not end: + end = -1 subscript = (int(start), int(end)) if sep == '-': display.deprecated("Use [x:y] inclusive subscripts instead of [x-y]", version=2.0, removed=True) @@ -375,6 +377,8 @@ class Inventory(object): (start, end) = subscript if end: + if end == -1: + end = len(hosts)-1 return hosts[start:end+1] else: return [ hosts[start] ] diff --git a/test/units/inventory/test_inventory.py b/test/units/inventory/test_inventory.py index e397143390b..e7bcceb85da 100644 --- a/test/units/inventory/test_inventory.py +++ b/test/units/inventory/test_inventory.py @@ -19,6 +19,8 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +import string + from ansible.compat.tests import unittest from ansible.compat.tests.mock import patch, MagicMock @@ -44,6 +46,7 @@ class TestInventory(unittest.TestCase): ' a : b ': ['a', 'b'], 'foo:bar:baz[1:2]': ['foo', 'bar', 'baz[1:2]'], } + pattern_lists = [ [['a'], ['a']], [['a', 'b'], ['a', 'b']], @@ -52,6 +55,21 @@ class TestInventory(unittest.TestCase): ['9a01:7f8:191:7701::9', '9a01:7f8:191:7701::9','foo']] ] + # pattern_string: [ ('base_pattern', (a,b)), ['x','y','z'] ] + # a,b are the bounds of the subscript; x..z are the results of the subscript + # when applied to string.ascii_letters. + + subscripts = { + 'a': [('a',None), list(string.ascii_letters)], + 'a[0]': [('a', (0, None)), ['a']], + 'a[1]': [('a', (1, None)), ['b']], + 'a[2:3]': [('a', (2, 3)), ['c', 'd']], + 'a[-1]': [('a', (-1, None)), ['Z']], + 'a[-2]': [('a', (-2, None)), ['Y']], + 'a[48:]': [('a', (48, -1)), ['W', 'X', 'Y', 'Z']], + 'a[49:]': [('a', (49, -1)), ['X', 'Y', 'Z']], + 'a[1:]': [('a', (1, -1)), list(string.ascii_letters[1:])], + } def setUp(self): v = VariableManager() @@ -67,3 +85,16 @@ class TestInventory(unittest.TestCase): for p, r in self.pattern_lists: self.assertEqual(r, self.i._split_pattern(p)) + + def test_ranges(self): + + for s in self.subscripts: + r = self.subscripts[s] + self.assertEqual(r[0], self.i._split_subscript(s)) + self.assertEqual( + r[1], + self.i._apply_subscript( + list(string.ascii_letters), + r[0][1] + ) + )