Merge "Subscribe Neutron port update events for FWG Logging handling"

This commit is contained in:
Zuul 2018-10-14 06:16:07 +00:00 committed by Gerrit Code Review
commit ca4517295c
5 changed files with 296 additions and 8 deletions

View File

@ -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')

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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