From 692007072d8d528dc67a0db0dd2ce03889f573dc Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Thu, 28 Sep 2017 19:26:04 -0400 Subject: [PATCH 01/14] Set capacity to zero if the isolated node has an old version --- awx/main/expect/isolated_manager.py | 26 ++++++++++++++++++++------ awx/main/tasks.py | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/awx/main/expect/isolated_manager.py b/awx/main/expect/isolated_manager.py index 48fd0e7b5c..1989f785f7 100644 --- a/awx/main/expect/isolated_manager.py +++ b/awx/main/expect/isolated_manager.py @@ -9,6 +9,7 @@ import stat import tempfile import time import logging +from distutils.version import LooseVersion as Version from django.conf import settings @@ -370,7 +371,24 @@ class IsolatedManager(object): logger.warning('Isolated job {} cleanup error, output:\n{}'.format(self.instance.id, output)) @classmethod - def health_check(cls, instance_qs): + def update_capacity(cls, instance, task_result, awx_application_version): + instance.version = task_result['version'] + + isolated_version = instance.version.split("-", 1)[0] + cluster_version = awx_application_version.split("-", 1)[0] + + if Version(cluster_version) > Version(isolated_version): + err_template = "Isolated instance {} reports version {}, cluster node is at {}, setting capacity to zero." + logger.error(err_template.format(instance.hostname, instance.version, awx_application_version)) + instance.capacity = 0 + else: + if instance.capacity == 0 and task_result['capacity']: + logger.warning('Isolated instance {} has re-joined.'.format(instance.hostname)) + instance.capacity = int(task_result['capacity']) + instance.save(update_fields=['capacity', 'version', 'modified']) + + @classmethod + def health_check(cls, instance_qs, awx_application_version): ''' :param instance_qs: List of Django objects representing the isolated instances to manage @@ -412,11 +430,7 @@ class IsolatedManager(object): except (KeyError, IndexError): task_result = {} if 'capacity' in task_result: - instance.version = task_result['version'] - if instance.capacity == 0 and task_result['capacity']: - logger.warning('Isolated instance {} has re-joined.'.format(instance.hostname)) - instance.capacity = int(task_result['capacity']) - instance.save(update_fields=['capacity', 'version', 'modified']) + cls.update_capacity(instance, awx_application_version) elif instance.capacity == 0: logger.debug('Isolated instance {} previously marked as lost, could not re-join.'.format( instance.hostname)) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index a1ca356210..fb83095580 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -280,7 +280,7 @@ def awx_isolated_heartbeat(self): # Slow pass looping over isolated IGs and their isolated instances if len(isolated_instance_qs) > 0: logger.debug("Managing isolated instances {}.".format(','.join([inst.hostname for inst in isolated_instance_qs]))) - isolated_manager.IsolatedManager.health_check(isolated_instance_qs) + isolated_manager.IsolatedManager.health_check(isolated_instance_qs, awx_application_version) @task(bind=True, queue='tower', base=LogErrorsTask) From 96fd07d0f347aa02f5b8a5f753264db481d87fd8 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Thu, 28 Sep 2017 19:26:30 -0400 Subject: [PATCH 02/14] Assert isolated nodes have capacity set to 0 and restored based on version --- awx/main/tests/functional/test_tasks.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/awx/main/tests/functional/test_tasks.py b/awx/main/tests/functional/test_tasks.py index 24451abbdd..2cfdcae849 100644 --- a/awx/main/tests/functional/test_tasks.py +++ b/awx/main/tests/functional/test_tasks.py @@ -117,6 +117,28 @@ class TestIsolatedManagementTask: inst.save() return inst + @pytest.fixture + def old_version(self, control_group): + ig = InstanceGroup.objects.create(name='thepentagon', controller=control_group) + inst = ig.instances.create(hostname='isolated-old', capacity=103) + inst.save() + return inst + + def test_old_version(self, control_instance, old_version): + update_capacity = isolated_manager.IsolatedManager.update_capacity + + assert old_version.capacity == 103 + with mock.patch('awx.main.tasks.settings', MockSettings()): + # Isolated node is reporting an older version than the cluster + # instance that issued the health check, set capacity to zero. + update_capacity(old_version, {'version': '1.0.0'}, '3.0.0') + assert old_version.capacity == 0 + + # Upgrade was completed, health check playbook now reports matching + # version, make sure capacity is set. + update_capacity(old_version, {'version': '5.0.0-things', 'capacity':103}, '5.0.0-stuff') + assert old_version.capacity == 103 + def test_takes_action(self, control_instance, needs_updating): original_isolated_instance = needs_updating.instances.all().first() with mock.patch('awx.main.tasks.settings', MockSettings()): From cad8710ac74998eb83bf19d1ca0892276c6fa30a Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Tue, 3 Oct 2017 10:52:26 -0400 Subject: [PATCH 03/14] fix scan job migration unicode issue --- awx/main/migrations/_scan_jobs.py | 2 +- .../tests/functional/test_scan_jobs_migration.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/awx/main/migrations/_scan_jobs.py b/awx/main/migrations/_scan_jobs.py index 4dfc7cf972..ffeb8007e3 100644 --- a/awx/main/migrations/_scan_jobs.py +++ b/awx/main/migrations/_scan_jobs.py @@ -12,7 +12,7 @@ logger = logging.getLogger('awx.main.migrations') def _create_fact_scan_project(ContentType, Project, org): ct = ContentType.objects.get_for_model(Project) - name = "Tower Fact Scan - {}".format(org.name if org else "No Organization") + name = u"Tower Fact Scan - {}".format(org.name if org else "No Organization") proj = Project(name=name, scm_url='https://github.com/ansible/awx-facts-playbooks', scm_type='git', diff --git a/awx/main/tests/functional/test_scan_jobs_migration.py b/awx/main/tests/functional/test_scan_jobs_migration.py index f7bc08364a..8582bd10c6 100644 --- a/awx/main/tests/functional/test_scan_jobs_migration.py +++ b/awx/main/tests/functional/test_scan_jobs_migration.py @@ -19,46 +19,46 @@ from awx.main.migrations._scan_jobs import _migrate_scan_job_templates @pytest.fixture def organizations(): - return [Organization.objects.create(name="org-{}".format(x)) for x in range(3)] + return [Organization.objects.create(name=u"org-\xe9-{}".format(x)) for x in range(3)] @pytest.fixture def inventories(organizations): - return [Inventory.objects.create(name="inv-{}".format(x), + return [Inventory.objects.create(name=u"inv-\xe9-{}".format(x), organization=organizations[x]) for x in range(3)] @pytest.fixture def job_templates_scan(inventories): - return [JobTemplate.objects.create(name="jt-scan-{}".format(x), + return [JobTemplate.objects.create(name=u"jt-\xe9-scan-{}".format(x), job_type=PERM_INVENTORY_SCAN, inventory=inventories[x]) for x in range(3)] @pytest.fixture def job_templates_deploy(inventories): - return [JobTemplate.objects.create(name="jt-deploy-{}".format(x), + return [JobTemplate.objects.create(name=u"jt-\xe9-deploy-{}".format(x), job_type=PERM_INVENTORY_DEPLOY, inventory=inventories[x]) for x in range(3)] @pytest.fixture def project_custom(organizations): - return Project.objects.create(name="proj-scan_custom", + return Project.objects.create(name=u"proj-\xe9-scan_custom", scm_url='https://giggity.com', organization=organizations[0]) @pytest.fixture def job_templates_custom_scan_project(project_custom): - return [JobTemplate.objects.create(name="jt-scan-custom-{}".format(x), + return [JobTemplate.objects.create(name=u"jt-\xe9-scan-custom-{}".format(x), project=project_custom, job_type=PERM_INVENTORY_SCAN) for x in range(3)] @pytest.fixture def job_template_scan_no_org(): - return JobTemplate.objects.create(name="jt-scan-no-org", + return JobTemplate.objects.create(name=u"jt-\xe9-scan-no-org", job_type=PERM_INVENTORY_SCAN) From 4c5ec2fb3a243f2f2a7d0266e7c3b7a89739c236 Mon Sep 17 00:00:00 2001 From: Ryan Petrello Date: Tue, 3 Oct 2017 15:45:11 -0400 Subject: [PATCH 04/14] work around an ansible 2.4 inventory caching bug see: https://github.com/ansible/awx/issues/246 --- awx/main/tasks.py | 7 ++++++- awx/main/tests/unit/models/test_survey_models.py | 7 ++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index fb83095580..02bea178f5 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -702,7 +702,12 @@ class BaseTask(LogErrorsTask): os.chmod(path, stat.S_IRUSR | stat.S_IXUSR) return path else: - return plugin + # work around an inventory caching bug in Ansible 2.4.0 + # see: https://github.com/ansible/ansible/pull/30817 + # see: https://github.com/ansible/awx/issues/246 + inventory_script = tempfile.mktemp(suffix='.awxrest.py', dir=kwargs['private_data_dir']) + shutil.copy(plugin, inventory_script) + return inventory_script def build_args(self, instance, **kwargs): raise NotImplementedError diff --git a/awx/main/tests/unit/models/test_survey_models.py b/awx/main/tests/unit/models/test_survey_models.py index 967ac406c1..ba05940abc 100644 --- a/awx/main/tests/unit/models/test_survey_models.py +++ b/awx/main/tests/unit/models/test_survey_models.py @@ -1,3 +1,4 @@ +import tempfile import pytest import json @@ -62,7 +63,7 @@ def test_survey_passwords_not_in_extra_vars(): def test_job_safe_args_redacted_passwords(job): """Verify that safe_args hides passwords in the job extra_vars""" - kwargs = {'ansible_version': '2.1'} + kwargs = {'ansible_version': '2.1', 'private_data_dir': tempfile.mkdtemp()} run_job = RunJob() safe_args = run_job.build_safe_args(job, **kwargs) ev_index = safe_args.index('-e') + 1 @@ -70,8 +71,8 @@ def test_job_safe_args_redacted_passwords(job): assert extra_vars['secret_key'] == '$encrypted$' -def test_job_args_unredacted_passwords(job): - kwargs = {'ansible_version': '2.1'} +def test_job_args_unredacted_passwords(job, tmpdir_factory): + kwargs = {'ansible_version': '2.1', 'private_data_dir': tempfile.mkdtemp()} run_job = RunJob() args = run_job.build_args(job, **kwargs) ev_index = args.index('-e') + 1 From 02e3f45422611a44c12482a7d60295fc429f14b0 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Wed, 4 Oct 2017 17:55:36 -0400 Subject: [PATCH 05/14] do not allow ansible connection type of local for ad_hoc --- awx/main/tasks.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 02bea178f5..2e116dff99 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -2139,6 +2139,9 @@ class RunAdHocCommand(BaseTask): args.append('-%s' % ('v' * min(5, ad_hoc_command.verbosity))) if ad_hoc_command.extra_vars_dict: + if ad_hoc_command.extra_vars_dict.get('ansible_connection') == 'local': + raise ValueError(_("unable to use the `local` connection plugin with ad hoc commands")) + args.extend(['-e', json.dumps(ad_hoc_command.extra_vars_dict)]) args.extend(['-m', ad_hoc_command.module_name]) From eacbeef6601bcebf9f65628698d6a5aa13d4d510 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 5 Oct 2017 12:14:05 -0400 Subject: [PATCH 06/14] Validate against ansible variables on ad hoc launch Share code between this check for ad hoc and JT callback --- awx/api/serializers.py | 10 +++++++++- awx/api/views.py | 5 +++-- awx/main/tasks.py | 9 ++++++--- awx/main/tests/unit/utils/test_common.py | 10 ++++++++++ awx/main/utils/common.py | 19 ++++++++++++------- 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 873f822171..98090bb611 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -45,7 +45,7 @@ from awx.main.fields import ImplicitRoleField from awx.main.utils import ( get_type_for_model, get_model_for_type, timestamp_apiformat, camelcase_to_underscore, getattrd, parse_yaml_or_json, - has_model_field_prefetched) + has_model_field_prefetched, extract_ansible_vars) from awx.main.utils.filters import SmartFilter from awx.main.validators import vars_validate_or_raise @@ -2759,6 +2759,14 @@ class AdHocCommandSerializer(UnifiedJobSerializer): ret['name'] = obj.module_name return ret + def validate_extra_vars(self, value): + redacted_extra_vars, removed_vars = extract_ansible_vars(value) + if removed_vars: + raise serializers.ValidationError(_( + "Variables {} are prohibited from use in ad hoc commands." + ).format(",".join(removed_vars))) + return vars_validate_or_raise(value) + class AdHocCommandCancelSerializer(AdHocCommandSerializer): diff --git a/awx/api/views.py b/awx/api/views.py index e07b7f8b96..ba7fc709e1 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -69,7 +69,7 @@ from awx.conf.license import get_license, feature_enabled, feature_exists, Licen from awx.main.models import * # noqa from awx.main.utils import * # noqa from awx.main.utils import ( - callback_filter_out_ansible_extra_vars, + extract_ansible_vars, decrypt_field, ) from awx.main.utils.filters import SmartFilter @@ -3160,7 +3160,8 @@ class JobTemplateCallback(GenericAPIView): # Everything is fine; actually create the job. kv = {"limit": limit, "launch_type": 'callback'} if extra_vars is not None and job_template.ask_variables_on_launch: - kv['extra_vars'] = callback_filter_out_ansible_extra_vars(extra_vars) + extra_vars_redacted, removed = extract_ansible_vars(extra_vars) + kv['extra_vars'] = extra_vars_redacted with transaction.atomic(): job = job_template.create_job(**kv) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 2e116dff99..3bc4bc9c45 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -56,7 +56,7 @@ from awx.main.utils import (get_ansible_version, get_ssh_version, decrypt_field, check_proot_installed, build_proot_temp_dir, get_licenser, wrap_args_with_proot, get_system_task_capacity, OutputEventFilter, parse_yaml_or_json, ignore_inventory_computed_fields, ignore_inventory_group_removal, - get_type_for_model) + get_type_for_model, extract_ansible_vars) from awx.main.utils.reload import restart_local_services, stop_local_services from awx.main.utils.handlers import configure_external_logger from awx.main.consumers import emit_channel_notification @@ -2139,8 +2139,11 @@ class RunAdHocCommand(BaseTask): args.append('-%s' % ('v' * min(5, ad_hoc_command.verbosity))) if ad_hoc_command.extra_vars_dict: - if ad_hoc_command.extra_vars_dict.get('ansible_connection') == 'local': - raise ValueError(_("unable to use the `local` connection plugin with ad hoc commands")) + redacted_extra_vars, removed_vars = extract_ansible_vars(ad_hoc_command.extra_vars_dict) + if removed_vars: + raise ValueError(_( + "unable to use {} variables with ad hoc commands" + ).format(",".format(removed_vars))) args.extend(['-e', json.dumps(ad_hoc_command.extra_vars_dict)]) diff --git a/awx/main/tests/unit/utils/test_common.py b/awx/main/tests/unit/utils/test_common.py index 41f9012040..44f960a673 100644 --- a/awx/main/tests/unit/utils/test_common.py +++ b/awx/main/tests/unit/utils/test_common.py @@ -5,6 +5,7 @@ import os import pytest from uuid import uuid4 +import json from django.core.cache import cache @@ -115,3 +116,12 @@ def test_memoize_parameter_error(): with pytest.raises(common.IllegalArgumentError): fn() + +def test_extract_ansible_vars(): + my_dict = { + "foobar": "baz", + "ansible_connetion_setting": "1928" + } + redacted, var_list = common.extract_ansible_vars(json.dumps(my_dict)) + assert var_list == set(['ansible_connetion_setting']) + assert redacted == {"foobar": "baz"} diff --git a/awx/main/utils/common.py b/awx/main/utils/common.py index dbbc589392..15d3388bdc 100644 --- a/awx/main/utils/common.py +++ b/awx/main/utils/common.py @@ -41,7 +41,7 @@ __all__ = ['get_object_or_400', 'get_object_or_403', 'camelcase_to_underscore', 'ignore_inventory_computed_fields', 'ignore_inventory_group_removal', '_inventory_updates', 'get_pk_from_dict', 'getattrd', 'NoDefaultProvided', 'get_current_apps', 'set_current_apps', 'OutputEventFilter', - 'callback_filter_out_ansible_extra_vars', 'get_search_fields', 'get_system_task_capacity', + 'extract_ansible_vars', 'get_search_fields', 'get_system_task_capacity', 'wrap_args_with_proot', 'build_proot_temp_dir', 'check_proot_installed', 'model_to_dict', 'model_instance_diff', 'timestamp_apiformat', 'parse_yaml_or_json', 'RequireDebugTrueOrTest', 'has_model_field_prefetched', 'set_environ', 'IllegalArgumentError',] @@ -877,13 +877,18 @@ class OutputEventFilter(object): self._current_event_data = None -def callback_filter_out_ansible_extra_vars(extra_vars): - extra_vars_redacted = {} +def is_ansible_variable(key): + return key.startswith('ansible_') + + +def extract_ansible_vars(extra_vars): extra_vars = parse_yaml_or_json(extra_vars) - for key, value in extra_vars.iteritems(): - if not key.startswith('ansible_'): - extra_vars_redacted[key] = value - return extra_vars_redacted + ansible_vars = set([]) + for key in extra_vars.keys(): + if is_ansible_variable(key): + extra_vars.pop(key) + ansible_vars.add(key) + return (extra_vars, ansible_vars) def get_search_fields(model): From 81c14ce9428accc05850bd4b3ed8c34394a91e93 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Thu, 5 Oct 2017 13:42:23 -0400 Subject: [PATCH 07/14] fix WARNING log when launching ad hoc command --- awx/main/models/unified_jobs.py | 22 +++++++--------------- awx/main/utils/common.py | 6 ++++-- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index e148d45862..91285ecc16 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -34,7 +34,7 @@ from awx.main.models.mixins import ResourceMixin, TaskManagerUnifiedJobMixin from awx.main.utils import ( decrypt_field, _inventory_updates, copy_model_by_class, copy_m2m_relationships, - get_type_for_model + get_type_for_model, parse_yaml_or_json ) from awx.main.redact import UriCleaner, REPLACE_STR from awx.main.consumers import emit_channel_notification @@ -878,21 +878,13 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique return [] def handle_extra_data(self, extra_data): - if hasattr(self, 'extra_vars'): - extra_vars = {} - if isinstance(extra_data, dict): - extra_vars = extra_data - elif extra_data is None: - return - else: - if extra_data == "": - return - try: - extra_vars = json.loads(extra_data) - except Exception as e: - logger.warn("Exception deserializing extra vars: " + str(e)) + if hasattr(self, 'extra_vars') and extra_data: + try: + extra_data_dict = parse_yaml_or_json(extra_data, silent_failure=False) + except Exception as e: + logger.warn("Exception deserializing extra vars: " + str(e)) evars = self.extra_vars_dict - evars.update(extra_vars) + evars.update(extra_data_dict) self.update_fields(extra_vars=json.dumps(evars)) @property diff --git a/awx/main/utils/common.py b/awx/main/utils/common.py index 15d3388bdc..22183c33dd 100644 --- a/awx/main/utils/common.py +++ b/awx/main/utils/common.py @@ -581,7 +581,7 @@ def cache_list_capabilities(page, prefetch_list, model, user): obj.capabilities_cache[display_method] = True -def parse_yaml_or_json(vars_str): +def parse_yaml_or_json(vars_str, silent_failure=True): ''' Attempt to parse a string with variables, and if attempt fails, return an empty dictionary. @@ -595,7 +595,9 @@ def parse_yaml_or_json(vars_str): vars_dict = yaml.safe_load(vars_str) assert isinstance(vars_dict, dict) except (yaml.YAMLError, TypeError, AttributeError, AssertionError): - vars_dict = {} + if silent_failure: + return {} + raise return vars_dict From 2818bb5833cefb4c6f33e1b809f748d3012dc1c9 Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 6 Oct 2017 00:23:12 -0400 Subject: [PATCH 08/14] fix missing parameter to update_capacity method --- awx/main/expect/isolated_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awx/main/expect/isolated_manager.py b/awx/main/expect/isolated_manager.py index 1989f785f7..265531443f 100644 --- a/awx/main/expect/isolated_manager.py +++ b/awx/main/expect/isolated_manager.py @@ -430,7 +430,7 @@ class IsolatedManager(object): except (KeyError, IndexError): task_result = {} if 'capacity' in task_result: - cls.update_capacity(instance, awx_application_version) + cls.update_capacity(instance, task_result, awx_application_version) elif instance.capacity == 0: logger.debug('Isolated instance {} previously marked as lost, could not re-join.'.format( instance.hostname)) From 8f883d8d43169dbbc965599dfaa47937f1434d5c Mon Sep 17 00:00:00 2001 From: Wayne Witzel III Date: Fri, 6 Oct 2017 09:25:43 -0400 Subject: [PATCH 09/14] Fix migrations to support 3.1.2 -> 3.2.1+ upgrade path --- .../0005_squashed_v310_v313_updates.py | 8 ------- .../0005a_squashed_v310_v313_updates.py | 24 +++++++++++++++++++ awx/main/migrations/0006_v320_release.py | 2 +- 3 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 awx/main/migrations/0005a_squashed_v310_v313_updates.py diff --git a/awx/main/migrations/0005_squashed_v310_v313_updates.py b/awx/main/migrations/0005_squashed_v310_v313_updates.py index 24a7f2d207..e948a1a9e2 100644 --- a/awx/main/migrations/0005_squashed_v310_v313_updates.py +++ b/awx/main/migrations/0005_squashed_v310_v313_updates.py @@ -13,7 +13,6 @@ class Migration(migrations.Migration): replaces = [ (b'main', '0035_v310_remove_tower_settings'), (b'main', '0036_v311_insights'), - (b'main', '0037_v313_instance_version'), ] operations = [ @@ -36,11 +35,4 @@ class Migration(migrations.Migration): name='scm_type', field=models.CharField(default=b'', choices=[(b'', 'Manual'), (b'git', 'Git'), (b'hg', 'Mercurial'), (b'svn', 'Subversion'), (b'insights', 'Red Hat Insights')], max_length=8, blank=True, help_text='Specifies the source control system used to store the project.', verbose_name='SCM Type'), ), - - migrations.AddField( - model_name='instance', - name='version', - field=models.CharField(max_length=24, blank=True), - ), - ] diff --git a/awx/main/migrations/0005a_squashed_v310_v313_updates.py b/awx/main/migrations/0005a_squashed_v310_v313_updates.py new file mode 100644 index 0000000000..0a8684f097 --- /dev/null +++ b/awx/main/migrations/0005a_squashed_v310_v313_updates.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0005_squashed_v310_v313_updates'), + ] + + replaces = [ + (b'main', '0037_v313_instance_version'), + ] + + operations = [ + # Remove Tower settings, these settings are now in separate awx.conf app. + migrations.AddField( + model_name='instance', + name='version', + field=models.CharField(max_length=24, blank=True), + ), + ] diff --git a/awx/main/migrations/0006_v320_release.py b/awx/main/migrations/0006_v320_release.py index 45eabbebc5..e526a5c762 100644 --- a/awx/main/migrations/0006_v320_release.py +++ b/awx/main/migrations/0006_v320_release.py @@ -18,7 +18,7 @@ from awx.main.models import Host class Migration(migrations.Migration): dependencies = [ - ('main', '0005_squashed_v310_v313_updates'), + ('main', '0005a_squashed_v310_v313_updates'), ] operations = [ From f97ca9c42fb3bcdcb21d88fe84373ad576cd2a64 Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Fri, 6 Oct 2017 11:57:08 -0400 Subject: [PATCH 10/14] Fix the way we include i18n files in sdist --- MANIFEST.in | 2 ++ setup.py | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index ff4d8ccddb..3c687ce2da 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,6 @@ recursive-include awx *.py +recursive-include awx *.po +recursive-include awx *.mo recursive-include awx/static * recursive-include awx/templates *.html recursive-include awx/api/templates *.md *.html diff --git a/setup.py b/setup.py index bf6d0f7393..627c604746 100755 --- a/setup.py +++ b/setup.py @@ -155,9 +155,7 @@ setup( }, data_files = proc_data_files([ ("%s" % homedir, ["config/wsgi.py", - "awx/static/favicon.ico", - "awx/locale/*/LC_MESSAGES/*.po", - "awx/locale/*/LC_MESSAGES/*.mo"]), + "awx/static/favicon.ico"]), ("%s" % siteconfig, ["config/awx-nginx.conf"]), # ("%s" % webconfig, ["config/uwsgi_params"]), ("%s" % sharedir, ["tools/scripts/request_tower_configuration.sh","tools/scripts/request_tower_configuration.ps1"]), From edda5e542043077773d693c51216de30950c9292 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 6 Oct 2017 14:07:38 -0400 Subject: [PATCH 11/14] feedback on ad hoc prohibited vars error msg --- awx/api/serializers.py | 4 ++-- awx/main/tasks.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 98090bb611..a95a7f0c41 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -2763,8 +2763,8 @@ class AdHocCommandSerializer(UnifiedJobSerializer): redacted_extra_vars, removed_vars = extract_ansible_vars(value) if removed_vars: raise serializers.ValidationError(_( - "Variables {} are prohibited from use in ad hoc commands." - ).format(",".join(removed_vars))) + "{} are prohibited from use in ad hoc commands." + ).format(", ".join(removed_vars))) return vars_validate_or_raise(value) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 3bc4bc9c45..66ba251f40 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -2142,8 +2142,8 @@ class RunAdHocCommand(BaseTask): redacted_extra_vars, removed_vars = extract_ansible_vars(ad_hoc_command.extra_vars_dict) if removed_vars: raise ValueError(_( - "unable to use {} variables with ad hoc commands" - ).format(",".format(removed_vars))) + "{} are prohibited from use in ad hoc commands." + ).format(", ".join(removed_vars))) args.extend(['-e', json.dumps(ad_hoc_command.extra_vars_dict)]) From 4237b9ed5cfb9d5e86a82b1caa32a92b41837f7d Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Mon, 9 Oct 2017 15:48:52 -0400 Subject: [PATCH 12/14] move 0005a migration to 0005b --- ...d_v310_v313_updates.py => 0005b_squashed_v310_v313_updates.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename awx/main/migrations/{0005a_squashed_v310_v313_updates.py => 0005b_squashed_v310_v313_updates.py} (100%) diff --git a/awx/main/migrations/0005a_squashed_v310_v313_updates.py b/awx/main/migrations/0005b_squashed_v310_v313_updates.py similarity index 100% rename from awx/main/migrations/0005a_squashed_v310_v313_updates.py rename to awx/main/migrations/0005b_squashed_v310_v313_updates.py From d4fc4bcd6147dbae9fffd4dc876f48753dfcb035 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Mon, 9 Oct 2017 15:49:38 -0400 Subject: [PATCH 13/14] fix migration problem from 3.1.1 --- .../0005_squashed_v310_v313_updates.py | 1 - .../0005a_squashed_v310_v313_updates.py | 28 +++++++++++++++++++ .../0005b_squashed_v310_v313_updates.py | 2 +- awx/main/migrations/0006_v320_release.py | 2 +- 4 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 awx/main/migrations/0005a_squashed_v310_v313_updates.py diff --git a/awx/main/migrations/0005_squashed_v310_v313_updates.py b/awx/main/migrations/0005_squashed_v310_v313_updates.py index e948a1a9e2..85dac8bbaa 100644 --- a/awx/main/migrations/0005_squashed_v310_v313_updates.py +++ b/awx/main/migrations/0005_squashed_v310_v313_updates.py @@ -12,7 +12,6 @@ class Migration(migrations.Migration): replaces = [ (b'main', '0035_v310_remove_tower_settings'), - (b'main', '0036_v311_insights'), ] operations = [ diff --git a/awx/main/migrations/0005a_squashed_v310_v313_updates.py b/awx/main/migrations/0005a_squashed_v310_v313_updates.py new file mode 100644 index 0000000000..6599268ce1 --- /dev/null +++ b/awx/main/migrations/0005a_squashed_v310_v313_updates.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0005_squashed_v310_v313_updates'), + ] + + replaces = [ + (b'main', '0036_v311_insights'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='scm_type', + field=models.CharField(default=b'', choices=[(b'', 'Manual'), (b'git', 'Git'), (b'hg', 'Mercurial'), (b'svn', 'Subversion'), (b'insights', 'Red Hat Insights')], max_length=8, blank=True, help_text='Specifies the source control system used to store the project.', verbose_name='SCM Type'), + ), + migrations.AlterField( + model_name='projectupdate', + name='scm_type', + field=models.CharField(default=b'', choices=[(b'', 'Manual'), (b'git', 'Git'), (b'hg', 'Mercurial'), (b'svn', 'Subversion'), (b'insights', 'Red Hat Insights')], max_length=8, blank=True, help_text='Specifies the source control system used to store the project.', verbose_name='SCM Type'), + ), + ] \ No newline at end of file diff --git a/awx/main/migrations/0005b_squashed_v310_v313_updates.py b/awx/main/migrations/0005b_squashed_v310_v313_updates.py index 0a8684f097..ea9d26d2bc 100644 --- a/awx/main/migrations/0005b_squashed_v310_v313_updates.py +++ b/awx/main/migrations/0005b_squashed_v310_v313_updates.py @@ -7,7 +7,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('main', '0005_squashed_v310_v313_updates'), + ('main', '0005a_squashed_v310_v313_updates'), ] replaces = [ diff --git a/awx/main/migrations/0006_v320_release.py b/awx/main/migrations/0006_v320_release.py index e526a5c762..4cb87f4fba 100644 --- a/awx/main/migrations/0006_v320_release.py +++ b/awx/main/migrations/0006_v320_release.py @@ -18,7 +18,7 @@ from awx.main.models import Host class Migration(migrations.Migration): dependencies = [ - ('main', '0005a_squashed_v310_v313_updates'), + ('main', '0005b_squashed_v310_v313_updates'), ] operations = [ From e2c398ade2084d9367bd16ce1c27ddba849906ae Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Tue, 10 Oct 2017 07:54:59 -0400 Subject: [PATCH 14/14] fallback to empty dict when processing extra_data --- awx/main/models/unified_jobs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index 91285ecc16..4e4af4c8ef 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -879,6 +879,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique def handle_extra_data(self, extra_data): if hasattr(self, 'extra_vars') and extra_data: + extra_data_dict = {} try: extra_data_dict = parse_yaml_or_json(extra_data, silent_failure=False) except Exception as e: