diff --git a/gbpui/_1550_gbp_project_add_panel_group.py b/gbpui/_1550_gbp_project_add_panel_group.py index 25d20e1..3a3e2ff 100644 --- a/gbpui/_1550_gbp_project_add_panel_group.py +++ b/gbpui/_1550_gbp_project_add_panel_group.py @@ -17,3 +17,4 @@ PANEL_GROUP_DASHBOARD = 'project' AUTO_DISCOVER_STATIC_FILES = True ADD_ANGULAR_MODULES = ['gbpui', ] +ADD_SCSS_FILES = ['dashboard/gbpui/group-member/group-member.scss'] diff --git a/gbpui/panels/policytargets/restApi.py b/gbpui/panels/policytargets/restApi.py new file mode 100644 index 0000000..fb679b7 --- /dev/null +++ b/gbpui/panels/policytargets/restApi.py @@ -0,0 +1,219 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +import re + +from django.conf import settings +from django import shortcuts +from django.utils.translation import ugettext_lazy as _ +from django.views import generic + +from openstack_dashboard import api +from openstack_dashboard.api.rest import utils as rest_utils + +from horizon import exceptions + +from gbpui import client + +from netaddr import IPAddress +from netaddr import IPNetwork + +import logging + +LOG = logging.getLogger(__name__) + + +class PolicyTargets(generic.View): + # todo: This is a direct port from the old form version; it needs to be + # tested and revised; It may be dodgy and/or redundant because: + # 1) The created regex might be nonsenseical (needs testing) + # 2) The proxy_group_ids might be logical duplication of the third + # third conditional + @staticmethod + def is_proxy_group(policy_target, proxy_group_ids): + if hasattr(settings, 'GBPUI_HIDE_PTG_NAMES_FROM_MEMBER_CREATE'): + regex = "(" + ")|(".join( + settings.GBPUI_HIDE_PTG_NAMES_FROM_MEMBER_CREATE) \ + + ")" + + if re.match(regex, policy_target.get('name')): + return True + + if policy_target.id in proxy_group_ids: + return True + + if policy_target.get('proxied_group_id'): + return True + + return False + + @rest_utils.ajax() + def get(self, request): + policy_targets = client.policy_target_list( + request, tenant_id=request.user.tenant_id + ) + + proxy_group_ids = [pt.get('proxy_group_id') for pt in policy_targets + if pt.get('proxy_group_id')] + + try: + policy_target_objects = [] + + for policy_target in policy_targets: + if not self.is_proxy_group(policy_target, proxy_group_ids): + subnet_objects = [] + + for subnet_id in policy_target.subnets: + try: + subnet = api.neutron.subnet_get(request, subnet_id) + allocation_pool_objects = [] + + allocation_pools = subnet['allocation_pools'] + if allocation_pools: + for allocation_pool in allocation_pools: + allocation_pool_object = { + "start": allocation_pool['start'], + "end": allocation_pool['end'] + } + allocation_pool_objects.append( + allocation_pool_object) + + subnet_object = { + "cidr": subnet['cidr'], + "allocation_pools": allocation_pool_objects + } + subnet_objects.append(subnet_object) + except Exception: + LOG.exception("Unable to retrieve subnet.") + + policy_target_object = { + "id": policy_target.id, + "name_or_id": policy_target.name_or_id, + "subnets": subnet_objects + } + policy_target_objects.append(policy_target_object) + + return rest_utils.JSONResponse(policy_target_objects) + except Exception: + msg = _("Failed to retrieve groups") + LOG.error(msg) + exceptions.handle(request, msg, redirect=shortcuts.redirect) + + +class Members(generic.View): + optional_arguments = [ + 'block_device_mapping', + 'block_device_mapping_v2', + 'availability_zone', + 'admin_pass', 'disk_config', + 'config_drive' + ] + + @rest_utils.ajax() + def post(self, request): + instance_count = request.DATA['instance_count'] + + try: + if instance_count == 1: + self.create_instance(request) + elif instance_count > 1: + for i in range(0, instance_count): + self.create_instance(request, "_" + str(i)) + + except Exception: + instance_name = request.DATA['name'] + + error = _("Unable to launch member %(count)s with name %(name)s") + message = error % { + 'count': instance_count, + 'name': instance_name + } + LOG.exception(message) + + raise rest_utils.AjaxError(400, message) + + return rest_utils.CreatedResponse('/api/nova/servers/%s') + + def create_instance(self, request, suffix=""): + # Instances need to be created one by one, because each instance + # needs to have it's own GBP port + kw = { + 'instance_count': 1 + } + + # Mandatory creation arguments and port creation + try: + instance_name = request.DATA['name'] + suffix + + meta_data, nics = self.create_ports(request, instance_name) + + kw['meta'] = meta_data + kw['nics'] = nics + + args = ( + request, + instance_name, + request.DATA['source_id'], + request.DATA['flavor_id'], + request.DATA['key_name'], + request.DATA['user_data'], + request.DATA['security_groups'], + ) + + except KeyError as e: + raise rest_utils.AjaxError(400, 'Missing required parameter ' + "'%s'" % e.args[0]) + + # Optional creation arguments + for name in self.optional_arguments: + if name in request.DATA: + kw[name] = request.DATA[name] + + return api.nova.server_create(*args, **kw) + + # 1) Missing request.DATA entries get propagated to 'create_instance' as + # KeyError + # 2) All other errors are propagated to 'post' as generic failure Exception + @staticmethod + def create_ports(request, instance_name): + nics = [] + pts = [] + + for policy_target_id in request.DATA["group_policy_targets"]: + policy_target = client.policy_target_get(request, + policy_target_id['id']) + + args = { + 'policy_target_group_id': policy_target.id, + 'name': instance_name[:41] + "_gbpui" + } + + for subnet_id in policy_target.subnets: + subnet = api.neutron.subnet_get(request, subnet_id) + + if 'fixed_ip' in policy_target_id and IPAddress( + policy_target_id['fixed_ip']) in \ + IPNetwork(subnet['cidr']): + args['fixed_ips'] = [{ + 'subnet_id': subnet['id'], + 'ip_address': policy_target_id['fixed_ip'] + }] + + port = client.pt_create(request, **args) + + nics.append({ + 'port-id': port.port_id + }) + pts.append(port.id) + + meta_data = {'pts': ','.join(pts)} + + return meta_data, nics diff --git a/gbpui/panels/policytargets/tables.py b/gbpui/panels/policytargets/tables.py index 11f7dac..d2a938f 100644 --- a/gbpui/panels/policytargets/tables.py +++ b/gbpui/panels/policytargets/tables.py @@ -136,14 +136,30 @@ class ExternalPTGsTable(tables.DataTable): class LaunchVMLink(tables.LinkAction): - name = "launch_vm" + name = "launch_vm-ng" verbose_name = _("Create Member") - classes = ("ajax-modal", "btn-addvm",) + url = "horizon:project:policytargets:policy_targetdetails" + ajax = False + classes = ("btn-launch", ) - def get_link_url(self): - return reverse("horizon:project:policytargets:addvm", - kwargs={'policy_target_id': - self.table.kwargs['policy_target_id']}) + def get_default_attrs(self): + url_kwargs = { + 'policy_target_id': self.table.kwargs['policy_target_id'] + } + url = reverse(self.url, kwargs=url_kwargs) + ngclick = "modal.openLaunchInstanceWizard(" \ + "{ successUrl: '%s'," \ + " defaults: ['%s'] }" \ + ")" % (url, self.table.kwargs['policy_target_id']) + + self.attrs.update({ + 'ng-controller': 'LaunchInstanceModalController as modal', + 'ng-click': ngclick + }) + return super(LaunchVMLink, self).get_default_attrs() + + def get_link_url(self, datum=None): + return "" class RemoveVMLink(tables.DeleteAction): diff --git a/gbpui/panels/policytargets/urls.py b/gbpui/panels/policytargets/urls.py index 5130ba4..b0f7123 100644 --- a/gbpui/panels/policytargets/urls.py +++ b/gbpui/panels/policytargets/urls.py @@ -10,11 +10,12 @@ # License for the specific language governing permissions and limitations # under the License. - from django.conf.urls import url # noqa import views +import restApi + urlpatterns = [ url(r'^$', views.IndexView.as_view(), @@ -37,8 +38,6 @@ urlpatterns = [ url(r'^ext_policy_target/(?P[^/]+)/$', views.ExternalPTGDetailsView.as_view(), name='ext_policy_targetdetails'), - url(r'^addvm/(?P[^/]+)/$', - views.LaunchVMView.as_view(), name='addvm'), url(r'^ext_add_policy_rule_set/(?P[^/]+)/$', views.ExtAddProvidedPRSView.as_view(), name='ext_add_provided_prs'), @@ -66,4 +65,11 @@ urlpatterns = [ url(r'/check_ip_availability', views.check_ip_availability, name='check_ip_availability'), + # Rest APIs for use with AJAX/ANGULARJS calls + url(r'policy_target_groups/$', + restApi.PolicyTargets.as_view(), + name='policy_target_groups'), + url(r'launch_instance/$', + restApi.Members.as_view(), + name='launch_instance'), ] diff --git a/gbpui/panels/policytargets/views.py b/gbpui/panels/policytargets/views.py index 608ad75..8a82f1a 100644 --- a/gbpui/panels/policytargets/views.py +++ b/gbpui/panels/policytargets/views.py @@ -88,14 +88,16 @@ class ExternalPTGDetailsView(tabs.TabbedTableView): return context +''' class LaunchVMView(workflows.WorkflowView): workflow_class = policy_target_workflows.LaunchInstance def get_initial(self): - initial = super(LaunchVMView, self).get_initial() + initial = super( LaunchVMView, self).get_initial() initial['project_id'] = self.request.user.tenant_id initial['user_id'] = self.request.user.id return initial +''' class UpdatePTGView(gbforms.HelpTextModalMixin, diff --git a/gbpui/static/dashboard/css/scspec.css b/gbpui/static/dashboard/css/scspec.css index 82ede00..5497cfb 100644 --- a/gbpui/static/dashboard/css/scspec.css +++ b/gbpui/static/dashboard/css/scspec.css @@ -1,16 +1,16 @@ -#nodeListSortContainer,#nodeListIdContainer{ - width:100%; +#nodeListSortContainer, #nodeListIdContainer { + width: 100%; } #selected_node { - margin-bottom: 1.5em; - counter-reset: v1 0; - background: #edf9ff; - border: 1px solid #c0d9e4; + margin-bottom: 1.5em; + counter-reset: v1 0; + background: #edf9ff; + border: 1px solid #c0d9e4; } #selected_node li { - position: relative; + position: relative; } #selected_node li a.btn:before { @@ -18,87 +18,103 @@ } #selected_node li:before { - content: "Node:" counter(v1); - counter-increment: v1; - display: inline-block; - margin-right: 5px; - background: #555555; - color: #ffffff; - font-size: 90%; - padding: 0px 4px; - vertical-align: middle; - border-radius: 2px; - position: absolute; - left: -4em; + content: "Node:" counter(v1); + counter-increment: v1; + display: inline-block; + margin-right: 5px; + background: #555555; + color: #ffffff; + font-size: 90%; + padding: 0px 4px; + vertical-align: middle; + border-radius: 2px; + position: absolute; + left: -4em; } #selected_node.dragging li:before { - content: "Node:"; - background-color: rgba(102, 102, 102, 0.5); - padding-right: 10px; + content: "Node:"; + background-color: rgba(102, 102, 102, 0.5); + padding-right: 10px; } #selected_node.dragging li.ui-state-highlight:before { - content: ""; - background: transparent; + content: ""; + background: transparent; } + .nodelist { - padding: 6px; - background: #eee; - border: 1px solid #dddddd; - min-height: 2em; - width: auto !important; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; } - .nodelist li { - width: 226px; - list-style-type: none; - margin: 6px auto; - padding: 3px; - background: #ffffff; - border: 1px solid #aaa; - line-height: 18px; - border-radius: 3px; - cursor: move; - padding-left: 23px; - background: #ffffff url('../img/drag.png?93ec7e23f795') no-repeat 11px 50%; } - .nodelist li em { - font-size: 0.5em; - line-height: 1em; - color: #999; - font-style: normal; - margin-left: 0.8em; } - .nodelist li i { - margin-right: 5px; - vertical-align: middle; } - .nodelist li a.btn { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - font-size: 11px; - line-height: 12px; - padding: 2px 5px 3px; - margin-right: 1px; - width: 18px; - text-align: center; - right: 5px; - vertical-align: middle; - float: right; } - .nodelist li a.btn:before { - content: "+"; } - .nodelist li.ui-sortable-helper { - background-color: #def; } - .nodelist li.ui-state-highlight { - border: 1px dotted #cccccc; - background: #efefef; - height: 0.5em; } - .nodelist li:after { - visibility: hidden; - display: block; - font-size: 0; - content: " "; - clear: both; - height: 0; } + padding: 6px; + background: #eee; + border: 1px solid #dddddd; + min-height: 2em; + width: auto !important; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.nodelist li { + width: 226px; + list-style-type: none; + margin: 6px auto; + padding: 3px; + background: #ffffff; + border: 1px solid #aaa; + line-height: 18px; + border-radius: 3px; + cursor: move; + padding-left: 23px; + background: #ffffff url('../img/drag.png?93ec7e23f795') no-repeat 11px 50%; +} +.nodelist li em { + font-size: 0.5em; + line-height: 1em; + color: #999; + font-style: normal; + margin-left: 0.8em; +} + +.nodelist li i { + margin-right: 5px; + vertical-align: middle; +} + +.nodelist li a.btn { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + font-size: 11px; + line-height: 12px; + padding: 2px 5px 3px; + margin-right: 1px; + width: 18px; + text-align: center; + right: 5px; + vertical-align: middle; + float: right; +} + +.nodelist li a.btn:before { + content: "+"; +} + +.nodelist li.ui-sortable-helper { + background-color: #def; +} + +.nodelist li.ui-state-highlight { + border: 1px dotted #cccccc; + background: #efefef; + height: 0.5em; +} + +.nodelist li:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; +} \ No newline at end of file diff --git a/gbpui/static/dashboard/gbpui/gbpui.module.js b/gbpui/static/dashboard/gbpui/gbpui.module.js index c7e453c..ee191f0 100644 --- a/gbpui/static/dashboard/gbpui/gbpui.module.js +++ b/gbpui/static/dashboard/gbpui/gbpui.module.js @@ -13,7 +13,7 @@ */ (function () { angular - .module('gbpui', ['gbpui.transfer-table-bridge']) + .module('gbpui', ['gbpui.transfer-table-bridge', 'gbpui.group-member']) .config(module_config); module_config.$inject = ["$provide","$windowProvider"]; diff --git a/gbpui/static/dashboard/gbpui/group-member/group-member-gbp-api.js b/gbpui/static/dashboard/gbpui/group-member/group-member-gbp-api.js new file mode 100644 index 0000000..afb45b2 --- /dev/null +++ b/gbpui/static/dashboard/gbpui/group-member/group-member-gbp-api.js @@ -0,0 +1,47 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function () { + angular + .module('gbpui.group-member') + .factory('gbpui.group-member.gbp-api', gbpApi); + + gbpApi.$inject = [ + 'horizon.framework.util.http.service', + 'horizon.framework.widgets.toast.service' + ]; + + function gbpApi(apiService, toastService) { + + function getPolicyTargetGroups() { + return apiService.get('project/policytargets/policy_target_groups') + .error(function () { + toastService.add('error', gettext('Unable to retrieve Policy Target Groups')) + }); + } + + function createServer(newServer) { + return apiService + .post('project/policytargets/launch_instance/', newServer) + .error(function () { + toastService.add('error', gettext('Unable to create Instance')) + }) + } + + return { + getPolicyTargetGroups: getPolicyTargetGroups, + createServer: createServer + }; + } +})(); diff --git a/gbpui/static/dashboard/gbpui/group-member/group-member-launch-context.service.js b/gbpui/static/dashboard/gbpui/group-member/group-member-launch-context.service.js new file mode 100644 index 0000000..d6e14c5 --- /dev/null +++ b/gbpui/static/dashboard/gbpui/group-member/group-member-launch-context.service.js @@ -0,0 +1,34 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + + +// Workaround service that allows for the modal launchContext object to be used +// in any controller/service. +(function () { + 'use strict'; + angular + .module('gbpui.group-member') + .factory('gbpui.group-member.launch-context.service', launchContextService); + + + launchContextService.$inject = []; + + function launchContextService() { + return { + launchContext: { + defaults: [] + } + }; + } +})(); \ No newline at end of file diff --git a/gbpui/static/dashboard/gbpui/group-member/group-member-launch-instance-model.patcher.js b/gbpui/static/dashboard/gbpui/group-member/group-member-launch-instance-model.patcher.js new file mode 100644 index 0000000..8dcb312 --- /dev/null +++ b/gbpui/static/dashboard/gbpui/group-member/group-member-launch-instance-model.patcher.js @@ -0,0 +1,71 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function () { + 'use strict'; + + angular + .module('gbpui.group-member') + .config(launchInstanceModelPatcher); + + launchInstanceModelPatcher.$inject = ['$provide']; + + function launchInstanceModelPatcher($provide) { + $provide.decorator( + 'launchInstanceModel', + ['$delegate', 'gbpui.group-member.gbp-api', '$q', + 'gbpui.group-member.launch-context.service', + function($delegate, gbpApi, $q, launchContextService) { + + var createInstance = $delegate.createInstance; + $delegate.createInstance = function() { + // This is a workaround for new instance initalization + // inside 'launchInstanceModel' done by functions not + // exposed by the $delegate + $delegate.newInstanceSpec.group_policy_targets = + $delegate.allocated_group_policy_targets; + return createInstance(); + }; + + $delegate.group_policy_targets = []; + $delegate.allocated_group_policy_targets = []; + + var initialize = $delegate.initialize; + + $delegate.initialize = function(deep) { + $delegate.group_policy_targets.length = 0; + $delegate.allocated_group_policy_targets.length = 0; + + $q.all([gbpApi.getPolicyTargetGroups().then(function(data){ + var defaults = + launchContextService.launchContext.defaults; + + angular.forEach(data.data, function(value, index) { + $delegate.group_policy_targets.push(value); + + if(defaults.indexOf(value.id) > -1) { + $delegate.allocated_group_policy_targets + .push(value); + } + }); + }), initialize(deep) + ]); + }; + + return $delegate + }] + ) + } + +})(); \ No newline at end of file diff --git a/gbpui/static/dashboard/gbpui/group-member/group-member-launch-instance-service.patcher.js b/gbpui/static/dashboard/gbpui/group-member/group-member-launch-instance-service.patcher.js new file mode 100644 index 0000000..5a4c035 --- /dev/null +++ b/gbpui/static/dashboard/gbpui/group-member/group-member-launch-instance-service.patcher.js @@ -0,0 +1,41 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function () { + 'use strict'; + + angular.module('gbpui.group-member') + .config(launchInstanceServicePatcher); + + launchInstanceServicePatcher.$inject = ['$provide']; + + function launchInstanceServicePatcher($provide) { + $provide.decorator( + 'horizon.dashboard.project.workflow.launch-instance.modal.service', + ['$delegate', 'gbpui.group-member.launch-context.service', function( + $delegate, launchContextService + ) { + + var open = $delegate.open; + $delegate.open = function(launchContext) { + angular.extend(launchContextService.launchContext, + launchContext); + return open(launchContext); + }; + + return $delegate + }] + ); + } +})(); \ No newline at end of file diff --git a/gbpui/static/dashboard/gbpui/group-member/group-member-launch-instance.patcher.js b/gbpui/static/dashboard/gbpui/group-member/group-member-launch-instance.patcher.js new file mode 100644 index 0000000..577aa7d --- /dev/null +++ b/gbpui/static/dashboard/gbpui/group-member/group-member-launch-instance.patcher.js @@ -0,0 +1,65 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function () { + 'use strict'; + + angular + .module('gbpui.group-member') + .config(launchInstancePatcher); + + launchInstancePatcher.$inject = ["$provide", 'gbpui.basePath']; + + function launchInstancePatcher($provide, basePath) { + $provide.decorator( + 'horizon.dashboard.project.workflow.launch-instance.workflow', + ["$delegate", 'horizon.app.core.workflow.factory', + function($delegate, dashboardWorkflow) + { + var steps = $delegate.steps; + var gbstep = { + id: 'gbp', + title: gettext('GBP'), + templateUrl: basePath + 'group-member/group-member.html', + helpUrl: basePath + 'group-member/group-member.help.html', + formName: 'gbpForm' + }; + + // Finds and replaces the Network and Port wizard pages with + // the GBP wizard page + var networkIndex = -1; + var portIndex = -1; + angular.forEach(steps, function (step) { + if(step.id == 'networks') { + networkIndex = steps.indexOf(step) + } else if(step.id == 'ports') { + portIndex = steps.indexOf(step); + } + }); + + if(networkIndex > -1) { + steps.splice(networkIndex, 1, gbstep); + } + + if(portIndex > -1) { + steps.splice(portIndex, 1); + } + + var result = dashboardWorkflow($delegate); + return result; + }] + ); + } + +})(); \ No newline at end of file diff --git a/gbpui/static/dashboard/gbpui/group-member/group-member-nova-api.patcher.js b/gbpui/static/dashboard/gbpui/group-member/group-member-nova-api.patcher.js new file mode 100644 index 0000000..ae50d5f --- /dev/null +++ b/gbpui/static/dashboard/gbpui/group-member/group-member-nova-api.patcher.js @@ -0,0 +1,39 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function () { + 'use strict'; + + angular + .module('gbpui.group-member') + .config(novaApiPatcher); + + novaApiPatcher.$inject = ['$provide']; + + function novaApiPatcher($provide) { + $provide.decorator( + 'horizon.app.core.openstack-service-api.nova', + ['$delegate', 'gbpui.group-member.gbp-api', + function($delegate, gbpApi) { + var createServer = $delegate.createServer; + + $delegate.createServer = function(newServer) { + return gbpApi.createServer(newServer); + }; + + return $delegate + }] + ); + } +})(); \ No newline at end of file diff --git a/gbpui/static/dashboard/gbpui/group-member/group-member.controller.js b/gbpui/static/dashboard/gbpui/group-member/group-member.controller.js new file mode 100644 index 0000000..dcffb8f --- /dev/null +++ b/gbpui/static/dashboard/gbpui/group-member/group-member.controller.js @@ -0,0 +1,64 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +(function() { + angular + .module('gbpui.group-member') + .controller('GBPController', gbpController); + + + gbpController.$inject = [ + '$scope', + 'horizon.dashboard.project.workflow.launch-instance.modal.service', + 'gbpui.group-member.launch-context.service' + ]; + + function gbpController( + $scope, launchInstanceModalService, launchContextService) + { + var ctrl = this; + + ctrl.launchContext = launchContextService.launchContext; + ctrl.inputIPs = { + + }; + + ctrl.tableHelp = { + noneAllocText: gettext('Select a group-member from the table below'), + availHelpText: gettext('Select one or more groups'), + noneAvailText: gettext('No groups available'), + }; + + ctrl.tableLimits = { + maxAllocation: -1 + }; + + ctrl.addFixedIp = function(row) { + var ipAddress = ctrl.inputIPs[row.id]; + row.fixed_ip = ipAddress; + }; + + ctrl.removeFixedIp = function(row) { + delete row['fixed_ip']; + }; + + ctrl.tableData = { + available: $scope.model.group_policy_targets, + allocated: $scope.model.allocated_group_policy_targets, + displayedAvailable: [], + displayedAllocated: [], + minItems: 1 + }; + } +})(); \ No newline at end of file diff --git a/gbpui/static/dashboard/gbpui/group-member/group-member.help.html b/gbpui/static/dashboard/gbpui/group-member/group-member.help.html new file mode 100644 index 0000000..35e2dcd --- /dev/null +++ b/gbpui/static/dashboard/gbpui/group-member/group-member.help.html @@ -0,0 +1,7 @@ +
+

