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 = "
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 = '
The selected project has a status of \"missing\". Please check the server and make sure ' +
+ ' the directory exists and file permissions are set correctly.
';
+ 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(){
- let kind = ($scope.source.value === "ec2") ? "aws" : $scope.source.value;
$state.go('.credential', {
credential_search: {
- kind: kind,
+ // TODO: get kind sorting for credential properly implemented
+ // kind: kind,
page_size: '5',
page: '1'
}
});
};
- $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,
- 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.lookupProject = function(){
+ $state.go('.project', {
+ project_search: {
+ page_size: '5',
+ page: '1'
+ }
});
};
+
+ $scope.projectBasePath = GetBasePath('projects');
+ $scope.credentialBasePath = GetBasePath('credentials') + '?credential_type__kind__in=cloud,network';
+
$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'){
$scope.credentialBasePath = GetBasePath('inventory_script');
}
- // equal to case 'ec2' || 'rax' || 'azure' || 'azure_rm' || 'vmware' || 'satellite6' || 'cloudforms' || 'openstack'
- else{
- $scope.credentialBasePath = (source === 'ec2') ? GetBasePath('credentials') + '?kind=aws' : GetBasePath('credentials') + (source === '' ? '' : '?kind=' + (source));
- }
- if (source === 'ec2' || source === 'custom' || source === 'vmware' || source === 'openstack') {
+
+ if (source === 'ec2' || source === 'custom' || source === 'vmware' || source === 'openstack' || source === 'scm') {
+ $scope.envParseType = 'yaml';
+
+ var varName;
+ if (source === 'scm') {
+ varName = 'custom_variables';
+ } else {
+ varName = source + '_variables';
+ }
+
ParseTypeChange({
scope: $scope,
- field_id: source + '_variables',
- variable: source + '_variables',
+ field_id: varName,
+ variable: varName,
parse_variable: 'envParseType'
});
}
+ if (source === 'scm') {
+ $scope.overwrite_vars = true;
+ $scope.inventory_source_form.inventory_file.$setPristine();
+ } else {
+ $scope.overwrite_vars = false;
+ }
+
// reset fields
$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
$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.source_regions = null;
$scope.credential = null;
@@ -107,6 +177,10 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition',
initRegionSelect();
});
+ $scope.$on('choicesReadyVerbosity', function() {
+ initVerbositySelect();
+ });
+
$scope.$on('sourceTypeOptionsReady', function() {
initSourceSelect();
});
@@ -121,6 +195,7 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition',
multiple: true
});
}
+
function initSourceSelect(){
CreateSelect2({
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(){
GetChoices({
scope: $scope,
@@ -174,11 +258,66 @@ export default ['$state', '$stateParams', '$scope', 'SourcesFormDefinition',
choice_name: 'ec2_group_by_choices',
callback: 'choicesReadyGroup'
});
+
+ GetChoices({
+ scope: $scope,
+ url: GetBasePath('inventory_sources'),
+ field: 'verbosity',
+ variable: 'verbosity_options',
+ callback: 'choicesReadyVerbosity'
+ });
+
GetSourceTypeOptions({
scope: $scope,
variable: 'source_type_options',
//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});
+ });
+ };
}
];
diff --git a/awx/ui/client/src/inventories/sources/edit/sources-edit.controller.js b/awx/ui/client/src/inventories/sources/edit/sources-edit.controller.js
index 98517f9e56..0c354958cb 100644
--- a/awx/ui/client/src/inventories/sources/edit/sources-edit.controller.js
+++ b/awx/ui/client/src/inventories/sources/edit/sources-edit.controller.js
@@ -5,13 +5,11 @@
*************************************************/
export default ['$state', '$stateParams', '$scope', 'ParseVariableString',
- 'rbacUiControlService', 'ToJSON', 'ParseTypeChange', 'GroupManageService',
- 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions',
- 'inventorySourceData', 'SourcesService', 'inventoryData',
+ 'rbacUiControlService', 'ToJSON', 'ParseTypeChange', 'GetChoices', 'GetBasePath', 'CreateSelect2', 'GetSourceTypeOptions',
+ 'inventorySourceData', 'SourcesService', 'inventoryData', 'Empty', 'Wait', 'Rest', 'Alert', 'ProcessErrors',
function($state, $stateParams, $scope, ParseVariableString,
- rbacUiControlService, ToJSON,ParseTypeChange, GroupManageService,
- GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions,
- inventorySourceData, SourcesService, inventoryData) {
+ rbacUiControlService, ToJSON,ParseTypeChange, GetChoices, GetBasePath, CreateSelect2, GetSourceTypeOptions,
+ inventorySourceData, SourcesService, inventoryData, Empty, Wait, Rest, Alert, ProcessErrors) {
init();
@@ -20,8 +18,19 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString',
.then(function(canAdd) {
$scope.canAdd = canAdd;
});
+
// 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) {
$scope.credential_name = inventorySourceData.summary_fields.credential.name;
}
@@ -41,6 +50,113 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString',
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 = "
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 = "
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 = '
The selected project has a status of \"missing\". Please check the server and make sure ' +
+ ' the directory exists and file permissions are set correctly.
';
+ 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() {
CreateSelect2({
element: '#inventory_source_source_regions',
@@ -89,45 +205,49 @@ export default ['$state', '$stateParams', '$scope', 'ParseVariableString',
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_path = $scope.inventory_file;
+ }
} else {
params.source = null;
}
- // switch (source) {
- // no inventory source set, just create a new group
- // '' is the value supplied for Manual source type
- // case null || '':
- 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;
- // }
+
+ SourcesService
+ .put(params)
+ .then(() => $state.go('.', null, { reload: true }));
};
$scope.sourceChange = function(source) {
$scope.source = source;
if (source.value === 'ec2' || source.value === 'custom' ||
- source.value === 'vmware' || source.value === 'openstack') {
- $scope[source.value + '_variables'] = $scope[source.value + '_variables'] === (null || undefined) ? '---' : $scope[source.value + '_variables'];
+ source.value === 'vmware' || source.value === 'openstack' ||
+ source.value === 'scm') {
+
+ var varName;
+ if (source === 'scm') {
+ varName = 'custom_variables';
+ } else {
+ varName = source + '_variables';
+ }
+
+ $scope[varName] = $scope[varName] === (null || undefined) ? '---' : $scope[varName];
ParseTypeChange({
scope: $scope,
- field_id: source.value + '_variables',
- variable: source.value + '_variables',
+ field_id: varName,
+ variable: varName,
parse_variable: 'envParseType',
});
}
// reset fields
// 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.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.source_regions = null;
$scope.credential = null;
$scope.credential_name = null;
+
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
$scope.$on('choicesReadyGroup', function() {
if (angular.isObject($scope.source)) {
diff --git a/awx/ui/client/src/inventories/sources/factories/get-source-type-options.factory.js b/awx/ui/client/src/inventories/sources/factories/get-source-type-options.factory.js
index befef8a499..659c1c112a 100644
--- a/awx/ui/client/src/inventories/sources/factories/get-source-type-options.factory.js
+++ b/awx/ui/client/src/inventories/sources/factories/get-source-type-options.factory.js
@@ -11,7 +11,7 @@ export default
.success(function (data) {
var i, choices = data.actions.GET.source.choices;
for (i = 0; i < choices.length; i++) {
- if (choices[i][0] !== 'file') {
+ if (choices[i][0] !== 'file' && choices[i][0] !== "") {
scope[variable].push({
label: choices[i][1],
value: choices[i][0]
diff --git a/awx/ui/client/src/inventories/sources/list/sources-list.controller.js b/awx/ui/client/src/inventories/sources/list/sources-list.controller.js
index 13a64a8305..0d9cc7c706 100644
--- a/awx/ui/client/src/inventories/sources/list/sources-list.controller.js
+++ b/awx/ui/client/src/inventories/sources/list/sources-list.controller.js
@@ -5,17 +5,18 @@
*************************************************/
export default
['$scope', '$rootScope', '$state', '$stateParams', 'SourcesListDefinition',
- 'InventoryUpdate', 'GroupManageService', 'CancelSourceUpdate',
+ 'InventoryUpdate', 'CancelSourceUpdate',
'ViewUpdateStatus', 'rbacUiControlService', 'GetBasePath',
'GetSyncStatusMsg', 'Dataset', 'Find', 'QuerySet',
'inventoryData', '$filter', 'Prompt', 'Wait', 'SourcesService', 'inventorySourceOptions',
function($scope, $rootScope, $state, $stateParams, SourcesListDefinition,
- InventoryUpdate, GroupManageService, CancelSourceUpdate,
+ InventoryUpdate, CancelSourceUpdate,
ViewUpdateStatus, rbacUiControlService, GetBasePath, GetSyncStatusMsg,
Dataset, Find, qs, inventoryData, $filter, Prompt,
Wait, SourcesService, inventorySourceOptions){
let list = SourcesListDefinition;
+ var inventory_source;
init();
@@ -24,7 +25,7 @@
$scope.canAdhoc = inventoryData.summary_fields.user_capabilities.adhoc;
$scope.canAdd = false;
- rbacUiControlService.canAdd(GetBasePath('inventory') + $scope.inventory_id + "/groups")
+ rbacUiControlService.canAdd(GetBasePath('inventory') + $scope.inventory_id + "/inventory_sources")
.then(function(canAdd) {
$scope.canAdd = canAdd;
});
@@ -39,7 +40,7 @@
optionsRequestDataProcessing();
$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) {
inventory_source = {};
@@ -125,11 +126,11 @@
Wait('start');
SourcesService.delete(inventory_source.id).then(() => {
$('#prompt-modal').modal('hide');
- // if (parseInt($state.params.source_id) === id) {
- // $state.go("sources", null, {reload: true});
- // } else {
+ if (parseInt($state.params.source_id) === inventory_source) {
+ $state.go("sources", null, {reload: true});
+ } else {
$state.go($state.current.name, null, {reload: true});
- // }
+ }
Wait('stop');
});
};
@@ -160,7 +161,7 @@
});
};
$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
$state.go('inventories.edit.inventory_sources.edit.schedules', {inventory_source_id: id}, {reload: true});
};
diff --git a/awx/ui/client/src/inventories/sources/list/sources-list.partial.html b/awx/ui/client/src/inventories/sources/list/sources-list.partial.html
index 1a02f3a515..03cc6ab1fd 100644
--- a/awx/ui/client/src/inventories/sources/list/sources-list.partial.html
+++ b/awx/ui/client/src/inventories/sources/list/sources-list.partial.html
@@ -5,7 +5,7 @@