From f7bc8fb662c517b67b6e68482ed054188de48311 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Tue, 22 Jan 2019 17:35:09 -0500 Subject: [PATCH] record the virtualenv used when a job or inventory sync runs see: https://github.com/ansible/awx/issues/2264 --- awx/api/serializers.py | 7 ++++-- .../0056_v350_custom_venv_history.py | 25 +++++++++++++++++++ awx/main/models/inventory.py | 3 ++- awx/main/models/jobs.py | 2 +- awx/main/tasks.py | 5 ++++ 5 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 awx/main/migrations/0056_v350_custom_venv_history.py diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 1dcd614222..829b0fd2f1 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -2175,10 +2175,12 @@ class InventorySourceUpdateSerializer(InventorySourceSerializer): class InventoryUpdateSerializer(UnifiedJobSerializer, InventorySourceOptionsSerializer): + custom_virtualenv = serializers.ReadOnlyField() + class Meta: model = InventoryUpdate fields = ('*', 'inventory', 'inventory_source', 'license_error', 'source_project_update', - '-controller_node',) + 'custom_virtualenv', '-controller_node',) def get_related(self, obj): res = super(InventoryUpdateSerializer, self).get_related(obj) @@ -3293,10 +3295,11 @@ class JobDetailSerializer(JobSerializer): playbook_counts = serializers.SerializerMethodField( help_text=_('A count of all plays and tasks for the job run.'), ) + custom_virtualenv = serializers.ReadOnlyField() class Meta: model = Job - fields = ('*', 'host_status_counts', 'playbook_counts',) + fields = ('*', 'host_status_counts', 'playbook_counts', 'custom_virtualenv') def get_playbook_counts(self, obj): task_count = obj.job_events.filter(event='playbook_on_task_start').count() diff --git a/awx/main/migrations/0056_v350_custom_venv_history.py b/awx/main/migrations/0056_v350_custom_venv_history.py new file mode 100644 index 0000000000..299b19d6b4 --- /dev/null +++ b/awx/main/migrations/0056_v350_custom_venv_history.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.16 on 2019-01-22 22:20 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0055_v340_add_grafana_notification'), + ] + + operations = [ + migrations.AddField( + model_name='inventoryupdate', + name='custom_virtualenv', + field=models.CharField(blank=True, default=None, help_text='Local absolute file path containing a custom Python virtualenv to use', max_length=100, null=True), + ), + migrations.AddField( + model_name='job', + name='custom_virtualenv', + field=models.CharField(blank=True, default=None, help_text='Local absolute file path containing a custom Python virtualenv to use', max_length=100, null=True), + ), + ] diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index aef7fef93b..5ebbb98477 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -41,6 +41,7 @@ from awx.main.models.mixins import ( ResourceMixin, TaskManagerInventoryUpdateMixin, RelatedJobsMixin, + CustomVirtualEnvMixin, ) from awx.main.models.notifications import ( NotificationTemplate, @@ -1622,7 +1623,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions, RelatedJobsMix return InventoryUpdate.objects.filter(inventory_source=self) -class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin, TaskManagerInventoryUpdateMixin): +class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin, TaskManagerInventoryUpdateMixin, CustomVirtualEnvMixin): ''' Internal job for tracking inventory updates from external sources. ''' diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index 43f2b01ea2..5675d09645 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -490,7 +490,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour return UnifiedJob.objects.filter(unified_job_template=self) -class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskManagerJobMixin): +class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskManagerJobMixin, CustomVirtualEnvMixin): ''' A job applies a project (with playbook) to an inventory source with a given credential. It represents a single invocation of ansible-playbook with the diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 4a65016f1e..18ffbafc77 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -948,6 +948,11 @@ class BaseTask(object): if not os.path.exists(settings.AWX_PROOT_BASE_PATH): raise RuntimeError('AWX_PROOT_BASE_PATH=%s does not exist' % settings.AWX_PROOT_BASE_PATH) + + # store a record of the venv used at runtime + if hasattr(instance, 'custom_virtualenv'): + self.update_model(pk, custom_virtualenv=getattr(instance, 'ansible_virtualenv_path', settings.ANSIBLE_VENV_PATH)) + # Fetch ansible version once here to support version-dependent features. kwargs['ansible_version'] = get_ansible_version() kwargs['private_data_dir'] = self.build_private_data_dir(instance, **kwargs)