[log]: add driver manager to LoggingPlugin

This patch adds driver manger to LoggingPlugin to manage
logging drivers are enabled belong ML2 dirvers.

Co-Authored-By: Yushiro FURUKAWA <y.furukawa_2@jp.fujitsu.com>

Change-Id: I5a0f896c9ec7f670b662f16825feccbc07db19dd
Partially-implements: blueprint security-group-logging
Related-Bug: #1468366
This commit is contained in:
Nguyen Phuong An 2016-11-09 17:02:48 +07:00
parent 12d6a3ea4c
commit 39a9ed4c50
9 changed files with 297 additions and 4 deletions

View File

@ -21,5 +21,5 @@ class LogResourceNotFound(n_exc.NotFound):
message = _("Log resource %(log_id)s could not be found.")
class InvalidLogReosurceType(n_exc.InvalidInput):
class InvalidLogResourceType(n_exc.InvalidInput):
message = _("Invalid log resource_type: %(resource_type)s.")

View File

@ -0,0 +1,79 @@
# Copyright (c) 2017 Fujitsu Limited
# 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 neutron_lib.callbacks import events
from neutron_lib.callbacks import registry
from oslo_log import log as logging
from neutron.services.logapi.common import constants as log_const
LOG = logging.getLogger(__name__)
@registry.has_registry_receivers
class DriverBase(object):
def __init__(self, name, vif_types, vnic_types,
supported_logging_types, requires_rpc=False):
"""Instantiate a log driver.
:param name: driver name.
:param vif_types: list of interfaces (VIFs) supported.
:param vnic_types: list of vnic types supported.
:param supported_logging_types: list of supported logging types.
:param requires_rpc: indicates if this driver expects rpc sever
to notify or callback
"""
self.name = name
self.vif_types = vif_types
self.vnic_types = vnic_types
self.supported_logging_types = supported_logging_types
self.requires_rpc = requires_rpc
# The log driver should advertise itself as supported driver by calling
# register_driver() on the LoggingServiceDriverManager. Therefore,
# logging plugin can discover which resources types are supported by
# the log driver.
@registry.receives(log_const.LOGGING_PLUGIN, [events.AFTER_INIT])
def _register(self, resource, event, trigger, **kwargs):
if self.is_loaded:
# trigger is the LoggingServiceDriverManager
trigger.register_driver(self)
def is_loaded(self):
"""True if the driver is active for the Neutron Server.
Implement this method to determine if your driver is actively
configured for this Neutron Server deployment.
"""
return True
def is_vif_type_compatible(self, vif_type):
"""True if the driver is compatible with the VIF type."""
return vif_type in self.vif_types
def is_vnic_compatible(self, vnic_type):
"""True if the driver is compatible with the specific VNIC type."""
return vnic_type in self.vnic_types
def is_logging_type_supported(self, log_type):
supported = log_type in self.supported_logging_types
if not supported:
LOG.debug("logging type %(log_type)s is not supported by "
"%(driver_name)s",
{'log_type': log_type,
'driver_name': self.name})
return supported

View File

@ -0,0 +1,53 @@
# Copyright (c) 2017 Fujitsu Limited
# 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 neutron_lib.callbacks import events
from neutron_lib.callbacks import registry
from oslo_log import log as logging
from neutron.services.logapi.common import constants as log_const
LOG = logging.getLogger(__name__)
class LoggingServiceDriverManager(object):
def __init__(self):
self._drivers = set()
registry.notify(log_const.LOGGING_PLUGIN, events.AFTER_INIT, self)
@property
def drivers(self):
return self._drivers
def register_driver(self, driver):
"""Register driver with logging plugin.
This method is called from drivers on INIT event.
"""
self._drivers.add(driver)
@property
def supported_logging_types(self):
if not self._drivers:
return set()
log_types = set()
for driver in self._drivers:
log_types |= set(driver.supported_logging_types)
LOG.debug("Supported logging types (logging types supported "
"by at least one loaded log_driver): %s", log_types)
return log_types

View File

@ -19,6 +19,7 @@ from neutron.extensions import logging as log_ext
from neutron.objects import base as base_obj
from neutron.objects.logapi import logging_resource as log_object
from neutron.services.logapi.common import exceptions as log_exc
from neutron.services.logapi.drivers import manager as driver_mgr
class LoggingPlugin(log_ext.LoggingPluginBase):
@ -29,11 +30,14 @@ class LoggingPlugin(log_ext.LoggingPluginBase):
__native_pagination_support = True
__native_sorting_support = True
def __init__(self):
super(LoggingPlugin, self).__init__()
self.driver_manager = driver_mgr.LoggingServiceDriverManager()
@property
def supported_logging_types(self):
# Todo(annp): supported_logging_types will dynamic load from
# log_drivers. So return value for this function is a temporary.
return []
# supported_logging_types are be dynamically loaded from log_drivers
return self.driver_manager.supported_logging_types
@db_base_plugin_common.filter_fields
@db_base_plugin_common.convert_result_to_dict
@ -60,6 +64,10 @@ class LoggingPlugin(log_ext.LoggingPluginBase):
def create_log(self, context, log):
"""Create a log object"""
log_data = log['log']
resource_type = log_data['resource_type']
if resource_type not in self.supported_logging_types:
raise log_exc.InvalidLogResourceType(
resource_type=resource_type)
with db_api.context_manager.writer.using(context):
# body 'log' contains both tenant_id and project_id
# but only latter needs to be used to create Log object.

View File

