From 8ca9c9cfeaa88cd3ada0026b82270df5c1544d90 Mon Sep 17 00:00:00 2001 From: Kanagaraj Manickam Date: Mon, 6 Jun 2016 10:07:52 +0530 Subject: [PATCH] Adds audit support for VIM, VNFD and VNF implements blueprint: audit-support Co-Authored-By: Vishwanath Jayaraman Change-Id: I8e6c4d6d47130c447c9a77e654d2dfad5d23ae7e --- .../versions/2ff0a0e360f1_audit_support.py | 37 +++++++++++++++++++ .../alembic_migrations/versions/HEAD | 3 +- tacker/db/models_v1.py | 9 +++++ tacker/db/nfvo/nfvo_db.py | 14 +++++-- tacker/db/vm/vm_db.py | 19 ++++++++-- tacker/extensions/nfvo.py | 10 +++++ tacker/extensions/vnfm.py | 20 ++++++++++ tacker/tests/unit/vm/nfvo/test_nfvo_plugin.py | 3 ++ tacker/tests/unit/vm/test_plugin.py | 5 +++ 9 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 tacker/db/migration/alembic_migrations/versions/2ff0a0e360f1_audit_support.py diff --git a/tacker/db/migration/alembic_migrations/versions/2ff0a0e360f1_audit_support.py b/tacker/db/migration/alembic_migrations/versions/2ff0a0e360f1_audit_support.py new file mode 100644 index 000000000..09dedfefb --- /dev/null +++ b/tacker/db/migration/alembic_migrations/versions/2ff0a0e360f1_audit_support.py @@ -0,0 +1,37 @@ +# 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 + +Revision ID: 2ff0a0e360f1 +Revises: 22f5385a3d50 +Create Date: 2016-06-02 15:14:31.888078 + +""" + +# revision identifiers, used by Alembic. +revision = '2ff0a0e360f1' +down_revision = '22f5385a3d50' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(active_plugins=None, options=None): + for table in ['vims', 'vnf', 'vnfd']: + op.add_column(table, + sa.Column('created_at', sa.DateTime(), nullable=True)) + op.add_column(table, + sa.Column('updated_at', sa.DateTime(), nullable=True)) diff --git a/tacker/db/migration/alembic_migrations/versions/HEAD b/tacker/db/migration/alembic_migrations/versions/HEAD index acd364f08..3fd0ce5e4 100644 --- a/tacker/db/migration/alembic_migrations/versions/HEAD +++ b/tacker/db/migration/alembic_migrations/versions/HEAD @@ -1 +1,2 @@ -22f5385a3d50 +2ff0a0e360f1 + diff --git a/tacker/db/models_v1.py b/tacker/db/models_v1.py index 6c7e3ed78..58817ae04 100644 --- a/tacker/db/models_v1.py +++ b/tacker/db/models_v1.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +from oslo_utils import timeutils from oslo_utils import uuidutils import sqlalchemy as sa @@ -31,3 +32,11 @@ class HasId(object): id = sa.Column(types.Uuid, primary_key=True, default=uuidutils.generate_uuid) + + +class Audit(object): + """Helps to add time stamp for create, update and delete actions. """ + + created_at = sa.Column(sa.DateTime, + default=lambda: timeutils.utcnow()) + updated_at = sa.Column(sa.DateTime) diff --git a/tacker/db/nfvo/nfvo_db.py b/tacker/db/nfvo/nfvo_db.py index 7da96edec..cabf7b3f8 100644 --- a/tacker/db/nfvo/nfvo_db.py +++ b/tacker/db/nfvo/nfvo_db.py @@ -18,6 +18,7 @@ import uuid from oslo_db import exception from oslo_utils import strutils +from oslo_utils import timeutils import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy.orm import exc as orm_exc @@ -33,11 +34,16 @@ from tacker import manager VIM_ATTRIBUTES = ('id', 'type', 'tenant_id', 'name', 'description', - 'placement_attr', 'shared', 'is_default', 'status') + 'placement_attr', 'shared', 'is_default', + 'created_at', 'updated_at', 'status') + VIM_AUTH_ATTRIBUTES = ('auth_url', 'vim_project', 'password', 'auth_cred') -class Vim(model_base.BASE, models_v1.HasId, models_v1.HasTenant): +class Vim(model_base.BASE, + models_v1.HasId, + models_v1.HasTenant, + models_v1.Audit): type = sa.Column(sa.String(64), nullable=False) name = sa.Column(sa.String(255), nullable=False) description = sa.Column(sa.Text, nullable=True) @@ -151,9 +157,9 @@ class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin): vim_cred = vim['auth_cred'] vim_project = vim['vim_project'] is_default = vim.get('is_default') + vim_db = self._get_resource(context, Vim, vim_id) try: if is_default: - vim_db = self._get_resource(context, Vim, vim_id) vim_db.update({'is_default': is_default}) vim_auth_db = (self._model_query(context, VimAuth).filter( VimAuth.vim_id == vim_id).with_lockmode('update').one()) @@ -162,6 +168,8 @@ class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin): vim_auth_db.update({'auth_cred': vim_cred, 'password': vim_cred.pop('password'), 'vim_project': vim_project}) + vim_db.update({'updated_at': timeutils.utcnow()}) + return self.get_vim(context, vim_id) def update_vim_status(self, context, vim_id, status): diff --git a/tacker/db/vm/vm_db.py b/tacker/db/vm/vm_db.py index ae11f0d0d..b4fa8f4c4 100644 --- a/tacker/db/vm/vm_db.py +++ b/tacker/db/vm/vm_db.py @@ -17,6 +17,8 @@ import uuid from oslo_log import log as logging +from oslo_utils import timeutils + import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy.orm import exc as orm_exc @@ -42,7 +44,8 @@ CREATE_STATES = (constants.PENDING_CREATE, constants.DEAD) ########################################################################### # db tables -class VNFD(model_base.BASE, models_v1.HasId, models_v1.HasTenant): +class VNFD(model_base.BASE, models_v1.HasId, models_v1.HasTenant, + models_v1.Audit): """Represents VNFD to create VNF.""" __tablename__ = 'vnfd' @@ -92,7 +95,8 @@ class VNFDAttribute(model_base.BASE, models_v1.HasId): value = sa.Column(sa.TEXT(65535), nullable=True) -class VNF(model_base.BASE, models_v1.HasId, models_v1.HasTenant): +class VNF(model_base.BASE, models_v1.HasId, models_v1.HasTenant, + models_v1.Audit): """Represents devices that hosts services. Here the term, 'VM', is intentionally avoided because it can be @@ -181,7 +185,8 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin): template.service_types) } key_list = ('id', 'tenant_id', 'name', 'description', - 'infra_driver', 'mgmt_driver') + 'infra_driver', 'mgmt_driver', + 'created_at', 'updated_at') res.update((key, template[key]) for key in key_list) return self._fields(res, fields) @@ -198,7 +203,7 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin): } key_list = ('id', 'tenant_id', 'name', 'description', 'instance_id', 'vim_id', 'placement_attr', 'vnfd_id', 'status', - 'mgmt_url', 'error_reason') + 'mgmt_url', 'error_reason', 'created_at', 'updated_at') res.update((key, device_db[key]) for key in key_list) return self._fields(res, fields) @@ -269,6 +274,7 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin): template_db = self._get_resource(context, VNFD, device_template_id) template_db.update(device_template['device_template']) + template_db.update({'updated_at': timeutils.utcnow()}) return self._make_template_dict(template_db) def delete_device_template(self, context, device_template_id): @@ -435,6 +441,11 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin): filter(VNF.id == device_id). filter(VNF.status == constants.PENDING_UPDATE). update({'status': new_status})) + (self._model_query(context, VNF). + filter(VNF.id == device_id). + filter(VNF.status == constants.PENDING_UPDATE). + update({'status': new_status, + 'updated_at': timeutils.utcnow()})) dev_attrs = new_device_dict.get('attributes', {}) (context.session.query(VNFAttribute). diff --git a/tacker/extensions/nfvo.py b/tacker/extensions/nfvo.py index d232dd838..4a0e64f39 100644 --- a/tacker/extensions/nfvo.py +++ b/tacker/extensions/nfvo.py @@ -160,6 +160,16 @@ RESOURCE_ATTRIBUTE_MAP = { 'is_visible': True, 'default': False }, + 'created_at': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, + 'updated_at': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, } } diff --git a/tacker/extensions/vnfm.py b/tacker/extensions/vnfm.py index 0a729f8fe..e2d82db54 100644 --- a/tacker/extensions/vnfm.py +++ b/tacker/extensions/vnfm.py @@ -224,6 +224,16 @@ RESOURCE_ATTRIBUTE_MAP = { 'is_visible': True, 'default': None, }, + 'created_at': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, + 'updated_at': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, }, 'vnfs': { @@ -303,6 +313,16 @@ RESOURCE_ATTRIBUTE_MAP = { 'allow_put': False, 'is_visible': True, }, + 'created_at': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, + 'updated_at': { + 'allow_post': False, + 'allow_put': False, + 'is_visible': True, + }, }, } diff --git a/tacker/tests/unit/vm/nfvo/test_nfvo_plugin.py b/tacker/tests/unit/vm/nfvo/test_nfvo_plugin.py index bb06b5640..4058732f7 100644 --- a/tacker/tests/unit/vm/nfvo/test_nfvo_plugin.py +++ b/tacker/tests/unit/vm/nfvo/test_nfvo_plugin.py @@ -88,6 +88,8 @@ class TestNfvoPlugin(db_base.SqlTestCase): self.assertEqual(SECRET_PASSWORD, res['auth_cred']['password']) self.assertIn('id', res) self.assertIn('placement_attr', res) + self.assertIn('created_at', res) + self.assertIn('updated_at', res) def test_delete_vim(self): self._insert_dummy_vim() @@ -118,3 +120,4 @@ class TestNfvoPlugin(db_base.SqlTestCase): self.assertEqual(vim_project, res['vim_project']) self.assertEqual(vim_auth_username, res['auth_cred']['username']) self.assertEqual(SECRET_PASSWORD, res['auth_cred']['password']) + self.assertIn('updated_at', res) diff --git a/tacker/tests/unit/vm/test_plugin.py b/tacker/tests/unit/vm/test_plugin.py index 6ee743b0a..1663a4b5c 100644 --- a/tacker/tests/unit/vm/test_plugin.py +++ b/tacker/tests/unit/vm/test_plugin.py @@ -152,6 +152,8 @@ class TestVNFMPlugin(db_base.SqlTestCase): self.assertIn('id', result) self.assertIn('service_types', result) self.assertIn('attributes', result) + self.assertIn('created_at', result) + self.assertIn('updated_at', result) self._device_manager.invoke.assert_called_once_with( mock.ANY, mock.ANY, @@ -183,6 +185,8 @@ class TestVNFMPlugin(db_base.SqlTestCase): self.assertIn('status', result) self.assertIn('attributes', result) self.assertIn('mgmt_url', result) + self.assertIn('created_at', result) + self.assertIn('updated_at', result) self._device_manager.invoke.assert_called_with(mock.ANY, mock.ANY, plugin=mock.ANY, context=mock.ANY, @@ -217,5 +221,6 @@ class TestVNFMPlugin(db_base.SqlTestCase): self.assertIn('status', result) self.assertIn('attributes', result) self.assertIn('mgmt_url', result) + self.assertIn('updated_at', result) self._pool.spawn_n.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY, mock.ANY)