diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 11e5aca889..ab22556233 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -70,6 +70,7 @@ import access from './access/main'; import './login/authenticationServices/pendo/ng-pendo'; import footer from './footer/main'; import scheduler from './scheduler/main'; +import instanceGroups from './instance-groups/main'; var tower = angular.module('Tower', [ // how to add CommonJS / AMD third-party dependencies: @@ -123,6 +124,7 @@ var tower = angular.module('Tower', [ users.name, projects.name, scheduler.name, + instanceGroups.name, 'Utilities', 'templates', diff --git a/awx/ui/client/src/instance-groups/instance-groups.list.js b/awx/ui/client/src/instance-groups/instance-groups.list.js new file mode 100644 index 0000000000..0be89f9260 --- /dev/null +++ b/awx/ui/client/src/instance-groups/instance-groups.list.js @@ -0,0 +1,27 @@ +export default ['i18n', function(i18n) { + return { + name: 'instance_groups' , + basePath: 'instance_groups', + iterator: 'instance_group', + listTitle: i18n._('INSTANCE GROUPS'), + index: false, + hover: false, + + fields: { + name: { + key: true, + label: i18n._('Name'), + columnClass: 'col-md-3 col-sm-9 col-xs-9', + modalColumnClass: 'col-md-8', + }, + capacity: { + label: i18n._('Capacity'), + nosort: true, + }, + running_jobs: { + label: i18n._('Running Jobs'), + nosort: true, + }, + } + }; +}]; diff --git a/awx/ui/client/src/instance-groups/instance-groups.service.js b/awx/ui/client/src/instance-groups/instance-groups.service.js new file mode 100644 index 0000000000..84605bca53 --- /dev/null +++ b/awx/ui/client/src/instance-groups/instance-groups.service.js @@ -0,0 +1,41 @@ +export default + ['Rest', function(Rest) { + return { + addInstanceGroups: function(url, instance_groups) { + let groups = (instance_groups || []); + Rest.setUrl(url); + let defers = groups.map((group) => Rest.post(group)); + return Promise.all(defers); + }, + editInstanceGroups: function(url, instance_groups) { + Rest.setUrl(url); + let currentGroups = Rest.get() + .then(({data}) => { + return data.results.map((i) => i.id); + }); + + return currentGroups.then(function(current) { + + let groupsToAdd = (instance_groups || []) + .map(val => val.id); + + let groupsToDisassociate = current + .filter(val => groupsToAdd + .indexOf(val) === -1) + .map(val => ({id: val, disassociate: true})); + + let groupsToAssociate = groupsToAdd + .filter(val => current + .indexOf(val) === -1) + .map(val => ({id: val, associate: true})); + + let pass = groupsToDisassociate + .concat(groupsToAssociate); + + Rest.setUrl(url); + let defers = pass.map((group) => Rest.post(group)); + Promise.resolve(defers); + }); + } + }; +}]; \ No newline at end of file diff --git a/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js b/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js new file mode 100644 index 0000000000..ffb46ac5c0 --- /dev/null +++ b/awx/ui/client/src/instance-groups/list/instance-groups-list.controller.js @@ -0,0 +1,42 @@ +export default ['$scope', 'InstanceGroupList', 'GetBasePath', 'Rest', 'Dataset','Find', '$state', '$q', + function($scope, InstanceGroupList, GetBasePath, Rest, Dataset, Find, $state, $q) { + let list = InstanceGroupList; + + init(); + + function init(){ + $scope.optionsDefer = $q.defer(); + $scope.list = list; + $scope[`${list.iterator}_dataset`] = Dataset.data; + $scope[list.name] = $scope[`${list.iterator}_dataset`].results; + } + + // iterate over the list and add fields like type label, after the + // OPTIONS request returns, or the list is sorted/paginated/searched + function optionsRequestDataProcessing(){ + $scope.optionsDefer.promise.then(function(options) { + if($scope.list.name === 'instance_groups'){ + if ($scope[list.name] !== undefined) { + $scope[list.name].forEach(function(item, item_idx) { + var itm = $scope[list.name][item_idx]; + // Set the item type label + if (list.fields.kind && options && options.actions && options.actions.GET && options.actions.GET.kind) { + options.actions.GET.kind.choices.forEach(function(choice) { + if (choice[0] === item.kind) { + itm.kind_label = choice[1]; + } + }); + } + + }); + } + } + }); + } + + $scope.$watchCollection(`${$scope.list.name}`, function() { + optionsRequestDataProcessing(); + } + ); + } +]; \ No newline at end of file diff --git a/awx/ui/client/src/instance-groups/main.js b/awx/ui/client/src/instance-groups/main.js new file mode 100644 index 0000000000..7747d36913 --- /dev/null +++ b/awx/ui/client/src/instance-groups/main.js @@ -0,0 +1,36 @@ +import InstanceGroupsList from './list/instance-groups-list.controller'; +import list from './instance-groups.list'; +import service from './instance-groups.service'; +import { N_ } from '../i18n'; + +export default +angular.module('instanceGroups', []) + .factory('InstanceGroupList', list) + .service('InstanceGroupsService', service) + .controller('InstanceGroupsList', InstanceGroupsList) + .config(['$stateProvider', 'stateDefinitionsProvider', + function($stateProvider, stateDefinitionsProvider) { + let stateDefinitions = stateDefinitionsProvider.$get(); + + $stateProvider.state({ + name: 'instanceGroups', + url: '/instance_groups', + lazyLoad: () => stateDefinitions.generateTree({ + parent: 'instanceGroups', + list: 'InstanceGroupList', + controllers: { + list: 'InstanceGroupsList' + }, + data: { + activityStream: true, + activityStreamTarget: 'instanceGroup' + }, + ncyBreadcrumb: { + parent: 'setup', + label: N_('INSTANCE GROUPS') + } + }) + }); + + } + ]); \ No newline at end of file diff --git a/awx/ui/client/src/inventories/inventories.route.js b/awx/ui/client/src/inventories/inventories.route.js index 509e76c734..6d0efc8431 100644 --- a/awx/ui/client/src/inventories/inventories.route.js +++ b/awx/ui/client/src/inventories/inventories.route.js @@ -42,6 +42,20 @@ export default { .catch(function() { return false; }); + }], + InstanceGroupsData: ['Rest', 'GetBasePath', 'ProcessErrors', (Rest, GetBasePath, ProcessErrors) => { + const url = GetBasePath('instance_groups'); + Rest.setUrl(url); + return Rest.get() + .then(({data}) => { + return data.results.map((i) => ({name: i.name, id: i.id})); + }) + .catch(({data, status}) => { + ProcessErrors(null, data, status, null, { + hdr: 'Error!', + msg: 'Failed to get instance groups info. GET returned status: ' + status + }); + }); }] } }; diff --git a/awx/ui/client/src/inventories/standard/add/inventory-add.controller.js b/awx/ui/client/src/inventories/standard/add/inventory-add.controller.js index 95748d99b7..200aa4c9ba 100644 --- a/awx/ui/client/src/inventories/standard/add/inventory-add.controller.js +++ b/awx/ui/client/src/inventories/standard/add/inventory-add.controller.js @@ -13,7 +13,7 @@ function InventoriesAdd($scope, $location, GenerateForm, InventoryForm, rbacUiControlService, Rest, Alert, ProcessErrors, ClearScope, GetBasePath, ParseTypeChange, Wait, ToJSON, - $state, canAdd) { + $state, canAdd, CreateSelect2, InstanceGroupsService, InstanceGroupsData) { $scope.canAdd = canAdd; @@ -42,6 +42,13 @@ function InventoriesAdd($scope, $location, }); } + $scope.instanceGroupOptions = InstanceGroupsData; + CreateSelect2({ + element: '#inventory_instance_groups', + multiple: true, + addNew: false + }); + // Save $scope.formSave = function() { Wait('start'); @@ -59,12 +66,24 @@ function InventoriesAdd($scope, $location, Rest.setUrl(defaultUrl); Rest.post(data) - .success(function(data) { - var inventory_id = data.id; - Wait('stop'); - $state.go('inventories.edit', {inventory_id: inventory_id}, {reload: true}); + .then(({data}) => { + const inventory_id = data.id, + instance_group_url = data.related.instance_groups; + + InstanceGroupsService.addInstanceGroups(instance_group_url, $scope.instance_groups) + .then(() => { + Wait('stop'); + $state.go('inventories.edit', {inventory_id: inventory_id}, {reload: true}); + }) + .catch(({data, status}) => { + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to post instance groups. POST returned ' + + 'status: ' + status + }); + }); }) - .error(function(data, status) { + .catch(({data, status}) => { ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: 'Failed to add new inventory. Post returned status: ' + status @@ -85,5 +104,5 @@ function InventoriesAdd($scope, $location, export default ['$scope', '$location', 'GenerateForm', 'InventoryForm', 'rbacUiControlService', 'Rest', 'Alert', 'ProcessErrors', 'ClearScope', 'GetBasePath', 'ParseTypeChange', - 'Wait', 'ToJSON', '$state', 'canAdd', InventoriesAdd -]; + 'Wait', 'ToJSON', '$state','canAdd', 'CreateSelect2', 'InstanceGroupsService', 'InstanceGroupsData', InventoriesAdd +]; \ No newline at end of file diff --git a/awx/ui/client/src/inventories/standard/edit/inventory-edit.controller.js b/awx/ui/client/src/inventories/standard/edit/inventory-edit.controller.js index 70500bac10..bf14f5dfdd 100644 --- a/awx/ui/client/src/inventories/standard/edit/inventory-edit.controller.js +++ b/awx/ui/client/src/inventories/standard/edit/inventory-edit.controller.js @@ -13,13 +13,14 @@ function InventoriesEdit($scope, $location, $stateParams, InventoryForm, Rest, ProcessErrors, ClearScope, GetBasePath, ParseTypeChange, Wait, ToJSON, - ParseVariableString, $state, OrgAdminLookup, $rootScope, resourceData) { + ParseVariableString, $state, OrgAdminLookup, $rootScope, resourceData, CreateSelect2, InstanceGroupsService, InstanceGroupsData) { // Inject dynamic view - var defaultUrl = GetBasePath('inventory'), + let defaultUrl = GetBasePath('inventory'), form = InventoryForm, fld, data, - inventoryData = resourceData.data; + inventoryData = resourceData.data, + instance_group_url = inventoryData.related.instance_groups; init(); @@ -34,6 +35,36 @@ function InventoriesEdit($scope, $location, $scope.inventory_variables = inventoryData.variables === null || inventoryData.variables === '' ? '---' : ParseVariableString(inventoryData.variables); $scope.parseType = 'yaml'; + $scope.instanceGroupOptions = InstanceGroupsData; + CreateSelect2({ + element: '#inventory_instance_groups', + multiple: true, + addNew: false + }); + + Rest.setUrl(instance_group_url); + Rest.get() + .then(({data}) => { + if (data.results.length > 0) { + var opts = data.results + .map(i => ({id: i.id + "", + name: i.name})); + CreateSelect2({ + element:'#inventory_instance_groups', + multiple: true, + addNew: false, + opts: opts + }); + } + }) + .catch(({data, status}) => { + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to get instance groups. GET returned ' + + 'status: ' + status + }); + }); + $rootScope.$on('$stateChangeSuccess', function(event, toState) { if(toState.name === 'inventories.edit') { ParseTypeChange({ @@ -75,11 +106,20 @@ function InventoriesEdit($scope, $location, Rest.setUrl(defaultUrl + $stateParams.inventory_id + '/'); Rest.put(data) - .success(function() { - Wait('stop'); - $state.go($state.current, {}, { reload: true }); + .then(() => { + InstanceGroupsService.editInstanceGroups(instance_group_url, $scope.instance_groups) + .then(() => { + Wait('stop'); + $state.go($state.current, {}, { reload: true }); + }) + .catch(({data, status}) => { + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to update instance groups. POST returned status: ' + status + }); + }); }) - .error(function(data, status) { + .catch(({data, status}) => { ProcessErrors($scope, data, status, form, { hdr: 'Error!', msg: 'Failed to update inventory. PUT returned status: ' + status @@ -101,5 +141,5 @@ export default ['$scope', '$location', '$stateParams', 'InventoryForm', 'Rest', 'ProcessErrors', 'ClearScope', 'GetBasePath', 'ParseTypeChange', 'Wait', 'ToJSON', 'ParseVariableString', - '$state', 'OrgAdminLookup', '$rootScope', 'resourceData', InventoriesEdit, + '$state', 'OrgAdminLookup', '$rootScope', 'resourceData', 'CreateSelect2', 'InstanceGroupsService', 'InstanceGroupsData', InventoriesEdit, ]; diff --git a/awx/ui/client/src/inventories/standard/inventory.form.js b/awx/ui/client/src/inventories/standard/inventory.form.js index 468ab1112f..0e606b7c12 100644 --- a/awx/ui/client/src/inventories/standard/inventory.form.js +++ b/awx/ui/client/src/inventories/standard/inventory.form.js @@ -79,6 +79,16 @@ function(i18n, InventoryCompletedJobsList) { credential_type: 13 //insights } }, + instance_groups: { + label: i18n._('Instance Groups'), + type: 'select', + awPopOver: "
" + i18n._("Select the Instance Groups for this Inventory to run on.") + "
", + dataTitle: i18n._('Instance Groups'), + dataPlacement: 'right', + dataContainer: 'body', + multiSelect: true, + ngOptions: 'group.name for group in instanceGroupOptions track by group.id', + }, inventory_variables: { realName: 'variables', label: i18n._('Variables'), diff --git a/awx/ui/client/src/job-results/job-results.partial.html b/awx/ui/client/src/job-results/job-results.partial.html index 8df5da0a6f..dedc200007 100644 --- a/awx/ui/client/src/job-results/job-results.partial.html +++ b/awx/ui/client/src/job-results/job-results.partial.html @@ -357,6 +357,17 @@ + +" + i18n._("Select the Instance Groups for this Organization to run on.") + "
", + dataContainer: 'body', + dataPlacement: 'right', + dataTitle: i18n._('Instance Groups'), + multiSelect: true, + ngOptions: 'group.name for group in instanceGroupOptions track by group.id', } }, diff --git a/awx/ui/client/src/setup-menu/setup-menu.partial.html b/awx/ui/client/src/setup-menu/setup-menu.partial.html index aa1bae3eea..b8fd513f25 100644 --- a/awx/ui/client/src/setup-menu/setup-menu.partial.html +++ b/awx/ui/client/src/setup-menu/setup-menu.partial.html @@ -56,6 +56,12 @@ View and edit your license information. + ++ View list and capacity of Tower instances. +
+diff --git a/awx/ui/client/src/shared/form-generator.js b/awx/ui/client/src/shared/form-generator.js index cf6b300ba5..01610052cc 100644 --- a/awx/ui/client/src/shared/form-generator.js +++ b/awx/ui/client/src/shared/form-generator.js @@ -130,7 +130,7 @@ * * If the field type is textarea and the name is one of variables, extra_vars, inventory_variables or source_vars, then the parse type radio button group is added. This is the radio button group allowing the user to switch between JSON and YAML. * - * Applying CodeMirror to the text area is handled by ParseTypeChange() found in helpers/Parse.js. Within the controller will be a call to ParseTypeChange that creates the CodeMirror object and sets up the required $scope methods for handles getting, settting and type conversion. + * Applying CodeMirror to the text area is handled by ParseTypeChange() found in helpers/Parse.js. Within the controller will be a call to ParseTypeChange that creates the CodeMirror object and sets up the required $scope methods for handles getting, setting and type conversion. */ import GeneratorHelpers from './generator-helpers'; diff --git a/awx/ui/client/src/templates/job-template.form.js b/awx/ui/client/src/templates/job-template.form.js index b2f00d1840..5431f5f0b6 100644 --- a/awx/ui/client/src/templates/job-template.form.js +++ b/awx/ui/client/src/templates/job-template.form.js @@ -243,6 +243,16 @@ function(NotificationsList, CompletedJobsList, i18n) { dataContainer: "body", ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate)' }, + instance_groups: { + label: i18n._('Instance Groups'), + type: 'select', + awPopOver: "
" + i18n._("Select the Instance Groups for this Job Template to run on.") + "
", + dataContainer: 'body', + dataPlacement: 'right', + dataTitle: i18n._('Instance Groups'), + multiSelect: true, + ngOptions: 'group.name for group in instanceGroupOptions track by group.id', + }, job_tags: { label: i18n._('Job Tags'), type: 'textarea', diff --git a/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js b/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js index 2deac3e27c..01d42d05a8 100644 --- a/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js +++ b/awx/ui/client/src/templates/job_templates/add-job-template/job-template-add.controller.js @@ -9,13 +9,14 @@ '$stateParams', 'JobTemplateForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'ClearScope', 'GetBasePath', 'md5Setup', 'ParseTypeChange', 'Wait', 'Empty', 'ToJSON', 'CallbackHelpInit', 'GetChoices', '$state', - 'CreateSelect2', '$q', 'i18n', 'Inventory', 'Project', + 'CreateSelect2', '$q', 'i18n', 'Inventory', 'Project', 'InstanceGroupsService', 'InstanceGroupsData', function( $filter, $scope, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert, ProcessErrors, ClearScope, GetBasePath, md5Setup, ParseTypeChange, Wait, Empty, ToJSON, CallbackHelpInit, GetChoices, - $state, CreateSelect2, $q, i18n, Inventory, Project + $state, CreateSelect2, $q, i18n, Inventory, Project, InstanceGroupsService, + InstanceGroupsData ) { Rest.setUrl(GetBasePath('job_templates')); @@ -29,7 +30,7 @@ ClearScope(); // Inject dynamic view - var defaultUrl = GetBasePath('job_templates'), + let defaultUrl = GetBasePath('job_templates'), form = JobTemplateForm(), generator = GenerateForm, master = {}, @@ -47,6 +48,13 @@ $scope.mode = "add"; $scope.parseType = 'yaml'; + $scope.instanceGroupOptions = InstanceGroupsData; + CreateSelect2({ + element: '#job_template_instance_groups', + multiple: true, + addNew: false + }); + md5Setup({ scope: $scope, master: master, @@ -93,7 +101,6 @@ element:'#playbook-select', multiple: false }); - CreateSelect2({ element:'#job_template_verbosity', multiple: false @@ -266,9 +273,10 @@ 'alert-danger', saveCompleted, null, null, null, true); } + + var orgDefer = $q.defer(); var associationDefer = $q.defer(); - Rest.setUrl(data.related.labels); var currentLabels = Rest.get() @@ -433,21 +441,29 @@ Rest.setUrl(defaultUrl); Rest.post(data) - .success(function(data) { - $scope.$emit('templateSaveSuccess', - data - ); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, form, - { - hdr: 'Error!', - msg: 'Failed to add new job ' + - 'template. POST returned status: ' + - status - }); - }); + .then(({data}) => { + $scope.$emit('templateSaveSuccess', data); + const instance_group_url = data.related.instance_groups; + InstanceGroupsService.addInstanceGroups(instance_group_url, $scope.instance_groups) + .then(() => { + Wait('stop'); + }) + .catch(({data, status}) => { + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to post instance groups. POST returned ' + + 'status: ' + status + }); + }); + }) + .catch(({data, status}) => { + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to add new job ' + + 'template. POST returned status: ' + status + }); + }); } catch (err) { Wait('stop'); Alert("Error", "Error parsing extra variables. " + diff --git a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js index a3d78189a6..9c3c5ba868 100644 --- a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js +++ b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js @@ -17,14 +17,14 @@ export default 'ParseTypeChange', 'Wait', 'Empty', 'Prompt', 'ToJSON', 'GetChoices', 'CallbackHelpInit', 'InitiatePlaybookRun' , 'initSurvey', '$state', 'CreateSelect2', - 'ToggleNotification','$q', + 'ToggleNotification','$q', 'InstanceGroupsService', 'InstanceGroupsData', function( $filter, $scope, $rootScope, $location, $stateParams, JobTemplateForm, GenerateForm, Rest, Alert, ProcessErrors, ClearScope, GetBasePath, md5Setup, ParseTypeChange, Wait, Empty, Prompt, ToJSON, GetChoices, CallbackHelpInit, InitiatePlaybookRun, SurveyControllerInit, $state, - CreateSelect2, ToggleNotification, $q + CreateSelect2, ToggleNotification, $q, InstanceGroupsService, InstanceGroupsData ) { ClearScope(); @@ -35,17 +35,19 @@ export default } }); - var defaultUrl = GetBasePath('job_templates'), + let defaultUrl = GetBasePath('job_templates'), generator = GenerateForm, form = JobTemplateForm(), base = $location.path().replace(/^\//, '').split('/')[0], master = {}, id = $stateParams.job_template_id, checkSCMStatus, getPlaybooks, callback, - choicesCount = 0; + choicesCount = 0, + instance_group_url = defaultUrl + id + '/instance_groups'; init(); - function init(){ + function init() { + CallbackHelpInit({ scope: $scope }); $scope.playbook_options = null; $scope.playbook = null; @@ -53,6 +55,36 @@ export default $scope.parseType = 'yaml'; $scope.showJobType = false; + $scope.instanceGroupOptions = InstanceGroupsData; + CreateSelect2({ + element: '#job_template_instance_groups', + multiple: true, + addNew: false + }); + + Rest.setUrl(instance_group_url); + Rest.get() + .then(({data}) => { + if (data.results.length > 0) { + let opts = data.results + .map(i => ({id: i.id + "", + name: i.name})); + CreateSelect2({ + element: '#job_template_instance_groups', + multiple: true, + addNew: false, + opts: opts + }); + } + }) + .catch(({data, status}) => { + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to get instance groups. GET returned ' + + 'status: ' + status + }); + }); + SurveyControllerInit({ scope: $scope, parent_scope: $scope, @@ -413,6 +445,16 @@ export default 'alert-danger', saveCompleted, null, null, null, true); } + + InstanceGroupsService.editInstanceGroups(instance_group_url, $scope.instance_groups) + .catch(({data, status}) => { + ProcessErrors($scope, data, status, form, { + hdr: 'Error!', + msg: 'Failed to update instance groups. POST returned status: ' + status + }); + }); + + var orgDefer = $q.defer(); var associationDefer = $q.defer(); var associatedLabelsDefer = $q.defer(); diff --git a/awx/ui/client/src/templates/main.js b/awx/ui/client/src/templates/main.js index 60cb212902..f4c8da4db8 100644 --- a/awx/ui/client/src/templates/main.js +++ b/awx/ui/client/src/templates/main.js @@ -46,7 +46,24 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesA let stateTree, addJobTemplate, editJobTemplate, addWorkflow, editWorkflow, workflowMaker, inventoryLookup, credentialLookup, stateDefinitions = stateDefinitionsProvider.$get(), - stateExtender = $stateExtenderProvider.$get(); + stateExtender = $stateExtenderProvider.$get(), + instanceGroupsResolve = { + InstanceGroupsData: ['Rest', 'GetBasePath', 'ProcessErrors', (Rest, GetBasePath, ProcessErrors) => { + const url = GetBasePath('instance_groups'); + Rest.setUrl(url); + return Rest.get() + .then(({data}) => { + return data.results.map((i) => ({name: i.name, id: i.id})); + }) + .catch(({data, status}) => { + ProcessErrors(null, data, status, null, { + hdr: 'Error!', + msg: 'Failed to get instance groups info. GET returned status: ' + status + }); + }); + }] + }; + function generateStateTree() { @@ -85,7 +102,8 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesA }); }); } - }] + }], + InstanceGroupsData: instanceGroupsResolve.InstanceGroupsData } } }); @@ -105,6 +123,9 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesA activityStream: true, activityStreamTarget: 'job_template', activityStreamId: 'job_template_id' + }, + resolve: { + edit: instanceGroupsResolve } });