summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Griffith <john.griffith@solidfire.com>2012-05-04 11:31:56 -0600
committerJohn Griffith <john.griffith@solidfire.com>2012-05-10 13:36:32 -0600
commitdcad314fb9713104f0029311c43907e362ec6d49 (patch)
tree4e6fffab1e7f064e94cee264192d05269ad1c0d4
parentd9ed81222048f589b6863aaf2a99983ba5a3094f (diff)
downloadnova-dcad314fb9713104f0029311c43907e362ec6d49.tar.gz
nova-dcad314fb9713104f0029311c43907e362ec6d49.tar.xz
nova-dcad314fb9713104f0029311c43907e362ec6d49.zip
Remove instance Foreign Key in volumes table, replace with instance_uuid
* Remove the instance relationship and instance_id FK * Add instance_uuuid column to volumes table * Passed unit tests and devstack tests Change-Id: Id598f1f1d7915d1af6bf3dd75e5819dce08aaa0f
-rw-r--r--etc/nova/nova.conf.sample2
-rw-r--r--nova/api/ec2/ec2utils.py4
-rw-r--r--nova/api/openstack/compute/contrib/volumes.py7
-rw-r--r--nova/api/openstack/volume/volumes.py12
-rw-r--r--nova/compute/api.py5
-rw-r--r--nova/compute/manager.py9
-rw-r--r--nova/db/api.py9
-rw-r--r--nova/db/sqlalchemy/api.py32
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/095_change_fk_instance_id_to_uuid.py94
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/095_sqlite_downgrade.sql133
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/095_sqlite_upgrade.sql132
-rw-r--r--nova/db/sqlalchemy/models.py7
-rw-r--r--nova/exception.py5
-rw-r--r--nova/tests/api/ec2/test_cloud.py42
-rw-r--r--nova/tests/api/openstack/fakes.py6
-rw-r--r--nova/tests/api/openstack/volume/test_volumes.py5
-rw-r--r--nova/tests/scheduler/test_scheduler.py1
-rw-r--r--nova/tests/test_libvirt.py4
-rw-r--r--nova/tests/test_volume.py25
-rw-r--r--nova/volume/api.py4
-rw-r--r--nova/volume/manager.py12
21 files changed, 456 insertions, 94 deletions
diff --git a/etc/nova/nova.conf.sample b/etc/nova/nova.conf.sample
index 027472310..02ccf265d 100644
--- a/etc/nova/nova.conf.sample
+++ b/etc/nova/nova.conf.sample
@@ -311,7 +311,7 @@
###### (StrOpt) Template string to be used to generate snapshot names
# snapshot_name_template="snapshot-%08x"
###### (StrOpt) Template string to be used to generate instance names
-# volume_name_template="volume-%08x"
+# volume_name_template="volume-%s"
######### defined in nova.crypto #########
diff --git a/nova/api/ec2/ec2utils.py b/nova/api/ec2/ec2utils.py
index 9006da93e..ceb695a5e 100644
--- a/nova/api/ec2/ec2utils.py
+++ b/nova/api/ec2/ec2utils.py
@@ -197,6 +197,10 @@ def get_snapshot_uuid_from_int_id(context, int_id):
return db.get_snapshot_uuid_by_ec2_id(context, int_id)
+def ec2_instance_id_to_uuid(context, ec2_id):
+ int_id = ec2_id_to_id(ec2_id)
+ return db.instance_get(context, int_id)['uuid']
+
_c2u = re.compile('(((?<=[a-z])[A-Z])|([A-Z](?![A-Z]|$)))')
diff --git a/nova/api/openstack/compute/contrib/volumes.py b/nova/api/openstack/compute/contrib/volumes.py
index 129b181c7..004a667b1 100644
--- a/nova/api/openstack/compute/contrib/volumes.py
+++ b/nova/api/openstack/compute/contrib/volumes.py
@@ -371,10 +371,9 @@ class VolumeAttachmentController(object):
except exception.NotFound:
raise exc.HTTPNotFound()
- instance = vol['instance']
- if instance is None or str(instance['uuid']) != server_id:
- LOG.debug("Instance not found (server_id=%(server_id)s)",
- {'server_id': server_id}, instance=instance)
+ instance_uuid = vol['instance_uuid']
+ if instance_uuid is None or instance_uuid != server_id:
+ LOG.debug(_("Instance %s is not attached."), instance_uuid)
raise exc.HTTPNotFound()
self.compute_api.detach_volume(context,
diff --git a/nova/api/openstack/volume/volumes.py b/nova/api/openstack/volume/volumes.py
index 7789bc1aa..5da007d13 100644
--- a/nova/api/openstack/volume/volumes.py
+++ b/nova/api/openstack/volume/volumes.py
@@ -48,15 +48,13 @@ def _translate_attachment_summary_view(_context, vol):
"""Maps keys for attachment summary view."""
d = {}
- # TODO(bcwaldon): remove str cast once we use uuids
- volume_id = str(vol['id'])
+ volume_id = vol['id']
# NOTE(justinsb): We use the volume id as the id of the attachment object
d['id'] = volume_id
d['volume_id'] = volume_id
- if vol.get('instance'):
- d['server_id'] = vol['instance']['uuid']
+ d['server_id'] = vol['instance_uuid']
if vol.get('mountpoint'):
d['device'] = vol['mountpoint']
@@ -77,8 +75,7 @@ def _translate_volume_summary_view(context, vol):
"""Maps keys for volumes summary view."""
d = {}
- # TODO(bcwaldon): remove str cast once we use uuids
- d['id'] = str(vol['id'])
+ d['id'] = vol['id']
d['status'] = vol['status']
d['size'] = vol['size']
d['availability_zone'] = vol['availability_zone']
@@ -99,9 +96,6 @@ def _translate_volume_summary_view(context, vol):
d['volume_type'] = str(vol['volume_type_id'])
d['snapshot_id'] = vol['snapshot_id']
- # TODO(bcwaldon): remove str cast once we use uuids
- if d['snapshot_id'] is not None:
- d['snapshot_id'] = str(d['snapshot_id'])
LOG.audit(_("vol=%s"), vol, context=context)
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 32b7d79ce..5e1426fce 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -1654,7 +1654,10 @@ class API(BaseAPI):
# the volume ID via volume API and pass it and the volume object here
def detach_volume(self, context, volume_id):
"""Detach a volume from an instance."""
- instance = self.db.volume_get_instance(context.elevated(), volume_id)
+ volume = self.volume_api.get(context, volume_id)
+ instance_uuid = volume['instance_uuid']
+ instance = self.db.instance_get_by_uuid(context.elevated(),
+ instance_uuid)
if not instance:
raise exception.VolumeUnattached(volume_id=volume_id)
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index 382464e7d..d558144ed 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -67,7 +67,6 @@ from nova.openstack.common import importutils
from nova import rpc
from nova import utils
from nova.virt import driver
-from nova import vnc
from nova import volume
@@ -261,7 +260,6 @@ class ComputeManager(manager.SchedulerDependentManager):
context = nova.context.get_admin_context()
instances = self.db.instance_get_all_by_host(context, self.host)
for instance in instances:
- instance_uuid = instance['uuid']
db_state = instance['power_state']
drv_state = self._get_power_state(context, instance)
@@ -1725,7 +1723,7 @@ class ComputeManager(manager.SchedulerDependentManager):
connection_info = self.volume_api.initialize_connection(context,
volume,
connector)
- self.volume_api.attach(context, volume, instance_id, mountpoint)
+ self.volume_api.attach(context, volume, instance_uuid, mountpoint)
return connection_info
@exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
@@ -1764,7 +1762,10 @@ class ComputeManager(manager.SchedulerDependentManager):
volume,
connector)
- self.volume_api.attach(context, volume, instance_ref['id'], mountpoint)
+ self.volume_api.attach(context,
+ volume,
+ instance_ref['uuid'],
+ mountpoint)
values = {
'instance_uuid': instance_ref['uuid'],
'connection_info': utils.dumps(connection_info),
diff --git a/nova/db/api.py b/nova/db/api.py
index 93eea8f10..ca1e420d7 100644
--- a/nova/db/api.py
+++ b/nova/db/api.py
@@ -986,9 +986,9 @@ def volume_get_all_by_host(context, host):
return IMPL.volume_get_all_by_host(context, host)
-def volume_get_all_by_instance(context, instance_id):
+def volume_get_all_by_instance_uuid(context, instance_uuid):
"""Get all volumes belonging to a instance."""
- return IMPL.volume_get_all_by_instance(context, instance_id)
+ return IMPL.volume_get_all_by_instance_uuid(context, instance_uuid)
def volume_get_all_by_project(context, project_id):
@@ -1001,11 +1001,6 @@ def volume_get_by_ec2_id(context, ec2_id):
return IMPL.volume_get_by_ec2_id(context, ec2_id)
-def volume_get_instance(context, volume_id):
- """Get the instance that a volume is attached to."""
- return IMPL.volume_get_instance(context, volume_id)
-
-
def volume_get_iscsi_target_num(context, volume_id):
"""Get the target num (tid) allocated to the volume."""
return IMPL.volume_get_iscsi_target_num(context, volume_id)
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 46e080578..f6af98d68 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -1374,7 +1374,6 @@ def _build_instance_get(context, session=None):
project_only=True).\
options(joinedload_all('security_groups.rules')).\
options(joinedload('info_cache')).\
- options(joinedload('volumes')).\
options(joinedload('metadata')).\
options(joinedload('instance_type'))
@@ -2402,15 +2401,17 @@ def volume_allocate_iscsi_target(context, volume_id, host):
@require_admin_context
-def volume_attached(context, volume_id, instance_id, mountpoint):
+def volume_attached(context, volume_id, instance_uuid, mountpoint):
+ if not utils.is_uuid_like(instance_uuid):
+ raise exception.InvalidUUID(instance_uuid)
+
session = get_session()
with session.begin():
volume_ref = volume_get(context, volume_id, session=session)
volume_ref['status'] = 'in-use'
volume_ref['mountpoint'] = mountpoint
volume_ref['attach_status'] = 'attached'
- volume_ref.instance = instance_get(context, instance_id,
- session=session)
+ volume_ref['instance_uuid'] = instance_uuid
volume_ref.save(session=session)
@@ -2471,7 +2472,7 @@ def volume_detached(context, volume_id):
volume_ref['status'] = 'available'
volume_ref['mountpoint'] = None
volume_ref['attach_status'] = 'detached'
- volume_ref.instance = None
+ volume_ref['instance_uuid'] = None
volume_ref.save(session=session)
@@ -2479,9 +2480,8 @@ def volume_detached(context, volume_id):
def _volume_get_query(context, session=None, project_only=False):
return model_query(context, models.Volume, session=session,
project_only=project_only).\
- options(joinedload('instance')).\
- options(joinedload('volume_metadata')).\
- options(joinedload('volume_type'))
+ options(joinedload('volume_metadata')).\
+ options(joinedload('volume_type'))
@require_context
@@ -2519,15 +2519,15 @@ def volume_get_all_by_host(context, host):
@require_admin_context
-def volume_get_all_by_instance(context, instance_id):
+def volume_get_all_by_instance_uuid(context, instance_uuid):
result = model_query(context, models.Volume, read_deleted="no").\
options(joinedload('volume_metadata')).\
options(joinedload('volume_type')).\
- filter_by(instance_id=instance_id).\
+ filter_by(instance_uuid=instance_uuid).\
all()
if not result:
- raise exception.VolumeNotFoundForInstance(instance_id=instance_id)
+ return []
return result
@@ -2539,16 +2539,6 @@ def volume_get_all_by_project(context, project_id):
@require_admin_context
-def volume_get_instance(context, volume_id):
- result = _volume_get_query(context).filter_by(id=volume_id).first()
-
- if not result:
- raise exception.VolumeNotFound(volume_id=volume_id)
-
- return result.instance
-
-
-@require_admin_context
def volume_get_iscsi_target_num(context, volume_id):
result = model_query(context, models.IscsiTarget, read_deleted="yes").\
filter_by(volume_id=volume_id).\
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/095_change_fk_instance_id_to_uuid.py b/nova/db/sqlalchemy/migrate_repo/versions/095_change_fk_instance_id_to_uuid.py
new file mode 100644
index 000000000..ea8684f3d
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/095_change_fk_instance_id_to_uuid.py
@@ -0,0 +1,94 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012 OpenStack LLC.
+# Copyright 2012 SolidFire Inc
+# 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 select, Column
+from sqlalchemy import MetaData, Integer, String, Table
+from migrate import ForeignKeyConstraint
+
+from nova import log as logging
+
+
+LOG = logging.getLogger(__name__)
+
+
+def upgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ instances = Table('instances', meta, autoload=True)
+ volumes = Table('volumes', meta, autoload=True)
+ instance_uuid_column = Column('instance_uuid', String(36))
+
+ instance_uuid_column.create(volumes)
+ try:
+ volumes.update().values(
+ instance_uuid=select(
+ [instances.c.uuid],
+ instances.c.id == volumes.c.instance_id)
+ ).execute()
+ except Exception:
+ instance_uuid_column.drop()
+
+ fkeys = list(volumes.c.instance_id.foreign_keys)
+ if fkeys:
+ try:
+ fk_name = fkeys[0].constraint.name
+ ForeignKeyConstraint(
+ columns=[volumes.c.instance_id],
+ refcolumns=[instances.c.id],
+ name=fk_name).drop()
+
+ except Exception:
+ LOG.error(_("foreign key could not be dropped"))
+ raise
+
+ volumes.c.instance_id.drop()
+
+
+def downgrade(migrate_engine):
+ meta = MetaData()
+ meta.bind = migrate_engine
+
+ instances = Table('instances', meta, autoload=True)
+ volumes = Table('volumes', meta, autoload=True)
+ instance_id_column = Column('instance_id', Integer)
+
+ instance_id_column.create(volumes)
+ try:
+ volumes.update().values(
+ instance_id=select(
+ [instances.c.id],
+ instances.c.uuid == volumes.c.instance_uuid)
+ ).execute()
+ except Exception:
+ instance_id_column.drop()
+
+ fkeys = list(volumes.c.instance_id.foreign_keys)
+ if fkeys:
+ try:
+ fk_name = fkeys[0].constraint.name
+ ForeignKeyConstraint(
+ columns=[volumes.c.instance_id],
+ refcolumns=[instances.c.id],
+ name=fk_name).create()
+
+ except Exception:
+ LOG.error(_("foreign key could not be created"))
+ raise
+
+ volumes.c.instance_uuid.drop()
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/095_sqlite_downgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/095_sqlite_downgrade.sql
new file mode 100644
index 000000000..7c13455e4
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/095_sqlite_downgrade.sql
@@ -0,0 +1,133 @@
+BEGIN TRANSACTION;
+ -- change instance_id volumes table
+ CREATE TABLE volumes_backup(
+ created_at DATETIME,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN,
+ id VARCHAR(36) NOT NULL,
+ ec2_id INTEGER,
+ user_id VARCHAR(255),
+ project_id VARCHAR(255),
+ snapshot_id VARCHAR(36),
+ host VARCHAR(255),
+ size INTEGER,
+ availability_zone VARCHAR(255),
+ instance_id INTEGER,
+ instance_uuid VARCHAR(36),
+ mountpoint VARCHAR(255),
+ attach_time VARCHAR(255),
+ status VARCHAR(255),
+ attach_status VARCHAR(255),
+ scheduled_at DATETIME,
+ launched_at DATETIME,
+ terminated_at DATETIME,
+ display_name VARCHAR(255),
+ display_description VARCHAR(255),
+ provider_location VARCHAR(255),
+ provider_auth VARCHAR(255),
+ volume_type_id INTEGER,
+ PRIMARY KEY (id),
+ FOREIGN KEY(instance_id) REFERENCES instances (id),
+ UNIQUE (id),
+ CHECK (deleted IN (0, 1))
+ );
+
+ INSERT INTO volumes_backup SELECT
+ created_at,
+ updated_at,
+ deleted_at,
+ deleted,
+ id,
+ ec2_id,
+ user_id,
+ project_id,
+ snapshot_id,
+ host,
+ size,
+ availability_zone,
+ NULL,
+ instance_uuid,
+ mountpoint,
+ attach_time,
+ status,
+ attach_status,
+ scheduled_at,
+ launched_at,
+ terminated_at,
+ display_name,
+ display_description,
+ provider_location,
+ provider_auth,
+ volume_type_id
+ FROM volumes;
+
+ UPDATE volumes_backup
+ SET instance_id =
+ (SELECT id
+ FROM instances
+ WHERE volumes_backup.instance_uuid = instances.uuid
+ );
+ DROP TABLE volumes;
+
+ CREATE TABLE volumes(
+ created_at DATETIME,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN,
+ id VARCHAR(36) NOT NULL,
+ ec2_id INTEGER,
+ user_id VARCHAR(255),
+ project_id VARCHAR(255),
+ snapshot_id VARCHAR(36),
+ host VARCHAR(255),
+ size INTEGER,
+ availability_zone VARCHAR(255),
+ instance_id INTEGER,
+ mountpoint VARCHAR(255),
+ attach_time VARCHAR(255),
+ status VARCHAR(255),
+ attach_status VARCHAR(255),
+ scheduled_at DATETIME,
+ launched_at DATETIME,
+ terminated_at DATETIME,
+ display_name VARCHAR(255),
+ display_description VARCHAR(255),
+ provider_location VARCHAR(255),
+ provider_auth VARCHAR(255),
+ volume_type_id INTEGER,
+ PRIMARY KEY (id),
+ FOREIGN KEY (instance_id) REFERENCES instances (id),
+ UNIQUE (id),
+ CHECK (deleted IN (0, 1))
+ );
+
+ INSERT INTO volumes
+ SELECT created_at,
+ updated_at,
+ deleted_at,
+ deleted,
+ id,
+ ec2_id,
+ user_id,
+ project_id,
+ snapshot_id,
+ host,
+ size,
+ availability_zone,
+ instance_id,
+ mountpoint,
+ attach_time,
+ status,
+ attach_status,
+ scheduled_at,
+ launched_at,
+ terminated_at,
+ display_name,
+ display_description,
+ provider_location,
+ provider_auth,
+ volume_type_id
+ FROM volumes_backup;
+ DROP TABLE volumes_backup;
+COMMIT;
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/095_sqlite_upgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/095_sqlite_upgrade.sql
new file mode 100644
index 000000000..130e11030
--- /dev/null
+++ b/nova/db/sqlalchemy/migrate_repo/versions/095_sqlite_upgrade.sql
@@ -0,0 +1,132 @@
+BEGIN TRANSACTION;
+ -- change instance_id volumes table
+ CREATE TABLE volumes_backup(
+ created_at DATETIME,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN,
+ id VARCHAR(36) NOT NULL,
+ ec2_id INTEGER,
+ user_id VARCHAR(255),
+ project_id VARCHAR(255),
+ snapshot_id VARCHAR(36),
+ host VARCHAR(255),
+ size INTEGER,
+ availability_zone VARCHAR(255),
+ instance_id INTEGER,
+ instance_uuid VARCHAR(36),
+ mountpoint VARCHAR(255),
+ attach_time VARCHAR(255),
+ status VARCHAR(255),
+ attach_status VARCHAR(255),
+ scheduled_at DATETIME,
+ launched_at DATETIME,
+ terminated_at DATETIME,
+ display_name VARCHAR(255),
+ display_description VARCHAR(255),
+ provider_location VARCHAR(255),
+ provider_auth VARCHAR(255),
+ volume_type_id INTEGER,
+ PRIMARY KEY (id),
+ FOREIGN KEY(instance_id) REFERENCES instances (id),
+ UNIQUE (id),
+ CHECK (deleted IN (0, 1))
+ );
+
+ INSERT INTO volumes_backup SELECT
+ created_at,
+ updated_at,
+ deleted_at,
+ deleted,
+ id,
+ ec2_id,
+ user_id,
+ project_id,
+ snapshot_id,
+ host,
+ size,
+ availability_zone,
+ instance_id,
+ NULL,
+ mountpoint,
+ attach_time,
+ status,
+ attach_status,
+ scheduled_at,
+ launched_at,
+ terminated_at,
+ display_name,
+ display_description,
+ provider_location,
+ provider_auth,
+ volume_type_id
+ FROM volumes;
+
+ UPDATE volumes_backup
+ SET instance_uuid =
+ (SELECT uuid
+ FROM instances
+ WHERE volumes_backup.instance_id = instances.id
+ );
+ DROP TABLE volumes;
+
+ CREATE TABLE volumes(
+ created_at DATETIME,
+ updated_at DATETIME,
+ deleted_at DATETIME,
+ deleted BOOLEAN,
+ id VARCHAR(36) NOT NULL,
+ ec2_id INTEGER,
+ user_id VARCHAR(255),
+ project_id VARCHAR(255),
+ snapshot_id VARCHAR(36),
+ host VARCHAR(255),
+ size INTEGER,
+ availability_zone VARCHAR(255),
+ instance_uuid VARCHAR(36),
+ mountpoint VARCHAR(255),
+ attach_time VARCHAR(255),
+ status VARCHAR(255),
+ attach_status VARCHAR(255),
+ scheduled_at DATETIME,
+ launched_at DATETIME,
+ terminated_at DATETIME,
+ display_name VARCHAR(255),
+ display_description VARCHAR(255),
+ provider_location VARCHAR(255),
+ provider_auth VARCHAR(255),
+ volume_type_id INTEGER,
+ PRIMARY KEY (id),
+ UNIQUE (id),
+ CHECK (deleted IN (0, 1))
+ );
+
+ INSERT INTO volumes
+ SELECT created_at,
+ updated_at,
+ deleted_at,
+ deleted,
+ id,
+ ec2_id,
+ user_id,
+ project_id,
+ snapshot_id,
+ host,
+ size,
+ availability_zone,
+ instance_uuid,
+ mountpoint,
+ attach_time,
+ status,
+ attach_status,
+ scheduled_at,
+ launched_at,
+ terminated_at,
+ display_name,
+ display_description,
+ provider_location,
+ provider_auth,
+ volume_type_id
+ FROM volumes_backup;
+ DROP TABLE volumes_backup;
+COMMIT;
diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py
index 9007de424..056ecd411 100644
--- a/nova/db/sqlalchemy/models.py
+++ b/nova/db/sqlalchemy/models.py
@@ -341,12 +341,7 @@ class Volume(BASE, NovaBase):
host = Column(String(255)) # , ForeignKey('hosts.id'))
size = Column(Integer)
availability_zone = Column(String(255)) # TODO(vish): foreign key?
- instance_id = Column(Integer, ForeignKey('instances.id'), nullable=True)
- instance = relationship(Instance,
- backref=backref('volumes'),
- foreign_keys=instance_id,
- primaryjoin='and_(Volume.instance_id==Instance.id,'
- 'Volume.deleted==False)')
+ instance_uuid = Column(String(36))
mountpoint = Column(String(255))
attach_time = Column(String(255)) # TODO(vish): datetime
status = Column(String(255)) # TODO(vish): enum?
diff --git a/nova/exception.py b/nova/exception.py
index d724063ef..c97290480 100644
--- a/nova/exception.py
+++ b/nova/exception.py
@@ -26,7 +26,6 @@ SHOULD include dedicated exception logging.
import functools
import itertools
-import sys
import webob.exc
@@ -420,6 +419,10 @@ class InvalidEc2Id(Invalid):
message = _("Ec2 id %(ec2_id)s is unacceptable.")
+class InvalidUUID(Invalid):
+ message = _("Expected a uuid but received %(uuid).")
+
+
class NotFound(NovaException):
message = _("Resource could not be found.")
code = 404
diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py
index 6d312369b..ec8bdf276 100644
--- a/nova/tests/api/ec2/test_cloud.py
+++ b/nova/tests/api/ec2/test_cloud.py
@@ -917,7 +917,7 @@ class CloudTestCase(test.TestCase):
result = self.cloud.describe_instances(self.context)
self.assertEqual(len(result['reservationSet']), 2)
- def _block_device_mapping_create(self, instance_id, mappings):
+ def _block_device_mapping_create(self, instance_uuid, mappings):
volumes = []
for bdm in mappings:
db.block_device_mapping_create(self.context, bdm)
@@ -931,7 +931,7 @@ class CloudTestCase(test.TestCase):
values[vol_key] = bdm[bdm_key]
vol = db.volume_create(self.context, values)
db.volume_attached(self.context, vol['id'],
- instance_id, bdm['device_name'])
+ instance_uuid, bdm['device_name'])
volumes.append(vol)
return volumes
@@ -987,7 +987,7 @@ class CloudTestCase(test.TestCase):
'device_name': '/dev/sdb9',
'virtual_name': 'ephemeral3'}]
- volumes = self._block_device_mapping_create(instance_id, mappings0)
+ volumes = self._block_device_mapping_create(instance_uuid, mappings0)
return (inst1, inst2, volumes)
def _tearDownBlockDeviceMapping(self, inst1, inst2, volumes):
@@ -1910,14 +1910,14 @@ class CloudTestCase(test.TestCase):
kwargs['id'] = volume_id
return db.volume_create(self.context, kwargs)
- def _assert_volume_attached(self, vol, instance_id, mountpoint):
- self.assertEqual(vol['instance_id'], instance_id)
+ def _assert_volume_attached(self, vol, instance_uuid, mountpoint):
+ self.assertEqual(vol['instance_uuid'], instance_uuid)
self.assertEqual(vol['mountpoint'], mountpoint)
self.assertEqual(vol['status'], "in-use")
self.assertEqual(vol['attach_status'], "attached")
def _assert_volume_detached(self, vol):
- self.assertEqual(vol['instance_id'], None)
+ self.assertEqual(vol['instance_uuid'], None)
self.assertEqual(vol['mountpoint'], None)
self.assertEqual(vol['status'], "available")
self.assertEqual(vol['attach_status'], "detached")
@@ -1941,18 +1941,20 @@ class CloudTestCase(test.TestCase):
'delete_on_termination': True},
]}
ec2_instance_id = self._run_instance(**kwargs)
+ instance_uuid = ec2utils.ec2_instance_id_to_uuid(self.context,
+ ec2_instance_id)
instance_id = ec2utils.ec2_id_to_id(ec2_instance_id)
- vols = db.volume_get_all_by_instance(self.context, instance_id)
+ vols = db.volume_get_all_by_instance_uuid(self.context, instance_uuid)
self.assertEqual(len(vols), 2)
for vol in vols:
self.assertTrue(vol['id'] == vol1['id'] or vol['id'] == vol2['id'])
vol = db.volume_get(self.context, vol1['id'])
- self._assert_volume_attached(vol, instance_id, '/dev/vdb')
+ self._assert_volume_attached(vol, instance_uuid, '/dev/vdb')
vol = db.volume_get(self.context, vol2['id'])
- self._assert_volume_attached(vol, instance_id, '/dev/vdc')
+ self._assert_volume_attached(vol, instance_uuid, '/dev/vdc')
result = self.cloud.stop_instances(self.context, [ec2_instance_id])
self.assertTrue(result)
@@ -1963,13 +1965,13 @@ class CloudTestCase(test.TestCase):
self._assert_volume_detached(vol)
self.cloud.start_instances(self.context, [ec2_instance_id])
- vols = db.volume_get_all_by_instance(self.context, instance_id)
+ vols = db.volume_get_all_by_instance_uuid(self.context, instance_uuid)
self.assertEqual(len(vols), 2)
for vol in vols:
self.assertTrue(vol['id'] == vol1['id'] or vol['id'] == vol2['id'])
self.assertTrue(vol['mountpoint'] == '/dev/vdb' or
vol['mountpoint'] == '/dev/vdc')
- self.assertEqual(vol['instance_id'], instance_id)
+ self.assertEqual(vol['instance_uuid'], instance_uuid)
self.assertEqual(vol['status'], "in-use")
self.assertEqual(vol['attach_status'], "attached")
@@ -2001,12 +2003,14 @@ class CloudTestCase(test.TestCase):
'delete_on_termination': True}]}
ec2_instance_id = self._run_instance(**kwargs)
instance_id = ec2utils.ec2_id_to_id(ec2_instance_id)
+ instance_uuid = ec2utils.ec2_instance_id_to_uuid(self.context,
+ ec2_instance_id)
- vols = db.volume_get_all_by_instance(self.context, instance_id)
+ vols = db.volume_get_all_by_instance_uuid(self.context, instance_uuid)
self.assertEqual(len(vols), 1)
for vol in vols:
self.assertEqual(vol['id'], vol1['id'])
- self._assert_volume_attached(vol, instance_id, '/dev/vdb')
+ self._assert_volume_attached(vol, instance_uuid, '/dev/vdb')
vol = db.volume_get(self.context, vol2['id'])
self._assert_volume_detached(vol)
@@ -2017,7 +2021,7 @@ class CloudTestCase(test.TestCase):
volume_id=vol2['id'],
device='/dev/vdc')
vol = db.volume_get(self.context, vol2['id'])
- self._assert_volume_attached(vol, instance_id, '/dev/vdc')
+ self._assert_volume_attached(vol, instance_uuid, '/dev/vdc')
self.cloud.compute_api.detach_volume(self.context,
volume_id=vol1['id'])
@@ -2032,11 +2036,11 @@ class CloudTestCase(test.TestCase):
self._assert_volume_detached(vol)
self.cloud.start_instances(self.context, [ec2_instance_id])
- vols = db.volume_get_all_by_instance(self.context, instance_id)
+ vols = db.volume_get_all_by_instance_uuid(self.context, instance_uuid)
self.assertEqual(len(vols), 1)
for vol in vols:
self.assertEqual(vol['id'], vol2['id'])
- self._assert_volume_attached(vol, instance_id, '/dev/vdc')
+ self._assert_volume_attached(vol, instance_uuid, '/dev/vdc')
vol = db.volume_get(self.context, vol1['id'])
self._assert_volume_detached(vol)
@@ -2077,8 +2081,10 @@ class CloudTestCase(test.TestCase):
'delete_on_termination': True}]}
ec2_instance_id = self._run_instance(**kwargs)
instance_id = ec2utils.ec2_id_to_id(ec2_instance_id)
+ instance_uuid = ec2utils.ec2_instance_id_to_uuid(self.context,
+ ec2_instance_id)
- vols = db.volume_get_all_by_instance(self.context, instance_id)
+ vols = db.volume_get_all_by_instance_uuid(self.context, instance_uuid)
self.assertEqual(len(vols), 2)
vol1_id = None
vol2_id = None
@@ -2093,7 +2099,7 @@ class CloudTestCase(test.TestCase):
else:
self.fail()
- self._assert_volume_attached(vol, instance_id, mountpoint)
+ self._assert_volume_attached(vol, instance_uuid, mountpoint)
self.assertTrue(vol1_id)
self.assertTrue(vol2_id)
diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py
index 90ee86676..f06878362 100644
--- a/nova/tests/api/openstack/fakes.py
+++ b/nova/tests/api/openstack/fakes.py
@@ -592,7 +592,7 @@ def stub_volume(id, **kwargs):
'host': 'fakehost',
'size': 1,
'availability_zone': 'fakeaz',
- 'instance': {'uuid': 'fakeuuid'},
+ 'instance_uuid': 'fakeuuid',
'mountpoint': '/',
'status': 'fakestatus',
'attach_status': 'attached',
@@ -611,7 +611,7 @@ def stub_volume(id, **kwargs):
def stub_volume_create(self, context, size, name, description, snapshot,
**param):
- vol = stub_volume(1)
+ vol = stub_volume('1')
vol['size'] = size
vol['display_name'] = name
vol['display_description'] = description
@@ -640,4 +640,4 @@ def stub_volume_get_notfound(self, context, volume_id):
def stub_volume_get_all(self, context, search_opts=None):
- return [stub_volume_get(self, context, 1)]
+ return [stub_volume_get(self, context, '1')]
diff --git a/nova/tests/api/openstack/volume/test_volumes.py b/nova/tests/api/openstack/volume/test_volumes.py
index 7befe9606..2b96d15a3 100644
--- a/nova/tests/api/openstack/volume/test_volumes.py
+++ b/nova/tests/api/openstack/volume/test_volumes.py
@@ -90,6 +90,7 @@ class VolumeApiTest(test.TestCase):
'created_at': datetime.datetime(1, 1, 1,
1, 1, 1),
'size': 1}]}
+ self.maxDiff = None
self.assertEqual(res_dict, expected)
def test_volume_list_detail(self):
@@ -114,7 +115,7 @@ class VolumeApiTest(test.TestCase):
def test_volume_show(self):
req = fakes.HTTPRequest.blank('/v1/volumes/1')
- res_dict = self.controller.show(req, 1)
+ res_dict = self.controller.show(req, '1')
expected = {'volume': {'status': 'fakestatus',
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
@@ -139,7 +140,7 @@ class VolumeApiTest(test.TestCase):
self.stubs.Set(volume_api.API, 'get', stub_volume_get)
req = fakes.HTTPRequest.blank('/v1/volumes/1')
- res_dict = self.controller.show(req, 1)
+ res_dict = self.controller.show(req, '1')
expected = {'volume': {'status': 'fakestatus',
'display_description': 'displaydesc',
'availability_zone': 'fakeaz',
diff --git a/nova/tests/scheduler/test_scheduler.py b/nova/tests/scheduler/test_scheduler.py
index 0b3c0e06a..a662d6930 100644
--- a/nova/tests/scheduler/test_scheduler.py
+++ b/nova/tests/scheduler/test_scheduler.py
@@ -477,7 +477,6 @@ class SchedulerTestCase(test.TestCase):
instance = self._live_migration_instance()
db.instance_get(self.context, instance['id']).AndReturn(instance)
- # Source checks (volume and source compute are up)
db.service_get_all_compute_by_host(self.context,
instance['host']).AndReturn(['fake_service2'])
utils.service_is_up('fake_service2').AndReturn(True)
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index 2391ea1ea..9e53df279 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -1215,7 +1215,9 @@ class LibvirtConnTestCase(test.TestCase):
instance_dict)
vol_dict = {'status': 'migrating', 'size': 1}
volume_ref = db.volume_create(self.context, vol_dict)
- db.volume_attached(self.context, volume_ref['id'], instance_ref['id'],
+ db.volume_attached(self.context,
+ volume_ref['id'],
+ instance_ref['uuid'],
'/dev/fake')
# Preparing mocks
diff --git a/nova/tests/test_volume.py b/nova/tests/test_volume.py
index 27969ee5b..d979c3d0e 100644
--- a/nova/tests/test_volume.py
+++ b/nova/tests/test_volume.py
@@ -48,7 +48,9 @@ class VolumeTestCase(test.TestCase):
self.flags(connection_type='fake')
self.volume = importutils.import_object(FLAGS.volume_manager)
self.context = context.get_admin_context()
- self.instance_id = db.instance_create(self.context, {})['id']
+ instance = db.instance_create(self.context, {})
+ self.instance_id = instance['id']
+ self.instance_uuid = instance['uuid']
def tearDown(self):
db.instance_destroy(self.context, self.instance_id)
@@ -175,25 +177,26 @@ class VolumeTestCase(test.TestCase):
inst['project_id'] = 'fake'
inst['instance_type_id'] = '2' # m1.tiny
inst['ami_launch_index'] = 0
- instance_id = db.instance_create(self.context, inst)['id']
+ instance = db.instance_create(self.context, {})
+ instance_id = instance['id']
+ instance_uuid = instance['uuid']
mountpoint = "/dev/sdf"
volume = self._create_volume()
volume_id = volume['id']
self.volume.create_volume(self.context, volume_id)
if FLAGS.fake_tests:
- db.volume_attached(self.context, volume_id, instance_id,
+ db.volume_attached(self.context, volume_id, instance_uuid,
mountpoint)
else:
self.compute.attach_volume(self.context,
- instance_id,
+ instance_uuid,
volume_id,
mountpoint)
vol = db.volume_get(context.get_admin_context(), volume_id)
self.assertEqual(vol['status'], "in-use")
self.assertEqual(vol['attach_status'], "attached")
self.assertEqual(vol['mountpoint'], mountpoint)
- instance_ref = db.volume_get_instance(self.context, volume_id)
- self.assertEqual(instance_ref['id'], instance_id)
+ self.assertEqual(vol['instance_uuid'], instance_uuid)
self.assertRaises(exception.NovaException,
self.volume.delete_volume,
@@ -203,7 +206,7 @@ class VolumeTestCase(test.TestCase):
db.volume_detached(self.context, volume_id)
else:
self.compute.detach_volume(self.context,
- instance_id,
+ instance_uuid,
volume_id)
vol = db.volume_get(self.context, volume_id)
self.assertEqual(vol['status'], "available")
@@ -323,7 +326,7 @@ class VolumeTestCase(test.TestCase):
volume = self._create_volume()
self.volume.create_volume(self.context, volume['id'])
- db.volume_attached(self.context, volume['id'], self.instance_id,
+ db.volume_attached(self.context, volume['id'], self.instance_uuid,
'/dev/sda1')
volume_api = nova.volume.api.API()
@@ -383,7 +386,9 @@ class DriverTestCase(test.TestCase):
log.logger.addHandler(logging.logging.StreamHandler(self.stream))
inst = {}
- self.instance_id = db.instance_create(self.context, inst)['id']
+ instance = db.instance_create(self.context, {})
+ self.instance_id = instance['id']
+ self.instance_uuid = instance['uuid']
def _attach_volume(self):
"""Attach volumes to an instance. This function also sets
@@ -436,7 +441,7 @@ class ISCSITestCase(DriverTestCase):
# each volume has a different mountpoint
mountpoint = "/dev/sd" + chr((ord('b') + index))
- db.volume_attached(self.context, vol_ref['id'], self.instance_id,
+ db.volume_attached(self.context, vol_ref['id'], self.instance_uuid,
mountpoint)
volume_id_list.append(vol_ref['id'])
diff --git a/nova/volume/api.py b/nova/volume/api.py
index f62d41ea6..33643954c 100644
--- a/nova/volume/api.py
+++ b/nova/volume/api.py
@@ -248,13 +248,13 @@ class API(base.Base):
self.update(context, volume, {"status": "available"})
@wrap_check_policy
- def attach(self, context, volume, instance_id, mountpoint):
+ def attach(self, context, volume, instance_uuid, mountpoint):
host = volume['host']
queue = self.db.queue_get_for(context, FLAGS.volume_topic, host)
return rpc.call(context, queue,
{"method": "attach_volume",
"args": {"volume_id": volume['id'],
- "instance_id": instance_id,
+ "instance_uuid": instance_uuid,
"mountpoint": mountpoint}})
@wrap_check_policy
diff --git a/nova/volume/manager.py b/nova/volume/manager.py
index bd03234d7..828451f7c 100644
--- a/nova/volume/manager.py
+++ b/nova/volume/manager.py
@@ -229,12 +229,15 @@ class VolumeManager(manager.SchedulerDependentManager):
LOG.debug(_("snapshot %s: deleted successfully"), snapshot_ref['name'])
return True
- def attach_volume(self, context, volume_id, instance_id, mountpoint):
+ def attach_volume(self, context, volume_id, instance_uuid, mountpoint):
"""Updates db to show volume is attached"""
# TODO(vish): refactor this into a more general "reserve"
+ if not utils.is_uuid_like(instance_uuid):
+ raise exception.InvalidUUID(instance_uuid)
+
self.db.volume_attached(context,
volume_id,
- instance_id,
+ instance_uuid,
mountpoint)
def detach_volume(self, context, volume_id):
@@ -293,7 +296,10 @@ class VolumeManager(manager.SchedulerDependentManager):
def check_for_export(self, context, instance_id):
"""Make sure whether volume is exported."""
instance_ref = self.db.instance_get(context, instance_id)
- for volume in instance_ref['volumes']:
+ volumes = self.db.volume_get_all_by_instance_uuid(context,
+ instance_ref['uuid'])
+
+ for volume in volumes:
self.driver.check_for_export(context, volume['id'])
def _volume_stats_changed(self, stat1, stat2):