diff --git a/awx/ui/client/legacy-styles/ansible-ui.less b/awx/ui/client/legacy-styles/ansible-ui.less index 4cc306cb36..763abab7f2 100644 --- a/awx/ui/client/legacy-styles/ansible-ui.less +++ b/awx/ui/client/legacy-styles/ansible-ui.less @@ -1671,7 +1671,7 @@ tr td button i { .modal-body { min-height: 120px; padding: 20px 0; - + .alert { padding: 10px; margin: 0; @@ -1984,10 +1984,6 @@ tr td button i { width: 73px; } -.JobDetails-status { - margin-bottom: 12px; -} - .red-text { color: @red; } diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 7289c1af85..f17df84b89 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -46,7 +46,6 @@ import inventories from './inventories/main'; import inventoryScripts from './inventory-scripts/main'; import organizations from './organizations/main'; import managementJobs from './management-jobs/main'; -import jobDetail from './job-detail/main'; import workflowResults from './workflow-results/main'; import jobResults from './job-results/main'; import jobSubmission from './job-submission/main'; @@ -119,7 +118,6 @@ var tower = angular.module('Tower', [ login.name, activityStream.name, footer.name, - jobDetail.name, workflowResults.name, jobResults.name, jobSubmission.name, @@ -191,7 +189,6 @@ var tower = angular.module('Tower', [ 'LogViewerStatusDefinition', 'StandardOutHelper', 'LogViewerOptionsDefinition', - 'JobDetailHelper', 'lrInfiniteScroll', 'LoadConfigHelper', 'PortalJobsListDefinition', @@ -350,19 +347,19 @@ var tower = angular.module('Tower', [ $rootScope.$on("$stateChangeStart", function (event, next) { // Remove any lingering intervals - // except on jobDetails.* states - var jobDetailStates = [ - 'jobDetail', - 'jobDetail.host-summary', - 'jobDetail.host-event.details', - 'jobDetail.host-event.json', - 'jobDetail.host-events', - 'jobDetail.host-event.stdout' + // except on jobResults.* states + var jobResultStates = [ + 'jobResult', + 'jobResult.host-summary', + 'jobResult.host-event.details', + 'jobResult.host-event.json', + 'jobResult.host-events', + 'jobResult.host-event.stdout' ]; - if ($rootScope.jobDetailInterval && !_.includes(jobDetailStates, next.name) ) { - window.clearInterval($rootScope.jobDetailInterval); + if ($rootScope.jobResultInterval && !_.includes(jobResultStates, next.name) ) { + window.clearInterval($rootScope.jobResultInterval); } - if ($rootScope.jobStdOutInterval && !_.includes(jobDetailStates, next.name) ) { + if ($rootScope.jobStdOutInterval && !_.includes(jobResultStates, next.name) ) { window.clearInterval($rootScope.jobStdOutInterval); } diff --git a/awx/ui/client/src/helpers.js b/awx/ui/client/src/helpers.js index b22938443d..765b81be5f 100644 --- a/awx/ui/client/src/helpers.js +++ b/awx/ui/client/src/helpers.js @@ -12,7 +12,6 @@ import Credentials from "./helpers/Credentials"; import Events from "./helpers/Events"; import Groups from "./helpers/Groups"; import Hosts from "./helpers/Hosts"; -import JobDetail from "./helpers/JobDetail"; import JobSubmission from "./helpers/JobSubmission"; import JobTemplates from "./helpers/JobTemplates"; import Jobs from "./helpers/Jobs"; @@ -38,7 +37,6 @@ export Events, Groups, Hosts, - JobDetail, JobSubmission, JobTemplates, Jobs, diff --git a/awx/ui/client/src/helpers/JobDetail.js b/awx/ui/client/src/helpers/JobDetail.js deleted file mode 100644 index ad049ed481..0000000000 --- a/awx/ui/client/src/helpers/JobDetail.js +++ /dev/null @@ -1,1198 +0,0 @@ -/************************************************* - * Copyright (c) 2015 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - /** - * @ngdoc function - * @name helpers.function:JobDetail - * @description helper moduler for jobdetails controller - # Playbook events will be structured to form the following hierarchy: - # - playbook_on_start (once for each playbook file) - # - playbook_on_vars_prompt (for each play, but before play starts, we - # currently don't handle responding to these prompts) - # - playbook_on_play_start (once for each play) - # - playbook_on_import_for_host - # - playbook_on_not_import_for_host - # - playbook_on_no_hosts_matched - # - playbook_on_no_hosts_remaining - # - playbook_on_setup - # - runner_on* - # - playbook_on_task_start (once for each task within a play) - # - runner_on_failed - # - runner_on_ok - # - runner_on_error - # - runner_on_skipped - # - runner_on_unreachable - # - runner_on_no_hosts - # - runner_on_async_poll - # - runner_on_async_ok - # - runner_on_async_failed - # - runner_on_file_diff - # - playbook_on_notify (once for each notification from the play) - # - playbook_on_stats - -*/ - - -export default - angular.module('JobDetailHelper', ['Utilities', 'RestServices', 'ModalDialog']) - - .factory('DigestEvent', ['$rootScope', '$log', 'UpdatePlayStatus', 'UpdateHostStatus', 'AddHostResult', - 'GetElapsed', 'UpdateTaskStatus', 'JobIsFinished', 'AddNewTask', 'AddNewPlay', - function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, GetElapsed, - UpdateTaskStatus, JobIsFinished, AddNewTask, AddNewPlay) { - return function(params) { - - var scope = params.scope, - event = params.event, - msg; - - $log.debug('processing event: ' + event.id); - $log.debug(event); - - function getMsg(event) { - var msg = ''; - if (event.event_data && event.event_data.res) { - if (typeof event.event_data.res === 'object') { - msg = event.event_data.res.msg; - } else { - msg = event.event_data.res; - } - } - return msg; - } - - switch (event.event) { - case 'playbook_on_start': - if (!JobIsFinished(scope)) { - scope.job_status.started = event.created; - scope.job_status.status = 'running'; - } - break; - - case 'playbook_on_play_start': - AddNewPlay({ scope: scope, event: event }); - break; - - case 'playbook_on_setup': - AddNewTask({ scope: scope, event: event }); - break; - - case 'playbook_on_task_start': - AddNewTask({ scope: scope, event: event }); - break; - - case 'runner_on_ok': - case 'runner_on_async_ok': - msg = getMsg(event); - UpdateHostStatus({ - scope: scope, - name: event.host_name, - host_id: event.host, - task_id: event.parent, - status: ( (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful' ), - id: event.id, - created: event.created, - modified: event.modified, - message: msg, - counter: event.counter, - item: (event.event_data && event.event_data.res) ? event.event_data.res.item : '' - }); - break; - - case 'playbook_on_no_hosts_matched': - UpdatePlayStatus({ - scope: scope, - play_id: event.parent, - failed: false, - changed: false, - modified: event.modified, - no_hosts: true - }); - break; - - case 'runner_on_unreachable': - msg = getMsg(event); - UpdateHostStatus({ - scope: scope, - name: event.host_name, - host_id: event.host, - task_id: event.parent, - status: 'unreachable', - id: event.id, - created: event.created, - modified: event.modified, - message: msg, - counter: event.counter, - item: (event.event_data && event.event_data.res) ? event.event_data.res.item : '' - }); - break; - - case 'runner_on_error': - case 'runner_on_async_failed': - msg = getMsg(event); - UpdateHostStatus({ - scope: scope, - name: event.host_name, - host_id: event.host, - task_id: event.parent, - status: 'failed', - id: event.id, - created: event.created, - modified: event.modified, - message: msg, - counter: event.counter, - item: (event.event_data && event.event_data.res) ? event.event_data.res.item : '' - }); - break; - - case 'runner_on_no_hosts': - UpdateTaskStatus({ - scope: scope, - failed: event.failed, - changed: event.changed, - task_id: event.parent, - modified: event.modified, - no_hosts: true - }); - break; - - case 'runner_on_skipped': - msg = getMsg(event); - UpdateHostStatus({ - scope: scope, - name: event.host_name, - host_id: event.host, - task_id: event.parent, - status: 'skipped', - id: event.id, - created: event.created, - modified: event.modified, - message: msg, - counter: event.counter, - item: (event.event_data && event.event_data.res) ? event.event_data.res.item : '' - }); - } - }; - }]) - - .factory('JobIsFinished', [ function() { - return function(scope) { - return (scope.job_status.status === 'failed' || scope.job_status.status === 'canceled' || - scope.job_status.status === 'error' || scope.job_status.status === 'successful'); - }; - }]) - - .factory('GetElapsed', [function() { - return function(params) { - var start = params.start, - end = params.end, - dt1, dt2, sec, hours, min; - dt1 = new Date(start); - dt2 = new Date(end); - if ( dt2.getTime() !== dt1.getTime() ) { - sec = Math.floor( (dt2.getTime() - dt1.getTime()) / 1000 ); - hours = Math.floor(sec / 3600); - sec = sec - (hours * 3600); - if (('' + hours).length < 2) { - hours = ('00' + hours).substr(-2, 2); - } - min = Math.floor(sec / 60); - sec = sec - (min * 60); - min = ('00' + min).substr(-2,2); - sec = ('00' + sec).substr(-2,2); - return hours + ':' + min + ':' + sec; - } - else { - return '00:00:00'; - } - }; - }]) - - .factory('SetActivePlay', [ function() { - return function(params) { - //find the most recent task in the list of 'active' tasks - - var scope = params.scope, - activeList = [], - newActivePlay, - key; - - for (key in scope.jobData.plays) { - if (scope.jobData.plays[key].taskCount > 0) { - activeList.push(key); - } - } - - if (activeList.length > 0) { - newActivePlay = scope.jobData.plays[activeList[activeList.length - 1]].id; - if (newActivePlay && scope.activePlay && newActivePlay !== scope.activePlay) { - scope.jobData.plays[scope.activePlay].tasks = {}; - scope.jobData.plays[scope.activePlay].playActiveClass = ''; - scope.activeTask = null; - } - if (newActivePlay) { - scope.activePlay = newActivePlay; - scope.jobData.plays[scope.activePlay].playActiveClass = 'JobDetail-tableRow--selected'; - } - } - }; - }]) - - .factory('SetActiveTask', [ function() { - return function(params) { - //find the most recent task in the list of 'active' tasks - var scope = params.scope, - key, - newActiveTask, - activeList = []; - - for (key in scope.jobData.plays[scope.activePlay].tasks) { - if (scope.jobData.plays[scope.activePlay].tasks[key].reportedHosts > 0 || scope.jobData.plays[scope.activePlay].tasks[key].status === 'no-matching-hosts') { - activeList.push(key); - } - } - - if (activeList.length > 0) { - newActiveTask = scope.jobData.plays[scope.activePlay].tasks[activeList[activeList.length - 1]].id; - if (newActiveTask && scope.activeTask && newActiveTask !== scope.activeTask) { - if (scope.activeTask && scope.jobData.plays[scope.activePlay].tasks[scope.activeTask] !== undefined) { - scope.jobData.plays[scope.activePlay].tasks[scope.activeTask].taskActiveClass = ''; - scope.jobData.plays[scope.activePlay].tasks[scope.activeTask].hostResults = {}; - } - } - if (newActiveTask) { - scope.activeTask = newActiveTask; - scope.jobData.plays[scope.activePlay].tasks[scope.activeTask].taskActiveClass = 'JobDetail-tableRow--selected'; - } - } - }; - }]) - - .factory('AddNewPlay', ['SetActivePlay', function(SetActivePlay) { - return function(params) { - var scope = params.scope, - event = params.event, - status, status_text; - - status = (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful'; - status_text = (event.failed) ? 'Failed' : (event.changed) ? 'Changed' : 'OK'; - - scope.jobData.plays[event.id] = { - id: event.id, - name: event.play, - created: event.created, - status: status, - status_text: status_text, - elapsed: '00:00:00', - hostCount: 0, - taskCount: 0, - fistTask: null, - unreachableCount: 0, - status_tip: "Event ID: " + event.id + "
Status: " + status_text, - tasks: {} - }; - - SetActivePlay({ scope: scope }); - }; - }]) - - .factory('AddNewTask', ['UpdatePlayStatus', 'SetActivePlay', 'SetActiveTask', function(UpdatePlayStatus, SetActivePlay, SetActiveTask) { - return function(params) { - var scope = params.scope, - event = params.event, - status, status_text; - - status = (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful'; - status_text = (event.failed) ? 'Failed' : (event.changed) ? 'Changed' : 'OK'; - - scope.jobData.plays[event.parent].tasks[event.id] = { - id: event.id, - play_id: event.parent, - name: (event.task) ? event.task : event.event_display, - status: status, - status_text: status_text, - status_tip: "Event ID: " + event.id + "
Status: " + status_text, - created: event.created, - modified: event.modified, - hostCount: (scope.jobData.plays[event.parent]) ? scope.jobData.plays[event.parent].hostCount : 0, - reportedHosts: 0, - successfulCount: 0, - failedCount: 0, - changedCount: 0, - skippedCount: 0, - unreachableCount: 0, - successfulStyle: { display: 'none'}, - failedStyle: { display: 'none' }, - changedStyle: { display: 'none' }, - skippedStyle: { display: 'none' }, - unreachableStyle: { display: 'none' }, - hostResults: {} - }; - - if (scope.jobData.plays[event.parent].firstTask === undefined || scope.jobData.plays[event.parent].firstTask === null) { - scope.jobData.plays[event.parent].firstTask = event.id; - } - scope.jobData.plays[event.parent].taskCount++; - - SetActivePlay({ scope: scope }); - - SetActiveTask({ scope: scope }); - - UpdatePlayStatus({ - scope: scope, - play_id: event.parent, - failed: event.failed, - changed: event.changed, - modified: event.modified - }); - }; - }]) - - .factory('UpdateJobStatus', ['GetElapsed', 'Empty', 'JobIsFinished', 'longDateFilter', function(GetElapsed, Empty, JobIsFinished, longDateFilter) { - return function(params) { - var scope = params.scope, - failed = params.failed, - modified = params.modified, - started = params.started, - finished = params.finished; - - if (failed && scope.job_status.status !== 'failed' && scope.job_status.status !== 'error' && - scope.job_status.status !== 'canceled') { - scope.job_status.status = 'failed'; - } - if (JobIsFinished(scope) && !Empty(modified)) { - scope.job_status.finished = longDateFilter(modified); - } - if (!Empty(started) && Empty(scope.job_status.started)) { - scope.job_status.started = longDateFilter(modified); - } - if (!Empty(scope.job_status.finished) && !Empty(scope.job_status.started)) { - scope.job_status.elapsed = GetElapsed({ - start: started, - end: finished - }); - } - }; - }]) - - // Update the status of a play - .factory('UpdatePlayStatus', ['GetElapsed', 'UpdateJobStatus', function(GetElapsed, UpdateJobStatus) { - return function(params) { - var scope = params.scope, - failed = params.failed, - changed = params.changed, - id = params.play_id, - modified = params.modified, - no_hosts = params.no_hosts, - play; - - if (scope.jobData.plays[id] !== undefined) { - play = scope.jobData.plays[id]; - if (failed) { - play.status = 'failed'; - play.status_text = 'Failed'; - } - else if (play.status !== 'changed' && play.status !== 'failed') { - // once the status becomes 'changed' or 'failed' don't modify it - if (no_hosts) { - play.status = 'no-matching-hosts'; - play.status_text = 'No matching hosts'; - } else { - play.status = (changed) ? 'changed' : (failed) ? 'failed' : 'successful'; - play.status_text = (changed) ? 'Changed' : (failed) ? 'Failed' : 'OK'; - } - } - play.taskCount = (play.taskCount > 0) ? play.taskCount : 1; // set to a minimum of 1 to force drawing - play.status_tip = "Event ID: " + play.id + "
Status: " + play.status_text; - play.finished = modified; - play.elapsed = GetElapsed({ - start: play.created, - end: modified - }); - //play.status_text = (status_text) ? status_text : play.status; - } - - UpdateJobStatus({ - scope: scope, - failed: null, - modified: modified - }); - }; - }]) - - .factory('UpdateTaskStatus', ['UpdatePlayStatus', 'GetElapsed', function(UpdatePlayStatus, GetElapsed) { - return function(params) { - var scope = params.scope, - failed = params.failed, - changed = params.changed, - id = params.task_id, - modified = params.modified, - no_hosts = params.no_hosts, - play, task; - - // find the task in our hierarchy - for (play in scope.jobData.plays) { - if (scope.jobData.plays[play].tasks[id]) { - task = scope.jobData.plays[play].tasks[id]; - } - } - - if (task) { - if (no_hosts){ - task.status = 'no-matching-hosts'; - task.status_text = 'No matching hosts'; - } - else if (failed) { - task.status = 'failed'; - task.status_text = 'Failed'; - } - else if (task.status !== 'changed' && task.status !== 'failed') { - // once the status becomes 'changed' or 'failed' don't modify it - task.status = (failed) ? 'failed' : (changed) ? 'changed' : 'successful'; - task.status_text = (failed) ? 'Failed' : (changed) ? 'Changed' : 'OK'; - } - task.status_tip = "Event ID: " + task.id + "
Status: " + task.status_text; - task.finished = params.modified; - task.elapsed = GetElapsed({ - start: task.created, - end: modified - }); - - UpdatePlayStatus({ - scope: scope, - failed: failed, - changed: changed, - play_id: task.play_id, - modified: modified, - no_hosts: no_hosts - }); - } - }; - }]) - - // Each time a runner event is received update host summary totals and the parent task - .factory('UpdateHostStatus', ['UpdateTaskStatus', 'AddHostResult', function(UpdateTaskStatus, AddHostResult) { - return function(params) { - var scope = params.scope, - status = params.status, // successful, changed, unreachable, failed, skipped - name = params.name, - event_id = params.id, - host_id = params.host_id, - task_id = params.task_id, - modified = params.modified, - created = params.created, - msg = params.message, - item = params.item, - counter = params.counter; - - if (scope.jobData.hostSummaries[host_id] !== undefined) { - scope.jobData.hostSummaries[host_id].ok += (status === 'successful') ? 1 : 0; - scope.jobData.hostSummaries[host_id].changed += (status === 'changed') ? 1 : 0; - scope.jobData.hostSummaries[host_id].unreachable += (status === 'unreachable') ? 1 : 0; - scope.jobData.hostSummaries[host_id].failed += (status === 'failed') ? 1 : 0; - if (status === 'failed' || status === 'unreachable') { - scope.jobData.hostSummaries[host_id].status = 'failed'; - } - } - else { - scope.jobData.hostSummaries[host_id] = { - id: host_id, - name: name, - ok: (status === 'successful') ? 1 : 0, - changed: (status === 'changed') ? 1 : 0, - unreachable: (status === 'unreachable') ? 1 : 0, - failed: (status === 'failed') ? 1 : 0, - status: (status === 'failed' || status === 'unreachable') ? 'failed' : 'successful' - }; - } - UpdateTaskStatus({ - scope: scope, - task_id: task_id, - failed: ((status === 'failed' || status === 'unreachable') ? true :false), - changed: ((status === 'changed') ? true : false), - modified: modified - }); - - AddHostResult({ - scope: scope, - task_id: task_id, - host_id: host_id, - event_id: event_id, - status: status, - name: name, - created: created, - counter: counter, - message: msg, - item: item - }); - }; - }]) - - // Add a new host result - .factory('AddHostResult', ['SetTaskStyles', 'SetActivePlay', 'SetActiveTask', function(SetTaskStyles, SetActivePlay, SetActiveTask) { - return function(params) { - var scope = params.scope, - task_id = params.task_id, - host_id = params.host_id, - event_id = params.event_id, - status = params.status, - created = params.created, - counter = params.counter, - name = params.name, - msg = params.message, - item = params.item, - status_text = '', - task, play, play_id; - - switch(status) { - case "successful": - status_text = 'OK'; - break; - case "changed": - status_text = "Changed"; - break; - case "failed": - status_text = "Failed"; - break; - case "unreachable": - status_text = "Unreachable"; - break; - case "skipped": - status_text = "Skipped"; - } - - if (typeof item === "object") { - item = JSON.stringify(item); - } - - for (play in scope.jobData.plays) { - for (task in scope.jobData.plays[play].tasks) { - if (parseInt(task,10) === parseInt(task_id,10)) { - play_id = parseInt(play,10); - } - } - } - - if (play_id) { - scope.jobData.plays[play_id].tasks[task_id].hostResults[event_id] = { - id: event_id, - status: status, - status_text: status_text, - host_id: host_id, - task_id: task_id, - name: name, - created: created, - counter: counter, - msg: msg, - item: item - }; - - // increment the unreachable count on the play - if (status === 'unreachable') { - scope.jobData.plays[play_id].unreachableCount++; - } - - // update the task status bar - task = scope.jobData.plays[play_id].tasks[task_id]; - - if (task_id === scope.jobData.plays[play_id].firstTask) { - scope.jobData.plays[play_id].hostCount++; - task.hostCount++; - } - - task.reportedHosts += 1; - task.failedCount += (status === 'failed') ? 1 : 0; - task.changedCount += (status === 'changed') ? 1 : 0; - task.successfulCount += (status === 'successful') ? 1 : 0; - task.skippedCount += (status === 'skipped') ? 1 : 0; - task.unreachableCount += (status === 'unreachable') ? 1 : 0; - - SetTaskStyles({ - task: task - }); - - SetActivePlay({ scope: scope }); - - SetActiveTask({ scope: scope }); - } - }; - }]) - - .factory('SetTaskStyles', [ function() { - return function(params) { - var task = params.task, - diff; - - task.missingCount = task.hostCount - (task.failedCount + task.changedCount + task.skippedCount + task.successfulCount + - task.unreachableCount); - if(task.missingCount<0){ - task.hostCount = (task.failedCount + task.changedCount + task.skippedCount + task.successfulCount + - task.unreachableCount); - } - task.missingPct = (task.hostCount > 0) ? Math.ceil((100 * (task.missingCount / task.hostCount))) : 0; - task.failedPct = (task.hostCount > 0) ? Math.ceil((100 * (task.failedCount / task.hostCount))) : 0; - task.changedPct = (task.hostCount > 0) ? Math.ceil((100 * (task.changedCount / task.hostCount))) : 0; - task.skippedPct = (task.hostCount > 0) ? Math.ceil((100 * (task.skippedCount / task.hostCount))) : 0; - task.successfulPct = (task.hostCount > 0) ? Math.ceil((100 * (task.successfulCount / task.hostCount))) : 0; - task.unreachablePct = (task.hostCount > 0) ? Math.ceil((100 * (task.unreachableCount / task.hostCount))) : 0; - - // cap % at 100 - task.missingPct = (task.missingPct > 100) ? 100 : task.missingPct; - task.failedPct = (task.failedPct > 100) ? 100 : task.failedPct; - task.changedPct = (task.changedPct > 100) ? 100 : task.changedPct; - task.skippedPct = (task.skippedPct > 100) ? 100 : task.skippedPct; - task.successfulPct = ( task.successfulPct > 100) ? 100 : task.successfulPct; - task.unreachablePct = (task.unreachablePct > 100) ? 100 : task.unreachablePct; - - diff = (task.failedPct + task.changedPct + task.skippedPct + task.successfulPct + task.unreachablePct + task.missingPct) - 100; - if (diff > 0) { - if (task.failedPct > diff) { - task.failedPct = task.failedPct - diff; - } - else if (task.changedPct > diff) { - task.changedPct = task.changedPct - diff; - } - else if (task.skippedPct > diff) { - task.skippedPct = task.skippedPct - diff; - } - else if (task.successfulPct > diff) { - task.successfulPct = task.successfulPct - diff; - } - else if (task.unreachablePct > diff) { - task.unreachablePct = task.unreachablePct - diff; - } - else if (task.missingPct > diff) { - task.missingPct = task.missingPct - diff; - } - } - task.successfulStyle = (task.successfulPct > 0) ? { 'display': 'inline-block' }: { 'display': 'none' }; - task.changedStyle = (task.changedPct > 0) ? { 'display': 'inline-block'} : { 'display': 'none' }; - task.skippedStyle = (task.skippedPct > 0) ? { 'display': 'inline-block' } : { 'display': 'none' }; - task.failedStyle = (task.failedPct > 0) ? { 'display': 'inline-block' } : { 'display': 'none' }; - task.unreachableStyle = (task.unreachablePct > 0) ? { 'display': 'inline-block' } : { 'display': 'none' }; - task.missingStyle = (task.missingPct > 0) ? { 'display': 'inline-block' } : { 'display': 'none' }; - }; - }]) - - .factory('LoadPlays', ['Rest', 'ProcessErrors', 'GetElapsed', 'SelectPlay', 'JobIsFinished', - function(Rest, ProcessErrors, GetElapsed, SelectPlay, JobIsFinished) { - return function(params) { - var scope = params.scope, - callback = params.callback, - url; - - scope.plays = []; - - scope.playsLoading = true; - Rest.setUrl(url); - Rest.get() - .success(function(data) { - scope.next_plays = data.next; - scope.plays = []; - data.results.forEach(function(event, idx) { - var status, status_text, start, end, elapsed; - - status = (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful'; - status_text = (event.failed) ? 'Failed' : (event.changed) ? 'Changed' : 'OK'; - start = event.started; - - if (idx < data.results.length - 1) { - // end date = starting date of the next event - end = data.results[idx + 1].started; - } - else if (JobIsFinished(scope)) { - // this is the last play and the job already finished - end = scope.job_status.finished; - } - if (end) { - elapsed = GetElapsed({ - start: start, - end: end - }); - } - else { - elapsed = '00:00:00'; - } - - scope.plays.push({ - id: event.id, - name: event.play, - created: start, - finished: end, - status: status, - status_text: status_text, - status_tip: "Event ID: " + event.id + "
Status: " + status_text, - elapsed: elapsed, - hostCount: 0, - fistTask: null, - playActiveClass: '', - unreachableCount: (event.unreachable_count) ? event.unreachable_count : 0, - }); - }); - - // set the active task - SelectPlay({ - scope: scope, - id: (scope.plays.length > 0) ? scope.plays[0].id : null, - callback: callback - }); - scope.playsLoading = false; - }) - .error(function(data) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - }; - }]) - - // Call when the selected Play needs to change - .factory('SelectPlay', ['LoadTasks', function(LoadTasks) { - return function(params) { - var scope = params.scope, - id = params.id, - callback = params.callback; - - scope.selectedPlay = id; - scope.plays.forEach(function(play, idx) { - if (play.id === scope.selectedPlay) { - scope.plays[idx].playActiveClass = 'JobDetail-tableRow--selected'; - } - else { - scope.plays[idx].playActiveClass = ''; - } - }); - - LoadTasks({ - scope: scope, - callback: callback, - clear: true - }); - - }; - }]) - - .factory('LoadTasks', ['Rest', 'ProcessErrors', 'GetElapsed', 'SelectTask', 'SetTaskStyles', function(Rest, ProcessErrors, GetElapsed, SelectTask, SetTaskStyles) { - return function(params) { - var scope = params.scope, - callback = params.callback, - url, play; - - scope.tasks = []; - if (scope.selectedPlay) { - url = scope.job.url + 'job_tasks/?event_id=' + scope.selectedPlay; - - scope.plays.every(function(p, idx) { - if (p.id === scope.selectedPlay) { - play = scope.plays[idx]; - return false; - } - return true; - }); - - scope.tasksLoading = true; - - Rest.setUrl(url); - Rest.get() - .success(function(data) { - scope.next_tasks = data.next; - scope.tasks = []; - data.results.forEach(function(event, idx) { - var end, elapsed, status, status_text; - - if (play.firstTask === undefined || play.firstTask === null) { - play.firstTask = event.id; - play.hostCount = (event.host_count) ? event.host_count : 0; - } - - if (idx < data.results.length - 1) { - // end date = starting date of the next event - end = data.results[idx + 1].created; - } - else { - // no next event (task), get the end time of the play - scope.plays.every(function(play) { - if (play.id === scope.selectedPlay) { - end = play.finished; - return false; - } - return true; - }); - } - - if (end) { - elapsed = GetElapsed({ - start: event.created, - end: end - }); - } - else { - elapsed = '00:00:00'; - } - - status = (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful'; - status_text = (event.failed) ? 'Failed' : (event.changed) ? 'Changed' : 'OK'; - - scope.tasks.push({ - id: event.id, - play_id: scope.selectedPlay, - name: event.name, - status: status, - status_text: status_text, - status_tip: "Event ID: " + event.id + "
Status: " + status_text, - created: event.created, - modified: event.modified, - finished: end, - elapsed: elapsed, - hostCount: (event.host_count) ? event.host_count : 0, - reportedHosts: (event.reported_hosts) ? event.reported_hosts : 0, - successfulCount: (event.successful_count) ? event.successful_count : 0, - failedCount: (event.failed_count) ? event.failed_count : 0, - changedCount: (event.changed_count) ? event.changed_count : 0, - skippedCount: (event.skipped_count) ? event.skipped_count : 0, - unreachableCount: (event.unreachable_count) ? event.unreachable_count : 0, - taskActiveClass: '' - }); - - if (play.firstTask !== event.id) { - // this is not the first task - scope.tasks[scope.tasks.length - 1].hostCount = play.hostCount; - } - - SetTaskStyles({ - task: scope.tasks[scope.tasks.length - 1] - }); - }); - - // set the active task - SelectTask({ - scope: scope, - id: (scope.tasks.length > 0) ? scope.tasks[0].id : null, - callback: callback - }); - - scope.tasksLoading = false; - - }) - .error(function(data) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - } - else { - SelectTask({ - scope: scope, - id: null, - callback: callback - }); - } - }; - }]) - - // Call when the selected task needs to change - .factory('SelectTask', ['JobDetailService', function(JobDetailService) { - return function(params) { - var scope = params.scope, - id = params.id; - - scope.selectedTask = id; - scope.tasks.forEach(function(task, idx) { - if (task.id === scope.selectedTask) { - scope.tasks[idx].taskActiveClass = 'JobDetail-tableRow--selected'; - } - else { - scope.tasks[idx].taskActiveClass = ''; - } - }); - if (scope.selectedTask !== null){ - params = { - parent: scope.selectedTask, - event__startswith: 'runner', - page_size: scope.hostResultsMaxRows, - order: 'host_name,counter', - }; - - JobDetailService.getRelatedJobEvents(scope.job.id, params).success(function(res){ - scope.hostResults = JobDetailService.processHostEvents(res.results); - scope.hostResultsLoading = false; - }); - } - else{ - scope.hostResults = []; - scope.hostResultsLoading = false; - } - }; - }]) - - .factory('DrawGraph', ['DonutChart', function(DonutChart) { - return function(params) { - var count = params.count, - graph_data = []; - // Ready the data - if (count.ok.length > 0) { - graph_data.push({ - label: 'OK', - value: count.ok.length, - color: '#5CB85C' - }); - } - if (count.changed.length > 0) { - graph_data.push({ - label: 'CHANGED', - value: count.changed.length, - color: '#FF9900' - }); - } - if (count.unreachable.length > 0) { - graph_data.push({ - label: 'UNREACHABLE', - value: count.unreachable.length, - color: '#FF0000' - }); - } - if (count.failures.length > 0) { - graph_data.push({ - label: 'FAILED', - value: count.failures.length, - color: '#D9534F' - }); - } - DonutChart({ - data: graph_data - }); - }; - }]) - - .factory('DonutChart', [function() { - return function(params) { - var dataset = params.data, - element = $("#graph-section"), - colors, total,job_detail_chart; - - colors = _.map(dataset, function(d){ - return d.color; - }); - total = d3.sum(dataset.map(function(d) { - return d.value; - })); - job_detail_chart = nv.models.pieChart() - .margin({bottom: 15}) - .x(function(d) { - return d.label +': '+ Math.floor((d.value/total)*100) + "%"; - }) - .y(function(d) { return d.value; }) - .showLabels(false) - .showLegend(true) - .growOnHover(false) - .labelThreshold(0.01) - .tooltipContent(function(x, y) { - return '

'+x+'

'+ '

' + Math.floor(y.replace(',','')) + ' HOSTS ' + '

'; - }) - .color(colors); - job_detail_chart.legend.rightAlign(false); - job_detail_chart.legend.margin({top: 5, right: 450, left:0, bottom: 0}); - d3.select(element.find('svg')[0]) - .datum(dataset) - .transition().duration(350) - .call(job_detail_chart) - .style({ - "font-family": 'Open Sans', - "font-style": "normal", - "font-weight":400, - "src": "url(/static/assets/OpenSans-Regular.ttf)", - "width": 600, - "height": 300, - "color": '#848992' - }); - d3.select(element.find(".nv-noData")[0]) - .style({ - "text-anchor": 'start' - }); - return job_detail_chart; - }; - }]) - - - .factory('DrawPlays', [function() { - return function(params) { - var scope = params.scope, - idx = 0, - result = [], - newKeys = [], - //plays = JSON.parse(JSON.stringify(scope.jobData.plays)), - plays = scope.jobData.plays, - filteredListX = [], - filteredListB = [], - key, - keys; - - function listSort(a,b) { - if (parseInt(a,10) < parseInt(b,10)) { - return -1; - } - if (parseInt(a,10) > parseInt(b,10)) { - return 1; - } - return 0; - } - - // Only draw plays that are in the 'active' list - for (key in plays) { - if (plays[key].taskCount > 0) { - filteredListX[key] = plays[key]; - } - } - - keys = Object.keys(filteredListB); - keys.sort(function(a,b) { return listSort(a,b); }).reverse(); - for (idx=0; idx < scope.playsMaxRows && idx < keys.length; idx++) { - newKeys.push(keys[idx]); - } - newKeys.sort(function(a,b) { return listSort(a,b); }); - idx = 0; - while (idx < newKeys.length) { - result.push(filteredListB[newKeys[idx]]); - idx++; - } - setTimeout( function() { - scope.$apply( function() { - scope.plays = result; - scope.selectedPlay = scope.activePlay; - if (scope.liveEventProcessing) { - $('#plays-table-detail').scrollTop($('#plays-table-detail').prop("scrollHeight")); - } - }); - }); - }; - }]) - - .factory('DrawTasks', [ function() { - return function(params) { - var scope = params.scope, - result = [], - filteredListX = [], - filteredListB = [], - idx, key, keys, newKeys, tasks, t; - - function listSort(a,b) { - if (parseInt(a,10) < parseInt(b,10)) { - return -1; - } - if (parseInt(a,10) > parseInt(b,10)) { - return 1; - } - return 0; - } - - if (scope.activePlay && scope.jobData.plays[scope.activePlay]) { - - //tasks = JSON.parse(JSON.stringify(scope.jobData.plays[scope.activePlay].tasks)); - tasks = scope.jobData.plays[scope.activePlay].tasks; - - // Only draw tasks that are in the 'active' list - for (key in tasks) { - t = tasks[key]; - if (t.reportedHosts > 0 || t.hostCount > 0 || t.successfulCount >0 || t.failedCount > 0 || - t.changedCount > 0 || t.skippedCount > 0 || t.unreachableCount > 0) { - filteredListX[key] = tasks[key]; - } - } - - keys = Object.keys(filteredListB); - keys.sort(function(a,b) { return listSort(a,b); }).reverse(); - newKeys = []; - for (idx=0; result.length < scope.tasksMaxRows && idx < keys.length; idx++) { - newKeys.push(keys[idx]); - } - newKeys.sort(function(a,b) { return listSort(a,b); }); - idx = 0; - while (idx < newKeys.length) { - result.push(filteredListB[newKeys[idx]]); - idx++; - } - } - - setTimeout( function() { - scope.$apply( function() { - scope.tasks = result; - scope.selectedTask = scope.activeTask; - if (scope.liveEventProcessing) { - $('#tasks-table-detail').scrollTop($('#tasks-table-detail').prop("scrollHeight")); - } - }); - }); - - }; - }]) - - .factory('DrawHostResults', [ function() { - return function(params) { - var scope = params.scope, - result = [], - filteredListB = [], - idx = 0, - hostResults, - keys; - - if (scope.activePlay && scope.activeTask && scope.jobData.plays[scope.activePlay] && - scope.jobData.plays[scope.activePlay].tasks[scope.activeTask]) { - - hostResults = scope.jobData.plays[scope.activePlay].tasks[scope.activeTask].hostResults; - - keys = Object.keys(filteredListB); - keys.sort(function compare(a, b) { - if (filteredListB[a].name === filteredListB[b].name) { - if (filteredListB[a].counter < filteredListB[b].counter) { - return -1; - } - if (filteredListB[a].counter >filteredListB[b].counter) { - return 1; - } - } else { - if (filteredListB[a].name < filteredListB[b].name) { - return -1; - } - if (filteredListB[a].name > filteredListB[b].name) { - return 1; - } - } - // a must be equal to b - return 0; - }); - while (idx < keys.length && result.length < scope.hostResultsMaxRows) { - result.push(filteredListB[keys[idx]]); - idx++; - } - } - - setTimeout( function() { - scope.$apply( function() { - scope.hostResults = result; - if (scope.liveEventProcessing) { - $('#hosts-table-detail').scrollTop($('#hosts-table-detail').prop("scrollHeight")); - } - }); - }); - - }; - }]) - - .factory('UpdateDOM', ['DrawPlays', 'DrawTasks', 'DrawHostResults', - function(DrawPlays, DrawTasks, DrawHostResults) { - return function(params) { - var scope = params.scope; - if (!scope.pauseLiveEvents) { - DrawPlays({ scope: scope }); - DrawTasks({ scope: scope }); - DrawHostResults({ scope: scope }); - } - - setTimeout(function() { - scope.playsLoading = false; - scope.tasksLoading = false; - scope.hostResultsLoading = false; - },100); - }; - }]); diff --git a/awx/ui/client/src/job-detail/host-event/host-event-codemirror.partial.html b/awx/ui/client/src/job-detail/host-event/host-event-codemirror.partial.html deleted file mode 100644 index 7c744d2169..0000000000 --- a/awx/ui/client/src/job-detail/host-event/host-event-codemirror.partial.html +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/awx/ui/client/src/job-detail/host-event/host-event-details.partial.html b/awx/ui/client/src/job-detail/host-event/host-event-details.partial.html deleted file mode 100644 index c287788f19..0000000000 --- a/awx/ui/client/src/job-detail/host-event/host-event-details.partial.html +++ /dev/null @@ -1,45 +0,0 @@ -
-
EVENT
-
- HOST - - {{event.host_name || "No result found"}} -
-
- STATUS - - - - - {{processEventStatus(event).status || "No result found"}} - -
-
- ID - {{event.id || "No result found"}} -
-
- CREATED - {{(event.created | longDate) || "No result found"}} -
-
- PLAY - {{event.play || "No result found"}} -
-
- TASK - {{event.task || "No result found"}} -
-
- MODULE - {{event.event_data.res.invocation.module_name || "No result found"}} -
-
-
-
RESULTS
- -
- {{key}} - {{value}} -
-
diff --git a/awx/ui/client/src/job-detail/host-event/host-event-modal.partial.html b/awx/ui/client/src/job-detail/host-event/host-event-modal.partial.html deleted file mode 100644 index db236894e8..0000000000 --- a/awx/ui/client/src/job-detail/host-event/host-event-modal.partial.html +++ /dev/null @@ -1,36 +0,0 @@ - diff --git a/awx/ui/client/src/job-detail/host-event/host-event-timing.partial.html b/awx/ui/client/src/job-detail/host-event/host-event-timing.partial.html deleted file mode 100644 index 06171bd1c5..0000000000 --- a/awx/ui/client/src/job-detail/host-event/host-event-timing.partial.html +++ /dev/null @@ -1 +0,0 @@ -
timing
\ No newline at end of file diff --git a/awx/ui/client/src/job-detail/host-event/host-event.block.less b/awx/ui/client/src/job-detail/host-event/host-event.block.less deleted file mode 100644 index 9b31b74e87..0000000000 --- a/awx/ui/client/src/job-detail/host-event/host-event.block.less +++ /dev/null @@ -1,150 +0,0 @@ -// @import "./client/src/shared/branding/colors.less"; -// @import "./client/src/shared/branding/colors.default.less"; -// @import "./client/src/shared/layouts/one-plus-two.less"; -// -// .noselect { -// -webkit-touch-callout: none; /* iOS Safari */ -// -webkit-user-select: none; /* Chrome/Safari/Opera */ -// -khtml-user-select: none; /* Konqueror */ -// -moz-user-select: none; /* Firefox */ -// -ms-user-select: none; /* Internet Explorer/Edge */ -// user-select: none; /* Non-prefixed version, currently -// not supported by any browser */ -// } -// -// @media screen and (min-width: 768px){ -// .HostEvent .modal-dialog{ -// width: 700px; -// } -// } -// .HostEvent .CodeMirror{ -// overflow-x: hidden; -// } -// .HostEvent-controls button.HostEvent-close{ -// color: #FFFFFF; -// text-transform: uppercase; -// padding-left: 15px; -// padding-right: 15px; -// background-color: @default-link; -// border-color: @default-link; -// &:hover{ -// background-color: @default-link-hov; -// border-color: @default-link-hov; -// } -// } -// .HostEvent-body{ -// margin-bottom: 10px; -// } -// .HostEvent-tab { -// color: @btn-txt; -// background-color: @btn-bg; -// font-size: 12px; -// border: 1px solid @btn-bord; -// height: 30px; -// border-radius: 5px; -// margin-right: 20px; -// padding-left: 10px; -// padding-right: 10px; -// padding-bottom: 5px; -// padding-top: 5px; -// transition: background-color 0.2s; -// text-transform: uppercase; -// text-align: center; -// white-space: nowrap; -// .noselect; -// } -// .HostEvent-tab:hover { -// color: @btn-txt; -// background-color: @btn-bg-hov; -// cursor: pointer; -// } -// .HostEvent-tab--selected{ -// color: @btn-txt-sel!important; -// background-color: @default-icon!important; -// border-color: @default-icon!important; -// } -// .HostEvent-view--container{ -// width: 100%; -// display: flex; -// flex-direction: row; -// flex-wrap: nowrap; -// justify-content: space-between; -// } -// .HostEvent .modal-footer{ -// border: 0; -// margin-top: 0px; -// padding-top: 5px; -// } -// .HostEvent-controls{ -// float: right; -// button { -// margin-left: 10px; -// } -// } -// .HostEvent-status--ok{ -// color: @green; -// } -// .HostEvent-status--unreachable{ -// color: @unreachable; -// } -// .HostEvent-status--changed{ -// color: @changed; -// } -// .HostEvent-status--failed{ -// color: @default-err; -// } -// .HostEvent-status--skipped{ -// color: @skipped; -// } -// .HostEvent-title{ -// color: @default-interface-txt; -// font-weight: 600; -// margin-bottom: 8px; -// } -// // .HostEvent .modal-body{ -// // max-height: 500px; -// // overflow-y: auto; -// // padding: 20px; -// // } -// .HostEvent-nav{ -// padding-top: 12px; -// padding-bottom: 12px; -// } -// .HostEvent-field{ -// margin-bottom: 8px; -// flex: 0 1 12em; -// } -// .HostEvent-field--label{ -// text-transform: uppercase; -// flex: 0 1 80px; -// max-width: 80px; -// font-size: 12px; -// word-wrap: break-word; -// } -// .HostEvent-field{ -// .OnePlusTwo-left--detailsRow; -// } -// .HostEvent-field--content{ -// word-wrap: break-word; -// max-width: 13em; -// flex: 0 1 13em; -// } -// .HostEvent-details--left, .HostEvent-details--right{ -// flex: 1 1 47%; -// } -// .HostEvent-details--left{ -// margin-right: 40px; -// } -// .HostEvent-details--right{ -// .HostEvent-field--label{ -// flex: 0 1 25em; -// } -// .HostEvent-field--content{ -// max-width: 15em; -// flex: 0 1 15em; -// align-self: flex-end; -// } -// } -// .HostEvent-button:disabled { -// pointer-events: all!important; -// } diff --git a/awx/ui/client/src/job-detail/host-event/host-event.controller.js b/awx/ui/client/src/job-detail/host-event/host-event.controller.js deleted file mode 100644 index f86452b005..0000000000 --- a/awx/ui/client/src/job-detail/host-event/host-event.controller.js +++ /dev/null @@ -1,110 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - - export default - ['$stateParams', '$scope', '$state', 'Wait', 'JobDetailService', 'hostEvent', 'hostResults', - function($stateParams, $scope, $state, Wait, JobDetailService, hostEvent, hostResults){ - - $scope.processEventStatus = JobDetailService.processEventStatus; - $scope.hostResults = []; - // Avoid rendering objects in the details fieldset - // ng-if="processResults(value)" via host-event-details.partial.html - $scope.processResults = function(value){ - if (typeof value === 'object'){return false;} - else {return true;} - }; - $scope.isStdOut = function(){ - if ($state.current.name === 'jobDetails.host-event.stdout' || $state.current.name === 'jobDetaisl.histe-event.stderr'){ - return 'StandardOut-preContainer StandardOut-preContent'; - } - }; - /*ignore jslint start*/ - var initCodeMirror = function(el, data, mode){ - var container = document.getElementById(el); - var editor = CodeMirror.fromTextArea(container, { // jshint ignore:line - lineNumbers: true, - mode: mode - }); - editor.setSize("100%", 300); - editor.getDoc().setValue(data); - }; - /*ignore jslint end*/ - $scope.isActiveState = function(name){ - return $state.current.name === name; - }; - - $scope.getActiveHostIndex = function(){ - var result = $scope.hostResults.filter(function( obj ) { - return obj.id === $scope.event.id; - }); - return $scope.hostResults.indexOf(result[0]); - }; - - $scope.showPrev = function(){ - return $scope.getActiveHostIndex() !== 0; - }; - - $scope.showNext = function(){ - return $scope.getActiveHostIndex() < $scope.hostResults.indexOf($scope.hostResults[$scope.hostResults.length - 1]); - }; - - $scope.goNext = function(){ - var index = $scope.getActiveHostIndex() + 1; - var id = $scope.hostResults[index].id; - $state.go('jobDetail.host-event.details', {eventId: id}); - }; - - $scope.goPrev = function(){ - var index = $scope.getActiveHostIndex() - 1; - var id = $scope.hostResults[index].id; - $state.go('jobDetail.host-event.details', {eventId: id}); - }; - - var init = function(){ - $scope.event = _.cloneDeep(hostEvent); - $scope.hostResults = hostResults; - $scope.json = JobDetailService.processJson(hostEvent); - - // grab standard out & standard error if present, and remove from the results displayed in the details panel - if (hostEvent.event_data.res.stdout){ - $scope.stdout = hostEvent.event_data.res.stdout; - delete $scope.event.event_data.res.stdout; - } - if (hostEvent.event_data.res.stderr){ - $scope.stderr = hostEvent.event_data.res.stderr; - delete $scope.event.event_data.res.stderr; - } - // instantiate Codemirror - // try/catch pattern prevents the abstract-state controller from complaining about element being null - if ($state.current.name === 'jobDetail.host-event.json'){ - try{ - initCodeMirror('HostEvent-codemirror', JSON.stringify($scope.json, null, 4), {name: "javascript", json: true}); - } - catch(err){ - // element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController - } - } - else if ($state.current.name === 'jobDetail.host-event.stdout'){ - try{ - initCodeMirror('HostEvent-codemirror', $scope.stdout, 'shell'); - } - catch(err){ - // element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController - } - } - else if ($state.current.name === 'jobDetail.host-event.stderr'){ - try{ - initCodeMirror('HostEvent-codemirror', $scope.stderr, 'shell'); - } - catch(err){ - // element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController - } - } - $('#HostEvent').modal('show'); - }; - init(); - }]; diff --git a/awx/ui/client/src/job-detail/host-event/host-event.route.js b/awx/ui/client/src/job-detail/host-event/host-event.route.js deleted file mode 100644 index 86e499c2b0..0000000000 --- a/awx/ui/client/src/job-detail/host-event/host-event.route.js +++ /dev/null @@ -1,65 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import { templateUrl } from '../../shared/template-url/template-url.factory'; - -var hostEventModal = { - name: 'jobDetail.host-event', - url: '/task/:taskId/host-event/:eventId', - controller: 'HostEventController', - templateUrl: templateUrl('job-detail/host-event/host-event-modal'), - 'abstract': true, - resolve: { - hostEvent: ['JobDetailService', '$stateParams', function(JobDetailService, $stateParams) { - return JobDetailService.getRelatedJobEvents($stateParams.id, { - id: $stateParams.eventId - }).then(function(res) { - return res.data.results[0]; }); - }], - hostResults: ['JobDetailService', '$stateParams', function(JobDetailService, $stateParams) { - return JobDetailService.getJobEventChildren($stateParams.taskUuid).then(res => res.data.results); - }] - }, - onExit: function() { - // close the modal - // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - $('#HostEvent').modal('hide'); - // hacky way to handle user browsing away via URL bar - $('.modal-backdrop').remove(); - $('body').removeClass('modal-open'); - } -}; - -var hostEventDetails = { - name: 'jobDetail.host-event.details', - url: '/details', - controller: 'HostEventController', - templateUrl: templateUrl('job-detail/host-event/host-event-details'), -}; - -var hostEventJson = { - name: 'jobDetail.host-event.json', - url: '/json', - controller: 'HostEventController', - templateUrl: templateUrl('job-detail/host-event/host-event-codemirror') -}; - -var hostEventStdout = { - name: 'jobDetail.host-event.stdout', - url: '/stdout', - controller: 'HostEventController', - templateUrl: templateUrl('job-detail/host-event/host-event-codemirror') -}; - -var hostEventStderr = { - name: 'jobDetail.host-event.stderr', - url: '/stderr', - controller: 'HostEventController', - templateUrl: templateUrl('job-detail/host-event/host-event-codemirror') -}; - - -export { hostEventDetails, hostEventJson, hostEventModal, hostEventStdout, hostEventStderr }; diff --git a/awx/ui/client/src/job-detail/host-event/main.js b/awx/ui/client/src/job-detail/host-event/main.js deleted file mode 100644 index 4379427ff8..0000000000 --- a/awx/ui/client/src/job-detail/host-event/main.js +++ /dev/null @@ -1,21 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - import {hostEventModal, hostEventDetails, - hostEventJson, hostEventStdout, hostEventStderr} from './host-event.route'; - import controller from './host-event.controller'; - - export default - angular.module('jobDetail.hostEvent', []) - .controller('HostEventController', controller) - - .run(['$stateExtender', function($stateExtender){ - $stateExtender.addState(hostEventModal); - $stateExtender.addState(hostEventDetails); - $stateExtender.addState(hostEventJson); - $stateExtender.addState(hostEventStdout); - $stateExtender.addState(hostEventStderr); - }]); diff --git a/awx/ui/client/src/job-detail/host-events/host-events.block.less b/awx/ui/client/src/job-detail/host-events/host-events.block.less deleted file mode 100644 index df52944cf0..0000000000 --- a/awx/ui/client/src/job-detail/host-events/host-events.block.less +++ /dev/null @@ -1,93 +0,0 @@ -@import "./client/src/shared/branding/colors.less"; -@import "./client/src/shared/branding/colors.default.less"; - -.HostEvents .CodeMirror{ - border: none; -} -.HostEvents .modal-footer{ - border: 0; - margin-top: 0px; - padding: 0px 20px 20px 20px; -} -button.HostEvents-close{ - width: 70px; - color: #FFFFFF!important; - text-transform: uppercase; - padding-left: 15px; - padding-right: 15px; - background-color: @default-link; - border-color: @default-link; - &:hover{ - background-color: @default-link-hov; - border-color: @default-link-hov; - } -} -.HostEvents-status--ok{ - color: @green; -} -.HostEvents-status--unreachable{ - color: @unreachable; -} -.HostEvents-status--changed{ - color: @changed; -} -.HostEvents-status--failed{ - color: @default-err; -} -.HostEvents-status--skipped{ - color: @skipped; -} - -.HostEvents-filter--form{ - padding-top: 15px; - padding-bottom: 15px; - float: right; - display: inline-block; -} -.HostEvents .modal-body{ - padding: 20px; -} -.HostEvents .select2-container{ - text-transform: capitalize; - max-width: 220px; - float: right; -} -.HostEvents-form--container{ - padding-top: 15px; - padding-bottom: 15px; -} -.HostEvents-title{ - text-transform: uppercase; - color: @default-interface-txt; - font-weight: 600; -} -.HostEvents-status i { - padding-right: 10px; -} -.HostEvents-table--header { - height: 30px; - font-size: 14px; - font-weight: normal; - text-transform: uppercase; - color: @default-interface-txt; - background-color: @default-list-header-bg; - padding-left: 15px; - padding-right: 15px; - border-bottom-width: 0px; -} -.HostEvents-table--header:first-of-type{ - border-top-left-radius: 5px; -} -.HostEvents-table--header:last-of-type{ - border-top-right-radius: 5px; -} -.HostEvents-table--row{ - color: @default-data-txt; - border: 0 !important; -} -.HostEvents-table--row:nth-child(odd){ - background: @default-tertiary-bg; -} -.HostEvents-table--cell{ - border: 0 !important; -} diff --git a/awx/ui/client/src/job-detail/host-events/host-events.controller.js b/awx/ui/client/src/job-detail/host-events/host-events.controller.js deleted file mode 100644 index 5664c89877..0000000000 --- a/awx/ui/client/src/job-detail/host-events/host-events.controller.js +++ /dev/null @@ -1,48 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - export default - ['$stateParams', '$scope', '$rootScope', '$state', 'Wait', - 'JobDetailService', 'CreateSelect2', 'hosts', - function($stateParams, $scope, $rootScope, $state, Wait, - JobDetailService, CreateSelect2, hosts){ - - // pagination not implemented yet, but it'll depend on this - $scope.page_size = $stateParams.page_size; - - $scope.processEventStatus = JobDetailService.processEventStatus; - $scope.activeFilter = $stateParams.filter || null; - - $scope.filters = ['all', 'changed', 'failed', 'ok', 'unreachable', 'skipped']; - - // watch select2 for changes - $('.HostEvents-select').on("select2:select", function () { - $scope.activeFilter = $('.HostEvents-select').val(); - }); - - var init = function(){ - $scope.hostName = $stateParams.hostName; - // create filter dropdown - CreateSelect2({ - element: '.HostEvents-select', - multiple: false - }); - // process the filter if one was passed - if ($stateParams.filter){ - $scope.activeFilter = $stateParams.filter; - - $('#HostEvents').modal('show'); - } - else{ - $scope.results = hosts.data.results; - $('#HostEvents').modal('show'); - } - }; - - - init(); - - }]; diff --git a/awx/ui/client/src/job-detail/host-events/host-events.partial.html b/awx/ui/client/src/job-detail/host-events/host-events.partial.html deleted file mode 100644 index 00bacf066c..0000000000 --- a/awx/ui/client/src/job-detail/host-events/host-events.partial.html +++ /dev/null @@ -1,52 +0,0 @@ - diff --git a/awx/ui/client/src/job-detail/host-events/host-events.route.js b/awx/ui/client/src/job-detail/host-events/host-events.route.js deleted file mode 100644 index 835d17b2b9..0000000000 --- a/awx/ui/client/src/job-detail/host-events/host-events.route.js +++ /dev/null @@ -1,32 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import {templateUrl} from '../../shared/template-url/template-url.factory'; - -export default { - name: 'jobDetail.host-events', - url: '/host-events/{hostName:any}?:filter', - controller: 'HostEventsController', - params: { - page_size: 10 - }, - templateUrl: templateUrl('job-detail/host-events/host-events'), - onExit: function(){ - // close the modal - // using an onExit event to handle cases where the user navs away using the url bar / back and not modal "X" - $('#HostEvents').modal('hide'); - // hacky way to handle user browsing away via URL bar - $('.modal-backdrop').remove(); - $('body').removeClass('modal-open'); - }, - resolve: { - hosts: ['JobDetailService','$stateParams', function(JobDetailService, $stateParams) { - return JobDetailService.getRelatedJobEvents($stateParams.id, { - host_name: $stateParams.hostName - }).success(function(res){ return res.results[0];}); - }] - } -}; diff --git a/awx/ui/client/src/job-detail/host-events/main.js b/awx/ui/client/src/job-detail/host-events/main.js deleted file mode 100644 index 766dd92ca4..0000000000 --- a/awx/ui/client/src/job-detail/host-events/main.js +++ /dev/null @@ -1,15 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import route from './host-events.route'; -import controller from './host-events.controller'; - -export default - angular.module('jobDetail.hostEvents', []) - .controller('HostEventsController', controller) - .run(['$stateExtender', function($stateExtender){ - $stateExtender.addState(route); - }]); diff --git a/awx/ui/client/src/job-detail/host-summary/host-summary.block.less b/awx/ui/client/src/job-detail/host-summary/host-summary.block.less deleted file mode 100644 index 00af0d30ea..0000000000 --- a/awx/ui/client/src/job-detail/host-summary/host-summary.block.less +++ /dev/null @@ -1,17 +0,0 @@ -@import '../../shared/branding/colors.default.less'; -.HostSummary-graph--successful{ - text-anchor: start !important; -} -.HostSummary-graph--failed{ - text-anchor: end !important; -} -.HostSummary-graph--changed{ - text-anchor: start !important; -} -.HostSummary-loading{ - border: none; -} -.HostSummary-loading{ - padding-left: 0px !important; - color: @default-interface-txt; -} diff --git a/awx/ui/client/src/job-detail/host-summary/host-summary.controller.js b/awx/ui/client/src/job-detail/host-summary/host-summary.controller.js deleted file mode 100644 index cd5a241622..0000000000 --- a/awx/ui/client/src/job-detail/host-summary/host-summary.controller.js +++ /dev/null @@ -1,131 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - - export default - ['$scope', '$rootScope', '$stateParams', 'Wait', 'JobDetailService', 'DrawGraph', function($scope, $rootScope, $stateParams, Wait, JobDetailService, DrawGraph){ - - var page_size = 200; - $scope.loading = $scope.hosts.length > 0 ? false : true; - $scope.filter = 'all'; - - var buildGraph = function(hosts){ - // status waterfall: unreachable > failed > changed > ok > skipped - var count; - count = { - ok : _.filter(hosts, function(o){ - return o.failures === 0 && o.changed === 0 && o.ok > 0; - }), - skipped : _.filter(hosts, function(o){ - return o.skipped > 0; - }), - unreachable : _.filter(hosts, function(o){ - return o.dark > 0; - }), - failures : _.filter(hosts, function(o){ - return o.failed === true; - }), - changed : _.filter(hosts, function(o){ - return o.changed > 0; - }) - }; - return count; - }; - var init = function(){ - Wait('start'); - JobDetailService.getJobHostSummaries($stateParams.id, {page_size: page_size, order_by: 'host_name'}) - .success(function(res){ - $scope.hosts = res.results; - $scope.next = res.next; - $scope.count = buildGraph(res.results); - Wait('stop'); - DrawGraph({count: $scope.count, resize:true}); - }); - JobDetailService.getJob({id: $stateParams.id}) - .success(function(res){ - $scope.status = res.results[0].status; - }); - }; - if ($rootScope.removeJobSummaryComplete) { - $rootScope.removeJobSummaryComplete(); - } - // emitted by the API in the same function used to persist host summary data - // JobEvent.update_host_summary_from_stats() from /awx/main.models.jobs.py - $scope.$on('ws-jobs-summary', function(e, data) { - // discard socket msgs we don't care about in this context - if (parseInt($stateParams.id) === data.unified_job_id){ - init(); - } - }); - - $scope.$on('ws-jobs', function(e, data) { - if (parseInt($stateParams.id) === data.unified_job_id){ - $scope.status = data.status; - } - }); - - - $scope.buildTooltip = function(n, status){ - var grammar = function(n, status){ - var dict = { - 0: 'No host events were ', - 1: ' host event was ', - 2: ' host events were ' - }; - if (n >= 2){ - return n + dict[2] + status; - } - else{ - return n !== 0 ? n + dict[n] + status : dict[n] + status; - } - }; - return grammar(n, status); - }; - $scope.getNextPage = function(){ - if ($scope.next){ - JobDetailService.getNextPage($scope.next).success(function(res){ - res.results.forEach(function(key, index){ - $scope.hosts.push(res.results[index]); - }); - $scope.hosts.push(res.results); - $scope.next = res.next; - }); - } - }; - - $scope.setFilter = function(filter){ - $scope.filter = filter; - var getAll = function(){ - Wait('start'); - JobDetailService.getJobHostSummaries($stateParams.id, { - page_size: page_size, - order_by: 'host_name' - }).success(function(res){ - Wait('stop'); - $scope.hosts = res.results; - $scope.next = res.next; - }); - }; - var getFailed = function(){ - Wait('start'); - JobDetailService.getJobHostSummaries($stateParams.id, { - page_size: page_size, - failed: true, - order_by: 'host_name' - }).success(function(res){ - Wait('stop'); - $scope.hosts = res.results; - $scope.next = res.next; - }); - }; - $scope.get = filter === 'all' ? getAll() : getFailed(); - }; - - init(); - // calling the init routine twice will size the d3 chart correctly - no idea why - // instantiating the graph inside a setTimeout() SHOULD have the same effect, but it doesn't - // instantiating the graph further down the promise chain e.g. .then() or .finally() also does not work - init(); - }]; diff --git a/awx/ui/client/src/job-detail/host-summary/host-summary.partial.html b/awx/ui/client/src/job-detail/host-summary/host-summary.partial.html deleted file mode 100644 index 5452990e76..0000000000 --- a/awx/ui/client/src/job-detail/host-summary/host-summary.partial.html +++ /dev/null @@ -1,72 +0,0 @@ - -
-
4 Please select a host below to view a summary of all associated tasks.
-
-
-
-
- - - -
-
-
-
-
- - -
-
-
- -
- - - - - - - -
HostsCompleted Tasks
-
- -
- - - - - - - - - - - - - - - - -
- {{ host.host_name }} - - {{ host.ok - host.changed }} - {{ host.changed }} - {{ host.skipped }} - {{ host.dark }} - {{ host.failures }} -
Initiating job run.
Job is running. Summary will be available on completion.
No matching hosts
-
- -
- -
- -
-
Host Status Summary
-
- -
diff --git a/awx/ui/client/src/job-detail/host-summary/host-summary.route.js b/awx/ui/client/src/job-detail/host-summary/host-summary.route.js deleted file mode 100644 index a2de70e5d4..0000000000 --- a/awx/ui/client/src/job-detail/host-summary/host-summary.route.js +++ /dev/null @@ -1,21 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import {templateUrl} from '../../shared/template-url/template-url.factory'; - -export default { - name: 'jobDetail.host-summary', - url: '/event-summary', - views:{ - 'host-summary': { - controller: 'HostSummaryController', - templateUrl: templateUrl('job-detail/host-summary/host-summary'), - } - }, - ncyBreadcrumb: { - skip: true // Never display this state in breadcrumb. - } -}; diff --git a/awx/ui/client/src/job-detail/host-summary/main.js b/awx/ui/client/src/job-detail/host-summary/main.js deleted file mode 100644 index fad85ffaf3..0000000000 --- a/awx/ui/client/src/job-detail/host-summary/main.js +++ /dev/null @@ -1,15 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -import route from './host-summary.route'; -import controller from './host-summary.controller'; - -export default - angular.module('jobDetail.hostSummary', []) - .controller('HostSummaryController', controller) - .run(['$stateExtender', function($stateExtender){ - $stateExtender.addState(route); - }]); \ No newline at end of file diff --git a/awx/ui/client/src/job-detail/job-detail.block.less b/awx/ui/client/src/job-detail/job-detail.block.less deleted file mode 100644 index c23b8d321d..0000000000 --- a/awx/ui/client/src/job-detail/job-detail.block.less +++ /dev/null @@ -1,240 +0,0 @@ -/** @define SetupItem */ - -@import '../shared/branding/colors.less'; -@import '../shared/branding/colors.default.less'; -@import '../shared/layouts/one-plus-two.less'; - -@breakpoint-md: 1200px; -@breakpoint-sm: 623px; - -.JobDetail-tasks.section{ - margin-top:40px; -} -.JobDetail-instructions{ - color: @default-interface-txt; - margin: 10px 0 10px 0; - - .badge { - background-color: @default-list-header-bg; - color: @default-interface-txt; - padding: 5px 7px; - } -} -.JobDetail{ - .OnePlusTwo-container(100%, @breakpoint-md); - - &.fullscreen { - .JobDetail-rightSide { - max-width: 100%; - } - } -} - -.JobDetail-leftSide{ - .OnePlusTwo-left--panel(100%, @breakpoint-md); -} - -.JobDetail-rightSide{ - .OnePlusTwo-right--panel(100%, @breakpoint-md); - @media (max-width: @breakpoint-md - 1px) { - padding-right: 15px; - } -} -.JobDetail-panelHeader{ - display: flex; - height: 30px; -} -.JobDetail-expandContainer{ - flex: 1; - margin: 0px; - line-height: 30px; - white-space: nowrap; -} - -.JobDetail-panelHeaderText{ - color: @default-interface-txt; - flex: 1 0 auto; - font-size: 14px; - font-weight: bold; - margin-right: 10px; - text-transform: uppercase; -} - -.JobDetail-panelHeaderText:hover{ - color: @default-interface-txt; - font-size: 14px; - font-weight: bold; - margin-right: 10px; - text-transform: uppercase; -} - -.JobDetail-expandArrow{ - color: @default-icon-hov; - font-size: 14px; - font-weight: bold; - margin-right: 10px; - text-transform: uppercase; - margin-left: 10px; -} - -.JobDetail-resultsDetails{ - display: flex; - flex-wrap: wrap; - flex-direction: row; - padding-top: 25px; - @media screen and(max-width: @breakpoint-sm){ - flex-direction: column; - } -} - -.JobDetail-resultRow{ - width: 100%; - display: flex; - padding-bottom: 10px; - flex-wrap: wrap; -} - -.JobDetail-resultRow--variables { - flex-direction: column; -} - -.JobDetail-resultRowLabel{ - text-transform: uppercase; - color: @default-interface-txt; - font-size: 14px; - font-weight: normal!important; - width: 30%; - margin-right: 20px; - @media screen and(max-width: @breakpoint-md){ - flex: 2.5 0 auto; - } -} - -.JobDetail-resultRowLabel--fullWidth { - width: 100%; - margin-right: 0px; -} - -.JobDetail-resultRowText{ - width: ~"calc(70% - 20px)"; - flex: 1 0 auto; - text-transform: none; - word-wrap: break-word; -} - -.JobDetail-resultRowText--fullWidth { - width: 100%; -} - -.JobDetail-searchHeaderRow{ - display: flex; - flex-wrap: wrap; - flex-direction: row; - height: 50px; - margin-top: 20px; - @media screen and(max-width: @breakpoint-sm){ - height: auto; - } -} - -.JobDetail-searchContainer{ - flex: 2 0 auto; - @media screen and(max-width: @breakpoint-sm){ - margin-bottom: 0px; - } -} - -.JobDetail-tableToggleContainer{ - flex: 1 0 auto; - display: flex; - justify-content: flex-end; -} - -.JobDetail-tableToggle{ - padding-left:10px; - padding-right: 10px; - border: 1px solid @d7grey; -} - -.JobDetail-tableToggle.active{ - background-color: @default-link; - border: 1px solid @default-link; - color: @default-bg; - - &:hover { - background-color: @default-link-hov; - } -} -.JobDetail .nvd3.nv-noData{ - color: @default-interface-txt; - font-size: 12px; - text-transform: uppercase; - font-family: 'Open Sans', sans-serif; -} -.JobDetail .nv-series{ - padding-right: 30px; - display: block; -} -.JobDetail-instructions .badge{ - background-color: @default-list-header-bg; - color: @default-interface-txt; -} -.JobDetail-tableToggle--left{ - border-top-left-radius: 5px; - border-bottom-left-radius: 5px; -} - -.JobDetail-tableToggle--right{ - border-top-right-radius: 5px; - border-bottom-right-radius: 5px; -} - -.JobDetail-searchInput{ - border-radius: 5px !important; -} - -.JobDetail-tableHeader:last-of-type{ - text-align:justify; -} - -.JobDetail-statusIcon{ - padding-right: 10px; - padding-left: 10px; -} - -.JobDetail-tableRow--selected, -.JobDetail-tableRow--selected > :first-child{ - border-left: 5px solid @list-row-select-bord; -} - -.JobDetail-tableRow--selected > :first-child > .JobDetail-statusIcon{ - margin-left: -5px; -} - -.JobDetails-table--noResults { - tr > td { - border-top: none !important; - } -} - -.JobDetail-statusIcon--results{ - padding-left: 0px; - padding-right: 10px; -} - -.JobDetail-graphSection{ - height: 320px; - width:100%; -} - -.JobDetail-stdoutActionButton--active{ - display: none; - visibility: hidden; - flex:none; - width:0px; - padding-right: 0px; -} - -.JobDetail-leftSide.JobDetail-stdoutActionButton--active { - margin-right: 0px; -} diff --git a/awx/ui/client/src/job-detail/job-detail.controller.js b/awx/ui/client/src/job-detail/job-detail.controller.js deleted file mode 100644 index c3479e397b..0000000000 --- a/awx/ui/client/src/job-detail/job-detail.controller.js +++ /dev/null @@ -1,1027 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -/** - * @ngdoc function - * @name controllers.function:JobDetail - * @description This controller's for the Job Detail Page -*/ - -export default - [ '$location', '$rootScope', '$filter', '$scope', '$compile', '$state', '$stateParams', '$log', 'ClearScope', - 'GetBasePath', 'Wait', 'ProcessErrors', 'SelectPlay', 'SelectTask', 'GetElapsed', 'JobIsFinished', - 'SetTaskStyles', 'DigestEvent', 'UpdateDOM', 'DeleteJob', 'RelaunchPlaybook', 'LoadPlays', 'LoadTasks', - 'ParseVariableString', 'GetChoices', 'fieldChoices', 'fieldLabels', 'EditSchedule', - 'ParseTypeChange', 'JobDetailService', - function( - $location, $rootScope, $filter, $scope, $compile, $state, $stateParams, $log, ClearScope, - GetBasePath, Wait, ProcessErrors, SelectPlay, SelectTask, GetElapsed, JobIsFinished, - SetTaskStyles, DigestEvent, UpdateDOM, DeleteJob, RelaunchPlaybook, LoadPlays, LoadTasks, - ParseVariableString, GetChoices, fieldChoices, fieldLabels, EditSchedule, - ParseTypeChange, JobDetailService - ) { - ClearScope(); - - var job_id = $stateParams.id, - scope = $scope, - api_complete = false, - refresh_count = 0, - lastEventId = 0, - verbosity_options, - job_type_options; - - scope.plays = []; - scope.parseType = 'yaml'; - scope.previousTaskFailed = false; - $scope.stdoutFullScreen = false; - - scope.$watch('job_status', function(job_status) { - if (job_status && job_status.explanation && job_status.explanation.split(":")[0] === "Previous Task Failed") { - scope.previousTaskFailed = true; - var taskObj = JSON.parse(job_status.explanation.substring(job_status.explanation.split(":")[0].length + 1)); - // return a promise from the options request with the permission type choices (including adhoc) as a param - var fieldChoice = fieldChoices({ - scope: $scope, - url: 'api/v1/unified_jobs/', - field: 'type' - }); - - // manipulate the choices from the options request to be set on - // scope and be usable by the list form - fieldChoice.then(function (choices) { - choices = - fieldLabels({ - choices: choices - }); - scope.explanation_fail_type = choices[taskObj.job_type]; - scope.explanation_fail_name = taskObj.job_name; - scope.explanation_fail_id = taskObj.job_id; - scope.task_detail = scope.explanation_fail_type + " failed for " + scope.explanation_fail_name + " with ID " + scope.explanation_fail_id + "."; - }); - } else { - scope.previousTaskFailed = false; - } - }, true); - - scope.$watch('plays', function(plays) { - for (var play in plays) { - if (plays[play].elapsed) { - plays[play].finishedTip = "Play completed at " + $filter("longDate")(plays[play].finished) + "."; - } else { - plays[play].finishedTip = "Play not completed."; - } - } - }); - scope.hosts = []; - scope.tasks = []; - scope.$watch('tasks', function(tasks) { - for (var task in tasks) { - if (tasks[task].elapsed) { - tasks[task].finishedTip = "Task completed at " + $filter("longDate")(tasks[task].finished) + "."; - } else { - tasks[task].finishedTip = "Task not completed."; - } - if (tasks[task].successfulCount) { - tasks[task].successfulCountTip = tasks[task].successfulCount; - tasks[task].successfulCountTip += (tasks[task].successfulCount === 1) ? " host event was" : " host events were"; - tasks[task].successfulCountTip += " ok."; - } else { - tasks[task].successfulCountTip = "No host events were ok."; - } - if (tasks[task].changedCount) { - tasks[task].changedCountTip = tasks[task].changedCount; - tasks[task].changedCountTip += (tasks[task].changedCount === 1) ? " host event" : " host events"; - tasks[task].changedCountTip += " changed."; - } else { - tasks[task].changedCountTip = "No host events changed."; - } - if (tasks[task].skippedCount) { - tasks[task].skippedCountTip = tasks[task].skippedCount; - tasks[task].skippedCountTip += (tasks[task].skippedCount === 1) ? " host event was" : " hosts events were"; - tasks[task].skippedCountTip += " skipped."; - } else { - tasks[task].skippedCountTip = "No host events were skipped."; - } - if (tasks[task].failedCount) { - tasks[task].failedCountTip = tasks[task].failedCount; - tasks[task].failedCountTip += (tasks[task].failedCount === 1) ? " host event" : " host events"; - tasks[task].failedCountTip += " failed."; - } else { - tasks[task].failedCountTip = "No host events failed."; - } - if (tasks[task].unreachableCount) { - tasks[task].unreachableCountTip = tasks[task].unreachableCount; - tasks[task].unreachableCountTip += (tasks[task].unreachableCount === 1) ? " host event was" : " hosts events were"; - tasks[task].unreachableCountTip += " unreachable."; - } else { - tasks[task].unreachableCountTip = "No host events were unreachable."; - } - if (tasks[task].missingCount) { - tasks[task].missingCountTip = tasks[task].missingCount; - tasks[task].missingCountTip += (tasks[task].missingCount === 1) ? " host event was" : " host events were"; - tasks[task].missingCountTip += " missing."; - } else { - tasks[task].missingCountTip = "No host events were missing."; - } - } - }); - scope.hostResults = []; - - scope.hostResultsMaxRows = 200; - scope.tasksMaxRows = 200; - scope.playsMaxRows = 200; - - // Set the following to true when 'Loading...' message desired - scope.playsLoading = true; - scope.tasksLoading = true; - scope.hostResultsLoading = true; - - // Turn on the 'Waiting...' message until events begin arriving - scope.waiting = true; - - scope.liveEventProcessing = true; // true while job is active and live events are arriving - scope.pauseLiveEvents = false; // control play/pause state of event processing - - scope.job_status = {}; - scope.job_id = job_id; - scope.auto_scroll = false; - - scope.haltEventQueue = false; - scope.processing = false; - scope.lessStatus = false; - scope.lessDetail = false; - // pops the event summary panel open if we're in the host summary child state - //scope.lessEvents = ($state.current.name === 'jobDetail.host-summary' || $state.current.name === 'jobDetail.host-events') ? false : true; - if ($state.current.name === 'jobDetail.host-summary' ){ - scope.lessEvents = false; - } - else{ - scope.lessEvents = true; - } - scope.jobData = {}; - scope.jobData.hostSummaries = {}; - - verbosity_options = [ - { value: 0, label: 'Default' }, - { value: 1, label: 'Verbose' }, - { value: 3, label: 'Debug' } - ]; - - job_type_options = [ - { value: 'run', label: 'Run' }, - { value: 'check', label: 'Check' } - ]; - - GetChoices({ - scope: scope, - url: GetBasePath('unified_jobs'), - field: 'status', - variable: 'status_choices', - }); - - scope.eventsHelpText = "

Successful

\n" + - "

Changed

\n" + - "

Unreachable

\n" + - "

Failed

\n"; - - scope.$on(`ws-job_events-${job_id}`, function(e, data) { - // update elapsed time on each event received - scope.job_status.elapsed = GetElapsed({ - start: scope.job.created, - end: Date.now() - }); - if (api_complete && data.id > lastEventId) { - scope.waiting = false; - data.event = data.event_name; - DigestEvent({ scope: scope, event: data }); - } - UpdateDOM({ scope: scope }); - }); - - scope.$on(`ws-jobs`, function(e, data) { - // if we receive a status change event for the current job indicating the job - // is finished, stop event queue processing and reload - if (parseInt(data.unified_job_id, 10) === parseInt(job_id,10)) { - if (data.status === 'failed' || data.status === 'canceled' || - data.status === 'error' || data.status === 'successful' || data.status === 'running') { - $scope.liveEventProcessing = false; - if (!scope.pauseLiveEvents) { - $scope.$emit('LoadJob'); //this is what is used for the refresh - } - } - } - }); - - scope.$on('ws-jobs-summary', function() { - // the job host summary should now be available from the API - $log.debug('Trigging reload of job_host_summaries'); - scope.$emit('InitialLoadComplete'); - }); - - if (scope.removeInitialLoadComplete) { - scope.removeInitialLoadComplete(); - } - scope.removeInitialLoadComplete = scope.$on('InitialLoadComplete', function() { - Wait('stop'); - - if (JobIsFinished(scope)) { - scope.liveEventProcessing = false; // signal that event processing is over and endless scroll - scope.pauseLiveEvents = false; // should be enabled - var params = { - event: 'playbook_on_stats' - }; - JobDetailService.getRelatedJobEvents(scope.job.id, params) - .success(function() { - UpdateDOM({ scope: scope }); - }) - .error(function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Call failed. GET returned: ' + status }); - }); - $log.debug('Job completed!'); - $log.debug(scope.jobData); - } - else { - api_complete = true; //trigger events to start processing - UpdateDOM({ scope: scope}); - } - }); - - if (scope.removeLoadHosts) { - scope.removeLoadHosts(); - } - scope.removeLoadHosts = scope.$on('LoadHosts', function() { - if (scope.activeTask) { - - var play = scope.jobData.plays[scope.activePlay], - task; - if(play){ - task = play.tasks[scope.activeTask]; - } - if (play && task) { - var params = { - parent: task.id, - event__startswith: 'runner', - page_size: scope.hostResultsMaxRows - }; - JobDetailService.getRelatedJobEvents(scope.job.id, params) - .success(function(data) { - if (data.results.length > 0) { - lastEventId = data.results[0].id; - } - scope.next_host_results = data.next; - task.hostResults = JobDetailService.processHostEvents(data.results); - scope.$emit('InitialLoadComplete'); - }); - } else { - scope.$emit('InitialLoadComplete'); - } - } else { - scope.$emit('InitialLoadComplete'); - } - }); - - if (scope.removeLoadTasks) { - scope.removeLoadTasks(); - } - scope.removeLoadTasks = scope.$on('LoadTasks', function() { - if (scope.activePlay) { - var play = scope.jobData.plays[scope.activePlay]; - - if (play) { - var params = { - event_id: play.id, - page_size: scope.tasksMaxRows, - order: 'id' - }; - JobDetailService.getJobTasks(scope.job.id, params) - .success(function(data) { - scope.next_tasks = data.next; - if (data.results.length > 0) { - lastEventId = data.results[data.results.length - 1].id; - if (scope.liveEventProcessing) { - scope.activeTask = data.results[data.results.length - 1].id; - } - else { - scope.activeTask = data.results[0].id; - } - scope.selectedTask = scope.activeTask; - } - data.results.forEach(function(event, idx) { - var end, elapsed, status, status_text; - - if (play.firstTask === undefined || play.firstTask === null) { - play.firstTask = event.id; - play.hostCount = (event.host_count) ? event.host_count : 0; - } - - if (idx < data.results.length - 1) { - // end date = starting date of the next event - end = data.results[idx + 1].created; - } - else { - // no next event (task), get the end time of the play - if(scope.jobData.plays[scope.activePlay]){ - end = scope.jobData.plays[scope.activePlay].finished; - } - } - - if (end) { - elapsed = GetElapsed({ - start: event.created, - end: end - }); - } - else { - elapsed = '00:00:00'; - } - - status = (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful'; - status_text = (event.failed) ? 'Failed' : (event.changed) ? 'Changed' : 'OK'; - - play.tasks[event.id] = { - id: event.id, - play_id: scope.activePlay, - name: event.name, - status: status, - status_text: status_text, - status_tip: "Event ID: " + event.id + "
Status: " + status_text, - created: event.created, - modified: event.modified, - finished: end, - elapsed: elapsed, - hostCount: (event.host_count) ? event.host_count : 0, - reportedHosts: (event.reported_hosts) ? event.reported_hosts : 0, - successfulCount: (event.successful_count) ? event.successful_count : 0, - failedCount: (event.failed_count) ? event.failed_count : 0, - changedCount: (event.changed_count) ? event.changed_count : 0, - skippedCount: (event.skipped_count) ? event.skipped_count : 0, - unreachableCount: (event.unreachable_count) ? event.unreachable_count : 0, - taskActiveClass: '', - hostResults: {} - }; - if (play.firstTask !== event.id) { - // this is not the first task - play.tasks[event.id].hostCount = play.tasks[play.firstTask].hostCount; - } - if (play.tasks[event.id].reportedHosts === 0 && play.tasks[event.id].successfulCount === 0 && - play.tasks[event.id].failedCount === 0 && play.tasks[event.id].changedCount === 0 && - play.tasks[event.id].skippedCount === 0 && play.tasks[event.id].unreachableCount === 0) { - play.tasks[event.id].status = 'no-matching-hosts'; - play.tasks[event.id].status_text = 'No matching hosts'; - play.tasks[event.id].status_tip = "Event ID: " + event.id + "
Status: No matching hosts"; - } - play.taskCount++; - SetTaskStyles({ - task: play.tasks[event.id] - }); - }); - if (scope.activeTask && scope.jobData.plays[scope.activePlay] && scope.jobData.plays[scope.activePlay].tasks[scope.activeTask]) { - scope.jobData.plays[scope.activePlay].tasks[scope.activeTask].taskActiveClass = 'JobDetail-tableRow--selected'; - } - scope.$emit('LoadHosts'); - }) - .error(function(data) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Call failed. GET returned: ' + status }); - }); - } else { - scope.$emit('InitialLoadComplete'); - } - } else { - scope.$emit('InitialLoadComplete'); - } - }); - - if (scope.removeLoadPlays) { - scope.removeLoadPlays(); - } - scope.removeLoadPlays = scope.$on('LoadPlays', function(e, events_url) { - scope.jobData.plays = {}; - var params = { - order_by: 'id' - }; - - JobDetailService.getJobPlays(scope.job.id, params) - .success( function(data) { - scope.next_plays = data.next; - if (data.results.length > 0) { - lastEventId = data.results[data.results.length - 1].id; - if (scope.liveEventProcessing) { - scope.activePlay = data.results[data.results.length - 1].id; - } - else { - scope.activePlay = data.results[0].id; - } - scope.selectedPlay = scope.activePlay; - } else { - // if we are here, there are no plays and the job has failed, let the user know they may want to consult stdout - if ( (scope.job_status.status === 'failed' || scope.job_status.status === 'error') && - (!scope.job_status.explanation)) { - scope.job_status.explanation = "See standard out for more details"; - } - } - data.results.forEach(function(event, idx) { - var status, status_text, start, end, elapsed, ok, changed, failed, skipped; - - status = (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful'; - status_text = (event.failed) ? 'Failed' : (event.changed) ? 'Changed' : 'OK'; - start = event.started; - - if (idx < data.results.length - 1) { - // end date = starting date of the next event - end = data.results[idx + 1].started; - } - else if (JobIsFinished(scope)) { - // this is the last play and the job already finished - end = scope.job_status.finished; - } - if (end) { - elapsed = GetElapsed({ - start: start, - end: end - }); - } - else { - elapsed = '00:00:00'; - } - - scope.jobData.plays[event.id] = { - id: event.id, - name: event.play, - created: start, - finished: end, - status: status, - status_text: status_text, - status_tip: "Event ID: " + event.id + "
Status: " + status_text, - elapsed: elapsed, - hostCount: 0, - fistTask: null, - taskCount: 0, - playActiveClass: '', - unreachableCount: (event.unreachable_count) ? event.unreachable_count : 0, - tasks: {} - }; - - ok = (event.ok_count) ? event.ok_count : 0; - changed = (event.changed_count) ? event.changed_count : 0; - failed = (event.failed_count) ? event.failed_count : 0; - skipped = (event.skipped_count) ? event.skipped_count : 0; - - scope.jobData.plays[event.id].hostCount = ok + changed + failed + skipped; - - if (scope.jobData.plays[event.id].hostCount > 0 || event.unreachable_count > 0 || scope.job_status.status === 'successful' || - scope.job_status.status === 'failed' || scope.job_status.status === 'error' || scope.job_status.status === 'canceled') { - // force the play to be on the 'active' list - scope.jobData.plays[event.id].taskCount = 1; - } - - if (scope.jobData.plays[event.id].hostCount === 0 && event.unreachable_count === 0) { - scope.jobData.plays[event.id].status = 'no-matching-hosts'; - scope.jobData.plays[event.id].status_text = 'No matching hosts'; - scope.jobData.plays[event.id].status_tip = "Event ID: " + event.id + "
Status: No matching hosts"; - } - }); - if (scope.activePlay && scope.jobData.plays[scope.activePlay]) { - scope.jobData.plays[scope.activePlay].playActiveClass = 'JobDetail-tableRow--selected'; - } - scope.$emit('LoadTasks', events_url); - }); - }); - - - if (scope.removeLoadJob) { - scope.removeLoadJob(); - } - scope.removeLoadJobRow = scope.$on('LoadJob', function() { - Wait('start'); - scope.job_status = {}; - - scope.playsLoading = true; - scope.tasksLoading = true; - scope.hostResultsLoading = true; - - // Load the job record - JobDetailService.getJob({id: job_id}) - .success(function(res) { - var i, - data = res.results[0]; - scope.job = data; - scope.job_template_name = data.name; - scope.project_name = (data.summary_fields.project) ? data.summary_fields.project.name : ''; - scope.inventory_name = (data.summary_fields.inventory) ? data.summary_fields.inventory.name : ''; - scope.job_template_url = '/#/templates/' + data.unified_job_template; - scope.inventory_url = (scope.inventory_name && data.inventory) ? '/#/inventories/' + data.inventory : ''; - scope.project_url = (scope.project_name && data.project) ? '/#/projects/' + data.project : ''; - scope.credential_url = (data.credential) ? '/#/credentials/' + data.credential : ''; - scope.cloud_credential_url = (data.cloud_credential) ? '/#/credentials/' + data.cloud_credential : ''; - scope.playbook = data.playbook; - scope.credential = data.credential; - scope.cloud_credential = data.cloud_credential; - scope.forks = data.forks; - scope.limit = data.limit; - scope.verbosity = data.verbosity; - scope.job_tags = data.job_tags; - scope.variables = ParseVariableString(data.extra_vars); - - // If we get created_by back from the server then use it. This means that the job was kicked - // off by a user and not a schedule AND that the user still exists in the system. - if(data.summary_fields.created_by) { - scope.users_url = '/#/users/' + data.summary_fields.created_by.id; - scope.created_by = data.summary_fields.created_by.username; - } - else { - if(data.summary_fields.schedule) { - // Build the Launched By link to point to the schedule that kicked it off - scope.scheduled_by = (data.summary_fields.schedule.name) ? data.summary_fields.schedule.name.toString() : ''; - } - // If there is no schedule or created_by then we can assume that the job was - // created by a deleted user - } - - if (data.summary_fields.credential) { - scope.credential_name = data.summary_fields.credential.name; - scope.credential_url = data.related.credential - .replace('api/v1', '#'); - } else { - scope.credential_name = ""; - } - - if (data.summary_fields.cloud_credential) { - scope.cloud_credential_name = data.summary_fields.cloud_credential.name; - scope.cloud_credential_url = data.related.cloud_credential - .replace('api/v1', '#'); - } else { - scope.cloud_credential_name = ""; - } - - if (data.summary_fields.network_credential) { - scope.network_credential_name = data.summary_fields.network_credential.name; - scope.network_credential_url = data.related.network_credential - .replace('api/v1', '#'); - } else { - scope.network_credential_name = ""; - } - - for (i=0; i < verbosity_options.length; i++) { - if (verbosity_options[i].value === data.verbosity) { - scope.verbosity = verbosity_options[i].label; - } - } - - for (i=0; i < job_type_options.length; i++) { - if (job_type_options[i].value === data.job_type) { - scope.job_type = job_type_options[i].label; - } - } - - // In the case the job is already completed, or an error already happened, - // populate scope.job_status info - scope.job_status.status = (data.status === 'waiting' || data.status === 'new') ? 'pending' : data.status; - scope.job_status.started = data.started; - scope.job_status.status_class = ((data.status === 'error' || data.status === 'failed') && data.job_explanation) ? "alert alert-danger" : ""; - scope.job_status.explanation = data.job_explanation; - if(data.result_traceback) { - scope.job_status.traceback = data.result_traceback.trim().split('\n').join('
'); - } - if (data.status === 'successful' || data.status === 'failed' || data.status === 'error' || data.status === 'canceled') { - scope.job_status.finished = data.finished; - scope.liveEventProcessing = false; - scope.pauseLiveEvents = false; - scope.waiting = false; - scope.playsLoading = false; - scope.tasksLoading = false; - scope.hostResultsLoading = false; - } - else { - scope.job_status.finished = null; - } - - if (data.started && data.finished) { - scope.job_status.elapsed = GetElapsed({ - start: data.started, - end: data.finished - }); - } - else { - scope.job_status.elapsed = '00:00:00'; - } - scope.status_choices.every(function(status) { - if (status.value === scope.job.status) { - scope.job_status.status_label = status.label; - return false; - } - return true; - }); - //scope.setSearchAll('host'); - ParseTypeChange({ scope: scope, field_id: 'pre-formatted-variables', readOnly: true }); - scope.$emit('LoadPlays', data.related.job_events); - }) - .error(function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to retrieve job: ' + $stateParams.id + '. GET returned: ' + status }); - }); - }); - - - if (scope.removeRefreshCompleted) { - scope.removeRefreshCompleted(); - } - scope.removeRefreshCompleted = scope.$on('RefreshCompleted', function() { - refresh_count++; - if (refresh_count === 1) { - // First time. User just loaded page. - scope.$emit('LoadJob'); - } - }); - - scope.adjustSize = function() { - var height, ww = $(window).width(); - if (ww < 1024) { - $('#job-summary-container').hide(); - $('#job-detail-container').css({ "width": "100%", "padding-right": "15px" }); - $('#summary-button').show(); - } - else { - $('.overlay').hide(); - $('#summary-button').hide(); - $('#hide-summary-button').hide(); - $('#job-summary-container .job_well').css({ - 'box-shadow': 'none', - 'height': 'auto' - }); - $('#job-summary-container').css({ - "width": "41.66666667%", - "padding-left": "7px", - "padding-right": "15px", - "z-index": 0 - }); - setTimeout(function() { $('#job-summary-container .job_well').height($('#job-detail-container').height() - 18); }, 500); - $('#job-summary-container').show(); - } - - scope.lessStatus = false; // close the view more status option - - - height = $(window).height() - $('#main-menu-container .navbar').outerHeight() - - $('#job-detail-container').outerHeight() - 20; - scope.$emit('RefreshCompleted'); - }; - - setTimeout(function() { scope.adjustSize(); }, 500); - - // Use debounce for the underscore library to adjust after user resizes window. - $(window).resize(_.debounce(function(){ - scope.adjustSize(); - }, 500)); - - function flashPlayTip() { - setTimeout(function(){ - $('#play-help').popover('show'); - },500); - setTimeout(function() { - $('#play-help').popover('hide'); - }, 5000); - } - - scope.selectPlay = function(id) { - if (scope.liveEventProcessing && !scope.pauseLiveEvents) { - scope.pauseLiveEvents = true; - flashPlayTip(); - } - SelectPlay({ - scope: scope, - id: id - }); - }; - - scope.selectTask = function(id) { - if (scope.liveEventProcessing && !scope.pauseLiveEvents) { - scope.pauseLiveEvents = true; - flashPlayTip(); - } - SelectTask({ - scope: scope, - id: id - }); - }; - - scope.togglePlayButton = function() { - if (scope.pauseLiveEvents) { - scope.pauseLiveEvents = false; - scope.$emit('LoadJob'); - } - }; - - scope.objectIsEmpty = function(obj) { - if (angular.isObject(obj)) { - return (Object.keys(obj).length > 0) ? false : true; - } - return true; - }; - - scope.toggleLessEvents = function() { - if (!scope.lessEvents) { - $('#events-summary').slideUp(0); - scope.lessEvents = true; - } - else { - $('#events-summary').slideDown(0); - scope.lessEvents = false; - } - }; - - scope.toggleLessStatus = function() { - if (!scope.lessStatus) { - $('#job-status-form').slideUp(200); - scope.lessStatus = true; - } - else { - $('#job-status-form').slideDown(200); - scope.lessStatus = false; - } - }; - - scope.toggleLessDetail = function() { - if (!scope.lessDetail) { - $('#job-detail-details').slideUp(200); - scope.lessDetail = true; - } - else { - $('#job-detail-details').slideDown(200); - scope.lessDetail = false; - } - }; - - if (scope.removeDeleteFinished) { - scope.removeDeleteFinished(); - } - scope.removeDeleteFinished = scope.$on('DeleteFinished', function(e, action) { - Wait('stop'); - if (action !== 'cancel') { - Wait('stop'); - $location.url('/jobs'); - } - }); - - scope.deleteJob = function() { - DeleteJob({ - scope: scope, - id: scope.job.id, - job: scope.job, - callback: 'DeleteFinished' - }); - }; - - scope.relaunchJob = function() { - RelaunchPlaybook({ - scope: scope, - id: scope.job.id - }); - }; - - scope.playsScrollDown = function() { - // check for more plays when user scrolls to bottom of play list... - if (((!scope.liveEventProcessing) || (scope.liveEventProcessing && scope.pauseLiveEvents)) && scope.next_plays) { - $('#playsMoreRows').fadeIn(); - scope.playsLoading = true; - JobDetailService.getNextPage(scope.next_plays) - .success( function(data) { - scope.next_plays = data.next; - data.results.forEach(function(event, idx) { - var status, status_text, start, end, elapsed, ok, changed, failed, skipped; - - status = (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful'; - status_text = (event.failed) ? 'Failed' : (event.changed) ? 'Changed' : 'OK'; - start = event.started; - - if (idx < data.results.length - 1) { - // end date = starting date of the next event - end = data.results[idx + 1].started; - } - else if (JobIsFinished(scope)) { - // this is the last play and the job already finished - end = scope.job_status.finished; - } - if (end) { - elapsed = GetElapsed({ - start: start, - end: end - }); - } - else { - elapsed = '00:00:00'; - } - - scope.plays.push({ - id: event.id, - name: event.play, - created: start, - finished: end, - status: status, - status_text: status_text, - status_tip: "Event ID: " + event.id + "
Status: " + status_text, - elapsed: elapsed, - hostCount: 0, - fistTask: null, - playActiveClass: '', - unreachableCount: (event.unreachable_count) ? event.unreachable_count : 0, - }); - - ok = (event.ok_count) ? event.ok_count : 0; - changed = (event.changed_count) ? event.changed_count : 0; - failed = (event.failed_count) ? event.failed_count : 0; - skipped = (event.skipped_count) ? event.skipped_count : 0; - - scope.plays[scope.plays.length - 1].hostCount = ok + changed + failed + skipped; - scope.playsLoading = false; - }); - $('#playsMoreRows').fadeOut(400); - }) - .error( function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + scope.next_plays + '. GET returned: ' + status }); - }); - } - }; - - scope.tasksScrollDown = function() { - // check for more tasks when user scrolls to bottom of task list... - if (((!scope.liveEventProcessing) || (scope.liveEventProcessing && scope.pauseLiveEvents)) && scope.next_tasks) { - $('#tasksMoreRows').fadeIn(); - scope.tasksLoading = true; - JobDetailService.getNextPage(scope.next_tasks) - .success(function(data) { - scope.next_tasks = data.next; - data.results.forEach(function(event, idx) { - var end, elapsed, status, status_text; - if (idx < data.results.length - 1) { - // end date = starting date of the next event - end = data.results[idx + 1].created; - } - else { - // no next event (task), get the end time of the play - scope.plays.every(function(p, j) { - if (p.id === scope.selectedPlay) { - end = scope.plays[j].finished; - return false; - } - return true; - }); - } - if (end) { - elapsed = GetElapsed({ - start: event.created, - end: end - }); - } - else { - elapsed = '00:00:00'; - } - - status = (event.failed) ? 'failed' : (event.changed) ? 'changed' : 'successful'; - status_text = (event.failed) ? 'Failed' : (event.changed) ? 'Changed' : 'OK'; - - scope.tasks.push({ - id: event.id, - play_id: scope.selectedPlay, - name: event.name, - status: status, - status_text: status_text, - status_tip: "Event ID: " + event.id + "
Status: " + status_text, - created: event.created, - modified: event.modified, - finished: end, - elapsed: elapsed, - hostCount: event.host_count, // hostCount, - reportedHosts: event.reported_hosts, - successfulCount: event.successful_count, - failedCount: event.failed_count, - changedCount: event.changed_count, - skippedCount: event.skipped_count, - taskActiveClass: '' - }); - SetTaskStyles({ - task: scope.tasks[scope.tasks.length - 1] - }); - }); - $('#tasksMoreRows').fadeOut(400); - scope.tasksLoading = false; - }) - .error(function(data, status) { - $('#tasksMoreRows').fadeOut(400); - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + scope.next_tasks + '. GET returned: ' + status }); - }); - } - }; - - scope.hostResultsScrollDown = function() { - // check for more hosts when user scrolls to bottom of host results list... - if (((!scope.liveEventProcessing) || (scope.liveEventProcessing && scope.pauseLiveEvents)) && scope.next_host_results) { - $('#hostResultsMoreRows').fadeIn(); - scope.hostResultsLoading = true; - JobDetailService.getNextPage(scope.next_host_results) - .success(function(data) { - scope.next_host_results = data.next; - data.results.forEach(function(row) { - var status, status_text, item, msg; - if (row.event === "runner_on_skipped") { - status = 'skipped'; - } - else if (row.event === "runner_on_unreachable") { - status = 'unreachable'; - } - else { - status = (row.failed) ? 'failed' : (row.changed) ? 'changed' : 'successful'; - } - switch(status) { - case "successful": - status_text = 'OK'; - break; - case "changed": - status_text = "Changed"; - break; - case "failed": - status_text = "Failed"; - break; - case "unreachable": - status_text = "Unreachable"; - break; - case "skipped": - status_text = "Skipped"; - } - if (row.event_data && row.event_data.res) { - item = row.event_data.res.item; - if (typeof item === "object") { - item = JSON.stringify(item); - } - } - msg = ''; - if (row.event_data && row.event_data.res) { - if (typeof row.event_data.res === 'object') { - msg = row.event_data.res.msg; - } else { - msg = row.event_data.res; - } - } - scope.hostResults.push({ - id: row.id, - status: status, - status_text: status_text, - host_id: row.host, - task_id: row.parent, - name: row.event_data.host, - created: row.created, - msg: (row.event_data && row.event_data.res) ? row.event_data.res.msg : '', - item: item - }); - scope.hostResultsLoading = false; - }); - $('#hostResultsMoreRows').fadeOut(400); - }) - .error(function(data, status) { - $('#hostResultsMoreRows').fadeOut(400); - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + scope.next_host_results + '. GET returned: ' + status }); - }); - } - }; - - scope.refresh = function(){ - $scope.$emit('LoadJob'); - }; - - // Click binding for the expand/collapse button on the standard out log - $scope.toggleStdoutFullscreen = function() { - $scope.stdoutFullScreen = !$scope.stdoutFullScreen; - }; - - scope.editSchedule = function() { - // We need to get the schedule's ID out of the related links - // An example of the related schedule link looks like /api/v1/schedules/5 - // where 5 is the ID we are trying to capture - var regex = /\/api\/v1\/schedules\/(\d+)\//; - var id = scope.job.related.schedule.match(regex)[1]; - - if(scope.job.job_template && id) { - $state.go('jobTemplateSchedules.edit', {id: scope.job.job_template, schedule_id: id}); - } - }; - - // SchedulesRefresh is the callback string that we passed to the edit schedule modal - // When the modal successfully updates the schedule it will emit this event and pass - // the updated schedule object - if (scope.removeSchedulesRefresh) { - scope.removeSchedulesRefresh(); - } - scope.$on('SchedulesRefresh', function(e, data) { - if (data) { - scope.scheduled_by = data.name; - } - }); - } -]; diff --git a/awx/ui/client/src/job-detail/job-detail.partial.html b/awx/ui/client/src/job-detail/job-detail.partial.html deleted file mode 100644 index bd0b488b18..0000000000 --- a/awx/ui/client/src/job-detail/job-detail.partial.html +++ /dev/null @@ -1,435 +0,0 @@ -
-
-
- -
- -
-
- -
- - - -
-
- -
-
- -
{{ job_status.status_label }}
-
- -
- -
-
Previous Task Failed - - - - -
-
- -
- -
-
- -
- - -
- -
- -
{{ job_status.started | longDate }}
-
- -
- -
{{ job_type }}
-
- -
- -
{{ job_status.finished | longDate }}
-
- -
- - -
- -
- -
{{ job_status.elapsed }}
-
- -
- - -
- -
- - -
- -
- - -
- -
- -
{{ job.playbook }}
-
- -
- - -
- -
- - -
- -
- - -
- -
- -
{{ job.forks }}
-
- -
- -
{{ job.limit }}
-
- -
- -
{{ verbosity }}
-
- -
- -
{{ job.job_tags }}
-
- -
- -
{{ job.skip_tags }}
-
- -
- - -
- -
-
- - - -
- - -
-
-
1 Please select from a play below to view its associated tasks.
-
-
-
- - - -
-
-
-
- - -
-
-
- - -
- - - - - - - - -
PlaysStartedElapsed
-
-
- - - - - - - - - - - - - - - - - - -
{{ play.name }}{{ play.created | date: 'HH:mm:ss' }}{{ play.elapsed }}
Waiting...
Loading...
No matching plays
-
-
- -
-
- - -
-
2 Please select a task below to view its associated hosts
-
-
-
- - - -
-
-
-
- - -
-
-
- -
- - - - - - - - - -
TasksStartedElapsed
-
-
- - - - - - - - - - - - - - - - - - - -
{{ task.name }}{{ task.created | date: 'HH:mm:ss' }}{{ task.elapsed }}
Waiting...
Loading...
No matching tasks
-
-
-
- - -
-
3 Please select a host below to view associated task details.
-
-
-
- - - -
-
-
-
- - -
-
-
-
- - - - - - - - -
HostsItemMessage
-
- -
- - - - - - - - - - - - - - - - - -
- {{ result.name }}{{ result.name }} - {{ result.item }}{{ result.msg }}
Waiting...
Loading...
No matching host events
-
-
-
- -
-
- - - - - - - -
- - - -
-
-
-
STANDARD OUT
-
- - - - -
-
- -
-
- -
-
- - - - diff --git a/awx/ui/client/src/job-detail/job-detail.route.js b/awx/ui/client/src/job-detail/job-detail.route.js deleted file mode 100644 index 94088c126b..0000000000 --- a/awx/ui/client/src/job-detail/job-detail.route.js +++ /dev/null @@ -1,91 +0,0 @@ -// <<<<<<< 4cf6a946a1aa14b7d64a8e1e8dabecfd3d056f27 -// //<<<<<<< bc59236851902d7c768aa26abdb7dc9c9dc27a5a -// /************************************************* -// * Copyright (c) 2016 Ansible, Inc. -// * -// * All Rights Reserved -// *************************************************/ -// -// // <<<<<<< a3d9eea2c9ddb4e16deec9ec38dea16bf37c559d -// // import { templateUrl } from '../shared/template-url/template-url.factory'; -// // -// // export default { -// // name: 'jobDetail', -// // url: '/jobs/{id: int}', -// // ncyBreadcrumb: { -// // parent: 'jobs', -// // label: "{{ job.id }} - {{ job.name }}" -// // }, -// // data: { -// // socket: { -// // "groups": { -// // "jobs": ["status_changed", "summary"], -// // "job_events": [] -// // } -// // } -// // }, -// // templateUrl: templateUrl('job-detail/job-detail'), -// // controller: 'JobDetailController' -// // }; -// // ======= -// // import {templateUrl} from '../shared/template-url/template-url.factory'; -// // -// // export default { -// // name: 'jobDetail', -// // url: '/jobs/:id', -// // ncyBreadcrumb: { -// // parent: 'jobs', -// // label: "{{ job.id }} - {{ job.name }}" -// // }, -// // socket: { -// // "groups":{ -// // "jobs": ["status_changed", "summary"], -// // "job_events": [] -// // } -// // }, -// // templateUrl: templateUrl('job-detail/job-detail'), -// // controller: 'JobDetailController' -// // }; -// //======= -// ======= -// >>>>>>> Rebase of devel (w/ channels) + socket rework for new job details -// // /************************************************* -// // * Copyright (c) 2016 Ansible, Inc. -// // * -// // * All Rights Reserved -// // *************************************************/ -// // -// // import {templateUrl} from '../shared/template-url/template-url.factory'; -// // -// // export default { -// // name: 'jobDetail', -// // url: '/jobs/:id', -// // ncyBreadcrumb: { -// // parent: 'jobs', -// // label: "{{ job.id }} - {{ job.name }}" -// // }, -// // socket: { -// // "groups":{ -// // "jobs": ["status_changed", "summary"], -// // "job_events": [] -// // } -// // }, -// // resolve: { -// // jobEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) { -// // if (!$rootScope.event_socket) { -// // $rootScope.event_socket = Socket({ -// // scope: $rootScope, -// // endpoint: "job_events" -// // }); -// // $rootScope.event_socket.init(); -// // // returns should really be providing $rootScope.event_socket -// // // otherwise, we have to inject the entire $rootScope into the controller -// // return true; -// // } else { -// // return true; -// // } -// // }] -// // }, -// // templateUrl: templateUrl('job-detail/job-detail'), -// // controller: 'JobDetailController' -// // }; diff --git a/awx/ui/client/src/job-detail/job-detail.service.js b/awx/ui/client/src/job-detail/job-detail.service.js deleted file mode 100644 index 8e436a3e96..0000000000 --- a/awx/ui/client/src/job-detail/job-detail.service.js +++ /dev/null @@ -1,215 +0,0 @@ -export default - ['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', function($rootScope, Rest, GetBasePath, ProcessErrors){ - return { - stringifyParams: function(params){ - return _.reduce(params, (result, value, key) => { - return result + key + '=' + value + '&'; - }, ''); - }, - - // the the API passes through Ansible's event_data response - // we need to massage away the verbose & redundant stdout/stderr properties - processJson: function(data){ - // configure fields to ignore - var ignored = [ - 'type', - 'event_data', - 'related', - 'summary_fields', - 'url', - 'ansible_facts', - ]; - // remove ignored properties - var result = _.chain(data).cloneDeep().forEach(function(value, key, collection){ - if (ignored.indexOf(key) > -1){ - delete collection[key]; - } - }).value(); - return result; - }, - // Return Ansible's passed-through response msg on a job_event - processEventMsg: function(event){ - return typeof event.event_data.res === 'object' ? event.event_data.res.msg : event.event_data.res; - }, - // Return only Ansible's passed-through response item on a job_event - processEventItem: function(event){ - try{ - var item = event.event_data.res.item; - return typeof item === 'object' ? JSON.stringify(item) : item; - } - catch(err){return;} - }, - processsEventTip: function(event, status){ - try{ - var string = `Event ID: ${ event.id }
Status: ${ _.capitalize(status.status)}. Click for details`; - return typeof item === 'object' ? JSON.stringify(string) : string; - } - catch(err){return;} - }, - // Generate a helper class for job_event statuses - // the stack for which status to display is - // unreachable > failed > changed > ok - // uses the API's runner events and convenience properties .failed .changed to determine status. - // see: job_event_callback.py for more filters to support - processEventStatus: function(event){ - if (event.event === 'runner_on_unreachable'){ - return { - class: 'HostEvents-status--unreachable', - status: 'unreachable' - }; - } - // equiv to 'runner_on_error' && 'runner on failed' - if (event.failed){ - return { - class: 'HostEvents-status--failed', - status: 'failed' - }; - } - // catch the changed case before ok, because both can be true - if (event.changed){ - return { - class: 'HostEvents-status--changed', - status: 'changed' - }; - } - if (event.event === 'runner_on_ok' || event.event === 'runner_on_async_ok'){ - return { - class: 'HostEvents-status--ok', - status: 'ok' - }; - } - if (event.event === 'runner_on_skipped'){ - return { - class: 'HostEvents-status--skipped', - status: 'skipped' - }; - } - }, - // Consumes a response from this.getRelatedJobEvents(id, params) - // returns an array for view logic to iterate over to build host result rows - processHostEvents: function(data){ - var self = this; - var results = []; - data.forEach(function(event){ - if (event.event !== 'runner_on_no_hosts'){ - var status = self.processEventStatus(event); - var msg = self.processEventMsg(event); - var item = self.processEventItem(event); - var tip = self.processsEventTip(event, status); - results.push({ - id: event.id, - status: status.status, - status_text: _.capitalize(status.status), - host_id: event.host, - task_id: event.parent, - name: event.event_data.host, - created: event.created, - tip: typeof tip === 'undefined' ? undefined : tip, - msg: typeof msg === 'undefined' ? undefined : msg, - item: typeof item === 'undefined' ? undefined : item - }); - } - }); - return results; - }, - // GET events related to a job run - // e.g. - // ?event=playbook_on_stats - // ?parent=206&event__startswith=runner&page_size=200&order=host_name,counter - getRelatedJobEvents: function(id, params){ - var url = GetBasePath('jobs'); - url = url + id + '/job_events/?' + this.stringifyParams(params); - Rest.setUrl(url); - return Rest.get() - .success(function(data){ - return data; - }) - .error(function(data, status) { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - }, - getJobEventChildren: function(uuid){ - var url = GetBasePath('job_events'); - url = `${url}?parent__uuid=${uuid}&order_by=host_name`; - Rest.setUrl(url); - return Rest.get() - .success(function(data){ - return data; - }) - .error(function(data, status) { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - }, - // GET job host summaries related to a job run - // e.g. ?page_size=200&order=host_name - getJobHostSummaries: function(id, params){ - var url = GetBasePath('jobs'); - url = url + id + '/job_host_summaries/?' + this.stringifyParams(params); - Rest.setUrl(url); - return Rest.get() - .success(function(data){ - return data; - }) - .error(function(data, status) { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - }, - // GET job plays related to a job run - // e.g. ?page_size=200 - getJobPlays: function(id, params){ - var url = GetBasePath('jobs'); - url = url + id + '/job_plays/?' + this.stringifyParams(params); - Rest.setUrl(url); - return Rest.get() - .success(function(data){ - return data; - }) - .error(function(data, status) { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - }, - getJobTasks: function(id, params){ - var url = GetBasePath('jobs'); - url = url + id + '/job_tasks/?' + this.stringifyParams(params); - Rest.setUrl(url); - return Rest.get() - .success(function(data){ - return data; - }) - .error(function(data, status) { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - }, - getJob: function(params){ - var url = GetBasePath('unified_jobs') + '?' + this.stringifyParams(params); - Rest.setUrl(url); - return Rest.get() - .success(function(data){ - return data; - }) - .error(function(data, status) { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - }, - // GET next set of paginated results - // expects 'next' param returned by the API e.g. - // "/api/v1/jobs/51/job_plays/?order_by=id&page=2&page_size=1" - getNextPage: function(url){ - Rest.setUrl(url); - return Rest.get() - .success(function(data){ - return data; - }) - .error(function(data, status) { - ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - } - }; - }]; diff --git a/awx/ui/client/src/job-detail/main.js b/awx/ui/client/src/job-detail/main.js deleted file mode 100644 index 628f537e43..0000000000 --- a/awx/ui/client/src/job-detail/main.js +++ /dev/null @@ -1,24 +0,0 @@ -/************************************************* - * Copyright (c) 2016 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -// import route from './job-detail.route'; -import controller from './job-detail.controller'; -import service from './job-detail.service'; -import hostEvents from './host-events/main'; -// import hostEvent from './host-event/main'; -import hostSummary from './host-summary/main'; - -export default - angular.module('jobDetail', [ - hostEvents.name, - // hostEvent.name, - hostSummary.name - ]) - .controller('JobDetailController', controller) - .service('JobDetailService', service); - // .run(['$stateExtender', function($stateExtender) { - // $stateExtender.addState(route); - // }]); diff --git a/awx/ui/client/src/job-results/host-event/host-event-modal.partial.html b/awx/ui/client/src/job-results/host-event/host-event-modal.partial.html index cf9dc67ac4..f8d488b4b3 100644 --- a/awx/ui/client/src/job-results/host-event/host-event-modal.partial.html +++ b/awx/ui/client/src/job-results/host-event/host-event-modal.partial.html @@ -9,7 +9,7 @@ {{event.host_name}} - @@ -40,19 +40,19 @@
- - - @@ -64,7 +64,7 @@
- +
diff --git a/awx/ui/client/src/job-results/host-event/host-event.controller.js b/awx/ui/client/src/job-results/host-event/host-event.controller.js index 6015dd3a63..781f971793 100644 --- a/awx/ui/client/src/job-results/host-event/host-event.controller.js +++ b/awx/ui/client/src/job-results/host-event/host-event.controller.js @@ -6,23 +6,15 @@ export default - ['$stateParams', '$scope', '$state', 'Wait', 'JobDetailService', 'hostEvent', 'hostResults', - function($stateParams, $scope, $state, Wait, JobDetailService, hostEvent, hostResults){ + ['$stateParams', '$scope', '$state', 'Wait', 'jobResultsService', 'hostEvent', + function($stateParams, $scope, $state, Wait, jobResultsService, hostEvent){ - $scope.processEventStatus = JobDetailService.processEventStatus; - $scope.hostResults = []; - // Avoid rendering objects in the details fieldset - // ng-if="processResults(value)" via host-event-details.partial.html + $scope.processEventStatus = jobResultsService.processEventStatus; $scope.processResults = function(value){ if (typeof value === 'object'){return false;} else {return true;} }; - $scope.isStdOut = function(){ - if ($state.current.name === 'jobDetail.host-event.stdout' || $state.current.name === 'jobDetail.host-event.stderr'){ - return 'StandardOut-preContainer StandardOut-preContent'; - } - }; - /*ignore jslint start*/ + var initCodeMirror = function(el, data, mode){ var container = document.getElementById(el); var editor = CodeMirror.fromTextArea(container, { // jshint ignore:line @@ -37,17 +29,9 @@ return $state.current.name === name; }; - $scope.getActiveHostIndex = function(){ - var result = $scope.hostResults.filter(function( obj ) { - return obj.id === $scope.event.id; - }); - return $scope.hostResults.indexOf(result[0]); - }; - var init = function(){ hostEvent.event_name = hostEvent.event; $scope.event = _.cloneDeep(hostEvent); - $scope.hostResults = hostResults; // grab standard out & standard error if present from the host // event's "res" object, for things like Ansible modules @@ -72,7 +56,7 @@ } // instantiate Codemirror // try/catch pattern prevents the abstract-state controller from complaining about element being null - if ($state.current.name === 'jobDetail.host-event.json'){ + if ($state.current.name === 'jobResult.host-event.json'){ try{ initCodeMirror('HostEvent-codemirror', JSON.stringify($scope.json, null, 4), {name: "javascript", json: true}); } @@ -80,7 +64,7 @@ // element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController } } - else if ($state.current.name === 'jobDetail.host-event.stdout'){ + else if ($state.current.name === 'jobResult.host-event.stdout'){ try{ initCodeMirror('HostEvent-codemirror', $scope.stdout, 'shell'); } @@ -88,7 +72,7 @@ // element with id HostEvent-codemirror is not the view controlled by this instance of HostEventController } } - else if ($state.current.name === 'jobDetail.host-event.stderr'){ + else if ($state.current.name === 'jobResult.host-event.stderr'){ try{ initCodeMirror('HostEvent-codemirror', $scope.stderr, 'shell'); } diff --git a/awx/ui/client/src/job-results/host-event/host-event.route.js b/awx/ui/client/src/job-results/host-event/host-event.route.js index 23d5fe2451..650a82bb25 100644 --- a/awx/ui/client/src/job-results/host-event/host-event.route.js +++ b/awx/ui/client/src/job-results/host-event/host-event.route.js @@ -7,20 +7,17 @@ import { templateUrl } from '../../shared/template-url/template-url.factory'; var hostEventModal = { - name: 'jobDetail.host-event', + name: 'jobResult.host-event', url: '/host-event/:eventId', controller: 'HostEventController', templateUrl: templateUrl('job-results/host-event/host-event-modal'), 'abstract': false, resolve: { - hostEvent: ['JobDetailService', '$stateParams', function(JobDetailService, $stateParams) { - return JobDetailService.getRelatedJobEvents($stateParams.id, { + hostEvent: ['jobResultsService', '$stateParams', function(jobResultsService, $stateParams) { + return jobResultsService.getRelatedJobEvents($stateParams.id, { id: $stateParams.eventId }).then(function(res) { return res.data.results[0]; }); - }], - hostResults: ['JobDetailService', '$stateParams', function(JobDetailService, $stateParams) { - return JobDetailService.getJobEventChildren($stateParams.taskId).then(res => res.data.results); }] }, onExit: function() { @@ -34,21 +31,21 @@ var hostEventModal = { }; var hostEventJson = { - name: 'jobDetail.host-event.json', + name: 'jobResult.host-event.json', url: '/json', controller: 'HostEventController', templateUrl: templateUrl('job-results/host-event/host-event-codemirror') }; var hostEventStdout = { - name: 'jobDetail.host-event.stdout', + name: 'jobResult.host-event.stdout', url: '/stdout', controller: 'HostEventController', templateUrl: templateUrl('job-results/host-event/host-event-stdout') }; var hostEventStderr = { - name: 'jobDetail.host-event.stderr', + name: 'jobResult.host-event.stderr', url: '/stderr', controller: 'HostEventController', templateUrl: templateUrl('job-results/host-event/host-event-stderr') diff --git a/awx/ui/client/src/job-results/job-results.partial.html b/awx/ui/client/src/job-results/job-results.partial.html index e48174311d..c2c56384de 100644 --- a/awx/ui/client/src/job-results/job-results.partial.html +++ b/awx/ui/client/src/job-results/job-results.partial.html @@ -108,7 +108,7 @@ ng-show="!previousTaskFailed"> {{job.job_explanation}} -
Previous Task Failed failed > changed > ok + // uses the API's runner events and convenience properties .failed .changed to determine status. + // see: job_event_callback.py for more filters to support + processEventStatus: function(event){ + if (event.event === 'runner_on_unreachable'){ + return { + class: 'HostEvent-status--unreachable', + status: 'unreachable' + }; + } + // equiv to 'runner_on_error' && 'runner on failed' + if (event.failed){ + return { + class: 'HostEvent-status--failed', + status: 'failed' + }; + } + // catch the changed case before ok, because both can be true + if (event.changed){ + return { + class: 'HostEvent-status--changed', + status: 'changed' + }; + } + if (event.event === 'runner_on_ok' || event.event === 'runner_on_async_ok'){ + return { + class: 'HostEvent-status--ok', + status: 'ok' + }; + } + if (event.event === 'runner_on_skipped'){ + return { + class: 'HostEvent-status--skipped', + status: 'skipped' + }; + } + }, + // GET events related to a job run + // e.g. + // ?event=playbook_on_stats + // ?parent=206&event__startswith=runner&page_size=200&order=host_name,counter + getRelatedJobEvents: function(id, params){ + var url = GetBasePath('jobs'); + url = url + id + '/job_events/?' + this.stringifyParams(params); + Rest.setUrl(url); + return Rest.get() + .success(function(data){ + return data; + }) + .error(function(data, status) { + ProcessErrors($rootScope, data, status, null, { hdr: 'Error!', + msg: 'Call to ' + url + '. GET returned: ' + status }); + }); + }, + stringifyParams: function(params){ + return _.reduce(params, (result, value, key) => { + return result + key + '=' + value + '&'; + }, ''); + }, + // the the API passes through Ansible's event_data response + // we need to massage away the verbose & redundant stdout/stderr properties + processJson: function(data){ + // configure fields to ignore + var ignored = [ + 'type', + 'event_data', + 'related', + 'summary_fields', + 'url', + 'ansible_facts', + ]; + // remove ignored properties + var result = _.chain(data).cloneDeep().forEach(function(value, key, collection){ + if (ignored.indexOf(key) > -1){ + delete collection[key]; + } + }).value(); + return result; } }; return val; diff --git a/awx/ui/client/src/job-results/parse-stdout.service.js b/awx/ui/client/src/job-results/parse-stdout.service.js index 03852eb185..14d804ab23 100644 --- a/awx/ui/client/src/job-results/parse-stdout.service.js +++ b/awx/ui/client/src/job-results/parse-stdout.service.js @@ -77,7 +77,7 @@ export default ['$log', 'moment', function($log, moment){ return `"`; } else{ - return ` JobResultsStdOut-stdoutColumn--clickable" ui-sref="jobDetail.host-event.json({eventId: ${event.id}, taskUuid: '${event.event_data.task_uuid}' })" aw-tool-tip="Event ID: ${event.id}
Status: ${event.event_display}
Click for details" data-placement="top"`; + return ` JobResultsStdOut-stdoutColumn--clickable" ui-sref="jobResult.host-event.json({eventId: ${event.id}, taskUuid: '${event.event_data.task_uuid}' })" aw-tool-tip="Event ID: ${event.id}
Status: ${event.event_display}
Click for details" data-placement="top"`; } }, 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 3cfb80e7a3..8269cac26b 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 @@ -124,12 +124,12 @@ export default if($rootScope.portalMode===false && Empty(data.system_job) || (base === 'home')){ // use $state.go with reload: true option to re-instantiate sockets in - var goToJobDetails = function(state) { + var goTojobResults = function(state) { $state.go(state, {id: job}, {reload:true}); }; if(_.has(data, 'job')) { - goToJobDetails('jobDetail'); + goTojobResults('jobResult'); } else if(base === 'jobs'){ if(scope.clearDialog) { scope.clearDialog(); @@ -137,20 +137,20 @@ export default return; } else if(data.type && data.type === 'workflow_job') { job = data.id; - goToJobDetails('workflowResults'); + goTojobResults('workflowResults'); } else if(_.has(data, 'ad_hoc_command')) { - goToJobDetails('adHocJobStdout'); + goTojobResults('adHocJobStdout'); } else if(_.has(data, 'system_job')) { - goToJobDetails('managementJobStdout'); + goTojobResults('managementJobStdout'); } else if(_.has(data, 'project_update')) { // If we are on the projects list or any child state of that list // then we want to stay on that page. Otherwise go to the stdout // view. if(!$state.includes('projects')) { - goToJobDetails('scmUpdateStdout'); + goTojobResults('scmUpdateStdout'); } } else if(_.has(data, 'inventory_update')) { @@ -158,7 +158,7 @@ export default // page then we want to stay on that page. Otherwise go to the stdout // view. if(!$state.includes('inventoryManage')) { - goToJobDetails('inventorySyncStdout'); + goTojobResults('inventorySyncStdout'); } } } 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 49fb8b7640..39e8404f71 100644 --- a/awx/ui/client/src/job-submission/job-submission.controller.js +++ b/awx/ui/client/src/job-submission/job-submission.controller.js @@ -136,7 +136,7 @@ export default // As of 3.0, the only place the user can relaunch a // playbook is on jobTemplates.edit (completed_jobs tab), - // jobs, and jobDetails $states. + // jobs, and jobResults $states. if (!$scope.submitJobRelaunch) { if($scope.submitJobType && $scope.submitJobType === 'job_template') { @@ -236,18 +236,18 @@ export default // Go out and get some of the job details like inv, cred, name Rest.setUrl(GetBasePath('jobs') + $scope.submitJobId); Rest.get() - .success(function (jobDetailData) { + .success(function (jobResultData) { $scope.job_template_data = { - name: jobDetailData.name + name: jobResultData.name }; $scope.defaults = {}; - if(jobDetailData.summary_fields.inventory) { - $scope.defaults.inventory = angular.copy(jobDetailData.summary_fields.inventory); - $scope.selected_inventory = angular.copy(jobDetailData.summary_fields.inventory); + if(jobResultData.summary_fields.inventory) { + $scope.defaults.inventory = angular.copy(jobResultData.summary_fields.inventory); + $scope.selected_inventory = angular.copy(jobResultData.summary_fields.inventory); } - if(jobDetailData.summary_fields.credential) { - $scope.defaults.credential = angular.copy(jobDetailData.summary_fields.credential); - $scope.selected_credential = angular.copy(jobDetailData.summary_fields.credential); + if(jobResultData.summary_fields.credential) { + $scope.defaults.credential = angular.copy(jobResultData.summary_fields.credential); + $scope.selected_credential = angular.copy(jobResultData.summary_fields.credential); updateRequiredPasswords(); } initiateModal(); diff --git a/awx/ui/client/src/jobs/jobs-list.controller.js b/awx/ui/client/src/jobs/jobs-list.controller.js index ac1ef684cd..1413488e0e 100644 --- a/awx/ui/client/src/jobs/jobs-list.controller.js +++ b/awx/ui/client/src/jobs/jobs-list.controller.js @@ -112,29 +112,29 @@ RelaunchJob({ scope: $scope, id: typeId, type: job.type, name: job.name }); }; - $scope.viewJobDetails = function(job) { + $scope.viewjobResults = function(job) { - var goToJobDetails = function(state) { + var goTojobResults = function(state) { $state.go(state, { id: job.id }, { reload: true }); }; switch (job.type) { case 'job': - goToJobDetails('jobDetail'); + goTojobResults('jobResult'); break; case 'ad_hoc_command': - goToJobDetails('adHocJobStdout'); + goTojobResults('adHocJobStdout'); break; case 'system_job': - goToJobDetails('managementJobStdout'); + goTojobResults('managementJobStdout'); break; case 'project_update': - goToJobDetails('scmUpdateStdout'); + goTojobResults('scmUpdateStdout'); break; case 'inventory_update': - goToJobDetails('inventorySyncStdout'); + goTojobResults('inventorySyncStdout'); break; case 'workflow_job': - goToJobDetails('workflowResults'); + goTojobResults('workflowResults'); break; } diff --git a/awx/ui/client/src/lists/AllJobs.js b/awx/ui/client/src/lists/AllJobs.js index b611cb47d6..b3ac5c5b71 100644 --- a/awx/ui/client/src/lists/AllJobs.js +++ b/awx/ui/client/src/lists/AllJobs.js @@ -30,12 +30,12 @@ export default dataTitle: "{{ job.status_popover_title }}", icon: 'icon-job-{{ job.status }}', iconOnly: true, - ngClick:"viewJobDetails(job)", + ngClick:"viewjobResults(job)", nosort: true }, id: { label: 'ID', - ngClick:"viewJobDetails(job)", + ngClick:"viewjobResults(job)", columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumnAdjacent', awToolTip: "{{ job.status_tip }}", dataPlacement: 'top', @@ -44,7 +44,7 @@ export default name: { label: i18n._('Name'), columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6', - ngClick: "viewJobDetails(job)", + ngClick: "viewjobResults(job)", badgePlacement: 'right', badgeCustom: true, badgeIcon: `
- - - + + +
diff --git a/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html b/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html index 451f10c5e8..5df1bd84c7 100644 --- a/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html +++ b/awx/ui/client/src/standard-out/inventory-sync/standard-out-inventory-sync.partial.html @@ -8,9 +8,9 @@ RESULTS
- - - + + +
diff --git a/awx/ui/client/src/standard-out/log/standard-out-log.controller.js b/awx/ui/client/src/standard-out/log/standard-out-log.controller.js index 45564fffc6..bf8a8f89ef 100644 --- a/awx/ui/client/src/standard-out/log/standard-out-log.controller.js +++ b/awx/ui/client/src/standard-out/log/standard-out-log.controller.js @@ -20,7 +20,7 @@ export default ['$log', '$rootScope', '$scope', '$state', '$stateParams', 'Proce // Open up a socket for events depending on the type of job function openSockets() { - if ($state.current.name === 'jobDetail') { + if ($state.current.name === 'jobResult') { $log.debug("socket watching on job_events-" + job_id); $scope.$on(`ws-job_events-${job_id}`, function() { $log.debug("socket fired on job_events-" + job_id); diff --git a/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.partial.html b/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.partial.html index bb7a6c1e64..8c9740a5eb 100644 --- a/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.partial.html +++ b/awx/ui/client/src/standard-out/management-jobs/standard-out-management-jobs.partial.html @@ -8,8 +8,8 @@ RESULTS
- - + +
diff --git a/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html b/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html index 458b2c6dd2..249cb0bf72 100644 --- a/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html +++ b/awx/ui/client/src/standard-out/scm-update/standard-out-scm-update.partial.html @@ -8,9 +8,9 @@ RESULTS
- - - + + +
diff --git a/awx/ui/client/src/standard-out/standard-out.controller.js b/awx/ui/client/src/standard-out/standard-out.controller.js index 64f34cb68f..0cabd74389 100644 --- a/awx/ui/client/src/standard-out/standard-out.controller.js +++ b/awx/ui/client/src/standard-out/standard-out.controller.js @@ -33,14 +33,14 @@ export function JobStdoutController ($rootScope, $scope, $state, $stateParams, if (data.status === 'failed' || data.status === 'canceled' || data.status === 'error' || data.status === 'successful') { // Go out and refresh the job details - getJobDetails(); + getjobResults(); } }); // Set the parse type so that CodeMirror knows how to display extra params YAML/JSON $scope.parseType = 'yaml'; - function getJobDetails() { + function getjobResults() { // Go out and get the job details based on the job type. jobType gets defined // in the data block of the route declaration for each of the different types @@ -260,7 +260,7 @@ export function JobStdoutController ($rootScope, $scope, $state, $stateParams, RelaunchJob({ scope: $scope, id: typeId, type: job.type, name: job.name }); }; - getJobDetails(); + getjobResults(); } diff --git a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js index e7379fe19c..08ea0b4142 100644 --- a/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js +++ b/awx/ui/client/src/templates/workflows/workflow-chart/workflow-chart.directive.js @@ -830,7 +830,7 @@ export default [ '$state','moment', '$timeout', '$window', this.on("click", function(d) { if(d.job.id && d.unifiedJobTemplate) { if(d.unifiedJobTemplate.unified_job_type === 'job') { - $state.go('jobDetail', {id: d.job.id}); + $state.go('jobResult', {id: d.job.id}); } else if(d.unifiedJobTemplate.unified_job_type === 'inventory_update') { $state.go('inventorySyncStdout', {id: d.job.id});