Merge pull request #6350 from jlmitch5/scmInvSourceUi

add scm inv source ui
This commit is contained in:
jlmitch5
2017-05-26 12:16:25 -04:00
committed by GitHub
11 changed files with 503 additions and 119 deletions

View File

@@ -5,13 +5,12 @@
*************************************************/ *************************************************/
export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition', export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition',
'ParseTypeChange', 'GenerateForm', 'inventoryData', 'GroupManageService', 'ParseTypeChange', 'GenerateForm', 'inventoryData', 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', 'Empty',
'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', 'rbacUiControlService', 'ToJSON', 'SourcesService', 'Wait', 'Rest', 'Alert', 'ProcessErrors',
'rbacUiControlService', 'ToJSON', 'SourcesService',
function($state, $stateParams, $scope, SourcesFormDefinition, ParseTypeChange, function($state, $stateParams, $scope, SourcesFormDefinition, ParseTypeChange,
GenerateForm, inventoryData, GroupManageService, GetChoices, GenerateForm, inventoryData, GetChoices,
GetBasePath, CreateSelect2, GetSourceTypeOptions, rbacUiControlService, GetBasePath, CreateSelect2, GetSourceTypeOptions, Empty, rbacUiControlService,
ToJSON, SourcesService) { ToJSON, SourcesService, Wait, Rest, Alert, ProcessErrors) {
let form = SourcesFormDefinition; let form = SourcesFormDefinition;
init(); init();
@@ -20,82 +19,153 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition',
// apply form definition's default field values // apply form definition's default field values
GenerateForm.applyDefaults(form, $scope); GenerateForm.applyDefaults(form, $scope);
rbacUiControlService.canAdd(GetBasePath('inventory') + $stateParams.inventory_id + "/inventory_sources") rbacUiControlService.canAdd(GetBasePath('inventory') + $stateParams.inventory_id + "/inventory_sources")
.then(function(canAdd) { .then(function(canAdd) {
$scope.canAdd = canAdd; $scope.canAdd = canAdd;
}); });
$scope.envParseType = 'yaml';
initSources(); initSources();
} }
var getInventoryFiles = function (project) {
var url;
if (!Empty(project)) {
url = GetBasePath('projects') + project + '/inventories/';
Wait('start');
Rest.setUrl(url);
Rest.get()
.success(function (data) {
$scope.inventory_files = data;
$scope.inventory_files.push("/ (project root)");
sync_inventory_file_select2();
Wait('stop');
})
.error(function () {
Alert('Cannot get inventory files', 'Unable to retrieve the list of inventory files for this project.', 'alert-info');
Wait('stop');
});
}
};
// Detect and alert user to potential SCM status issues
var checkSCMStatus = function () {
if (!Empty($scope.project)) {
Rest.setUrl(GetBasePath('projects') + $scope.project + '/');
Rest.get()
.success(function (data) {
var msg;
switch (data.status) {
case 'failed':
msg = "<div>The Project selected has a status of \"failed\". You must run a successful update before you can select an inventory file.";
break;
case 'never updated':
msg = "<div>The Project selected has a status of \"never updated\". You must run a successful update before you can select an inventory file.";
break;
case 'missing':
msg = '<div>The selected project has a status of \"missing\". Please check the server and make sure ' +
' the directory exists and file permissions are set correctly.</div>';
break;
}
if (msg) {
Alert('Warning', msg, 'alert-info alert-info--noTextTransform', null, null, null, null, true);
}
})
.error(function (data, status) {
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to get project ' + $scope.project + '. GET returned status: ' + status });
});
}
};
// Register a watcher on project_name
if ($scope.getInventoryFilesUnregister) {
$scope.getInventoryFilesUnregister();
}
$scope.getInventoryFilesUnregister = $scope.$watch('project', function (newValue, oldValue) {
if (newValue !== oldValue) {
getInventoryFiles(newValue);
checkSCMStatus();
}
});
function sync_inventory_file_select2() {
CreateSelect2({
element:'#inventory-file-select',
addNew: true,
multiple: false,
scope: $scope,
options: 'inventory_files',
model: 'inventory_file'
});
}
$scope.lookupCredential = function(){ $scope.lookupCredential = function(){
let kind = ($scope.source.value === "ec2") ? "aws" : $scope.source.value;
$state.go('.credential', { $state.go('.credential', {
credential_search: { credential_search: {
kind: kind, // TODO: get kind sorting for credential properly implemented
// kind: kind,
page_size: '5', page_size: '5',
page: '1' page: '1'
} }
}); });
}; };
$scope.formCancel = function() { $scope.lookupProject = function(){
$state.go('^'); $state.go('.project', {
}; project_search: {
page_size: '5',
$scope.formSave = function() { page: '1'
var params; }
params = {
name: $scope.name,
description: $scope.description,
inventory: inventoryData.id,
instance_filters: $scope.instance_filters,
source_script: $scope.inventory_script,
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(',')
};
if ($scope.source) {
params.source_vars = $scope[$scope.source.value + '_variables'] === '---' || $scope[$scope.source.value + '_variables'] === '{}' ? null : $scope[$scope.source.value + '_variables'];
params.source = $scope.source.value;
} else {
params.source = null;
}
SourcesService.post(params).then(function(res){
let inventory_source_id = res.data.id;
$state.go('^.edit', {inventory_source_id: inventory_source_id}, {reload: true});
}); });
}; };
$scope.projectBasePath = GetBasePath('projects');
$scope.credentialBasePath = GetBasePath('credentials') + '?credential_type__kind__in=cloud,network';
$scope.sourceChange = function(source) { $scope.sourceChange = function(source) {
source = source.value; if (source) {
source = source.value;
} else {
source = "";
}
$scope.credentialBasePath = GetBasePath('credentials') + '?credential_type__kind__in=cloud,network';
if (source === 'custom'){ if (source === 'custom'){
$scope.credentialBasePath = GetBasePath('inventory_script'); $scope.credentialBasePath = GetBasePath('inventory_script');
} }
// equal to case 'ec2' || 'rax' || 'azure' || 'azure_rm' || 'vmware' || 'satellite6' || 'cloudforms' || 'openstack'
else{ if (source === 'ec2' || source === 'custom' || source === 'vmware' || source === 'openstack' || source === 'scm') {
$scope.credentialBasePath = (source === 'ec2') ? GetBasePath('credentials') + '?kind=aws' : GetBasePath('credentials') + (source === '' ? '' : '?kind=' + (source)); $scope.envParseType = 'yaml';
}
if (source === 'ec2' || source === 'custom' || source === 'vmware' || source === 'openstack') { var varName;
if (source === 'scm') {
varName = 'custom_variables';
} else {
varName = source + '_variables';
}
ParseTypeChange({ ParseTypeChange({
scope: $scope, scope: $scope,
field_id: source + '_variables', field_id: varName,
variable: source + '_variables', variable: varName,
parse_variable: 'envParseType' parse_variable: 'envParseType'
}); });
} }
if (source === 'scm') {
$scope.overwrite_vars = true;
$scope.inventory_source_form.inventory_file.$setPristine();
} else {
$scope.overwrite_vars = false;
}
// reset fields // reset fields
$scope.group_by_choices = source === 'ec2' ? $scope.ec2_group_by : null; $scope.group_by_choices = source === 'ec2' ? $scope.ec2_group_by : null;
// azure_rm regions choices are keyed as "azure" in an OPTIONS request to the inventory_sources endpoint // azure_rm regions choices are keyed as "azure" in an OPTIONS request to the inventory_sources endpoint
$scope.source_region_choices = source === 'azure_rm' ? $scope.azure_regions : $scope[source + '_regions']; $scope.source_region_choices = source === 'azure_rm' ? $scope.azure_regions : $scope[source + '_regions'];
$scope.cloudCredentialRequired = source !== '' && source !== 'custom' && source !== 'ec2' ? true : false; $scope.cloudCredentialRequired = source !== '' && source !== 'scm' && source !== 'custom' && source !== 'ec2' ? true : false;
$scope.group_by = null; $scope.group_by = null;
$scope.source_regions = null; $scope.source_regions = null;
$scope.credential = null; $scope.credential = null;
@@ -107,6 +177,10 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition',
initRegionSelect(); initRegionSelect();
}); });
$scope.$on('choicesReadyVerbosity', function() {
initVerbositySelect();
});
$scope.$on('sourceTypeOptionsReady', function() { $scope.$on('sourceTypeOptionsReady', function() {
initSourceSelect(); initSourceSelect();
}); });
@@ -121,6 +195,7 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition',
multiple: true multiple: true
}); });
} }
function initSourceSelect(){ function initSourceSelect(){
CreateSelect2({ CreateSelect2({
element: '#inventory_source_source', element: '#inventory_source_source',
@@ -128,6 +203,15 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition',
}); });
} }
function initVerbositySelect(){
CreateSelect2({
element: '#inventory_source_verbosity',
multiple: false
});
$scope.verbosity = $scope.verbosity_options[0];
}
function initSources(){ function initSources(){
GetChoices({ GetChoices({
scope: $scope, scope: $scope,
@@ -174,11 +258,66 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition',
choice_name: 'ec2_group_by_choices', choice_name: 'ec2_group_by_choices',
callback: 'choicesReadyGroup' callback: 'choicesReadyGroup'
}); });
GetChoices({
scope: $scope,
url: GetBasePath('inventory_sources'),
field: 'verbosity',
variable: 'verbosity_options',
callback: 'choicesReadyVerbosity'
});
GetSourceTypeOptions({ GetSourceTypeOptions({
scope: $scope, scope: $scope,
variable: 'source_type_options', variable: 'source_type_options',
//callback: 'sourceTypeOptionsReady' this callback is hard-coded into GetSourceTypeOptions(), included for ref //callback: 'sourceTypeOptionsReady' this callback is hard-coded into GetSourceTypeOptions(), included for ref
}); });
} }
$scope.formCancel = function() {
$state.go('^');
};
$scope.formSave = function() {
var params;
params = {
name: $scope.name,
description: $scope.description,
inventory: inventoryData.id,
instance_filters: $scope.instance_filters,
source_script: $scope.inventory_script,
credential: $scope.credential,
overwrite: $scope.overwrite,
overwrite_vars: $scope.overwrite_vars,
update_on_launch: $scope.update_on_launch,
verbosity: $scope.verbosity.value,
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(',')
};
if ($scope.source) {
params.source_vars = $scope[$scope.source.value + '_variables'] === '---' || $scope[$scope.source.value + '_variables'] === '{}' ? null : $scope[$scope.source.value + '_variables'];
params.source = $scope.source.value;
if ($scope.source.value === 'scm') {
params.update_on_project_update = $scope.update_on_project_update;
params.source_project = $scope.project;
if ($scope.inventory_file === '/ (project root)') {
params.source_path = "";
} else {
params.source_path = $scope.inventory_file;
}
}
} else {
params.source = null;
}
SourcesService.post(params).then(function(res){
let inventory_source_id = res.data.id;
$state.go('^.edit', {inventory_source_id: inventory_source_id}, {reload: true});
});
};
} }
]; ];

