From ae839341458583eee63db0d3753fcd079b986494 Mon Sep 17 00:00:00 2001 From: Leigh Johnson Date: Thu, 7 Apr 2016 15:12:56 -0400 Subject: [PATCH] modularize host-summary and use socket.io/jobs/ endpoint --- awx/ui/client/src/helpers/JobDetail.js | 106 +----------------- awx/ui/client/src/helpers/JobSubmission.js | 1 + .../job-detail/host-event/host-event.route.js | 30 +---- .../client/src/job-detail/host-event/main.js | 3 +- .../host-summary/host-summary-factory.js | 0 .../host-summary/host-summary.controller.js | 53 +++++++++ .../host-summary/host-summary.partial.html | 66 +++++++++++ .../src/job-detail/job-detail.controller.js | 53 +++------ .../src/job-detail/job-detail.partial.html | 73 +----------- .../client/src/job-detail/job-detail.route.js | 25 ++++- .../src/job-detail/job-detail.service.js | 70 ++++++------ 11 files changed, 205 insertions(+), 275 deletions(-) create mode 100644 awx/ui/client/src/job-detail/host-summary/host-summary-factory.js create mode 100644 awx/ui/client/src/job-detail/host-summary/host-summary.controller.js create mode 100644 awx/ui/client/src/job-detail/host-summary/host-summary.partial.html diff --git a/awx/ui/client/src/helpers/JobDetail.js b/awx/ui/client/src/helpers/JobDetail.js index 0c6ea3359c..4911981939 100644 --- a/awx/ui/client/src/helpers/JobDetail.js +++ b/awx/ui/client/src/helpers/JobDetail.js @@ -930,7 +930,7 @@ export default }]) // Call when the selected task needs to change - .factory('SelectTask', ['LoadHosts', 'JobDetailService', function(LoadHosts, JobDetailService) { + .factory('SelectTask', ['JobDetailService', function(JobDetailService) { return function(params) { var scope = params.scope, id = params.id, @@ -953,111 +953,12 @@ export default order: 'host_name,counter', }; JobDetailService.getRelatedJobEvents(scope.job.id, params).success(function(res){ - scope.hostResults = JobDetailService.processHostResults(res.results) + scope.hostResults = JobDetailService.processHostEvents(res.results) scope.hostResultsLoading = false; }); }; }]) - // Refresh the list of hosts - .factory('LoadHosts', ['Rest', 'ProcessErrors', function(Rest, ProcessErrors) { - return function(params) { - var scope = params.scope, - callback = params.callback, - url; - scope.hostResults = []; - - if (scope.selectedTask) { - // If we have a selected task, then get the list of hosts - url = scope.job.related.job_events + '?parent=' + scope.selectedTask + '&'; - url += (scope.search_host_name) ? 'host__name__icontains=' + scope.search_host_name + '&' : ''; - url += (scope.search_host_status === 'failed') ? 'failed=true&' : ''; - url += 'event__startswith=runner&page_size=' + scope.hostResultsMaxRows + '&order=host_name,counter'; - console.log(url) - scope.hostResultsLoading = true; - Rest.setUrl(url); - Rest.get() - .success(function(data) { - scope.next_host_results = data.next; - scope.hostResults = []; - data.results.forEach(function(event) { - var status, status_text, item, msg; - if (event.event === "runner_on_skipped") { - status = 'skipped'; - } - else if (event.event === "runner_on_unreachable") { - status = 'unreachable'; - } - else { - status = (event.failed) ? 'failed' : (event.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 (event.event_data && event.event_data.res) { - item = event.event_data.res.item; - if (typeof item === "object") { - item = JSON.stringify(item); - item = item.replace(/\"/g,'').replace(/:/g,': ').replace(/,/g,', '); - } - } - - 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; - } - } - if (event.event !== "runner_on_no_hosts") { - scope.hostResults.push({ - id: event.id, - status: status, - status_text: status_text, - host_id: event.host, - task_id: event.parent, - name: event.event_data.host, - created: event.created, - msg: msg, - item: item - }); - } - }); - - scope.hostResultsLoading = false; - if (callback) { - scope.$emit(callback); - } - }) - .error(function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Call to ' + url + '. GET returned: ' + status }); - }); - } - else { - if (callback) { - scope.$emit(callback); - } - //$('#hosts-table-detail').mCustomScrollbar("update"); - } - }; - }]) - // Refresh the list of hosts in the hosts summary section .factory('ReloadHostSummaryList', ['Rest', 'ProcessErrors', function(Rest, ProcessErrors) { return function(params) { @@ -1069,7 +970,6 @@ export default url += (scope.search_host_summary_name) ? 'host_name__icontains=' + scope.search_host_summary_name + '&': ''; url += (scope.search_host_summary_status === 'failed') ? 'failed=true&' : ''; url += '&page_size=' + scope.hostSummariesMaxRows + '&order=host_name'; - scope.hosts = []; scope.hostSummariesLoading = true; @@ -1098,7 +998,6 @@ export default }); scope.hostSummariesLoading = false; - if (callback) { scope.$emit(callback); } @@ -1564,7 +1463,6 @@ export default function(DrawPlays, DrawTasks, DrawHostResults, DrawHostSummaries, DrawGraph) { return function(params) { var scope = params.scope; - if (!scope.pauseLiveEvents) { DrawPlays({ scope: scope }); DrawTasks({ scope: scope }); diff --git a/awx/ui/client/src/helpers/JobSubmission.js b/awx/ui/client/src/helpers/JobSubmission.js index aee3570311..37caa00329 100644 --- a/awx/ui/client/src/helpers/JobSubmission.js +++ b/awx/ui/client/src/helpers/JobSubmission.js @@ -804,6 +804,7 @@ function($compile, Rest, GetBasePath, TextareaResize,CreateDialog, GenerateForm, if((scope.portalMode===false || scope.$parent.portalMode===false ) && Empty(data.system_job) || (base === 'home')){ $location.path('/jobs/' + job); + // use $state.go with reload: true option to re-instantiate sockets in } }); 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 index 7d4f1cc011..1d270039ab 100644 --- 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 @@ -35,19 +35,14 @@ var hostEventModal = { $('.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'), - resolve: { - features: ['FeaturesService', function(FeaturesService){ - return FeaturesService.get(); - }] - } - } + }; var hostEventJson = { name: 'jobDetail.host-event.json', @@ -60,27 +55,12 @@ var hostEventModal = { }] } }; - var hostEventTiming = { - name: 'jobDetail.host-event.timing', - url: '/timing', - controller: 'HostEventController', - templateUrl: templateUrl('job-detail/host-event/host-event-timing'), - resolve: { - features: ['FeaturesService', function(FeaturesService){ - return FeaturesService.get(); - }] - } - }; + var hostEventStdout = { name: 'jobDetail.host-event.stdout', url: '/stdout', controller: 'HostEventController', - templateUrl: templateUrl('job-detail/host-event/host-event-stdout'), - resolve: { - features: ['FeaturesService', function(FeaturesService){ - return FeaturesService.get(); - }] - } + templateUrl: templateUrl('job-detail/host-event/host-event-stdout') }; - export {hostEventDetails, hostEventJson, hostEventTiming, hostEventStdout, hostEventModal} \ No newline at end of file + export {hostEventDetails, hostEventJson, hostEventStdout, hostEventModal} \ No newline at end of file diff --git a/awx/ui/client/src/job-detail/host-event/main.js b/awx/ui/client/src/job-detail/host-event/main.js index c2b82530a1..0670181f09 100644 --- a/awx/ui/client/src/job-detail/host-event/main.js +++ b/awx/ui/client/src/job-detail/host-event/main.js @@ -4,7 +4,7 @@ * All Rights Reserved *************************************************/ - import {hostEventModal, hostEventDetails, hostEventTiming, + import {hostEventModal, hostEventDetails, hostEventJson, hostEventStdout} from './host-event.route'; import controller from './host-event.controller'; @@ -15,7 +15,6 @@ .run(['$stateExtender', function($stateExtender){ $stateExtender.addState(hostEventModal); $stateExtender.addState(hostEventDetails); - $stateExtender.addState(hostEventTiming); $stateExtender.addState(hostEventJson); $stateExtender.addState(hostEventStdout); }]); \ No newline at end of file diff --git a/awx/ui/client/src/job-detail/host-summary/host-summary-factory.js b/awx/ui/client/src/job-detail/host-summary/host-summary-factory.js new file mode 100644 index 0000000000..e69de29bb2 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 new file mode 100644 index 0000000000..2af1b81443 --- /dev/null +++ b/awx/ui/client/src/job-detail/host-summary/host-summary.controller.js @@ -0,0 +1,53 @@ +/************************************************* + * Copyright (c) 2016 Ansible, Inc. + * + * All Rights Reserved + *************************************************/ + + export default + ['$scope', '$rootScope', '$stateParams', 'JobDetailService', 'jobSocket', function($scope, $rootScope, $stateParams, JobDetailService, jobSocket){ + + // the job_events socket should be substituted for a job_host_summary socket post 3.0 + var page_size = 200; + var socketListener = function(){ + console.log(jobSocket) + jobSocket.on('summary_complete', function(data) { + JobDetailService.getJob($stateParams.id).success(function(res){ + console.log('job at summary_complete.', res) + }); + }); + jobSocket.on('status_changed', function(data) { + JobDetailService.getJob($stateParams.id).success(function(res){ + console.log('job at data.stats.', data.status, res) + }); + JobDetailService.getJobHostSummaries($stateParams.id, {}).success(function(res){ + console.log('jobhostSummaries at summary_complete.', data.status, res) + }); + }); + } + $scope.loading = $scope.hosts.length > 0 ? false : true; + $scope.done = true; + $scope.events = []; + $scope.$watchCollection('events', function(c){ + var filtered = $scope.events.filter(function(event){ + return ((event.failed || event.changed || + 'runner_on_ok' || 'runner_on_async_ok' || + 'runner_on_unreachable' || 'runner_on_skipped') + && event.host_name != ''); + }); + var grouped = _.groupBy(filtered, 'host_name'); + //$scope.hosts = + }); + + + $scope.search = function(host_name){}; + $scope.filter = function(filter){}; + + var init = function(){ + socketListener(); + JobDetailService.getJobHostSummaries($stateParams.id, {}).success(function(res){ + console.log('jobhostSummaries at init.', res) + }); + }; + init(); + }]; \ No newline at end of file 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 new file mode 100644 index 0000000000..d86c5b3545 --- /dev/null +++ b/awx/ui/client/src/job-detail/host-summary/host-summary.partial.html @@ -0,0 +1,66 @@ + +
+ +
+
+
+ + + +
+
+
+
+ + +
+
+
+ +
+ + + + + + + +
HostsCompleted Tasks
+
+ +
+ + + + + + + + + + + + + + + + +
+ {{ host.name }} + + {{ host.ok }} + {{ host.changed }} + {{ host.unreachable }} + {{ host.failed }} +
Waiting...
Loading...
No matching hosts
+
+ +
+ +
+ +
+ +
+ +
diff --git a/awx/ui/client/src/job-detail/job-detail.controller.js b/awx/ui/client/src/job-detail/job-detail.controller.js index 91264dd1b4..cbedbde7cb 100644 --- a/awx/ui/client/src/job-detail/job-detail.controller.js +++ b/awx/ui/client/src/job-detail/job-detail.controller.js @@ -277,7 +277,6 @@ export default scope.$emit('LoadHostSummaries'); }); - if (scope.removeInitialLoadComplete) { scope.removeInitialLoadComplete(); } @@ -292,12 +291,10 @@ export default }; JobDetailService.getRelatedJobEvents(scope.job.id, params) .success(function(data) { - if (data.results.length > 0) { LoadHostSummary({ scope: scope, data: data.results[0].event_data }); - } UpdateDOM({ scope: scope }); }) .error(function(data, status) { @@ -379,33 +376,12 @@ export default }; JobDetailService.getRelatedJobEvents(scope.job.id, params) .success(function(data) { - console.log(data) var idx, event, status, status_text, item, msg; if (data.results.length > 0) {$ lastEventId = data.results[0].id; } scope.next_host_results = data.next; - for (idx=data.results.length - 1; idx >= 0; idx--) { - event = data.results[idx]; - event.status = JobDetailService.processEventStatus(event).status; - msg = JobDetailService.processEventMsg(event); - item = JobDetailService.processEventItem(event); - - if (event.event !== "runner_on$_no_hosts") { - task.hostResults[event.id] = { - id: event.id, - status: event.status, - status_text: event.status.toUpperCase(), - host_id: event.host, - task_id: event.parent, - name: event.event_data.host, - created: event.created, - msg: msg, - counter: event.counter, - item: item - }; - } - } + task.hostResults = JobDetailService.processHostEvents(data.results); scope.$emit('LoadHostSummaries'); }); } else { @@ -999,13 +975,6 @@ export default scope.searchTasksEnabled = true; } if (!scope.liveEventProcessing || scope.pauseLiveEvents) { - // /api/v1/jobs/15/job_tasks/?event_id=762&task__icontains=create&page_size=200&order=id - var params = { - event_id: scope.selectedPlay, - task__icontains: scope.search_task_name, - page_size: scope.tasksMaxRows, - - }; if (scope.search_task_status === 'failed'){ params.failed = true; } @@ -1042,7 +1011,7 @@ export default params.failed = true; } JobDetailService.getRelatedJobEvents(scope.job.id, params).success(function(res){ - scope.hostResults = JobDetailService.processHostResults(res.results) + scope.hostResults = JobDetailService.processHostEvents(res.results) scope.hostResultsLoading = false; }); } @@ -1088,7 +1057,6 @@ export default scope.filterHostStatus = function() { scope.search_host_status = (scope.search_host_status === 'all') ? 'failed' : 'all'; if (!scope.liveEventProcessing || scope.pauseLiveEvents) { - console.log('filterHostStattus', scope) var params = { parent: scope.selectedTask, event__startswith: 'runner', @@ -1100,7 +1068,7 @@ export default } scope.hostResultsLoading = true; JobDetailService.getRelatedJobEvents(scope.job.id, params).success(function(res){ - scope.hostResults = JobDetailService.processHostResults(res.results) + scope.hostResults = JobDetailService.processHostEvents(res.results) scope.hostResultsLoading = false; }); } @@ -1109,6 +1077,21 @@ export default scope.filterHostSummaryStatus = function() { scope.search_host_summary_status = (scope.search_host_summary_status === 'all') ? 'failed' : 'all'; if (!scope.liveEventProcessing || scope.pauseLiveEvents) { + // /api/v1/jobs/11/job_host_summaries/?failed=true&&page_size=200&order=host_name + var params = { + page_size: scope.hostSummariesMaxRows, + order: 'host_name' + } + if (scope.search_host_summary_status === 'failed'){ + params.failed = true; + } + /* + JobDetailService.getJobHostSummaries(scope.job.id, params).success(function(res){ + scope.next_host_summaries = res.next; + scope.hosts = res.results; + console.log(res.results) + }); + */ ReloadHostSummaryList({ scope: scope }); diff --git a/awx/ui/client/src/job-detail/job-detail.partial.html b/awx/ui/client/src/job-detail/job-detail.partial.html index 2ced4bf551..616b463386 100644 --- a/awx/ui/client/src/job-detail/job-detail.partial.html +++ b/awx/ui/client/src/job-detail/job-detail.partial.html @@ -388,75 +388,10 @@ - - + + diff --git a/awx/ui/client/src/job-detail/job-detail.route.js b/awx/ui/client/src/job-detail/job-detail.route.js index 3494845759..62678016fe 100644 --- a/awx/ui/client/src/job-detail/job-detail.route.js +++ b/awx/ui/client/src/job-detail/job-detail.route.js @@ -5,12 +5,11 @@ *************************************************/ import {templateUrl} from '../shared/template-url/template-url.factory'; +import HostSummaryController from './host-summary/host-summary.controller'; export default { name: 'jobDetail', url: '/jobs/:id', - templateUrl: templateUrl('job-detail/job-detail'), - controller: 'JobDetailController', ncyBreadcrumb: { parent: 'jobs', label: "{{ job.id }} - {{ job.name }}" @@ -26,10 +25,32 @@ export default { 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; } + }], + jobSocket: ['Socket', '$rootScope', function(Socket, $rootScope) { + var job_socket = Socket({ + scope: $rootScope, + endpoint: "jobs" + }); + job_socket.init(); + // returns should really be providing $rootScope.job_socket + // otherwise, we have to inject the entire $rootScope into the controller + return job_socket; }] + }, + views: { + '': { + templateUrl: templateUrl('job-detail/job-detail'), + controller: 'JobDetailController', + }, + 'host-summary@jobDetail': { + templateUrl: templateUrl('job-detail/host-summary/host-summary'), + controller: HostSummaryController + } } }; diff --git a/awx/ui/client/src/job-detail/job-detail.service.js b/awx/ui/client/src/job-detail/job-detail.service.js index 2beecdc215..640aa4def7 100644 --- a/awx/ui/client/src/job-detail/job-detail.service.js +++ b/awx/ui/client/src/job-detail/job-detail.service.js @@ -63,52 +63,47 @@ export default // 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 + // 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' - } + 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' - } + } + // 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' - } + } + // 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'){ - return { - class: 'HostEvents-status--ok', - status: 'ok' - } + } + 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' - } - } - else{ - // study a case where none of these apply + } + if (event.event == 'runner_on_skipped'){ + return { + class: 'HostEvents-status--skipped', + status: 'skipped' } + } }, - - // Consumes a response from this.getRelatedJobEvents + // Consumes a response from this.getRelatedJobEvents(id, params) // returns an array for view logic to iterate over to build host result rows - processHostResults: function(data){ + processHostEvents: function(data){ var self = this; var results = []; - data.forEach(function(element, index, array){ - var event = element; + data.forEach(function(event){ if (event.event !== 'runner_on_no_hosts'){ var status = self.processEventStatus(event); var msg = self.processEventMsg(event); @@ -116,7 +111,7 @@ export default results.push({ id: event.id, status: status.status, - status_text: status.status.charAt(0).toUpperCase() + status.status.slice(1), + status_text: _.head(status.status).toUpperCase() + _.tail(status.status), host_id: event.host, task_id: event.parent, name: event.event_data.host, @@ -128,7 +123,6 @@ export default }); return results; }, - // GET events related to a job run // e.g. // ?event=playbook_on_stats