1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-30 22:21:13 +03:00

Inventory refactor: inventory sync icon turns into red cancel icon upon job submission. cancel sync now works. Refresh process emits on completion. Created Find() utility to search an array of objects.

This commit is contained in:
Chris Houseknecht 2014-01-09 22:45:27 +00:00
parent d3ced7896f
commit 0d2417e4ae
9 changed files with 190 additions and 148 deletions

View File

@ -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'
];

View File

@ -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 <em>Refresh</em> 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: <em>' + group.name + '</em>. Click the <em>Refresh</em> ' +
'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 <i class="fa fa-refresh fa-lg"></i> 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 <i class="fa fa-refresh fa-lg"></i> 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 <i class="fa fa-refresh fa-lg"></i> 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: '<p>Are you sure you want to remove group <em>' + node.name +
'</em> from <em>' + parent_name + '</em>?</p>', action: action_to_take,
'class': 'btn-danger' });
Prompt({ hdr: 'Delete Group', body: '<p>Are you sure you want to delete group <em>' + node.name + '?</p>',
action: action_to_take, 'class': 'btn-danger' });
//'</em> from <em>' + parent_name + '</em>
//Force binds to work. Not working usual way.
//$('#prompt-header').text('Delete Group');
//$('#prompt-body').html('<p>Are you sure you want to remove group <em>' + $(obj).attr('data-name') + '</em> from group <em>' +

View File

@ -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 });
}
}])

View File

@ -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 <em>Refresh</em> 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 <i class="fa fa-refresh fa-lg"></i> 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 <em>Refresh</em> 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');
}
});

View File

@ -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:
//"<div style=\"text-align:left;\"><img src=\"/static/img/cow.png\" style=\"width:50px; height:56px; float:left; padding-right:5px;\">" +
@ -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

View File

@ -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 {

View File

@ -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) {

View File

@ -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: <key value> });
*/
.factory('Find', [ function(){
return function(params) {
var list = params.list;
var key = params.key;
var val = params.val;
var found = false;
if (typeof list == 'object' && Array.isArray(list)) {
for (var i=0; i < params.list.length; i++) {
if (list[i][key] == val) {
found = true;
break;
}
}
return (found) ? list[i] : null;
}
else {
// list parameter is not an array
return null;
}
}
}])
/* DeugForm({ form: <form object>, scope: <current scope object> });
/*
* DeugForm({ form: <form object>, scope: <current scope object> });
*
* Use to log the $pristine and $valid properties of each form element. Helpful when form
* buttons fail to enable/disable properly.

View File

@ -346,6 +346,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
var fAction = list.fieldActions[action];
html += "<a ";
html += (fAction.href) ? "href=\"" + fAction.href + "\" " : "";
html += (action == 'cancel') ? " class=\"red-txt\" " : "";
for (itm in fAction) {
if (itm != 'href' && itm != 'label' && itm != 'icon' && itm != 'class' && itm != 'iconClass') {
html += Attr(fAction, itm);