Represent External Connectivity in GBP: PLUGIN

Today, the GBP model only represents east-west traffic policies.
This new API and reference implementation
allows north-south traffic in a GBP enabled cloud
partial implements blueprint external-connectivity

Change-Id: I6feac27f89efdc2ad61dad2fc39bf62e280ee2c6
This commit is contained in:
Ivar Lazzaro
2014-11-28 18:52:10 -08:00
parent 3eaf550069
commit e73055c232
10 changed files with 1442 additions and 23 deletions

View File

@@ -738,6 +738,7 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
external_segment_id=es_db.id,
destination=rt['destination'],
nexthop=rt['nexthop'] or ADDRESS_NOT_SPECIFIED)
es_db.external_routes.append(target)
def _set_ess_for_l3p(self, context, l3p_db, es_dict):
@@ -1555,10 +1556,17 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
return self._make_external_policy_dict(ep_db)
@log.log
def get_external_policies(self, context, filters=None, fields=None):
def get_external_policies(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
marker_obj = self._get_marker_obj(context, 'external_policy', limit,
marker)
return self._get_collection(context, ExternalPolicy,
self._make_external_policy_dict,
filters=filters, fields=fields)
filters=filters, fields=fields,
sorts=sorts, limit=limit,
marker_obj=marker_obj,
page_reverse=page_reverse)
@log.log
def get_external_policies_count(self, context, filters=None):
@@ -1608,10 +1616,17 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
return self._make_external_segment_dict(es_db)
@log.log
def get_external_segments(self, context, filters=None, fields=None):
def get_external_segments(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
marker_obj = self._get_marker_obj(context, 'external_segment', limit,
marker)
return self._get_collection(context, ExternalSegment,
self._make_external_segment_dict,
filters=filters, fields=fields)
filters=filters, fields=fields,
sorts=sorts, limit=limit,
marker_obj=marker_obj,
page_reverse=page_reverse)
@log.log
def get_external_segments_count(self, context, filters=None):
@@ -1655,10 +1670,17 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
return self._make_nat_pool_dict(np_db)
@log.log
def get_nat_pools(self, context, filters=None, fields=None):
def get_nat_pools(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
marker_obj = self._get_marker_obj(context, 'nat_pool', limit,
marker)
return self._get_collection(context, NATPool,
self._make_nat_pool_dict,
filters=filters, fields=fields)
filters=filters, fields=fields,
sorts=sorts, limit=limit,
marker_obj=marker_obj,
page_reverse=page_reverse)
@log.log
def get_nat_pools_count(self, context, filters=None):

View File

@@ -108,3 +108,23 @@ class InvalidSharedAttributeUpdate(GroupPolicyBadRequest):
message = _("Invalid shared attribute update. Shared resource %(id)s is"
"referenced by %(rid)s, which is either shared or owned by a "
"different tenant.")
class ExternalRouteOverlapsWithL3PIpPool(GroupPolicyBadRequest):
message = _("Destination %(destination)s for ES %(es_id)s overlaps with "
"L3P %(l3p_id)s.")
class ExternalSegmentSubnetOverlapsWithL3PIpPool(GroupPolicyBadRequest):
message = _("Subnet %(subnet)s for ES %(es_id)s overlaps with "
"L3P %(l3p_id)s.")
class ExternalRouteNextHopNotInExternalSegment(GroupPolicyBadRequest):
message = _("One or more external routes' nexthop are not part of "
"subnet %(cidr)s.")
class InvalidL3PExternalIPAddress(GroupPolicyBadRequest):
message = _("Address %(ip)s allocated for l3p %(l3p_id)s on segment "
"%(es_id)s doesn't belong to the segment subnet %(es_cidr)s")

View File

@@ -1,17 +1,15 @@
# Copyright (c) 2014 OpenStack Foundation
# All Rights Reserved.
# 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
#
# 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
#
# 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.
# 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.
from neutron.openstack.common import log
from oslo.config import cfg
@@ -209,3 +207,48 @@ class ExtensionManager(stevedore.named.NamedExtensionManager):
"""Call all extension drivers to extend NSP dictionary."""
for driver in self.ordered_ext_drivers:
driver.obj.extend_network_service_policy_dict(session, result)
def process_create_external_segment(self, session, data, result):
"""Call all extension drivers during EP creation."""
self._call_on_ext_drivers("process_create_external_segment",
session, data, result)
def process_update_external_segment(self, session, data, result):
"""Call all extension drivers during EP update."""
self._call_on_ext_drivers("process_update_external_segment",
session, data, result)
def extend_external_segment_dict(self, session, result):
"""Call all extension drivers to extend EP dictionary."""
for driver in self.ordered_ext_drivers:
driver.obj.extend_external_segment_dict(session, result)
def process_create_external_policy(self, session, data, result):
"""Call all extension drivers during EP creation."""
self._call_on_ext_drivers("process_create_external_policy",
session, data, result)
def process_update_external_policy(self, session, data, result):
"""Call all extension drivers during EP update."""
self._call_on_ext_drivers("process_update_external_policy",
session, data, result)
def extend_external_policy_dict(self, session, result):
"""Call all extension drivers to extend EP dictionary."""
for driver in self.ordered_ext_drivers:
driver.obj.extend_external_policy_dict(session, result)
def process_create_nat_pool(self, session, data, result):
"""Call all extension drivers during NP creation."""
self._call_on_ext_drivers("process_create_nat_pool",
session, data, result)
def process_update_nat_pool(self, session, data, result):
"""Call all extension drivers during NP update."""
self._call_on_ext_drivers("process_update_nat_pool",
session, data, result)
def extend_nat_pool_dict(self, session, result):
"""Call all extension drivers to extend NP dictionary."""
for driver in self.ordered_ext_drivers:
driver.obj.extend_nat_pool_dict(session, result)

View File

