From 6b46cc5c73544074331af1ff50460fd6f2c24212 Mon Sep 17 00:00:00 2001 From: Brian Coca Date: Fri, 10 Mar 2017 19:13:17 -0500 Subject: [PATCH] added new 'order' directive to sort hosts in play fixes #10964 --- lib/ansible/inventory/__init__.py | 18 ++++++++++++++++-- lib/ansible/playbook/play.py | 4 ++-- lib/ansible/plugins/strategy/__init__.py | 8 ++++++++ lib/ansible/plugins/strategy/free.py | 3 ++- lib/ansible/plugins/strategy/linear.py | 2 +- 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/ansible/inventory/__init__.py b/lib/ansible/inventory/__init__.py index fb56e7414d..20e117d0ab 100644 --- a/lib/ansible/inventory/__init__.py +++ b/lib/ansible/inventory/__init__.py @@ -221,7 +221,7 @@ class Inventory(object): results.append(item) return results - def get_hosts(self, pattern="all", ignore_limits=False, ignore_restrictions=False): + def get_hosts(self, pattern="all", ignore_limits=False, ignore_restrictions=False, order=None): """ Takes a pattern or list of patterns and returns a list of matching inventory host names, taking into account any active restrictions @@ -258,7 +258,21 @@ class Inventory(object): seen = set() HOSTS_PATTERNS_CACHE[pattern_hash] = [x for x in hosts if x not in seen and not seen.add(x)] - return HOSTS_PATTERNS_CACHE[pattern_hash][:] + # sort hosts list if needed (should only happen when called from strategy) + if order in ['sorted', 'reverse_sorted']: + from operator import attrgetter + hosts = sorted(HOSTS_PATTERNS_CACHE[pattern_hash][:], key=attrgetter('name'), reverse=(order == 'reverse_sorted')) + elif order == 'reverse_inventory': + hosts = sorted(HOSTS_PATTERNS_CACHE[pattern_hash][:], reverse=True) + else: + hosts = HOSTS_PATTERNS_CACHE[pattern_hash][:] + if order == 'shuffle': + from random import shuffle + shuffle(hosts) + elif order not in [None, 'inventory']: + AnsibleError("Invalid 'order' specified for inventory hosts: %s" % order) + + return hosts @classmethod def split_host_pattern(cls, pattern): diff --git a/lib/ansible/playbook/play.py b/lib/ansible/playbook/play.py index e5353ab7d8..429019cbb1 100644 --- a/lib/ansible/playbook/play.py +++ b/lib/ansible/playbook/play.py @@ -56,7 +56,7 @@ class Play(Base, Taggable, Become): """ # ================================================================================= - # Connection-Related Attributes + _name = FieldAttribute(isa='string', default='', always_post_validate=True) # TODO: generalize connection _accelerate = FieldAttribute(isa='bool', default=False, always_post_validate=True) @@ -69,7 +69,6 @@ class Play(Base, Taggable, Become): _gather_subset = FieldAttribute(isa='barelist', default=None, always_post_validate=True) _gather_timeout = FieldAttribute(isa='int', default=None, always_post_validate=True) _hosts = FieldAttribute(isa='list', required=True, listof=string_types, always_post_validate=True) - _name = FieldAttribute(isa='string', default='', always_post_validate=True) # Variable Attributes _vars_files = FieldAttribute(isa='list', default=[], priority=99) @@ -90,6 +89,7 @@ class Play(Base, Taggable, Become): _max_fail_percentage = FieldAttribute(isa='percent', always_post_validate=True) _serial = FieldAttribute(isa='list', default=[], always_post_validate=True) _strategy = FieldAttribute(isa='string', default=C.DEFAULT_STRATEGY, always_post_validate=True) + _order = FieldAttribute(isa='string', always_post_validate=True) # ================================================================================= diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index 3dbe550514..5c8c578788 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -918,3 +918,11 @@ class StrategyBase: display.vv("META: %s" % msg) return [TaskResult(target_host, task, result)] + + def get_hosts_left(self, iterator): + + hosts_left = [] + for host in self._inventory.get_hosts(iterator._play.hosts, order=iterator._play.order): + if host.name not in self._tqm._unreachable_hosts: + hosts_left.append(host) + return hosts_left diff --git a/lib/ansible/plugins/strategy/free.py b/lib/ansible/plugins/strategy/free.py index d92d9543fc..e63148b8b5 100644 --- a/lib/ansible/plugins/strategy/free.py +++ b/lib/ansible/plugins/strategy/free.py @@ -61,7 +61,8 @@ class StrategyModule(StrategyBase): work_to_do = True while work_to_do and not self._tqm._terminated: - hosts_left = [host for host in self._inventory.get_hosts(iterator._play.hosts) if host.name not in self._tqm._unreachable_hosts] + hosts_left = self.get_hosts_left(iterator) + if len(hosts_left) == 0: self._tqm.send_callback('v2_playbook_on_no_hosts_remaining') result = False diff --git a/lib/ansible/plugins/strategy/linear.py b/lib/ansible/plugins/strategy/linear.py index 801d7c8058..12a9197f67 100644 --- a/lib/ansible/plugins/strategy/linear.py +++ b/lib/ansible/plugins/strategy/linear.py @@ -167,7 +167,7 @@ class StrategyModule(StrategyBase): try: display.debug("getting the remaining hosts for this loop") - hosts_left = [host for host in self._inventory.get_hosts(iterator._play.hosts) if host.name not in self._tqm._unreachable_hosts] + hosts_left = self.get_hosts_left(iterator) display.debug("done getting the remaining hosts for this loop") # queue up this task for each host in the inventory