From 6d31136bfaf729e64fe913f74ef0f221713a11f7 Mon Sep 17 00:00:00 2001 From: Ken Hoes Date: Mon, 18 Apr 2016 10:00:06 -0400 Subject: [PATCH 1/3] Demo feedback - breadcrumbs, capitalization, labels. Covers 1554 + 1555. --- .../fact-data-table.block.less | 1 - .../fact-module-pickers.block.less | 36 +++++-------------- .../system-tracking/system-tracking.route.js | 3 ++ 3 files changed, 11 insertions(+), 29 deletions(-) diff --git a/awx/ui/client/src/system-tracking/fact-data-table/fact-data-table.block.less b/awx/ui/client/src/system-tracking/fact-data-table/fact-data-table.block.less index 67bfcf42fa..f2a2c74995 100644 --- a/awx/ui/client/src/system-tracking/fact-data-table/fact-data-table.block.less +++ b/awx/ui/client/src/system-tracking/fact-data-table/fact-data-table.block.less @@ -49,7 +49,6 @@ padding: 1em 12px; color: @default-as-detail-txt; font-size: 14px; - text-transform: uppercase; } &-column { diff --git a/awx/ui/client/src/system-tracking/fact-module-pickers.block.less b/awx/ui/client/src/system-tracking/fact-module-pickers.block.less index b494456981..f4d6d35022 100644 --- a/awx/ui/client/src/system-tracking/fact-module-pickers.block.less +++ b/awx/ui/client/src/system-tracking/fact-module-pickers.block.less @@ -1,31 +1,3 @@ -// /** @define FactModulePickers */ -// -// .FactModulePickers { -// //width: 100%; -// display: flex; -// margin-bottom: 15px; -// -// &-dateContainer { -// flex: 1 0 auto; -// display: flex; -// flex-direction: column -// } -// -// &-dateContainer--left { -// margin-right: 7px; -// } -// -// &-dateContainer--right { -// margin-left: 7px; -// } -// -// &-label { -// flex: 1 0 auto; -// font-weight: 700; -// padding-bottom: 5px; -// } -// } - @import "../../shared/branding/colors.default.less"; .FactModulePickers-label { @@ -36,3 +8,11 @@ color: @default-interface-txt; line-height: 17px; } + +.FactModulePickers-warning { + float: right; + clear: both; + font-size: 12px; + width: 75%; + text-align: right; +} diff --git a/awx/ui/client/src/system-tracking/system-tracking.route.js b/awx/ui/client/src/system-tracking/system-tracking.route.js index 0149587769..4fcd8e1b25 100644 --- a/awx/ui/client/src/system-tracking/system-tracking.route.js +++ b/awx/ui/client/src/system-tracking/system-tracking.route.js @@ -13,6 +13,9 @@ export default { templateUrl: templateUrl('system-tracking/system-tracking'), params: {hosts: null, inventory: null}, reloadOnSearch: false, + ncyBreadcrumb: { + label: "SYSTEM TRACKING" + }, resolve: { moduleOptions: [ 'getModuleOptions', From 226cb9acdff7b1826099853b8c25a5c02662a077 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Mon, 18 Apr 2016 14:24:53 -0400 Subject: [PATCH 2/3] Protect job template and project template notifiers in the case where the project or org may not be available. --- awx/main/models/jobs.py | 7 ++++--- awx/main/models/projects.py | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index 725e55bbe4..dcac5e4a34 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -460,9 +460,10 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, ResourceMixin): success_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_success__in=[self, self.project])) any_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_any__in=[self, self.project])) # Get Organization Notifiers - error_notifiers = set(error_notifiers + list(base_notifiers.filter(organization_notifiers_for_errors=self.project.organization))) - success_notifiers = set(success_notifiers + list(base_notifiers.filter(organization_notifiers_for_success=self.project.organization))) - any_notifiers = set(any_notifiers + list(base_notifiers.filter(organization_notifiers_for_any=self.project.organization))) + if self.project is not None and self.project.organization is not None: + error_notifiers = set(error_notifiers + list(base_notifiers.filter(organization_notifiers_for_errors=self.project.organization))) + success_notifiers = set(success_notifiers + list(base_notifiers.filter(organization_notifiers_for_success=self.project.organization))) + any_notifiers = set(any_notifiers + list(base_notifiers.filter(organization_notifiers_for_any=self.project.organization))) return dict(error=list(error_notifiers), success=list(success_notifiers), any=list(any_notifiers)) class Job(UnifiedJob, JobOptions): diff --git a/awx/main/models/projects.py b/awx/main/models/projects.py index e5d1d58d19..0f931cea91 100644 --- a/awx/main/models/projects.py +++ b/awx/main/models/projects.py @@ -360,9 +360,10 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin): success_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_success=self)) any_notifiers = list(base_notifiers.filter(unifiedjobtemplate_notifiers_for_any=self)) # Get Organization Notifiers - error_notifiers = set(error_notifiers + list(base_notifiers.filter(organization_notifiers_for_errors=self.organization))) - success_notifiers = set(success_notifiers + list(base_notifiers.filter(organization_notifiers_for_success=self.organization))) - any_notifiers = set(any_notifiers + list(base_notifiers.filter(organization_notifiers_for_any=self.organization))) + if self.organization is not None: + error_notifiers = set(error_notifiers + list(base_notifiers.filter(organization_notifiers_for_errors=self.organization))) + success_notifiers = set(success_notifiers + list(base_notifiers.filter(organization_notifiers_for_success=self.organization))) + any_notifiers = set(any_notifiers + list(base_notifiers.filter(organization_notifiers_for_any=self.organization))) return dict(error=list(error_notifiers), success=list(success_notifiers), any=list(any_notifiers)) def get_absolute_url(self): From d87213705b7ce6f5cb9118a38edd81432f4f8267 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Mon, 18 Apr 2016 15:57:31 -0400 Subject: [PATCH 3/3] Adjust job launch blocking logic Previously a job template would always block another job template launch regardless of the details of the job template. We now restrict that blocking logic to only block in the case that the job template was launched with the same inventory. We keep the exclusion where if the launch type is 'callback' and the limits differ then they won't be blocked --- awx/main/models/jobs.py | 13 ++++++------- awx/main/tests/functional/conftest.py | 11 +++++++++++ awx/main/tests/functional/test_jobs.py | 24 ++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 awx/main/tests/functional/test_jobs.py diff --git a/awx/main/models/jobs.py b/awx/main/models/jobs.py index dcac5e4a34..83d507615d 100644 --- a/awx/main/models/jobs.py +++ b/awx/main/models/jobs.py @@ -531,14 +531,13 @@ class Job(UnifiedJob, JobOptions): def is_blocked_by(self, obj): from awx.main.models import InventoryUpdate, ProjectUpdate if type(obj) == Job: - if obj.job_template is not None and obj.job_template == self.job_template: - if obj.launch_type == 'callback' and self.launch_type == 'callback': - if obj.limit != self.limit: - # NOTE: This is overriden by api/views.py.JobTemplateCallback.post() check - # which limits job runs on a JT to one per host in a callback scenario - # I'm leaving this here in case we change that + if obj.job_template is not None and obj.inventory is not None: + if obj.job_template == self.job_template and \ + obj.inventory == self.inventory: + if obj.launch_type == 'callback' and self.launch_type == 'callback' and \ + obj.limit != self.limit: return False - return True + return True return False if type(obj) == InventoryUpdate: if self.inventory == obj.inventory_source.inventory: diff --git a/awx/main/tests/functional/conftest.py b/awx/main/tests/functional/conftest.py index e187d8cd03..97e31c3269 100644 --- a/awx/main/tests/functional/conftest.py +++ b/awx/main/tests/functional/conftest.py @@ -28,6 +28,7 @@ from awx.main.models.credential import Credential from awx.main.models.jobs import JobTemplate from awx.main.models.inventory import ( Group, + Inventory, ) from awx.main.models.organization import ( Organization, @@ -175,6 +176,16 @@ def machine_credential(): def inventory(organization): return organization.inventories.create(name="test-inv") +@pytest.fixture +def inventory_factory(organization): + def factory(name, org=organization): + try: + inv = Inventory.objects.get(name=name, organization=org) + except Inventory.DoesNotExist: + inv = Inventory.objects.create(name=name, organization=org) + return inv + return factory + @pytest.fixture def label(organization): return organization.labels.create(name="test-label", description="test-label-desc") diff --git a/awx/main/tests/functional/test_jobs.py b/awx/main/tests/functional/test_jobs.py new file mode 100644 index 0000000000..aefb4e8bb7 --- /dev/null +++ b/awx/main/tests/functional/test_jobs.py @@ -0,0 +1,24 @@ +from awx.main.models import Job + +import pytest + +@pytest.mark.django_db +def test_job_blocking(get, post, job_template, inventory, inventory_factory): + j1 = Job.objects.create(job_template=job_template, + inventory=inventory) + j2 = Job.objects.create(job_template=job_template, + inventory=inventory) + assert j1.is_blocked_by(j2) + j2.inventory = inventory_factory(name='test-different-inventory') + assert not j1.is_blocked_by(j2) + j_callback_1 = Job.objects.create(job_template=job_template, + inventory=inventory, + launch_type='callback', + limit='a') + j_callback_2 = Job.objects.create(job_template=job_template, + inventory=inventory, + launch_type='callback', + limit='a') + assert j_callback_1.is_blocked_by(j_callback_2) + j_callback_2.limit = 'b' + assert not j_callback_1.is_blocked_by(j_callback_2)