diff options
author | Matt Martz <matt@sivel.net> | 2019-07-22 14:25:20 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-22 14:25:20 -0500 |
commit | 284dafe476870a924e21c7d060f5abf5742ebaa9 (patch) | |
tree | 3db1457c613676963f478fddfadcdf2538484417 /lib/ansible/inventory | |
parent | 6adf0c581ee005f514ea68e80c0f91e97e63dcea (diff) | |
download | ansible-284dafe476870a924e21c7d060f5abf5742ebaa9.tar.gz |
Perfy McPerferton (#58400)
* InventoryManager start of perf improvements
* 0 not 1
* More startswith to [0] improvements
* Remove unused var
* The hash doesn't need to be a string, start as a list, make it into a tuple
* set actually appears faster than frozenset, and these don't need to be frozen
* Cache hosts lists, to avoid extra get_hosts calls, pass to get_vars too
* negligible perf improvement, it could help with memory later
* Try the fast way, fallback to the safe way
* Revert to previous logic, linting fix
* Extend pre-caching to free
* Address test failures
* Hosts are strings
* Fix unit test
* host is a string
* update test assumption
* drop SharedPluginLoaderObj, pre-create a set, instead of 2 comparisons in the list comprehension
* Dedupe code
* Change to _hosts and _hosts_all in get_vars
* Add backwards compat for strategies that don't do set host caches
* Add deprecation message to SharedPluginLoaderObj
* Remove unused SharedPluginLoaderObj import
* Update docs/comments
* Remove debugging
* Indicate what patterh_hash is
* That won't work
* Re-fix tests
* Update _set_hosts_cache to accept the play directly, use without refresh in get_hosts_remaining and get_failed_hosts for backwards compat
* Rename variable to avoid confusion
* On add_host only manipulate _hosts_cache_all
* Add warning docs around _hosts and _hosts_all args
Diffstat (limited to 'lib/ansible/inventory')
-rw-r--r-- | lib/ansible/inventory/manager.py | 87 |
1 files changed, 47 insertions, 40 deletions
diff --git a/lib/ansible/inventory/manager.py b/lib/ansible/inventory/manager.py index 32ec5ff3ea..7c328668d1 100644 --- a/lib/ansible/inventory/manager.py +++ b/lib/ansible/inventory/manager.py @@ -48,6 +48,18 @@ IGNORED_EXTS = [b'%s$' % to_bytes(re.escape(x)) for x in C.INVENTORY_IGNORE_EXTS IGNORED = re.compile(b'|'.join(IGNORED_ALWAYS + IGNORED_PATTERNS + IGNORED_EXTS)) +PATTERN_WITH_SUBSCRIPT = re.compile( + r'''^ + (.+) # A pattern expression ending with... + \[(?: # A [subscript] expression comprising: + (-?[0-9]+)| # A single positive or negative number + ([0-9]+)([:-]) # Or an x:y or x: range. + ([0-9]*) + )\] + $ + ''', re.X +) + def order_patterns(patterns): ''' takes a list of patterns and reorders them by modifier to apply them consistently ''' @@ -57,9 +69,9 @@ def order_patterns(patterns): pattern_intersection = [] pattern_exclude = [] for p in patterns: - if p.startswith("!"): + if p[0] == "!": pattern_exclude.append(p) - elif p.startswith("&"): + elif p[0] == "&": pattern_intersection.append(p) elif p: pattern_regular.append(p) @@ -316,7 +328,7 @@ class InventoryManager(object): def _match_list(self, items, pattern_str): # compile patterns try: - if not pattern_str.startswith('~'): + if not pattern_str[0] == '~': pattern = re.compile(fnmatch.translate(pattern_str)) else: pattern = re.compile(pattern_str[1:]) @@ -341,41 +353,45 @@ class InventoryManager(object): # Check if pattern already computed if isinstance(pattern, list): - pattern_hash = u":".join(pattern) + pattern_list = pattern[:] else: - pattern_hash = pattern + pattern_list = [pattern] - if pattern_hash: + if pattern_list: if not ignore_limits and self._subset: - pattern_hash += u":%s" % to_text(self._subset, errors='surrogate_or_strict') + pattern_list.extend(self._subset) if not ignore_restrictions and self._restriction: - pattern_hash += u":%s" % to_text(self._restriction, errors='surrogate_or_strict') + pattern_list.extend(self._restriction) + + # This is only used as a hash key in the self._hosts_patterns_cache dict + # a tuple is faster than stringifying + pattern_hash = tuple(pattern_list) if pattern_hash not in self._hosts_patterns_cache: patterns = split_host_pattern(pattern) - hosts = self._evaluate_patterns(patterns) + hosts[:] = self._evaluate_patterns(patterns) # mainly useful for hostvars[host] access if not ignore_limits and self._subset: # exclude hosts not in a subset, if defined - subset_uuids = [s._uuid for s in self._evaluate_patterns(self._subset)] - hosts = [h for h in hosts if h._uuid in subset_uuids] + subset_uuids = set(s._uuid for s in self._evaluate_patterns(self._subset)) + hosts[:] = [h for h in hosts if h._uuid in subset_uuids] if not ignore_restrictions and self._restriction: # exclude hosts mentioned in any restriction (ex: failed hosts) - hosts = [h for h in hosts if h.name in self._restriction] + hosts[:] = [h for h in hosts if h.name in self._restriction] self._hosts_patterns_cache[pattern_hash] = deduplicate_list(hosts) # sort hosts list if needed (should only happen when called from strategy) if order in ['sorted', 'reverse_sorted']: - hosts = sorted(self._hosts_patterns_cache[pattern_hash][:], key=attrgetter('name'), reverse=(order == 'reverse_sorted')) + hosts[:] = sorted(self._hosts_patterns_cache[pattern_hash][:], key=attrgetter('name'), reverse=(order == 'reverse_sorted')) elif order == 'reverse_inventory': - hosts = self._hosts_patterns_cache[pattern_hash][::-1] + hosts[:] = self._hosts_patterns_cache[pattern_hash][::-1] else: - hosts = self._hosts_patterns_cache[pattern_hash][:] + hosts[:] = self._hosts_patterns_cache[pattern_hash][:] if order == 'shuffle': shuffle(hosts) elif order not in [None, 'inventory']: @@ -398,12 +414,15 @@ class InventoryManager(object): hosts.append(self._inventory.get_host(p)) else: that = self._match_one_pattern(p) - if p.startswith("!"): - hosts = [h for h in hosts if h not in frozenset(that)] - elif p.startswith("&"): - hosts = [h for h in hosts if h in frozenset(that)] + if p[0] == "!": + that = set(that) + hosts = [h for h in hosts if h not in that] + elif p[0] == "&": + that = set(that) + hosts = [h for h in hosts if h in that] else: - hosts.extend([h for h in that if h.name not in frozenset([y.name for y in hosts])]) + existing_hosts = set(y.name for y in hosts) + hosts.extend([h for h in that if h.name not in existing_hosts]) return hosts def _match_one_pattern(self, pattern): @@ -444,7 +463,7 @@ class InventoryManager(object): Duplicate matches are always eliminated from the results. """ - if pattern.startswith("&") or pattern.startswith("!"): + if pattern[0] in ("&", "!"): pattern = pattern[1:] if pattern not in self._pattern_cache: @@ -469,27 +488,15 @@ class InventoryManager(object): """ # Do not parse regexes for enumeration info - if pattern.startswith('~'): + if pattern[0] == '~': return (pattern, None) # We want a pattern followed by an integer or range subscript. # (We can't be more restrictive about the expression because the # fnmatch semantics permit [\[:\]] to occur.) - pattern_with_subscript = re.compile( - r'''^ - (.+) # A pattern expression ending with... - \[(?: # A [subscript] expression comprising: - (-?[0-9]+)| # A single positive or negative number - ([0-9]+)([:-]) # Or an x:y or x: range. - ([0-9]*) - )\] - $ - ''', re.X - ) - subscript = None - m = pattern_with_subscript.match(pattern) + m = PATTERN_WITH_SUBSCRIPT.match(pattern) if m: (pattern, idx, start, sep, end) = m.groups() if idx: @@ -535,7 +542,7 @@ class InventoryManager(object): results.extend(self._inventory.groups[groupname].get_hosts()) # check hosts if no groups matched or it is a regex/glob pattern - if not matching_groups or pattern.startswith('~') or any(special in pattern for special in ('.', '?', '*', '[')): + if not matching_groups or pattern[0] == '~' or any(special in pattern for special in ('.', '?', '*', '[')): # pattern might match host matching_hosts = self._match_list(self._inventory.hosts, pattern) if matching_hosts: @@ -585,7 +592,7 @@ class InventoryManager(object): return elif not isinstance(restriction, list): restriction = [restriction] - self._restriction = [h.name for h in restriction] + self._restriction = set(to_text(h.name) for h in restriction) def subset(self, subset_pattern): """ @@ -601,12 +608,12 @@ class InventoryManager(object): results = [] # allow Unix style @filename data for x in subset_patterns: - if x.startswith("@"): + if x[0] == "@": fd = open(x[1:]) - results.extend([l.strip() for l in fd.read().split("\n")]) + results.extend([to_text(l.strip()) for l in fd.read().split("\n")]) fd.close() else: - results.append(x) + results.append(to_text(x)) self._subset = results def remove_restriction(self): |