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:
parent
fb1dab7857
commit
ea430ee82e
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue