Add support for Endpoint Group

Current VPNaaS is lacking of endpoint group interaction and
new way of IPsec Site Connection creating with endpoint group.
This patch filed the gap.

Co-Authored-By: Akihiro Motoki <amotoki@gmail.com>
Change-Id: Ibdeabd667e00617c7c4d3cee611fa75ac2877d9b
Closes-Bug: 1704066
This commit is contained in:
Cao Xuan Hoang 2017-07-13 13:57:31 +07:00
parent 5092245c5a
commit 9f9adbb83f
18 changed files with 906 additions and 79 deletions

View File

@ -40,6 +40,10 @@ class VPNService(neutron.NeutronAPIDictWrapper):
"""Wrapper for neutron VPNService."""
class EndpointGroup(neutron.NeutronAPIDictWrapper):
"""Wrapper for neutron Endpoint Group."""
@profiler.trace
def vpnservice_create(request, **kwargs):
"""Create VPNService
@ -55,9 +59,11 @@ def vpnservice_create(request, **kwargs):
{'admin_state_up': kwargs['admin_state_up'],
'name': kwargs['name'],
'description': kwargs['description'],
'router_id': kwargs['router_id'],
'subnet_id': kwargs['subnet_id']}
'router_id': kwargs['router_id']
}
}
if kwargs.get('subnet_id'):
body['vpnservice']['subnet_id'] = kwargs['subnet_id']
vpnservice = neutronclient(request).create_vpnservice(body).get(
'vpnservice')
return VPNService(vpnservice)
@ -65,7 +71,8 @@ def vpnservice_create(request, **kwargs):
@profiler.trace
def vpnservice_list(request, **kwargs):
return _vpnservice_list(request, expand_subnet=True, expand_router=True,
return _vpnservice_list(request, expand_subnet=True,
expand_router=True,
expand_conns=True, **kwargs)
@ -77,7 +84,8 @@ def _vpnservice_list(request, expand_subnet=False, expand_router=False,
subnets = neutron.subnet_list(request)
subnet_dict = OrderedDict((s.id, s) for s in subnets)
for s in vpnservices:
s['subnet_name'] = subnet_dict.get(s['subnet_id']).cidr
if s.get('subnet_id'):
s['subnet_name'] = subnet_dict.get(s['subnet_id']).cidr
if expand_router:
routers = neutron.router_list(request)
router_dict = OrderedDict((r.id, r) for r in routers)
@ -101,9 +109,10 @@ def _vpnservice_get(request, vpnservice_id, expand_subnet=False,
expand_router=False, expand_conns=False):
vpnservice = neutronclient(request).show_vpnservice(vpnservice_id).get(
'vpnservice')
if expand_subnet:
vpnservice['subnet'] = neutron.subnet_get(
request, vpnservice['subnet_id'])
if expand_subnet and ('subnet_id' in vpnservice):
if vpnservice['subnet_id'] is not None:
vpnservice['subnet'] = neutron.subnet_get(
request, vpnservice['subnet_id'])
if expand_router:
vpnservice['router'] = neutron.router_get(
request, vpnservice['router_id'])
@ -126,6 +135,74 @@ def vpnservice_delete(request, vpnservice_id):
neutronclient(request).delete_vpnservice(vpnservice_id)
@profiler.trace
def endpointgroup_create(request, **kwargs):
"""Create Endpoint Group
:param request: request context
:param name: name for Endpoint Group
:param description: description for Endpoint Group
:param type: type of Endpoint Group
:param endpoints: endpoint(s) of Endpoint Group
"""
body = {'endpoint_group':
{'name': kwargs['name'],
'description': kwargs['description'],
'type': kwargs['type'],
'endpoints': kwargs['endpoints']}
}
endpointgroup = neutronclient(request).create_endpoint_group(body).get(
'endpoint_group')
return EndpointGroup(endpointgroup)
@profiler.trace
def endpointgroup_list(request, **kwargs):
return _endpointgroup_list(request, expand_conns=True, **kwargs)
def _endpointgroup_list(request, expand_conns=False, **kwargs):
endpointgroups = neutronclient(request).list_endpoint_groups(
**kwargs).get('endpoint_groups')
if expand_conns:
ipsecsiteconns = _ipsecsiteconnection_list(request)
for g in endpointgroups:
g['ipsecsiteconns'] = [
c.id for c in ipsecsiteconns
if (c.get('local_ep_group_id') == g['id'] or
c.get('peer_ep_group_id') == g['id'])]
return [EndpointGroup(v) for v in endpointgroups]
@profiler.trace
def endpointgroup_get(request, endpoint_group_id):
return _endpointgroup_get(request, endpoint_group_id, expand_conns=True)
def _endpointgroup_get(request, endpoint_group_id, expand_conns=False):
endpointgroup = neutronclient(request).show_endpoint_group(
endpoint_group_id).get('endpoint_group')
if expand_conns:
ipsecsiteconns = _ipsecsiteconnection_list(request)
endpointgroup['ipsecsiteconns'] = [
c for c in ipsecsiteconns
if (c.get('local_ep_group_id') == endpointgroup['id'] or
c.get('peer_ep_group_id') == endpointgroup['id'])]
return EndpointGroup(endpointgroup)
@profiler.trace
def endpointgroup_update(request, endpoint_group_id, **kwargs):
endpointgroup = neutronclient(request).update_endpoint_group(
endpoint_group_id, kwargs).get('endpoint_group')
return EndpointGroup(endpointgroup)
@profiler.trace
def endpointgroup_delete(request, endpoint_group_id):
neutronclient(request).delete_endpoint_group(endpoint_group_id)
@profiler.trace
def ikepolicy_create(request, **kwargs):
"""Create IKEPolicy
@ -290,23 +367,28 @@ def ipsecsiteconnection_create(request, **kwargs):
:param vpnservice_id: VPNService associated with this connection
:param admin_state_up: admin state (default on)
"""
body = {'ipsec_site_connection':
{'name': kwargs['name'],
'description': kwargs['description'],
'dpd': kwargs['dpd'],
'ikepolicy_id': kwargs['ikepolicy_id'],
'initiator': kwargs['initiator'],
'ipsecpolicy_id': kwargs['ipsecpolicy_id'],
'mtu': kwargs['mtu'],
'peer_address': kwargs['peer_address'],
'peer_cidrs': kwargs['peer_cidrs'],
'peer_id': kwargs['peer_id'],
'psk': kwargs['psk'],
'vpnservice_id': kwargs['vpnservice_id'],
'admin_state_up': kwargs['admin_state_up']}
}
body = {
'name': kwargs['name'],
'description': kwargs['description'],
'dpd': kwargs['dpd'],
'ikepolicy_id': kwargs['ikepolicy_id'],
'initiator': kwargs['initiator'],
'ipsecpolicy_id': kwargs['ipsecpolicy_id'],
'mtu': kwargs['mtu'],
'peer_address': kwargs['peer_address'],
'peer_id': kwargs['peer_id'],
'psk': kwargs['psk'],
'vpnservice_id': kwargs['vpnservice_id'],
'admin_state_up': kwargs['admin_state_up']
}
cidrs = kwargs.get('peer_cidrs', [])
if not cidrs:
body['local_ep_group_id'] = kwargs['local_ep_group_id']
body['peer_ep_group_id'] = kwargs['peer_ep_group_id']
else:
body['peer_cidrs'] = kwargs['peer_cidrs']
ipsecsiteconnection = neutronclient(request).create_ipsec_site_connection(
body).get('ipsec_site_connection')
{'ipsec_site_connection': body}).get('ipsec_site_connection')
return IPSecSiteConnection(ipsecsiteconnection)

View File

@ -59,6 +59,41 @@ class UpdateVPNService(forms.SelfHandlingForm):
exceptions.handle(request, msg, redirect=redirect)
class UpdateEndpointGroup(forms.SelfHandlingForm):
name = forms.CharField(
max_length=80,
label=_("Name"),
required=False)
endpoint_group_id = forms.CharField(
label=_("ID"),
widget=forms.TextInput(attrs={'readonly': 'readonly'}))
description = forms.CharField(
required=False,
max_length=80,
label=_("Description"))
failure_url = 'horizon:project:vpn:index'
def handle(self, request, context):
try:
data = {'endpoint_group':
{'name': context['name'],
'description': context['description']}
}
endpointgroup = api_vpn.endpointgroup_update(
request, context['endpoint_group_id'], **data)
msg = (_('Endpoint Group %s was successfully updated.')
% context['name'])
messages.success(request, msg)
return endpointgroup
except Exception as e:
LOG.info('Failed to update Endpint Group %(id)s: %(exc)s',
{'id': context['endpoint_group_id'], 'exc': e})
msg = _('Failed to update Endpint Group %s') % context['name']
redirect = reverse(self.failure_url)
exceptions.handle(request, msg, redirect=redirect)
class UpdateIKEPolicy(forms.SelfHandlingForm):
name = forms.CharField(max_length=80, label=_("Name"), required=False)
ikepolicy_id = forms.CharField(
@ -236,6 +271,7 @@ class UpdateIPSecSiteConnection(forms.SelfHandlingForm):
version=forms.IPv4 | forms.IPv6,
mask=False)
peer_cidrs = forms.MultiIPField(
required=False,
label=_("Remote peer subnet(s)"),
help_text=_("Remote peer subnet(s) address(es) "
"with mask(s) in CIDR format "
@ -243,6 +279,16 @@ class UpdateIPSecSiteConnection(forms.SelfHandlingForm):
"(e.g. 20.1.0.0/24, 21.1.0.0/24)"),
version=forms.IPv4 | forms.IPv6,
mask=True)
local_ep_group_id = forms.CharField(
required=False,
label=_("Local Endpoint Group(s)"),
help_text=_("IPsec connection validation requires "
"that local endpoints are subnets"))
peer_ep_group_id = forms.CharField(
required=False,
label=_("Peer Endpoint Group(s)"),
help_text=_("IPSec connection validation requires "
"that peer endpoints are CIDRs"))
psk = forms.CharField(
widget=forms.PasswordInput(render_value=True),
max_length=80, label=_("Pre-Shared Key (PSK) string"))
@ -293,23 +339,29 @@ class UpdateIPSecSiteConnection(forms.SelfHandlingForm):
def handle(self, request, context):
try:
data = {'ipsec_site_connection':
{'name': context['name'],
'description': context['description'],
'peer_address': context['peer_address'],
'peer_id': context['peer_id'],
'peer_cidrs': context[
'peer_cidrs'].replace(" ", "").split(","),
'psk': context['psk'],
'mtu': context['mtu'],
'dpd': {'action': context['dpd_action'],
'interval': context['dpd_interval'],
'timeout': context['dpd_timeout']},
'initiator': context['initiator'],
'admin_state_up': context['admin_state_up'],
}}
data = {
'name': context['name'],
'description': context['description'],
'peer_address': context['peer_address'],
'peer_id': context['peer_id'],
'psk': context['psk'],
'mtu': context['mtu'],
'dpd': {'action': context['dpd_action'],
'interval': context['dpd_interval'],
'timeout': context['dpd_timeout']},
'initiator': context['initiator'],
'admin_state_up': context['admin_state_up']
}
if not context['peer_cidrs']:
data['local_ep_group_id'] = context['local_ep_group_id']
data['peer_ep_group_id'] = context['peer_ep_group_id']
else:
cidrs = context['peer_cidrs']
data['peer_cidrs'] = [cidr.strip() for cidr in cidrs.split(',')
if cidr.strip()]
ipsecsiteconnection = api_vpn.ipsecsiteconnection_update(
request, context['ipsecsiteconnection_id'], **data)
request, context['ipsecsiteconnection_id'],
ipsec_site_connection=data)
msg = (_('IPSec Site Connection %s was successfully updated.')
% context['name'])
messages.success(request, msg)

View File

@ -57,6 +57,15 @@ class AddVPNServiceLink(tables.LinkAction):
policy_rules = (("network", "create_vpnservice"),)
class AddEndpointGroupLink(tables.LinkAction):
name = "addendpointgroup"
verbose_name = _("Add Endpoint Group")
url = "horizon:project:vpn:addendpointgroup"
classes = ("ajax-modal",)
icon = "plus"
policy_rules = (("network", "create_endpointgroup"),)
class AddIPSecSiteConnectionLink(tables.LinkAction):
name = "addipsecsiteconnection"
verbose_name = _("Add IPSec Site Connection")
@ -99,6 +108,34 @@ class DeleteVPNServiceLink(policy.PolicyTargetMixin, tables.DeleteAction):
request, _('Unable to delete VPN Service. %s') % e)
class DeleteEndpointGroupLink(policy.PolicyTargetMixin, tables.DeleteAction):
name = "deleteendpointgroup"
policy_rules = (("network", "delete_endpointgroup"),)
@staticmethod
def action_present(count):
return ungettext_lazy(
u"Delete Endpoint Group",
u"Delete Endpoint Groups",
count
)
@staticmethod
def action_past(count):
return ungettext_lazy(
u"Scheduled deletion of Endpoint Group",
u"Scheduled deletion of Endpoint Groups",
count
)
def delete(self, request, obj_id):
try:
api_vpn.endpointgroup_delete(request, obj_id)
except Exception as e:
exceptions.handle(
request, _('Unable to delete Endpoint Group. %s') % e)
class DeleteIKEPolicyLink(policy.PolicyTargetMixin, tables.DeleteAction):
name = "deleteikepolicy"
policy_rules = (("network", "delete_ikepolicy"),)
@ -210,6 +247,17 @@ class UpdateVPNServiceLink(tables.LinkAction):
return False
class UpdateEndpointGroupLink(tables.LinkAction):
name = "updateendpointgroup"
verbose_name = _("Edit Endpoint Group")
classes = ("ajax-modal", "btn-update",)
policy_rules = (("network", "update_endpointgroup"),)
def get_link_url(self, endpoint_group):
return reverse("horizon:project:vpn:update_endpointgroup",
kwargs={'endpoint_group_id': endpoint_group.id})
class UpdateIKEPolicyLink(tables.LinkAction):
name = "updateikepolicy"
verbose_name = _("Edit IKE Policy")
@ -355,13 +403,21 @@ def get_local_ips(vpn):
return template.loader.render_to_string(template_name, context)
def get_subnet_name(vpn):
try:
return vpn.subnet_name
except AttributeError:
return _("-")
class UpdateVPNServiceRow(tables.Row):
ajax = True
def get_data(self, request, vpn_id):
vpn = api_vpn.vpnservice_get(request, vpn_id)
vpn.router_name = vpn['router'].get('name', vpn['router_id'])
vpn.subnet_name = vpn['subnet'].get('cidr', vpn['subnet_id'])
if 'subnet' in vpn:
vpn.subnet_name = vpn['subnet'].get('cidr', vpn['subnet_id'])
return vpn
@ -384,7 +440,7 @@ class VPNServicesTable(tables.DataTable):
description = tables.Column('description', verbose_name=_('Description'))
local_ips = tables.Column(get_local_ips,
verbose_name=_("Local Side Public IPs"))
subnet_name = tables.Column('subnet_name', verbose_name=_('Subnet'))
subnet_name = tables.Column(get_subnet_name, verbose_name=_('Subnet'))
router_name = tables.Column('router_name', verbose_name=_('Router'))
status = tables.Column("status",
verbose_name=_("Status"),
@ -406,6 +462,40 @@ class VPNServicesTable(tables.DataTable):
row_actions = (UpdateVPNServiceLink, DeleteVPNServiceLink)
class EndpointGroupFilterAction(tables.FilterAction):
name = 'endpointgroups_project'
filter_type = 'server'
filter_choices = (
('name', _("Name ="), True),
('type', _("Type ="), True),
('endpoints', _("Endpoints ="), True),
)
def _get_endpoints(epg):
return ', '.join(epg.endpoints)
class EndpointGroupTable(tables.DataTable):
id = tables.Column('id', hidden=True)
name = tables.Column("name_or_id", verbose_name=_('Name'),
link="horizon:project:vpn:endpointgroupdetails")
description = tables.Column('description', verbose_name=_('Description'))
type = tables.Column('type', verbose_name=_('Type'))
endpoints = tables.Column(_get_endpoints, verbose_name=_('Endpoints'))
class Meta(object):
name = "endpointgroupstable"
verbose_name = _("Endpoint Groups")
table_actions = (AddEndpointGroupLink,
DeleteEndpointGroupLink,
EndpointGroupFilterAction)
row_actions = (UpdateEndpointGroupLink, DeleteEndpointGroupLink)
def get_object_display(self, endpoitgroup):
return endpoitgroup.name_or_id
class PoliciesFilterAction(tables.FilterAction):
name = 'filter_project_IKEPolicies'
filter_type = 'server'

View File

@ -114,6 +114,32 @@ class VPNServicesTab(tabs.TableTab, htables.DataTableView):
return super(VPNServicesTab, self).get_filters()
class EndpointGroupTab(tabs.TableTab, htables.DataTableView):
table_classes = (tables.EndpointGroupTable,)
name = _("Endpoint Groups")
slug = "endpointgroups"
template_name = ("horizon/common/_detail_table.html")
def get_endpointgroupstable_data(self):
try:
filters = self.get_filters()
tenant_id = self.request.user.tenant_id
endpointgroups = api_vpn.endpointgroup_list(
self.tab_group.request, tenant_id=tenant_id, **filters)
except Exception:
endpointgroups = []
exceptions.handle(self.tab_group.request,
_('Unable to retrieve endpoint group list.'))
return endpointgroups
def get_filters(self):
self.table = self._tables['endpointgroupstable']
self.handle_server_filter(self.request, table=self.table)
self.update_server_filter_action(self.request, table=self.table)
return super(EndpointGroupTab, self).get_filters()
class IKEPoliciesTab(tabs.TableTab, htables.DataTableView):
table_classes = (tables.IKEPoliciesTable,)
name = _("IKE Policies")
@ -169,7 +195,8 @@ class IPSecPoliciesTab(tabs.TableTab, htables.DataTableView):
class VPNTabs(tabs.TabGroup):
slug = "vpntabs"
tabs = (IKEPoliciesTab, IPSecPoliciesTab,
VPNServicesTab, IPSecSiteConnectionsTab,)
VPNServicesTab, EndpointGroupTab,
IPSecSiteConnectionsTab,)
sticky = True
@ -218,6 +245,21 @@ class VPNServiceDetailsTabs(tabs.TabGroup):
tabs = (VPNServiceDetailsTab,)
class EndpointGroupDetailsTab(tabs.Tab):
name = _("Endpoint Groups Details")
slug = "endpointgroupdetails"
template_name = "project/vpn/_endpointgroup_details.html"
def get_context_data(self, request):
endpointgroup = self.tab_group.kwargs['endpointgroup']
return {'endpointgroup': endpointgroup}
class EndpointGroupDetailsTabs(tabs.TabGroup):
slug = "endpointgrouptabs"
tabs = (EndpointGroupDetailsTab,)
class IPSecSiteConnectionDetailsTab(tabs.Tab):
name = _("IPSec Site Connection Details")
slug = "ipsecsiteconnectiondetails"

View File

@ -0,0 +1,3 @@
{% load i18n %}
<p>{% trans "Create endpoint group for current project." %}</p>

View File

@ -1,7 +1,15 @@
{% load i18n %}
<p>{% trans "Create VPN service for current project." %}</p>
<p>{% trans "The VPN service is attached to a router and references to a single subnet to push to a remote site." %}</p>
<p>{% trans "Specify a name, description, router, and subnet for the VPN Service." %}</p>
<p>{% blocktrans trimmed %}
The VPN service is attached to a router and references to endpoint group
or a single subnet to push to a remote site.
{% endblocktrans %}</p>
<p>{% trans "Specify a name, description, router, and subnet (optional) for the VPN Service." %}</p>
<p>{% trans "Admin State is enabled by default." %}</p>
<p>{% trans "The router, subnet and admin state fields require to be enabled. All others are optional." %} </p>
<p>{% trans "The router and admin state fields require to be enabled. All others are optional." %} </p>
<p>{% blocktrans trimmed %}
Note: The recommended way to specify local subnets is to use endpoint groups
in IPsec site connection. It is deprecated to specify subnet in VPN service.
For a new VPN service or IPsec site connection, using endpoint group is recommended.
{% endblocktrans %}</p>

View File

@ -0,0 +1,32 @@
{% load i18n sizeformat parse_date %}
<div class="detail">
<dl class="dl-horizontal">
<dt>{% trans "Name" %}</dt>
<dd>{{ endpointgroup.name|default:_("None") }}</dd>
<dt>{% trans "Description" %}</dt>
<dd>{{ endpointgroup.description|default:_("None") }}</dd>
<dt>{% trans "ID" %}</dt>
<dd>{{ endpointgroup.id }}</dd>
<dt>{% trans "Project ID" %}</dt>
<dd>{{ endpointgroup.tenant_id }}</dd>
<dt>{% trans "Type" %}</dt>
<dd>{{ endpointgroup.type }}</dd>
<dt>{% trans "Endpoints" %}</dt>
{% if endpointgroup.type == 'subnet' %}
{% for ep in endpointgroup.endpoints %}
{% url 'horizon:project:networks:subnets:detail' ep as subnet_url %}
<dd><a href="{{ subnet_url }}">{{ ep }}</a></dd>
{% endfor %}
{% else %}
{% for cidr in endpointgroup.endpoints %}
<dd>{{ cidr }}</dd>
{% endfor %}
{% endif %}
</dl>
</div>

View File

@ -18,6 +18,10 @@
{% url 'horizon:project:vpn:vpnservicedetails' ipsecsiteconnection.vpnservice_id as vpnservice_url %}
<dd><a href="{{ vpnservice_url }}">{{ ipsecsiteconnection.vpnservice.name_or_id }}</a></dd>
<dt>{% trans "Local Endpoint Group" %}</dt>
{% url 'horizon:project:vpn:endpointgroupdetails' ipsecsiteconnection.local_ep_group_id as local_epg_url %}
<dd><a href="{{ local_epg_url }}">{{ ipsecsiteconnection.local_ep_group_id }}</a></dd>
<dt>{% trans "IKE Policy" %}</dt>
{% url 'horizon:project:vpn:ikepolicydetails' ipsecsiteconnection.ikepolicy_id as ikepolicy_url %}
<dd><a href="{{ ikepolicy_url }}">{{ ipsecsiteconnection.ikepolicy.name_or_id }}</a></dd>
@ -39,6 +43,10 @@
{% endfor %}
</dd>
<dt>{% trans "Peer Endpoint Group" %}</dt>
{% url 'horizon:project:vpn:endpointgroupdetails' ipsecsiteconnection.peer_ep_group_id as peer_epg_url %}
<dd><a href="{{ peer_epg_url }}">{{ ipsecsiteconnection.peer_ep_group_id }}</a></dd>
<dt>{% trans "MTU" %}</dt>
<dd>{{ ipsecsiteconnection.mtu }}</dd>

View File

@ -0,0 +1,7 @@
{% extends "horizon/common/_modal_form.html" %}
{% load i18n %}
{% block modal-body-right %}
<h3>{% trans "Description:" %}</h3>
<p>{% trans "You may update endpoint group details here." %}</p>
{% endblock %}

View File

@ -19,8 +19,12 @@
<dd><a href="{{ router_url }}">{{ vpnservice.router.name_or_id }}</a></dd>
<dt>{% trans "Subnet ID" %}</dt>
{% url 'horizon:project:networks:subnets:detail' vpnservice.subnet_id as subnet_url %}
<dd><a href="{{ subnet_url }}">{{ vpnservice.subnet.name_or_id }} {{ vpnservice.subnet.cidr }}</a></dd>
{% if vpnservice.subnet_id %}
{% url 'horizon:project:networks:subnets:detail' vpnservice.subnet_id as subnet_url %}
<dd><a href="{{ subnet_url }}">{{ vpnservice.subnet.name_or_id }} {{ vpnservice.subnet.cidr }}</a></dd>
{% else %}
<dd>{% trans "None" %}</dd>
{% endif %}
<dt>{% trans "VPN Connections" %}</dt>
<dd>

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% load i18n %}
{% block title %}{% trans "Edit Endpoint Group" %}{% endblock %}
{% block main %}
{% include 'project/vpn/_update_endpointgroup.html' %}
{% endblock %}

View File

@ -40,17 +40,22 @@ class VPNTests(test.TestCase):
ADDIKEPOLICY_PATH = 'horizon:%s:vpn:addikepolicy' % DASHBOARD
ADDIPSECPOLICY_PATH = 'horizon:%s:vpn:addipsecpolicy' % DASHBOARD
ADDVPNSERVICE_PATH = 'horizon:%s:vpn:addvpnservice' % DASHBOARD
ADDENDPOINTGROUP_PATH = 'horizon:%s:vpn:addendpointgroup' % DASHBOARD
ADDVPNCONNECTION_PATH = 'horizon:%s:vpn:addipsecsiteconnection' % DASHBOARD
IKEPOLICY_DETAIL_PATH = 'horizon:%s:vpn:ikepolicydetails' % DASHBOARD
IPSECPOLICY_DETAIL_PATH = 'horizon:%s:vpn:ipsecpolicydetails' % DASHBOARD
VPNSERVICE_DETAIL_PATH = 'horizon:%s:vpn:vpnservicedetails' % DASHBOARD
ENDPOINTGROUP_DETAIL_PATH = 'horizon:%s:vpn:endpointgroupdetails' %\
DASHBOARD
VPNCONNECTION_DETAIL_PATH = 'horizon:%s:vpn:ipsecsiteconnectiondetails' %\
DASHBOARD
UPDATEIKEPOLICY_PATH = 'horizon:%s:vpn:update_ikepolicy' % DASHBOARD
UPDATEIPSECPOLICY_PATH = 'horizon:%s:vpn:update_ipsecpolicy' % DASHBOARD
UPDATEVPNSERVICE_PATH = 'horizon:%s:vpn:update_vpnservice' % DASHBOARD
UPDATEENDPOINTGROUP_PATH = 'horizon:%s:vpn:update_endpointgroup' %\
DASHBOARD
UPDATEVPNCONNECTION_PATH = 'horizon:%s:vpn:update_ipsecsiteconnection' %\
DASHBOARD
@ -60,6 +65,11 @@ class VPNTests(test.TestCase):
IsA(http.HttpRequest), tenant_id=self.tenant.id) \
.AndReturn(self.vpnservices.list())
# retrieves endpoint groups
api_vpn.endpointgroup_list(
IsA(http.HttpRequest), tenant_id=self.tenant.id) \
.AndReturn(self.endpointgroups.list())
# retrieves ikepolicies
api_vpn.ikepolicy_list(
IsA(http.HttpRequest), tenant_id=self.tenant.id) \
@ -79,6 +89,9 @@ class VPNTests(test.TestCase):
api_vpn.vpnservice_list(
IsA(http.HttpRequest),
tenant_id=self.tenant.id).AndRaise(self.exceptions.neutron)
api_vpn.endpointgroup_list(
IsA(http.HttpRequest),
tenant_id=self.tenant.id).AndRaise(self.exceptions.neutron)
api_vpn.ikepolicy_list(
IsA(http.HttpRequest),
tenant_id=self.tenant.id).AndRaise(self.exceptions.neutron)
@ -90,7 +103,7 @@ class VPNTests(test.TestCase):
tenant_id=self.tenant.id).AndRaise(self.exceptions.neutron)
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_list')})
def test_index_vpnservices(self):
self.set_up_expect()
@ -106,7 +119,23 @@ class VPNTests(test.TestCase):
len(self.vpnservices.list()))
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_list')})
def test_index_endpointgroups(self):
self.set_up_expect()
self.mox.ReplayAll()
res = self.client.get(self.INDEX_URL + '?tab=vpntabs__endpointgroups')
self.assertTemplateUsed(res, '%s/vpn/index.html'
% self.DASHBOARD)
self.assertTemplateUsed(res, 'horizon/common/_detail_table.html')
self.assertEqual(len(res.context['endpointgroupstable_table'].data),
len(self.endpointgroups.list()))
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_list')})
def test_index_ikepolicies(self):
self.set_up_expect()
@ -122,7 +151,7 @@ class VPNTests(test.TestCase):
len(self.ikepolicies.list()))
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_list')})
def test_index_ipsecpolicies(self):
self.set_up_expect()
@ -138,7 +167,7 @@ class VPNTests(test.TestCase):
len(self.ipsecpolicies.list()))
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_list')})
def test_index_ipsecsiteconnections(self):
self.set_up_expect()
@ -156,7 +185,7 @@ class VPNTests(test.TestCase):
len(self.ipsecsiteconnections.list()))
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_list')})
def test_index_exception_vpnservices(self):
self.set_up_expect_with_exception()
@ -172,7 +201,23 @@ class VPNTests(test.TestCase):
self.assertEqual(len(res.context['table'].data), 0)
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_list')})
def test_index_exception_endpointgroups(self):
self.set_up_expect_with_exception()
self.mox.ReplayAll()
res = self.client.get(self.INDEX_URL + '?tab=vpntabs__endpointgroups')
self.assertTemplateUsed(res, '%s/vpn/index.html'
% self.DASHBOARD)
self.assertTemplateUsed(res,
'horizon/common/_detail_table.html')
self.assertEqual(len(res.context['table'].data), 0)
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_list')})
def test_index_exception_ikepolicies(self):
self.set_up_expect_with_exception()
@ -188,7 +233,7 @@ class VPNTests(test.TestCase):
self.assertEqual(len(res.context['table'].data), 0)
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_list')})
def test_index_exception_ipsecpolicies(self):
self.set_up_expect_with_exception()
@ -204,7 +249,7 @@ class VPNTests(test.TestCase):
self.assertEqual(len(res.context['table'].data), 0)
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_list')})
def test_index_exception_ipsecsiteconnections(self):
self.set_up_expect_with_exception()
@ -293,7 +338,68 @@ class VPNTests(test.TestCase):
res = self.client.post(reverse(self.ADDVPNSERVICE_PATH), form_data)
self.assertFormErrors(res, 2)
self.assertFormErrors(res, 1)
@test.create_stubs({api.neutron: ('network_list_for_tenant', )})
def test_add_endpointgroup_get(self):
networks = [{'subnets': [self.subnets.first(), ]}, ]
api.neutron.network_list_for_tenant(
IsA(http.HttpRequest), self.tenant.id).AndReturn(networks)
self.mox.ReplayAll()
res = self.client.get(reverse(self.ADDENDPOINTGROUP_PATH))
workflow = res.context['workflow']
self.assertTemplateUsed(res, views.WorkflowView.template_name)
self.assertEqual(workflow.name, workflows.AddEndpointGroup.name)
expected_objs = ['<AddEndpointGroupStep: addendpointgroupaction>', ]
self.assertQuerysetEqual(workflow.steps, expected_objs)
@test.create_stubs({api.neutron: ('network_list_for_tenant', ),
api_vpn: ('endpointgroup_create', )})
def test_add_endpointgroup_post(self):
endpointgroup = self.endpointgroups.first()
networks = [{'subnets': [self.subnets.first(), ]}, ]
api.neutron.network_list_for_tenant(
IsA(http.HttpRequest), self.tenant.id).AndReturn(networks)
form_data = {'name': endpointgroup['name'],
'description': endpointgroup['description'],
'endpoints': endpointgroup['endpoints'],
'type': endpointgroup['type']}
api_vpn.endpointgroup_create(
IsA(http.HttpRequest), **form_data).AndReturn(endpointgroup)
self.mox.ReplayAll()
res = self.client.post(reverse(self.ADDENDPOINTGROUP_PATH), form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, str(self.INDEX_URL))
@test.create_stubs({api.neutron: ('network_list_for_tenant', )})
def test_add_endpointgroup_post_error(self):
endpointgroup = self.endpointgroups.first()
networks = [{'subnets': [self.subnets.first(), ]}, ]
api.neutron.network_list_for_tenant(
IsA(http.HttpRequest), self.tenant.id).AndReturn(networks)
self.mox.ReplayAll()
form_data = {'name': endpointgroup['name'],
'description': endpointgroup['description'],
'endpoints': endpointgroup['endpoints'],
'type': ''}
res = self.client.post(reverse(self.ADDENDPOINTGROUP_PATH), form_data)
self.assertFormErrors(res, 1)
def test_add_ikepolicy_get(self):
res = self.client.get(reverse(self.ADDIKEPOLICY_PATH))
@ -408,7 +514,7 @@ class VPNTests(test.TestCase):
self.assertFormErrors(res, 1)
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list')})
'vpnservice_list', 'endpointgroup_list',)})
def test_add_ipsecsiteconnection_get(self):
ikepolicies = self.ikepolicies.list()
ipsecpolicies = self.ipsecpolicies.list()
@ -439,13 +545,13 @@ class VPNTests(test.TestCase):
self.assertQuerysetEqual(workflow.steps, expected_objs)
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_create')})
def test_add_ipsecsiteconnection_post(self):
self._test_add_ipsecsiteconnection_post()
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_create')})
def test_add_ipsecsiteconnection_post_single_subnet(self):
self._test_add_ipsecsiteconnection_post(subnet_list=False)
@ -498,13 +604,13 @@ class VPNTests(test.TestCase):
self.assertRedirectsNoFollow(res, str(self.INDEX_URL))
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_create')})
def test_add_ipsecsiteconnection_post_required_fields_error(self):
self._test_add_ipsecsiteconnection_post_error()
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ipsecpolicy_list',
'vpnservice_list',
'vpnservice_list', 'endpointgroup_list',
'ipsecsiteconnection_create')})
def test_add_ipsecsiteconnection_post_peer_cidrs_error(self):
self._test_add_ipsecsiteconnection_post_error(subnets=True)
@ -548,7 +654,10 @@ class VPNTests(test.TestCase):
res = self.client.post(reverse(self.ADDVPNCONNECTION_PATH), form_data)
self.assertFormErrors(res, 7)
if subnets:
self.assertFormErrors(res, 7)
else:
self.assertFormErrors(res, 6)
@test.create_stubs({api_vpn: ('vpnservice_get', )})
def test_update_vpnservice_get(self):
@ -590,6 +699,48 @@ class VPNTests(test.TestCase):
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, str(self.INDEX_URL))
@test.create_stubs({api_vpn: ('endpointgroup_get', )})
def test_update_endpointgroup_get(self):
endpointgroup = self.endpointgroups.first()
api_vpn.endpointgroup_get(IsA(http.HttpRequest), endpointgroup.id)\
.AndReturn(endpointgroup)
self.mox.ReplayAll()
res = self.client.get(
reverse(self.UPDATEENDPOINTGROUP_PATH, args=(endpointgroup.id,)))
self.assertTemplateUsed(
res, 'project/vpn/update_endpointgroup.html')
@test.create_stubs({api_vpn: ('endpointgroup_get',
'endpointgroup_update')})
def test_update_endpointgroup_post(self):
endpointgroup = self.endpointgroups.first()
api_vpn.endpointgroup_get(IsA(http.HttpRequest), endpointgroup.id)\
.AndReturn(endpointgroup)
data = {'name': endpointgroup.name,
'description': endpointgroup.description}
api_vpn.endpointgroup_update(IsA(http.HttpRequest), endpointgroup.id,
endpointgroup=data
).AndReturn(endpointgroup)
self.mox.ReplayAll()
form_data = data.copy()
form_data['endpoint_group_id'] = endpointgroup.id
res = self.client.post(reverse(self.UPDATEENDPOINTGROUP_PATH,
args=(endpointgroup.id, )
), form_data)
self.assertNoFormErrors(res)
self.assertRedirectsNoFollow(res, str(self.INDEX_URL))
@test.create_stubs({api_vpn: ('ikepolicy_get', )})
def test_update_ikepolicy_get(self):
ikepolicy = self.ikepolicies.first()
@ -764,6 +915,23 @@ class VPNTests(test.TestCase):
self.assertNoFormErrors(res)
@test.create_stubs({api_vpn: ('endpointgroup_list',
'endpointgroup_delete',)})
def test_delete_endpointgroup(self):
endpointgroup = self.endpointgroups.list()[0]
api_vpn.endpointgroup_list(
IsA(http.HttpRequest), tenant_id=self.tenant.id) \
.AndReturn(self.endpointgroups.list())
api_vpn.endpointgroup_delete(IsA(http.HttpRequest), endpointgroup.id)
self.mox.ReplayAll()
form_data = {"action":
"endpointgroupstable__deleteendpointgroup__%s"
% endpointgroup.id}
res = self.client.post(self.INDEX_URL, form_data)
self.assertNoFormErrors(res)
@test.create_stubs({api_vpn: ('ikepolicy_list', 'ikepolicy_delete',)})
def test_delete_ikepolicy(self):
ikepolicy = self.ikepolicies.list()[1]

View File

@ -36,12 +36,18 @@ urlpatterns = [
views.AddVPNServiceView.as_view(), name='addvpnservice'),
url(r'^update_vpnservice/(?P<vpnservice_id>[^/]+)/$',
views.UpdateVPNServiceView.as_view(), name='update_vpnservice'),
url(r'^addendpointgroup$',
views.AddEndpointGroupView.as_view(), name='addendpointgroup'),
url(r'^update_endpointgroup/(?P<endpoint_group_id>[^/]+)/$',
views.UpdateEndpointGroupView.as_view(), name='update_endpointgroup'),
url(r'^ikepolicy/(?P<ikepolicy_id>[^/]+)/$',
views.IKEPolicyDetailsView.as_view(), name='ikepolicydetails'),
url(r'^ipsecpolicy/(?P<ipsecpolicy_id>[^/]+)/$',
views.IPSecPolicyDetailsView.as_view(), name='ipsecpolicydetails'),
url(r'^vpnservice/(?P<vpnservice_id>[^/]+)/$',
views.VPNServiceDetailsView.as_view(), name='vpnservicedetails'),
url(r'^endpointgroup/(?P<endpoint_group_id>[^/]+)/$',
views.EndpointGroupDetailsView.as_view(), name='endpointgroupdetails'),
url(r'^ipsecsiteconnection/(?P<ipsecsiteconnection_id>[^/]+)/$',
views.IPSecSiteConnectionDetailsView.as_view(),
name='ipsecsiteconnectiondetails'),

View File

@ -39,6 +39,10 @@ class AddVPNServiceView(horizon_workflows.WorkflowView):
workflow_class = workflows.AddVPNService
class AddEndpointGroupView(horizon_workflows.WorkflowView):
workflow_class = workflows.AddEndpointGroup
class AddIPSecSiteConnectionView(horizon_workflows.WorkflowView):
workflow_class = workflows.AddIPSecSiteConnection
@ -161,6 +165,50 @@ class VPNServiceDetailsView(horizon_tabs.TabView):
return reverse_lazy('horizon:project:vpn:index')
class EndpointGroupDetailsView(horizon_tabs.TabView):
tab_group_class = tabs.EndpointGroupDetailsTabs
template_name = 'horizon/common/_detail.html'
page_title = "{{ endpointgroup.name|default:endpointgroup.id }}"
@memoized.memoized_method
def get_data(self):
gid = self.kwargs['endpoint_group_id']
try:
endpointgroup = api_vpn.endpointgroup_get(self.request, gid)
except Exception:
msg = _('Unable to retrieve endpoint group details.')
exceptions.handle(self.request, msg,
redirect=self.get_redirect_url())
try:
connections = api_vpn.ipsecsiteconnection_list(
self.request, endpoint_group_id=gid)
endpointgroup.vpnconnections = connections
except Exception:
endpointgroup.vpnconnections = []
return endpointgroup
def get_context_data(self, **kwargs):
context = super(EndpointGroupDetailsView, self).get_context_data(
**kwargs)
endpointgroup = self.get_data()
table = tables.EndpointGroupTable(self.request)
context["endpointgroup"] = endpointgroup
context["url"] = self.get_redirect_url()
context["actions"] = table.render_row_actions(endpointgroup)
return context
def get_tabs(self, request, *args, **kwargs):
endpointgroup = self.get_data()
return self.tab_group_class(request, endpointgroup=endpointgroup,
**kwargs)
@staticmethod
def get_redirect_url():
return reverse('horizon:project:vpn:index')
class IPSecSiteConnectionDetailsView(horizon_tabs.TabView):
tab_group_class = tabs.IPSecSiteConnectionDetailsTabs
template_name = 'horizon/common/_detail.html'
@ -232,6 +280,41 @@ class UpdateVPNServiceView(horizon_forms.ModalFormView):
'admin_state_up': vpnservice['admin_state_up']}
class UpdateEndpointGroupView(horizon_forms.ModalFormView):
form_class = forms.UpdateEndpointGroup
form_id = "update_endpointgroup_form"
template_name = "project/vpn/update_endpointgroup.html"
context_object_name = 'endpointgroup'
submit_label = _("Save Changes")
submit_url = "horizon:project:vpn:update_endpointgroup"
success_url = reverse_lazy("horizon:project:vpn:index")
page_title = _("Edit Endpoint Group")
def get_context_data(self, **kwargs):
context = super(UpdateEndpointGroupView, self).get_context_data(
**kwargs)
context["endpoint_group_id"] = self.kwargs['endpoint_group_id']
args = (self.kwargs['endpoint_group_id'],)
context['submit_url'] = reverse(self.submit_url, args=args)
return context
@memoized.memoized_method
def _get_object(self, *args, **kwargs):
endpoint_group_id = self.kwargs['endpoint_group_id']
try:
return api_vpn.endpointgroup_get(self.request, endpoint_group_id)
except Exception as e:
redirect = self.success_url
msg = _('Unable to retrieve Endpoint Group details. %s') % e
exceptions.handle(self.request, msg, redirect=redirect)
def get_initial(self):
endpointgroup = self._get_object()
return {'name': endpointgroup['name'],
'endpoint_group_id': endpointgroup['id'],
'description': endpointgroup['description']}
class UpdateIKEPolicyView(horizon_forms.ModalFormView):
form_class = forms.UpdateIKEPolicy
form_id = "update_ikepolicy_form"
@ -346,16 +429,25 @@ class UpdateIPSecSiteConnectionView(horizon_forms.ModalFormView):
def get_initial(self):
ipsecsiteconnection = self._get_object()
return {'name': ipsecsiteconnection['name'],
'ipsecsiteconnection_id': ipsecsiteconnection['id'],
'description': ipsecsiteconnection['description'],
'peer_address': ipsecsiteconnection['peer_address'],
'peer_id': ipsecsiteconnection['peer_id'],
'peer_cidrs': ", ".join(ipsecsiteconnection['peer_cidrs']),
'psk': ipsecsiteconnection['psk'],
'mtu': ipsecsiteconnection['mtu'],
'dpd_action': ipsecsiteconnection['dpd']['action'],
'dpd_interval': ipsecsiteconnection['dpd']['interval'],
'dpd_timeout': ipsecsiteconnection['dpd']['timeout'],
'initiator': ipsecsiteconnection['initiator'],
'admin_state_up': ipsecsiteconnection['admin_state_up']}
data = {
'name': ipsecsiteconnection['name'],
'ipsecsiteconnection_id': ipsecsiteconnection['id'],
'description': ipsecsiteconnection['description'],
'peer_address': ipsecsiteconnection['peer_address'],
'peer_id': ipsecsiteconnection['peer_id'],
'psk': ipsecsiteconnection['psk'],
'mtu': ipsecsiteconnection['mtu'],
'dpd_action': ipsecsiteconnection['dpd']['action'],
'dpd_interval': ipsecsiteconnection['dpd']['interval'],
'dpd_timeout': ipsecsiteconnection['dpd']['timeout'],
'initiator': ipsecsiteconnection['initiator'],
'admin_state_up': ipsecsiteconnection['admin_state_up']
}
if 'local_ep_group_id' in ipsecsiteconnection:
data['local_ep_group_id'] = \
ipsecsiteconnection['local_ep_group_id']
data['peer_ep_group_id'] = ipsecsiteconnection['peer_ep_group_id']
return data
else:
data['peer_cidrs'] = ", ".join(ipsecsiteconnection['peer_cidrs'])
return data

View File

@ -29,7 +29,11 @@ class AddVPNServiceAction(workflows.Action):
initial="", required=False,
max_length=80, label=_("Description"))
router_id = forms.ChoiceField(label=_("Router"))
subnet_id = forms.ChoiceField(label=_("Subnet"))
subnet_id = forms.ChoiceField(
label=_("Subnet"),
help_text=_("Optional. No need to be specified "
"when you use endpoint groups."),
required=False)
admin_state_up = forms.BooleanField(
label=_("Enable Admin State"),
help_text=_("The state of VPN service to start in. If disabled "
@ -106,6 +110,109 @@ class AddVPNService(workflows.Workflow):
return False
class AddEndpointGroupAction(workflows.Action):
name = forms.CharField(
max_length=80,
label=_("Name"),
required=False)
description = forms.CharField(
initial="",
required=False,
max_length=80,
label=_("Description"))
type = forms.ThemableChoiceField(
label=_("Type"),
help_text=_("IPSec connection validation requires that local "
"endpoints are subnets, and peer endpoints are CIDRs."),
choices=[('cidr', _('CIDR (for external systems)')),
('subnet', _('Subnet (for local systems)'))],
widget=forms.ThemableSelectWidget(attrs={
'class': 'switchable',
'data-slug': 'type', }))
cidrs = forms.MultiIPField(
required=False,
label=_("External System CIDRs"),
widget=forms.TextInput(attrs={
'class': 'switched',
'data-switch-on': 'type',
'data-type-cidr': _("External System CIDRs"),
}),
help_text=_("Remote peer subnet(s) address(es) "
"with mask(s) in CIDR format "
"separated with commas if needed "
"(e.g. 20.1.0.0/24, 21.1.0.0/24). "
"This field is valid if type is CIDR"),
version=forms.IPv4 | forms.IPv6,
mask=True)
subnets = forms.MultipleChoiceField(
required=False,
label=_("Local System Subnets"),
widget=forms.ThemableCheckboxSelectMultiple(attrs={
'class': 'switched',
'data-switch-on': 'type',
'data-type-subnet': _("External System Subnets"),
}),
help_text=_("Local subnet(s). "
"This field is valid if type is Subnet"),)
def populate_subnets_choices(self, request, context):
subnets_choices = []
try:
tenant_id = request.user.tenant_id
networks = api.neutron.network_list_for_tenant(request, tenant_id)
except Exception:
exceptions.handle(request,
_('Unable to retrieve networks list.'))
networks = []
for n in networks:
for s in n['subnets']:
subnets_choices.append((s.id, s.cidr))
self.fields['subnets'].choices = subnets_choices
return subnets_choices
class Meta(object):
name = _("Add New Endpoint Groups")
permissions = ('openstack.services.network',)
help_text_template = "project/vpn/_add_endpoint_group_help.html"
class AddEndpointGroupStep(workflows.Step):
action_class = AddEndpointGroupAction
contributes = ("name", "description", "type",
"cidrs", "subnets", "endpoints")
def contribute(self, data, context):
context = super(AddEndpointGroupStep, self).contribute(data, context)
if context['type'] == 'cidr':
cidrs = context['cidrs']
context['endpoints'] = [
cidr.strip() for cidr in cidrs.split(',') if cidr.strip()]
else:
context['endpoints'] = context['subnets']
if data:
return context
class AddEndpointGroup(workflows.Workflow):
slug = "addendpointgroup"
name = _("Add Endpoint Group")
finalize_button_name = _("Add")
success_message = _('Added Endpoint Group "%s".')
failure_message = _('Unable to add Endpoint Group "%s".')
success_url = "horizon:project:vpn:index"
default_steps = (AddEndpointGroupStep,)
def format_status_message(self, message):
return message % self.context.get('name')
def handle(self, request, context):
try:
api_vpn.endpointgroup_create(request, **context)
return True
except Exception:
return False
class AddIKEPolicyAction(workflows.Action):
name = forms.CharField(max_length=80, label=_("Name"), required=False)
description = forms.CharField(
@ -315,6 +422,12 @@ class AddIPSecSiteConnectionAction(workflows.Action):
max_length=80, label=_("Description"))
vpnservice_id = forms.ChoiceField(
label=_("VPN Service associated with this connection"))
local_ep_group_id = forms.ChoiceField(
required=False,
label=_("Endpoint Group for local subnet(s)"),
help_text=_("Local subnets which the new IPsec connection is "
"connected to. Required if no subnet is specified "
"in a VPN service selected."))
ikepolicy_id = forms.ChoiceField(
label=_("IKE Policy associated with this connection"))
ipsecpolicy_id = forms.ChoiceField(
@ -331,9 +444,15 @@ class AddIPSecSiteConnectionAction(workflows.Action):
"Can be IPv4/IPv6 address, e-mail, key ID, or FQDN"),
version=forms.IPv4 | forms.IPv6,
mask=False)
peer_ep_group_id = forms.ChoiceField(
required=False,
label=_("Endpoint Group for remote peer CIDR(s)"),
help_text=_("Remove peer CIDR(s) connected to the new IPSec "
"connection."))
peer_cidrs = forms.MultiIPField(
required=False,
label=_("Remote peer subnet(s)"),
help_text=_("Remote peer subnet(s) address(es) "
help_text=_("(Deprecated) Remote peer subnet(s) address(es) "
"with mask(s) in CIDR format "
"separated with commas if needed "
"(e.g. 20.1.0.0/24, 21.1.0.0/24)"),
@ -389,6 +508,36 @@ class AddIPSecSiteConnectionAction(workflows.Action):
self.fields['vpnservice_id'].choices = vpnservice_id_choices
return vpnservice_id_choices
def populate_local_ep_group_id_choices(self, request, context):
try:
tenant_id = self.request.user.tenant_id
endpointgroups = api_vpn.endpointgroup_list(request,
tenant_id=tenant_id)
except Exception:
exceptions.handle(request,
_('Unable to retrieve endpoint group list.'))
endpointgroups = []
local_ep_group_ids = [(s.id, s.name) for s in endpointgroups
if s.type == 'subnet']
local_ep_group_ids.insert(0, ('', _("Select local endpoint group")))
self.fields['local_ep_group_id'].choices = local_ep_group_ids
return local_ep_group_ids
def populate_peer_ep_group_id_choices(self, request, context):
try:
tenant_id = self.request.user.tenant_id
endpointgroups = api_vpn.endpointgroup_list(request,
tenant_id=tenant_id)
except Exception:
exceptions.handle(request,
_('Unable to retrieve endpoint group list.'))
endpointgroups = []
peer_ep_group_ids = [(s.id, s.name) for s in endpointgroups
if s.type == 'cidr']
peer_ep_group_ids.insert(0, ('', _("Select peer endpoint group")))
self.fields['peer_ep_group_id'].choices = peer_ep_group_ids
return peer_ep_group_ids
class Meta(object):
name = _("Add New IPSec Site Connection")
permissions = ('openstack.services.network',)
@ -403,7 +552,8 @@ class AddIPSecSiteConnectionStep(workflows.Step):
action_class = AddIPSecSiteConnectionAction
contributes = ("name", "description",
"vpnservice_id", "ikepolicy_id", "ipsecpolicy_id",
"peer_address", "peer_id", "peer_cidrs", "psk")
"peer_address", "peer_id", "peer_cidrs", "psk",
"local_ep_group_id", "peer_ep_group_id")
class AddIPSecSiteConnectionOptionalAction(workflows.Action):
@ -489,7 +639,8 @@ class AddIPSecSiteConnectionOptionalStep(workflows.Step):
context.pop('dpd_timeout')
cidrs = context['peer_cidrs']
context['peer_cidrs'] = cidrs.replace(" ", "").split(",")
context['peer_cidrs'] = [cidr.strip() for cidr in cidrs.split(',')
if cidr.strip()]
if data:
return context

View File

@ -88,6 +88,64 @@ class VPNaasApiTests(test.APITestCase):
ret_val = api_vpn.vpnservice_get(self.request, vpnservice.id)
self.assertIsInstance(ret_val, api_vpn.VPNService)
@test.create_stubs({neutronclient: ('create_endpoint_group',)})
def test_endpointgroup_create(self):
endpointgroup = self.api_endpointgroups.first()
form_data = {
'name': endpointgroup['name'],
'description': endpointgroup['description'],
'type': endpointgroup['type'],
'endpoints': endpointgroup['endpoints']
}
endpoint_group = {'endpoint_group': self.api_endpointgroups.first()}
neutronclient.create_endpoint_group(
{'endpoint_group': form_data}).AndReturn(endpoint_group)
self.mox.ReplayAll()
ret_val = api_vpn.endpointgroup_create(self.request, **form_data)
self.assertIsInstance(ret_val, api_vpn.EndpointGroup)
@test.create_stubs({neutronclient: ('list_endpoint_groups',
'list_ipsec_site_connections')})
def test_endpointgroup_list(self):
endpointgroups = {'endpoint_groups': self.endpointgroups.list()}
endpointgroups_dict = {
'endpoint_groups': self.api_endpointgroups.list()}
ipsecsiteconnections_dict = {
'ipsec_site_connections': self.api_ipsecsiteconnections.list()}
neutronclient.list_endpoint_groups().AndReturn(endpointgroups_dict)
neutronclient.list_ipsec_site_connections().AndReturn(
ipsecsiteconnections_dict)
self.mox.ReplayAll()
ret_val = api_vpn.endpointgroup_list(self.request)
for (v, d) in zip(ret_val, endpointgroups['endpoint_groups']):
self.assertIsInstance(v, api_vpn.EndpointGroup)
self.assertTrue(v.name, d.name)
self.assertTrue(v.id)
@test.create_stubs({neutronclient: ('show_endpoint_group',
'list_ipsec_site_connections')})
def test_endpointgroup_get(self):
endpoint_group = self.endpointgroups.first()
endpoint_group_dict = {
'endpoint_group': self.api_endpointgroups.first()}
ipsecsiteconnections_dict = {
'ipsec_site_connections': self.api_ipsecsiteconnections.list()}
neutronclient.show_endpoint_group(
endpoint_group.id).AndReturn(endpoint_group_dict)
neutronclient.list_ipsec_site_connections().AndReturn(
ipsecsiteconnections_dict)
self.mox.ReplayAll()
ret_val = api_vpn.endpointgroup_get(self.request, endpoint_group.id)
self.assertIsInstance(ret_val, api_vpn.EndpointGroup)
@test.create_stubs({neutronclient: ('create_ikepolicy',)})
def test_ikepolicy_create(self):
ikepolicy1 = self.api_ikepolicies.first()

View File

@ -23,12 +23,14 @@ def data(TEST):
TEST.ikepolicies = utils.TestDataContainer()
TEST.ipsecpolicies = utils.TestDataContainer()
TEST.ipsecsiteconnections = utils.TestDataContainer()
TEST.endpointgroups = utils.TestDataContainer()
# Data return by neutronclient.
TEST.api_vpnservices = utils.TestDataContainer()
TEST.api_ikepolicies = utils.TestDataContainer()
TEST.api_ipsecpolicies = utils.TestDataContainer()
TEST.api_ipsecsiteconnections = utils.TestDataContainer()
TEST.api_endpointgroups = utils.TestDataContainer()
# 1st VPNService.
vpnservice_dict = {'id': '09a26949-6231-4f72-942a-0c8c0ddd4d61',
@ -64,6 +66,17 @@ def data(TEST):
TEST.api_vpnservices.add(vpnservice_dict)
TEST.vpnservices.add(vpn.VPNService(vpnservice_dict))
# 1st Endpoint Group
endpointgroup_dict = {'id': 'baa588ff-e1b9-4256-8687-9f06315f64b7',
'tenant_id': '1',
'name': 'endpoint_group_one',
'description': 'the first test endpoint group',
'type': 'subnet',
'endpoints': [TEST.subnets.first().id]
}
TEST.api_endpointgroups.add(endpointgroup_dict)
TEST.endpointgroups.add(vpn.EndpointGroup(endpointgroup_dict))
# 1st IKEPolicy
ikepolicy_dict = {'id': 'a1f009b7-0ffa-43a7-ba19-dcabb0b4c981',
'tenant_id': '1',

View File

@ -0,0 +1,4 @@
---
features:
- |
Add support for Endpoint Group feature.