Adds common services plugin for audit support
This commit enables the event model/db, common services plugin and extenstions for audit support of Tacker resources. Implements: blueprint: audit-support Change-Id: I8fe824d335917c07d4b51dd63effae5f33bf0c32 Co-Authored-By: Kanagaraj Manickam <mkr1481@gmail.com>
This commit is contained in:
parent
30a1d2464b
commit
e94357abac
@ -42,6 +42,7 @@ tacker.service_plugins =
|
||||
dummy = tacker.tests.unit.dummy_plugin:DummyServicePlugin
|
||||
vnfm = tacker.vm.plugin:VNFMPlugin
|
||||
nfvo = tacker.nfvo.nfvo_plugin:NfvoPlugin
|
||||
commonservices = tacker.plugins.common_services.common_services_plugin:CommonServicesPlugin
|
||||
tacker.nfvo.vim.drivers =
|
||||
openstack = tacker.nfvo.drivers.vim.openstack_driver:OpenStack_Driver
|
||||
tacker.openstack.common.cache.backends =
|
||||
|
@ -40,7 +40,7 @@ core_opts = [
|
||||
help=_("The API paste config file to use")),
|
||||
cfg.StrOpt('api_extensions_path', default="",
|
||||
help=_("The path for API extensions")),
|
||||
cfg.ListOpt('service_plugins', default=['nfvo', 'vnfm'],
|
||||
cfg.ListOpt('service_plugins', default=['nfvo', 'vnfm', 'commonservices'],
|
||||
help=_("The service plugins Tacker will use")),
|
||||
cfg.StrOpt('policy_file', default="policy.json",
|
||||
help=_("The policy file to use")),
|
||||
|
0
tacker/db/common_services/__init__.py
Normal file
0
tacker/db/common_services/__init__.py
Normal file
99
tacker/db/common_services/common_services_db.py
Normal file
99
tacker/db/common_services/common_services_db.py
Normal file
@ -0,0 +1,99 @@
|
||||
# Copyright 2016 Brocade Communications System, Inc.
|
||||
# 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 sqlalchemy as sa
|
||||
from sqlalchemy.orm import exc as orm_exc
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from tacker.common import log
|
||||
from tacker.db import db_base
|
||||
from tacker.db import model_base
|
||||
from tacker.db import types
|
||||
from tacker.extensions import common_services
|
||||
from tacker import manager
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
EVENT_ATTRIBUTES = ('id', 'resource_id', 'resource_type', 'resource_state',
|
||||
'timestamp', 'event_type', 'event_details')
|
||||
|
||||
|
||||
class Event(model_base.BASE):
|
||||
id = sa.Column(sa.Integer, primary_key=True, nullable=False,
|
||||
autoincrement=True)
|
||||
resource_id = sa.Column(types.Uuid, nullable=False)
|
||||
resource_state = sa.Column(sa.String(64), nullable=False)
|
||||
resource_type = sa.Column(sa.String(64), nullable=False)
|
||||
timestamp = sa.Column(sa.DateTime, nullable=False)
|
||||
event_type = sa.Column(sa.String(64), nullable=False)
|
||||
event_details = sa.Column(types.Json)
|
||||
|
||||
|
||||
class CommonServicesPluginDb(common_services.CommonServicesPluginBase,
|
||||
db_base.CommonDbMixin):
|
||||
|
||||
def __init__(self):
|
||||
super(CommonServicesPluginDb, self).__init__()
|
||||
|
||||
@property
|
||||
def _core_plugin(self):
|
||||
return manager.TackerManager.get_plugin()
|
||||
|
||||
def _make_event_dict(self, event_db, fields=None):
|
||||
res = dict((key, event_db[key]) for key in EVENT_ATTRIBUTES)
|
||||
return self._fields(res, fields)
|
||||
|
||||
def _fields(self, resource, fields):
|
||||
if fields:
|
||||
return dict(((key, item) for key, item in resource.items()
|
||||
if key in fields))
|
||||
return resource
|
||||
|
||||
@log.log
|
||||
def create_event(self, context, res_id, res_type, res_state, evt_type,
|
||||
tstamp, details=""):
|
||||
try:
|
||||
with context.session.begin(subtransactions=True):
|
||||
event_db = Event(
|
||||
resource_id=res_id,
|
||||
resource_type=res_type,
|
||||
resource_state=res_state,
|
||||
event_details=details,
|
||||
event_type=evt_type,
|
||||
timestamp=tstamp)
|
||||
context.session.add(event_db)
|
||||
except Exception as e:
|
||||
LOG.exception(_("create event error: %s"), str(e))
|
||||
raise common_services.EventCreationFailureException(
|
||||
error_str=str(e))
|
||||
return self._make_event_dict(event_db)
|
||||
|
||||
@log.log
|
||||
def get_event(self, context, event_id, fields=None):
|
||||
try:
|
||||
events_db = self._get_by_id(context, Event, event_id)
|
||||
except orm_exc.NoResultFound:
|
||||
raise common_services.EventNotFoundException(evt_id=event_id)
|
||||
return self._make_event_dict(events_db, fields)
|
||||
|
||||
@log.log
|
||||
def get_events(self, context, filters=None, fields=None, sorts=None,
|
||||
limit=None, marker_obj=None, page_reverse=False):
|
||||
return self._get_collection(context, Event, self._make_event_dict,
|
||||
filters, fields, sorts, limit,
|
||||
marker_obj, page_reverse)
|
@ -0,0 +1,45 @@
|
||||
# Copyright 2016 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.
|
||||
#
|
||||
|
||||
"""audit_support_events
|
||||
|
||||
Revision ID: 4ee19c8a6d0a
|
||||
Revises: acf941e54075
|
||||
Create Date: 2016-06-07 03:16:53.513392
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4ee19c8a6d0a'
|
||||
down_revision = '941b5a6fff9e'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
from tacker.db import types
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
op.create_table('events',
|
||||
sa.Column('id', sa.Integer, nullable=False, autoincrement=True),
|
||||
sa.Column('resource_id', types.Uuid, nullable=False),
|
||||
sa.Column('resource_state', sa.String(64), nullable=False),
|
||||
sa.Column('resource_type', sa.String(64), nullable=False),
|
||||
sa.Column('event_type', sa.String(64), nullable=False),
|
||||
sa.Column('timestamp', sa.DateTime, nullable=False),
|
||||
sa.Column('event_details', types.Json),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_engine='InnoDB'
|
||||
)
|
@ -1,2 +1 @@
|
||||
941b5a6fff9e
|
||||
|
||||
4ee19c8a6d0a
|
145
tacker/extensions/common_services.py
Normal file
145
tacker/extensions/common_services.py
Normal file
@ -0,0 +1,145 @@
|
||||
# Copyright 2016 Brocade Communications Systems Inc
|
||||
# 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 abc
|
||||
|
||||
import six
|
||||
|
||||
from tacker.api import extensions
|
||||
from tacker.api.v1 import attributes as attr
|
||||
from tacker.api.v1 import resource_helper
|
||||
from tacker.common import exceptions
|
||||
from tacker.plugins.common import constants
|
||||
from tacker.services import service_base
|
||||
|
||||
|
||||
class EventCreationFailureException(exceptions.TackerException):
|
||||
message = _("Failed to create an event: %(error_str)s")
|
||||
|
||||
|
||||
class EventNotFoundException(exceptions.TackerException):
|
||||
message = _("Specified Event id %(evt_id)s is invalid. Please verify and "
|
||||
"pass a valid Event id")
|
||||
|
||||
|
||||
class InvalidModelException(exceptions.TackerException):
|
||||
message = _("Specified model is invalid, only Event model supported")
|
||||
|
||||
|
||||
RESOURCE_ATTRIBUTE_MAP = {
|
||||
|
||||
'events': {
|
||||
'id': {
|
||||
'allow_post': False,
|
||||
'allow_put': False,
|
||||
'is_visible': True,
|
||||
},
|
||||
'resource_id': {
|
||||
'allow_post': False,
|
||||
'allow_put': False,
|
||||
'is_visible': True
|
||||
},
|
||||
'resource_type': {
|
||||
'allow_post': False,
|
||||
'allow_put': False,
|
||||
'is_visible': True
|
||||
},
|
||||
'resource_state': {
|
||||
'allow_post': False,
|
||||
'allow_put': False,
|
||||
'is_visible': True
|
||||
},
|
||||
'timestamp': {
|
||||
'allow_post': False,
|
||||
'allow_put': False,
|
||||
'is_visible': True,
|
||||
},
|
||||
'event_details': {
|
||||
'allow_post': False,
|
||||
'allow_put': False,
|
||||
'is_visible': True,
|
||||
},
|
||||
'event_type': {
|
||||
'allow_post': False,
|
||||
'allow_put': False,
|
||||
'is_visible': True,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Common_services(extensions.ExtensionDescriptor):
|
||||
@classmethod
|
||||
def get_name(cls):
|
||||
return 'COMMONSERVICES'
|
||||
|
||||
@classmethod
|
||||
def get_alias(cls):
|
||||
return 'Commonservices'
|
||||
|
||||
@classmethod
|
||||
def get_description(cls):
|
||||
return "Extension for CommonServices"
|
||||
|
||||
@classmethod
|
||||
def get_namespace(cls):
|
||||
return 'http://wiki.openstack.org/Tacker'
|
||||
|
||||
@classmethod
|
||||
def get_updated(cls):
|
||||
return "2016-06-06T13:00:00-00:00"
|
||||
|
||||
@classmethod
|
||||
def get_resources(cls):
|
||||
special_mappings = {}
|
||||
plural_mappings = resource_helper.build_plural_mappings(
|
||||
special_mappings, RESOURCE_ATTRIBUTE_MAP)
|
||||
attr.PLURALS.update(plural_mappings)
|
||||
return resource_helper.build_resource_info(
|
||||
plural_mappings, RESOURCE_ATTRIBUTE_MAP, constants.COMMONSERVICES,
|
||||
translate_name=True)
|
||||
|
||||
@classmethod
|
||||
def get_plugin_interface(cls):
|
||||
return CommonServicesPluginBase
|
||||
|
||||
def update_attributes_map(self, attributes):
|
||||
super(Common_services, self).update_attributes_map(
|
||||
attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP)
|
||||
|
||||
def get_extended_resources(self, version):
|
||||
version_map = {'1.0': RESOURCE_ATTRIBUTE_MAP}
|
||||
return version_map.get(version, {})
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class CommonServicesPluginBase(service_base.NFVPluginBase):
|
||||
def get_plugin_name(self):
|
||||
return constants.COMMONSERVICES
|
||||
|
||||
def get_plugin_type(self):
|
||||
return constants.COMMONSERVICES
|
||||
|
||||
def get_plugin_description(self):
|
||||
return 'Tacker CommonServices plugin'
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_event(self, context, event_id, fields=None):
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_events(self, context, filters=None, fields=None, sorts=None,
|
||||
limit=None, marker_obj=None, page_reverse=False):
|
||||
pass
|
@ -18,12 +18,14 @@ CORE = "CORE"
|
||||
DUMMY = "DUMMY"
|
||||
VNFM = "VNFM"
|
||||
NFVO = "NFVO"
|
||||
COMMONSERVICES = "COMMONSERVICES"
|
||||
|
||||
COMMON_PREFIXES = {
|
||||
CORE: "",
|
||||
DUMMY: "/dummy_svc",
|
||||
VNFM: "",
|
||||
NFVO: ""
|
||||
NFVO: "",
|
||||
COMMONSERVICES: ""
|
||||
}
|
||||
|
||||
# Service operation status constants
|
||||
|
0
tacker/plugins/common_services/__init__.py
Normal file
0
tacker/plugins/common_services/__init__.py
Normal file
48
tacker/plugins/common_services/common_services_plugin.py
Normal file
48
tacker/plugins/common_services/common_services_plugin.py
Normal file
@ -0,0 +1,48 @@
|
||||
# Copyright 2016 Brocade Communications System, Inc.
|
||||
# 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_log import log as logging
|
||||
|
||||
from tacker.common import log
|
||||
from tacker.db.common_services import common_services_db
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CommonServicesPlugin(common_services_db.CommonServicesPluginDb):
|
||||
"""Reference plugin for COMMONSERVICES extension
|
||||
|
||||
Implements the COMMONSERVICES extension and defines public facing APIs for
|
||||
common utility operations.
|
||||
"""
|
||||
|
||||
supported_extension_aliases = ['CommonServices']
|
||||
|
||||
def __init__(self):
|
||||
super(CommonServicesPlugin, self).__init__()
|
||||
|
||||
@log.log
|
||||
def get_event(self, context, event_id, fields=None):
|
||||
return super(CommonServicesPlugin, self).get_event(context, event_id,
|
||||
fields)
|
||||
|
||||
@log.log
|
||||
def get_events(self, context, filters=None, fields=None, sorts=None,
|
||||
limit=None, marker_obj=None, page_reverse=False):
|
||||
return super(CommonServicesPlugin, self).get_events(context, filters,
|
||||
fields, sorts, limit,
|
||||
marker_obj,
|
||||
page_reverse)
|
159
tacker/tests/unit/test_common_services_plugin.py
Normal file
159
tacker/tests/unit/test_common_services_plugin.py
Normal file
@ -0,0 +1,159 @@
|
||||
# Copyright 2016 Brocade Communications System, Inc.
|
||||
# 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 oslo_utils import timeutils
|
||||
|
||||
from tacker import context
|
||||
from tacker.db.common_services import common_services_db
|
||||
from tacker.extensions import common_services
|
||||
from tacker.plugins.common_services import common_services_plugin
|
||||
from tacker.tests.unit.db import base as db_base
|
||||
|
||||
|
||||
class TestCommonServicesPlugin(db_base.SqlTestCase):
|
||||
def setUp(self):
|
||||
super(TestCommonServicesPlugin, self).setUp()
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
self.context = context.get_admin_context()
|
||||
self.event_db_plugin = common_services_db.CommonServicesPluginDb()
|
||||
self.coreutil_plugin = common_services_plugin.CommonServicesPlugin()
|
||||
|
||||
def _get_dummy_event_obj(self):
|
||||
return {
|
||||
'resource_id': '6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||
'resource_state': 'ACTIVE',
|
||||
'resource_type': 'VNF',
|
||||
'event_details': '',
|
||||
'event_type': 'scale_up',
|
||||
'timestamp': timeutils.parse_strtime('2016-07-20T05:43:52.765172')
|
||||
}
|
||||
|
||||
def test_create_event(self):
|
||||
evt_obj = self._get_dummy_event_obj()
|
||||
result = self.event_db_plugin.create_event(self.context,
|
||||
evt_obj['resource_id'],
|
||||
evt_obj['resource_type'],
|
||||
evt_obj['resource_state'],
|
||||
evt_obj['event_type'],
|
||||
evt_obj['timestamp'],
|
||||
evt_obj['event_details'])
|
||||
self.assertIsNotNone(result)
|
||||
self.assertIn('id', result)
|
||||
self.assertIn('resource_id', result)
|
||||
self.assertIn('resource_state', result)
|
||||
self.assertIn('resource_type', result)
|
||||
self.assertIn('event_type', result)
|
||||
self.assertIn('event_details', result)
|
||||
self.assertIn('timestamp', result)
|
||||
|
||||
def test_event_not_found(self):
|
||||
self.assertRaises(common_services.EventNotFoundException,
|
||||
self.coreutil_plugin.get_event, self.context, '99')
|
||||
|
||||
def test_InvalidModelInputExceptionNotThrown(self):
|
||||
evt_obj = self._get_dummy_event_obj()
|
||||
result = self.event_db_plugin.create_event(self.context,
|
||||
evt_obj['resource_id'],
|
||||
evt_obj['resource_type'],
|
||||
evt_obj['resource_state'],
|
||||
evt_obj['event_type'],
|
||||
evt_obj['timestamp'],
|
||||
evt_obj['event_details'])
|
||||
try:
|
||||
self.coreutil_plugin.get_event(self, context, str(result['id']))
|
||||
except common_services.InvalidModelException:
|
||||
self.assertTrue(False)
|
||||
except Exception:
|
||||
self.assertTrue(True)
|
||||
|
||||
def test_get_event_by_id(self):
|
||||
evt_obj = self._get_dummy_event_obj()
|
||||
evt_created = self.event_db_plugin.create_event(
|
||||
self.context, evt_obj['resource_id'],
|
||||
evt_obj['resource_type'],
|
||||
evt_obj['resource_state'],
|
||||
evt_obj['event_type'],
|
||||
evt_obj['timestamp'],
|
||||
evt_obj['event_details'])
|
||||
self.assertIsNotNone(evt_created)
|
||||
evt_get = self.coreutil_plugin.get_event(self.context,
|
||||
evt_created['id'])
|
||||
self.assertEqual(evt_created['resource_id'], evt_get['resource_id'])
|
||||
self.assertEqual(evt_created['resource_state'],
|
||||
evt_get['resource_state'])
|
||||
self.assertEqual(evt_created['resource_type'],
|
||||
evt_get['resource_type'])
|
||||
self.assertEqual(evt_created['event_type'], evt_get['event_type'])
|
||||
self.assertEqual(evt_created['event_details'],
|
||||
evt_get['event_details'])
|
||||
self.assertEqual(evt_created['timestamp'], evt_get['timestamp'])
|
||||
|
||||
def test_get_events(self):
|
||||
evt_obj = self._get_dummy_event_obj()
|
||||
self.event_db_plugin.create_event(self.context,
|
||||
evt_obj['resource_id'],
|
||||
evt_obj['resource_type'],
|
||||
evt_obj['resource_state'],
|
||||
evt_obj['event_type'],
|
||||
evt_obj['timestamp'],
|
||||
evt_obj['event_details'])
|
||||
result = self.coreutil_plugin.get_events(self.context)
|
||||
self.assertTrue(len(result))
|
||||
|
||||
def test_get_events_filtered_invalid_id(self):
|
||||
evt_obj = self._get_dummy_event_obj()
|
||||
self.event_db_plugin.create_event(self.context,
|
||||
evt_obj['resource_id'],
|
||||
evt_obj['resource_type'],
|
||||
evt_obj['resource_state'],
|
||||
evt_obj['event_type'],
|
||||
evt_obj['timestamp'],
|
||||
evt_obj['event_details'])
|
||||
result = self.coreutil_plugin.get_events(self.context, {'id': 'xyz'})
|
||||
self.assertFalse(len(result))
|
||||
|
||||
def test_get_events_filtered_valid_id(self):
|
||||
evt_obj = self._get_dummy_event_obj()
|
||||
self.event_db_plugin.create_event(self.context,
|
||||
evt_obj['resource_id'],
|
||||
evt_obj['resource_type'],
|
||||
evt_obj['resource_state'],
|
||||
evt_obj['event_type'],
|
||||
evt_obj['timestamp'],
|
||||
evt_obj['event_details'])
|
||||
result = self.coreutil_plugin.get_events(self.context, {'id': '1'})
|
||||
self.assertTrue(len(result))
|
||||
|
||||
def test_get_events_valid_fields(self):
|
||||
evt_obj = self._get_dummy_event_obj()
|
||||
self.event_db_plugin.create_event(self.context,
|
||||
evt_obj['resource_id'],
|
||||
evt_obj['resource_type'],
|
||||
evt_obj['resource_state'],
|
||||
evt_obj['event_type'],
|
||||
evt_obj['timestamp'],
|
||||
evt_obj['event_details'])
|
||||
result = self.coreutil_plugin.get_events(self.context, {'id': '1'},
|
||||
['id', 'event_type'])
|
||||
self.assertTrue(len(result))
|
||||
self.assertIn('id', result[0])
|
||||
self.assertNotIn('resource_id', result[0])
|
||||
self.assertNotIn('resource_state', result[0])
|
||||
self.assertNotIn('resource_type', result[0])
|
||||
self.assertIn('event_type', result[0])
|
||||
self.assertNotIn('event_details', result[0])
|
||||
self.assertNotIn('timestamp', result[0])
|
Loading…
Reference in New Issue
Block a user