mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 02:19:58 -03:30
Merge pull request #1303 from kensible/1044-inventory-manageModal
1044 inventory manage modalectomy
This commit is contained in:
commit
359da2d958
@ -26,6 +26,7 @@ import {CredentialsAdd, CredentialsEdit, CredentialsList} from './controllers/Cr
|
||||
import {JobsListController} from './controllers/Jobs';
|
||||
import {PortalController} from './controllers/Portal';
|
||||
import systemTracking from './system-tracking/main';
|
||||
import inventories from './inventories/main';
|
||||
import inventoryScripts from './inventory-scripts/main';
|
||||
import organizations from './organizations/main';
|
||||
import permissions from './permissions/main';
|
||||
@ -54,7 +55,7 @@ import {ProjectsList, ProjectsAdd, ProjectsEdit} from './controllers/Projects';
|
||||
import OrganizationsList from './organizations/list/organizations-list.controller';
|
||||
import OrganizationsAdd from './organizations/add/organizations-add.controller';
|
||||
import OrganizationsEdit from './organizations/edit/organizations-edit.controller';
|
||||
import {InventoriesList, InventoriesAdd, InventoriesEdit, InventoriesManage} from './controllers/Inventories';
|
||||
import {InventoriesAdd, InventoriesEdit, InventoriesList, InventoriesManage} from './inventories/main';
|
||||
import {AdminsList} from './controllers/Admins';
|
||||
import {UsersList, UsersAdd, UsersEdit} from './controllers/Users';
|
||||
import {TeamsList, TeamsAdd, TeamsEdit} from './controllers/Teams';
|
||||
@ -87,6 +88,7 @@ var tower = angular.module('Tower', [
|
||||
RestServices.name,
|
||||
browserData.name,
|
||||
systemTracking.name,
|
||||
inventories.name,
|
||||
inventoryScripts.name,
|
||||
organizations.name,
|
||||
permissions.name,
|
||||
@ -369,69 +371,6 @@ var tower = angular.module('Tower', [
|
||||
}
|
||||
}).
|
||||
|
||||
state('inventories', {
|
||||
url: '/inventories',
|
||||
templateUrl: urlPrefix + 'partials/inventories.html',
|
||||
controller: InventoriesList,
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'inventory'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORIES"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
}).
|
||||
|
||||
state('inventories.add', {
|
||||
url: '/add',
|
||||
templateUrl: urlPrefix + 'partials/inventories.html',
|
||||
controller: InventoriesAdd,
|
||||
ncyBreadcrumb: {
|
||||
parent: "inventories",
|
||||
label: "CREATE INVENTORY"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
}).
|
||||
|
||||
state('inventories.edit', {
|
||||
url: '/:inventory_id',
|
||||
templateUrl: urlPrefix + 'partials/inventories.html',
|
||||
controller: InventoriesEdit,
|
||||
data: {
|
||||
activityStreamId: 'inventory_id'
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
}).
|
||||
|
||||
state('inventoryManage', {
|
||||
url: '/inventories/:inventory_id/manage?groups',
|
||||
templateUrl: urlPrefix + 'partials/inventory-manage.html',
|
||||
controller: InventoriesManage,
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'inventory',
|
||||
activityStreamId: 'inventory_id'
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
}).
|
||||
|
||||
state('organizationAdmins', {
|
||||
url: '/organizations/:organization_id/admins',
|
||||
templateUrl: urlPrefix + 'partials/organizations.html',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -437,10 +437,10 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', listGenerator.name,
|
||||
|
||||
.factory('HostsEdit', ['$rootScope', '$location', '$log', '$stateParams', 'Rest', 'Alert', 'HostForm', 'GenerateForm',
|
||||
'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange', 'Wait', 'Find', 'SetStatus', 'ApplyEllipsis',
|
||||
'ToJSON', 'ParseVariableString', 'CreateDialog', 'TextareaResize',
|
||||
'ToJSON', 'ParseVariableString', 'CreateDialog', 'TextareaResize', 'ParamPass',
|
||||
function($rootScope, $location, $log, $stateParams, Rest, Alert, HostForm, GenerateForm, Prompt, ProcessErrors,
|
||||
GetBasePath, HostsReload, ParseTypeChange, Wait, Find, SetStatus, ApplyEllipsis, ToJSON,
|
||||
ParseVariableString, CreateDialog, TextareaResize) {
|
||||
ParseVariableString, CreateDialog, TextareaResize, ParamPass) {
|
||||
return function(params) {
|
||||
|
||||
var parent_scope = params.host_scope,
|
||||
|
||||
@ -0,0 +1,95 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Inventories
|
||||
* @description This controller's for the Inventory page
|
||||
*/
|
||||
|
||||
function InventoriesAdd($scope, $rootScope, $compile, $location, $log,
|
||||
$stateParams, InventoryForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
ReturnToCaller, ClearScope, generateList, OrganizationList, SearchInit,
|
||||
PaginateInit, LookUpInit, GetBasePath, ParseTypeChange, Wait, ToJSON,
|
||||
$state) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
// Inject dynamic view
|
||||
var defaultUrl = GetBasePath('inventory'),
|
||||
form = InventoryForm(),
|
||||
generator = GenerateForm;
|
||||
|
||||
form.formLabelSize = null;
|
||||
form.formFieldSize = null;
|
||||
|
||||
generator.inject(form, { mode: 'add', related: false, scope: $scope });
|
||||
|
||||
generator.reset();
|
||||
|
||||
$scope.parseType = 'yaml';
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
variable: 'variables',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'inventory_variables'
|
||||
});
|
||||
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: ($stateParams.organization_id) ? $stateParams.organization_id : null,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio'
|
||||
});
|
||||
|
||||
// Save
|
||||
$scope.formSave = function () {
|
||||
generator.clearApiErrors();
|
||||
Wait('start');
|
||||
try {
|
||||
var fld, json_data, data;
|
||||
|
||||
json_data = ToJSON($scope.parseType, $scope.variables, true);
|
||||
|
||||
data = {};
|
||||
for (fld in form.fields) {
|
||||
if (form.fields[fld].realName) {
|
||||
data[form.fields[fld].realName] = $scope[fld];
|
||||
} else {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
}
|
||||
|
||||
Rest.setUrl(defaultUrl);
|
||||
Rest.post(data)
|
||||
.success(function (data) {
|
||||
var inventory_id = data.id;
|
||||
Wait('stop');
|
||||
$location.path('/inventories/' + inventory_id + '/manage');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors( $scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to add new inventory. Post returned status: ' + status });
|
||||
});
|
||||
} catch (err) {
|
||||
Wait('stop');
|
||||
Alert("Error", "Error parsing inventory variables. Parser returned: " + err);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$state.transitionTo('inventories');
|
||||
};
|
||||
}
|
||||
|
||||
export default['$scope', '$rootScope', '$compile', '$location',
|
||||
'$log', '$stateParams', 'InventoryForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'generateList',
|
||||
'OrganizationList', 'SearchInit', 'PaginateInit', 'LookUpInit',
|
||||
'GetBasePath', 'ParseTypeChange', 'Wait', 'ToJSON', '$state', InventoriesAdd]
|
||||
24
awx/ui/client/src/inventories/add/inventory-add.route.js
Normal file
24
awx/ui/client/src/inventories/add/inventory-add.route.js
Normal file
@ -0,0 +1,24 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
import InventoriesAdd from './inventory-add.controller';
|
||||
|
||||
export default {
|
||||
name: 'inventories.add',
|
||||
route: '/add',
|
||||
templateUrl: templateUrl('inventories/inventories'),
|
||||
controller: InventoriesAdd,
|
||||
ncyBreadcrumb: {
|
||||
parent: "inventories",
|
||||
label: "CREATE INVENTORY"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
};
|
||||
14
awx/ui/client/src/inventories/add/main.js
Normal file
14
awx/ui/client/src/inventories/add/main.js
Normal file
@ -0,0 +1,14 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './inventory-add.route';
|
||||
import controller from './inventory-add.controller';
|
||||
|
||||
export default
|
||||
angular.module('inventoryAdd', [])
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
329
awx/ui/client/src/inventories/edit/inventory-edit.controller.js
Normal file
329
awx/ui/client/src/inventories/edit/inventory-edit.controller.js
Normal file
@ -0,0 +1,329 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Inventories
|
||||
* @description This controller's for the Inventory page
|
||||
*/
|
||||
|
||||
function InventoriesEdit($scope, $rootScope, $compile, $location,
|
||||
$log, $stateParams, InventoryForm, GenerateForm, Rest, Alert, ProcessErrors,
|
||||
ReturnToCaller, ClearScope, generateList, OrganizationList, SearchInit,
|
||||
PaginateInit, LookUpInit, GetBasePath, ParseTypeChange, Wait, ToJSON,
|
||||
ParseVariableString, RelatedSearchInit, RelatedPaginateInit,
|
||||
Prompt, PlaybookRun, CreateDialog, deleteJobTemplate, $state) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
// Inject dynamic view
|
||||
var defaultUrl = GetBasePath('inventory'),
|
||||
form = InventoryForm(),
|
||||
generator = GenerateForm,
|
||||
inventory_id = $stateParams.inventory_id,
|
||||
master = {},
|
||||
fld, json_data, data,
|
||||
relatedSets = {};
|
||||
|
||||
form.formLabelSize = null;
|
||||
form.formFieldSize = null;
|
||||
$scope.inventory_id = inventory_id;
|
||||
generator.inject(form, { mode: 'edit', related: true, scope: $scope });
|
||||
|
||||
generator.reset();
|
||||
|
||||
|
||||
// After the project is loaded, retrieve each related set
|
||||
if ($scope.inventoryLoadedRemove) {
|
||||
$scope.inventoryLoadedRemove();
|
||||
}
|
||||
$scope.projectLoadedRemove = $scope.$on('inventoryLoaded', function () {
|
||||
var set;
|
||||
for (set in relatedSets) {
|
||||
$scope.search(relatedSets[set].iterator);
|
||||
}
|
||||
});
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
relatedSets = form.relatedSets(data.related);
|
||||
|
||||
// Initialize related search functions. Doing it here to make sure relatedSets object is populated.
|
||||
RelatedSearchInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
RelatedPaginateInit({
|
||||
scope: $scope,
|
||||
relatedSets: relatedSets
|
||||
});
|
||||
|
||||
Wait('stop');
|
||||
$scope.parseType = 'yaml';
|
||||
ParseTypeChange({
|
||||
scope: $scope,
|
||||
variable: 'variables',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'inventory_variables'
|
||||
});
|
||||
LookUpInit({
|
||||
scope: $scope,
|
||||
form: form,
|
||||
current_item: $scope.organization,
|
||||
list: OrganizationList,
|
||||
field: 'organization',
|
||||
input_type: 'radio'
|
||||
});
|
||||
$scope.$emit('inventoryLoaded');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Failed to get inventory: ' + inventory_id + '. GET returned: ' + status });
|
||||
});
|
||||
// Save
|
||||
$scope.formSave = function () {
|
||||
Wait('start');
|
||||
|
||||
// Make sure we have valid variable data
|
||||
json_data = ToJSON($scope.parseType, $scope.variables);
|
||||
|
||||
data = {};
|
||||
for (fld in form.fields) {
|
||||
if (form.fields[fld].realName) {
|
||||
data[form.fields[fld].realName] = $scope[fld];
|
||||
} else {
|
||||
data[fld] = $scope[fld];
|
||||
}
|
||||
}
|
||||
|
||||
Rest.setUrl(defaultUrl + inventory_id + '/');
|
||||
Rest.put(data)
|
||||
.success(function () {
|
||||
Wait('stop');
|
||||
$location.path('/inventories/');
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
|
||||
msg: 'Failed to update inventory. PUT returned status: ' + status });
|
||||
});
|
||||
};
|
||||
|
||||
$scope.manageInventory = function(){
|
||||
$location.path($location.path() + '/manage');
|
||||
};
|
||||
|
||||
$scope.formCancel = function () {
|
||||
$state.transitionTo('inventories');
|
||||
};
|
||||
|
||||
$scope.addScanJob = function(){
|
||||
$location.path($location.path()+'/job_templates/add');
|
||||
};
|
||||
|
||||
$scope.launchScanJob = function(){
|
||||
PlaybookRun({ scope: $scope, id: this.scan_job_template.id });
|
||||
};
|
||||
|
||||
$scope.scheduleScanJob = function(){
|
||||
$location.path('/job_templates/'+this.scan_job_template.id+'/schedules');
|
||||
};
|
||||
|
||||
$scope.editScanJob = function(){
|
||||
$location.path($location.path()+'/job_templates/'+this.scan_job_template.id);
|
||||
};
|
||||
|
||||
$scope.copyScanJobTemplate = function(){
|
||||
var id = this.scan_job_template.id,
|
||||
name = this.scan_job_template.name,
|
||||
element,
|
||||
buttons = [{
|
||||
"label": "Cancel",
|
||||
"onClick": function() {
|
||||
$(this).dialog('close');
|
||||
},
|
||||
"icon": "fa-times",
|
||||
"class": "btn btn-default",
|
||||
"id": "copy-close-button"
|
||||
},{
|
||||
"label": "Copy",
|
||||
"onClick": function() {
|
||||
copyAction();
|
||||
},
|
||||
"icon": "fa-copy",
|
||||
"class": "btn btn-primary",
|
||||
"id": "job-copy-button"
|
||||
}],
|
||||
copyAction = function () {
|
||||
// retrieve the copy of the job template object from the api, then overwrite the name and throw away the id
|
||||
Wait('start');
|
||||
var url = GetBasePath('job_templates')+id;
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
data.name = $scope.new_copy_name;
|
||||
delete data.id;
|
||||
$scope.$emit('GoToCopy', data);
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
CreateDialog({
|
||||
id: 'copy-job-modal' ,
|
||||
title: "Copy",
|
||||
scope: $scope,
|
||||
buttons: buttons,
|
||||
width: 500,
|
||||
height: 300,
|
||||
minWidth: 200,
|
||||
callback: 'CopyDialogReady'
|
||||
});
|
||||
|
||||
$('#job_name').text(name);
|
||||
$('#copy-job-modal').show();
|
||||
|
||||
|
||||
if ($scope.removeCopyDialogReady) {
|
||||
$scope.removeCopyDialogReady();
|
||||
}
|
||||
$scope.removeCopyDialogReady = $scope.$on('CopyDialogReady', function() {
|
||||
//clear any old remaining text
|
||||
$scope.new_copy_name = "" ;
|
||||
$scope.copy_form.$setPristine();
|
||||
$('#copy-job-modal').dialog('open');
|
||||
$('#job-copy-button').attr('ng-disabled', "!copy_form.$valid");
|
||||
element = angular.element(document.getElementById('job-copy-button'));
|
||||
$compile(element)($scope);
|
||||
|
||||
});
|
||||
|
||||
if ($scope.removeGoToCopy) {
|
||||
$scope.removeGoToCopy();
|
||||
}
|
||||
$scope.removeGoToCopy = $scope.$on('GoToCopy', function(e, data) {
|
||||
var url = GetBasePath('job_templates'),
|
||||
old_survey_url = (data.related.survey_spec) ? data.related.survey_spec : "" ;
|
||||
Rest.setUrl(url);
|
||||
Rest.post(data)
|
||||
.success(function (data) {
|
||||
if(data.survey_enabled===true){
|
||||
$scope.$emit("CopySurvey", data, old_survey_url);
|
||||
}
|
||||
else {
|
||||
$('#copy-job-modal').dialog('close');
|
||||
Wait('stop');
|
||||
$location.path($location.path() + '/job_templates/' + data.id);
|
||||
}
|
||||
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
});
|
||||
|
||||
if ($scope.removeCopySurvey) {
|
||||
$scope.removeCopySurvey();
|
||||
}
|
||||
$scope.removeCopySurvey = $scope.$on('CopySurvey', function(e, new_data, old_url) {
|
||||
// var url = data.related.survey_spec;
|
||||
Rest.setUrl(old_url);
|
||||
Rest.get()
|
||||
.success(function (survey_data) {
|
||||
|
||||
Rest.setUrl(new_data.related.survey_spec);
|
||||
Rest.post(survey_data)
|
||||
.success(function () {
|
||||
$('#copy-job-modal').dialog('close');
|
||||
Wait('stop');
|
||||
$location.path($location.path() + '/job_templates/' + new_data.id);
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + new_data.related.survey_spec + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + old_url + ' failed. DELETE returned status: ' + status });
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
$scope.deleteScanJob = function () {
|
||||
var id = this.scan_job_template.id ,
|
||||
action = function () {
|
||||
$('#prompt-modal').modal('hide');
|
||||
Wait('start');
|
||||
deleteJobTemplate(id)
|
||||
.success(function () {
|
||||
$('#prompt-modal').modal('hide');
|
||||
$scope.search(form.related.scan_job_templates.iterator);
|
||||
})
|
||||
.error(function (data) {
|
||||
Wait('stop');
|
||||
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'DELETE returned status: ' + status });
|
||||
});
|
||||
};
|
||||
|
||||
Prompt({
|
||||
hdr: 'Delete',
|
||||
body: '<div class="Prompt-bodyQuery">Are you sure you want to delete the job template below?</div><div class="Prompt-bodyTarget">' + this.scan_job_template.name + '</div>',
|
||||
action: action,
|
||||
actionText: 'DELETE'
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export default ['$scope', '$rootScope', '$compile', '$location',
|
||||
'$log', '$stateParams', 'InventoryForm', 'GenerateForm', 'Rest', 'Alert',
|
||||
'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'generateList',
|
||||
'OrganizationList', 'SearchInit', 'PaginateInit', 'LookUpInit',
|
||||
'GetBasePath', 'ParseTypeChange', 'Wait', 'ToJSON', 'ParseVariableString',
|
||||
'RelatedSearchInit', 'RelatedPaginateInit', 'Prompt',
|
||||
'PlaybookRun', 'CreateDialog', 'deleteJobTemplate', '$state',
|
||||
InventoriesEdit,
|
||||
];
|
||||
26
awx/ui/client/src/inventories/edit/inventory-edit.route.js
Normal file
26
awx/ui/client/src/inventories/edit/inventory-edit.route.js
Normal file
@ -0,0 +1,26 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
import InventoriesEdit from './inventory-edit.controller';
|
||||
|
||||
export default {
|
||||
name: 'inventories.edit',
|
||||
route: '/:inventory_id',
|
||||
templateUrl: templateUrl('inventories/inventories'),
|
||||
controller: InventoriesEdit,
|
||||
data: {
|
||||
activityStreamId: 'inventory_id'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORY EDIT"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
};
|
||||
14
awx/ui/client/src/inventories/edit/main.js
Normal file
14
awx/ui/client/src/inventories/edit/main.js
Normal file
@ -0,0 +1,14 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './inventory-edit.route';
|
||||
import controller from './inventory-edit.controller';
|
||||
|
||||
export default
|
||||
angular.module('inventoryEdit', [])
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
364
awx/ui/client/src/inventories/list/inventory-list.controller.js
Normal file
364
awx/ui/client/src/inventories/list/inventory-list.controller.js
Normal file
@ -0,0 +1,364 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name controllers.function:Inventories
|
||||
* @description This controller's for the Inventory page
|
||||
*/
|
||||
|
||||
function InventoriesList($scope, $rootScope, $location, $log,
|
||||
$stateParams, $compile, $filter, sanitizeFilter, Rest, Alert, InventoryList,
|
||||
generateList, Prompt, SearchInit, PaginateInit, ReturnToCaller,
|
||||
ClearScope, ProcessErrors, GetBasePath, Wait,
|
||||
Find, Empty, $state) {
|
||||
|
||||
var list = InventoryList,
|
||||
defaultUrl = GetBasePath('inventory'),
|
||||
view = generateList,
|
||||
paths = $location.path().replace(/^\//, '').split('/'),
|
||||
mode = (paths[0] === 'inventories') ? 'edit' : 'select';
|
||||
|
||||
function ellipsis(a) {
|
||||
if (a.length > 20) {
|
||||
return a.substr(0,20) + '...';
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function attachElem(event, html, title) {
|
||||
var elem = $(event.target).parent();
|
||||
try {
|
||||
elem.tooltip('hide');
|
||||
elem.popover('destroy');
|
||||
}
|
||||
catch(err) {
|
||||
//ignore
|
||||
}
|
||||
$('.popover').each(function() {
|
||||
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
|
||||
$(this).remove();
|
||||
});
|
||||
$('.tooltip').each( function() {
|
||||
// close any lingering tool tipss
|
||||
$(this).hide();
|
||||
});
|
||||
elem.attr({
|
||||
"aw-pop-over": html,
|
||||
"data-popover-title": title,
|
||||
"data-placement": "right" });
|
||||
$compile(elem)($scope);
|
||||
elem.on('shown.bs.popover', function() {
|
||||
$('.popover').each(function() {
|
||||
$compile($(this))($scope); //make nested directives work!
|
||||
});
|
||||
$('.popover-content, .popover-title').click(function() {
|
||||
elem.popover('hide');
|
||||
});
|
||||
});
|
||||
elem.popover('show');
|
||||
}
|
||||
|
||||
view.inject(InventoryList, { mode: mode, scope: $scope });
|
||||
$rootScope.flashMessage = null;
|
||||
|
||||
SearchInit({
|
||||
scope: $scope,
|
||||
set: 'inventories',
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
|
||||
PaginateInit({
|
||||
scope: $scope,
|
||||
list: list,
|
||||
url: defaultUrl
|
||||
});
|
||||
|
||||
if ($stateParams.name) {
|
||||
$scope[InventoryList.iterator + 'InputDisable'] = false;
|
||||
$scope[InventoryList.iterator + 'SearchValue'] = $stateParams.name;
|
||||
$scope[InventoryList.iterator + 'SearchField'] = 'name';
|
||||
$scope[InventoryList.iterator + 'SearchFieldLabel'] = InventoryList.fields.name.label;
|
||||
$scope[InventoryList.iterator + 'SearchSelectValue'] = null;
|
||||
}
|
||||
|
||||
if ($stateParams.has_active_failures) {
|
||||
$scope[InventoryList.iterator + 'InputDisable'] = true;
|
||||
$scope[InventoryList.iterator + 'SearchValue'] = $stateParams.has_active_failures;
|
||||
$scope[InventoryList.iterator + 'SearchField'] = 'has_active_failures';
|
||||
$scope[InventoryList.iterator + 'SearchFieldLabel'] = InventoryList.fields.has_active_failures.label;
|
||||
$scope[InventoryList.iterator + 'SearchSelectValue'] = ($stateParams.has_active_failures === 'true') ? {
|
||||
value: 1
|
||||
} : {
|
||||
value: 0
|
||||
};
|
||||
}
|
||||
|
||||
if ($stateParams.has_inventory_sources) {
|
||||
$scope[InventoryList.iterator + 'InputDisable'] = true;
|
||||
$scope[InventoryList.iterator + 'SearchValue'] = $stateParams.has_inventory_sources;
|
||||
$scope[InventoryList.iterator + 'SearchField'] = 'has_inventory_sources';
|
||||
$scope[InventoryList.iterator + 'SearchFieldLabel'] = InventoryList.fields.has_inventory_sources.label;
|
||||
$scope[InventoryList.iterator + 'SearchSelectValue'] = ($stateParams.has_inventory_sources === 'true') ? {
|
||||
value: 1
|
||||
} : {
|
||||
value: 0
|
||||
};
|
||||
}
|
||||
|
||||
if ($stateParams.inventory_sources_with_failures) {
|
||||
// pass a value of true, however this field actually contains an integer value
|
||||
$scope[InventoryList.iterator + 'InputDisable'] = true;
|
||||
$scope[InventoryList.iterator + 'SearchValue'] = $stateParams.inventory_sources_with_failures;
|
||||
$scope[InventoryList.iterator + 'SearchField'] = 'inventory_sources_with_failures';
|
||||
$scope[InventoryList.iterator + 'SearchFieldLabel'] = InventoryList.fields.inventory_sources_with_failures.label;
|
||||
$scope[InventoryList.iterator + 'SearchType'] = 'gtzero';
|
||||
}
|
||||
|
||||
$scope.search(list.iterator);
|
||||
|
||||
if ($scope.removePostRefresh) {
|
||||
$scope.removePostRefresh();
|
||||
}
|
||||
$scope.removePostRefresh = $scope.$on('PostRefresh', function () {
|
||||
//If we got here by deleting an inventory, stop the spinner and cleanup events
|
||||
Wait('stop');
|
||||
try {
|
||||
$('#prompt-modal').modal('hide');
|
||||
}
|
||||
catch(e) {
|
||||
// ignore
|
||||
}
|
||||
$scope.inventories.forEach(function(inventory, idx) {
|
||||
$scope.inventories[idx].launch_class = "";
|
||||
if (inventory.has_inventory_sources) {
|
||||
if (inventory.inventory_sources_with_failures > 0) {
|
||||
$scope.inventories[idx].syncStatus = 'error';
|
||||
$scope.inventories[idx].syncTip = inventory.inventory_sources_with_failures + ' groups with sync failures. Click for details';
|
||||
}
|
||||
else {
|
||||
$scope.inventories[idx].syncStatus = 'successful';
|
||||
$scope.inventories[idx].syncTip = 'No inventory sync failures. Click for details.';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$scope.inventories[idx].syncStatus = 'na';
|
||||
$scope.inventories[idx].syncTip = 'Not configured for inventory sync.';
|
||||
$scope.inventories[idx].launch_class = "btn-disabled";
|
||||
}
|
||||
if (inventory.has_active_failures) {
|
||||
$scope.inventories[idx].hostsStatus = 'error';
|
||||
$scope.inventories[idx].hostsTip = inventory.hosts_with_active_failures + ' hosts with failures. Click for details.';
|
||||
}
|
||||
else if (inventory.total_hosts) {
|
||||
$scope.inventories[idx].hostsStatus = 'successful';
|
||||
$scope.inventories[idx].hostsTip = 'No hosts with failures. Click for details.';
|
||||
}
|
||||
else {
|
||||
$scope.inventories[idx].hostsStatus = 'none';
|
||||
$scope.inventories[idx].hostsTip = 'Inventory contains 0 hosts.';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if ($scope.removeRefreshInventories) {
|
||||
$scope.removeRefreshInventories();
|
||||
}
|
||||
$scope.removeRefreshInventories = $scope.$on('RefreshInventories', function () {
|
||||
// Reflect changes after inventory properties edit completes
|
||||
$scope.search(list.iterator);
|
||||
});
|
||||
|
||||
if ($scope.removeHostSummaryReady) {
|
||||
$scope.removeHostSummaryReady();
|
||||
}
|
||||
$scope.removeHostSummaryReady = $scope.$on('HostSummaryReady', function(e, event, data) {
|
||||
|
||||
var html, title = "Recent Jobs";
|
||||
Wait('stop');
|
||||
if (data.count > 0) {
|
||||
html = "<table class=\"table table-condensed flyout\" style=\"width: 100%\">\n";
|
||||
html += "<thead>\n";
|
||||
html += "<tr>";
|
||||
html += "<th>Status</th>";
|
||||
html += "<th>Finished</th>";
|
||||
html += "<th>Name</th>";
|
||||
html += "</tr>\n";
|
||||
html += "</thead>\n";
|
||||
html += "<tbody>\n";
|
||||
|
||||
data.results.forEach(function(row) {
|
||||
html += "<tr>\n";
|
||||
html += "<td><a href=\"#/jobs/" + row.id + "\" " + "aw-tool-tip=\"" + row.status.charAt(0).toUpperCase() + row.status.slice(1) +
|
||||
". Click for details\" aw-tip-placement=\"top\"><i class=\"fa icon-job-" + row.status + "\"></i></a></td>\n";
|
||||
html += "<td>" + ($filter('longDate')(row.finished)).replace(/ /,'<br />') + "</td>";
|
||||
html += "<td><a href=\"#/jobs/" + row.id + "\" " + "aw-tool-tip=\"" + row.status.charAt(0).toUpperCase() + row.status.slice(1) +
|
||||
". Click for details\" aw-tip-placement=\"top\">" + ellipsis(row.name) + "</a></td>";
|
||||
html += "</tr>\n";
|
||||
});
|
||||
html += "</tbody>\n";
|
||||
html += "</table>\n";
|
||||
}
|
||||
else {
|
||||
html = "<p>No recent job data available for this inventory.</p>\n";
|
||||
}
|
||||
attachElem(event, html, title);
|
||||
});
|
||||
|
||||
if ($scope.removeGroupSummaryReady) {
|
||||
$scope.removeGroupSummaryReady();
|
||||
}
|
||||
$scope.removeGroupSummaryReady = $scope.$on('GroupSummaryReady', function(e, event, inventory, data) {
|
||||
var html, title;
|
||||
|
||||
Wait('stop');
|
||||
|
||||
// Build the html for our popover
|
||||
html = "<table class=\"table table-condensed flyout\" style=\"width: 100%\">\n";
|
||||
html += "<thead>\n";
|
||||
html += "<tr>";
|
||||
html += "<th>Status</th>";
|
||||
html += "<th>Last Sync</th>";
|
||||
html += "<th>Group</th>";
|
||||
html += "</tr>";
|
||||
html += "</thead>\n";
|
||||
html += "<tbody>\n";
|
||||
data.results.forEach( function(row) {
|
||||
if (row.related.last_update) {
|
||||
html += "<tr>";
|
||||
html += "<td><a href=\"\" ng-click=\"viewJob('" + row.related.last_update + "')\" aw-tool-tip=\"" + row.status.charAt(0).toUpperCase() + row.status.slice(1) + ". Click for details\" aw-tip-placement=\"top\"><i class=\"fa icon-job-" + row.status + "\"></i></a></td>";
|
||||
html += "<td>" + ($filter('longDate')(row.last_updated)).replace(/ /,'<br />') + "</td>";
|
||||
html += "<td><a href=\"\" ng-click=\"viewJob('" + row.related.last_update + "')\">" + ellipsis(row.summary_fields.group.name) + "</a></td>";
|
||||
html += "</tr>\n";
|
||||
}
|
||||
else {
|
||||
html += "<tr>";
|
||||
html += "<td><a href=\"\" aw-tool-tip=\"No sync data\" aw-tip-placement=\"top\"><i class=\"fa icon-job-none\"></i></a></td>";
|
||||
html += "<td>NA</td>";
|
||||
html += "<td><a href=\"\">" + ellipsis(row.summary_fields.group.name) + "</a></td>";
|
||||
html += "</tr>\n";
|
||||
}
|
||||
});
|
||||
html += "</tbody>\n";
|
||||
html += "</table>\n";
|
||||
title = "Sync Status";
|
||||
attachElem(event, html, title);
|
||||
});
|
||||
|
||||
$scope.showGroupSummary = function(event, id) {
|
||||
var inventory;
|
||||
if (!Empty(id)) {
|
||||
inventory = Find({ list: $scope.inventories, key: 'id', val: id });
|
||||
if (inventory.syncStatus !== 'na') {
|
||||
Wait('start');
|
||||
Rest.setUrl(inventory.related.inventory_sources + '?or__source=ec2&or__source=rax&order_by=-last_job_run&page_size=5');
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
$scope.$emit('GroupSummaryReady', event, inventory, data);
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors( $scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + inventory.related.inventory_sources + ' failed. GET returned status: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.showHostSummary = function(event, id) {
|
||||
var url, inventory;
|
||||
if (!Empty(id)) {
|
||||
inventory = Find({ list: $scope.inventories, key: 'id', val: id });
|
||||
if (inventory.total_hosts > 0) {
|
||||
Wait('start');
|
||||
url = GetBasePath('jobs') + "?type=job&inventory=" + id + "&failed=";
|
||||
url += (inventory.has_active_failures) ? 'true' : "false";
|
||||
url += "&order_by=-finished&page_size=5";
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success( function(data) {
|
||||
$scope.$emit('HostSummaryReady', event, data);
|
||||
})
|
||||
.error( function(data, status) {
|
||||
ProcessErrors( $scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. GET returned: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.viewJob = function(url) {
|
||||
|
||||
// Pull the id out of the URL
|
||||
var id = url.replace(/^\//, '').split('/')[3];
|
||||
|
||||
$state.go('inventorySyncStdout', {id: id});
|
||||
|
||||
};
|
||||
|
||||
$scope.addInventory = function () {
|
||||
$state.go('inventories.add');
|
||||
};
|
||||
|
||||
$scope.editInventory = function (id) {
|
||||
$state.go('inventories.edit', {inventory_id: id});
|
||||
};
|
||||
|
||||
$scope.manageInventory = function(id){
|
||||
$location.path($location.path() + '/' + id + '/manage');
|
||||
};
|
||||
|
||||
$scope.deleteInventory = function (id, name) {
|
||||
|
||||
var action = function () {
|
||||
var url = defaultUrl + id + '/';
|
||||
Wait('start');
|
||||
$('#prompt-modal').modal('hide');
|
||||
Rest.setUrl(url);
|
||||
Rest.destroy()
|
||||
.success(function () {
|
||||
$scope.search(list.iterator);
|
||||
})
|
||||
.error(function (data, status) {
|
||||
ProcessErrors( $scope, data, status, null, { hdr: 'Error!',
|
||||
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Prompt({
|
||||
hdr: 'Delete',
|
||||
body: '<div class="Prompt-bodyQuery">Are you sure you want to delete the inventory below?</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(name) + '</div>',
|
||||
action: action,
|
||||
actionText: 'DELETE'
|
||||
});
|
||||
};
|
||||
|
||||
$scope.lookupOrganization = function (organization_id) {
|
||||
Rest.setUrl(GetBasePath('organizations') + organization_id + '/');
|
||||
Rest.get()
|
||||
.success(function (data) {
|
||||
return data.name;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Failed jobs link. Go to the jobs tabs, find all jobs for the inventory and sort by status
|
||||
$scope.viewJobs = function (id) {
|
||||
$location.url('/jobs/?inventory__int=' + id);
|
||||
};
|
||||
|
||||
$scope.viewFailedJobs = function (id) {
|
||||
$location.url('/jobs/?inventory__int=' + id + '&status=failed');
|
||||
};
|
||||
}
|
||||
|
||||
export default ['$scope', '$rootScope', '$location', '$log',
|
||||
'$stateParams', '$compile', '$filter', 'sanitizeFilter', 'Rest', 'Alert', 'InventoryList',
|
||||
'generateList', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller',
|
||||
'ClearScope', 'ProcessErrors', 'GetBasePath', 'Wait', 'Find', 'Empty', '$state', InventoriesList];
|
||||
27
awx/ui/client/src/inventories/list/inventory-list.route.js
Normal file
27
awx/ui/client/src/inventories/list/inventory-list.route.js
Normal file
@ -0,0 +1,27 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
import InventoriesList from './inventory-list.controller';
|
||||
|
||||
export default {
|
||||
name: 'inventories',
|
||||
route: '/inventories',
|
||||
templateUrl: templateUrl('inventories/inventories'),
|
||||
controller: InventoriesList,
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'inventory'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORIES"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
};
|
||||
14
awx/ui/client/src/inventories/list/main.js
Normal file
14
awx/ui/client/src/inventories/list/main.js
Normal file
@ -0,0 +1,14 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './inventory-list.route';
|
||||
import controller from './inventory-list.controller';
|
||||
|
||||
export default
|
||||
angular.module('inventoryList', [])
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
18
awx/ui/client/src/inventories/main.js
Normal file
18
awx/ui/client/src/inventories/main.js
Normal file
@ -0,0 +1,18 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import inventoryAdd from './add/main';
|
||||
import inventoryEdit from './edit/main';
|
||||
import inventoryList from './list/main';
|
||||
import inventoryManage from './manage/main';
|
||||
|
||||
export default
|
||||
angular.module('inventory', [
|
||||
inventoryAdd.name,
|
||||
inventoryEdit.name,
|
||||
inventoryList.name,
|
||||
inventoryManage.name,
|
||||
]);
|
||||
@ -0,0 +1,525 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* 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, ToggleHostEnabled, 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();
|
||||
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;
|
||||
|
||||
// Add groups view
|
||||
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.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.copyGroup = function(id) {
|
||||
PreviousSearchParams = Store('group_current_search_params');
|
||||
GroupsCopy({
|
||||
scope: $scope,
|
||||
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');
|
||||
HostsCopy({
|
||||
group_scope: $scope,
|
||||
host_scope: hostScope,
|
||||
host_id: id
|
||||
});
|
||||
};
|
||||
|
||||
hostScope.toggleHostEnabled = function (host_id, external_source) {
|
||||
ToggleHostEnabled({
|
||||
parent_scope: $scope,
|
||||
host_scope: hostScope,
|
||||
host_id: host_id,
|
||||
external_source: external_source
|
||||
});
|
||||
};
|
||||
|
||||
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', 'ToggleHostEnabled', 'ShowJobSummary',
|
||||
'InventoryGroupsHelp', 'HelpDialog', 'GroupsCopy',
|
||||
'HostsCopy', '$stateParams', 'ParamPass', InventoriesManage,
|
||||
];
|
||||
@ -10,9 +10,6 @@
|
||||
<div id="host-list-container" class="Panel"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="inventory-modal-container"></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>
|
||||
@ -0,0 +1,28 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import {templateUrl} from '../../shared/template-url/template-url.factory';
|
||||
import InventoriesManage from './inventory-manage.controller';
|
||||
|
||||
export default {
|
||||
name: 'inventoryManage',
|
||||
url: '/inventories/:inventory_id/manage?groups',
|
||||
templateUrl: templateUrl('inventories/manage/inventory-manage'),
|
||||
controller: InventoriesManage,
|
||||
data: {
|
||||
activityStream: true,
|
||||
activityStreamTarget: 'inventory',
|
||||
activityStreamId: 'inventory_id'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORY MANAGE"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
};
|
||||
19
awx/ui/client/src/inventories/manage/main.js
Normal file
19
awx/ui/client/src/inventories/manage/main.js
Normal file
@ -0,0 +1,19 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './inventory-manage.route';
|
||||
|
||||
import manageHosts from './manage-hosts/main';
|
||||
import manageGroups from './manage-groups/main';
|
||||
|
||||
export default
|
||||
angular.module('inventoryManage', [
|
||||
manageHosts.name,
|
||||
manageGroups.name
|
||||
])
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route);
|
||||
}]);
|
||||
@ -0,0 +1,550 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
function manageGroupsDirectiveController($filter, $rootScope, $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,
|
||||
SourceForm, SetSchedulesInnerDialogSize, CreateSelect2, ParamPass) {
|
||||
|
||||
var vm = this;
|
||||
var params = ParamPass.get();
|
||||
if(params === undefined) {
|
||||
params = {};
|
||||
params.scope = $scope.$new();
|
||||
}
|
||||
var parent_scope = params.scope,
|
||||
group_id = $stateParams.group_id,
|
||||
mode = $state.current.data.mode, // 'add' or 'edit'
|
||||
inventory_id = $stateParams.inventory_id,
|
||||
generator = GenerateForm,
|
||||
group_created = false,
|
||||
defaultUrl,
|
||||
master = {},
|
||||
choicesReady,
|
||||
modal_scope = parent_scope.$new(),
|
||||
properties_scope = parent_scope.$new(),
|
||||
sources_scope = parent_scope.$new(),
|
||||
elem, group,
|
||||
schedules_url = '';
|
||||
|
||||
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 GroupForm.fields) {
|
||||
if (data[fld]) {
|
||||
properties_scope[fld] = data[fld];
|
||||
master[fld] = properties_scope[fld];
|
||||
}
|
||||
}
|
||||
if(mode === 'edit') {
|
||||
schedules_url = data.related.inventory_source + 'schedules/';
|
||||
properties_scope.variable_url = data.related.variable_data;
|
||||
sources_scope.source_url = data.related.inventory_source;
|
||||
modal_scope.$emit('LoadSourceData');
|
||||
}
|
||||
})
|
||||
.error(function(data, status) {
|
||||
ProcessErrors(modal_scope, data, status, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve group: ' + defaultUrl + '. GET status: ' + status
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('#properties-tab').empty();
|
||||
$('#sources-tab').empty();
|
||||
|
||||
elem = document.getElementById('group-manage-panel');
|
||||
$compile(elem)(modal_scope);
|
||||
|
||||
$scope.parseType = 'yaml';
|
||||
|
||||
var form_scope =
|
||||
generator.inject(GroupForm, {
|
||||
mode: mode,
|
||||
id: 'properties-tab',
|
||||
related: false,
|
||||
scope: properties_scope,
|
||||
cancelButton: false,
|
||||
});
|
||||
var source_form_scope =
|
||||
generator.inject(SourceForm, {
|
||||
mode: mode,
|
||||
id: 'sources-tab',
|
||||
related: false,
|
||||
scope: sources_scope,
|
||||
cancelButton: false
|
||||
});
|
||||
|
||||
generator.reset();
|
||||
|
||||
GetSourceTypeOptions({
|
||||
scope: sources_scope,
|
||||
variable: 'source_type_options'
|
||||
});
|
||||
sources_scope.source = SourceForm.fields.source['default'];
|
||||
sources_scope.sourcePathRequired = false;
|
||||
sources_scope[SourceForm.fields.source_vars.parseTypeName] = 'yaml';
|
||||
sources_scope.update_cache_timeout = 0;
|
||||
properties_scope.parseType = 'yaml';
|
||||
|
||||
function waitStop() {
|
||||
Wait('stop');
|
||||
}
|
||||
|
||||
function initSourceChange() {
|
||||
parent_scope.showSchedulesTab = (mode === 'edit' && sources_scope.source && sources_scope.source.value !== "manual") ? true : false;
|
||||
SourceChange({
|
||||
scope: sources_scope,
|
||||
form: SourceForm
|
||||
});
|
||||
}
|
||||
|
||||
// JT -- this gets called after the properties & properties variables are loaded, and is emitted from (groupLoaded)
|
||||
if (modal_scope.removeLoadSourceData) {
|
||||
modal_scope.removeLoadSourceData();
|
||||
}
|
||||
modal_scope.removeLoadSourceData = modal_scope.$on('LoadSourceData', function() {
|
||||
ParseTypeChange({
|
||||
scope: form_scope,
|
||||
variable: 'variables',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'group_variables'
|
||||
});
|
||||
|
||||
if (sources_scope.source_url) {
|
||||
// get source data
|
||||
Rest.setUrl(sources_scope.source_url);
|
||||
Rest.get()
|
||||
.success(function(data) {
|
||||
var fld, i, j, flag, found, set, opts, list, form;
|
||||
form = SourceForm;
|
||||
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) {
|
||||
sources_scope[flag.name] = data[flag.name];
|
||||
master[flag.name] = sources_scope[flag.name];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fld === 'source') {
|
||||
found = false;
|
||||
data.source = (data.source === "") ? "manual" : data.source;
|
||||
for (i = 0; i < sources_scope.source_type_options.length; i++) {
|
||||
if (sources_scope.source_type_options[i].value === data.source) {
|
||||
sources_scope.source = sources_scope.source_type_options[i];
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found || sources_scope.source.value === "manual") {
|
||||
sources_scope.groupUpdateHide = true;
|
||||
} else {
|
||||
sources_scope.groupUpdateHide = false;
|
||||
}
|
||||
master.source = sources_scope.source;
|
||||
} else if (fld === 'source_vars') {
|
||||
// Parse source_vars, converting to YAML.
|
||||
sources_scope.source_vars = ParseVariableString(data.source_vars);
|
||||
master.source_vars = sources_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;
|
||||
sources_scope.inventory_script = data.source_script;
|
||||
master.inventory_script = sources_scope.inventory_script;
|
||||
} else if (fld === "source_regions") {
|
||||
if (data[fld] === "") {
|
||||
sources_scope[fld] = data[fld];
|
||||
master[fld] = sources_scope[fld];
|
||||
} else {
|
||||
sources_scope[fld] = data[fld].split(",");
|
||||
master[fld] = sources_scope[fld];
|
||||
}
|
||||
} else if (data[fld] !== undefined) {
|
||||
sources_scope[fld] = data[fld];
|
||||
master[fld] = sources_scope[fld];
|
||||
}
|
||||
|
||||
if (form.fields[fld].sourceModel && data.summary_fields &&
|
||||
data.summary_fields[form.fields[fld].sourceModel]) {
|
||||
sources_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];
|
||||
}
|
||||
}
|
||||
|
||||
initSourceChange();
|
||||
|
||||
if (data.source_regions) {
|
||||
if (data.source === 'ec2' ||
|
||||
data.source === 'rax' ||
|
||||
data.source === 'gce' ||
|
||||
data.source === 'azure') {
|
||||
if (data.source === 'ec2') {
|
||||
set = sources_scope.ec2_regions;
|
||||
} else if (data.source === 'rax') {
|
||||
set = sources_scope.rax_regions;
|
||||
} else if (data.source === 'gce') {
|
||||
set = sources_scope.gce_regions;
|
||||
} else if (data.source === 'azure') {
|
||||
set = sources_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: "#source_source_regions",
|
||||
opts: opts
|
||||
});
|
||||
|
||||
}
|
||||
} else {
|
||||
// If empty, default to all
|
||||
master.source_regions = [{
|
||||
id: 'all',
|
||||
text: 'All'
|
||||
}];
|
||||
}
|
||||
if (data.group_by && data.source === 'ec2') {
|
||||
set = sources_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: "#source_group_by",
|
||||
opts: opts
|
||||
});
|
||||
}
|
||||
|
||||
sources_scope.group_update_url = data.related.update;
|
||||
})
|
||||
.error(function(data, status) {
|
||||
sources_scope.source = "";
|
||||
ProcessErrors(modal_scope, data, status, null, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to retrieve inventory source. GET status: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (sources_scope.removeScopeSourceTypeOptionsReady) {
|
||||
sources_scope.removeScopeSourceTypeOptionsReady();
|
||||
}
|
||||
sources_scope.removeScopeSourceTypeOptionsReady = sources_scope.$on('sourceTypeOptionsReady', function() {
|
||||
if (mode === 'add') {
|
||||
sources_scope.source = Find({
|
||||
list: sources_scope.source_type_options,
|
||||
key: 'value',
|
||||
val: ''
|
||||
});
|
||||
modal_scope.showSchedulesTab = false;
|
||||
}
|
||||
});
|
||||
|
||||
choicesReady = 0;
|
||||
|
||||
if (sources_scope.removeChoicesReady) {
|
||||
sources_scope.removeChoicesReady();
|
||||
}
|
||||
sources_scope.removeChoicesReady = sources_scope.$on('choicesReadyGroup', function() {
|
||||
CreateSelect2({
|
||||
element: '#source_source',
|
||||
multiple: false
|
||||
});
|
||||
modal_scope.$emit('LoadSourceData');
|
||||
|
||||
choicesReady++;
|
||||
if (choicesReady === 5) {
|
||||
if (mode !== 'edit') {
|
||||
properties_scope.variables = "---";
|
||||
master.variables = properties_scope.variables;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Load options for source regions
|
||||
GetChoices({
|
||||
scope: sources_scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'rax_regions',
|
||||
choice_name: 'rax_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: sources_scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'ec2_regions',
|
||||
choice_name: 'ec2_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: sources_scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'source_regions',
|
||||
variable: 'gce_regions',
|
||||
choice_name: 'gce_region_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
GetChoices({
|
||||
scope: sources_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: sources_scope,
|
||||
url: GetBasePath('inventory_sources'),
|
||||
field: 'group_by',
|
||||
variable: 'ec2_group_by',
|
||||
choice_name: 'ec2_group_by_choices',
|
||||
callback: 'choicesReadyGroup'
|
||||
});
|
||||
|
||||
//Wait('start');
|
||||
|
||||
if (parent_scope.removeAddTreeRefreshed) {
|
||||
parent_scope.removeAddTreeRefreshed();
|
||||
}
|
||||
parent_scope.removeAddTreeRefreshed = parent_scope.$on('GroupTreeRefreshed', function() {
|
||||
// Clean up
|
||||
Wait('stop');
|
||||
|
||||
if (modal_scope.searchCleanUp) {
|
||||
modal_scope.searchCleanup();
|
||||
}
|
||||
try {
|
||||
//$('#group-modal-dialog').dialog('close');
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
});
|
||||
|
||||
if (modal_scope.removeSaveComplete) {
|
||||
modal_scope.removeSaveComplete();
|
||||
}
|
||||
modal_scope.removeSaveComplete = modal_scope.$on('SaveComplete', function(e, error) {
|
||||
if (!error) {
|
||||
modal_scope.cancelPanel();
|
||||
}
|
||||
});
|
||||
|
||||
if (modal_scope.removeFormSaveSuccess) {
|
||||
modal_scope.removeFormSaveSuccess();
|
||||
}
|
||||
modal_scope.removeFormSaveSuccess = modal_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: ((sources_scope.source && sources_scope.source.value !== 'manual') ? sources_scope.source.value : ''),
|
||||
source_path: sources_scope.source_path,
|
||||
credential: sources_scope.credential,
|
||||
overwrite: sources_scope.overwrite,
|
||||
overwrite_vars: sources_scope.overwrite_vars,
|
||||
source_script: sources_scope.inventory_script,
|
||||
update_on_launch: sources_scope.update_on_launch,
|
||||
update_cache_timeout: (sources_scope.update_cache_timeout || 0)
|
||||
};
|
||||
|
||||
// Create a string out of selected list of regions
|
||||
if (sources_scope.source_regions) {
|
||||
regions = $('#source_source_regions').select2("data");
|
||||
r = [];
|
||||
for (i = 0; i < regions.length; i++) {
|
||||
r.push(regions[i].id);
|
||||
}
|
||||
data.source_regions = r.join();
|
||||
}
|
||||
|
||||
if (sources_scope.source && (sources_scope.source.value === 'ec2')) {
|
||||
data.instance_filters = sources_scope.instance_filters;
|
||||
// Create a string out of selected list of regions
|
||||
group_by = $('#source_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 (sources_scope.source && (sources_scope.source.value === 'ec2')) {
|
||||
// for ec2, validate variable data
|
||||
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.source_vars, true);
|
||||
}
|
||||
|
||||
if (sources_scope.source && (sources_scope.source.value === 'custom')) {
|
||||
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.extra_vars, true);
|
||||
}
|
||||
|
||||
if (sources_scope.source && (sources_scope.source.value === 'vmware' ||
|
||||
sources_scope.source.value === 'openstack')) {
|
||||
data.source_vars = ToJSON(sources_scope.envParseType, sources_scope.inventory_variables, true);
|
||||
}
|
||||
|
||||
// the API doesn't expect the credential to be passed with a custom inv script
|
||||
if (sources_scope.source && sources_scope.source.value === 'custom') {
|
||||
delete(data.credential);
|
||||
}
|
||||
|
||||
if (!parseError) {
|
||||
Rest.setUrl(sources_scope.source_url);
|
||||
Rest.put(data)
|
||||
.success(function() {
|
||||
modal_scope.$emit('SaveComplete', false);
|
||||
})
|
||||
.error(function(data, status) {
|
||||
$('#group_tabs a:eq(1)').tab('show');
|
||||
ProcessErrors(sources_scope, data, status, SourceForm, {
|
||||
hdr: 'Error!',
|
||||
msg: 'Failed to update group inventory source. PUT status: ' + status
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Cancel
|
||||
modal_scope.cancelPanel = function() {
|
||||
Wait('stop');
|
||||
$state.go('inventoryManage', {}, {reload: true})
|
||||
};
|
||||
|
||||
// Save
|
||||
modal_scope.saveGroup = function() {
|
||||
Wait('start');
|
||||
var fld, data, json_data;
|
||||
|
||||
try {
|
||||
|
||||
json_data = ToJSON(properties_scope.parseType, properties_scope.variables, true);
|
||||
|
||||
data = {};
|
||||
for (fld in GroupForm.fields) {
|
||||
data[fld] = properties_scope[fld];
|
||||
}
|
||||
|
||||
data.inventory = inventory_id;
|
||||
|
||||
Rest.setUrl(defaultUrl);
|
||||
if (mode === 'edit' || (mode === 'add' && group_created)) {
|
||||
Rest.put(data)
|
||||
.success(function() {
|
||||
modal_scope.$emit('formSaveSuccess');
|
||||
})
|
||||
.error(function(data, status) {
|
||||
$('#group_tabs a:eq(0)').tab('show');
|
||||
ProcessErrors(properties_scope, data, status, GroupForm, {
|
||||
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;
|
||||
sources_scope.source_url = data.related.inventory_source;
|
||||
modal_scope.$emit('formSaveSuccess');
|
||||
})
|
||||
.error(function(data, status) {
|
||||
$('#group_tabs a:eq(0)').tab('show');
|
||||
ProcessErrors(properties_scope, data, status, GroupForm, {
|
||||
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
|
||||
modal_scope.updateGroup = function() {
|
||||
if (sources_scope.source === "manual" || sources_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 (sources_scope.status === 'updating') {
|
||||
Alert('Update in Progress', 'The inventory update process is currently running for group <em>' +
|
||||
$filter('sanitize')(sources_scope.summary_fields.group.name) + '</em>. Use the Refresh button to monitor the status.', 'alert-info', null, null, null, null, true);
|
||||
} else {
|
||||
InventoryUpdate({
|
||||
scope: parent_scope,
|
||||
group_id: group_id,
|
||||
url: properties_scope.group_update_url,
|
||||
group_name: properties_scope.name,
|
||||
group_source: sources_scope.source.value
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Change the lookup and regions when the source changes
|
||||
sources_scope.sourceChange = function() {
|
||||
sources_scope.credential_name = "";
|
||||
sources_scope.credential = "";
|
||||
if (sources_scope.credential_name_api_error) {
|
||||
delete sources_scope.credential_name_api_error;
|
||||
}
|
||||
initSourceChange();
|
||||
};
|
||||
|
||||
|
||||
angular.extend(vm, {
|
||||
cancelPanel : modal_scope.cancelPanel,
|
||||
saveGroup: modal_scope.saveGroup
|
||||
})
|
||||
}
|
||||
|
||||
export default ['$filter', '$rootScope', '$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', 'SourceForm', 'SetSchedulesInnerDialogSize', 'CreateSelect2', 'ParamPass',
|
||||
manageGroupsDirectiveController
|
||||
];
|
||||
@ -0,0 +1,25 @@
|
||||
/*************************************************
|
||||
* 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
|
||||
};
|
||||
}
|
||||
];
|
||||
@ -0,0 +1,20 @@
|
||||
<div>
|
||||
<div class="Form-exitHolder">
|
||||
<button class="Form-exit" ng-click="vm.cancelPanel()">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="group-manage-panel">
|
||||
<div id="properties-tab"></div>
|
||||
<div id="sources-tab"></div>
|
||||
</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-groupManage--okButton" ng-click="vm.saveGroup()">
|
||||
Save</button>
|
||||
<button type="button" class="btn btn-default Form-cancelButton" id="Inventory-groupManage--cancelButton" ng-click="vm.cancelPanel()">
|
||||
Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
16
awx/ui/client/src/inventories/manage/manage-groups/main.js
Normal file
16
awx/ui/client/src/inventories/manage/manage-groups/main.js
Normal file
@ -0,0 +1,16 @@
|
||||
/*************************************************
|
||||
* 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);
|
||||
}]);
|
||||
@ -0,0 +1,5 @@
|
||||
<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,46 @@
|
||||
/*************************************************
|
||||
* 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"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
add: {
|
||||
name: 'inventoryManage.addGroup',
|
||||
route: '/addGroup',
|
||||
templateUrl: templateUrl('inventories/manage/manage-groups/manage-groups'),
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORY ADD GROUP"
|
||||
},
|
||||
data: {
|
||||
mode: 'add'
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
@ -0,0 +1,192 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
function manageHostsDirectiveController($rootScope, $location, $log, $stateParams, $state, $scope, Rest, Alert, HostForm,
|
||||
GenerateForm, Prompt, ProcessErrors, GetBasePath, HostsReload, ParseTypeChange, Wait,
|
||||
Find, SetStatus, ApplyEllipsis, ToJSON, ParseVariableString, CreateDialog, TextareaResize, ParamPass) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
var params = ParamPass.get();
|
||||
if(params === undefined) {
|
||||
params = {};
|
||||
params.host_scope = $scope.$new();
|
||||
params.group_scope = $scope.$new();
|
||||
}
|
||||
var parent_scope = params.host_scope,
|
||||
group_scope = params.group_scope,
|
||||
inventory_id = $stateParams.inventory_id,
|
||||
mode = $state.current.data.mode, // 'add' or 'edit'
|
||||
selected_group_id = params.selected_group_id,
|
||||
generator = GenerateForm,
|
||||
form = HostForm,
|
||||
defaultUrl,
|
||||
scope = parent_scope.$new(),
|
||||
master = {},
|
||||
relatedSets = {},
|
||||
url, form_scope;
|
||||
|
||||
var host_id = $stateParams.host_id || undefined;
|
||||
|
||||
form_scope =
|
||||
generator.inject(HostForm, {
|
||||
mode: 'edit',
|
||||
id: 'host-panel-form',
|
||||
related: false,
|
||||
scope: scope,
|
||||
cancelButton: false
|
||||
});
|
||||
generator.reset();
|
||||
console.info(angular.element(document.getElementById('host_variables')));
|
||||
|
||||
$scope.parseType = 'yaml';
|
||||
ParseTypeChange({
|
||||
scope: form_scope,
|
||||
variable: 'variables',
|
||||
parse_variable: 'parseType',
|
||||
field_id: 'host_variables'
|
||||
});
|
||||
|
||||
|
||||
// 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;
|
||||
})
|
||||
.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() {
|
||||
Wait('stop');
|
||||
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();
|
||||
$state.go('inventoryManage', {}, {
|
||||
reload: true
|
||||
});
|
||||
});
|
||||
|
||||
// Save changes to the parent
|
||||
var saveHost = 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
|
||||
}
|
||||
};
|
||||
|
||||
var cancelPanel = function() {
|
||||
scope.$destroy();
|
||||
if (scope.codeMirror) {
|
||||
scope.codeMirror.destroy();
|
||||
}
|
||||
$state.go('inventoryManage');
|
||||
};
|
||||
|
||||
angular.extend(vm, {
|
||||
cancelPanel: cancelPanel,
|
||||
saveHost: saveHost,
|
||||
mode: mode
|
||||
});
|
||||
}
|
||||
|
||||
export default ['$rootScope', '$location', '$log', '$stateParams', '$state', '$scope', 'Rest', 'Alert', 'HostForm',
|
||||
'GenerateForm', 'Prompt', 'ProcessErrors', 'GetBasePath', 'HostsReload', 'ParseTypeChange',
|
||||
'Wait', 'Find', 'SetStatus', 'ApplyEllipsis', 'ToJSON', 'ParseVariableString',
|
||||
'CreateDialog', 'TextareaResize', 'ParamPass', manageHostsDirectiveController
|
||||
];
|
||||
@ -0,0 +1,25 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
/* jshint unused: vars */
|
||||
import manageHostsDirectiveController from './manage-hosts.directive.controller';
|
||||
|
||||
export default ['templateUrl', 'ParamPass',
|
||||
function(templateUrl, ParamPass) {
|
||||
return {
|
||||
restrict: 'EA',
|
||||
scope: true,
|
||||
replace: true,
|
||||
templateUrl: templateUrl('inventories/manage/manage-hosts/directive/manage-hosts.directive'),
|
||||
link: function(scope, element, attrs) {
|
||||
|
||||
},
|
||||
controller: manageHostsDirectiveController,
|
||||
controllerAs: 'vm',
|
||||
bindToController: true
|
||||
};
|
||||
}
|
||||
];
|
||||
@ -0,0 +1,25 @@
|
||||
<div>
|
||||
<div class="Form-header" ng-if="vm.mode === 'add'">
|
||||
<div class="Form-title ng-binding" >Create Host</div>
|
||||
<div class="Form-exitHolder">
|
||||
<button class="Form-exit" ng-click="vm.cancelPanel()">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="Form-exitHolder" ng-if="vm.mode === 'edit'">
|
||||
<button class="Form-exit" ng-click="vm.cancelPanel()">
|
||||
<i class="fa fa-times-circle"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="host-panel-form"></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-hostManage--okButton" ng-click="vm.saveHost()">
|
||||
Save</button>
|
||||
<button type="button" class="btn btn-default Form-cancelButton" id="Inventory-hostManage--cancelButton" ng-click="vm.cancelPanel()">
|
||||
Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
16
awx/ui/client/src/inventories/manage/manage-hosts/main.js
Normal file
16
awx/ui/client/src/inventories/manage/manage-hosts/main.js
Normal file
@ -0,0 +1,16 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2015 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
import route from './manage-hosts.route';
|
||||
import manageHostsDirective from './directive/manage-hosts.directive';
|
||||
|
||||
export default
|
||||
angular.module('manage-hosts', [])
|
||||
.directive('manageHosts', manageHostsDirective)
|
||||
.run(['$stateExtender', function($stateExtender) {
|
||||
$stateExtender.addState(route.edit);
|
||||
$stateExtender.addState(route.add);
|
||||
}]);
|
||||
@ -0,0 +1,5 @@
|
||||
<div class="tab-pane" id="Inventory-hostManage">
|
||||
<div ng-cloak id="Inventory-hostManage--panel" class="Panel">
|
||||
<manage-hosts></manage-hosts>
|
||||
</div>
|
||||
</div>
|
||||
@ -0,0 +1,46 @@
|
||||
/*************************************************
|
||||
* Copyright (c) 2016 Ansible, Inc.
|
||||
*
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
import {
|
||||
templateUrl
|
||||
} from '../../../shared/template-url/template-url.factory';
|
||||
|
||||
export default {
|
||||
edit: {
|
||||
name: 'inventoryManage.editHost',
|
||||
route: '/:host_id/editHost',
|
||||
templateUrl: templateUrl('inventories/manage/manage-hosts/manage-hosts'),
|
||||
data: {
|
||||
host_id: 'host_id',
|
||||
mode: 'edit'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORY EDIT HOSTS"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
add: {
|
||||
name: 'inventoryManage.addHost',
|
||||
route: '/addHost',
|
||||
templateUrl: templateUrl('inventories/manage/manage-hosts/manage-hosts'),
|
||||
data: {
|
||||
mode: 'add'
|
||||
},
|
||||
ncyBreadcrumb: {
|
||||
label: "INVENTORY ADD HOST"
|
||||
},
|
||||
resolve: {
|
||||
features: ['FeaturesService', function(FeaturesService) {
|
||||
return FeaturesService.get();
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
};
|
||||
18
awx/ui/client/src/inventories/manage/manage.block.less
Normal file
18
awx/ui/client/src/inventories/manage/manage.block.less
Normal file
@ -0,0 +1,18 @@
|
||||
#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%;
|
||||
}
|
||||
}
|
||||
@ -871,4 +871,20 @@ angular.module('Utilities', ['RestServices', 'Utilities', 'sanitizeFilter'])
|
||||
|
||||
};
|
||||
}
|
||||
]);
|
||||
])
|
||||
.factory('ParamPass', function() {
|
||||
var savedData = undefined;
|
||||
|
||||
function set(data) {
|
||||
savedData = data;
|
||||
}
|
||||
|
||||
function get() {
|
||||
return savedData;
|
||||
}
|
||||
|
||||
return {
|
||||
set: set,
|
||||
get: get
|
||||
}
|
||||
});
|
||||
|
||||
@ -1394,13 +1394,18 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
|
||||
"ng-if=is_superuser>Admin</span>";
|
||||
}
|
||||
html += "</div>\n";
|
||||
if(options.cancelButton !== undefined && options.cancelButton === false) {
|
||||
html += "<div class=\"Form-exitHolder\">";
|
||||
html += "</div>";
|
||||
} else {
|
||||
html += "<div class=\"Form-exitHolder\">";
|
||||
html += "<button class=\"Form-exit\" ng-click=\"formCancel()\">";
|
||||
html += "<i class=\"fa fa-times-circle\"></i>";
|
||||
html += "</button></div>\n";
|
||||
}
|
||||
html += "</div>\n"; //end of Form-header
|
||||
|
||||
html += "<div class=\"Form-exitHolder\">";
|
||||
html += "<button class=\"Form-exit\" ng-click=\"formCancel()\">";
|
||||
html += "<i class=\"fa fa-times-circle\"></i>";
|
||||
html += "</button></div>\n";
|
||||
|
||||
html += "</div>\n"; //end of Form-header
|
||||
|
||||
if (this.form.tabs) {
|
||||
var collection;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user