Following up patch of 6673dbb88f
This removes flavor extra specs table and related methods, moving extra specs to flavor table to make things simple, as we have already changed to use flavor PATCH method to manage such things. Change-Id: I1fb5445b7d194b50b052b77624a70e59af547911
This commit is contained in:
parent
ef730d75df
commit
81f9d78d05
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"extra_specs":{
|
||||
"key_1": "value_1",
|
||||
"key_2": "value_2"
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"key_3": "value_3"
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"extra_specs":{
|
||||
"key_1": "value_1",
|
||||
"key_2": "value_2",
|
||||
"key_3": "value_3"
|
||||
}
|
||||
}
|
|
@ -255,16 +255,6 @@ class NoValidNode(MoganException):
|
|||
_msg_fmt = _("No valid node was found. %(reason)s")
|
||||
|
||||
|
||||
class FlavorExtraSpecUpdateCreateFailed(MoganException):
|
||||
_msg_fmt = _("Flavor %(id)s extra spec cannot be updated or"
|
||||
"created after %(retries)d retries.")
|
||||
|
||||
|
||||
class FlavorExtraSpecsNotFound(NotFound):
|
||||
_msg_fmt = _("Flavor %(flavor_id)s has no extra specs with "
|
||||
"key %(extra_specs_key)s.")
|
||||
|
||||
|
||||
class InterfacePlugException(MoganException):
|
||||
_msg_fmt = _("Interface plugin failed")
|
||||
|
||||
|
|
|
@ -141,15 +141,6 @@ server_policies = [
|
|||
policy.RuleDefault('mogan:flavor_access:get_all',
|
||||
'rule:allow',
|
||||
description='Retrieve all flavor access'),
|
||||
policy.RuleDefault('mogan:flavor_extra_specs:patch',
|
||||
'rule:admin_api',
|
||||
description='Create/Update flavor extra specs'),
|
||||
policy.RuleDefault('mogan:flavor_extra_specs:delete',
|
||||
'rule:admin_api',
|
||||
description='Remove flavor extra specs'),
|
||||
policy.RuleDefault('mogan:flavor_extra_specs:get_all',
|
||||
'rule:allow',
|
||||
description='Retrieve flavor extra specs'),
|
||||
policy.RuleDefault('mogan:node:get_all',
|
||||
'rule:admin_api',
|
||||
description='Retrieve all compute nodes'),
|
||||
|
|
|
@ -157,28 +157,6 @@ class Connection(object):
|
|||
def compute_disk_update(self, context, disk_uuid, values):
|
||||
"""Update a compute disk."""
|
||||
|
||||
# Flavor extra specs
|
||||
@abc.abstractmethod
|
||||
def extra_specs_update_or_create(self, context,
|
||||
flavor_uuid, extra_specs):
|
||||
"""Create or update server type extra specs.
|
||||
|
||||
This adds or modifies the key/value pairs specified in the
|
||||
extra specs dict argument
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def flavor_extra_specs_get(self, context, type_id):
|
||||
"""Get server type extra specs"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def type_extra_specs_delete(self, context, flavor_uuid, key):
|
||||
"""Delete server type extra specs.
|
||||
|
||||
This deletes the key/value pairs specified in the
|
||||
extra specs dict argument
|
||||
"""
|
||||
|
||||
# Flavor access
|
||||
@abc.abstractmethod
|
||||
def flavor_access_add(self, context, flavor_id, project_id):
|
||||
|
|
|
@ -35,25 +35,12 @@ def upgrade():
|
|||
sa.Column('uuid', sa.String(length=36), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('description', sa.String(length=255), nullable=True),
|
||||
sa.Column('extra_specs', sa.Text(), nullable=True),
|
||||
sa.Column('is_public', sa.Boolean(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('uuid'),
|
||||
mysql_ENGINE='InnoDB',
|
||||
mysql_DEFAULT_CHARSET='UTF8'
|
||||
)
|
||||
op.create_table(
|
||||
'flavor_extra_specs',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('flavor_uuid', sa.String(length=36), nullable=False),
|
||||
sa.Column('key', sa.String(length=255), nullable=False),
|
||||
sa.Column('value', sa.String(length=255), nullable=False),
|
||||
sa.ForeignKeyConstraint(['flavor_uuid'],
|
||||
['flavors.uuid']),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_ENGINE='InnoDB',
|
||||
mysql_DEFAULT_CHARSET='UTF8'
|
||||
)
|
||||
op.create_table(
|
||||
'flavor_projects',
|
||||
sa.Column('created_at', sa.DateTime(), nullable=True),
|
||||
|
|
|
@ -100,11 +100,6 @@ def _dict_with_extra_fields(flavor_query):
|
|||
"""Takes a server type query and returns it as a dictionary."""
|
||||
flavor_dict = dict(flavor_query)
|
||||
|
||||
# extra specs
|
||||
extra_specs = {x['key']: x['value']
|
||||
for x in flavor_query['extra_specs']}
|
||||
flavor_dict['extra_specs'] = extra_specs
|
||||
|
||||
# cpus
|
||||
cpus = {}
|
||||
for c in flavor_query['cpus']:
|
||||
|
@ -190,7 +185,7 @@ class Connection(api.Connection):
|
|||
|
||||
def flavor_get(self, context, flavor_uuid):
|
||||
query = model_query(context, models.Flavors).filter_by(
|
||||
uuid=flavor_uuid).options(joinedload('extra_specs'))
|
||||
uuid=flavor_uuid)
|
||||
|
||||
if not context.is_admin:
|
||||
the_filter = [models.Flavors.is_public == true()]
|
||||
|
@ -231,13 +226,7 @@ class Connection(api.Connection):
|
|||
|
||||
def flavor_destroy(self, context, flavor_uuid):
|
||||
with _session_for_write():
|
||||
# First clean up all extra specs related to this type
|
||||
type_id = _type_get_id_from_type(context, flavor_uuid)
|
||||
extra_query = model_query(
|
||||
context,
|
||||
models.FlavorExtraSpecs).filter_by(
|
||||
flavor_uuid=type_id)
|
||||
extra_query.delete()
|
||||
|
||||
# Clean up cpus related to this flavor
|
||||
cpus_query = model_query(
|
||||
|
@ -525,61 +514,6 @@ class Connection(api.Connection):
|
|||
ref.update(values)
|
||||
return ref
|
||||
|
||||
def extra_specs_update_or_create(self, context,
|
||||
flavor_uuid, specs,
|
||||
max_retries=10):
|
||||
"""Create or update server type extra specs.
|
||||
|
||||
This adds or modifies the key/value pairs specified in the
|
||||
extra specs dict argument
|
||||
"""
|
||||
for attempt in range(max_retries):
|
||||
with _session_for_write() as session:
|
||||
try:
|
||||
spec_refs = model_query(
|
||||
context, models.FlavorExtraSpecs). \
|
||||
filter_by(flavor_uuid=flavor_uuid). \
|
||||
filter(models.FlavorExtraSpecs.key.in_(
|
||||
specs.keys())).with_lockmode('update').all()
|
||||
|
||||
existing_keys = set()
|
||||
for spec_ref in spec_refs:
|
||||
key = spec_ref["key"]
|
||||
existing_keys.add(key)
|
||||
spec_ref.update({"value": specs[key]})
|
||||
|
||||
for key, value in specs.items():
|
||||
if key in existing_keys:
|
||||
continue
|
||||
spec_ref = models.FlavorExtraSpecs()
|
||||
spec_ref.update(
|
||||
{"key": key, "value": value,
|
||||
"flavor_uuid": flavor_uuid})
|
||||
|
||||
session.add(spec_ref)
|
||||
session.flush()
|
||||
|
||||
return specs
|
||||
except db_exc.DBDuplicateEntry:
|
||||
# a concurrent transaction has been committed,
|
||||
# try again unless this was the last attempt
|
||||
if attempt == max_retries - 1:
|
||||
raise exception.FlavorExtraSpecUpdateCreateFailed(
|
||||
id=flavor_uuid, retries=max_retries)
|
||||
|
||||
def flavor_extra_specs_get(self, context, type_id):
|
||||
rows = _type_extra_specs_get_query(context, type_id).all()
|
||||
return {row['key']: row['value'] for row in rows}
|
||||
|
||||
def type_extra_specs_delete(self, context, type_id, key):
|
||||
result = _type_extra_specs_get_query(context, type_id). \
|
||||
filter(models.FlavorExtraSpecs.key == key). \
|
||||
delete(synchronize_session=False)
|
||||
# did not find the extra spec
|
||||
if result == 0:
|
||||
raise exception.FlavorExtraSpecsNotFound(
|
||||
extra_specs_key=key, flavor_id=type_id)
|
||||
|
||||
def flavor_access_get(self, context, flavor_id):
|
||||
return _flavor_access_query(context, flavor_id)
|
||||
|
||||
|
@ -1023,11 +957,6 @@ def _type_get_id_from_type(context, type_id):
|
|||
return result.uuid
|
||||
|
||||
|
||||
def _type_extra_specs_get_query(context, type_id):
|
||||
return model_query(context, models.FlavorExtraSpecs). \
|
||||
filter_by(flavor_uuid=type_id)
|
||||
|
||||
|
||||
def _flavor_access_query(context, flavor_id):
|
||||
return model_query(context, models.FlavorProjects). \
|
||||
filter_by(flavor_uuid=flavor_id)
|
||||
|
|
|
@ -178,6 +178,7 @@ class Flavors(Base):
|
|||
uuid = Column(String(36), primary_key=True)
|
||||
name = Column(String(255), nullable=False)
|
||||
description = Column(String(255), nullable=True)
|
||||
extra_specs = Column(db_types.JsonEncodedDict)
|
||||
is_public = Column(Boolean, default=True)
|
||||
servers = orm.relationship(
|
||||
Server,
|
||||
|
@ -208,29 +209,6 @@ class FlavorProjects(Base):
|
|||
' == Flavors.uuid')
|
||||
|
||||
|
||||
class FlavorExtraSpecs(Base):
|
||||
"""Represents additional specs as key/value pairs for an flavor."""
|
||||
__tablename__ = 'flavor_extra_specs'
|
||||
__table_args__ = (
|
||||
schema.UniqueConstraint(
|
||||
"flavor_uuid", "key",
|
||||
name=("uniq_flavor_extra_specs0"
|
||||
"flavor_uuid")
|
||||
),
|
||||
{'mysql_collate': 'utf8_bin'},
|
||||
)
|
||||
id = Column(Integer, primary_key=True)
|
||||
key = Column(String(255))
|
||||
value = Column(String(255))
|
||||
flavor_uuid = Column(String(36), ForeignKey('flavors.uuid'),
|
||||
nullable=False)
|
||||
flavor = orm.relationship(
|
||||
Flavors, backref="extra_specs",
|
||||
foreign_keys=flavor_uuid,
|
||||
primaryjoin='FlavorExtraSpecs.flavor_uuid '
|
||||
'== Flavors.uuid')
|
||||
|
||||
|
||||
class FlavorCpus(Base):
|
||||
"""Represents the flavor cpus."""
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ from mogan.objects import base
|
|||
from mogan.objects import fields as object_fields
|
||||
|
||||
|
||||
OPTIONAL_FIELDS = ['extra_specs', 'projects']
|
||||
OPTIONAL_FIELDS = ['projects']
|
||||
|
||||
|
||||
@base.MoganObjectRegistry.register
|
||||
|
@ -40,13 +40,12 @@ class Flavor(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||
'nics': object_fields.ListOfDictOfNullableStringsField(),
|
||||
'disks': object_fields.ListOfDictOfNullableStringsField(),
|
||||
'is_public': object_fields.BooleanField(),
|
||||
'extra_specs': object_fields.FlexibleDictField(),
|
||||
'extra_specs': object_fields.FlexibleDictField(nullable=True),
|
||||
'projects': object_fields.ListOfStringsField(),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Flavor, self).__init__(*args, **kwargs)
|
||||
self._orig_extra_specs = {}
|
||||
self._orig_projects = {}
|
||||
|
||||
@staticmethod
|
||||
|
@ -62,9 +61,6 @@ class Flavor(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||
value = value if value is not None else 0
|
||||
flavor[name] = value
|
||||
|
||||
if 'extra_specs' in expected_attrs:
|
||||
flavor.extra_specs = db_flavor['extra_specs']
|
||||
|
||||
if 'projects' in expected_attrs:
|
||||
flavor._load_projects(context)
|
||||
|
||||
|
@ -79,10 +75,6 @@ class Flavor(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||
def obj_reset_changes(self, fields=None, recursive=False):
|
||||
super(Flavor, self).obj_reset_changes(fields=fields,
|
||||
recursive=recursive)
|
||||
if fields is None or 'extra_specs' in fields:
|
||||
self._orig_extra_specs = (dict(self.extra_specs)
|
||||
if self.obj_attr_is_set('extra_specs')
|
||||
else {})
|
||||
if fields is None or 'projects' in fields:
|
||||
self._orig_projects = (list(self.projects)
|
||||
if self.obj_attr_is_set('projects')
|
||||
|
@ -90,9 +82,6 @@ class Flavor(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||
|
||||
def obj_what_changed(self):
|
||||
changes = super(Flavor, self).obj_what_changed()
|
||||
if ('extra_specs' in self and
|
||||
self.extra_specs != self._orig_extra_specs):
|
||||
changes.add('extra_specs')
|
||||
if 'projects' in self and self.projects != self._orig_projects:
|
||||
changes.add('projects')
|
||||
return changes
|
||||
|
@ -100,8 +89,7 @@ class Flavor(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||
@staticmethod
|
||||
def _from_db_object_list(db_objects, cls, context):
|
||||
"""Converts a list of database entities to a list of formal objects."""
|
||||
return [Flavor._from_db_object(context, cls(context), obj,
|
||||
expected_attrs=['extra_specs'])
|
||||
return [Flavor._from_db_object(context, cls(context), obj)
|
||||
for obj in db_objects]
|
||||
|
||||
@classmethod
|
||||
|
@ -116,15 +104,14 @@ class Flavor(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||
db_flavor = cls.dbapi.flavor_get(context, flavor_uuid)
|
||||
flavor = Flavor._from_db_object(
|
||||
context, cls(context), db_flavor,
|
||||
expected_attrs=['extra_specs', 'projects'])
|
||||
expected_attrs=['projects'])
|
||||
return flavor
|
||||
|
||||
def create(self, context=None):
|
||||
"""Create a Flavor record in the DB."""
|
||||
values = self.obj_get_changes()
|
||||
db_flavor = self.dbapi.flavor_create(context, values)
|
||||
self._from_db_object(context, self, db_flavor,
|
||||
expected_attrs=['extra_specs'])
|
||||
self._from_db_object(context, self, db_flavor)
|
||||
|
||||
def destroy(self, context=None):
|
||||
"""Delete the Flavor from the DB."""
|
||||
|
@ -134,23 +121,11 @@ class Flavor(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||
def save(self, context=None):
|
||||
updates = self.obj_get_changes()
|
||||
projects = updates.pop('projects', None)
|
||||
extra_specs = updates.pop('extra_specs', None)
|
||||
updates.pop('cpus', None)
|
||||
updates.pop('memory', None)
|
||||
updates.pop('nics', None)
|
||||
updates.pop('disks', None)
|
||||
|
||||
# extra specs
|
||||
if extra_specs is not None:
|
||||
deleted_keys = (set(self._orig_extra_specs.keys()) -
|
||||
set(extra_specs.keys()))
|
||||
added_keys = self.extra_specs
|
||||
else:
|
||||
added_keys = deleted_keys = None
|
||||
|
||||
if added_keys or deleted_keys:
|
||||
self.save_extra_specs(context, self.extra_specs, deleted_keys)
|
||||
|
||||
# access projects
|
||||
if projects is not None:
|
||||
deleted_projects = set(self._orig_projects) - set(projects)
|
||||
|
@ -163,24 +138,6 @@ class Flavor(base.MoganObject, object_base.VersionedObjectDictCompat):
|
|||
|
||||
self.dbapi.flavor_update(context, self.uuid, updates)
|
||||
|
||||
def save_extra_specs(self, context, to_add=None, to_delete=None):
|
||||
"""Add or delete extra_specs.
|
||||
|
||||
:param:to_add: A dict of new keys to add/update
|
||||
:param:to_delete: A list of keys to remove
|
||||
"""
|
||||
ident = self.uuid
|
||||
|
||||
to_add = to_add if to_add is not None else {}
|
||||
to_delete = to_delete if to_delete is not None else []
|
||||
|
||||
if to_add:
|
||||
self.dbapi.extra_specs_update_or_create(context, ident, to_add)
|
||||
|
||||
for key in to_delete:
|
||||
self.dbapi.type_extra_specs_delete(context, ident, key)
|
||||
self.obj_reset_changes(['extra_specs'])
|
||||
|
||||
def save_projects(self, context, to_add=None, to_delete=None):
|
||||
"""Add or delete projects.
|
||||
|
||||
|
|
|
@ -1,67 +0,0 @@
|
|||
# Copyright 2016 Huawei Technologies Co.,LTD.
|
||||
# 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.
|
||||
|
||||
"""Tests for manipulating Flavor Extra Specs via the DB API"""
|
||||
|
||||
from mogan.common import exception
|
||||
from mogan.tests.unit.db import base
|
||||
from mogan.tests.unit.db import utils
|
||||
|
||||
|
||||
class DbFlavorExtraSpecsTestCase(base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(DbFlavorExtraSpecsTestCase, self).setUp()
|
||||
self.context = {}
|
||||
self.flavor = utils.create_test_flavor()
|
||||
self.specs = {'k1': 'v1', 'k2': 'v2'}
|
||||
|
||||
def test_create_extra_specs(self):
|
||||
self.dbapi.extra_specs_update_or_create(
|
||||
self.context, self.flavor['uuid'], self.specs)
|
||||
|
||||
def test_get_extra_specs(self):
|
||||
self.dbapi.extra_specs_update_or_create(
|
||||
self.context, self.flavor['uuid'], self.specs)
|
||||
extra_specs = self.dbapi.flavor_extra_specs_get(
|
||||
self.context, self.flavor['uuid'])
|
||||
|
||||
self.assertEqual(self.specs, extra_specs)
|
||||
|
||||
def test_get_extra_specs_empty(self):
|
||||
extra_specs = self.dbapi.flavor_extra_specs_get(
|
||||
self.context, self.flavor['uuid'])
|
||||
|
||||
self.assertEqual({}, extra_specs)
|
||||
|
||||
def test_destroy_extra_specs(self):
|
||||
self.dbapi.extra_specs_update_or_create(
|
||||
self.context, self.flavor['uuid'], self.specs)
|
||||
|
||||
self.dbapi.type_extra_specs_delete(
|
||||
self.context, self.flavor['uuid'], 'k1')
|
||||
extra_specs = self.dbapi.flavor_extra_specs_get(
|
||||
self.context, self.flavor['uuid'])
|
||||
|
||||
self.assertEqual({'k2': 'v2'}, extra_specs)
|
||||
|
||||
def test_delete_extra_specs_does_not_exist(self):
|
||||
self.dbapi.extra_specs_update_or_create(
|
||||
self.context, self.flavor['uuid'], self.specs)
|
||||
self.assertRaises(exception.FlavorExtraSpecsNotFound,
|
||||
self.dbapi.type_extra_specs_delete,
|
||||
self.context,
|
||||
self.flavor['uuid'],
|
||||
'k3')
|
|
@ -76,7 +76,6 @@ class TestFlavorObject(base.DbTestCase):
|
|||
flavor.name = 'changed_name'
|
||||
updates = flavor.obj_get_changes()
|
||||
flavor.save(self.context)
|
||||
updates.pop('extra_specs', None)
|
||||
updates.pop('cpus', None)
|
||||
updates.pop('memory', None)
|
||||
updates.pop('disks', None)
|
||||
|
|
|
@ -391,7 +391,7 @@ expected_object_fingerprints = {
|
|||
'ComputeDiskList': '1.0-33a2e1bb91ad4082f9f63429b77c1244',
|
||||
'ServerFault': '1.0-74349ff701259e4834b4e9dc2dac1b12',
|
||||
'ServerFaultList': '1.0-43e8aad0258652921f929934e9e048fd',
|
||||
'Flavor': '1.0-7cc125bfe2dda6e5ffa07651d58047cd',
|
||||
'Flavor': '1.0-ec52868368ce4bbf77b0532897067c4d',
|
||||
'MyObj': '1.1-aad62eedc5a5cc8bcaf2982c285e753f',
|
||||
'ServerNic': '1.0-ebbd767c2f6a7f14bd524c6067f2b382',
|
||||
'ServerNics': '1.0-33a2e1bb91ad4082f9f63429b77c1244',
|
||||
|
|
Loading…
Reference in New Issue