Represent External Connectivity in GBP: API-DB

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: I9799e6e8ce439bf45d5aef3d800c90df29c06fb3
This commit is contained in:
Ivar Lazzaro
2014-11-26 18:41:48 -08:00
parent 0ff6e01eee
commit 672f53f1b4
10 changed files with 1386 additions and 39 deletions

View File

@@ -14,6 +14,7 @@ import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.orm import exc
from neutron.api.v2 import attributes as attr
from neutron.common import log
from neutron import context
from neutron.db import common_db_mixin
@@ -30,6 +31,18 @@ from gbp.neutron.services.grouppolicy.common import constants as gp_constants
LOG = logging.getLogger(__name__)
MAX_IPV4_SUBNET_PREFIX_LENGTH = 31
MAX_IPV6_SUBNET_PREFIX_LENGTH = 127
ADDRESS_NOT_SPECIFIED = ''
class HasNameDescription(object):
name = sa.Column(sa.String(50))
description = sa.Column(sa.String(255))
class BaseSharedGbpResource(models_v2.HasId, models_v2.HasTenant,
HasNameDescription):
shared = sa.Column(sa.Boolean)
pass
class PolicyTarget(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
@@ -118,6 +131,21 @@ class L2Policy(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
shared = sa.Column(sa.Boolean)
class ESToL3PAssociation(model_base.BASEV2):
"""Many to many consuming relation between ESs and L3Ps."""
__tablename__ = 'gp_es_to_l3p_associations'
__table_args__ = (
sa.UniqueConstraint('external_segment_id', 'allocated_address'),
)
l3_policy_id = sa.Column(sa.String(36), sa.ForeignKey('gp_l3_policies.id'),
primary_key=True)
external_segment_id = sa.Column(
sa.String(36), sa.ForeignKey('gp_external_segments.id'),
primary_key=True)
allocated_address = sa.Column(sa.String(64), nullable=False,
primary_key=True)
class L3Policy(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
"""Represents a L3 Policy with a non-overlapping IP address space."""
__tablename__ = 'gp_l3_policies'
@@ -133,6 +161,9 @@ class L3Policy(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
subnet_prefix_length = sa.Column(sa.Integer, nullable=False)
l2_policies = orm.relationship(L2Policy, backref='l3_policy')
shared = sa.Column(sa.Boolean)
external_segments = orm.relationship(
ESToL3PAssociation, backref='l3_policies',
cascade='all, delete-orphan')
class NetworkServiceParam(model_base.BASEV2, models_v2.HasId):
@@ -235,6 +266,28 @@ class PolicyAction(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
shared = sa.Column(sa.Boolean)
class EPToPRSProvidingAssociation(model_base.BASEV2):
"""Many to many providing relation between EPs and Policy Rule Sets."""
__tablename__ = 'gp_ep_to_prs_providing_associations'
policy_rule_set_id = sa.Column(sa.String(36),
sa.ForeignKey('gp_policy_rule_sets.id'),
primary_key=True)
external_policy_id = sa.Column(
sa.String(36), sa.ForeignKey('gp_external_policies.id'),
primary_key=True)
class EPToPRSConsumingAssociation(model_base.BASEV2):
"""Many to many consuming relation between EPs and Policy Rule Sets."""
__tablename__ = 'gp_ep_to_prs_consuming_associations'
policy_rule_set_id = sa.Column(sa.String(36),
sa.ForeignKey('gp_policy_rule_sets.id'),
primary_key=True)
external_policy_id = sa.Column(
sa.String(36), sa.ForeignKey('gp_external_policies.id'),
primary_key=True)
class PolicyRuleSet(model_base.BASEV2, models_v2.HasTenant):
"""It is a collection of Policy rules."""
__tablename__ = 'gp_policy_rule_sets'
@@ -256,9 +309,81 @@ class PolicyRuleSet(model_base.BASEV2, models_v2.HasTenant):
consuming_policy_target_groups = orm.relationship(
PTGToPRSConsumingAssociation,
backref='consumed_policy_rule_set', lazy="joined", cascade='all')
providing_external_policies = orm.relationship(
EPToPRSProvidingAssociation,
backref='provided_policy_rule_set', lazy="joined", cascade='all')
consuming_external_policies = orm.relationship(
EPToPRSConsumingAssociation,
backref='consumed_policy_rule_set', lazy="joined", cascade='all')
shared = sa.Column(sa.Boolean)
class NATPool(model_base.BASEV2, BaseSharedGbpResource):
__tablename__ = 'gp_nat_pools'
ip_version = sa.Column(sa.Integer, nullable=False)
ip_pool = sa.Column(sa.String(64), nullable=False)
external_segment_id = sa.Column(
sa.String(36), sa.ForeignKey('gp_external_segments.id'))
class ExternalRoute(model_base.BASEV2):
__tablename__ = 'gp_external_routes'
external_segment_id = sa.Column(
sa.String(36), sa.ForeignKey('gp_external_segments.id',
ondelete='CASCADE'),
primary_key=True)
destination = sa.Column(sa.String(64), nullable=False, primary_key=True)
nexthop = sa.Column(sa.String(64), primary_key=True)
class EPToESAssociation(model_base.BASEV2):
"""Many to many consuming relation between ESs and EPs."""
__tablename__ = 'gp_es_to_ep_associations'
external_policy_id = sa.Column(
sa.String(36), sa.ForeignKey('gp_external_policies.id'),
primary_key=True)
external_segment_id = sa.Column(
sa.String(36), sa.ForeignKey('gp_external_segments.id'),
primary_key=True)
class ExternalSegment(model_base.BASEV2, BaseSharedGbpResource):
__tablename__ = 'gp_external_segments'
type = sa.Column(sa.String(15))
__mapper_args__ = {
'polymorphic_on': type,
'polymorphic_identity': 'base'
}
ip_version = sa.Column(sa.Integer, nullable=False)
cidr = sa.Column(sa.String(64), nullable=False)
port_address_translation = sa.Column(sa.Boolean)
nat_pools = orm.relationship(NATPool, backref='external_segment')
external_policies = orm.relationship(
EPToESAssociation, backref='external_segments',
cascade='all, delete-orphan')
l3_policies = orm.relationship(
ESToL3PAssociation, backref='external_segments',
cascade='all, delete-orphan')
external_routes = orm.relationship(
ExternalRoute, backref='external_segment',
cascade='all, delete-orphan')
class ExternalPolicy(model_base.BASEV2, BaseSharedGbpResource):
__tablename__ = 'gp_external_policies'
external_segments = orm.relationship(
EPToESAssociation,
backref='external_policies', cascade='all, delete-orphan')
provided_policy_rule_sets = orm.relationship(
EPToPRSProvidingAssociation,
backref='providing_external_policies',
cascade='all, delete-orphan')
consumed_policy_rule_sets = orm.relationship(
EPToPRSConsumingAssociation,
backref='consuming_external_policies',
cascade='all, delete-orphan')
class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
common_db_mixin.CommonDbMixin):
"""GroupPolicy plugin interface implementation using SQLAlchemy models."""
@@ -271,6 +396,13 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
def __init__(self, *args, **kwargs):
super(GroupPolicyDbPlugin, self).__init__(*args, **kwargs)
def _find_gbp_resource(self, context, type, id, on_fail=None):
try:
return self._get_by_id(context, type, id)
except exc.NoResultFound:
if on_fail:
raise on_fail(id=id)
def _get_policy_target(self, context, policy_target_id):
try:
return self._get_by_id(context, PolicyTarget, policy_target_id)
@@ -341,6 +473,21 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
policy_rule_set_id=policy_rule_set_id)
return policy_rule_set
def _get_external_policy(self, context, external_policy_id):
return self._find_gbp_resource(
context, ExternalPolicy, external_policy_id,
gpolicy.ExternalPolicyNotFound)
def _get_external_segment(self, context, external_segment_id):
return self._find_gbp_resource(
context, ExternalSegment, external_segment_id,
gpolicy.ExternalSegmentNotFound)
def _get_nat_pool(self, context, nat_pool_id):
return self._find_gbp_resource(
context, NATPool, nat_pool_id,
gpolicy.NATPoolNotFound)
@staticmethod
def _get_min_max_ports_from_range(port_range):
if not port_range:
@@ -378,7 +525,7 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
# New list of actions is valid so we will first reset the existing
# list and then add each action in order.
# Note that the list could be empty in which case we interpret
# it as clearing existing rules.
# it as clering existing rules.
pr_db.policy_actions = []
for action_id in action_id_list:
assoc = PolicyRuleActionAssociation(policy_rule_id=pr_db.id,
@@ -403,14 +550,31 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
def _set_providers_or_consumers_for_policy_target_group(
self, context, ptg_db, policy_rule_sets_dict, provider=True):
assoc_table = (PTGToPRSProvidingAssociation if provider else
PTGToPRSConsumingAssociation)
self._set_providers_or_consumers_for_res(
context, 'policy_target_group', ptg_db, policy_rule_sets_dict,
assoc_table, provider=provider)
def _set_providers_or_consumers_for_ep(
self, context, ep_db, policy_rule_sets_dict, provider=True):
assoc_table = (EPToPRSProvidingAssociation if provider else
EPToPRSConsumingAssociation)
self._set_providers_or_consumers_for_res(
context, 'external_policy', ep_db, policy_rule_sets_dict,
assoc_table, provider=provider)
def _set_providers_or_consumers_for_res(
self, context, type, db_res, policy_rule_sets_dict, assoc_table,
provider=True):
# TODO(Sumit): Check that the same policy_rule_set ID does not belong
# to provider and consumer dicts
if not policy_rule_sets_dict:
if provider:
ptg_db.provided_policy_rule_sets = []
db_res.provided_policy_rule_sets = []
return
else:
ptg_db.consumed_policy_rule_sets = []
db_res.consumed_policy_rule_sets = []
return
with context.session.begin(subtransactions=True):
policy_rule_sets_id_list = policy_rule_sets_dict.keys()
@@ -420,22 +584,19 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
# New list of policy_rule_sets is valid so we will first reset the
# existing list and then add each policy_rule_set.
# Note that the list could be empty in which case we interpret
# it as clearing existing rules.
# it as clering existing rules.
if provider:
ptg_db.provided_policy_rule_sets = []
db_res.provided_policy_rule_sets = []
else:
ptg_db.consumed_policy_rule_sets = []
db_res.consumed_policy_rule_sets = []
for policy_rule_set_id in policy_rule_sets_id_list:
kwargs = {type + '_id': db_res.id,
'policy_rule_set_id': policy_rule_set_id}
assoc = assoc_table(**kwargs)
if provider:
assoc = PTGToPRSProvidingAssociation(
policy_target_group_id=ptg_db.id,
policy_rule_set_id=policy_rule_set_id)
ptg_db.provided_policy_rule_sets.append(assoc)
db_res.provided_policy_rule_sets.append(assoc)
else:
assoc = PTGToPRSConsumingAssociation(
policy_target_group_id=ptg_db.id,
policy_rule_set_id=policy_rule_set_id)
ptg_db.consumed_policy_rule_sets.append(assoc)
db_res.consumed_policy_rule_sets.append(assoc)
def _set_children_for_policy_rule_set(self, context,
policy_rule_set_db, child_id_list):
@@ -462,7 +623,7 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
# New list of child policy_rule_sets is valid so we will first
# reset the existing list and then add each policy_rule_set.
# Note that the list could be empty in which case we interpret
# it as clearing existing child policy_rule_sets.
# it as clering existing child policy_rule_sets.
policy_rule_set_db.child_policy_rule_sets = []
for child in policy_rule_sets_in_db:
policy_rule_set_db.child_policy_rule_sets.append(child)
@@ -487,7 +648,7 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
# New list of rules is valid so we will first reset the existing
# list and then add each rule in order.
# Note that the list could be empty in which case we interpret
# it as clearing existing rules.
# it as clering existing rules.
prs_db.policy_rules = []
for rule_id in rule_id_list:
prs_rule_db = PRSToPRAssociation(
@@ -495,17 +656,28 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
policy_rule_set_id=prs_db.id)
prs_db.policy_rules.append(prs_rule_db)
def _process_policy_rule_sets_for_ptg(self, context, ptg_db, ptg):
def _process_policy_rule_sets_for_ptg(self, context, db_res, ptg):
if 'provided_policy_rule_sets' in ptg:
self._set_providers_or_consumers_for_policy_target_group(
context, ptg_db, ptg['provided_policy_rule_sets'])
context, db_res, ptg['provided_policy_rule_sets'])
del ptg['provided_policy_rule_sets']
if 'consumed_policy_rule_sets' in ptg:
self._set_providers_or_consumers_for_policy_target_group(
context, ptg_db, ptg['consumed_policy_rule_sets'], False)
context, db_res, ptg['consumed_policy_rule_sets'], False)
del ptg['consumed_policy_rule_sets']
return ptg
def _process_policy_rule_sets_for_ep(self, context, db_res, res):
if 'provided_policy_rule_sets' in res:
self._set_providers_or_consumers_for_ep(
context, db_res, res['provided_policy_rule_sets'])
del res['provided_policy_rule_sets']
if 'consumed_policy_rule_sets' in res:
self._set_providers_or_consumers_for_ep(
context, db_res, res['consumed_policy_rule_sets'], False)
del res['consumed_policy_rule_sets']
return res
def _set_l3_policy_for_l2_policy(self, context, l2p_id, l3p_id):
with context.session.begin(subtransactions=True):
l2p_db = self._get_l2_policy(context, l2p_id)
@@ -539,6 +711,67 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
nsp_db.network_service_params.append(param_db)
del network_service_policy['network_service_params']
def _set_ess_for_ep(self, context, ep_db, es_id_list):
if not es_id_list:
ep_db.external_segments = []
return
with context.session.begin(subtransactions=True):
filters = {'id': es_id_list}
eps_in_db = self._get_collection_query(
context, ExternalSegment, filters=filters)
not_found = set(es_id_list) - set(ep['id'] for ep in eps_in_db)
if not_found:
raise gpolicy.ExternalSegmentNotFound(
id=not_found.pop())
ep_db.external_segments = []
for ep_id in es_id_list:
assoc = EPToESAssociation(
external_policy_id=ep_db.id,
external_segment_id=ep_id)
ep_db.external_segments.append(assoc)
def _process_segment_ers(self, context, es_db, es):
if es['external_routes'] is not attr.ATTR_NOT_SPECIFIED:
es_db.external_routes = []
for rt in es['external_routes']:
target = ExternalRoute(
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):
if es_dict is attr.ATTR_NOT_SPECIFIED:
return
if not es_dict:
l3p_db.external_segments = []
return
with context.session.begin(subtransactions=True):
# Validate ESs exist
es_set = set(es_dict.keys())
filters = {'id': es_set}
es_in_db = self._get_collection_query(
context, ExternalSegment, filters=filters)
not_found = es_set - set(es['id'] for es in es_in_db)
if not_found:
raise gpolicy.ExternalSegmentNotFound(
id=not_found.pop())
l3p_db.external_segments = []
for es in es_in_db:
if not es_dict[es['id']]:
assoc = ESToL3PAssociation(
external_segment_id=es['id'],
l3_policy_id=l3p_db['id'],
allocated_address=ADDRESS_NOT_SPECIFIED)
l3p_db.external_segments.append(assoc)
else:
# Create address allocation
for ip in es_dict[es['id']]:
assoc = ESToL3PAssociation(
external_segment_id=es['id'],
l3_policy_id=l3p_db['id'], allocated_address=ip)
l3p_db.external_segments.append(assoc)
def _make_policy_target_dict(self, pt, fields=None):
res = {'id': pt['id'],
'tenant_id': pt['tenant_id'],
@@ -588,6 +821,13 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
'shared': l3p.get('shared', False), }
res['l2_policies'] = [l2p['id']
for l2p in l3p['l2_policies']]
es_dict = {}
for es in l3p['external_segments']:
es_id = es['external_segment_id']
if es_id not in es_dict:
es_dict[es_id] = []
es_dict[es_id].append(es['allocated_address'])
res['external_segments'] = es_dict
return self._fields(res, fields)
def _make_network_service_policy_dict(self, nsp, fields=None):
@@ -672,9 +912,67 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
res['providing_policy_target_groups'] = [
ptg['policy_target_group_id']
for ptg in prs['providing_policy_target_groups']]
res['consuming_policy_target_groups'] = [
ptg['policy_target_group_id']
for ptg in prs['consuming_policy_target_groups']]
res['providing_external_policies'] = [
ptg['external_policy_id']
for ptg in prs['providing_external_policies']]
res['consuming_external_policies'] = [
ptg['external_policy_id']
for ptg in prs['consuming_external_policies']]
return self._fields(res, fields)
def _make_external_segment_dict(self, es, fields=None):
res = {'id': es['id'],
'tenant_id': es['tenant_id'],
'name': es['name'],
'description': es['description'],
'shared': es.get('shared', False),
'ip_version': es['ip_version'],
'cidr': es['cidr'],
'port_address_translation': es['port_address_translation']}
res['external_routes'] = [{'destination': er['destination'],
'nexthop': er['nexthop']} for er in
es['external_routes']]
res['nat_pools'] = [np['id'] for np in es['nat_pools']]
res['external_policies'] = [
ep['external_policy_id']
for ep in es['external_policies']]
res['l3_policies'] = [
l3p['l3_policy_id'] for l3p in es['l3_policies']]
return self._fields(res, fields)
def _make_external_policy_dict(self, ep, fields=None):
res = {'id': ep['id'],
'tenant_id': ep['tenant_id'],
'name': ep['name'],
'description': ep['description'],
'shared': ep.get('shared', False), }
res['external_segments'] = [
es['external_segment_id']
for es in ep['external_segments']]
res['provided_policy_rule_sets'] = [
pprs['policy_rule_set_id'] for pprs in
ep['provided_policy_rule_sets']]
res['consumed_policy_rule_sets'] = [
cprs['policy_rule_set_id'] for cprs in
ep['consumed_policy_rule_sets']]
return self._fields(res, fields)
def _make_nat_pool_dict(self, np, fields=None):
res = {'id': np['id'],
'tenant_id': np['tenant_id'],
'name': np['name'],
'description': np['description'],
'shared': np.get('shared', False),
'ip_version': np['ip_version'],
'ip_pool': np['ip_pool'],
'external_segment_id': np['external_segment_id']}
return self._fields(res, fields)
def _get_policy_rule_policy_rule_sets(self, context, policy_rule_id):
@@ -687,6 +985,17 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
context.session.query(PolicyRuleActionAssociation).filter_by(
policy_action_id=policy_action_id)]
def _get_external_segment_external_policies(self, context, es_id):
return [x['external_policy_id'] for x in
context.session.query(EPToESAssociation).filter_by(
external_segment_id=es_id)]
def _get_attribute(self, attrs, key):
value = attrs.get(key)
if value is attr.ATTR_NOT_SPECIFIED:
value = None
return value
@staticmethod
def validate_subnet_prefix_length(ip_version, new_prefix_length):
if (new_prefix_length < 2) or (
@@ -870,7 +1179,10 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
ip_pool=l3p['ip_pool'],
subnet_prefix_length=l3p['subnet_prefix_length'],
shared=l3p.get('shared', False))
context.session.add(l3p_db)
if 'external_segments' in l3p:
self._set_ess_for_l3p(context, l3p_db,
l3p['external_segments'])
context.session.add(l3p_db)
return self._make_l3_policy_dict(l3p_db)
@log.log
@@ -882,6 +1194,10 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
self.validate_subnet_prefix_length(
l3p_db.ip_version,
l3p['subnet_prefix_length'])
if 'external_segments' in l3p:
self._set_ess_for_l3p(context, l3p_db,
l3p['external_segments'])
del l3p['external_segments']
l3p_db.update(l3p)
return self._make_l3_policy_dict(l3p_db)
@@ -1206,3 +1522,155 @@ class GroupPolicyDbPlugin(gpolicy.GroupPolicyPluginBase,
def get_policy_rule_sets_count(self, context, filters=None):
return self._get_collection_count(context, PolicyRuleSet,
filters=filters)
@log.log
def create_external_policy(self, context, external_policy):
ep = external_policy['external_policy']
tenant_id = self._get_tenant_id_for_create(context, ep)
with context.session.begin(subtransactions=True):
ep_db = ExternalPolicy(
id=uuidutils.generate_uuid(), tenant_id=tenant_id,
name=ep['name'], description=ep['description'],
shared=ep.get('shared', False))
context.session.add(ep_db)
if 'external_segments' in ep:
self._set_ess_for_ep(context, ep_db,
ep['external_segments'])
self._process_policy_rule_sets_for_ep(context, ep_db, ep)
return self._make_external_policy_dict(ep_db)
@log.log
def update_external_policy(self, context, external_policy_id,
external_policy):
ep = external_policy['external_policy']
with context.session.begin(subtransactions=True):
ep_db = self._get_external_policy(
context, external_policy_id)
if 'external_segments' in ep:
self._set_ess_for_ep(context, ep_db,
ep['external_segments'])
del ep['external_segments']
self._process_policy_rule_sets_for_ep(context, ep_db, ep)
ep_db.update(ep)
return self._make_external_policy_dict(ep_db)
@log.log
def get_external_policies(self, context, filters=None, fields=None):
return self._get_collection(context, ExternalPolicy,
self._make_external_policy_dict,
filters=filters, fields=fields)
@log.log
def get_external_policies_count(self, context, filters=None):
return self._get_collection_count(context, ExternalPolicy,
filters=filters)
@log.log
def get_external_policy(self, context, external_policy_id, fields=None):
ep = self._get_external_policy(
context, external_policy_id)
return self._make_external_policy_dict(ep, fields)
@log.log
def delete_external_policy(self, context, external_policy_id):
with context.session.begin(subtransactions=True):
ep_db = self._get_external_policy(
context, external_policy_id)
context.session.delete(ep_db)
@log.log
def create_external_segment(self, context, external_segment):
es = external_segment['external_segment']
tenant_id = self._get_tenant_id_for_create(context, es)
with context.session.begin(subtransactions=True):
es_db = ExternalSegment(
id=uuidutils.generate_uuid(), tenant_id=tenant_id,
name=es['name'], description=es['description'],
shared=es.get('shared', False), ip_version=es['ip_version'],
cidr=es['cidr'],
port_address_translation=es['port_address_translation'])
context.session.add(es_db)
if 'external_routes' in es:
self._process_segment_ers(context, es_db, es)
return self._make_external_segment_dict(es_db)
@log.log
def update_external_segment(self, context, external_segment_id,
external_segment):
es = external_segment['external_segment']
with context.session.begin(subtransactions=True):
es_db = self._get_external_segment(
context, external_segment_id)
if 'external_routes' in es:
self._process_segment_ers(context, es_db, es)
del es['external_routes']
es_db.update(es)
return self._make_external_segment_dict(es_db)
@log.log
def get_external_segments(self, context, filters=None, fields=None):
return self._get_collection(context, ExternalSegment,
self._make_external_segment_dict,
filters=filters, fields=fields)
@log.log
def get_external_segments_count(self, context, filters=None):
return self._get_collection_count(context, ExternalSegment,
filters=filters)
@log.log
def get_external_segment(self, context, external_segment_id, fields=None):
es = self._get_external_segment(
context, external_segment_id)
return self._make_external_segment_dict(es, fields)
@log.log
def delete_external_segment(self, context, external_segment_id):
with context.session.begin(subtransactions=True):
es_db = self._get_external_segment(
context, external_segment_id)
context.session.delete(es_db)
@log.log
def create_nat_pool(self, context, nat_pool):
np = nat_pool['nat_pool']
tenant_id = self._get_tenant_id_for_create(context, np)
with context.session.begin(subtransactions=True):
np_db = NATPool(
id=uuidutils.generate_uuid(), tenant_id=tenant_id,
name=np['name'], description=np['description'],
shared=np.get('shared', False), ip_version=np['ip_version'],
ip_pool=np['ip_pool'],
external_segment_id=np['external_segment_id'])
context.session.add(np_db)
return self._make_nat_pool_dict(np_db)
@log.log
def update_nat_pool(self, context, nat_pool_id, nat_pool):
np = nat_pool['nat_pool']
with context.session.begin(subtransactions=True):
np_db = self._get_nat_pool(
context, nat_pool_id)
np_db.update(np)
return self._make_nat_pool_dict(np_db)
@log.log
def get_nat_pools(self, context, filters=None, fields=None):
return self._get_collection(context, NATPool,
self._make_nat_pool_dict,
filters=filters, fields=fields)
@log.log
def get_nat_pools_count(self, context, filters=None):
return self._get_collection_count(context, NATPool, filters=filters)
@log.log
def get_nat_pool(self, context, nat_pool_id, fields=None):
np = self._get_nat_pool(context, nat_pool_id)
return self._make_nat_pool_dict(np, fields)
@log.log
def delete_nat_pool(self, context, nat_pool_id):
with context.session.begin(subtransactions=True):
np_db = self._get_nat_pool(context, nat_pool_id)
context.session.delete(np_db)

View File

@@ -241,6 +241,9 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
router_id=router
)
l3p_db.routers.append(assoc)
if 'external_segments' in l3p:
self._set_ess_for_l3p(context, l3p_db,
l3p['external_segments'])
return self._make_l3_policy_dict(l3p_db)
@log.log
@@ -269,5 +272,9 @@ class GroupPolicyMappingDbPlugin(gpdb.GroupPolicyDbPlugin):
context.session.delete(assoc)
# Don't update l3p_db.routers with router IDs.
del l3p['routers']
if 'external_segments' in l3p:
self._set_ess_for_l3p(context, l3p_db,
l3p['external_segments'])
del l3p['external_segments']
l3p_db.update(l3p)
return self._make_l3_policy_dict(l3p_db)

View File

@@ -1 +1 @@
f4d890a9c126
f16efdc10a71

View File

@@ -0,0 +1,151 @@
# Copyright 2014 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
""" external_connectivity
Revision ID: f16efdc10a71
"""
# revision identifiers, used by Alembic.
revision = 'f16efdc10a71'
down_revision = 'f4d890a9c126'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.create_table(
'gp_external_segments',
sa.Column('id', sa.String(36), nullable=False),
sa.Column('tenant_id', sa.String(length=255), nullable=True),
sa.Column('name', sa.String(length=50), nullable=True),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('shared', sa.Boolean),
sa.Column('ip_version', sa.Integer, nullable=False),
sa.Column('cidr', sa.String(64), nullable=False),
sa.Column('port_address_translation', sa.Boolean),
sa.PrimaryKeyConstraint('id'))
op.create_table(
'gp_external_policies',
sa.Column('id', sa.String(36), nullable=False),
sa.Column('tenant_id', sa.String(length=255), nullable=True),
sa.Column('name', sa.String(length=50), nullable=True),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('shared', sa.Boolean),
sa.PrimaryKeyConstraint('id'))
op.create_table(
'gp_ep_to_prs_providing_associations',
sa.Column('policy_rule_set_id', sa.String(length=36), nullable=True),
sa.ForeignKeyConstraint(['policy_rule_set_id'],
['gp_policy_rule_sets.id'],
ondelete='CASCADE'),
sa.Column('external_policy_id', sa.String(length=36),
nullable=True),
sa.ForeignKeyConstraint(['external_policy_id'],
['gp_external_policies.id'],
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('policy_rule_set_id',
'external_policy_id'))
op.create_table(
'gp_ep_to_prs_consuming_associations',
sa.Column('policy_rule_set_id', sa.String(length=36), nullable=True),
sa.ForeignKeyConstraint(['policy_rule_set_id'],
['gp_policy_rule_sets.id'],
ondelete='CASCADE'),
sa.Column('external_policy_id', sa.String(length=36),
nullable=True),
sa.ForeignKeyConstraint(['external_policy_id'],
['gp_external_policies.id'],
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('policy_rule_set_id',
'external_policy_id'))
op.create_table(
'gp_es_to_ep_associations',
sa.Column('external_policy_id', sa.String(length=36),
nullable=True),
sa.ForeignKeyConstraint(['external_policy_id'],
['gp_external_policies.id'],
ondelete='CASCADE'),
sa.Column('external_segment_id', sa.String(length=36),
nullable=True),
sa.ForeignKeyConstraint(['external_segment_id'],
['gp_external_segments.id'],
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('external_policy_id',
'external_segment_id'))
op.create_table(
'gp_external_routes',
sa.Column('destination', sa.String(64)),
sa.Column('nexthop', sa.String(64)),
sa.Column('external_segment_id', sa.String(length=36),
nullable=True),
sa.ForeignKeyConstraint(['external_segment_id'],
['gp_external_segments.id'],
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('external_segment_id',
'destination', 'nexthop'))
op.create_table(
'gp_nat_pools',
sa.Column('id', sa.String(36), nullable=False),
sa.Column('tenant_id', sa.String(length=255), nullable=True),
sa.Column('name', sa.String(length=50), nullable=True),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('shared', sa.Boolean),
sa.Column('ip_version', sa.Integer, nullable=False),
sa.Column('ip_pool', sa.String(64), nullable=False),
sa.Column('external_segment_id', sa.String(length=36),
nullable=True),
sa.ForeignKeyConstraint(['external_segment_id'],
['gp_external_segments.id']),
sa.PrimaryKeyConstraint('id'))
op.create_table(
'gp_es_to_l3p_associations',
sa.Column('l3_policy_id', sa.String(length=36), nullable=True),
sa.ForeignKeyConstraint(['l3_policy_id'],
['gp_l3_policies.id'],
ondelete='CASCADE'),
sa.Column('external_segment_id', sa.String(length=36),
nullable=True),
sa.ForeignKeyConstraint(['external_segment_id'],
['gp_external_segments.id'],
ondelete='CASCADE'),
sa.Column('allocated_address', sa.String(64), nullable=False,
primary_key=True),
sa.PrimaryKeyConstraint(
'l3_policy_id', 'external_segment_id', 'allocated_address'),
sa.UniqueConstraint('external_segment_id', 'allocated_address'),
)
def downgrade():
op.drop_table('gp_es_to_l3p_associations')
op.drop_table('gp_nat_pools')
op.drop_table('gp_external_routes')
op.drop_table('gp_es_to_ep_associations')
op.drop_table('gp_ep_to_prs_consuming_associations')
op.drop_table('gp_ep_to_prs_providing_associations')
op.drop_table('gp_external_policies')
op.drop_table('gp_external_segments')

View File

@@ -19,6 +19,7 @@ from neutron.api.v2 import attributes as attr
from neutron.api.v2 import resource_helper
from neutron.common import exceptions as nexc
from neutron.openstack.common import log as logging
from neutron.openstack.common import uuidutils
from neutron.plugins.common import constants
from neutron.services import service_base
@@ -83,6 +84,18 @@ class PolicyRuleSetNotFound(nexc.NotFound):
message = _("Policy Rule Set %(policy_rule_set_id)s could not be found")
class ExternalPolicyNotFound(nexc.NotFound):
message = _("External Policy %(id)s could not be found")
class ExternalSegmentNotFound(nexc.NotFound):
message = _("External Segment %(id)s could not be found")
class NATPoolNotFound(nexc.NotFound):
message = _("NAT Pool %(id)s could not be found")
class BadPolicyRuleSetRelationship(nexc.BadRequest):
message = _("Policy Rule Set %(parent_id)s is an invalid parent for "
"%(child_id)s, make sure that child policy_rule_set has no "
@@ -160,6 +173,13 @@ def convert_port_to_string(value):
return str(value)
def convert_to_int_if_needed(value):
if not value or value is attr.ATTR_NOT_SPECIFIED:
return value
else:
return attr.convert_to_int(value)
def _validate_gbp_port_range(data, key_specs=None):
if data is None:
return
@@ -226,8 +246,57 @@ def _validate_network_svc_params(data, key_specs=None):
return msg
def _validate_external_dict(data, key_specs=None):
if data is None:
return
if not isinstance(data, dict):
msg = _("'%s' is not a dictionary") % data
LOG.debug(msg)
return msg
for d in data:
if not uuidutils.is_uuid_like(d):
msg = _("'%s' is not a valid UUID") % d
LOG.debug(msg)
return msg
if not isinstance(data[d], list):
msg = _("'%s' is not a list") % data[d]
LOG.debug(msg)
return msg
def _validate_gbproutes(data, valid_values=None):
# Shamelessly copied from Neutron, will pass even if nexthop is valid
if not isinstance(data, list):
msg = _("Invalid data format for hostroute: '%s'") % data
LOG.debug(msg)
return msg
expected_keys = ['destination', 'nexthop']
hostroutes = []
for hostroute in data:
msg = attr._verify_dict_keys(expected_keys, hostroute)
if msg:
LOG.debug(msg)
return msg
msg = attr._validate_subnet(hostroute['destination'])
if msg:
LOG.debug(msg)
return msg
if hostroute['nexthop'] is not None:
msg = attr._validate_ip_address(hostroute['nexthop'])
if msg:
LOG.debug(msg)
return msg
if hostroute in hostroutes:
msg = _("Duplicate hostroute '%s'") % hostroute
LOG.debug(msg)
return msg
hostroutes.append(hostroute)
attr.validators['type:gbp_port_range'] = _validate_gbp_port_range
attr.validators['type:network_service_params'] = _validate_network_svc_params
attr.validators['type:external_dict'] = _validate_external_dict
attr.validators['type:gbproutes'] = _validate_gbproutes
POLICY_TARGETS = 'policy_targets'
@@ -239,7 +308,9 @@ POLICY_ACTIONS = 'policy_actions'
POLICY_RULES = 'policy_rules'
POLICY_RULE_SETS = 'policy_rule_sets'
NETWORK_SERVICE_POLICIES = 'network_service_policies'
EXTERNAL_POLICIES = 'external_policies'
EXTERNAL_SEGMENTS = 'external_segments'
NAT_POOLS = 'nat_pools'
RESOURCE_ATTRIBUTE_MAP = {
POLICY_TARGETS: {
@@ -361,6 +432,10 @@ RESOURCE_ATTRIBUTE_MAP = {
'default': False, 'convert_to': attr.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
'external_segments': {
'allow_post': True, 'allow_put': True, 'default': None,
'validate': {'type:external_dict': None},
'convert_to': attr.convert_none_to_empty_dict, 'is_visible': True},
},
POLICY_CLASSIFIERS: {
'id': {'allow_post': False, 'allow_put': False,
@@ -506,6 +581,109 @@ RESOURCE_ATTRIBUTE_MAP = {
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
},
EXTERNAL_POLICIES: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'external_segments': {
'allow_post': True, 'allow_put': True, 'default': None,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list, 'is_visible': True},
'provided_policy_rule_sets': {'allow_post': True, 'allow_put': True,
'validate': {'type:dict_or_none': None},
'convert_to':
attr.convert_none_to_empty_dict,
'default': None, 'is_visible': True},
'consumed_policy_rule_sets': {'allow_post': True, 'allow_put': True,
'validate': {'type:dict_or_none': None},
'convert_to':
attr.convert_none_to_empty_dict,
'default': None, 'is_visible': True},
attr.SHARED: {'allow_post': True, 'allow_put': True,
'default': False, 'convert_to': attr.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
},
EXTERNAL_SEGMENTS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'ip_version': {'allow_post': True, 'allow_put': False,
'convert_to': attr.convert_to_int,
'validate': {'type:values': [4, 6]},
'default': 4, 'is_visible': True},
'cidr': {'allow_post': True, 'allow_put': False,
'validate': {'type:subnet': None},
'default': '172.16.0.0/12', 'is_visible': True},
'external_policies': {
'allow_post': False, 'allow_put': False, 'default': None,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list, 'is_visible': True},
'external_routes': {
'allow_post': True, 'allow_put': True,
'default': attr.ATTR_NOT_SPECIFIED,
'validate': {'type:gbproutes': None},
'is_visible': True},
'l3_policies': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid_list': None},
'convert_to': attr.convert_none_to_empty_list,
'default': None, 'is_visible': True},
'port_address_translation': {
'allow_post': True, 'allow_put': True,
'default': False, 'convert_to': attr.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
attr.SHARED: {'allow_post': True, 'allow_put': True,
'default': False, 'convert_to': attr.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
},
NAT_POOLS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'default': '', 'is_visible': True},
'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None},
'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None},
'required_by_policy': True, 'is_visible': True},
'ip_version': {'allow_post': True, 'allow_put': False,
'convert_to': attr.convert_to_int,
'validate': {'type:values': [4, 6]},
'default': 4, 'is_visible': True},
'ip_pool': {'allow_post': True, 'allow_put': False,
'validate': {'type:subnet': None},
'is_visible': True},
'external_segment_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:uuid_or_none': None},
'is_visible': True, 'required': True},
attr.SHARED: {'allow_post': True, 'allow_put': True,
'default': False, 'convert_to': attr.convert_to_boolean,
'is_visible': True, 'required_by_policy': True,
'enforce_policy': True},
}
}
@@ -535,7 +713,8 @@ class Group_policy(extensions.ExtensionDescriptor):
def get_resources(cls):
special_mappings = {
'l2_policies': 'l2_policy', 'l3_policies': 'l3_policy',
'network_service_policies': 'network_service_policy'}
'network_service_policies': 'network_service_policy',
'external_policies': 'external_policy'}
plural_mappings = resource_helper.build_plural_mappings(
special_mappings, RESOURCE_ATTRIBUTE_MAP)
attr.PLURALS.update(plural_mappings)
@@ -757,3 +936,66 @@ class GroupPolicyPluginBase(service_base.ServicePluginBase):
@abc.abstractmethod
def delete_policy_rule_set(self, context, policy_rule_set_id):
pass
@abc.abstractmethod
def create_external_policy(self, context, external_policy):
pass
@abc.abstractmethod
def update_external_policy(self, context, external_policy_id,
external_policy):
pass
@abc.abstractmethod
def get_external_policies(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_external_policy(self, context, external_policy_id,
fields=None):
pass
@abc.abstractmethod
def delete_external_policy(self, context, external_policy_id):
pass
@abc.abstractmethod
def create_external_segment(self, context, external_segment):
pass
@abc.abstractmethod
def update_external_segment(self, context, external_segment_id,
external_segment):
pass
@abc.abstractmethod
def get_external_segments(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_external_segment(self, context, external_segment_id, fields=None):
pass
@abc.abstractmethod
def delete_external_segment(self, context, external_segment_id):
pass
@abc.abstractmethod
def create_nat_pool(self, context, nat_pool):
pass
@abc.abstractmethod
def update_nat_pool(self, context, nat_pool_id, nat_pool):
pass
@abc.abstractmethod
def get_nat_pools(self, context, filters=None, fields=None):
pass
@abc.abstractmethod
def get_nat_pool(self, context, nat_pool_id, fields=None):
pass
@abc.abstractmethod
def delete_nat_pool(self, context, nat_pool_id):
pass

View File

@@ -69,7 +69,8 @@ class GroupPolicyPlugin(group_policy_mapping_db.GroupPolicyMappingDbPlugin):
'policy_classifier': {},
'policy_rule_set': {
'parent_id': 'policy_rule_set',
'policy_rules': 'policy_rule'}}
'policy_rules': 'policy_rule'},
}
_plurals = None
@property

View File

@@ -11,9 +11,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
import webob.exc
from neutron.api import extensions
from neutron.api.v2 import attributes as nattr
from neutron import context
from neutron.openstack.common import importutils
from neutron.openstack.common import uuidutils
@@ -94,11 +96,14 @@ class GroupPolicyDBTestBase(object):
def _get_test_l3_policy_attrs(self, name='l3p1',
description='test l3_policy',
ip_version=4, ip_pool='10.0.0.0/8',
subnet_prefix_length=24):
subnet_prefix_length=24,
external_segments=None):
external_segments = external_segments or {}
attrs = {'name': name, 'description': description,
'tenant_id': self._tenant_id, 'ip_version': ip_version,
'ip_pool': ip_pool,
'subnet_prefix_length': subnet_prefix_length}
'subnet_prefix_length': subnet_prefix_length,
'external_segments': external_segments}
return attrs
@@ -160,6 +165,45 @@ class GroupPolicyDBTestBase(object):
return attrs
def _get_test_ep_attrs(self, name='ep_1', description='test ep',
external_segments=None,
provided_policy_rule_sets=None,
consumed_policy_rule_sets=None):
pprs_ids = cprs_ids = es_ids = []
if provided_policy_rule_sets:
pprs_ids = [pprs_id for pprs_id in provided_policy_rule_sets]
if consumed_policy_rule_sets:
cprs_ids = [cc_id for cc_id in consumed_policy_rule_sets]
if external_segments:
es_ids = [es_id for es_id in external_segments]
attrs = {'name': name, 'description': description,
'tenant_id': self._tenant_id,
'external_segments': es_ids,
'provided_policy_rule_sets': pprs_ids,
'consumed_policy_rule_sets': cprs_ids}
return attrs
def _get_test_es_attrs(self, name='es_1', description='test es',
ip_version=4, cidr='172.0.0.0/8',
external_routes=None,
port_address_translation=False):
ear = external_routes or []
attrs = {'name': name, 'description': description,
'ip_version': ip_version,
'tenant_id': self._tenant_id, 'cidr': cidr,
'external_routes': ear,
'port_address_translation': port_address_translation}
return attrs
def _get_test_np_attrs(self, name='es_1', description='test es',
ip_version=4, ip_pool='10.160.0.0/16',
external_segment_id=None):
attrs = {'name': name, 'description': description,
'ip_version': ip_version,
'tenant_id': self._tenant_id, 'ip_pool': ip_pool,
'external_segment_id': external_segment_id}
return attrs
def create_policy_target(self, policy_target_group_id=None,
expected_res_status=None, **kwargs):
defaults = {'name': 'pt1', 'description': 'test pt'}
@@ -230,7 +274,7 @@ class GroupPolicyDBTestBase(object):
def create_l3_policy(self, expected_res_status=None, **kwargs):
defaults = {'name': 'l3p1', 'description': 'test l3_policy',
'ip_version': 4, 'ip_pool': '10.0.0.0/8',
'subnet_prefix_length': 24}
'subnet_prefix_length': 24, 'external_segments': {}}
defaults.update(kwargs)
data = {'l3_policy': {'tenant_id': self._tenant_id}}
@@ -358,6 +402,41 @@ class GroupPolicyDBTestBase(object):
return prs
def _create_gbp_resource(self, type, expected_res_status, body):
plural = self._get_resource_plural(type)
data = {type: body}
req = self.new_create_request(plural, data, self.fmt)
res = req.get_response(self.ext_api)
if expected_res_status:
self.assertEqual(res.status_int, expected_res_status)
elif res.status_int >= webob.exc.HTTPClientError.code:
raise webob.exc.HTTPClientError(code=res.status_int)
prs = self.deserialize(self.fmt, res)
return prs
def create_external_policy(self, expected_res_status=None, **kwargs):
defaults = {'name': 'ptg1', 'description': 'test ptg',
'provided_policy_rule_sets': {},
'consumed_policy_rule_sets': {},
'tenant_id': self._tenant_id,
'external_segments': []}
defaults.update(kwargs)
return self._create_gbp_resource(
'external_policy', expected_res_status, defaults)
def create_external_segment(self, expected_res_status=None, **kwargs):
body = self._get_test_es_attrs()
body.update(kwargs)
return self._create_gbp_resource(
'external_segment', expected_res_status, body)
def create_nat_pool(self, expected_res_status=None, **kwargs):
body = self._get_test_np_attrs()
body.update(kwargs)
return self._create_gbp_resource(
'nat_pool', expected_res_status, body)
class GroupPolicyDBTestPlugin(gpdb.GroupPolicyDbPlugin):
@@ -379,7 +458,7 @@ class GroupPolicyDbTestCase(GroupPolicyDBTestBase,
self.plugin = importutils.import_object(gp_plugin)
if not service_plugins:
service_plugins = {'gp_plugin_name': gp_plugin}
nattr.PLURALS['nat_pools'] = 'nat_pool'
super(GroupPolicyDbTestCase, self).setUp(
plugin=core_plugin, ext_mgr=ext_mgr,
service_plugins=service_plugins
@@ -594,19 +673,22 @@ class TestGroupResources(GroupPolicyDbTestCase):
ctx, l2p_id)
def test_create_and_show_l3_policy(self):
attrs = self._get_test_l3_policy_attrs()
es = self.create_external_segment()['external_segment']
es_dict = {es['id']: ['172.0.0.2', '172.0.0.3']}
attrs = self._get_test_l3_policy_attrs(
external_segments=es_dict)
l3p = self.create_l3_policy()
l3p = self.create_l3_policy(external_segments=es_dict)
for k, v in attrs.iteritems():
self.assertEqual(l3p['l3_policy'][k], v)
self.assertEqual(v, l3p['l3_policy'][k])
req = self.new_show_request('l3_policies', l3p['l3_policy']['id'],
fmt=self.fmt)
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
for k, v in attrs.iteritems():
self.assertEqual(res['l3_policy'][k], v)
self.assertEqual(v, res['l3_policy'][k])
self._test_show_resource('l3_policy', l3p['l3_policy']['id'], attrs)
@@ -630,13 +712,17 @@ class TestGroupResources(GroupPolicyDbTestCase):
name = "new_l3_policy"
description = 'new desc'
prefix_length = 26
es = self.create_external_segment()['external_segment']
es_dict = {es['id']: ['172.0.0.2', '172.0.0.3']}
attrs = self._get_test_l3_policy_attrs(
name=name, description=description,
subnet_prefix_length=prefix_length)
subnet_prefix_length=prefix_length,
external_segments=es_dict)
l3p = self.create_l3_policy()
data = {'l3_policy': {'name': name, 'description': description,
'subnet_prefix_length': prefix_length}}
'subnet_prefix_length': prefix_length,
'external_segments': es_dict}}
req = self.new_update_request('l3_policies', data,
l3p['l3_policy']['id'])
@@ -1080,3 +1166,158 @@ class TestGroupResources(GroupPolicyDbTestCase):
req = self.new_update_request('policy_rule_sets', data, prs['id'])
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code)
def _test_create_and_show(self, type, attrs, expected=None):
plural = self._get_resource_plural(type)
res = self._create_gbp_resource(type, None, attrs)
expected = expected or attrs
for k, v in expected.iteritems():
self.assertEqual(v, res[type][k])
req = self.new_show_request(plural, res[type]['id'], fmt=self.fmt)
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
for k, v in expected.iteritems():
self.assertEqual(v, res[type][k])
self._test_show_resource(type, res[type]['id'], expected)
def test_create_and_show_ep(self):
es = self.create_external_segment()['external_segment']
prs = self.create_policy_rule_set()['policy_rule_set']
attrs = {'external_segments': [es['id']],
'provided_policy_rule_sets': {prs['id']: None},
'consumed_policy_rule_sets': {prs['id']: None}}
body = self._get_test_ep_attrs()
body.update(attrs)
expected = copy.deepcopy(body)
expected['provided_policy_rule_sets'] = [prs['id']]
expected['consumed_policy_rule_sets'] = [prs['id']]
self._test_create_and_show('external_policy', body,
expected=expected)
def test_create_and_show_es(self):
route = {'destination': '0.0.0.0/0', 'nexthop': '172.0.0.1'}
attrs = self._get_test_es_attrs(external_routes=[route])
self._test_create_and_show('external_segment', attrs)
def test_create_and_show_np(self):
es = self.create_external_segment()['external_segment']
attrs = self._get_test_np_attrs(external_segment_id=es['id'])
self._test_create_and_show('nat_pool', attrs)
def test_list_ep(self):
external_policies = [
self.create_external_policy(name='ep1', description='ep'),
self.create_external_policy(name='ep2', description='ep'),
self.create_external_policy(name='ep3', description='ep')]
self._test_list_resources('external_policy',
external_policies,
query_params='description=ep')
def test_list_es(self):
external_segments = [
self.create_external_segment(name='es1', description='es'),
self.create_external_segment(name='es2', description='es'),
self.create_external_segment(name='es3', description='es')]
self._test_list_resources('external_segment',
external_segments,
query_params='description=es')
def test_update_external_policy(self):
name = 'new_ep'
description = 'new desc'
es = self.create_external_segment()['external_segment']
prs = self.create_policy_rule_set()['policy_rule_set']
attrs = self._get_test_ep_attrs(
name=name, description=description,
external_segments=[es['id']],
provided_policy_rule_sets={prs['id']: None},
consumed_policy_rule_sets={prs['id']: None})
ep = self.create_external_policy()['external_policy']
data = {'external_policy': {
'name': name, 'description': description,
'external_segments': [es['id']],
'provided_policy_rule_sets': {prs['id']: None},
'consumed_policy_rule_sets': {prs['id']: None}}}
req = self.new_update_request('external_policies', data,
ep['id'])
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
res = res['external_policy']
for k, v in attrs.iteritems():
self.assertEqual(v, res[k])
self._test_show_resource('external_policy', ep['id'], attrs)
def test_update_external_segment(self):
name = 'new_es'
description = 'new desc'
route = {'destination': '0.0.0.0/0', 'nexthop': '172.0.0.1'}
attrs = self._get_test_es_attrs(name=name, description=description,
external_routes=[route])
es = self.create_external_segment()['external_segment']
data = {'external_segment': {
'name': name, 'description': description,
'external_routes': [route]}}
req = self.new_update_request('external_segments', data,
es['id'])
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
res = res['external_segment']
for k, v in attrs.iteritems():
self.assertEqual(res[k], v)
self._test_show_resource('external_segment', es['id'], attrs)
def test_update_nat_pool(self):
name = 'new_np'
description = 'new desc'
es = self.create_external_segment()['external_segment']
attrs = self._get_test_np_attrs(name=name, description=description,
external_segment_id=es['id'])
np = self.create_nat_pool()['nat_pool']
data = {'nat_pool': {
'name': name, 'description': description,
'external_segment_id': es['id']}}
req = self.new_update_request('nat_pools', data,
np['id'])
res = self.deserialize(self.fmt, req.get_response(self.ext_api))
res = res['nat_pool']
for k, v in attrs.iteritems():
self.assertEqual(v, res[k])
self._test_show_resource('nat_pool', np['id'], attrs)
def test_delete_ep(self):
ctx = context.get_admin_context()
ep = self.create_external_policy()
ep_id = ep['external_policy']['id']
req = self.new_delete_request('external_policies', ep_id)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, webob.exc.HTTPNoContent.code)
self.assertRaises(gpolicy.ExternalPolicyNotFound,
self.plugin.get_external_policy, ctx, ep_id)
def test_delete_es(self):
ctx = context.get_admin_context()
ep = self.create_external_segment()
ep_id = ep['external_segment']['id']
req = self.new_delete_request('external_segments', ep_id)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, webob.exc.HTTPNoContent.code)
self.assertRaises(gpolicy.ExternalSegmentNotFound,
self.plugin.get_external_segment, ctx, ep_id)
def test_delete_np(self):
ctx = context.get_admin_context()
ep = self.create_nat_pool()
ep_id = ep['nat_pool']['id']
req = self.new_delete_request('nat_pools', ep_id)
res = req.get_response(self.ext_api)
self.assertEqual(res.status_int, webob.exc.HTTPNoContent.code)
self.assertRaises(gpolicy.NATPoolNotFound,
self.plugin.get_nat_pool, ctx, ep_id)