@ -0,0 +1,58 @@
# Copyright (C) 2017 Fujitsu Limited
# 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 neutron_lib.api.definitions import portbindings
from neutron.services.logapi.drivers import base as log_base_driver
from neutron.tests import base
SUPPORTED_LOGGING_TYPES = ['security_group']
class FakeDriver(log_base_driver.DriverBase):
@staticmethod
def create():
return FakeDriver(
name='fake_driver',
vif_types=[portbindings.VIF_TYPE_OVS],
vnic_types=[portbindings.VNIC_NORMAL],
supported_logging_types=SUPPORTED_LOGGING_TYPES,
requires_rpc=False
)
class TestDriverBase(base.BaseTestCase):
def setUp(self):
super(TestDriverBase, self).setUp()
self.driver = FakeDriver.create()
def test_is_vif_type_compatible(self):
self.assertFalse(
self.driver.is_vif_type_compatible(portbindings.VIF_TYPE_OTHER))
self.assertTrue(
self.driver.is_vif_type_compatible(portbindings.VIF_TYPE_OVS))
def test_is_vnic_compatible(self):
self.assertFalse(
self.driver.is_vnic_compatible(portbindings.VNIC_BAREMETAL))
self.assertTrue(
self.driver.is_vnic_compatible(portbindings.VNIC_NORMAL))
def test_is_logging_type_supported(self):
self.assertTrue(
self.driver.is_logging_type_supported('security_group'))
self.assertFalse(self.driver.is_logging_type_supported('firewall'))

View File

@ -0,0 +1,79 @@
# Copyright (c) 2017 Fujitsu Limited
# 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 neutron.services.logapi.drivers import base as log_driver_base
from neutron.services.logapi.drivers import manager as driver_mgr
from neutron.tests.unit.services.logapi import base
class TestLogDriversManagerBase(base.BaseLogTestCase):
def setUp(self):
super(TestLogDriversManagerBase, self).setUp()
self.config_parse()
self.setup_coreplugin(load_plugins=False)
@staticmethod
def _create_manager_with_drivers(drivers_details):
for name, driver_details in drivers_details.items():
class LogDriver(log_driver_base.DriverBase):
@property
def is_loaded(self):
return driver_details['is_loaded']
LogDriver(name,
driver_details.get('vif_types', []),
driver_details.get('vnic_types', []),
driver_details.get('supported_logging_types', []))
return driver_mgr.LoggingServiceDriverManager()
class TestLogDriversManagerMulti(TestLogDriversManagerBase):
"""Test calls happen to all drivers"""
def test_driver_manager_empty_with_no_drivers(self):
driver_manager = self._create_manager_with_drivers({})
self.assertEqual(0, len(driver_manager.drivers))
def test_driver_manager_empty_with_no_loaded_drivers(self):
driver_manager = self._create_manager_with_drivers(
{'driver-A': {'is_loaded': False}})
self.assertEqual(0, len(driver_manager.drivers))
def test_driver_manager_with_one_loaded_driver(self):
driver_manager = self._create_manager_with_drivers(
{'driver-A': {'is_loaded': True}})
self.assertEqual(1, len(driver_manager.drivers))
def test_driver_manager_with_two_loaded_drivers(self):
driver_manager = self._create_manager_with_drivers(
{'driver-A': {'is_loaded': True},
'driver-B': {'is_loaded': True}})
self.assertEqual(2, len(driver_manager.drivers))
class TestLogDriversManagerLoggingTypes(TestLogDriversManagerBase):
"""Test supported logging types"""
def test_available_logging_types(self):
driver_manager = self._create_manager_with_drivers(
{'driver-A': {'is_loaded': True,
'supported_logging_types': ['security_group']},
'driver-B': {'is_loaded': True,
'supported_logging_types':
['security_group', 'firewall']}
})
self.assertEqual(set(['security_group', 'firewall']),
driver_manager.supported_logging_types)

View File

@ -28,6 +28,7 @@ from neutron.services.logapi.common import exceptions as log_exc
from neutron.tests.unit.services.logapi import base
DB_PLUGIN_KLASS = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2'
SUPPORTED_LOGGING_TYPES = ['security_group']
class TestLoggingPlugin(base.BaseLogTestCase):
@ -51,6 +52,12 @@ class TestLoggingPlugin(base.BaseLogTestCase):
manager.init()
self.log_plugin = directory.get_plugin(constants.LOG_API)
self.log_plugin.driver_manager = mock.Mock()
log_types = mock.PropertyMock(return_value=SUPPORTED_LOGGING_TYPES)
self.log_plugin.driver_manager.supported_logging_types = \
mock.patch('neutron.services.logapi.drivers.manager.'
'LoggingServiceDriverManager.supported_logging_types',
new_callable=log_types).start()
self.ctxt = context.Context('fake_user', 'fake_tenant')
mock.patch.object(self.ctxt.session, 'refresh').start()
mock.patch.object(self.ctxt.session, 'expunge').start()
@ -159,6 +166,15 @@ class TestLoggingPlugin(base.BaseLogTestCase):
context=self.ctxt, **log_data['log'])
self.assertTrue(new_log.create.called)
def test_create_log_with_unsupported_logging_type(self):
log = {'log': {'resource_type': 'fake_type',
'enabled': True}}
self.assertRaises(
log_exc.InvalidLogResourceType,
self.log_plugin.create_log,
self.ctxt,
log)
def test_update_log(self):
log_data = {'log': {'enabled': True}}
new_log = mock.Mock()