Files
awx/awx/ui/static/js/controllers/Jobs.js
Chris Houseknecht a75c455633 Activity Streams
Activity stream button was not working. Broke when tabs were removed from index.html. Fixed a bug in Users stream search on related username.
2014-07-17 17:30:50 -04:00

566 lines
21 KiB
JavaScript

/************************************
* Copyright (c) 2014 AnsibleWorks, Inc.
*
*
* Jobs.js
*
* Controller functions for the Inventory model.
*
*/
'use strict';
function JobsListController ($log, $scope, $compile, $routeParams, ClearScope, Breadcrumbs, LoadBreadCrumbs, LoadSchedulesScope, LoadJobsScope, RunningJobsList, CompletedJobsList, QueuedJobsList,
ScheduledJobsList, GetChoices, GetBasePath, Wait, Socket) {
ClearScope();
var completed_scope, running_scope, queued_scope, scheduled_scope,
choicesCount = 0,
listCount = 0,
api_complete = false,
schedule_socket,
job_socket,
event_queue = [],
expecting = 0,
max_rows;
job_socket = Socket({
scope: $scope,
endpoint: "jobs"
});
job_socket.init();
job_socket.on("status_changed", function(data) {
if (api_complete) {
processEvent(data);
}
else {
event_queue.push(data);
}
});
schedule_socket = Socket({
scope: $scope,
endpoint: "schedules"
});
schedule_socket.init();
schedule_socket.on("status_change", function() {
if (api_complete) {
scheduled_scope.search('schedule');
}
});
function processEvent(event) {
expecting = 0;
switch(event.status) {
case 'running':
if (!inList(running_scope[RunningJobsList.name], event.unified_job_id)) {
expecting = 2;
running_scope.search('running_job');
queued_scope.search('queued_job');
}
break;
case 'new':
case 'pending':
case 'waiting':
if (!inList(queued_scope[QueuedJobsList.name], event.unified_job_id)) {
expecting = 1;
queued_scope.search('queued_job');
}
break;
case 'successful':
case 'failed':
case 'error':
case 'canceled':
if (!inList(completed_scope[CompletedJobsList.name], event.unified_job_id)) {
expecting = 2;
completed_scope.search('completed_job');
running_scope.search('running_job');
}
break;
}
}
function inList(list, id) {
var found = false;
list.every( function(row) {
if (row.id === id) {
found = true;
return false;
}
return true;
});
return found;
}
if ($scope.removeProcessQueue) {
$scope.removeProcessQueue();
}
$scope.removeProcessQueue = $scope.$on('ProcessQueue', function() {
var event;
listCount=0;
if (event_queue.length > 0) {
event = event_queue[0];
processEvent(event);
event_queue.splice(0,1);
if ($scope.removeListLoaded) {
$scope.removeListLoaded();
}
$scope.removeListLoaded = $scope.$on('listLoaded', function() {
listCount++;
if (listCount === expecting) {
$scope.$emit('ProcessQueue');
}
});
}
});
LoadBreadCrumbs();
if ($scope.removeListLoaded) {
$scope.removeListLoaded();
}
$scope.removeListLoaded = $scope.$on('listLoaded', function() {
listCount++;
if (listCount === 4) {
api_complete = true;
$scope.$emit('ProcessQueue');
}
});
// After all choices are ready, load up the lists and populate the page
if ($scope.removeBuildJobsList) {
$scope.removeBuildJobsList();
}
$scope.removeBuildJobsList = $scope.$on('buildJobsList', function() {
var opt, search_params;
if (CompletedJobsList.fields.type) {
CompletedJobsList.fields.type.searchOptions = $scope.type_choices;
}
if (RunningJobsList.fields.type) {
RunningJobsList.fields.type.searchOptions = $scope.type_choices;
}
if (QueuedJobsList.fields.type) {
QueuedJobsList.fields.type.searchOptions = $scope.type_choices;
}
if ($routeParams.status) {
search_params[CompletedJobsList.iterator + 'SearchField'] = 'status';
search_params[CompletedJobsList.iterator + 'SelectShow'] = true;
search_params[CompletedJobsList.iterator + 'SearchSelectOpts'] = CompletedJobsList.fields.status.searchOptions;
search_params[CompletedJobsList.iterator + 'SearchFieldLabel'] = CompletedJobsList.fields.status.label.replace(/<br\>/g,' ');
search_params[CompletedJobsList.iterator + 'SearchType'] = '';
for (opt in CompletedJobsList.fields.status.searchOptions) {
if (CompletedJobsList.fields.status.searchOptions[opt].value === $routeParams.status) {
search_params[CompletedJobsList.iterator + 'SearchSelectValue'] = CompletedJobsList.fields.status.searchOptions[opt];
break;
}
}
}
completed_scope = $scope.$new(true);
completed_scope.showJobType = true;
LoadJobsScope({
parent_scope: $scope,
scope: completed_scope,
list: CompletedJobsList,
id: 'completed-jobs',
url: GetBasePath('unified_jobs') + '?or__status=successful&or__status=failed&or__status=error&or__status=canceled',
searchParams: search_params,
pageSize: max_rows
});
running_scope = $scope.$new(true);
LoadJobsScope({
parent_scope: $scope,
scope: running_scope,
list: RunningJobsList,
id: 'active-jobs',
url: GetBasePath('unified_jobs') + '?status=running',
pageSize: max_rows
});
queued_scope = $scope.$new(true);
LoadJobsScope({
parent_scope: $scope,
scope: queued_scope,
list: QueuedJobsList,
id: 'queued-jobs',
url: GetBasePath('unified_jobs') + '?or__status=pending&or__status=waiting&or__status=new',
pageSize: max_rows
});
scheduled_scope = $scope.$new(true);
LoadSchedulesScope({
parent_scope: $scope,
scope: scheduled_scope,
list: ScheduledJobsList,
id: 'scheduled-jobs',
url: GetBasePath('schedules') + '?next_run__isnull=false',
pageSize: max_rows
});
$scope.refreshJobs = function() {
queued_scope.search('queued_job');
running_scope.search('running_job');
completed_scope.search('completed_job');
scheduled_scope.search('schedule');
};
$(window).resize(_.debounce(function() {
resizeContainers();
}, 500));
});
if ($scope.removeChoicesReady) {
$scope.removeChoicesReady();
}
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
choicesCount++;
if (choicesCount === 2) {
setHeight();
$scope.$emit('buildJobsList');
}
});
Wait('start');
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'
});
// Set the height of each container and calc max number of rows containers can hold
function setHeight() {
var docw = $(window).width(),
//doch = $(window).height(),
available_height,
search_row, page_row, height, header, row_height;
$log.debug('docw: ' + docw);
if (docw > 1200) {
// customize the container height and # of rows based on available viewport height
available_height = $(window).height() - $('#main-menu-container .navbar').outerHeight() - 80;
$log.debug('available_height: ' + available_height);
$('.jobs-list-container').each(function() {
$(this).height(Math.floor(available_height / 2));
});
search_row = Math.max($('.search-row:eq(0)').outerHeight(), 50);
page_row = Math.max($('.page-row:eq(0)').outerHeight(), 33);
header = Math.max($('#completed_jobs_table thead').height(), 24);
height = Math.floor(available_height / 2) - header - page_row - search_row - 30;
row_height = (docw < 1350) ? 47 : 27;
max_rows = Math.floor(height / row_height);
}
else {
// when width < 1240px || height < 800px put things back to their default state
$('.jobs-list-container').each(function() {
$(this).css({ 'height': 'auto' });
});
max_rows = 5;
}
$log.debug('max_rows: ' + max_rows);
}
// Set container height and return the number of allowed rows
function resizeContainers() {
setHeight();
completed_scope[CompletedJobsList.iterator + '_page_size'] = max_rows;
completed_scope.changePageSize(CompletedJobsList.name, CompletedJobsList.iterator);
running_scope[RunningJobsList.iterator + '_page_size'] = max_rows;
running_scope.changePageSize(RunningJobsList.name, RunningJobsList.iterator);
queued_scope[QueuedJobsList.iterator + '_page_size'] = max_rows;
queued_scope.changePageSize(QueuedJobsList.name, QueuedJobsList.iterator);
scheduled_scope[ScheduledJobsList.iterator + '_page_size'] = max_rows;
scheduled_scope.changePageSize(ScheduledJobsList.name, ScheduledJobsList.iterator);
}
}
JobsListController.$inject = [ '$log', '$scope', '$compile', '$routeParams', 'ClearScope', 'Breadcrumbs', 'LoadBreadCrumbs', 'LoadSchedulesScope', 'LoadJobsScope', 'RunningJobsList', 'CompletedJobsList',
'QueuedJobsList', 'ScheduledJobsList', 'GetChoices', 'GetBasePath', 'Wait', 'Socket'];
function JobsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobForm, JobTemplateForm, GenerateForm, Rest,
Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList,
CredentialList, ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, FormatDate, JobStatusToolTip, Wait, Empty,
ParseVariableString, GetChoices) {
ClearScope();
var defaultUrl = GetBasePath('jobs'),
generator = GenerateForm,
id = $routeParams.id,
loadingFinishedCount = 0,
templateForm = {},
choicesCount = 0;
generator.inject(JobForm, { mode: 'edit', related: true, scope: $scope });
$scope.job_id = id;
$scope.parseType = 'yaml';
$scope.statusSearchSpin = false;
$scope.disableParseSelection = true;
function getPlaybooks(project, playbook) {
if (!Empty(project)) {
var url = GetBasePath('projects') + project + '/playbooks/';
Rest.setUrl(url);
Rest.get()
.success(function (data) {
var i;
$scope.playbook_options = [];
for (i = 0; i < data.length; i++) {
$scope.playbook_options.push(data[i]);
}
for (i = 0; i < $scope.playbook_options.length; i++) {
if ($scope.playbook_options[i] === playbook) {
$scope.playbook = $scope.playbook_options[i];
}
}
$scope.$emit('jobTemplateLoadFinished');
})
.error(function () {
$scope.$emit('jobTemplateLoadFinished');
});
} else {
$scope.$emit('jobTemplateLoadFinished');
}
}
// Retrieve each related set and populate the playbook list
if ($scope.jobLoadedRemove) {
$scope.jobLoadedRemove();
}
$scope.jobLoadedRemove = $scope.$on('jobLoaded', function (e, related_cloud_credential, project, playbook) {
getPlaybooks(project, playbook);
if (related_cloud_credential) {
//Get the name of the cloud credential
Rest.setUrl(related_cloud_credential);
Rest.get()
.success(function (data) {
$scope.cloud_credential_name = data.name;
$scope.$emit('jobTemplateLoadFinished');
})
.error(function (data, status) {
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to related cloud credential. GET returned status: ' + status });
});
} else {
$scope.$emit('jobTemplateLoadFinished');
}
});
// Turn off 'Wait' after both cloud credential and playbook list come back
if ($scope.removeJobTemplateLoadFinished) {
$scope.removeJobTemplateLoadFinished();
}
$scope.removeJobTemplateLoadFinished = $scope.$on('jobTemplateLoadFinished', function () {
loadingFinishedCount++;
if (loadingFinishedCount >= 2) {
// The initial template load finished. Now load related jobs, which
// will turn off the 'working' spinner.
Wait('stop');
}
});
$scope.verbosity_options = [{
value: 0,
label: 'Default'
}, {
value: 1,
label: 'Verbose'
}, {
value: 3,
label: 'Debug'
}];
$scope.playbook_options = null;
$scope.playbook = null;
function calcRows(content) {
var n = content.match(/\n/g),
rows = (n) ? n.length : 1;
return (rows > 15) ? 15 : rows;
}
if ($scope.removeLoadJobTemplate) {
$scope.removeLoadJobTemplate();
}
$scope.removeLoadJobTemplate = $scope.$on('loadJobTemplate', function() {
// Retrieve the job detail record and prepopulate the form
Rest.setUrl(defaultUrl + ':id/');
Rest.get({ params: { id: id } })
.success(function (data) {
var i, fld;
LoadBreadCrumbs();
$scope.status = data.status;
$scope.created = FormatDate(data.created);
$scope.modified = FormatDate(data.modified);
$scope.result_stdout = data.result_stdout;
$scope.result_traceback = data.result_traceback;
$scope.stdout_rows = calcRows($scope.result_stdout);
$scope.traceback_rows = calcRows($scope.result_traceback);
$scope.job_explanation = data.job_explanation || 'Things may have ended badly or gone swimingly well';
// Now load the job template form
templateForm.addTitle = 'Create Job Templates';
templateForm.editTitle = '{{ name }}';
templateForm.name = 'job_templates';
templateForm.twoColumns = true;
templateForm.fields = angular.copy(JobTemplateForm.fields);
for (fld in templateForm.fields) {
templateForm.fields[fld].readonly = true;
}
if (data.type === "playbook_run") {
$('#ui-accordion-jobs-collapse-0-panel-1').empty();
generator.inject(templateForm, {
mode: 'edit',
id: 'ui-accordion-jobs-collapse-0-panel-1',
related: false,
scope: $scope,
breadCrumbs: false
});
}
else {
$('#ui-accordion-jobs-collapse-0-header-1').hide();
$('#ui-accordion-jobs-collapse-0-panel-1').empty().hide();
$('#jobs-collapse-0').accordion( "option", "collapsible", false );
}
for (fld in templateForm.fields) {
if (fld !== 'variables' && data[fld] !== null && data[fld] !== undefined) {
if (JobTemplateForm.fields[fld].type === 'select') {
if ($scope[fld + '_options'] && $scope[fld + '_options'].length > 0) {
for (i = 0; i < $scope[fld + '_options'].length; i++) {
if (data[fld] === $scope[fld + '_options'][i].value) {
$scope[fld] = $scope[fld + '_options'][i];
}
}
} else {
$scope[fld] = data[fld];
}
} else {
$scope[fld] = data[fld];
}
}
if (fld === 'variables') {
$scope.variables = ParseVariableString(data.extra_vars);
}
if (JobTemplateForm.fields[fld].type === 'lookup' && data.summary_fields[JobTemplateForm.fields[fld].sourceModel]) {
$scope[JobTemplateForm.fields[fld].sourceModel + '_' + JobTemplateForm.fields[fld].sourceField] =
data.summary_fields[JobTemplateForm.fields[fld].sourceModel][JobTemplateForm.fields[fld].sourceField];
}
}
$scope.id = data.id;
$scope.name = (data.summary_fields && data.summary_fields.job_template) ? data.summary_fields.job_template.name : '';
$scope.statusToolTip = JobStatusToolTip(data.status);
$scope.url = data.url;
$scope.project = data.project;
$scope.launch_type = data.launch_type;
// set the type
data.type = 'playbook_run'; //temporary
$scope.type_choices.every( function(choice) {
if (choice.value === data.type) {
$scope.type = choice.label;
return false;
}
return true;
});
$scope.$emit('jobLoaded', data.related.cloud_credential, data.project, data.playbook);
})
.error(function (data, status) {
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to retrieve job: ' + $routeParams.id + '. GET status: ' + status });
});
});
Wait('start');
if ($scope.removeChoicesReady) {
$scope.removeChoicesReady();
}
$scope.removeChoicesReady = $scope.$on('choicesReady', function() {
choicesCount++;
if (choicesCount === 2) {
$scope.$emit('loadJobTemplate');
}
});
GetChoices({
scope: $scope,
url: GetBasePath('unified_jobs'),
field: 'job_type',
variable: 'job_type_options',
callback: 'choicesReady'
});
/*GetChoices({
scope: $scope,
url: GetBasePath('jobs'),
field: 'status',
variable: 'status_choices',
callback: 'choicesReady'
});*/
GetChoices({
scope: $scope,
url: GetBasePath('unified_jobs'), //'/static/sample/data/types/data.json'
field: 'type',
variable: 'type_choices',
callback: 'choicesReady'
});
$scope.refresh = function () {
Wait('start');
Rest.setUrl(defaultUrl + id + '/');
Rest.get()
.success(function (data) {
$scope.status = data.status;
$scope.result_stdout = data.result_stdout;
$scope.result_traceback = data.result_traceback;
$scope.stdout_rows = calcRows($scope.result_stdout);
$scope.traceback_rows = calcRows($scope.result_traceback);
Wait('stop');
})
.error(function (data, status) {
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
msg: 'Attempt to load job failed. GET returned status: ' + status });
});
};
$scope.jobSummary = function () {
$location.path('/jobs/' + id + '/job_host_summaries');
};
$scope.jobEvents = function () {
$location.path('/jobs/' + id + '/job_events');
};
}
JobsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobForm', 'JobTemplateForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit',
'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit', 'PromptPasswords',
'GetBasePath', 'md5Setup', 'FormatDate', 'JobStatusToolTip', 'Wait', 'Empty', 'ParseVariableString', 'GetChoices',
'LoadDialogPartial'
];