diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index 39b8a0fe68..79f84df241 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -6,7 +6,7 @@ */ var urlPrefix = $basePath; -angular.module('ansible', [ +angular.module('Tower', [ 'ngRoute', 'ngSanitize', 'ngCookies', @@ -97,7 +97,8 @@ angular.module('ansible', [ 'JobsListDefinition', 'LogViewerStatusDefinition', 'LogViewerHelper', - 'LogViewerOptionsDefinition' + 'LogViewerOptionsDefinition', + 'JobDetailHelper' ]) .constant('AngularScheduler.partials', $basePath + 'lib/angular-scheduler/lib/') @@ -107,11 +108,18 @@ angular.module('ansible', [ .config(['$routeProvider', function ($routeProvider) { + $routeProvider. + when('/jobs', { templateUrl: urlPrefix + 'partials/jobs.html', controller: 'JobsListController' }). + + when('/jobs/:id', { + templateUrl: urlPrefix + 'partials/job_detail.html', + controller: 'JobDetailController' + }). when('/job_events/:id', { templateUrl: urlPrefix + 'partials/job_events.html', @@ -405,7 +413,6 @@ angular.module('ansible', [ $rootScope.sessionTimer = Timer.init(); $rootScope.$on("$routeChangeStart", function (event, next) { - // Before navigating away from current tab, make sure the primary view is visible if ($('#stream-container').is(':visible')) { HideStream(); diff --git a/awx/ui/static/js/controllers/Credentials.js b/awx/ui/static/js/controllers/Credentials.js index b164b82f29..e7ff79e9be 100644 --- a/awx/ui/static/js/controllers/Credentials.js +++ b/awx/ui/static/js/controllers/Credentials.js @@ -23,7 +23,7 @@ function CredentialsList($scope, $rootScope, $location, $log, $routeParams, Rest base = $location.path().replace(/^\//, '').split('/')[0], mode = (base === 'credentials') ? 'edit' : 'select', url; - + view.inject(list, { mode: mode, scope: $scope }); $scope.selected = []; diff --git a/awx/ui/static/js/controllers/JobDetail.js b/awx/ui/static/js/controllers/JobDetail.js new file mode 100644 index 0000000000..8bd10b1c70 --- /dev/null +++ b/awx/ui/static/js/controllers/JobDetail.js @@ -0,0 +1,88 @@ +/************************************ + * Copyright (c) 2014 AnsibleWorks, Inc. + * + * JobDetail.js + * + */ + +'use strict'; + +function JobDetailController ($scope, $compile, $routeParams, ClearScope, Breadcrumbs, LoadBreadCrumbs, GetBasePath, Wait, Rest, ProcessErrors, DigestEvents) { + + ClearScope(); + + var job_id = $routeParams.id, + job; + + /*LoadBreadCrumbs(); + + e = angular.element(document.getElementById('breadcrumbs')); + e.html(Breadcrumbs({ list: { editTitle: 'Jobs' } , mode: 'edit' })); + $compile(e)($scope); + */ + + $scope.plays = []; + $scope.tasks = []; + + // Apply each event to the view + if ($scope.removeEventsReady) { + $scope.removeEventsReady(); + } + $scope.removeEventsReady = $scope.$on('EventsReady', function(e, events) { + DigestEvents({ + scope: $scope, + events: events + }); + }); + + // Get events, page size 50 + if ($scope.removeJobReady) { + $scope.removeJobReady(); + } + $scope.removeJobReady = $scope.$on('JobReady', function(e, next) { + if (next) { + Rest.setUrl(next); + Rest.get() + .success(function(data) { + $scope.$emit('EventsReady', data.results); + if (data.next) { + $scope.$emit('JobReady', data.next); + } + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', + msg: 'Failed to retrieve job events: ' + next + ' GET returned: ' + status }); + }); + } + }); + + // Load the job record + Rest.setUrl(GetBasePath('jobs') + job_id + '/'); + Rest.get() + .success(function(data) { + 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 = '/#/job_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.job_type = data.job_type; + $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.$emit('JobReady', data.related.job_events + '?page_size=50&order_by=id'); + }) + .error(function(data, status) { + ProcessErrors($scope, data, status, null, { hdr: 'Error!', + msg: 'Failed to retrieve job: ' + $routeParams.id + '. GET returned: ' + status }); + }); +} + +JobDetailController.$inject = [ '$scope', '$compile', '$routeParams', 'ClearScope', 'Breadcrumbs', 'LoadBreadCrumbs', 'GetBasePath', 'Wait', + 'Rest', 'ProcessErrors', 'DigestEvents' +]; diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js index 3c33eaa566..5a0ec2da5d 100644 --- a/awx/ui/static/js/helpers/Hosts.js +++ b/awx/ui/static/js/helpers/Hosts.js @@ -209,7 +209,7 @@ function($routeParams, Empty, InventoryHosts, GetBasePath, SearchInit, PaginateI scope[list.iterator + 'SearchFieldLabel'] = list.fields.has_active_failures.label; scope[list.iterator + 'SearchSelectValue'] = { value: 1 }; } - scope.search(list.iterator); + scope.search(list.iterator, null, true); }; }]) diff --git a/awx/ui/static/js/helpers/JobDetail.js b/awx/ui/static/js/helpers/JobDetail.js new file mode 100644 index 0000000000..3de066a733 --- /dev/null +++ b/awx/ui/static/js/helpers/JobDetail.js @@ -0,0 +1,108 @@ +/************************************ + * Copyright (c) 2014 AnsibleWorks, Inc. + * + * JobDetail.js + * + * 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 + +*/ + +'use strict'; + +angular.module('JobDetailHelper', ['Utilities', 'RestServices']) + +.factory('DigestEvents', ['UpdatePlayStatus', 'UpdatePlayNoHostsMatched', function(UpdatePlayStatus, UpdatePlayNoHostsMatched) { + return function(params) { + var scope = params.scope, + events = params.events; + events.forEach(function(event) { + if (event.event === 'playbook_on_play_start') { + scope.plays.push({ + id: event.id, + name: event.play, + status: (event.failed) ? 'failed' : 'successful' + }); + } + if (event.event === 'playbook_on_task_start') { + scope.tasks.push({ + id: event.id, + name: event.task, + play_id: event.parent, + status: (event.failed) ? 'failed' : 'successful' + }); + UpdatePlayStatus({ scope: scope, play_id: event.parent, status: event.status }); + } + if (event.event === 'playbook_on_no_hosts_matched') { + UpdatePlayNoHostsMatched({ scope: scope, play_id: event.parent }); + } + if (event.event === 'runner_on_failed') { + + } + if (event.event === 'playbook_on_stats') { + + } + + }); + }; +}]) + +// Update the status of a play +.factory('UpdatePlayStatus', [ function() { + return function(params) { + var scope = params.scope, + status = params.status, + id = params.play_id; + scope.plays.every(function(play,idx) { + if (play.id === id) { + scope.plays[idx].status = (status) ? 'failed' : 'successful'; + return false; + } + return true; + }); + }; +}]) + +.factory('UpdatePlayNoHostsMatched', [ function() { + return function(params) { + var scope = params.scope, + id = params.play_id; + scope.plays.every(function(play,idx) { + if (play.id === id) { + scope.plays[idx].status = 'none'; + return false; + } + return true; + }); + }; +}]); + + + diff --git a/awx/ui/static/js/helpers/Jobs.js b/awx/ui/static/js/helpers/Jobs.js index c0565f3763..86489468e9 100644 --- a/awx/ui/static/js/helpers/Jobs.js +++ b/awx/ui/static/js/helpers/Jobs.js @@ -86,10 +86,15 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job list = scope.queued_jobs; } job = Find({ list: list, key: 'id', val: id }); - LogViewer({ - scope: scope, - url: job.url - }); + if (job.type === 'job') { + $location.url('/jobs/' + job.id); + } + else { + LogViewer({ + scope: scope, + url: job.url + }); + } } }; }; diff --git a/awx/ui/static/js/helpers/refresh.js b/awx/ui/static/js/helpers/refresh.js index 203e4bfe84..c83b32e178 100644 --- a/awx/ui/static/js/helpers/refresh.js +++ b/awx/ui/static/js/helpers/refresh.js @@ -26,7 +26,7 @@ angular.module('RefreshHelper', ['RestServices', 'Utilities', 'PaginationHelpers iterator = params.iterator, url = params.url; - scope[iterator + 'Loading'] = true; + //scope[iterator + 'Loading'] = true; scope.current_url = url; Rest.setUrl(url); Rest.get() @@ -45,6 +45,7 @@ angular.module('RefreshHelper', ['RestServices', 'Utilities', 'PaginationHelpers } scope[set] = data.results; scope[iterator + 'Loading'] = false; + scope[iterator + "HidePaginator"] = false; Wait('stop'); scope.$emit('PostRefresh'); }) diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 5c8313bf8a..e9999361d4 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -1569,17 +1569,17 @@ tr td button i { } } - /* - .activity-btn { - padding-left: 2px; - padding-right: 2px; - padding-bottom: 2px; - img { - width: 16px; - height: 16px; +/* New job detail page */ + +.job-detail-tables { + .table>tbody>tr>td { + border-top-color: #fff; } - } - */ + .table>thead>tr>th { + border-bottom-color: #fff; + } +} + /* ng-cloak directive */ diff --git a/awx/ui/static/lib/ansible/generator-helpers.js b/awx/ui/static/lib/ansible/generator-helpers.js index 0c70c45546..5a77b61435 100644 --- a/awx/ui/static/lib/ansible/generator-helpers.js +++ b/awx/ui/static/lib/ansible/generator-helpers.js @@ -792,7 +792,7 @@ angular.module('GeneratorHelpers', []) html += "\n"; html += "
\n"; html += "
\n"; - html += "
-