Introduce base interface for core resource extensions
The interface can be found in neutron.core_extensions.base. Adopted the interface in qos core resource extension. Alos moved qos_extension under neutron.core_extensions.qos. Partially, this is to avoid confusion around the fact that the module does not really contain a neutron API extension but core resource extension. Change-Id: I6f6976aa49694f7ef17afa4e93bc769cd0069f65 Partially-Implements: blueprint quantum-qos-api
This commit is contained in:
parent
f58d14ca02
commit
d148e68b71
|
@ -35,19 +35,22 @@ Service side design
|
||||||
MQ-based reference notification driver which updates agents via messaging
|
MQ-based reference notification driver which updates agents via messaging
|
||||||
bus, using `RPC callbacks <rpc_callbacks.html>`_.
|
bus, using `RPC callbacks <rpc_callbacks.html>`_.
|
||||||
|
|
||||||
* neutron.services.qos.qos_extension:
|
* neutron.core_extensions.base:
|
||||||
Contains a class that can be used by external code to extend core
|
Contains an interface class to implement core resource (port/network)
|
||||||
(network/port) resources with QoS details (at the moment, it's just
|
extensions. Core resource extensions are then easily integrated into
|
||||||
qos_policy_id). This class is designed in a way that should allow its
|
interested plugins. We may need to have a core resource extension manager
|
||||||
integration into different plugins. Alternatively, we may want to have a core
|
that would utilize those extensions, to avoid plugin modifications for every
|
||||||
resource extension manager that would utilize it, among other extensions, and
|
new core resource extension.
|
||||||
that could be easily integrated into plugins.
|
|
||||||
|
* neutron.core_extensions.qos:
|
||||||
|
Contains QoS core resource extension that conforms to the interface described
|
||||||
|
above.
|
||||||
|
|
||||||
* neutron.plugins.ml2.extensions.qos:
|
* neutron.plugins.ml2.extensions.qos:
|
||||||
Contains ml2 extension driver that handles core resource updates by reusing
|
Contains ml2 extension driver that handles core resource updates by reusing
|
||||||
the qos_extension module mentioned above. In the future, we would like to see
|
the core_extensions.qos module mentioned above. In the future, we would like
|
||||||
a plugin-agnostic core resource extension manager that could be integrated
|
to see a plugin-agnostic core resource extension manager that could be
|
||||||
into other plugins with ease.
|
integrated into other plugins with ease.
|
||||||
|
|
||||||
|
|
||||||
Supported QoS rule types
|
Supported QoS rule types
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Copyright (c) 2015 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 abc
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
|
||||||
|
NETWORK = 'network'
|
||||||
|
PORT = 'port'
|
||||||
|
|
||||||
|
|
||||||
|
CORE_RESOURCES = [NETWORK, PORT]
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class CoreResourceExtension(object):
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def process_fields(self, context, resource_type,
|
||||||
|
requested_resource, actual_resource):
|
||||||
|
"""Process extension fields.
|
||||||
|
|
||||||
|
:param context: neutron api request context
|
||||||
|
:param resource_type: core resource type (one of CORE_RESOURCES)
|
||||||
|
:param requested_resource: resource dict that contains extension fields
|
||||||
|
:param actual_resource: actual resource dict known to plugin
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def extract_fields(self, resource_type, resource):
|
||||||
|
"""Extract extension fields.
|
||||||
|
|
||||||
|
:param resource_type: core resource type (one of CORE_RESOURCES)
|
||||||
|
:param resource: resource dict that contains extension fields
|
||||||
|
"""
|
|
@ -13,18 +13,15 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from neutron.core_extensions import base
|
||||||
from neutron.db import api as db_api
|
from neutron.db import api as db_api
|
||||||
from neutron import manager
|
from neutron import manager
|
||||||
from neutron.objects.qos import policy as policy_object
|
from neutron.objects.qos import policy as policy_object
|
||||||
from neutron.plugins.common import constants as plugin_constants
|
from neutron.plugins.common import constants as plugin_constants
|
||||||
from neutron.services.qos import qos_consts
|
from neutron.services.qos import qos_consts
|
||||||
|
|
||||||
NETWORK = 'network'
|
|
||||||
PORT = 'port'
|
|
||||||
|
|
||||||
|
class QosCoreResourceExtension(base.CoreResourceExtension):
|
||||||
# TODO(QoS): Add interface to define how this should look like
|
|
||||||
class QosResourceExtensionHandler(object):
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def plugin_loaded(self):
|
def plugin_loaded(self):
|
||||||
|
@ -70,15 +67,15 @@ class QosResourceExtensionHandler(object):
|
||||||
with db_api.autonested_transaction(context.session):
|
with db_api.autonested_transaction(context.session):
|
||||||
return getattr(self, method_name)(context=context, **kwargs)
|
return getattr(self, method_name)(context=context, **kwargs)
|
||||||
|
|
||||||
def process_resource(self, context, resource_type, requested_resource,
|
def process_fields(self, context, resource_type,
|
||||||
actual_resource):
|
requested_resource, actual_resource):
|
||||||
if (qos_consts.QOS_POLICY_ID in requested_resource and
|
if (qos_consts.QOS_POLICY_ID in requested_resource and
|
||||||
self.plugin_loaded):
|
self.plugin_loaded):
|
||||||
self._exec('_update_%s_policy' % resource_type, context,
|
self._exec('_update_%s_policy' % resource_type, context,
|
||||||
{resource_type: actual_resource,
|
{resource_type: actual_resource,
|
||||||
"%s_changes" % resource_type: requested_resource})
|
"%s_changes" % resource_type: requested_resource})
|
||||||
|
|
||||||
def extract_resource_fields(self, resource_type, resource):
|
def extract_fields(self, resource_type, resource):
|
||||||
if not self.plugin_loaded:
|
if not self.plugin_loaded:
|
||||||
return {}
|
return {}
|
||||||
|
|
|
@ -15,8 +15,9 @@
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from neutron.core_extensions import base as base_core
|
||||||
|
from neutron.core_extensions import qos as qos_core
|
||||||
from neutron.plugins.ml2 import driver_api as api
|
from neutron.plugins.ml2 import driver_api as api
|
||||||
from neutron.services.qos import qos_extension
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -24,27 +25,26 @@ LOG = logging.getLogger(__name__)
|
||||||
class QosExtensionDriver(api.ExtensionDriver):
|
class QosExtensionDriver(api.ExtensionDriver):
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
self.qos_ext_handler = qos_extension.QosResourceExtensionHandler()
|
self.core_ext_handler = qos_core.QosCoreResourceExtension()
|
||||||
LOG.debug("QosExtensionDriver initialization complete")
|
LOG.debug("QosExtensionDriver initialization complete")
|
||||||
|
|
||||||
def process_create_network(self, context, data, result):
|
def process_create_network(self, context, data, result):
|
||||||
self.qos_ext_handler.process_resource(
|
self.core_ext_handler.process_fields(
|
||||||
context, qos_extension.NETWORK, data, result)
|
context, base_core.NETWORK, data, result)
|
||||||
|
|
||||||
process_update_network = process_create_network
|
process_update_network = process_create_network
|
||||||
|
|
||||||
def process_create_port(self, context, data, result):
|
def process_create_port(self, context, data, result):
|
||||||
self.qos_ext_handler.process_resource(
|
self.core_ext_handler.process_fields(
|
||||||
context, qos_extension.PORT, data, result)
|
context, base_core.PORT, data, result)
|
||||||
|
|
||||||
process_update_port = process_create_port
|
process_update_port = process_create_port
|
||||||
|
|
||||||
def extend_network_dict(self, session, db_data, result):
|
def extend_network_dict(self, session, db_data, result):
|
||||||
result.update(
|
result.update(
|
||||||
self.qos_ext_handler.extract_resource_fields(qos_extension.NETWORK,
|
self.core_ext_handler.extract_fields(
|
||||||
db_data))
|
base_core.NETWORK, db_data))
|
||||||
|
|
||||||
def extend_port_dict(self, session, db_data, result):
|
def extend_port_dict(self, session, db_data, result):
|
||||||
result.update(
|
result.update(
|
||||||
self.qos_ext_handler.extract_resource_fields(qos_extension.PORT,
|
self.core_ext_handler.extract_fields(base_core.PORT, db_data))
|
||||||
db_data))
|
|
||||||
|
|
|
@ -140,7 +140,7 @@ class QosTestJSON(base.BaseAdminNetworkTest):
|
||||||
description='test policy',
|
description='test policy',
|
||||||
shared=False)
|
shared=False)
|
||||||
#TODO(QoS): This currently raises an exception on the server side. See
|
#TODO(QoS): This currently raises an exception on the server side. See
|
||||||
# services/qos/qos_extension.py for comments on this subject.
|
# core_extensions/qos.py for comments on this subject.
|
||||||
network = self.create_network('test network',
|
network = self.create_network('test network',
|
||||||
qos_policy_id=policy['id'])
|
qos_policy_id=policy['id'])
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ class QosTestJSON(base.BaseAdminNetworkTest):
|
||||||
shared=False)
|
shared=False)
|
||||||
network = self.create_shared_network('test network')
|
network = self.create_shared_network('test network')
|
||||||
#TODO(QoS): This currently raises an exception on the server side. See
|
#TODO(QoS): This currently raises an exception on the server side. See
|
||||||
# services/qos/qos_extension.py for comments on this subject.
|
# core_extensions/qos.py for comments on this subject.
|
||||||
port = self.create_port(network, qos_policy_id=policy['id'])
|
port = self.create_port(network, qos_policy_id=policy['id'])
|
||||||
|
|
||||||
retrieved_port = self.admin_client.show_port(port['id'])
|
retrieved_port = self.admin_client.show_port(port['id'])
|
||||||
|
|
|
@ -16,9 +16,10 @@
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from neutron import context
|
from neutron import context
|
||||||
|
from neutron.core_extensions import base as base_core
|
||||||
|
from neutron.core_extensions import qos as qos_core
|
||||||
from neutron.plugins.common import constants as plugin_constants
|
from neutron.plugins.common import constants as plugin_constants
|
||||||
from neutron.services.qos import qos_consts
|
from neutron.services.qos import qos_consts
|
||||||
from neutron.services.qos import qos_extension
|
|
||||||
from neutron.tests import base
|
from neutron.tests import base
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,18 +28,18 @@ def _get_test_dbdata(qos_policy_id):
|
||||||
'network_id': 'fake_net_id'}}
|
'network_id': 'fake_net_id'}}
|
||||||
|
|
||||||
|
|
||||||
class QosResourceExtensionHandlerTestCase(base.BaseTestCase):
|
class QosCoreResourceExtensionTestCase(base.BaseTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(QosResourceExtensionHandlerTestCase, self).setUp()
|
super(QosCoreResourceExtensionTestCase, self).setUp()
|
||||||
self.ext_handler = qos_extension.QosResourceExtensionHandler()
|
self.core_extension = qos_core.QosCoreResourceExtension()
|
||||||
policy_p = mock.patch('neutron.objects.qos.policy.QosPolicy')
|
policy_p = mock.patch('neutron.objects.qos.policy.QosPolicy')
|
||||||
self.policy_m = policy_p.start()
|
self.policy_m = policy_p.start()
|
||||||
self.context = context.get_admin_context()
|
self.context = context.get_admin_context()
|
||||||
|
|
||||||
def test_process_resource_no_qos_policy_id(self):
|
def test_process_fields_no_qos_policy_id(self):
|
||||||
self.ext_handler.process_resource(
|
self.core_extension.process_fields(
|
||||||
self.context, qos_extension.PORT, {}, None)
|
self.context, base_core.PORT, {}, None)
|
||||||
self.assertFalse(self.policy_m.called)
|
self.assertFalse(self.policy_m.called)
|
||||||
|
|
||||||
def _mock_plugin_loaded(self, plugin_loaded):
|
def _mock_plugin_loaded(self, plugin_loaded):
|
||||||
|
@ -48,28 +49,28 @@ class QosResourceExtensionHandlerTestCase(base.BaseTestCase):
|
||||||
return mock.patch('neutron.manager.NeutronManager.get_service_plugins',
|
return mock.patch('neutron.manager.NeutronManager.get_service_plugins',
|
||||||
return_value=plugins)
|
return_value=plugins)
|
||||||
|
|
||||||
def test_process_resource_no_qos_plugin_loaded(self):
|
def test_process_fields_no_qos_plugin_loaded(self):
|
||||||
with self._mock_plugin_loaded(False):
|
with self._mock_plugin_loaded(False):
|
||||||
self.ext_handler.process_resource(
|
self.core_extension.process_fields(
|
||||||
self.context, qos_extension.PORT,
|
self.context, base_core.PORT,
|
||||||
{qos_consts.QOS_POLICY_ID: None}, None)
|
{qos_consts.QOS_POLICY_ID: None}, None)
|
||||||
self.assertFalse(self.policy_m.called)
|
self.assertFalse(self.policy_m.called)
|
||||||
|
|
||||||
def test_process_resource_port_new_policy(self):
|
def test_process_fields_port_new_policy(self):
|
||||||
with self._mock_plugin_loaded(True):
|
with self._mock_plugin_loaded(True):
|
||||||
qos_policy_id = mock.Mock()
|
qos_policy_id = mock.Mock()
|
||||||
actual_port = {'id': mock.Mock(),
|
actual_port = {'id': mock.Mock(),
|
||||||
qos_consts.QOS_POLICY_ID: qos_policy_id}
|
qos_consts.QOS_POLICY_ID: qos_policy_id}
|
||||||
qos_policy = mock.MagicMock()
|
qos_policy = mock.MagicMock()
|
||||||
self.policy_m.get_by_id = mock.Mock(return_value=qos_policy)
|
self.policy_m.get_by_id = mock.Mock(return_value=qos_policy)
|
||||||
self.ext_handler.process_resource(
|
self.core_extension.process_fields(
|
||||||
self.context, qos_extension.PORT,
|
self.context, base_core.PORT,
|
||||||
{qos_consts.QOS_POLICY_ID: qos_policy_id},
|
{qos_consts.QOS_POLICY_ID: qos_policy_id},
|
||||||
actual_port)
|
actual_port)
|
||||||
|
|
||||||
qos_policy.attach_port.assert_called_once_with(actual_port['id'])
|
qos_policy.attach_port.assert_called_once_with(actual_port['id'])
|
||||||
|
|
||||||
def test_process_resource_port_updated_policy(self):
|
def test_process_fields_port_updated_policy(self):
|
||||||
with self._mock_plugin_loaded(True):
|
with self._mock_plugin_loaded(True):
|
||||||
qos_policy_id = mock.Mock()
|
qos_policy_id = mock.Mock()
|
||||||
port_id = mock.Mock()
|
port_id = mock.Mock()
|
||||||
|
@ -80,29 +81,29 @@ class QosResourceExtensionHandlerTestCase(base.BaseTestCase):
|
||||||
return_value=old_qos_policy)
|
return_value=old_qos_policy)
|
||||||
new_qos_policy = mock.MagicMock()
|
new_qos_policy = mock.MagicMock()
|
||||||
self.policy_m.get_by_id = mock.Mock(return_value=new_qos_policy)
|
self.policy_m.get_by_id = mock.Mock(return_value=new_qos_policy)
|
||||||
self.ext_handler.process_resource(
|
self.core_extension.process_fields(
|
||||||
self.context, qos_extension.PORT,
|
self.context, base_core.PORT,
|
||||||
{qos_consts.QOS_POLICY_ID: qos_policy_id},
|
{qos_consts.QOS_POLICY_ID: qos_policy_id},
|
||||||
actual_port)
|
actual_port)
|
||||||
|
|
||||||
old_qos_policy.detach_port.assert_called_once_with(port_id)
|
old_qos_policy.detach_port.assert_called_once_with(port_id)
|
||||||
new_qos_policy.attach_port.assert_called_once_with(port_id)
|
new_qos_policy.attach_port.assert_called_once_with(port_id)
|
||||||
|
|
||||||
def test_process_resource_network_new_policy(self):
|
def test_process_fields_network_new_policy(self):
|
||||||
with self._mock_plugin_loaded(True):
|
with self._mock_plugin_loaded(True):
|
||||||
qos_policy_id = mock.Mock()
|
qos_policy_id = mock.Mock()
|
||||||
actual_network = {'id': mock.Mock(),
|
actual_network = {'id': mock.Mock(),
|
||||||
qos_consts.QOS_POLICY_ID: qos_policy_id}
|
qos_consts.QOS_POLICY_ID: qos_policy_id}
|
||||||
qos_policy = mock.MagicMock()
|
qos_policy = mock.MagicMock()
|
||||||
self.policy_m.get_by_id = mock.Mock(return_value=qos_policy)
|
self.policy_m.get_by_id = mock.Mock(return_value=qos_policy)
|
||||||
self.ext_handler.process_resource(
|
self.core_extension.process_fields(
|
||||||
self.context, qos_extension.NETWORK,
|
self.context, base_core.NETWORK,
|
||||||
{qos_consts.QOS_POLICY_ID: qos_policy_id}, actual_network)
|
{qos_consts.QOS_POLICY_ID: qos_policy_id}, actual_network)
|
||||||
|
|
||||||
qos_policy.attach_network.assert_called_once_with(
|
qos_policy.attach_network.assert_called_once_with(
|
||||||
actual_network['id'])
|
actual_network['id'])
|
||||||
|
|
||||||
def test_process_resource_network_updated_policy(self):
|
def test_process_fields_network_updated_policy(self):
|
||||||
with self._mock_plugin_loaded(True):
|
with self._mock_plugin_loaded(True):
|
||||||
qos_policy_id = mock.Mock()
|
qos_policy_id = mock.Mock()
|
||||||
network_id = mock.Mock()
|
network_id = mock.Mock()
|
||||||
|
@ -113,42 +114,42 @@ class QosResourceExtensionHandlerTestCase(base.BaseTestCase):
|
||||||
return_value=old_qos_policy)
|
return_value=old_qos_policy)
|
||||||
new_qos_policy = mock.MagicMock()
|
new_qos_policy = mock.MagicMock()
|
||||||
self.policy_m.get_by_id = mock.Mock(return_value=new_qos_policy)
|
self.policy_m.get_by_id = mock.Mock(return_value=new_qos_policy)
|
||||||
self.ext_handler.process_resource(
|
self.core_extension.process_fields(
|
||||||
self.context, qos_extension.NETWORK,
|
self.context, base_core.NETWORK,
|
||||||
{qos_consts.QOS_POLICY_ID: qos_policy_id}, actual_network)
|
{qos_consts.QOS_POLICY_ID: qos_policy_id}, actual_network)
|
||||||
|
|
||||||
old_qos_policy.detach_network.assert_called_once_with(network_id)
|
old_qos_policy.detach_network.assert_called_once_with(network_id)
|
||||||
new_qos_policy.attach_network.assert_called_once_with(network_id)
|
new_qos_policy.attach_network.assert_called_once_with(network_id)
|
||||||
|
|
||||||
def test_extract_resource_fields_plugin_not_loaded(self):
|
def test_extract_fields_plugin_not_loaded(self):
|
||||||
with self._mock_plugin_loaded(False):
|
with self._mock_plugin_loaded(False):
|
||||||
fields = self.ext_handler.extract_resource_fields(None, None)
|
fields = self.core_extension.extract_fields(None, None)
|
||||||
self.assertEqual({}, fields)
|
self.assertEqual({}, fields)
|
||||||
|
|
||||||
def _test_extract_resource_fields_for_port(self, qos_policy_id):
|
def _test_extract_fields_for_port(self, qos_policy_id):
|
||||||
with self._mock_plugin_loaded(True):
|
with self._mock_plugin_loaded(True):
|
||||||
fields = self.ext_handler.extract_resource_fields(
|
fields = self.core_extension.extract_fields(
|
||||||
qos_extension.PORT, _get_test_dbdata(qos_policy_id))
|
base_core.PORT, _get_test_dbdata(qos_policy_id))
|
||||||
self.assertEqual({qos_consts.QOS_POLICY_ID: qos_policy_id}, fields)
|
self.assertEqual({qos_consts.QOS_POLICY_ID: qos_policy_id}, fields)
|
||||||
|
|
||||||
def test_extract_resource_fields_no_port_policy(self):
|
def test_extract_fields_no_port_policy(self):
|
||||||
self._test_extract_resource_fields_for_port(None)
|
self._test_extract_fields_for_port(None)
|
||||||
|
|
||||||
def test_extract_resource_fields_port_policy_exists(self):
|
def test_extract_fields_port_policy_exists(self):
|
||||||
qos_policy_id = mock.Mock()
|
qos_policy_id = mock.Mock()
|
||||||
self._test_extract_resource_fields_for_port(qos_policy_id)
|
self._test_extract_fields_for_port(qos_policy_id)
|
||||||
|
|
||||||
def _test_extract_resource_fields_for_network(self, qos_policy_id):
|
def _test_extract_fields_for_network(self, qos_policy_id):
|
||||||
with self._mock_plugin_loaded(True):
|
with self._mock_plugin_loaded(True):
|
||||||
fields = self.ext_handler.extract_resource_fields(
|
fields = self.core_extension.extract_fields(
|
||||||
qos_extension.NETWORK, _get_test_dbdata(qos_policy_id))
|
base_core.NETWORK, _get_test_dbdata(qos_policy_id))
|
||||||
self.assertEqual({qos_consts.QOS_POLICY_ID: qos_policy_id}, fields)
|
self.assertEqual({qos_consts.QOS_POLICY_ID: qos_policy_id}, fields)
|
||||||
|
|
||||||
def test_extract_resource_fields_no_network_policy(self):
|
def test_extract_fields_no_network_policy(self):
|
||||||
self._test_extract_resource_fields_for_network(None)
|
self._test_extract_fields_for_network(None)
|
||||||
|
|
||||||
def test_extract_resource_fields_network_policy_exists(self):
|
def test_extract_fields_network_policy_exists(self):
|
||||||
qos_policy_id = mock.Mock()
|
qos_policy_id = mock.Mock()
|
||||||
qos_policy = mock.Mock()
|
qos_policy = mock.Mock()
|
||||||
qos_policy.id = qos_policy_id
|
qos_policy.id = qos_policy_id
|
||||||
self._test_extract_resource_fields_for_network(qos_policy_id)
|
self._test_extract_fields_for_network(qos_policy_id)
|
Loading…
Reference in New Issue