mirror of
https://github.com/ansible/awx.git
synced 2026-05-20 07:17:40 -02:30
Dashboard
Built the job status dashboard widget.
This commit is contained in:
@@ -20,7 +20,7 @@ function JobsListController ($scope, $compile, $routeParams, ClearScope, Breadcr
|
|||||||
listCount = 0,
|
listCount = 0,
|
||||||
api_complete = false,
|
api_complete = false,
|
||||||
event_socket,
|
event_socket,
|
||||||
event_queue = [{"status":"pending","endpoint":"/socket.io/jobs","unified_job_id":4129,"event":"status_changed"}],
|
event_queue = [],
|
||||||
expecting = 0,
|
expecting = 0,
|
||||||
max_rows;
|
max_rows;
|
||||||
|
|
||||||
|
|||||||
@@ -372,7 +372,6 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
|
|||||||
return function (params) {
|
return function (params) {
|
||||||
var scope = params.scope,
|
var scope = params.scope,
|
||||||
id = params.id,
|
id = params.id,
|
||||||
base = $location.path().replace(/^\//, '').split('/')[0],
|
|
||||||
url,
|
url,
|
||||||
job_template,
|
job_template,
|
||||||
new_job_id,
|
new_job_id,
|
||||||
@@ -381,13 +380,7 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
|
|||||||
prompt_for_vars = false,
|
prompt_for_vars = false,
|
||||||
passwords;
|
passwords;
|
||||||
|
|
||||||
if (!Empty($routeParams.template_id)) {
|
url = GetBasePath('jobs') + id + '/';
|
||||||
// launching a job from job_template detail page
|
|
||||||
url = GetBasePath('jobs') + id + '/';
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
url = GetBasePath(base) + id + '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scope.removePostTheJob) {
|
if (scope.removePostTheJob) {
|
||||||
scope.removePostTheJob();
|
scope.removePostTheJob();
|
||||||
|
|||||||
@@ -46,7 +46,12 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job
|
|||||||
else if (scope.queued_jobs) {
|
else if (scope.queued_jobs) {
|
||||||
list = scope.queued_jobs;
|
list = scope.queued_jobs;
|
||||||
}
|
}
|
||||||
|
else if (scope.jobs) {
|
||||||
|
list = scope.jobs;
|
||||||
|
}
|
||||||
job = Find({ list: list, key: 'id', val: id });
|
job = Find({ list: list, key: 'id', val: id });
|
||||||
|
console.log('found job:');
|
||||||
|
console.log(job);
|
||||||
if (job.type === 'inventory_update') {
|
if (job.type === 'inventory_update') {
|
||||||
typeId = job.inventory_source;
|
typeId = job.inventory_source;
|
||||||
}
|
}
|
||||||
@@ -81,6 +86,9 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job
|
|||||||
else if (scope.queued_jobs) {
|
else if (scope.queued_jobs) {
|
||||||
list = scope.queued_jobs;
|
list = scope.queued_jobs;
|
||||||
}
|
}
|
||||||
|
else if (scope.jobs) {
|
||||||
|
list = scope.jobs;
|
||||||
|
}
|
||||||
job = Find({ list: list, key: 'id', val: id });
|
job = Find({ list: list, key: 'id', val: id });
|
||||||
if (job.type === 'job') {
|
if (job.type === 'job') {
|
||||||
$location.url('/jobs/' + job.id);
|
$location.url('/jobs/' + job.id);
|
||||||
@@ -432,6 +440,9 @@ function(Find, GetBasePath, Rest, Wait, ProcessErrors, Prompt, Alert){
|
|||||||
else if (scope.queued_jobs) {
|
else if (scope.queued_jobs) {
|
||||||
jobs = scope.queued_jobs;
|
jobs = scope.queued_jobs;
|
||||||
}
|
}
|
||||||
|
else if (scope.jobs) {
|
||||||
|
jobs = scope.jobs;
|
||||||
|
}
|
||||||
job = Find({list: jobs, key: 'id', val: id });
|
job = Find({list: jobs, key: 'id', val: id });
|
||||||
|
|
||||||
if (job.status === 'pending' || job.status === 'running' || job.status === 'waiting') {
|
if (job.status === 'pending' || job.status === 'running' || job.status === 'waiting') {
|
||||||
|
|||||||
@@ -105,20 +105,8 @@ angular.module('CompletedJobsDefinition', [])
|
|||||||
stdout: {
|
stdout: {
|
||||||
mode: 'all',
|
mode: 'all',
|
||||||
href: '/#/jobs/{{ completed_job.id }}/stdout',
|
href: '/#/jobs/{{ completed_job.id }}/stdout',
|
||||||
awToolTip: 'View standard output. Opens in a new window or tab.',
|
awToolTip: 'View standard output',
|
||||||
dataPlacement: 'top'
|
dataPlacement: 'top'
|
||||||
}
|
}
|
||||||
/*dropdown: {
|
|
||||||
type: 'DropDown',
|
|
||||||
ngShow: "completed_job.type === 'job'",
|
|
||||||
label: 'View',
|
|
||||||
icon: 'fa-search-plus',
|
|
||||||
'class': 'btn-default btn-xs',
|
|
||||||
options: [
|
|
||||||
//{ ngHref: '/#/jobs/{{ completed_job.id }}', label: 'Status' },
|
|
||||||
{ ngHref: '/#/job_events/{{ completed_job.id }}', label: 'Events', ngHide: "completed_job.status == 'new'" },
|
|
||||||
{ ngHref: '/#/job_host_summaries/{{ completed_job.id }}', label: 'Host Summary' }
|
|
||||||
]
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
* Copyright (c) 2014 AnsibleWorks, Inc.
|
* Copyright (c) 2014 AnsibleWorks, Inc.
|
||||||
*
|
*
|
||||||
* Jobs.js
|
* Jobs.js
|
||||||
* List view object for Team data model.
|
* List view object for job data model.
|
||||||
*
|
*
|
||||||
|
* Used on dashboard to provide a list of all jobs, regardless of
|
||||||
|
* status.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -15,112 +17,92 @@ angular.module('JobsListDefinition', [])
|
|||||||
name: 'jobs',
|
name: 'jobs',
|
||||||
iterator: 'job',
|
iterator: 'job',
|
||||||
editTitle: 'Jobs',
|
editTitle: 'Jobs',
|
||||||
showTitle: false,
|
'class': 'table-condensed',
|
||||||
index: false,
|
index: false,
|
||||||
hover: true,
|
hover: true,
|
||||||
well: false,
|
well: false,
|
||||||
"class": 'jobs-table',
|
|
||||||
|
|
||||||
fields: {
|
fields: {
|
||||||
id: {
|
id: {
|
||||||
label: 'ID',
|
label: 'ID',
|
||||||
|
ngClick:"viewJobLog(job.id)",
|
||||||
key: true,
|
key: true,
|
||||||
desc: true,
|
desc: true,
|
||||||
searchType: 'int'
|
|
||||||
},
|
|
||||||
inventory: {
|
|
||||||
label: 'Inventory ID',
|
|
||||||
searchType: 'int',
|
searchType: 'int',
|
||||||
searchOnly: true
|
columnClass: 'col-md-1 col-sm-2 col-xs-2',
|
||||||
},
|
awToolTip: "{{ job.status_tip }}",
|
||||||
created: {
|
awTipPlacement: "top",
|
||||||
label: 'Create On',
|
|
||||||
link: false,
|
|
||||||
searchable: false,
|
|
||||||
filter: "date:'MM/dd HH:mm:ss'"
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
label: 'Name',
|
|
||||||
link: false
|
|
||||||
},
|
|
||||||
failed: {
|
|
||||||
label: 'Job failed?',
|
|
||||||
searchSingleValue: true,
|
|
||||||
searchType: 'boolean',
|
|
||||||
searchValue: 'true',
|
|
||||||
searchOnly: true,
|
|
||||||
nosort: true
|
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
label: 'Status',
|
label: 'Status',
|
||||||
"class": 'job-{{ job.status }}',
|
columnClass: 'col-lg-1 col-md-2 col-sm-2 col-xs-2',
|
||||||
|
awToolTip: "{{ job.status_tip }}",
|
||||||
|
awTipPlacement: "top",
|
||||||
|
dataTitle: "{{ job.status_popover_title }}",
|
||||||
|
icon: 'icon-job-{{ job.status }}',
|
||||||
|
iconOnly: true,
|
||||||
|
ngClick:"viewJobLog(job.id)",
|
||||||
|
searchable: false
|
||||||
|
},
|
||||||
|
started: {
|
||||||
|
label: 'Started On',
|
||||||
|
noLink: true,
|
||||||
|
searchable: false,
|
||||||
|
filter: "date:'MM/dd HH:mm:ss'",
|
||||||
|
columnClass: "col-lg-1 col-md-2 hidden-xs"
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
label: 'Type',
|
||||||
|
ngBind: 'job.type_label',
|
||||||
|
link: false,
|
||||||
|
columnClass: "col-lg-1 col-md-2 hidden-sm hidden-xs",
|
||||||
|
searchable: true,
|
||||||
searchType: 'select',
|
searchType: 'select',
|
||||||
linkTo: "{{ job.statusLinkTo }}",
|
searchOptions: [] // populated via GetChoices() in controller
|
||||||
searchOptions: [
|
},
|
||||||
{ name: "new", value: "new" },
|
name: {
|
||||||
{ name: "waiting", value: "waiting" },
|
label: 'Name',
|
||||||
{ name: "pending", value: "pending" },
|
columnClass: 'col-md-3 col-xs-5',
|
||||||
{ name: "running", value: "running" },
|
ngClick: "viewJobLog(job.id, job.nameHref)",
|
||||||
{ name: "successful", value: "successful" },
|
defaultSearchField: true
|
||||||
{ name: "error", value: "error" },
|
|
||||||
{ name: "failed", value: "failed" },
|
|
||||||
{ name: "canceled", value: "canceled" }
|
|
||||||
],
|
|
||||||
badgeIcon: 'fa icon-job-{{ job.status }}',
|
|
||||||
badgePlacement: 'left',
|
|
||||||
badgeToolTip: "{{ job.statusBadgeToolTip }}",
|
|
||||||
badgeTipPlacement: 'top',
|
|
||||||
badgeNgHref: "{{ job.statusLinkTo }}",
|
|
||||||
awToolTip: "{{ job.statusBadgeToolTip }}",
|
|
||||||
dataPlacement: 'top'
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: { },
|
||||||
refresh: {
|
|
||||||
mode: 'all',
|
|
||||||
awToolTip: "Refresh the page",
|
|
||||||
ngClick: "refresh()"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
fieldActions: {
|
fieldActions: {
|
||||||
submit: {
|
submit: {
|
||||||
label: 'Relaunch',
|
|
||||||
icon: 'icon-rocket',
|
|
||||||
mode: 'all',
|
mode: 'all',
|
||||||
ngClick: 'submitJob(job.id, job.summary_fields.job_template.name)',
|
icon: 'icon-rocket',
|
||||||
awToolTip: 'Start the job',
|
ngClick: 'relaunchJob($event, job.id)',
|
||||||
|
awToolTip: 'Relaunch using the same parameters',
|
||||||
dataPlacement: 'top'
|
dataPlacement: 'top'
|
||||||
},
|
},
|
||||||
cancel: {
|
cancel: {
|
||||||
label: 'Stop',
|
|
||||||
mode: 'all',
|
mode: 'all',
|
||||||
ngClick: 'deleteJob(job.id)',
|
ngClick: 'deleteJob(job.id)',
|
||||||
awToolTip: 'Cancel a running or pending job',
|
awToolTip: 'Cancel the job',
|
||||||
ngShow: "job.status == 'pending' || job.status == 'running' || job.status == 'waiting'",
|
dataPlacement: 'top',
|
||||||
dataPlacement: 'top'
|
ngShow: "job.status == 'running'"
|
||||||
},
|
},
|
||||||
"delete": {
|
"delete": {
|
||||||
label: 'Delete',
|
|
||||||
mode: 'all',
|
mode: 'all',
|
||||||
ngClick: 'deleteJob(job.id)',
|
ngClick: 'deleteJob(job.id)',
|
||||||
awToolTip: 'Delete the job',
|
awToolTip: 'Delete the job',
|
||||||
ngShow: "job.status != 'pending' && job.status != 'running' && job.status != 'waiting'",
|
dataPlacement: 'top',
|
||||||
|
ngShow: "job.status != 'running'"
|
||||||
|
},
|
||||||
|
job_details: {
|
||||||
|
mode: 'all',
|
||||||
|
href: '/#/jobs/{{ job.id }}',
|
||||||
|
awToolTip: 'View job details',
|
||||||
dataPlacement: 'top'
|
dataPlacement: 'top'
|
||||||
},
|
},
|
||||||
dropdown: {
|
stdout: {
|
||||||
type: 'DropDown',
|
mode: 'all',
|
||||||
label: 'View',
|
href: '/#/jobs/{{ job.id }}/stdout',
|
||||||
icon: 'fa-search-plus',
|
awToolTip: 'View standard output',
|
||||||
'class': 'btn-default btn-xs',
|
dataPlacement: 'top'
|
||||||
options: [
|
|
||||||
{ ngClick: 'editJob(job.id, job.summary_fields.job_template.name)', label: 'Status' },
|
|
||||||
{ ngClick: 'viewEvents(job.id, job.summary_fields.job_template.name)', label: 'Events',
|
|
||||||
ngHide: "job.status == 'new'" },
|
|
||||||
{ ngClick: 'viewSummary(job.id, job.summary_fields.job_template.name)', label: 'Host Summary',
|
|
||||||
ngHide: "job.status == 'new'" }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ angular.module('RunningJobsDefinition', [])
|
|||||||
stdout: {
|
stdout: {
|
||||||
mode: 'all',
|
mode: 'all',
|
||||||
href: '/#/jobs/{{ running_job.id }}/stdout',
|
href: '/#/jobs/{{ running_job.id }}/stdout',
|
||||||
awToolTip: 'View standard output. Opens in a new window or tab.',
|
awToolTip: 'View standard output',
|
||||||
dataPlacement: 'top'
|
dataPlacement: 'top'
|
||||||
}
|
}
|
||||||
/*dropdown: {
|
/*dropdown: {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ angular.module('ScheduledJobsDefinition', [])
|
|||||||
fields: {
|
fields: {
|
||||||
status: {
|
status: {
|
||||||
label: 'Status',
|
label: 'Status',
|
||||||
columnClass: 'col-md-2 col-sm-2 col-xs-2',
|
columnClass: 'col-lg-1 col-md-2 col-sm-2 col-xs-2',
|
||||||
awToolTip: "{{ schedule.status_tip }}",
|
awToolTip: "{{ schedule.status_tip }}",
|
||||||
awTipPlacement: "top",
|
awTipPlacement: "top",
|
||||||
icon: 'icon-job-{{ schedule.status }}',
|
icon: 'icon-job-{{ schedule.status }}',
|
||||||
@@ -35,14 +35,14 @@ angular.module('ScheduledJobsDefinition', [])
|
|||||||
label: 'Next Run',
|
label: 'Next Run',
|
||||||
noLink: true,
|
noLink: true,
|
||||||
searchable: false,
|
searchable: false,
|
||||||
columnClass: "col-md-2 hidden-xs",
|
columnClass: "col-lg-1 col-md-2 hidden-xs",
|
||||||
filter: "date:'MM/dd HH:mm:ss'",
|
filter: "date:'MM/dd HH:mm:ss'",
|
||||||
key: true
|
key: true
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
label: 'Type',
|
label: 'Type',
|
||||||
noLink: true,
|
noLink: true,
|
||||||
columnClass: "col-md-2 hidden-sm hidden-xs",
|
columnClass: "col-lg-1 col-md-2 hidden-sm hidden-xs",
|
||||||
sourceModel: 'unified_job_template',
|
sourceModel: 'unified_job_template',
|
||||||
sourceField: 'unified_job_type',
|
sourceField: 'unified_job_type',
|
||||||
ngBind: 'schedule.type_label',
|
ngBind: 'schedule.type_label',
|
||||||
|
|||||||
@@ -10,13 +10,19 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('DashboardJobsWidget', ['RestServices', 'Utilities'])
|
angular.module('DashboardJobsWidget', ['RestServices', 'Utilities'])
|
||||||
.factory('DashboardJobs', ['$rootScope', '$compile', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', function ($rootScope, $compile) {
|
.factory('DashboardJobs', ['$rootScope', '$compile', 'LoadSchedulesScope', 'LoadJobsScope', 'JobsList', 'ScheduledJobsList', 'GetChoices', 'GetBasePath',
|
||||||
|
function ($rootScope, $compile, LoadSchedulesScope, LoadJobsScope, JobsList, ScheduledJobsList, GetChoices, GetBasePath) {
|
||||||
return function (params) {
|
return function (params) {
|
||||||
|
|
||||||
var scope = params.scope,
|
var scope = params.scope,
|
||||||
target = params.target,
|
target = params.target,
|
||||||
|
choicesCount = 0,
|
||||||
|
listCount = 0,
|
||||||
|
jobs_scope = scope.$new(true),
|
||||||
|
scheduled_scope = scope.$new(true),
|
||||||
|
max_rows = 15,
|
||||||
html, e;
|
html, e;
|
||||||
|
|
||||||
|
html = '';
|
||||||
html += "<ul id=\"job_status_tabs\" class=\"nav nav-tabs\">\n";
|
html += "<ul id=\"job_status_tabs\" class=\"nav nav-tabs\">\n";
|
||||||
html += "<li class=\"active\"><a id=\"active_jobs_link\" ng-click=\"toggleTab($event, 'active_jobs_link', 'job_status_tabs')\"\n";
|
html += "<li class=\"active\"><a id=\"active_jobs_link\" ng-click=\"toggleTab($event, 'active_jobs_link', 'job_status_tabs')\"\n";
|
||||||
html += " href=\"#active-jobs-tab\" data-toggle=\"tab\">Jobs</a></li>\n";
|
html += " href=\"#active-jobs-tab\" data-toggle=\"tab\">Jobs</a></li>\n";
|
||||||
@@ -31,7 +37,68 @@ angular.module('DashboardJobsWidget', ['RestServices', 'Utilities'])
|
|||||||
e = angular.element(document.getElementById(target));
|
e = angular.element(document.getElementById(target));
|
||||||
e.html(html);
|
e.html(html);
|
||||||
$compile(e)(scope);
|
$compile(e)(scope);
|
||||||
scope.$emit('WidgetLoaded');
|
|
||||||
|
|
||||||
|
if (scope.removeListLoaded) {
|
||||||
|
scope.removeListLoaded();
|
||||||
|
}
|
||||||
|
scope.removeListLoaded = scope.$on('listLoaded', function() {
|
||||||
|
listCount++;
|
||||||
|
if (listCount === 1) {
|
||||||
|
//api_complete = true;
|
||||||
|
scope.$emit('WidgetLoaded');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// After all choices are ready, load up the lists and populate the page
|
||||||
|
if (scope.removeBuildJobsList) {
|
||||||
|
scope.removeBuildJobsList();
|
||||||
|
}
|
||||||
|
scope.removeBuildJobsList = scope.$on('buildJobsList', function() {
|
||||||
|
if (JobsList.fields.type) {
|
||||||
|
JobsList.fields.type.searchOptions = scope.type_choices;
|
||||||
|
}
|
||||||
|
LoadJobsScope({
|
||||||
|
parent_scope: scope,
|
||||||
|
scope: jobs_scope,
|
||||||
|
list: JobsList,
|
||||||
|
id: 'active-jobs-tab',
|
||||||
|
url: GetBasePath('unified_jobs') + '?status__in=running,completed,failed,successful,error,canceled',
|
||||||
|
pageSize: max_rows
|
||||||
|
});
|
||||||
|
LoadSchedulesScope({
|
||||||
|
parent_scope: scope,
|
||||||
|
scope: scheduled_scope,
|
||||||
|
list: ScheduledJobsList,
|
||||||
|
id: 'scheduled-jobs-tab',
|
||||||
|
url: GetBasePath('schedules') + '?next_run__isnull=false',
|
||||||
|
pageSize: max_rows
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (scope.removeChoicesReady) {
|
||||||
|
scope.removeChoicesReady();
|
||||||
|
}
|
||||||
|
scope.removeChoicesReady = scope.$on('choicesReady', function() {
|
||||||
|
choicesCount++;
|
||||||
|
if (choicesCount === 2) {
|
||||||
|
scope.$emit('buildJobsList');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
GetChoices({
|
||||||
|
scope: scope,
|
||||||
|
url: GetBasePath('unified_jobs'),
|
||||||
|
field: 'status',
|
||||||
|
variable: 'status_choices',
|
||||||
|
callback: 'choicesReady'
|
||||||
|
});
|
||||||
|
|
||||||
|
GetChoices({
|
||||||
|
scope: scope,
|
||||||
|
url: GetBasePath('unified_jobs'),
|
||||||
|
field: 'type',
|
||||||
|
variable: 'type_choices',
|
||||||
|
callback: 'choicesReady'
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}]);
|
}]);
|
||||||
@@ -1,21 +1,22 @@
|
|||||||
|
|
||||||
<div class="tab-pane" id="home">
|
<div class="tab-pane" id="home">
|
||||||
<div ng-cloak id="htmlTemplate">
|
<div ng-cloak id="htmlTemplate">
|
||||||
<div id="refresh-row" class="row">
|
<div id="refresh-row" class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
<div id="home-list-actions" class="list-actions pull-right"></div>
|
<div id="home-list-actions" class="list-actions pull-right"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div id="container1" class="col-lg-12"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div id="container2" class="col-lg-12"></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div id="container3" class="col-lg-12"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div id="container1" class="col-lg-12"></div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div id="container2" class="col-lg-12"></div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div id="container3" class="col-lg-12"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div ng-include="'/static/partials/schedule_dialog.html'"></div>
|
||||||
Reference in New Issue
Block a user