@@ -20,6 +20,21 @@ class GroupPolicyContext(object):
self._plugin_context = plugin_context
class BaseResouceContext(GroupPolicyContext):
def __init__(self, plugin, plugin_context, resource, original=None):
super(BaseResouceContext, self).__init__(plugin, plugin_context)
self._resource = resource
self._original = original
@property
def current(self):
return self._resource
@property
def original(self):
return self._original
class PolicyTargetContext(GroupPolicyContext, api.PolicyTargetContext):
def __init__(self, plugin, plugin_context, policy_target,
@@ -211,3 +226,15 @@ class PolicyRuleSetContext(GroupPolicyContext, api.PolicyRuleSetContext):
@property
def original(self):
return self._original_policy_rule_set
class ExternalSegmentContext(BaseResouceContext, api.ExternalSegmentContext):
pass
class ExternalPolicyContext(BaseResouceContext, api.ExternalPolicyContext):
pass
class NatPoolContext(BaseResouceContext, api.NatPoolContext):
pass

View File

@@ -357,6 +357,102 @@ class PolicyRuleSetContext(object):
pass
@six.add_metaclass(abc.ABCMeta)
class ExternalSegmentContext(object):
"""Context passed to policy engine for external_segment resource.
A ExternalSegmentContext instance wraps an external_segment
resource.
It provides helper methods for accessing other relevant information.
Results from expensive operations are cached for convenient access.
"""
@abc.abstractproperty
def current(self):
"""Return the current state of the external_segment.
Return the current state of the external_segment, as defined by
GroupPolicyPlugin.create_external_segment.
"""
pass
@abc.abstractproperty
def original(self):
"""Return the original state of the external_segment.
Return the original state of the external_segment, prior to a
call to update_external_segment. Method is only valid within
calls to update_external_segment_precommit and
update_external_segment_postcommit.
"""
pass
@six.add_metaclass(abc.ABCMeta)
class ExternalPolicyContext(object):
"""Context passed to policy engine for external_policy resource.
A ExternalPolicyContext instance wraps an external_policy
resource.
It provides helper methods for accessing other relevant information.
Results from expensive operations are cached for convenient access.
"""
@abc.abstractproperty
def current(self):
"""Return the current state of the external_policy.
Return the current state of the external_policy, as defined by
GroupPolicyPlugin.create_external_policy.
"""
pass
@abc.abstractproperty
def original(self):
"""Return the original state of the external_policy.
Return the original state of the external_policy, prior to a
call to update_external_policy. Method is only valid within
calls to update_external_policy_precommit and
update_external_policy_postcommit.
"""
pass
@six.add_metaclass(abc.ABCMeta)
class NatPoolContext(object):
"""Context passed to policy engine for nat_pool resource.
A NatPoolContext instance wraps an nat_pool
resource.
It provides helper methods for accessing other relevant information.
Results from expensive operations are cached for convenient access.
"""
@abc.abstractproperty
def current(self):
"""Return the current state of the nat_pool.
Return the current state of the nat_pool, as defined by
GroupPolicyPlugin.create_nat_pool.
"""
pass
@abc.abstractproperty
def original(self):
"""Return the original state of the nat_pool.
Return the original state of the nat_pool, prior to a
call to update_nat_pool. Method is only valid within
calls to update_nat_pool_precommit and
update_nat_pool_postcommit.
"""
pass
@six.add_metaclass(abc.ABCMeta)
class PolicyDriver(object):
"""Define stable abstract interface for Group Policy drivers.
@@ -843,6 +939,162 @@ class PolicyDriver(object):
"""
pass
def create_external_segment_precommit(self, context):
"""Allocate resources for a new network service policy.
:param context: ExternalSegmentContext instance describing the
new network service policy.
"""
pass
def create_external_segment_postcommit(self, context):
"""Create a network service policy.
:param context: ExternalSegmentContext instance describing the
new network service policy.
"""
pass
def update_external_segment_precommit(self, context):
"""Update resources of a network service policy.
:param context: ExternalSegmentContext instance describing the
new state of the ExternalSegment, as well as the original state
prior to the update_external_segment call.
"""
pass
def update_external_segment_postcommit(self, context):
"""Update a network service policy.
:param context: ExternalSegmentContext instance describing the
new state of the ExternalSegment, as well as the original state
prior to the update_external_segment call.
"""
pass
def delete_external_segment_precommit(self, context):
"""Delete resources for a network service policy.
:param context: ExternalSegmentContext instance describing the
current state of the ExternalSegment, prior to the call to
delete it.
"""
pass
def delete_external_segment_postcommit(self, context):
"""Delete a network service policy.
:param context: ExternalSegmentContext instance describing the
current state of the ExternalSegment, prior to the call to
delete it.
"""
pass
def create_external_policy_precommit(self, context):
"""Allocate resources for a new network service policy.
:param context: ExternalPolicyContext instance describing the
new network service policy.
"""
pass
def create_external_policy_postcommit(self, context):
"""Create a network service policy.
:param context: ExternalPolicyContext instance describing the
new network service policy.
"""
pass
def update_external_policy_precommit(self, context):
"""Update resources of a network service policy.
:param context: ExternalPolicyContext instance describing the
new state of the ExternalPolicy, as well as the original state
prior to the update_external_policy call.
"""
pass
def update_external_policy_postcommit(self, context):
"""Update a network service policy.
:param context: ExternalPolicyContext instance describing the
new state of the ExternalPolicy, as well as the original state
prior to the update_external_policy call.
"""
pass
def delete_external_policy_precommit(self, context):
"""Delete resources for a network service policy.
:param context: ExternalPolicyContext instance describing the
current state of the ExternalPolicy, prior to the call to
delete it.
"""
pass
def delete_external_policy_postcommit(self, context):
"""Delete a network service policy.
:param context: ExternalPolicyContext instance describing the
current state of the ExternalPolicy, prior to the call to
delete it.
"""
pass
def create_nat_pool_precommit(self, context):
"""Allocate resources for a new network service policy.
:param context: NatPoolContext instance describing the
new network service policy.
"""
pass
def create_nat_pool_postcommit(self, context):
"""Create a network service policy.
:param context: NatPoolContext instance describing the
new network service policy.
"""
pass
def update_nat_pool_precommit(self, context):
"""Update resources of a network service policy.
:param context: NatPoolContext instance describing the
new state of the NatPool, as well as the original state
prior to the update_nat_pool call.
"""
pass
def update_nat_pool_postcommit(self, context):
"""Update a network service policy.
:param context: NatPoolContext instance describing the
new state of the NatPool, as well as the original state
prior to the update_nat_pool call.
"""
pass
def delete_nat_pool_precommit(self, context):
"""Delete resources for a network service policy.
:param context: NatPoolContext instance describing the
current state of the NatPool, prior to the call to
delete it.
"""
pass
def delete_nat_pool_postcommit(self, context):
"""Delete a network service policy.
:param context: NatPoolContext instance describing the
current state of the NatPool, prior to the call to
delete it.
"""
pass
@six.add_metaclass(abc.ABCMeta)
class ExtensionDriver(object):
@@ -1248,3 +1500,129 @@ class ExtensionDriver(object):
network_service_policy operation.
"""
pass
def process_create_external_segment(self, session, data, result):
"""Process extended attributes for external_segment creation.
:param session: database session
:param data: dictionary of incoming external_segment data
:param result: external_segment dictionary to extend
Called inside transaction context on session to validate and
persist any extended external_segment attributes defined
by this driver. Extended attribute values must also be added
to result.
"""
pass
def process_update_external_segment(self, session, data, result):
"""Process extended attributes for external_segment update.
:param session: database session
:param data: dictionary of incoming external_segment data
:param result: external_segment dictionary to extend
Called inside transaction context on session to validate and
update any extended external_segment attributes defined by this
driver. Extended attribute values, whether updated or not,
must also be added to result.
"""
pass
def extend_external_segment_dict(self, session, result):
"""Add extended attributes to external_segment dictionary.
:param session: database session
:param result: external_segment dictionary to extend
Called inside transaction context on session to add any
extended attributes defined by this driver to a
external_segment dictionary to be used for mechanism
driver calls and/or returned as the result of a
external_segment operation.
"""
pass
def process_create_external_policy(self, session, data, result):
"""Process extended attributes for external_policy creation.
:param session: database session
:param data: dictionary of incoming external_policy data
:param result: external_policy dictionary to extend
Called inside transaction context on session to validate and
persist any extended external_policy attributes defined
by this driver. Extended attribute values must also be added
to result.
"""
pass
def process_update_external_policy(self, session, data, result):
"""Process extended attributes for external_policy update.
:param session: database session
:param data: dictionary of incoming external_policy data
:param result: external_policy dictionary to extend
Called inside transaction context on session to validate and
update any extended external_policy attributes defined by this
driver. Extended attribute values, whether updated or not,
must also be added to result.
"""
pass
def extend_external_policy_dict(self, session, result):
"""Add extended attributes to external_policy dictionary.
:param session: database session
:param result: external_policy dictionary to extend
Called inside transaction context on session to add any
extended attributes defined by this driver to a
external_policy dictionary to be used for mechanism
driver calls and/or returned as the result of a
external_policy operation.
"""
pass
def process_create_nat_pool(self, session, data, result):
"""Process extended attributes for nat_pool creation.
:param session: database session
:param data: dictionary of incoming nat_pool data
:param result: nat_pool dictionary to extend
Called inside transaction context on session to validate and
persist any extended nat_pool attributes defined
by this driver. Extended attribute values must also be added
to result.
"""
pass
def process_update_nat_pool(self, session, data, result):
"""Process extended attributes for nat_pool update.
:param session: database session
:param data: dictionary of incoming nat_pool data
:param result: nat_pool dictionary to extend
Called inside transaction context on session to validate and
update any extended nat_pool attributes defined by this
driver. Extended attribute values, whether updated or not,
must also be added to result.
"""
pass
def extend_nat_pool_dict(self, session, result):
"""Add extended attributes to nat_pool dictionary.
:param session: database session
:param result: nat_pool dictionary to extend
Called inside transaction context on session to add any
extended attributes defined by this driver to a
nat_pool dictionary to be used for mechanism
driver calls and/or returned as the result of a
nat_pool operation.
"""
pass

View File

@@ -10,11 +10,14 @@
# License for the specific language governing permissions and limitations
# under the License.
import netaddr
from neutron.api.v2 import attributes as nattr
from neutron.common import log
from neutron.openstack.common import excutils
from neutron.openstack.common import log as logging
from gbp.neutron.db.grouppolicy import group_policy_db as gdb
from gbp.neutron.db.grouppolicy import group_policy_mapping_db
from gbp.neutron.services.grouppolicy.common import exceptions as gp_exc
from gbp.neutron.services.grouppolicy import extension_manager as ext_manager
@@ -54,7 +57,8 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
# <attribute> is the field on the <to_check> dictionary that can be used
# to retrieve the UUID/s of the specific object <type>
usage_graph = {'l3_policy': {},
usage_graph = {'l3_policy': {'external_segments':
'external_segment'},
'l2_policy': {'l3_policy_id': 'l3_policy'},
'policy_target_group': {
'network_service_policy_id': 'network_service_policy',
@@ -70,6 +74,13 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
'policy_rule_set': {
'parent_id': 'policy_rule_set',
'policy_rules': 'policy_rule'},
'external_segment': {},
'external_policy': {
'external_segments': 'external_segment',
'provided_policy_rule_sets': 'policy_rule_set',
'consumed_policy_rule_sets': 'policy_rule_set'},
'nat_pool': {'external_segment_id':
'external_segment'}
}
_plurals = None
@@ -101,7 +112,7 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
def _validate_shared_update(self, context, original, updated, identity):
if updated.get('shared'):
# Even though the shared attribute may not be changed, the objects
# it is referring to might. For this reason we run the reference
# it is referring to might. For this reson we run the reference
# validation every time a shared resource is updated
# TODO(ivar): run only when relevant updates happen
self._validate_shared_create(context, updated, identity)
@@ -139,6 +150,10 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
context, obj, self.get_policy_target_groups, 'id',
obj['providing_policy_target_groups'] +
obj['consuming_policy_target_groups'])
self._check_shared_or_different_tenant(
context, obj, self.get_external_policies, 'id',
obj['providing_external_policies'] +
obj['consuming_external_policies'])
def _validate_policy_classifier_unshare(self, context, obj):
self._check_shared_or_different_tenant(
@@ -154,6 +169,85 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
self._check_shared_or_different_tenant(
context, obj, self.get_policy_rules, 'id', r_ids)
def _validate_external_segment_unshare(self, context, obj):
self._check_shared_or_different_tenant(
context, obj, self.get_l3_policies, 'id', obj['l3_policies'])
self._check_shared_or_different_tenant(
context, obj, self.get_external_policies, 'id',
obj['external_policies'])
self._check_shared_or_different_tenant(
context, obj, self.get_nat_pools, 'external_segment_id')
def _validate_external_policy_unshare(self, context, obj):
pass
def _validate_nat_pool_unshare(self, context, obj):
pass
def _validate_routes(self, context, current, original=None):
if original:
added = (set((x['destination'], x['nexthop']) for x in
current['external_routes']) -
set((x['destination'], x['nexthop']) for x in
original['external_routes']))
else:
added = set((x['destination'], x['nexthop']) for x in
current['external_routes'])
if added:
# Verify new ones don't overlap with the existing L3P
added_dest = set(x[0] for x in added)
# Remove default routes
added_dest.discard('0.0.0.0/0')
added_dest.discard('::/0')
added_ipset = netaddr.IPSet(added_dest)
if current['l3_policies']:
l3ps = self.get_l3_policies(
context, filters={'id': current['l3_policies']})
for l3p in l3ps:
if netaddr.IPSet([l3p['ip_pool']]) & added_ipset:
raise gp_exc.ExternalRouteOverlapsWithL3PIpPool(
destination=added_dest, l3p_id=l3p['id'],
es_id=current['id'])
# Verify NH in ES pool
added_nexthop = netaddr.IPSet(x[1] for x in added if x[1])
es_subnet = netaddr.IPSet([current['cidr']])
if added_nexthop & es_subnet != added_nexthop:
raise gp_exc.ExternalRouteNextHopNotInExternalSegment(
cidr=current['cidr'])
def _validate_l3p_es(self, context, current, original=None):
if original:
added = (set(current['external_segments'].keys()) -
set(original['external_segments'].keys()))
else:
added = set(current['external_segments'].keys())
if added:
es_list = self.get_external_segments(context,
filters={'id': added})
l3p_ipset = netaddr.IPSet([current['ip_pool']])
for es in es_list:
# Verify no route overlap
dest_set = set(x['destination'] for x in
es['external_routes'])
dest_set.discard('0.0.0.0/0')
dest_set.discard('::/0')
if l3p_ipset & netaddr.IPSet(dest_set):
raise gp_exc.ExternalRouteOverlapsWithL3PIpPool(
destination=dest_set, l3p_id=current['id'],
es_id=es['id'])
# Verify segment CIDR doesn't overlap with L3P's
if l3p_ipset & netaddr.IPSet([es['cidr']]):
raise gp_exc.ExternalSegmentSubnetOverlapsWithL3PIpPool(
subnet=es['cidr'], l3p_id=current['id'],
es_id=current['id'])
# Verify allocated address correctly in subnet
for addr in current['external_segments'][es['id']]:
if addr != gdb.ADDRESS_NOT_SPECIFIED:
if addr not in netaddr.IPNetwork(es['cidr']):
raise gp_exc.InvalidL3PExternalIPAddress(
ip=addr, es_id=es['id'], l3p_id=current['id'],
es_cidr=es['cidr'])
def __init__(self):
self.extension_manager = ext_manager.ExtensionManager()
self.policy_driver_manager = manager.PolicyDriverManager()
@@ -547,6 +641,7 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
self.extension_manager.process_create_l3_policy(
session, l3_policy, result)
self._validate_shared_create(context, result, 'l3_policy')
self._validate_l3p_es(context, result)
policy_context = p_context.L3PolicyContext(self, context,
result)
self.policy_driver_manager.create_l3_policy_precommit(
@@ -575,6 +670,8 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
session, l3_policy, updated_l3_policy)
self._validate_shared_update(context, original_l3_policy,
updated_l3_policy, 'l3_policy')
self._validate_l3p_es(context, updated_l3_policy,
original_l3_policy)
policy_context = p_context.L3PolicyContext(
self, context, updated_l3_policy,
original_l3_policy=original_l3_policy)
@@ -995,3 +1092,291 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
self.extension_manager.extend_policy_rule_set_dict(
session, result)
return [self._fields(result, fields) for result in results]
@log.log
def create_external_segment(self, context, external_segment):
session = context.session
with session.begin(subtransactions=True):
result = super(GroupPolicyPlugin,
self).create_external_segment(context,
external_segment)
self.extension_manager.process_create_external_segment(
session, external_segment, result)
self._validate_shared_create(context, result,
'external_segment')
self._validate_routes(context, result)
policy_context = p_context.ExternalSegmentContext(
self, context, result)
(self.policy_driver_manager.
create_external_segment_precommit(policy_context))
try:
(self.policy_driver_manager.
create_external_segment_postcommit(policy_context))
except gp_exc.GroupPolicyDriverError:
with excutils.save_and_reraise_exception():
LOG.error(_("create_external_segment_postcommit "
"failed, deleting external_segment "
"'%s'"), result['id'])
self.delete_external_segment(context, result['id'])
return result
@log.log
def update_external_segment(self, context, external_segment_id,
external_segment):
session = context.session
with session.begin(subtransactions=True):
original_external_segment = super(
GroupPolicyPlugin, self).get_external_segment(
context, external_segment_id)
updated_external_segment = super(
GroupPolicyPlugin, self).update_external_segment(
context, external_segment_id,
external_segment)
self.extension_manager.process_update_external_segment(
session, external_segment, updated_external_segment)
self._validate_shared_update(
context, original_external_segment,
updated_external_segment, 'external_segment')
self._validate_routes(context, updated_external_segment,
original_external_segment)
# TODO(ivar): Validate Routes' GW in es subnet
policy_context = p_context.ExternalSegmentContext(
self, context, updated_external_segment,
original_external_segment)
(self.policy_driver_manager.
update_external_segment_precommit(policy_context))
self.policy_driver_manager.update_external_segment_postcommit(
policy_context)
return updated_external_segment
@log.log
def delete_external_segment(self, context, external_segment_id):
session = context.session
with session.begin(subtransactions=True):
es = self.get_external_segment(context, external_segment_id)
if es['l3_policies'] or es['nat_pools'] or es['external_policies']:
return False
policy_context = p_context.ExternalSegmentContext(
self, context, es)
(self.policy_driver_manager.
delete_external_segment_precommit(policy_context))
super(GroupPolicyPlugin, self).delete_external_segment(
context, external_segment_id)
try:
(self.policy_driver_manager.
delete_external_segment_postcommit(policy_context))
except gp_exc.GroupPolicyDriverError:
with excutils.save_and_reraise_exception():
LOG.error(_("delete_external_segment_postcommit "
"failed, deleting external_segment '%s'"),
external_segment_id)
return True
def get_external_segment(self, context, external_segment_id, fields=None):
session = context.session
with session.begin(subtransactions=True):
result = super(GroupPolicyPlugin, self).get_external_segment(
context, external_segment_id, None)
self.extension_manager.extend_external_segment_dict(session,
result)
return self._fields(result, fields)
def get_external_segments(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
session = context.session
with session.begin(subtransactions=True):
results = super(GroupPolicyPlugin, self).get_external_segments(
context, filters, None, sorts, limit, marker, page_reverse)
for result in results:
self.extension_manager.extend_external_segment_dict(
session, result)
return [self._fields(result, fields) for result in results]
@log.log
def create_external_policy(self, context, external_policy):
session = context.session
with session.begin(subtransactions=True):
result = super(GroupPolicyPlugin,
self).create_external_policy(
context, external_policy)
self.extension_manager.process_create_external_policy(
session, external_policy, result)
self._validate_shared_create(context, result,
'external_policy')
policy_context = p_context.ExternalPolicyContext(
self, context, result)
(self.policy_driver_manager.
create_external_policy_precommit(policy_context))
try:
(self.policy_driver_manager.
create_external_policy_postcommit(policy_context))
except gp_exc.GroupPolicyDriverError:
with excutils.save_and_reraise_exception():
LOG.error(_("create_external_policy_postcommit "
"failed, deleting external_policy "
"'%s'"), result['id'])
self.delete_external_policy(context, result['id'])
return result
@log.log
def update_external_policy(self, context, external_policy_id,
external_policy):
session = context.session
with session.begin(subtransactions=True):
original_external_policy = super(
GroupPolicyPlugin, self).get_external_policy(
context, external_policy_id)
updated_external_policy = super(
GroupPolicyPlugin, self).update_external_policy(
context, external_policy_id,
external_policy)
self.extension_manager.process_update_external_policy(
session, external_policy, updated_external_policy)
self._validate_shared_update(
context, original_external_policy,
updated_external_policy, 'external_policy')
policy_context = p_context.ExternalPolicyContext(
self, context, updated_external_policy,
original_external_policy)
(self.policy_driver_manager.
update_external_policy_precommit(policy_context))
self.policy_driver_manager.update_external_policy_postcommit(
policy_context)
return updated_external_policy
@log.log
def delete_external_policy(self, context, external_policy_id,
check_unused=False):
session = context.session
with session.begin(subtransactions=True):
es = self.get_external_policy(context, external_policy_id)
policy_context = p_context.ExternalPolicyContext(
self, context, es)
(self.policy_driver_manager.
delete_external_policy_precommit(policy_context))
super(GroupPolicyPlugin, self).delete_external_policy(
context, external_policy_id)
try:
(self.policy_driver_manager.
delete_external_policy_postcommit(policy_context))
except gp_exc.GroupPolicyDriverError:
with excutils.save_and_reraise_exception():
LOG.error(_("delete_external_policy_postcommit "
"failed, deleting external_policy '%s'"),
external_policy_id)
return True
def get_external_policy(self, context, external_policy_id, fields=None):
session = context.session
with session.begin(subtransactions=True):
result = super(GroupPolicyPlugin, self).get_external_policy(
context, external_policy_id, None)
self.extension_manager.extend_external_policy_dict(session,
result)
return self._fields(result, fields)
def get_external_policies(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
session = context.session
with session.begin(subtransactions=True):
results = super(GroupPolicyPlugin, self).get_external_policies(
context, filters, None, sorts, limit, marker, page_reverse)
for result in results:
self.extension_manager.extend_external_policy_dict(
session, result)
return [self._fields(result, fields) for result in results]
@log.log
def create_nat_pool(self, context, nat_pool):
session = context.session
with session.begin(subtransactions=True):
result = super(GroupPolicyPlugin, self).create_nat_pool(
context, nat_pool)
self.extension_manager.process_create_nat_pool(session, nat_pool,
result)
self._validate_shared_create(context, result, 'nat_pool')
policy_context = p_context.NatPoolContext(self, context, result)
(self.policy_driver_manager.
create_nat_pool_precommit(policy_context))
try:
(self.policy_driver_manager.
create_nat_pool_postcommit(policy_context))
except gp_exc.GroupPolicyDriverError:
with excutils.save_and_reraise_exception():
LOG.error(_("create_nat_pool_postcommit failed, deleting "
"nat_pool '%s'"), result['id'])
self.delete_nat_pool(context, result['id'])
return result
@log.log
def update_nat_pool(self, context, nat_pool_id, nat_pool):
session = context.session
with session.begin(subtransactions=True):
original_nat_pool = super(
GroupPolicyPlugin, self).get_nat_pool(context, nat_pool_id)
updated_nat_pool = super(
GroupPolicyPlugin, self).update_nat_pool(context, nat_pool_id,
nat_pool)
self.extension_manager.process_update_nat_pool(
session, nat_pool, updated_nat_pool)
self._validate_shared_update(context, original_nat_pool,
updated_nat_pool, 'nat_pool')
policy_context = p_context.NatPoolContext(
self, context, updated_nat_pool, original_nat_pool)
(self.policy_driver_manager.
update_nat_pool_precommit(policy_context))
self.policy_driver_manager.update_nat_pool_postcommit(policy_context)
return updated_nat_pool
@log.log
def delete_nat_pool(self, context, nat_pool_id, check_unused=False):
session = context.session
with session.begin(subtransactions=True):
es = self.get_nat_pool(context, nat_pool_id)
policy_context = p_context.NatPoolContext(self, context, es)
(self.policy_driver_manager.delete_nat_pool_precommit(
policy_context))
super(GroupPolicyPlugin, self).delete_nat_pool(context,
nat_pool_id)
try:
(self.policy_driver_manager.
delete_nat_pool_postcommit(policy_context))
except gp_exc.GroupPolicyDriverError:
with excutils.save_and_reraise_exception():
LOG.error(_("delete_nat_pool_postcommit failed, deleting "
"nat_pool '%s'"), nat_pool_id)
return True
def get_nat_pool(self, context, nat_pool_id, fields=None):
session = context.session
with session.begin(subtransactions=True):
result = super(GroupPolicyPlugin, self).get_nat_pool(
context, nat_pool_id, None)
self.extension_manager.extend_nat_pool_dict(session, result)
return self._fields(result, fields)
def get_nat_pools(self, context, filters=None, fields=None,
sorts=None, limit=None, marker=None,
page_reverse=False):
session = context.session
with session.begin(subtransactions=True):
results = super(GroupPolicyPlugin, self).get_nat_pools(
context, filters, None, sorts, limit, marker, page_reverse)
for result in results:
self.extension_manager.extend_nat_pool_dict(
session, result)
return [self._fields(result, fields) for result in results]

View File

@@ -310,3 +310,70 @@ class PolicyDriverManager(stevedore.named.NamedExtensionManager):
def delete_policy_rule_set_postcommit(self, context):
self._call_on_drivers("delete_policy_rule_set_postcommit", context,
continue_on_failure=True)
def create_external_segment_precommit(self, context):
self._call_on_drivers("create_external_segment_precommit",
context)
def create_external_segment_postcommit(self, context):
self._call_on_drivers("create_external_segment_postcommit",
context)
def update_external_segment_precommit(self, context):
self._call_on_drivers("update_external_segment_precommit",
context)
def update_external_segment_postcommit(self, context):
self._call_on_drivers("update_external_segment_postcommit",
context)
def delete_external_segment_precommit(self, context):
self._call_on_drivers("delete_external_segment_precommit",
context)
def delete_external_segment_postcommit(self, context):
self._call_on_drivers("delete_external_segment_postcommit",
context, continue_on_failure=True)
def create_external_policy_precommit(self, context):
self._call_on_drivers("create_external_policy_precommit",
context)
def create_external_policy_postcommit(self, context):
self._call_on_drivers("create_external_policy_postcommit",
context)
def update_external_policy_precommit(self, context):
self._call_on_drivers("update_external_policy_precommit",
context)
def update_external_policy_postcommit(self, context):
self._call_on_drivers("update_external_policy_postcommit",
context)
def delete_external_policy_precommit(self, context):
self._call_on_drivers("delete_external_policy_precommit",
context)
def delete_external_policy_postcommit(self, context):
self._call_on_drivers("delete_external_policy_postcommit",
context, continue_on_failure=True)
def create_nat_pool_precommit(self, context):
self._call_on_drivers("create_nat_pool_precommit", context)
def create_nat_pool_postcommit(self, context):
self._call_on_drivers("create_nat_pool_postcommit", context)
def update_nat_pool_precommit(self, context):
self._call_on_drivers("update_nat_pool_precommit", context)
def update_nat_pool_postcommit(self, context):
self._call_on_drivers("update_nat_pool_postcommit", context)
def delete_nat_pool_precommit(self, context):
self._call_on_drivers("delete_nat_pool_precommit", context)
def delete_nat_pool_postcommit(self, context):
self._call_on_drivers("delete_nat_pool_postcommit", context,
continue_on_failure=True)

View File

@@ -80,6 +80,27 @@ EXTENDED_ATTRIBUTES_2_0 = {
'is_visible': True,
'enforce_policy': True},
},
gp.EXTERNAL_SEGMENTS: {
'es_extension': {'allow_post': True,
'allow_put': True,
'default': attr.ATTR_NOT_SPECIFIED,
'is_visible': True,
'enforce_policy': True},
},
gp.EXTERNAL_POLICIES: {
'ep_extension': {'allow_post': True,
'allow_put': True,
'default': attr.ATTR_NOT_SPECIFIED,
'is_visible': True,
'enforce_policy': True},
},
gp.NAT_POOLS: {
'np_extension': {'allow_post': True,
'allow_put': True,
'default': attr.ATTR_NOT_SPECIFIED,
'is_visible': True,
'enforce_policy': True},
},
}

View File

@@ -373,6 +373,55 @@ class ExtensionDriverTestCase(
val = res['network_service_policy']['nsp_extension']
self.assertEqual("def", val)
def test_es_attr(self):
self._test_attr('external_segment')
def test_ep_attr(self):
self._test_attr('external_policy')
def test_np_attr(self):
self._test_attr('nat_pool')
def _test_attr(self, type):
# Test create with default value.
acronim = _acronim(type)
plural = self._get_resource_plural(type)
obj = getattr(self, 'create_%s' % type)()
id = obj[type]['id']
val = obj[type][acronim + '_extension']
self.assertEqual("", val)
req = self.new_show_request(plural, id)
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
val = res[type][acronim + '_extension']
self.assertEqual("", val)
# Test list.
res = self._list(plural)
val = res[plural][0][acronim + '_extension']
self.assertEqual("", val)
# Test create with explict value.
kwargs = {acronim + '_extension': "abc"}
obj = getattr(self, 'create_%s' % type)(**kwargs)
id = obj[type]['id']
val = obj[type][acronim + '_extension']
self.assertEqual("abc", val)
req = self.new_show_request(plural, id)
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
val = res[type][acronim + '_extension']
self.assertEqual("abc", val)
# Test update.
data = {type: {acronim + '_extension': "def"}}
req = self.new_update_request(plural, data, id)
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
val = res[type][acronim + '_extension']
self.assertEqual("def", val)
req = self.new_show_request(plural, id)
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
val = res[type][acronim + '_extension']
self.assertEqual("def", val)
class TestPolicyTargetExtension(model_base.BASEV2):
__tablename__ = 'test_policy_target_extension'
@@ -455,6 +504,33 @@ class TestNetworkServicePolicyExtension(model_base.BASEV2):
value = sa.Column(sa.String(64))
class TestExternalSegmentExtension(model_base.BASEV2):
__tablename__ = 'test_external_segment_extension'
es_id = sa.Column(sa.String(36),
sa.ForeignKey('gp_external_segments.id',
ondelete="CASCADE"),
primary_key=True)
value = sa.Column(sa.String(64))
class TestExternalPolicyExtension(model_base.BASEV2):
__tablename__ = 'test_external_policy_extension'
ep_id = sa.Column(sa.String(36),
sa.ForeignKey('gp_external_policies.id',
ondelete="CASCADE"),
primary_key=True)
value = sa.Column(sa.String(64))
class TestNatPoolExtension(model_base.BASEV2):
__tablename__ = 'test_nat_pool_extension'
np_id = sa.Column(sa.String(36),
sa.ForeignKey('gp_nat_pools.id',
ondelete="CASCADE"),
primary_key=True)
value = sa.Column(sa.String(64))
class TestExtensionDriver(api.ExtensionDriver):
_supported_extension_alias = 'test_extension'
@@ -677,3 +753,67 @@ class TestExtensionDriver(api.ExtensionDriver):
filter_by(nsp_id=result['id']).
one())
result['nsp_extension'] = record.value
def process_create_external_segment(self, session, data, result):
self._process_create(session, data, result, 'external_segment',
TestExternalSegmentExtension)
def process_update_external_segment(self, session, data, result):
self._process_update(session, data, result, 'external_segment',
TestExternalSegmentExtension)
def extend_external_segment_dict(self, session, result):
self._extend(session, result, 'external_segment',
TestExternalSegmentExtension)
def process_create_external_policy(self, session, data, result):
self._process_create(session, data, result, 'external_policy',
TestExternalPolicyExtension)
def process_update_external_policy(self, session, data, result):
self._process_update(session, data, result, 'external_policy',
TestExternalPolicyExtension)
def extend_external_policy_dict(self, session, result):
self._extend(session, result, 'external_policy',
TestExternalPolicyExtension)
def process_create_nat_pool(self, session, data, result):
self._process_create(session, data, result, 'nat_pool',
TestNatPoolExtension)
def process_update_nat_pool(self, session, data, result):
self._process_update(session, data, result, 'nat_pool',
TestNatPoolExtension)
def extend_nat_pool_dict(self, session, result):
self._extend(session, result, 'nat_pool', TestNatPoolExtension)
def _process_create(self, session, data, result, type, klass):
acronim = _acronim(type)
value = data[type][acronim + '_extension']
if not attributes.is_attr_set(value):
value = ''
kwargs = {acronim + '_id': result['id'], 'value': value}
record = klass(**kwargs)
session.add(record)
result[acronim + '_extension'] = value
def _process_update(self, session, data, result, type, klass):
acronim = _acronim(type)
kwargs = {acronim + '_id': result['id']}
record = session.query(klass).filter_by(**kwargs).one()
value = data[type].get(acronim + '_extension')
if value and value != record.value:
record.value = value
result[acronim + '_extension'] = record.value
def _extend(self, session, result, type, klass):
acronim = _acronim(type)
kwargs = {acronim + '_id': result['id']}
record = session.query(klass).filter_by(**kwargs).one()
result[acronim + '_extension'] = record.value
def _acronim(type):
return ''.join([x[0] for x in type.split('_')])

View File

@@ -90,8 +90,20 @@ class GroupPolicyPluginTestCase(tgpmdb.GroupPolicyMappingDbTestCase):
return self.create_policy_rule_set(policy_rules=[pr['id']],
**kwargs)['policy_rule_set']
def _update_gbp_resource(self, id, type, plural, expected_res_status=None,
**kwargs):
def _create_external_policy_on_shared(self, **kwargs):
es = self.create_external_segment(shared=True)
return self.create_external_policy(
external_segments=[es['external_segment']['id']],
**kwargs)['external_policy']
def _create_nat_pool_on_shared(self, **kwargs):
es = self.create_external_segment(shared=True)
return self.create_nat_pool(
external_segment_id=es['external_segment']['id'],
**kwargs)['nat_pool']
def _update_gbp_resource_full_response(
self, id, type, plural, expected_res_status=None, **kwargs):
data = {type: kwargs}
# Create PT with bound port
req = self.new_update_request(plural, data, id, self.fmt)
@@ -101,11 +113,20 @@ class GroupPolicyPluginTestCase(tgpmdb.GroupPolicyMappingDbTestCase):
self.assertEqual(res.status_int, expected_res_status)
elif res.status_int >= webob.exc.HTTPClientError.code:
raise webob.exc.HTTPClientError(code=res.status_int)
return self.deserialize(self.fmt, res).get(type)
return self.deserialize(self.fmt, res)
def _update_gbp_resource(self, id, type, plural, expected_res_status=None,
**kwargs):
return self._update_gbp_resource_full_response(
id, type, plural, expected_res_status=expected_res_status,
**kwargs).get(type)
class TestL3Policy(GroupPolicyPluginTestCase):
def _get_es_dict(self, es, addr=None):
return {es['external_segment']['id']: addr or []}
def test_shared_l3_policy_create(self):
# Verify default is False
l3p = self.create_l3_policy()
@@ -114,6 +135,30 @@ class TestL3Policy(GroupPolicyPluginTestCase):
l3p = self.create_l3_policy(shared=True)
self.assertEqual(True, l3p['l3_policy']['shared'])
def test_shared_l3p_create_with_es(self):
def combination(l3p, es):
return {'l3p': l3p, 'es': es}
allowed = [combination(False, False), combination(True, True),
combination(False, True)]
for shared in allowed:
es = self.create_external_segment(
cidr='172.0.0.0/8', shared=shared['es'])
es_dict = self._get_es_dict(es, ['172.0.0.2', '172.0.0.3'])
l3p = self.create_l3_policy(
external_segments=es_dict, shared=shared['l3p'],
expected_res_status=201)['l3_policy']
# Verify create successful
self.assertEqual(es_dict, l3p['external_segments'])
def test_shared_l3p_create_with_es_negative(self):
# Not allowed: Unshared ES with shared L3P
es = self.create_external_segment(cidr='172.0.0.0/8')
es_dict = self._get_es_dict(es, ['172.0.0.2', '172.0.0.3'])
res = self.create_l3_policy(external_segments=es_dict,
shared=True, expected_res_status=400)
self.assertEqual('SharedResourceReferenceError',
res['NeutronError']['type'])
def test_shared_l3_policy_update(self):
l3p = self.create_l3_policy()['l3_policy']
# Accept share if nothing referenced
@@ -124,6 +169,36 @@ class TestL3Policy(GroupPolicyPluginTestCase):
self.create_l2_policy(l3_policy_id=l3p['id'])
self._update_gbp_resource(l3p['id'], 'l3_policy', 'l3_policies',
expected_res_status=200, shared=False)
es = self.create_external_segment(cidr='172.0.0.0/8')
es_dict = self._get_es_dict(es, ['172.0.0.2', '172.0.0.3'])
# Set ES
l3p = self._update_gbp_resource(l3p['id'], 'l3_policy', 'l3_policies',
expected_res_status=200,
external_segments=es_dict)
self.assertEqual(es_dict, l3p['external_segments'])
# Share ES
self._update_gbp_resource(
es['external_segment']['id'], 'external_segment',
'external_segments', expected_res_status=200, shared=True)
# Verify sharing/unsharing successful
for shared in [True, False]:
self._update_gbp_resource(l3p['id'], 'l3_policy', 'l3_policies',
expected_res_status=200, shared=shared)
# Remove ES
l3p = self._update_gbp_resource(l3p['id'], 'l3_policy', 'l3_policies',
expected_res_status=200,
external_segments={})
self.assertEqual({}, l3p['external_segments'])
# Verify ES update with sharing successful
l3p = self._update_gbp_resource(l3p['id'], 'l3_policy', 'l3_policies',
expected_res_status=200,
external_segments=es_dict,
shared=True)
# Verify ES correctly set
self.assertEqual(es_dict, l3p['external_segments'])
def test_shared_l3_policy_update_negative(self):
l3p = self.create_l3_policy(shared=True)['l3_policy']
@@ -140,6 +215,69 @@ class TestL3Policy(GroupPolicyPluginTestCase):
self._update_gbp_resource(l3p['id'], 'l3_policy', 'l3_policies',
expected_res_status=400, shared=False)
es = self.create_external_segment(cidr='172.0.0.0/8')
es_dict = self._get_es_dict(es, ['172.0.0.2', '172.0.0.3'])
res = self._update_gbp_resource_full_response(
l3p['id'], 'l3_policy', 'l3_policies', expected_res_status=400,
external_segments=es_dict, shared=True)
self.assertEqual('SharedResourceReferenceError',
res['NeutronError']['type'])
def test_create_with_es_negative(self):
attrs = {'external_routes': [{'destination': '10.160.0.0/16',
'nexthop': '172.1.1.1'}],
'cidr': '172.1.1.0/24'}
es = self.create_external_segment(**attrs)['external_segment']
# Overlapping pool
attrs = {'ip_pool': '172.1.1.0/20',
'external_segments': {es['id']: ['172.1.1.2']}}
res = self.create_l3_policy(expected_res_status=400, **attrs)
self.assertEqual('ExternalSegmentSubnetOverlapsWithL3PIpPool',
res['NeutronError']['type'])
# Overlapping route
attrs['ip_pool'] = '10.160.1.0/24'
res = self.create_l3_policy(expected_res_status=400, **attrs)
self.assertEqual('ExternalRouteOverlapsWithL3PIpPool',
res['NeutronError']['type'])
# Allocated address not in pool
attrs = {'ip_pool': '192.168.0.0/24',
'external_segments': {es['id']: ['172.1.2.2']}}
res = self.create_l3_policy(expected_res_status=400, **attrs)
self.assertEqual('InvalidL3PExternalIPAddress',
res['NeutronError']['type'])
def test_update_with_es_negative(self):
attrs = {'external_routes': [{'destination': '10.160.0.0/16',
'nexthop': '172.1.1.1'}],
'cidr': '172.1.1.0/24'}
es = self.create_external_segment(**attrs)['external_segment']
# Overlapping pool
l3p = self.create_l3_policy(ip_pool='172.1.1.0/20')['l3_policy']
attrs = {'external_segments': {es['id']: ['172.1.1.2']}}
res = self._update_gbp_resource_full_response(
l3p['id'], 'l3_policy', 'l3_policies', expected_res_status=400,
**attrs)
self.assertEqual('ExternalSegmentSubnetOverlapsWithL3PIpPool',
res['NeutronError']['type'])
# Overlapping route
l3p = self.create_l3_policy(ip_pool='10.160.1.0/24')['l3_policy']
res = self._update_gbp_resource_full_response(
l3p['id'], 'l3_policy', 'l3_policies', expected_res_status=400,
**attrs)
self.assertEqual('ExternalRouteOverlapsWithL3PIpPool',
res['NeutronError']['type'])
# Allocated address not in pool
l3p = self.create_l3_policy(ip_pool='192.168.0.0/24')['l3_policy']
attrs = {'external_segments': {es['id']: ['172.1.2.2']}}
res = self._update_gbp_resource_full_response(
l3p['id'], 'l3_policy', 'l3_policies', expected_res_status=400,
**attrs)
self.assertEqual('InvalidL3PExternalIPAddress',
res['NeutronError']['type'])
class TestL2Policy(GroupPolicyPluginTestCase):
@@ -474,6 +612,184 @@ class TestPolicyTargetGroup(GroupPolicyPluginTestCase):
expected_res_status=201)
class TestExternalSegment(GroupPolicyPluginTestCase):
def test_shared_es_create(self):
# Verify default is False
es = self.create_external_segment()
self.assertEqual(False, es['external_segment']['shared'])
# Verify shared True created without errors
es = self.create_external_segment(shared=True)
self.assertEqual(True, es['external_segment']['shared'])
def test_shared_es_update(self):
es = self.create_external_segment()['external_segment']
for shared in [True, False]:
self._update_gbp_resource(
es['id'], 'external_segment',
'external_segments', expected_res_status=200,
shared=shared)
def test_create_routes(self):
attrs = {'external_routes': [{'destination': '0.0.0.0/0',
'nexthop': '172.1.0.1'}],
'cidr': '172.1.0.0/24'}
self.create_external_segment(expected_res_status=201, **attrs)
def test_routes_negative(self):
# Verify wrong NH
attrs = {'external_routes': [{'destination': '0.0.0.0/0',
'nexthop': '172.1.1.1'}],
'cidr': '172.1.0.0/24'}
res = self.create_external_segment(expected_res_status=400, **attrs)
self.assertEqual('ExternalRouteNextHopNotInExternalSegment',
res['NeutronError']['type'])
attrs['cidr'] = '172.1.1.0/24'
es = self.create_external_segment(**attrs)['external_segment']
self.create_l3_policy(
ip_pool='192.160.0.0/16',
external_segments={es['id']: ['172.1.1.2']})['l3_policy']
# Verify refused because overlapping with L3P
attrs = {'external_routes': [{'destination': '192.168.2.0/0',
'nexthop': '172.1.1.1'}]}
res = self._update_gbp_resource_full_response(
es['id'], 'external_segment', 'external_segments',
expected_res_status=400, **attrs)
self.assertEqual('ExternalRouteOverlapsWithL3PIpPool',
res['NeutronError']['type'])
class TestExternalPolicy(GroupPolicyPluginTestCase):
def test_shared_ep_create(self):
es = self.create_external_segment(
shared=True)['external_segment']
esns = self.create_external_segment(
)['external_segment']
prs = self._create_policy_rule_set_on_shared(shared=True)
prsns = self._create_policy_rule_set_on_shared()
# Verify non-shared ep providing and consuming shared and non shared
# policy_rule_sets
ep = self.create_external_policy(
external_segments=[es['id']], expected_res_status=201)
self.assertEqual(False, ep['external_policy']['shared'])
ep = self.create_external_policy(
external_segments=[es['id']],
provided_policy_rule_sets={prs['id']: '', prsns['id']: ''},
consumed_policy_rule_sets={prs['id']: '', prsns['id']: ''},
expected_res_status=201)
self.assertEqual(False, ep['external_policy']['shared'])
# Verify shared True created without errors by providing/consuming
# shared policy_rule_sets
ep = self.create_external_policy(
external_segments=[es['id']], shared=True,
expected_res_status=201)
self.assertEqual(True, ep['external_policy']['shared'])
ep = self.create_external_policy(
external_segments=[es['id']],
provided_policy_rule_sets={prs['id']: ''},
consumed_policy_rule_sets={prs['id']: ''}, shared=True,
expected_res_status=201)
self.assertEqual(True, ep['external_policy']['shared'])
# Verify not shared created without error on not shared es
self.create_external_policy(
external_segments=[esns['id']], expected_res_status=201)
def test_shared_ep_update(self):
ep = self._create_external_policy_on_shared()
self._update_gbp_resource(
ep['id'], 'external_policy', 'external_policies',
expected_res_status=200, shared=True)
self._update_gbp_resource(
ep['id'], 'external_policy', 'external_policies',
expected_res_status=200, shared=False)
def test_shared_ep_create_negative(self):
es = self.create_external_segment()['external_segment']
prs = self._create_policy_rule_set_on_shared()
# Verify shared EP fails on non-shared es
res = self.create_external_policy(
external_segments=[es['id']], shared=True,
expected_res_status=400)
self.assertEqual('SharedResourceReferenceError',
res['NeutronError']['type'])
# Verify shared EP fails to provide/consume non shared
# policy_rule_sets
res = self.create_external_policy(
shared=True, provided_policy_rule_sets={prs['id']: ''},
consumed_policy_rule_sets={prs['id']: ''},
expected_res_status=400)
self.assertEqual('SharedResourceReferenceError',
res['NeutronError']['type'])
def test_shared_ep_update_negative(self):
ep = self._create_external_policy_on_shared(shared=True)
# Verify update to non shared ES fails
es = self.create_external_segment()['external_segment']
self._update_gbp_resource(
ep['id'], 'external_policy', 'external_policies',
expected_res_status=400, external_segments=[es['id']])
# Verify update to non shared provided PRS fails
prs = self._create_policy_rule_set_on_shared()
self._update_gbp_resource(
ep['id'], 'external_policy', 'external_policies',
expected_res_status=400,
provided_policy_rule_sets={prs['id']: ''})
# Verify update to non shared consumed PRS fails
self._update_gbp_resource(
ep['id'], 'external_policy', 'external_policies',
expected_res_status=400,
consumed_policy_rule_sets={prs['id']: ''})
class TestNatPool(GroupPolicyPluginTestCase):
def test_nat_pool_shared_create(self):
def combination(np, es):
return {'np': np, 'es': es}
allowed = [combination(False, False), combination(True, True),
combination(False, True)]
for shared in allowed:
es = self.create_external_segment(
shared=shared['es'])['external_segment']
self.create_nat_pool(external_segment_id=es['id'],
shared=shared['np'], expected_res_status=201)
def test_nat_pool_shared_create_negative(self):
es = self.create_external_segment(
shared=False)['external_segment']
res = self.create_nat_pool(external_segment_id=es['id'],
shared=True, expected_res_status=400)
self.assertEqual('SharedResourceReferenceError',
res['NeutronError']['type'])
def test_nat_pool_shared_update(self):
np = self.create_nat_pool(shared=False)['nat_pool']
for shared in [False, True]:
es = self.create_external_segment(
shared=shared)['external_segment']
self._update_gbp_resource(
np['id'], 'nat_pool', 'nat_pools', expected_res_status=200,
external_segment_id=es['id'])
np = self.create_nat_pool(shared=True)['nat_pool']
es = self.create_external_segment(
shared=True)['external_segment']
# Verify shared NP on shared ES
self._update_gbp_resource(
np['id'], 'nat_pool', 'nat_pools', expected_res_status=200,
external_segment_id=es['id'])
# Verify unshare NP
self._update_gbp_resource(
np['id'], 'nat_pool', 'nat_pools', expected_res_status=200,
shared=False)
class TestGroupPolicyPluginGroupResources(
GroupPolicyPluginTestCase, tgpdb.TestGroupResources):