Add an ODL mapping driver for odl/gbp
Define an ODL mapping driver, provide methods to:
create/delete l3-policy
create/delete l2-policy
create/delete policy target group
create/delete policy target, associated with rule sets
create/delete policy action
create DHCP PT if needed
Add UT for ODL mapping driver
Change-Id: I78c4a20d7280ef60b612884214a746d8ad014f09
Signed-off-by: Yi Yang <yyos1999@gmail.com>
Author: Yi Yang <yyos1999@gmail.com>
Co-Authored-By: Yapeng Wu <yapengwu@gmail.com>
This commit is contained in:
589
gbp/neutron/services/grouppolicy/drivers/odl/odl_mapping.py
Normal file
589
gbp/neutron/services/grouppolicy/drivers/odl/odl_mapping.py
Normal file
@@ -0,0 +1,589 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
from gbp.neutron.db.grouppolicy import group_policy_mapping_db as gpdb
|
||||
from gbp.neutron.services.grouppolicy.common import constants as g_const
|
||||
from gbp.neutron.services.grouppolicy.common import exceptions as gpexc
|
||||
from gbp.neutron.services.grouppolicy.drivers.odl import odl_manager
|
||||
from gbp.neutron.services.grouppolicy.drivers import resource_mapping as api
|
||||
from neutron import manager
|
||||
from neutron.openstack.common import lockutils # noqa
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.common import constants
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UpdateL3PolicyNotSupportedOnOdlDriver(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Update L3 Policy currently not supported on ODL GBP "
|
||||
"driver.")
|
||||
|
||||
|
||||
class UpdateL2PolicyNotSupportedOnOdlDriver(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Update L2 Policy currently not supported on ODL GBP "
|
||||
"driver.")
|
||||
|
||||
|
||||
class UpdatePTNotSupportedOnOdlDriver(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Update Policy Target currently not supported on ODL GBP "
|
||||
"driver.")
|
||||
|
||||
|
||||
class UpdatePTGNotSupportedOnOdlDriver(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Update Policy Target Group currently not supported on ODL "
|
||||
"GBP driver.")
|
||||
|
||||
|
||||
class L2PolicyMultiplePolicyTargetGroupNotSupportedOnOdlDriver(
|
||||
gpexc.GroupPolicyBadRequest):
|
||||
message = _("An L2 policy can't have multiple policy target groups on "
|
||||
"ODL GBP driver.")
|
||||
|
||||
|
||||
class UpdatePolicyActionNotSupportedOnOdlDriver(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Update Policy Action currently not supported on ODL GBP "
|
||||
"driver.")
|
||||
|
||||
|
||||
class RedirectActionNotSupportedOnOdlDriver(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Redirect action is currently not supported for ODL GBP "
|
||||
"driver.")
|
||||
|
||||
|
||||
class OnlyAllowActionSupportedOnOdlDriver(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Currently only allow action is supported for ODL GBP "
|
||||
"driver.")
|
||||
|
||||
|
||||
class UpdateClassifierNotSupportedOnOdlDriver(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Update Policy Classifier currently not supported on ODL GBP "
|
||||
"driver.")
|
||||
|
||||
|
||||
class PolicyRuleUpdateNotSupportedOnOdlDriver(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Policy rule update is not supported on for ODL GBP"
|
||||
"driver.")
|
||||
|
||||
|
||||
class ExactlyOneActionPerRuleIsSupportedOnOdlDriver(
|
||||
gpexc.GroupPolicyBadRequest):
|
||||
message = _("Exactly one action per rule is supported on ODL GBP driver.")
|
||||
|
||||
|
||||
class ClassifierTcpUdpPortRangeNotSupportedOnOdlDriver(
|
||||
gpexc.GroupPolicyBadRequest):
|
||||
message = _("Tcp or Udp port range is not supported on ODL GBP driver.")
|
||||
|
||||
|
||||
class ClassifierUnknownIPProtocolNotSupportedOnOdlDriver(
|
||||
gpexc.GroupPolicyBadRequest):
|
||||
message = _("Unknown IP Protocol is not supported on ODL GBP driver.")
|
||||
|
||||
|
||||
class OdlMappingDriver(api.ResourceMappingDriver):
|
||||
"""ODL Mapping driver for Group Policy plugin.
|
||||
|
||||
This driver implements group policy semantics by mapping group
|
||||
policy resources to various other neutron resources, and leverages
|
||||
ODL backend for enforcing the policies.
|
||||
"""
|
||||
|
||||
me = None
|
||||
manager = None
|
||||
|
||||
@staticmethod
|
||||
def get_odl_manager():
|
||||
if not OdlMappingDriver.manager:
|
||||
OdlMappingDriver.manager = odl_manager.OdlManager()
|
||||
return OdlMappingDriver.manager
|
||||
|
||||
def initialize(self):
|
||||
super(OdlMappingDriver, self).initialize()
|
||||
self.odl_manager = OdlMappingDriver.get_odl_manager()
|
||||
self._gbp_plugin = None
|
||||
OdlMappingDriver.me = self
|
||||
|
||||
@property
|
||||
def gbp_plugin(self):
|
||||
if not self._gbp_plugin:
|
||||
self._gbp_plugin = (manager.NeutronManager.get_service_plugins()
|
||||
.get("GROUP_POLICY"))
|
||||
return self._gbp_plugin
|
||||
|
||||
@staticmethod
|
||||
def get_initialized_instance():
|
||||
return OdlMappingDriver.me
|
||||
|
||||
def create_dhcp_policy_target_if_needed(self, plugin_context, port):
|
||||
session = plugin_context.session
|
||||
if (self._port_is_owned(session, port['id'])):
|
||||
# Nothing to do
|
||||
return
|
||||
|
||||
# Retrieve PTG
|
||||
# TODO(ywu): optimize later
|
||||
subnets = self._core_plugin._get_subnets_by_network(
|
||||
plugin_context, port['network_id']
|
||||
)
|
||||
ptg = (plugin_context.session.query(gpdb.PolicyTargetGroupMapping).
|
||||
join(gpdb.PolicyTargetGroupMapping.subnets).
|
||||
filter(gpdb.PTGToSubnetAssociation.subnet_id ==
|
||||
subnets[0]['id']).
|
||||
first())
|
||||
|
||||
# Create PolicyTarget
|
||||
attrs = {'policy_target':
|
||||
{'tenant_id': port['tenant_id'],
|
||||
'name': 'dhcp-%s' % ptg['id'],
|
||||
'description': ("Implicitly created DHCP policy "
|
||||
"target"),
|
||||
'policy_target_group_id': ptg['id'],
|
||||
'port_id': port['id']}}
|
||||
self.gbp_plugin.create_policy_target(plugin_context, attrs)
|
||||
# TODO(ODL): security group is not required
|
||||
# sg_id = self._ensure_default_security_group(plugin_context,
|
||||
# port['tenant_id'])
|
||||
# data = {'port': {'security_groups': [sg_id]}}
|
||||
# self._core_plugin.update_port(plugin_context, port['id'], data)
|
||||
|
||||
def create_policy_target_postcommit(self, context):
|
||||
super(OdlMappingDriver, self).create_policy_target_postcommit(context)
|
||||
pt = self._get_pt_detail(context)
|
||||
ep = {
|
||||
"endpoint-group": pt['ptg_id'],
|
||||
"l2-context": pt['l2ctx_id'],
|
||||
"l3-address": pt['l3_list'],
|
||||
"mac-address": pt['mac_address'],
|
||||
"neutron-port-id": pt['neutron_port_id'],
|
||||
"tenant": pt['tenant_id']
|
||||
}
|
||||
self.odl_manager.register_endpoints([ep])
|
||||
|
||||
def update_policy_target_precommit(self, context):
|
||||
raise UpdatePTNotSupportedOnOdlDriver()
|
||||
|
||||
def delete_policy_target_postcommit(self, context):
|
||||
pt = self._get_pt_detail(context)
|
||||
ep = {
|
||||
"l2": pt['l2_list'],
|
||||
"l3": pt['l3_list']
|
||||
}
|
||||
self.odl_manager.unregister_endpoints([ep])
|
||||
# Delete Neutron's port
|
||||
super(OdlMappingDriver, self).delete_policy_target_postcommit(context)
|
||||
|
||||
def create_l3_policy_postcommit(self, context):
|
||||
tenant_id = uuid.UUID(context.current['tenant_id']).urn[9:]
|
||||
l3ctx = {
|
||||
"id": context.current['id'],
|
||||
"name": context.current['name'],
|
||||
"description": context.current['description']
|
||||
}
|
||||
self.odl_manager.create_update_l3_context(tenant_id, l3ctx)
|
||||
|
||||
def update_l3_policy_precommit(self, context):
|
||||
raise UpdateL3PolicyNotSupportedOnOdlDriver()
|
||||
|
||||
def delete_l3_policy_postcommit(self, context):
|
||||
tenant_id = uuid.UUID(context.current['tenant_id']).urn[9:]
|
||||
l3ctx = {
|
||||
"id": context.current['id']
|
||||
}
|
||||
self.odl_manager.delete_l3_context(tenant_id, l3ctx)
|
||||
|
||||
def create_l2_policy_postcommit(self, context):
|
||||
super(OdlMappingDriver, self).create_l2_policy_postcommit(context)
|
||||
tenant_id = uuid.UUID(context.current['tenant_id']).urn[9:]
|
||||
|
||||
# l2_policy mapped to l2_bridge_domain in ODL
|
||||
l2bd = {
|
||||
"id": context.current['id'],
|
||||
"name": context.current['name'],
|
||||
"description": context.current['description'],
|
||||
"parent": context.current['l3_policy_id']
|
||||
}
|
||||
self.odl_manager.create_update_l2_bridge_domain(tenant_id, l2bd)
|
||||
|
||||
# Implicit network within l2 policy mapped to l2 FD in ODL
|
||||
net_id = context.current['network_id']
|
||||
network = self._core_plugin.get_network(context._plugin_context,
|
||||
net_id)
|
||||
l2fd = {
|
||||
"id": net_id,
|
||||
"name": network['name'],
|
||||
"parent": context.current['id']
|
||||
}
|
||||
self.odl_manager.create_update_l2_flood_domain(tenant_id, l2fd)
|
||||
|
||||
def update_l2_policy_precommit(self, context):
|
||||
raise UpdateL2PolicyNotSupportedOnOdlDriver()
|
||||
|
||||
def delete_l2_policy_postcommit(self, context):
|
||||
super(OdlMappingDriver, self).delete_l2_policy_postcommit(context)
|
||||
tenant_id = uuid.UUID(context.current['tenant_id']).urn[9:]
|
||||
|
||||
# l2_policy mapped to l2_bridge_domain in ODL
|
||||
l2bd = {
|
||||
"id": context.current['id']
|
||||
}
|
||||
self.odl_manager.delete_l2_bridge_domain(tenant_id, l2bd)
|
||||
|
||||
# Implicit network within l2 policy mapped to l2 FD in ODL
|
||||
net_id = context.current['network_id']
|
||||
l2fd = {
|
||||
"id": net_id,
|
||||
}
|
||||
self.odl_manager.delete_l2_flood_domain(tenant_id, l2fd)
|
||||
|
||||
def create_policy_target_group_postcommit(self, context):
|
||||
super(OdlMappingDriver, self).create_policy_target_group_postcommit(
|
||||
context)
|
||||
tenant_id = uuid.UUID(context.current['tenant_id']).urn[9:]
|
||||
subnets = context.current['subnets']
|
||||
provided_contract = self._make_odl_contract_and_clause(
|
||||
context, context.current['provided_policy_rule_sets']
|
||||
)
|
||||
consumed_contract = self._make_odl_contract_and_clause(
|
||||
context, context.current['consumed_policy_rule_sets']
|
||||
)
|
||||
|
||||
# PTG mapped to EPG in ODL
|
||||
# TODO(ODL); add back description field after PoC
|
||||
epg = {
|
||||
"id": context.current['id'],
|
||||
"name": context.current['name'],
|
||||
"network-domain": subnets[0]
|
||||
}
|
||||
|
||||
if provided_contract:
|
||||
epg['provider-named-selector'] = {
|
||||
"name": 'Contract-' + provided_contract['id'],
|
||||
"contract": provided_contract['id']
|
||||
}
|
||||
self.odl_manager.create_update_contract(tenant_id,
|
||||
provided_contract)
|
||||
if consumed_contract:
|
||||
epg['consumer-named-selector'] = {
|
||||
"name": 'Contract-' + consumed_contract['id'],
|
||||
"contract": consumed_contract['id']
|
||||
}
|
||||
self.odl_manager.create_update_contract(tenant_id,
|
||||
consumed_contract)
|
||||
|
||||
self.odl_manager.create_update_endpoint_group(tenant_id, epg)
|
||||
|
||||
# Implicit subnet within policy target group mapped to subnet in ODL
|
||||
for subnet_id in subnets:
|
||||
neutron_subnet = self._core_plugin.get_subnet(
|
||||
context._plugin_context, subnet_id
|
||||
)
|
||||
odl_subnet = {
|
||||
"id": subnet_id,
|
||||
"ip-prefix": neutron_subnet['cidr'],
|
||||
"parent": neutron_subnet['network_id'],
|
||||
"virtual-router-ip": neutron_subnet['gateway_ip']
|
||||
}
|
||||
self.odl_manager.create_update_subnet(tenant_id, odl_subnet)
|
||||
|
||||
def _make_odl_contract_and_clause(self, context, rule_sets):
|
||||
# As no contract/clause in O.S., they will be generated dynamically
|
||||
# when rule sets are associated with PTG:
|
||||
# 1. an association is mapped to a contract with single clause
|
||||
# 2. rule sets mapped to subjects
|
||||
# 3. clause name is concatenation of sorted subject names
|
||||
# 4. contract ID is generated based on the clause name
|
||||
# 5. As a combination of same rule sets produce same sorted subject
|
||||
# names, consistent clause name and contract ID are guaranteed
|
||||
contract = None
|
||||
if rule_sets:
|
||||
subjects = []
|
||||
subject_names = []
|
||||
for rule_set_id in rule_sets:
|
||||
# a subject is mapped to a rule set
|
||||
subject = self._make_subject(context, rule_set_id)
|
||||
subjects.append(subject)
|
||||
subject_names.append(subject['name'])
|
||||
clause_name = "-".join(sorted(subject_names)).encode('ascii',
|
||||
'ignore')
|
||||
contract_id = uuid.uuid3(uuid.NAMESPACE_DNS, clause_name).urn[9:]
|
||||
clauses = [
|
||||
{
|
||||
"name": clause_name,
|
||||
"subject-refs": subject_names
|
||||
}
|
||||
]
|
||||
contract = {
|
||||
"id": contract_id,
|
||||
"clause": clauses,
|
||||
"subject": subjects
|
||||
}
|
||||
return contract
|
||||
|
||||
def _make_subject(self, context, rule_set_id):
|
||||
rule_set = context._plugin.get_policy_rule_set(
|
||||
context._plugin_context, rule_set_id
|
||||
)
|
||||
rules = []
|
||||
for rule_id in rule_set['policy_rules']:
|
||||
rule = self._make_odl_rule(context, rule_id)
|
||||
rules.append(rule)
|
||||
return {
|
||||
"name": rule_set['name'],
|
||||
"rule": rules
|
||||
}
|
||||
|
||||
def _make_odl_rule(self, context, rule_id):
|
||||
rule = context._plugin.get_policy_rule(
|
||||
context._plugin_context, rule_id
|
||||
)
|
||||
stack_classifier = context._plugin.get_policy_classifier(
|
||||
context._plugin_context, rule['policy_classifier_id']
|
||||
)
|
||||
|
||||
# while openstack supports only one classifier per rule, the classifier
|
||||
# may mapped to multi classifier in ODL
|
||||
classifier_refs = []
|
||||
classifiers = self._make_odl_classifiers(stack_classifier)
|
||||
for classifier in classifiers:
|
||||
classifier_ref = {
|
||||
"name": classifier['name']
|
||||
}
|
||||
if classifier['direction'] != "bidirectional":
|
||||
classifier_ref['direction'] = classifier['direction']
|
||||
classifier_refs.append(classifier_ref)
|
||||
action_refs = []
|
||||
for action_id in rule['policy_actions']:
|
||||
action = context._plugin.get_policy_action(
|
||||
context._plugin_context, action_id
|
||||
)
|
||||
action_refs.append(
|
||||
{
|
||||
"name": action['name']
|
||||
}
|
||||
)
|
||||
|
||||
# TODO(ODL): send action_refs later but not for PoC
|
||||
return {
|
||||
"name": rule['name'],
|
||||
"classifier-ref": classifier_refs,
|
||||
}
|
||||
|
||||
def update_policy_target_group_precommit(self, context):
|
||||
raise UpdatePTGNotSupportedOnOdlDriver()
|
||||
|
||||
def delete_policy_target_group_postcommit(self, context):
|
||||
# TODO(ODL): delete contract if no one uses it
|
||||
tenant_id = uuid.UUID(context.current['tenant_id']).urn[9:]
|
||||
subnets = context.current['subnets']
|
||||
|
||||
# delete mapped subnets in ODL, and clean them up from neutron
|
||||
for subnet_id in subnets:
|
||||
self._cleanup_subnet(context._plugin_context, subnet_id, None)
|
||||
odl_subnet = {
|
||||
"id": subnet_id
|
||||
}
|
||||
self.odl_manager.delete_subnet(tenant_id, odl_subnet)
|
||||
|
||||
# delete mapped EPG in ODL
|
||||
epg = {
|
||||
"id": context.current['id'],
|
||||
}
|
||||
self.odl_manager.delete_endpoint_group(tenant_id, epg)
|
||||
|
||||
def create_policy_action_precommit(self, context):
|
||||
# TODO(odl): allow redirect for service chaining
|
||||
if context.current['action_type'] == g_const.GP_ACTION_REDIRECT:
|
||||
raise RedirectActionNotSupportedOnOdlDriver()
|
||||
|
||||
def create_policy_action_postcommit(self, context):
|
||||
super(OdlMappingDriver, self).create_policy_action_postcommit(context)
|
||||
# TODO(ODL): remove comment out after PoC
|
||||
# tenant_id = uuid.UUID(context.current['tenant_id']).urn[9:]
|
||||
|
||||
# fill in action instance data
|
||||
if context.current['action_type'] == g_const.GP_ACTION_ALLOW:
|
||||
# TODO(ODL): remove the return and comment out after POC
|
||||
return
|
||||
# action_definition_id = "f942e8fd-e957-42b7-bd18-f73d11266d17"
|
||||
# action_instance = {
|
||||
# "action-definition-id": action_definition_id,
|
||||
# "name": context.current['name'],
|
||||
# "parameter-value": [
|
||||
# {
|
||||
# "name": context.current['name'],
|
||||
# "string-value": context.current['action_type'],
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
# self.odl_manager.create_action(tenant_id, action_instance)
|
||||
else:
|
||||
raise OnlyAllowActionSupportedOnOdlDriver()
|
||||
|
||||
def update_policy_action_precommit(self, context):
|
||||
raise UpdatePolicyActionNotSupportedOnOdlDriver()
|
||||
|
||||
def delete_policy_action_postcommit(self, context):
|
||||
super(OdlMappingDriver, self).delete_policy_action_postcommit(context)
|
||||
# TODO(ODL): remove comment out after PoC
|
||||
# tenant_id = uuid.UUID(context.current['tenant_id']).urn[9:]
|
||||
#
|
||||
# # fill in action instance data
|
||||
# action_instance = {
|
||||
# "name": context.current['name']
|
||||
# }
|
||||
# self.odl_manager.delete_action(tenant_id, action_instance)
|
||||
|
||||
def create_policy_classifier_postcommit(self, context):
|
||||
tenant_id = uuid.UUID(context.current['tenant_id']).urn[9:]
|
||||
classifiers = self._make_odl_classifiers(context.current)
|
||||
|
||||
for classifier in classifiers:
|
||||
classifier_instance = {
|
||||
"classifier-definition-id":
|
||||
classifier['classifier-definition-id'],
|
||||
"name": classifier['name'],
|
||||
"parameter-value": classifier['parameter-value']
|
||||
}
|
||||
self.odl_manager.create_classifier(tenant_id, classifier_instance)
|
||||
|
||||
def _make_odl_classifiers(self, stack_classifier):
|
||||
classifiers = []
|
||||
if stack_classifier['protocol'] == constants.ICMP:
|
||||
direction = stack_classifier['direction']
|
||||
if direction == 'bi':
|
||||
direction = "bidirectional"
|
||||
classifier = {
|
||||
# Use hard coded value based on current ODL implementation
|
||||
"classifier-definition-id":
|
||||
'79c6fdb2-1e1a-4832-af57-c65baf5c2335',
|
||||
"name": stack_classifier['name'],
|
||||
"parameter-value": [
|
||||
{
|
||||
"name": "proto",
|
||||
# TODO(yapeng): change the hard code value
|
||||
"int-value": 1,
|
||||
}
|
||||
],
|
||||
"direction": direction
|
||||
}
|
||||
classifiers.append(classifier)
|
||||
else:
|
||||
# For TCP and UDP protoocol create two classifier (in and out)
|
||||
for port in ['sourceport', 'destport']:
|
||||
if stack_classifier['direction'] == 'in':
|
||||
if port == 'destport':
|
||||
direction = 'in'
|
||||
else:
|
||||
direction = 'out'
|
||||
elif stack_classifier['direction'] == 'out':
|
||||
if port == 'destport':
|
||||
direction = 'out'
|
||||
else:
|
||||
direction = 'in'
|
||||
else:
|
||||
direction = 'bidirectional'
|
||||
|
||||
classifier = {
|
||||
# Use hard coded value based on current ODL implementation
|
||||
"classifier-definition-id":
|
||||
'4250ab32-e8b8-445a-aebb-e1bd2cdd291f',
|
||||
"direction": direction,
|
||||
"name": stack_classifier['name'] + '-' + port,
|
||||
"parameter-value": [
|
||||
{
|
||||
"name": "type",
|
||||
"string-value": stack_classifier['protocol'],
|
||||
},
|
||||
{
|
||||
"name": port,
|
||||
"int-value": stack_classifier['port_range'],
|
||||
}
|
||||
]
|
||||
}
|
||||
classifiers.append(classifier)
|
||||
return classifiers
|
||||
|
||||
def update_policy_classifier_precommit(self, context):
|
||||
raise UpdateClassifierNotSupportedOnOdlDriver()
|
||||
|
||||
def delete_policy_classifier_postcommit(self, context):
|
||||
tenant_id = uuid.UUID(context.current['tenant_id']).urn[9:]
|
||||
|
||||
if context.current['protocol'] == constants.ICMP:
|
||||
# fill in classifier instance data
|
||||
classifier_instance = {
|
||||
"name": context.current['name']
|
||||
}
|
||||
self.odl_manager.delete_classifier(tenant_id, classifier_instance)
|
||||
return
|
||||
|
||||
# fill in classifier instance data
|
||||
for port in ['sourceport', 'destport']:
|
||||
classifier_instance = {
|
||||
"name": context.current['name'] + '-' + port,
|
||||
}
|
||||
self.odl_manager.delete_classifier(tenant_id, classifier_instance)
|
||||
|
||||
def create_policy_rule_precommit(self, context):
|
||||
if ('policy_actions' in context.current and
|
||||
len(context.current['policy_actions']) != 1):
|
||||
# TODO(odl): to be fixed when redirect is supported
|
||||
raise ExactlyOneActionPerRuleIsSupportedOnOdlDriver()
|
||||
|
||||
def update_policy_rule_precommit(self, context):
|
||||
# TODO(ivar): add support for action update on policy rules
|
||||
raise PolicyRuleUpdateNotSupportedOnOdlDriver()
|
||||
|
||||
def _get_pt_detail(self, context):
|
||||
port_id = context.current['port_id']
|
||||
port = self._core_plugin.get_port(context._plugin_context, port_id)
|
||||
tenant_id = uuid.UUID(context.current['tenant_id']).urn[9:]
|
||||
ptg_id = context.current['policy_target_group_id']
|
||||
ptg = self.gbp_plugin.get_policy_target_group(context._plugin_context,
|
||||
ptg_id)
|
||||
l2ctx_id = ptg['l2_policy_id']
|
||||
l2ctx = self.gbp_plugin.get_l2_policy(context._plugin_context,
|
||||
l2ctx_id)
|
||||
l3ctx_id = l2ctx['l3_policy_id']
|
||||
mac_address = port['mac_address']
|
||||
neutron_port_id = 'tap' + port_id[:11]
|
||||
|
||||
l3_list = []
|
||||
for fixed_ip in port['fixed_ips']:
|
||||
l3_list.append(
|
||||
{
|
||||
"ip-address": fixed_ip['ip_address'],
|
||||
"l3-context": l3ctx_id
|
||||
}
|
||||
)
|
||||
|
||||
l2_list = [
|
||||
{
|
||||
"l2-context": l2ctx_id,
|
||||
"mac-address": mac_address
|
||||
}
|
||||
]
|
||||
|
||||
return {
|
||||
"port_id": port_id,
|
||||
"tenant_id": tenant_id,
|
||||
"ptg_id": ptg_id,
|
||||
"l2ctx_id": l2ctx_id,
|
||||
"l3ctx_id": l3ctx_id,
|
||||
"mac_address": mac_address,
|
||||
"neutron_port_id": neutron_port_id,
|
||||
"l3_list": l3_list,
|
||||
"l2_list": l2_list,
|
||||
}
|
||||
383
gbp/neutron/tests/unit/services/grouppolicy/test_odl_mapping.py
Normal file
383
gbp/neutron/tests/unit/services/grouppolicy/test_odl_mapping.py
Normal file
@@ -0,0 +1,383 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from gbp.neutron.services.grouppolicy.common import constants as g_const
|
||||
from gbp.neutron.services.grouppolicy.common import exceptions as gpexc
|
||||
from gbp.neutron.services.grouppolicy import config
|
||||
from gbp.neutron.tests.unit.services.grouppolicy import test_grouppolicy_plugin
|
||||
from neutron.tests.unit.ml2 import test_ml2_plugin
|
||||
|
||||
|
||||
TENANT_ID = '33336c48e9b84dcfb3a70813bb3b3333'
|
||||
|
||||
|
||||
class NoPluginContextError(gpexc.GroupPolicyBadRequest):
|
||||
message = _("Missing Plugin Context")
|
||||
|
||||
|
||||
class FakeCorePlugin(object):
|
||||
|
||||
def __init__(self):
|
||||
self._networks = {}
|
||||
self._subnets = {}
|
||||
self._ports = {}
|
||||
|
||||
def add_network(self, net_id, net):
|
||||
self._networks[net_id] = net
|
||||
|
||||
def get_network(self, plugin_context, net_id):
|
||||
if not plugin_context:
|
||||
raise NoPluginContextError()
|
||||
return self._networks[net_id]
|
||||
|
||||
def add_subnet(self, subnet_id, subnet):
|
||||
self._subnets[subnet_id] = subnet
|
||||
|
||||
def get_subnet(self, plugin_context, subnet_id):
|
||||
if not plugin_context:
|
||||
raise NoPluginContextError()
|
||||
return self._subnets[subnet_id]
|
||||
|
||||
def add_port(self, port_id, port):
|
||||
self._ports[port_id] = port
|
||||
|
||||
def get_port(self, plugin_context, port_id):
|
||||
if not plugin_context:
|
||||
raise NoPluginContextError()
|
||||
return self._ports[port_id]
|
||||
|
||||
|
||||
class FakeGBPPlugin(object):
|
||||
def __init__(self):
|
||||
self._l3ps = {}
|
||||
self._l2ps = {}
|
||||
self._groups = {}
|
||||
self._eps = {}
|
||||
self._classifiers = {}
|
||||
self._actions = {}
|
||||
self._rules = {}
|
||||
self._rule_sets = {}
|
||||
|
||||
def add_l3_policy(self, l3p_id, l3p):
|
||||
self._l3ps[l3p_id] = l3p
|
||||
|
||||
def get_l3_policy(self, plugin_context, l3p_id):
|
||||
if not plugin_context:
|
||||
raise NoPluginContextError()
|
||||
return self._l3ps[l3p_id]
|
||||
|
||||
def add_l2_policy(self, l2p_id, l2p):
|
||||
self._l2ps[l2p_id] = l2p
|
||||
|
||||
def get_l2_policy(self, plugin_context, l2p_id):
|
||||
if not plugin_context:
|
||||
raise NoPluginContextError()
|
||||
return self._l2ps[l2p_id]
|
||||
|
||||
def add_policy_target_group(self, group_id, group):
|
||||
self._groups[group_id] = group
|
||||
|
||||
def get_policy_target_group(self, plugin_context, group_id):
|
||||
if not plugin_context:
|
||||
raise NoPluginContextError()
|
||||
return self._groups[group_id]
|
||||
|
||||
def add_policy_target(self, ep_id, ep):
|
||||
self._eps[ep_id] = ep
|
||||
|
||||
def get_policy_target(self, plugin_context, ep_id):
|
||||
if not plugin_context:
|
||||
raise NoPluginContextError()
|
||||
return self._eps[ep_id]
|
||||
|
||||
def add_policy_classifier(self, classifier_id, classifier):
|
||||
self._classifiers[classifier_id] = classifier
|
||||
|
||||
def get_policy_classifier(self, plugin_context, classifier_id):
|
||||
if not plugin_context:
|
||||
raise NoPluginContextError()
|
||||
return self._classifiers[classifier_id]
|
||||
|
||||
def add_policy_action(self, action_id, action):
|
||||
self._actions[action_id] = action
|
||||
|
||||
def get_policy_action(self, plugin_context, action_id):
|
||||
if not plugin_context:
|
||||
raise NoPluginContextError()
|
||||
return self._actions[action_id]
|
||||
|
||||
def add_policy_rule(self, rule_id, rule):
|
||||
self._rules[rule_id] = rule
|
||||
|
||||
def get_policy_rule(self, plugin_context, rule_id):
|
||||
if not plugin_context:
|
||||
raise NoPluginContextError()
|
||||
return self._rules[rule_id]
|
||||
|
||||
def add_policy_rule_set(self, rule_set_id, rule_set):
|
||||
self._rule_sets[rule_set_id] = rule_set
|
||||
|
||||
def get_policy_rule_set(self, plugin_context, rule_set_id):
|
||||
if not plugin_context:
|
||||
raise NoPluginContextError()
|
||||
return self._rule_sets[rule_set_id]
|
||||
|
||||
|
||||
class OdlMappingTestCase(
|
||||
test_grouppolicy_plugin.GroupPolicyPluginTestCase):
|
||||
|
||||
def setUp(self):
|
||||
config.cfg.CONF.set_override('policy_drivers',
|
||||
['implicit_policy', 'odl'],
|
||||
group='group_policy')
|
||||
super(OdlMappingTestCase, self).setUp(
|
||||
core_plugin=test_ml2_plugin.PLUGIN_NAME)
|
||||
|
||||
|
||||
class TestL3Policy(OdlMappingTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestL3Policy, self).setUp()
|
||||
self._gbp_plugin = FakeGBPPlugin()
|
||||
|
||||
l3p_id = '3333ba05-3333-45ba-ace2-765706b23333'
|
||||
l3p = {
|
||||
'id': l3p_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': 'fake_l3p_name',
|
||||
'description': 'Fake l3 policy for L3P test'
|
||||
}
|
||||
self._gbp_plugin.add_l3_policy(l3p_id, l3p)
|
||||
self._l3p_id = l3p_id
|
||||
|
||||
def _get_l3_policy_operation_context(self):
|
||||
current = self._gbp_plugin.get_l3_policy(
|
||||
"test_l3p_context", self._l3p_id
|
||||
)
|
||||
context = mock.Mock(current=current)
|
||||
return context
|
||||
|
||||
|
||||
class TestL2Policy(OdlMappingTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestL2Policy, self).setUp()
|
||||
self._gbp_plugin = FakeGBPPlugin()
|
||||
|
||||
l2p_id = '2222bdbd-eb07-45ba-ace2-765706b22222'
|
||||
l2p = {
|
||||
'id': l2p_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': 'fake_l2p_name',
|
||||
'description': 'Fake l2 policy for L2P test',
|
||||
'l3_policy_id': '3333ba05-eb07-45ba-ace2-765706b23333',
|
||||
'network_id': '2222eded-eb07-45ba-ace2-765706b22222'
|
||||
}
|
||||
self._gbp_plugin.add_l2_policy(l2p_id, l2p)
|
||||
self._l2p_id = l2p_id
|
||||
|
||||
def _get_l2_policy_operation_context(self):
|
||||
current = self._gbp_plugin.get_l2_policy(
|
||||
"test_l2p_context", self._l2p_id
|
||||
)
|
||||
_plugin_context = 'fake_plugin_context_for_l2p_test'
|
||||
context = mock.Mock(current=current,
|
||||
_plugin_context=_plugin_context)
|
||||
return context
|
||||
|
||||
|
||||
class TestPolicyClassifier(OdlMappingTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPolicyClassifier, self).setUp()
|
||||
self._gbp_plugin = FakeGBPPlugin()
|
||||
|
||||
classifier_id = '2222bdbd-6666-1111-ace2-765706b22222'
|
||||
classifier = {
|
||||
'id': classifier_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': 'fake_http_classifier_name',
|
||||
'description': 'Fake policy classifier for http traffic',
|
||||
'protocol': 'tcp',
|
||||
'port_range': '80',
|
||||
'direction': 'in'
|
||||
}
|
||||
self._gbp_plugin.add_policy_classifier(classifier_id, classifier)
|
||||
self._classifier_id = classifier_id
|
||||
|
||||
def _get_policy_classifier_context(self):
|
||||
current = self._gbp_plugin.get_policy_classifier(
|
||||
"test_pc_context", self._classifier_id
|
||||
)
|
||||
context = mock.Mock(current=current)
|
||||
return context
|
||||
|
||||
|
||||
class TestPolicyTargetGroup(OdlMappingTestCase):
|
||||
def setUp(self):
|
||||
super(TestPolicyTargetGroup, self).setUp()
|
||||
self._gbp_plugin = FakeGBPPlugin()
|
||||
self._core_plugin = FakeCorePlugin()
|
||||
|
||||
action_id = "2222bdbd-6666-2222-ace2-765706b22222"
|
||||
action = {
|
||||
'id': action_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': "fake_allow_action_name",
|
||||
'description': "Fake policy action to allow",
|
||||
'action_type': g_const.GP_ACTION_ALLOW
|
||||
}
|
||||
self._gbp_plugin.add_policy_action(action_id, action)
|
||||
self._action_id = action_id
|
||||
|
||||
http_classifier_id = "2222bdbd-6666-1111-ace2-765706b22222"
|
||||
http_classifier = {
|
||||
'id': http_classifier_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': 'fake_http_classifier_name',
|
||||
'description': 'Fake policy classifier for http traffic',
|
||||
'protocol': 'tcp',
|
||||
'port_range': '80',
|
||||
'direction': 'in'
|
||||
}
|
||||
self._gbp_plugin.add_policy_classifier(http_classifier_id,
|
||||
http_classifier)
|
||||
self._http_classifier_id = http_classifier_id
|
||||
|
||||
http_rule_id = "2222bdbd-6666-3333-ace2-765706b22222"
|
||||
http_rule = {
|
||||
'id': http_rule_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': "fake_web_policy_rule",
|
||||
'description': "Fake policy rule to allow http traffic",
|
||||
'policy_classifier_id': http_classifier_id,
|
||||
'policy_actions': [action_id]
|
||||
}
|
||||
self._gbp_plugin.add_policy_rule(http_rule_id, http_rule)
|
||||
self._http_rule_id = http_rule_id
|
||||
|
||||
icmp_classifier_id = "2222bdbd-6666-5555-ace2-765706b22222"
|
||||
icmp_classifier = {
|
||||
'id': icmp_classifier_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': 'fake_icmp_classifier_name',
|
||||
'description': 'Fake policy classifier for icmp traffic',
|
||||
'protocol': 'icmp',
|
||||
'direction': 'bi'
|
||||
}
|
||||
self._gbp_plugin.add_policy_classifier(icmp_classifier_id,
|
||||
icmp_classifier)
|
||||
self._icmp_classifier_id = icmp_classifier_id
|
||||
|
||||
icmp_rule_id = "2222bdbd-6666-6666-ace2-765706b22222"
|
||||
icmp_rule = {
|
||||
'id': icmp_rule_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': "fake_icmp_policy_rule",
|
||||
'description': "Fake policy rule to allow icmp traffic",
|
||||
'policy_classifier_id': icmp_classifier_id,
|
||||
'policy_actions': [action_id]
|
||||
}
|
||||
self._gbp_plugin.add_policy_rule(icmp_rule_id, icmp_rule)
|
||||
self._icmp_rule_id = icmp_rule_id
|
||||
|
||||
icmp_rule_set_id = "2222bdbd-7777-6666-ace2-765706b22222"
|
||||
icmp_rule_set = {
|
||||
'id': icmp_rule_set_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': "fake_icmp_rule_set_name",
|
||||
'description': "Fake policy rule set for icmp",
|
||||
'policy_rules': [icmp_rule_id]
|
||||
}
|
||||
self._gbp_plugin.add_policy_rule_set(icmp_rule_set_id,
|
||||
icmp_rule_set)
|
||||
self._icmp_rule_set_id = icmp_rule_set_id
|
||||
|
||||
http_rule_set_id = "2222bdbd-7777-3333-ace2-765706b22222"
|
||||
http_rule_set = {
|
||||
'id': http_rule_set_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': "fake_http_rule_set_name",
|
||||
'description': "Fake policy rule set for http",
|
||||
'policy_rules': [http_rule_id]
|
||||
}
|
||||
self._gbp_plugin.add_policy_rule_set(http_rule_set_id,
|
||||
http_rule_set)
|
||||
self._http_rule_set_id = http_rule_set_id
|
||||
|
||||
network_id = "ed2e3c10-2222-1111-9006-2863a2d1abbc"
|
||||
network = {
|
||||
'id': network_id,
|
||||
'name': "fake_network_for_PTG_test"
|
||||
}
|
||||
self._core_plugin.add_network(network_id, network)
|
||||
self._network_id = network_id
|
||||
|
||||
subnet_id = "ed2e3c10-2222-2222-9006-2863a2d1abbc"
|
||||
subnet = {
|
||||
'id': subnet_id,
|
||||
'cidr': "10.10.0.0/24",
|
||||
'network_id': network_id,
|
||||
'gatewat_ip': "10.10.0.1"
|
||||
}
|
||||
self._core_plugin.add_subnet(subnet_id, subnet)
|
||||
self._subnet_id = subnet_id
|
||||
|
||||
l3p_id = '3333ba05-3333-45ba-ace2-765706b23333'
|
||||
l3p = {
|
||||
'id': l3p_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': 'fake_l3p_name',
|
||||
'description': 'Fake l3 policy for PTG test'
|
||||
}
|
||||
self._gbp_plugin.add_l3_policy(l3p_id, l3p)
|
||||
|
||||
l2p_id = '2222bdbd-eb07-45ba-ace2-765706b22222'
|
||||
l2p = {
|
||||
'id': l2p_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': 'fake_l2p_name',
|
||||
'description': 'Fake l2 policy for PTG test',
|
||||
'l3_policy_id': l3p_id,
|
||||
'network_id': network_id
|
||||
}
|
||||
self._gbp_plugin.add_l2_policy(l2p_id, l2p)
|
||||
|
||||
group_id = '1111bdbd-eb07-45ba-ace2-765706b21111'
|
||||
group = {
|
||||
'id': group_id,
|
||||
'tenant_id': TENANT_ID,
|
||||
'name': 'fake_ptg_name',
|
||||
'description': 'Fake PTG for ptg test',
|
||||
'l2_policy_id': l2p_id,
|
||||
'subnets': [subnet_id],
|
||||
'provided_policy_rule_sets': [http_rule_set_id],
|
||||
'consumed_policy_rule_sets': [icmp_rule_set_id]
|
||||
}
|
||||
self._gbp_plugin.add_policy_target_group(group_id, group)
|
||||
self._group_id = group_id
|
||||
|
||||
def _get_policy_target_group_context(self):
|
||||
current = self._gbp_plugin.get_policy_target_group(
|
||||
"test_ptg_context", self._group_id
|
||||
)
|
||||
_plugin_context = "fake_plugin_context_for_ptg_test"
|
||||
_plugin = self._gbp_plugin
|
||||
context = mock.Mock(
|
||||
current=current,
|
||||
_plugin_context=_plugin_context,
|
||||
_plugin=_plugin
|
||||
)
|
||||
return context
|
||||
@@ -38,6 +38,7 @@ gbp.neutron.group_policy.policy_drivers =
|
||||
implicit_policy = gbp.neutron.services.grouppolicy.drivers.implicit_policy:ImplicitPolicyDriver
|
||||
resource_mapping = gbp.neutron.services.grouppolicy.drivers.resource_mapping:ResourceMappingDriver
|
||||
apic = gbp.neutron.services.grouppolicy.drivers.cisco.apic.apic_mapping:ApicMappingDriver
|
||||
odl = gbp.neutron.services.grouppolicy.drivers.odl.odl_mapping:OdlMappingDriver
|
||||
oneconvergence_gbp_driver = gbp.neutron.services.grouppolicy.drivers.oneconvergence.nvsd_gbp_driver:NvsdGbpDriver
|
||||
nuage_gbp_driver = gbp.neutron.services.grouppolicy.drivers.nuage.driver:NuageGBPDriver
|
||||
neutron.ml2.mechanism_drivers =
|
||||
|
||||
Reference in New Issue
Block a user