Adding resources callback handler for FWaaS v2 logging

This patch allows logging plugin to trigger logging driver in event of
receiving callback events AFTER_UPDATE/AFTER_CREATE/AFTER_DELETE from
firewall group resources.

Co-Authored-By: Nguyen Phuong An <AnNP@vn.fujitsu.com>
Partial-Bug: #1720727
Change-Id: I4e529273fe978d52e8948b844880a48f342fbd8c
This commit is contained in:
Kim Bao Long 2018-07-09 16:22:52 +07:00
parent fb1dab7857
commit ea430ee82e
3 changed files with 284 additions and 0 deletions

View File

@ -0,0 +1,61 @@
# 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.objects import ports as port_objects
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 FirewallGroupCallBack(manager.ResourceCallBackBase):
def handle_event(self, resource, event, trigger, **kwargs):
payload = kwargs.get('payload')
context = payload.context
ports_delta = []
if event == events.AFTER_CREATE:
# Update log when a new firewall group is created with ports
ports_delta = payload.latest_state['ports']
elif event == events.AFTER_UPDATE:
old_ports = payload.states[0]['ports']
new_ports = payload.states[1]['ports']
# Check whether port is updated from firewall group or not
ports_delta = \
set(new_ports).symmetric_difference(set(old_ports))
if self.need_to_notify(context, ports_delta):
self.trigger_logging(context, payload.resource_id, ports_delta)
def trigger_logging(self, context, fwg_id, ports_delta):
log_resources = log_db_api.get_logs_for_fwg(
context, fwg_id, ports_delta)
if log_resources:
self.resource_push_api(
log_const.RESOURCE_UPDATE, context, log_resources)
def need_to_notify(self, context, ports):
notify = False
for port_id in ports:
port = port_objects.Port.get_object(context, id=port_id)
device_owner = port.get('device_owner', '')
if device_owner in nl_const.ROUTER_INTERFACE_OWNERS:
notify = True
break
return notify

View File

