Adds unique constraint on name and tenant_id

Update the schema for VIM, VNFD, VNF, NS and NSD tables
to constraint the name per tenant as unique.

Change-Id: I463fe02ebd4b0f720260bda1c3da4cdebc6ec238
Closes-bug: #1667641
This commit is contained in:
Kanagaraj Manickam 2017-03-01 12:52:49 +05:30
parent 0b91ca2f81
commit 8b517c0029
10 changed files with 209 additions and 138 deletions

View File

@ -20,6 +20,8 @@ Tacker base exception handling.
from oslo_utils import excutils
import six
from tacker._i18n import _
class TackerException(Exception):
"""Base Tacker Exception.
@ -281,3 +283,7 @@ class DuplicateResourceName(TackerException):
class InvalidParam(TackerException):
message = _("Param values must be a dict type")
class DuplicateEntity(TackerException):
message = _("%(_type)s already exist with given %(entry)s")

View File

@ -1 +1 @@
ef14f8026327
c256228ed37c

View File

@ -0,0 +1,40 @@
# 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.
#
"""unique constraint on name and id
Revision ID: c256228ed37c
Revises: 8f7145914cb0
Create Date: 2017-03-01 12:28:58.467900
"""
# revision identifiers, used by Alembic.
revision = 'c256228ed37c'
down_revision = 'ef14f8026327'
from alembic import op
def _add_unique_constraint(table):
op.create_unique_constraint(
constraint_name='uniq_%s0tenant_id0name' % table,
table_name=table,
columns=['tenant_id', 'name'])
def upgrade(active_plugins=None, options=None):
for table in ['vnf', 'vnfd', 'vims', 'ns', 'nsd']:
_add_unique_constraint(table)

View File

@ -16,13 +16,16 @@
import uuid
from oslo_db.exception import DBDuplicateEntry
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
from sqlalchemy import schema
from sqlalchemy import sql
from tacker.common import exceptions
from tacker.db.common_services import common_services_db
from tacker.db import db_base
from tacker.db import model_base
@ -56,6 +59,13 @@ class Vim(model_base.BASE,
vim_auth = orm.relationship('VimAuth')
status = sa.Column(sa.String(255), nullable=False)
__table_args__ = (
schema.UniqueConstraint(
"tenant_id",
"name",
name="uniq_vim0tenant_id0name"),
)
class VimAuth(model_base.BASE, models_v1.HasId):
vim_id = sa.Column(types.Uuid, sa.ForeignKey('vims.id'),
@ -105,25 +115,31 @@ class NfvoPluginDb(nfvo.NFVOPluginBase, db_base.CommonDbMixin):
def create_vim(self, context, vim):
self._validate_default_vim(context, vim)
vim_cred = vim['auth_cred']
with context.session.begin(subtransactions=True):
vim_db = Vim(
id=vim.get('id'),
type=vim.get('type'),
tenant_id=vim.get('tenant_id'),
name=vim.get('name'),
description=vim.get('description'),
placement_attr=vim.get('placement_attr'),
is_default=vim.get('is_default'),
status=vim.get('status'))
context.session.add(vim_db)
vim_auth_db = VimAuth(
id=str(uuid.uuid4()),
vim_id=vim.get('id'),
password=vim_cred.pop('password'),
vim_project=vim.get('vim_project'),
auth_url=vim.get('auth_url'),
auth_cred=vim_cred)
context.session.add(vim_auth_db)
try:
with context.session.begin(subtransactions=True):
vim_db = Vim(
id=vim.get('id'),
type=vim.get('type'),
tenant_id=vim.get('tenant_id'),
name=vim.get('name'),
description=vim.get('description'),
placement_attr=vim.get('placement_attr'),
is_default=vim.get('is_default'),
status=vim.get('status'))
context.session.add(vim_db)
vim_auth_db = VimAuth(
id=str(uuid.uuid4()),
vim_id=vim.get('id'),
password=vim_cred.pop('password'),
vim_project=vim.get('vim_project'),
auth_url=vim.get('auth_url'),
auth_cred=vim_cred)
context.session.add(vim_auth_db)
except DBDuplicateEntry as e:
raise exceptions.DuplicateEntity(
_type="vim",
entry=e.columns)
vim_dict = self._make_vim_dict(vim_db)
self._cos_db_plg.create_event(
context, res_id=vim_dict['id'],

View File

@ -13,6 +13,7 @@
import ast
import uuid
from oslo_db.exception import DBDuplicateEntry
from oslo_log import log as logging
from oslo_utils import timeutils
from six import iteritems
@ -20,7 +21,9 @@ from six import iteritems
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.orm import exc as orm_exc
from sqlalchemy import schema
from tacker.common import exceptions
from tacker.db.common_services import common_services_db
from tacker.db import db_base
from tacker.db import model_base
@ -55,6 +58,13 @@ class NSD(model_base.BASE, models_v1.HasId, models_v1.HasTenant,
attributes = orm.relationship('NSDAttribute',
backref='nsd')
__table_args__ = (
schema.UniqueConstraint(
"tenant_id",
"name",
name="uniq_nsd0tenant_id0name"),
)
class NSDAttribute(model_base.BASE, models_v1.HasId):
"""Represents attributes necessary for creation of ns in (key, value) pair
@ -91,6 +101,13 @@ class NS(model_base.BASE, models_v1.HasId, models_v1.HasTenant,
vim_id = sa.Column(types.Uuid, sa.ForeignKey('vims.id'), nullable=False)
error_reason = sa.Column(sa.Text, nullable=True)
__table_args__ = (
schema.UniqueConstraint(
"tenant_id",
"name",
name="uniq_ns0tenant_id0name"),
)
class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
@ -151,23 +168,27 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
LOG.debug(_('nsd %s'), nsd)
tenant_id = self._get_tenant_id_for_create(context, nsd)
with context.session.begin(subtransactions=True):
nsd_id = str(uuid.uuid4())
nsd_db = NSD(
id=nsd_id,
tenant_id=tenant_id,
name=nsd.get('name'),
vnfds=vnfds,
description=nsd.get('description'))
context.session.add(nsd_db)
for (key, value) in nsd.get('attributes', {}).items():
attribute_db = NSDAttribute(
id=str(uuid.uuid4()),
nsd_id=nsd_id,
key=key,
value=value)
context.session.add(attribute_db)
try:
with context.session.begin(subtransactions=True):
nsd_id = str(uuid.uuid4())
nsd_db = NSD(
id=nsd_id,
tenant_id=tenant_id,
name=nsd.get('name'),
vnfds=vnfds,
description=nsd.get('description'))
context.session.add(nsd_db)
for (key, value) in nsd.get('attributes', {}).items():
attribute_db = NSDAttribute(
id=str(uuid.uuid4()),
nsd_id=nsd_id,
key=key,
value=value)
context.session.add(attribute_db)
except DBDuplicateEntry as e:
raise exceptions.DuplicateEntity(
_type="nsd",
entry=e.columns)
LOG.debug(_('nsd_db %(nsd_db)s %(attributes)s '),
{'nsd_db': nsd_db,
'attributes': nsd_db.attributes})
@ -224,20 +245,25 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
vim_id = ns['vim_id']
name = ns.get('name')
ns_id = str(uuid.uuid4())
with context.session.begin(subtransactions=True):
nsd_db = self._get_resource(context, NSD,
nsd_id)
ns_db = NS(id=ns_id,
tenant_id=tenant_id,
name=name,
description=nsd_db.description,
vnf_ids=None,
status=constants.PENDING_CREATE,
mgmt_urls=None,
nsd_id=nsd_id,
vim_id=vim_id,
error_reason=None)
context.session.add(ns_db)
try:
with context.session.begin(subtransactions=True):
nsd_db = self._get_resource(context, NSD,
nsd_id)
ns_db = NS(id=ns_id,
tenant_id=tenant_id,
name=name,
description=nsd_db.description,
vnf_ids=None,
status=constants.PENDING_CREATE,
mgmt_urls=None,
nsd_id=nsd_id,
vim_id=vim_id,
error_reason=None)
context.session.add(ns_db)
except DBDuplicateEntry as e:
raise exceptions.DuplicateEntity(
_type="ns",
entry=e.columns)
evt_details = "NS UUID assigned."
self._cos_db_plg.create_event(
context, res_id=ns_id,

View File

@ -16,6 +16,7 @@
import uuid
from oslo_db.exception import DBDuplicateEntry
from oslo_log import log as logging
from oslo_utils import timeutils
from oslo_utils import uuidutils
@ -23,8 +24,10 @@ from oslo_utils import uuidutils
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.orm import exc as orm_exc
from sqlalchemy import schema
from tacker.api.v1 import attributes
from tacker.common import exceptions
from tacker import context as t_context
from tacker.db.common_services import common_services_db
from tacker.db import db_base
@ -70,6 +73,13 @@ class VNFD(model_base.BASE, models_v1.HasId, models_v1.HasTenant,
# vnfd template source - inline or onboarded
template_source = sa.Column(sa.String(255), server_default='onboarded')
__table_args__ = (
schema.UniqueConstraint(
"tenant_id",
"name",
name="uniq_vnfd0tenant_id0name"),
)
class ServiceType(model_base.BASE, models_v1.HasId, models_v1.HasTenant):
"""Represents service type which hosting vnf provides.
@ -127,6 +137,13 @@ class VNF(model_base.BASE, models_v1.HasId, models_v1.HasTenant,
vim = orm.relationship('Vim')
error_reason = sa.Column(sa.Text, nullable=True)
__table_args__ = (
schema.UniqueConstraint(
"tenant_id",
"name",
name="uniq_vnf0tenant_id0name"),
)
class VNFAttribute(model_base.BASE, models_v1.HasId):
"""Represents kwargs necessary for spinning up VM in (key, value) pair.
@ -229,32 +246,36 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
LOG.debug(_('service types unspecified'))
raise vnfm.ServiceTypesNotSpecified()
with context.session.begin(subtransactions=True):
vnfd_id = str(uuid.uuid4())
vnfd_db = VNFD(
id=vnfd_id,
tenant_id=tenant_id,
name=vnfd.get('name'),
description=vnfd.get('description'),
mgmt_driver=mgmt_driver,
template_source=template_source)
context.session.add(vnfd_db)
for (key, value) in vnfd.get('attributes', {}).items():
attribute_db = VNFDAttribute(
id=str(uuid.uuid4()),
vnfd_id=vnfd_id,
key=key,
value=value)
context.session.add(attribute_db)
for service_type in (item['service_type']
for item in vnfd['service_types']):
service_type_db = ServiceType(
id=str(uuid.uuid4()),
try:
with context.session.begin(subtransactions=True):
vnfd_id = str(uuid.uuid4())
vnfd_db = VNFD(
id=vnfd_id,
tenant_id=tenant_id,
vnfd_id=vnfd_id,
service_type=service_type)
context.session.add(service_type_db)
name=vnfd.get('name'),
description=vnfd.get('description'),
mgmt_driver=mgmt_driver,
template_source=template_source)
context.session.add(vnfd_db)
for (key, value) in vnfd.get('attributes', {}).items():
attribute_db = VNFDAttribute(
id=str(uuid.uuid4()),
vnfd_id=vnfd_id,
key=key,
value=value)
context.session.add(attribute_db)
for service_type in (item['service_type']
for item in vnfd['service_types']):
service_type_db = ServiceType(
id=str(uuid.uuid4()),
tenant_id=tenant_id,
vnfd_id=vnfd_id,
service_type=service_type)
context.session.add(service_type_db)
except DBDuplicateEntry as e:
raise exceptions.DuplicateEntity(
_type="vnfd",
entry=e.columns)
LOG.debug(_('vnfd_db %(vnfd_db)s %(attributes)s '),
{'vnfd_db': vnfd_db,
'attributes': vnfd_db.attributes})
@ -371,25 +392,30 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
attributes = vnf.get('attributes', {})
vim_id = vnf.get('vim_id')
placement_attr = vnf.get('placement_attr', {})
with context.session.begin(subtransactions=True):
vnfd_db = self._get_resource(context, VNFD,
vnfd_id)
vnf_db = VNF(id=vnf_id,
tenant_id=tenant_id,
name=name,
description=vnfd_db.description,
instance_id=None,
vnfd_id=vnfd_id,
vim_id=vim_id,
placement_attr=placement_attr,
status=constants.PENDING_CREATE,
error_reason=None)
context.session.add(vnf_db)
for key, value in attributes.items():
arg = VNFAttribute(
id=str(uuid.uuid4()), vnf_id=vnf_id,
key=key, value=value)
context.session.add(arg)
try:
with context.session.begin(subtransactions=True):
vnfd_db = self._get_resource(context, VNFD,
vnfd_id)
vnf_db = VNF(id=vnf_id,
tenant_id=tenant_id,
name=name,
description=vnfd_db.description,
instance_id=None,
vnfd_id=vnfd_id,
vim_id=vim_id,
placement_attr=placement_attr,
status=constants.PENDING_CREATE,
error_reason=None)
context.session.add(vnf_db)
for key, value in attributes.items():
arg = VNFAttribute(
id=str(uuid.uuid4()), vnf_id=vnf_id,
key=key, value=value)
context.session.add(arg)
except DBDuplicateEntry as e:
raise exceptions.DuplicateEntity(
_type="vnf",
entry=e.columns)
evt_details = "VNF UUID assigned."
self._cos_db_plg.create_event(
context, res_id=vnf_id,

View File

@ -118,9 +118,6 @@ class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
LOG.debug(_('Create vim called with parameters %s'),
strutils.mask_password(vim))
vim_obj = vim['vim']
name = vim_obj['name']
if self._get_by_name(context, nfvo_db.Vim, name):
raise exceptions.DuplicateResourceName(resource='VIM', name=name)
vim_type = vim_obj['type']
vim_obj['id'] = str(uuid.uuid4())
vim_obj['status'] = 'PENDING'
@ -446,10 +443,6 @@ class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
template)
LOG.debug(_('nsd %s'), nsd_data)
name = nsd_data['name']
if self._get_by_name(context, ns_db.NSD, name):
raise exceptions.DuplicateResourceName(resource='NSD', name=name)
self._parse_template_input(context, nsd)
return super(NfvoPlugin, self).create_nsd(
context, nsd)
@ -535,9 +528,6 @@ class NfvoPlugin(nfvo_db.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
driver_type = vim_res['vim_type']
if not ns['ns']['vim_id']:
ns['ns']['vim_id'] = vim_res['vim_id']
if self._get_by_name(context, ns_db.NS, ns['ns']['name']):
raise exceptions.DuplicateResourceName(resource='NS',
name=ns['ns']['name'])
# Step-1
param_values = ns['ns']['attributes'].get('param_values', {})

View File

@ -22,7 +22,6 @@ import uuid
from mock import patch
from tacker.common import exceptions
from tacker import context
from tacker.db.common_services import common_services_db
from tacker.db.nfvo import nfvo_db
@ -271,14 +270,6 @@ class TestNfvoPlugin(db_base.SqlTestCase):
self.assertIn('created_at', res)
self.assertIn('updated_at', res)
def test_create_vim_duplicate_name(self):
self._insert_dummy_vim()
vim_dict = utils.get_vim_obj()
vim_dict['vim']['name'] = 'fake_vim'
self.assertRaises(exceptions.DuplicateResourceName,
self.nfvo_plugin.create_vim,
self.context, vim_dict)
def test_delete_vim(self):
self._insert_dummy_vim()
vim_type = 'openstack'

View File

@ -19,7 +19,6 @@ import mock
from mock import patch
import yaml
from tacker.common import exceptions
from tacker import context
from tacker.db.common_services import common_services_db
from tacker.db.nfvo import nfvo_db
@ -250,14 +249,6 @@ class TestVNFMPlugin(db_base.SqlTestCase):
self.vnfm_plugin.create_vnfd,
self.context, vnfd_obj)
def test_create_vnfd_duplicate_name(self):
self._insert_dummy_device_template()
vnfd_obj = utils.get_dummy_vnfd_obj()
vnfd_obj['vnfd']['name'] = 'fake_template'
self.assertRaises(exceptions.DuplicateResourceName,
self.vnfm_plugin.create_vnfd,
self.context, vnfd_obj)
def test_create_vnf_with_vnfd(self):
self._insert_dummy_device_template()
vnf_obj = utils.get_dummy_vnf_obj()
@ -327,15 +318,6 @@ class TestVNFMPlugin(db_base.SqlTestCase):
self.assertIn('type', resources)
self.assertIn('id', resources)
def test_create_vnf_duplicate_name(self):
self._insert_dummy_device_template()
self._insert_dummy_device()
vnf_obj = utils.get_dummy_vnf_obj()
vnf_obj['vnf']['name'] = 'fake_device'
self.assertRaises(exceptions.DuplicateResourceName,
self.vnfm_plugin.create_vnf,
self.context, vnf_obj)
def test_delete_vnf(self):
self._insert_dummy_device_template()
dummy_device_obj = self._insert_dummy_device()

View File

@ -150,10 +150,6 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
LOG.debug(_('vnfd %s'), vnfd_data)
name = vnfd_data['name']
if self._get_by_name(context, vnfm_db.VNFD, name):
raise exceptions.DuplicateResourceName(resource='VNFD', name=name)
service_types = vnfd_data.get('service_types')
if not attributes.is_attr_set(service_types):
LOG.debug(_('service type must be specified'))
@ -337,8 +333,6 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
def create_vnf(self, context, vnf):
vnf_info = vnf['vnf']
name = vnf_info['name']
if self._get_by_name(context, vnfm_db.VNF, name):
raise exceptions.DuplicateResourceName(resource='VNF', name=name)
# if vnfd_template specified, create vnfd from template
# create template dictionary structure same as needed in create_vnfd()