Merge "Subscribe Neutron port update events for FWG Logging handling"
This commit is contained in:
commit
ca4517295c
|
@ -22,6 +22,7 @@ from oslo_utils import importutils
|
|||
|
||||
from neutron_fwaas.common import fwaas_constants
|
||||
from neutron_fwaas.services.logapi.common import fwg_callback
|
||||
from neutron_fwaas.services.logapi.common import port_callback
|
||||
from neutron_fwaas.services.logapi import constants as fw_const
|
||||
from neutron_fwaas.services.logapi.rpc import log_server as rpc_server
|
||||
|
||||
|
@ -64,4 +65,7 @@ def register():
|
|||
# Register resource callback handler
|
||||
manager.register(
|
||||
fwaas_constants.FIREWALL_GROUP, fwg_callback.FirewallGroupCallBack)
|
||||
# Register resource callback handler for Neutron ports
|
||||
manager.register(resources.PORT, port_callback.NeutronPortCallBack)
|
||||
|
||||
LOG.debug('FWaaS L3 Logging driver based iptables registered')
|
||||
|
|
|
@ -55,10 +55,12 @@ def _get_ports_being_logged(context, log_obj):
|
|||
# TODO(longkb): L2 ports will be supported in the future
|
||||
# Check whether a port is router port or not.
|
||||
if device_owner in nl_const.ROUTER_INTERFACE_OWNERS:
|
||||
# Check whether a port is attached to firewall group or not
|
||||
fwg = fw_plugin_db.get_fwg_attached_to_port(context, port_id)
|
||||
if fwg:
|
||||
filtered_port_ids.append(port_id)
|
||||
# Checking port status
|
||||
if port.get('status') == nl_const.PORT_STATUS_ACTIVE:
|
||||
# Check whether a port is attached to firewall group or not
|
||||
fwg = fw_plugin_db.get_fwg_attached_to_port(context, port_id)
|
||||
if fwg:
|
||||
filtered_port_ids.append(port_id)
|
||||
return filtered_port_ids
|
||||
|
||||
|
||||
|
@ -75,6 +77,15 @@ def _make_log_info_dict(log_obj, port_ids):
|
|||
def get_logs_for_port(context, port_id):
|
||||
"""Return a list of log_resources bound to a given port_id"""
|
||||
|
||||
global fw_plugin_db
|
||||
if not fw_plugin_db:
|
||||
fw_plugin = directory.get_plugin(fwaas_constants.FIREWALL_V2)
|
||||
|
||||
# NOTE(longkb): check whether fw plugin was loaded or not.
|
||||
if not fw_plugin:
|
||||
return []
|
||||
fw_plugin_db = fw_plugin.driver.firewall_db
|
||||
|
||||
logs_bounded = []
|
||||
port = port_objects.Port.get_object(context, id=port_id)
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
# Copyright (c) 2018 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.common import constants as log_const
|
||||
from neutron.services.logapi.drivers import manager
|
||||
from neutron_lib.callbacks import events
|
||||
from neutron_lib import constants as nl_const
|
||||
|
||||
from neutron_fwaas.services.logapi.common import log_db_api
|
||||
|
||||
|
||||
class NeutronPortCallBack(manager.ResourceCallBackBase):
|
||||
|
||||
def handle_event(self, resource, event, trigger, **kwargs):
|
||||
if event == events.AFTER_UPDATE:
|
||||
context = kwargs.get('context')
|
||||
original_port = kwargs.get('original_port')
|
||||
port = kwargs.get('port')
|
||||
|
||||
if port['device_owner'] in nl_const.ROUTER_INTERFACE_OWNERS:
|
||||
if original_port['status'] != port['status']:
|
||||
self.trigger_logging(context, port)
|
||||
|
||||
def trigger_logging(self, context, port):
|
||||
log_resources = log_db_api.get_logs_for_port(context, port['id'])
|
||||
if log_resources:
|
||||
self.resource_push_api(
|
||||
log_const.RESOURCE_UPDATE, context, log_resources)
|
|
@ -54,13 +54,15 @@ def _fake_log_info(id, project_id, ports_id, event='ALL'):
|
|||
return expected
|
||||
|
||||
|
||||
def _fake_port_object(port_id, device_owner,
|
||||
def _fake_port_object(port_id, device_owner, status,
|
||||
project_id=uuidutils.generate_uuid()):
|
||||
port_data = {
|
||||
'id': port_id,
|
||||
'device_owner': device_owner,
|
||||
'project_id': project_id
|
||||
}
|
||||
if status:
|
||||
port_data['status'] = status
|
||||
return port_data
|
||||
|
||||
|
||||
|
@ -77,13 +79,16 @@ class LoggingRpcCallbackTestCase(base.BaseTestCase):
|
|||
self.router_port = uuidutils.generate_uuid()
|
||||
self.fake_vm_port = \
|
||||
_fake_port_object(self.vm_port,
|
||||
nl_const.DEVICE_OWNER_COMPUTE_PREFIX)
|
||||
nl_const.DEVICE_OWNER_COMPUTE_PREFIX,
|
||||
nl_const.PORT_STATUS_ACTIVE)
|
||||
|
||||
self.fake_router_port = \
|
||||
_fake_port_object(self.router_port,
|
||||
nl_const.DEVICE_OWNER_ROUTER_INTF)
|
||||
nl_const.DEVICE_OWNER_ROUTER_INTF,
|
||||
nl_const.PORT_STATUS_ACTIVE)
|
||||
self.fake_router_ports = \
|
||||
[_fake_port_object(self.router_port, device)
|
||||
[_fake_port_object(self.router_port, device,
|
||||
nl_const.PORT_STATUS_ACTIVE)
|
||||
for device in nl_const.ROUTER_INTERFACE_OWNERS]
|
||||
|
||||
def test_get_fwg_log_info_for_log_resources(self):
|
||||
|
@ -159,6 +164,19 @@ class LoggingRpcCallbackTestCase(base.BaseTestCase):
|
|||
log_db_api._get_ports_being_logged(self.context, log_obj)
|
||||
self.assertEqual([self.router_port], logged_port_ids)
|
||||
|
||||
# Test with inactive router port
|
||||
self.fake_router_port['status'] = nl_const.PORT_STATUS_DOWN
|
||||
log_obj = _create_log_object(tenant_id, resource_id=fwg_id,
|
||||
target_id=self.router_port)
|
||||
|
||||
log_db_api.fw_plugin_db. \
|
||||
get_fwg_attached_to_port = mock.Mock(return_value='fwg_id')
|
||||
with mock.patch.object(port_objects.Port, 'get_object',
|
||||
return_value=self.fake_router_port):
|
||||
logged_port_ids = \
|
||||
log_db_api._get_ports_being_logged(self.context, log_obj)
|
||||
self.assertEqual([], logged_port_ids)
|
||||
|
||||
def test_get_ports_being_logged_with_resource_id(self):
|
||||
tenant_id = uuidutils.generate_uuid()
|
||||
fwg_id = uuidutils.generate_uuid()
|
||||
|
@ -199,6 +217,18 @@ class LoggingRpcCallbackTestCase(base.BaseTestCase):
|
|||
log_db_api._get_ports_being_logged(self.context, log_obj)
|
||||
self.assertEqual([self.router_port], logged_port_ids)
|
||||
|
||||
# Test with inactive router port
|
||||
log_db_api.fw_plugin_db.get_ports_in_firewall_group = \
|
||||
mock.Mock(return_value=[self.router_port])
|
||||
log_db_api.fw_plugin_db. \
|
||||
get_fwg_attached_to_port = mock.Mock(return_value='fwg_id')
|
||||
|
||||
with mock.patch.object(port_objects.Port, 'get_object',
|
||||
return_value=self.fake_router_port):
|
||||
logged_port_ids = \
|
||||
log_db_api._get_ports_being_logged(self.context, log_obj)
|
||||
self.assertEqual([self.router_port], logged_port_ids)
|
||||
|
||||
def test_get_ports_being_logged_with_ports_in_tenant(self):
|
||||
tenant_id = uuidutils.generate_uuid()
|
||||
log_obj = _create_log_object(tenant_id)
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
# Copyright (c) 2018 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.
|
||||
|
||||
import mock
|
||||
from neutron.objects import ports as port_objects
|
||||
from neutron.services.logapi.drivers import base as log_driver_base
|
||||
from neutron.services.logapi.drivers import manager as driver_mgr
|
||||
from neutron.tests import base
|
||||
from neutron_lib.callbacks import events
|
||||
from neutron_lib.callbacks import registry
|
||||
from neutron_lib.callbacks import resources
|
||||
from neutron_lib import constants as nl_const
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from neutron_fwaas.services.logapi.common import log_db_api
|
||||
from neutron_fwaas.services.logapi.common import port_callback
|
||||
|
||||
FAKE_DRIVER = None
|
||||
|
||||
|
||||
class FakeDriver(log_driver_base.DriverBase):
|
||||
|
||||
@staticmethod
|
||||
def create():
|
||||
return FakeDriver(
|
||||
name='fake_driver',
|
||||
vif_types=[],
|
||||
vnic_types=[],
|
||||
supported_logging_types=['firewall_group'],
|
||||
requires_rpc=True
|
||||
)
|
||||
|
||||
|
||||
def fake_register():
|
||||
global FAKE_DRIVER
|
||||
if not FAKE_DRIVER:
|
||||
FAKE_DRIVER = FakeDriver.create()
|
||||
driver_mgr.register(resources.PORT, port_callback.NeutronPortCallBack)
|
||||
|
||||
|
||||
class TestFirewallGroupRuleCallback(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestFirewallGroupRuleCallback, self).setUp()
|
||||
self.driver_manager = driver_mgr.LoggingServiceDriverManager()
|
||||
self.port_callback = port_callback.NeutronPortCallBack(mock.Mock(),
|
||||
mock.Mock())
|
||||
self.m_context = mock.Mock()
|
||||
|
||||
def _create_port_object(self, name=None, device_owner=None,
|
||||
status=nl_const.PORT_STATUS_ACTIVE):
|
||||
port_data = {
|
||||
'id': uuidutils.generate_uuid(),
|
||||
'project_id': 'fake_tenant_id',
|
||||
'status': status
|
||||
}
|
||||
if name:
|
||||
port_data['name'] = name
|
||||
if device_owner:
|
||||
port_data['device_owner'] = device_owner
|
||||
return port_objects.Port(**port_data)
|
||||
|
||||
@mock.patch.object(port_callback.NeutronPortCallBack, 'handle_event')
|
||||
def test_handle_event(self, m_port_cb_handler):
|
||||
fake_register()
|
||||
self.driver_manager.register_driver(FAKE_DRIVER)
|
||||
|
||||
registry.notify(resources.PORT, events.AFTER_CREATE, mock.ANY)
|
||||
m_port_cb_handler.assert_called_once_with(
|
||||
resources.PORT, events.AFTER_CREATE, mock.ANY)
|
||||
|
||||
m_port_cb_handler.reset_mock()
|
||||
registry.notify(
|
||||
resources.PORT, events.AFTER_UPDATE, mock.ANY)
|
||||
m_port_cb_handler.assert_called_once_with(
|
||||
resources.PORT, events.AFTER_UPDATE, mock.ANY)
|
||||
|
||||
m_port_cb_handler.reset_mock()
|
||||
registry.notify(
|
||||
'non_registered_resource', events.AFTER_CREATE, mock.ANY)
|
||||
m_port_cb_handler.assert_not_called()
|
||||
|
||||
m_port_cb_handler.reset_mock()
|
||||
registry.notify(
|
||||
'non_registered_resource', events.AFTER_UPDATE, mock.ANY)
|
||||
m_port_cb_handler.assert_not_called()
|
||||
|
||||
def test_trigger_logging(self):
|
||||
fake_log_obj = mock.Mock()
|
||||
self.port_callback.resource_push_api = mock.Mock()
|
||||
port = self._create_port_object(device_owner='fake_device_owner')
|
||||
|
||||
# Test with log resource could be found from DB
|
||||
with mock.patch.object(log_db_api, 'get_logs_for_port',
|
||||
return_value=[fake_log_obj]):
|
||||
self.port_callback.trigger_logging(self.m_context, port)
|
||||
self.port_callback.resource_push_api.assert_called()
|
||||
|
||||
# Test with log resource could not be found from DB
|
||||
self.port_callback.resource_push_api.reset_mock()
|
||||
with mock.patch.object(log_db_api, 'get_logs_for_port',
|
||||
return_value=[]):
|
||||
self.port_callback.trigger_logging(self.m_context, port)
|
||||
self.port_callback.resource_push_api.assert_not_called()
|
||||
|
||||
def test_handle_event_with_router_port(self):
|
||||
with mock.patch.object(self.port_callback, 'trigger_logging'):
|
||||
# Test for router port enabling
|
||||
f_port_config = self._fake_port_config(
|
||||
nl_const.DEVICE_OWNER_ROUTER_INTF, action='enable')
|
||||
self.port_callback.handle_event(mock.ANY,
|
||||
events.AFTER_UPDATE,
|
||||
mock.ANY,
|
||||
**f_port_config)
|
||||
self.port_callback.trigger_logging.assert_called()
|
||||
|
||||
# Test for router port disabling
|
||||
self.port_callback.trigger_logging.reset_mock()
|
||||
f_port_config = self._fake_port_config(
|
||||
nl_const.DEVICE_OWNER_ROUTER_INTF, action='disable')
|
||||
self.port_callback.handle_event(mock.ANY,
|
||||
events.AFTER_UPDATE,
|
||||
mock.ANY,
|
||||
**f_port_config)
|
||||
self.port_callback.trigger_logging.assert_called()
|
||||
|
||||
# Test for router port status does not change
|
||||
self.port_callback.trigger_logging.reset_mock()
|
||||
f_port_config = \
|
||||
self._fake_port_config(nl_const.DEVICE_OWNER_ROUTER_INTF)
|
||||
self.port_callback.handle_event(mock.ANY,
|
||||
events.AFTER_UPDATE,
|
||||
mock.ANY,
|
||||
**f_port_config)
|
||||
self.port_callback.trigger_logging.assert_not_called()
|
||||
|
||||
def test_handle_event_with_non_router_port(self):
|
||||
with mock.patch.object(self.port_callback, 'trigger_logging'):
|
||||
# Test for port enabling
|
||||
f_port_config = self._fake_port_config('fake_port_type',
|
||||
action='enable')
|
||||
self.port_callback.handle_event(mock.ANY,
|
||||
events.AFTER_UPDATE,
|
||||
mock.ANY,
|
||||
**f_port_config)
|
||||
self.port_callback.trigger_logging.assert_not_called()
|
||||
|
||||
# Test for port disabling
|
||||
self.port_callback.trigger_logging.reset_mock()
|
||||
f_port_config = self._fake_port_config('fake_port_type',
|
||||
action='disable')
|
||||
self.port_callback.handle_event(mock.ANY,
|
||||
events.AFTER_UPDATE,
|
||||
mock.ANY,
|
||||
**f_port_config)
|
||||
self.port_callback.trigger_logging.assert_not_called()
|
||||
|
||||
def _fake_port_config(self, device_owner, action=None):
|
||||
f_kwargs = {}
|
||||
f_kwargs['context'] = self.m_context
|
||||
if action == 'enable':
|
||||
# Create original port with DOWN status
|
||||
original_port = self._create_port_object(
|
||||
device_owner=device_owner, status=nl_const.PORT_STATUS_DOWN)
|
||||
|
||||
# Create port with ACTIVE status
|
||||
port = self._create_port_object(
|
||||
device_owner=device_owner, status=nl_const.PORT_STATUS_ACTIVE)
|
||||
f_kwargs['original_port'] = original_port
|
||||
f_kwargs['port'] = port
|
||||
elif action == 'disable':
|
||||
# Create original port with ACTIVE status
|
||||
original_port = self._create_port_object(
|
||||
device_owner=device_owner, status=nl_const.PORT_STATUS_ACTIVE)
|
||||
|
||||
# Create port with DOWN status
|
||||
port = self._create_port_object(
|
||||
device_owner=device_owner, status=nl_const.PORT_STATUS_DOWN)
|
||||
f_kwargs['original_port'] = original_port
|
||||
f_kwargs['port'] = port
|
||||
else:
|
||||
# Create original port with ACTIVE status
|
||||
original_port = self._create_port_object(
|
||||
device_owner=device_owner, status=nl_const.PORT_STATUS_ACTIVE)
|
||||
|
||||
# Create port with ACTIVE status
|
||||
port = self._create_port_object(
|
||||
device_owner=device_owner, status=nl_const.PORT_STATUS_ACTIVE)
|
||||
f_kwargs['original_port'] = original_port
|
||||
f_kwargs['port'] = port
|
||||
return f_kwargs
|
Loading…
Reference in New Issue