mirror of
https://github.com/ansible/awx.git
synced 2026-01-14 11:20:39 -03:30
Refactor Inventories > Inventory Manage module (#2013)
* refactor InventoryManage modules, resolves #1748, #1354, #1754, #1960, #1961, #1962, #1963, #1965 * refactor InventoryManage modules, resolves #1748, #1354, #1754, #1960, #1961, #1962, #1963, #1965 * instantiate lookupfields with correct values / endpoint queries #1979 * fix groups/host list sort and remove some unnecessary service abstractions #1979 * fix childless delete wording * add host > toggle enabled/disabled * fix breadcrumb indexing * remove extra required asterisk from inventory script lookup field * fix delete prompt styling * remove group badge linking, add toolip * full width host var form, fix add/edit group inventory source vars * restrict copy/moving multiselect to one item * break word on host/group names and support multiple rows of breakcrumbs * hide trailing breadcrumb slash at root group view * fix failing unit test dependency path, hook up system tracking * fix ec2 group by label rendering in group > edit view, fix add/edit/adhoc breadcrumb display when breadcrumbs are 2+ rows * fix select spacing on copy/move groups * limit group/host/copyMove search to current context * refresh status icons on page/search event * move status indicators to left side of lists * fix typo in select2 init * fix assorted typos * $track by statement needs to tail | filterBy:xyz * fix JSHint failure * PR feedback, hook up filter for hosts with active failures, hook up status tooltips/popovers
This commit is contained in:
parent
0f742fb7a7
commit
bef61f4003
@ -1,14 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
route: '/adhoc',
|
||||
name: 'inventoryManage.adhoc',
|
||||
templateUrl: templateUrl('adhoc/adhoc'),
|
||||
controller: 'adhocController'
|
||||
};
|
||||
@ -1,10 +0,0 @@
|
||||
import route from './adhoc.route';
|
||||
import adhocController from './adhoc.controller';
|
||||
import form from './adhoc.form';
|
||||
|
||||
export default angular.module('adhoc', [])
|
||||
.controller('adhocController', adhocController)
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}])
|
||||
.factory('adhocForm', form);
|
||||
@ -47,7 +47,6 @@ import browserData from './browser-data/main';
|
||||
import dashboard from './dashboard/main';
|
||||
import moment from './shared/moment/main';
|
||||
import templateUrl from './shared/template-url/main';
|
||||
import adhoc from './adhoc/main';
|
||||
import login from './login/main';
|
||||
import activityStream from './activity-stream/main';
|
||||
import standardOut from './standard-out/main';
|
||||
@ -67,7 +66,6 @@ import './shared/Modal';
|
||||
import './shared/prompt-dialog';
|
||||
import './shared/directives';
|
||||
import './shared/filters';
|
||||
import './shared/InventoryTree';
|
||||
import './shared/Socket';
|
||||
import './shared/features/main';
|
||||
import './login/authenticationServices/pendo/ng-pendo';
|
||||
@ -99,7 +97,6 @@ var tower = angular.module('Tower', [
|
||||
dashboard.name,
|
||||
moment.name,
|
||||
templateUrl.name,
|
||||
adhoc.name,
|
||||
login.name,
|
||||
activityStream.name,
|
||||
footer.name,
|
||||
@ -169,7 +166,6 @@ var tower = angular.module('Tower', [
|
||||
'StreamWidget',
|
||||
'JobsHelper',
|
||||
'InventoryGroupsHelpDefinition',
|
||||
'InventoryTree',
|
||||
'CredentialsHelper',
|
||||
'StreamListDefinition',
|
||||
'HomeGroupListDefinition',
|
||||
@ -224,6 +220,7 @@ var tower = angular.module('Tower', [
|
||||
|
||||
// route to the details pane of /job/:id/host-event/:eventId if no other child specified
|
||||
$urlRouterProvider.when('/jobs/*/host-event/*', '/jobs/*/host-event/*/details');
|
||||
|
||||
// $urlRouterProvider.otherwise("/home");
|
||||
$urlRouterProvider.otherwise(function($injector){
|
||||
var $state = $injector.get("$state");
|
||||
|
||||
@ -61,18 +61,10 @@ export default
|
||||
label: 'Source',
|
||||
type: 'select',
|
||||
ngOptions: 'source.label for source in source_type_options track by source.value',
|
||||
ngChange: 'sourceChange()',
|
||||
ngChange: 'sourceChange(source)',
|
||||
addRequired: false,
|
||||
editRequired: false
|
||||
},
|
||||
source_path: {
|
||||
label: 'Script Path',
|
||||
ngShow: "source && source.value == 'file'",
|
||||
type: 'text',
|
||||
awRequiredWhen: {
|
||||
reqExpression: "sourcePathRequired",
|
||||
init: "false"
|
||||
}
|
||||
editRequired: false,
|
||||
ngModel: 'source'
|
||||
},
|
||||
credential: {
|
||||
label: 'Cloud Credential',
|
||||
@ -147,7 +139,6 @@ export default
|
||||
},
|
||||
inventory_script: {
|
||||
label : "Custom Inventory Script",
|
||||
labelClass: 'prepend-asterisk',
|
||||
type: 'lookup',
|
||||
ngShow: "source && source.value === 'custom'",
|
||||
sourceModel: 'inventory_script',
|
||||
@ -157,7 +148,8 @@ export default
|
||||
editRequired: true,
|
||||
ngRequired: "source && source.value === 'custom'",
|
||||
},
|
||||
extra_vars: {
|
||||
custom_variables: {
|
||||
id: 'custom_variables',
|
||||
label: 'Environment Variables', //"{{vars_label}}" ,
|
||||
ngShow: "source && source.value=='custom' ",
|
||||
type: 'textarea',
|
||||
@ -176,9 +168,10 @@ export default
|
||||
"<blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>\n",
|
||||
dataContainer: 'body'
|
||||
},
|
||||
source_vars: {
|
||||
ec2_variables: {
|
||||
id: 'ec2_variables',
|
||||
label: 'Source Variables', //"{{vars_label}}" ,
|
||||
ngShow: "source && (source.value == 'file' || source.value == 'ec2')",
|
||||
ngShow: "source && source.value == 'ec2'",
|
||||
type: 'textarea',
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
addRequired: false,
|
||||
@ -200,11 +193,11 @@ export default
|
||||
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>',
|
||||
dataContainer: 'body'
|
||||
},
|
||||
inventory_variables: {
|
||||
vmware_variables: {
|
||||
id: 'vmware_variables',
|
||||
label: 'Source Variables', //"{{vars_label}}" ,
|
||||
|
||||
ngShow: "source && (source.value == 'vmware' || " +
|
||||
"source.value == 'openstack')",
|
||||
ngShow: "source && source.value == 'vmware'",
|
||||
type: 'textarea',
|
||||
addRequired: false,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
@ -226,6 +219,32 @@ export default
|
||||
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>',
|
||||
dataContainer: 'body'
|
||||
},
|
||||
openstack_variables: {
|
||||
id: 'openstack_variables',
|
||||
label: 'Source Variables', //"{{vars_label}}" ,
|
||||
|
||||
ngShow: "source && source.value == 'openstack'",
|
||||
type: 'textarea',
|
||||
addRequired: false,
|
||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||
editRequird: false,
|
||||
rows: 6,
|
||||
'default': '---',
|
||||
parseTypeName: 'envParseType',
|
||||
dataTitle: "Source Variables",
|
||||
dataPlacement: 'right',
|
||||
awPopOver: "<p>Override variables found in openstack.yml and used by the inventory update script. For an example variable configuration " +
|
||||
"<a href=\"https://github.com/ansible/ansible/blob/devel/contrib/inventory/openstack.yml\" target=\"_blank\">" +
|
||||
"view openstack.yml in the Ansible github repo.</a></p>" +
|
||||
"<p>Enter variables using either JSON or YAML syntax. Use the radio button to toggle between the two.</p>" +
|
||||
"JSON:<br />\n" +
|
||||
"<blockquote>{<br /> \"somevar\": \"somevalue\",<br /> \"password\": \"magic\"<br /> }</blockquote>\n" +
|
||||
"YAML:<br />\n" +
|
||||
"<blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>\n" +
|
||||
'<p>View JSON examples at <a href="http://www.json.org" target="_blank">www.json.org</a></p>' +
|
||||
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>',
|
||||
dataContainer: 'body'
|
||||
},
|
||||
checkbox_group: {
|
||||
label: 'Update Options',
|
||||
type: 'checkbox_group',
|
||||
@ -295,12 +314,12 @@ export default
|
||||
},
|
||||
|
||||
buttons: {
|
||||
save: {
|
||||
ngClick: 'formSave()'
|
||||
},
|
||||
cancel: {
|
||||
ngClick: 'formCancel()'
|
||||
},
|
||||
save: {
|
||||
ngClick: 'saveGroup()'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
related: {
|
||||
|
||||
@ -60,7 +60,7 @@ export default
|
||||
addRequired: false,
|
||||
editRequird: false,
|
||||
rows: 6,
|
||||
"class": "modal-input-xlarge Form-textArea",
|
||||
"class": "modal-input-xlarge Form-textArea Form-formGroup--fullWidth",
|
||||
"default": "---",
|
||||
awPopOver: "<p>Enter variables using either JSON or YAML syntax. Use the radio button to toggle between the two.</p>" +
|
||||
"JSON:<br />\n" +
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -217,489 +217,6 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', listGenerator.name,
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('InjectHosts', ['generateList', 'InventoryHosts', 'HostsReload',
|
||||
function(GenerateList, InventoryHosts, HostsReload) {
|
||||
return function(params) {
|
||||
|
||||
var group_scope = params.group_scope,
|
||||
host_scope = params.host_scope,
|
||||
inventory_id = params.inventory_id,
|
||||
group_id = params.group_id,
|
||||
pageSize = params.pageSize,
|
||||
generator = GenerateList;
|
||||
|
||||
// Inject the list html
|
||||
generator.inject(InventoryHosts, { scope: host_scope, mode: 'edit', id: 'host-list-container', searchSize: 'col-lg-6 col-md-6 col-sm-6 col-xs-12' });
|
||||
|
||||
// Load data
|
||||
HostsReload({ scope: host_scope, group_id: group_id, inventory_id: inventory_id, parent_scope: group_scope, pageSize: pageSize });
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('HostsList', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'HostList', 'generateList',
|
||||
'Prompt', 'SearchInit', 'PaginateInit', 'ProcessErrors', 'GetBasePath', 'HostsAdd', 'HostsReload', 'SelectionInit',
|
||||
function($rootScope, $location, $log, $stateParams, Rest, Alert, HostList, GenerateList, Prompt, SearchInit,
|
||||
PaginateInit, ProcessErrors, GetBasePath, HostsAdd, HostsReload, SelectionInit) {
|
||||
return function(params) {
|
||||
|
||||
var inventory_id = params.inventory_id,
|
||||
group_id = params.group_id,
|
||||
list = HostList,
|
||||
generator = GenerateList,
|
||||
defaultUrl, scope;
|
||||
|
||||
list.iterator = 'subhost'; //Override the iterator and name so the scope of the modal dialog
|
||||
list.name = 'subhosts'; //will not conflict with the parent scope
|
||||
|
||||
|
||||
|
||||
scope = generator.inject(list, {
|
||||
id: 'form-modal-body',
|
||||
mode: 'select',
|
||||
selectButton: false
|
||||
});
|
||||
|
||||
defaultUrl = GetBasePath('inventory') + inventory_id + '/hosts/?not__groups__id=' + scope.group_id;
|
||||
|
||||
scope.formModalActionLabel = 'Select';
|
||||
scope.formModalHeader = 'Add Existing Hosts';
|
||||
scope.formModalCancelShow = true;
|
||||
|
||||
SelectionInit({ scope: scope, list: list, url: GetBasePath('groups') + group_id + '/hosts/' });
|
||||
|
||||
if (scope.removeModalClosed) {
|
||||
scope.removeModalClosed();
|
||||
}
|
||||
scope.removeModalClosed = scope.$on('modalClosed', function() {
|
||||
// if the modal closed, assume something got changed and reload the host list
|
||||
HostsReload(params);
|
||||
});
|
||||
|
||||
$('.popover').popover('hide'); //remove any lingering pop-overs
|
||||
$('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success');
|
||||
$('#form-modal').modal({ backdrop: 'static', keyboard: false });
|
||||
|
||||
SearchInit({ scope: scope, set: 'subhosts', list: list, url: defaultUrl });
|
||||
PaginateInit({ scope: scope, list: list, url: defaultUrl, mode: 'lookup' });
|
||||
scope.search(list.iterator);
|
||||
|
||||
if (!scope.$$phase) {
|
||||
scope.$digest();
|
||||
}
|
||||
|
||||
scope.createHost = function() {
|
||||
$('#form-modal').modal('hide');
|
||||
HostsAdd({ scope: params.scope, inventory_id: inventory_id, group_id: group_id });
|
||||
};
|
||||
|
||||
};
|
||||
}])
|
||||
|
||||
|
||||
.factory('HostsCreate', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm',
|
||||
'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'ToJSON',
|
||||
function($rootScope, $location, $log, $stateParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors,
|
||||
GetBasePath, HostsReload, ParseTypeChange, Wait, ToJSON) {
|
||||
return function(params) {
|
||||
|
||||
var parent_scope = params.scope,
|
||||
inventory_id = parent_scope.inventory_id,
|
||||
group_id = parent_scope.selected_group_id,
|
||||
defaultUrl = GetBasePath('groups') + group_id + '/hosts/',
|
||||
form = HostForm,
|
||||
generator = GenerateForm,
|
||||
scope = generator.inject(form, {mode: 'add', modal: true, related: false}),
|
||||
master={};
|
||||
|
||||
scope.formModalActionLabel = 'Save';
|
||||
scope.formModalHeader = 'Create New Host';
|
||||
scope.formModalCancelShow = true;
|
||||
|
||||
scope.parseType = 'yaml';
|
||||
ParseTypeChange({ scope: scope, field_id: 'host_variables' });
|
||||
|
||||
if (scope.removeHostsReload) {
|
||||
scope.removeHostsReload();
|
||||
}
|
||||
scope.removeHostsReload = scope.$on('hostsReload', function() {
|
||||
HostsReload(params);
|
||||
});
|
||||
|
||||
$('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success');
|
||||
//$('#form-modal').unbind('hidden');
|
||||
//$('#form-modal').on('hidden', function () { scope.$emit('hostsReload'); });
|
||||
|
||||
generator.reset();
|
||||
master={};
|
||||
|
||||
if (!scope.$$phase) {
|
||||
scope.$digest();
|
||||
}
|
||||
|
||||
if (scope.removeHostSaveComplete) {
|
||||
scope.removeHostSaveComplete();
|
||||
}
|
||||
scope.removeHostSaveComplete = scope.$on('HostSaveComplete', function() {
|
||||
Wait('stop');
|
||||
$('#form-modal').modal('hide');
|
||||
|
||||
HostsReload({
|
||||
scope: parent_scope,
|
||||
group_id: parent_scope.selected_group_id,
|
||||
tree_id: parent_scope.selected_tree_id,
|
||||
inventory_id: parent_scope.inventory_id
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// Save
|
||||
scope.formModalAction = function() {
|
||||
|
||||
Wait('start');
|
||||
|
||||
var fld, data={};
|
||||
scope.formModalActionDisabled = true;
|
||||
data.variables = ToJSON(scope.parseType, scope.variables, true);
|
||||
for (fld in form.fields) {
|
||||
if (fld !== 'variables') {
|
||||
data[fld] = scope[fld];
|
||||
}
|
||||
}
|
||||
data.inventory = inventory_id;
|
||||
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.post(data)
|
||||
.success( function() {
|
||||
scope.$emit('HostSaveComplete');
|
||||
})
|
||||
.error( function(data, status) {
|
||||
Wait('stop');
|
||||
scope.formModalActionDisabled = false;
|
||||
ProcessErrors(scope, data, status, form,
|
||||
{ hdr: 'Error!', msg: 'Failed to add new host. POST returned status: ' + status });
|
||||
});
|
||||
};
|
||||
|
||||
// Cancel
|
||||
scope.formReset = function() {
|
||||
// Defaults
|
||||
generator.reset();
|
||||
};
|
||||
|
||||
scope.cancelModal = function() {
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
}])
|
||||
|
||||
|
||||
.factory('HostsEdit', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm',
|
||||
'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'Find', 'SetStatus', 'ApplyEllipsis',
|
||||
'ToJSON', 'ParseVariableString', 'CreateDialog', 'TextareaResize',
|
||||
function($rootScope, $location, $log, $stateParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors,
|
||||
GetBasePath, HostsReload, ParseTypeChange, Wait, Find, SetStatus, ApplyEllipsis, ToJSON,
|
||||
ParseVariableString, CreateDialog, TextareaResize) {
|
||||
return function(params) {
|
||||
|
||||
var parent_scope = params.host_scope,
|
||||
group_scope = params.group_scope,
|
||||
host_id = params.host_id,
|
||||
inventory_id = params.inventory_id,
|
||||
mode = params.mode, // 'add' or 'edit'
|
||||
selected_group_id = params.selected_group_id,
|
||||
generator = GenerateForm,
|
||||
form = HostForm,
|
||||
defaultUrl,
|
||||
scope = parent_scope.$new(),
|
||||
master = {},
|
||||
relatedSets = {},
|
||||
buttons, url, form_scope;
|
||||
|
||||
form_scope =
|
||||
generator.inject(HostForm, { mode: 'edit', id: 'host-modal-dialog', related: false, scope: scope });
|
||||
generator.reset();
|
||||
|
||||
buttons = [{
|
||||
label: "Cancel",
|
||||
onClick: function() {
|
||||
scope.cancelModal();
|
||||
},
|
||||
icon: "fa-times",
|
||||
"class": "btn btn-default",
|
||||
"id": "host-cancel-button"
|
||||
},{
|
||||
label: "Save",
|
||||
onClick: function() {
|
||||
scope.saveModal();
|
||||
},
|
||||
icon: "fa-check",
|
||||
"class": "btn btn-primary",
|
||||
"id": "host-save-button"
|
||||
}];
|
||||
|
||||
CreateDialog({
|
||||
scope: scope,
|
||||
buttons: buttons,
|
||||
width: 675,
|
||||
height: 750,
|
||||
minWidth: 400,
|
||||
title: 'Host Properties',
|
||||
id: 'host-modal-dialog',
|
||||
closeOnEscape: false,
|
||||
form: form_scope.host_form,
|
||||
onClose: function() {
|
||||
Wait('stop');
|
||||
scope.codeMirror.destroy();
|
||||
$('#host-modal-dialog').empty();
|
||||
},
|
||||
onResizeStop: function() {
|
||||
TextareaResize({
|
||||
scope: scope,
|
||||
textareaId: 'host_variables',
|
||||
modalId: 'host-modal-dialog',
|
||||
formId: 'host_form'
|
||||
});
|
||||
},
|
||||
beforeDestroy: function() {
|
||||
if (scope.codeMirror) {
|
||||
scope.codeMirror.destroy();
|
||||
}
|
||||
$('#host-modal-dialog').empty();
|
||||
},
|
||||
onOpen: function() {
|
||||
$('#host_name').focus();
|
||||
},
|
||||
callback: 'HostEditDialogReady'
|
||||
});
|
||||
|
||||
scope.parseType = 'yaml';
|
||||
|
||||
if (scope.hostVariablesLoadedRemove) {
|
||||
scope.hostVariablesLoadedRemove();
|
||||
}
|
||||
scope.hostVariablesLoadedRemove = scope.$on('hostVariablesLoaded', function() {
|
||||
$('#host-modal-dialog').dialog('open');
|
||||
setTimeout(function() {
|
||||
TextareaResize({
|
||||
scope: scope,
|
||||
textareaId: 'host_variables',
|
||||
modalId: 'host-modal-dialog',
|
||||
formId: 'host_form',
|
||||
parse: true
|
||||
});
|
||||
}, 300);
|
||||
//ParseTypeChange({ scope: scope, field_id: 'host_variables', onReady: callback });
|
||||
});
|
||||
|
||||
Wait('start');
|
||||
|
||||
// Retrieve detail record and prepopulate the form
|
||||
if (mode === 'edit') {
|
||||
defaultUrl = GetBasePath('hosts') + host_id + '/';
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.get()
|
||||
.success( function(data) {
|
||||
var set, fld, related;
|
||||
for (fld in form.fields) {
|
||||
if (data[fld]) {
|
||||
scope[fld] = data[fld];
|
||||
master[fld] = scope[fld];
|
||||
}
|
||||
}
|
||||
related = data.related;
|
||||
for (set in form.related) {
|
||||
if (related[set]) {
|
||||
relatedSets[set] = { url: related[set], iterator: form.related[set].iterator };
|
||||
}
|
||||
}
|
||||
scope.variable_url = data.related.variable_data;
|
||||
scope.has_inventory_sources = data.has_inventory_sources;
|
||||
scope.$emit('hostVariablesLoaded');
|
||||
})
|
||||
.error( function(data, status) {
|
||||
ProcessErrors(parent_scope, data, status, form,
|
||||
{ hdr: 'Error!', msg: 'Failed to retrieve host: ' + host_id + '. GET returned status: ' + status });
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (selected_group_id) {
|
||||
// adding hosts to a group
|
||||
url = GetBasePath('groups') + selected_group_id + '/';
|
||||
} else {
|
||||
// adding hosts to the top-level (inventory)
|
||||
url = GetBasePath('inventory') + inventory_id + '/';
|
||||
}
|
||||
// Add mode
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success( function(data) {
|
||||
scope.has_inventory_sources = data.has_inventory_sources;
|
||||
scope.enabled = true;
|
||||
scope.variables = '---';
|
||||
defaultUrl = data.related.hosts;
|
||||
scope.$emit('hostVariablesLoaded');
|
||||
})
|
||||
.error( function(data, status) {
|
||||
ProcessErrors(parent_scope, data, status, form,
|
||||
{ hdr: 'Error!', msg: 'Failed to retrieve group: ' + selected_group_id + '. GET returned status: ' + status });
|
||||
});
|
||||
}
|
||||
|
||||
if (scope.removeSaveCompleted) {
|
||||
scope.removeSaveCompleted();
|
||||
}
|
||||
scope.removeSaveCompleted = scope.$on('saveCompleted', function() {
|
||||
try {
|
||||
$('#host-modal-dialog').dialog('close');
|
||||
}
|
||||
catch(err) {
|
||||
// ignore
|
||||
}
|
||||
if (group_scope && group_scope.refreshHosts) {
|
||||
group_scope.refreshHosts();
|
||||
}
|
||||
if (parent_scope.refreshHosts) {
|
||||
parent_scope.refreshHosts();
|
||||
}
|
||||
scope.$destroy();
|
||||
});
|
||||
|
||||
// Save changes to the parent
|
||||
scope.saveModal = function() {
|
||||
|
||||
Wait('start');
|
||||
var fld, data={};
|
||||
|
||||
try {
|
||||
data.variables = ToJSON(scope.parseType, scope.variables, true);
|
||||
for (fld in form.fields) {
|
||||
data[fld] = scope[fld];
|
||||
}
|
||||
data.inventory = inventory_id;
|
||||
Rest.setUrl(defaultUrl);
|
||||
if (mode === 'edit') {
|
||||
Rest.put(data)
|
||||
.success( function() {
|
||||
scope.$emit('saveCompleted');
|
||||
})
|
||||
.error( function(data, status) {
|
||||
ProcessErrors(scope, data, status, form,
|
||||
{ hdr: 'Error!', msg: 'Failed to update host: ' + host_id + '. PUT returned status: ' + status });
|
||||
});
|
||||
}
|
||||
else {
|
||||
Rest.post(data)
|
||||
.success( function() {
|
||||
scope.$emit('saveCompleted');
|
||||
})
|
||||
.error( function(data, status) {
|
||||
ProcessErrors(scope, data, status, form,
|
||||
{ hdr: 'Error!', msg: 'Failed to create host. POST returned status: ' + status });
|
||||
});
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
// ignore. ToJSON will have already alerted the user
|
||||
}
|
||||
};
|
||||
|
||||
// Cancel
|
||||
scope.formReset = function() {
|
||||
generator.reset();
|
||||
for (var fld in master) {
|
||||
scope[fld] = master[fld];
|
||||
}
|
||||
scope.parseType = 'yaml';
|
||||
};
|
||||
|
||||
scope.cancelModal = function() {
|
||||
try {
|
||||
$('#host-modal-dialog').dialog('close');
|
||||
}
|
||||
catch(err) {
|
||||
// ignore
|
||||
}
|
||||
scope.$destroy();
|
||||
};
|
||||
|
||||
};
|
||||
}])
|
||||
|
||||
|
||||
.factory('HostsDelete', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'Wait',
|
||||
function($rootScope, $location, $log, $stateParams, Rest, Alert, Prompt, ProcessErrors, GetBasePath, HostsReload, Wait) {
|
||||
return function(params) {
|
||||
// Remove the selected host from the current group by disassociating
|
||||
|
||||
var action_to_take, body,
|
||||
scope = params.parent_scope,
|
||||
host_id = params.host_id,
|
||||
host_name = params.host_name,
|
||||
group,
|
||||
url_list = [];
|
||||
|
||||
if (scope.selected_group_id) {
|
||||
//group = Find({ list: parent_scope.groups, key: 'id', val: parent_scope.selected_group_id });
|
||||
//getChildren(group.id);
|
||||
url_list.push(GetBasePath('groups') + scope.selected_group_id + '/hosts/');
|
||||
}
|
||||
else {
|
||||
url_list.push(GetBasePath('inventory') + scope.inventory.id + '/hosts/');
|
||||
}
|
||||
|
||||
if (scope.removeHostsReload) {
|
||||
scope.removeHostsReload();
|
||||
}
|
||||
scope.removeHostsReload = scope.$on('hostsReload', function() {
|
||||
$('#prompt-modal').modal('hide');
|
||||
scope.refreshHosts();
|
||||
});
|
||||
|
||||
$('#prompt-modal').on('hidden.bs.modal', function(){ Wait('stop'); });
|
||||
|
||||
action_to_take = function() {
|
||||
var count=0, i;
|
||||
|
||||
Wait('start');
|
||||
|
||||
if (scope.removeHostRemoved) {
|
||||
scope.removeHostRemoved();
|
||||
}
|
||||
scope.removeHostRemoved = scope.$on('hostRemoved', function(){
|
||||
count++;
|
||||
if (count === url_list.length) {
|
||||
Wait('start');
|
||||
scope.$emit('hostsReload');
|
||||
}
|
||||
});
|
||||
|
||||
for(i=0; i < url_list.length; i++) {
|
||||
Rest.setUrl(url_list[i]);
|
||||
Rest.post({ id: host_id, disassociate: 1 })
|
||||
.success( function() {
|
||||
scope.$emit('hostRemoved');
|
||||
})
|
||||
.error( function(data, status) {
|
||||
ProcessErrors(scope, data, status, null,
|
||||
{ hdr: 'Error!', msg: 'Attempt to delete ' + host_name + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
body = (group) ? '<div class=\"Prompt-bodyQuery\"><p>Are you sure you want to remove the host below from group ' + group.name + '?' +
|
||||
' It will still be part of the inventory and available in All Hosts.</p></div><div class=\"Prompt-bodyTarget\">' + host_name + '</div>' :
|
||||
'<div class=\"Prompt-bodyQuery\">Are you sure you want to permanently delete the host below from the inventory?</div><div class=\"Prompt-bodyTarget\">' + host_name + '</div>';
|
||||
Prompt({
|
||||
hdr: 'Delete Host',
|
||||
body: body,
|
||||
action: action_to_take,
|
||||
actionText: 'DELETE'
|
||||
});
|
||||
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('HostsCopy', ['$compile', 'Rest', 'ProcessErrors', 'CreateDialog', 'GetBasePath', 'Wait', 'generateList', 'GroupList', 'SearchInit',
|
||||
'PaginateInit',
|
||||
function($compile, Rest, ProcessErrors, CreateDialog, GetBasePath, Wait, GenerateList, GroupList, SearchInit, PaginateInit) {
|
||||
@ -933,304 +450,4 @@ return function(params) {
|
||||
|
||||
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('EditHostGroups', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'GenerateForm', 'Prompt',
|
||||
'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait',
|
||||
function($rootScope, $location, $log, $stateParams, Rest, Alert, GenerateForm, Prompt, ProcessErrors, GetBasePath, HostsReload,
|
||||
ParseTypeChange, Wait) {
|
||||
return function(params) {
|
||||
|
||||
var host_id = params.host_id,
|
||||
inventory_id = params.inventory_id,
|
||||
generator = GenerateForm,
|
||||
actions = [],
|
||||
i, html, defaultUrl, scope, postAction;
|
||||
|
||||
html = "<div class=\"row host-groups\">\n";
|
||||
html += "<div class=\"col-lg-6\">\n";
|
||||
html += "<label>Available Groups:</label>\n";
|
||||
html += "<select multiple class=\"form-control\" name=\"available-groups\" ng-model=\"selectedGroups\" ng-change=\"leftChange()\" " +
|
||||
"ng-options=\"avail_group.name for avail_group in available_groups\"></select>\n";
|
||||
html += "</div>\n";
|
||||
html += "<div class=\"col-lg-6\">\n";
|
||||
html += "<label>Belongs to Groups:</label>\n";
|
||||
html += "<select multiple class=\"form-control\" name=\"selected-groups\" ng-model=\"assignedGroups\" ng-change=\"rightChange()\" " +
|
||||
"ng-options=\"host_group.name for host_group in host_groups\"></select>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
html += "<div class=\"row host-group-buttons\">\n";
|
||||
html += "<div class=\"col-lg-12\">\n";
|
||||
html += "<button type=\"button\" ng-click=\"moveLeft()\" class=\"btn btn-sm btn-primary left-button\" ng-disabled=\"leftButtonDisabled\">" +
|
||||
"<i class=\"icon-arrow-left\"></i></button>\n";
|
||||
html += "<button type=\"button\" ng-click=\"moveRight()\" class=\"btn btn-sm btn-primary right-button\" ng-disabled=\"rightButtonDisabled\">" +
|
||||
"<i class=\"icon-arrow-right\"></i></button>\n";
|
||||
html += "<p>(move selected groups)</p>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
|
||||
defaultUrl = GetBasePath('hosts') + host_id + '/';
|
||||
scope = generator.inject(null, { mode: 'edit', modal: true, related: false, html: html });
|
||||
|
||||
for (i=0; i < scope.hosts.length; i++) {
|
||||
if (scope.hosts[i].id === host_id) {
|
||||
scope.host = scope.hosts[i];
|
||||
}
|
||||
}
|
||||
|
||||
scope.selectedGroups = null;
|
||||
scope.assignedGroups = null;
|
||||
scope.leftButtonDisabled = true;
|
||||
scope.rightButtonDisabled = true;
|
||||
|
||||
scope.formModalActionLabel = 'Save';
|
||||
//scope.formModalHeader = 'Host Groups';
|
||||
scope.formModalHeader = scope.host.name + ' - <span class=\"subtitle\">Groups</span>';
|
||||
scope.formModalCancelShow = true;
|
||||
scope.formModalActionDisabled = true;
|
||||
|
||||
$('#form-modal .btn-none').removeClass('btn-none').addClass('btn-success');
|
||||
|
||||
if (scope.hostGroupChangeRemove) {
|
||||
scope.hostGroupChangeRemove();
|
||||
}
|
||||
scope.hostGroupChangeRemove = scope.$on('hostGroupChange', function() {
|
||||
actions.pop();
|
||||
if (actions.length === 0) {
|
||||
postAction = function() {
|
||||
setTimeout(function() { Wait('stop'); }, 500);
|
||||
};
|
||||
HostsReload({ scope: scope, inventory_id: inventory_id, group_id: scope.group_id , action: postAction });
|
||||
}
|
||||
});
|
||||
|
||||
// Save changes
|
||||
scope.formModalAction = function() {
|
||||
var i, j, found;
|
||||
|
||||
$('#form-modal').modal('hide');
|
||||
Wait('start');
|
||||
|
||||
// removed host from deleted groups
|
||||
for (i=0; i < scope.original_groups.length; i++) {
|
||||
found = false;
|
||||
for (j=0; j < scope.host_groups.length; j++) {
|
||||
if (scope.original_groups[i].id === scope.host_groups[j].id) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// group was removed
|
||||
actions.push({ group_id: scope.original_groups[i].id , action: 'delete' });
|
||||
Rest.setUrl(GetBasePath('groups') + scope.original_groups[i].id + '/hosts/');
|
||||
Rest.post({ id: host_id, disassociate: 1 })
|
||||
.success( function() {
|
||||
scope.$emit('hostGroupChange');
|
||||
})
|
||||
.error( function(data, status) {
|
||||
scope.$emit('hostGroupChange');
|
||||
ProcessErrors(scope, data, status, null,
|
||||
{ hdr: 'Error!', msg: 'Attempt to remove host from group ' + scope.original_groups[i].name +
|
||||
' failed. POST returned status: ' + status });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// add host to new groups
|
||||
for (i=0; i < scope.host_groups.length; i++) {
|
||||
found = false;
|
||||
for (j=0; j < scope.original_groups.length; j++) {
|
||||
if (scope.original_groups[j].id === scope.host_groups[i].id) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// group was added
|
||||
actions.push({ group_id: scope.host_groups[i].id , action: 'add' });
|
||||
Rest.setUrl(GetBasePath('groups') + scope.host_groups[i].id + '/hosts/');
|
||||
Rest.post(scope.host)
|
||||
.success( function() {
|
||||
scope.$emit('hostGroupChange');
|
||||
})
|
||||
.error( function(data, status) {
|
||||
scope.$emit('hostGroupChange');
|
||||
ProcessErrors(scope, data, status, null,
|
||||
{ hdr: 'Error!', msg: 'Attempt to add host to group ' + scope.host_groups[i].name +
|
||||
' failed. POST returned status: ' + status });
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scope.leftChange = function() {
|
||||
// Select/deselect on available groups list
|
||||
if (scope.selectedGroups !== null && scope.selectedGroups.length > 0) {
|
||||
scope.assignedGroups = null;
|
||||
scope.leftButtonDisabled = true;
|
||||
scope.rightButtonDisabled = false;
|
||||
}
|
||||
else {
|
||||
scope.rightButtonDisabled = true;
|
||||
}
|
||||
};
|
||||
|
||||
scope.rightChange = function() {
|
||||
// Select/deselect made on host groups list
|
||||
if (scope.assignedGroups !== null && scope.assignedGroups.length > 0) {
|
||||
scope.selectedGroups = null;
|
||||
scope.leftButtonDisabled = false;
|
||||
scope.rightButtonDisabled = true;
|
||||
}
|
||||
else {
|
||||
scope.leftButtonDisabled = true;
|
||||
}
|
||||
};
|
||||
|
||||
scope.moveLeft = function() {
|
||||
// Remove selected groups from the list of assigned groups
|
||||
|
||||
var i, j, found, placed;
|
||||
|
||||
for (i=0; i < scope.assignedGroups.length; i++){
|
||||
for (j=0 ; j < scope.host_groups.length; j++) {
|
||||
if (scope.host_groups[j].id === scope.assignedGroups[i].id) {
|
||||
scope.host_groups.splice(j,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0; i < scope.assignedGroups.length; i++){
|
||||
found = false;
|
||||
for (j=0; j < scope.available_groups.length && !found; j++){
|
||||
if (scope.available_groups[j].id === scope.assignedGroups[i].id) {
|
||||
found=true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
placed = false;
|
||||
for (j=0; j < scope.available_groups.length && !placed; j++){
|
||||
if (j === 0 && scope.assignedGroups[i].name.toLowerCase() < scope.available_groups[j].name.toLowerCase()) {
|
||||
// prepend to the beginning of the array
|
||||
placed=true;
|
||||
scope.available_groups.unshift(scope.assignedGroups[i]);
|
||||
}
|
||||
else if (j + 1 < scope.available_groups.length) {
|
||||
if (scope.assignedGroups[i].name.toLowerCase() > scope.available_groups[j].name.toLowerCase() &&
|
||||
scope.assignedGroups[i].name.toLowerCase() < scope.available_groups[j + 1].name.toLowerCase() ) {
|
||||
// insert into the middle of the array
|
||||
placed = true;
|
||||
scope.available_groups.splice(j + 1, 0, scope.assignedGroups[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!placed) {
|
||||
// append to the end of the array
|
||||
scope.available_groups.push(scope.assignedGroups[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
scope.assignedGroups = null;
|
||||
scope.leftButtonDisabled = true;
|
||||
scope.rightButtonDisabled = true;
|
||||
scope.formModalActionDisabled = false;
|
||||
};
|
||||
|
||||
scope.moveRight = function() {
|
||||
// Remove selected groups from list of available groups
|
||||
|
||||
var i, j, found, placed;
|
||||
|
||||
for (i=0; i < scope.selectedGroups.length; i++){
|
||||
for (j=0 ; j < scope.available_groups.length; j++) {
|
||||
if (scope.available_groups[j].id === scope.selectedGroups[i].id) {
|
||||
scope.available_groups.splice(j,1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i=0; i < scope.selectedGroups.length; i++){
|
||||
found = false;
|
||||
for (j=0; j < scope.host_groups.length && !found; j++){
|
||||
if (scope.host_groups[j].id === scope.selectedGroups[i].id) {
|
||||
found=true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
placed = false;
|
||||
for (j=0; j < scope.host_groups.length && !placed; j++) {
|
||||
if (j === 0 && scope.selectedGroups[i].name.toLowerCase() < scope.host_groups[j].name.toLowerCase()) {
|
||||
// prepend to the beginning of the array
|
||||
placed=true;
|
||||
scope.host_groups.unshift(scope.selectedGroups[i]);
|
||||
}
|
||||
else if (j + 1 < scope.host_groups.length) {
|
||||
if (scope.selectedGroups[i].name.toLowerCase() > scope.host_groups[j].name.toLowerCase() &&
|
||||
scope.selectedGroups[i].name.toLowerCase() < scope.host_groups[j + 1].name.toLowerCase() ) {
|
||||
// insert into the middle of the array
|
||||
placed = true;
|
||||
scope.host_groups.splice(j + 1, 0, scope.selectedGroups[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!placed) {
|
||||
// append to the end of the array
|
||||
scope.host_groups.push(scope.selectedGroups[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
scope.selectedGroups = null;
|
||||
scope.leftButtonDisabled = true;
|
||||
scope.rightButtonDisabled = true;
|
||||
scope.formModalActionDisabled = false;
|
||||
};
|
||||
|
||||
|
||||
// Load the host's current list of groups
|
||||
scope.host_groups = [];
|
||||
scope.original_groups = [];
|
||||
scope.available_groups = [];
|
||||
Rest.setUrl(scope.host.related.groups + '?order_by=name');
|
||||
Rest.get()
|
||||
.success( function(data) {
|
||||
var i, j, found;
|
||||
for (i=0; i < data.results.length; i++) {
|
||||
scope.host_groups.push({
|
||||
id: data.results[i].id,
|
||||
name: data.results[i].name,
|
||||
description: data.results[i].description
|
||||
});
|
||||
scope.original_groups.push({
|
||||
id: data.results[i].id,
|
||||
name: data.results[i].name,
|
||||
description: data.results[i].description
|
||||
});
|
||||
}
|
||||
for (i=0; i < scope.inventory_groups.length; i++) {
|
||||
found = false;
|
||||
for (j=0; j < scope.host_groups.length; j++) {
|
||||
if (scope.inventory_groups[i].id === scope.host_groups[j].id) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
scope.available_groups.push(scope.inventory_groups[i]);
|
||||
}
|
||||
}
|
||||
})
|
||||
.error( function(data, status) {
|
||||
ProcessErrors(scope, data, status, null,
|
||||
{ hdr: 'Error!', msg: 'Failed to get current groups for host: ' + host_id + '. GET returned: ' + status });
|
||||
});
|
||||
|
||||
if (scope.removeHostsReload) {
|
||||
scope.removeHostsReload();
|
||||
}
|
||||
scope.removeHostsReload = scope.$on('hostsReload', function() {
|
||||
HostsReload(params);
|
||||
});
|
||||
|
||||
if (!scope.$$phase) {
|
||||
scope.$digest();
|
||||
}
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
@ -156,126 +156,7 @@ export default
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
.factory('ShowJobSummary', ['Rest', 'Wait', 'GetBasePath', 'FormatDate', 'ProcessErrors', 'GenerateForm', 'JobSummary',
|
||||
function (Rest, Wait, GetBasePath, FormatDate, ProcessErrors, GenerateForm, JobSummary) {
|
||||
return function (params) {
|
||||
// Display status info in a modal dialog- called from inventory edit page
|
||||
|
||||
var job_id = params.job_id,
|
||||
generator = GenerateForm,
|
||||
form = JobSummary,
|
||||
scope, ww, wh, x, y, maxrows, url, html;
|
||||
|
||||
html = '<div id=\"status-modal-dialog\" title=\"Job ' + job_id + '\">' +
|
||||
'<div id=\"form-container\" style=\"width: 100%;\"></div></div>\n';
|
||||
|
||||
$('#inventory-modal-container').empty().append(html);
|
||||
|
||||
scope = generator.inject(form, { mode: 'edit', id: 'form-container', related: false });
|
||||
|
||||
// Set modal dimensions based on viewport width
|
||||
ww = $(document).width();
|
||||
wh = $('body').height();
|
||||
if (ww > 1199) {
|
||||
// desktop
|
||||
x = 675;
|
||||
y = (750 > wh) ? wh - 20 : 750;
|
||||
maxrows = 20;
|
||||
} else if (ww <= 1199 && ww >= 768) {
|
||||
x = 550;
|
||||
y = (620 > wh) ? wh - 15 : 620;
|
||||
maxrows = 15;
|
||||
} else {
|
||||
x = (ww - 20);
|
||||
y = (500 > wh) ? wh : 500;
|
||||
maxrows = 10;
|
||||
}
|
||||
|
||||
// Create the modal
|
||||
$('#status-modal-dialog').dialog({
|
||||
buttons: {
|
||||
'OK': function () {
|
||||
$(this).dialog('close');
|
||||
}
|
||||
},
|
||||
modal: true,
|
||||
width: x,
|
||||
height: y,
|
||||
autoOpen: false,
|
||||
closeOnEscape: false,
|
||||
create: function () {
|
||||
// fix the close button
|
||||
$('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-titlebar button')
|
||||
.empty().attr({
|
||||
'class': 'close'
|
||||
}).text('x');
|
||||
// fix the OK button
|
||||
$('.ui-dialog[aria-describedby="status-modal-dialog"]').find('.ui-dialog-buttonset button:first')
|
||||
.attr({
|
||||
'class': 'btn btn-primary'
|
||||
});
|
||||
},
|
||||
resizeStop: function () {
|
||||
// for some reason, after resizing dialog the form and fields (the content) doesn't expand to 100%
|
||||
var dialog = $('.ui-dialog[aria-describedby="status-modal-dialog"]'),
|
||||
titleHeight = dialog.find('.ui-dialog-titlebar').outerHeight(),
|
||||
buttonHeight = dialog.find('.ui-dialog-buttonpane').outerHeight(),
|
||||
content = dialog.find('#status-modal-dialog');
|
||||
content.width(dialog.width() - 28);
|
||||
content.css({ height: (dialog.height() - titleHeight - buttonHeight - 10) });
|
||||
},
|
||||
close: function () {
|
||||
// Destroy on close
|
||||
$('.tooltip').each(function () {
|
||||
// Remove any lingering tooltip <div> elements
|
||||
$(this).remove();
|
||||
});
|
||||
$('.popover').each(function () {
|
||||
// remove lingering popover <div> elements
|
||||
$(this).remove();
|
||||
});
|
||||
$('#status-modal-dialog').dialog('destroy');
|
||||
$('#inventory-modal-container').empty();
|
||||
},
|
||||
open: function () {
|
||||
Wait('stop');
|
||||
}
|
||||
});
|
||||
|
||||
function calcRows(content) {
|
||||
var n = content.match(/\n/g),
|
||||
rows = (n) ? n.length : 1;
|
||||
return (rows > maxrows) ? 20 : rows;
|
||||
}
|
||||
|
||||
Wait('start');
|
||||
url = GetBasePath('jobs') + job_id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
var cDate;
|
||||
scope.id = data.id;
|
||||
scope.name = data.name;
|
||||
scope.status = data.status;
|
||||
scope.result_stdout = data.result_stdout;
|
||||
scope.result_traceback = data.result_traceback;
|
||||
scope.stdout_rows = calcRows(scope.result_stdout);
|
||||
scope.traceback_rows = calcRows(scope.result_traceback);
|
||||
cDate = new Date(data.created);
|
||||
scope.created = FormatDate(cDate);
|
||||
$('#status-modal-dialog').dialog('open');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Attempt to load job failed. GET returned status: ' + status });
|
||||
});
|
||||
};
|
||||
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
|
||||
.factory('JobsListUpdate', ['Rest', function(Rest) {
|
||||
return function(params) {
|
||||
var scope = params.scope,
|
||||
|
||||
@ -81,217 +81,4 @@ export default
|
||||
});
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
.factory('EditInventoryProperties', ['InventoryForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LookUpInit', 'OrganizationList',
|
||||
'GetBasePath', 'ParseTypeChange', 'SaveInventory', 'Wait', 'Store', 'SearchInit', 'ParseVariableString', 'CreateDialog', 'TextareaResize',
|
||||
function (InventoryForm, GenerateForm, Rest, Alert, ProcessErrors, LookUpInit, OrganizationList, GetBasePath, ParseTypeChange, SaveInventory,
|
||||
Wait, Store, SearchInit, ParseVariableString, CreateDialog, TextareaResize) {
|
||||
return function (params) {
|
||||
|
||||
var parent_scope = params.scope,
|
||||
inventory_id = params.inventory_id,
|
||||
generator = GenerateForm,
|
||||
form = InventoryForm,
|
||||
master = {},
|
||||
//PreviousSearchParams = Store('CurrentSearchParams'),
|
||||
buttons,
|
||||
scope = parent_scope.$new();
|
||||
|
||||
form.well = false;
|
||||
|
||||
var form_scope =
|
||||
generator.inject(form, {
|
||||
mode: 'edit',
|
||||
showButtons: false,
|
||||
showActions: false,
|
||||
id: 'inventory-edit-modal-dialog',
|
||||
related: false,
|
||||
scope: scope
|
||||
});
|
||||
|
||||
/* Reset form properties. Otherwise it screws up future requests of the Inventories detail page */
|
||||
form.well = true;
|
||||
|
||||
buttons = [{
|
||||
label: "Cancel",
|
||||
onClick: function() {
|
||||
scope.cancelModal();
|
||||
},
|
||||
icon: "fa-times",
|
||||
"class": "btn btn-default",
|
||||
"id": "inventory-edit-cancel-button"
|
||||
},{
|
||||
label: "Save",
|
||||
onClick: function() {
|
||||
scope.saveModal();
|
||||
},
|
||||
icon: "fa-check",
|
||||
"class": "btn btn-primary",
|
||||
"id": "inventory-edit-save-button"
|
||||
}];
|
||||
|
||||
CreateDialog({
|
||||
scope: scope,
|
||||
buttons: buttons,
|
||||
width: 675,
|
||||
height: 750,
|
||||
minWidth: 400,
|
||||
title: 'Inventory Properties',
|
||||
id: 'inventory-edit-modal-dialog',
|
||||
closeOnEscape: false,
|
||||
form: form_scope.inventory_form,
|
||||
onClose: function() {
|
||||
Wait('stop');
|
||||
scope.codeMirror.destroy();
|
||||
$('#inventory-edit-modal-dialog').empty();
|
||||
},
|
||||
onResizeStop: function() {
|
||||
TextareaResize({
|
||||
scope: scope,
|
||||
textareaId: 'inventory_variables',
|
||||
modalId: 'inventory-edit-modal-dialog',
|
||||
formId: 'inventory_form'
|
||||
});
|
||||
},
|
||||
beforeDestroy: function() {
|
||||
if (scope.codeMirror) {
|
||||
scope.codeMirror.destroy();
|
||||
}
|
||||
$('#inventory-edit-modal-dialog').empty();
|
||||
},
|
||||
onOpen: function() {
|
||||
$('#inventory_name').focus();
|
||||
setTimeout(function() {
|
||||
TextareaResize({
|
||||
scope: scope,
|
||||
textareaId: 'inventory_variables',
|
||||
modalId: 'inventory-edit-modal-dialog',
|
||||
formId: 'inventory_form',
|
||||
parse: true
|
||||
});
|
||||
}, 300);
|
||||
},
|
||||
callback: 'InventoryEditDialogReady'
|
||||
});
|
||||
|
||||
scope.parseType = 'yaml';
|
||||
|
||||
if (scope.removeInventoryPropertiesLoaded) {
|
||||
scope.removeInventoryPropertiesLoaded();
|
||||
}
|
||||
scope.removeInventoryPropertiesLoaded = scope.$on('inventoryPropertiesLoaded', function() {
|
||||
Wait('stop');
|
||||
$('#inventory-edit-modal-dialog').dialog('open');
|
||||
});
|
||||
|
||||
scope.formModalActionLabel = 'Save';
|
||||
scope.formModalCancelShow = true;
|
||||
scope.formModalInfo = false;
|
||||
scope.formModalHeader = 'Inventory Properties';
|
||||
|
||||
Wait('start');
|
||||
Rest.setUrl(GetBasePath('inventory') + inventory_id + '/');
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
var fld;
|
||||
for (fld in form.fields) {
|
||||
if (fld === 'variables') {
|
||||
scope.variables = ParseVariableString(data.variables);
|
||||
master.variables = scope.variables;
|
||||
} else if (fld === 'inventory_name') {
|
||||
scope[fld] = data.name;
|
||||
master[fld] = scope[fld];
|
||||
} else if (fld === 'inventory_description') {
|
||||
scope[fld] = data.description;
|
||||
master[fld] = scope[fld];
|
||||
} else if (data[fld]) {
|
||||
scope[fld] = data[fld];
|
||||
master[fld] = scope[fld];
|
||||
}
|
||||
if (form.fields[fld].sourceModel && data.summary_fields &&
|
||||
data.summary_fields[form.fields[fld].sourceModel]) {
|
||||
scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
}
|
||||
}
|
||||
|
||||
LookUpInit({
|
||||
scope: scope,
|
||||
form: form,
|
||||
current_item: scope.organization,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio'
|
||||
});
|
||||
|
||||
scope.$emit('inventoryPropertiesLoaded');
|
||||
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status });
|
||||
});
|
||||
|
||||
if (scope.removeInventorySaved) {
|
||||
scope.removeInventorySaved();
|
||||
}
|
||||
scope.removeInventorySaved = scope.$on('InventorySaved', function () {
|
||||
//$('#form-modal').modal('hide');
|
||||
// Restore prior search state
|
||||
//if (scope.searchCleanp) {
|
||||
// scope.searchCleanup();
|
||||
//}
|
||||
//SearchInit({
|
||||
// scope: parent_scope,
|
||||
// set: PreviousSearchParams.set,
|
||||
// list: PreviousSearchParams.list,
|
||||
// url: PreviousSearchParams.defaultUrl,
|
||||
// iterator: PreviousSearchParams.iterator,
|
||||
// sort_order: PreviousSearchParams.sort_order,
|
||||
// setWidgets: false
|
||||
//});
|
||||
//parent_scope.$emit('RefreshInventories');
|
||||
try {
|
||||
$('#inventory-edit-modal-dialog').dialog('close');
|
||||
}
|
||||
catch(err) {
|
||||
// ignore
|
||||
}
|
||||
parent_scope.$emit('RefreshInventories');
|
||||
scope.$destroy();
|
||||
});
|
||||
|
||||
scope.cancelModal = function () {
|
||||
// Restore prior search state
|
||||
/*if (scope.searchCleanp) {
|
||||
scope.searchCleanup();
|
||||
}
|
||||
SearchInit({
|
||||
scope: parent_scope,
|
||||
set: PreviousSearchParams.set,
|
||||
list: PreviousSearchParams.list,
|
||||
url: PreviousSearchParams.defaultUrl,
|
||||
iterator: PreviousSearchParams.iterator,
|
||||
sort_order: PreviousSearchParams.sort_order,
|
||||
setWidgets: false
|
||||
});*/
|
||||
try {
|
||||
$('#inventory-edit-modal-dialog').dialog('close');
|
||||
}
|
||||
catch(err) {
|
||||
// ignore
|
||||
}
|
||||
scope.$destroy();
|
||||
};
|
||||
|
||||
scope.saveModal = function () {
|
||||
scope.inventory_id = inventory_id;
|
||||
SaveInventory({ scope: scope, parent_scope: parent_scope });
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
]);
|
||||
|
||||
@ -1,13 +1,5 @@
|
||||
<div class="tab-pane" id="inventories">
|
||||
<div ui-view></div>
|
||||
<div ui-view=""></div>
|
||||
<div ng-cloak id="htmlTemplate" class="Panel"></div>
|
||||
<div id="inventory-edit-modal-dialog"></div>
|
||||
<div ng-include="'/static/partials/logviewer.html'"></div>
|
||||
<div id="copy-job-modal" style="display:none">
|
||||
<form name="copy_form" id="copy_form">
|
||||
What would you like to name the copy of job template <b><span id=job_name></span></b>?<br>
|
||||
<input id="new_copy_name" name="new_copy_name" ng-model ="new_copy_name" ng-required="true" class="form-control ng-pristine ng-invalid-required ng-invalid" style="margin-top:10px;">
|
||||
<div class="error survey_error ng-hide" ng-show="copy_form.new_copy_name.$dirty && copy_form.new_copy_name.$error.required">Please enter a name for this job template copy.</div></input>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
* @name controllers.function:Adhoc
|
||||
* @description This controller controls the adhoc form creation, command launching and navigating to standard out after command has been succesfully ran.
|
||||
*/
|
||||
function adhocController($q, $scope, $rootScope, $location, $stateParams,
|
||||
function adhocController($q, $scope, $location, $stateParams,
|
||||
$state, CheckPasswords, PromptForPasswords, CreateLaunchDialog, adhocForm,
|
||||
GenerateForm, Rest, ProcessErrors, ClearScope, GetBasePath, GetChoices,
|
||||
KindChange, LookUpInit, CredentialList, Empty, Wait) {
|
||||
@ -23,7 +23,7 @@ function adhocController($q, $scope, $rootScope, $location, $stateParams,
|
||||
this.privateFn = privateFn;
|
||||
|
||||
var id = $stateParams.inventory_id,
|
||||
hostPattern = $rootScope.hostPatterns || "all";
|
||||
hostPattern = $stateParams.pattern;
|
||||
|
||||
// note: put any urls that the controller will use in here!!!!
|
||||
privateFn.setAvailableUrls = function() {
|
||||
@ -102,7 +102,6 @@ function adhocController($q, $scope, $rootScope, $location, $stateParams,
|
||||
privateFn.instantiateHostPatterns = function(hostPattern) {
|
||||
$scope.limit = hostPattern;
|
||||
$scope.providedHostPatterns = $scope.limit;
|
||||
delete $rootScope.hostPatterns;
|
||||
};
|
||||
|
||||
// call helpers to initialize lookup and select fields through get
|
||||
@ -295,7 +294,7 @@ function adhocController($q, $scope, $rootScope, $location, $stateParams,
|
||||
|
||||
}
|
||||
|
||||
export default ['$q', '$scope', '$rootScope', '$location', '$stateParams',
|
||||
export default ['$q', '$scope', '$location', '$stateParams',
|
||||
'$state', 'CheckPasswords', 'PromptForPasswords', 'CreateLaunchDialog', 'adhocForm',
|
||||
'GenerateForm', 'Rest', 'ProcessErrors', 'ClearScope', 'GetBasePath',
|
||||
'GetChoices', 'KindChange', 'LookUpInit', 'CredentialList', 'Empty', 'Wait',
|
||||
@ -122,20 +122,19 @@ export default function() {
|
||||
dataContainer: "body"
|
||||
},
|
||||
},
|
||||
|
||||
buttons: {
|
||||
reset: {
|
||||
ngClick: 'formReset()',
|
||||
ngDisabled: true,
|
||||
label: 'Reset',
|
||||
'class': 'Form-buttonDefault Form-button'
|
||||
},
|
||||
launch: {
|
||||
label: 'Save',
|
||||
ngClick: 'launchJob()',
|
||||
ngDisabled: true,
|
||||
'class': 'Form-buttonDefault Form-button'
|
||||
}
|
||||
'class': 'btn btn-sm List-buttonSubmit'
|
||||
},
|
||||
reset: {
|
||||
ngClick: 'formReset()',
|
||||
ngDisabled: true,
|
||||
label: 'Reset',
|
||||
'class': 'btn btn-sm Form-cancelButton'
|
||||
}
|
||||
},
|
||||
|
||||
related: {}
|
||||
24
awx/ui/client/src/inventories/manage/adhoc/adhoc.route.js
Normal file
24
awx/ui/client/src/inventories/manage/adhoc/adhoc.route.js
Normal file
@ -0,0 +1,24 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
route: '/adhoc',
|
||||
params:{
|
||||
pattern: {
|
||||
value: 'all',
|
||||
squash: true
|
||||
}
|
||||
},
|
||||
name: 'inventoryManage.adhoc',
|
||||
views: {
|
||||
'form@inventoryManage': {
|
||||
templateUrl: templateUrl('inventories/manage/adhoc/adhoc'),
|
||||
controller: 'adhocController'
|
||||
}
|
||||
}
|
||||
};
|
||||
11
awx/ui/client/src/inventories/manage/adhoc/main.js
Normal file
11
awx/ui/client/src/inventories/manage/adhoc/main.js
Normal file
@ -0,0 +1,11 @@
|
||||
import route from './adhoc.route';
|
||||
import adhocController from './adhoc.controller';
|
||||
import form from './adhoc.form';
|
||||
|
||||
export default
|
||||
angular.module('adhoc', [])
|
||||
.controller('adhocController', adhocController)
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}])
|
||||
.factory('adhocForm', form);
|
||||
@ -0,0 +1,25 @@
|
||||
.InventoryManageBreadCrumbs .BreadCrumb-list{
|
||||
padding-right: 0px;
|
||||
}
|
||||
.InventoryManageBreadCrumb-ncy.BreadCrumb-list{
|
||||
padding-left: 5px;
|
||||
}
|
||||
.InventoryManageBreadCrumbs-separator{
|
||||
content: "/";
|
||||
padding: 0 5px;
|
||||
color: #B7B7B7;
|
||||
}
|
||||
.InventoryManageBreadCrumbs{
|
||||
position: relative;
|
||||
height: auto;
|
||||
top: -40px;
|
||||
.BreadCrumb-list{
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
.InventoryManage-breakWord{
|
||||
word-break: break-all;
|
||||
}
|
||||
ol.BreadCrumb-list{
|
||||
display: inline-block;
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$state', '$stateParams', '$scope', 'inventoryData', 'breadCrumbData', function($state, $stateParams, $scope, inventoryData, breadCrumbData){
|
||||
// process result data into the same order specified in the traversal path
|
||||
$scope.groups = _.sortBy(breadCrumbData, function(item){
|
||||
var index = _.indexOf($stateParams.group, item.id);
|
||||
return (index === -1) ? $stateParams.group.length : index;
|
||||
});
|
||||
$scope.inventory = inventoryData;
|
||||
// slices the group stack at $index to supply new group params to $state.go()
|
||||
$scope.goToGroup = function($index){
|
||||
var group = $stateParams.group.slice(0, $index);
|
||||
$state.go('inventoryManage', {group: group}, {reload: true});
|
||||
};
|
||||
$scope.state = $state;
|
||||
$scope.isRootState = function(){
|
||||
return $state.current.name === 'inventoryManage';
|
||||
};
|
||||
$scope.goToInventory = function(){
|
||||
$state.go('inventoryManage', {group: undefined}, {reload: true});
|
||||
};
|
||||
}];
|
||||
@ -0,0 +1,50 @@
|
||||
<div class="BreadCrumb InventoryManageBreadCrumbs">
|
||||
<ol class="BreadCrumb-list">
|
||||
<li class="BreadCrumb-item"><a ui-sref="inventories">Inventories</a></li>
|
||||
<li href class="BreadCrumb-item"><a href ng-click="goToInventory()">Manage {{inventory.name}}</a></li>
|
||||
<!-- inside inventoryManage list view (last item is not clickable) -->
|
||||
<span ng-if="isRootState()">
|
||||
<li class="BreadCrumb-item"></li>
|
||||
<li ng-repeat="group in groups | limitTo:(groups.length-1) track by $index" class="BreadCrumb-item">
|
||||
<a href ng-click="goToGroup($index+1)">{{group.name}}</a>
|
||||
</li>
|
||||
<li ng-hide="groups.length == 0" class="BreadCrumb-item"><span>{{groups[groups.length-1].name}}</span></li>
|
||||
</span>
|
||||
<!-- inside inventoryManage.child like add/edit (last item is clickable)-->
|
||||
<span ng-if="!isRootState()">
|
||||
<li class="BreadCrumb-item"></li>
|
||||
<li ng-repeat="group in groups track by $index" class="BreadCrumb-item">
|
||||
<a href ng-click="goToGroup($index)">{{group.name}}</a>
|
||||
</li>
|
||||
<li class="BreadCrumb-item"></li>
|
||||
</span>
|
||||
<div class="InventoryManageBreadCrumb-ncy" ng-if="!licenseMissing" ncy-breadcrumb></div>
|
||||
</ol>
|
||||
<div class="BreadCrumb-menuLink"
|
||||
id="bread_crumb_activity_stream"
|
||||
aw-tool-tip="View Activity Stream"
|
||||
data-placement="left"
|
||||
data-trigger="hover"
|
||||
data-container="body"
|
||||
ng-class="{'BreadCrumb-menuLinkActive' : activityStreamActive}"
|
||||
ng-if="showActivityStreamButton"
|
||||
ng-hide= "licenseMissing"
|
||||
ng-click="openActivityStream()">
|
||||
<i class="BreadCrumb-menuLinkImage icon-activity-stream"
|
||||
alt="Activity Stream">
|
||||
</i>
|
||||
</div>
|
||||
<a class="BreadCrumb-menuLink"
|
||||
id="bread_crumb_dashboard"
|
||||
ui-sref="dashboard"
|
||||
aw-tool-tip="View Dashboard"
|
||||
data-placement="left"
|
||||
data-trigger="hover"
|
||||
data-container="body"
|
||||
ng-hide="licenseMissing"
|
||||
ng-if="!showActivityStreamButton">
|
||||
<i class="BreadCrumb-menuLinkImage fa fa-tachometer"
|
||||
alt="Dashboard">
|
||||
</i>
|
||||
</a>
|
||||
</div>
|
||||
@ -0,0 +1,68 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$scope', '$state', '$stateParams', 'generateList', 'SearchInit', 'PaginateInit', 'GroupManageService', 'GetBasePath', 'CopyMoveGroupList', 'group',
|
||||
function($scope, $state, $stateParams, GenerateList, SearchInit, PaginateInit, GroupManageService, GetBasePath, CopyMoveGroupList, group){
|
||||
var list = CopyMoveGroupList,
|
||||
view = GenerateList;
|
||||
$scope.item = group;
|
||||
$scope.submitMode = $stateParams.groups === undefined ? 'move' : 'copy';
|
||||
$scope['toggle_'+ list.iterator] = function(id){
|
||||
// toggle off anything else currently selected
|
||||
_.forEach($scope.groups, (item) => {return item.id === id ? item.checked = 1 : item.checked = null;});
|
||||
// yoink the currently selected thing
|
||||
$scope.selected = _.find($scope.groups, (item) => {return item.id === id;});
|
||||
};
|
||||
$scope.formCancel = function(){
|
||||
$state.go('^');
|
||||
};
|
||||
$scope.formSave = function(){
|
||||
switch($scope.submitMode) {
|
||||
case 'copy':
|
||||
GroupManageService.associateGroup(group, $scope.selected.id).then(() => $state.go('^', null, {reload: true}));
|
||||
break;
|
||||
case 'move':
|
||||
// at the root group level, no dissassociation is needed
|
||||
if (!$stateParams.group){
|
||||
GroupManageService.associateGroup(group, $scope.selected.id).then(() => $state.go('^', null, {reload: true}));
|
||||
}
|
||||
else{
|
||||
// unsure if orphaned resources get garbage collected, safe bet is to associate before disassociate
|
||||
GroupManageService.associateGroup(group, $scope.selected.id).then(() => {
|
||||
GroupManageService.disassociateGroup(group, _.last($stateParams.group))
|
||||
.then(() => $state.go('^', null, {reload: true}));
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
var init = function(){
|
||||
var url = GetBasePath('inventory') + $stateParams.inventory_id + '/groups/';
|
||||
url += $stateParams.group ? '?not__id__in=' + group.id + ',' + _.last($stateParams.group) : '?not__id=' + group.id;
|
||||
list.basePath = url;
|
||||
view.inject(list, {
|
||||
mode: 'lookup',
|
||||
id: 'copyMove-list',
|
||||
scope: $scope
|
||||
});
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: list.name,
|
||||
list: list,
|
||||
url: url
|
||||
});
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url : url,
|
||||
mode: 'lookup'
|
||||
});
|
||||
$scope.search(list.iterator, null, true, false);
|
||||
// remove the current group from list
|
||||
};
|
||||
init();
|
||||
}];
|
||||
@ -0,0 +1,65 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$scope', '$state', '$stateParams', 'generateList', 'SearchInit', 'PaginateInit', 'HostManageService', 'GetBasePath', 'CopyMoveGroupList', 'host',
|
||||
function($scope, $state, $stateParams, GenerateList, SearchInit, PaginateInit, HostManageService, GetBasePath, CopyMoveGroupList, host){
|
||||
var list = CopyMoveGroupList,
|
||||
view = GenerateList;
|
||||
$scope.item = host;
|
||||
$scope.submitMode = 'copy';
|
||||
$scope['toggle_'+ list.iterator] = function(id){
|
||||
// toggle off anything else currently selected
|
||||
_.forEach($scope.groups, (item) => {return item.id === id ? item.checked = 1 : item.checked = null;});
|
||||
// yoink the currently selected thing
|
||||
$scope.selected = _.find($scope.groups, (item) => {return item.id === id;});
|
||||
};
|
||||
$scope.formCancel = function(){
|
||||
$state.go('^');
|
||||
};
|
||||
$scope.formSave = function(){
|
||||
switch($scope.submitMode) {
|
||||
case 'copy':
|
||||
HostManageService.associateGroup(host, $scope.selected.id).then(() => $state.go('^'));
|
||||
break;
|
||||
case 'move':
|
||||
// at the root group level, no dissassociation is needed
|
||||
if (!$stateParams.group){
|
||||
HostManageService.associateGroup(host, $scope.selected.id).then(() => $state.go('^', null, {reload: true}));
|
||||
}
|
||||
else{
|
||||
HostManageService.associateGroup(host, $scope.selected.id).then(() => {
|
||||
HostManageService.disassociateGroup(host, _.last($stateParams.group))
|
||||
.then(() => $state.go('^', null, {reload: true}));
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
var init = function(){
|
||||
var url = GetBasePath('inventory') + $stateParams.inventory_id + '/groups/';
|
||||
list.basePath = url;
|
||||
view.inject(list, {
|
||||
mode: 'lookup',
|
||||
id: 'copyMove-list',
|
||||
scope: $scope
|
||||
});
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: list.name,
|
||||
list: list,
|
||||
url: url
|
||||
});
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url : url,
|
||||
mode: 'lookup'
|
||||
});
|
||||
$scope.search(list.iterator, null, true, false);
|
||||
};
|
||||
init();
|
||||
}];
|
||||
@ -4,13 +4,6 @@
|
||||
.List-searchRow {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.ui-dialog-buttonpane.ui-widget-content {
|
||||
border: none;
|
||||
text-align: right;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.Form-header {
|
||||
width: 50%;
|
||||
margin-top: -20px;
|
||||
@ -22,9 +15,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.copyMove-directive--copyMoveChoices {
|
||||
.copyMove-choices {
|
||||
float: right;
|
||||
width: 25%;
|
||||
text-align: right;
|
||||
}
|
||||
.copyMove-buttons{
|
||||
height: 30px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
<div class="Panel copyMove-panel">
|
||||
<div class="Form-header">
|
||||
<div class="Form-title ng-binding">{{item.name}}</div>
|
||||
</div>
|
||||
<div class="form-group copyMove-choices">
|
||||
<label class="radio-inline">
|
||||
<input type="radio" ng-model="submitMode" value="copy" class="ng-pristine ng-valid ng-touched"> Copy
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" ng-model="submitMode" value="move" class="ng-pristine ng-untouched ng-valid"> Move
|
||||
</label>
|
||||
</div>
|
||||
<div id="copyMove-select"></div>
|
||||
<div id="copyMove-list"></div>
|
||||
<div class="copyMove-buttons">
|
||||
<button type="button" class="pull-right btn btn-sm btn-default Form-cancelButton" ng-click="formCancel()">Cancel</button>
|
||||
<button type="button" class="pull-right btn btn-sm Form-saveButton" ng-disabled="!selected" ng-click="formSave()">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,51 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
import {templateUrl} from '../../../shared/template-url/template-url.factory';
|
||||
|
||||
import CopyMoveGroupsController from './copy-move-groups.controller';
|
||||
import CopyMoveHostsController from './copy-move-hosts.controller';
|
||||
|
||||
var copyMoveGroup = {
|
||||
name: 'inventoryManage.copyMoveGroup',
|
||||
route: '/copy-move-group/{group_id}',
|
||||
data: {
|
||||
group_id: 'group_id',
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "COPY OR MOVE {{item.name}}"
|
||||
},
|
||||
resolve: {
|
||||
group: ['GroupManageService', '$stateParams', function(GroupManageService, $stateParams){
|
||||
return GroupManageService.get({id: $stateParams.group_id}).then(res => res.data.results[0]);
|
||||
}]
|
||||
},
|
||||
views: {
|
||||
'form@inventoryManage' : {
|
||||
controller: CopyMoveGroupsController,
|
||||
templateUrl: templateUrl('inventories/manage/copy-move/copy-move'),
|
||||
}
|
||||
}
|
||||
};
|
||||
var copyMoveHost = {
|
||||
name: 'inventoryManage.copyMoveHost',
|
||||
route: '/copy-move-host/{host_id}',
|
||||
ncyBreadcrumb: {
|
||||
label: "COPY OR MOVE {{item.name}}"
|
||||
},
|
||||
resolve: {
|
||||
host: ['HostManageService', '$stateParams', function(HostManageService, $stateParams){
|
||||
return HostManageService.get({id: $stateParams.host_id}).then(res => res.data.results[0]);
|
||||
}]
|
||||
},
|
||||
views: {
|
||||
'form@inventoryManage': {
|
||||
templateUrl: templateUrl('inventories/manage/copy-move/copy-move'),
|
||||
controller: CopyMoveHostsController,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export {copyMoveGroup, copyMoveHost};
|
||||
@ -4,12 +4,11 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './copy.route';
|
||||
import {copyMoveGroup, copyMoveHost} from './copy-move.route';
|
||||
|
||||
export default
|
||||
angular.module('inventory-copy', [])
|
||||
angular.module('manageCopyMove', [])
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route.copy);
|
||||
$stateExtender.addState(route.copyGroup);
|
||||
$stateExtender.addState(route.copyHost);
|
||||
$stateExtender.addState(copyMoveGroup);
|
||||
$stateExtender.addState(copyMoveHost);
|
||||
}]);
|
||||
@ -1,306 +0,0 @@
|
||||
function CopyGroupsCtrl($compile, $state, $scope, $location, Rest, ProcessErrors, CreateDialog,
|
||||
GetBasePath, Wait, GenerateList, GroupList, SearchInit, PaginateInit, GetRootGroups, ParamPass, Store) {
|
||||
var vm = this;
|
||||
var name;
|
||||
|
||||
var params = ParamPass.get(),
|
||||
group_id,
|
||||
parent_scope,
|
||||
scope;
|
||||
|
||||
if (params !== undefined) {
|
||||
group_id = $state.params.group_id;
|
||||
parent_scope = params.scope;
|
||||
scope = parent_scope.$new();
|
||||
var parent_group = parent_scope.selected_group_id,
|
||||
url, group;
|
||||
} else {
|
||||
group_id = $state.params.group_id;
|
||||
parent_scope = $scope.$new();
|
||||
scope = parent_scope.$new();
|
||||
}
|
||||
|
||||
var inventory_id = $state.params.inventory_id;
|
||||
var PreviousSearchParams = Store('group_current_search_params');
|
||||
|
||||
if (scope.removeGroupsCopyPostRefresh) {
|
||||
scope.removeGroupsCopyPostRefresh();
|
||||
}
|
||||
|
||||
scope.removeGroupCopyPostRefresh = scope.$on('PostRefresh', function() {
|
||||
scope.copy_groups.forEach(function(row, i) {
|
||||
scope.copy_groups[i].checked = '0';
|
||||
});
|
||||
Wait('stop');
|
||||
|
||||
// prevent backspace from navigation when not in input or textarea field
|
||||
$(document).on('keydown', function(e) {
|
||||
if (e.which === 8 && !$(e.target).is('input[type="text"], textarea')) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
if (scope.removeCopyDialogReady) {
|
||||
scope.removeCopyDialogReady();
|
||||
}
|
||||
|
||||
scope.removeCopyDialogReady = scope.$on('CopyDialogReady', function() {
|
||||
var url = GetBasePath('inventory') + inventory_id + '/groups/';
|
||||
url += (parent_group) ? '?not__id__in=' + group_id + ',' + parent_group : '?not__id=' + group_id;
|
||||
GenerateList.inject(GroupList, {
|
||||
mode: 'lookup',
|
||||
id: 'copyMove-directive--copyGroupSelect',
|
||||
scope: scope
|
||||
});
|
||||
SearchInit({
|
||||
scope: scope,
|
||||
set: GroupList.name,
|
||||
list: GroupList,
|
||||
url: url
|
||||
});
|
||||
PaginateInit({
|
||||
scope: scope,
|
||||
list: GroupList,
|
||||
url: url,
|
||||
mode: 'lookup'
|
||||
});
|
||||
scope.search(GroupList.iterator);
|
||||
});
|
||||
|
||||
if (scope.removeShowDialog) {
|
||||
scope.removeShowDialog();
|
||||
}
|
||||
|
||||
scope.removeShowDialog = scope.$on('ShowDialog', function() {
|
||||
var d;
|
||||
scope.name = group.name;
|
||||
scope.copy_choice = "copy";
|
||||
d = angular.element(document.getElementById('copyMove-directive--copyGroupSelect'));
|
||||
$compile(d)(scope);
|
||||
scope.$emit('CopyDialogReady');
|
||||
});
|
||||
|
||||
if (scope.removeRootGroupsReady) {
|
||||
scope.removeRootGroupsReady();
|
||||
}
|
||||
|
||||
scope.removeRootGroupsReady = scope.$on('RootGroupsReady', function(e, root_groups) {
|
||||
scope.offer_root_group = true;
|
||||
scope.use_root_group = false;
|
||||
root_groups.every(function(row) {
|
||||
if (row.id === group_id) {
|
||||
scope.offer_root_group = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
url = GetBasePath('groups') + group_id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
group = data;
|
||||
vm.name = group.name;
|
||||
scope.$emit('ShowDialog');
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors(scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. GET returned: ' + status
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Wait('start');
|
||||
|
||||
GetRootGroups({
|
||||
scope: scope,
|
||||
group_id: group_id,
|
||||
inventory_id: $state.params.inventory_id,
|
||||
callback: 'RootGroupsReady'
|
||||
});
|
||||
|
||||
var restoreSearch = function() {
|
||||
// Restore search params and related stuff, plus refresh
|
||||
// groups and hosts lists
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: PreviousSearchParams.set,
|
||||
list: PreviousSearchParams.list,
|
||||
url: PreviousSearchParams.defaultUrl,
|
||||
iterator: PreviousSearchParams.iterator,
|
||||
sort_order: PreviousSearchParams.sort_order,
|
||||
setWidgets: false
|
||||
});
|
||||
$scope.refreshHostsOnGroupRefresh = true;
|
||||
//$scope.search(InventoryGroups.iterator, null, true, false, true);
|
||||
};
|
||||
|
||||
var cancel = function() {
|
||||
restoreSearch(); // Restore all parent search stuff and refresh hosts and groups lists
|
||||
scope.$destroy();
|
||||
$state.go('inventoryManage', {}, {
|
||||
reload: true
|
||||
});
|
||||
};
|
||||
|
||||
var allowSave = false;
|
||||
scope['toggle_' + GroupList.iterator] = function(id) {
|
||||
var count = 0,
|
||||
list = GroupList;
|
||||
scope[list.name].forEach(function(row, i) {
|
||||
if (row.id === id) {
|
||||
if (row.checked) {
|
||||
scope[list.name][i].success_class = 'success';
|
||||
} else {
|
||||
scope[list.name][i].success_class = '';
|
||||
}
|
||||
} else {
|
||||
scope[list.name][i].checked = 0;
|
||||
scope[list.name][i].success_class = '';
|
||||
}
|
||||
});
|
||||
// Check if any rows are checked
|
||||
scope[list.name].forEach(function(row) {
|
||||
if (row.checked) {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
if (count === 0) {
|
||||
vm.allowSave = false;
|
||||
} else {
|
||||
vm.allowSave = true;
|
||||
}
|
||||
};
|
||||
|
||||
scope.toggleUseRootGroup = function() {
|
||||
var list = GroupList;
|
||||
if (scope.use_root_group) {
|
||||
$('#group-copy-ok-button').removeAttr('disabled');
|
||||
} else {
|
||||
// check for group selection
|
||||
$('#group-copy-ok-button').attr('disabled', 'disabled');
|
||||
scope[list.name].every(function(row) {
|
||||
if (row.checked === 1) {
|
||||
$('#group-copy-ok-button').removeAttr('disabled');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var performCopy = function() {
|
||||
var list = GroupList,
|
||||
target,
|
||||
url;
|
||||
|
||||
Wait('start');
|
||||
|
||||
if (scope.use_root_group) {
|
||||
target = null;
|
||||
} else {
|
||||
scope[list.name].every(function(row) {
|
||||
if (row.checked === 1) {
|
||||
target = row;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (vm.copy_choice === 'move') {
|
||||
// Respond to move
|
||||
|
||||
// disassociate the group from the original parent
|
||||
if (scope.removeGroupRemove) {
|
||||
scope.removeGroupRemove();
|
||||
}
|
||||
scope.removeGroupRemove = scope.$on('RemoveGroup', function() {
|
||||
if (parent_group > 0) {
|
||||
// Only remove a group from a parent when the parent is a group and not the inventory root
|
||||
url = GetBasePath('groups') + parent_group + '/children/';
|
||||
Rest.setUrl(url);
|
||||
Rest.post({
|
||||
id: group.id,
|
||||
disassociate: 1
|
||||
})
|
||||
.success(function() {
|
||||
vm.cancel();
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors(scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to remove ' + group.name + ' from group ' + parent_group + '. POST returned: ' + status
|
||||
});
|
||||
});
|
||||
} else {
|
||||
vm.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
// add the new group to the target
|
||||
url = (target) ?
|
||||
GetBasePath('groups') + target.id + '/children/' :
|
||||
GetBasePath('inventory') + inventory_id + '/groups/';
|
||||
group = {
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
description: group.description,
|
||||
inventory: inventory_id
|
||||
};
|
||||
Rest.setUrl(url);
|
||||
Rest.post(group)
|
||||
.success(function() {
|
||||
scope.$emit('RemoveGroup');
|
||||
})
|
||||
.error(function(data, status) {
|
||||
var target_name = (target) ? target.name : 'inventory';
|
||||
ProcessErrors(scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to add ' + group.name + ' to ' + target_name + '. POST returned: ' + status
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Respond to copy by adding the new group to the target
|
||||
url = (target) ?
|
||||
GetBasePath('groups') + target.id + '/children/' :
|
||||
GetBasePath('inventory') + inventory_id + '/groups/';
|
||||
|
||||
group = {
|
||||
id: group.id,
|
||||
name: group.name,
|
||||
description: group.description,
|
||||
inventory: inventory_id
|
||||
};
|
||||
|
||||
Rest.setUrl(url);
|
||||
Rest.post(group)
|
||||
.success(function() {
|
||||
vm.cancel();
|
||||
})
|
||||
.error(function(data, status) {
|
||||
var target_name = (target) ? target.name : 'inventory';
|
||||
ProcessErrors(scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to add ' + group.name + ' to ' + target_name + '. POST returned: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var copy_choice = 'copy';
|
||||
|
||||
angular.extend(vm, {
|
||||
cancel: cancel,
|
||||
performCopy: performCopy,
|
||||
copy_choice: copy_choice,
|
||||
name: name,
|
||||
allowSave: allowSave
|
||||
});
|
||||
}
|
||||
|
||||
export default ['$compile', '$state', '$scope', '$location', 'Rest', 'ProcessErrors', 'CreateDialog', 'GetBasePath', 'Wait', 'generateList', 'GroupList', 'SearchInit',
|
||||
'PaginateInit', 'GetRootGroups', 'ParamPass', 'Store', CopyGroupsCtrl
|
||||
];
|
||||
@ -1,22 +0,0 @@
|
||||
<div>
|
||||
<div class="Form-header">
|
||||
<div class="Form-title ng-binding">{{vm.name}}</div>
|
||||
</div>
|
||||
<div class="form-group copyMove-directive--copyMoveChoices">
|
||||
<label class="radio-inline">
|
||||
<input type="radio" ng-model="vm.copy_choice" value="copy" class="ng-pristine ng-valid ng-touched" name="199"> Copy
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" ng-model="vm.copy_choice" value="move" class="ng-pristine ng-untouched ng-valid" name="200"> Move
|
||||
</label>
|
||||
</div>
|
||||
<div id="copyMove-directive--copyGroupSelect"></div>
|
||||
<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix">
|
||||
<div class="ui-dialog-buttonset">
|
||||
<button type="button" class="btn btn-primary Form-saveButton" id="Inventory-copyGroup--saveButton" ng-disabled="!vm.allowSave" ng-click="vm.performCopy()">
|
||||
Save</button>
|
||||
<button type="button" class="btn btn-default Form-cancelButton" id="Inventory-copyGroup--cancelButton" ng-click="vm.cancel()">
|
||||
Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,239 +0,0 @@
|
||||
function CopyHostsCtrl($compile, $state, $scope, Rest, ProcessErrors, CreateDialog, GetBasePath, Wait, GenerateList, GroupList, SearchInit, PaginateInit, ParamPass, Store) {
|
||||
var vm = this;
|
||||
var name;
|
||||
|
||||
var host_id = $state.params.host_id;
|
||||
var inventory_id = $state.params.inventory_id;
|
||||
var url, host, group_scope, parent_scope, scope, parent_group;
|
||||
|
||||
var params = ParamPass.get();
|
||||
if (params !== undefined) {
|
||||
group_scope = params.group_scope;
|
||||
parent_scope = params.host_scope;
|
||||
parent_group = group_scope.selected_group_id;
|
||||
scope = parent_scope.$new();
|
||||
} else {
|
||||
group_scope = $scope.$new();
|
||||
parent_scope = $scope.$new();
|
||||
scope = parent_scope.$new();
|
||||
}
|
||||
|
||||
var PreviousSearchParams = Store('group_current_search_params');
|
||||
|
||||
if (scope.removeHostCopyPostRefresh) {
|
||||
scope.removeHostCopyPostRefresh();
|
||||
}
|
||||
scope.removeHostCopyPostRefresh = scope.$on('PostRefresh', function() {
|
||||
scope.copy_groups.forEach(function(row, i) {
|
||||
scope.copy_groups[i].checked = '0';
|
||||
});
|
||||
Wait('stop');
|
||||
// prevent backspace from navigation when not in input or textarea field
|
||||
$(document).on("keydown", function(e) {
|
||||
if (e.which === 8 && !$(e.target).is('input[type="text"], textarea')) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (scope.removeHostCopyDialogReady) {
|
||||
scope.removeHostCopyDialogReady();
|
||||
}
|
||||
scope.removeCopyDialogReady = scope.$on('HostCopyDialogReady', function() {
|
||||
var url = GetBasePath('inventory') + inventory_id + '/groups/';
|
||||
GenerateList.inject(GroupList, {
|
||||
mode: 'lookup',
|
||||
id: 'copyMove-directive--copyHostSelect',
|
||||
scope: scope
|
||||
//,
|
||||
//instructions: instructions
|
||||
});
|
||||
SearchInit({
|
||||
scope: scope,
|
||||
set: GroupList.name,
|
||||
list: GroupList,
|
||||
url: url
|
||||
});
|
||||
PaginateInit({
|
||||
scope: scope,
|
||||
list: GroupList,
|
||||
url: url,
|
||||
mode: 'lookup'
|
||||
});
|
||||
scope.search(GroupList.iterator, null, true, false);
|
||||
});
|
||||
|
||||
if (scope.removeShowDialog) {
|
||||
scope.removeShowDialog();
|
||||
}
|
||||
scope.removeShowDialog = scope.$on('ShowDialog', function() {
|
||||
var d;
|
||||
scope.name = host.name;
|
||||
d = angular.element(document.getElementById('copyMove-directive--copyHostPanel'));
|
||||
$compile(d)(scope);
|
||||
scope.$emit('HostCopyDialogReady');
|
||||
});
|
||||
|
||||
Wait('start');
|
||||
|
||||
url = GetBasePath('hosts') + host_id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
host = data;
|
||||
vm.name = host.name;
|
||||
scope.$emit('ShowDialog');
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors(scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. GET returned: ' + status
|
||||
});
|
||||
});
|
||||
|
||||
var restoreSearch = function() {
|
||||
// Restore search params and related stuff, plus refresh
|
||||
// groups and hosts lists
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: PreviousSearchParams.set,
|
||||
list: PreviousSearchParams.list,
|
||||
url: PreviousSearchParams.defaultUrl,
|
||||
iterator: PreviousSearchParams.iterator,
|
||||
sort_order: PreviousSearchParams.sort_order,
|
||||
setWidgets: false
|
||||
});
|
||||
$scope.refreshHostsOnGroupRefresh = true;
|
||||
};
|
||||
|
||||
var cancel = function() {
|
||||
$(document).off("keydown");
|
||||
restoreSearch(); // Restore all parent search stuff and refresh hosts and groups lists
|
||||
scope.$destroy();
|
||||
$state.go('inventoryManage', {}, {
|
||||
reload: true
|
||||
});
|
||||
};
|
||||
|
||||
var allowSave = false;
|
||||
scope['toggle_' + GroupList.iterator] = function(id) {
|
||||
var count = 0,
|
||||
list = GroupList;
|
||||
scope[list.name].forEach(function(row, i) {
|
||||
if (row.id === id) {
|
||||
if (row.checked) {
|
||||
scope[list.name][i].success_class = 'success';
|
||||
} else {
|
||||
scope[list.name][i].success_class = '';
|
||||
}
|
||||
} else {
|
||||
scope[list.name][i].checked = 0;
|
||||
scope[list.name][i].success_class = '';
|
||||
}
|
||||
});
|
||||
// Check if any rows are checked
|
||||
scope[list.name].forEach(function(row) {
|
||||
if (row.checked) {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
if (count === 0) {
|
||||
vm.allowSave = false;
|
||||
} else {
|
||||
vm.allowSave = true;
|
||||
}
|
||||
};
|
||||
|
||||
var performCopy = function() {
|
||||
var list = GroupList,
|
||||
target,
|
||||
url;
|
||||
|
||||
Wait('start');
|
||||
|
||||
if (scope.use_root_group) {
|
||||
target = null;
|
||||
} else {
|
||||
scope[list.name].every(function(row) {
|
||||
if (row.checked === 1) {
|
||||
target = row;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
if (vm.copy_choice === 'move') {
|
||||
// Respond to move
|
||||
// disassociate the host from the original parent
|
||||
if (scope.removeHostRemove) {
|
||||
scope.removeHostRemove();
|
||||
}
|
||||
scope.removeHostRemove = scope.$on('RemoveHost', function() {
|
||||
if (parent_group > 0) {
|
||||
// Only remove a host from a parent when the parent is a group and not the inventory root
|
||||
url = GetBasePath('groups') + parent_group + '/hosts/';
|
||||
Rest.setUrl(url);
|
||||
Rest.post({
|
||||
id: host.id,
|
||||
disassociate: 1
|
||||
})
|
||||
.success(function() {
|
||||
vm.cancel();
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors(scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to remove ' + host.name + ' from group ' + parent_group + '. POST returned: ' + status
|
||||
});
|
||||
});
|
||||
} else {
|
||||
vm.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
// add the new host to the target
|
||||
url = GetBasePath('groups') + target.id + '/hosts/';
|
||||
Rest.setUrl(url);
|
||||
Rest.post(host)
|
||||
.success(function() {
|
||||
scope.$emit('RemoveHost');
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors(scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to add ' + host.name + ' to ' + target.name + '. POST returned: ' + status
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Respond to copy by adding the new host to the target
|
||||
url = GetBasePath('groups') + target.id + '/hosts/';
|
||||
Rest.setUrl(url);
|
||||
Rest.post(host)
|
||||
.success(function() {
|
||||
vm.cancel();
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors(scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to add ' + host.name + ' to ' + target.name + '. POST returned: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var copy_choice = 'copy';
|
||||
|
||||
angular.extend(vm, {
|
||||
copy_choice: copy_choice,
|
||||
name: name,
|
||||
cancel: cancel,
|
||||
allowSave: allowSave,
|
||||
performCopy: performCopy
|
||||
});
|
||||
}
|
||||
|
||||
export default ['$compile', '$state', '$scope', 'Rest', 'ProcessErrors', 'CreateDialog', 'GetBasePath', 'Wait', 'generateList', 'GroupList', 'SearchInit',
|
||||
'PaginateInit', 'ParamPass', 'Store', CopyHostsCtrl
|
||||
];
|
||||
@ -1,23 +0,0 @@
|
||||
<div>
|
||||
<div class="Form-header">
|
||||
<div class="Form-title ng-binding">{{vm.name}}</div>
|
||||
</div>
|
||||
<div class="form-group copyMove-directive--copyMoveChoices">
|
||||
<label class="radio-inline">
|
||||
<input type="radio" ng-model="vm.copy_choice" value="copy" class="ng-pristine ng-valid ng-touched" name="199"> Copy
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" ng-model="vm.copy_choice" value="move" class="ng-pristine ng-untouched ng-valid" name="200"> Move
|
||||
</label>
|
||||
</div>
|
||||
<div id="copyMove-directive--copyHostSelect"></div>
|
||||
<div id="copyMove-directive--copyHostPanel"></div>
|
||||
<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix">
|
||||
<div class="ui-dialog-buttonset">
|
||||
<button type="button" class="btn btn-primary Form-saveButton" id="Inventory-copyGroup--saveButton" ng-disabled="!vm.allowSave" ng-click="vm.performCopy()">
|
||||
Save</button>
|
||||
<button type="button" class="btn btn-default Form-cancelButton" id="Inventory-copyGroup--cancelButton" ng-click="vm.cancel()">
|
||||
Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,16 +0,0 @@
|
||||
function inventoryManageCopyCtrl($state) {
|
||||
var vm = this;
|
||||
|
||||
var cancelPanel = function() {
|
||||
$state.go('inventoryManage', {}, {
|
||||
reload: true
|
||||
});
|
||||
};
|
||||
|
||||
angular.extend(vm, {
|
||||
cancelPanel: cancelPanel
|
||||
});
|
||||
}
|
||||
|
||||
export default ['$state', inventoryManageCopyCtrl
|
||||
];
|
||||
@ -1,10 +0,0 @@
|
||||
<div class="tab-pane" id="Inventory-copyMovePanel">
|
||||
<div ng-cloak id="Inventory-groupCopy--panel" class="Panel">
|
||||
<div class="Form-exitHolder">
|
||||
<button class="Form-exit" ng-click="vm.cancelPanel()">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
<ui-view></ui-view>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,54 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
import {
|
||||
templateUrl
|
||||
} from '../../../shared/template-url/template-url.factory';
|
||||
|
||||
import inventoryManageCopyCtrl from './copy.controller';
|
||||
import CopyGroupsCtrl from './copy-groups.controller';
|
||||
import CopyHostsCtrl from './copy-hosts.controller';
|
||||
|
||||
export default {
|
||||
copy: {
|
||||
name: 'inventoryManage.copy',
|
||||
route: '/copy',
|
||||
templateUrl: templateUrl('inventories/manage/copy/copy'),
|
||||
ncyBreadcrumb: {
|
||||
label: "COPY"
|
||||
},
|
||||
controller: inventoryManageCopyCtrl,
|
||||
controllerAs: 'vm',
|
||||
bindToController: true,
|
||||
},
|
||||
copyGroup: {
|
||||
name: 'inventoryManage.copy.group',
|
||||
route: '/group/:group_id?groups',
|
||||
templateUrl: templateUrl('inventories/manage/copy/copy-groups'),
|
||||
data: {
|
||||
group_id: 'group_id',
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "GROUP"
|
||||
},
|
||||
controller: CopyGroupsCtrl,
|
||||
controllerAs: 'vm',
|
||||
bindToController: true
|
||||
},
|
||||
copyHost: {
|
||||
name: 'inventoryManage.copy.host',
|
||||
route: '/host/:host_id?groups',
|
||||
templateUrl: templateUrl('inventories/manage/copy/copy-hosts'),
|
||||
data: {
|
||||
host_id: 'host_id',
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "HOST"
|
||||
},
|
||||
controller: CopyHostsCtrl,
|
||||
controllerAs: 'vm',
|
||||
bindToController: true
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,215 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$state', '$stateParams', '$scope', 'GroupForm', 'CredentialList', 'inventoryScriptsListObject', 'ParseTypeChange', 'GenerateForm', 'inventoryData', 'LookUpInit',
|
||||
'GroupManageService', 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions',
|
||||
function($state, $stateParams, $scope, GroupForm, CredentialList, InventoryScriptsList, ParseTypeChange, GenerateForm, inventoryData, LookUpInit,
|
||||
GroupManageService, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions){
|
||||
var generator = GenerateForm,
|
||||
form = GroupForm();
|
||||
|
||||
$scope.formCancel = function(){
|
||||
$state.go('^');
|
||||
};
|
||||
$scope.formSave = function(){
|
||||
var params, source;
|
||||
// group fields
|
||||
var group = {
|
||||
variables: $scope.variables === ('---' || '{}') ? null : $scope.variables,
|
||||
name: $scope.name,
|
||||
description: $scope.description,
|
||||
inventory: inventoryData.id
|
||||
};
|
||||
if ($scope.source){
|
||||
// inventory_source fields
|
||||
params = {
|
||||
instance_filters: $scope.instance_filters,
|
||||
source_vars: $scope[$scope.source.value + '_variables'] === ('---' || '{}') ? null : $scope[$scope.source.value + '_variables'],
|
||||
source_script: $scope.inventory_script,
|
||||
source: $scope.source.value,
|
||||
credential: $scope.credential,
|
||||
overwrite: $scope.overwrite,
|
||||
overwrite_vars: $scope.overwrite_vars,
|
||||
update_on_launch: $scope.update_on_launch,
|
||||
update_cache_timeout: $scope.update_cache_timeout || 0,
|
||||
// comma-delimited strings
|
||||
group_by: _.map($scope.group_by, 'value').join(','),
|
||||
source_regions: _.map($scope.source_regions, 'value').join(',')
|
||||
};
|
||||
source = $scope.source.value;
|
||||
}
|
||||
else{
|
||||
source = null;
|
||||
}
|
||||
switch(source){
|
||||
// no inventory source set, just create a new group
|
||||
// '' is the value supplied for Manual source type
|
||||
case null || '':
|
||||
GroupManageService.post(group).then(res => {
|
||||
// associate
|
||||
if ($stateParams.group){
|
||||
return GroupManageService.associateGroup(res.data, _.last($stateParams.group))
|
||||
.then(() => $state.go('^', null, {reload: true}));
|
||||
}
|
||||
else{
|
||||
$state.go('^', null, {reload: true});
|
||||
}
|
||||
});
|
||||
break;
|
||||
// create a new group and create/associate an inventory source
|
||||
// equal to case 'rax' || 'ec2' || 'azure' || 'azure_rm' || 'vmware' || 'foreman' || 'cloudforms' || 'openstack' || 'custom'
|
||||
default:
|
||||
GroupManageService.post(group)
|
||||
// associate to group
|
||||
.then(res => {
|
||||
if ($stateParams.group){
|
||||
GroupManageService.associateGroup(res.data, _.last($stateParams.group));
|
||||
return res;
|
||||
}
|
||||
else {return res;}
|
||||
// pass the original POST response and not the association response
|
||||
})
|
||||
.then(res => GroupManageService.putInventorySource(
|
||||
// put the received group ID into inventory source payload
|
||||
// and pass the related endpoint
|
||||
_.assign(params, {group: res.data.id}), res.data.related.inventory_source))
|
||||
.then(res => $state.go('inventoryManage.editGroup', {group_id: res.data.group}, {reload: true}));
|
||||
break;
|
||||
}
|
||||
};
|
||||
$scope.sourceChange = function(source){
|
||||
source = source.value === 'azure_rm' ? 'azure' : source.value;
|
||||
if (source === 'custom'){
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_script'),
|
||||
form: form,
|
||||
list: InventoryScriptsList,
|
||||
field: 'inventory_script',
|
||||
input_type: "radio"
|
||||
});
|
||||
}
|
||||
else if (source === 'ec2'){
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('credentials') + '?kind=aws',
|
||||
form: form,
|
||||
list: CredentialList,
|
||||
field: 'credential',
|
||||
input_type: "radio"
|
||||
});
|
||||
}
|
||||
// equal to case 'rax' || 'azure' || 'azure_rm' || 'vmware' || 'foreman' || 'cloudforms' || 'openstack' || 'custom'
|
||||
else{
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('credentials') + (source === '' ? '' : '?kind=' + source),
|
||||
form: form,
|
||||
list: CredentialList,
|
||||
field: 'credential',
|
||||
input_type: "radio"
|
||||
});
|
||||
}
|
||||
// reset fields
|
||||
$scope.group_by_choices = source === 'ec2' ? $scope.ec2_group_by : null;
|
||||
$scope.source_region_choices = $scope[source + '_regions'];
|
||||
$scope.cloudCredentialRequired = source !== 'manual' && source !== 'custom' ? true : false;
|
||||
$scope.group_by = null;
|
||||
$scope.source_regions = null;
|
||||
$scope.credential = null;
|
||||
$scope.credential_name = null;
|
||||
initRegionSelect();
|
||||
};
|
||||
var initRegionSelect = function(){
|
||||
CreateSelect2({
|
||||
element: '#group_source_regions',
|
||||
multiple: true
|
||||
});
|
||||
CreateSelect2({
|
||||
element: '#group_group_by',
|
||||
multiple: true
|
||||
});
|
||||
};
|
||||
var initSourceSelect = function(){
|
||||
CreateSelect2({
|
||||
element: '#group_source',
|
||||
multiple: false
|
||||
});
|
||||
};
|
||||
var initSources = function(){
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'rax_regions',
|
||||
choice_name: 'rax_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'ec2_regions',
|
||||
choice_name: 'ec2_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'gce_regions',
|
||||
choice_name: 'gce_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'azure_regions',
|
||||
choice_name: 'azure_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
// Load options for group_by
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'group_by',
|
||||
variable: 'ec2_group_by',
|
||||
choice_name: 'ec2_group_by_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
GetSourceTypeOptions({
|
||||
scope: $scope,
|
||||
variable: 'source_type_options',
|
||||
//callback: 'sourceTypeOptionsReady' this callback is hard-coded into GetSourceTypeOptions(), included for ref
|
||||
});
|
||||
};
|
||||
// region / source options callback
|
||||
$scope.$on('choicesReadyGroup', function(){
|
||||
initRegionSelect();
|
||||
});
|
||||
|
||||
$scope.$on('sourceTypeOptionsReady', function(){
|
||||
initSourceSelect();
|
||||
});
|
||||
var init = function(){
|
||||
$scope.parseType = 'yaml';
|
||||
$scope.variables = '---';
|
||||
generator.inject(form, {mode: 'add', related: false, id: 'Inventory-groupManage--panel', scope: $scope});
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
field_id: 'group_variables',
|
||||
variable: 'variables',
|
||||
});
|
||||
initSources();
|
||||
};
|
||||
init();
|
||||
}];
|
||||
@ -0,0 +1,288 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$state', '$stateParams', '$scope', 'GroupForm', 'CredentialList', 'inventoryScriptsListObject', 'ToggleNotification',
|
||||
'ParseTypeChange', 'GenerateForm', 'LookUpInit', 'RelatedSearchInit', 'RelatedPaginateInit', 'NotificationsListInit',
|
||||
'GroupManageService','GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', 'groupData', 'inventorySourceData',
|
||||
function($state, $stateParams, $scope, GroupForm, CredentialList, InventoryScriptsList, ToggleNotification,
|
||||
ParseTypeChange, GenerateForm, LookUpInit, RelatedSearchInit, RelatedPaginateInit, NotificationsListInit,
|
||||
GroupManageService, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions, groupData, inventorySourceData){
|
||||
var generator = GenerateForm,
|
||||
form = GroupForm();
|
||||
$scope.formCancel = function(){
|
||||
$state.go('^');
|
||||
};
|
||||
$scope.formSave = function(){
|
||||
var params, source;
|
||||
// group fields
|
||||
var group = {
|
||||
variables: $scope.variables === ('---' || '{}') ? null : $scope.variables,
|
||||
name: $scope.name,
|
||||
description: $scope.description,
|
||||
inventory: $scope.inventory,
|
||||
id: groupData.id
|
||||
};
|
||||
if ($scope.source){
|
||||
// inventory_source fields
|
||||
params = {
|
||||
group: groupData.id,
|
||||
source: $scope.source.value,
|
||||
credential: $scope.credential,
|
||||
overwrite: $scope.overwrite,
|
||||
overwrite_vars: $scope.overwrite_vars,
|
||||
source_script: $scope.inventory_script,
|
||||
update_on_launch: $scope.update_on_launch,
|
||||
update_cache_timeout: $scope.update_cache_timeout || 0,
|
||||
// comma-delimited strings
|
||||
group_by: _.map($scope.group_by, 'value').join(','),
|
||||
source_regions: _.map($scope.source_regions, 'value').join(','),
|
||||
instance_filters: $scope.instance_filters,
|
||||
source_vars: $scope[$scope.source.value + '_variables'] === '' ? null : $scope[$scope.source.value + '_variables']
|
||||
};
|
||||
source = $scope.source.value;
|
||||
}
|
||||
else{
|
||||
source = null;
|
||||
}
|
||||
switch(source){
|
||||
// no inventory source set, just create a new group
|
||||
// '' is the value supplied for Manual source type
|
||||
case null || '':
|
||||
GroupManageService.put(group).then(() => $state.go('^', null, {reload: true}));
|
||||
break;
|
||||
// create a new group and create/associate an inventory source
|
||||
// equal to case 'rax' || 'ec2' || 'azure' || 'azure_rm' || 'vmware' || 'foreman' || 'cloudforms' || 'openstack' || 'custom'
|
||||
default:
|
||||
GroupManageService.put(group)
|
||||
.then(() => GroupManageService.putInventorySource(params, groupData.related.inventory_source))
|
||||
.then(() => $state.go('^', null, {reload: true}));
|
||||
break;
|
||||
}
|
||||
};
|
||||
$scope.toggleNotification = function(event, notifier_id, column) {
|
||||
var notifier = this.notification;
|
||||
try {
|
||||
$(event.target).tooltip('hide');
|
||||
}
|
||||
catch(e) {
|
||||
// ignore
|
||||
}
|
||||
ToggleNotification({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
id: inventorySourceData.id,
|
||||
notifier: notifier,
|
||||
column: column,
|
||||
callback: 'NotificationRefresh'
|
||||
});
|
||||
};
|
||||
$scope.sourceChange = function(source){
|
||||
$scope.source = source;
|
||||
if (source.value === 'custom'){
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_script'),
|
||||
form: form,
|
||||
list: InventoryScriptsList,
|
||||
field: 'inventory_script',
|
||||
input_type: "radio"
|
||||
});
|
||||
}
|
||||
else if (source.value === 'ec2'){
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('credentials') + '?kind=aws',
|
||||
form: form,
|
||||
list: CredentialList,
|
||||
field: 'credential',
|
||||
input_type: "radio"
|
||||
});
|
||||
}
|
||||
else{
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('credentials') + (source.value === '' ? '' : '?kind=' + (source.value)),
|
||||
form: form,
|
||||
list: CredentialList,
|
||||
field: 'credential',
|
||||
input_type: "radio"
|
||||
});
|
||||
}
|
||||
// reset fields
|
||||
$scope.source_region_choices = $scope[source + '_regions'];
|
||||
$scope.cloudCredentialRequired = source !== 'manual' && source !== 'custom' ? true : false;
|
||||
$scope.group_by = null;
|
||||
$scope.source_regions = null;
|
||||
$scope.credential = null;
|
||||
$scope.credential_name = null;
|
||||
initRegionSelect();
|
||||
};
|
||||
var initRegionSelect = function(){
|
||||
CreateSelect2({
|
||||
element: '#group_source_regions',
|
||||
multiple: true
|
||||
});
|
||||
CreateSelect2({
|
||||
element: '#group_group_by',
|
||||
multiple: true
|
||||
});
|
||||
};
|
||||
var initSourceSelect = function(){
|
||||
$scope.source = _.find($scope.source_type_options, {value: inventorySourceData.source});
|
||||
CreateSelect2({
|
||||
element: '#group_source',
|
||||
multiple: false
|
||||
});
|
||||
};
|
||||
var initRegionData = function(){
|
||||
var source = $scope.source.value === 'azure_rm' ? 'azure' : $scope.source.value;
|
||||
var regions = inventorySourceData.source_regions.split(',');
|
||||
$scope.source_region_choices = $scope[source + '_regions'];
|
||||
$scope.source_regions = _.map(regions, (region) => _.find($scope[source+'_regions'], {value: region}));
|
||||
$scope.group_by_choices = source === 'ec2' ? $scope.ec2_group_by : null;
|
||||
if (source ==='ec2'){
|
||||
var group_by = inventorySourceData.group_by.split(',');
|
||||
$scope.group_by = _.map(group_by, (item) => _.find($scope.ec2_group_by, {value: item}));
|
||||
}
|
||||
initRegionSelect();
|
||||
};
|
||||
var initSources = function(){
|
||||
GetSourceTypeOptions({
|
||||
scope: $scope,
|
||||
variable: 'source_type_options',
|
||||
//callback: 'sourceTypeOptionsReady' this callback is hard-coded into GetSourceTypeOptions(), included for ref
|
||||
});
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'rax_regions',
|
||||
choice_name: 'rax_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'ec2_regions',
|
||||
choice_name: 'ec2_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'gce_regions',
|
||||
choice_name: 'gce_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'azure_regions',
|
||||
choice_name: 'azure_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'group_by',
|
||||
variable: 'ec2_group_by',
|
||||
choice_name: 'ec2_group_by_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
};
|
||||
// region / source options callback
|
||||
$scope.$on('choicesReadyGroup', function(){
|
||||
if (angular.isObject($scope.source)){
|
||||
initRegionData();
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('sourceTypeOptionsReady', function(){
|
||||
initSourceSelect();
|
||||
});
|
||||
var init = function(){
|
||||
// instantiate expected $scope values from inventorySourceData & groupData
|
||||
var relatedSets = form.relatedSets(groupData.related);
|
||||
generator.inject(form, {mode: 'edit', related: false, id: 'Inventory-groupManage--panel', scope: $scope});
|
||||
_.assign($scope,
|
||||
{credential: inventorySourceData.credential},
|
||||
{overwrite: inventorySourceData.overwrite},
|
||||
{overwrite_vars: inventorySourceData.overwrite_vars},
|
||||
{update_on_launch: inventorySourceData.update_on_launch},
|
||||
{update_cache_timeout: inventorySourceData.update_cache_timeout},
|
||||
{instance_filters: inventorySourceData.instance_filters},
|
||||
{inventory_script: inventorySourceData.source_script}
|
||||
);
|
||||
if(inventorySourceData.source === ('ec2' || 'openstack' || 'custom' || 'vmware')){
|
||||
$scope[inventorySourceData.source + '_variables'] = inventorySourceData.source_vars;
|
||||
}
|
||||
if (inventorySourceData.credential){
|
||||
GroupManageService.getCredential(inventorySourceData.credential).then(res => $scope.credential_name = res.data.name);
|
||||
}
|
||||
$scope = angular.extend($scope, groupData);
|
||||
$scope.variables = $scope.variables === (null || '') ? '---' : $scope.variables;
|
||||
$scope.parseType = 'yaml';
|
||||
// instantiate lookup fields
|
||||
if (inventorySourceData.source !== 'custom'){
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('credentials') + (inventorySourceData.source === '' ? '' : 'kind=' + (inventorySourceData.source)),
|
||||
form: form,
|
||||
list: CredentialList,
|
||||
field: 'credential',
|
||||
input_type: "radio"
|
||||
});
|
||||
}
|
||||
else if (inventorySourceData.source === 'ec2'){
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('credentials') + '?kind=aws',
|
||||
form: form,
|
||||
list: CredentialList,
|
||||
field: 'credential',
|
||||
input_type: "radio"
|
||||
});
|
||||
}
|
||||
// equal to case 'rax' || 'azure' || 'azure_rm' || 'vmware' || 'foreman' || 'cloudforms' || 'openstack' || 'custom'
|
||||
else{
|
||||
$scope.inventory_script_name = inventorySourceData.summary_fields.source_script.name;
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_script'),
|
||||
form: form,
|
||||
list: InventoryScriptsList,
|
||||
field: 'inventory_script',
|
||||
input_type: "radio"
|
||||
});
|
||||
}
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
field_id: 'group_variables',
|
||||
variable: 'variables',
|
||||
});
|
||||
NotificationsListInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
id: inventorySourceData.id
|
||||
});
|
||||
RelatedSearchInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
RelatedPaginateInit({
|
||||
scope: $scope,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
initSources();
|
||||
_.forEach(relatedSets, (value, key) => $scope.search(relatedSets[key].iterator));
|
||||
};
|
||||
init();
|
||||
}];
|
||||
@ -1,5 +1,4 @@
|
||||
<div class="tab-pane" id="Inventory-groupManage">
|
||||
<div ng-cloak id="Inventory-groupManage--panel" class="Panel">
|
||||
<manage-groups></manage-groups>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,145 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
export default
|
||||
['$scope', '$rootScope', '$state', '$stateParams', 'InventoryGroups', 'generateList', 'InventoryUpdate', 'GroupManageService', 'GroupsCancelUpdate', 'ViewUpdateStatus',
|
||||
'InventoryManageService', 'groupsUrl', 'SearchInit', 'PaginateInit', 'GetSyncStatusMsg', 'GetHostsStatusMsg',
|
||||
function($scope, $rootScope, $state, $stateParams, InventoryGroups, generateList, InventoryUpdate, GroupManageService, GroupsCancelUpdate, ViewUpdateStatus,
|
||||
InventoryManageService, groupsUrl, SearchInit, PaginateInit, GetSyncStatusMsg, GetHostsStatusMsg){
|
||||
var list = InventoryGroups,
|
||||
view = generateList,
|
||||
pageSize = 20;
|
||||
$scope.inventory_id = $stateParams.inventory_id;
|
||||
$scope.groupSelect = function(id){
|
||||
var group = $stateParams.group === undefined ? [id] : _($stateParams.group).concat(id).value();
|
||||
$state.go('inventoryManage', {inventory_id: $stateParams.inventory_id, group: group}, {reload: true});
|
||||
};
|
||||
$scope.createGroup = function(){
|
||||
$state.go('inventoryManage.addGroup');
|
||||
};
|
||||
$scope.editGroup = function(id){
|
||||
$state.go('inventoryManage.editGroup', {group_id: id});
|
||||
};
|
||||
$scope.deleteGroup = function(group){
|
||||
$scope.toDelete = {};
|
||||
angular.extend($scope.toDelete, group);
|
||||
$('#group-delete-modal').modal('show');
|
||||
};
|
||||
$scope.confirmDelete = function(){
|
||||
switch($scope.deleteOption){
|
||||
case 'promote':
|
||||
GroupManageService.promote($scope.toDelete.id, $stateParams.inventory_id)
|
||||
.then(() => {
|
||||
$state.go('inventoryManage', null, {reload: true});
|
||||
$('#group-delete-modal').modal('hide');
|
||||
});
|
||||
break;
|
||||
case 'delete':
|
||||
GroupManageService.delete($scope.toDelete.id).then(() => {
|
||||
$state.go('inventoryManage', null, {reload: true});
|
||||
$('#group-delete-modal').modal('hide');
|
||||
});
|
||||
}
|
||||
};
|
||||
$scope.updateGroup = function(group) {
|
||||
GroupManageService.getInventorySource({group: group.id}).then(res =>InventoryUpdate({
|
||||
scope: $scope,
|
||||
group_id: group.id,
|
||||
url: res.data.results[0].related.update,
|
||||
group_name: group.name,
|
||||
group_source: res.data.results[0].source
|
||||
}));
|
||||
$scope.$emit('WatchUpdateStatus'); // init socket io conneciton and start watching for status updates
|
||||
$rootScope.$on('JobStatusChange-inventory', (event, data) => {
|
||||
switch(data.status){
|
||||
case 'failed' || 'successful':
|
||||
$state.reload();
|
||||
break;
|
||||
default:
|
||||
var status = GetSyncStatusMsg({
|
||||
status: data.status,
|
||||
has_inventory_sources: group.has_inventory_sources,
|
||||
source: group.source
|
||||
});
|
||||
group.status = data.status;
|
||||
group.status_class = status.class;
|
||||
group.status_tooltip = status.tooltip;
|
||||
group.launch_tooltip = status.launch_tip;
|
||||
group.launch_class = status.launch_class;
|
||||
}
|
||||
});
|
||||
};
|
||||
$scope.cancelUpdate = function (id) {
|
||||
GroupsCancelUpdate({ scope: $scope, id: id });
|
||||
};
|
||||
$scope.viewUpdateStatus = function (id) {
|
||||
ViewUpdateStatus({
|
||||
scope: $scope,
|
||||
group_id: id
|
||||
});
|
||||
};
|
||||
$scope.showFailedHosts = function(x, y, z){
|
||||
$state.go('inventoryManage', {failed: true}, {reload: true});
|
||||
};
|
||||
$scope.scheduleGroup = function(id) {
|
||||
$state.go('inventoryManage.schedules', {id: id});
|
||||
};
|
||||
// $scope.$parent governed by InventoryManageController, for unified multiSelect options
|
||||
$scope.$on('multiSelectList.selectionChanged', (event, selection) => {
|
||||
$scope.$parent.groupsSelected = selection.length > 0 ? true : false;
|
||||
$scope.$parent.groupsSelectedItems = selection.selectedItems;
|
||||
});
|
||||
$scope.$on('PostRefresh', () =>{
|
||||
$scope.groups.forEach( (group, index) => {
|
||||
var group_status, hosts_status;
|
||||
group_status = GetSyncStatusMsg({
|
||||
status: group.summary_fields.inventory_source.status,
|
||||
has_inventory_sources: group.has_inventory_sources,
|
||||
source: ( (group.summary_fields.inventory_source) ? group.summary_fields.inventory_source.source : null )
|
||||
});
|
||||
hosts_status = GetHostsStatusMsg({
|
||||
active_failures: group.hosts_with_active_failures,
|
||||
total_hosts: group.total_hosts,
|
||||
inventory_id: $scope.inventory_id,
|
||||
group_id: group.id
|
||||
});
|
||||
_.assign($scope.groups[index],
|
||||
{status_class: group_status.class},
|
||||
{status_tooltip: group_status.tooltip},
|
||||
{launch_tooltip: group_status.launch_tip},
|
||||
{launch_class: group_status.launch_class},
|
||||
{hosts_status_tip: hosts_status.tooltip},
|
||||
{hosts_status_class: hosts_status.class},
|
||||
{source: group.summary_fields.inventory_source ? group.summary_fields.inventory_source.source : null},
|
||||
{status: group.summary_fields.inventory_source ? group.summary_fields.inventory_source.status : null});
|
||||
});
|
||||
});
|
||||
$scope.copyMoveGroup = function(id){
|
||||
$state.go('inventoryManage.copyMoveGroup', {group_id: id, groups: $stateParams.groups});
|
||||
};
|
||||
|
||||
var init = function(){
|
||||
list.basePath = groupsUrl;
|
||||
view.inject(list,{
|
||||
id: 'groups-list',
|
||||
$scope: $scope,
|
||||
mode: 'edit'
|
||||
});
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: groupsUrl,
|
||||
set: 'groups'
|
||||
});
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: groupsUrl,
|
||||
pageSize: pageSize
|
||||
});
|
||||
$scope.search(list.iterator);
|
||||
};
|
||||
init();
|
||||
}];
|
||||
@ -0,0 +1,73 @@
|
||||
<div id="groups-list" class="Panel"></div>
|
||||
<div class="modal fade GroupDelete" id="group-delete-modal" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content Modal-content">
|
||||
<div class="Modal-header">
|
||||
<div class="Modal-title ng-binding">Delete Group</div>
|
||||
<div class="Modal-exitHolder">
|
||||
<button class="close Modal-exit" data-target="#group-delete-modal" data-dismiss="modal" aria-hidden="true"><i class="fa fa-times-circle"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div ng-show="toDelete.total_groups > 0 || toDelete.total_hosts > 0">
|
||||
<div>
|
||||
<p class="Prompt-bodyQuery">Deleting group <em>{{ toDelete.name }}</em>.
|
||||
<span ng-show="toDelete.total_groups > 0 && toDelete.total_hosts > 0"> This group contains {{ toDelete.total_groups }} groups and {{ toDelete.total_hosts }} hosts. </span>
|
||||
<span ng-show="toDelete.total_groups == 0 && toDelete.total_hosts > 0"> This group contains {{ toDelete.total_hosts }} hosts. </span>
|
||||
<span ng-show="groupsCopunt > 0 && toDelete.total_hosts == 0"> This group contains {{ toDelete.total_groups }} groups. </span>
|
||||
Delete or promote the group's children?</p>
|
||||
<div style="margin: 15px auto;">
|
||||
|
||||
<div class="radio" ng-show="toDelete.total_groups > 0 && toDelete.total_hosts > 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" value="promote"> Promote groups and hosts
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio" ng-show="toDelete.total_groups > 0 && toDelete.total_hosts > 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" value="delete"> Delete groups and hosts
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="radio" ng-show="toDelete.total_groups > 0 && toDelete.total_hosts == 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" value="promote"> Promote groups
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio" ng-show="toDelete.total_groups > 0 && toDelete.total_hosts == 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" value="delete"> Delete groups
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="radio" ng-show="toDelete.total_groups == 0 && toDelete.total_hosts > 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" value="promote"> Promote hosts
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio" ng-show="toDelete.total_groups == 0 && toDelete.total_hosts > 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" value="delete"> Delete hosts
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div ng-show="toDelete.total_groups == 0 && toDelete.total_hosts == 0">
|
||||
<div class="Prompt-bodyQuery">Are you sure you want to permanently delete the group below from the inventory?</div>
|
||||
<div class="Prompt-bodyTarget">{{ toDelete.name }}</div>
|
||||
</div>
|
||||
<div class="Modal-footer">
|
||||
<div class="GroupDelete-help--container">
|
||||
<a href="" id="awp-promote" href="" aw-pop-over="<dl><dt>Delete</dt><dd>Deletes groups and hosts associated with the group being deleted. If a group or host is associated with other groups, it will still exist within those groups. Otherwise, the associated groups and hosts will no longer appear in the inventory.</dd>\n<dt style='margin-top: 5px;'>Promote</dt><dd>Groups and hosts associated with the group being removed will be promoted root level. Note: groups already associated with other groups cannot be promoted.</dd></dl>\n" aw-tool-tip="Click for help" data-placement="right" data-container="body" data-title="Help" class="help-link"><i class="fa fa-question-circle"></i> Click for help</a>
|
||||
</div>
|
||||
<a href="" ng-class="promptActionBtnClass" ng-click="confirmDelete()" id="prompt_action_btn" class="btn Modal-footerButton Modal-errorButton">DELETE</a>
|
||||
<a href="#" data-target="#group-delete-modal" data-dismiss="modal" id="prompt_cancel_btn" class="btn Modal-defaultButton Modal-footerButton">CANCEL</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,19 @@
|
||||
.select2-selection.select2-selection--multiple.Form-dropDown{
|
||||
height: auto !important;
|
||||
}
|
||||
.GroupDelete-help--container{
|
||||
margin-right: auto;
|
||||
}
|
||||
.GroupDelete .Modal-header{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.GroupDelete .modal-body{
|
||||
padding-top: 20px;
|
||||
}
|
||||
.Inventory-groupManage{
|
||||
// ugly hack to avoid the surface area of changing form generator's default classes
|
||||
.checkbox-inline{
|
||||
display: block;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
51
awx/ui/client/src/inventories/manage/groups/groups.route.js
Normal file
51
awx/ui/client/src/inventories/manage/groups/groups.route.js
Normal file
@ -0,0 +1,51 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
import {templateUrl} from '../../../shared/template-url/template-url.factory';
|
||||
import addController from './groups-add.controller';
|
||||
import editController from './groups-edit.controller';
|
||||
|
||||
var ManageGroupsEdit = {
|
||||
name: 'inventoryManage.editGroup',
|
||||
route: '/edit-group?group_id',
|
||||
ncyBreadcrumb: {
|
||||
label: "EDIT {{group.name}}"
|
||||
},
|
||||
data: {
|
||||
mode: 'edit'
|
||||
},
|
||||
resolve: {
|
||||
groupData: ['$stateParams', 'GroupManageService', function($stateParams, GroupManageService){
|
||||
return GroupManageService.get({id: $stateParams.group_id}).then(res => res.data.results[0]);
|
||||
}],
|
||||
inventorySourceData: ['$stateParams', 'GroupManageService', function($stateParams, GroupManageService){
|
||||
return GroupManageService.getInventorySource({group: $stateParams.group_id}).then(res => res.data.results[0]);
|
||||
}]
|
||||
},
|
||||
views: {
|
||||
'form@inventoryManage': {
|
||||
controller: editController,
|
||||
templateUrl: templateUrl('inventories/manage/groups/groups-form'),
|
||||
}
|
||||
}
|
||||
};
|
||||
var ManageGroupsAdd = {
|
||||
name: 'inventoryManage.addGroup',
|
||||
route: '/add-group',
|
||||
// use a query string to break regex search
|
||||
ncyBreadcrumb: {
|
||||
label: "ADD GROUP"
|
||||
},
|
||||
data: {
|
||||
mode: 'add'
|
||||
},
|
||||
views: {
|
||||
'form@inventoryManage': {
|
||||
controller: addController,
|
||||
templateUrl: templateUrl('inventories/manage/groups/groups-form'),
|
||||
}
|
||||
}
|
||||
};
|
||||
export {ManageGroupsAdd, ManageGroupsEdit};
|
||||
113
awx/ui/client/src/inventories/manage/groups/groups.service.js
Normal file
113
awx/ui/client/src/inventories/manage/groups/groups.service.js
Normal file
@ -0,0 +1,113 @@
|
||||
export default
|
||||
['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', function($rootScope, Rest, GetBasePath, ProcessErrors, Wait){
|
||||
return {
|
||||
stringifyParams: function(params){
|
||||
return _.reduce(params, (result, value, key) => {
|
||||
return result + key + '=' + value + '&';
|
||||
}, '');
|
||||
},
|
||||
// cute abstractions via fn.bind()
|
||||
url: function(){
|
||||
return '';
|
||||
},
|
||||
error: function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + this.url + '. GET returned: ' + status });
|
||||
},
|
||||
success: function(data){
|
||||
return data;
|
||||
},
|
||||
// HTTP methods
|
||||
get: function(params){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('groups') + '?' + this.stringifyParams(params);
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.get()
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
post: function(group){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('groups');
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.post(group)
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
put: function(group){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('groups') + group.id;
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.put(group)
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
delete: function(id){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('groups') + id;
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.destroy()
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
getCredential: function(id){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('credentials') + id;
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.get()
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
getInventorySource: function(params){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('inventory_sources') + '?' + this.stringifyParams(params);
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.get()
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
putInventorySource: function(params, url){
|
||||
Wait('start');
|
||||
this.url = url;
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.put(params)
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
// these relationship setters could be consolidated, but verbosity makes the operation feel more clear @ controller level
|
||||
associateGroup: function(group, target){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('groups') + target + '/children/';
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.post(group)
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
disassociateGroup: function(group, parent){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('groups') + parent + '/children/';
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.post({id: group, disassociate: 1})
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
promote: function(group, inventory){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('inventory') + inventory + '/groups/';
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.post({id: group, disassociate: 1})
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
}
|
||||
};
|
||||
}];
|
||||
14
awx/ui/client/src/inventories/manage/groups/main.js
Normal file
14
awx/ui/client/src/inventories/manage/groups/main.js
Normal file
@ -0,0 +1,14 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {ManageGroupsAdd, ManageGroupsEdit} from './groups.route';
|
||||
|
||||
export default
|
||||
angular.module('manageGroups', [])
|
||||
.run(['$stateExtender', function($stateExtender){
|
||||
$stateExtender.addState(ManageGroupsAdd);
|
||||
$stateExtender.addState(ManageGroupsEdit);
|
||||
}]);
|
||||
@ -5,16 +5,16 @@
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange', 'GenerateForm', 'ManageHostsService',
|
||||
function($state, $stateParams, $scope, HostForm, ParseTypeChange, GenerateForm, ManageHostsService){
|
||||
['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange', 'GenerateForm', 'HostManageService',
|
||||
function($state, $stateParams, $scope, HostForm, ParseTypeChange, GenerateForm, HostManageService){
|
||||
var generator = GenerateForm,
|
||||
form = HostForm;
|
||||
$scope.parseType = 'yaml';
|
||||
$scope.extraVars = '---';
|
||||
$scope.formCancel = function(){
|
||||
$state.go('^', null, {reload: true});
|
||||
$state.go('^');
|
||||
};
|
||||
$scope.toggleEnabled = function(){
|
||||
$scope.toggleHostEnabled = function(){
|
||||
$scope.host.enabled = !$scope.host.enabled;
|
||||
};
|
||||
$scope.formSave = function(){
|
||||
@ -25,8 +25,16 @@
|
||||
enabled: $scope.host.enabled,
|
||||
inventory: $stateParams.inventory_id
|
||||
};
|
||||
ManageHostsService.post(params).then(function(){
|
||||
$state.go('^', null, {reload: true});
|
||||
HostManageService.post(params).then(function(res){
|
||||
// assign the host to current group if not at the root level
|
||||
if ($stateParams.group){
|
||||
HostManageService.associateGroup(res.data, _.last($stateParams.group)).then(function(){
|
||||
$state.go('inventoryManage', null, {reload: true});
|
||||
});
|
||||
}
|
||||
else{
|
||||
$state.go('inventoryManage', null, {reload: true});
|
||||
}
|
||||
});
|
||||
};
|
||||
var init = function(){
|
||||
@ -5,13 +5,13 @@
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange', 'GenerateForm', 'ManageHostsService', 'host',
|
||||
function($state, $stateParams, $scope, HostForm, ParseTypeChange, GenerateForm, ManageHostsService, host){
|
||||
['$state', '$stateParams', '$scope', 'HostForm', 'ParseTypeChange', 'GenerateForm', 'HostManageService', 'host',
|
||||
function($state, $stateParams, $scope, HostForm, ParseTypeChange, GenerateForm, HostManageService, host){
|
||||
var generator = GenerateForm,
|
||||
form = HostForm;
|
||||
$scope.parseType = 'yaml';
|
||||
$scope.formCancel = function(){
|
||||
$state.go('^', null, {reload: true});
|
||||
$state.go('^');
|
||||
};
|
||||
$scope.toggleHostEnabled = function(){
|
||||
$scope.host.enabled = !$scope.host.enabled;
|
||||
@ -24,7 +24,7 @@
|
||||
description: $scope.description,
|
||||
enabled: $scope.host.enabled
|
||||
};
|
||||
ManageHostsService.put(host).then(function(){
|
||||
HostManageService.put(host).then(function(){
|
||||
$state.go('^', null, {reload: true});
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,84 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
export default
|
||||
['$scope', '$rootScope', '$state', '$stateParams', 'InventoryHosts', 'generateList', 'InventoryManageService', 'HostManageService',
|
||||
'hostsUrl', 'SearchInit', 'PaginateInit', 'SetStatus', 'Prompt', 'Wait', 'inventoryData',
|
||||
function($scope, $rootScope, $state, $stateParams, InventoryHosts, generateList, InventoryManageService, HostManageService,
|
||||
hostsUrl, SearchInit, PaginateInit, SetStatus, Prompt, Wait, inventoryData){
|
||||
var list = InventoryHosts,
|
||||
view = generateList,
|
||||
pageSize = 20;
|
||||
$scope.createHost = function(){
|
||||
$state.go('inventoryManage.addHost');
|
||||
};
|
||||
$scope.editHost = function(id){
|
||||
$state.go('inventoryManage.editHost', {host_id: id});
|
||||
};
|
||||
$scope.deleteHost = function(id, name){
|
||||
var body = '<div class=\"Prompt-bodyQuery\">Are you sure you want to permanently delete the host below from the inventory?</div><div class=\"Prompt-bodyTarget\">' + name + '</div>';
|
||||
var action = function(){
|
||||
delete $rootScope.promptActionBtnClass;
|
||||
Wait('start');
|
||||
HostManageService.delete(id).then(() => {
|
||||
$('#prompt-modal').modal('hide');
|
||||
$state.go($state.current.name, null, {reload: true});
|
||||
Wait('stop');
|
||||
});
|
||||
};
|
||||
// Prompt depends on having $rootScope.promptActionBtnClass available...
|
||||
Prompt({
|
||||
hdr: 'Delete Host',
|
||||
body: body,
|
||||
action: action,
|
||||
actionText: 'DELETE',
|
||||
});
|
||||
$rootScope.promptActionBtnClass = 'Modal-errorButton';
|
||||
};
|
||||
$scope.copyMoveHost = function(id){
|
||||
$state.go('inventoryManage.copyMoveHost', {host_id: id});
|
||||
};
|
||||
$scope.systemTracking = function(){
|
||||
var hostIds = _.map($scope.$parent.hostsSelectedItems, (host) => host.id);
|
||||
$state.go('systemTracking', {
|
||||
inventory: inventoryData,
|
||||
inventoryId: $stateParams.inventory_id,
|
||||
hosts: $scope.$parent.hostsSelectedItems,
|
||||
hostIds: hostIds
|
||||
});
|
||||
};
|
||||
// $scope.$parent governed by InventoryManageController, for unified multiSelect options
|
||||
$scope.$on('multiSelectList.selectionChanged', (event, selection) => {
|
||||
$scope.$parent.hostsSelected = selection.length > 0 ? true : false;
|
||||
$scope.$parent.hostsSelectedItems = selection.selectedItems;
|
||||
$scope.$parent.systemTrackingDisabled = selection.length > 0 && selection.length < 3 ? false : true;
|
||||
$scope.$parent.systemTrackingTooltip = selection.length === 1 ? "Compare host facts over time" : "Compare hosts' facts";
|
||||
});
|
||||
$scope.$on('PostRefresh', ()=>{
|
||||
_.forEach($scope.hosts, (host) => SetStatus({scope: $scope, host: host}));
|
||||
});
|
||||
var init = function(){
|
||||
list.basePath = hostsUrl;
|
||||
view.inject(list,{
|
||||
id: 'hosts-list',
|
||||
scope: $scope,
|
||||
mode: 'edit'
|
||||
});
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: hostsUrl,
|
||||
set: 'hosts'
|
||||
});
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: hostsUrl,
|
||||
pageSize: pageSize
|
||||
});
|
||||
$scope.search(list.iterator);
|
||||
};
|
||||
init();
|
||||
}];
|
||||
50
awx/ui/client/src/inventories/manage/hosts/hosts.route.js
Normal file
50
awx/ui/client/src/inventories/manage/hosts/hosts.route.js
Normal file
@ -0,0 +1,50 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
import {templateUrl} from '../../../shared/template-url/template-url.factory';
|
||||
import addController from './hosts-add.controller';
|
||||
import editController from './hosts-edit.controller';
|
||||
|
||||
var ManageHostsEdit = {
|
||||
name: 'inventoryManage.editHost',
|
||||
route: '/edit-host?host_id',
|
||||
ncyBreadcrumb: {
|
||||
label: "EDIT {{host.name}}",
|
||||
},
|
||||
data: {
|
||||
mode: 'edit'
|
||||
},
|
||||
resolve: {
|
||||
host: ['$stateParams', 'HostManageService', function($stateParams, HostManageService){
|
||||
return HostManageService.get({id: $stateParams.host_id}).then(function(res){
|
||||
return res.data.results[0];
|
||||
});
|
||||
}]
|
||||
},
|
||||
views: {
|
||||
'form@inventoryManage': {
|
||||
controller: editController,
|
||||
templateUrl: templateUrl('inventories/manage/hosts/hosts-form'),
|
||||
}
|
||||
}
|
||||
};
|
||||
var ManageHostsAdd = {
|
||||
name: 'inventoryManage.addHost',
|
||||
route: '/add-host',
|
||||
// use a query string to break regex search
|
||||
ncyBreadcrumb: {
|
||||
label: "ADD HOST"
|
||||
},
|
||||
data: {
|
||||
mode: 'add'
|
||||
},
|
||||
views: {
|
||||
'form@inventoryManage': {
|
||||
controller: addController,
|
||||
templateUrl: templateUrl('inventories/manage/hosts/hosts-form'),
|
||||
}
|
||||
}
|
||||
};
|
||||
export {ManageHostsAdd, ManageHostsEdit};
|
||||
84
awx/ui/client/src/inventories/manage/hosts/hosts.service.js
Normal file
84
awx/ui/client/src/inventories/manage/hosts/hosts.service.js
Normal file
@ -0,0 +1,84 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait',
|
||||
function($rootScope, Rest, GetBasePath, ProcessErrors, Wait){
|
||||
return {
|
||||
stringifyParams: function(params){
|
||||
return _.reduce(params, (result, value, key) => {
|
||||
return result + key + '=' + value + '&';
|
||||
}, '');
|
||||
},
|
||||
// cute abstractions via fn.bind()
|
||||
url: function(){
|
||||
return '';
|
||||
},
|
||||
error: function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + this.url + '. GET returned: ' + status });
|
||||
},
|
||||
success: function(data){
|
||||
return data;
|
||||
},
|
||||
// HTTP methods
|
||||
get: function(params){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('hosts') + '?' + this.stringifyParams(params);
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.get()
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
post: function(host){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('hosts');
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.post(host)
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
put: function(host){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('hosts') + host.id;
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.put(host)
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
delete: function(id){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('hosts') + id;
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.destroy()
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
// these relationship setters could be consolidated, but verbosity makes the operation feel more clear @ controller level
|
||||
associateGroup: function(host, group){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('groups') + group + '/hosts/';
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.post(host)
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
disassociateGroup: function(host, group){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('groups') + group + '/hosts/';
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.post({id: host.id, disassociate: 1})
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
}
|
||||
};
|
||||
}];
|
||||
@ -4,13 +4,11 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {ManageHostsAdd, ManageHostsEdit} from './manage-hosts.route';
|
||||
import service from './manage-hosts.service';
|
||||
import {ManageHostsAdd, ManageHostsEdit} from './hosts.route';
|
||||
|
||||
export default
|
||||
angular.module('manageHosts', [])
|
||||
.service('ManageHostsService', service)
|
||||
.run(['$stateExtender', function($stateExtender){
|
||||
.run(['$stateExtender', '$state', function($stateExtender, $state){
|
||||
$stateExtender.addState(ManageHostsAdd);
|
||||
$stateExtender.addState(ManageHostsEdit);
|
||||
}]);
|
||||
@ -0,0 +1,3 @@
|
||||
.InventoryManage-container{
|
||||
margin-top: -40px;
|
||||
}
|
||||
@ -3,532 +3,18 @@
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Inventories
|
||||
* @description This controller's for the Inventory page
|
||||
*/
|
||||
|
||||
function InventoriesManage($log, $scope, $rootScope, $location,
|
||||
$state, $compile, generateList, ClearScope, Empty, Wait, Rest, Alert,
|
||||
GetBasePath, ProcessErrors, InventoryGroups,
|
||||
InjectHosts, Find, HostsReload, SearchInit, PaginateInit, GetSyncStatusMsg,
|
||||
GetHostsStatusMsg, GroupsEdit, InventoryUpdate, GroupsCancelUpdate,
|
||||
ViewUpdateStatus, GroupsDelete, Store, HostsEdit, HostsDelete,
|
||||
EditInventoryProperties, ShowJobSummary,
|
||||
InventoryGroupsHelp, HelpDialog,
|
||||
GroupsCopy, HostsCopy, $stateParams, ParamPass) {
|
||||
|
||||
var PreviousSearchParams,
|
||||
url,
|
||||
hostScope = $scope.$new();
|
||||
|
||||
ClearScope();
|
||||
|
||||
// TODO: only display adhoc button if the user has permission to use it.
|
||||
// TODO: figure out how to get the action-list partial to update so that
|
||||
// the tooltip can be changed based off things being selected or not.
|
||||
$scope.adhocButtonTipContents = "Launch adhoc command for the inventory";
|
||||
|
||||
// watcher for the group list checkbox changes
|
||||
$scope.$on('multiSelectList.selectionChanged', function(e, selection) {
|
||||
if (selection.length > 0) {
|
||||
$scope.groupsSelected = true;
|
||||
// $scope.adhocButtonTipContents = "Launch adhoc command for the "
|
||||
// + "selected groups and hosts.";
|
||||
} else {
|
||||
$scope.groupsSelected = false;
|
||||
// $scope.adhocButtonTipContents = "Launch adhoc command for the "
|
||||
// + "inventory.";
|
||||
}
|
||||
$scope.groupsSelectedItems = selection.selectedItems;
|
||||
});
|
||||
|
||||
// watcher for the host list checkbox changes
|
||||
hostScope.$on('multiSelectList.selectionChanged', function(e, selection) {
|
||||
// you need this so that the event doesn't bubble to the watcher above
|
||||
// for the host list
|
||||
e.stopPropagation();
|
||||
var trackingButton = angular.element(document.querySelector('.system-tracking'));
|
||||
trackingButton.html('SYSTEM TRACKING');
|
||||
if (selection.length === 0) {
|
||||
$scope.hostsSelected = false;
|
||||
} else if (selection.length === 1) {
|
||||
$scope.systemTrackingTooltip = "Compare host over time";
|
||||
$scope.hostsSelected = true;
|
||||
$scope.systemTrackingDisabled = false;
|
||||
} else if (selection.length === 2) {
|
||||
$scope.systemTrackingTooltip = "Compare hosts against each other";
|
||||
$scope.hostsSelected = true;
|
||||
$scope.systemTrackingDisabled = false;
|
||||
} else {
|
||||
$scope.hostsSelected = true;
|
||||
$scope.systemTrackingDisabled = true;
|
||||
}
|
||||
$scope.hostsSelectedItems = selection.selectedItems;
|
||||
});
|
||||
|
||||
$scope.systemTracking = function() {
|
||||
var hostIds = _.map($scope.hostsSelectedItems, function(x){
|
||||
return x.id;
|
||||
});
|
||||
$state.transitionTo('systemTracking',
|
||||
{ inventory: $scope.inventory,
|
||||
inventoryId: $scope.inventory.id,
|
||||
hosts: $scope.hostsSelectedItems,
|
||||
hostIds: hostIds
|
||||
});
|
||||
};
|
||||
|
||||
// populates host patterns based on selected hosts/groups
|
||||
$scope.populateAdhocForm = function() {
|
||||
var host_patterns = "all";
|
||||
if ($scope.hostsSelected || $scope.groupsSelected) {
|
||||
var allSelectedItems = [];
|
||||
if ($scope.groupsSelectedItems) {
|
||||
allSelectedItems = allSelectedItems.concat($scope.groupsSelectedItems);
|
||||
}
|
||||
if ($scope.hostsSelectedItems) {
|
||||
allSelectedItems = allSelectedItems.concat($scope.hostsSelectedItems);
|
||||
}
|
||||
if (allSelectedItems) {
|
||||
host_patterns = _.pluck(allSelectedItems, "name").join(":");
|
||||
}
|
||||
}
|
||||
$rootScope.hostPatterns = host_patterns;
|
||||
$state.go('inventoryManage.adhoc');
|
||||
};
|
||||
|
||||
$scope.refreshHostsOnGroupRefresh = false;
|
||||
$scope.selected_group_id = null;
|
||||
|
||||
Wait('start');
|
||||
|
||||
|
||||
if ($scope.removeHostReloadComplete) {
|
||||
$scope.removeHostReloadComplete();
|
||||
}
|
||||
$scope.removeHostReloadComplete = $scope.$on('HostReloadComplete', function() {
|
||||
if ($scope.initial_height) {
|
||||
var host_height = $('#hosts-container .well').height(),
|
||||
group_height = $('#group-list-container .well').height(),
|
||||
new_height;
|
||||
|
||||
if (host_height > group_height) {
|
||||
new_height = host_height - (host_height - group_height);
|
||||
}
|
||||
else if (host_height < group_height) {
|
||||
new_height = host_height + (group_height - host_height);
|
||||
}
|
||||
if (new_height) {
|
||||
$('#hosts-container .well').height(new_height);
|
||||
}
|
||||
$scope.initial_height = null;
|
||||
}
|
||||
});
|
||||
|
||||
if ($scope.removeRowCountReady) {
|
||||
$scope.removeRowCountReady();
|
||||
}
|
||||
$scope.removeRowCountReady = $scope.$on('RowCountReady', function(e, rows) {
|
||||
// Add hosts view
|
||||
$scope.show_failures = false;
|
||||
InjectHosts({
|
||||
group_scope: $scope,
|
||||
host_scope: hostScope,
|
||||
inventory_id: $scope.inventory.id,
|
||||
tree_id: null,
|
||||
group_id: null,
|
||||
pageSize: rows
|
||||
});
|
||||
|
||||
SearchInit({ scope: $scope, set: 'groups', list: InventoryGroups, url: $scope.inventory.related.root_groups });
|
||||
PaginateInit({ scope: $scope, list: InventoryGroups , url: $scope.inventory.related.root_groups, pageSize: rows });
|
||||
$scope.search(InventoryGroups.iterator, null, true);
|
||||
});
|
||||
|
||||
if ($scope.removeInventoryLoaded) {
|
||||
$scope.removeInventoryLoaded();
|
||||
}
|
||||
$scope.removeInventoryLoaded = $scope.$on('InventoryLoaded', function() {
|
||||
var rows;
|
||||
generateList.inject(InventoryGroups, {
|
||||
mode: 'edit',
|
||||
id: 'group-list-container',
|
||||
searchSize: 'col-lg-6 col-md-6 col-sm-6 col-xs-12',
|
||||
scope: $scope
|
||||
});
|
||||
|
||||
rows = 20;
|
||||
hostScope.host_page_size = rows;
|
||||
$scope.group_page_size = rows;
|
||||
|
||||
$scope.show_failures = false;
|
||||
InjectHosts({
|
||||
group_scope: $scope,
|
||||
host_scope: hostScope,
|
||||
inventory_id: $scope.inventory.id,
|
||||
tree_id: null,
|
||||
group_id: null,
|
||||
pageSize: rows
|
||||
});
|
||||
|
||||
// Load data
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: 'groups',
|
||||
list: InventoryGroups,
|
||||
url: $scope.inventory.related.root_groups
|
||||
});
|
||||
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: InventoryGroups ,
|
||||
url: $scope.inventory.related.root_groups,
|
||||
pageSize: rows
|
||||
});
|
||||
|
||||
$scope.search(InventoryGroups.iterator, null, true);
|
||||
|
||||
$scope.$emit('WatchUpdateStatus'); // init socket io conneciton and start watching for status updates
|
||||
});
|
||||
|
||||
if ($scope.removePostRefresh) {
|
||||
$scope.removePostRefresh();
|
||||
}
|
||||
$scope.removePostRefresh = $scope.$on('PostRefresh', function(e, set) {
|
||||
if (set === 'groups') {
|
||||
$scope.groups.forEach( function(group, idx) {
|
||||
var stat, hosts_status;
|
||||
stat = GetSyncStatusMsg({
|
||||
status: group.summary_fields.inventory_source.status,
|
||||
has_inventory_sources: group.has_inventory_sources,
|
||||
source: ( (group.summary_fields.inventory_source) ? group.summary_fields.inventory_source.source : null )
|
||||
}); // from helpers/Groups.js
|
||||
$scope.groups[idx].status_class = stat['class'];
|
||||
$scope.groups[idx].status_tooltip = stat.tooltip;
|
||||
$scope.groups[idx].launch_tooltip = stat.launch_tip;
|
||||
$scope.groups[idx].launch_class = stat.launch_class;
|
||||
hosts_status = GetHostsStatusMsg({
|
||||
active_failures: group.hosts_with_active_failures,
|
||||
total_hosts: group.total_hosts,
|
||||
inventory_id: $scope.inventory.id,
|
||||
group_id: group.id
|
||||
}); // from helpers/Groups.js
|
||||
$scope.groups[idx].hosts_status_tip = hosts_status.tooltip;
|
||||
$scope.groups[idx].show_failures = hosts_status.failures;
|
||||
$scope.groups[idx].hosts_status_class = hosts_status['class'];
|
||||
|
||||
$scope.groups[idx].source = (group.summary_fields.inventory_source) ? group.summary_fields.inventory_source.source : null;
|
||||
$scope.groups[idx].status = (group.summary_fields.inventory_source) ? group.summary_fields.inventory_source.status : null;
|
||||
|
||||
});
|
||||
if ($scope.refreshHostsOnGroupRefresh) {
|
||||
$scope.refreshHostsOnGroupRefresh = false;
|
||||
HostsReload({
|
||||
scope: hostScope,
|
||||
group_id: $scope.selected_group_id,
|
||||
inventory_id: $scope.inventory.id,
|
||||
pageSize: hostScope.host_page_size
|
||||
});
|
||||
}
|
||||
else {
|
||||
Wait('stop');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Load Inventory
|
||||
url = GetBasePath('inventory') + $stateParams.inventory_id + '/';
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
$scope.inventory = data;
|
||||
$scope.$emit('InventoryLoaded');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve inventory: ' + $stateParams.inventory_id +
|
||||
' GET returned status: ' + status });
|
||||
});
|
||||
|
||||
// start watching for real-time updates
|
||||
if ($rootScope.removeWatchUpdateStatus) {
|
||||
$rootScope.removeWatchUpdateStatus();
|
||||
}
|
||||
$rootScope.removeWatchUpdateStatus = $rootScope.$on('JobStatusChange-inventory', function(e, data) {
|
||||
var stat, group;
|
||||
if (data.group_id) {
|
||||
group = Find({ list: $scope.groups, key: 'id', val: data.group_id });
|
||||
if (data.status === "failed" || data.status === "successful") {
|
||||
if (data.group_id === $scope.selected_group_id || group) {
|
||||
// job completed, fefresh all groups
|
||||
$log.debug('Update completed. Refreshing the tree.');
|
||||
$scope.refreshGroups();
|
||||
}
|
||||
}
|
||||
else if (group) {
|
||||
// incremental update, just update
|
||||
$log.debug('Status of group: ' + data.group_id + ' changed to: ' + data.status);
|
||||
stat = GetSyncStatusMsg({
|
||||
status: data.status,
|
||||
has_inventory_sources: group.has_inventory_sources,
|
||||
source: group.source
|
||||
});
|
||||
$log.debug('changing tooltip to: ' + stat.tooltip);
|
||||
group.status = data.status;
|
||||
group.status_class = stat['class'];
|
||||
group.status_tooltip = stat.tooltip;
|
||||
group.launch_tooltip = stat.launch_tip;
|
||||
group.launch_class = stat.launch_class;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Load group on selection
|
||||
function loadGroups(url) {
|
||||
SearchInit({ scope: $scope, set: 'groups', list: InventoryGroups, url: url });
|
||||
PaginateInit({ scope: $scope, list: InventoryGroups , url: url, pageSize: $scope.group_page_size });
|
||||
$scope.search(InventoryGroups.iterator, null, true, false, true);
|
||||
}
|
||||
|
||||
$scope.refreshHosts = function() {
|
||||
HostsReload({
|
||||
scope: hostScope,
|
||||
group_id: $scope.selected_group_id,
|
||||
inventory_id: $scope.inventory.id,
|
||||
pageSize: hostScope.host_page_size
|
||||
});
|
||||
};
|
||||
|
||||
$scope.refreshGroups = function() {
|
||||
$scope.refreshHostsOnGroupRefresh = true;
|
||||
$scope.search(InventoryGroups.iterator, null, true, false, true);
|
||||
};
|
||||
|
||||
$scope.restoreSearch = function() {
|
||||
// Restore search params and related stuff, plus refresh
|
||||
// groups and hosts lists
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: PreviousSearchParams.set,
|
||||
list: PreviousSearchParams.list,
|
||||
url: PreviousSearchParams.defaultUrl,
|
||||
iterator: PreviousSearchParams.iterator,
|
||||
sort_order: PreviousSearchParams.sort_order,
|
||||
setWidgets: false
|
||||
});
|
||||
$scope.refreshHostsOnGroupRefresh = true;
|
||||
$scope.search(InventoryGroups.iterator, null, true, false, true);
|
||||
};
|
||||
|
||||
$scope.groupSelect = function(id) {
|
||||
var groups = [], group = Find({ list: $scope.groups, key: 'id', val: id });
|
||||
if($state.params.groups){
|
||||
groups.push($state.params.groups);
|
||||
}
|
||||
groups.push(group.id);
|
||||
groups = groups.join();
|
||||
$state.transitionTo('inventoryManage', {inventory_id: $state.params.inventory_id, groups: groups}, { notify: false});
|
||||
loadGroups(group.related.children, group.id);
|
||||
$scope.selected_group_id = group.id;
|
||||
HostsReload({
|
||||
scope: hostScope,
|
||||
group_id: $scope.selected_group_id,
|
||||
inventory_id: $scope.inventory.id,
|
||||
pageSize: hostScope.host_page_size
|
||||
});
|
||||
};
|
||||
|
||||
$scope.createGroup = function () {
|
||||
PreviousSearchParams = Store('group_current_search_params');
|
||||
var params = {
|
||||
scope: $scope,
|
||||
inventory_id: $scope.inventory.id,
|
||||
group_id: $scope.selected_group_id,
|
||||
mode: 'add'
|
||||
};
|
||||
ParamPass.set(params);
|
||||
$state.go('inventoryManage.addGroup');
|
||||
};
|
||||
|
||||
$scope.editGroup = function (id) {
|
||||
PreviousSearchParams = Store('group_current_search_params');
|
||||
var params = {
|
||||
scope: $scope,
|
||||
inventory_id: $scope.inventory.id,
|
||||
group_id: id,
|
||||
mode: 'edit'
|
||||
};
|
||||
ParamPass.set(params);
|
||||
$state.go('inventoryManage.editGroup', {group_id: id});
|
||||
};
|
||||
|
||||
// Launch inventory sync
|
||||
$scope.updateGroup = function (id) {
|
||||
var group = Find({ list: $scope.groups, key: 'id', val: id });
|
||||
if (group) {
|
||||
if (Empty(group.source)) {
|
||||
// if no source, do nothing.
|
||||
} else if (group.status === 'updating') {
|
||||
Alert('Update in Progress', 'The inventory update process is currently running for group <em>' +
|
||||
group.name + '</em> Click the <i class="fa fa-refresh"></i> button to monitor the status.', 'alert-info', null, null, null, null, true);
|
||||
} else {
|
||||
Wait('start');
|
||||
Rest.setUrl(group.related.inventory_source);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
InventoryUpdate({
|
||||
scope: $scope,
|
||||
url: data.related.update,
|
||||
group_name: data.summary_fields.group.name,
|
||||
group_source: data.source,
|
||||
group_id: group.id,
|
||||
});
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to retrieve inventory source: ' +
|
||||
group.related.inventory_source + ' GET returned status: ' + status });
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cancelUpdate = function (id) {
|
||||
GroupsCancelUpdate({ scope: $scope, id: id });
|
||||
};
|
||||
|
||||
$scope.viewUpdateStatus = function (id) {
|
||||
ViewUpdateStatus({
|
||||
scope: $scope,
|
||||
group_id: id
|
||||
});
|
||||
};
|
||||
|
||||
$scope.scheduleGroup = function(id) {
|
||||
$state.go('inventoryManageSchedules', {
|
||||
inventory_id: $scope.inventory.id, id: id
|
||||
});
|
||||
};
|
||||
|
||||
$scope.copyGroup = function(id) {
|
||||
PreviousSearchParams = Store('group_current_search_params');
|
||||
var params = {
|
||||
scope: $scope
|
||||
};
|
||||
ParamPass.set(params);
|
||||
$location.search('groups', null);
|
||||
$state.go('inventoryManage.copy.group', {group_id: id});
|
||||
};
|
||||
|
||||
$scope.deleteGroup = function (id) {
|
||||
GroupsDelete({
|
||||
scope: $scope,
|
||||
group_id: id,
|
||||
inventory_id: $scope.inventory.id
|
||||
});
|
||||
};
|
||||
|
||||
$scope.editInventoryProperties = function () {
|
||||
// EditInventoryProperties({ scope: $scope, inventory_id: $scope.inventory.id });
|
||||
$location.path('/inventories/' + $scope.inventory.id + '/');
|
||||
};
|
||||
|
||||
hostScope.createHost = function () {
|
||||
var params = {
|
||||
host_scope: hostScope,
|
||||
group_scope: $scope,
|
||||
mode: 'add',
|
||||
host_id: null,
|
||||
selected_group_id: $scope.selected_group_id,
|
||||
inventory_id: $scope.inventory.id
|
||||
};
|
||||
ParamPass.set(params);
|
||||
$state.go('inventoryManage.addHost');
|
||||
};
|
||||
|
||||
hostScope.editHost = function (host_id) {
|
||||
var params = {
|
||||
host_scope: hostScope,
|
||||
group_scope: $scope,
|
||||
mode: 'edit',
|
||||
host_id: host_id,
|
||||
inventory_id: $scope.inventory.id
|
||||
};
|
||||
ParamPass.set(params);
|
||||
$state.go('inventoryManage.editHost', {host_id: host_id});
|
||||
};
|
||||
|
||||
hostScope.deleteHost = function (host_id, host_name) {
|
||||
HostsDelete({
|
||||
parent_scope: $scope,
|
||||
host_scope: hostScope,
|
||||
host_id: host_id,
|
||||
host_name: host_name
|
||||
});
|
||||
};
|
||||
|
||||
hostScope.copyHost = function(id) {
|
||||
PreviousSearchParams = Store('group_current_search_params');
|
||||
var params = {
|
||||
group_scope: $scope,
|
||||
host_scope: hostScope,
|
||||
host_id: id
|
||||
};
|
||||
|
||||
ParamPass.set(params);
|
||||
|
||||
$state.go('inventoryManage.copy.host', {host_id: id});
|
||||
};
|
||||
|
||||
hostScope.showJobSummary = function (job_id) {
|
||||
ShowJobSummary({
|
||||
job_id: job_id
|
||||
});
|
||||
};
|
||||
|
||||
$scope.showGroupHelp = function (params) {
|
||||
var opts = {
|
||||
defn: InventoryGroupsHelp
|
||||
};
|
||||
if (params) {
|
||||
opts.autoShow = params.autoShow || false;
|
||||
}
|
||||
HelpDialog(opts);
|
||||
}
|
||||
;
|
||||
$scope.showHosts = function (group_id, show_failures) {
|
||||
// Clicked on group
|
||||
if (group_id !== null) {
|
||||
Wait('start');
|
||||
hostScope.show_failures = show_failures;
|
||||
$scope.groupSelect(group_id);
|
||||
hostScope.hosts = [];
|
||||
$scope.show_failures = show_failures; // turn on failed hosts
|
||||
// filter in hosts view
|
||||
} else {
|
||||
Wait('stop');
|
||||
}
|
||||
};
|
||||
|
||||
if ($scope.removeGroupDeleteCompleted) {
|
||||
$scope.removeGroupDeleteCompleted();
|
||||
}
|
||||
$scope.removeGroupDeleteCompleted = $scope.$on('GroupDeleteCompleted',
|
||||
function() {
|
||||
$scope.refreshGroups();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default [
|
||||
'$log', '$scope', '$rootScope', '$location',
|
||||
'$state', '$compile', 'generateList', 'ClearScope', 'Empty', 'Wait',
|
||||
'Rest', 'Alert', 'GetBasePath', 'ProcessErrors',
|
||||
'InventoryGroups', 'InjectHosts', 'Find', 'HostsReload',
|
||||
'SearchInit', 'PaginateInit', 'GetSyncStatusMsg', 'GetHostsStatusMsg',
|
||||
'GroupsEdit', 'InventoryUpdate', 'GroupsCancelUpdate', 'ViewUpdateStatus',
|
||||
'GroupsDelete', 'Store', 'HostsEdit', 'HostsDelete',
|
||||
'EditInventoryProperties', 'ShowJobSummary', 'InventoryGroupsHelp', 'HelpDialog', 'GroupsCopy',
|
||||
'HostsCopy', '$stateParams', 'ParamPass', InventoriesManage
|
||||
];
|
||||
export default
|
||||
['$scope', '$state', function($scope, $state){
|
||||
$scope.groupsSelected = false;
|
||||
$scope.hostsSelected = false;
|
||||
$scope.hostsSelectedItems = [];
|
||||
$scope.groupsSelectedItems = [];
|
||||
$scope.setAdhocPattern = function(){
|
||||
var pattern = _($scope.groupsSelectedItems)
|
||||
.concat($scope.hostsSelectedItems)
|
||||
.map(function(item){
|
||||
return item.name;
|
||||
}).value().join(':');
|
||||
$state.go('inventoryManage.adhoc', {pattern: pattern});
|
||||
};
|
||||
}];
|
||||
@ -1,152 +1,10 @@
|
||||
<div class="tab-pane" id="inventory_edit">
|
||||
<div ui-view></div>
|
||||
<div class="tab-pane InventoryManage-container" id="inventory_edit">
|
||||
<div ui-view="form"></div>
|
||||
<div ng-cloak id="htmlTemplate">
|
||||
<div class="row">
|
||||
<div id="groups-container" class="col-lg-6">
|
||||
<div id="group-list-container" class="Panel"></div>
|
||||
</div>
|
||||
<div id="hosts-container" class="col-lg-6">
|
||||
<div id="host-list-container" class="Panel"></div>
|
||||
</div>
|
||||
<div ui-view="groupsList" class="col-lg-6"></div>
|
||||
<div ui-view="hostsList" class="col-lg-6"></div>
|
||||
</div>
|
||||
<div id="group-copy-dialog" style="display: none;">
|
||||
<div id="copy-group-radio-container" class="well">
|
||||
<div class="title"><span class="highlight">1.</span> Copy or move <span ng-bind="name"></span>?</div>
|
||||
<div class="form-group">
|
||||
<label class="radio-inline">
|
||||
<input type="radio" ng-model="copy_choice" value="copy"> Copy
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" ng-model="copy_choice" value="move"> Move
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="copy-group-target-container" class="well">
|
||||
<div class="title" ng-show="offer_root_group"><span class="highlight">2.</span> Select a target group below, OR choose the inventory root:
|
||||
<div class="form-group">
|
||||
<label><input type="checkbox" ng-model="use_root_group" ng-change="toggleUseRootGroup()"> Use the inventory root</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="title" ng-show="!offer_root_group"><span class="highlight">2.</span> Select a target group:</div>
|
||||
<div id="copy-select-container" ng-show="!use_root_group"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="host-copy-dialog" style="display: none;">
|
||||
<div id="copy-group-radio-container" class="well">
|
||||
<div class="title"><span class="highlight">1.</span> Copy or move <span ng-bind="name"></span>?</div>
|
||||
<div class="form-group">
|
||||
<label class="radio-inline">
|
||||
<input type="radio" ng-model="copy_choice" value="copy"> Copy
|
||||
</label>
|
||||
<label class="radio-inline">
|
||||
<input type="radio" ng-model="copy_choice" value="move"> Move
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="copy-group-target-container" class="well">
|
||||
<div class="title"><span class="highlight">2.</span> Select a target group:</div>
|
||||
<div id="copy-host-select-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="group-modal-dialog" style="display: none;">
|
||||
<ul id="group_tabs" class="nav nav-tabs">
|
||||
<li class="active"><a id="properties_link" ng-click="toggleTab($event, 'properties_link', 'group_tabs')"
|
||||
href="#properties-tab" data-toggle="tab">Properties</a></li>
|
||||
<li ng-show="showSourceTab"><a id="source_link" ng-click="toggleTab($event, 'source_link', 'group_tabs')"
|
||||
href="#sources-tab" data-toggle="tab">Source</a></li>
|
||||
<li ng-show="showSchedulesTab"><a id="schedules_link" ng-click="toggleTab($event, 'schedules_link', 'group_tabs')"
|
||||
href="#schedules-tab" data-toggle="tab">Schedule</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="properties-tab"></div>
|
||||
<div class="tab-pane" id="sources-tab"></div>
|
||||
<div class="tab-pane" id="schedules-tab">
|
||||
<div id="schedules-overlay"></div>
|
||||
<div id="schedules-list"></div>
|
||||
<div id="schedules-form-container">
|
||||
<div id="schedules-title">
|
||||
<h4 ng-bind="schedulesTitle"></h4>
|
||||
<button type="button" class="close pull-right" ng-click="cancelScheduleForm()">x</button>
|
||||
</div>
|
||||
<div id="schedules-form-container-body">
|
||||
<div id="schedules-form"></div>
|
||||
<div id="schedules-detail"></div>
|
||||
</div>
|
||||
<div id="schedules-buttons">
|
||||
<a id="schedules-flip-link" ng-show="formShowing" ng-click="showScheduleDetail()" href=""><i class="fa fa-search-plus"></i> View Details</a>
|
||||
<a id="schedules-flip-link" ng-show="!formShowing" ng-click="showScheduleDetail()" href=""><i class="fa fa-arrow-circle-left"></i> Back to options</a>
|
||||
<button type="button" class="btn btn-default btn-sm" id="reset-button" ng-click="cancelScheduleForm()"><i class="fa fa-times"></i> Cancel</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" id="save-button" ng-click="saveScheduleForm()"><i class="fa fa-check"></i> Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="host-modal-dialog" style="display: none;" class="dialog-content"></div>
|
||||
|
||||
<div id="group-delete-dialog" style="display: none;" class="dialog-content">
|
||||
<div ng-show="groupsCount > 0 || hostsCount > 0">
|
||||
<div class="alert alert-info">
|
||||
<p>Deleting group <em>{{ group_name }}</em>.
|
||||
<span ng-show="groupsCount > 0 && hostsCount > 0"> This group contains {{ groupsCount }} groups and {{ hostsCount }} hosts. </span>
|
||||
<span ng-show="groupsCount == 0 && hostsCount > 0"> This group contains {{ hostsCount }} hosts. </span>
|
||||
<span ng-show="groupsCopunt > 0 && hostsCount == 0"> This group contains {{ groupsCount }} groups. </span>
|
||||
Delete or promote the group's children?</p>
|
||||
<div style="width: 50%; margin: 15px auto;">
|
||||
|
||||
<div class="radio" ng-show="groupsCount > 0 && hostsCount > 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" name="delete_option1" value="preserve-all"> Promote groups and hosts
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio" ng-show="groupsCount > 0 && hostsCount > 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" name="delete_option1" value="delete-all"> Delete groups and hosts
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="radio" ng-show="groupsCount > 0 && hostsCount == 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" name="delete_option2" value="preserve-all"> Promote groups
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio" ng-show="groupsCount > 0 && hostsCount == 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" name="delete_option2" value="delete-all"> Delete groups
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="radio" ng-show="groupsCount == 0 && hostsCount > 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" name="delete_option3" value="preserve-all"> Promote hosts
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio" ng-show="groupsCount == 0 && hostsCount > 0">
|
||||
<label>
|
||||
<input type="radio" ng-model="deleteOption" name="delete_option3" value="delete-all"> Delete hosts
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="help-container" style="text-align:right;">
|
||||
<a href="" id="awp-promote" href="" aw-pop-over="{{ helpText }}" aw-tool-tip="Click for help" aw-pop-over-watch="helpText" data-placement="top" data-container="body" data-title="Help" class="help-link"><i class="fa fa-question-circle"></i> click for help</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div ng-show="groupsCount == 0 && hostsCount == 0">
|
||||
<div class="alert alert-info">Delete group <em>{{ group_name }}</em>?</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="inventory-edit-modal-dialog"></div>
|
||||
|
||||
<div ng-include="'/static/partials/logviewer.html'"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -6,18 +6,71 @@
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
import InventoriesManage from './inventory-manage.controller';
|
||||
import BreadcrumbsController from './breadcrumbs/breadcrumbs.controller';
|
||||
import HostsListController from './hosts/hosts-list.controller';
|
||||
import GroupsListController from './groups/groups-list.controller';
|
||||
|
||||
export default {
|
||||
name: 'inventoryManage',
|
||||
url: '/inventories/:inventory_id/manage?groups',
|
||||
templateUrl: templateUrl('inventories/manage/inventory-manage'),
|
||||
controller: InventoriesManage,
|
||||
url: '/inventories/:inventory_id/manage?{group:int}{failed}',
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'inventory',
|
||||
activityStreamId: 'inventory_id'
|
||||
},
|
||||
params:{
|
||||
group:{
|
||||
array: true
|
||||
},
|
||||
failed:{
|
||||
value: 'false',
|
||||
squash: true
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORY MANAGE"
|
||||
skip: true // Never display this state in ncy-breadcrumb.
|
||||
},
|
||||
// enforce uniqueness in group param
|
||||
onEnter: function($stateParams){
|
||||
$stateParams.group = _.uniq($stateParams.group);
|
||||
},
|
||||
resolve: {
|
||||
groupsUrl: ['InventoryManageService', '$stateParams', function(InventoryManageService, $stateParams){
|
||||
return !$stateParams.group ?
|
||||
InventoryManageService.rootGroupsUrl($stateParams.inventory_id) :
|
||||
InventoryManageService.childGroupsUrl(_.last($stateParams.group));
|
||||
}],
|
||||
hostsUrl: ['InventoryManageService', '$stateParams', function(InventoryManageService, $stateParams){
|
||||
// at the root group level
|
||||
return !$stateParams.group ?
|
||||
InventoryManageService.rootHostsUrl($stateParams.inventory_id, $stateParams.failed) :
|
||||
InventoryManageService.childHostsUrl(_.last($stateParams.group, $stateParams.failed));
|
||||
}],
|
||||
inventoryData: ['InventoryManageService', '$stateParams', function(InventoryManageService, $stateParams){
|
||||
return InventoryManageService.getInventory($stateParams.inventory_id).then(res => res.data);
|
||||
}],
|
||||
breadCrumbData: ['InventoryManageService', '$stateParams', function(InventoryManageService, $stateParams){
|
||||
return ( (!$stateParams.group) ? false : InventoryManageService.getBreadcrumbs($stateParams.group).then(res => res.data.results));
|
||||
}]
|
||||
},
|
||||
views:{
|
||||
// target the ui-view with name "groupBreadcrumbs" at the root template level
|
||||
'groupBreadcrumbs@': {
|
||||
controller: BreadcrumbsController,
|
||||
templateUrl: templateUrl('inventories/manage/breadcrumbs/breadcrumbs')
|
||||
},
|
||||
'': {
|
||||
templateUrl: templateUrl('inventories/manage/inventory-manage'),
|
||||
controller: InventoriesManage
|
||||
},
|
||||
// target ui-views with name@inventoryManage template level
|
||||
'groupsList@inventoryManage': {
|
||||
templateUrl: templateUrl('inventories/manage/groups/groups-list'),
|
||||
controller: GroupsListController
|
||||
},
|
||||
'hostsList@inventoryManage': {
|
||||
template: '<div id="hosts-list" class="Panel"></div>',
|
||||
controller: HostsListController
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait',
|
||||
function($rootScope, Rest, GetBasePath, ProcessErrors, Wait){
|
||||
return {
|
||||
// cute abstractions via fn.bind()
|
||||
url: function(){
|
||||
return '';
|
||||
},
|
||||
error: function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + this.url + '. GET returned: ' + status });
|
||||
},
|
||||
success: function(data){
|
||||
return data;
|
||||
},
|
||||
// data getters
|
||||
getInventory: function(id){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('inventory') + id;
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.get()
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this))
|
||||
.finally(Wait('stop'));
|
||||
},
|
||||
getBreadcrumbs: function(groups){
|
||||
Wait('start');
|
||||
this.url = GetBasePath('groups') + '?' + _.map(groups, function(item){
|
||||
return '&or__id=' + item;
|
||||
}).join('');
|
||||
Rest.setUrl(this.url);
|
||||
return Rest.get()
|
||||
.success(this.success.bind(this))
|
||||
.error(this.error.bind(this));
|
||||
},
|
||||
// these methods generate a query string to pass to PaginateInit(), SearchInit()
|
||||
// always supply trailing slashes and ? prefix
|
||||
rootHostsUrl: function(id, failed){
|
||||
var url = GetBasePath('inventory') + id + '/hosts' +
|
||||
(failed === 'true' ? '?has_active_failures=true' : '?');
|
||||
return url;
|
||||
},
|
||||
childHostsUrl: function(id, failed){
|
||||
var url = GetBasePath('groups') + id + '/hosts' +
|
||||
(failed === 'true' ? '?has_active_failures=true' : '?');
|
||||
return url;
|
||||
},
|
||||
childGroupsUrl: function(id){
|
||||
var url = GetBasePath('groups') + id + '/children?';
|
||||
return url;
|
||||
},
|
||||
rootGroupsUrl: function(id){
|
||||
var url = GetBasePath('inventory') + id+ '/root_groups/';
|
||||
return url;
|
||||
}
|
||||
};
|
||||
}];
|
||||
@ -1,21 +1,28 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './inventory-manage.route';
|
||||
|
||||
import manageHosts from './manage-hosts/main';
|
||||
import manageGroups from './manage-groups/main';
|
||||
import copy from './copy/main';
|
||||
import InventoryManageService from './inventory-manage.service';
|
||||
import HostManageService from './hosts/hosts.service';
|
||||
import GroupManageService from './groups/groups.service';
|
||||
import hosts from './hosts/main';
|
||||
import groups from './groups/main';
|
||||
import adhoc from './adhoc/main';
|
||||
import copyMove from './copy-move/main';
|
||||
|
||||
export default
|
||||
angular.module('inventoryManage', [
|
||||
manageHosts.name,
|
||||
manageGroups.name,
|
||||
copy.name,
|
||||
hosts.name,
|
||||
groups.name,
|
||||
copyMove.name,
|
||||
adhoc.name
|
||||
])
|
||||
.service('InventoryManageService', InventoryManageService)
|
||||
.service('HostManageService', HostManageService)
|
||||
.service('GroupManageService', GroupManageService)
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
|
||||
@ -1,574 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
function manageGroupsDirectiveController($filter, $location, $log,
|
||||
$stateParams, $compile, $state, $scope, Rest, Alert, GroupForm,
|
||||
GenerateForm, Prompt, ProcessErrors, GetBasePath, SetNodeName,
|
||||
ParseTypeChange, GetSourceTypeOptions, InventoryUpdate, LookUpInit, Empty,
|
||||
Wait, GetChoices, UpdateGroup, SourceChange, Find, ParseVariableString,
|
||||
ToJSON, GroupsScheduleListInit, SetSchedulesInnerDialogSize,
|
||||
CreateSelect2, ToggleNotification, NotificationsListInit,
|
||||
RelatedSearchInit, RelatedPaginateInit) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
var group_id = $stateParams.group_id,
|
||||
mode = $state.current.data.mode,
|
||||
inventory_id = $stateParams.inventory_id,
|
||||
generator = GenerateForm,
|
||||
group_created = false,
|
||||
defaultUrl,
|
||||
master = {},
|
||||
form = GroupForm(),
|
||||
relatedSets = {},
|
||||
choicesReady, group;
|
||||
|
||||
if (mode === 'edit') {
|
||||
defaultUrl = GetBasePath('groups') + group_id + '/';
|
||||
} else {
|
||||
defaultUrl = (group_id !== undefined) ? GetBasePath('groups') + group_id + '/children/' :
|
||||
GetBasePath('inventory') + inventory_id + '/groups/';
|
||||
}
|
||||
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
group = data;
|
||||
for (var fld in form.fields) {
|
||||
if (data[fld]) {
|
||||
$scope[fld] = data[fld];
|
||||
master[fld] = $scope[fld];
|
||||
}
|
||||
}
|
||||
if(mode === 'edit') {
|
||||
$scope.variable_url = data.related.variable_data;
|
||||
$scope.source_url = data.related.inventory_source;
|
||||
$scope.source_id = data.related.inventory_source.split('/')[4];
|
||||
$scope.$emit('LoadSourceData');
|
||||
}
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($scope, data, status, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve group: ' + defaultUrl + '. GET status: ' + status
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$scope.parseType = 'yaml';
|
||||
|
||||
generator.inject(form, {
|
||||
mode: mode,
|
||||
id: 'group-manage-panel',
|
||||
scope: $scope,
|
||||
related: false,
|
||||
cancelButton: false
|
||||
});
|
||||
|
||||
generator.reset();
|
||||
|
||||
GetSourceTypeOptions({
|
||||
scope: $scope,
|
||||
variable: 'source_type_options'
|
||||
});
|
||||
|
||||
$scope.source = form.fields.source['default'];
|
||||
$scope.sourcePathRequired = false;
|
||||
$scope[form.fields.source_vars.parseTypeName] = 'yaml';
|
||||
$scope.update_cache_timeout = 0;
|
||||
$scope.parseType = 'yaml';
|
||||
|
||||
|
||||
function initSourceChange() {
|
||||
$scope.showSchedulesTab = (mode === 'edit' && $scope.source && $scope.source.value !== "manual") ? true : false;
|
||||
SourceChange({
|
||||
scope: $scope,
|
||||
form: form
|
||||
});
|
||||
}
|
||||
|
||||
// JT -- this gets called after the properties & properties variables are loaded, and is emitted from (groupLoaded)
|
||||
if ($scope.removeLoadSourceData) {
|
||||
$scope.removeLoadSourceData();
|
||||
}
|
||||
$scope.removeLoadSourceData = $scope.$on('LoadSourceData', function() {
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
variable: 'variables',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'group_variables'
|
||||
});
|
||||
|
||||
NotificationsListInit({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
id: $scope.source_id
|
||||
});
|
||||
|
||||
if ($scope.source_url) {
|
||||
// get source data
|
||||
Rest.setUrl($scope.source_url);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
var fld, i, j, flag, found, set, opts, list;
|
||||
for (fld in form.fields) {
|
||||
if (fld === 'checkbox_group') {
|
||||
for (i = 0; i < form.fields[fld].fields.length; i++) {
|
||||
flag = form.fields[fld].fields[i];
|
||||
if (data[flag.name] !== undefined) {
|
||||
$scope[flag.name] = data[flag.name];
|
||||
master[flag.name] = $scope[flag.name];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fld === 'source') {
|
||||
found = false;
|
||||
data.source = (data.source === "") ? "manual" : data.source;
|
||||
for (i = 0; i < $scope.source_type_options.length; i++) {
|
||||
if ($scope.source_type_options[i].value === data.source) {
|
||||
$scope.source = $scope.source_type_options[i];
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found || $scope.source.value === "manual") {
|
||||
$scope.groupUpdateHide = true;
|
||||
} else {
|
||||
$scope.groupUpdateHide = false;
|
||||
}
|
||||
master.source = $scope.source;
|
||||
} else if (fld === 'source_vars') {
|
||||
// Parse source_vars, converting to YAML.
|
||||
$scope.source_vars = ParseVariableString(data.source_vars);
|
||||
master.source_vars = $scope.variables;
|
||||
} else if (fld === "inventory_script") {
|
||||
// the API stores it as 'source_script', we call it inventory_script
|
||||
data.summary_fields.inventory_script = data.summary_fields.source_script;
|
||||
$scope.inventory_script = data.source_script;
|
||||
master.inventory_script = $scope.inventory_script;
|
||||
} else if (fld === "source_regions") {
|
||||
if (data[fld] === "") {
|
||||
$scope[fld] = data[fld];
|
||||
master[fld] = $scope[fld];
|
||||
} else {
|
||||
$scope[fld] = data[fld].split(",");
|
||||
master[fld] = $scope[fld];
|
||||
}
|
||||
} else if (data[fld] !== undefined &&
|
||||
fld !== "description" &&
|
||||
fld !== "name" &&
|
||||
fld !== "variables") {
|
||||
$scope[fld] = data[fld];
|
||||
master[fld] = $scope[fld];
|
||||
}
|
||||
|
||||
if (form.fields[fld].sourceModel && data.summary_fields &&
|
||||
data.summary_fields[form.fields[fld].sourceModel]) {
|
||||
$scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] =
|
||||
data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField];
|
||||
}
|
||||
}
|
||||
relatedSets = form.relatedSets(data.related);
|
||||
RelatedSearchInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
|
||||
RelatedPaginateInit({
|
||||
scope: $scope,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
initSourceChange();
|
||||
|
||||
if (data.source_regions) {
|
||||
if (data.source === 'ec2' ||
|
||||
data.source === 'rax' ||
|
||||
data.source === 'gce' ||
|
||||
data.source === 'azure') {
|
||||
if (data.source === 'ec2') {
|
||||
set = $scope.ec2_regions;
|
||||
} else if (data.source === 'rax') {
|
||||
set = $scope.rax_regions;
|
||||
} else if (data.source === 'gce') {
|
||||
set = $scope.gce_regions;
|
||||
} else if (data.source === 'azure') {
|
||||
set = $scope.azure_regions;
|
||||
}
|
||||
opts = [];
|
||||
list = data.source_regions.split(',');
|
||||
for (i = 0; i < list.length; i++) {
|
||||
for (j = 0; j < set.length; j++) {
|
||||
if (list[i] === set[j].value) {
|
||||
opts.push({
|
||||
id: set [j].value,
|
||||
text: set [j].label
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
master.source_regions = opts;
|
||||
CreateSelect2({
|
||||
element: "group_source_regions",
|
||||
multiple: true,
|
||||
opts: opts
|
||||
});
|
||||
|
||||
}
|
||||
} else {
|
||||
// If empty, default to all
|
||||
master.source_regions = [{
|
||||
id: 'all',
|
||||
text: 'All'
|
||||
}];
|
||||
}
|
||||
if (data.group_by && data.source === 'ec2') {
|
||||
set = $scope.ec2_group_by;
|
||||
opts = [];
|
||||
list = data.group_by.split(',');
|
||||
for (i = 0; i < list.length; i++) {
|
||||
for (j = 0; j < set.length; j++) {
|
||||
if (list[i] === set[j].value) {
|
||||
opts.push({
|
||||
id: set [j].value,
|
||||
text: set [j].label
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
master.group_by = opts;
|
||||
CreateSelect2({
|
||||
element: "#group_group_by",
|
||||
multiple: true,
|
||||
opts: opts
|
||||
});
|
||||
}
|
||||
|
||||
$scope.group_update_url = data.related.update;
|
||||
for (set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
}
|
||||
})
|
||||
.error(function(data, status) {
|
||||
$scope.source = "";
|
||||
ProcessErrors($scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve inventory source. GET status: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if ($scope.remove$scopeSourceTypeOptionsReady) {
|
||||
$scope.remove$scopeSourceTypeOptionsReady();
|
||||
}
|
||||
$scope.remove$scopeSourceTypeOptionsReady = $scope.$on('sourceTypeOptionsReady', function() {
|
||||
if (mode === 'add') {
|
||||
$scope.source = Find({
|
||||
list: $scope.source_type_options,
|
||||
key: 'value',
|
||||
val: ''
|
||||
});
|
||||
$scope.showSchedulesTab = false;
|
||||
}
|
||||
});
|
||||
|
||||
choicesReady = 0;
|
||||
|
||||
if ($scope.removeChoicesReady) {
|
||||
$scope.removeChoicesReady();
|
||||
}
|
||||
$scope.removeChoicesReady = $scope.$on('choicesReadyGroup', function() {
|
||||
CreateSelect2({
|
||||
element: '#group_source',
|
||||
multiple: false
|
||||
});
|
||||
$scope.$emit('LoadSourceData');
|
||||
|
||||
choicesReady++;
|
||||
if (choicesReady === 5) {
|
||||
if (mode !== 'edit') {
|
||||
$scope.variables = "---";
|
||||
master.variables = $scope.variables;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Load options for source regions
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'rax_regions',
|
||||
choice_name: 'rax_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'ec2_regions',
|
||||
choice_name: 'ec2_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'gce_regions',
|
||||
choice_name: 'gce_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'azure_regions',
|
||||
choice_name: 'azure_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
// Load options for group_by
|
||||
GetChoices({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'group_by',
|
||||
variable: 'ec2_group_by',
|
||||
choice_name: 'ec2_group_by_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
//Wait('start');
|
||||
|
||||
if ($scope.removeAddTreeRefreshed) {
|
||||
$scope.removeAddTreeRefreshed();
|
||||
}
|
||||
$scope.removeAddTreeRefreshed = $scope.$on('GroupTreeRefreshed', function() {
|
||||
// Clean up
|
||||
Wait('stop');
|
||||
|
||||
if ($scope.searchCleanUp) {
|
||||
$scope.searchCleanup();
|
||||
}
|
||||
try {
|
||||
//$('#group-modal-dialog').dialog('close');
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
|
||||
if ($scope.removeSaveComplete) {
|
||||
$scope.removeSaveComplete();
|
||||
}
|
||||
$scope.removeSaveComplete = $scope.$on('SaveComplete', function(e, error) {
|
||||
if (!error) {
|
||||
$scope.formCancel();
|
||||
}
|
||||
});
|
||||
|
||||
if ($scope.removeFormSaveSuccess) {
|
||||
$scope.removeFormSaveSuccess();
|
||||
}
|
||||
$scope.removeFormSaveSuccess = $scope.$on('formSaveSuccess', function() {
|
||||
|
||||
// Source data gets stored separately from the group. Validate and store Source
|
||||
// related fields, then call SaveComplete to wrap things up.
|
||||
|
||||
var parseError = false,
|
||||
regions, r, i,
|
||||
group_by,
|
||||
data = {
|
||||
group: group_id,
|
||||
source: (($scope.source && $scope.source.value !== 'manual') ? $scope.source.value : ''),
|
||||
source_path: $scope.source_path,
|
||||
credential: $scope.credential,
|
||||
overwrite: $scope.overwrite,
|
||||
overwrite_vars: $scope.overwrite_vars,
|
||||
source_script: $scope.inventory_script,
|
||||
update_on_launch: $scope.update_on_launch,
|
||||
update_cache_timeout: ($scope.update_cache_timeout || 0)
|
||||
};
|
||||
|
||||
// Create a string out of selected list of regions
|
||||
if ($scope.source_regions) {
|
||||
regions = $('#group_source_regions').select2("data");
|
||||
r = [];
|
||||
for (i = 0; i < regions.length; i++) {
|
||||
r.push(regions[i].id);
|
||||
}
|
||||
data.source_regions = r.join();
|
||||
}
|
||||
|
||||
if ($scope.source && ($scope.source.value === 'ec2')) {
|
||||
data.instance_filters = $scope.instance_filters;
|
||||
// Create a string out of selected list of regions
|
||||
group_by = $('#group_group_by').select2("data");
|
||||
r = [];
|
||||
for (i = 0; i < group_by.length; i++) {
|
||||
r.push(group_by[i].id);
|
||||
}
|
||||
data.group_by = r.join();
|
||||
}
|
||||
|
||||
if ($scope.source && ($scope.source.value === 'ec2')) {
|
||||
// for ec2, validate variable data
|
||||
data.source_vars = ToJSON($scope.envParseType, $scope.source_vars, true);
|
||||
}
|
||||
|
||||
if ($scope.source && ($scope.source.value === 'custom')) {
|
||||
data.source_vars = ToJSON($scope.envParseType, $scope.extra_vars, true);
|
||||
}
|
||||
|
||||
if ($scope.source && ($scope.source.value === 'vmware' ||
|
||||
$scope.source.value === 'openstack')) {
|
||||
data.source_vars = ToJSON($scope.envParseType, $scope.inventory_variables, true);
|
||||
}
|
||||
|
||||
// the API doesn't expect the credential to be passed with a custom inv script
|
||||
if ($scope.source && $scope.source.value === 'custom') {
|
||||
delete(data.credential);
|
||||
}
|
||||
|
||||
if (!parseError) {
|
||||
Rest.setUrl($scope.source_url);
|
||||
Rest.put(data)
|
||||
.success(function() {
|
||||
$scope.$emit('SaveComplete', false);
|
||||
})
|
||||
.error(function(data, status) {
|
||||
$('#group_tabs a:eq(1)').tab('show');
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to update group inventory source. PUT status: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
$scope.toggleNotification = function(event, notifier_id, column) {
|
||||
var notifier = this.notification;
|
||||
try {
|
||||
$(event.target).tooltip('hide');
|
||||
}
|
||||
catch(e) {
|
||||
// ignore
|
||||
}
|
||||
ToggleNotification({
|
||||
scope: $scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
id: $scope.source_id,
|
||||
notifier: notifier,
|
||||
column: column,
|
||||
callback: 'NotificationRefresh'
|
||||
});
|
||||
};
|
||||
// Cancel
|
||||
$scope.formCancel = function() {
|
||||
Wait('stop');
|
||||
$state.go('inventoryManage', {}, {reload: true});
|
||||
};
|
||||
|
||||
// Save
|
||||
$scope.saveGroup = function() {
|
||||
Wait('start');
|
||||
var fld, data, json_data;
|
||||
|
||||
try {
|
||||
|
||||
json_data = ToJSON($scope.parseType, $scope.variables, true);
|
||||
|
||||
data = {};
|
||||
for (fld in form.fields) {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
|
||||
data.inventory = inventory_id;
|
||||
|
||||
Rest.setUrl(defaultUrl);
|
||||
if (mode === 'edit' || (mode === 'add' && group_created)) {
|
||||
Rest.put(data)
|
||||
.success(function() {
|
||||
$scope.$emit('formSaveSuccess');
|
||||
})
|
||||
.error(function(data, status) {
|
||||
$('#group_tabs a:eq(0)').tab('show');
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to update group: ' + group_id + '. PUT status: ' + status
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Rest.post(data)
|
||||
.success(function(data) {
|
||||
group_created = true;
|
||||
group_id = data.id;
|
||||
$scope.source_url = data.related.inventory_source;
|
||||
$scope.source_id = $scope.source_url.split('/')[4];
|
||||
$scope.$emit('formSaveSuccess');
|
||||
})
|
||||
.error(function(data, status) {
|
||||
$('#group_tabs a:eq(0)').tab('show');
|
||||
ProcessErrors($scope, data, status, form, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to create group: ' + group_id + '. POST status: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore. ToJSON will have already alerted the user
|
||||
}
|
||||
};
|
||||
|
||||
// Start the update process
|
||||
$scope.updateGroup = function() {
|
||||
if ($scope.source === "manual" || $scope.source === null) {
|
||||
Alert('Missing Configuration', 'The selected group is not configured for updates. You must first edit the group, provide Source settings, ' +
|
||||
'and then run an update.', 'alert-info');
|
||||
} else if ($scope.status === 'updating') {
|
||||
Alert('Update in Progress', 'The inventory update process is currently running for group <em>' +
|
||||
$filter('sanitize')($scope.summary_fields.group.name) + '</em>. Use the Refresh button to monitor the status.', 'alert-info', null, null, null, null, true);
|
||||
} else {
|
||||
InventoryUpdate({
|
||||
scope: $scope,
|
||||
group_id: group_id,
|
||||
url: $scope.group_update_url,
|
||||
group_name: $scope.name,
|
||||
group_source: $scope.source.value
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Change the lookup and regions when the source changes
|
||||
$scope.sourceChange = function() {
|
||||
$scope.credential_name = "";
|
||||
$scope.credential = "";
|
||||
if ($scope.credential_name_api_error) {
|
||||
delete $scope.credential_name_api_error;
|
||||
}
|
||||
initSourceChange();
|
||||
};
|
||||
|
||||
|
||||
angular.extend(vm, {
|
||||
formCancel : $scope.formCancel,
|
||||
saveGroup: $scope.saveGroup
|
||||
});
|
||||
}
|
||||
|
||||
export default ['$filter', '$location', '$log', '$stateParams',
|
||||
'$compile', '$state', '$scope', 'Rest', 'Alert', 'GroupForm',
|
||||
'GenerateForm','Prompt', 'ProcessErrors', 'GetBasePath', 'SetNodeName',
|
||||
'ParseTypeChange', 'GetSourceTypeOptions', 'InventoryUpdate', 'LookUpInit',
|
||||
'Empty', 'Wait', 'GetChoices', 'UpdateGroup', 'SourceChange', 'Find',
|
||||
'ParseVariableString', 'ToJSON', 'GroupsScheduleListInit',
|
||||
'SetSchedulesInnerDialogSize', 'CreateSelect2',
|
||||
'ToggleNotification', 'NotificationsListInit', 'RelatedSearchInit',
|
||||
'RelatedPaginateInit',
|
||||
manageGroupsDirectiveController
|
||||
];
|
||||
@ -1,25 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/* jshint unused: vars */
|
||||
import manageGroupsDirectiveController from './manage-groups.directive.controller';
|
||||
|
||||
export default ['templateUrl', 'ParamPass',
|
||||
function(templateUrl, ParamPass) {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
scope: true,
|
||||
replace: true,
|
||||
templateUrl: templateUrl('inventories/manage/manage-groups/directive/manage-groups.directive'),
|
||||
link: function(scope, element, attrs) {
|
||||
|
||||
},
|
||||
controller: manageGroupsDirectiveController,
|
||||
controllerAs: 'vm',
|
||||
bindToController: true
|
||||
};
|
||||
}
|
||||
];
|
||||
@ -1,5 +0,0 @@
|
||||
<div>
|
||||
<div id="group-manage-panel">
|
||||
<div id="properties-tab"></div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,16 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './manage-groups.route';
|
||||
import manageGroupsDirective from './directive/manage-groups.directive';
|
||||
|
||||
export default
|
||||
angular.module('manage-groups', [])
|
||||
.directive('manageGroups', manageGroupsDirective)
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route.edit);
|
||||
$stateExtender.addState(route.add);
|
||||
}]);
|
||||
@ -1,36 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
import {
|
||||
templateUrl
|
||||
} from '../../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
edit: {
|
||||
name: 'inventoryManage.editGroup',
|
||||
route: '/:group_id/editGroup',
|
||||
templateUrl: templateUrl('inventories/manage/manage-groups/manage-groups'),
|
||||
data: {
|
||||
group_id: 'group_id',
|
||||
mode: 'edit'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORY EDIT GROUPS"
|
||||
}
|
||||
},
|
||||
|
||||
add: {
|
||||
name: 'inventoryManage.addGroup',
|
||||
route: '/addGroup',
|
||||
templateUrl: templateUrl('inventories/manage/manage-groups/manage-groups'),
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORY ADD GROUP"
|
||||
},
|
||||
data: {
|
||||
mode: 'add'
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
@ -1,41 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
import {templateUrl} from '../../../shared/template-url/template-url.factory';
|
||||
import addController from './manage-hosts-add.controller';
|
||||
import editController from './manage-hosts-edit.controller';
|
||||
|
||||
var ManageHostsEdit = {
|
||||
name: 'inventoryManage.editHost',
|
||||
route: '/host/:host_id',
|
||||
controller: editController,
|
||||
templateUrl: templateUrl('inventories/manage/manage-hosts/manage-hosts'),
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORY EDIT HOSTS"
|
||||
},
|
||||
data: {
|
||||
mode: 'edit'
|
||||
},
|
||||
resolve: {
|
||||
host: ['$stateParams', 'ManageHostsService', function($stateParams, ManageHostsService){
|
||||
return ManageHostsService.get({id: $stateParams.host_id}).then(function(res){
|
||||
return res.data.results[0];
|
||||
});
|
||||
}]
|
||||
}
|
||||
};
|
||||
var ManageHostsAdd = {
|
||||
name: 'inventoryManage.addHost',
|
||||
route: '/host/add',
|
||||
controller: addController,
|
||||
templateUrl: templateUrl('inventories/manage/manage-hosts/manage-hosts'),
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORY ADD HOST"
|
||||
},
|
||||
data: {
|
||||
mode: 'add'
|
||||
}
|
||||
};
|
||||
export {ManageHostsAdd, ManageHostsEdit};
|
||||
@ -1,53 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default
|
||||
['$rootScope', 'Rest', 'GetBasePath', 'ProcessErrors',
|
||||
function($rootScope, Rest, GetBasePath, ProcessErrors){
|
||||
return {
|
||||
stringifyParams: function(params){
|
||||
return _.reduce(params, (result, value, key) => {
|
||||
return result + key + '=' + value + '&';
|
||||
}, '');
|
||||
},
|
||||
get: function(params){
|
||||
var url = GetBasePath('hosts') + '?' + this.stringifyParams(params);
|
||||
Rest.setUrl(url);
|
||||
return Rest.get()
|
||||
.success(function(res){
|
||||
return res;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
},
|
||||
post: function(params){
|
||||
var url = GetBasePath('hosts');
|
||||
Rest.setUrl(url);
|
||||
return Rest.post(params)
|
||||
.success(function(res){
|
||||
return res;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
},
|
||||
put: function(host){
|
||||
var url = GetBasePath('hosts') + host.id;
|
||||
Rest.setUrl(url);
|
||||
return Rest.put(host)
|
||||
.success(function(res){
|
||||
return res;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors($rootScope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + '. GET returned: ' + status });
|
||||
});
|
||||
}
|
||||
};
|
||||
}];
|
||||
@ -1,18 +0,0 @@
|
||||
#Inventory-groupManage--panel,
|
||||
#Inventory-hostManage--panel {
|
||||
.ui-dialog-buttonpane.ui-widget-content {
|
||||
border: none;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#host-panel-form,
|
||||
#properties-tab {
|
||||
.Form-header {
|
||||
margin-top: -20px;
|
||||
}
|
||||
}
|
||||
|
||||
.Form-textArea {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@ -11,22 +11,17 @@
|
||||
*/
|
||||
|
||||
export default
|
||||
[ '$location', '$rootScope', '$filter', '$scope', '$compile',
|
||||
'$stateParams', '$log', 'ClearScope', 'GetBasePath', 'Wait',
|
||||
'ProcessErrors', 'SelectPlay', 'SelectTask', 'GetElapsed',
|
||||
'JobIsFinished', 'SetTaskStyles', 'DigestEvent', 'UpdateDOM', 'DeleteJob', 'InitiatePlaybookRun',
|
||||
'LoadPlays', 'LoadTasks', 'HostsEdit',
|
||||
'ParseVariableString', 'GetChoices', 'fieldChoices', 'fieldLabels',
|
||||
'EditSchedule', 'ParseTypeChange', 'JobDetailService',
|
||||
[ '$location', '$rootScope', '$filter', '$scope', '$compile', '$stateParams', '$log', 'ClearScope',
|
||||
'GetBasePath', 'Wait', 'ProcessErrors', 'SelectPlay', 'SelectTask', 'GetElapsed', 'JobIsFinished',
|
||||
'SetTaskStyles', 'DigestEvent', 'UpdateDOM', 'DeleteJob', 'InitiatePlaybookRun', 'LoadPlays', 'LoadTasks',
|
||||
'ParseVariableString', 'GetChoices', 'fieldChoices', 'fieldLabels', 'EditSchedule',
|
||||
'ParseTypeChange', 'JobDetailService',
|
||||
function(
|
||||
$location, $rootScope, $filter, $scope, $compile, $stateParams,
|
||||
$log, ClearScope, GetBasePath, Wait, ProcessErrors,
|
||||
SelectPlay, SelectTask, GetElapsed,
|
||||
JobIsFinished,
|
||||
SetTaskStyles, DigestEvent, UpdateDOM, DeleteJob,
|
||||
InitiatePlaybookRun, LoadPlays, LoadTasks,
|
||||
HostsEdit, ParseVariableString, GetChoices, fieldChoices,
|
||||
fieldLabels, EditSchedule, ParseTypeChange, JobDetailService
|
||||
$location, $rootScope, $filter, $scope, $compile, $stateParams, $log, ClearScope,
|
||||
GetBasePath, Wait, ProcessErrors, SelectPlay, SelectTask, GetElapsed, JobIsFinished,
|
||||
SetTaskStyles, DigestEvent, UpdateDOM, DeleteJob, InitiatePlaybookRun, LoadPlays, LoadTasks,
|
||||
ParseVariableString, GetChoices, fieldChoices, fieldLabels, EditSchedule,
|
||||
ParseTypeChange, JobDetailService
|
||||
) {
|
||||
ClearScope();
|
||||
|
||||
|
||||
@ -9,41 +9,17 @@
|
||||
|
||||
export default
|
||||
angular.module('GroupListDefinition', [])
|
||||
.value('GroupList', {
|
||||
.value('CopyMoveGroupList', {
|
||||
|
||||
name: 'copy_groups',
|
||||
iterator: 'copy_group',
|
||||
name: 'groups',
|
||||
iterator: 'group',
|
||||
selectTitle: 'Copy Groups',
|
||||
editTitle: 'Groups',
|
||||
index: false,
|
||||
well: false,
|
||||
|
||||
well: false,
|
||||
fields: {
|
||||
name: {
|
||||
key: true,
|
||||
label: 'Name'
|
||||
}
|
||||
},
|
||||
|
||||
actions: { },
|
||||
|
||||
fieldActions: {
|
||||
edit: {
|
||||
label: 'Edit',
|
||||
ngClick: "editGroup(group.id)",
|
||||
icon: 'icon-edit',
|
||||
"class": 'btn-xs',
|
||||
awToolTip: 'Edit group',
|
||||
dataPlacement: 'top'
|
||||
},
|
||||
|
||||
"delete": {
|
||||
label: 'Delete',
|
||||
ngClick: "deleteGroup(group.id, group.name)",
|
||||
icon: 'icon-trash',
|
||||
"class": 'btn-xs',
|
||||
awToolTip: 'Delete group',
|
||||
dataPlacement: 'top'
|
||||
label: 'Target Group Name'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -96,7 +96,7 @@ export default
|
||||
|
||||
edit: {
|
||||
label: 'Edit',
|
||||
ngClick: 'editInventory(inventory.id)', //'editInventoryProperties(inventory.id)',
|
||||
ngClick: 'editInventory(inventory.id)',
|
||||
awToolTip: 'Edit inventory',
|
||||
dataPlacement: 'top'
|
||||
},
|
||||
|
||||
@ -21,18 +21,47 @@ export default
|
||||
multiSelect: true,
|
||||
|
||||
fields: {
|
||||
sync_status: {
|
||||
label: '',
|
||||
nosort: true,
|
||||
searchable: false,
|
||||
mode: 'all',
|
||||
iconOnly: true,
|
||||
ngClick: 'viewUpdateStatus(group.id)',
|
||||
awToolTip: "{{ group.status_tooltip }}",
|
||||
dataTipWatch: "group.status_tooltip",
|
||||
icon: "{{ 'fa icon-cloud-' + group.status_class }}",
|
||||
ngClass: "group.status_class",
|
||||
dataPlacement: "top",
|
||||
columnClass: 'status-column List-staticColumn--smallStatus'
|
||||
},
|
||||
failed_hosts: {
|
||||
label: '',
|
||||
nosort: true,
|
||||
searchable: false,
|
||||
mode: 'all',
|
||||
iconOnly: true,
|
||||
awToolTip: "{{ group.hosts_status_tip }}",
|
||||
dataPlacement: "top",
|
||||
ngClick: "showFailedHosts(group)",
|
||||
icon: "{{ 'fa icon-job-' + group.hosts_status_class }}",
|
||||
columnClass: 'status-column List-staticColumn--smallStatus'
|
||||
},
|
||||
name: {
|
||||
label: 'Groups',
|
||||
key: true,
|
||||
ngClick: "groupSelect(group.id)",
|
||||
columnClick: "groupSelect(group.id)",
|
||||
columnClass: 'col-lg-3 col-md-3 col-sm-3 col-xs-3'
|
||||
columnClass: 'col-lg-3 col-md-3 col-sm-3 col-xs-3',
|
||||
class: 'InventoryManage-breakWord'
|
||||
},
|
||||
total_groups: {
|
||||
nosort: true,
|
||||
label: '',
|
||||
type: 'badgeCount',
|
||||
ngHide: 'group.total_groups == 0',
|
||||
noLink: true,
|
||||
awToolTip: "{{group.name}} contains {{group.total_groups}} {{group.total_groups === 1 ? 'child' : 'children'}}",
|
||||
|
||||
},
|
||||
source: {
|
||||
label: 'Source',
|
||||
@ -100,8 +129,9 @@ export default
|
||||
},
|
||||
launch: {
|
||||
mode: 'all',
|
||||
ngShow: 'inventory.can_run_ad_hoc_commands',
|
||||
ngClick: 'populateAdhocForm()',
|
||||
// $scope.$parent is governed by InventoryManageController,
|
||||
ngShow: '$parent.groupsSelected || $parent.hostsSelected',
|
||||
ngClick: '$parent.setAdhocPattern()',
|
||||
awToolTip: "Run a command on the selected inventory",
|
||||
actionClass: 'btn List-buttonDefault',
|
||||
buttonContent: 'RUN COMMANDS'
|
||||
@ -116,7 +146,7 @@ export default
|
||||
ngClick: "createGroup()",
|
||||
awToolTip: "Create a new group",
|
||||
actionClass: 'btn List-buttonSubmit',
|
||||
buttonContent: '+ ADD'
|
||||
buttonContent: '+ ADD GROUP'
|
||||
}
|
||||
},
|
||||
|
||||
@ -124,32 +154,16 @@ export default
|
||||
|
||||
columnClass: 'col-lg-6 col-md-6 col-sm-6 col-xs-6 text-right',
|
||||
|
||||
sync_status: {
|
||||
mode: 'all',
|
||||
ngClick: "viewUpdateStatus(group.id)",
|
||||
awToolTip: "{{ group.status_tooltip }}",
|
||||
dataTipWatch: "group.status_tooltip",
|
||||
iconClass: "{{ 'fa icon-cloud-' + group.status_class }}",
|
||||
ngClass: "group.status_class",
|
||||
dataPlacement: "top"
|
||||
},
|
||||
failed_hosts: {
|
||||
mode: 'all',
|
||||
awToolTip: "{{ group.hosts_status_tip }}",
|
||||
dataPlacement: "top",
|
||||
ngClick: "showHosts(group.id, group.group_id, group.show_failures)",
|
||||
iconClass: "{{ 'fa icon-job-' + group.hosts_status_class }}"
|
||||
},
|
||||
group_update: {
|
||||
//label: 'Sync',
|
||||
mode: 'all',
|
||||
ngClick: 'updateGroup(group.id)',
|
||||
ngClick: 'updateGroup(group)',
|
||||
awToolTip: "{{ group.launch_tooltip }}",
|
||||
dataTipWatch: "group.launch_tooltip",
|
||||
ngShow: "group.status !== 'running' && group.status " +
|
||||
"!== 'pending' && group.status !== 'updating'",
|
||||
ngClass: "group.launch_class",
|
||||
dataPlacement: "top"
|
||||
dataPlacement: "top",
|
||||
},
|
||||
cancel: {
|
||||
//label: 'Cancel',
|
||||
@ -159,11 +173,12 @@ export default
|
||||
'class': 'red-txt',
|
||||
ngShow: "group.status == 'running' || group.status == 'pending' " +
|
||||
"|| group.status == 'updating'",
|
||||
dataPlacement: "top"
|
||||
dataPlacement: "top",
|
||||
iconClass: "fa fa-minus-circle"
|
||||
},
|
||||
copy: {
|
||||
mode: 'all',
|
||||
ngClick: "copyGroup(group.id)",
|
||||
ngClick: "copyMoveGroup(group.id)",
|
||||
awToolTip: 'Copy or move group',
|
||||
ngShow: "group.id > 0",
|
||||
dataPlacement: "top"
|
||||
@ -186,7 +201,7 @@ export default
|
||||
"delete": {
|
||||
//label: 'Delete',
|
||||
mode: 'all',
|
||||
ngClick: "deleteGroup(group.id)",
|
||||
ngClick: "deleteGroup(group)",
|
||||
awToolTip: 'Delete group',
|
||||
dataPlacement: "top"
|
||||
}
|
||||
|
||||
@ -22,6 +22,23 @@ export default
|
||||
multiSelect: true,
|
||||
|
||||
fields: {
|
||||
active_failures: {
|
||||
label: '',
|
||||
iconOnly: true,
|
||||
searchable: false,
|
||||
nosort: true,
|
||||
// do not remove this ng-click directive
|
||||
// the list generator case to handle fields without ng-click
|
||||
// cannot handle the aw-* directives
|
||||
ngClick: 'noop()',
|
||||
awPopOver: "{{ host.job_status_html }}",
|
||||
dataTitle: "{{ host.job_status_title }}",
|
||||
awToolTip: "{{ host.badgeToolTip }}",
|
||||
dataPlacement: 'top',
|
||||
icon: "{{ 'fa icon-job-' + host.active_failures }}",
|
||||
id: 'active-failures-action',
|
||||
columnClass: 'status-column List-staticColumn--smallStatus'
|
||||
},
|
||||
name: {
|
||||
key: true,
|
||||
label: 'Hosts',
|
||||
@ -29,7 +46,8 @@ export default
|
||||
ngClass: "{ 'host-disabled-label': !host.enabled }",
|
||||
columnClass: 'col-lg-6 col-md-8 col-sm-8 col-xs-7',
|
||||
dataHostId: "{{ host.id }}",
|
||||
dataType: "host"
|
||||
dataType: "host",
|
||||
class: 'InventoryManage-breakWord'
|
||||
},
|
||||
enabled: {
|
||||
label: 'Disabled?',
|
||||
@ -50,20 +68,9 @@ export default
|
||||
fieldActions: {
|
||||
|
||||
columnClass: 'col-lg-6 col-md-4 col-sm-4 col-xs-5 text-right',
|
||||
label: false,
|
||||
|
||||
active_failures: {
|
||||
awPopOver: "{{ host.job_status_html }}",
|
||||
dataTitle: "{{ host.job_status_title }}",
|
||||
awToolTip: "{{ host.badgeToolTip }}",
|
||||
awTipPlacement: 'top',
|
||||
dataPlacement: 'left',
|
||||
iconClass: "{{ 'fa icon-job-' + host.active_failures }}",
|
||||
id: 'active-failutes-action'
|
||||
},
|
||||
copy: {
|
||||
mode: 'all',
|
||||
ngClick: "copyHost(host.id)",
|
||||
ngClick: "copyMoveHost(host.id)",
|
||||
awToolTip: 'Copy or move host to another group',
|
||||
dataPlacement: "top"
|
||||
},
|
||||
@ -85,15 +92,15 @@ export default
|
||||
|
||||
actions: {
|
||||
system_tracking: {
|
||||
label: 'System Tracking',
|
||||
ngClick: 'systemTracking()', //'editInventoryProperties(inventory.id)',
|
||||
buttonContent: 'System Tracking',
|
||||
ngClick: 'systemTracking()',
|
||||
awToolTip: "{{ systemTrackingTooltip }}",
|
||||
dataTipWatch: "systemTrackingTooltip",
|
||||
dataPlacement: 'top',
|
||||
awFeature: 'system_tracking',
|
||||
ngDisabled: 'systemTrackingDisabled',
|
||||
actionClass: 'btn List-buttonDefault system-tracking',
|
||||
ngShow: 'hostsSelected'
|
||||
ngShow: 'hostsSelected',
|
||||
ngDisabled: 'systemTrackingDisabled'
|
||||
},
|
||||
refresh: {
|
||||
mode: 'all',
|
||||
@ -108,7 +115,7 @@ export default
|
||||
ngClick: "createHost()",
|
||||
awToolTip: "Create a new host",
|
||||
actionClass: 'btn List-buttonSubmit',
|
||||
buttonContent: '+ ADD'
|
||||
buttonContent: '+ ADD HOST'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,9 @@ export default
|
||||
scope.isCurrentState = function(name){
|
||||
return $state.current.name === name;
|
||||
};
|
||||
scope.includesCurrentState = function(name){
|
||||
return $state.includes(name);
|
||||
};
|
||||
|
||||
// set up the user tooltip
|
||||
$rootScope.$on('current_user', function(user) {
|
||||
|
||||
@ -52,19 +52,26 @@ export default
|
||||
controller: 'schedulerEditController'
|
||||
});
|
||||
$stateExtender.addState({
|
||||
name: 'inventoryManageSchedules',
|
||||
route: '/inventory/:inventory_id/manage/:id/schedules',
|
||||
templateUrl: templateUrl("scheduler/scheduler"),
|
||||
controller: 'schedulerController'
|
||||
name: 'inventoryManage.schedules',
|
||||
route: '/schedules/:id',
|
||||
views: {
|
||||
'form@inventoryManage': {
|
||||
templateUrl: templateUrl("scheduler/scheduler"),
|
||||
controller: 'schedulerController'
|
||||
}
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "{{name}} SCHEDULES"
|
||||
},
|
||||
});
|
||||
$stateExtender.addState({
|
||||
name: 'inventoryManageSchedules.add',
|
||||
name: 'inventoryManage.schedules.add',
|
||||
route: '/add',
|
||||
templateUrl: templateUrl("scheduler/schedulerForm"),
|
||||
controller: 'schedulerAddController'
|
||||
});
|
||||
$stateExtender.addState({
|
||||
name: 'inventoryManageSchedules.edit',
|
||||
name: 'inventoryManage.schedules.edit',
|
||||
route: '/:schedule_id',
|
||||
templateUrl: templateUrl("scheduler/schedulerForm"),
|
||||
controller: 'schedulerEditController'
|
||||
|
||||
@ -1,609 +0,0 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name shared.function:inventoryTree
|
||||
* @description
|
||||
* InventoryTree.js
|
||||
*
|
||||
* Build data for the tree selector table used on inventory detail page.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
export default
|
||||
angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper', 'PromptDialog'])
|
||||
|
||||
.factory('SortNodes', [
|
||||
function () {
|
||||
return function (data) {
|
||||
//Sort nodes by name
|
||||
var i, j, names = [], newData = [];
|
||||
for (i = 0; i < data.length; i++) {
|
||||
names.push(data[i].name);
|
||||
}
|
||||
names.sort();
|
||||
for (j = 0; j < names.length; j++) {
|
||||
for (i = 0; i < data.length; i++) {
|
||||
if (data[i].name === names[j]) {
|
||||
newData.push(data[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newData;
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
.factory('BuildTree', ['$location', 'Rest', 'GetBasePath', 'ProcessErrors', 'SortNodes', 'Wait', 'GetSyncStatusMsg', 'GetHostsStatusMsg', 'Store',
|
||||
function ($location, Rest, GetBasePath, ProcessErrors, SortNodes, Wait, GetSyncStatusMsg, GetHostsStatusMsg, Store) {
|
||||
return function (params) {
|
||||
|
||||
var inventory_id = params.inventory_id,
|
||||
scope = params.scope,
|
||||
refresh = params.refresh,
|
||||
emit = params.emit,
|
||||
new_group_id = params.new_group_id,
|
||||
groups = [],
|
||||
id = 1,
|
||||
local_child_store,
|
||||
path = $location.path();
|
||||
|
||||
function buildAllHosts(tree_data) {
|
||||
// Start our tree object with All Hosts
|
||||
var children = [],
|
||||
sorted = SortNodes(tree_data),
|
||||
j, all_hosts;
|
||||
|
||||
for (j = 0; j < sorted.length; j++) {
|
||||
children.push(sorted[j].id);
|
||||
}
|
||||
|
||||
all_hosts = {
|
||||
name: 'All Hosts',
|
||||
id: 1,
|
||||
group_id: null,
|
||||
parent: 0,
|
||||
description: '',
|
||||
show: true,
|
||||
ngicon: null,
|
||||
has_children: false,
|
||||
related: {},
|
||||
selected_class: '',
|
||||
show_failures: false,
|
||||
isDraggable: false,
|
||||
isDroppable: true,
|
||||
children: children
|
||||
};
|
||||
groups.push(all_hosts);
|
||||
}
|
||||
|
||||
function getExpandState(key) {
|
||||
var result = true;
|
||||
local_child_store.every(function(child) {
|
||||
if (child.key === key) {
|
||||
result = child.expand;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function getShowState(key) {
|
||||
var result = null;
|
||||
local_child_store.every(function(child) {
|
||||
if (child.key === key) {
|
||||
result = (child.show !== undefined) ? child.show : true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function buildGroups(tree_data, parent, level) {
|
||||
|
||||
var children, stat, hosts_status, group,
|
||||
sorted = SortNodes(tree_data),
|
||||
expand, show;
|
||||
|
||||
sorted.forEach( function(row, i) {
|
||||
id++;
|
||||
|
||||
stat = GetSyncStatusMsg({
|
||||
status: sorted[i].summary_fields.inventory_source.status,
|
||||
has_inventory_sources: sorted[i].has_inventory_sources,
|
||||
source: ( (sorted[i].summary_fields.inventory_source) ? sorted[i].summary_fields.inventory_source.source : null )
|
||||
}); // from helpers/Groups.js
|
||||
|
||||
hosts_status = GetHostsStatusMsg({
|
||||
active_failures: sorted[i].hosts_with_active_failures,
|
||||
total_hosts: sorted[i].total_hosts,
|
||||
inventory_id: inventory_id,
|
||||
group_id: sorted[i].id
|
||||
}); // from helpers/Groups.js
|
||||
|
||||
children = [];
|
||||
sorted[i].children.forEach( function(child, j) {
|
||||
children.push(sorted[i].children[j].id);
|
||||
});
|
||||
|
||||
expand = (sorted[i].children.length > 0) ? getExpandState(sorted[i].id) : false;
|
||||
show = getShowState(sorted[i].id);
|
||||
if (show === null) {
|
||||
// this is a node we haven't seen before, so check the parent expand/collapse state
|
||||
// If parent is not expanded, then child should be hidden.
|
||||
show = true;
|
||||
if (parent > 0) {
|
||||
groups.every(function(g) {
|
||||
if (g.id === parent) {
|
||||
show = getExpandState(g.key);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
group = {
|
||||
name: sorted[i].name,
|
||||
has_active_failures: sorted[i].has_active_failures,
|
||||
total_hosts: sorted[i].total_hosts,
|
||||
hosts_with_active_failures: sorted[i].hosts_with_active_failures,
|
||||
total_groups: sorted[i].total_groups,
|
||||
groups_with_active_failures: sorted[i].groups_with_active_failures,
|
||||
parent: parent,
|
||||
has_children: (sorted[i].children.length > 0) ? true : false,
|
||||
has_inventory_sources: sorted[i].has_inventory_sources,
|
||||
id: id,
|
||||
source: sorted[i].summary_fields.inventory_source.source,
|
||||
key: sorted[i].id,
|
||||
group_id: sorted[i].id,
|
||||
event_level: level,
|
||||
children: children,
|
||||
show: show,
|
||||
related: sorted[i].related,
|
||||
status: sorted[i].summary_fields.inventory_source.status,
|
||||
status_class: stat['class'],
|
||||
status_tooltip: stat.tooltip,
|
||||
launch_tooltip: stat.launch_tip,
|
||||
launch_class: stat.launch_class,
|
||||
hosts_status_tip: hosts_status.tooltip,
|
||||
show_failures: hosts_status.failures,
|
||||
hosts_status_class: hosts_status['class'],
|
||||
inventory_id: inventory_id,
|
||||
selected_class: '',
|
||||
isDraggable: true,
|
||||
isDroppable: true
|
||||
};
|
||||
if (sorted[i].children.length > 0) {
|
||||
if (expand) {
|
||||
group.ngicon = 'fa fa-minus-square-o node-toggle';
|
||||
}
|
||||
else {
|
||||
group.ngicon = 'fa fa-plus-square-o node-toggle';
|
||||
}
|
||||
}
|
||||
else {
|
||||
group.ngicon = 'fa fa-square-o node-no-toggle';
|
||||
}
|
||||
if (new_group_id && group.group_id === new_group_id) {
|
||||
scope.selected_tree_id = id;
|
||||
scope.selected_group_id = group.group_id;
|
||||
}
|
||||
groups.push(group);
|
||||
if (sorted[i].children.length > 0) {
|
||||
buildGroups(sorted[i].children, id, level + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Build the HTML for our tree
|
||||
if (scope.buildAllGroupsRemove) {
|
||||
scope.buildAllGroupsRemove();
|
||||
}
|
||||
scope.buildAllGroupsRemove = scope.$on('buildAllGroups', function (e, inventory_name, inventory_tree) {
|
||||
Rest.setUrl(inventory_tree);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
buildAllHosts(data);
|
||||
buildGroups(data, 0, 0);
|
||||
scope.autoShowGroupHelp = (data.length === 0) ? true : false;
|
||||
if (refresh) {
|
||||
scope.groups = groups;
|
||||
scope.$emit('GroupTreeRefreshed', inventory_name, groups, emit);
|
||||
} else {
|
||||
scope.$emit('GroupTreeLoaded', inventory_name, groups, emit);
|
||||
}
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to get inventory tree for: ' + inventory_id + '. GET returned: ' + status
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function loadTreeData() {
|
||||
// Load the inventory root node
|
||||
Wait('start');
|
||||
Rest.setUrl(GetBasePath('inventory') + inventory_id + '/');
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
scope.$emit('buildAllGroups', data.name, data.related.tree, data.related.groups);
|
||||
})
|
||||
.error(function (data, status) {
|
||||
Wait('stop');
|
||||
ProcessErrors(scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
local_child_store = Store(path + '_children');
|
||||
if (!local_child_store) {
|
||||
local_child_store = [];
|
||||
}
|
||||
loadTreeData();
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
// Update a group with a set of properties
|
||||
.factory('UpdateGroup', ['ApplyEllipsis', 'GetSyncStatusMsg', 'Empty',
|
||||
function (ApplyEllipsis, GetSyncStatusMsg, Empty) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope,
|
||||
group_id = params.group_id,
|
||||
properties = params.properties,
|
||||
i, p, grp, old_name, stat;
|
||||
|
||||
for (i = 0; i < scope.groups.length; i++) {
|
||||
if (scope.groups[i].id === group_id) {
|
||||
grp = scope.groups[i];
|
||||
for (p in properties) {
|
||||
if (p === 'name') {
|
||||
old_name = scope.groups[i].name;
|
||||
}
|
||||
if (p === 'source') {
|
||||
if (properties[p] !== scope.groups[i][p]) {
|
||||
// User changed source
|
||||
if (!Empty(properties[p]) && (scope.groups[i].status === 'none' || Empty(scope.groups[i].status))) {
|
||||
// We have a source but no status, seed the status with 'never' to enable sync button
|
||||
scope.groups[i].status = 'never updated';
|
||||
} else if (!properties[p]) {
|
||||
// User removed source
|
||||
scope.groups[i].status = 'none';
|
||||
}
|
||||
// Update date sync status links/icons
|
||||
stat = GetSyncStatusMsg({
|
||||
status: scope.groups[i].status,
|
||||
has_inventory_sources: properties.has_inventory_sources,
|
||||
source: properties.source
|
||||
});
|
||||
scope.groups[i].status_class = stat['class'];
|
||||
scope.groups[i].status_tooltip = stat.tooltip;
|
||||
scope.groups[i].launch_tooltip = stat.launch_tip;
|
||||
scope.groups[i].launch_class = stat.launch_class;
|
||||
}
|
||||
}
|
||||
scope.groups[i][p] = properties[p];
|
||||
}
|
||||
}
|
||||
/*if (scope.groups[i].id === scope.selected_tree_id) {
|
||||
//Make sure potential group name change gets reflected throughout the page
|
||||
scope.selected_group_name = scope.groups[i].name;
|
||||
scope.search_place_holder = 'Search ' + scope.groups[i].name;
|
||||
scope.hostSearchPlaceholder = 'Search ' + scope.groups[i].name;
|
||||
}*/
|
||||
}
|
||||
|
||||
// Update any titles attributes created by ApplyEllipsis
|
||||
if (old_name) {
|
||||
setTimeout(function () {
|
||||
$('#groups_table .group-name a[title="' + old_name + '"]').attr('title', properties.name);
|
||||
ApplyEllipsis('#groups_table .group-name a');
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
// Set node name and description after an update to Group properties.
|
||||
.factory('SetNodeName', [
|
||||
function () {
|
||||
return function (params) {
|
||||
var name = params.name,
|
||||
descr = params.description,
|
||||
group_id = (params.group_id !== undefined) ? params.group_id : null,
|
||||
inventory_id = (params.inventory_id !== undefined) ? params.inventory_id : null;
|
||||
|
||||
if (group_id !== null) {
|
||||
$('#inventory-tree').find('li [data-group-id="' + group_id + '"]').each(function () {
|
||||
$(this).attr('data-name', name);
|
||||
$(this).attr('data-description', descr);
|
||||
$(this).find('.activate').first().text(name);
|
||||
});
|
||||
}
|
||||
|
||||
if (inventory_id !== null) {
|
||||
$('#inventory-root-node').attr('data-name', name).attr('data-description', descr).find('.activate').first().text(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
// Copy or Move a group on the tree after drag-n-drop
|
||||
.factory('CopyMoveGroup', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath',
|
||||
function ($compile, Alert, ProcessErrors, Find, Wait, Rest, Empty, GetBasePath) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope,
|
||||
target = Find({ list: scope.groups, key: 'id', val: params.target_tree_id }),
|
||||
inbound = Find({ list: scope.groups, key: 'id', val: params.inbound_tree_id }),
|
||||
e, html = '';
|
||||
|
||||
// Build the html for our prompt dialog
|
||||
html += "<div id=\"copy-prompt-modal\" class=\"modal fade\">\n";
|
||||
html += "<div class=\"modal-dialog\">\n";
|
||||
html += "<div class=\"modal-content\">\n";
|
||||
html += "<div class=\"modal-header\">\n";
|
||||
html += "<button type=\"button\" class=\"close\" data-target=\"#copy-prompt-modal\" " +
|
||||
"data-dismiss=\"modal\" aria-hidden=\"true\">×</button>\n";
|
||||
|
||||
if (target.id === 1 || inbound.parent === 0) {
|
||||
// We're moving the group to the top level, or we're moving a top level group down
|
||||
html += "<h3>Move Group</h3>\n";
|
||||
} else {
|
||||
html += "<h3>Copy or Move?</h3>\n";
|
||||
}
|
||||
|
||||
html += "</div>\n";
|
||||
html += "<div class=\"modal-body\">\n";
|
||||
|
||||
if (target.id === 1) {
|
||||
html += "<div class=\"alert alert-info\">Are you sure you want to move group " + inbound.name + " to the top level?</div>";
|
||||
} else if (inbound.parent === 0) {
|
||||
html += "<div class=\"alert alert-info\">Are you sure you want to move group " + inbound.name + " from the top level and make it a child of " +
|
||||
target.name + "?</div>";
|
||||
} else {
|
||||
html += "<div class=\"text-center\">\n";
|
||||
html += "<p>Would you like to copy or move group <em>" + inbound.name + "</em> to group <em>" + target.name + "</em>?</p>\n";
|
||||
html += "<div style=\"margin-top: 30px;\">\n";
|
||||
html += "<a href=\"\" ng-click=\"moveGroup()\" class=\"btn btn-primary\" style=\"margin-right: 15px;\"><i class=\"fa fa-cut\"></i> Move</a>\n";
|
||||
html += "<a href=\"\" ng-click=\"copyGroup()\" class=\"btn btn-primary\"><i class=\"fa fa-copy\"></i> Copy</a>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
|
||||
html += "</div>\n";
|
||||
html += "<div class=\"modal-footer\">\n";
|
||||
html += "<a href=\"#\" data-target=\"#prompt-modal\" data-dismiss=\"modal\" class=\"btn btn-default\">Cancel</a>\n";
|
||||
|
||||
if (target.id === 1 || inbound.parent === 0) {
|
||||
// We're moving the group to the top level, or we're moving a top level group down
|
||||
html += "<a href=\"\" data-target=\"#prompt-modal\" ng-click=\"moveGroup()\" class=\"btn btn-primary\">Yes</a>\n";
|
||||
}
|
||||
|
||||
html += "</div>\n";
|
||||
html += "</div><!-- modal-content -->\n";
|
||||
html += "</div><!-- modal-dialog -->\n";
|
||||
html += "</div><!-- modal -->\n";
|
||||
|
||||
// Inject our custom dialog
|
||||
e= angular.element(document.getElementById('inventory-modal-container'));
|
||||
e.empty().append(html);
|
||||
$compile(e)(scope);
|
||||
|
||||
// Display it
|
||||
$('#copy-prompt-modal').modal({
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
show: true
|
||||
});
|
||||
|
||||
// Respond to move
|
||||
scope.moveGroup = function () {
|
||||
var url, group, parent;
|
||||
$('#copy-prompt-modal').modal('hide');
|
||||
Wait('start');
|
||||
|
||||
// disassociate the group from the original parent
|
||||
if (scope.removeGroupRemove) {
|
||||
scope.removeGroupRemove();
|
||||
}
|
||||
scope.removeGroupRemove = scope.$on('removeGroup', function () {
|
||||
if (inbound.parent > 0) {
|
||||
// Only remove a group from a parent when the parent is a group and not the inventory root
|
||||
parent = Find({ list: scope.groups, key: 'id', val: inbound.parent });
|
||||
url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/';
|
||||
Rest.setUrl(url);
|
||||
Rest.post({ id: inbound.group_id, disassociate: 1 })
|
||||
.success(function () {
|
||||
//Triggers refresh of group list in inventory controller
|
||||
scope.$emit('GroupDeleteCompleted');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
Wait('stop');
|
||||
ProcessErrors(scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to remove ' + inbound.name +
|
||||
' from ' + parent.name + '. POST returned status: ' + status
|
||||
});
|
||||
});
|
||||
} else {
|
||||
//Triggers refresh of group list in inventory controller
|
||||
scope.$emit('GroupDeleteCompleted');
|
||||
}
|
||||
});
|
||||
|
||||
// add the new group to the target parent
|
||||
url = (!Empty(target.group_id)) ?
|
||||
GetBasePath('base') + 'groups/' + target.group_id + '/children/' :
|
||||
GetBasePath('inventory') + scope.inventory_id + '/groups/';
|
||||
group = {
|
||||
id: inbound.group_id,
|
||||
name: inbound.name,
|
||||
description: inbound.description,
|
||||
inventory: scope.inventory_id
|
||||
};
|
||||
Rest.setUrl(url);
|
||||
Rest.post(group)
|
||||
.success(function () {
|
||||
scope.$emit('removeGroup');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
var target_name = (Empty(target.group_id)) ? 'inventory' : target.name;
|
||||
Wait('stop');
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to add ' + inbound.name + ' to ' + target_name + '. POST returned status: ' + status });
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
scope.copyGroup = function () {
|
||||
$('#copy-prompt-modal').modal('hide');
|
||||
Wait('start');
|
||||
// add the new group to the target parent
|
||||
var url = (!Empty(target.group_id)) ?
|
||||
GetBasePath('base') + 'groups/' + target.group_id + '/children/' :
|
||||
GetBasePath('inventory') + scope.inventory_id + '/groups/',
|
||||
group = {
|
||||
id: inbound.group_id,
|
||||
name: inbound.name,
|
||||
description: inbound.description,
|
||||
inventory: scope.inventory_id
|
||||
};
|
||||
|
||||
Rest.setUrl(url);
|
||||
Rest.post(group)
|
||||
.success(function () {
|
||||
//Triggers refresh of group list in inventory controller
|
||||
scope.$emit('GroupDeleteCompleted');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
var target_name = (Empty(target.group_id)) ? 'inventory' : target.name;
|
||||
Wait('stop');
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to add ' + inbound.name + ' to ' + target_name + '. POST returned status: ' + status
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
// Copy a host after drag-n-drop
|
||||
.factory('CopyMoveHost', ['$compile', 'Alert', 'ProcessErrors', 'Find', 'Wait', 'Rest', 'Empty', 'GetBasePath',
|
||||
function ($compile, Alert, ProcessErrors, Find, Wait, Rest, Empty, GetBasePath) {
|
||||
return function (params) {
|
||||
|
||||
var scope = params.scope,
|
||||
target = Find({ list: scope.groups, key: 'id', val: params.target_tree_id }),
|
||||
host = Find({ list: scope.hosts, key: 'id', val: params.host_id }),
|
||||
found = false, e, i, html = '';
|
||||
|
||||
if (host.summary_fields.all_groups) {
|
||||
for (i = 0; i < host.summary_fields.all_groups.length; i++) {
|
||||
if (host.summary_fields.all_groups[i].id === target.group_id) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
html += "<div id=\"copy-alert-modal\" class=\"modal fade\">\n";
|
||||
html += "<div class=\"modal-dialog\">\n";
|
||||
html += "<div class=\"modal-content\">\n";
|
||||
html += "<div class=\"modal-header\">\n";
|
||||
html += "<button type=\"button\" class=\"close\" ng-hide=\"disableButtons\" data-target=\"#copy-alert-modal\"\n";
|
||||
html += "data-dismiss=\"modal\" class=\"modal\" aria-hidden=\"true\">×</button>\n";
|
||||
html += "<h3>Already in Group</h3>\n";
|
||||
html += "</div>\n";
|
||||
html += "<div class=\"modal-body\">\n";
|
||||
html += "<div class=\"alert alert-info\"><p>Host " + host.name + " is already in group " + target.name + ".</p></div>\n";
|
||||
html += "</div>\n";
|
||||
html += "<div class=\"modal-footer\">\n";
|
||||
html += "<a href=\"#\" data-target=\"#copy-alert-modal\" data-dismiss=\"modal\" class=\"btn btn-primary\">OK</a>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div>\n";
|
||||
|
||||
// Inject our custom dialog
|
||||
e = angular.element(document.getElementById('inventory-modal-container'));
|
||||
e.empty().append(html);
|
||||
$compile(e)(scope);
|
||||
|
||||
// Display it
|
||||
$('#copy-alert-modal').modal({
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
show: true
|
||||
});
|
||||
|
||||
} else {
|
||||
// Build the html for our prompt dialog
|
||||
html = '';
|
||||
html += "<div id=\"copy-prompt-modal\" class=\"modal fade\">\n";
|
||||
html += "<div class=\"modal-dialog\">\n";
|
||||
html += "<div class=\"modal-content\">\n";
|
||||
html += "<div class=\"modal-header\">\n";
|
||||
html += "<button type=\"button\" class=\"close\" data-target=\"#copy-prompt-modal\" " +
|
||||
"data-dismiss=\"modal\" aria-hidden=\"true\">×</button>\n";
|
||||
html += "<h3>Copy Host</h3>\n";
|
||||
html += "</div>\n";
|
||||
html += "<div class=\"modal-body\">\n";
|
||||
html += "<div class=\"alert alert-info\">Are you sure you want to copy host " + host.name + ' to group ' + target.name + '?</div>';
|
||||
html += "</div>\n";
|
||||
html += "<div class=\"modal-footer\">\n";
|
||||
html += "<a href=\"#\" data-target=\"#prompt-modal\" data-dismiss=\"modal\" class=\"btn btn-default\">No</a>\n";
|
||||
html += "<a href=\"\" data-target=\"#prompt-modal\" ng-click=\"copyHost()\" class=\"btn btn-primary\">Yes</a>\n";
|
||||
html += "</div>\n";
|
||||
html += "</div><!-- modal-content -->\n";
|
||||
html += "</div><!-- modal-dialog -->\n";
|
||||
html += "</div><!-- modal -->\n";
|
||||
|
||||
// Inject our custom dialog
|
||||
e = angular.element(document.getElementById('inventory-modal-container'));
|
||||
e.empty().append(html);
|
||||
$compile(e)(scope);
|
||||
|
||||
// Display it
|
||||
$('#copy-prompt-modal').modal({
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
show: true
|
||||
});
|
||||
|
||||
scope.copyHost = function () {
|
||||
$('#copy-prompt-modal').modal('hide');
|
||||
Wait('start');
|
||||
Rest.setUrl(GetBasePath('groups') + target.group_id + '/hosts/');
|
||||
Rest.post(host)
|
||||
.success(function () {
|
||||
// Signal the controller to refresh the hosts view
|
||||
scope.$emit('GroupTreeRefreshed');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
Wait('stop');
|
||||
ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to add ' + host.name + ' to ' +
|
||||
target.name + '. POST returned status: ' + status });
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
]);
|
||||
@ -277,10 +277,14 @@ angular.module('GeneratorHelpers', [systemStatus.name])
|
||||
html = "<td class=\"" + fld + "-column";
|
||||
html += (field.columnClass) ? " " + field.columnClass : "";
|
||||
html += "\">\n";
|
||||
html += "<a ng-href=\"" + field.ngHref + "\" aw-tool-tip=\"" + field.awToolTip + "\"";
|
||||
if (!field.noLink){
|
||||
html += "<a ng-href=\"" + field.ngHref + "\" aw-tool-tip=\"" + field.awToolTip + "\"";
|
||||
html += (field.dataPlacement) ? " data-placement=\"" + field.dataPlacement + "\"" : "";
|
||||
html += ">";
|
||||
}
|
||||
html += "<span class=\"badge\"";
|
||||
html += " aw-tool-tip=\"" + field.awToolTip + "\"";
|
||||
html += (field.dataPlacement) ? " data-placement=\"" + field.dataPlacement + "\"" : "";
|
||||
html += ">";
|
||||
html += "<span class=\"badge";
|
||||
html += (field['class']) ? " " + field['class'] : "";
|
||||
html += (field.ngHide) ? "\" ng-hide=\"" + field.ngHide : "";
|
||||
html += "\">";
|
||||
|
||||
@ -439,6 +439,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
innerTable += "ng-class-odd=\"'List-tableRow--oddRow'\" ";
|
||||
innerTable += "ng-class-even=\"'List-tableRow--evenRow'\" ";
|
||||
innerTable += "ng-repeat=\"" + list.iterator + " in " + list.name;
|
||||
innerTable += (list.trackBy) ? " track by " + list.trackBy : " track by $index";
|
||||
innerTable += (list.orderBy) ? " | orderBy:'" + list.orderBy + "'" : "";
|
||||
innerTable += (list.filterBy) ? " | filter: " + list.filterBy : "";
|
||||
innerTable += "\">\n";
|
||||
@ -459,7 +460,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
"ng-false-value=\"0\" id=\"check_" + list.iterator + "_{{" + list.iterator + ".id}}\" /></td>";
|
||||
}
|
||||
else { // its assumed that options.input_type = checkbox
|
||||
innerTable += "<td class=\"List-tableCell\"><input type=\"checkbox\" ng-model=\"" + list.iterator + ".checked\" name=\"check_" + list.iterator + "_{{" +
|
||||
innerTable += "<td class=\"List-tableCell select-column List-staticColumn--smallStatus\"><input type=\"checkbox\" ng-model=\"" + list.iterator + ".checked\" name=\"check_{{" +
|
||||
list.iterator + ".id }}\" ng-click=\"toggle_" + list.iterator + "(" + list.iterator + ".id, true)\" ng-true-value=\"1\" " +
|
||||
"ng-false-value=\"0\" id=\"check_" + list.iterator + "_{{" + list.iterator + ".id}}\" /></td>";
|
||||
}
|
||||
@ -619,7 +620,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
|
||||
html += buildSelectAll().prop('outerHTML');
|
||||
}
|
||||
else if (options.mode === 'lookup') {
|
||||
html += "<th class=\"List-tableHeader List-staticColumn--smallStatus col-lg-1 col-md-1 col-sm-2 col-xs-2\"></th>";
|
||||
html += "<th class=\"List-tableHeader select-column List-staticColumn--smallStatus\"></th>";
|
||||
}
|
||||
for (fld in list.fields) {
|
||||
if ((list.fields[fld].searchOnly === undefined || list.fields[fld].searchOnly === false) &&
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import '../support/node';
|
||||
|
||||
import adhocModule from 'adhoc/main';
|
||||
import adhocModule from 'inventories/manage/adhoc/main';
|
||||
import RestStub from '../support/rest-stub';
|
||||
|
||||
describe("adhoc.controller", function() {
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
<include-svg href="{{ STATIC_URL }}assets/icons.svg"></include-svg>
|
||||
|
||||
<main-menu></main-menu>
|
||||
<bread-crumb></bread-crumb>
|
||||
<bread-crumb ng-if="!includesCurrentState('inventoryManage')"></bread-crumb><div ui-view="groupBreadcrumbs" ng-if="includesCurrentState('inventoryManage')"></div>
|
||||
<toast></toast>
|
||||
|
||||
<div class="container-fluid" id="content-container">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user