diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js
index 2a02013c9d..93e7668c5b 100644
--- a/awx/ui/static/js/app.js
+++ b/awx/ui/static/js/app.js
@@ -32,6 +32,7 @@ angular.module('ansible', [
'InventoryHelper',
'InventoryHostsFormDefinition',
'InventoryGroupsFormDefinition',
+ 'InventorySummaryDefinition',
'AWFilters',
'HostFormDefinition',
'HostListDefinition',
diff --git a/awx/ui/static/js/controllers/Groups.js b/awx/ui/static/js/controllers/Groups.js
index d01b884a9b..abb803b8df 100644
--- a/awx/ui/static/js/controllers/Groups.js
+++ b/awx/ui/static/js/controllers/Groups.js
@@ -10,7 +10,7 @@
function InventoryGroups ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryGroupsForm,
GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope, Prompt,
TreeInit, GetBasePath, GroupsList, GroupsAdd, GroupsEdit, LoadInventory,
- GroupsDelete, RefreshGroupName, EditInventory, SetShowGroupHelp)
+ GroupsDelete, RefreshGroupName, EditInventory, SetShowGroupHelp, InventoryStatus)
{
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
//scope.
@@ -155,7 +155,6 @@ function InventoryGroups ($scope, $rootScope, $compile, $location, $log, $routeP
$('input:first').focus();
}
else if (type == 'inventory') {
- $('#tree-form').hide().empty();
url = node.attr('hosts');
scope.groupAddHide = true;
scope.groupCreateHide = false;
@@ -164,6 +163,8 @@ function InventoryGroups ($scope, $rootScope, $compile, $location, $log, $routeP
scope.groupDeleteHide = true;
scope.createButtonShow = false;
scope.group_id = null;
+ InventoryStatus();
+ $('#tree-form').show();
}
if (!scope.$$phase) {
@@ -221,6 +222,6 @@ function InventoryGroups ($scope, $rootScope, $compile, $location, $log, $routeP
InventoryGroups.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'InventoryGroupsForm',
'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller', 'ClearScope', 'Prompt',
'TreeInit', 'GetBasePath', 'GroupsList', 'GroupsAdd', 'GroupsEdit', 'LoadInventory',
- 'GroupsDelete', 'RefreshGroupName', 'EditInventory', 'SetShowGroupHelp'
+ 'GroupsDelete', 'RefreshGroupName', 'EditInventory', 'SetShowGroupHelp', 'InventoryStatus'
];
\ No newline at end of file
diff --git a/awx/ui/static/js/controllers/Organizations.js b/awx/ui/static/js/controllers/Organizations.js
index 460c0e0e63..f325f8161f 100644
--- a/awx/ui/static/js/controllers/Organizations.js
+++ b/awx/ui/static/js/controllers/Organizations.js
@@ -91,7 +91,7 @@ function OrganizationsAdd ($scope, $rootScope, $compile, $location, $log, $route
// Save
scope.formSave = function() {
- form.clearApiErrors();s
+ form.clearApiErrors();
var url = GetBasePath(base);
url += (base != 'organizations') ? $routeParams['project_id'] + '/organizations/' : '';
Rest.setUrl(url);
diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js
index d84f69bdb9..f56e45d06d 100644
--- a/awx/ui/static/js/helpers/Groups.js
+++ b/awx/ui/static/js/helpers/Groups.js
@@ -232,7 +232,6 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
}
}])
-
.factory('GroupsEdit', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm',
'Prompt', 'ProcessErrors', 'GetBasePath', 'RefreshGroupName', 'ParseTypeChange', 'getSourceTypeOptions',
function($rootScope, $location, $log, $routeParams, Rest, Alert, GroupForm, GenerateForm, Prompt, ProcessErrors,
@@ -488,6 +487,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
{ hdr: 'Error!', msg: 'Failed to update group varaibles. PUT status: ' + status });
});
}
+ RefreshGroupName(scope['selectedNode'], data.name, data.description);
scope.$emit('formSaveSuccess', data.id);
})
.error( function(data, status, headers, config) {
diff --git a/awx/ui/static/js/helpers/inventory.js b/awx/ui/static/js/helpers/inventory.js
index 2d14a08799..9682f9a80e 100644
--- a/awx/ui/static/js/helpers/inventory.js
+++ b/awx/ui/static/js/helpers/inventory.js
@@ -11,7 +11,7 @@
angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationListDefinition',
'SearchHelper', 'PaginateHelper', 'ListGenerator', 'AuthService',
'InventoryHelper', 'RelatedSearchHelper', 'RelatedPaginateHelper',
- 'InventoryFormDefinition', 'ParseHelper'
+ 'InventoryFormDefinition', 'ParseHelper', 'InventorySummaryDefinition'
])
.factory('LoadTreeData', ['Alert', 'Rest', 'Authorization', '$http', 'Wait', 'SortNodes',
@@ -160,6 +160,7 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi
$(tree_id).bind("loaded.jstree", function () {
scope['treeLoading'] = false;
Wait('stop');
+ $(tree_id).prepend('
Group Selector
');
scope.$emit('treeLoaded');
});
@@ -666,5 +667,65 @@ angular.module('InventoryHelper', [ 'RestServices', 'Utilities', 'OrganizationLi
}
return newData;
}
+ }])
+
+ .factory('InventoryStatus', [ 'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'InventorySummary', 'GenerateList',
+ function(Rest, Aler, ProcessErrors, GetBasePath, FormatDate, InventorySummary, GenerateList) {
+ return function(params) {
+ //Build a summary of a given inventory
+
+ $('#tree-form').hide().empty();
+ var view = GenerateList;
+ var scope = view.inject(InventorySummary, { mode: 'summary', id: 'tree-form', breadCrumbs: false });
+ scope['groups'] = [];
+
+ function checkSource(url) {
+ Rest.setUrl(url);
+ Rest.get()
+ .success( function(data, status, headers, config) {
+ //console.log(data);
+ var last_update = (data.last_updated == null) ? 'n/a' : FormatDate(new Date(data.last_updated));
+ var source = 'Manual';
+ switch(data.source) {
+ case 'file':
+ source = 'File';
+ break;
+ case 'ec2':
+ source = 'Amazon EC2';
+ break;
+ case 'rackspace':
+ source = 'Rackspace';
+ break;
+ }
+ scope['groups'].push({
+ name: data.summary_fields.group.name,
+ description: data.summary_fields.group.description,
+ failures: data.summary_fields.group.hosts_with_active_failures,
+ source: source,
+ last_update: last_update,
+ status: data.status
+ });
+ })
+ .error( function(data, status, headers, config) {
+ ProcessErrors(scope, data, status, null,
+ { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST status: ' + status });
+ });
+ }
+
+ var url = GetBasePath('inventory') + scope['inventory_id'] + '/groups/';
+ Rest.setUrl(url);
+ Rest.get()
+ .success( function(data, status, headers, config) {
+ for (var i=0; i < data.results.length; i++) {
+ if (data.results[i].related.inventory_source) {
+ checkSource(data.results[i].related.inventory_source);
+ }
+ }
+ })
+ .error( function(data, status, headers, config) {
+ ProcessErrors(scope, data, status, null,
+ { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST status: ' + status });
+ });
+ }
}]);
diff --git a/awx/ui/static/js/lists/InventorySummary.js b/awx/ui/static/js/lists/InventorySummary.js
new file mode 100644
index 0000000000..905320fa56
--- /dev/null
+++ b/awx/ui/static/js/lists/InventorySummary.js
@@ -0,0 +1,44 @@
+/*********************************************
+ * Copyright (c) 2013 AnsibleWorks, Inc.
+ *
+ * InventorySummary.js
+ *
+ * Summary of groups contained within an inventory
+ *
+ */
+angular.module('InventorySummaryDefinition', [])
+ .value(
+ 'InventorySummary', {
+
+ name: 'groups',
+ iterator: 'group',
+ editTitle: 'Inventory Summary',
+ index: false,
+ hover: true,
+
+ fields: {
+ name: {
+ key: true,
+ label: 'Name'
+ },
+ failures: {
+ label: 'Hosts Failures'
+ },
+ source: {
+ label: 'Source'
+ },
+ last_update: {
+ label: 'Last Update'
+ },
+ status: {
+ label: 'Update Status'
+ }
+ },
+
+ actions: {
+ },
+
+ fieldActions: {
+ }
+ });
+
\ No newline at end of file
diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less
index a01307505f..1b6d5d5cba 100644
--- a/awx/ui/static/less/ansible-ui.less
+++ b/awx/ui/static/less/ansible-ui.less
@@ -771,8 +771,15 @@ select.field-mini-height {
border-radius: 6px;
background-color: #e3e3e3;
padding-top: 10px;
- margin-top: 10px;
+ padding-left: 10px;
+ padding-bottom: 10px;
min-height: 100px;
+
+ .title {
+ color: #888;
+ padding-bottom: 5px;
+ margin-left: 5px;
+ }
}
/*
@@ -792,13 +799,13 @@ select.field-mini-height {
#tree-form {
display: none;
padding: 15px 10px 0 10px;
- margin-top: 10px;
+ margin-top: 5px;
border: 1px solid #e3e3e3;
background-color: #e3e3e3;
border-radius: 6px;
.form-title {
- color: #999;
+ color: #888;
padding-left: 10px;
}
@@ -1102,7 +1109,7 @@ tr td button i {
#tree-view {
margin-left: 10px;
- margin-top: 10px;
+ margin-top: 5px;
}
.label-text {
diff --git a/awx/ui/static/lib/ansible/form-generator.js b/awx/ui/static/lib/ansible/form-generator.js
index 8d71c078df..0cc4c70e50 100644
--- a/awx/ui/static/lib/ansible/form-generator.js
+++ b/awx/ui/static/lib/ansible/form-generator.js
@@ -1230,7 +1230,9 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies'])
html += "\n";
html += "
\n";
html += "\n";
- html += "
\n";
+ html += "
\n";
html += "
\n";
html += "
\n";
html += "\n";
diff --git a/awx/ui/static/lib/ansible/list-generator.js b/awx/ui/static/lib/ansible/list-generator.js
index c61fbac8da..ab88f902ee 100644
--- a/awx/ui/static/lib/ansible/list-generator.js
+++ b/awx/ui/static/lib/ansible/list-generator.js
@@ -169,56 +169,59 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
if (options.mode != 'lookup' && (list.well == undefined || list.well == 'true')) {
html += "\n";
}
-
- if (options.mode == 'lookup' || options.id != undefined) {
- html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-8' });
- }
- else {
- html += SearchWidget({ iterator: list.iterator, template: list, mini: true });
- }
- if (options.mode != 'lookup') {
- //actions
- var base = $location.path().replace(/^\//,'').split('/')[0];
- html += "
\n";
- for (action in list.actions) {
- if (list.actions[action].mode == 'all' || list.actions[action].mode == options.mode) {
- if ( (list.actions[action].basePaths == undefined) ||
- (list.actions[action].basePaths && list.actions[action].basePaths.indexOf(base) > -1) ) {
- html += this.button(list.actions[action], action);
- }
- }
- }
+ if (options.mode !== 'summary') {
- if (list.name == 'inventories' && options.mode !== 'select') {
- html += "
\n";
+ if ( options.mode == 'lookup' || options.id != undefined ) {
+ html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-8' });
}
-
- //select instructions
- if (options.mode == 'select' && list.selectInstructions) {
- var btn = {
- awPopOver: list.selectInstructions,
- dataPlacement: 'left',
- dataContainer: 'body',
- icon: "icon-question-sign",
- 'class': 'btn-sm btn-help btn-info',
- awToolTip: 'Click for help',
- dataTitle: 'Help',
- iconSize: 'large'
- };
- html += this.button(btn, 'select');
+ else {
+ html += SearchWidget({ iterator: list.iterator, template: list, mini: true });
}
- }
- else {
- html += "
\n";
- }
- html += "
\n";
- html += "
\n";
-
+ if (options.mode != 'lookup') {
+ //actions
+ var base = $location.path().replace(/^\//,'').split('/')[0];
+ html += "\n";
+ for (action in list.actions) {
+ if (list.actions[action].mode == 'all' || list.actions[action].mode == options.mode) {
+ if ( (list.actions[action].basePaths == undefined) ||
+ (list.actions[action].basePaths && list.actions[action].basePaths.indexOf(base) > -1) ) {
+ html += this.button(list.actions[action], action);
+ }
+ }
+ }
+
+ if (list.name == 'inventories' && options.mode !== 'select') {
+ html += "
\n";
+ }
+
+ //select instructions
+ if (options.mode == 'select' && list.selectInstructions) {
+ var btn = {
+ awPopOver: list.selectInstructions,
+ dataPlacement: 'left',
+ dataContainer: 'body',
+ icon: "icon-question-sign",
+ 'class': 'btn-sm btn-help btn-info',
+ awToolTip: 'Click for help',
+ dataTitle: 'Help',
+ iconSize: 'large'
+ };
+ html += this.button(btn, 'select');
+ }
+ }
+ else {
+ html += "
\n";
+ }
+
+ html += "
\n";
+ html += "\n";
+ }
+
// table header row
html += "