Enables soft deletion for VIM, VNFD and VNF

implements blueprint: audit-support

Change-Id: Ia73389d76f7112fa7849a4904b3b2f32089406f0
Co-Authored-By: Vishwanath Jayaraman <vishwanathj@hotmail.com>
This commit is contained in:
Kanagaraj Manickam 2016-06-06 10:14:54 +05:30 committed by vish
parent 8ca9c9cfea
commit 30a1d2464b
6 changed files with 95 additions and 27 deletions

View File

@ -101,6 +101,11 @@ class CommonDbMixin(object):
# condition, raising an exception # condition, raising an exception
if query_filter is not None: if query_filter is not None:
query = query.filter(query_filter) query = query.filter(query_filter)
# Don't list the deleted entries
if hasattr(model, 'deleted_at'):
query = query.filter_by(deleted_at=None)
return query return query
def _fields(self, resource, fields): def _fields(self, resource, fields):
@ -138,6 +143,7 @@ class CommonDbMixin(object):
if result_filter: if result_filter:
query = result_filter(query, filters) query = result_filter(query, filters)
return query return query
def _apply_dict_extend_functions(self, resource_type, def _apply_dict_extend_functions(self, resource_type,

View File

@ -0,0 +1,39 @@
# 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.
#
"""enable soft delete
Revision ID: 941b5a6fff9e
Revises: 2ff0a0e360f1
Create Date: 2016-06-06 10:12:49.787430
"""
# revision identifiers, used by Alembic.
revision = '941b5a6fff9e'
down_revision = '2ff0a0e360f1'
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('deleted_at', sa.DateTime(), nullable=True))
# unique constraint is taken care by the nfvo_db plugin to support
# soft deletion of vim
op.drop_index('auth_url', table_name='vimauths')

View File

@ -1,2 +1,2 @@
2ff0a0e360f1 941b5a6fff9e

View File

@ -40,3 +40,4 @@ class Audit(object):
created_at = sa.Column(sa.DateTime, created_at = sa.Column(sa.DateTime,
default=lambda: timeutils.utcnow()) default=lambda: timeutils.utcnow())
updated_at = sa.Column(sa.DateTime) updated_at = sa.Column(sa.DateTime)
deleted_at = sa.Column(sa.DateTime)

View File

@ -16,7 +16,6 @@
import uuid import uuid
from oslo_db import exception
from oslo_utils import strutils from oslo_utils import strutils
from oslo_utils import timeutils from oslo_utils import timeutils
import sqlalchemy as sa import sqlalchemy as sa
@ -63,7 +62,6 @@ class VimAuth(model_base.BASE, models_v1.HasId):
auth_url = sa.Column(sa.String(255), nullable=False) auth_url = sa.Column(sa.String(255), nullable=False)
vim_project = sa.Column(types.Json, nullable=False) vim_project = sa.Column(types.Json, nullable=False)
auth_cred = sa.Column(types.Json, nullable=False) auth_cred = sa.Column(types.Json, nullable=False)
__table_args__ = (sa.UniqueConstraint('auth_url'), {})
class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin): class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin):
@ -101,10 +99,23 @@ class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin):
else: else:
raise raise
def _does_already_exist(self, context, vim):
try:
query = self._model_query(context, VimAuth)
for v_auth in query.filter(VimAuth.auth_url == vim.get('auth_url')
).all():
vim = self._get_by_id(context, Vim, v_auth.get('vim_id'))
if vim.get('deleted_at') is None:
return True
except orm_exc.NoResultFound:
pass
return False
def create_vim(self, context, vim): def create_vim(self, context, vim):
self._validate_default_vim(context, vim) self._validate_default_vim(context, vim)
vim_cred = vim['auth_cred'] vim_cred = vim['auth_cred']
try: if not self._does_already_exist(context, vim):
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
vim_db = Vim( vim_db = Vim(
id=vim.get('id'), id=vim.get('id'),
@ -124,20 +135,23 @@ class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin):
auth_url=vim.get('auth_url'), auth_url=vim.get('auth_url'),
auth_cred=vim_cred) auth_cred=vim_cred)
context.session.add(vim_auth_db) context.session.add(vim_auth_db)
except exception.DBDuplicateEntry: else:
raise nfvo.VimDuplicateUrlException() raise nfvo.VimDuplicateUrlException()
return self._make_vim_dict(vim_db) return self._make_vim_dict(vim_db)
def delete_vim(self, context, vim_id): def delete_vim(self, context, vim_id, soft_delete=True):
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
vim_db = self._get_resource(context, Vim, vim_id) vim_db = self._get_resource(context, Vim, vim_id)
context.session.query(VimAuth).filter_by( if soft_delete:
vim_id=vim_id).delete() vim_db.update({'deleted_at': timeutils.utcnow()})
context.session.delete(vim_db) else:
context.session.query(VimAuth).filter_by(
vim_id=vim_id).delete()
context.session.delete(vim_db)
def is_vim_still_in_use(self, context, vim_id): def is_vim_still_in_use(self, context, vim_id):
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
devices_db = context.session.query(vm_db.VNF).filter_by( devices_db = self._model_query(context, vm_db.VNF).filter_by(
vim_id=vim_id).first() vim_id=vim_id).first()
if devices_db is not None: if devices_db is not None:
raise nfvo.VimInUseException(vim_id=vim_id) raise nfvo.VimInUseException(vim_id=vim_id)