+ Instances belonging to network groups will automatically have network + policy rule sets applied to them as defined in the individual network + groups. +

+
diff --git a/gbpui/static/dashboard/gbpui/group-member/group-member.html b/gbpui/static/dashboard/gbpui/group-member/group-member.html new file mode 100644 index 0000000..a1c35cb --- /dev/null +++ b/gbpui/static/dashboard/gbpui/group-member/group-member.html @@ -0,0 +1,187 @@ +
+

Select one or more policy groups for the new instance.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Groups
+
+ {$ ::trCtrl.helpText.noneAllocText $} +
+
+ + {$ row.name_or_id $} ({$ row.fixed_ip $}) + + + + + + +
+
+
+
+ Subnets +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ CIDR + + Allocation pools +
StartEnd
+ {$ subnet.cidr $} + + {$ + subnet.allocation_pools.length + > 0 ? + subnet.allocation_pools[0].start + : '' $} + + {$ + subnet.allocation_pools.length + > 0 ? + subnet.allocation_pools[0].end + : '' $} +
+ {$ allocation_pool.start $} + + {$ allocation_pool.end $} +
+ +
+
+
+
+ Fixed IP +
+
+
+
+
+
+ + + +
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + +
Groups
+
+ {$ ::trCtrl.helpText.noneAvailText $} +
+
{$ row.name_or_id $} + + + + + + + + +
+ + +
+
+
\ No newline at end of file diff --git a/gbpui/static/dashboard/gbpui/group-member/group-member.module.js b/gbpui/static/dashboard/gbpui/group-member/group-member.module.js new file mode 100644 index 0000000..cbd1e60 --- /dev/null +++ b/gbpui/static/dashboard/gbpui/group-member/group-member.module.js @@ -0,0 +1,25 @@ +/** + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +(function(){ + angular + .module('gbpui.group-member', []) + .config(module_config); + + module_config.$inject = ["$provide","$windowProvider"]; + + function module_config($provide, $windowProvider) { + var path = $windowProvider.$get().STATIC_URL + 'dashboard/gbpui/'; + $provide.constant('gbpui.basePath', path); + } +})(); \ No newline at end of file diff --git a/gbpui/static/dashboard/gbpui/group-member/group-member.scss b/gbpui/static/dashboard/gbpui/group-member/group-member.scss new file mode 100644 index 0000000..0cfd94d --- /dev/null +++ b/gbpui/static/dashboard/gbpui/group-member/group-member.scss @@ -0,0 +1,12 @@ +.subnet-table { + width: 100%; +} + +.subnet-table td, .subnet-table th { + background-color: white; + text-align: center; +} + +.subnet-table > thead > tr > th { + padding:2px; +} diff --git a/gbpui/static/dashboard/gbpui/transfer-table-bridge/transfer-table.module.js b/gbpui/static/dashboard/gbpui/transfer-table-bridge/transfer-table-bridge.module.js similarity index 100% rename from gbpui/static/dashboard/gbpui/transfer-table-bridge/transfer-table.module.js rename to gbpui/static/dashboard/gbpui/transfer-table-bridge/transfer-table-bridge.module.js