diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index 916a01650e..cee1fd14d9 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -318,7 +318,7 @@ InventoriesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$lo function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateList, ClearScope, InventoryGroups, InventoryHosts, BuildTree, Wait, GetSyncStatusMsg, InjectHosts, HostsReload, GroupsAdd, GroupsEdit, GroupsDelete, Breadcrumbs, LoadBreadCrumbs, Empty, - Rest, ProcessErrors, InventoryUpdate, Alert, ToggleChildren, ViewUpdateStatus) + Rest, ProcessErrors, InventoryUpdate, Alert, ToggleChildren, ViewUpdateStatus, GroupsCancelUpdate) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -362,16 +362,15 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis // Add hosts view $scope.show_failures = false; InjectHosts({ scope: $scope, inventory_id: $scope.inventory_id, tree_id: $scope.selected_tree_id, group_id: $scope.selected_group_id }); - }); if ($scope.removeGroupTreeRefreshed) { $scope.removeGroupTreeRefreshed(); } - $scope.removeGroupTreeRefreshed = $scope.$on('groupTreeRefreshed', function(e, inventory_name, groups) { + $scope.removeGroupTreeRefreshed = $scope.$on('groupTreeRefreshed', function(e, inventory_name, groups, emit) { // Called after tree data is reloaded on refresh button click. // Reselect the preveiously selected group node, causing host view to refresh. - $scope.showHosts($scope.selected_tree_id, $scope.selected_group_id); + $scope.showHosts($scope.selected_tree_id, $scope.selected_group_id, false, emit); }); if ($scope.removeGroupDeleteCompleted) { @@ -382,7 +381,7 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: true }); }); - $scope.showHosts = function(tree_id, group_id, show_failures) { + $scope.showHosts = function(tree_id, group_id, show_failures, emit) { // Clicked on group if (tree_id !== null) { Wait('start'); @@ -401,7 +400,7 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis $scope.groups[i].active_class = ''; } } - HostsReload({ scope: $scope, group_id: group_id, tree_id: tree_id, inventory_id: $scope.inventory_id }); + HostsReload({ scope: $scope, group_id: group_id, tree_id: tree_id, inventory_id: $scope.inventory_id, emit: emit }); } } @@ -445,7 +444,11 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis } break; } + } } + + $scope.cancelUpdate = function(id) { + GroupsCancelUpdate({ scope: $scope, id: id }); } $scope.toggle = function(id) { @@ -453,9 +456,10 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis ToggleChildren({ scope: $scope, list: list, id: id }); } - $scope.refreshGroups = function() { + $scope.refreshGroups = function(emit) { // Refresh the tree data when refresh button cicked - BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: true }); + // Pass a string label value, if you want to attach scope.$on() to the completion of the refresh + BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: true, emit: emit }); } $scope.viewUpdateStatus = function(id) { @@ -474,6 +478,6 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis InventoriesEdit.$inject = [ '$scope', '$location', '$routeParams', '$compile', 'GenerateList', 'ClearScope', 'InventoryGroups', 'InventoryHosts', 'BuildTree', 'Wait', 'GetSyncStatusMsg', 'InjectHosts', 'HostsReload', 'GroupsAdd', 'GroupsEdit', 'GroupsDelete', 'Breadcrumbs', 'LoadBreadCrumbs', 'Empty', 'Rest', 'ProcessErrors', 'InventoryUpdate', 'Alert', 'ToggleChildren', - 'ViewUpdateStatus' + 'ViewUpdateStatus', 'GroupsCancelUpdate' ]; diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js index e8d850c7a9..87fb816939 100644 --- a/awx/ui/static/js/helpers/Groups.js +++ b/awx/ui/static/js/helpers/Groups.js @@ -449,88 +449,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' ClickNode({ selector: '#inventory-tree li[data-group-id="' + group_id + '"]' }); } - if (scope.removeCancelUpdate) { - scope.removeCancelUpdate(); - } - scope.removeCancelUpdate = scope.$on('Cancel_Update', function(e, url) { - // Cancel the project update process - Rest.setUrl(url) - Rest.post() - .success( function(data, status, headers, config) { - Alert('SCM Update Cancel', 'Your request to cancel the update was submitted to the task maanger.', 'alert-info'); - scope.refresh(); - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST status: ' + status }); - }); - }); - - if (scope.removeCheckCancel) { - scope.removeCheckCancel(); - } - scope.removeCheckCancel = scope.$on('Check_Cancel', function(e, last_update, current_update) { - // Check that we 'can' cancel the update - var url = (current_update) ? current_update : last_update; - url += 'cancel/'; - Rest.setUrl(url); - Rest.get() - .success( function(data, status, headers, config) { - if (data.can_cancel) { - scope.$emit('Cancel_Update', url); - } - else { - Alert('Cancel Not Allowed', 'Either you do not have access or the Inventory update process completed. Click the Refresh button to' + - ' view the latest status.', 'alert-info'); - } - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Call to ' + url + ' failed. GET status: ' + status }); - }); - }); - - scope.cancelUpdate = function(id, name) { - // Cancel the update process - var group; - var found = false; - for (var i=0; i < scope.groups.length; i++) { - if (scope.groups[i].id == id) { - group = scope.groups[i]; - found = true; - break; - } - } - if (group.summary_fields.inventory_source.source !== '' && - group.summary_fields.inventory_source.source !== null) { - // the group has a source - if (group.summary_fields.inventory_source.status == 'updating' || - group.summary_fields.inventory_source.status == 'pending') { - // there is an update currently running - Rest.setUrl(group.related.inventory_source); - Rest.get() - .success( function(data, status, headers, config) { - scope.$emit('Check_Cancel', data.related.last_update, data.related.current_update); - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Call to ' + group.related.inventory_source + ' failed. GET status: ' + status }); - }); - } - } - // The button appears disabled, so act like it is and do not respond to a click. - /* else { - Alert('Update Not Found', 'An Inventory update does not appear to be running for group: ' + group.name + '. Click the Refresh ' + - 'button to view the latet status.', 'alert-info'); - } - } - else { - Alert('Missing Configuration', 'The selected group is not configured for updates. You must first edit the group and provide external Source settings ' + - 'before attempting an update.', 'alert-info'); - }*/ - - } - + // Respond to refresh button scope.refresh = function() { /*scope.search(list.iterator, false, true); @@ -632,6 +551,84 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' } }]) + + // Cancel a pending or running inventory sync + .factory('GroupsCancelUpdate', ['Rest', 'ProcessErrors', 'Alert', 'Wait', 'Find', + function(Rest, ProcessErrors, Alert, Wait, Find) { + return function(params) { + + var scope = params.scope; + var id = params.tree_id; + + if (scope.removeCancelUpdate) { + scope.removeCancelUpdate(); + } + scope.removeCancelUpdate = scope.$on('CancelUpdate', function(e, url) { + // Cancel the update process + Rest.setUrl(url) + Rest.post() + .success( function(data, status, headers, config) { + Wait('stop'); + Alert('Inventory Sync Cancelled', 'Your request to cancel the update was submitted to the task maanger. ' + + 'Click the to check the sync status.', 'alert-info'); + }) + .error( function(data, status, headers, config) { + Wait('stop'); + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST status: ' + status }); + }); + }); + + if (scope.removeCheckCancel) { + scope.removeCheckCancel(); + } + scope.removeCheckCancel = scope.$on('CheckCancel', function(e, last_update, current_update) { + // Check that we have access to cancelling an update + var url = (current_update) ? current_update : last_update; + url += 'cancel/'; + Rest.setUrl(url); + Rest.get() + .success( function(data, status, headers, config) { + if (data.can_cancel) { + scope.$emit('CancelUpdate', url); + } + else { + Wait('stop'); + Alert('Cancel Inventory Sync', 'Either you do not have access or the sync process completed. ' + + 'Click the to view the latest status.', 'alert-info'); + } + }) + .error( function(data, status, headers, config) { + Wait('stop'); + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Call to ' + url + ' failed. GET status: ' + status }); + }); + }); + + // Cancel the update process + var group = Find({ list: scope.groups, key: 'id', val: id }); + if (group && (group.status == 'updating' || group.status == 'pending')) { + // We found the group, and there is a running update + Wait('start'); + Rest.setUrl(group.related.inventory_source); + Rest.get() + .success( function(data, status, headers, config) { + scope.$emit('CheckCancel', data.related.last_update, data.related.current_update); + }) + .error( function(data, status, headers, config) { + Wait('stop'); + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Call to ' + group.related.inventory_source + ' failed. GET status: ' + status }); + }); + } + else { + Alert('Cancel Inventory Sync', 'The sync process completed. Click the to' + + ' view the latest status.', 'alert-info'); + } + + } + }]) + .factory('GroupsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', 'Prompt', 'ProcessErrors', 'GetBasePath', 'ParseTypeChange', 'GroupsEdit', 'ClickNode', 'Wait', 'GetChoices', 'GetSourceTypeOptions', 'LookUpInit', 'BuildTree', 'SourceChange', @@ -1344,24 +1341,24 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' node = scope.groups[i]; } } - if (node.parent != 0) { - for (var i=0; i < scope.groups.length; i++) { - if (scope.groups[i].id == node.parent) { - parent = scope.groups[i]; - parent_name = scope.groups[i].name; - } - } - } - else { - parent_name = scope.inventory_name; - } + //if (node.parent != 0) { + // for (var i=0; i < scope.groups.length; i++) { + // if (scope.groups[i].id == node.parent) { + // parent = scope.groups[i]; + // parent_name = scope.groups[i].name; + // } + // } + // } + // else { + // parent_name = scope.inventory_name; + // } - if (parent) { - url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/'; - } - else { + //if (parent) { + // url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/'; + //} + //else { url = GetBasePath('inventory') + inventory_id + '/groups/'; - } + //} var action_to_take = function() { $('#prompt-modal').on('hidden.bs.modal', function(){ Wait('start'); }); $('#prompt-modal').modal('hide'); @@ -1378,10 +1375,10 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' }); }; - Prompt({ hdr: 'Delete Group', body: '
Are you sure you want to remove group ' + node.name + - ' from ' + parent_name + '?
', action: action_to_take, - 'class': 'btn-danger' }); - + Prompt({ hdr: 'Delete Group', body: 'Are you sure you want to delete group ' + node.name + '?
', + action: action_to_take, 'class': 'btn-danger' }); + + //' from ' + parent_name + ' //Force binds to work. Not working usual way. //$('#prompt-header').text('Delete Group'); //$('#prompt-body').html('Are you sure you want to remove group ' + $(obj).attr('data-name') + ' from group ' +
diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js
index 6a2905f71d..21276e3462 100644
--- a/awx/ui/static/js/helpers/Hosts.js
+++ b/awx/ui/static/js/helpers/Hosts.js
@@ -14,20 +14,31 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
])
- .factory('HostsReload', [ 'Empty', 'InventoryHosts', 'GetBasePath', 'SearchInit', 'PaginateInit',
- function(Empty, InventoryHosts, GetBasePath, SearchInit, PaginateInit) {
+ .factory('HostsReload', [ 'Empty', 'InventoryHosts', 'GetBasePath', 'SearchInit', 'PaginateInit', 'Wait',
+ function(Empty, InventoryHosts, GetBasePath, SearchInit, PaginateInit, Wait) {
return function(params) {
var scope = params.scope;
var group_id = params.group_id;
var tree_id = params.tree_id
var inventory_id = params.inventory_id;
+ var emit = params.emit;
var url = ( !Empty(group_id) ) ? GetBasePath('groups') + group_id + '/all_hosts/' :
GetBasePath('inventory') + inventory_id + '/hosts/';
scope.search_place_holder='Search ' + scope.selected_group_name;
+ if (scope.removePostRefresh) {
+ scope.removePostRefresh();
+ }
+ scope.removePostRefresh = scope.$on('PostRefresh', function(e) {
+ Wait('stop');
+ if (emit) {
+ scope.$emit(emit);
+ }
+ });
+
SearchInit({ scope: scope, set: 'hosts', list: InventoryHosts, url: url });
PaginateInit({ scope: scope, list: InventoryHosts, url: url });
scope.search(InventoryHosts.iterator);
@@ -42,10 +53,14 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H
var inventory_id = params.inventory_id;
var group_id = params.group_id;
var tree_id = params.tree_id;
+ var emit = params.emit;
+ // Inject the list html
var generator = GenerateList;
generator.inject(InventoryHosts, { scope: scope, mode: 'edit', id: 'hosts-container', breadCrumbs: false, searchSize: 'col-lg-6 col-md-6 col-sm-6' });
- HostsReload({ scope: scope, group_id: group_id, tree_id: tree_id, inventory_id: inventory_id });
+
+ // Load data
+ HostsReload({ scope: scope, group_id: group_id, tree_id: tree_id, inventory_id: inventory_id, emit: emit });
}
}])
diff --git a/awx/ui/static/js/helpers/JobSubmission.js b/awx/ui/static/js/helpers/JobSubmission.js
index 2e919407e5..6cca02c1f7 100644
--- a/awx/ui/static/js/helpers/JobSubmission.js
+++ b/awx/ui/static/js/helpers/JobSubmission.js
@@ -9,8 +9,7 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
.factory('PromptPasswords', ['CredentialForm', 'JobTemplateForm', '$compile', 'Rest', '$location', 'ProcessErrors',
'GetBasePath', 'Alert', 'Empty', 'Wait',
- function(CredentialForm, JobTemplateForm, $compile, Rest, $location, ProcessErrors, GetBasePath, Alert, Empty,
- Wait) {
+ function(CredentialForm, JobTemplateForm, $compile, Rest, $location, ProcessErrors, GetBasePath, Alert, Empty, Wait) {
return function(params) {
var scope = params.scope;
@@ -79,7 +78,6 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
Rest.setUrl(start_url);
Rest.post(pswd)
.success( function(data, status, headers, config) {
- Wait('stop');
scope.$emit('UpdateSubmitted','started');
if (form.name == 'credential') {
navigate(false);
@@ -332,6 +330,7 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
}
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) {
// 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 Refresh button.', 'alert-info');
scope.refresh();
@@ -415,29 +414,21 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential
var group_name = params.group_name;
var group_source = params.group_source;
+ if (scope.removeSubmitRefreshCompleted) {
+ scope.removeSubmitRefreshCompleted();
+ }
+ scope.removeSubmitRefreshCompleted = scope.$on('SubmitRefreshCompleted', function(e) {
+ Wait('stop');
+ Alert('Update Started', 'The request to start the inventory update process was submitted. Monitor progress of the update process ' +
+ 'by clicking the button.', 'alert-info');
+ });
+
if (scope.removeUpdateSubmitted) {
scope.removeUpdateSubmitted();
}
scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) {
if (action == 'started') {
- // Refresh the project list after update request submitted
- Alert('Update Started', 'The request to start the inventory update process was submitted. Monitor progress of the update process ' +
- 'by clicking the Refresh button.', 'alert-info');
- //var node = $('#inventory-node')
- //var selected = $('#tree-view').jstree('get_selected');
- //scope['inventorySummaryGroup'] = null;
- //selected.each(function(idx) {
- // $('#tree-view').jstree('deselect_node', $(this));
- // });
- //$('#tree-view').jstree('select_node', node);
- BuildTree({
- scope: scope,
- inventory_id: scope['inventory_id'],
- emit_on_select: 'NodeSelect',
- target_id: 'search-tree-container',
- refresh: false,
- moveable: true
- });
+ scope.refreshGroups('SubmitRefreshComplete');
}
});
diff --git a/awx/ui/static/js/lists/InventoryGroups.js b/awx/ui/static/js/lists/InventoryGroups.js
index d86e6a6dd9..2b06eb7c42 100644
--- a/awx/ui/static/js/lists/InventoryGroups.js
+++ b/awx/ui/static/js/lists/InventoryGroups.js
@@ -75,14 +75,11 @@ angular.module('InventoryGroupsDefinition', [])
create: {
mode: 'all',
ngClick: "createGroup()",
- ngHide: "groupCreateHide",
- ngDisabled: 'grpBtnDisabled',
+ ngHide: 'selected_tree_id == 1', //disable when 'All Hosts' selected
awToolTip: "Create a new group"
},
properties: {
mode: 'all',
- ngHide: "groupEditHide",
- ngDisabled: 'grpBtnDisabled',
awToolTip: "Edit inventory properties"
},
refresh: {
@@ -96,7 +93,7 @@ angular.module('InventoryGroupsDefinition', [])
mode: 'all',
ngShow: "user_is_superuser"
},
- help: {
+ help: {
mode: 'all',
awToolTip:
//"
" +
@@ -126,22 +123,25 @@ angular.module('InventoryGroupsDefinition', [])
},
group_update: {
//label: 'Sync',
+ mode: 'all',
ngClick: 'updateGroup(\{\{ group.id \}\})',
awToolTip: "\{\{ group.launch_tooltip \}\}",
- ngShow: "group.id > 1", // hide for all hosts
+ ngShow: "group.id > 1 && (group.status !== 'running' && group.status !== 'pending' && group.status !== 'updating')",
ngClass: "group.launch_class",
dataPlacement: "top"
},
cancel: {
//label: 'Cancel',
- ngClick: "cancelUpdate(\{\{ group.id \}\}, '\{\{ group.name \}\}')",
+ mode: 'all',
+ ngClick: "cancelUpdate(\{\{ group.id \}\})",
awToolTip: "Cancel sync process",
- ngClass: "group.cancel_class",
- ngShow: "group.id > 1 && (group.status == 'running' || group.status == 'pending')",
+ 'class': 'red-txt',
+ ngShow: "group.id > 1 && (group.status == 'running' || group.status == 'pending' || group.status == 'updating')",
dataPlacement: "top"
},
edit: {
//label: 'Edit',
+ mode: 'all',
ngClick: "editGroup(\{\{ group.group_id \}\})",
awToolTip: 'Edit group',
ngShow: "group.id > 1", // hide for all hosts
@@ -149,6 +149,7 @@ angular.module('InventoryGroupsDefinition', [])
},
"delete": {
//label: 'Delete',
+ mode: 'all',
ngClick: "deleteGroup(\{\{ group.id \}\})",
awToolTip: 'Delete group',
ngShow: "group.id != 1", // hide for all hosts
diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less
index 1e12f4ea1b..116765d940 100644
--- a/awx/ui/static/less/ansible-ui.less
+++ b/awx/ui/static/less/ansible-ui.less
@@ -55,6 +55,7 @@ body.modal-open {
.no-bullets { list-style: none; }
.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; }
@@ -1040,6 +1041,8 @@ input[type="checkbox"].checkbox-no-label {
.active-row {
background-color: @white;
+ border-bottom: 1px solid #ddd;
+ border-right: 1px solid #ddd;
}
.node-toggle {
diff --git a/awx/ui/static/lib/ansible/InventoryTree.js b/awx/ui/static/lib/ansible/InventoryTree.js
index a8b798a216..9a07418779 100644
--- a/awx/ui/static/lib/ansible/InventoryTree.js
+++ b/awx/ui/static/lib/ansible/InventoryTree.js
@@ -178,6 +178,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
var inventory_id = params.inventory_id;
var scope = params.scope;
var refresh = params.refresh;
+ var emit = params.emit;
//var selected_id = params.
@@ -249,13 +250,13 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper'])
Rest.get()
.success( function(data, status, headers, config) {
buildGroups(data, 0, 0);
- console.log(groups);
+ //console.log(groups);
if (refresh) {
scope.groups = groups;
- scope.$emit('groupTreeRefreshed');
+ scope.$emit('groupTreeRefreshed', inventory_name, groups, emit);
}
else {
- scope.$emit('groupTreeLoaded', inventory_name, groups);
+ scope.$emit('groupTreeLoaded', inventory_name, groups, emit);
}
})
.error( function(data, status, headers, config) {
diff --git a/awx/ui/static/lib/ansible/Utilities.js b/awx/ui/static/lib/ansible/Utilities.js
index 7a69eb0085..0a8c8d91b7 100644
--- a/awx/ui/static/lib/ansible/Utilities.js
+++ b/awx/ui/static/lib/ansible/Utilities.js
@@ -463,8 +463,37 @@ angular.module('Utilities',['RestServices', 'Utilities'])
});
}
}])
+
+ /*
+ * Search an array of objects, returning the matchting object or null
+ *
+ * Find({ list: [], key: "key", val: