Add log validator for FWaaS v2 logging
This patch adds a method to validate logging request for FWaaS. It also introduces get_fwg_attached_to_port method in db_v2 as helper function for logging feature. Co-Authored-By: Nguyen Phuong An <AnNP@vn.fujitsu.com> Co-Authored-By: Kim Bao Long <longkb@vn.fujitsu.com> Partial-Bug: #1720727 Change-Id: I723b48a75ba3f19476785bec0c5d5295d04aac95
This commit is contained in:
parent
d61bd2961c
commit
dd9aec1745
|
@ -844,6 +844,14 @@ class FirewallPluginDb(common_db_mixin.CommonDbMixin):
|
|||
return default_fwg.id
|
||||
return None
|
||||
|
||||
def get_fwg_attached_to_port(self, context, port_id):
|
||||
"""Return a firewall group ID is associated to a port"""
|
||||
fwg_port = self._model_query(context, FirewallGroupPortAssociation).\
|
||||
filter_by(port_id=port_id).first()
|
||||
if fwg_port:
|
||||
return fwg_port.firewall_group_id
|
||||
return None
|
||||
|
||||
def _ensure_default_firewall_group(self, context, tenant_id):
|
||||
"""Create a default firewall group if one doesn't exist for a tenant
|
||||
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# 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.
|
||||
|
||||
|
||||
# Firewall group logging resource type
|
||||
FIREWALL_GROUP = 'firewall_group'
|
||||
|
||||
# Target logging resource type
|
||||
TARGET_RESOURCE = 'port which is associated with the firewall group'
|
|
@ -0,0 +1,34 @@
|
|||
# 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._i18n import _
|
||||
from neutron_lib import exceptions as n_exc
|
||||
|
||||
# TODO(annp or longkb): move to neutron-lib
|
||||
|
||||
|
||||
class FWGIsNotReadyForLogging(n_exc.InvalidInput):
|
||||
message = _("Firewall group %(fwg_id)s is not ready for logging "
|
||||
"because of %(fwg_status)s status.")
|
||||
|
||||
|
||||
class TargetResourceNotAssociated(n_exc.InvalidInput):
|
||||
message = _("Target resource %(target_id)s is not associated with "
|
||||
"any firewall group.")
|
||||
|
||||
|
||||
class PortIsNotReadyForLogging(n_exc.InvalidInput):
|
||||
message = _("Target resource %(target_id)s is not ready for logging "
|
||||
"because of %(port_status)s status.")
|
|
@ -0,0 +1,125 @@
|
|||
# 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
|
||||
from neutron.services.logapi.common import exceptions as log_exc
|
||||
from neutron.services.logapi.common import validators
|
||||
from neutron_lib import constants as nl_const
|
||||
from neutron_lib.plugins import directory
|
||||
from sqlalchemy.orm import exc as orm_exc
|
||||
|
||||
from neutron_fwaas.common import fwaas_constants
|
||||
from neutron_fwaas.services.logapi import constants as log_const
|
||||
from neutron_fwaas.services.logapi import exceptions as fwg_log_exc
|
||||
|
||||
fwg_plugin = None
|
||||
|
||||
|
||||
def _check_fwg(context, fwg_id):
|
||||
try:
|
||||
fwg = fwg_plugin.get_firewall_group(context, id=fwg_id)
|
||||
except orm_exc.NoResultFound:
|
||||
raise log_exc.ResourceNotFound(resource_id=fwg_id)
|
||||
|
||||
if fwg['status'] != nl_const.ACTIVE:
|
||||
raise fwg_log_exc.FWGIsNotReadyForLogging(
|
||||
fwg_id=fwg_id, fwg_status=fwg['status'])
|
||||
|
||||
|
||||
def _check_fwg_port(context, port_id):
|
||||
|
||||
# Checking port exists
|
||||
port = ports.Port.get_object(context, id=port_id)
|
||||
if not port:
|
||||
raise log_exc.TargetResourceNotFound(target_id=port_id)
|
||||
|
||||
device_owner = port.get('device_owner', '')
|
||||
# Checking supported firewall group logging for vm port
|
||||
if device_owner.startswith(nl_const.DEVICE_OWNER_COMPUTE_PREFIX):
|
||||
if not validators.validate_log_type_for_port(
|
||||
log_const.FIREWALL_GROUP, port):
|
||||
raise log_exc.LoggingTypeNotSupported(
|
||||
log_type=log_const.FIREWALL_GROUP,
|
||||
port_id=port_id)
|
||||
# Checking supported firewall group for router interface, DVR interface,
|
||||
# and HA replicated interface
|
||||
elif device_owner not in nl_const.ROUTER_INTERFACE_OWNERS:
|
||||
raise log_exc.LoggingTypeNotSupported(
|
||||
log_type=log_const.FIREWALL_GROUP, port_id=port_id)
|
||||
|
||||
# Checking port status
|
||||
port_status = port.get('status')
|
||||
if port_status != nl_const.PORT_STATUS_ACTIVE:
|
||||
raise fwg_log_exc.PortIsNotReadyForLogging(target_id=port_id,
|
||||
port_status=port_status)
|
||||
|
||||
# Checking whether router port or vm port binding with any firewall group
|
||||
fwg_id = fwg_plugin.driver.firewall_db.get_fwg_attached_to_port(
|
||||
context, port_id=port_id)
|
||||
|
||||
if not fwg_id:
|
||||
raise fwg_log_exc.TargetResourceNotAssociated(target_id=port_id)
|
||||
|
||||
fwg = fwg_plugin.get_firewall_group(context, id=fwg_id)
|
||||
|
||||
if fwg['status'] != nl_const.ACTIVE:
|
||||
raise fwg_log_exc.FWGIsNotReadyForLogging(fwg_id=fwg_id,
|
||||
fwg_status=fwg['status'])
|
||||
|
||||
|
||||
def _check_target_resource_bound_fwg(context, fwg_id, target_id):
|
||||
ports = fwg_plugin.driver.firewall_db.get_ports_in_firewall_group(
|
||||
context=context, firewall_group_id=fwg_id)
|
||||
if target_id not in ports:
|
||||
raise log_exc.InvalidResourceConstraint(
|
||||
resource=log_const.FIREWALL_GROUP,
|
||||
resource_id=fwg_id,
|
||||
target_resource=log_const.TARGET_RESOURCE,
|
||||
target_id=target_id)
|
||||
|
||||
|
||||
@validators.ResourceValidateRequest.register(log_const.FIREWALL_GROUP)
|
||||
def validate_firewall_group_request(context, log_data):
|
||||
"""Validate a log request
|
||||
|
||||
This method validates log request is satisfied or not.
|
||||
|
||||
A ResourceNotFound will be raised if resource_id in log_data not exists or
|
||||
a TargetResourceNotFound will be raised if target_id in log_data not
|
||||
exists. Beside, FWGIsNotReadyForLogging will be raised in the case of
|
||||
queried firewall group is not in ACTIVE state. PortIsNotReadyForLogging
|
||||
exception will be raised if port is not in ACTIVE status. Besides,
|
||||
TargetResourceNotAssociated exception will be raised if a given port does
|
||||
not have any firewall group attach to. This method will also raise a
|
||||
LoggingTypeNotSupported, if there is no log_driver supporting for
|
||||
resource_type in log_data.
|
||||
|
||||
In addition, if log_data specify both resource_id and target_id. A
|
||||
InvalidResourceConstraint will be raised if there is no constraint between
|
||||
resource_id and target_id.
|
||||
|
||||
"""
|
||||
|
||||
global fwg_plugin
|
||||
if not fwg_plugin:
|
||||
fwg_plugin = directory.get_plugin(fwaas_constants.FIREWALL_V2)
|
||||
resource_id = log_data.get('resource_id')
|
||||
target_id = log_data.get('target_id')
|
||||
if resource_id and target_id:
|
||||
_check_target_resource_bound_fwg(context, resource_id, target_id)
|
||||
if resource_id:
|
||||
_check_fwg(context, resource_id)
|
||||
if target_id:
|
||||
_check_fwg_port(context, target_id)
|
|
@ -0,0 +1,157 @@
|
|||
# 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
|
||||
from neutron.services.logapi.common import exceptions as log_exc
|
||||
from neutron.services.logapi.common import validators
|
||||
from neutron.tests import base
|
||||
from neutron_lib import constants as nl_const
|
||||
from sqlalchemy.orm import exc as orm_exc
|
||||
|
||||
from neutron_fwaas.services.logapi import exceptions as fwg_log_exc
|
||||
from neutron_fwaas.services.logapi import fwg_validate
|
||||
|
||||
|
||||
class TestFWGLogRequestValidations(base.BaseTestCase):
|
||||
"""Test validator for a log creation request"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestFWGLogRequestValidations, self).setUp()
|
||||
fwg_validate.fwg_plugin = mock.Mock()
|
||||
fwg_validate.fwg_plugin.driver = mock.Mock()
|
||||
fwg_validate.fwg_plugin.driver.firewall_db = mock.Mock()
|
||||
|
||||
def test_validate_fwg_request(self):
|
||||
m_context = mock.Mock()
|
||||
fake_data = {
|
||||
'resource_type': 'firewall_group',
|
||||
'resource_id': 'fake_fwg_id'
|
||||
}
|
||||
with mock.patch.object(fwg_validate, '_check_fwg'):
|
||||
fwg_validate.validate_firewall_group_request(m_context, fake_data)
|
||||
fwg_validate._check_fwg.\
|
||||
assert_called_with(m_context, fake_data['resource_id'])
|
||||
fake_data = {
|
||||
'resource_type': 'firewall_group',
|
||||
'resource_id': 'fake_fwg_id',
|
||||
'target_id': 'fake_port_id'
|
||||
}
|
||||
with mock.patch.object(fwg_validate,
|
||||
'_check_target_resource_bound_fwg'):
|
||||
with mock.patch.object(fwg_validate, '_check_fwg'):
|
||||
with mock.patch.object(fwg_validate, '_check_fwg_port'):
|
||||
fwg_validate.validate_firewall_group_request(m_context,
|
||||
fake_data)
|
||||
fwg_validate._check_target_resource_bound_fwg.\
|
||||
assert_called_with(m_context,
|
||||
fake_data['resource_id'],
|
||||
fake_data['target_id'])
|
||||
fwg_validate._check_fwg. \
|
||||
assert_called_with(m_context,
|
||||
fake_data['resource_id'])
|
||||
fwg_validate._check_fwg_port. \
|
||||
assert_called_with(m_context,
|
||||
fake_data['target_id'])
|
||||
|
||||
def test_validate_request_fwg_id_not_exists(self):
|
||||
|
||||
with mock.patch.object(fwg_validate.fwg_plugin, 'get_firewall_group',
|
||||
side_effect=orm_exc.NoResultFound):
|
||||
self.assertRaises(
|
||||
log_exc.ResourceNotFound,
|
||||
fwg_validate._check_fwg,
|
||||
mock.ANY,
|
||||
'fake_fwg_id')
|
||||
|
||||
def test_validate_request_fwg_not_active(self):
|
||||
fake_fwg = {'id': '1234', 'status': 'PENDING'}
|
||||
with mock.patch.object(fwg_validate.fwg_plugin, 'get_firewall_group',
|
||||
return_value=fake_fwg):
|
||||
self.assertRaises(
|
||||
fwg_log_exc.FWGIsNotReadyForLogging,
|
||||
fwg_validate._check_fwg,
|
||||
mock.ANY,
|
||||
'fake_fwg_id')
|
||||
|
||||
def test_validate_request_router_or_port_id_not_exists(self):
|
||||
with mock.patch.object(ports.Port, 'get_object', return_value=None):
|
||||
self.assertRaises(
|
||||
log_exc.TargetResourceNotFound,
|
||||
fwg_validate._check_fwg_port,
|
||||
mock.ANY,
|
||||
'fake_port_id')
|
||||
|
||||
def test_validate_request_unsupported_fwg_log_on_vm_port(self):
|
||||
|
||||
fake_port = {'device_owner': "compute:"}
|
||||
with mock.patch.object(ports.Port, 'get_object',
|
||||
return_value=fake_port):
|
||||
with mock.patch.object(validators, 'validate_log_type_for_port',
|
||||
return_value=False):
|
||||
self.assertRaises(
|
||||
log_exc.LoggingTypeNotSupported,
|
||||
fwg_validate._check_fwg_port,
|
||||
mock.ANY,
|
||||
'fake_port_id')
|
||||
|
||||
def test_validate_request_router_port_is_not_active(self):
|
||||
|
||||
non_active_status = [nl_const.PORT_STATUS_DOWN,
|
||||
nl_const.PORT_STATUS_ERROR,
|
||||
nl_const.PORT_STATUS_NOTAPPLICABLE,
|
||||
nl_const.PORT_STATUS_BUILD]
|
||||
fake_port = [{'device_owner': nl_const.DEVICE_OWNER_ROUTER_INTF,
|
||||
'status': status}
|
||||
for status in non_active_status]
|
||||
with mock.patch.object(ports.Port, 'get_object',
|
||||
side_effect=fake_port):
|
||||
for status in non_active_status:
|
||||
self.assertRaises(
|
||||
fwg_log_exc.PortIsNotReadyForLogging,
|
||||
fwg_validate._check_fwg_port,
|
||||
mock.ANY,
|
||||
'fake_port_id')
|
||||
|
||||
def test_validate_request_router_port_was_not_associated_fwg(self):
|
||||
|
||||
fake_port = {'device_owner': nl_const.DEVICE_OWNER_ROUTER_INTF,
|
||||
'status': nl_const.PORT_STATUS_ACTIVE}
|
||||
|
||||
with mock.patch.object(ports.Port, 'get_object',
|
||||
return_value=fake_port):
|
||||
with mock.patch.object(fwg_validate.fwg_plugin.driver.firewall_db,
|
||||
'get_fwg_attached_to_port',
|
||||
return_value=None):
|
||||
self.assertRaises(
|
||||
fwg_log_exc.TargetResourceNotAssociated,
|
||||
fwg_validate._check_fwg_port,
|
||||
mock.ANY,
|
||||
'fake_port_id')
|
||||
|
||||
def test_validate_request_target_resource_not_bound_fwg(self):
|
||||
|
||||
fake_ports_in_fwg = ['fake_port_id1, fake_port_id2']
|
||||
with mock.patch.object(
|
||||
fwg_validate.fwg_plugin.driver.firewall_db,
|
||||
'get_ports_in_firewall_group',
|
||||
return_value=fake_ports_in_fwg):
|
||||
|
||||
self.assertRaises(
|
||||
log_exc.InvalidResourceConstraint,
|
||||
fwg_validate._check_target_resource_bound_fwg,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
'fake_target_id')
|
Loading…
Reference in New Issue