diff --git a/awx/ui/client/legacy/styles/ansible-ui.less b/awx/ui/client/legacy/styles/ansible-ui.less index b05aa51148..6646529a7f 100644 --- a/awx/ui/client/legacy/styles/ansible-ui.less +++ b/awx/ui/client/legacy/styles/ansible-ui.less @@ -1047,6 +1047,8 @@ input[type="checkbox"].checkbox-no-label { color: @green; } + .icon-host-all:before, + .icon-host-failed:before, .icon-job-active:before, .icon-job-running:before, .icon-job-success:before, @@ -1098,6 +1100,7 @@ input[type="checkbox"].checkbox-no-label { color: @changed; } + .icon-host-failed, .icon-job-stopped, .icon-job-error, .icon-job-failed, @@ -1106,6 +1109,10 @@ input[type="checkbox"].checkbox-no-label { color: @red; } + .icon-host-all { + color: @at-blue; + } + .icon-job-unreachable { color: @unreachable; } diff --git a/awx/ui/client/legacy/styles/lists.less b/awx/ui/client/legacy/styles/lists.less index d3948f6c79..466c8b75b1 100644 --- a/awx/ui/client/legacy/styles/lists.less +++ b/awx/ui/client/legacy/styles/lists.less @@ -19,7 +19,6 @@ table, tbody { width: 100%; margin-top: 20px; table-layout: fixed; - overflow: hidden; } .List-tableHeader{ @@ -70,7 +69,7 @@ table, tbody { } .List-tableRow--selected { - border-left: 10px solid @list-row-select-bord; + border-left: 5px solid @list-row-select-bord; } .List-tableRow--selected > :first-child { @@ -124,7 +123,8 @@ table, tbody { color: @list-action-icon; background-color: @list-actn-bg; border: none; - border-radius: 50%; + border-radius: 4px; + margin-left: 15px; } .List-actionButton:hover { @@ -136,10 +136,6 @@ table, tbody { background-color: @list-actn-del-bg-hov !important; } -.List-actionButton + .List-actionButton { - margin-left: 15px; -} - .List-header { display: flex; } diff --git a/awx/ui/client/lib/components/_index.less b/awx/ui/client/lib/components/_index.less index f4985cf931..7bb09843e0 100644 --- a/awx/ui/client/lib/components/_index.less +++ b/awx/ui/client/lib/components/_index.less @@ -7,3 +7,4 @@ @import 'tabs/_index'; @import 'utility/_index'; @import 'truncate/_index'; +@import 'relaunchButton/_index'; \ No newline at end of file diff --git a/awx/ui/client/lib/components/components.strings.js b/awx/ui/client/lib/components/components.strings.js index db27701a46..52ffdb1f1c 100644 --- a/awx/ui/client/lib/components/components.strings.js +++ b/awx/ui/client/lib/components/components.strings.js @@ -76,6 +76,11 @@ function ComponentsStrings (BaseString) { ns.capacityBar = { IS_OFFLINE: t.s('Unavailable to run jobs.') }; + + ns.relaunch = { + DEFAULT: t.s('Relaunch using the same parameters'), + HOSTS: t.s('Relaunch using host parameters') + }; } ComponentsStrings.$inject = ['BaseStringService']; diff --git a/awx/ui/client/lib/components/index.js b/awx/ui/client/lib/components/index.js index 426b9244cd..c82d8dc77e 100644 --- a/awx/ui/client/lib/components/index.js +++ b/awx/ui/client/lib/components/index.js @@ -26,6 +26,7 @@ import tab from '~components/tabs/tab.directive'; import tabGroup from '~components/tabs/group.directive'; import topNavItem from '~components/layout/top-nav-item.directive'; import truncate from '~components/truncate/truncate.directive'; +import relaunch from '~components/relaunchButton/relaunchButton.directive'; import BaseInputController from '~components/input/base.controller'; import ComponentsStrings from '~components/components.strings'; @@ -62,6 +63,7 @@ angular .directive('atTabGroup', tabGroup) .directive('atTopNavItem', topNavItem) .directive('atTruncate', truncate) + .component('atRelaunch', relaunch) .service('BaseInputController', BaseInputController) .service('ComponentsStrings', ComponentsStrings); diff --git a/awx/ui/client/lib/components/relaunchButton/_index.less b/awx/ui/client/lib/components/relaunchButton/_index.less new file mode 100644 index 0000000000..7bc6e55e13 --- /dev/null +++ b/awx/ui/client/lib/components/relaunchButton/_index.less @@ -0,0 +1,39 @@ +.at-Relaunch { + margin-left: 15px; + + &--button { + font-size: 16px; + height: 30px; + min-width: 30px; + color: #848992; + background-color: inherit; + border: none; + border-radius: 4px; + } + &--button:hover { + background-color: @at-blue; + color: white; + } + &--dropdownTitle { + color: #707070; + font-size: 12px; + font-weight: bold; + text-transform: uppercase; + padding: 3px 10px; + } + &--dropdownOptions { + i { + padding-right: 5px; + } + a:hover { + cursor: pointer; + } + } +} + +.open { + .at-Relaunch--button { + background-color: @at-blue; + color: white; + } +} diff --git a/awx/ui/client/lib/components/relaunchButton/relaunchButton.directive.js b/awx/ui/client/lib/components/relaunchButton/relaunchButton.directive.js new file mode 100644 index 0000000000..0d50f02d90 --- /dev/null +++ b/awx/ui/client/lib/components/relaunchButton/relaunchButton.directive.js @@ -0,0 +1,55 @@ +import templateUrl from './relaunchButton.partial.html'; + +const atRelaunch = { + templateUrl, + bindings: { + state: '<' + }, + controller: ['RelaunchJob', 'InitiatePlaybookRun', 'ComponentsStrings', '$scope', atRelaunchCtrl], + controllerAs: 'vm', + replace: true +}; + +function atRelaunchCtrl (RelaunchJob, InitiatePlaybookRun, strings, $scope) { + const vm = this; + const scope = $scope.$parent; + const { job } = $scope.$parent; + + vm.$onInit = () => { + vm.showRelaunch = !(job.type === 'system_job') && job.summary_fields.user_capabilities.start; + vm.showDropdown = job.type === 'job' && job.failed === true; + + if (vm.showDropdown) { + vm.tooltip = strings.get('relaunch.HOSTS'); + } else { + vm.tooltip = strings.get('relaunch.DEFAULT'); + } + }; + + vm.relaunchJob = () => { + let typeId; + + if (job.type === 'inventory_update') { + typeId = job.inventory_source; + } else if (job.type === 'project_update') { + typeId = job.project; + } else if (job.type === 'job' || job.type === 'system_job' + || job.type === 'ad_hoc_command' || job.type === 'workflow_job') { + typeId = job.id; + } + + RelaunchJob({ scope, id: typeId, type: job.type, name: job.name }); + }; + + vm.relaunchOn = (option) => { + InitiatePlaybookRun({ + scope, + id: job.id, + relaunch: true, + job_type: job.type, + host_type: (option.name).toLowerCase() + }); + }; +} + +export default atRelaunch; diff --git a/awx/ui/client/lib/components/relaunchButton/relaunchButton.partial.html b/awx/ui/client/lib/components/relaunchButton/relaunchButton.partial.html new file mode 100644 index 0000000000..eca5f42fc4 --- /dev/null +++ b/awx/ui/client/lib/components/relaunchButton/relaunchButton.partial.html @@ -0,0 +1,33 @@ +
+ +
+ + + +
+ + +
\ No newline at end of file diff --git a/awx/ui/client/src/job-submission/job-submission-factories/initiateplaybookrun.factory.js b/awx/ui/client/src/job-submission/job-submission-factories/initiateplaybookrun.factory.js index 7c8816b10b..01e04a25bc 100644 --- a/awx/ui/client/src/job-submission/job-submission-factories/initiateplaybookrun.factory.js +++ b/awx/ui/client/src/job-submission/job-submission-factories/initiateplaybookrun.factory.js @@ -10,10 +10,13 @@ export default var scope = params.scope.$new(), id = params.id, relaunch = params.relaunch || false, - job_type = params.job_type; + job_type = params.job_type, + host_type = params.host_type || ""; scope.job_template_id = id; - var el = $compile( "" )( scope ); + var el = $compile( "" )( scope ); $('#content-container').remove('submit-job').append( el ); }; } diff --git a/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js b/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js index 58f45e38cf..3e521fa4fe 100644 --- a/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js +++ b/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js @@ -11,6 +11,7 @@ export default job_launch_data = {}, url = params.url, submitJobType = params.submitJobType, + relaunchHostType = params.relaunchHostType, vars_url = GetBasePath('job_templates')+scope.job_template_id + '/', base = $location.path().replace(/^\//, '').split('/')[0], extra_vars; @@ -132,6 +133,14 @@ export default job_launch_data.diff_mode = scope.other_prompt_data.diff_mode; } + if(scope.relaunchHostType) { + job_launch_data.hosts = scope.relaunchHostType; + } + console.log(job_launch_data); + // if(_.get(scope, 'retry_counts.failed') === true) { + // job_launch_data.hosts = "failed"; + // } + // If the extra_vars dict is empty, we don't want to include it if we didn't prompt for anything. if(jQuery.isEmptyObject(job_launch_data.extra_vars)===true && scope.prompt_for_vars===false){ delete job_launch_data.extra_vars; diff --git a/awx/ui/client/src/job-submission/job-submission.controller.js b/awx/ui/client/src/job-submission/job-submission.controller.js index 97f4002119..36cb42a7ed 100644 --- a/awx/ui/client/src/job-submission/job-submission.controller.js +++ b/awx/ui/client/src/job-submission/job-submission.controller.js @@ -83,7 +83,8 @@ export default LaunchJob({ scope: $scope, url: launch_url, - submitJobType: $scope.submitJobType + submitJobType: $scope.submitJobType, + relaunchHostType: $scope.relaunchHostType }); }; diff --git a/awx/ui/client/src/job-submission/job-submission.directive.js b/awx/ui/client/src/job-submission/job-submission.directive.js index 5346e53ae1..d248e2cd05 100644 --- a/awx/ui/client/src/job-submission/job-submission.directive.js +++ b/awx/ui/client/src/job-submission/job-submission.directive.js @@ -12,7 +12,8 @@ export default [ 'templateUrl', 'CreateDialog', 'Wait', 'CreateSelect2', 'ParseT scope: { submitJobId: '=', submitJobType: '@', - submitJobRelaunch: '=' + submitJobRelaunch: '=', + relaunchHostType: '@' }, templateUrl: templateUrl('job-submission/job-submission'), controller: jobSubmissionController, diff --git a/awx/ui/client/src/jobs/all-jobs.list.js b/awx/ui/client/src/jobs/all-jobs.list.js index bc5b9e6657..4d3109842e 100644 --- a/awx/ui/client/src/jobs/all-jobs.list.js +++ b/awx/ui/client/src/jobs/all-jobs.list.js @@ -92,12 +92,24 @@ export default ['i18n', function(i18n) { dataPlacement: "top" }, submit: { - icon: 'icon-rocket', + icon: 'icon-launch', mode: 'all', ngClick: 'relaunchJob($event, job.id)', awToolTip: i18n._('Relaunch using the same parameters'), dataPlacement: 'top', - ngShow: "!(job.type == 'system_job') && job.summary_fields.user_capabilities.start" + ngShow: "!(job.type == 'system_job') && job.summary_fields.user_capabilities.start", + relaunch: true, + dropdownTitle: 'Relaunch On', + dropdownOptions: [ + { + name: 'All', + icon: 'icon-host-all' + }, + { + name: 'Failed', + icon: 'icon-host-failed' + } + ], }, cancel: { mode: 'all', diff --git a/awx/ui/client/src/shared/list-generator/list-generator.factory.js b/awx/ui/client/src/shared/list-generator/list-generator.factory.js index 8ec37ebfcd..b6e37705c2 100644 --- a/awx/ui/client/src/shared/list-generator/list-generator.factory.js +++ b/awx/ui/client/src/shared/list-generator/list-generator.factory.js @@ -396,7 +396,10 @@ export default ['$compile', 'Attr', 'Icon', if (field_action === 'pending_deletion') { innerTable += `Pending Delete`; } - else { + // Plug in Dropdown Component + if (field_action === 'submit' && list.fieldActions[field_action].relaunch === true) { + innerTable += `` + } else { fAction = list.fieldActions[field_action]; innerTable += "