Unifies the compute and GBP member launch modals
Modifies the create member button in group details to use the compute launch instance AngularJS based launch dialog. The dialog itself is changed through the Angular API to replace standard networking pages with a group setting page. Change-Id: Ib9f5b601939ed9e8258e4eab2f2180efc98378ae Closes-Bug: 1582457
This commit is contained in:
parent
84cac65360
commit
d86c6ea3cc
|
@ -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']
|
||||
|
|
|
@ -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
|
|
@ -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):
|
||||
|
|
|
@ -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<ext_policy_target_id>[^/]+)/$',
|
||||
views.ExternalPTGDetailsView.as_view(),
|
||||
name='ext_policy_targetdetails'),
|
||||
url(r'^addvm/(?P<policy_target_id>[^/]+)/$',
|
||||
views.LaunchVMView.as_view(), name='addvm'),
|
||||
url(r'^ext_add_policy_rule_set/(?P<ext_policy_target_id>[^/]+)/$',
|
||||
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'),
|
||||
]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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"];
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -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: []
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -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
|
||||
}]
|
||||
)
|
||||
}
|
||||
|
||||
})();
|
|
@ -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
|
||||
}]
|
||||
);
|
||||
}
|
||||
})();
|
|
@ -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;
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
})();
|
|
@ -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
|
||||
}]
|
||||
);
|
||||
}
|
||||
})();
|
|
@ -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
|
||||
};
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,7 @@
|
|||
<div>
|
||||
<p translate>
|
||||
Instances belonging to network groups will automatically have network
|
||||
policy rule sets applied to them as defined in the individual network
|
||||
groups.
|
||||
</p>
|
||||
</div>
|
|
@ -0,0 +1,187 @@
|
|||
<div ng-controller="GBPController as ctrl">
|
||||
<p class="step-description" translate>Select one or more policy groups for the new instance.</p>
|
||||
|
||||
<transfer-table tr-model="ctrl.tableData" limits="ctrl.tableLimits"
|
||||
help-text="ctrl.tableHelp">
|
||||
<allocated ng-model="ctrl.tableData.allocated.length"
|
||||
validate-number-min="{$ ctrl.tableData.minItems $}">
|
||||
<table st-table="ctrl.tableData.allocated"
|
||||
hz-table
|
||||
class="table table-striped table-rsp table-detail">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Groups</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr ng-if="ctrl.tableData.allocated.length === 0">
|
||||
<td colspan="8">
|
||||
<div class="no-rows-help">
|
||||
{$ ::trCtrl.helpText.noneAllocText $}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr ng-repeat-start="row in ctrl.tableData.allocated track by row.id">
|
||||
<td class="expander">
|
||||
<span class="fa fa-chevron-right" hz-expand-detail
|
||||
title="{$ 'Click to see more details'|translate $}"></span>
|
||||
</td>
|
||||
<td>{$ row.name_or_id $} <span ng-if="row.fixed_ip">({$ row.fixed_ip $})</span>
|
||||
</td>
|
||||
<td class="actions_column">
|
||||
<action-list>
|
||||
<action action-classes="'btn btn-default'"
|
||||
callback="trCtrl.deallocate"
|
||||
item="row">
|
||||
<span class="fa fa-arrow-down"></span>
|
||||
</action>
|
||||
</action-list>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr ng-repeat-end class="detail-row">
|
||||
<td colspan="9" class="detail">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<span class="ng-scope">Subnets</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table class="table table-bordered text-center subnet-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowspan="2">
|
||||
CIDR
|
||||
</th>
|
||||
<th colspan="2">
|
||||
Allocation pools
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Start</th>
|
||||
<th>End</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat-start="subnet in row.subnets">
|
||||
<td>
|
||||
{$ subnet.cidr $}
|
||||
</td>
|
||||
<td>
|
||||
{$
|
||||
subnet.allocation_pools.length
|
||||
> 0 ?
|
||||
subnet.allocation_pools[0].start
|
||||
: '' $}
|
||||
</td>
|
||||
<td>
|
||||
{$
|
||||
subnet.allocation_pools.length
|
||||
> 0 ?
|
||||
subnet.allocation_pools[0].end
|
||||
: '' $}
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat="allocation_pool in subnet.allocation_pools | limitTo: (1 - subnet.allocation_pools.length)">
|
||||
<td></td>
|
||||
<td>
|
||||
{$ allocation_pool.start $}
|
||||
</td>
|
||||
<td>
|
||||
{$ allocation_pool.end $}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr ng-if="0" ng-repeat-end>
|
||||
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<span class="ng-scope">Fixed IP</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<input class="form-control"
|
||||
type="text"
|
||||
ng-model="ctrl.inputIPs[row.id]">
|
||||
<input class="form-control"
|
||||
type="button" value="Set"
|
||||
ng-click="ctrl.addFixedIp(row)">
|
||||
<input class="form-control"
|
||||
type="button" value="Unset"
|
||||
ng-click="ctrl.removeFixedIp(row)">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</allocated>
|
||||
<available>
|
||||
<table st-table="ctrl.tableData.available"
|
||||
hz-table
|
||||
class="table table-striped table-rsp table-detail">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Groups</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-if="trCtrl.numAvailable() === 0">
|
||||
<td colspan="8">
|
||||
<div class="no-rows-help">
|
||||
{$ ::trCtrl.helpText.noneAvailText $}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr ng-repeat-start="row in ctrl.tableData.available track by row.id"
|
||||
ng-if="!trCtrl.allocatedIds[row.id]">
|
||||
<td>{$ row.name_or_id $}</td>
|
||||
<td class="actions_column">
|
||||
<action-list button-tooltip="row.warningMessage"
|
||||
bt-model="ctrl.tooltipModel"
|
||||
bt-disabled="!row.disabled"
|
||||
warning-classes="'invalid'">
|
||||
<notifications>
|
||||
<span class="fa fa-exclamation-circle invalid"
|
||||
ng-show="row.disabled"></span>
|
||||
</notifications>
|
||||
<action action-classes="'btn btn-default'"
|
||||
callback="trCtrl.allocate"
|
||||
item="row"
|
||||
disabled="row.disabled">
|
||||
<span class="fa fa-arrow-up"></span>
|
||||
</action>
|
||||
</action-list>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat-end class="detail-row">
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
</available>
|
||||
</transfer-table>
|
||||
</div>
|
|
@ -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);
|
||||
}
|
||||
})();
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue