0 ?÷# (c) 2012-2014, Michael DeHaan # # This file is part of Ansible # # Ansible is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ansible is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ansible. If not, see . ############################################# from __future__ import (absolute_import, division, print_function) __metaclass__ = type import fnmatch import os import sys import re import itertools import traceback from operator import attrgetter from random import shuffle from ansible import constants as C from ansible.errors import AnsibleError, AnsibleOptionsError, AnsibleParserError from ansible.inventory.data import InventoryData from ansible.module_utils.six import string_types from ansible.module_utils._text import to_bytes, to_text from ansible.parsing.utils.addresses import parse_address from ansible.plugins.loader import inventory_loader from ansible.utils.helpers import deduplicate_list from ansible.utils.path import unfrackpath from ansible.utils.display import Display from ansible.utils.vars import combine_vars from ansible.vars.plugins import get_vars_from_inventory_sources display = Display() IGNORED_ALWAYS = [br"^\.", b"^host_vars$", b"^group_vars$", b"^vars_plugins$"] IGNORED_PATTERNS = [to_bytes(x) for x in C.INVENTORY_IGNORE_PATTERNS] 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 ''' # FIXME: this goes away if we apply patterns incrementally or by groups pattern_regular = [] pattern_intersection = [] pattern_exclude = [] for p in patterns: if not p: continue if p[0] == "!": pattern_exclude.append(p) elif p[0] == "&": pattern_intersection.append(p) else: pattern_regular.append(p) # if no regular pattern was given, hence only exclude and/or intersection # make that magically work if pattern_regular == []: pattern_regular = ['all'] # when applying the host selectors, run those without the "&" or "!" # first, then the &s, then the !s. return pattern_regular + pattern_intersection + pattern_exclude def split_host_pattern(pattern): """ Takes a string containing host patterns separated by commas (or a list thereof) and returns a list of single patterns (which may not contain commas). Whitespace is ignored. Also accepts ':' as a separator for backwards compatibility, but it is not recommended due to the conflict with IPv6 addresses and host ranges. Example: 'a,b[1], c[2:3] , d' -> ['a', 'b[1]', 'c[2:3]', 'd'] """ if isinstance(pattern, list): results = (split_host_pattern(p) for p in pattern) # flatten the results return list(itertools.chain.from_iterable(results)) elif not isinstance(pattern, string_types): pattern = to_text(pattern, errors='surrogate_or_strict') # If it's got commas in it, we'll treat it as a straightforward # comma-separated list of patterns. if u',