diff --git a/awx/ui/client/src/inventories/inventories.block.less b/awx/ui/client/src/inventories/inventories.block.less
new file mode 100644
index 0000000000..91727c2e7d
--- /dev/null
+++ b/awx/ui/client/src/inventories/inventories.block.less
@@ -0,0 +1,3 @@
+.Inventories-hostStatus {
+ margin-left: 5px;
+}
diff --git a/awx/ui/client/src/inventories/inventory.list.js b/awx/ui/client/src/inventories/inventory.list.js
index 201b9b9138..7535a31cce 100644
--- a/awx/ui/client/src/inventories/inventory.list.js
+++ b/awx/ui/client/src/inventories/inventory.list.js
@@ -22,16 +22,22 @@ export default ['i18n', function(i18n) {
fields: {
status: {
label: '',
- columnClass: 'col-md-1 col-sm-2 col-xs-2 List-staticColumn--smallStatus',
+ columnClass: 'List-staticColumn--mediumStatus',
nosort: true,
ngClick: "null",
iconOnly: true,
excludeModal: true,
icons: [{
+ icon: "{{ 'icon-cloud-' + inventory.syncStatus }}",
+ awToolTip: "{{ inventory.syncTip }}",
+ awTipPlacement: "right",
+ ngClick: "showSourceSummary($event, inventory.id)",
+ ngClass: "inventory.launch_class"
+ },{
icon: "{{ 'icon-job-' + inventory.hostsStatus }}",
awToolTip: false,
ngClick: "showHostSummary($event, inventory.id)",
- ngClass: ""
+ ngClass: "inventory.host_status_class"
}]
},
name: {
@@ -83,6 +89,13 @@ export default ['i18n', function(i18n) {
columnClass: 'col-md-2 col-sm-4 col-xs-4',
+ inventory_update: {
+ mode: 'all',
+ ngClick: 'syncInventory(inventory)',
+ awToolTip: i18n._('Sync all inventory sources'),
+ ngShow: "inventory.kind === ''",
+ dataPlacement: "top",
+ },
edit: {
label: i18n._('Edit'),
ngClick: 'editInventory(inventory.id)',
diff --git a/awx/ui/client/src/inventories/list/inventory-list.controller.js b/awx/ui/client/src/inventories/list/inventory-list.controller.js
index 0063b94fa4..baf410473e 100644
--- a/awx/ui/client/src/inventories/list/inventory-list.controller.js
+++ b/awx/ui/client/src/inventories/list/inventory-list.controller.js
@@ -12,7 +12,8 @@
function InventoriesList($scope, $rootScope, $location,
$compile, $filter, Rest, InventoryList, Prompt,
- ProcessErrors, GetBasePath, Wait, Find, Empty, $state, rbacUiControlService, Dataset) {
+ ProcessErrors, GetBasePath, Wait, Find, Empty, $state,
+ rbacUiControlService, Dataset, InventoryUpdate) {
let list = InventoryList,
defaultUrl = GetBasePath('inventory');
@@ -37,15 +38,16 @@ function InventoriesList($scope, $rootScope, $location,
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
$rootScope.flashMessage = null;
-
}
function buildStatusIndicators(inventory){
inventory.launch_class = "";
+ inventory.host_status_class = "Inventories-hostStatus";
+
if (inventory.has_inventory_sources) {
if (inventory.inventory_sources_with_failures > 0) {
inventory.syncStatus = 'error';
- inventory.syncTip = inventory.inventory_sources_with_failures + ' groups with sync failures. Click for details';
+ inventory.syncTip = inventory.inventory_sources_with_failures + ' sources with sync failures. Click for details';
}
else {
inventory.syncStatus = 'successful';
@@ -57,6 +59,7 @@ function InventoriesList($scope, $rootScope, $location,
inventory.syncTip = 'Not configured for inventory sync.';
inventory.launch_class = "btn-disabled";
}
+
if (inventory.has_active_failures) {
inventory.hostsStatus = 'error';
inventory.hostsTip = inventory.hosts_with_active_failures + ' hosts with failures. Click for details.';
@@ -139,10 +142,10 @@ function InventoriesList($scope, $rootScope, $location,
attachElem(event, html, title);
});
- if ($scope.removeGroupSummaryReady) {
- $scope.removeGroupSummaryReady();
+ if ($scope.removeSourceSummaryReady) {
+ $scope.removeSourceSummaryReady();
}
- $scope.removeGroupSummaryReady = $scope.$on('GroupSummaryReady', function(e, event, inventory, data) {
+ $scope.removeSourceSummaryReady = $scope.$on('SourceSummaryReady', function(e, event, inventory, data) {
var html, title;
Wait('stop');
@@ -153,7 +156,7 @@ function InventoriesList($scope, $rootScope, $location,
html += "
";
html += "| Status | ";
html += "Last Sync | ";
- html += "Group | ";
+ html += "Source | ";
html += "
";
html += "\n";
html += "\n";
@@ -162,14 +165,14 @@ function InventoriesList($scope, $rootScope, $location,
html += "";
html += ` | `;
html += "" + ($filter('longDate')(row.last_updated)).replace(/ /,' ') + " | ";
- html += "" + $filter('sanitize')(ellipsis(row.summary_fields.group.name)) + " | ";
+ html += "" + $filter('sanitize')(ellipsis(row.name)) + " | ";
html += "
\n";
}
else {
html += "";
html += " | ";
html += "NA | ";
- html += "" + $filter('sanitize')(ellipsis(row.summary_fields.group.name)) + " | ";
+ html += "" + $filter('sanitize')(ellipsis(row.name)) + " | ";
html += "
\n";
}
});
@@ -179,7 +182,7 @@ function InventoriesList($scope, $rootScope, $location,
attachElem(event, html, title);
});
- $scope.showGroupSummary = function(event, id) {
+ $scope.showSourceSummary = function(event, id) {
try{
var elem = $(event.target).parent();
// if the popover is visible already, then exit the function here
@@ -193,10 +196,10 @@ function InventoriesList($scope, $rootScope, $location,
inventory = Find({ list: $scope.inventories, key: 'id', val: id });
if (inventory.syncStatus !== 'na') {
Wait('start');
- Rest.setUrl(inventory.related.inventory_sources + '?or__source=ec2&or__source=rax&order_by=-last_job_run&page_size=5');
+ Rest.setUrl(inventory.related.inventory_sources + '?order_by=-last_job_run&page_size=5');
Rest.get()
.success(function(data) {
- $scope.$emit('GroupSummaryReady', event, inventory, data);
+ $scope.$emit('SourceSummaryReady', event, inventory, data);
})
.error(function(data, status) {
ProcessErrors( $scope, data, status, null, { hdr: 'Error!',
@@ -300,9 +303,18 @@ function InventoriesList($scope, $rootScope, $location,
$scope.viewFailedJobs = function (id) {
$location.url('/jobs/?inventory__int=' + id + '&status=failed');
};
+
+ $scope.syncInventory = function(inventory) {
+ InventoryUpdate({
+ scope: $scope,
+ url: inventory.related.update_inventory_sources,
+ updateAllSources: true
+ });
+ };
}
export default ['$scope', '$rootScope', '$location',
- '$compile', '$filter', 'Rest', 'InventoryList',
- 'Prompt', 'ProcessErrors', 'GetBasePath', 'Wait', 'Find', 'Empty', '$state', 'rbacUiControlService', 'Dataset', InventoriesList
+ '$compile', '$filter', 'Rest', 'InventoryList', 'Prompt',
+ 'ProcessErrors', 'GetBasePath', 'Wait', 'Find', 'Empty',
+ '$state', 'rbacUiControlService', 'Dataset', 'InventoryUpdate', InventoriesList
];
diff --git a/awx/ui/client/src/inventories/sources/list/sources-list.controller.js b/awx/ui/client/src/inventories/sources/list/sources-list.controller.js
index 0e09051f16..09d3c3e4d1 100644
--- a/awx/ui/client/src/inventories/sources/list/sources-list.controller.js
+++ b/awx/ui/client/src/inventories/sources/list/sources-list.controller.js
@@ -8,12 +8,12 @@
'InventoryUpdate', 'GroupManageService', 'CancelSourceUpdate',
'ViewUpdateStatus', 'rbacUiControlService', 'GetBasePath',
'GetSyncStatusMsg', 'Dataset', 'Find', 'QuerySet',
- 'inventoryData', '$filter', 'Prompt', 'Wait', 'SourcesService',
+ 'inventoryData', '$filter', 'Prompt', 'Wait', 'SourcesService', 'inventorySourceOptions',
function($scope, $rootScope, $state, $stateParams, SourcesListDefinition,
InventoryUpdate, GroupManageService, CancelSourceUpdate,
ViewUpdateStatus, rbacUiControlService, GetBasePath, GetSyncStatusMsg,
Dataset, Find, qs, inventoryData, $filter, Prompt,
- Wait, SourcesService){
+ Wait, SourcesService, inventorySourceOptions){
let list = SourcesListDefinition;
@@ -36,6 +36,7 @@
$scope.inventory_id = $stateParams.inventory_id;
_.forEach($scope[list.name], buildStatusIndicators);
+ optionsRequestDataProcessing();
$scope.$on(`ws-jobs`, function(e, data){
var inventory_source = Find({ list: $scope.inventory_sources, key: 'id', val: data.inventory_source_id });
@@ -52,6 +53,7 @@
$scope[`${list.iterator}_dataset`] = searchResponse.data;
$scope[list.name] = $scope[`${list.iterator}_dataset`].results;
_.forEach($scope[list.name], buildStatusIndicators);
+ optionsRequestDataProcessing();
});
} else {
var status = GetSyncStatusMsg({
@@ -64,6 +66,28 @@
inventory_source.launch_class = status.launch_class;
}
});
+
+ $scope.$watchCollection(`${$scope.list.name}`, function() {
+ _.forEach($scope[list.name], buildStatusIndicators);
+ optionsRequestDataProcessing();
+ });
+ }
+
+ function optionsRequestDataProcessing(){
+ if ($scope[list.name] !== undefined) {
+ $scope[list.name].forEach(function(item, item_idx) {
+ var itm = $scope[list.name][item_idx];
+
+ // Set the item source label
+ if (list.fields.source && inventorySourceOptions && inventorySourceOptions.hasOwnProperty('source')) {
+ inventorySourceOptions.source.choices.forEach(function(choice) {
+ if (choice[0] === item.source) {
+ itm.source_label = choice[1];
+ }
+ });
+ }
+ });
+ }
}
function buildStatusIndicators(inventory_source){
diff --git a/awx/ui/client/src/inventories/sources/list/sources-list.route.js b/awx/ui/client/src/inventories/sources/list/sources-list.route.js
index 73e7a1ffc9..b00085b824 100644
--- a/awx/ui/client/src/inventories/sources/list/sources-list.route.js
+++ b/awx/ui/client/src/inventories/sources/list/sources-list.route.js
@@ -42,6 +42,9 @@ export default {
}
},
resolve: {
+ inventorySourceOptions: ['SourcesService', (SourcesService) => {
+ return SourcesService.options().then(res => res.data.actions.GET);
+ }],
Dataset: ['SourcesListDefinition', 'QuerySet', '$stateParams', 'GetBasePath', '$interpolate', '$rootScope',
(list, qs, $stateParams, GetBasePath, $interpolate, $rootScope) => {
// allow related list definitions to use interpolated $rootScope / $stateParams in basePath field
diff --git a/awx/ui/client/src/inventories/sources/sources.list.js b/awx/ui/client/src/inventories/sources/sources.list.js
index 38288bf04e..6e88931c65 100644
--- a/awx/ui/client/src/inventories/sources/sources.list.js
+++ b/awx/ui/client/src/inventories/sources/sources.list.js
@@ -33,8 +33,13 @@ export default {
label: 'Sources',
key: true,
ngClick: "editSource(inventory_source.id)",
- columnClass: 'col-lg-6 col-md-6 col-sm-6 col-xs-6',
+ columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
class: 'InventoryManage-breakWord',
+ },
+ source: {
+ label: 'Type',
+ ngBind: 'inventory_source.source_label',
+ columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-4'
}
},
@@ -60,7 +65,7 @@ export default {
fieldActions: {
- columnClass: 'col-lg-6 col-md-6 col-sm-6 col-xs-6 text-right',
+ columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-4 text-right',
group_update: {
//label: 'Sync',
diff --git a/awx/ui/client/src/inventories/sources/sources.service.js b/awx/ui/client/src/inventories/sources/sources.service.js
index a913951806..7544de056f 100644
--- a/awx/ui/client/src/inventories/sources/sources.service.js
+++ b/awx/ui/client/src/inventories/sources/sources.service.js
@@ -54,6 +54,13 @@ export default
.error(this.error.bind(this))
.finally(Wait('stop'));
},
+ options: function(){
+ this.url = GetBasePath('inventory_sources');
+ Rest.setUrl(this.url);
+ return Rest.options()
+ .success(this.success.bind(this))
+ .error(this.error.bind(this));
+ },
getCredential: function(id){
Wait('start');
this.url = GetBasePath('credentials') + id;
diff --git a/awx/ui/client/src/job-submission/job-submission-factories/inventory-update.factory.js b/awx/ui/client/src/job-submission/job-submission-factories/inventory-update.factory.js
index 80e13c09d6..cb4c0bf983 100644
--- a/awx/ui/client/src/job-submission/job-submission-factories/inventory-update.factory.js
+++ b/awx/ui/client/src/job-submission/job-submission-factories/inventory-update.factory.js
@@ -43,19 +43,33 @@ export default
Rest.setUrl(url);
Rest.get()
.success(function (data) {
- inventory_source = data;
- if (data.can_update) {
- if (data.passwords_needed_to_update) {
- Wait('stop');
- scope.$emit('PromptForPasswords');
- }
- else {
+ if(params.updateAllSources) {
+ let userCanUpdateAllSources = true;
+ _.forEach(data, function(inventory_source){
+ if (!inventory_source.can_update) {
+ userCanUpdateAllSources = false;
+ }
+ });
+
+ if(userCanUpdateAllSources) {
scope.$emit('StartTheUpdate', {});
}
- } else {
- Wait('stop');
- Alert('Permission Denied', 'You do not have access to run the inventory sync. Please contact your system administrator.',
- 'alert-danger');
+ }
+ else {
+ inventory_source = data;
+ if (data.can_update) {
+ if (data.passwords_needed_to_update) {
+ Wait('stop');
+ scope.$emit('PromptForPasswords');
+ }
+ else {
+ scope.$emit('StartTheUpdate', {});
+ }
+ } else {
+ Wait('stop');
+ Alert('Permission Denied', 'You do not have access to run the inventory sync. Please contact your system administrator.',
+ 'alert-danger');
+ }
}
})
.error(function (data, status) {
diff --git a/awx/ui/client/src/shared/generator-helpers.js b/awx/ui/client/src/shared/generator-helpers.js
index d6c8f03c6d..eddb7214e3 100644
--- a/awx/ui/client/src/shared/generator-helpers.js
+++ b/awx/ui/client/src/shared/generator-helpers.js
@@ -134,6 +134,9 @@ angular.module('GeneratorHelpers', [systemStatus.name])
case 'group_update':
icon = 'fa-refresh';
break;
+ case 'inventory_update':
+ icon = 'fa-refresh';
+ break;
case 'scm_update':
icon = 'fa-cloud-download';
break;