[log]: db models and migration rules

This patch includes db models and migration rules for initial
logging object. The implementation bases on logging api for
security group spec[1]

[1] https://goo.gl/t3NUlr

Partially-implements: blueprint security-group-logging
Related-Bug: #1468366

Change-Id: I3f91b3927c33021facc9eb9238555c0e06a918c0
This commit is contained in:
Nguyen Phuong An 2016-11-09 15:35:59 +07:00
parent 31d24b4e1a
commit ae791696b8
16 changed files with 327 additions and 2 deletions

View File

@ -1 +1 @@
62c781cb6192
c8c222d42aa9

View File

@ -0,0 +1,60 @@
# Copyright 2017 OpenStack Foundation
#
# 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.
#
"""logging api
Revision ID: c8c222d42aa9
Revises: 62c781cb6192
Create Date: 2017-05-30 11:51:08.173604
"""
# revision identifiers, used by Alembic.
revision = 'c8c222d42aa9'
down_revision = '62c781cb6192'
from alembic import op
import sqlalchemy as sa
from neutron_lib.db import constants as db_const
def upgrade():
op.create_table(
'logs',
sa.Column('project_id',
sa.String(length=db_const.PROJECT_ID_FIELD_SIZE),
nullable=True,
index=True),
sa.Column('id', sa.String(length=db_const.UUID_FIELD_SIZE),
nullable=False),
sa.Column('standard_attr_id', sa.BigInteger(), nullable=False),
sa.Column('name', sa.String(length=db_const.NAME_FIELD_SIZE),
nullable=True),
sa.Column('resource_type', sa.String(length=36), nullable=False),
sa.Column('resource_id', sa.String(length=db_const.UUID_FIELD_SIZE),
nullable=True,
index=True),
sa.Column('target_id', sa.String(length=db_const.UUID_FIELD_SIZE),
nullable=True,
index=True),
sa.Column('event', sa.String(length=255), nullable=False),
sa.Column('enabled', sa.Boolean(), nullable=True),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['standard_attr_id'],
['standardattributes.id'],
ondelete='CASCADE'),
sa.UniqueConstraint('standard_attr_id'))

View File

@ -0,0 +1,37 @@
# Copyright (c) 2017 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_lib.db import constants as db_const
from neutron_lib.db import model_base
import sqlalchemy as sa
from neutron.db import standard_attr
class Log(standard_attr.HasStandardAttributes, model_base.BASEV2,
model_base.HasId, model_base.HasProject):
"""Represents neutron logging resource database"""
__tablename__ = 'logs'
name = sa.Column(sa.String(db_const.NAME_FIELD_SIZE))
resource_type = sa.Column(sa.String(36), nullable=False)
resource_id = sa.Column(sa.String(db_const.UUID_FIELD_SIZE),
nullable=True, index=True)
event = sa.Column(sa.String(255), nullable=False)
target_id = sa.Column(sa.String(db_const.UUID_FIELD_SIZE),
nullable=True, index=True)
enabled = sa.Column(sa.Boolean())
api_collections = ['logs']

View File

View File

@ -0,0 +1,38 @@
# 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 oslo_versionedobjects import fields as obj_fields
from neutron._i18n import _
from neutron.services.logapi.common import constants as log_const
class SecurityEvent(obj_fields.String):
def __init__(self, valid_values, **kwargs):
self._valid_values = valid_values
super(SecurityEvent, self).__init__(**kwargs)
def coerce(self, obj, attr, value):
if value not in self._valid_values:
msg = (
_("Field value %(value)s is not in the list "
"of valid values: %(values)s") %
{'value': value, 'values': self._valid_values}
)
raise ValueError(msg)
return super(SecurityEvent, self).coerce(obj, attr, value)
class SecurityEventField(obj_fields.AutoTypedField):
AUTO_TYPE = SecurityEvent(valid_values=log_const.VALID_EVENTS)

View File

@ -0,0 +1,45 @@
# Copyright (c) 2017 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 oslo_versionedobjects import base as obj_base
from oslo_versionedobjects import fields as obj_fields
from neutron.db.models import loggingapi as log_db
from neutron.objects import base
from neutron.objects import common_types
from neutron.objects.logapi import event_types
from neutron.services.logapi.common import constants as log_const
@obj_base.VersionedObjectRegistry.register
class Log(base.NeutronDbObject):
# Version 1.0: Initial version
VERSION = '1.0'
db_model = log_db.Log
fields = {
'id': common_types.UUIDField(),
'project_id': obj_fields.StringField(nullable=True),
'name': obj_fields.StringField(nullable=True),
'resource_type': obj_fields.StringField(),
'resource_id': common_types.UUIDField(nullable=True, default=None),
'target_id': common_types.UUIDField(nullable=True, default=None),
'event': event_types.SecurityEventField(default=log_const.ALL_EVENT),
'enabled': obj_fields.BooleanField(default=True),
}
fields_no_update = ['project_id', 'resource_type', 'resource_id',
'target_id', 'event']

View File

View File

@ -0,0 +1,20 @@
# Copyright 2017 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.
ACCEPT_EVENT = 'ACCEPT'
DROP_EVENT = 'DROP'
ALL_EVENT = 'ALL'
VALID_EVENTS = [ACCEPT_EVENT, DROP_EVENT, ALL_EVENT]
LOGGING_PLUGIN = 'logging-plugin'

View File

