192 lines
8.7 KiB
Python
192 lines
8.7 KiB
Python
# Copyright (c) 2019 Red Hat, Inc.
|
|
# All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# 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 collections
|
|
|
|
from neutron_lib.api.definitions import expose_l3_conntrack_helper as exposedef
|
|
from neutron_lib.api.definitions import l3
|
|
from neutron_lib.api.definitions import l3_conntrack_helper as apidef
|
|
from neutron_lib.callbacks import registry
|
|
from neutron_lib.db import api as db_api
|
|
from neutron_lib.db import resource_extend
|
|
from neutron_lib import exceptions as lib_exc
|
|
from neutron_lib.exceptions import l3 as lib_l3_exc
|
|
from neutron_lib.objects import exceptions as obj_exc
|
|
from neutron_lib.plugins import constants
|
|
from neutron_lib.plugins import directory
|
|
from oslo_config import cfg
|
|
from oslo_db import exception as oslo_db_exc
|
|
|
|
from neutron._i18n import _
|
|
from neutron.api.rpc.callbacks import events as rpc_events
|
|
from neutron.api.rpc.handlers import resources_rpc
|
|
from neutron.db import db_base_plugin_common
|
|
from neutron.extensions import l3_conntrack_helper
|
|
from neutron.objects import base as base_obj
|
|
from neutron.objects import conntrack_helper as cth
|
|
from neutron.objects import router
|
|
from neutron.services.conntrack_helper.common import exceptions as cth_exc
|
|
|
|
|
|
@resource_extend.has_resource_extenders
|
|
@registry.has_registry_receivers
|
|
class Plugin(l3_conntrack_helper.ConntrackHelperPluginBase):
|
|
"""Implementation of the Neutron Conntrack Helper Service Plugin.
|
|
|
|
This class implements a Conntrack Helper plugin.
|
|
"""
|
|
|
|
required_service_plugins = [l3.ROUTER]
|
|
|
|
supported_extension_aliases = [apidef.ALIAS, exposedef.ALIAS]
|
|
|
|
__native_pagination_support = True
|
|
__native_sorting_support = True
|
|
__filter_validation_support = True
|
|
|
|
def __init__(self):
|
|
super(Plugin, self).__init__()
|
|
self.push_api = resources_rpc.ResourcesPushRpcApi()
|
|
self.l3_plugin = directory.get_plugin(constants.L3)
|
|
self.core_plugin = directory.get_plugin()
|
|
|
|
# Option allowed_conntrack_helpers is a list of key, value pairs.
|
|
# The list can contain same key (conntrack helper module) multiple
|
|
# times with a different value (protocol). Merge to a dictonary
|
|
# with key (conntrack helper) and values (protocols) as a list.
|
|
self.constraints = collections.defaultdict(list)
|
|
for x in cfg.CONF.allowed_conntrack_helpers:
|
|
self.constraints[next(iter(x.keys()))].append(
|
|
next(iter(x.values())))
|
|
|
|
@staticmethod
|
|
@resource_extend.extends([l3.ROUTERS])
|
|
def _extend_router_dict(result_dict, db):
|
|
fields = [apidef.PROTOCOL, apidef.PORT, apidef.HELPER]
|
|
result_dict[apidef.COLLECTION_NAME] = []
|
|
if db.conntrack_helpers:
|
|
conntack_helper_result = []
|
|
for conntack_helper in db.conntrack_helpers:
|
|
cth_dict = cth.ConntrackHelper.modify_fields_from_db(
|
|
conntack_helper)
|
|
for key in list(cth_dict.keys()):
|
|
if key not in fields:
|
|
cth_dict.pop(key)
|
|
conntack_helper_result.append(cth_dict)
|
|
result_dict[apidef.COLLECTION_NAME] = conntack_helper_result
|
|
return result_dict
|
|
|
|
def get_router(self, context, router_id, fields=None):
|
|
router_obj = router.Router.get_object(context, id=router_id)
|
|
if not router_obj:
|
|
raise lib_l3_exc.RouterNotFound(router_id=router_id)
|
|
return router_obj
|
|
|
|
def _find_existing_conntrack_helper(self, context, router_id,
|
|
conntrack_helper):
|
|
# Because the session had been flushed by NeutronDbObjectDuplicateEntry
|
|
# so if we want to use the context to get another db queries, we need
|
|
# to rollback first.
|
|
context.session.rollback()
|
|
param = {'router_id': router_id,
|
|
'protocol': conntrack_helper['protocol'],
|
|
'port': conntrack_helper['port'],
|
|
'helper': conntrack_helper['helper']}
|
|
objs = cth.ConntrackHelper.get_objects(context, **param)
|
|
if objs:
|
|
return (objs[0], param)
|
|
|
|
def _get_conntrack_helper(self, context, id):
|
|
cth_obj = cth.ConntrackHelper.get_object(context, id=id)
|
|
if not cth_obj:
|
|
raise cth_exc.ConntrackHelperNotFound(id=id)
|
|
return cth_obj
|
|
|
|
def _check_conntrack_helper_constraints(self, cth_obj):
|
|
if cth_obj.helper not in self.constraints:
|
|
raise cth_exc.ConntrackHelperNotAllowed(helper=cth_obj.helper)
|
|
elif cth_obj.protocol not in self.constraints[cth_obj.helper]:
|
|
raise cth_exc.InvalidProtocolForHelper(
|
|
helper=cth_obj.helper, protocol=cth_obj.protocol,
|
|
supported_protocols=', '.join(
|
|
self.constraints[cth_obj.helper]))
|
|
|
|
@db_base_plugin_common.convert_result_to_dict
|
|
def create_router_conntrack_helper(self, context, router_id,
|
|
conntrack_helper):
|
|
conntrack_helper = conntrack_helper.get(apidef.RESOURCE_NAME)
|
|
conntrack_helper['router_id'] = router_id
|
|
cth_obj = cth.ConntrackHelper(context, **conntrack_helper)
|
|
self._check_conntrack_helper_constraints(cth_obj)
|
|
try:
|
|
with db_api.CONTEXT_WRITER.using(context):
|
|
# If this get_router does not raise an exception, a router
|
|
# with router_id exists.
|
|
self.get_router(context, router_id)
|
|
cth_obj.create()
|
|
except obj_exc.NeutronDbObjectDuplicateEntry:
|
|
(__, conflict_params) = self._find_existing_conntrack_helper(
|
|
context, router_id, cth_obj.to_dict())
|
|
message = _("A duplicate conntrack helper entry with same "
|
|
"attributes already exists, conflicting values "
|
|
"are %s") % conflict_params
|
|
raise lib_exc.BadRequest(resource=apidef.RESOURCE_NAME,
|
|
msg=message)
|
|
self.push_api.push(context, [cth_obj], rpc_events.CREATED)
|
|
return cth_obj
|
|
|
|
@db_base_plugin_common.convert_result_to_dict
|
|
def update_router_conntrack_helper(self, context, id, router_id,
|
|
conntrack_helper):
|
|
conntrack_helper = conntrack_helper.get(apidef.RESOURCE_NAME)
|
|
try:
|
|
with db_api.CONTEXT_WRITER.using(context):
|
|
cth_obj = self._get_conntrack_helper(context, id)
|
|
cth_obj.update_fields(conntrack_helper, reset_changes=True)
|
|
self._check_conntrack_helper_constraints(cth_obj)
|
|
cth_obj.update()
|
|
except oslo_db_exc.DBDuplicateEntry:
|
|
(__, conflict_params) = self._find_existing_conntrack_helper(
|
|
context, cth_obj.router_id, cth_obj.to_dict())
|
|
message = _("A duplicate conntrack helper entry with same "
|
|
"attributes already exists, conflicting values "
|
|
"are %s") % conflict_params
|
|
raise lib_exc.BadRequest(resource=apidef.RESOURCE_NAME,
|
|
msg=message)
|
|
self.push_api.push(context, [cth_obj], rpc_events.UPDATED)
|
|
return cth_obj
|
|
|
|
@db_base_plugin_common.make_result_with_fields
|
|
@db_base_plugin_common.convert_result_to_dict
|
|
def get_router_conntrack_helper(self, context, id, router_id, fields=None):
|
|
return self._get_conntrack_helper(context, id)
|
|
|
|
@db_base_plugin_common.make_result_with_fields
|
|
@db_base_plugin_common.convert_result_to_dict
|
|
def get_router_conntrack_helpers(self, context, router_id=None,
|
|
filters=None, fields=None, sorts=None,
|
|
limit=None, marker=None,
|
|
page_reverse=False):
|
|
filters = filters or {}
|
|
pager = base_obj.Pager(sorts, limit, page_reverse, marker)
|
|
return cth.ConntrackHelper.get_objects(context, _pager=pager,
|
|
router_id=router_id, **filters)
|
|
|
|
def delete_router_conntrack_helper(self, context, id, router_id):
|
|
cth_obj = self._get_conntrack_helper(context, id)
|
|
with db_api.CONTEXT_WRITER.using(context):
|
|
cth_obj.delete()
|
|
self.push_api.push(context, [cth_obj], rpc_events.DELETED)
|