diff --git a/lib/ansible/inventory/__init__.py b/lib/ansible/inventory/__init__.py
index fb56e7414d5..20e117d0ab6 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 e5353ab7d8b..429019cbb1a 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 3dbe5505146..5c8c578788b 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 d92d9543fc7..e63148b8b5c 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 801d7c80582..12a9197f679 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