From 99288a5e184f01ec8c50f2ffee1394718f14c92f Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Sun, 20 Jan 2019 12:56:14 -0500 Subject: [PATCH] Use custom virtual environment in inventory updates --- .../management/commands/inventory_import.py | 29 ++++++++++++++----- awx/main/models/inventory.py | 8 +++++ awx/main/tasks.py | 6 +++- 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/awx/main/management/commands/inventory_import.py b/awx/main/management/commands/inventory_import.py index 62ff53ccce..9cb1edbc11 100644 --- a/awx/main/management/commands/inventory_import.py +++ b/awx/main/management/commands/inventory_import.py @@ -4,6 +4,7 @@ # Python import json import logging +import fnmatch import os import re import subprocess @@ -72,25 +73,36 @@ class AnsibleInventoryLoader(object): /usr/bin/ansible/ansible-inventory -i hosts --list ''' - def __init__(self, source, is_custom=False): + def __init__(self, source, is_custom=False, venv_path=None): self.source = source self.source_dir = functioning_dir(self.source) self.is_custom = is_custom self.tmp_private_dir = None self.method = 'ansible-inventory' + if venv_path: + self.venv_path = venv_path + else: + self.venv_path = settings.ANSIBLE_VENV_PATH def build_env(self): env = dict(os.environ.items()) - env['VIRTUAL_ENV'] = settings.ANSIBLE_VENV_PATH - env['PATH'] = os.path.join(settings.ANSIBLE_VENV_PATH, "bin") + ":" + env['PATH'] + env['VIRTUAL_ENV'] = self.venv_path + env['PATH'] = os.path.join(self.venv_path, "bin") + ":" + env['PATH'] # Set configuration items that should always be used for updates for key, value in STANDARD_INVENTORY_UPDATE_ENV.items(): if key not in env: env[key] = value - venv_libdir = os.path.join(settings.ANSIBLE_VENV_PATH, "lib") + venv_libdir = os.path.join(self.venv_path, "lib") env.pop('PYTHONPATH', None) # default to none if no python_ver matches - if os.path.isdir(os.path.join(venv_libdir, "python2.7")): - env['PYTHONPATH'] = os.path.join(venv_libdir, "python2.7", "site-packages") + ":" + for version in os.listdir(venv_libdir): + if fnmatch.fnmatch(version, 'python[23].*'): + if os.path.isdir(os.path.join(venv_libdir, version)): + env['PYTHONPATH'] = os.path.join(venv_libdir, version, "site-packages") + ":" + break + # For internal inventory updates, these are not reported in the job_env API + logger.info('Using VIRTUAL_ENV: {}'.format(env['VIRTUAL_ENV'])) + logger.info('Using PATH: {}'.format(env['PATH'])) + logger.info('Using PYTHONPATH: {}'.format(env.get('PYTHONPATH', None))) return env def get_base_args(self): @@ -179,6 +191,8 @@ class Command(BaseCommand): parser.add_argument('--inventory-id', dest='inventory_id', type=int, default=None, metavar='i', help='id of inventory to sync') + parser.add_argument('--venv', dest='venv', type=str, default=None, + help='absolute path to the AWX custom virtualenv to use') parser.add_argument('--overwrite', dest='overwrite', action='store_true', default=False, help='overwrite the destination hosts and groups') parser.add_argument('--overwrite-vars', dest='overwrite_vars', @@ -850,6 +864,7 @@ class Command(BaseCommand): self.set_logging_level() self.inventory_name = options.get('inventory_name', None) self.inventory_id = options.get('inventory_id', None) + venv_path = options.get('venv', None) self.overwrite = bool(options.get('overwrite', False)) self.overwrite_vars = bool(options.get('overwrite_vars', False)) self.keep_vars = bool(options.get('keep_vars', False)) @@ -912,7 +927,7 @@ class Command(BaseCommand): source = self.get_source_absolute_path(self.source) - data = AnsibleInventoryLoader(source=source, is_custom=self.is_custom).load() + data = AnsibleInventoryLoader(source=source, is_custom=self.is_custom, venv_path=venv_path).load() logger.debug('Finished loading from source: %s', source) logger.info('Processing JSON output...') diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index 22fa45b8b8..aef7fef93b 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -1741,6 +1741,14 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin, return self.global_instance_groups return selected_groups + @property + def ansible_virtualenv_path(self): + if self.inventory_source and self.inventory_source.inventory: + organization = self.inventory_source.inventory.organization + if organization and organization.custom_virtualenv: + return organization.custom_virtualenv + return settings.ANSIBLE_VENV_PATH + def cancel(self, job_explanation=None, is_chain=False): res = super(InventoryUpdate, self).cancel(job_explanation=job_explanation, is_chain=is_chain) if res: diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 61955b96ec..07a87baa0c 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -2114,8 +2114,12 @@ class RunInventoryUpdate(BaseTask): args.append('--overwrite') if inventory_update.overwrite_vars: args.append('--overwrite-vars') - src = inventory_update.source + # Declare the virtualenv the management command should activate + # as it calls ansible-inventory + args.extend(['--venv', inventory_update.ansible_virtualenv_path]) + + src = inventory_update.source # Add several options to the shell arguments based on the # inventory-source-specific setting in the AWX configuration. # These settings are "per-source"; it's entirely possible that