diff --git a/magnum_ui/api/rest/magnum.py b/magnum_ui/api/rest/magnum.py index 61a884f3..8bfcd05c 100644 --- a/magnum_ui/api/rest/magnum.py +++ b/magnum_ui/api/rest/magnum.py @@ -16,6 +16,7 @@ from django.views import generic from magnum_ui.api import magnum +from openstack_dashboard.api import neutron from openstack_dashboard.api.rest import urls from openstack_dashboard.api.rest import utils as rest_utils @@ -181,3 +182,23 @@ class Certificates(generic.View): return rest_utils.CreatedResponse( '/api/container_infra/certificates/', new_cert.to_dict()) + + +@urls.register +class Networks(generic.View): + """API for Neutron networks for Cluster Templates creation""" + url_regex = r'container_infra/networks/$' + + @rest_utils.ajax() + def get(self, request): + """Get a list of the Networks for a project. + + Networks includes external and private. Also, each network + has subnets. + The returned result is an object with property 'items' and each + item under this is a Network. + """ + tenant_id = request.user.tenant_id + result = neutron.network_list_for_tenant(request, tenant_id, + include_external=True) + return {'items': [n.to_dict() for n in result]} diff --git a/magnum_ui/static/dashboard/container-infra/cluster-templates/update/update.service.js b/magnum_ui/static/dashboard/container-infra/cluster-templates/update/update.service.js index 0fafb6c8..c261716d 100644 --- a/magnum_ui/static/dashboard/container-infra/cluster-templates/update/update.service.js +++ b/magnum_ui/static/dashboard/container-infra/cluster-templates/update/update.service.js @@ -117,6 +117,9 @@ } } config.model.labels = labels; // + + // update workflow + workflow.update(config); } return modal.open(config).then(submit); diff --git a/magnum_ui/static/dashboard/container-infra/cluster-templates/update/update.service.spec.js b/magnum_ui/static/dashboard/container-infra/cluster-templates/update/update.service.spec.js index 8c1ef18d..305692ac 100644 --- a/magnum_ui/static/dashboard/container-infra/cluster-templates/update/update.service.spec.js +++ b/magnum_ui/static/dashboard/container-infra/cluster-templates/update/update.service.spec.js @@ -41,6 +41,8 @@ init: function (action, title) { action = title; return {model: model}; + }, + update: function () { } }; @@ -66,6 +68,7 @@ spyOn(magnum, 'getClusterTemplate').and.returnValue(deferred.promise); spyOn(magnum, 'updateClusterTemplate').and.returnValue(deferred.promise); spyOn(workflow, 'init').and.returnValue({model: model}); + spyOn(workflow, 'update').and.callThrough(); spyOn(modal, 'open').and.callThrough(); })); diff --git a/magnum_ui/static/dashboard/container-infra/cluster-templates/workflow/workflow.service.js b/magnum_ui/static/dashboard/container-infra/cluster-templates/workflow/workflow.service.js index cbb4cfa0..16d562e6 100644 --- a/magnum_ui/static/dashboard/container-infra/cluster-templates/workflow/workflow.service.js +++ b/magnum_ui/static/dashboard/container-infra/cluster-templates/workflow/workflow.service.js @@ -24,25 +24,27 @@ ClusterTemplateWorkflow); ClusterTemplateWorkflow.$inject = [ + '$q', 'horizon.dashboard.container-infra.basePath', 'horizon.app.core.workflow.factory', 'horizon.framework.util.i18n.gettext', + 'horizon.app.core.openstack-service-api.magnum', 'horizon.app.core.openstack-service-api.nova', 'horizon.app.core.openstack-service-api.glance' ]; - function ClusterTemplateWorkflow(basePath, workflowService, gettext, nova, glance) { + function ClusterTemplateWorkflow($q, basePath, workflowService, gettext, magnum, nova, glance) { var workflow = { - init: init + init: init, + update: update }; - function init(action, title) { - var schema, form, model; - var images = [{value:"", name: gettext("Choose an Image")}]; - var nflavors = [{value:"", name: gettext("Choose a Flavor for the Node")}]; - var mflavors = [{value:"", name: gettext("Choose a Flavor for the Master Node")}]; - var keypairs = [{value:"", name: gettext("Choose a Keypair")}]; + var form, model, images, nflavors, mflavors, keypairs, + externalNetworks, fixedNetworks, fixedSubnets; + var fixedSubnetsInitial = gettext("Choose a Private Network at first"); + function init(action, title) { + var schema; var coes = [{value: '', name: gettext("Choose a Container Orchestration Engine")}, {value: "swarm", name: gettext("Docker Swarm")}, {value: "kubernetes", name: gettext("Kubernetes")}, @@ -161,30 +163,15 @@ }, 'external_network_id': { title: gettext('External Network ID'), - type: 'string', - 'x-schema-form': { - type: 'string', - placeholder: gettext( - 'The external Neutron network ID to connect to this cluster template') - } + type: 'string' }, 'fixed_network': { title: gettext('Fixed Network'), - type: 'string', - 'x-schema-form': { - type: 'string', - placeholder: gettext( - 'The private Neutron network name to connect to this cluster template') - } + type: 'string' }, 'fixed_subnet': { title: gettext('Fixed Subnet'), - type: 'string', - 'x-schema-form': { - type: 'string', - placeholder: gettext( - 'The private Neutron subnet name to connect to this cluster template') - } + type: 'string' }, 'dns_nameserver': { title: gettext('DNS'), @@ -393,13 +380,22 @@ }, { key: 'external_network_id', + type: 'select', + titleMap: externalNetworks, required: true }, { - key: 'fixed_network' + key: 'fixed_network', + type: 'select', + titleMap: fixedNetworks, + onChange: function () { + changeFixedNetwork(model); + } }, { - key: 'fixed_subnet' + key: 'fixed_subnet', + type: 'select', + titleMap: fixedSubnets }, { key: 'dns_nameserver' @@ -446,29 +442,6 @@ } ]; - glance.getImages().then(onGetImages); - nova.getFlavors(false, false).then(onGetFlavors); - nova.getKeypairs().then(onGetKeypairs); - - function onGetImages(response) { - angular.forEach(response.data.items, function(item) { - images.push({value: item.name, name: item.name}); - }); - } - - function onGetFlavors(response) { - angular.forEach(response.data.items, function(item) { - nflavors.push({value: item.name, name: item.name}); - mflavors.push({value: item.name, name: item.name}); - }); - } - - function onGetKeypairs(response) { - angular.forEach(response.data.items, function(item) { - keypairs.push({value: item.keypair.name, name: item.keypair.name}); - }); - } - model = { name: "", coe: "", @@ -502,10 +475,95 @@ model: model }; + update(config); return config; } + // called by update.service + function update(config) { + $q.all({ + images: glance.getImages().then(onGetImages), + flavors: nova.getFlavors(false, false).then(onGetFlavors), + keypairs: nova.getKeypairs().then(onGetKeypairs), + networks: magnum.getNetworks().then(onGetNetworks) + }).then(function() { + changeFixedNetwork(config.model, init); + }); + } + + function onGetImages(response) { + images = [{value:"", name: gettext("Choose an Image")}]; + angular.forEach(response.data.items, function(item) { + images.push({value: item.name, name: item.name}); + }); + form[0].tabs[1].items[0].items[0].items[0].titleMap = images; + var deferred = $q.defer(); + deferred.resolve(images); + return deferred.promise; + } + + function onGetFlavors(response) { + nflavors = [{value:"", name: gettext("Choose a Flavor for the Node")}]; + mflavors = [{value:"", name: gettext("Choose a Flavor for the Master Node")}]; + angular.forEach(response.data.items, function(item) { + nflavors.push({value: item.name, name: item.name}); + mflavors.push({value: item.name, name: item.name}); + }); + form[0].tabs[1].items[0].items[0].items[1].titleMap = nflavors; + form[0].tabs[1].items[0].items[1].items[1].titleMap = mflavors; + var deferred = $q.defer(); + deferred.resolve(nflavors); + return deferred.promise; + } + + function onGetKeypairs(response) { + keypairs = [{value:"", name: gettext("Choose a Keypair")}]; + angular.forEach(response.data.items, function(item) { + keypairs.push({value: item.keypair.name, name: item.keypair.name}); + }); + form[0].tabs[1].items[0].items[1].items[0].titleMap = keypairs; + var deferred = $q.defer(); + deferred.resolve(keypairs); + return deferred.promise; + } + + function onGetNetworks(response) { + externalNetworks = [{value:"", name: gettext("Choose a External Network")}]; + fixedNetworks = [{value:"", name: gettext("Choose a Private Network")}]; + angular.forEach(response.data.items, function(item) { + if (item["router:external"]) { + externalNetworks.push({value: item.id, name: item.name}); + } else { + fixedNetworks.push({value: item.id, name: item.name, subnets: item.subnets}); + } + }); + form[0].tabs[2].items[0].items[0].items[4].titleMap = externalNetworks; + form[0].tabs[2].items[0].items[0].items[5].titleMap = fixedNetworks; + var deferred = $q.defer(); + deferred.resolve({ + externalNetworks: externalNetworks, + fixedNetworks: fixedNetworks + }); + return deferred.promise; + } + + function changeFixedNetwork(model) { + if (model.fixed_network) { + fixedSubnets = [{value:"", name: gettext("Choose a Private Subnet")}]; + angular.forEach(fixedNetworks, function(fixed) { + if (fixed.value === model.fixed_network) { + angular.forEach(fixed.subnets, function(subnet) { + fixedSubnets.push({value: subnet.id, name: subnet.name}); + }); + } + }); + } else { + fixedSubnets = [{value:"", name: fixedSubnetsInitial}]; + model.fixed_subnet = ""; + } + form[0].tabs[2].items[0].items[0].items[6].titleMap = fixedSubnets; + } + return workflow; } - })(); diff --git a/magnum_ui/static/dashboard/container-infra/cluster-templates/workflow/workflow.service.spec.js b/magnum_ui/static/dashboard/container-infra/cluster-templates/workflow/workflow.service.spec.js index df41319b..ef01f619 100644 --- a/magnum_ui/static/dashboard/container-infra/cluster-templates/workflow/workflow.service.spec.js +++ b/magnum_ui/static/dashboard/container-infra/cluster-templates/workflow/workflow.service.spec.js @@ -19,7 +19,7 @@ describe('horizon.dashboard.container-infra.cluster-templates.workflow', function() { - var workflow, nova, glance, $q, deferred, keyDeferred; + var workflow, magnum, nova, glance, $q, deferred, keyDeferred; beforeEach(module('horizon.app.core')); beforeEach(module('horizon.framework')); @@ -31,6 +31,7 @@ 'horizon.dashboard.container-infra.cluster-templates.workflow'); nova = $injector.get('horizon.app.core.openstack-service-api.nova'); glance = $injector.get('horizon.app.core.openstack-service-api.glance'); + magnum = $injector.get('horizon.app.core.openstack-service-api.magnum'); deferred = $q.defer(); deferred.resolve({data:{items:{1:{name:1},2:{name:2}}}}); keyDeferred = $q.defer(); @@ -38,7 +39,7 @@ spyOn(glance, 'getImages').and.returnValue(deferred.promise); spyOn(nova, 'getFlavors').and.returnValue(deferred.promise); spyOn(nova, 'getKeypairs').and.returnValue(keyDeferred.promise); - + spyOn(magnum, 'getNetworks').and.returnValue(deferred.promise); })); it('should be init', inject(function($timeout) { diff --git a/magnum_ui/static/dashboard/container-infra/magnum.service.js b/magnum_ui/static/dashboard/container-infra/magnum.service.js index 6e8179c3..4785e2fc 100644 --- a/magnum_ui/static/dashboard/container-infra/magnum.service.js +++ b/magnum_ui/static/dashboard/container-infra/magnum.service.js @@ -43,7 +43,8 @@ deleteClusterTemplates: deleteClusterTemplates, showCertificate: showCertificate, signCertificate: signCertificate, - downloadTextAsFile: downloadTextAsFile + downloadTextAsFile: downloadTextAsFile, + getNetworks: getNetworks }; return service; @@ -180,5 +181,24 @@ a.remove(); }, 0); } + + ////////////////// + // Networks // + ////////////////// + + /** + * @name getNetworks + * @description + * Get a list of networks for a tenant including external and private. + * Also, each network has subnets. + * @returns {Object} An object with property "items". Each item is a network. + */ + function getNetworks() { + return apiService.get('/api/container_infra/networks/') + .error(function () { + toastService.add('error', gettext('Unable to retrieve the networks.')); + }); + } + } }());