From 826f2b681f14aaee91dd7047c55f80b09202d8cc Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Mon, 30 Jun 2014 17:34:10 -0400 Subject: [PATCH] Job detail page refactor Job tasks now include an unreachable count and %. Change unreachable color from grey to a different shade of red. While live events are happening host name filter is disabled and status filter is available. Created a custom status filter that accounts for live event processing. While live event processing is active angular filtering on status is disabled for tasks and plays, otherwise no tasks or plays would show up. However, status filtering does apply to host results and host summaries during live event processing. --- awx/ui/static/js/controllers/JobDetail.js | 58 +++++++++++---------- awx/ui/static/js/helpers/JobDetail.js | 63 +++++++++++++---------- awx/ui/static/less/ansible-ui.less | 10 ++-- awx/ui/static/less/job-details.less | 4 +- awx/ui/static/lib/ansible/filters.js | 20 +++++++ awx/ui/static/partials/job_detail.html | 10 ++-- 6 files changed, 103 insertions(+), 62 deletions(-) diff --git a/awx/ui/static/js/controllers/JobDetail.js b/awx/ui/static/js/controllers/JobDetail.js index d17e24ef6f..ceed30f3b3 100644 --- a/awx/ui/static/js/controllers/JobDetail.js +++ b/awx/ui/static/js/controllers/JobDetail.js @@ -135,7 +135,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, } scope.removeHostSummaries = scope.$on('LoadHostSummaries', function() { var url = scope.job.related.job_host_summaries + '?'; - url += '&page_size=' + scope.hostSummariesMaxRows + '&order_by=host__name'; + url += '&host__name__isnull=false&page_size=' + scope.hostSummariesMaxRows + '&order_by=host__name'; scope.jobData.hostSummaries = {}; @@ -245,7 +245,12 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, .success(function(data) { if (data.results.length > 0) { lastEventId = data.results[data.results.length - 1].id; - scope.activeTask = data.results[0].id; + if (scope.liveEventProcessing) { + scope.activeTask = data.results[data.results.length - 1].id; + } + else { + scope.activeTask = data.results[0].id; + } } data.results.forEach(function(event, idx) { var end, elapsed, status, status_text; @@ -294,6 +299,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, 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: {} }; @@ -338,7 +344,12 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, .success( function(data) { if (data.results.length > 0) { lastEventId = data.results[data.results.length - 1].id; - scope.activePlay = data.results[0].id; + if (scope.liveEventProcessing) { + scope.activePlay = data.results[data.results.length - 1].id; + } + else { + scope.activePlay = data.results[0].id; + } } data.results.forEach(function(event, idx) { var status, status_text, start, end, elapsed, ok, changed, failed, skipped; @@ -472,9 +483,16 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, 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.finished = (data.status === 'successful' || data.status === 'failed' || data.status === 'error') ? data.finished : null; scope.job_status.explanation = data.job_explanation; + if (data.status === 'successful' || data.status === 'failed' || data.status === 'error' || data.status === 'canceled') { + scope.job_status.finished = data.finsished; + scope.liveEventProcessing = false; + } + else { + scope.job_status.finished = null; + } + if (data.started && data.finished) { scope.job_status.elapsed = GetElapsed({ start: data.started, @@ -484,7 +502,7 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, else { scope.job_status.elapsed = '00:00:00'; } - scope.setSearchAll('host'); + //scope.setSearchAll('host'); scope.$emit('LoadPlays', data.related.job_events); if (!scope.credential_name) { scope.$emit('GetCredentialNames', data); @@ -604,20 +622,6 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, scope.adjustSize(); }, 500)); - scope.setSearchAll = function(search) { - if (search === 'host') { - scope.search_all_label = 'Host'; - scope.searchAllDisabled = false; - scope.search_all_placeholder = 'Search all by host name'; - } - else { - scope.search_all_label = 'Failures'; - scope.search_all_placeholder = 'Show failed events'; - scope.searchAllDisabled = true; - scope.search_all_placeholder = ''; - } - }; - scope.selectPlay = function(id) { scope.auto_scroll_plays = false; SelectPlay({ @@ -1139,13 +1143,15 @@ function JobDetailController ($rootScope, $scope, $compile, $routeParams, $log, scope.searchAllStatus = ''; nxtPlay = (scope.plays.length > 0) ? scope.plays[0].id : null; } - SelectPlay({ - scope: scope, - id: nxtPlay - }); - ReloadHostSummaryList({ - scope: scope - }); + if (!scope.liveEventProcessing) { + SelectPlay({ + scope: scope, + id: nxtPlay + }); + ReloadHostSummaryList({ + scope: scope + }); + } }; scope.viewHostResults = function(id) { diff --git a/awx/ui/static/js/helpers/JobDetail.js b/awx/ui/static/js/helpers/JobDetail.js index e204a37d4b..f9272559a8 100644 --- a/awx/ui/static/js/helpers/JobDetail.js +++ b/awx/ui/static/js/helpers/JobDetail.js @@ -169,20 +169,6 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge modified: event.modified, message: (event.event_data && event.event_data.res) ? event.event_data.res.msg : '' }); - break; - - // We will respond to the job status change event. No need to do this 2x. - /*case 'playbook_on_stats': - scope.job_status.finished = event.modified; - scope.job_status.elapsed = GetElapsed({ - start: scope.job_status.started, - end: scope.job_status.finished - }); - scope.job_status.status = (event.failed) ? 'failed' : 'successful'; - scope.job_status.status_class = ""; - //LoadHostSummary({ scope: scope, data: event.event_data }); - //DrawGraph({ scope: scope, resize: true }); - break;*/ } }; }]) @@ -417,6 +403,9 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge 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] = { @@ -426,7 +415,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge changed: (status === 'changed') ? 1 : 0, unreachable: (status === 'unreachable') ? 1 : 0, failed: (status === 'failed') ? 1 : 0, - status: (status === 'failed') ? 'failed' : 'successful' + status: (status === 'failed' || status === 'unreachable') ? 'failed' : 'successful' }; } @@ -503,7 +492,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge if (scope.jobData.plays[scope.activePlay].tasks[task_id] !== undefined) { task = scope.jobData.plays[scope.activePlay].tasks[task_id]; - if (task_id === scope.jobData.plays[scope.activePlay].firstTask && status !== 'unreachable') { + if (task_id === scope.jobData.plays[scope.activePlay].firstTask) { scope.jobData.plays[scope.activePlay].hostCount++; task.hostCount++; } @@ -513,6 +502,8 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge 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 }); @@ -525,14 +516,13 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge var task = params.task, diff; - //task = scope.jobData.plays[scope.activePlay].tasks[task_id]; - //task.hostCount = task.failedCount + task.changedCount + task.skippedCount + task.successfulCount; 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; - diff = (task.failedPct + task.changedPct + task.skippedPct + task.successfulPct) - 100; + diff = (task.failedPct + task.changedPct + task.skippedPct + task.successfulPct + task.unreachablePct) - 100; if (diff > 0) { if (task.failedPct > diff) { task.failedPct = task.failedPct - diff; @@ -546,11 +536,15 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge else if (task.successfulPct > diff) { task.successfulPct = task.successfulPct - diff; } + else if (task.unreachablePct > diff) { + task.unreachablePct = task.unreachablePct - diff; + } } task.successfulStyle = (task.successfulPct > 0) ? { 'display': 'inline-block', 'width': task.successfulPct + "%" } : { 'display': 'none' }; task.changedStyle = (task.changedPct > 0) ? { 'display': 'inline-block', 'width': task.changedPct + "%" } : { 'display': 'none' }; task.skippedStyle = (task.skippedPct > 0) ? { 'display': 'inline-block', 'width': task.skippedPct + "%" } : { 'display': 'none' }; task.failedStyle = (task.failedPct > 0) ? { 'display': 'inline-block', 'width': task.failedPct + "%" } : { 'display': 'none' }; + task.unreachableStyle = (task.unreachablePct > 0) ? { 'display': 'inline-block', 'width': task.unreachablePct + "%" } : { 'display': 'none' }; }; }]) @@ -667,6 +661,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge 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: '' }); @@ -823,7 +818,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge url = scope.job.related.job_host_summaries + '?'; url += (scope.search_all_hosts_name) ? 'host__name__icontains=' + scope.search_all_hosts_name + '&': ''; url += (scope.searchAllStatus === 'failed') ? 'failed=true&' : ''; - url += 'page_size=' + scope.hostSummariesMaxRows + '&order_by=host__name'; + url += '&page_size=' + scope.hostSummariesMaxRows + '&order_by=host__name'; scope.hosts = []; @@ -904,7 +899,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge graph_data.push({ label: 'Unreachable', value: (scope.host_summary.unreachable === scope.host_summary.total) ? 1 : scope.host_summary.unreachable, - color: '#A9A9A9' + color: '#FF3366' }); } if (scope.host_summary.failed) { @@ -975,7 +970,7 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge keys = Object.keys(tasks); keys.reverse(); newKeys = []; - for (idx=0; idx < scope.tasksMaxRows && idx < keys.length; idx++) { + for (idx=0; result.length < scope.tasksMaxRows && idx < keys.length; idx++) { newKeys.push(keys[idx]); } newKeys.sort(); @@ -1012,8 +1007,15 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge // a must be equal to b return 0; }); - while (idx < keys.length && idx < scope.hostResultsMaxRows) { - result.unshift(hostResults[keys[idx]]); + while (idx < keys.length && result.length < scope.hostResultsMaxRows) { + if (scope.searchAllStatus === 'failed') { + if (hostResults[keys[idx]].status === 'failed') { + result.unshift(hostResults[keys[idx]]); + } + } + else { + result.unshift(hostResults[keys[idx]]); + } idx++; } } @@ -1045,8 +1047,17 @@ function($rootScope, $log, UpdatePlayStatus, UpdateHostStatus, AddHostResult, Ge return 0; }); - while (idx < keys.length && idx < scope.hostSummariesMaxRows) { - result.push(hostSummaries[keys[idx]]); + console.log(hostSummaries); + + while (idx < keys.length && result.length < scope.hostSummariesMaxRows) { + if (scope.searchAllStatus === 'failed') { + if (hostSummaries[keys[idx]].status === 'failed') { + result.push(hostSummaries[keys[idx]]); + } + } + else { + result.push(hostSummaries[keys[idx]]); + } idx++; } } diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 8813fefc91..4f026634ad 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -18,8 +18,9 @@ @info: #d9edf7; /* alert info background color */ @info-border: #bce8f1; /* alert info border color */ @info-color: #3a87ad; -@red: #aa0000; // Ansible Unreachable +@red: #aa0000; // Ansible Failed @red-hover: #AE3F3A; +@unreachable: #FF3366; @changed: #FF9900; // Ansible Changed @skipped: #00aaaa; // Ansible Skipped @warning: #FF9900; @@ -1022,11 +1023,14 @@ input[type="checkbox"].checkbox-no-label { .icon-job-stopped, .icon-job-error, .icon-job-failed, - .icon-job-canceled, - .icon-job-unreachable { + .icon-job-canceled { color: @red; } + .icon-job-unreachable { + color: @unreachable; + } + .icon-job-none, .icon-job-pending, .icon-job-waiting, diff --git a/awx/ui/static/less/job-details.less b/awx/ui/static/less/job-details.less index a0740e42ff..76a801df07 100644 --- a/awx/ui/static/less/job-details.less +++ b/awx/ui/static/less/job-details.less @@ -11,7 +11,7 @@ @successful-hosts-color: @green; @changed-hosts-color: @changed; @skipped-hosts-color: @skipped; -@unreachable-hosts-color: #A9A9A9; +@unreachable-hosts-color: @unreachable; .job_summary { .table { @@ -73,7 +73,7 @@ background-color: @unreachable-hosts-color; } .unreachable-hosts-color { - color: @grey; + color: @unreachable-hosts-color; } .job_well { diff --git a/awx/ui/static/lib/ansible/filters.js b/awx/ui/static/lib/ansible/filters.js index 066ec54397..1334237370 100644 --- a/awx/ui/static/lib/ansible/filters.js +++ b/awx/ui/static/lib/ansible/filters.js @@ -67,4 +67,24 @@ angular.module('AWFilters', []) } return input; }; + }]) + + .filter('FilterFailedEvents', [ function() { + return function(input, liveEventProcessing, searchAllStatus) { + var results = []; + if (liveEventProcessing) { + // while live events are happening, we don't want angular to filter out anything + return input; + } + else if (searchAllStatus === 'failed') { + // filter by failed + input.forEach(function(row) { + if (row.status === 'failed') { + results.push(row); + } + }); + return results; + } + return input; + }; }]); \ No newline at end of file diff --git a/awx/ui/static/partials/job_detail.html b/awx/ui/static/partials/job_detail.html index 177cf1b3e0..a65c5d2097 100644 --- a/awx/ui/static/partials/job_detail.html +++ b/awx/ui/static/partials/job_detail.html @@ -83,7 +83,7 @@
-