add the limit option, which can be used to further confine the patterns selected by "hosts:" in ansible-playbooks

to an additional pattern (a subset) specified on the command line.  For instance, a playbook could be reusable
and target "webservers" and "dbservers", but you want to test only in the stage environment, or a few boxes at a time.
This commit is contained in:
Michael DeHaan 2012-08-10 02:45:29 -04:00
parent 05a128c2be
commit 1c81ddf8d4
6 changed files with 38 additions and 8 deletions

View file

@ -28,6 +28,7 @@ Ansible Changes By Release
* make remote_md5 internal function work with non-bash shells * make remote_md5 internal function work with non-bash shells
* allow user to be passed in via --extra-vars (regression) * allow user to be passed in via --extra-vars (regression)
* add ability to store the result of any command in a register (see examples/playbooks/register_logic.yml) * add ability to store the result of any command in a register (see examples/playbooks/register_logic.yml)
* add --limit option, which can be used to further confine the pattern given in ansible-playbooks
0.6 "Cabo" -- August 6, 2012 0.6 "Cabo" -- August 6, 2012

View file

@ -52,7 +52,7 @@ def main(args):
# create parser for CLI options # create parser for CLI options
usage = "%prog playbook.yml" usage = "%prog playbook.yml"
parser = utils.base_parser(constants=C, usage=usage, connect_opts=True, runas_opts=True) parser = utils.base_parser(constants=C, usage=usage, connect_opts=True, runas_opts=True, subset_opts=True)
parser.add_option('-e', '--extra-vars', dest="extra_vars", default=None, parser.add_option('-e', '--extra-vars', dest="extra_vars", default=None,
help="set additional key=value variables from the CLI") help="set additional key=value variables from the CLI")
parser.add_option('-t', '--tags', dest='tags', default='all', parser.add_option('-t', '--tags', dest='tags', default='all',
@ -102,6 +102,7 @@ def main(args):
extra_vars=extra_vars, extra_vars=extra_vars,
private_key_file=options.private_key_file, private_key_file=options.private_key_file,
only_tags=only_tags, only_tags=only_tags,
subset=options.subset,
) )
try: try:

View file

@ -35,4 +35,5 @@ DEFAULT_SUDO_USER = os.environ.get('ANSIBLE_SUDO_USER','root')
DEFAULT_REMOTE_PORT = 22 DEFAULT_REMOTE_PORT = 22
DEFAULT_TRANSPORT = os.environ.get('ANSIBLE_TRANSPORT','paramiko') DEFAULT_TRANSPORT = os.environ.get('ANSIBLE_TRANSPORT','paramiko')
DEFAULT_TRANSPORT_OPTS = ['local', 'paramiko', 'ssh'] DEFAULT_TRANSPORT_OPTS = ['local', 'paramiko', 'ssh']
DEFAULT_SUBSET = None

View file

