[Inventory] Cache the result of enumerating groups and host names

for `VariableManager._get_magic_variables()`.

This saves a lot of time re-iterating the nearly always constant global
list of groups and their members.

Generate once and cache, and invalidate cache in case `add_host:` or
`group_by:` are used.
This commit is contained in:
Tobias Wolf 2016-08-30 14:27:06 +02:00 committed by James Cammarata
parent 28227546fa
commit c23b11d212
4 changed files with 38 additions and 11 deletions

View file

@ -25,7 +25,7 @@ import sys
import re import re
import itertools import itertools
from ansible.compat.six import string_types from ansible.compat.six import string_types, iteritems
from ansible import constants as C from ansible import constants as C
from ansible.errors import AnsibleError from ansible.errors import AnsibleError
@ -67,6 +67,7 @@ class Inventory(object):
self._vars_per_group = {} self._vars_per_group = {}
self._hosts_cache = {} self._hosts_cache = {}
self._pattern_cache = {} self._pattern_cache = {}
self._group_dict_cache = {}
self._vars_plugins = [] self._vars_plugins = []
self._basedir = self.basedir() self._basedir = self.basedir()
@ -88,6 +89,7 @@ class Inventory(object):
# clear the cache here, which is only useful if more than # clear the cache here, which is only useful if more than
# one Inventory objects are created when using the API directly # one Inventory objects are created when using the API directly
self.clear_pattern_cache() self.clear_pattern_cache()
self.clear_group_dict_cache()
self.parse_inventory(host_list) self.parse_inventory(host_list)
@ -220,7 +222,7 @@ class Inventory(object):
hosts = [ h for h in hosts if h in subset ] hosts = [ h for h in hosts if h in subset ]
# exclude hosts mentioned in any restriction (ex: failed hosts) # exclude hosts mentioned in any restriction (ex: failed hosts)
if self._restriction is not None: if self._restriction:
hosts = [ h for h in hosts if h.name in self._restriction ] hosts = [ h for h in hosts if h.name in self._restriction ]
seen = set() seen = set()
@ -502,6 +504,10 @@ class Inventory(object):
HOSTS_PATTERNS_CACHE = {} HOSTS_PATTERNS_CACHE = {}
self._pattern_cache = {} self._pattern_cache = {}
def clear_group_dict_cache(self):
''' called exclusively by the add_host and group_by plugins '''
self._group_dict_cache = {}
def groups_for_host(self, host): def groups_for_host(self, host):
if host in self._hosts_cache: if host in self._hosts_cache:
return self._hosts_cache[host].get_groups() return self._hosts_cache[host].get_groups()
@ -568,6 +574,20 @@ class Inventory(object):
return vars return vars
def get_group_dict(self):
"""
In get_vars() we merge a 'magic' dictionary 'groups' with group name
keys and hostname list values into every host variable set.
Cache the creation of this structure here
"""
if not self._group_dict_cache:
for (group_name, group) in iteritems(self.groups):
self._group_dict_cache[group_name] = [h.name for h in group.get_hosts()]
return self._group_dict_cache
def get_vars(self, hostname, update_cached=False, vault_password=None): def get_vars(self, hostname, update_cached=False, vault_password=None):
host = self.get_host(hostname) host = self.get_host(hostname)
@ -829,6 +849,7 @@ class Inventory(object):
def refresh_inventory(self): def refresh_inventory(self):
self.clear_pattern_cache() self.clear_pattern_cache()
self.clear_group_dict_cache()
self._hosts_cache = {} self._hosts_cache = {}
self._vars_per_host = {} self._vars_per_host = {}

View file

@ -475,6 +475,9 @@ class StrategyBase:
# patterns may have referenced the group # patterns may have referenced the group
self._inventory.clear_pattern_cache() self._inventory.clear_pattern_cache()
# clear cache of group dict, which is used in magic host variables
self._inventory.clear_group_dict_cache()
# also clear the hostvar cache entry for the given play, so that # also clear the hostvar cache entry for the given play, so that
# the new hosts are available if hostvars are referenced # the new hosts are available if hostvars are referenced
self._variable_manager.invalidate_hostvars_cache(play=iterator._play) self._variable_manager.invalidate_hostvars_cache(play=iterator._play)
@ -495,6 +498,9 @@ class StrategyBase:
group_name = result_item.get('add_group') group_name = result_item.get('add_group')
new_group = self._inventory.get_group(group_name) new_group = self._inventory.get_group(group_name)
if not new_group: if not new_group:
# clear cache of group dict, which is used in magic host variables
self._inventory.clear_group_dict_cache()
# create the new group and add it to inventory # create the new group and add it to inventory
new_group = Group(name=group_name) new_group = Group(name=group_name)
self._inventory.add_group(new_group) self._inventory.add_group(new_group)

View file

@ -393,10 +393,9 @@ class VariableManager:
if host: if host:
variables['group_names'] = sorted([group.name for group in host.get_groups() if group.name != 'all']) variables['group_names'] = sorted([group.name for group in host.get_groups() if group.name != 'all'])
if self._inventory is not None: if self._inventory:
variables['groups'] = dict() variables['groups'] = self._inventory.get_group_dict()
for (group_name, group) in iteritems(self._inventory.groups):
variables['groups'][group_name] = [h.name for h in group.get_hosts()]
if play: if play:
variables['role_names'] = [r._role_name for r in play.roles] variables['role_names'] = [r._role_name for r in play.roles]

View file

@ -237,6 +237,7 @@ class TestStrategyBase(unittest.TestCase):
mock_inventory.get_host.side_effect = _get_host mock_inventory.get_host.side_effect = _get_host
mock_inventory.get_group.side_effect = _get_group mock_inventory.get_group.side_effect = _get_group
mock_inventory.clear_pattern_cache.return_value = None mock_inventory.clear_pattern_cache.return_value = None
mock_inventory.clear_group_dict_cache.return_value = None
mock_inventory.get_host_vars.return_value = {} mock_inventory.get_host_vars.return_value = {}
mock_var_mgr = MagicMock() mock_var_mgr = MagicMock()