From adcf0af2a0a6099043014c6cc81e99b1280810be Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Wed, 2 Nov 2016 15:45:57 -0500 Subject: [PATCH] on host callback, upate dynamic inv only once * During a job template launch via the host callback launch feature, when the host launching the job template is not found; we try to launch associated dyn inv updates to pick up the host. Then the inv is searched again for the host. If the host is found, then the jt is launched. This fix carries forward the feature that prevents the dyn inv update with cache timeout of 0 from being re-launched with the new task manager. --- awx/main/scheduler/__init__.py | 5 +++++ awx/main/scheduler/partial.py | 14 +++++++++++++- awx/main/utils.py | 27 ++++++++++++++++++--------- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/awx/main/scheduler/__init__.py b/awx/main/scheduler/__init__.py index 7c7e9e5ab3..d1ee7912df 100644 --- a/awx/main/scheduler/__init__.py +++ b/awx/main/scheduler/__init__.py @@ -230,7 +230,12 @@ class TaskManager(): dependencies.append(project_task) # Inventory created 2 seconds behind job + inventory_sources_already_updated = task.get_inventory_sources_already_updated() + for inventory_source_task in self.graph.get_inventory_sources(task['inventory_id']): + if inventory_source_task['id'] in inventory_sources_already_updated: + print("Inventory already updated") + continue if self.graph.should_update_related_inventory_source(task, inventory_source_task['id']): inventory_task = self.create_inventory_update(task, inventory_source_task) dependencies.append(inventory_task) diff --git a/awx/main/scheduler/partial.py b/awx/main/scheduler/partial.py index c6b8a2e575..e8355cbcfc 100644 --- a/awx/main/scheduler/partial.py +++ b/awx/main/scheduler/partial.py @@ -1,5 +1,9 @@ +# Python +import json + # AWX +from awx.main.utils import decrypt_field_value from awx.main.models import ( Job, ProjectUpdate, @@ -61,7 +65,7 @@ class JobDict(PartialModelDict): 'id', 'status', 'job_template_id', 'inventory_id', 'project_id', 'launch_type', 'limit', 'allow_simultaneous', 'created', 'job_type', 'celery_task_id', 'project__scm_update_on_launch', - 'forks', + 'forks', 'start_args', ) model = Job @@ -71,6 +75,14 @@ class JobDict(PartialModelDict): def task_impact(self): return (5 if self.data['forks'] == 0 else self.data['forks']) * 10 + def get_inventory_sources_already_updated(self): + try: + start_args = json.loads(decrypt_field_value(self.data['id'], 'start_args', self.data['start_args'])) + except Exception: + return [] + start_args = start_args or {} + return start_args.get('inventory_sources_already_updated', []) + class ProjectUpdateDict(PartialModelDict): FIELDS = ( 'id', 'status', 'project_id', 'created', 'celery_task_id', diff --git a/awx/main/utils.py b/awx/main/utils.py index 58123972b7..9da2e76591 100644 --- a/awx/main/utils.py +++ b/awx/main/utils.py @@ -155,17 +155,20 @@ def get_awx_version(): return __version__ -def get_encryption_key(instance, field_name): +def get_encryption_key_for_pk(pk, field_name): ''' Generate key for encrypted password based on instance pk and field name. ''' from django.conf import settings h = hashlib.sha1() h.update(settings.SECRET_KEY) - h.update(str(instance.pk)) + h.update(str(pk)) h.update(field_name) return h.digest()[:16] +def get_encryption_key(instance, field_name): + return get_encryption_key_for_pk(instance.pk, field_name) + def encrypt_field(instance, field_name, ask=False, subfield=None): ''' Return content of the given instance and field name encrypted. @@ -184,6 +187,14 @@ def encrypt_field(instance, field_name, ask=False, subfield=None): b64data = base64.b64encode(encrypted) return '$encrypted$%s$%s' % ('AES', b64data) +def decrypt_value(encryption_key, value): + algo, b64data = value[len('$encrypted$'):].split('$', 1) + if algo != 'AES': + raise ValueError('unsupported algorithm: %s' % algo) + encrypted = base64.b64decode(b64data) + cipher = AES.new(encryption_key, AES.MODE_ECB) + value = cipher.decrypt(encrypted) + return value.rstrip('\x00') def decrypt_field(instance, field_name, subfield=None): ''' @@ -194,15 +205,13 @@ def decrypt_field(instance, field_name, subfield=None): value = value[subfield] if not value or not value.startswith('$encrypted$'): return value - algo, b64data = value[len('$encrypted$'):].split('$', 1) - if algo != 'AES': - raise ValueError(_('unsupported algorithm: %s') % algo) - encrypted = base64.b64decode(b64data) key = get_encryption_key(instance, field_name) - cipher = AES.new(key, AES.MODE_ECB) - value = cipher.decrypt(encrypted) - return value.rstrip('\x00') + return decrypt_value(key, value) + +def decrypt_field_value(pk, field_name, value): + key = get_encryption_key_for_pk(pk, field_name) + return decrypt_value(key, value) def update_scm_url(scm_type, url, username=True, password=True, check_special_cases=True, scp_format=False):