AC-1014 fixed issued related to deleted templates and templates containing quotes in the name. De-linted job templates and the jobs controller.

This commit is contained in:
Chris Houseknecht
2014-02-05 16:34:07 -05:00
parent 229df6d4f7
commit 5e7290aa85
6 changed files with 484 additions and 496 deletions

View File

@@ -8,79 +8,81 @@
* *
*/ */
/* global jsyaml:false */
'use strict'; 'use strict';
function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobList, function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobList, GenerateList, LoadBreadCrumbs, Prompt,
GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, LookUpInit, SubmitJob, FormatDate, Refresh,
ClearScope, ProcessErrors, GetBasePath, LookUpInit, SubmitJob, FormatDate, Refresh, JobStatusToolTip, Empty, Wait) {
JobStatusToolTip, Empty, Wait)
{
ClearScope('htmlTemplate'); ClearScope('htmlTemplate');
var list = JobList;
var defaultUrl = GetBasePath('jobs'); var list = JobList,
var view = GenerateList; defaultUrl = GetBasePath('jobs'),
var base = $location.path().replace(/^\//,'').split('/')[0]; view = GenerateList,
var scope = view.inject(list, { mode: 'edit' }); scope = view.inject(list, { mode: 'edit' }),
opt;
$rootScope.flashMessage = null; $rootScope.flashMessage = null;
scope.selected = []; scope.selected = [];
if (scope.removePostRefresh) { if (scope.removePostRefresh) {
scope.removePostRefresh(); scope.removePostRefresh();
} }
scope.removePostRefresh = scope.$on('PostRefresh', function() { scope.removePostRefresh = scope.$on('PostRefresh', function() {
$("tr.success").each(function(index) { var i,cDate;
$("tr.success").each(function() {
// Make sure no rows have a green background // Make sure no rows have a green background
var ngc = $(this).attr('ng-class'); var ngc = $(this).attr('ng-class');
scope[ngc] = ""; scope[ngc] = "";
});
if (scope['jobs'] && scope['jobs'].length) {
var cDate;
for (var i=0; i < scope['jobs'].length; i++) {
// Convert created date to local time zone
cDate = new Date(scope['jobs'][i].created);
scope['jobs'][i].created = FormatDate(cDate);
// Set tooltip and link
scope.jobs[i].statusBadgeToolTip = JobStatusToolTip(scope.jobs[i].status) +
" Click to view status details.";
scope.jobs[i].statusLinkTo = '/#/jobs/' + scope.jobs[i].id;
}
}
}); });
if (scope.jobs && scope.jobs.length) {
for (i=0; i < scope.jobs.length; i++) {
// Convert created date to local time zone
cDate = new Date(scope.jobs[i].created);
scope.jobs[i].created = FormatDate(cDate);
// Set tooltip and link
scope.jobs[i].statusBadgeToolTip = JobStatusToolTip(scope.jobs[i].status) +
" Click to view status details.";
scope.jobs[i].statusLinkTo = '/#/jobs/' + scope.jobs[i].id;
}
}
});
if ($routeParams['job_host_summaries__host']) { if ($routeParams.job_host_summaries__host) {
defaultUrl += '?job_host_summaries__host=' + $routeParams['job_host_summaries__host']; defaultUrl += '?job_host_summaries__host=' + $routeParams.job_host_summaries__host;
} }
else if ($routeParams['inventory__int'] && $routeParams['status']) { else if ($routeParams.inventory__int && $routeParams.status) {
defaultUrl += '?inventory__int=' + $routeParams['inventory__int'] + '&status=' + defaultUrl += '?inventory__int=' + $routeParams.inventory__int + '&status=' +
$routeParams['status']; $routeParams.status;
} }
SearchInit({ scope: scope, set: 'jobs', list: list, url: defaultUrl }); SearchInit({ scope: scope, set: 'jobs', list: list, url: defaultUrl });
PaginateInit({ scope: scope, list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl });
// Called from Inventories page, failed jobs link. Find jobs for selected inventory. // Called from Inventories page, failed jobs link. Find jobs for selected inventory.
if ($routeParams['inventory__int']) { if ($routeParams.inventory__int) {
scope[list.iterator + 'SearchField'] = 'inventory'; scope[list.iterator + 'SearchField'] = 'inventory';
scope[list.iterator + 'SearchValue'] = $routeParams['inventory__int']; scope[list.iterator + 'SearchValue'] = $routeParams.inventory__int;
scope[list.iterator + 'SearchFieldLabel'] = 'Inventory ID'; scope[list.iterator + 'SearchFieldLabel'] = 'Inventory ID';
} }
if ($routeParams['id__int']) { if ($routeParams.id__int) {
scope[list.iterator + 'SearchField'] = 'id'; scope[list.iterator + 'SearchField'] = 'id';
scope[list.iterator + 'SearchValue'] = $routeParams['id__int']; scope[list.iterator + 'SearchValue'] = $routeParams.id__int;
scope[list.iterator + 'SearchFieldLabel'] = 'Job ID'; scope[list.iterator + 'SearchFieldLabel'] = 'Job ID';
} }
if ($routeParams['status']) { if ($routeParams.status) {
scope[list.iterator + 'SearchField'] = 'status'; scope[list.iterator + 'SearchField'] = 'status';
scope[list.iterator + 'SelectShow'] = true; scope[list.iterator + 'SelectShow'] = true;
scope[list.iterator + 'SearchSelectOpts'] = list.fields['status'].searchOptions; scope[list.iterator + 'SearchSelectOpts'] = list.fields.status.searchOptions;
scope[list.iterator + 'SearchFieldLabel'] = list.fields['status'].label.replace(/\<br\>/g,' '); scope[list.iterator + 'SearchFieldLabel'] = list.fields.status.label.replace(/<br>/g,' ');
for (var opt in list.fields['status'].searchOptions) { for (opt in list.fields.status.searchOptions) {
if (list.fields['status'].searchOptions[opt].value == $routeParams['status']) { if (list.fields.status.searchOptions[opt].value === $routeParams.status) {
scope[list.iterator + 'SearchSelectValue'] = list.fields['status'].searchOptions[opt]; scope[list.iterator + 'SearchSelectValue'] = list.fields.status.searchOptions[opt];
break; break;
} }
} }
} }
scope.search(list.iterator); scope.search(list.iterator);
@@ -88,88 +90,89 @@ function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest,
LoadBreadCrumbs(); LoadBreadCrumbs();
scope.refresh = function() { scope.refresh = function() {
Wait('start'); Wait('start');
scope['jobLoading'] = false; scope.jobLoading = false;
Refresh({ scope: scope, set: 'jobs', iterator: 'job', url: scope['current_url'] }); Refresh({ scope: scope, set: 'jobs', iterator: 'job', url: scope.current_url });
} };
scope.refreshJob = scope.refresh; scope.refreshJob = scope.refresh;
scope.editJob = function(id, name) { scope.editJob = function(id, name) {
LoadBreadCrumbs({ path: '/jobs/' + id, title: id + ' - ' + name }); LoadBreadCrumbs({ path: '/jobs/' + id, title: id + ' - ' + name });
$location.path($location.path() + '/' + id); $location.path($location.path() + '/' + id);
} };
scope.viewEvents = function(id, name) { scope.viewEvents = function(id, name) {
LoadBreadCrumbs({ path: '/jobs/' + id, title: id + ' - ' + name }); LoadBreadCrumbs({ path: '/jobs/' + id, title: id + ' - ' + name });
$location.path($location.path() + '/' + id + '/job_events'); $location.path($location.path() + '/' + id + '/job_events');
} };
scope.viewSummary = function(id, name) { scope.viewSummary = function(id, name) {
LoadBreadCrumbs({ path: '/jobs/' + id, title: id + ' - ' + name }); LoadBreadCrumbs({ path: '/jobs/' + id, title: id + ' - ' + name });
$location.path($location.path() + '/' + id + '/job_host_summaries'); $location.path($location.path() + '/' + id + '/job_host_summaries');
} };
scope.deleteJob = function(id, name) { scope.deleteJob = function(id) {
Rest.setUrl(defaultUrl + id + '/'); Rest.setUrl(defaultUrl + id + '/');
Rest.get() Rest.get()
.success( function(data, status, headers, config) { .success( function(data) {
var url, action_label, restcall, hdr;
if (data.status == 'pending' || data.status == 'running' || data.status == 'waiting') { var action, url, action_label, hdr;
url = data.related.cancel;
action_label = 'cancel'; if (data.status === 'pending' || data.status === 'running' || data.status === 'waiting') {
hdr = 'Cancel Job'; url = data.related.cancel;
} action_label = 'cancel';
else { hdr = 'Cancel Job';
url = defaultUrl + id + '/'; }
action_label = 'delete'; else {
hdr = 'Delete Job'; url = defaultUrl + id + '/';
} action_label = 'delete';
hdr = 'Delete Job';
}
var action = function() { action = function() {
Rest.setUrl(url); Rest.setUrl(url);
if (action_label == 'cancel') { if (action_label === 'cancel') {
Rest.post() Rest.post()
.success( function(data, status, headers, config) { .success( function() {
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
scope.search(list.iterator); scope.search(list.iterator);
}) })
.error( function(data, status, headers, config) { .error( function(data, status) {
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST returned status: ' + status }); { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST returned status: ' + status });
}); });
} }
else { else {
Rest.destroy() Rest.destroy()
.success( function(data, status, headers, config) { .success( function() {
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
scope.search(list.iterator); scope.search(list.iterator);
}) })
.error( function(data, status, headers, config) { .error( function(data, status) {
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status }); { hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
}); });
} }
}; };
Prompt({ Prompt({
hdr: hdr, hdr: hdr,
body: 'Are you sure you want to ' + action_label + ' job ' + id + '?', body: 'Are you sure you want to ' + action_label + ' job ' + id + '?',
action: action action: action
}); });
}) })
.error( function(data, status, headers, config) { .error( function(data, status) {
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get job details. GET returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to get job details. GET returned status: ' + status });
}); });
};
}
scope.submitJob = function(id, template) { scope.submitJob = function(id, template) {
SubmitJob({ scope: scope, id: id, template: template }); SubmitJob({ scope: scope, id: id, template: template });
} };
} }
JobsListCtrl.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobList', JobsListCtrl.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobList',
@@ -179,294 +182,282 @@ JobsListCtrl.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routePar
]; ];
function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, JobForm, function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, JobForm, GenerateForm, Rest, Alert, ProcessErrors,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, LoadBreadCrumbs, RelatedSearchInit,RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList, CredentialList,
RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList, CredentialList, ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, FormatDate, JobStatusToolTip, Wait) {
ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, FormatDate,
JobStatusToolTip, Wait) ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
{
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope. //scope.
var defaultUrl= GetBasePath('jobs'); var defaultUrl= GetBasePath('jobs'),
var generator = GenerateForm; generator = GenerateForm,
var form = JobForm; form = JobForm,
scope = generator.inject(form, {mode: 'edit', related: true}),
master = {},
id = $routeParams.id,
relatedSets = {},
loadingFinishedCount = 0;
generator.reset();
var scope = generator.inject(form, {mode: 'edit', related: true}); scope.job_id = id;
generator.reset(); scope.parseType = 'yaml';
var base = $location.path().replace(/^\//,'').split('/')[0]; scope.statusSearchSpin = false;
var master = {};
var id = $routeParams.id;
var relatedSets = {};
var loadingFinishedCount = 0;
scope.job_id = id;
scope.parseType = 'yaml';
scope.statusSearchSpin = false;
function getPlaybooks(project) { function getPlaybooks(project) {
if (project !== null && project !== '' && project !== undefined) { if (project !== null && project !== '' && project !== undefined) {
var url = GetBasePath('projects') + project + '/playbooks/'; var url = GetBasePath('projects') + project + '/playbooks/';
Rest.setUrl(url); Rest.setUrl(url);
Rest.get() Rest.get()
.success( function(data, status, headers, config) { .success( function(data) {
scope.playbook_options = []; scope.playbook_options = [];
for (var i=0; i < data.length; i++) { for (var i=0; i < data.length; i++) {
scope.playbook_options.push(data[i]); scope.playbook_options.push(data[i]);
} }
scope.$emit('jobTemplateLoadFinished'); scope.$emit('jobTemplateLoadFinished');
}) })
.error( function(data, status, headers, config) { .error( function() {
scope.$emit('jobTemplateLoadFinished'); scope.$emit('jobTemplateLoadFinished');
}); });
} }
else { else {
scope.$emit('jobTemplateLoadFinished'); scope.$emit('jobTemplateLoadFinished');
} }
} }
// Retrieve each related set and populate the playbook list // Retrieve each related set and populate the playbook list
if (scope.jobLoadedRemove) { if (scope.jobLoadedRemove) {
scope.jobLoadedRemove(); scope.jobLoadedRemove();
} }
scope.jobLoadedRemove = scope.$on('jobLoaded', function(e, related_cloud_credential) { scope.jobLoadedRemove = scope.$on('jobLoaded', function(e, related_cloud_credential) {
getPlaybooks(scope.project); getPlaybooks(scope.project);
scope[form.name + 'ReadOnly'] = (scope.status == 'new') ? false : true; scope[form.name + 'ReadOnly'] = (scope.status === 'new') ? false : true;
$('#forks-slider').slider("option", "value", scope.forks); $('#forks-slider').slider("option", "value", scope.forks);
$('#forks-slider').slider("disable"); $('#forks-slider').slider("disable");
$('input[type="checkbox"]').attr('disabled','disabled'); $('input[type="checkbox"]').attr('disabled','disabled');
$('input[type="radio"]').attr('disabled','disabled'); $('input[type="radio"]').attr('disabled','disabled');
$('#host_config_key-gen-btn').attr('disabled','disabled'); $('#host_config_key-gen-btn').attr('disabled','disabled');
$('textarea').attr('readonly','readonly'); $('textarea').attr('readonly','readonly');
// Get job template and display/hide host callback fields // Get job template and display/hide host callback fields
Rest.setUrl(scope.template_url); Rest.setUrl(scope.template_url);
Rest.get() Rest.get()
.success( function(data, status, headers, config) { .success( function(data) {
var dft = (data['host_config_key']) ? 'true' : 'false'; var dft = (data.host_config_key) ? 'true' : 'false';
scope['host_config_key'] = data['host_config_key']; scope.host_config_key = data.host_config_key;
md5Setup({ md5Setup({
scope: scope, scope: scope,
master: master, master: master,
check_field: 'allow_callbacks', check_field: 'allow_callbacks',
default_val: dft default_val: dft
}); });
scope['callback_url'] = data.related['callback']; scope.callback_url = data.related.callback;
scope.$emit('jobTemplateLoadFinished'); scope.$emit('jobTemplateLoadFinished');
}) })
.error( function(data, status, headers, config) { .error( function() {
scope['callback_url'] = '<< Job template not found >>'; Wait('stop');
}); scope.callback_url = '<< Job template not found >>';
});
if (related_cloud_credential) { if (related_cloud_credential) {
//Get the name of the cloud credential //Get the name of the cloud credential
Rest.setUrl(related_cloud_credential); Rest.setUrl(related_cloud_credential);
Rest.get() Rest.get()
.success( function(data, status, headers, config) { .success( function(data) {
scope['cloud_credential_name'] = data.name; scope.cloud_credential_name = data.name;
scope.$emit('jobTemplateLoadFinished'); scope.$emit('jobTemplateLoadFinished');
}) })
.error( function(data, status, headers, config) { .error( function(data, status) {
ProcessErrors(scope, data, status, null, ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to related cloud credential. GET returned status: ' + status }); { hdr: 'Error!', msg: 'Failed to related cloud credential. GET returned status: ' + status });
}); });
} }
else { else {
scope.$emit('jobTemplateLoadFinished'); scope.$emit('jobTemplateLoadFinished');
} }
}); });
// Turn off 'Wait' after both cloud credential and playbook list come back // Turn off 'Wait' after both cloud credential and playbook list come back
if (scope.removeJobTemplateLoadFinished) { if (scope.removeJobTemplateLoadFinished) {
scope.removeJobTemplateLoadFinished(); scope.removeJobTemplateLoadFinished();
} }
scope.removeJobTemplateLoadFinished = scope.$on('jobTemplateLoadFinished', function() { scope.removeJobTemplateLoadFinished = scope.$on('jobTemplateLoadFinished', function() {
loadingFinishedCount++; loadingFinishedCount++;
if (loadingFinishedCount >= 3) { if (loadingFinishedCount >= 3) {
// The initial template load finished. Now load related jobs, which // The initial template load finished. Now load related jobs, which
// will turn off the 'working' spinner. // will turn off the 'working' spinner.
Wait('stop'); Wait('stop');
} }
}); });
// Our job type options // Our job type options
scope.job_type_options = [{ value: 'run', label: 'Run' }, { value: 'check', label: 'Check' }]; scope.job_type_options = [{ value: 'run', label: 'Run' }, { value: 'check', label: 'Check' }];
scope.verbosity_options = [ scope.verbosity_options = [
{ value: '0', label: 'Default' }, { value: '0', label: 'Default' },
{ value: '1', label: 'Verbose' }, { value: '1', label: 'Verbose' },
{ value: '3', label: 'Debug' }]; { value: '3', label: 'Debug' }
scope.playbook_options = null; ];
scope.playbook = null; scope.playbook_options = null;
scope.playbook = null;
function calcRows (content) { function calcRows (content) {
var n = content.match(/\n/g); var n = content.match(/\n/g),
var rows = (n) ? n.length : 1; rows = (n) ? n.length : 1;
return (rows > 15) ? 15 : rows; return (rows > 15) ? 15 : rows;
} }
// Retrieve detail record and prepopulate the form // Retrieve detail record and prepopulate the form
Wait('start'); Wait('start');
Rest.setUrl(defaultUrl + ':id/'); Rest.setUrl(defaultUrl + ':id/');
Rest.get({ params: {id: id} }) Rest.get({ params: {id: id} })
.success( function(data, status, headers, config) { .success( function(data) {
//LoadBreadCrumbs({ path: '/jobs/' + id, title: data.id + ' - ' + data.summary_fields.job_template.name });
var i, cDate, fld, json_obj, related, set;
LoadBreadCrumbs();
for (fld in form.fields) {
if (fld !== 'variables' && data[fld] !== null && data[fld] !== undefined) {
if (form.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];
}
master[fld] = scope[fld];
}
scope.id = data.id;
scope.name = data.summary_fields.job_template.name;
if (fld === 'variables') {
// Parse extra_vars, converting to YAML.
if ($.isEmptyObject(data.extra_vars) || data.extra_vars === "{}" || data.extra_vars === "null" ||
data.extra_vars === "" || data.extra_vars === null) {
scope.variables = "---";
}
else {
json_obj = JSON.parse(data.extra_vars);
scope.variables = jsyaml.safeDump(json_obj);
}
master.variables = scope.variables;
}
if (form.fields[fld].type === 'lookup' && data.summary_fields[form.fields[fld].sourceModel]) {
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField];
}
//LoadBreadCrumbs({ path: '/jobs/' + id, title: data.id + ' - ' + data.summary_fields.job_template.name }); for (fld in form.statusFields) {
LoadBreadCrumbs(); if (data[fld] !== null && data[fld] !== undefined) {
if (fld === 'created') {
for (var fld in form.fields) { // Convert created date to local time zone
if (fld != 'variables' && data[fld] !== null && data[fld] !== undefined) { cDate = new Date(data.created);
if (form.fields[fld].type == 'select') { scope.created = FormatDate(cDate);
if (scope[fld + '_options'] && scope[fld + '_options'].length > 0) {
for (var i=0; i < scope[fld + '_options'].length; i++) {
if (data[fld] == scope[fld + '_options'][i].value) {
scope[fld] = scope[fld + '_options'][i];
}
}
} }
else { else {
scope[fld] = data[fld]; scope[fld] = data[fld];
} }
} }
else { }
scope[fld] = data[fld];
}
master[fld] = scope[fld];
}
scope.id = data.id;
scope.name = data.summary_fields.job_template.name;
if (fld == 'variables') { scope.statusToolTip = JobStatusToolTip(data.status);
// Parse extra_vars, converting to YAML.
if ($.isEmptyObject(data.extra_vars) || data.extra_vars == "\{\}" || data.extra_vars == "null"
|| data.extra_vars == "" || data.extra_vars == null) {
scope.variables = "---";
}
else {
var json_obj = JSON.parse(data.extra_vars);
scope.variables = jsyaml.safeDump(json_obj);
}
master.variables = scope.variables;
}
if (form.fields[fld].type == 'lookup' && data.summary_fields[form.fields[fld].sourceModel]) {
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField];
}
}
for (var fld in form.statusFields) {
if (data[fld] !== null && data[fld] !== undefined) {
if (fld == 'created') {
// Convert created date to local time zone
var cDate = new Date(data.created);
scope.created = FormatDate(cDate);
}
else {
scope[fld] = data[fld];
}
}
}
scope.statusToolTip = JobStatusToolTip(data.status);
$('form[name="jobs_form"] input[type="text"], form[name="jobs_form"] jobs_form textarea').attr('readonly','readonly'); $('form[name="jobs_form"] input[type="text"], form[name="jobs_form"] jobs_form textarea').attr('readonly','readonly');
$('form[name="jobs_form"] select').prop('disabled', 'disabled'); $('form[name="jobs_form"] select').prop('disabled', 'disabled');
$('form[name="jobs_form"] .lookup-btn').prop('disabled', 'disabled'); $('form[name="jobs_form"] .lookup-btn').prop('disabled', 'disabled');
$('form[name="jobs_form"] .buttons, form[name="jobs_form"] hr').hide(); $('form[name="jobs_form"] .buttons, form[name="jobs_form"] hr').hide();
scope.url = data.url; scope.url = data.url;
var related = data.related; related = data.related;
for (var set in form.related) { for (set in form.related) {
if (related[set]) { if (related[set]) {
relatedSets[set] = { url: related[set], iterator: form.related[set].iterator }; relatedSets[set] = { url: related[set], iterator: form.related[set].iterator };
} }
} }
// Calc row size of stdout and traceback textarea fields
//var n = scope['result_stdout'].match(/\n/g);
//var rows = (n) ? n.length : 1;
//rows = (rows > 15) ? 15 : rows;
//rows;
scope['stdout_rows'] = calcRows(scope['result_stdout']); scope.stdout_rows = calcRows(scope.result_stdout);
scope.traceback_rows = calcRows(scope.result_traceback);
//n = scope['result_traceback'].match(/\n/g); LookUpInit({
//var rows = (n) ? n.length : 1; scope: scope,
//rows = (rows > 15) ? 15 : rows; form: form,
current_item: data.inventory,
list: InventoryList,
field: 'inventory'
});
scope['traceback_rows'] = calcRows(scope['result_traceback']); LookUpInit({
scope: scope,
form: form,
current_item: data.credential,
list: CredentialList,
field: 'credential'
});
LookUpInit({ LookUpInit({
scope: scope, scope: scope,
form: form, form: form,
current_item: data.inventory, current_item: data.project,
list: InventoryList, list: ProjectList,
field: 'inventory' field: 'project'
}); });
LookUpInit({ // Initialize related search functions. Doing it here to make sure relatedSets object is populated.
scope: scope, RelatedSearchInit({ scope: scope, form: form, relatedSets: relatedSets });
form: form, RelatedPaginateInit({ scope: scope, relatedSets: relatedSets });
current_item: data.credential, scope.template_url = data.related.job_template;
list: CredentialList, scope.$emit('jobLoaded', data.related.cloud_credential);
field: 'credential' })
}); .error( function(data, status) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve job: ' + $routeParams.id + '. GET status: ' + status });
});
LookUpInit({ scope.refresh = function() {
scope: scope, Wait('start');
form: form, Rest.setUrl(defaultUrl + id + '/');
current_item: data.project, Rest.get()
list: ProjectList, .success( function(data) {
field: 'project' 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) {
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Attempt to load job failed. GET returned status: ' + status });
});
};
// Initialize related search functions. Doing it here to make sure relatedSets object is populated. scope.jobSummary = function() {
RelatedSearchInit({ scope: scope, form: form, relatedSets: relatedSets }); $location.path('/jobs/' + id + '/job_host_summaries');
RelatedPaginateInit({ scope: scope, relatedSets: relatedSets }); };
scope.template_url = data.related.job_template;
scope.$emit('jobLoaded', data.related.cloud_credential);
})
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, form,
{ hdr: 'Error!', msg: 'Failed to retrieve job: ' + $routeParams.id + '. GET status: ' + status });
});
scope.refresh = function() { scope.jobEvents = function() {
Wait('start'); $location.path('/jobs/' + id + '/job_events');
Rest.setUrl(defaultUrl + id + '/'); };
Rest.get()
.success( function(data, status, headers, config) {
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, headers, config) {
Wait('stop');
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', JobsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit',
'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList',
'ProjectList', 'LookUpInit', 'PromptPasswords', 'GetBasePath', 'md5Setup', 'FormatDate', 'ProjectList', 'LookUpInit', 'PromptPasswords', 'GetBasePath', 'md5Setup', 'FormatDate',
'JobStatusToolTip', 'Wait' 'JobStatusToolTip', 'Wait'

View File

@@ -68,7 +68,7 @@ angular.module('JobEventsListDefinition', [])
event_display: { event_display: {
label: 'Event', label: 'Event',
hasChildren: true, hasChildren: true,
ngClick: 'toggleChildren({{ jobevent.id }}, \'{{ jobevent.related.children }}\')', ngClick: 'toggleChildren(jobevent.id, jobevent.related.children)',
nosort: true, nosort: true,
searchable: false, searchable: false,
ngClass: '{{ jobevent.class }}', ngClass: '{{ jobevent.class }}',
@@ -101,7 +101,7 @@ angular.module('JobEventsListDefinition', [])
view: { view: {
label: 'View', label: 'View',
ngClick: 'viewJobEvent({{ jobevent.id }})', ngClick: 'viewJobEvent(jobevent.id)',
awToolTip: 'View event details', awToolTip: 'View event details',
dataPlacement: 'top' dataPlacement: 'top'
} }

View File

@@ -7,8 +7,7 @@
* *
*/ */
angular.module('JobHostDefinition', []) angular.module('JobHostDefinition', [])
.value( .value('JobHostList', {
'JobHostList', {
name: 'jobhosts', name: 'jobhosts',
iterator: 'jobhost', iterator: 'jobhost',
@@ -23,50 +22,50 @@ angular.module('JobHostDefinition', [])
label: 'Status', label: 'Status',
icon: 'icon-zoom-in', icon: 'icon-zoom-in',
ngShow: "job_id !== null" ngShow: "job_id !== null"
}, },
events: { events: {
href: "/#/jobs/{{ job_id }}/job_events", href: "/#/jobs/{{ job_id }}/job_events",
label: 'Events', label: 'Events',
icon: 'icon-list-ul' icon: 'icon-list-ul'
}, },
hosts: { hosts: {
href: "/#/jobs/{{ job_id }}/job_host_summaries", href: "/#/jobs/{{ job_id }}/job_host_summaries",
label: 'Host Summary', label: 'Host Summary',
active: true, active: true,
icon: 'icon-laptop' icon: 'icon-laptop'
} }
}, },
fields: { fields: {
job: { job: {
label: 'Job ID', label: 'Job ID',
ngClick: "showJob(\{\{ jobhost.job \}\})", ngClick: "showJob(jobhost.job)",
columnShow: 'host_id !== null', columnShow: 'host_id !== null',
key: true, key: true,
desc: true desc: true
}, },
host: { host: {
label: 'Host', label: 'Host',
key: true, key: true,
sourceModel: 'host', sourceModel: 'host',
sourceField: 'name', sourceField: 'name',
ngBind: 'jobhost.host_name', ngBind: 'jobhost.host_name',
ngHref: "\{\{ jobhost.hostLinkTo \}\}" ngHref: "jobhost.hostLinkTo"
}, },
status: { status: {
label: 'Status', label: 'Status',
badgeNgHref: "\{\{ jobhost.statusLinkTo \}\}", badgeNgHref: "{{ jobhost.statusLinkTo }}",
badgeIcon: 'fa icon-job-\{\{ jobhost.status \}\}', badgeIcon: 'fa icon-job-{{ jobhost.status }}',
badgePlacement: 'left', badgePlacement: 'left',
badgeToolTip: "\{\{ jobhost.statusBadgeToolTip \}\}", badgeToolTip: "{{ jobhost.statusBadgeToolTip }}",
badgeTipPlacement: 'top', badgeTipPlacement: 'top',
ngHref: "\{\{ jobhost.statusLinkTo \}\}", ngHref: "{{ jobhost.statusLinkTo }}",
awToolTip: "\{\{ jobhost.statusBadgeToolTip \}\}", awToolTip: "{{ jobhost.statusBadgeToolTip }}",
dataPlacement: 'top', dataPlacement: 'top',
searchField: 'failed', searchField: 'failed',
searchType: 'boolean', searchType: 'boolean',
searchOptions: [{ name: "success", value: 0 }, { name: "error", value: 1 }] searchOptions: [{ name: "success", value: 0 }, { name: "error", value: 1 }]
}, },
failed: { failed: {
label: 'Job failed?', label: 'Job failed?',
searchSingleValue: true, searchSingleValue: true,
@@ -74,32 +73,32 @@ angular.module('JobHostDefinition', [])
searchValue: 'true', searchValue: 'true',
searchOnly: true, searchOnly: true,
nosort: true nosort: true
}, },
ok: { ok: {
label: 'Success', label: 'Success',
searchable: false searchable: false
}, },
changed: { changed: {
label: 'Changed', label: 'Changed',
searchable: false searchable: false
}, },
failures: { failures: {
label: 'Failure', label: 'Failure',
searchable: true, searchable: true,
searchLabel: 'Contains failed events?', searchLabel: 'Contains failed events?',
searchType: 'gtzero' searchType: 'gtzero'
}, },
dark: { dark: {
label: 'Unreachable', label: 'Unreachable',
searchable: true, searchable: true,
searchType: 'gtzero', searchType: 'gtzero',
searchLabel: 'Contains unreachable hosts?' searchLabel: 'Contains unreachable hosts?'
}, },
skipped: { skipped: {
label: 'Skipped', label: 'Skipped',
searchable: false searchable: false
} }
}, },
actions: { actions: {
help: { help: {
@@ -116,17 +115,16 @@ angular.module('JobHostDefinition', [])
awToolTip: 'Click for help', awToolTip: 'Click for help',
dataTitle: 'Job Host Summary', dataTitle: 'Job Host Summary',
id: 'jobhost-help-button' id: 'jobhost-help-button'
}, },
refresh: { refresh: {
mode: 'all', mode: 'all',
'class': 'btn-xs', 'class': 'btn-xs',
awToolTip: "Refresh the page", awToolTip: "Refresh the page",
ngClick: "refresh()", ngClick: "refresh()",
ngShow: "host_id == null" //don't show when viewing from inventory->hosts ngShow: "host_id == null" //don't show when viewing from inventory->hosts
}
},
fieldActions: {
} }
},
}); fieldActions: {}
});

View File

@@ -7,8 +7,7 @@
* *
*/ */
angular.module('JobsListDefinition', []) angular.module('JobsListDefinition', [])
.value( .value( 'JobList', {
'JobList', {
name: 'jobs', name: 'jobs',
iterator: 'job', iterator: 'job',
@@ -22,26 +21,26 @@ angular.module('JobsListDefinition', [])
label: 'Job ID', label: 'Job ID',
key: true, key: true,
desc: true, desc: true,
searchType: 'int' searchType: 'int'
}, },
inventory: { inventory: {
label: 'Inventory ID', label: 'Inventory ID',
searchType: 'int', searchType: 'int',
searchOnly: true searchOnly: true
}, },
created: { created: {
label: 'Create On', label: 'Create On',
link: false, link: false,
searchable: false searchable: false
}, },
job_template: { job_template: {
label: 'Job Template', label: 'Job Template',
ngBind: 'job.summary_fields.job_template.name', ngBind: 'job.summary_fields.job_template.name',
//ngHref: "\{\{ '/#/job_templates/?name=' + job.summary_fields.job_template.name \}\}", //ngHref: "{{ '/#/job_templates/?name=' + job.summary_fields.job_template.name }}",
ngHref:"\{\{ '/#/job_templates/' + job.job_template \}\}", ngHref:"{{ '/#/job_templates/' + job.job_template }}",
sourceModel: 'job_template', sourceModel: 'job_template',
sourceField: 'name' sourceField: 'name'
}, },
failed: { failed: {
label: 'Job failed?', label: 'Job failed?',
searchSingleValue: true, searchSingleValue: true,
@@ -49,76 +48,77 @@ angular.module('JobsListDefinition', [])
searchValue: 'true', searchValue: 'true',
searchOnly: true, searchOnly: true,
nosort: true nosort: true
}, },
status: { status: {
label: 'Status', label: 'Status',
"class": 'job-\{\{ job.status \}\}', "class": 'job-{{ job.status }}',
searchType: 'select', searchType: 'select',
linkTo: "\{\{ job.statusLinkTo \}\}", linkTo: "{{ job.statusLinkTo }}",
searchOptions: [ searchOptions: [
{ name: "new", value: "new" }, { name: "new", value: "new" },
{ name: "waiting", value: "waiting" }, { name: "waiting", value: "waiting" },
{ name: "pending", value: "pending" }, { name: "pending", value: "pending" },
{ name: "running", value: "running" }, { name: "running", value: "running" },
{ name: "successful", value: "successful" }, { name: "successful", value: "successful" },
{ name: "error", value: "error" }, { name: "error", value: "error" },
{ name: "failed", value: "failed" }, { name: "failed", value: "failed" },
{ name: "canceled", value: "canceled" } ], { name: "canceled", value: "canceled" }
badgeIcon: 'fa icon-job-\{\{ job.status \}\}', ],
badgeIcon: 'fa icon-job-{{ job.status }}',
badgePlacement: 'left', badgePlacement: 'left',
badgeToolTip: "\{\{ job.statusBadgeToolTip \}\}", badgeToolTip: "{{ job.statusBadgeToolTip }}",
badgeTipPlacement: 'top', badgeTipPlacement: 'top',
badgeNgHref: "\{\{ job.statusLinkTo \}\}", badgeNgHref: "{{ job.statusLinkTo }}",
awToolTip: "\{\{ job.statusBadgeToolTip \}\}", awToolTip: "{{ job.statusBadgeToolTip }}",
dataPlacement: 'top' dataPlacement: 'top'
} }
}, },
actions: { actions: {
refresh: { refresh: {
mode: 'all', mode: 'all',
awToolTip: "Refresh the page", awToolTip: "Refresh the page",
ngClick: "refresh()" ngClick: "refresh()"
} }
}, },
fieldActions: { fieldActions: {
submit: { submit: {
label: 'Relaunch', label: 'Relaunch',
icon: 'icon-rocket', icon: 'icon-rocket',
mode: 'all', mode: 'all',
ngClick: "submitJob(\{\{ job.id \}\}, '\{\{ job.summary_fields.job_template.name \}\}' )", ngClick: 'submitJob(job.id, job.summary_fields.job_template.name)',
awToolTip: 'Start the job', awToolTip: 'Start the job',
dataPlacement: 'top' dataPlacement: 'top'
}, },
cancel: { cancel: {
label: 'Stop', label: 'Stop',
mode: 'all', mode: 'all',
ngClick: 'deleteJob(\{\{ job.id \}\})', ngClick: 'deleteJob(job.id)',
awToolTip: 'Cancel a running or pending job', awToolTip: 'Cancel a running or pending job',
ngShow: "job.status == 'pending' || job.status == 'running' || job.status == 'waiting'", ngShow: "job.status == 'pending' || job.status == 'running' || job.status == 'waiting'",
dataPlacement: 'top' dataPlacement: 'top'
}, },
"delete": { "delete": {
label: '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'", ngShow: "job.status != 'pending' && job.status != 'running' && job.status != 'waiting'",
dataPlacement: 'top' dataPlacement: 'top'
}, },
dropdown: { dropdown: {
type: 'DropDown', type: 'DropDown',
label: 'View', label: 'View',
icon: 'fa-search-plus', icon: 'fa-search-plus',
'class': 'btn-default btn-xs', 'class': 'btn-default btn-xs',
options: [ options: [
{ ngClick: "editJob(\{\{ job.id \}\}, '\{\{ job.summary_fields.job_template.name \}\}')", label: 'Status' }, { ngClick: 'editJob(job.id, job.summary_fields.job_template.name)', label: 'Status' },
{ ngClick: "viewEvents(\{{ job.id \}\}, '\{\{ job.summary_fields.job_template.name \}\}')", label: 'Events', { ngClick: 'viewEvents(job.id, job.summary_fields.job_template.name)', label: 'Events',
ngHide: "job.status == 'new'" }, ngHide: "job.status == 'new'" },
{ ngClick: "viewSummary(\{{ job.id \}\}, '\{\{ job.summary_fields.job_template.name \}\}')", label: 'Host Summary', { ngClick: 'viewSummary(job.id, job.summary_fields.job_template.name)', label: 'Host Summary',
ngHide: "job.status == 'new'" } ngHide: "job.status == 'new'" }
] ]
}
} }
}); }
});

