1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 16:51:11 +03:00

Dashboard

Built the job status dashboard widget.
This commit is contained in:
Chris Houseknecht 2014-06-27 15:41:50 -04:00
parent d46aa1c4de
commit 8477cb150e
9 changed files with 166 additions and 124 deletions

View File

@ -20,7 +20,7 @@ function JobsListController ($scope, $compile, $routeParams, ClearScope, Breadcr
listCount = 0,
api_complete = false,
event_socket,
event_queue = [{"status":"pending","endpoint":"/socket.io/jobs","unified_job_id":4129,"event":"status_changed"}],
event_queue = [],
expecting = 0,
max_rows;

View File

@ -372,7 +372,6 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
return function (params) {
var scope = params.scope,
id = params.id,
base = $location.path().replace(/^\//, '').split('/')[0],
url,
job_template,
new_job_id,
@ -381,13 +380,7 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi
prompt_for_vars = false,
passwords;
if (!Empty($routeParams.template_id)) {
// launching a job from job_template detail page
url = GetBasePath('jobs') + id + '/';
}
else {
url = GetBasePath(base) + id + '/';
}
url = GetBasePath('jobs') + id + '/';
if (scope.removePostTheJob) {
scope.removePostTheJob();

View File

@ -46,7 +46,12 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job
else if (scope.queued_jobs) {
list = scope.queued_jobs;
}
else if (scope.jobs) {
list = scope.jobs;
}
job = Find({ list: list, key: 'id', val: id });
console.log('found job:');
console.log(job);
if (job.type === 'inventory_update') {
typeId = job.inventory_source;
}
@ -81,6 +86,9 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job
else if (scope.queued_jobs) {
list = scope.queued_jobs;
}
else if (scope.jobs) {
list = scope.jobs;
}
job = Find({ list: list, key: 'id', val: id });
if (job.type === 'job') {
$location.url('/jobs/' + job.id);
@ -432,6 +440,9 @@ function(Find, GetBasePath, Rest, Wait, ProcessErrors, Prompt, Alert){
else if (scope.queued_jobs) {
jobs = scope.queued_jobs;
}
else if (scope.jobs) {
jobs = scope.jobs;
}
job = Find({list: jobs, key: 'id', val: id });
if (job.status === 'pending' || job.status === 'running' || job.status === 'waiting') {

View File

@ -105,20 +105,8 @@ angular.module('CompletedJobsDefinition', [])
stdout: {
mode: 'all',
href: '/#/jobs/{{ completed_job.id }}/stdout',
awToolTip: 'View standard output. Opens in a new window or tab.',
awToolTip: 'View standard output',
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' }
]
}*/
}
});

View File

@ -2,8 +2,10 @@
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* 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',
iterator: 'job',
editTitle: 'Jobs',
showTitle: false,
'class': 'table-condensed',
index: false,
hover: true,
well: false,
"class": 'jobs-table',
fields: {
id: {
label: 'ID',
ngClick:"viewJobLog(job.id)",
key: true,
desc: true,
searchType: 'int'
},
inventory: {
label: 'Inventory ID',
searchType: 'int',
searchOnly: true
},
created: {
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
columnClass: 'col-md-1 col-sm-2 col-xs-2',
awToolTip: "{{ job.status_tip }}",
awTipPlacement: "top",
},
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',
linkTo: "{{ job.statusLinkTo }}",
searchOptions: [
{ name: "new", value: "new" },
{ name: "waiting", value: "waiting" },
{ name: "pending", value: "pending" },
{ name: "running", value: "running" },
{ name: "successful", value: "successful" },
{ 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'
searchOptions: [] // populated via GetChoices() in controller
},
name: {
label: 'Name',
columnClass: 'col-md-3 col-xs-5',
ngClick: "viewJobLog(job.id, job.nameHref)",
defaultSearchField: true
}
},
actions: {
refresh: {
mode: 'all',
awToolTip: "Refresh the page",
ngClick: "refresh()"
}
},
actions: { },
fieldActions: {
submit: {
label: 'Relaunch',
icon: 'icon-rocket',
mode: 'all',
ngClick: 'submitJob(job.id, job.summary_fields.job_template.name)',
awToolTip: 'Start the job',
icon: 'icon-rocket',
ngClick: 'relaunchJob($event, job.id)',
awToolTip: 'Relaunch using the same parameters',
dataPlacement: 'top'
},
cancel: {
label: 'Stop',
mode: 'all',
ngClick: 'deleteJob(job.id)',
awToolTip: 'Cancel a running or pending job',
ngShow: "job.status == 'pending' || job.status == 'running' || job.status == 'waiting'",
dataPlacement: 'top'
awToolTip: 'Cancel the job',
dataPlacement: 'top',
ngShow: "job.status == 'running'"
},
"delete": {
label: 'Delete',
mode: 'all',
ngClick: 'deleteJob(job.id)',
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'
},
dropdown: {
type: 'DropDown',
label: 'View',
icon: 'fa-search-plus',
'class': 'btn-default btn-xs',
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'" }
]
stdout: {
mode: 'all',
href: '/#/jobs/{{ job.id }}/stdout',
awToolTip: 'View standard output',
dataPlacement: 'top'
}
}
});

View File

@ -90,7 +90,7 @@ angular.module('RunningJobsDefinition', [])
stdout: {
mode: 'all',
href: '/#/jobs/{{ running_job.id }}/stdout',
awToolTip: 'View standard output. Opens in a new window or tab.',
awToolTip: 'View standard output',
dataPlacement: 'top'
}
/*dropdown: {

View File

@ -1,16 +1,16 @@
/*********************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
* ScheduledJobs.js
* ScheduledJobs.js
*
*
*
*/
'use strict';
angular.module('ScheduledJobsDefinition', [])
.value( 'ScheduledJobsList', {
name: 'schedules',
iterator: 'schedule',
editTitle: 'Scheduled Jobs',
@ -18,11 +18,11 @@ angular.module('ScheduledJobsDefinition', [])
index: true,
hover: true,
well: false,
fields: {
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 }}",
awTipPlacement: "top",
icon: 'icon-job-{{ schedule.status }}',
@ -35,14 +35,14 @@ angular.module('ScheduledJobsDefinition', [])
label: 'Next Run',
noLink: true,
searchable: false,
columnClass: "col-md-2 hidden-xs",
columnClass: "col-lg-1 col-md-2 hidden-xs",
filter: "date:'MM/dd HH:mm:ss'",
key: true
},
type: {
label: 'Type',
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',
sourceField: 'unified_job_type',
ngBind: 'schedule.type_label',
@ -62,7 +62,7 @@ angular.module('ScheduledJobsDefinition', [])
},
actions: { },
fieldActions: {
"play": {
mode: "all",

View File

@ -10,13 +10,19 @@
'use strict';
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) {
var scope = params.scope,
target = params.target,
choicesCount = 0,
listCount = 0,
jobs_scope = scope.$new(true),
scheduled_scope = scope.$new(true),
max_rows = 15,
html, e;
html = '';
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 += " 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.html(html);
$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'
});
};
}]);

View File

@ -1,21 +1,22 @@
<div class="tab-pane" id="home">
<div ng-cloak id="htmlTemplate">
<div id="refresh-row" class="row">
<div class="col-lg-12">
<div id="home-list-actions" class="list-actions pull-right"></div>
<div id="refresh-row" class="row">
<div class="col-lg-12">
<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 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 ng-include="'/static/partials/schedule_dialog.html'"></div>