View File

@@ -5,13 +5,11 @@
*************************************************/ *************************************************/
export default ['$state', '$stateParams', '$scope', 'ParseVariableString', export default ['$state', '$stateParams', '$scope', 'ParseVariableString',
'rbacUiControlService', 'ToJSON', 'ParseTypeChange', 'GroupManageService', 'rbacUiControlService', 'ToJSON', 'ParseTypeChange', 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions',
'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions', 'inventorySourceData', 'SourcesService', 'inventoryData', 'Empty', 'Wait', 'Rest', 'Alert', 'ProcessErrors',
'inventorySourceData', 'SourcesService', 'inventoryData',
function($state, $stateParams, $scope, ParseVariableString, function($state, $stateParams, $scope, ParseVariableString,
rbacUiControlService, ToJSON,ParseTypeChange, GroupManageService, rbacUiControlService, ToJSON,ParseTypeChange, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions,
GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions, inventorySourceData, SourcesService, inventoryData, Empty, Wait, Rest, Alert, ProcessErrors) {
inventorySourceData, SourcesService, inventoryData) {
init(); init();
@@ -20,8 +18,19 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString',
.then(function(canAdd) { .then(function(canAdd) {
$scope.canAdd = canAdd; $scope.canAdd = canAdd;
}); });
// instantiate expected $scope values from inventorySourceData // instantiate expected $scope values from inventorySourceData
_.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 }); _.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},
{verbosity: inventorySourceData.verbosity});
$scope.inventory_source_obj = inventorySourceData;
if (inventorySourceData.credential) { if (inventorySourceData.credential) {
$scope.credential_name = inventorySourceData.summary_fields.credential.name; $scope.credential_name = inventorySourceData.summary_fields.credential.name;
} }
@@ -41,6 +50,113 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString',
initSources(); initSources();
} }
var getInventoryFiles = function (project) {
var url;
if (!Empty(project)) {
url = GetBasePath('projects') + project + '/inventories/';
Wait('start');
Rest.setUrl(url);
Rest.get()
.success(function (data) {
$scope.inventory_files = data;
$scope.inventory_files.push("/ (project root)");
if (inventorySourceData.source_path !== "") {
$scope.inventory_file = inventorySourceData.source_path;
if ($scope.inventory_files.indexOf($scope.inventory_file) < 0) {
$scope.inventory_files.push($scope.inventory_file);
}
} else {
$scope.inventory_file = "/ (project root)";
}
sync_inventory_file_select2();
Wait('stop');
})
.error(function () {
Alert('Cannot get inventory files', 'Unable to retrieve the list of inventory files for this project.', 'alert-info');
Wait('stop');
});
}
};
// Detect and alert user to potential SCM status issues
var checkSCMStatus = function () {
if (!Empty($scope.project)) {
Rest.setUrl(GetBasePath('projects') + $scope.project + '/');
Rest.get()
.success(function (data) {
var msg;
switch (data.status) {
case 'failed':
msg = "<div>The Project selected has a status of \"failed\". You must run a successful update before you can select an inventory file.";
break;
case 'never updated':
msg = "<div>The Project selected has a status of \"never updated\". You must run a successful update before you can select an inventory file.";
break;
case 'missing':
msg = '<div>The selected project has a status of \"missing\". Please check the server and make sure ' +
' the directory exists and file permissions are set correctly.</div>';
break;
}
if (msg) {
Alert('Warning', msg, 'alert-info alert-info--noTextTransform', null, null, null, null, true);
}
})
.error(function (data, status) {
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
msg: 'Failed to get project ' + $scope.project + '. GET returned status: ' + status });
});
}
};
// Register a watcher on project_name
if ($scope.getInventoryFilesUnregister) {
$scope.getInventoryFilesUnregister();
}
$scope.getInventoryFilesUnregister = $scope.$watch('project', function (newValue, oldValue) {
if (newValue !== oldValue) {
getInventoryFiles(newValue);
checkSCMStatus();
}
});
function sync_inventory_file_select2() {
CreateSelect2({
element:'#inventory-file-select',
addNew: true,
multiple: false,
scope: $scope,
options: 'inventory_files',
model: 'inventory_file'
});
// TODO: figure out why the inventory file model is being set to
// dirty
}
$scope.lookupCredential = function(){
$state.go('.credential', {
credential_search: {
// TODO: get kind sorting for credential properly implemented
// kind: kind,
page_size: '5',
page: '1'
}
});
};
$scope.lookupProject = function(){
$state.go('.project', {
project_search: {
page_size: '5',
page: '1'
}
});
};
$scope.projectBasePath = GetBasePath('projects');
var initRegionSelect = function() { var initRegionSelect = function() {
CreateSelect2({ CreateSelect2({
element: '#inventory_source_source_regions', element: '#inventory_source_source_regions',
@@ -89,45 +205,49 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString',
if ($scope.source) { if ($scope.source) {
params.source_vars = $scope[$scope.source.value + '_variables'] === '---' || $scope[$scope.source.value + '_variables'] === '{}' ? null : $scope[$scope.source.value + '_variables']; params.source_vars = $scope[$scope.source.value + '_variables'] === '---' || $scope[$scope.source.value + '_variables'] === '{}' ? null : $scope[$scope.source.value + '_variables'];
params.source = $scope.source.value; params.source = $scope.source.value;
if ($scope.source.value === 'scm') {
params.update_on_project_update = $scope.update_on_project_update;
params.source_path = $scope.inventory_file;
}
} else { } else {
params.source = null; params.source = null;
} }
// switch (source) {
// no inventory source set, just create a new group SourcesService
// '' is the value supplied for Manual source type .put(params)
// case null || '': .then(() => $state.go('.', null, { reload: true }));
SourcesService.put(params).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' || 'satellite6' || 'cloudforms' || 'openstack' || 'custom'
// default:
// GroupManageService.put(group)
// .then(() => GroupManageService.putInventorySource(params, groupData.related.inventory_source))
// .then(() => $state.go($state.current, null, { reload: true }));
// break;
// }
}; };
$scope.sourceChange = function(source) { $scope.sourceChange = function(source) {
$scope.source = source; $scope.source = source;
if (source.value === 'ec2' || source.value === 'custom' || if (source.value === 'ec2' || source.value === 'custom' ||
source.value === 'vmware' || source.value === 'openstack') { source.value === 'vmware' || source.value === 'openstack' ||
$scope[source.value + '_variables'] = $scope[source.value + '_variables'] === (null || undefined) ? '---' : $scope[source.value + '_variables']; source.value === 'scm') {
var varName;
if (source === 'scm') {
varName = 'custom_variables';
} else {
varName = source + '_variables';
}
$scope[varName] = $scope[varName] === (null || undefined) ? '---' : $scope[varName];
ParseTypeChange({ ParseTypeChange({
scope: $scope, scope: $scope,
field_id: source.value + '_variables', field_id: varName,
variable: source.value + '_variables', variable: varName,
parse_variable: 'envParseType', parse_variable: 'envParseType',
}); });
} }
// reset fields // reset fields
// azure_rm regions choices are keyed as "azure" in an OPTIONS request to the inventory_sources endpoint // azure_rm regions choices are keyed as "azure" in an OPTIONS request to the inventory_sources endpoint
$scope.source_region_choices = source.value === 'azure_rm' ? $scope.azure_regions : $scope[source.value + '_regions']; $scope.source_region_choices = source.value === 'azure_rm' ? $scope.azure_regions : $scope[source.value + '_regions'];
$scope.cloudCredentialRequired = source.value !== '' && source.value !== 'custom' && source.value !== 'ec2' ? true : false; $scope.cloudCredentialRequired = source.value !== '' && source.value !== 'custom' && source.value !== 'scm' && source.value !== 'ec2' ? true : false;
$scope.group_by = null; $scope.group_by = null;
$scope.source_regions = null; $scope.source_regions = null;
$scope.credential = null; $scope.credential = null;
$scope.credential_name = null; $scope.credential_name = null;
initRegionSelect(); initRegionSelect();
}; };
@@ -222,6 +342,41 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString',
}); });
} }
function initVerbositySelect(){
CreateSelect2({
element: '#inventory_source_verbosity',
multiple: false
});
}
function sync_verbosity_select2() {
CreateSelect2({
element:'#inventory_source_verbosity',
multiple: false
});
}
$scope.$on('choicesReadyVerbosity', function() {
var i;
for (i = 0; i < $scope.verbosity_options.length; i++) {
if ($scope.verbosity_options[i].value === $scope.verbosity) {
$scope.verbosity = $scope.verbosity_options[i];
}
}
initVerbositySelect();
});
$scope.$watch('verbosity', sync_verbosity_select2);
GetChoices({
scope: $scope,
url: GetBasePath('inventory_sources'),
field: 'verbosity',
variable: 'verbosity_options',
callback: 'choicesReadyVerbosity'
});
// region / source options callback // region / source options callback
$scope.$on('choicesReadyGroup', function() { $scope.$on('choicesReadyGroup', function() {
if (angular.isObject($scope.source)) { if (angular.isObject($scope.source)) {

View File

@@ -11,7 +11,7 @@ export default
.success(function (data) { .success(function (data) {
var i, choices = data.actions.GET.source.choices; var i, choices = data.actions.GET.source.choices;
for (i = 0; i < choices.length; i++) { for (i = 0; i < choices.length; i++) {
if (choices[i][0] !== 'file') { if (choices[i][0] !== 'file' && choices[i][0] !== "") {
scope[variable].push({ scope[variable].push({
label: choices[i][1], label: choices[i][1],
value: choices[i][0] value: choices[i][0]

View File

@@ -5,17 +5,18 @@
*************************************************/ *************************************************/
export default export default
['$scope', '$rootScope', '$state', '$stateParams', 'SourcesListDefinition', ['$scope', '$rootScope', '$state', '$stateParams', 'SourcesListDefinition',
'InventoryUpdate', 'GroupManageService', 'CancelSourceUpdate', 'InventoryUpdate', 'CancelSourceUpdate',
'ViewUpdateStatus', 'rbacUiControlService', 'GetBasePath', 'ViewUpdateStatus', 'rbacUiControlService', 'GetBasePath',
'GetSyncStatusMsg', 'Dataset', 'Find', 'QuerySet', 'GetSyncStatusMsg', 'Dataset', 'Find', 'QuerySet',
'inventoryData', '$filter', 'Prompt', 'Wait', 'SourcesService', 'inventorySourceOptions', 'inventoryData', '$filter', 'Prompt', 'Wait', 'SourcesService', 'inventorySourceOptions',
function($scope, $rootScope, $state, $stateParams, SourcesListDefinition, function($scope, $rootScope, $state, $stateParams, SourcesListDefinition,
InventoryUpdate, GroupManageService, CancelSourceUpdate, InventoryUpdate, CancelSourceUpdate,
ViewUpdateStatus, rbacUiControlService, GetBasePath, GetSyncStatusMsg, ViewUpdateStatus, rbacUiControlService, GetBasePath, GetSyncStatusMsg,
Dataset, Find, qs, inventoryData, $filter, Prompt, Dataset, Find, qs, inventoryData, $filter, Prompt,
Wait, SourcesService, inventorySourceOptions){ Wait, SourcesService, inventorySourceOptions){
let list = SourcesListDefinition; let list = SourcesListDefinition;
var inventory_source;
init(); init();
@@ -24,7 +25,7 @@
$scope.canAdhoc = inventoryData.summary_fields.user_capabilities.adhoc; $scope.canAdhoc = inventoryData.summary_fields.user_capabilities.adhoc;
$scope.canAdd = false; $scope.canAdd = false;
rbacUiControlService.canAdd(GetBasePath('inventory') + $scope.inventory_id + "/groups") rbacUiControlService.canAdd(GetBasePath('inventory') + $scope.inventory_id + "/inventory_sources")
.then(function(canAdd) { .then(function(canAdd) {
$scope.canAdd = canAdd; $scope.canAdd = canAdd;
}); });
@@ -39,7 +40,7 @@
optionsRequestDataProcessing(); optionsRequestDataProcessing();
$scope.$on(`ws-jobs`, function(e, data){ $scope.$on(`ws-jobs`, function(e, data){
var inventory_source = Find({ list: $scope.inventory_sources, key: 'id', val: data.inventory_source_id }); inventory_source = Find({ list: $scope.inventory_sources, key: 'id', val: data.inventory_source_id });
if (inventory_source === undefined || inventory_source === null) { if (inventory_source === undefined || inventory_source === null) {
inventory_source = {}; inventory_source = {};
@@ -125,11 +126,11 @@
Wait('start'); Wait('start');
SourcesService.delete(inventory_source.id).then(() => { SourcesService.delete(inventory_source.id).then(() => {
$('#prompt-modal').modal('hide'); $('#prompt-modal').modal('hide');
// if (parseInt($state.params.source_id) === id) { if (parseInt($state.params.source_id) === inventory_source) {
// $state.go("sources", null, {reload: true}); $state.go("sources", null, {reload: true});
// } else { } else {
$state.go($state.current.name, null, {reload: true}); $state.go($state.current.name, null, {reload: true});
// } }
Wait('stop'); Wait('stop');
}); });
}; };
@@ -160,7 +161,7 @@
}); });
}; };
$scope.scheduleSource = function(id) { $scope.scheduleSource = function(id) {
// Add this group's id to the array of group id's so that it gets // Add this inv source's id to the array of inv source id's so that it gets
// added to the breadcrumb trail // added to the breadcrumb trail
$state.go('inventories.edit.inventory_sources.edit.schedules', {inventory_source_id: id}, {reload: true}); $state.go('inventories.edit.inventory_sources.edit.schedules', {inventory_source_id: id}, {reload: true});
}; };

View File

@@ -5,7 +5,7 @@
<div class="Modal-title ng-binding"> <div class="Modal-title ng-binding">
Delete Group Delete Group
<a href="" id="awp-promote" href="" <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" aw-pop-over="<dl><dt>Delete</dt><dd>Deletes groups and hosts associated with the inventory source being deleted. If a group or host is associated with other inventory sources, it will still exist within those inventory sources. 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 inventory source being removed will be promoted root level. Note: groups already associated with other inventory sources cannot be promoted.</dd></dl>\n" aw-tool-tip="Click for help"
data-placement="right" data-placement="right"
data-container="body" data-container="body"
data-title="Delete Group" data-title="Delete Group"
@@ -66,7 +66,7 @@
</div> </div>
<div ng-show="toDelete.total_groups == 0 && toDelete.total_hosts == 0"> <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-bodyQuery">Are you sure you want to permanently delete the inventory source below from the inventory?</div>
<div class="Prompt-bodyTarget">{{ toDelete.name }}</div> <div class="Prompt-bodyTarget">{{ toDelete.name }}</div>
</div> </div>
<div class="Modal-footer"> <div class="Modal-footer">

View File

@@ -47,14 +47,14 @@ return {
name: { name: {
label: 'Name', label: 'Name',
type: 'text', type: 'text',
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)', ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)',
required: true, required: true,
tab: 'properties' tab: 'properties'
}, },
description: { description: {
label: 'Description', label: 'Description',
type: 'text', type: 'text',
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)', ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)',
tab: 'properties' tab: 'properties'
}, },
source: { source: {
@@ -62,20 +62,15 @@ return {
type: 'select', type: 'select',
ngOptions: 'source.label for source in source_type_options track by source.value', ngOptions: 'source.label for source in source_type_options track by source.value',
ngChange: 'sourceChange(source)', ngChange: 'sourceChange(source)',
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)', ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)',
ngModel: 'source' ngModel: 'source'
}, },
credential: { credential: {
// initializes a default value for this search param label: 'Credential',
// search params with default values set will not generate user-interactable search tags
search: {
kind: null
},
label: 'Cloud Credential',
type: 'lookup', type: 'lookup',
list: 'CredentialList', list: 'CredentialList',
basePath: 'credentials', basePath: 'credentials',
ngShow: "source && source.value !== '' && source.value !== 'custom'", ngShow: "source && source.value !== ''",
sourceModel: 'credential', sourceModel: 'credential',
sourceField: 'name', sourceField: 'name',
ngClick: 'lookupCredential()', ngClick: 'lookupCredential()',
@@ -83,9 +78,45 @@ return {
reqExpression: "cloudCredentialRequired", reqExpression: "cloudCredentialRequired",
init: "false" init: "false"
}, },
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)', ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)',
watchBasePath: "credentialBasePath" watchBasePath: "credentialBasePath"
}, },
project: {
// initializes a default value for this search param
// search params with default values set will not generate user-interactable search tags
label: 'Project',
type: 'lookup',
list: 'ProjectList',
basePath: 'projects',
ngShow: "source && source.value === 'scm'",
sourceModel: 'project',
sourceField: 'name',
ngClick: 'lookupProject()',
awRequiredWhen: {
reqExpression: "projectRequired",
init: "false"
},
ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)',
watchBasePath: "projectBasePath"
},
inventory_file: {
label: i18n._('Inventory File'),
type:'select',
ngOptions: 'file for file in inventory_files track by file',
ngShow: "source && source.value === 'scm'",
ngDisabled: "!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd) || disableInventoryFileBecausePermissionDenied",
id: 'inventory-file-select',
awRequiredWhen: {
reqExpression: "inventoryfilerequired",
init: "true"
},
column: 1,
awPopOver: "<p>" + i18n._("Select the inventory file to be synced by this source. You can select from the dropdown or enter a file within the input.") + "</p>",
dataTitle: i18n._('Inventory File'),
dataPlacement: 'right',
dataContainer: "body",
includeInventoryFileNotFoundError: true
},
source_regions: { source_regions: {
label: 'Regions', label: 'Regions',
type: 'select', type: 'select',
@@ -100,7 +131,7 @@ return {
"or choose <em>All</em> to include all regions. Tower will only be updated with Hosts associated with the selected regions." + "or choose <em>All</em> to include all regions. Tower will only be updated with Hosts associated with the selected regions." +
"</p>", "</p>",
dataContainer: 'body', dataContainer: 'body',
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)' ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)'
}, },
instance_filters: { instance_filters: {
label: 'Instance Filters', label: 'Instance Filters',
@@ -119,7 +150,7 @@ return {
"<p>View the <a href=\"http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeInstances.html\" target=\"_blank\">Describe Instances documentation</a> " + "<p>View the <a href=\"http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-DescribeInstances.html\" target=\"_blank\">Describe Instances documentation</a> " +
"for a complete list of supported filters.</p>", "for a complete list of supported filters.</p>",
dataContainer: 'body', dataContainer: 'body',
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)' ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)'
}, },
group_by: { group_by: {
label: 'Only Group By', label: 'Only Group By',
@@ -143,7 +174,7 @@ return {
"<li>Tag None: <strong>tags &raquo; tag_none</strong></li>" + "<li>Tag None: <strong>tags &raquo; tag_none</strong></li>" +
"</ul><p>If blank, all groups above are created except <em>Instance ID</em>.</p>", "</ul><p>If blank, all groups above are created except <em>Instance ID</em>.</p>",
dataContainer: 'body', dataContainer: 'body',
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)' ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)'
}, },
inventory_script: { inventory_script: {
label : "Custom Inventory Script", label : "Custom Inventory Script",
@@ -157,12 +188,12 @@ return {
reqExpression: "source && source.value === 'custom'", reqExpression: "source && source.value === 'custom'",
init: "false" init: "false"
}, },
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)', ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)',
}, },
custom_variables: { custom_variables: {
id: 'custom_variables', id: 'custom_variables',
label: 'Environment Variables', //"{{vars_label}}" , label: 'Environment Variables', //"{{vars_label}}" ,
ngShow: "source && source.value=='custom' ", ngShow: "source && source.value=='custom' || source.value === 'scm'",
type: 'textarea', type: 'textarea',
class: 'Form-textAreaLabel Form-formGroup--fullWidth', class: 'Form-textAreaLabel Form-formGroup--fullWidth',
rows: 6, rows: 6,
@@ -249,6 +280,20 @@ return {
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>', '<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>',
dataContainer: 'body' dataContainer: 'body'
}, },
verbosity: {
label: i18n._('Verbosity'),
type: 'select',
ngOptions: 'v.label for v in verbosity_options track by v.value',
ngShow: "source && (source.value !== '' && source.value !== null)",
"default": 0,
required: true,
column: 1,
awPopOver: "<p>" + i18n._("Control the level of output ansible will produce for inventory source update jobs.") + "</p>",
dataTitle: i18n._('Verbosity'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)',
},
checkbox_group: { checkbox_group: {
label: 'Update Options', label: 'Update Options',
type: 'checkbox_group', type: 'checkbox_group',
@@ -268,7 +313,7 @@ return {
dataContainer: 'body', dataContainer: 'body',
dataPlacement: 'right', dataPlacement: 'right',
labelClass: 'checkbox-options', labelClass: 'checkbox-options',
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)' ngDisabled: "(!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd))"
}, { }, {
name: 'overwrite_vars', name: 'overwrite_vars',
label: 'Overwrite Variables', label: 'Overwrite Variables',
@@ -283,7 +328,7 @@ return {
dataContainer: 'body', dataContainer: 'body',
dataPlacement: 'right', dataPlacement: 'right',
labelClass: 'checkbox-options', labelClass: 'checkbox-options',
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)' ngDisabled: "(!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd) || source.value === 'scm')"
}, { }, {
name: 'update_on_launch', name: 'update_on_launch',
label: 'Update on Launch', label: 'Update on Launch',
@@ -295,14 +340,25 @@ return {
dataContainer: 'body', dataContainer: 'body',
dataPlacement: 'right', dataPlacement: 'right',
labelClass: 'checkbox-options', labelClass: 'checkbox-options',
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)' ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)'
}, {
name: 'update_on_project_update',
label: 'Update on Project Update',
type: 'checkbox',
ngShow: "source.value === 'scm'",
awPopOver: '<p>TODO</p>',
dataTitle: 'Update on Project Update',
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options',
ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)'
}] }]
}, },
update_cache_timeout: { update_cache_timeout: {
label: "Cache Timeout <span class=\"small-text\"> (seconds)</span>", label: "Cache Timeout <span class=\"small-text\"> (seconds)</span>",
id: 'source-cache-timeout', id: 'source-cache-timeout',
type: 'number', type: 'number',
ngDisabled: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)', ngDisabled: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)',
integer: true, integer: true,
min: 0, min: 0,
ngShow: "source && source.value !== '' && update_on_launch", ngShow: "source && source.value !== '' && update_on_launch",
@@ -320,16 +376,16 @@ return {
buttons: { buttons: {
cancel: { cancel: {
ngClick: 'formCancel()', ngClick: 'formCancel()',
ngShow: '(group_obj.summary_fields.user_capabilities.edit || canAdd)' ngShow: '(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)'
}, },
close: { close: {
ngClick: 'formCancel()', ngClick: 'formCancel()',
ngShow: '!(group_obj.summary_fields.user_capabilities.edit || canAdd)' ngShow: '!(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)'
}, },
save: { save: {
ngClick: 'formSave()', ngClick: 'formSave()',
ngDisabled: true, ngDisabled: true,
ngShow: '(group_obj.summary_fields.user_capabilities.edit || canAdd)' ngShow: '(inventory_source_obj.summary_fields.user_capabilities.edit || canAdd)'
} }
}, },

View File

@@ -67,7 +67,7 @@ export default {
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-4 text-right', columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-4 text-right',
group_update: { source_update: {
//label: 'Sync', //label: 'Sync',
mode: 'all', mode: 'all',
ngClick: 'updateSource(inventory_source)', ngClick: 'updateSource(inventory_source)',
@@ -102,7 +102,7 @@ export default {
awToolTip: "{{ inventory_source.group_schedule_tooltip }}", awToolTip: "{{ inventory_source.group_schedule_tooltip }}",
ngClass: "inventory_source.scm_type_class", ngClass: "inventory_source.scm_type_class",
dataPlacement: 'top', dataPlacement: 'top',
ngShow: "!(inventory_source.summary_fields.inventory_source.source === '')" ngShow: "!(inventory_source.summary_fields.inventory_source.source === '') && inventory_source.summary_fields.user_capabilities.schedule"
}, },
edit: { edit: {
//label: 'Edit', //label: 'Edit',

View File

@@ -17,6 +17,7 @@ export default ['i18n', function(i18n) {
'<em>Select</em> button, located bottom right.</p><p>Create a new project by clicking the <i class=\"fa fa-plus\"></i> button.</p>', '<em>Select</em> button, located bottom right.</p><p>Create a new project by clicking the <i class=\"fa fa-plus\"></i> button.</p>',
index: false, index: false,
hover: true, hover: true,
emptyListText: i18n._('No Projects Have Been Created'),
fields: { fields: {
status: { status: {

View File

@@ -620,7 +620,15 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
multiple = (params.multiple !== undefined) ? params.multiple : true, multiple = (params.multiple !== undefined) ? params.multiple : true,
placeholder = params.placeholder, placeholder = params.placeholder,
customDropdownAdapter = (params.customDropdownAdapter !== undefined) ? params.customDropdownAdapter : true, customDropdownAdapter = (params.customDropdownAdapter !== undefined) ? params.customDropdownAdapter : true,
addNew = params.addNew; addNew = params.addNew,
scope = params.scope,
selectOptions = params.options,
model = params.model,
original_options;
if (scope && selectOptions) {
original_options = _.cloneDeep(scope[selectOptions]);
}
$.fn.select2.amd.require([ $.fn.select2.amd.require([
'select2/utils', 'select2/utils',
@@ -658,6 +666,10 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
if (addNew) { if (addNew) {
config.tags = true; config.tags = true;
config.tokenSeparators = []; config.tokenSeparators = [];
if (!multiple) {
config.minimumResultsForSearch = 1;
}
} }
$(element).select2(config); $(element).select2(config);
@@ -680,6 +692,17 @@ angular.module('Utilities', ['RestServices', 'Utilities'])
}); });
} }
if (addNew && !multiple) {
$(element).on('select2:select', (e) => {
scope[model] = e.params.data.text;
scope[selectOptions] = _.cloneDeep(original_options);
if (scope[selectOptions].indexOf(e.params.data.text) === -1) {
scope[selectOptions].push(e.params.data.text);
}
$(element).select2(config);
});
}
if (options) { if (options) {
for (var d = 0; d < $(element + " option").length; d++) { for (var d = 0; d < $(element + " option").length; d++) {
var item = $(element + " option")[d]; var item = $(element + " option")[d];

View File

@@ -515,6 +515,10 @@ function(ConfigurationUtils, i18n, $rootScope) {
_doAutoPopulate(); _doAutoPopulate();
} }
}); });
if (attrs.watchbasepath === 'projectBasePath') {
_doAutoPopulate();
}
} }
function _doAutoPopulate() { function _doAutoPopulate() {
@@ -522,7 +526,11 @@ function(ConfigurationUtils, i18n, $rootScope) {
if (attrs.watchbasepath !== undefined && scope[attrs.watchbasepath] !== undefined) { if (attrs.watchbasepath !== undefined && scope[attrs.watchbasepath] !== undefined) {
basePath = scope[attrs.watchbasepath]; basePath = scope[attrs.watchbasepath];
query = '&role_level=use_role'; if (attrs.watchbasepath !== "projectBasePath") {
query = '&role_level=use_role';
} else {
query = '';
}
} }
else { else {
basePath = GetBasePath(elm.attr('data-basePath')) || elm.attr('data-basePath'); basePath = GetBasePath(elm.attr('data-basePath')) || elm.attr('data-basePath');

View File

@@ -132,6 +132,7 @@ angular.module('GeneratorHelpers', [systemStatus.name])
icon = "fa-trash-o"; icon = "fa-trash-o";
break; break;
case 'group_update': case 'group_update':
case 'source_update':
icon = 'fa-refresh'; icon = 'fa-refresh';
break; break;
case 'inventory_update': case 'inventory_update':