# Copyright (c) 2017 Ansible by Red Hat # All Rights Reserved. # Python import codecs import re import os import logging from itertools import islice from configparser import ConfigParser # Django from django.utils.encoding import smart_str logger = logging.getLogger('awx.main.utils.ansible') __all__ = ['skip_directory', 'could_be_playbook', 'could_be_inventory'] valid_playbook_re = re.compile(r'^\s*?-?\s*?(?:hosts|include|import_playbook):\s*?.*?$') valid_inventory_re = re.compile(r'^[a-zA-Z0-9_.=\[\]]') def skip_directory(relative_directory_path): path_elements = relative_directory_path.split(os.sep) # Exclude files in a roles subdirectory. if 'roles' in path_elements: return True # Filter files in a tasks subdirectory. if 'tasks' in path_elements: return True for element in path_elements: # Do not include dot files or dirs if element.startswith('.'): return True # Exclude anything inside of group or host vars directories if 'group_vars' in path_elements or 'host_vars' in path_elements: return True return False def could_be_playbook(project_path, dir_path, filename): if os.path.splitext(filename)[-1] not in ['.yml', '.yaml']: return None playbook_path = os.path.join(dir_path, filename) # Filter files that do not have either hosts or top-level # includes. Use regex to allow files with invalid YAML to # show up. matched = False try: for n, line in enumerate(codecs.open( playbook_path, 'r', encoding='utf-8', errors='ignore' )): if valid_playbook_re.match(line): matched = True break # Any YAML file can also be encrypted with vault; # allow these to be used as the main playbook. elif n == 0 and line.startswith('$ANSIBLE_VAULT;'): matched = True break except IOError: return None if not matched: return None return os.path.relpath(playbook_path, smart_str(project_path)) def could_be_inventory(project_path, dir_path, filename): # Decisions based exclusively on filename inventory_path = os.path.join(dir_path, filename) inventory_rel_path = os.path.relpath(inventory_path, smart_str(project_path)) suspected_ext = os.path.splitext(filename)[-1] if filename in ['inventory', 'hosts']: # Users commonly name their inventory files these names return inventory_rel_path elif suspected_ext == '.ini' or os.access(inventory_path, os.X_OK): # Files with any of these extensions are always included return inventory_rel_path elif '.' in suspected_ext: # If not using those extensions, inventory must have _no_ extension return None # Filter files that do not use a character set consistent with # Ansible inventory mainly try: # only read through first 10 lines for performance with codecs.open( inventory_path, 'r', encoding='utf-8', errors='ignore' ) as inv_file: for line in islice(inv_file, 10): if not valid_inventory_re.match(line): return None except IOError: return None return inventory_rel_path def read_ansible_config(project_path, variables_of_interest): fnames = ['/etc/ansible/ansible.cfg'] if project_path: fnames.append(os.path.join(project_path, 'ansible.cfg')) values = {} try: parser = ConfigParser() parser.read(fnames) if 'defaults' in parser: for var in variables_of_interest: if var in parser['defaults']: values[var] = parser['defaults'][var] except Exception: logger.exception('Failed to read ansible configuration(s) {}'.format(fnames)) return values