summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvladimir.p <vladimir@zadarastorage.com>2011-08-23 15:18:50 -0700
committervladimir.p <vladimir@zadarastorage.com>2011-08-23 15:18:50 -0700
commitddc7d9470674a4d7300d15e5c6fa54b784b6a36f (patch)
tree99d5ea16405985b5ed31f31d11b8663b06d89494
parent1d121a42f5072026a3ad19cb5fd1915d7cd2ff63 (diff)
added volume_types APIs
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/037_add_volume_types_and_extradata.py4
-rw-r--r--nova/tests/test_volume_types.py156
-rw-r--r--nova/volume/api.py9
-rw-r--r--nova/volume/volume_types.py14
4 files changed, 170 insertions, 13 deletions
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
index fc365d2b2..27c8afcee 100644
--- 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
@@ -2,7 +2,6 @@
# 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
@@ -45,7 +44,8 @@ volume_types = Table('volume_types', meta,
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)))
+ unicode_error=None, _warn_on_bytestring=False),
+ unique=True))
volume_type_extra_specs_table = Table('volume_type_extra_specs', meta,
Column('created_at', DateTime(timezone=False)),
diff --git a/nova/tests/test_volume_types.py b/nova/tests/test_volume_types.py
new file mode 100644
index 000000000..3d906a5fd
--- /dev/null
+++ b/nova/tests/test_volume_types.py
@@ -0,0 +1,156 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2011 Zadara Storage Inc.
+# Copyright (c) 2011 OpenStack LLC.
+# 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.
+"""
+Unit Tests for volume types code
+"""
+import time
+
+from nova import context
+from nova import db
+from nova import exception
+from nova import flags
+from nova import log as logging
+from nova import test
+from nova import utils
+from nova.volume import volume_types
+from nova.db.sqlalchemy.session import get_session
+from nova.db.sqlalchemy import models
+
+FLAGS = flags.FLAGS
+LOG = logging.getLogger('nova.tests.test_volume_types')
+
+
+class VolumeTypeTestCase(test.TestCase):
+ """Test cases for volume type code"""
+ def setUp(self):
+ super(VolumeTypeTestCase, self).setUp()
+
+ self.ctxt = context.get_admin_context()
+ self.vol_type1_name = str(int(time.time()))
+ self.vol_type1_specs = dict(
+ type="physical drive",
+ drive_type="SAS",
+ size="300",
+ rpm="7200",
+ visible="True")
+ self.vol_type1 = dict(name=self.vol_type1_name,
+ extra_specs=self.vol_type1_specs)
+
+ def test_volume_type_create_then_destroy(self):
+ """Ensure volume types can be created and deleted"""
+ prev_all_vtypes = volume_types.get_all_types(self.ctxt)
+
+ volume_types.create(self.ctxt,
+ self.vol_type1_name,
+ self.vol_type1_specs)
+ new = volume_types.get_volume_type_by_name(self.ctxt,
+ self.vol_type1_name)
+
+ LOG.info(_("Given data: %s"), self.vol_type1_specs)
+ LOG.info(_("Result data: %s"), new)
+
+ for k, v in self.vol_type1_specs.iteritems():
+ self.assertEqual(v, new['extra_specs'][k],
+ 'one of fields doesnt match')
+
+ new_all_vtypes = volume_types.get_all_types(self.ctxt)
+ self.assertEqual(len(prev_all_vtypes) + 1,
+ len(new_all_vtypes),
+ 'drive type was not created')
+
+ volume_types.destroy(self.ctxt, self.vol_type1_name)
+ new_all_vtypes = volume_types.get_all_types(self.ctxt)
+ self.assertEqual(prev_all_vtypes,
+ new_all_vtypes,
+ 'drive type was not deleted')
+
+ def test_volume_type_create_then_purge(self):
+ """Ensure volume types can be created and deleted"""
+ prev_all_vtypes = volume_types.get_all_types(self.ctxt, inactive=1)
+
+ volume_types.create(self.ctxt,
+ self.vol_type1_name,
+ self.vol_type1_specs)
+ new = volume_types.get_volume_type_by_name(self.ctxt,
+ self.vol_type1_name)
+
+ for k, v in self.vol_type1_specs.iteritems():
+ self.assertEqual(v, new['extra_specs'][k],
+ 'one of fields doesnt match')
+
+ new_all_vtypes = volume_types.get_all_types(self.ctxt, inactive=1)
+ self.assertEqual(len(prev_all_vtypes) + 1,
+ len(new_all_vtypes),
+ 'drive type was not created')
+
+ volume_types.destroy(self.ctxt, self.vol_type1_name)
+ new_all_vtypes2 = volume_types.get_all_types(self.ctxt, inactive=1)
+ self.assertEqual(len(new_all_vtypes),
+ len(new_all_vtypes2),
+ 'drive type was incorrectly deleted')
+
+ volume_types.purge(self.ctxt, self.vol_type1_name)
+ new_all_vtypes2 = volume_types.get_all_types(self.ctxt, inactive=1)
+ self.assertEqual(len(new_all_vtypes) - 1,
+ len(new_all_vtypes2),
+ 'drive type was not purged')
+
+ def test_get_all_volume_types(self):
+ """Ensures that all volume types can be retrieved"""
+ session = get_session()
+ total_volume_types = session.query(models.VolumeTypes).\
+ count()
+ vol_types = volume_types.get_all_types(self.ctxt)
+ self.assertEqual(total_volume_types, len(vol_types))
+
+ def test_non_existant_inst_type_shouldnt_delete(self):
+ """Ensures that volume type creation fails with invalid args"""
+ self.assertRaises(exception.ApiError,
+ volume_types.destroy, self.ctxt, "sfsfsdfdfs")
+
+ def test_repeated_vol_types_should_raise_api_error(self):
+ """Ensures that volume duplicates raises ApiError"""
+ new_name = self.vol_type1_name + "dup"
+ volume_types.create(self.ctxt, new_name)
+ volume_types.destroy(self.ctxt, new_name)
+ self.assertRaises(
+ exception.ApiError,
+ volume_types.create, self.ctxt, new_name)
+
+ def test_invalid_volume_types_params(self):
+ """Ensures that volume type creation fails with invalid args"""
+ self.assertRaises(exception.InvalidVolumeType,
+ volume_types.destroy, self.ctxt, None)
+ self.assertRaises(exception.InvalidVolumeType,
+ volume_types.purge, self.ctxt, None)
+ self.assertRaises(exception.InvalidVolumeType,
+ volume_types.get_volume_type, self.ctxt, None)
+ self.assertRaises(exception.InvalidVolumeType,
+ volume_types.get_volume_type_by_name,
+ self.ctxt, None)
+
+ def test_volume_type_get_by_id_and_name(self):
+ """Ensure volume types get returns same entry"""
+ volume_types.create(self.ctxt,
+ self.vol_type1_name,
+ self.vol_type1_specs)
+ new = volume_types.get_volume_type_by_name(self.ctxt,
+ self.vol_type1_name)
+
+ new2 = volume_types.get_volume_type(self.ctxt, new['id'])
+ self.assertEqual(new, new2)
+
+
diff --git a/nova/volume/api.py b/nova/volume/api.py
index 7a78e244f..80e8bd85f 100644
--- a/nova/volume/api.py
+++ b/nova/volume/api.py
@@ -42,7 +42,7 @@ class API(base.Base):
"""API for interacting with the volume manager."""
def create(self, context, size, snapshot_id, name, description,
- volume_type=None, metadata=None):
+ volume_type=None, metadata=None, availability_zone=None):
if snapshot_id != None:
snapshot = self.get_snapshot(context, snapshot_id)
if snapshot['status'] != "available":
@@ -58,18 +58,21 @@ class API(base.Base):
raise quota.QuotaError(_("Volume quota exceeded. You cannot "
"create a volume of size %sG") % size)
+ if availability_zone is None:
+ availability_zone = FLAGS.storage_availability_zone
+
options = {
'size': size,
'user_id': context.user_id,
'project_id': context.project_id,
'snapshot_id': snapshot_id,
- 'availability_zone': FLAGS.storage_availability_zone,
+ 'availability_zone': availability_zone,
'status': "creating",
'attach_status': "detached",
'display_name': name,
'display_description': description,
'volume_type_id': volume_type.get('id', None),
- 'metadata' metadata,
+ 'metadata': metadata,
}
volume = self.db.volume_create(context, options)
diff --git a/nova/volume/volume_types.py b/nova/volume/volume_types.py
index c1fce1627..9df1e39f8 100644
--- a/nova/volume/volume_types.py
+++ b/nova/volume/volume_types.py
@@ -4,7 +4,6 @@
# Copyright (c) 2011 OpenStack LLC.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
-# All Rights Reserved.
# Copyright (c) 2010 Citrix Systems, Inc.
# Copyright 2011 Ken Pepple
#
@@ -29,16 +28,15 @@ from nova import flags
from nova import log as logging
FLAGS = flags.FLAGS
-LOG = logging.getLogger('nova.volume_types')
+LOG = logging.getLogger('nova.volume.volume_types')
def create(context, name, extra_specs={}):
"""Creates volume types."""
try:
- db.volume_type_create(
- context,
- dict(name=name,
- extra_specs=extra_specs))
+ db.volume_type_create(context,
+ dict(name=name,
+ extra_specs=extra_specs))
except exception.DBError, e:
LOG.exception(_('DB error: %s') % e)
raise exception.ApiError(_("Cannot create volume_type with "
@@ -82,7 +80,7 @@ def get_all_types(context, inactive=0):
def get_volume_type(context, id):
"""Retrieves single volume type by id."""
if id is None:
- raise exception.ApiError(_("Invalid volume type: %s") % id)
+ raise exception.InvalidVolumeType(volume_type=id)
try:
return db.volume_type_get(context, id)
@@ -93,7 +91,7 @@ def get_volume_type(context, id):
def get_volume_type_by_name(context, name):
"""Retrieves single volume type by name."""
if name is None:
- raise exception.ApiError(_("Invalid volume type name: %s") % name)
+ raise exception.InvalidVolumeType(volume_type=name)
try:
return db.volume_type_get_by_name(context, name)