AC-414 Making red/green bubbles consistent throughout the application. Changes made to Inventory and jobs pages. Tooltip text now dynamic. Improved deep linking, especially on status fields.

This commit is contained in:
chouseknecht
2013-10-07 03:19:30 -04:00
parent 6bf8f9cd98
commit b28fe1254e
25 changed files with 628 additions and 210 deletions

View File

@@ -70,7 +70,8 @@ angular.module('ansible', [
'LicenseFormDefinition',
'License',
'HostGroupsFormDefinition',
'ObjectCountWidget'
'ObjectCountWidget',
'JobsHelper'
])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.

View File

@@ -43,15 +43,15 @@ function InventoryHosts ($scope, $rootScope, $compile, $location, $log, $routePa
LoadSearchTree({ scope: scope, inventory_id: scope['inventory_id'] });
// Add the selected flag to the hosts set.
if (scope.relatedHostsRemove) {
scope.relatedHostsRemove();
}
scope.relatedHostsRemove = scope.$on('relatedhosts', function() {
scope.toggleAllFlag = false;
for (var i=0; i < scope.hosts.length; i++) {
scope.hosts[i].selected = 0;
}
});
//if (scope.relatedHostsRemove) {
// scope.relatedHostsRemove();
//}
//scope.relatedHostsRemove = scope.$on('relatedhosts', function() {
// scope.toggleAllFlag = false;
// for (var i=0; i < scope.hosts.length; i++) {
// scope.hosts[i].selected = 0;
// }
// });
scope.filterHosts = function() {
HostsReload({ scope: scope, inventory_id: scope['inventory_id'], group_id: scope['group_id'] });

View File

@@ -30,65 +30,79 @@ function InventoriesList ($scope, $rootScope, $location, $log, $routeParams, Res
scope.search(list.iterator);
LoadBreadCrumbs();
if (scope.projectsPostRefresh) {
scope.projectsPostRefresh();
}
scope.projectsPostRefresh = scope.$on('PostRefresh', function() {
for (var i=0; i < scope.inventories.length; i++) {
if (scope.inventories[i].hosts_with_active_failures > 0) {
scope.inventories[i].active_failures_params = "/?has_active_failures=true";
}
//if (scope.inventories[i].hosts_with_active_failures < 99) {
// scope.inventories[i].hosts_with_active_failures = ('00' + scope.inventories[i].hosts_with_active_failures).substr(-2);
//}
}
});
scope.addInventory = function() {
$location.path($location.path() + '/add');
}
$location.path($location.path() + '/add');
}
scope.editInventory = function(id) {
$location.path($location.path() + '/' + id);
}
$location.path($location.path() + '/' + id);
}
scope.deleteInventory = function(id, name) {
var action = function() {
var url = defaultUrl + id + '/';
$('#prompt-modal').modal('hide');
Wait('start');
Rest.setUrl(url);
Rest.destroy()
.success( function(data, status, headers, config) {
scope.search(list.iterator);
Wait('stop');
})
.error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
});
};
var action = function() {
var url = defaultUrl + id + '/';
$('#prompt-modal').modal('hide');
Wait('start');
Rest.setUrl(url);
Rest.destroy()
.success( function(data, status, headers, config) {
scope.search(list.iterator);
Wait('stop');
})
.error( function(data, status, headers, config) {
Wait('stop');
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
});
};
Prompt({ hdr: 'Delete',
body: 'Are you sure you want to delete ' + name + '?',
action: action
});
}
Prompt({ hdr: 'Delete',
body: 'Are you sure you want to delete ' + name + '?',
action: action
});
}
scope.lookupOrganization = function(organization_id) {
Rest.setUrl(GetBasePath('organizations') + organization_id + '/');
Rest.get()
.success( function(data, status, headers, config) {
return data.name;
});
}
scope.lookupOrganization = function(organization_id) {
Rest.setUrl(GetBasePath('organizations') + organization_id + '/');
Rest.get()
.success( function(data, status, headers, config) {
return data.name;
});
}
// Failed jobs link. Go to the jobs tabs, find all jobs for the inventory and sort by status
scope.viewJobs = function(id) {
$location.url('/jobs/?inventory__int=' + id);
}
// Failed jobs link. Go to the jobs tabs, find all jobs for the inventory and sort by status
scope.viewJobs = function(id) {
$location.url('/jobs/?inventory__int=' + id);
}
scope.viewFailedJobs = function(id) {
$location.url('/jobs/?inventory__int=' + id + '&status=failed');
}
scope.viewFailedJobs = function(id) {
$location.url('/jobs/?inventory__int=' + id + '&status=failed');
}
scope.editHosts = function(id) {
$location.url('/inventories/' + id + '/hosts');
}
scope.editHosts = function(id) {
$location.url('/inventories/' + id + '/hosts');
}
scope.editGroups = function(id) {
$location.url('/inventories/' + id + '/groups');
}
scope.editGroups = function(id) {
$location.url('/inventories/' + id + '/groups');
}
}
InventoriesList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'InventoryList', 'GenerateList',

