summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvladimir.p <vladimir@zadarastorage.com>2011-08-18 13:24:56 -0700
committervladimir.p <vladimir@zadarastorage.com>2011-08-18 13:24:56 -0700
commit50b7db2ab71c40732a979b1f424bd60627a74768 (patch)
treefb80204fc505196227fe4607896d89abb56c9787
parentabf7e2f767e1e535f40550945af466436d0cf541 (diff)
first cut on types & extra-data (only DB work, no tests)
-rw-r--r--nova/db/api.py60
-rw-r--r--nova/db/sqlalchemy/api.py180
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py103
-rw-r--r--nova/db/sqlalchemy/models.py30
-rw-r--r--nova/exception.py18
5 files changed, 390 insertions, 1 deletions
diff --git a/nova/db/api.py b/nova/db/api.py
index b9ea8757c..47e73226a 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -1424,3 +1424,63 @@ def instance_type_extra_specs_update_or_create(context, instance_type_id,
key/value pairs specified in the extra specs dict argument"""
IMPL.instance_type_extra_specs_update_or_create(context, instance_type_id,
extra_specs)
+
+
+##################
+
+
+def volume_type_create(context, values):
+ """Create a new volume type."""
+ return IMPL.volume_type_create(context, values)
+
+
+def volume_type_get_all(context, inactive=False):
+ """Get all volume types."""
+ return IMPL.volume_type_get_all(context, inactive)
+
+
+def volume_type_get(context, id):
+ """Get volume type by id."""
+ return IMPL.volume_type_get(context, id)
+
+
+def volume_type_get_by_name(context, name):
+ """Get volume type by name."""
+ return IMPL.volume_type_get_by_name(context, name)
+
+
+def volume_type_destroy(context, name):
+ """Delete a volume type."""
+ return IMPL.volume_type_destroy(context, name)
+
+
+def volume_type_purge(context, name):
+ """Purges (removes) a volume type from DB.
+
+ Use volume_type_destroy for most cases
+
+ """
+ return IMPL.volume_type_purge(context, name)
+
+
+####################
+
+
+def volume_type_extra_specs_get(context, volume_type_id):
+ """Get all extra specs for a volume type."""
+ return IMPL.volume_type_extra_specs_get(context, volume_type_id)
+
+
+def volume_type_extra_specs_delete(context, volume_type_id, key):
+ """Delete the given extra specs item."""
+ IMPL.volume_type_extra_specs_delete(context, volume_type_id, key)
+
+
+def volume_type_extra_specs_update_or_create(context, volume_type_id,
+ extra_specs):
+ """Create or update volume type extra specs. This adds or modifies the
+ key/value pairs specified in the extra specs dict argument"""
+ IMPL.volume_type_extra_specs_update_or_create(context, volume_type_id,
+ extra_specs)
+
+
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 95ec3f715..ce1066e42 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -3080,7 +3080,7 @@ def instance_type_create(_context, values):
def _dict_with_extra_specs(inst_type_query):
- """Takes an instance type query returned by sqlalchemy
+ """Takes an instance OR volume type query returned by sqlalchemy
and returns it as a dictionary, converting the extra_specs
entry from a list of dicts:
@@ -3462,3 +3462,181 @@ def instance_type_extra_specs_update_or_create(context, instance_type_id,
"deleted": 0})
spec_ref.save(session=session)
return specs
+
+
+##################
+
+
+@require_admin_context
+def volume_type_create(_context, values):
+ """Create a new instance type. In order to pass in extra specs,
+ the values dict should contain a 'extra_specs' key/value pair:
+
+ {'extra_specs' : {'k1': 'v1', 'k2': 'v2', ...}}
+
+ """
+ try:
+ specs = values.get('extra_specs')
+ specs_refs = []
+ if specs:
+ for k, v in specs.iteritems():
+ specs_ref = models.VolumeTypeExtraSpecs()
+ specs_ref['key'] = k
+ specs_ref['value'] = v
+ specs_refs.append(specs_ref)
+ values['extra_specs'] = specs_refs
+ volume_type_ref = models.VolumeTypes()
+ volume_type_ref.update(values)
+ volume_type_ref.save()
+ except Exception, e:
+ raise exception.DBError(e)
+ return volume_type_ref
+
+
+@require_context
+def volume_type_get_all(context, inactive=False):
+ """
+ Returns a dict describing all volume_types with name as key.
+ """
+ session = get_session()
+ if inactive:
+ inst_types = session.query(models.VolumeTypes).\
+ options(joinedload('extra_specs')).\
+ order_by("name").\
+ all()
+ else:
+ inst_types = session.query(models.VolumeTypes).\
+ options(joinedload('extra_specs')).\
+ filter_by(deleted=False).\
+ order_by("name").\
+ all()
+ inst_dict = {}
+ if inst_types:
+ for i in inst_types:
+ inst_dict[i['name']] = _dict_with_extra_specs(i)
+ return inst_dict
+
+
+@require_context
+def volume_type_get(context, id):
+ """Returns a dict describing specific volume_type"""
+ session = get_session()
+ inst_type = session.query(models.VolumeTypes).\
+ options(joinedload('extra_specs')).\
+ filter_by(id=id).\
+ first()
+
+ if not inst_type:
+ raise exception.VolumeTypeNotFound(volume_type=id)
+ else:
+ return _dict_with_extra_specs(inst_type)
+
+
+@require_context
+def volume_type_get_by_name(context, name):
+ """Returns a dict describing specific volume_type"""
+ session = get_session()
+ inst_type = session.query(models.VolumeTypes).\
+ options(joinedload('extra_specs')).\
+ filter_by(name=name).\
+ first()
+ if not inst_type:
+ raise exception.VolumeTypeNotFoundByName(volume_type_name=name)
+ else:
+ return _dict_with_extra_specs(inst_type)
+
+
+@require_admin_context
+def volume_type_destroy(context, name):
+ """ Marks specific volume_type as deleted"""
+ session = get_session()
+ volume_type_ref = session.query(models.VolumeTypes).\
+ filter_by(name=name)
+ records = volume_type_ref.update(dict(deleted=True))
+ if records == 0:
+ raise exception.VolumeTypeNotFoundByName(volume_type_name=name)
+ else:
+ return volume_type_ref
+
+
+@require_admin_context
+def volume_type_purge(context, name):
+ """ Removes specific volume_type from DB
+ Usually volume_type_destroy should be used
+ """
+ session = get_session()
+ volume_type_ref = session.query(models.VolumeTypes).\
+ filter_by(name=name)
+ records = volume_type_ref.delete()
+ if records == 0:
+ raise exception.VolumeTypeNotFoundByName(volume_type_name=name)
+ else:
+ return volume_type_ref
+
+
+####################
+
+
+@require_context
+def volume_type_extra_specs_get(context, volume_type_id):
+ session = get_session()
+
+ spec_results = session.query(models.VolumeTypeExtraSpecs).\
+ filter_by(volume_type_id=volume_type_id).\
+ filter_by(deleted=False).\
+ all()
+
+ spec_dict = {}
+ for i in spec_results:
+ spec_dict[i['key']] = i['value']
+ return spec_dict
+
+
+@require_context
+def volume_type_extra_specs_delete(context, volume_type_id, key):
+ session = get_session()
+ session.query(models.VolumeTypeExtraSpecs).\
+ filter_by(volume_type_id=volume_type_id).\
+ filter_by(key=key).\
+ filter_by(deleted=False).\
+ update({'deleted': True,
+ 'deleted_at': utils.utcnow(),
+ 'updated_at': literal_column('updated_at')})
+
+
+@require_context
+def volume_type_extra_specs_get_item(context, volume_type_id, key,
+ session=None):
+
+ if not session:
+ session = get_session()
+
+ spec_result = session.query(models.VolumeTypeExtraSpecs).\
+ filter_by(volume_type_id=volume_type_id).\
+ filter_by(key=key).\
+ filter_by(deleted=False).\
+ first()
+
+ if not spec_result:
+ raise exception.\
+ VolumeTypeExtraSpecsNotFound(extra_specs_key=key,
+ volume_type_id=volume_type_id)
+ return spec_result
+
+
+@require_context
+def volume_type_extra_specs_update_or_create(context, volume_type_id,
+ specs):
+ session = get_session()
+ spec_ref = None
+ for key, value in specs.iteritems():
+ try:
+ spec_ref = volume_type_extra_specs_get_item(
+ context, volume_type_id, key, session)
+ except exception.VolumeTypeExtraSpecsNotFound, e:
+ spec_ref = models.VolumeTypeExtraSpecs()
+ spec_ref.update({"key": key, "value": value,
+ "volume_type_id": volume_type_id,
+ "deleted": 0})
+ spec_ref.save(session=session)
+ return specs
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py b/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py
new file mode 100644
index 000000000..1bfa26845
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py
@@ -0,0 +1,103 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 Zadara Storage Inc.
+# Copyright (c) 2011 OpenStack LLC.
+# 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 sqlalchemy import Column, DateTime, Integer, MetaData, String, Table
+from sqlalchemy import Text, Boolean, ForeignKey
+
+from nova import log as logging
+
+meta = MetaData()
+
+# Just for the ForeignKey and column creation to succeed, these are not the
+# actual definitions of tables .
+#
+
+volumes = Table('volumes', meta,
+ Column('id', Integer(), primary_key=True, nullable=False),
+ )
+
+volume_type_id = Column('volume_type_id', Integer(), nullable=True)
+
+
+# New Tables
+#
+
+volume_types = Table('volume_types', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(), primary_key=True, nullable=False),
+ Column('name',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)))
+
+volume_type_extra_specs_table = Table('volume_type_extra_specs', meta,
+ Column('created_at', DateTime(timezone=False)),
+ Column('updated_at', DateTime(timezone=False)),
+ Column('deleted_at', DateTime(timezone=False)),
+ Column('deleted', Boolean(create_constraint=True, name=None)),
+ Column('id', Integer(), primary_key=True, nullable=False),
+ Column('volume_type_id',
+ Integer(),
+ ForeignKey('volume_types.id'),
+ nullable=False),
+ Column('key',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)),
+ Column('value',
+ String(length=255, convert_unicode=False, assert_unicode=None,
+ unicode_error=None, _warn_on_bytestring=False)))
+
+
+new_tables = (volume_types, volume_type_extra_specs_table)
+
+#
+# Tables to alter
+#
+
+
+def upgrade(migrate_engine):
+
+ from nova import context
+ from nova import db
+ from nova import flags
+
+ FLAGS = flags.FLAGS
+
+ # Upgrade operations go here. Don't create your own engine;
+ # bind migrate_engine to your metadata
+ meta.bind = migrate_engine
+
+ for table in new_tables:
+ try:
+ table.create()
+ except Exception:
+ logging.info(repr(table))
+ logging.exception('Exception while creating table')
+ raise
+
+ volumes.create_column(volume_type_id)
+
+def downgrade(migrate_engine):
+ meta.bind = migrate_engine
+
+ volumes.drop_column(volume_type_id)
+
+ for table in new_tables:
+ table.drop()
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index f2a4680b0..70834ddb5 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -312,6 +312,36 @@ class Volume(BASE, NovaBase):
provider_location = Column(String(255))
provider_auth = Column(String(255))
+ volume_type_id = Column(Integer)
+
+
+class VolumeTypes(BASE, NovaBase):
+ """Represent possible volume_types of volumes offered"""
+ __tablename__ = "volume_types"
+ id = Column(Integer, primary_key=True)
+ name = Column(String(255), unique=True)
+
+ volumes = relationship(Volume,
+ backref=backref('volume_type', uselist=False),
+ foreign_keys=id,
+ primaryjoin='and_(Volume.volume_type_id == '
+ 'VolumeTypes.id)')
+
+
+class VolumeTypeExtraSpecs(BASE, NovaBase):
+ """Represents additional specs as key/value pairs for a volume_type"""
+ __tablename__ = 'volume_type_extra_specs'
+ id = Column(Integer, primary_key=True)
+ key = Column(String(255))
+ value = Column(String(255))
+ volume_type_id = Column(Integer, ForeignKey('volume_types.id'),
+ nullable=False)
+ volume_type = relationship(VolumeTypes, backref="extra_specs",
+ foreign_keys=volume_type_id,
+ primaryjoin='and_('
+ 'VolumeTypeExtraSpecs.instance_type_id == VolumeTypes.id,'
+ 'VolumeTypeExtraSpecs.deleted == False)')
+
class Quota(BASE, NovaBase):
"""Represents a single quota override for a project.
diff --git a/nova/exception.py b/nova/exception.py
index b09d50797..ff4b7c80e 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -338,6 +338,24 @@ class VolumeNotFoundForInstance(VolumeNotFound):
message = _("Volume not found for instance %(instance_id)s.")
+class NoVolumeTypesFound(NotFound):
+ message = _("Zero volume types found.")
+
+
+class VolumeTypeNotFound(NotFound):
+ message = _("Volume type %(volume_type_id)s could not be found.")
+
+
+class VolumeTypeNotFoundByName(VolumeTypeNotFound):
+ message = _("Volume type with name %(volume_type_name)s "
+ "could not be found.")
+
+
+class VolumeTypeExtraSpecsNotFound(NotFound):
+ message = _("Volume Type %(volume_type_id)s has no extra specs with "
+ "key %(extra_specs_key)s.")
+
+
class SnapshotNotFound(NotFound):
message = _("Snapshot %(snapshot_id)s could not be found.")