@ -0,0 +1,223 @@
# 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 import constants as nl_const
from neutron_fwaas.common import fwaas_constants as fw_const
from neutron_fwaas.services.logapi.common import fwg_callback
from neutron_fwaas.services.logapi.common import log_db_api
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(fw_const.FIREWALL_GROUP,
fwg_callback.FirewallGroupCallBack)
class TestFirewallGroupRuleCallback(base.BaseTestCase):
def setUp(self):
super(TestFirewallGroupRuleCallback, self).setUp()
self.driver_manager = driver_mgr.LoggingServiceDriverManager()
self.fwg_callback = fwg_callback.FirewallGroupCallBack(mock.Mock(),
mock.Mock())
self.m_context = mock.Mock()
@mock.patch.object(fwg_callback.FirewallGroupCallBack, 'handle_event')
def test_handle_event(self, mock_fwg_cb):
fake_register()
self.driver_manager.register_driver(FAKE_DRIVER)
registry.notify(
fw_const.FIREWALL_GROUP, events.AFTER_CREATE, mock.ANY)
mock_fwg_cb.assert_called_once_with(
fw_const.FIREWALL_GROUP, events.AFTER_CREATE, mock.ANY)
mock_fwg_cb.reset_mock()
registry.notify(
fw_const.FIREWALL_GROUP, events.AFTER_UPDATE, mock.ANY)
mock_fwg_cb.assert_called_once_with(
fw_const.FIREWALL_GROUP, events.AFTER_UPDATE, mock.ANY)
mock_fwg_cb.reset_mock()
registry.notify(
'non_registered_resource', events.AFTER_CREATE, mock.ANY)
mock_fwg_cb.assert_not_called()
mock_fwg_cb.reset_mock()
registry.notify(
'non_registered_resource', events.AFTER_UPDATE, mock.ANY)
mock_fwg_cb.assert_not_called()
def test_need_to_notify(self):
port_objects.Port.get_object = \
mock.Mock(side_effect=self._get_object_side_effect)
# Test with router devices
for device in nl_const.ROUTER_INTERFACE_OWNERS:
result = self.fwg_callback.need_to_notify(self.m_context, [device])
self.assertEqual(True, result)
# Test with non-router device
result = self.fwg_callback.need_to_notify(self.m_context,
['fake_port'])
self.assertEqual(False, result)
# Test with ports_delta is empty
result = self.fwg_callback.need_to_notify(self.m_context, [])
self.assertEqual(False, result)
def test_trigger_logging(self):
m_payload = mock.Mock()
self.fwg_callback.resource_push_api = mock.Mock()
m_payload.resource_id = 'fake_resource_id'
ports_delta = ['fake_port_id']
# Test with log resource could be found from DB
with mock.patch.object(log_db_api, 'get_logs_for_fwg',
return_value={'fake': 'fake'}):
self.fwg_callback.trigger_logging(self.m_context,
m_payload.resource_id,
ports_delta)
self.fwg_callback.resource_push_api.assert_called()
# Test with log resource could not be found from DB
self.fwg_callback.resource_push_api.reset_mock()
with mock.patch.object(log_db_api, 'get_logs_for_fwg',
return_value={}):
self.fwg_callback.trigger_logging(self.m_context,
m_payload.resource_id,
ports_delta)
self.fwg_callback.resource_push_api.assert_not_called()
def _get_object_side_effect(self, context, id):
fake_port = {
'id': 'fake_id',
'device_owner': id,
}
return fake_port
def test_handle_event_with_router_port(self):
with mock.patch.object(self.fwg_callback, 'need_to_notify',
return_value=True):
with mock.patch.object(self.fwg_callback, 'trigger_logging'):
# Test for firewall group creation with router port
m_payload = self._mock_payload(events.AFTER_CREATE,
'fake_port_id')
self.fwg_callback.handle_event(mock.ANY,
events.AFTER_CREATE,
mock.ANY,
**{'payload': m_payload})
self.fwg_callback.trigger_logging.assert_called()
# Test for firewall group update with router port
self.fwg_callback.trigger_logging.reset_mock()
m_payload = self._mock_payload(events.AFTER_UPDATE,
'fake_port_id')
self.fwg_callback.handle_event(mock.ANY,
events.AFTER_UPDATE,
mock.ANY,
**{'payload': m_payload})
self.fwg_callback.trigger_logging.assert_called()
def test_handle_event_with_non_router_port(self):
with mock.patch.object(self.fwg_callback, 'need_to_notify',
return_value=False):
with mock.patch.object(self.fwg_callback, 'trigger_logging'):
# Test for firewall group creation with non router ports
m_payload = self._mock_payload(events.AFTER_CREATE,
'fake_port_id')
self.fwg_callback.handle_event(mock.ANY,
events.AFTER_CREATE,
mock.ANY,
**{'payload': m_payload})
self.fwg_callback.trigger_logging.assert_not_called()
# Test for firewall group creation without ports
self.fwg_callback.trigger_logging.reset_mock()
m_payload = self._mock_payload(events.AFTER_CREATE)
self.fwg_callback.handle_event(mock.ANY,
events.AFTER_CREATE,
mock.ANY,
**{'payload': m_payload})
self.fwg_callback.trigger_logging.assert_not_called()
# Test for firewall group update with non router ports
self.fwg_callback.trigger_logging.reset_mock()
m_payload = self._mock_payload(events.AFTER_UPDATE,
'fake_port_id')
self.fwg_callback.handle_event(mock.ANY,
events.AFTER_UPDATE,
mock.ANY,
**{'payload': m_payload})
self.fwg_callback.trigger_logging.assert_not_called()
# Test for firewall group update without ports
self.fwg_callback.trigger_logging.reset_mock()
m_payload = self._mock_payload(events.AFTER_UPDATE)
self.fwg_callback.handle_event(mock.ANY,
events.AFTER_UPDATE,
mock.ANY,
**{'payload': m_payload})
self.fwg_callback.trigger_logging.assert_not_called()
def _mock_payload(self, event, ports_delta=None):
m_payload = mock.Mock()
m_payload.context = self.m_context
if event == events.AFTER_CREATE:
if ports_delta:
m_payload.latest_state = {
'ports': [ports_delta]
}
else:
m_payload.latest_state = {
'ports': []
}
if event == events.AFTER_UPDATE:
if ports_delta:
m_payload.states = [
{'ports': [ports_delta]},
{'ports': []}
]
else:
m_payload.states = [
{'ports': []},
{'ports': []}
]
return m_payload