307 lines
15 KiB
Python
307 lines
15 KiB
Python
# Copyright (c) 2016 Cisco Systems 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.
|
|
|
|
from gbpservice.neutron.plugins.ml2plus import driver_api
|
|
|
|
from neutron.db import api as db_api
|
|
from neutron.plugins.ml2.common import exceptions as ml2_exc
|
|
from neutron.plugins.ml2 import managers
|
|
from neutron.quota import resource_registry
|
|
from oslo_log import log
|
|
from oslo_utils import excutils
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
class MechanismManager(managers.MechanismManager):
|
|
|
|
def __init__(self):
|
|
super(MechanismManager, self).__init__()
|
|
|
|
def _call_on_drivers(self, method_name, context,
|
|
continue_on_failure=False, raise_db_retriable=False):
|
|
super(MechanismManager, self)._call_on_drivers(
|
|
method_name, context, continue_on_failure=False,
|
|
raise_db_retriable=False)
|
|
if method_name.endswith('_precommit'):
|
|
# This does the same thing as:
|
|
# https://github.com/openstack/neutron/blob/newton-eol/neutron/
|
|
# api/v2/base.py#L489
|
|
# but from within the scope of the plugin's transaction, such
|
|
# that if it fails, everything that happened prior to this in
|
|
# precommit phase can also be rolled back.
|
|
resource_name = method_name.replace('_precommit', '').replace(
|
|
'create_', '').replace('update_', '').replace(
|
|
'delete_', '')
|
|
tracked_resource = resource_registry.get_resource(resource_name)
|
|
tracked_resource._dirty_tenants.add(context.current['tenant_id'])
|
|
resource_registry.set_resources_dirty(context._plugin_context)
|
|
|
|
def _call_on_extended_drivers(self, method_name, context,
|
|
continue_on_failure=False,
|
|
raise_db_retriable=False):
|
|
"""Call a method on all extended mechanism drivers.
|
|
|
|
:param method_name: name of the method to call
|
|
:param context: context parameter to pass to each method call
|
|
:param continue_on_failure: whether or not to continue to call
|
|
all mechanism drivers once one has raised an exception
|
|
:param raise_db_retriable: whether or not to treat retriable db
|
|
exception by mechanism drivers to propagate up to upper layer so
|
|
that upper layer can handle it or error in ML2 player
|
|
:raises: neutron.plugins.ml2.common.MechanismDriverError
|
|
if any mechanism driver call fails. or DB retriable error when
|
|
raise_db_retriable=False. See neutron.db.api.is_retriable for
|
|
what db exception is retriable
|
|
"""
|
|
errors = []
|
|
for driver in self.ordered_mech_drivers:
|
|
if isinstance(driver.obj, driver_api.MechanismDriver):
|
|
try:
|
|
getattr(driver.obj, method_name)(context)
|
|
except Exception as e:
|
|
if raise_db_retriable and db_api.is_retriable(e):
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.debug("DB exception raised by Mechanism "
|
|
"driver '%(name)s' in %(method)s",
|
|
{'name': driver.name,
|
|
'method': method_name},
|
|
exc_info=e)
|
|
LOG.exception(
|
|
"Mechanism driver '%(name)s' failed in "
|
|
"%(method)s",
|
|
{'name': driver.name, 'method': method_name}
|
|
)
|
|
errors.append(e)
|
|
if not continue_on_failure:
|
|
break
|
|
if errors:
|
|
raise ml2_exc.MechanismDriverError(
|
|
method=method_name,
|
|
errors=errors
|
|
)
|
|
|
|
def ensure_tenant(self, plugin_context, tenant_id):
|
|
for driver in self.ordered_mech_drivers:
|
|
if isinstance(driver.obj, driver_api.MechanismDriver):
|
|
try:
|
|
driver.obj.ensure_tenant(plugin_context, tenant_id)
|
|
except Exception as e:
|
|
if db_api.is_retriable(e):
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.debug("DB exception raised by Mechanism "
|
|
"driver '%(name)s' in ensure_tenant",
|
|
{'name': driver.name},
|
|
exc_info=e)
|
|
LOG.exception("Mechanism driver '%s' failed in "
|
|
"ensure_tenant", driver.name)
|
|
raise ml2_exc.MechanismDriverError(method="ensure_tenant")
|
|
|
|
def create_subnetpool_precommit(self, context):
|
|
self._call_on_extended_drivers("create_subnetpool_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def create_subnetpool_postcommit(self, context):
|
|
self._call_on_extended_drivers("create_subnetpool_postcommit",
|
|
context)
|
|
|
|
def update_subnetpool_precommit(self, context):
|
|
self._call_on_extended_drivers("update_subnetpool_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def update_subnetpool_postcommit(self, context):
|
|
self._call_on_extended_drivers("update_subnetpool_postcommit",
|
|
context, continue_on_failure=True)
|
|
|
|
def delete_subnetpool_precommit(self, context):
|
|
self._call_on_extended_drivers("delete_subnetpool_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def delete_subnetpool_postcommit(self, context):
|
|
self._call_on_extended_drivers("delete_subnetpool_postcommit",
|
|
context, continue_on_failure=True)
|
|
|
|
def create_address_scope_precommit(self, context):
|
|
self._call_on_extended_drivers("create_address_scope_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def create_address_scope_postcommit(self, context):
|
|
self._call_on_extended_drivers("create_address_scope_postcommit",
|
|
context)
|
|
|
|
def update_address_scope_precommit(self, context):
|
|
self._call_on_extended_drivers("update_address_scope_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def update_address_scope_postcommit(self, context):
|
|
self._call_on_extended_drivers("update_address_scope_postcommit",
|
|
context, continue_on_failure=True)
|
|
|
|
def delete_address_scope_precommit(self, context):
|
|
self._call_on_extended_drivers("delete_address_scope_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def delete_address_scope_postcommit(self, context):
|
|
self._call_on_extended_drivers("delete_address_scope_postcommit",
|
|
context, continue_on_failure=True)
|
|
|
|
def create_security_group_precommit(self, context):
|
|
self._call_on_extended_drivers("create_security_group_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def create_security_group_postcommit(self, context):
|
|
self._call_on_extended_drivers("create_security_group_postcommit",
|
|
context)
|
|
|
|
def update_security_group_precommit(self, context):
|
|
self._call_on_extended_drivers("update_security_group_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def update_security_group_postcommit(self, context):
|
|
self._call_on_extended_drivers("update_security_group_postcommit",
|
|
context, continue_on_failure=True)
|
|
|
|
def delete_security_group_precommit(self, context):
|
|
self._call_on_extended_drivers("delete_security_group_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def delete_security_group_postcommit(self, context):
|
|
self._call_on_extended_drivers("delete_security_group_postcommit",
|
|
context, continue_on_failure=True)
|
|
|
|
def create_security_group_rule_precommit(self, context):
|
|
self._call_on_extended_drivers("create_security_group_rule_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def create_security_group_rule_postcommit(self, context):
|
|
self._call_on_extended_drivers("create_security_group_rule_postcommit",
|
|
context)
|
|
|
|
def update_security_group_rule_precommit(self, context):
|
|
self._call_on_extended_drivers("update_security_group_rule_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def update_security_group_rule_postcommit(self, context):
|
|
self._call_on_extended_drivers("update_security_group_rule_postcommit",
|
|
context, continue_on_failure=True)
|
|
|
|
def delete_security_group_rule_precommit(self, context):
|
|
self._call_on_extended_drivers("delete_security_group_rule_precommit",
|
|
context, raise_db_retriable=True)
|
|
|
|
def delete_security_group_rule_postcommit(self, context):
|
|
self._call_on_extended_drivers("delete_security_group_rule_postcommit",
|
|
context, continue_on_failure=True)
|
|
|
|
|
|
class ExtensionManager(managers.ExtensionManager):
|
|
|
|
def __init__(self):
|
|
super(ExtensionManager, self).__init__()
|
|
|
|
def _call_on_extended_drivers(self, method_name, plugin_context, data,
|
|
result):
|
|
"""Call a method on all extended extension drivers."""
|
|
for driver in self.ordered_ext_drivers:
|
|
if isinstance(driver.obj, driver_api.ExtensionDriver):
|
|
try:
|
|
getattr(driver.obj, method_name)(plugin_context, data,
|
|
result)
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.info("Extension driver '%(name)s' failed in "
|
|
"%(method)s",
|
|
{'name': driver.name, 'method': method_name})
|
|
|
|
# Overrides ML2 implementation to avoid eating retriable
|
|
# exceptions, as well as to support calling only on extension
|
|
# drivers extended for ML2Plus.
|
|
def _call_on_dict_driver(self, method_name, session, base_model, result,
|
|
extended_only=False, has_base_model=True):
|
|
|
|
# Bulk operations might not be implemented by all drivers
|
|
def noop(*args, **kwargs):
|
|
pass
|
|
|
|
for driver in self.ordered_ext_drivers:
|
|
if not extended_only or isinstance(
|
|
driver.obj, driver_api.ExtensionDriver):
|
|
try:
|
|
if not has_base_model:
|
|
getattr(driver.obj, method_name, noop)(session,
|
|
result)
|
|
else:
|
|
getattr(driver.obj, method_name, noop)(session,
|
|
base_model,
|
|
result)
|
|
except Exception as e:
|
|
if db_api.is_retriable(e):
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.debug(
|
|
"DB exception raised by extension driver "
|
|
"'%(name)s' in %(method)s",
|
|
{'name': driver.name, 'method': method_name},
|
|
exc_info=e)
|
|
LOG.exception(
|
|
"Extension driver '%(name)s' failed in %(method)s",
|
|
{'name': driver.name, 'method': method_name})
|
|
raise ml2_exc.ExtensionDriverError(driver=driver.name)
|
|
|
|
def process_create_subnetpool(self, plugin_context, data, result):
|
|
self._call_on_extended_drivers("process_create_subnetpool",
|
|
plugin_context, data, result)
|
|
|
|
def process_update_subnetpool(self, plugin_context, data, result):
|
|
self._call_on_extended_drivers("process_update_subnetpool",
|
|
plugin_context, data, result)
|
|
|
|
def extend_subnetpool_dict(self, session, base_model, result):
|
|
self._call_on_dict_driver("extend_subnetpool_dict",
|
|
session, base_model, result, True)
|
|
|
|
def process_create_address_scope(self, plugin_context, data, result):
|
|
self._call_on_extended_drivers("process_create_address_scope",
|
|
plugin_context, data, result)
|
|
|
|
def process_update_address_scope(self, plugin_context, data, result):
|
|
self._call_on_extended_drivers("process_update_address_scope",
|
|
plugin_context, data, result)
|
|
|
|
def extend_address_scope_dict(self, session, base_model, result):
|
|
self._call_on_dict_driver("extend_address_scope_dict",
|
|
session, base_model, result, True)
|
|
|
|
def extend_subnetpool_dict_bulk(self, session, result):
|
|
self._call_on_dict_driver("extend_subnetpool_dict_bulk",
|
|
session, None, result, True,
|
|
has_base_model=False)
|
|
|
|
def extend_address_scope_dict_bulk(self, session, result):
|
|
self._call_on_dict_driver("extend_address_scope_dict_bulk",
|
|
session, None, result, True,
|
|
has_base_model=False)
|
|
|
|
def extend_network_dict_bulk(self, session, result):
|
|
self._call_on_dict_driver("extend_network_dict_bulk", session,
|
|
None, result, has_base_model=False)
|
|
|
|
def extend_subnet_dict_bulk(self, session, result):
|
|
self._call_on_dict_driver("extend_subnet_dict_bulk", session,
|
|
None, result, has_base_model=False)
|
|
|
|
def extend_port_dict_bulk(self, session, result):
|
|
self._call_on_dict_driver("extend_port_dict_bulk", session, None,
|
|
result, has_base_model=False)
|