View File

@ -277,23 +277,29 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
template_db.update({'updated_at': timeutils.utcnow()}) template_db.update({'updated_at': timeutils.utcnow()})
return self._make_template_dict(template_db) return self._make_template_dict(template_db)
def delete_device_template(self, context, device_template_id): def delete_device_template(self,
context,
device_template_id,
soft_delete=True):
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
# TODO(yamahata): race. prevent from newly inserting hosting device # TODO(yamahata): race. prevent from newly inserting hosting device
# that refers to this template # that refers to this template
devices_db = context.session.query(VNF).filter_by( devices_db = context.session.query(VNF).filter_by(
vnfd_id=device_template_id).first() vnfd_id=device_template_id).first()
if devices_db is not None: if devices_db is not None and devices_db.deleted_at is None:
raise vnfm.DeviceTemplateInUse( raise vnfm.DeviceTemplateInUse(
device_template_id=device_template_id) device_template_id=device_template_id)
context.session.query(ServiceType).filter_by(
vnfd_id=device_template_id).delete()
context.session.query(VNFDAttribute).filter_by(
vnfd_id=device_template_id).delete()
template_db = self._get_resource(context, VNFD, template_db = self._get_resource(context, VNFD,
device_template_id) device_template_id)
context.session.delete(template_db) if soft_delete:
template_db.update({'deleted_at': timeutils.utcnow()})
else:
context.session.query(ServiceType).filter_by(
vnfd_id=device_template_id).delete()
context.session.query(VNFDAttribute).filter_by(
vnfd_id=device_template_id).delete()
context.session.delete(template_db)
def get_device_template(self, context, device_template_id, fields=None): def get_device_template(self, context, device_template_id, fields=None):
template_db = self._get_resource(context, VNFD, template_db = self._get_resource(context, VNFD,
@ -437,10 +443,6 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
def _update_device_post(self, context, device_id, new_status, def _update_device_post(self, context, device_id, new_status,
new_device_dict=None): new_device_dict=None):
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
(self._model_query(context, VNF).
filter(VNF.id == device_id).
filter(VNF.status == constants.PENDING_UPDATE).
update({'status': new_status}))
(self._model_query(context, VNF). (self._model_query(context, VNF).
filter(VNF.id == device_id). filter(VNF.id == device_id).
filter(VNF.status == constants.PENDING_UPDATE). filter(VNF.status == constants.PENDING_UPDATE).
@ -466,7 +468,7 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
return self._make_device_dict(device_db) return self._make_device_dict(device_db)
def _delete_device_post(self, context, device_id, error): def _delete_device_post(self, context, device_id, error, soft_delete=True):
with context.session.begin(subtransactions=True): with context.session.begin(subtransactions=True):
query = ( query = (
self._model_query(context, VNF). self._model_query(context, VNF).
@ -475,9 +477,12 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
if error: if error:
query.update({'status': constants.ERROR}) query.update({'status': constants.ERROR})
else: else:
(self._model_query(context, VNFAttribute). if soft_delete:
filter(VNFAttribute.vnf_id == device_id).delete()) query.update({'deleted_at': timeutils.utcnow()})
query.delete() else:
(self._model_query(context, VNFAttribute).
filter(VNFAttribute.vnf_id == device_id).delete())
query.delete()
# reference implementation. needs to be overrided by subclass # reference implementation. needs to be overrided by subclass
def create_device(self, context, device): def create_device(self, context, device):
@ -503,12 +508,15 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
return device_dict return device_dict
# reference implementation. needs to be overrided by subclass # reference implementation. needs to be overrided by subclass
def delete_device(self, context, device_id): def delete_device(self, context, device_id, soft_delete=True):
self._delete_device_pre(context, device_id) self._delete_device_pre(context, device_id)
# start actual deletion of hosting device. # start actual deletion of hosting device.
# Waiting for completion of deletion should be done backgroundly # Waiting for completion of deletion should be done backgroundly
# by another thread if it takes a while. # by another thread if it takes a while.
self._delete_device_post(context, device_id, False) self._delete_device_post(context,
device_id,
False,
soft_delete=soft_delete)
def get_device(self, context, device_id, fields=None): def get_device(self, context, device_id, fields=None):
device_db = self._get_resource(context, VNF, device_id) device_db = self._get_resource(context, VNF, device_id)