View File

@@ -131,12 +131,39 @@ function JobEventsList ($scope, $rootScope, $location, $log, $routeParams, Rest,
set[i]['spaces'] = set[i].event_level * 24;
if (scope.jobevents[i].failed) {
scope.jobevents[i].status = 'error';
if (i == set.length - 1) {
scope.jobevents[i].statusBadgeToolTip = "A failure occurred durring one or more playbook tasks.";
}
else if (set[i].event_level < 3) {
scope.jobevents[i].statusBadgeToolTip = "A failure occurred within the children of this event.";
}
else {
scope.jobevents[i].statusBadgeToolTip = "A failure occurred. Click to view details";
}
}
else if (scope.jobevents[i].changed) {
scope.jobevents[i].status = 'changed';
if (i == set.length - 1) {
scope.jobevents[i].statusBadgeToolTip = "A change was completed durring one or more playbook tasks.";
}
else if (set[i].event_level < 3) {
scope.jobevents[i].statusBadgeToolTip = "A change was completed by one or more children of this event.";
}
else {
scope.jobevents[i].statusBadgeToolTip = "A change was completed. Click to view details";
}
}
else {
scope.jobevents[i].status = 'success';
if (i == set.length - 1) {
scope.jobevents[i].statusBadgeToolTip = "All playbook tasks completed successfully.";
}
else if (set[i].event_level < 3) {
scope.jobevents[i].statusBadgeToolTip = "All the children of this event completed successfully.";
}
else {
scope.jobevents[i].statusBadgeToolTip = "No errors occurred. Click to view details";
}
}
cDate = new Date(set[i].created);
set[i].created = FormatDate(cDate);
@@ -147,6 +174,8 @@ function JobEventsList ($scope, $rootScope, $location, $log, $routeParams, Rest,
Rest.get()
.success( function(data, status, headers, config) {
scope.job_status = data.status;
scope.job_name = data.summary_fields.job_template.name;
LoadBreadCrumbs({ path: '/jobs/' + scope.job_id, title: scope.job_name });
if (!(data.status == 'pending' || data.status == 'waiting' || data.status == 'running')) {
if ($rootScope.timer) {
clearInterval($rootScope.timer);
@@ -181,8 +210,6 @@ function JobEventsList ($scope, $rootScope, $location, $log, $routeParams, Rest,
children: children
});
}
LoadBreadCrumbs();
scope.viewJobEvent = function(id) {
EventView({ event_id: id });

View File

@@ -12,13 +12,14 @@
function JobHostSummaryList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobHostList,
GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
ClearScope, ProcessErrors, GetBasePath, Refresh)
ClearScope, ProcessErrors, GetBasePath, Refresh, JobStatusToolTip)
{
ClearScope('htmlTemplate');
var list = JobHostList;
var base = $location.path().replace(/^\//,'').split('/')[0];
var defaultUrl = GetBasePath(base) + $routeParams.id + '/job_host_summaries/';
var inventory_id;
// When viewing all summaries for a particular host, show job ID, otherwise row ID.
if (base == 'hosts') {
list.index = false;
@@ -42,46 +43,71 @@ function JobHostSummaryList ($scope, $rootScope, $location, $log, $routeParams,
scope.host_id = null;
}
if (scope.RemoveSetHostLink) {
scope.RemoveSetHostLink();
}
scope.RemoveSetHostLink = scope.$on('setHostLink', function(e, inventory_id) {
for (var i=0; i < scope.jobhosts.length; i++) {
scope.jobhosts[i].hostLinkTo = '/#/inventories/' + inventory_id + '/hosts/?name=' +
escape(scope.jobhosts[i].summary_fields.host.name);
}
});
// After a refresh, populate any needed summary field values on each row
if (scope.PostRefreshRemove) {
scope.PostRefreshRemove();
}
scope.PostRefershRemove = scope.$on('PostRefresh', function() {
scope.PostRefreshRemove = scope.$on('PostRefresh', function() {
// Set status, tooltips, badget icons, etc.
for( var i=0; i < scope.jobhosts.length; i++) {
scope.jobhosts[i].host_name = scope.jobhosts[i].summary_fields.host.name;
scope.jobhosts[i].status = (scope.jobhosts[i].failed) ? 'error' : 'success';
scope.jobhosts[i].status = (scope.jobhosts[i].failed) ? 'failed' : 'success';
scope.jobhosts[i].statusBadgeToolTip = JobStatusToolTip(scope.jobhosts[i].status) +
" Click to view details.";
scope.jobhosts[i].statusLinkTo = '/#/jobs/' + scope.jobhosts[i].job + '/job_events/?host=' +
escape(scope.jobhosts[i].summary_fields.host.name);
}
if (scope.job_id !== null && scope.job_id !== undefined && scope.job_id !== '') {
// need job_status so we can show/hide refresh button
Rest.setUrl(GetBasePath('jobs') + scope.job_id);
Rest.get()
.success( function(data, status, headers, config) {
scope.job_status = data.status;
scope.$emit('setHostLink', data.inventory);
if (!(data.status == 'pending' || data.status == 'waiting' || data.status == 'running')) {
if ($rootScope.timer) {
clearInterval($rootScope.timer);
}
}
})
.error( function(data, status, headers, config) {
.error( function(data, status, headers, config) {
ProcessErrors(scope, data, status, null,
{ hdr: 'Error!', msg: 'Failed to get job status for job: ' + scope.job_id + '. GET status: ' + status });
});
}
if (base == 'hosts' && $routeParams['host_name']) {
// Make the host name appear in breadcrumbs
LoadBreadCrumbs({ path: '/hosts/' + scope['host_id'], title: $routeParams['host_name'] });
}
else {
LoadBreadCrumbs();
}
});
SearchInit({ scope: scope, set: 'jobhosts', list: list, url: defaultUrl });
PaginateInit({ scope: scope, list: list, url: defaultUrl });
// Called from Inventories tab, host failed events link:
if ($routeParams.host) {
if ($routeParams['host_name']) {
scope[list.iterator + 'SearchField'] = 'host';
scope[list.iterator + 'SearchValue'] = $routeParams.host;
scope[list.iterator + 'SearchValue'] = $routeParams['host_name'];
scope[list.iterator + 'SearchFieldLabel'] = list.fields['host'].label;
}
scope.search(list.iterator);
LoadBreadCrumbs();
scope.showEvents = function(host_name, last_job) {
// When click on !Failed Events link, redirect to latest job/job_events for the host
@@ -113,5 +139,5 @@ function JobHostSummaryList ($scope, $rootScope, $location, $log, $routeParams,
JobHostSummaryList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobHostList',
'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
'ProcessErrors', 'GetBasePath', 'Refresh'
'ProcessErrors', 'GetBasePath', 'Refresh', 'JobStatusToolTip'
];

View File

@@ -27,6 +27,14 @@ function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Re
SearchInit({ scope: scope, set: 'job_templates', list: list, url: defaultUrl });
PaginateInit({ scope: scope, list: list, url: defaultUrl });
// Called from Inventories tab, host failed events link:
if ($routeParams['name']) {
scope[list.iterator + 'SearchField'] = 'name';
scope[list.iterator + 'SearchValue'] = $routeParams['name'];
scope[list.iterator + 'SearchFieldLabel'] = list.fields['name'].label;
}
scope.search(list.iterator);
LoadBreadCrumbs();

View File

@@ -12,7 +12,8 @@
function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobList,
GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
ClearScope, ProcessErrors, GetBasePath, LookUpInit, SubmitJob, FormatDate, Refresh)
ClearScope, ProcessErrors, GetBasePath, LookUpInit, SubmitJob, FormatDate, Refresh,
JobStatusToolTip)
{
ClearScope('htmlTemplate');
var list = JobList;
@@ -41,6 +42,13 @@ function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest,
scope[list.name][i].created = FormatDate(cDate);
}
}
for (var i=0; i < scope.jobs.length; i++) {
scope.jobs[i].statusBadgeToolTip = JobStatusToolTip(scope.jobs[i].status) +
" Click to view status details.";
scope.jobs[i].statusLinkTo = '/#/jobs/' + scope.jobs[i].id;
}
});
@@ -157,14 +165,14 @@ function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest,
JobsListCtrl.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobList',
'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
'ProcessErrors','GetBasePath', 'LookUpInit', 'SubmitJob', 'FormatDate', 'Refresh'
'ProcessErrors','GetBasePath', 'LookUpInit', 'SubmitJob', 'FormatDate', 'Refresh', 'JobStatusToolTip'
];
function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, JobForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit,
RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList, CredentialList,
ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, FormatDate)
ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, FormatDate, JobStatusToolTip)
{
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope.
@@ -334,7 +342,9 @@ function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
}
}
}
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"] select').prop('disabled', 'disabled');
$('form[name="jobs_form"] .lookup-btn').prop('disabled', 'disabled');
@@ -503,5 +513,6 @@ function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams,
JobsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit',
'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList',
'ProjectList', 'LookUpInit', 'PromptPasswords', 'GetBasePath', 'md5Setup', 'FormatDate'
'ProjectList', 'LookUpInit', 'PromptPasswords', 'GetBasePath', 'md5Setup', 'FormatDate',
'JobStatusToolTip'
];

View File

@@ -18,13 +18,29 @@ angular.module('InventoryHostsFormDefinition', [])
fields: {
name: {
key: true,
label: 'Host Name',
ngClick: "editHost(\{\{ host.id \}\}, '\{\{ host.name \}\}')",
label: 'Name',
ngClick: "editHost(\{\{ host.id \}\}, '\{\{ host.name \}\}')"
//columnClass: 'col-lg-3'
},
active_failures: {
label: 'Current<br>Job Status?',
ngHref: "\{\{ host.activeFailuresLink \}\}",
awToolTip: "\{\{ host.badgeToolTip \}\}",
dataPlacement: 'bottom',
badgeNgHref: '\{\{ host.activeFailuresLink \}\}',
badgeIcon: "\{\{ 'icon-failures-' + host.has_active_failures \}\}",
badgeToolTip: 'Indicates if host has active failures',
badgePlacement: 'left',
badgeToolTip: "\{\{ host.badgeToolTip \}\}",
badgeTipPlacement: 'bottom',
columnClass: 'col-lg-3'
searchable: false,
nosort: true
},
has_active_failures: {
label: 'Current job failed?',
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
searchOnly: true
},
groups: {
label: 'Groups',
@@ -32,7 +48,7 @@ angular.module('InventoryHostsFormDefinition', [])
sourceModel: 'groups',
sourceField: 'name',
nosort: true
}
},
},
actions: {

View File

@@ -270,7 +270,7 @@ angular.module('JobFormDefinition', [])
status: {
label: 'Job Status',
type: 'custom',
control: '<div class="job-detail-status job-\{\{ status \}\}"><i class="icon-circle"></i> \{\{ status \}\}</div>',
control: '<div class=\"job-detail-status\"><i class=\"icon-job-\{\{ status \}\}\"></i> \{\{ status \}\}</div>',
readonly: true
},
created: {

View File

@@ -27,7 +27,7 @@ angular.module('EventsHelper', ['RestServices', 'Utilities', 'JobEventDataDefini
fields: {
status: {
labelClass: 'job-\{\{ status \}\}',
icon: 'icon-circle',
icon: 'icon-job-\{\{ status \}\}',
type: 'custom',
section: 'Event',
control: '<div class=\"job-event-status job-\{\{ status \}\}\">\{\{ status \}\}</div>'

View File

@@ -147,26 +147,35 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
for (var i=0; i < scope.groups.length; i++) {
var last_update = (scope.groups[i].last_updated == null) ? '' : FormatDate(new Date(scope.groups[i].last_updated));
var source = 'Manual';
var stat;
var stat_class;
var stat, stat_class, status_tip;
stat = scope.groups[i].status;
stat_class = stat;
switch (scope.groups[i].status) {
case 'never updated':
stat = 'never';
stat_class = 'never';
status_tip = 'Inventory update has not been performed. Click Update button to start it now.';
break;
case 'none':
stat = 'n/a';
stat_class = 'na';
status_tip = 'Not configured for inventory update.';
break;
case 'failed':
status_tip = 'Inventory update completed with errors. Click to view process output.';
break;
case 'successful':
status_tip = 'Inventory update completed with no errors. Click to view process output.';
break;
case 'updating':
status_tip = 'Inventory update process running now.';
break;
default:
stat = scope.groups[i].status;
stat_class = stat;
}
switch (scope.groups[i].source) {
case 'file':
source = 'File';
source = 'Local Script';
break;
case 'ec2':
source = 'Amazon EC2';
@@ -175,10 +184,21 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
source = 'Rackspace';
break;
}
if (scope.groups[i].summary_fields.group.hosts_with_active_failures > 0) {
scope.groups[i].active_failures_params = "/?has_active_failures=true";
}
else {
scope.groups[i].active_failures_params = '';
}
scope.groups[i].hosts_with_active_failures = scope.groups[i].summary_fields.group.hosts_with_active_failures;
scope.groups[i].has_active_failures = scope.groups[i].summary_fields.group.has_active_failures;
scope.groups[i].status = stat;
scope.groups[i].source = source;
scope.groups[i].last_updated = last_update;
scope.groups[i].status_class = stat_class;
scope.groups[i].status_badge_class = stat_class;
scope.groups[i].status_badge_tooltip = status_tip;
}
});
@@ -688,6 +708,36 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
});
}
}
// Start the update process
scope.updateGroup = function() {
if (scope[source] == "" || scope.groups[i].source == null) {
Alert('Missing Configuration', 'The group is not configured for updates. You must provide Source settings before running the update ' +
'process.');
}
else if (scope[status] == 'updating') {
Alert('Update in Progress', 'The inventory update process is currently running for this group <em>' +
scope.groups[i].summary_fields.group.name + '</em>. Under the Groupmonitor the status.', 'alert-info');
}
else {
if (scope['source'] == 'Amazon EC2') {
scope['sourceUsernameLabel'] = 'Access Key ID';
scope['sourcePasswordLabel'] = 'Secret Access Key';
scope['sourcePasswordConfirmLabel'] = 'Confirm Secret Access Key';
}
else {
scope['sourceUsernameLabel'] = 'Username';
scope['sourcePasswordLabel'] = 'Password';
scope['sourcePasswordConfirmLabel'] = 'Confirm Password';
}
InventoryUpdate({
scope: scope,
group_id: id,
url: scope.groups[i].related.update,
group_name: scope.groups[i].summary_fields.group.name,
group_source: scope.groups[i].source
});
}
}
// Cancel
scope.formReset = function() {
@@ -697,6 +747,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
}
scope.parseType = 'yaml';
}
}
}])

