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 += "
-