Latest jobs page changes. Integrated log viewer. Added support to log viewer for variables (extra vars and source vars). Most things on jobs pages are working, execept for scheduled jobs bit.

This commit is contained in:
Chris Houseknecht 2014-03-31 03:25:41 -04:00
parent e1d3da731e
commit 1e0af7b82e
25 changed files with 362 additions and 230 deletions

View File

@ -113,18 +113,18 @@ angular.module('ansible', [
controller: 'JobsListController'
}).
when('/jobs/:id', {
/* when('/jobs/:id', {
templateUrl: urlPrefix + 'partials/jobs.html',
controller: 'JobsEdit'
}).
}). */
when('/jobs/:id/job_events', {
templateUrl: urlPrefix + 'partials/jobs.html',
templateUrl: urlPrefix + 'partials/job_events.html',
controller: 'JobEventsList'
}).
when('/jobs/:id/job_host_summaries', {
templateUrl: urlPrefix + 'partials/jobs.html',
templateUrl: urlPrefix + 'partials/job_host_summaries.html',
controller: 'JobHostSummaryList'
}).

View File

@ -189,7 +189,7 @@ function InventoriesList($scope, $rootScope, $location, $log, $routeParams, Rest
Prompt({
hdr: 'Delete',
body: 'Are you sure you want to delete ' + name + '?',
body: '<div class=\"alert alert-info\">Are you sure you want to delete ' + name + '?</div>',
action: action
});
};

View File

@ -11,14 +11,14 @@
'use strict';
function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBreadCrumbs, LoadScope, RunningJobsList, CompletedJobsList, QueuedJobsList,
ScheduledJobsList, GetChoices, GetBasePath, Wait, DeleteJob, Find, DeleteSchedule, ToggleSchedule, RelaunchInventory, RelaunchPlaybook, RelaunchSCM,
ScheduledJobsList, GetChoices, GetBasePath, Wait, Find, JobsControllerInit, DeleteSchedule, ToggleSchedule,
LoadDialogPartial, ScheduledJobEdit) {
ClearScope();
var e,
completed_scope, running_scope, queued_scope, scheduled_scope,
choicesCount = 0, listsCount = 0, relaunch, getTypeId;
choicesCount = 0;
LoadBreadCrumbs();
@ -27,47 +27,7 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea
e.html(Breadcrumbs({ list: { editTitle: 'Jobs' } , mode: 'edit' }));
$compile(e)($scope);
relaunch = function(params) {
var scope = params.scope,
id = params.id,
type = params.type,
name = params.name;
if (type === 'inventory_sync') {
RelaunchInventory({ scope: scope, id: id});
}
else if (type === 'playbook_run') {
RelaunchPlaybook({ scope: scope, id: id, name: name });
}
else if (type === 'scm_sync') {
RelaunchSCM({ });
}
};
getTypeId = function(job) {
var type_id;
if (job.type === 'inventory_sync') {
type_id = job.inventory_source;
}
else if (job.type === 'scm_sync') {
type_id = job.poject;
}
else if (job.type === 'playbook_run') {
type_id = job.id;
}
return type_id;
};
// After all the lists are loaded
if ($scope.removeListLoaded) {
$scope.removeListLoaded();
}
$scope.removeListLoaded = $scope.$on('listLoaded', function() {
listsCount++;
if (listsCount === 3) {
Wait('stop');
}
});
// After all choices are ready, load up the lists and populate the page
if ($scope.removeBuildJobsList) {
$scope.removeBuildJobsList();
@ -113,18 +73,6 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea
scheduled_scope.search(ScheduledJobsList.iterator);
});
completed_scope.deleteJob = function(id) {
DeleteJob({ scope: completed_scope, id: id });
};
queued_scope.deleteJob = function(id) {
DeleteJob({ scope: queued_scope, id: id });
};
running_scope.deleteJob = function(id) {
DeleteJob({ scope: running_scope, id: id });
};
scheduled_scope.toggleSchedule = function(id) {
ToggleSchedule({
scope: scheduled_scope,
@ -144,25 +92,6 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea
scheduled_scope.editSchedule = function(id) {
ScheduledJobEdit({ scope: scheduled_scope, id: id });
};
completed_scope.relaunch = function(id) {
var job = Find({ list: completed_scope.completed_jobs, key: 'id', val: id }),
type_id = getTypeId(job);
relaunch({ scope: completed_scope, id: type_id, type: job.type, name: job.name });
};
running_scope.relaunch = function(id) {
var job = Find({ list: running_scope.running_jobs, key: 'id', val: id }),
type_id = getTypeId(job);
relaunch({ scope: running_scope, id: type_id, type: job.type, name: job.name });
};
queued_scope.relaunch = function(id) {
var job = Find({ list: queued_scope.queued_jobs, key: 'id', val: id }),
type_id = getTypeId(job);
relaunch({ scope: queued_scope, id: type_id, type: job.type, name: job.name });
};
});
if ($scope.removeChoicesReady) {
@ -198,11 +127,17 @@ function JobsListController ($scope, $compile, ClearScope, Breadcrumbs, LoadBrea
element_id: 'schedule-dialog-target',
callback: 'choicesReady'
});
$scope.refreshJobs = function() {
queued_scope.search('queued_job');
running_scope.search('running_job');
completed_scope.search('completed_job');
};
}
JobsListController.$inject = ['$scope', '$compile', 'ClearScope', 'Breadcrumbs', 'LoadBreadCrumbs', 'LoadScope', 'RunningJobsList', 'CompletedJobsList',
'QueuedJobsList', 'ScheduledJobsList', 'GetChoices', 'GetBasePath', 'Wait', 'DeleteJob', 'Find', 'DeleteSchedule', 'ToggleSchedule', 'RelaunchInventory',
'RelaunchPlaybook', 'RelaunchSCM', 'LoadDialogPartial', 'ScheduledJobEdit'];
'QueuedJobsList', 'ScheduledJobsList', 'GetChoices', 'GetBasePath', 'Wait', 'Find', 'JobsControllerInit',
'DeleteSchedule', 'ToggleSchedule', 'LoadDialogPartial', 'ScheduledJobEdit'];
function JobsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobForm, JobTemplateForm, GenerateForm, Rest,
Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList,