View File

@@ -9,51 +9,49 @@
'use strict'; 'use strict';
angular.module('ApiLoader', ['ngCookies']) angular.module('ApiLoader', ['Utilities'])
.factory('LoadBasePaths', ['$http', '$rootScope', '$cookieStore', 'ProcessErrors',
function($http, $rootScope, $cookieStore, ProcessErrors) { .factory('LoadBasePaths', ['$http', '$rootScope', 'Store', 'ProcessErrors',
return function() { function($http, $rootScope, Store, ProcessErrors) {
$http.get('/api/') return function() {
.success( function(data, status, headers, config) { $http.get('/api/')
var base = data.current_version; .success( function(data) {
$http.get(base) var base = data.current_version;
.success( function(data, status, headers, config) { $http.get(base)
data['base'] = base; .success( function(data) {
$rootScope['defaultUrls'] = data; data.base = base;
$cookieStore.remove('api'); $rootScope.defaultUrls = data;
$cookieStore.put('api',data); //Preserve in cookie to prevent against Store('api', data);
//loss during browser refresh })
}) .error ( function(data, status) {
.error ( function(data, status, headers, config) { $rootScope.defaultUrls = { status: 'error' };
$rootScope['defaultUrls'] = { status: 'error' }; ProcessErrors(null, data, status, null,
ProcessErrors(null, data, status, null, { hdr: 'Error', msg: 'Failed to read ' + base + '. GET status: ' + status });
{ hdr: 'Error', msg: 'Failed to read ' + base + '. GET status: ' + status }); });
}); })
}) .error( function(data, status) {
.error( function(data, status, headers, config) { $rootScope.defaultUrls = { status: 'error' };
$rootScope['defaultUrls'] = { status: 'error' }; ProcessErrors(null, data, status, null,
ProcessErrors(null, data, status, null, { hdr: 'Error', msg: 'Failed to read /api. GET status: ' + status });
{ hdr: 'Error', msg: 'Failed to read /api. GET status: ' + status }); });
}); };
} }])
}])
.factory('GetBasePath', ['$rootScope', 'Store', 'LoadBasePaths', 'Empty',
function($rootScope, Store, LoadBasePaths, Empty) {
return function(set) {
// use /api/v1/ results to construct API URLs.
if (Empty($rootScope.defaultUrls)) {
// browser refresh must have occurred. load from local storage
if (Store('api')) {
$rootScope.defaultUrls = Store('api');
return $rootScope.defaultUrls[set];
}
return ''; //we should never get here
}
return $rootScope.defaultUrls[set];
};
}]);
.factory('GetBasePath', ['$rootScope', '$cookieStore', 'LoadBasePaths',
function($rootScope, $cookieStore, LoadBasePaths) {
return function(set) {
// use /api/v1/ results to construct API URLs.
var answer;
if ($rootScope['defaultUrls'] == null || $rootScope['defaultUrls'] == undefined) {
// browser refresh must have occurred. use what's in session cookie and refresh
answer = $cookieStore.get('api')[set];
LoadBasePaths();
}
else {
answer = $rootScope['defaultUrls'][set];
}
return answer;
}
}]);

View File

@@ -12,14 +12,15 @@ angular.module('AWFilters', [])
// capitalize -capitalize the first letter of each word // capitalize -capitalize the first letter of each word
// //
.filter('capitalize', [ function() { .filter('capitalize', [ function() {
return function(input) { return function(input) {
if (input) { var values, result, i;
var values = input.replace(/\_/g,' ').split(" "); if (input) {
var result = ""; values = input.replace(/\_/g,' ').split(" ");
for (var i = 0; i < values.length; i++){ result = "";
result += values[i].charAt(0).toUpperCase() + values[i].substr(1) + ' '; for (i = 0; i < values.length; i++){
result += values[i].charAt(0).toUpperCase() + values[i].substr(1) + ' ';
}
return result.trim();
} }
return result.trim(); };
} }]);
}
}]);