View File

@@ -408,8 +408,8 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
}])
.factory('HostsReload', ['SearchInit', 'PaginateInit', 'InventoryHostsForm', 'GetBasePath', 'Wait',
function(SearchInit, PaginateInit, InventoryHostsForm, GetBasePath, Wait) {
.factory('HostsReload', ['$routeParams', 'SearchInit', 'PaginateInit', 'InventoryHostsForm', 'GetBasePath', 'Wait',
function($routeParams, SearchInit, PaginateInit, InventoryHostsForm, GetBasePath, Wait) {
return function(params) {
// Rerfresh the Hosts view on right side of page
@@ -424,15 +424,13 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
var url = (group_id !== null && group_id !== undefined) ? GetBasePath('groups') + group_id + '/all_hosts/' :
GetBasePath('inventory') + params.inventory_id + '/hosts/';
if (scope.hostFailureFilter) {
url += '?has_active_failures=true';
}
// Set the groups value in each element of hosts array
if (scope.removePostRefresh) {
scope.removePostRefresh();
}
scope.removePostRefresh = scope.$on('PostRefresh', function() {
// Add a list of groups to each host
var groups, descr, found, list;
for (var i=0; i < scope.hosts.length; i++) {
groups = scope.hosts[i].summary_fields.groups;
@@ -444,14 +442,54 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
}
scope.hosts[i].groups = scope.hosts[i].groups.replace(/\, $/,'');
}
// Add the value displayed in Job Status column
for (var i=0; i < scope.hosts.length; i++) {
scope.hosts[i].activeFailuresLink = '/#/hosts/' + scope.hosts[i].id + '/job_host_summaries/?inventory=' + scope['inventory_id'] +
'&host_name=' + escape(scope.hosts[i].name);
if (scope.hosts[i].has_active_failures == true) {
scope.hosts[i].badgeToolTip = 'Most recent job failed. Click to view jobs.';
scope.hosts[i].active_failures = 'failed';
}
else if (scope.hosts[i].has_active_failures == false && scope.hosts[i].last_job == null) {
scope.hosts[i].has_active_failures = 'none';
scope.hosts[i].badgeToolTip = "No job data available.";
scope.hosts[i].active_failures = 'n/a';
}
else if (scope.hosts[i].has_active_failures == false && scope.hosts[i].last_job !== null) {
scope.hosts[i].badgeToolTip = "Most recent job successful. Click to view jobs.";
scope.hosts[i].active_failures = 'success';
}
}
if (postAction) {
postAction();
}
});
});
SearchInit({ scope: scope, set: 'hosts', list: InventoryHostsForm, url: url });
PaginateInit({ scope: scope, list: InventoryHostsForm, url: url });
scope.search('host');
if ($routeParams['has_active_failures']) {
//scope.resetSearch(InventoryHostsForm.iterator);
scope[InventoryHostsForm.iterator + 'InputDisable'] = true;
scope[InventoryHostsForm.iterator + 'SearchValue'] = $routeParams['has_active_failures'];
scope[InventoryHostsForm.iterator + 'SearchField'] = 'has_active_failures';
scope[InventoryHostsForm.iterator + 'SearchFieldLabel'] = InventoryHostsForm.fields['has_active_failures'].label;
scope[InventoryHostsForm.iterator + 'SearchSelectValue'] = ($routeParams['has_active_failures'] == 'true') ? { value: 1 } : { value: 0 };
}
if ($routeParams['name']) {
console.log('here!');
scope[InventoryHostsForm.iterator + 'InputDisable'] = false;
scope[InventoryHostsForm.iterator + 'SearchValue'] = $routeParams['name'];
scope[InventoryHostsForm.iterator + 'SearchField'] = 'name';
scope[InventoryHostsForm.iterator + 'SearchFieldLabel'] = InventoryHostsForm.fields['name'].label;
scope[InventoryHostsForm.iterator + 'SearchSelectValue'] = null;
}
scope.search(InventoryHostsForm.iterator);
if (!params.scope.$$phase) {
params.scope.$digest();
@@ -467,6 +505,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
var scope = params.scope;
var inventory_id = params.inventory_id;
var html = '';
var toolTip = 'Hosts have failed jobs?';
function buildHTML(tree_data) {
var sorted = SortNodes(tree_data);
@@ -480,7 +519,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
"data-group-id=\"" + sorted[i].id + "\" " +
"><a href=\"\" class=\"expand\"><i class=\"icon-caret-down\"></i></a> " +
"<i class=\"field-badge icon-failures-" + sorted[i].has_active_failures + "\" " +
"aw-tool-tip=\"Indicates if group contains hosts with active failures\" data-placement=\"bottom\"></i> " +
"aw-tool-tip=\"" + toolTip + "\" data-placement=\"bottom\"></i> " +
"<a href=\"\" class=\"activate\">" + sorted[i].name + "</a> ";
if (sorted[i].children.length > 0) {
buildHTML(sorted[i].children);
@@ -651,7 +690,7 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
"data-name=\"" + data.name + "\" " +
"><a href=\"\" class=\"expand\"><i class=\"icon-caret-down\"></i></a> " +
"<i class=\"field-badge icon-failures-" + data.has_active_failures + "\"" +
"aw-tool-tip=\"Indicates if group contains hosts with active failures\" data-placement=\"bottom\"></i> " +
"aw-tool-tip=\"" + toolTip + "\" data-placement=\"bottom\"></i> " +
"<a href=\"\" class=\"activate active\">" + data.name + "</a>";
scope.$emit('buildAllGroups', data.name, data.related.tree, data.related.groups);
scope.$emit('refreshHost', null, 'All Hosts');

View File

@@ -0,0 +1,40 @@
/*********************************************
* Copyright (c) 2013 AnsibleWorks, Inc.
*
* JobsHelper
*
* Routines shared by job related controllers
*
*/
angular.module('JobsHelper', [ ])
.factory('JobStatusToolTip', [ function() {
return function(status) {
var toolTip;
switch (status) {
case 'successful':
case 'success':
toolTip = 'There were no failed tasks.';
break;
case 'failed':
toolTip = 'Some tasks encountered errors.';
break;
case 'canceled':
toolTip = 'Stopped by user request.';
break;
case 'new':
toolTip = 'In queue, waiting on task manager.';
break;
case 'waiting':
toolTip = 'SCM Update or Inventory Update is executing.';
break;
case 'pending':
toolTip = 'Not in queue, waiting on task manager.';
break;
case 'running':
toolTip = 'Playbook tasks executing.';
break;
}
return toolTip;
}
}]);

View File

@@ -166,7 +166,6 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi
$(tree_id).bind('move_node.jstree', function(e, data) {
// When user drags-n-drops a node, update the API
Wait('start');
var node, target, url, parent, inv_id, variables;
@@ -273,12 +272,11 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi
}
});
// When user clicks on a group
$(tree_id).bind("select_node.jstree", function(e, data){
scope.$emit('NodeSelect', data.inst.get_json()[0]);
});
// When user clicks on a group
$(tree_id).bind("select_node.jstree", function(e, data){
scope.$emit('NodeSelect', data.inst.get_json()[0]);
});
Wait('start');
LoadTreeData(params);

View File

@@ -83,19 +83,57 @@ angular.module('SearchHelper', ['RestServices', 'Utilities', 'RefreshHelper'])
scope[iterator + 'SelectShow'] = false;
scope[iterator + 'HideSearchType'] = false;
scope[iterator + 'InputHide'] = false;
scope[iterator + 'InputDisable'] = false;
scope[iterator + 'SearchType'] = 'icontains';
if (list.fields[fld].searchType && list.fields[fld].searchType == 'gtzero') {
scope[iterator + "InputHide"] = true;
}
if (list.fields[fld].searchType && (list.fields[fld].searchType == 'boolean'
else if (list.fields[fld].searchSingleValue){
// Query a specific attribute for one specific value
// searchSingleValue: true
// searchType: 'boolean|int|etc.'
// searchValue: < value to match for boolean use 'true'|'false' >
scope[iterator + "SearchType"] = list.fields[fld].searchType;
scope[iterator + 'InputDisable'] = true;
scope[iterator + "SearchValue"] = list.fields[fld].searchValue;
// For boolean type, SearchValue must be an object
if (list.fields[fld].searchType == 'boolean' && list.fields[fld].searchValue == 'true') {
scope[iterator + "SearchSelectValue"] = { value: 1 };
}
else if (list.fields[fld].searchType == 'boolean' && list.fields[fld].searchValue == 'false') {
scope[iterator + "SearchSelectValue"] = { value: 0 };
}
else if (list.fields[fld].searchType == 'boolean') {
scope[iterator + "SearchSelectValue"] = { value: list.fields[fld].searchValue };
}
}
else if (list.fields[fld].searchType && (list.fields[fld].searchType == 'boolean'
|| list.fields[fld].searchType == 'select')) {
scope[iterator + 'SelectShow'] = true;
scope[iterator + 'SearchSelectOpts'] = list.fields[fld].searchOptions;
}
if (list.fields[fld].searchType && list.fields[fld].searchType == 'int') {
else if (list.fields[fld].searchType && list.fields[fld].searchType == 'int') {
scope[iterator + 'HideSearchType'] = true;
}
scope.search(iterator);
}
scope.resetSearch = function(iterator) {
// Respdond to click of reset button
scope[iterator + "SearchValue"] = '';
scope[iterator + "SearchSelectValue"] = '';
scope[iterator + 'SelectShow'] = false;
scope[iterator + 'HideSearchType'] = false;
scope[iterator + 'InputHide'] = false;
scope[iterator + 'InputDisable'] = false;
for (fld in list.fields) {
if (list.fields[fld].searchable == undefined || list.fields[fld].searchable == true) {
scope[iterator + 'SearchFieldLabel'] = list.fields[fld].label;
scope[iterator + 'SearchField'] = fld;
break;
}
}
scope.search(iterator);
}

View File

@@ -21,21 +21,37 @@ angular.module('InventoriesListDefinition', [])
fields: {
name: {
key: true,
label: 'Name',
badgeIcon: "\{\{ 'icon-failures-' + inventory.has_active_failures \}\}",
badgePlacement: 'left',
badgeToolTip: 'Indicates if inventory contains hosts with active failures',
badgeTipPlacement: 'bottom'
label: 'Name'
},
description: {
label: 'Description'
label: 'Description',
link: true
},
hosts_with_active_failures: {
label: 'Hosts with<br>Failed Job?',
ngHref: '/#/inventories/{{ inventory.id }}/hosts{{ inventory.active_failures_params }}',
type: 'badgeCount',
"class": "{{ 'failures-' + inventory.has_active_failures }}",
//badgeIcon: "\{\{ 'icon-failures-' + inventory.has_active_failures \}\}",
//badgePlacement: 'left',
awToolTip: '# of hosts with failed jobs. Click to view hosts.',
dataPlacement: 'bottom',
searchable: false
},
organization: {
label: 'Organization',
ngBind: 'inventory.summary_fields.organization.name',
linkTo: '/organizations/{{ inventory.organization }}',
sourceModel: 'organization',
sourceField: 'name',
excludeModal: true
},
has_active_failures: {
label: 'Hosts with failed jobs?',
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
searchOnly: true
}
},

View File

@@ -25,27 +25,35 @@ angular.module('InventorySummaryDefinition', [])
noLink: true,
ngBind: "group.summary_fields.group.name",
sourceModel: 'group',
sourceField: 'name',
badges: [
{ //Active Failures
icon: "\{\{ 'icon-failures-' + group.summary_fields.group.has_active_failures \}\}",
toolTip: 'Indicates if inventory contains hosts with active failures',
toolTipPlacement: 'bottom'
},
{ //Cloud Status
icon: "\{\{ 'icon-cloud-' + group.status_class \}\}",
toolTip: 'Indicates status of inventory update process',
toolTipPlacement: 'bottom'
}]
sourceField: 'name'
},
failures: {
label: 'Active<br>Failures',
ngBind: "group.summary_fields.group.hosts_with_active_failures",
sourceModel: 'group',
sourceField: 'hosts_with_active_failures',
searchField: 'group__has_active_failures',
searchType: 'boolean',
searchOptions: [{ name: "yes", value: 1 }, { name: "no", value: 0 }]
hosts_with_active_failures: {
label: 'Hosts with<br>Job Failures?',
ngHref: '/#/inventories/{{ inventory_id }}/hosts{{ group.active_failures_params }}',
type: 'badgeCount',
"class": "{{ 'failures-' + group.has_active_failures }}",
awToolTip: '# of hosts with job failures. Click to view hosts.',
dataPlacement: 'bottom',
searchable: false,
nosort: true
},
status: {
label: 'Update<br>Status',
searchType: 'select',
badgeIcon: 'icon-cloud',
badgeToolTip: "\{\{ group.status_badge_tooltip \}\}",
badgePlacement: 'left',
badgeClass: "\{\{ 'icon-cloud-' + group.status_badge_class \}\}",
searchOptions: [
{ name: "failed", value: "failed" },
{ name: "never", value: "never updated" },
{ name: "n/a", value: "none" },
{ name: "successful", value: "successful" },
{ name: "updating", value: "updating" }]
},
last_updated: {
label: 'Last<br>Updated',
searchable: false
},
source: {
label: 'Source',
@@ -56,25 +64,20 @@ angular.module('InventorySummaryDefinition', [])
{ name: "Manual", value: "" },
{ name: "Rackspace", value: "rackspace" }]
},
last_updated: {
label: 'Last<br>Updated',
searchable: false
},
status: {
label: 'Update<br>Status',
searchType: 'select',
searchOptions: [
{ name: "failed", value: "failed" },
{ name: "never", value: "never updated" },
{ name: "n/a", value: "none" },
{ name: "successful", value: "successful" },
{ name: "updating", value: "updating" }]
has_active_failures: {
label: 'Hosts have job failures?',
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
searchOnly: true,
sourceModel: 'group',
sourceField: 'has_active_failures'
}
},
actions: {
refresh: {
awRefresh: true,
awRefresh: false,
mode: 'all'
},
help: {

View File

@@ -48,14 +48,20 @@ angular.module('JobEventsListDefinition', [])
},
status: {
label: 'Status',
icon: 'icon-circle',
showValue: true,
"class": 'job-\{\{ jobevent.status \}\}',
searchField: 'failed',
searchType: 'boolean',
searchOptions: [{ name: "success", value: 0 }, { name: "error", value: 1 }],
nosort: true,
searchable: false
searchable: false,
ngClick: "viewJobEvent(\{\{ jobevent.id \}\})",
awToolTip: "\{\{ jobevent.statusBadgeToolTip \}\}",
dataPlacement: 'top',
badgeIcon: 'icon-job-\{\{ jobevent.status \}\}',
badgePlacement: 'left',
badgeToolTip: "\{\{ jobevent.statusBadgeToolTip \}\}",
badgeTipPlacement: 'top',
badgeNgClick: "viewJobEvent(\{\{ jobevent.id \}\})"
},
event_display: {
label: 'Event',

View File

@@ -38,7 +38,7 @@ angular.module('JobHostDefinition', [])
},
fields: {
id: {
job: {
label: 'Job ID',
ngClick: "showJob(\{\{ jobhost.job \}\})",
columnShow: 'host_id !== null',
@@ -51,12 +51,18 @@ angular.module('JobHostDefinition', [])
sourceModel: 'host',
sourceField: 'name',
ngBind: 'jobhost.host_name',
ngClick:"showEvents('\{\{ jobhost.summary_fields.host.name \}\}','\{\{ jobhost.related.job \}\}')"
ngHref: "\{\{ jobhost.hostLinkTo \}\}"
},
status: {
label: 'Status',
icon: 'icon-circle',
"class": 'job-\{\{ jobhost.status \}\}',
badgeNgHref: "\{\{ jobhost.statusLinkTo \}\}",
badgeIcon: 'icon-job-\{\{ jobhost.status \}\}',
badgePlacement: 'left',
badgeToolTip: "\{\{ jobhost.statusBadgeToolTip \}\}",
badgeTipPlacement: 'top',
ngHref: "\{\{ jobhost.statusLinkTo \}\}",
awToolTip: "\{\{ jobhost.statusBadgeToolTip \}\}",
dataPlacement: 'top',
searchField: 'failed',
searchType: 'boolean',
searchOptions: [{ name: "success", value: 0 }, { name: "error", value: 1 }]

View File

@@ -37,15 +37,15 @@ angular.module('JobsListDefinition', [])
job_template: {
label: 'Job Template',
ngBind: 'job.summary_fields.job_template.name',
link: true,
ngHref: "\{\{ '/#/job_templates/?name=' + job.summary_fields.job_template.name \}\}",
sourceModel: 'job_template',
sourceField: 'name'
},
status: {
label: 'Status',
icon: 'icon-circle',
"class": 'job-\{\{ job.status \}\}',
searchType: 'select',
linkTo: "\{\{ job.statusLinkTo \}\}",
searchOptions: [
{ name: "new", value: "new" },
{ name: "waiting", value: "waiting" },
@@ -54,7 +54,14 @@ angular.module('JobsListDefinition', [])
{ name: "successful", value: "successful" },
{ name: "error", value: "error" },
{ name: "failed", value: "failed" },
{ name: "canceled", value: "canceled" } ]
{ name: "canceled", value: "canceled" } ],
badgeIcon: 'icon-job-\{\{ job.status \}\}',
badgePlacement: 'left',
badgeToolTip: "\{\{ job.statusBadgeToolTip \}\}",
badgeTipPlacement: 'top',
badgeNgHref: "\{\{ job.statusLinkTo \}\}",
awToolTip: "\{\{ job.statusBadgeToolTip \}\}",
dataPlacement: 'top'
}
},