@ -0,0 +1,30 @@
# Copyright 2016 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
class ResourceLogNotFound(n_exc.NotFound):
message = _("Resource log %(id)s could not be found.")
class ParentResourceNotFound(n_exc.NotFound):
message = _("Parent resource %(parent_resource_id)s could not be found.")
class ResourceNotFound(n_exc.NotFound):
message = _("Resource %(resource_id)s could not be found.")

View File

@ -34,6 +34,7 @@ import unittest2
from neutron.api.v2 import attributes
from neutron.common import constants as n_const
from neutron.plugins.common import constants as p_const
from neutron.services.logapi.common import constants as log_const
class AttributeMapMemento(fixtures.Fixture):
@ -319,3 +320,7 @@ def reset_random_seed():
def get_random_ipv6_mode():
return random.choice(constants.IPV6_MODES)
def get_random_security_event():
return random.choice(log_const.VALID_EVENTS)

View File

@ -69,7 +69,8 @@ class StandardAttrAPIImapctTestCase(testlib_api.SqlTestCase):
# doc/source/devref/api_extensions.rst
expected = ['subnets', 'trunks', 'routers', 'segments',
'security_group_rules', 'networks', 'policies',
'subnetpools', 'ports', 'security_groups', 'floatingips']
'subnetpools', 'ports', 'security_groups', 'floatingips',
'logs']
self.assertEqual(
set(expected),
set(standard_attr.get_standard_attr_resource_model_map().keys())

View File

@ -0,0 +1,86 @@
# 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 oslo_utils import uuidutils
from neutron.objects.logapi import logging_resource as log_res
from neutron.objects import securitygroup
from neutron.tests.unit.objects import test_base
from neutron.tests.unit import testlib_api
class LogObjectTestCase(test_base.BaseObjectIfaceTestCase):
_test_class = log_res.Log
class LogDBObjectTestCase(test_base.BaseDbObjectTestCase,
testlib_api.SqlTestCase):
_test_class = log_res.Log
def setUp(self):
super(LogDBObjectTestCase, self).setUp()
self._network_id = self._create_test_network_id()
self._port_id = self._create_test_port_id(network_id=self._network_id)
self._security_group = self._create_test_security_group()
self.update_obj_fields({'resource_id': self._security_group['id'],
'target_id': self._port_id})
def _create_test_security_group(self):
sg_fields = self.get_random_object_fields(securitygroup.SecurityGroup)
sg_obj = securitygroup.SecurityGroup(self.context, **sg_fields)
return sg_obj
def test_create_sg_log_with_secgroup(self):
sg = self._create_test_security_group()
sg_log = log_res.Log(context=self.context,
id=uuidutils.generate_uuid(),
name='test-create',
resource_type='security_group',
resource_id=sg.id,
enabled=False)
sg_log.create()
self.assertEqual(sg.id, sg_log.resource_id)
def test_create_sg_log_with_port(self):
port_id = self._create_test_port_id(network_id=self._network_id)
sg_log = log_res.Log(context=self.context,
id=uuidutils.generate_uuid(),
name='test-create',
resource_type='security_group',
target_id=port_id,
enabled=False)
sg_log.create()
self.assertEqual(port_id, sg_log.target_id)
def _test_update_multiple_fields(self):
sg_log = log_res.Log(context=self.context,
id=uuidutils.generate_uuid(),
name='test-create',
description='test-description',
resource_type='security_group',
enabled=False)
sg_log.create()
fields = {'name': 'test-update', 'description': 'test-update-descr',
'enabled': True}
sg_log.update_fields(**fields)
sg_log.update()
new_sg_log = log_res.Log.get_object(self.context, id=sg_log.id)
self._assert_attrs(new_sg_log, **fields)
def _assert_attrs(self, sg_log, **kwargs):
"""Check the values passed in kwargs match the values of the sg log"""
for k in sg_log.fields:
if k in kwargs:
self.assertEqual(kwargs[k], sg_log[k])

View File

@ -40,6 +40,7 @@ from neutron.objects import common_types
from neutron.objects.db import api as obj_db_api
from neutron.objects import exceptions as o_exc
from neutron.objects import flavor
from neutron.objects.logapi import event_types
from neutron.objects import network as net_obj
from neutron.objects import ports
from neutron.objects import rbac_db
@ -445,6 +446,7 @@ FIELD_TYPE_VALUE_GENERATOR_MAP = {
common_types.SetOfUUIDsField: get_set_of_random_uuids,
common_types.UUIDField: uuidutils.generate_uuid,
common_types.VlanIdRangeField: tools.get_random_vlan,
event_types.SecurityEventField: tools.get_random_security_event,
obj_fields.BooleanField: tools.get_random_boolean,
obj_fields.DateTimeField: tools.get_random_datetime,
obj_fields.DictOfStringsField: get_random_dict_of_strings,

View File

@ -50,6 +50,7 @@ object_data = {
'IpamSubnet': '1.0-713de401682a70f34891e13af645fa08',
'MeteringLabel': '1.0-cc4b620a3425222447cbe459f62de533',
'MeteringLabelRule': '1.0-b5c5717e7bab8d1af1623156012a5842',
'Log': '1.0-6391351c0f34ed34375a19202f361d24',
'Network': '1.0-f2f6308f79731a767b92b26b0f4f3849',
'NetworkDNSDomain': '1.0-420db7910294608534c1e2e30d6d8319',
'NetworkPortSecurity': '1.0-b30802391a87945ee9c07582b4ff95e3',