diff options
| author | John Griffith <john.griffith@solidfire.com> | 2012-04-12 20:36:48 -0600 |
|---|---|---|
| committer | John Griffith <john.griffith@solidfire.com> | 2012-05-02 13:32:19 -0600 |
| commit | 407e16b863bac1dfbf4e954837009abf9c17f018 (patch) | |
| tree | 10af13810b6746c0fad249b42d3f71466ca87428 /nova/db | |
| parent | ca2bb061a728bb5db8781f298c18c980d9d91863 (diff) | |
Convert Volume and Snapshot IDs to use UUID
* Three migrations
1. create id mappings
2. convert volume_id and snapshot_id from int to string
3. change volume/snapshot id's from int to uuid
* DB migration for Volume and Related tables
* Addition of new volume id mapping tables
* Added methods in ec2utils
* Minor tweaks to unit tests
* Other changes to migration to ensure consistency in id's
* Fixed bug in the block-device-mapping table (wasn't setting autoinc)
Change-Id: Ic6c3646e0f01c26467a4a3c20e13eebaa2baa97e
Diffstat (limited to 'nova/db')
| -rw-r--r-- | nova/db/api.py | 23 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/api.py | 102 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/migrate_repo/versions/089_add_volume_id_mappings.py | 116 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/migrate_repo/versions/090_modify_volume_id_datatype.py | 236 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/migrate_repo/versions/090_sqlite_downgrade.sql | 226 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/migrate_repo/versions/090_sqlite_upgrade.sql | 226 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/migrate_repo/versions/091_convert_volume_ids_to_uuid.py | 145 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/models.py | 35 |
8 files changed, 1098 insertions, 11 deletions
diff --git a/nova/db/api.py b/nova/db/api.py index 5de921667..d6eb43146 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -60,10 +60,10 @@ db_opts = [ default='instance-%08x', help='Template string to be used to generate instance names'), cfg.StrOpt('volume_name_template', - default='volume-%08x', + default='volume-%s', help='Template string to be used to generate instance names'), cfg.StrOpt('snapshot_name_template', - default='snapshot-%08x', + default='snapshot-%s', help='Template string to be used to generate snapshot names'), ] @@ -1025,6 +1025,25 @@ def volume_update(context, volume_id, values): return IMPL.volume_update(context, volume_id, values) +def get_ec2_volume_id_by_uuid(context, volume_id): + return IMPL.get_ec2_volume_id_by_uuid(context, volume_id) + + +def get_volume_uuid_by_ec2_id(context, ec2_id): + return IMPL.get_volume_uuid_by_ec2_id(context, ec2_id) + + +def ec2_volume_create(context, volume_id, forced_id=None): + return IMPL.ec2_volume_create(context, volume_id, forced_id) + + +def get_snapshot_uuid_by_ec2_id(context, ec2_id): + return IMPL.get_snapshot_uuid_by_ec2_id(context, ec2_id) + + +def get_ec2_snapshot_id_by_uuid(context, snapshot_id): + return IMPL.get_ec2_snapshot_id_by_uuid(context, snapshot_id) + #################### diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 1d7509aef..15a843306 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -2171,6 +2171,7 @@ def iscsi_target_count_by_host(context, host): @require_admin_context def iscsi_target_create_safe(context, values): iscsi_target_ref = models.IscsiTarget() + for (key, value) in values.iteritems(): iscsi_target_ref[key] = value try: @@ -2409,11 +2410,15 @@ def volume_create(context, values): values['volume_metadata'] = _metadata_refs(values.get('metadata'), models.VolumeMetadata) volume_ref = models.Volume() + if not values.get('id'): + values['id'] = str(utils.gen_uuid()) volume_ref.update(values) session = get_session() with session.begin(): volume_ref.save(session=session) + + ec2_volume_create(context, volume_ref['id']) return volume_ref @@ -2471,6 +2476,18 @@ def _volume_get_query(context, session=None, project_only=False): @require_context +def _ec2_volume_get_query(context, session=None, project_only=False): + return model_query(context, models.VolumeIdMapping, session=session, + project_only=project_only) + + +@require_context +def _ec2_snapshot_get_query(context, session=None, project_only=False): + return model_query(context, models.SnapshotIdMapping, session=session, + project_only=project_only) + + +@require_context def volume_get(context, volume_id, session=None): result = _volume_get_query(context, session=session, project_only=True).\ filter_by(id=volume_id).\ @@ -2549,6 +2566,88 @@ def volume_update(context, volume_id, values): volume_ref.save(session=session) +@require_context +def ec2_volume_create(context, volume_uuid, id=None): + """Create ec2 compatable volume by provided uuid""" + ec2_volume_ref = models.VolumeIdMapping() + ec2_volume_ref.update({'uuid': volume_uuid}) + if id is not None: + ec2_volume_ref.update({'id': id}) + + ec2_volume_ref.save() + + return ec2_volume_ref + + +@require_context +def get_ec2_volume_id_by_uuid(context, volume_id, session=None): + result = _ec2_volume_get_query(context, + session=session, + project_only=True).\ + filter_by(uuid=volume_id).\ + first() + + if not result: + raise exception.VolumeNotFound(uuid=volume_id) + + return result['id'] + + +@require_context +def get_volume_uuid_by_ec2_id(context, ec2_id, session=None): + result = _ec2_volume_get_query(context, + session=session, + project_only=True).\ + filter_by(id=ec2_id).\ + first() + + if not result: + raise exception.VolumeNotFound(ec2_id=ec2_id) + + return result['uuid'] + + +@require_context +def ec2_snapshot_create(context, snapshot_uuid, id=None): + """Create ec2 compatable snapshot by provided uuid""" + ec2_snapshot_ref = models.SnapshotIdMapping() + ec2_snapshot_ref.update({'uuid': snapshot_uuid}) + if id is not None: + ec2_snapshot_ref.update({'id': id}) + + ec2_snapshot_ref.save() + + return ec2_snapshot_ref + + +@require_context +def get_ec2_snapshot_id_by_uuid(context, snapshot_id, session=None): + result = _ec2_snapshot_get_query(context, + session=session, + project_only=True).\ + filter_by(uuid=snapshot_id).\ + first() + + if not result: + raise exception.SnapshotNotFound(uuid=snapshot_id) + + return result['id'] + + +@require_context +def get_snapshot_uuid_by_ec2_id(context, ec2_id, session=None): + result = _ec2_snapshot_get_query(context, + session=session, + project_only=True).\ + filter_by(id=ec2_id).\ + first() + + if not result: + raise exception.SnapshotNotFound(ec2_id=ec2_id) + + return result['uuid'] + + #################### def _volume_metadata_get_query(context, volume_id, session=None): @@ -2633,11 +2732,14 @@ def volume_metadata_update(context, volume_id, metadata, delete): @require_context def snapshot_create(context, values): snapshot_ref = models.Snapshot() + if not values.get('id'): + values['id'] = str(utils.gen_uuid()) snapshot_ref.update(values) session = get_session() with session.begin(): snapshot_ref.save(session=session) + ec2_snapshot_create(context, snapshot_ref['id']) return snapshot_ref diff --git a/nova/db/sqlalchemy/migrate_repo/versions/089_add_volume_id_mappings.py b/nova/db/sqlalchemy/migrate_repo/versions/089_add_volume_id_mappings.py new file mode 100644 index 000000000..d3a705abc --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/089_add_volume_id_mappings.py @@ -0,0 +1,116 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 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 Boolean, Column, DateTime, Integer +from sqlalchemy import MetaData, String, Table +from nova import log as logging +from nova import utils + +LOG = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + """Build mapping tables for our volume uuid migration. + + These mapping tables serve two purposes: + 1. Provide a method for downgrade after UUID conversion + 2. Provide a uuid to associate with existing volumes and snapshots + when we do the actual datatype migration from int to uuid + + """ + meta = MetaData() + meta.bind = migrate_engine + + volume_id_mappings = Table('volume_id_mappings', 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, + autoincrement=True), + Column('uuid', String(36), + nullable=False)) + try: + volume_id_mappings.create() + except Exception: + LOG.exception("Exception while creating table 'volume_id_mappings'") + meta.drop_all(tables=[volume_id_mappings]) + raise + + snapshot_id_mappings = Table('snapshot_id_mappings', 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, + autoincrement=True), + Column('uuid', String(36), + nullable=False)) + try: + snapshot_id_mappings.create() + except Exception: + LOG.exception("Exception while creating table 'snapshot_id_mappings'") + meta.drop_all(tables=[snapshot_id_mappings]) + raise + + if migrate_engine.name == "mysql": + migrate_engine.execute("ALTER TABLE volume_id_mappings Engine=InnoDB") + migrate_engine.execute("ALTER TABLE snapshot_id_mappings "\ + "Engine=InnoDB") + + volumes = Table('volumes', meta, autoload=True) + snapshots = Table('snapshots', meta, autoload=True) + volume_id_mappings = Table('volume_id_mappings', meta, autoload=True) + snapshot_id_mappings = Table('snapshot_id_mappings', meta, autoload=True) + + volume_list = list(volumes.select().execute()) + for v in volume_list: + old_id = v['id'] + new_id = utils.gen_uuid() + row = volume_id_mappings.insert() + row.execute({'id': old_id, + 'uuid': str(new_id)}) + + snapshot_list = list(snapshots.select().execute()) + for s in snapshot_list: + old_id = s['id'] + new_id = utils.gen_uuid() + row = snapshot_id_mappings.insert() + row.execute({'id': old_id, + 'uuid': str(new_id)}) + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + volume_id_mappings = Table('volume_id_mappings', meta, autoload=True) + volume_id_mappings.drop() + + snapshot_id_mappings = Table('snapshot_id_mappings', meta, autoload=True) + snapshot_id_mappings.drop() diff --git a/nova/db/sqlalchemy/migrate_repo/versions/090_modify_volume_id_datatype.py b/nova/db/sqlalchemy/migrate_repo/versions/090_modify_volume_id_datatype.py new file mode 100644 index 000000000..36a6ba200 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/090_modify_volume_id_datatype.py @@ -0,0 +1,236 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 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 Integer +from sqlalchemy import MetaData, String, Table +from migrate import ForeignKeyConstraint +from nova import log as logging + +LOG = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + """Convert volume and snapshot id columns from int to varchar.""" + meta = MetaData() + meta.bind = migrate_engine + dialect = migrate_engine.url.get_dialect().name + + volumes = Table('volumes', meta, autoload=True) + snapshots = Table('snapshots', meta, autoload=True) + iscsi_targets = Table('iscsi_targets', meta, autoload=True) + volume_metadata = Table('volume_metadata', meta, autoload=True) + sm_volume = Table('sm_volume', meta, autoload=True) + block_device_mapping = Table('block_device_mapping', meta, autoload=True) + + try: + fkeys = list(snapshots.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[snapshots.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).drop() + + fkeys = list(iscsi_targets.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[iscsi_targets.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).drop() + + fkeys = list(volume_metadata.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[volume_metadata.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).drop() + + fkeys = list(sm_volume.c.id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[sm_volume.c.id], + refcolumns=[volumes.c.id], + name=fkey_name).drop() + + fkeys = list(block_device_mapping.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[block_device_mapping.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).drop() + + fkeys = list(block_device_mapping.c.snapshot_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[block_device_mapping.c.snapshot_id], + refcolumns=[snapshots.c.id], + name=fkey_name).drop() + + except Exception: + LOG.error(_("Foreign Key constraint couldn't be removed")) + raise + + volumes.c.id.alter(String(36), primary_key=True) + volumes.c.snapshot_id.alter(String(36)) + volume_metadata.c.volume_id.alter(String(36), nullable=False) + snapshots.c.id.alter(String(36), primary_key=True) + snapshots.c.volume_id.alter(String(36)) + sm_volume.c.id.alter(String(36)) + block_device_mapping.c.volume_id.alter(String(36)) + block_device_mapping.c.snapshot_id.alter(String(36)) + iscsi_targets.c.volume_id.alter(String(36), nullable=True) + + try: + fkeys = list(snapshots.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[snapshots.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).create() + + fkeys = list(iscsi_targets.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[iscsi_targets.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).create() + + fkeys = list(volume_metadata.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[volume_metadata.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).create() + + fkeys = list(sm_volume.c.id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[sm_volume.c.id], + refcolumns=[volumes.c.id], + name=fkey_name).create() + # NOTE(jdg) We're intentionally leaving off FK's on BDM + + except Exception: + LOG.error(_("Foreign Key constraint couldn't be removed")) + raise + + +def downgrade(migrate_engine): + """Convert volume and snapshot id columns back to int.""" + meta = MetaData() + meta.bind = migrate_engine + dialect = migrate_engine.url.get_dialect().name + + if dialect.startswith('sqlite'): + return + + volumes = Table('volumes', meta, autoload=True) + snapshots = Table('snapshots', meta, autoload=True) + iscsi_targets = Table('iscsi_targets', meta, autoload=True) + volume_metadata = Table('volume_metadata', meta, autoload=True) + sm_volume = Table('sm_volume', meta, autoload=True) + block_device_mapping = Table('block_device_mapping', meta, autoload=True) + + try: + fkeys = list(snapshots.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[snapshots.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).drop() + + fkeys = list(iscsi_targets.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[iscsi_targets.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).drop() + + fkeys = list(volume_metadata.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[volume_metadata.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).drop() + + fkeys = list(sm_volume.c.id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[sm_volume.c.id], + refcolumns=[volumes.c.id], + name=fkey_name).drop() + + except Exception: + LOG.error(_("Foreign Key constraint couldn't be removed")) + raise + + volumes.c.id.alter(Integer, primary_key=True, autoincrement=True) + volumes.c.snapshot_id.alter(Integer) + volume_metadata.c.volume_id.alter(Integer, nullable=False) + snapshots.c.id.alter(Integer, primary_key=True, autoincrement=True) + snapshots.c.volume_id.alter(Integer) + sm_volume.c.id.alter(Integer) + block_device_mapping.c.volume_id.alter(Integer) + block_device_mapping.c.snapshot_id.alter(Integer) + iscsi_targets.c.volume_id.alter(Integer, nullable=True) + + try: + fkeys = list(snapshots.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[snapshots.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).create() + + fkeys = list(iscsi_targets.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[iscsi_targets.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).create() + + fkeys = list(volume_metadata.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[volume_metadata.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).create() + + fkeys = list(sm_volume.c.id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[sm_volume.c.id], + refcolumns=[volumes.c.id], + name=fkey_name).create() + + # NOTE(jdg) Put the BDM foreign keys back in place + fkeys = list(block_device_mapping.c.volume_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[block_device_mapping.c.volume_id], + refcolumns=[volumes.c.id], + name=fkey_name).drop() + + fkeys = list(block_device_mapping.c.snapshot_id.foreign_keys) + if fkeys: + fkey_name = fkeys[0].constraint.name + ForeignKeyConstraint(columns=[block_device_mapping.c.snapshot_id], + refcolumns=[snapshots.c.id], + name=fkey_name).drop() + + except Exception: + LOG.error(_("Foreign Key constraint couldn't be removed")) + raise diff --git a/nova/db/sqlalchemy/migrate_repo/versions/090_sqlite_downgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/090_sqlite_downgrade.sql new file mode 100644 index 000000000..7d89da247 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/090_sqlite_downgrade.sql @@ -0,0 +1,226 @@ +BEGIN TRANSACTION; + + -- change id and snapshot_id datatypes in volumes table + CREATE TABLE volumes_backup( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + ec2_id INTEGER, + user_id VARCHAR(255), + project_id VARCHAR(255), + snapshot_id VARCHAR(255), + 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_backup 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; + DROP TABLE volumes; + ALTER TABLE volumes_backup RENAME TO volumes; + + -- change id and volume_id datatypes in snapshots table + CREATE TABLE snapshots_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + user_id VARCHAR(255), + project_id VARCHAR(255), + volume_id INTEGER, + status VARCHAR(255), + progress VARCHAR(255), + volume_size INTEGER, + display_name VARCHAR(255), + display_description VARCHAR(255), + PRIMARY KEY (id), + UNIQUE (id), + CHECK (deleted IN (0, 1)) + ); + INSERT INTO snapshots_backup SELECT + created_at, + updated_at, + deleted_at, + deleted, + id, + user_id, + project_id, + volume_id, + status, + progress, + volume_size, + display_name, + display_description + FROM snapshots; + DROP TABLE snapshots; + ALTER TABLE snapshots_backup RENAME TO snapshots; + + -- change id and volume_id datatypes in iscsi_targets table + CREATE TABLE iscsi_targets_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + target_num INTEGER, + host VARCHAR(255), + volume_id INTEGER, + PRIMARY KEY (id), + FOREIGN KEY(volume_id) REFERENCES volumes(id), + UNIQUE (id), + CHECK (deleted IN (0, 1)) + ); + INSERT INTO iscsi_targets_backup SELECT + created_at, + updated_at, + deleted_at, + deleted, + id, + target_num, + host, + volume_id + FROM iscsi_targets; + DROP TABLE iscsi_targets; + ALTER TABLE iscsi_targets_backup RENAME TO iscsi_targets; + + CREATE TABLE volume_metadata_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + key VARCHAR(255), + value VARCHAR(255), + volume_id INTEGER, + PRIMARY KEY (id), + FOREIGN KEY(volume_id) REFERENCES volumes(id), + UNIQUE (id), + CHECK (deleted IN (0, 1)) + ); + INSERT INTO volume_metadata_backup SELECT + created_at, + updated_at, + deleted_at, + deleted, + id, + key, + value, + volume_id + FROM volume_metadata; + DROP TABLE volume_metadata; + ALTER TABLE volume_metadata_backup RENAME TO volume_metadata; + + -- change volume_id and snapshot_id datatypes in bdm table + CREATE TABLE block_device_mapping_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + instance_uuid VARCHAR(36) NOT NULL, + device_name VARCHAR(255), + delete_on_termination BOOLEAN, + virtual_name VARCHAR(255), + snapshot_id INTEGER, + volume_id INTEGER, + volume_size INTEGER, + no_device BOOLEAN, + connection_info VARCHAR(255), + FOREIGN KEY(instance_uuid) REFERENCES instances(id), + FOREIGN KEY(volume_id) REFERENCES volumes(id), + FOREIGN KEY(snapshot_id) REFERENCES snapshots(id), + PRIMARY KEY (id), + UNIQUE (id), + CHECK (deleted IN (0, 1)) + ); + INSERT INTO block_device_mapping_backup SELECT + created_at, + updated_at, + deleted_at, + deleted, + id, + instance_uuid, + device_name, + delete_on_termination, + virtual_name, + snapshot_id, + volume_id, + volume_size, + no_device, + connection_info + FROM block_device_mapping; + DROP TABLE block_device_mapping; + ALTER TABLE block_device_mapping_backup RENAME TO block_device_mapping; + + -- change volume_id and sm_volume_table + CREATE TABLE sm_volume_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + backend_id INTEGER NOT NULL, + vdi_uuid VARCHAR(255), + PRIMARY KEY (id), + FOREIGN KEY(id) REFERENCES volumes(id), + UNIQUE (id), + CHECK (deleted IN (0,1)) + ); + INSERT INTO sm_volume_backup SELECT + created_at, + updated_at, + deleted_at, + deleted, + id, + backend_id, + vdi_uuid + FROM sm_volume; + DROP TABLE sm_volume; + ALTER TABLE sm_volume_backup RENAME TO sm_volume; + +COMMIT; diff --git a/nova/db/sqlalchemy/migrate_repo/versions/090_sqlite_upgrade.sql b/nova/db/sqlalchemy/migrate_repo/versions/090_sqlite_upgrade.sql new file mode 100644 index 000000000..53fbc69f6 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/090_sqlite_upgrade.sql @@ -0,0 +1,226 @@ +BEGIN TRANSACTION; + + -- change id and snapshot_id datatypes in 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, + 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, + 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; + DROP TABLE volumes; + ALTER TABLE volumes_backup RENAME TO volumes; + + -- change id and volume_id datatypes in snapshots table + CREATE TABLE snapshots_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id VARCHAR(36) NOT NULL, + user_id VARCHAR(255), + project_id VARCHAR(255), + volume_id VARCHAR(36), + status VARCHAR(255), + progress VARCHAR(255), + volume_size INTEGER, + display_name VARCHAR(255), + display_description VARCHAR(255), + PRIMARY KEY (id), + UNIQUE (id), + CHECK (deleted IN (0, 1)) + ); + INSERT INTO snapshots_backup SELECT + created_at, + updated_at, + deleted_at, + deleted, + id, + user_id, + project_id, + volume_id, + status, + progress, + volume_size, + display_name, + display_description + FROM snapshots; + DROP TABLE snapshots; + ALTER TABLE snapshots_backup RENAME TO snapshots; + + -- change id and volume_id datatypes in iscsi_targets table + CREATE TABLE iscsi_targets_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + target_num INTEGER, + host VARCHAR(255), + volume_id VARCHAR(36), + PRIMARY KEY (id), + FOREIGN KEY(volume_id) REFERENCES volumes(id), + UNIQUE (id), + CHECK (deleted IN (0, 1)) + ); + INSERT INTO iscsi_targets_backup SELECT + created_at, + updated_at, + deleted_at, + deleted, + id, + target_num, + host, + volume_id + FROM iscsi_targets; + DROP TABLE iscsi_targets; + ALTER TABLE iscsi_targets_backup RENAME TO iscsi_targets; + + CREATE TABLE volume_metadata_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + key VARCHAR(255), + value VARCHAR(255), + volume_id VARCHAR(36), + PRIMARY KEY (id), + FOREIGN KEY(volume_id) REFERENCES volumes(id), + UNIQUE (id), + CHECK (deleted IN (0, 1)) + ); + INSERT INTO volume_metadata_backup SELECT + created_at, + updated_at, + deleted_at, + deleted, + id, + key, + value, + volume_id + FROM volume_metadata; + DROP TABLE volume_metadata; + ALTER TABLE volume_metadata_backup RENAME TO volume_metadata; + + -- change volume_id and snapshot_id datatypes in bdm table + CREATE TABLE block_device_mapping_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id INTEGER NOT NULL, + instance_uuid VARCHAR(36) NOT NULL, + device_name VARCHAR(255), + delete_on_termination BOOLEAN, + virtual_name VARCHAR(255), + snapshot_id VARCHAR(36), + volume_id VARCHAR(36), + volume_size INTEGER, + no_device BOOLEAN, + connection_info VARCHAR(255), + FOREIGN KEY(instance_uuid) REFERENCES instances(id), + FOREIGN KEY(volume_id) REFERENCES volumes(id), + FOREIGN KEY(snapshot_id) REFERENCES snapshots(id), + PRIMARY KEY (id), + UNIQUE (id), + CHECK (deleted IN (0, 1)) + ); + INSERT INTO block_device_mapping_backup SELECT + created_at, + updated_at, + deleted_at, + deleted, + id, + instance_uuid, + device_name, + delete_on_termination, + virtual_name, + snapshot_id, + volume_id, + volume_size, + no_device, + connection_info + FROM block_device_mapping; + DROP TABLE block_device_mapping; + ALTER TABLE block_device_mapping_backup RENAME TO block_device_mapping; + + -- change volume_id and sm_volume_table + CREATE TABLE sm_volume_backup ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + id VARCHAR(36) NOT NULL, + backend_id INTEGER NOT NULL, + vdi_uuid VARCHAR(255), + PRIMARY KEY (id), + FOREIGN KEY(id) REFERENCES volumes(id), + UNIQUE (id), + CHECK (deleted IN (0,1)) + ); + INSERT INTO sm_volume_backup SELECT + created_at, + updated_at, + deleted_at, + deleted, + id, + backend_id, + vdi_uuid + FROM sm_volume; + DROP TABLE sm_volume; + ALTER TABLE sm_volume_backup RENAME TO sm_volume; + +COMMIT; diff --git a/nova/db/sqlalchemy/migrate_repo/versions/091_convert_volume_ids_to_uuid.py b/nova/db/sqlalchemy/migrate_repo/versions/091_convert_volume_ids_to_uuid.py new file mode 100644 index 000000000..5d0440ffe --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/091_convert_volume_ids_to_uuid.py @@ -0,0 +1,145 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 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 MetaData, select, Table +from nova import log as logging + +LOG = logging.getLogger(__name__) + + +def upgrade(migrate_engine): + """Convert volume and snapshot id columns from int to varchar.""" + meta = MetaData() + meta.bind = migrate_engine + + volumes = Table('volumes', meta, autoload=True) + snapshots = Table('snapshots', meta, autoload=True) + iscsi_targets = Table('iscsi_targets', meta, autoload=True) + volume_metadata = Table('volume_metadata', meta, autoload=True) + block_device_mapping = Table('block_device_mapping', meta, autoload=True) + sm_volumes = Table('sm_volume', meta, autoload=True) + + volume_mappings = Table('volume_id_mappings', meta, autoload=True) + snapshot_mappings = Table('snapshot_id_mappings', meta, autoload=True) + + volume_list = list(volumes.select().execute()) + for v in volume_list: + new_id = select([volume_mappings.c.uuid], + volume_mappings.c.id == v['id']) + + volumes.update().\ + where(volumes.c.id == v['id']).\ + values(id=new_id).execute() + + sm_volumes.update().\ + where(sm_volumes.c.id == v['id']).\ + values(id=new_id).execute() + + snapshots.update().\ + where(snapshots.c.volume_id == v['id']).\ + values(volume_id=new_id).execute() + + iscsi_targets.update().\ + where(iscsi_targets.c.volume_id == v['id']).\ + values(volume_id=new_id).execute() + + volume_metadata.update().\ + where(volume_metadata.c.volume_id == v['id']).\ + values(volume_id=new_id).execute() + + block_device_mapping.update().\ + where(block_device_mapping.c.volume_id == v['id']).\ + values(volume_id=new_id).execute() + + snapshot_list = list(snapshots.select().execute()) + for s in snapshot_list: + new_id = select([snapshot_mappings.c.uuid], + volume_mappings.c.id == s['id']) + + volumes.update().\ + where(volumes.c.snapshot_id == s['id']).\ + values(snapshot_id=new_id).execute() + + snapshots.update().\ + where(snapshots.c.id == s['id']).\ + values(volume_id=new_id).execute() + + block_device_mapping.update().\ + where(block_device_mapping.c.snapshot_id == s['id']).\ + values(snapshot_id=new_id).execute() + + +def downgrade(migrate_engine): + """Convert volume and snapshot id columns back to int.""" + meta = MetaData() + meta.bind = migrate_engine + + volumes = Table('volumes', meta, autoload=True) + snapshots = Table('snapshots', meta, autoload=True) + iscsi_targets = Table('iscsi_targets', meta, autoload=True) + volume_metadata = Table('volume_metadata', meta, autoload=True) + block_device_mapping = Table('block_device_mapping', meta, autoload=True) + sm_volumes = Table('sm_volume', meta, autoload=True) + + volume_mappings = Table('volume_id_mappings', meta, autoload=True) + snapshot_mappings = Table('snapshot_id_mappings', meta, autoload=True) + + volume_list = list(volumes.select().execute()) + for v in volume_list: + new_id = select([volume_mappings.c.id], + volume_mappings.c.uuid == v['id']) + + volumes.update().\ + where(volumes.c.id == v['id']).\ + values(id=new_id).execute() + + sm_volumes.update().\ + where(sm_volumes.c.id == v['id']).\ + values(id=new_id).execute() + + snapshots.update().\ + where(snapshots.c.volume_id == v['id']).\ + values(volume_id=new_id).execute() + + iscsi_targets.update().\ + where(iscsi_targets.c.volume_id == v['id']).\ + values(volume_id=new_id).execute() + + volume_metadata.update().\ + where(volume_metadata.c.volume_id == v['id']).\ + values(volume_id=new_id).execute() + + block_device_mapping.update().\ + where(block_device_mapping.c.volume_id == v['id']).\ + values(volume_id=new_id).execute() + + snapshot_list = list(snapshots.select().execute()) + for s in snapshot_list: + new_id = select([snapshot_mappings.c.id], + volume_mappings.c.uuid == s['id']) + + volumes.update().\ + where(volumes.c.snapshot_id == s['id']).\ + values(snapshot_id=new_id).execute() + + snapshots.update().\ + where(snapshots.c.id == s['id']).\ + values(volume_id=new_id).execute() + + block_device_mapping.update().\ + where(block_device_mapping.c.snapshot_id == s['id']).\ + values(snapshot_id=new_id).execute() diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 1544629ff..e4e47c882 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -335,16 +335,17 @@ class InstanceTypes(BASE, NovaBase): class Volume(BASE, NovaBase): """Represents a block storage device that can be attached to a vm.""" __tablename__ = 'volumes' - id = Column(Integer, primary_key=True, autoincrement=True) + id = Column(String(36), primary_key=True) @property def name(self): return FLAGS.volume_name_template % self.id + ec2_id = Column(Integer) user_id = Column(String(255)) project_id = Column(String(255)) - snapshot_id = Column(String(255)) + snapshot_id = Column(String(36)) host = Column(String(255)) # , ForeignKey('hosts.id')) size = Column(Integer) @@ -379,7 +380,7 @@ class VolumeMetadata(BASE, NovaBase): id = Column(Integer, primary_key=True) key = Column(String(255)) value = Column(String(255)) - volume_id = Column(Integer, ForeignKey('volumes.id'), nullable=False) + volume_id = Column(String(36), ForeignKey('volumes.id'), nullable=False) volume = relationship(Volume, backref="volume_metadata", foreign_keys=volume_id, primaryjoin='and_(' @@ -455,7 +456,7 @@ class QuotaClass(BASE, NovaBase): class Snapshot(BASE, NovaBase): """Represents a block storage device that can be attached to a vm.""" __tablename__ = 'snapshots' - id = Column(Integer, primary_key=True, autoincrement=True) + id = Column(String(36), primary_key=True) @property def name(self): @@ -468,7 +469,7 @@ class Snapshot(BASE, NovaBase): user_id = Column(String(255)) project_id = Column(String(255)) - volume_id = Column(Integer) + volume_id = Column(String(36)) status = Column(String(255)) progress = Column(String(255)) volume_size = Column(Integer) @@ -504,12 +505,12 @@ class BlockDeviceMapping(BASE, NovaBase): virtual_name = Column(String(255), nullable=True) # for snapshot or volume - snapshot_id = Column(Integer, ForeignKey('snapshots.id'), nullable=True) + snapshot_id = Column(String(36), ForeignKey('snapshots.id')) # outer join snapshot = relationship(Snapshot, foreign_keys=snapshot_id) - volume_id = Column(Integer, ForeignKey('volumes.id'), nullable=True) + volume_id = Column(String(36), ForeignKey('volumes.id'), nullable=True) volume = relationship(Volume, foreign_keys=volume_id) volume_size = Column(Integer, nullable=True) @@ -528,7 +529,7 @@ class IscsiTarget(BASE, NovaBase): id = Column(Integer, primary_key=True) target_num = Column(Integer) host = Column(String(255)) - volume_id = Column(Integer, ForeignKey('volumes.id'), nullable=True) + volume_id = Column(String(36), ForeignKey('volumes.id'), nullable=True) volume = relationship(Volume, backref=backref('iscsi_target', uselist=False), foreign_keys=volume_id, @@ -962,6 +963,20 @@ class S3Image(BASE, NovaBase): uuid = Column(String(36), nullable=False) +class VolumeIdMapping(BASE, NovaBase): + """Compatability layer for the EC2 volume service""" + __tablename__ = 'volume_id_mappings' + id = Column(Integer, primary_key=True, nullable=False, autoincrement=True) + uuid = Column(String(36), nullable=False) + + +class SnapshotIdMapping(BASE, NovaBase): + """Compatability layer for the EC2 snapshot service""" + __tablename__ = 'snapshot_id_mappings' + id = Column(Integer, primary_key=True, nullable=False, autoincrement=True) + uuid = Column(String(36), nullable=False) + + class SMFlavors(BASE, NovaBase): """Represents a flavor for SM volumes.""" __tablename__ = 'sm_flavors' @@ -982,7 +997,7 @@ class SMBackendConf(BASE, NovaBase): class SMVolume(BASE, NovaBase): __tablename__ = 'sm_volume' - id = Column(Integer(), ForeignKey(Volume.id), primary_key=True) + id = Column(String(36), ForeignKey(Volume.id), primary_key=True) backend_id = Column(Integer, ForeignKey('sm_backend_config.id'), nullable=False) vdi_uuid = Column(String(255)) @@ -1040,6 +1055,8 @@ def register_models(): VolumeMetadata, VolumeTypeExtraSpecs, VolumeTypes, + VolumeIdMapping, + SnapshotIdMapping, ) engine = create_engine(FLAGS.sql_connection, echo=False) for model in models: |
