diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cd35fd8a0..ab636f6405 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Ansible Changes By Release * make remote_md5 internal function work with non-bash shells * 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 --limit option, which can be used to further confine the pattern given in ansible-playbooks 0.6 "Cabo" -- August 6, 2012 diff --git a/bin/ansible-playbook b/bin/ansible-playbook index 61fe1cd0bc..f372a91d88 100755 --- a/bin/ansible-playbook +++ b/bin/ansible-playbook @@ -52,7 +52,7 @@ def main(args): # create parser for CLI options 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, help="set additional key=value variables from the CLI") parser.add_option('-t', '--tags', dest='tags', default='all', @@ -102,6 +102,7 @@ def main(args): extra_vars=extra_vars, private_key_file=options.private_key_file, only_tags=only_tags, + subset=options.subset, ) try: diff --git a/lib/ansible/constants.py b/lib/ansible/constants.py index e723cf1197..8af1983b40 100644 --- a/lib/ansible/constants.py +++ b/lib/ansible/constants.py @@ -35,4 +35,5 @@ DEFAULT_SUDO_USER = os.environ.get('ANSIBLE_SUDO_USER','root') DEFAULT_REMOTE_PORT = 22 DEFAULT_TRANSPORT = os.environ.get('ANSIBLE_TRANSPORT','paramiko') DEFAULT_TRANSPORT_OPTS = ['local', 'paramiko', 'ssh'] +DEFAULT_SUBSET = None diff --git a/lib/ansible/inventory/__init__.py b/lib/ansible/inventory/__init__.py index 3b56567d7e..47f01dedc8 100644 --- a/lib/ansible/inventory/__init__.py +++ b/lib/ansible/inventory/__init__.py @@ -34,7 +34,7 @@ class Inventory(object): 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' ] 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 self._restriction = None + self._subset = None # whether the inventory file is a script self._is_script = False @@ -103,7 +104,9 @@ class Inventory(object): inverted = False for group in groups: 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 if self._restriction==None or host.name in self._restriction: if inverted: @@ -187,12 +190,28 @@ class Inventory(object): def get_restriction(self): return self._restriction - def restrict_to(self, restriction, append_missing=False): - """ Restrict list operations to the hosts given in restriction """ + def restrict_to(self, 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: 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): """ Do not restrict list operations """ diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index b668592674..118d5c11f8 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -57,7 +57,8 @@ class PlayBook(object): sudo = False, sudo_user = C.DEFAULT_SUDO_USER, extra_vars = None, - only_tags = None): + only_tags = None, + subset = C.DEFAULT_SUBSET): """ playbook: path to a playbook file @@ -104,7 +105,8 @@ class PlayBook(object): self.private_key_file = private_key_file 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: self.global_vars.update(self.inventory.get_group_variables('all')) diff --git a/lib/ansible/utils.py b/lib/ansible/utils.py index 9582ab9f82..e6d9bc8022 100644 --- a/lib/ansible/utils.py +++ b/lib/ansible/utils.py @@ -336,7 +336,8 @@ def increment_debug(option, opt, value, parser): global VERBOSITY 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 ''' 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', help="specify path(s) to module library (default=%s)" % 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', dest='timeout', help="override the SSH timeout in seconds (default=%s)" % constants.DEFAULT_TIMEOUT)