View File

@ -37,8 +37,7 @@ angular.module('GroupsHelper', ['RestServices', 'Utilities', 'ListGenerator', 'G
scope.$emit('sourceTypeOptionsReady');
})
.error(function (data, status) {
ProcessErrors(scope, data, status, null, {
hdr: 'Error!',
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to retrieve options for inventory_sources.source. OPTIONS status: ' + status
});
});
@ -651,7 +650,7 @@ function(SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize) {
if (ww > 1199) {
// desktop
x = 675;
y = (780 > wh) ? wh - 15 : 780;
y = (800 > wh) ? wh - 15 : 800;
maxrows = 18;
} else if (ww <= 1199 && ww >= 768) {
x = 550;
@ -740,8 +739,8 @@ function(SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize) {
if (sources_scope.codeMirror) {
sources_scope.codeMirror.destroy();
}
$('#group-modal-dialog').dialog('destroy');
$('#group-modal-dialog').hide();
$('#group-modal-dialog').dialog('destroy');
$('#properties-tab').empty();
$('#sources-tab').empty();
$('#schedules-list').empty();
@ -983,9 +982,12 @@ function(SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize) {
if (modal_scope.searchCleanUp) {
modal_scope.searchCleanup();
}
$('#group-modal-dialog').dialog('close');
try {
$('#group-modal-dialog').dialog('close');
}
catch(e) {
// ignore
}
// Change the selected group
if (groups_reload && parent_scope.selected_tree_id !== tree_id) {
parent_scope.showHosts(tree_id, group_id, false);
@ -1058,7 +1060,7 @@ function(SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize) {
overwrite: sources_scope.overwrite,
overwrite_vars: sources_scope.overwrite_vars,
update_on_launch: sources_scope.update_on_launch,
update_cache_timeout: sources_scope.update_cache_timeout
update_cache_timeout: (sources_scope.update_cache_timeout || 0)
};
// Create a string out of selected list of regions
@ -1165,7 +1167,6 @@ function(SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize) {
}
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(properties_scope, data, status, GroupForm, { hdr: 'Error!',
msg: 'Failed to create group: ' + group_id + '. POST status: ' + status
});
@ -1243,7 +1244,7 @@ function(SchedulerInit, Rest, Wait, SetSchedulesInnerDialogSize) {
Prompt({
hdr: 'Delete Group',
body: '<p>Are you sure you want to delete group <em>' + node.name + '?</p>',
body: '<div class=\"alert alert-info\">Are you sure you want to delete group <em>' + node.name + '?</div>',
action: action_to_take,
'class': 'btn-danger'
});

View File

@ -8,7 +8,7 @@
'use strict';
angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'CredentialFormDefinition', 'CredentialsListDefinition',
'LookUpHelper', 'JobSubmissionHelper' ])
'LookUpHelper', 'JobSubmissionHelper', 'JobTemplateFormDefinition' ])
.factory('LaunchJob', ['Rest', 'Wait', 'ProcessErrors', function(Rest, Wait, ProcessErrors) {
return function(params) {
@ -48,7 +48,7 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) {
LookUpInit({
url: GetBasePath('credentials') + '?kind=ssh',
scope: scope,
form: JobTemplateForm,
form: JobTemplateForm(),
current_item: null,
list: CredentialList,
field: 'credential',
@ -68,7 +68,7 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) {
callback = params.callback || 'PasswordsAccepted',
password,
form = CredentialForm,
html,
html = "",
acceptedPasswords = {},
scope = parent_scope.$new();
@ -80,14 +80,12 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) {
password = passwords.pop();
// Prompt for password
html += "<form class=\"form-horizontal\" name=\"password_form\" novalidate>\n";
html += "<form name=\"password_form\" novalidate>\n";
field = form.fields[password];
fld = password;
scope[fld] = '';
html += "<div class=\"form-group\">\n";
html += "<label class=\"col-md-offset-1 col-md-2 col-sm-offset-1 col-sm-2 col-xs-3\" for=\"" + fld + "\">* ";
html += "</label>\n";
html += "<div class=\"col-md-8 col-sm-8 col-xs-9\">\n";
html += "<label for=\"" + fld + "\">* " + field.label + "</label>\n";
html += "<input type=\"password\" ";
html += "ng-model=\"" + fld + '" ';
html += 'name="' + fld + '" ';
@ -100,7 +98,6 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) {
"password_form." + fld + ".$error.required\">A value is required!</span>\n";
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
html += "</div>\n";
html += "</div>\n";
// Add the related confirm field
if (field.associated) {
@ -108,9 +105,7 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) {
field = form.fields[field.associated];
scope[fld] = '';
html += "<div class=\"form-group\">\n";
html += "<label class=\"col-md-offset-1 col-md-2 col-sm-offset-1 col-sm-2 col-xs-3\" for=\"" + fld + "\">* ";
html += "</label>\n";
html += "<div class=\"col-md-8 col-sm-8 col-xs-9\">\n";
html += "<label for=\"" + fld + "\">* " + field.label + "</label>\n";
html += "<input type=\"password\" ";
html += "ng-model=\"" + fld + '" ';
html += 'name="' + fld + '" ';
@ -126,7 +121,6 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) {
".$error.awpassmatch\">Must match Password value</span>\n" : "";
html += "<span class=\"error api-error\" ng-bind=\"" + fld + "_api_error\"></span>\n";
html += "</div>\n";
html += "</div>\n";
}
html += "</form>\n";
$('#password-body').empty().html(html);
@ -139,8 +133,9 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) {
}
scope.passwordAccept = function() {
$('#password-modal').modal('hide');
acceptedPasswords[password] = scope[password];
if (password.length > 0) {
if (passwords.length > 0) {
promptPassword();
}
else {
@ -149,9 +144,12 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) {
};
scope.passwordCancel = function() {
Alert('Missing Password', 'Required password(s) not provided. The request will not be submitted.', 'alert-info');
$('#password-modal').modal('hide');
Alert('Missing Password', 'Required password(s) not provided. Your request will not be submitted.', 'alert-info');
parent_scope.$emit('PasswordsCanceled');
};
promptPassword();
};
}])
@ -178,7 +176,7 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) {
new_job_id = data.id;
launch_url = data.related.start;
if (data.passwords_needed_to_start.length > 0) {
scope.$emit('PromptForPasswords');
scope.$emit('PromptForPasswords', data.passwords_needed_to_start);
} else {
scope.$emit('StartPlaybookRun', {});
}
@ -293,8 +291,13 @@ function(Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialList) {
// Refresh the project list after update request submitted
Wait('stop');
Alert('Update Started', 'The request to start the SCM update process was submitted. ' +
'To monitor the update status, refresh the page by clicking the <em>Refresh</em> button.', 'alert-info');
scope.refresh();
'To monitor the update status, refresh the page by clicking the <i class="fa fa-refresh"></i> button.', 'alert-info');
if (scope.refreshJobs) {
scope.refreshJobs();
}
else if (scope.refresh) {
scope.refresh();
}
});
if (scope.removePromptForPasswords) {

View File

@ -10,7 +10,98 @@
'use strict';
angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'JobSummaryDefinition', 'InventoryHelper', 'GeneratorHelpers',
'JobSubmissionHelper', 'SchedulesHelper'])
'JobSubmissionHelper', 'SchedulesHelper', 'LogViewerHelper'])
/**
* JobsControllerInit({ scope: $scope });
*
* Initialize calling scope with all the bits required to support a jobs list
*
*/
.factory('JobsControllerInit', ['$location', 'Find', 'DeleteJob', 'RelaunchJob', 'LogViewer',
function($location, Find, DeleteJob, RelaunchJob, LogViewer) {
return function(params) {
var scope = params.scope,
parent_scope = params.parent_scope;
scope.deleteJob = function(id) {
DeleteJob({ scope: scope, id: id });
};
scope.relaunchJob = function(id) {
var list, job, typeId;
if (scope.completed_jobs) {
list = scope.completed_jobs;
}
else if (scope.running_jobs) {
list = scope.running_jobs;
}
else if (scope.queued_jobs) {
list = scope.queued_jobs;
}
job = Find({ list: list, key: 'id', val: id });
if (job.type === 'inventory_update') {
typeId = job.inventory_source;
}
else if (job.type === 'project_update') {
typeId = job.project;
}
else if (job.type === 'job') {
typeId = job.id;
}
RelaunchJob({ scope: scope, id: typeId, type: job.type, name: job.name });
};
scope.refreshJobs = function() {
parent_scope.refreshJobs();
};
scope.viewJobLog = function(id, url) {
var list, job;
if (url) {
$location.path(url);
}
else {
if (scope.completed_jobs) {
list = scope.completed_jobs;
}
else if (scope.running_jobs) {
list = scope.running_jobs;
}
else if (scope.queued_jobs) {
list = scope.queued_jobs;
}
job = Find({ list: list, key: 'id', val: id });
LogViewer({
scope: scope,
url: job.url,
status_icon: 'icon-job-' + job.status
});
}
};
};
}
])
.factory('RelaunchJob', ['RelaunchInventory', 'RelaunchPlaybook', 'RelaunchSCM',
function(RelaunchInventory, RelaunchPlaybook, RelaunchSCM) {
return function(params) {
var scope = params.scope,
id = params.id,
type = params.type,
name = params.name;
if (type === 'inventory_update') {
RelaunchInventory({ scope: scope, id: id});
}
else if (type === 'job') {
RelaunchPlaybook({ scope: scope, id: id, name: name });
}
else if (type === 'project_update') {
RelaunchSCM({ scope: scope, id: id });
}
};
}
])
.factory('JobStatusToolTip', [
function () {
@ -170,8 +261,8 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job
* Called from JobsList controller to load each section or list on the page
*
*/
.factory('LoadScope', ['SearchInit', 'PaginateInit', 'GenerateList', 'PageRangeSetup', 'ProcessErrors', 'Rest',
function(SearchInit, PaginateInit, GenerateList) {
.factory('LoadScope', ['SearchInit', 'PaginateInit', 'GenerateList', 'JobsControllerInit', 'Rest',
function(SearchInit, PaginateInit, GenerateList, JobsControllerInit, Rest) {
return function(params) {
var parent_scope = params.parent_scope,
scope = params.scope,
@ -199,7 +290,7 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job
scope: scope,
list: list,
url: url,
pageSize: 10
pageSize: 5
});
scope.iterator = list.iterator;
@ -208,6 +299,8 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job
scope.removePostRefresh();
}
scope.$on('PostRefresh', function(){
JobsControllerInit({ scope: scope, parent_scope: parent_scope });
scope[list.name].forEach(function(item, item_idx) {
var fld, field,
@ -231,6 +324,20 @@ angular.module('JobsHelper', ['Utilities', 'RestServices', 'FormGenerator', 'Job
}
return true;
});
//Set the name link
if (item.type === "inventory_update") {
Rest.setUrl(item.related.inventory_source);
Rest.get()
.success(function(data) {
itm.nameHref = "/inventories/" + data.inventory;
});
}
else if (item.type === "project_update") {
itm.nameHref = "/projects/" + item.project;
}
else if (item.type === "job") {
itm.nameHref = "";
}
if (list.name === 'completed_jobs' || list.name === 'running_jobs') {
itm.status_tip = itm.status_label + '. Click for details.';
@ -288,7 +395,7 @@ function(Find, GetBasePath, Rest, Wait, ProcessErrors, Prompt){
action_label = 'cancel';
hdr = 'Cancel Job';
} else {
url = GetBasePath('jobs') + id + '/';
url = job.related.cancel; //GetBasePath('unified_jobs') + id + '/';
action_label = 'delete';
hdr = 'Delete Job';
}
@ -368,7 +475,7 @@ function(Find, Wait, Rest, InventoryUpdate, ProcessErrors, GetBasePath) {
return function(params) {
var scope = params.scope,
id = params.id;
ProjectUpdate({ scope: scope, id: id });
ProjectUpdate({ scope: scope, project_id: id });
};
}])

View File

@ -7,12 +7,12 @@
'use strict';
angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator'])
angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator', 'VariablesHelper'])
.factory('LogViewer', ['$compile', 'CreateDialog', 'GetJob', 'Wait', 'GenerateForm', 'LogViewerStatusForm', 'AddTable', 'AddTextarea',
'LogViewerOptionsForm', 'EnvTable', 'GetBasePath', 'LookUpName', 'Empty',
'LogViewerOptionsForm', 'EnvTable', 'GetBasePath', 'LookUpName', 'Empty', 'AddPreFormattedText', 'ParseVariableString',
function($compile, CreateDialog, GetJob, Wait, GenerateForm, LogViewerStatusForm, AddTable, AddTextarea, LogViewerOptionsForm, EnvTable,
GetBasePath, LookUpName, Empty) {
GetBasePath, LookUpName, Empty, AddPreFormattedText, ParseVariableString) {
return function(params) {
var parent_scope = params.scope,
url = params.url,
@ -41,21 +41,19 @@ angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator'])
AddTable({ scope: scope, form: LogViewerOptionsForm, id: 'options-form-container', status_icon: status_icon });
if (data.result_stdout) {
AddTextarea({
container_id: 'stdout-form-container',
val: data.result_stdout,
fld_id: 'stdout-textarea'
AddPreFormattedText({
id: 'stdout-form-container',
val: data.result_stdout
});
}
else {
$('#logview-tabs li:eq(2)').hide();
$('#logview-tabs li:eq(1)').hide();
}
if (data.result_traceback) {
AddTextarea({
container_id: 'traceback-form-container',
val: data.result_traceback,
fld_id: 'traceback-textarea'
AddPreFormattedText({
id: 'traceback-form-container',
val: data.result_traceback
});
}
else {
@ -68,6 +66,28 @@ angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator'])
vars: data.job_env
});
}*/
if (data.extra_vars) {
AddTextarea({
container_id: 'variables-container',
fld_id: 'variables',
val: ParseVariableString(data.extra_vars)
});
}
else {
$('#logview-tabs li:eq(4)').hide();
}
if (data.source_vars) {
AddTextarea({
container_id: 'source-container',
fld_id: 'source-variables',
val: ParseVariableString(data.source_vars)
});
}
else {
$('#logview-tabs li:eq(5)').hide();
}
if (!Empty(scope.credential)) {
LookUpName({
@ -81,7 +101,7 @@ angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator'])
LookUpName({
scope: scope,
scope_var: 'inventory',
url: GetBasePath('inventories') + scope.inventory + '/'
url: GetBasePath('inventory') + scope.inventory + '/'
});
}
@ -115,8 +135,8 @@ angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator'])
rows = Math.floor((h - u) / 20);
rows -= 3;
rows = (rows < 6) ? 6 : rows;
$('#stdout-textarea').attr({ rows: rows });
$('#traceback-textarea').attr({ rows: rows });
$('#logviewer-modal-dialog #variables').attr({ rows: rows });
$('#logviewer-modal-dialog #source-variables').attr({ rows: rows });
};
elem = angular.element(document.getElementById('logviewer-modal-dialog'));
@ -237,12 +257,22 @@ angular.module('LogViewerHelper', ['ModalDialog', 'Utilities', 'FormGenerator'])
fld_id = params.fld_id,
html;
html = "<div class=\"form-group\">\n" +
"<textarea id=\"" + fld_id + "\" class=\"form-control nowrap mono-space\" rows=\"12\" readonly>" + val + "</textarea>" +
"<textarea id=\"" + fld_id + "\" class=\"form-control mono-space\" rows=\"12\" readonly>" + val + "</textarea>" +
"</div>\n";
$('#' + container_id).empty().html(html);
};
}])
.factory('AddPreFormattedText', [function() {
return function(params) {
var id = params.id,
val = params.val,
html;
html = "<pre>" + val + "</pre>\n";
$('#' + id).empty().html(html);
};
}])
.factory('EnvTable', [ function() {
return function(params) {
var id = params.id,

View File

@ -3,8 +3,6 @@
*
* VariablesHelper
*
* Show the CodeMirror variable editor and allow
* toggle between JSON and YAML
*
*/
@ -35,6 +33,7 @@ angular.module('VariablesHelper', ['Utilities'])
$log.info('Attempt to parse extra_vars as JSON failed. Attempting to parse as YAML');
try {
json_obj = jsyaml.safeLoad(variables);
json_obj = SortVariables(json_obj);
result = jsyaml.safeDump(json_obj);
}
catch(e2) {

View File

@ -22,7 +22,7 @@ angular.module('CompletedJobsDefinition', [])
fields: {
id: {
label: 'Job ID',
linkTo: '/#/jobs/{{ completed_job.id }}',
ngClick:"viewJobLog(completed_job.id)",
key: true,
desc: true,
searchType: 'int',
@ -30,13 +30,15 @@ angular.module('CompletedJobsDefinition', [])
},
status: {
label: 'Status',
columnClass: 'col-md-2 col-sm-2 col-xs-2',
awToolTip: "{{ completed_job.status_tip }}",
awTipPlacement: "top",
dataTitle: "{{ completed_job.status_popover_title }}",
icon: 'icon-job-{{ completed_job.status }}',
iconOnly: true,
awPopOver: "{{ completed_job.status_popover }}",
dataPlacement: 'right',
ngClick:"viewJobLog(completed_job.id)",
/*awPopOver: "{{ completed_job.status_popover }}",
dataPlacement: 'right',*/
searchType: 'select',
searchOptions: [
{ name: "Success", value: "successful" },
@ -50,8 +52,8 @@ angular.module('CompletedJobsDefinition', [])
searchType: 'int',
searchOnly: true
},
modified: {
label: 'Completed On',
finished: {
label: 'Finished On',
link: false,
searchable: false,
filter: "date:'MM/dd/yy HH:mm:ss'",
@ -66,9 +68,7 @@ angular.module('CompletedJobsDefinition', [])
name: {
label: 'Name',
columnClass: 'col-md-3 col-xs-5',
ngHref: 'nameHref',
sourceModel: 'template',
sourceField: 'name'
ngClick: "viewJobLog(completed_job.id, completed_job.nameHref)"
},
failed: {
label: 'Job failed?',
@ -85,7 +85,7 @@ angular.module('CompletedJobsDefinition', [])
refresh: {
mode: 'all',
awToolTip: "Refresh the page",
ngClick: "refresh()"
ngClick: "refreshJobs()"
}
},
@ -93,8 +93,8 @@ angular.module('CompletedJobsDefinition', [])
submit: {
icon: 'icon-rocket',
mode: 'all',
ngClick: 'relaunch(completed_job.id)',
awToolTip: 'Relaunch the job',
ngClick: 'relaunchJob(completed_job.id)',
awToolTip: 'Relaunch using the same parameters',
dataPlacement: 'top'
},
"delete": {
@ -105,11 +105,12 @@ angular.module('CompletedJobsDefinition', [])
},
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: '/#/jobs/{{ completed_job.id }}', label: 'Status' },
{ ngHref: '/#/jobs/{{ completed_job.id }}/job_events', label: 'Events', ngHide: "completed_job.status == 'new'" },
{ ngHref: '/#/jobs/{{ completed_job.id }}/job_host_summaries', label: 'Host Summary' }
]

View File

@ -21,12 +21,12 @@ angular.module('JobEventsListDefinition', [])
filterBy: '{ show: true }',
navigationLinks: {
details: {
href: '/#/jobs/{{ job_id }}',
label: 'Status',
icon: 'icon-zoom-in',
ngShow: 'job_id !== null'
},
//details: {
// href: '/#/jobs/{{ job_id }}',
// label: 'Status',
// icon: 'icon-zoom-in',
// ngShow: 'job_id !== null'
//},
events: {
href: '/#/jobs/{{ job_id }}/job_events',
label: 'Events',

View File

@ -20,12 +20,12 @@ angular.module('JobHostDefinition', [])
navigationLinks: {
ngHide: 'host_id !== null',
details: {
href: "/#/jobs/{{ job_id }}",
label: 'Status',
icon: 'icon-zoom-in',
ngShow: "job_id !== null"
},
//details: {
// href: "/#/jobs/{{ job_id }}",
// label: 'Status',
// icon: 'icon-zoom-in',
// ngShow: "job_id !== null"
//},
events: {
href: "/#/jobs/{{ job_id }}/job_events",
label: 'Events',

View File

@ -22,11 +22,22 @@ angular.module('QueuedJobsDefinition', [])
fields: {
id: {
label: 'Job ID',
ngClick:"viewJobLog(queued_job.id)",
key: true,
desc: true,
searchType: 'int',
columnClass: 'col-md-1 col-sm-2 col-xs-2'
},
status: {
label: 'Status',
columnClass: 'col-md-2 col-sm-2 col-xs-2',
awToolTip: "{{ queued_job.status_tip }}",
awTipPlacement: "top",
dataTitle: "{{ queued_job.status_popover_title }}",
icon: 'icon-job-{{ queued_job.status }}',
iconOnly: true,
ngClick:"viewJobLog(queued_job.id)"
},
inventory: {
label: 'Inventory ID',
searchType: 'int',
@ -48,9 +59,7 @@ angular.module('QueuedJobsDefinition', [])
name: {
label: 'Name',
columnClass: 'col-sm-3 col-xs-5',
ngHref: 'nameHref',
sourceModel: 'template',
sourceField: 'name'
ngClick: "viewJobLog(queued_job.id, queued_job.nameHref)"
}
},
@ -59,22 +68,16 @@ angular.module('QueuedJobsDefinition', [])
refresh: {
mode: 'all',
awToolTip: "Refresh the page",
ngClick: "refresh()"
ngClick: "refreshJobs()"
}
},
fieldActions: {
status: {
mode: 'all',
iconClass: 'fa icon-job-{{ queued_job.status }}',
awToolTip: "{{ queued_job.statusToolTip }}",
dataPlacement: 'top'
},
submit: {
icon: 'icon-rocket',
mode: 'all',
ngClick: 'relaunch(queued_job.id)',
awToolTip: 'Launch another instance of the job',
ngClick: 'relaunchJob(queued_job.id)',
awToolTip: 'Relaunch using the same parameters',
dataPlacement: 'top'
},
cancel: {

View File

@ -22,18 +22,29 @@ angular.module('RunningJobsDefinition', [])
fields: {
id: {
label: 'Job ID',
ngClick:"viewJobLog(running_job.id)",
key: true,
desc: true,
searchType: 'int',
columnClass: 'col-md-1 col-sm-2 col-xs-2'
},
status: {
label: 'Status',
columnClass: 'col-md-2 col-sm-2 col-xs-2',
awToolTip: "{{ running_job.status_tip }}",
awTipPlacement: "top",
dataTitle: "{{ running_job.status_popover_title }}",
icon: 'icon-job-{{ running_job.status }}',
iconOnly: true,
ngClick:"viewJobLog(running_job.id)"
},
inventory: {
label: 'Inventory ID',
searchType: 'int',
searchOnly: true
},
modified: {
label: 'Last Updated',
started: {
label: 'Started On',
link: false,
searchable: false,
filter: "date:'MM/dd/yy HH:mm:ss'",
@ -48,9 +59,7 @@ angular.module('RunningJobsDefinition', [])
name: {
label: 'Name',
columnClass: 'col-md-3 col-xs-5',
ngHref: 'nameHref',
sourceModel: 'template',
sourceField: 'name'
ngClick: "viewJobLog(running_job.id, running_job.nameHref)"
}
},
@ -59,25 +68,16 @@ angular.module('RunningJobsDefinition', [])
refresh: {
mode: 'all',
awToolTip: "Refresh the page",
ngClick: "refresh()"
ngClick: "refreshJobs()"
}
},
fieldActions: {
status: {
mode: 'all',
awToolTip: "{{ running_job.status_tip }}",
awTipPlacement: "top",
dataTitle: "{{ running_job.status_popover_title }}",
iconClass: 'fa icon-job-{{ running_job.status }}',
awPopOver: "{{ running_job.status_popover }}",
dataPlacement: 'left'
},
submit: {
icon: 'icon-rocket',
mode: 'all',
ngClick: 'relaunch(running_job.id)',
awToolTip: 'Launch another instance of the job',
ngClick: 'relaunchJob(running_job.id)',
awToolTip: 'Relaunch using the same parameters',
dataPlacement: 'top'
},
cancel: {
@ -88,11 +88,12 @@ angular.module('RunningJobsDefinition', [])
},
dropdown: {
type: 'DropDown',
ngShow: "running_job.type === 'job'",
label: 'View',
icon: 'fa-search-plus',
'class': 'btn-default btn-xs',
options: [
{ ngHref: '/#/jobs/{{ running_job.id }}', label: 'Status' },
//{ ngHref: '/#/jobs/{{ running_job.id }}', label: 'Status' },
{ ngHref: '/#/jobs/{{ running_job.id }}/job_events', label: 'Events' },
{ ngHref: '/#/jobs/{{ running_job.id }}/job_host_summaries', label: 'Host Summary' }
]

View File

@ -53,7 +53,7 @@ angular.module('ScheduledJobsDefinition', [])
refresh: {
mode: 'all',
awToolTip: "Refresh the page",
ngClick: "refresh()"
ngClick: "refreshJobs()"
}
},

View File

@ -64,9 +64,14 @@ body.modal-open {
.nowrap { white-space: nowrap; }
.capitalize { text-transform: capitalize; }
.grey-txt { color: @grey; }
.red-txt { color: @red; } a.red-txt:hover { color: @red; } //make red links (for things like cancel)
.text-center { text-align: center !important; }
.red-txt,
a.red-txt:visited,
a.red-txt:hover,
a.red-txt:active {
color: @red;
}
/* Used on inventory groups/hosts lists for long names */
.ellipsis {
@ -973,12 +978,9 @@ input[type="checkbox"].checkbox-no-label {
color: @green;
}
.icon-job-pending:before,
.icon-job-running:before,
.icon-job-success:before,
.icon-job-successful:before,
.icon-job-waiting:before,
.icon-job-new:before,
.icon-job-changed:before {
content: "\f111";
}
@ -989,12 +991,16 @@ input[type="checkbox"].checkbox-no-label {
content: "\f06a";
}
.icon-job-pending,
.icon-job-pending:before,
.icon-job-waiting:before,
.icon-job-new:before,
.icon-job-none:before {
content: "\f10c";
}
.icon-job-running,
.icon-job-success,
.icon-job-successful,
.icon-job-waiting,
.icon-job-new {
.icon-job-successful {
color: @green;
}
@ -1013,15 +1019,14 @@ input[type="checkbox"].checkbox-no-label {
color: @red;
}
.icon-job-none {
.icon-job-none,
.icon-job-pending,
.icon-job-waiting,
.icon-job-new {
color: @grey;
opacity: 0.45;
}
.icon-job-none:before {
content: "\f10c";
}
.icon-schedule-enabled-true:before {
content: "\f04c";
}
@ -1048,14 +1053,11 @@ input[type="checkbox"].checkbox-no-label {
.pagination li a {
font-size: 12px;
}
.list-table-container {
/*.list-table-container {
min-height: 338px;
}
}*/
}
.job-list-target {
min-height: 445px;
}
/* Inventory job status badge */
.failures-true {
background-color: @red;

View File

@ -88,3 +88,29 @@ table.ui-datepicker-calendar {
.ui-dialog-content.ui-widget-content {
padding-top: 20px;
}
.ui-widget-content {
a,
a:visited,
a:active {
color: @blue;
text-decoration: none;
}
a:hover,
a:focus {
color: @blue-dark;
text-decoration: none;
}
.red-txt,
a.red-txt:visited,
a.red-txt:hover,
a.red-txt:active {
color: @red;
}
.dropdown-menu>li>a {
color: @black;
}
}

View File

@ -10,7 +10,13 @@
#logviewer-modal-dialog {
textarea {
overflow: auto;
overflow: scroll;
}
pre {
overflow: scroll;
word-wrap: normal;
word-break: normal;
white-space: pre-wrap;
}
}

View File

@ -162,9 +162,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
}
})
.error(function (data, status) {
Wait('stop');
ProcessErrors(scope, data, status, null, {
hdr: 'Error!',
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to get inventory tree for: ' + inventory_id + '. GET returned: ' + status
});
});
@ -309,10 +307,10 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
html += "<div class=\"modal-body\">\n";
if (target.id === 1) {
html += "<p>Are you sure you want to move group " + inbound.name + " to the top level?</p>";
html += "<div class=\"alert alert-info\">Are you sure you want to move group " + inbound.name + " to the top level?</div>";
} else if (inbound.parent === 0) {
html += "<p>Are you sure you want to move group " + inbound.name + " from the top level and make it a child of " +
target.name + "?</p>";
html += "<div class=\"alert alert-info\">Are you sure you want to move group " + inbound.name + " from the top level and make it a child of " +
target.name + "?</div>";
} else {
html += "<div class=\"text-center\">\n";
html += "<p>Would you like to copy or move group <em>" + inbound.name + "</em> to group <em>" + target.name + "</em>?</p>\n";
@ -502,7 +500,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'P
html += "<h3>Copy Host</h3>\n";
html += "</div>\n";
html += "<div class=\"modal-body\">\n";
html += "<p>Are you sure you want to copy host " + host.name + ' to group ' + target.name + '?</p>';
html += "<div class=\"alert alert-info\">Are you sure you want to copy host " + host.name + ' to group ' + target.name + '?</div>';
html += "</div>\n";
html += "<div class=\"modal-footer\">\n";
html += "<a href=\"#\" data-target=\"#prompt-modal\" data-dismiss=\"modal\" class=\"btn btn-default\">No</a>\n";

View File

@ -79,6 +79,7 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
return function (hdr, msg, cls, action, secondAlert, disableButtons) {
var scope = $rootScope.$new(), e;
if (secondAlert) {
$('#alert2-modal-msg').attr({ "class": "alert" });
scope.alertHeader2 = hdr;
scope.alertBody2 = msg;
scope.alertClass2 = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger
@ -102,9 +103,11 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
}
});
} else {
$('#alert-modal-msg').attr({ "class": "alert" });
scope.alertHeader = hdr;
scope.alertBody = msg;
scope.alertClass = (cls) ? cls : 'alert-danger'; //default alert class is alert-danger
//console.log('msg: ' + msg + ' cls: ' + cls);
e = angular.element(document.getElementById('alert-modal'));
$compile(e)(scope);
$('#alert-modal').modal({

View File

@ -264,6 +264,7 @@ angular.module('GeneratorHelpers', [])
html += "<a href=\"\" class=\"toggle";
html += "\" ";
html += (field.ngDisabled) ? "ng-disabled=\"" + field.ngDisabled + "\" " : "";
html += (field.ngShow) ? "ng-show=\"" + field.ngShow + "\" " : "";
html += "data-toggle=\"dropdown\" ";
html += ">";
html += (field.icon) ? Icon(field.icon) : "";
@ -448,7 +449,7 @@ angular.module('GeneratorHelpers', [])
options = params.options,
base = params.base,
field = list.fields[fld],
html = '', cap;
html = '';
if (field.type !== undefined && field.type === 'DropDown') {
html = DropDown(params);
@ -493,21 +494,17 @@ angular.module('GeneratorHelpers', [])
}
// Start the Link
if ((field.key || field.link || field.linkTo || field.ngClick || field.ngHref) &&
if ((field.key || field.link || field.linkTo || field.ngClick || field.ngHref || field.awToolTip || field.awPopOver) &&
options.mode !== 'lookup' && options.mode !== 'select' && !field.noLink && !field.ngBindHtml) {
cap = false;
html += "<a ";
if (field.linkTo) {
html += "<a href=\"" + field.linkTo + "\" ";
cap = true;
html += "href=\"" + field.linkTo + "\" ";
} else if (field.ngClick) {
html += "<a href=\"\"" + Attr(field, 'ngClick') + " ";
cap = true;
html += "href=\"\"" + Attr(field, 'ngClick') + " ";
} else if (field.ngHref) {
html += "<a ng-href=\"" + field.ngHref + "\" ";
cap = true;
html += "ng-href=\"" + field.ngHref + "\" ";
} else if (field.link || (field.key && (field.link === undefined || field.link))) {
html += "<a href=\"#/" + base + "/{{" + list.iterator + ".id }}\" ";
cap = true;
html += "href=\"#/" + base + "/{{" + list.iterator + ".id }}\" ";
}
if (field.awDroppable) {
html += Attr(field, 'awDroppable');
@ -523,10 +520,15 @@ angular.module('GeneratorHelpers', [])
}
if (field.awToolTip) {
html += Attr(field, 'awToolTip');
html += (field.dataPlacement) ? Attr(field, 'dataPlacement') : "";
html += (field.dataPlacement && !field.awPopOver) ? Attr(field, 'dataPlacement') : "";
html += (field.dataTipWatch) ? Attr(field, 'dataTipWatch') : "";
html += (field.awTipPlacement) ? Attr(field, 'awTipPlacement') : "";
}
html += (cap) ? ">" : "";
if (field.awPopOver) {
html += "aw-pop-over=\"" + field.awPopOver + "\" ";
html += (field.dataPlacement) ? "data-placement=\"" + field.dataPlacement + "\" " : "";
}
html += ">";
}
// Add icon:
@ -563,7 +565,7 @@ angular.module('GeneratorHelpers', [])
//}
// close the link
if ((field.key || field.link || field.linkTo || field.ngClick || field.ngHref) &&
if ((field.key || field.link || field.linkTo || field.ngClick || field.ngHref || field.awToolTip || field.awPopOver) &&
options.mode !== 'lookup' && options.mode !== 'select' && !field.noLink && !field.ngBindHtml) {
html += "</a>";
}

View File

@ -0,0 +1,3 @@
<div class="tab-pane" id="job_events">
<div ng-cloak id="htmlTemplate"></div>
</div>

View File

@ -0,0 +1,3 @@
<div class="tab-pane" id="job_host_summaries">
<div ng-cloak id="htmlTemplate"></div>
</div>

View File

@ -33,6 +33,7 @@
</div>
</div>
</div>
<div id="schedule-dialog-target"></div>
<div id="schedule-dialog-target"></div>
<div ng-include="'/static/partials/logviewer.html'"></div>
</div>
</div>

View File

@ -5,6 +5,8 @@
<li><a href="#stdout" id="stdout-link" data-toggle="tab" ng-click="toggleTab($event, 'stdout-link', 'logview-tabs')">Standard Out</a></li>
<li><a href="#traceback" id="traceback-link" data-toggle="tab" ng-click="toggleTab($event, 'traceback-link', 'logview-tabs')">Traceback</a></li>
<li><a href="#options" id="options-link" data-toggle="tab" ng-click="toggleTab($event, 'options-link', 'logview-tabs')">Options</a></li>
<li><a href="#variables" id="variables-link" data-toggle="tab" ng-click="toggleTab($event, 'variable-link', 'logview-tabs')">Extra Vars</a></li>
<li><a href="#source-variables" id="source-variables-link" data-toggle="tab" ng-click="toggleTab($event, 'source-variable-link', 'logview-tabs')">Source Vars</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="status">
@ -19,5 +21,11 @@
<div class="tab-pane" id="options">
<div id="options-form-container"></div>
</div>
<div class="tab-pane" id="variables">
<div id="variables-container"></div>
</div>
<div class="tab-pane" id="source-variables">
<div id="source-container"></div>
</div>
</div>
</div>

View File

@ -331,7 +331,7 @@
<h3 ng-bind="alertHeader"></h3>
</div>
<div class="modal-body">
<div class="alert" ng-class="alertClass" ng-bind-html="alertBody"></div>
<div id="alert-modal-msg" class="alert" ng-class="alertClass" ng-bind-html="alertBody"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons" data-target="#form-modal" data-dismiss="modal" id="alert_ok_btn" class="btn btn-primary">OK</a>
@ -349,7 +349,7 @@
<h3 ng-bind="alertHeader2"></h3>
</div>
<div class="modal-body">
<div class="alert" ng-class="alertClass2" ng-bind-html="alertBody2"></div>
<div id="alert2-modal-msg" class="alert" ng-class="alertClass2" ng-bind-html="alertBody2"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons2" data-target="#form-modal2" data-dismiss="modal" id="alert2_ok_btn" class="btn btn-primary">OK</a>