View File

@@ -84,10 +84,12 @@ class GroupPolicyMappingDbTestCase(tgpdb.GroupPolicyDbTestCase,
def _get_test_l3_policy_attrs(self, name='l3p1',
description='test l3_policy',
ip_version=4, ip_pool='10.0.0.0/8',
subnet_prefix_length=24, routers=None):
subnet_prefix_length=24, routers=None,
external_segments=None):
attrs = (super(GroupPolicyMappingDbTestCase, self).
_get_test_l3_policy_attrs(name, description, ip_version,
ip_pool, subnet_prefix_length))
ip_pool, subnet_prefix_length,
external_segments))
attrs.update({'routers': routers or []})
return attrs

View File

@@ -37,6 +37,13 @@ POLICY_CLASSIFIERS_URI = GROUPPOLICY_URI + '/' + 'policy_classifiers'
POLICY_ACTIONS_URI = GROUPPOLICY_URI + '/' + 'policy_actions'
POLICY_RULE_SETS_URI = GROUPPOLICY_URI + '/' + 'policy_rule_sets'
NET_SVC_POLICIES_URI = GROUPPOLICY_URI + '/' + 'network_service_policies'
EAP_POLICIES_URI = GROUPPOLICY_URI + '/' + 'external_policies'
EAS_POLICIES_URI = GROUPPOLICY_URI + '/' + 'external_segments'
NP_POLICIES_URI = GROUPPOLICY_URI + '/' + 'nat_pools'
RES_TO_URI = {'external_policy': EAP_POLICIES_URI,
'external_segment': EAS_POLICIES_URI,
'nat_pool': NP_POLICIES_URI}
class GroupPolicyExtensionTestCase(test_api_v2_extension.ExtensionTestCase):
@@ -46,7 +53,8 @@ class GroupPolicyExtensionTestCase(test_api_v2_extension.ExtensionTestCase):
super(GroupPolicyExtensionTestCase, self).setUp()
plural_mappings = {
'l2_policy': 'l2_policies', 'l3_policy': 'l3_policies',
'network_service_policy': 'network_service_policies'}
'network_service_policy': 'network_service_policies',
'external_policy': 'external_policies'}
self._setUpExtension(
GP_PLUGIN_BASE_NAME, constants.GROUP_POLICY,
gp.RESOURCE_ATTRIBUTE_MAP, gp.Group_policy, GROUPPOLICY_URI,
@@ -372,12 +380,13 @@ class GroupPolicyExtensionTestCase(test_api_v2_extension.ExtensionTestCase):
def _get_create_l3_policy_default_attrs(self):
return {'name': '', 'description': '', 'ip_version': 4,
'ip_pool': '10.0.0.0/8', 'subnet_prefix_length': 24,
'shared': False}
'external_segments': {}, 'shared': False}
def _get_create_l3_policy_attrs(self):
return {'name': 'l3p1', 'tenant_id': _uuid(),
'description': 'test L3 policy', 'ip_version': 6,
'ip_pool': 'fd01:2345:6789::/48',
'external_segments': {_uuid(): ['192.168.0.3']},
'subnet_prefix_length': 64, 'shared': False}
def _get_update_l3_policy_attrs(self):
@@ -1016,6 +1025,209 @@ class GroupPolicyExtensionTestCase(test_api_v2_extension.ExtensionTestCase):
def test_delete_network_service_policy(self):
self._test_entity_delete('network_service_policy')
def _test_entity_create(self, entity, data, expected_value,
default_data=None, non_specified=None):
default_data = default_data or data
create_method = getattr(self.instance, 'create_%s' % entity)
create_method.return_value = expected_value
res = self.api.post(_get_path(RES_TO_URI[entity], fmt=self.fmt),
self.serialize(data),
content_type='application/%s' % self.fmt)
default_data[entity].update(non_specified or {})
kwargs = {entity: default_data}
create_method.assert_called_once_with(
mock.ANY, **kwargs)
self.assertEqual(exc.HTTPCreated.code, res.status_int)
res = self.deserialize(res)
self.assertIn(entity, res)
self.assertEqual(expected_value, res[entity])
def _test_create_entity_with_defaults(self, entity, default_attrs,
non_specified=None):
entity_id = _uuid()
data = {entity: {'tenant_id': _uuid()}}
default_data = copy.copy(data)
default_data[entity].update(default_attrs)
expected_value = copy.deepcopy(default_data[entity])
expected_value['id'] = entity_id
self._test_entity_create(entity, data, expected_value, default_data,
non_specified)
def _test_create_entity_with_attrs(self, entity, attrs):
entity_id = _uuid()
data = {entity: attrs}
expected_value = copy.deepcopy(data[entity])
expected_value['id'] = entity_id
self._test_entity_create(entity, data, expected_value)
def _test_get_entity(self, entity, list=False):
entity_id = _uuid()
value = {'tenant_id': _uuid(), 'id': entity_id}
expected_value = value if not list else [value]
resource = entity if not list else self._plural_mappings.get(
entity, entity + 's')
list_method = getattr(self.instance, 'get_%s' % resource)
list_method.return_value = expected_value
kwargs = {'fmt': self.fmt}
if not list:
kwargs['id'] = entity_id
res = self.api.get(_get_path(RES_TO_URI[entity], **kwargs))
if list:
list_method.assert_called_once_with(mock.ANY, fields=mock.ANY,
filters=mock.ANY)
else:
list_method.assert_called_once_with(mock.ANY, entity_id,
fields=mock.ANY)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn(resource, res)
self.assertEqual(expected_value, res[resource])
def _test_update_entity(self, entity, attrs):
entity_id = _uuid()
update_data = {entity: attrs}
expected_value = {'tenant_id': _uuid(), 'id': entity_id}
update_method = getattr(self.instance, 'update_%s' % entity)
update_method.return_value = expected_value
res = self.api.put(_get_path(RES_TO_URI[entity], id=entity_id,
fmt=self.fmt),
self.serialize(update_data))
kwargs = {entity: update_data}
update_method.assert_called_once_with(mock.ANY, entity_id, **kwargs)
self.assertEqual(exc.HTTPOk.code, res.status_int)
res = self.deserialize(res)
self.assertIn(entity, res)
self.assertEqual(expected_value, res[entity])
def _get_create_external_policy_default_attrs(self):
return {'name': '', 'description': '',
'external_segments': [],
'provided_policy_rule_sets': {},
'consumed_policy_rule_sets': {},
'shared': False}
def _get_create_external_policy_attrs(self):
return {'name': 'ep1', 'tenant_id': _uuid(),
'description': 'test ep',
'external_segments': [_uuid()],
'provided_policy_rule_sets': {_uuid(): None},
'consumed_policy_rule_sets': {_uuid(): None},
'shared': False}
def _get_update_external_policy_attrs(self):
return {'name': 'new_name'}
def test_create_external_policy_with_defaults(self):
default_attrs = self._get_create_external_policy_default_attrs()
self._test_create_entity_with_defaults('external_policy',
default_attrs)
def test_create_external_policy(self):
attrs = self._get_create_external_policy_attrs()
self._test_create_entity_with_attrs('external_policy', attrs)
def test_list_external_policies(self):
self._test_get_entity('external_policy', list=True)
def test_get_external_policy(self):
self._test_get_entity('external_policy')
def test_update_external_policy(self):
update_data = self._get_update_external_policy_attrs()
self._test_update_entity('external_policy', update_data)
def test_delete_external_policy_(self):
self._test_entity_delete('external_policy')
def _get_create_external_segment_default_attrs(self):
return {'name': '', 'description': '',
'external_routes': [],
'ip_version': 4,
'cidr': '172.16.0.0/12',
'port_address_translation': False,
'shared': False}
def _get_create_external_segment_attrs(self):
return {'name': 'es1', 'tenant_id': _uuid(),
'description': 'test ep',
'external_routes': [{'destination': '0.0.0.0/0',
'nexthop': '192.168.0.1'}],
'cidr': '192.168.0.0/24',
'ip_version': 4, 'port_address_translation': True,
'shared': False}
def _get_update_external_segment_attrs(self):
return {'name': 'new_name'}
def test_create_external_segment_with_defaults(self):
default_attrs = (
self._get_create_external_segment_default_attrs())
self._test_create_entity_with_defaults('external_segment',
default_attrs)
def test_create_external_segment(self):
attrs = self._get_create_external_segment_attrs()
self._test_create_entity_with_attrs('external_segment', attrs)
def test_list_external_segments(self):
self._test_get_entity('external_segment', list=True)
def test_get_external_segment(self):
self._test_get_entity('external_segment')
def test_update_external_segment(self):
update_data = self._get_update_external_segment_attrs()
self._test_update_entity('external_segment', update_data)
def test_delete_external_segment_(self):
self._test_entity_delete('external_segment')
def _get_create_nat_pool_default_attrs(self):
return {'name': '', 'description': '',
'external_segment_id': None, 'ip_version': 4,
'ip_pool': '172.16.0.0/16',
'shared': False}
def _get_create_nat_pool_attrs(self):
return {'name': 'es1', 'tenant_id': _uuid(),
'description': 'test ep',
'ip_version': 4,
'ip_pool': '172.16.0.0/16',
'external_segment_id': _uuid(),
'shared': False}
def _get_update_nat_pool_attrs(self):
return {'name': 'new_name'}
def test_create_nat_pool_with_defaults(self):
default_attrs = (
self._get_create_nat_pool_default_attrs())
self._test_create_entity_with_defaults('nat_pool',
default_attrs)
def test_create_nat_pool(self):
attrs = self._get_create_nat_pool_attrs()
self._test_create_entity_with_attrs('nat_pool', attrs)
def test_list_nat_pools(self):
self._test_get_entity('nat_pool', list=True)
def test_get_nat_pool(self):
self._test_get_entity('nat_pool')
def test_update_nat_pool(self):
update_data = self._get_update_nat_pool_attrs()
self._test_update_entity('nat_pool', update_data)
def test_delete_nat_pool_(self):
self._test_entity_delete('nat_pool')
class TestGroupPolicyAttributeConverters(base.BaseTestCase):
@@ -1129,3 +1341,24 @@ class TestGroupPolicyAttributeValidators(base.BaseTestCase):
msg = gp._validate_network_svc_params(test_params)
self.assertEqual(
msg, "Network service param value 'subnet' is not supported")
def test_validate_external_dict(self):
self.assertIsNone(gp._validate_external_dict(None))
uuid = uuidutils.generate_uuid()
uuid_2 = uuidutils.generate_uuid()
correct = [{uuid: []}, {}, {uuid: ['192.168.1.1']},
{uuid_2: ['192.168.0.1'], uuid: []}]
for x in correct:
self.assertIsNone(gp._validate_external_dict(x))
incorrect = 'not_a_dict'
self.assertEqual(gp._validate_external_dict(incorrect),
"'%s' is not a dictionary" % incorrect)
not_a_uuid = 'not_a_uuid'
incorrect = {'not_a_uuid': []}
self.assertEqual(gp._validate_external_dict(incorrect),
"'%s' is not a valid UUID" % not_a_uuid)
not_a_list = 'not_a_list'
incorrect = {uuid: not_a_list}
self.assertEqual(gp._validate_external_dict(incorrect),
"'%s' is not a list" % not_a_list)

View File

@@ -38,7 +38,9 @@ class GroupPolicyMappingExtTestCase(tgp.GroupPolicyExtensionTestCase):
plural_mappings = {'l2_policy': 'l2_policies',
'l3_policy': 'l3_policies',
'network_service_policy':
'network_service_policies'}
'network_service_policies',
'external_policy':
'external_policies'}
self._setUpExtension(
tgp.GP_PLUGIN_BASE_NAME, constants.GROUP_POLICY, attr_map,
gp.Group_policy, tgp.GROUPPOLICY_URI,