@ -34,7 +34,7 @@ class Inventory(object):
Host inventory for ansible. Host inventory for ansible.
""" """
__slots__ = [ 'host_list', 'groups', '_restriction', '_is_script', __slots__ = [ 'host_list', 'groups', '_restriction', '_subset', '_is_script',
'parser', '_vars_per_host', '_vars_per_group', '_hosts_cache' ] 'parser', '_vars_per_host', '_vars_per_group', '_hosts_cache' ]
def __init__(self, host_list=C.DEFAULT_HOST_LIST): def __init__(self, host_list=C.DEFAULT_HOST_LIST):
@ -55,6 +55,7 @@ class Inventory(object):
# a list of host(names) to contain current inquiries to # a list of host(names) to contain current inquiries to
self._restriction = None self._restriction = None
self._subset = None
# whether the inventory file is a script # whether the inventory file is a script
self._is_script = False self._is_script = False
@ -103,7 +104,9 @@ class Inventory(object):
inverted = False inverted = False
for group in groups: for group in groups:
for host in group.get_hosts(): for host in group.get_hosts():
if self._match(group.name, pat) or pat == 'all' or self._match(host.name, pat): 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 # must test explicitly for None because [] means no hosts allowed
if self._restriction==None or host.name in self._restriction: if self._restriction==None or host.name in self._restriction:
if inverted: if inverted:
@ -187,12 +190,28 @@ class Inventory(object):
def get_restriction(self): def get_restriction(self):
return self._restriction return self._restriction
def restrict_to(self, restriction, append_missing=False): def restrict_to(self, restriction):
""" Restrict list operations to the hosts given in restriction """ """
Restrict list operations to the hosts given in restriction. This is used
to exclude failed hosts in main playbook code, don't use this for other
reasons.
"""
if type(restriction) != list: if type(restriction) != list:
restriction = [ restriction ] restriction = [ restriction ]
self._restriction = restriction self._restriction = restriction
def subset(self, subset_pattern):
"""
Limits inventory results to a subset of inventory that matches a given
pattern, such as to select a given geographic of numeric slice amongst
a previous 'hosts' selection that only select roles, or vice versa.
Corresponds to --limit parameter to ansible-playbook
"""
if subset_pattern is None:
self._subset = None
else:
self._subset = self.list_hosts(subset_pattern)
def lift_restriction(self): def lift_restriction(self):
""" Do not restrict list operations """ """ Do not restrict list operations """

View file

@ -57,7 +57,8 @@ class PlayBook(object):
sudo = False, sudo = False,
sudo_user = C.DEFAULT_SUDO_USER, sudo_user = C.DEFAULT_SUDO_USER,
extra_vars = None, extra_vars = None,
only_tags = None): only_tags = None,
subset = C.DEFAULT_SUBSET):
""" """
playbook: path to a playbook file playbook: path to a playbook file
@ -105,6 +106,7 @@ class PlayBook(object):
self.only_tags = only_tags self.only_tags = only_tags
self.inventory = ansible.inventory.Inventory(host_list) self.inventory = ansible.inventory.Inventory(host_list)
self.inventory.subset(subset)
if not self.inventory._is_script: if not self.inventory._is_script:
self.global_vars.update(self.inventory.get_group_variables('all')) self.global_vars.update(self.inventory.get_group_variables('all'))

View file

@ -336,7 +336,8 @@ def increment_debug(option, opt, value, parser):
global VERBOSITY global VERBOSITY
VERBOSITY += 1 VERBOSITY += 1
def base_parser(constants=C, usage="", output_opts=False, runas_opts=False, async_opts=False, connect_opts=False): def base_parser(constants=C, usage="", output_opts=False, runas_opts=False,
async_opts=False, connect_opts=False, subset_opts=False):
''' create an options parser for any ansible script ''' ''' create an options parser for any ansible script '''
parser = SortedOptParser(usage, version=version("%prog")) parser = SortedOptParser(usage, version=version("%prog"))
@ -357,6 +358,11 @@ def base_parser(constants=C, usage="", output_opts=False, runas_opts=False, asyn
parser.add_option('-M', '--module-path', dest='module_path', parser.add_option('-M', '--module-path', dest='module_path',
help="specify path(s) to module library (default=%s)" % constants.DEFAULT_MODULE_PATH, help="specify path(s) to module library (default=%s)" % constants.DEFAULT_MODULE_PATH,
default=constants.DEFAULT_MODULE_PATH) default=constants.DEFAULT_MODULE_PATH)
if subset_opts:
parser.add_option('-l', '--limit', default=constants.DEFAULT_SUBSET, dest='subset',
help='further limit selected hosts to an additional pattern')
parser.add_option('-T', '--timeout', default=constants.DEFAULT_TIMEOUT, type='int', parser.add_option('-T', '--timeout', default=constants.DEFAULT_TIMEOUT, type='int',
dest='timeout', dest='timeout',
help="override the SSH timeout in seconds (default=%s)" % constants.DEFAULT_TIMEOUT) help="override the SSH timeout in seconds (default=%s)" % constants.DEFAULT_TIMEOUT)