From: Ivan Kolodyazhny Date: Thu, 4 Sep 2014 10:24:12 +0000 (+0300) Subject: Added UUID as primary key for Encryption model X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=a21979c1230edb80a08b3bdd6083b2b088cc6aa1;p=openstack-build%2Fcinder-build.git Added UUID as primary key for Encryption model volume_type_id could not be used as PK because soft deletes are using for Encryption Change-Id: I5c4423da4442f2458417dc3492750412a74ebe09 Closes-Bug: #1316540 --- diff --git a/cinder/db/sqlalchemy/api.py b/cinder/db/sqlalchemy/api.py index c53bdbb6c..ee23660e6 100644 --- a/cinder/db/sqlalchemy/api.py +++ b/cinder/db/sqlalchemy/api.py @@ -32,6 +32,7 @@ from oslo.db import options from oslo.db.sqlalchemy import session as db_session from oslo.utils import timeutils import osprofiler.sqlalchemy +import six import sqlalchemy from sqlalchemy import or_ from sqlalchemy.orm import joinedload, joinedload_all @@ -2597,6 +2598,9 @@ def volume_type_encryption_create(context, volume_type_id, values): if 'volume_type_id' not in values: values['volume_type_id'] = volume_type_id + if 'encryption_id' not in values: + values['encryption_id'] = six.text_type(uuid.uuid4()) + encryption.update(values) session.add(encryption) diff --git a/cinder/db/sqlalchemy/migrate_repo/versions/033_add_encryption_unique_key.py b/cinder/db/sqlalchemy/migrate_repo/versions/033_add_encryption_unique_key.py new file mode 100644 index 000000000..5075e65b8 --- /dev/null +++ b/cinder/db/sqlalchemy/migrate_repo/versions/033_add_encryption_unique_key.py @@ -0,0 +1,79 @@ +# 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. + +import uuid + +from migrate import PrimaryKeyConstraint, ForeignKeyConstraint +from sqlalchemy import Column +from sqlalchemy import MetaData, String, Table + + +def upgrade(migrate_engine): + """Add UUID primary key column to encryption.""" + meta = MetaData() + meta.bind = migrate_engine + + encryptions = Table('encryption', meta, autoload=True) + + encryption_id = Column('encryption_id', String(36)) + encryptions.create_column(encryption_id) + + encryption_items = list(encryptions.select().execute()) + + for item in encryption_items: + encryptions.update().\ + where(encryptions.c.volume_type_id == item['volume_type_id']).\ + values(encryption_id=str(uuid.uuid4())).execute() + + # NOTE (e0ne): need to drop FK first for MySQL + if migrate_engine.name == 'mysql': + ref_table = Table('volume_types', meta, autoload=True) + params = {'columns': [encryptions.c['volume_type_id']], + 'refcolumns': [ref_table.c['id']], + 'name': 'encryption_ibfk_1'} + volume_type_fk = ForeignKeyConstraint(**params) + volume_type_fk.drop() + + try: + volume_type_pk = PrimaryKeyConstraint('volume_type_id', + table=encryptions) + volume_type_pk.drop() + except Exception: + # NOTE (e0ne): SQLite doesn't support 'drop constraint' statament + if migrate_engine.url.get_dialect().name.startswith('sqlite'): + pass + else: + raise + + pkey = PrimaryKeyConstraint(encryptions.columns.encryption_id) + pkey.create() + + +def downgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + encryptions = Table('encryption', meta, autoload=True) + encryption_id_pk = PrimaryKeyConstraint(encryptions.columns.encryption_id) + + encryption_id_pk.drop() + encryptions.drop_column(encryptions.columns.encryption_id) + + volume_type_pk = PrimaryKeyConstraint(encryptions.columns.volume_type_id) + volume_type_pk.create() + + ref_table = Table('volume_types', meta, autoload=True) + params = {'columns': [encryptions.c['volume_type_id']], + 'refcolumns': [ref_table.c['id']], + 'name': 'encryption_ibfk_1'} + volume_type_fk = ForeignKeyConstraint(**params) + volume_type_fk.create() diff --git a/cinder/db/sqlalchemy/migrate_repo/versions/033_sqlite_downgrade.sql b/cinder/db/sqlalchemy/migrate_repo/versions/033_sqlite_downgrade.sql new file mode 100644 index 000000000..996f6cd8b --- /dev/null +++ b/cinder/db/sqlalchemy/migrate_repo/versions/033_sqlite_downgrade.sql @@ -0,0 +1,28 @@ +CREATE TABLE encryption_v32 ( + created_at DATETIME, + updated_at DATETIME, + deleted_at DATETIME, + deleted BOOLEAN, + cipher VARCHAR(255), + control_location VARCHAR(255), + key_size INTEGER, + provider VARCHAR(255), + volume_type_id VARCHAR(36), + PRIMARY KEY (volume_type_id), + FOREIGN KEY(volume_type_id) REFERENCES volume_types(id) +); + +INSERT INTO encryption_v32 + SELECT created_at, + updated_at, + deleted_at, + deleted, + cipher, + control_location, + key_size, + provider, + volume_type_id + FROM encryption; + +DROP TABLE encryption; +ALTER TABLE encryption_v32 RENAME TO encryption; diff --git a/cinder/db/sqlalchemy/models.py b/cinder/db/sqlalchemy/models.py index 7acb4cacf..c7a53efd0 100644 --- a/cinder/db/sqlalchemy/models.py +++ b/cinder/db/sqlalchemy/models.py @@ -512,13 +512,12 @@ class Encryption(BASE, CinderBase): """ __tablename__ = 'encryption' + encryption_id = Column(String(36), primary_key=True) cipher = Column(String(255)) key_size = Column(Integer) provider = Column(String(255)) control_location = Column(String(255)) - volume_type_id = Column(String(36), - ForeignKey('volume_types.id'), - primary_key=True) + volume_type_id = Column(String(36), ForeignKey('volume_types.id')) volume_type = relationship( VolumeTypes, backref="encryption", diff --git a/cinder/tests/test_db_api.py b/cinder/tests/test_db_api.py index 899a4bc13..5b402c14b 100644 --- a/cinder/tests/test_db_api.py +++ b/cinder/tests/test_db_api.py @@ -814,6 +814,7 @@ class DBAPIEncryptionTestCase(BaseTest): 'deleted_at', 'created_at', 'updated_at', + 'encryption_id', ] def setUp(self): diff --git a/cinder/tests/test_migrations.py b/cinder/tests/test_migrations.py index 0880915bd..94a1d96ed 100644 --- a/cinder/tests/test_migrations.py +++ b/cinder/tests/test_migrations.py @@ -681,6 +681,19 @@ class MigrationsMixin(test_migrations.WalkVersionsMixin): volume_types = db_utils.get_table(engine, 'volume_types') self.assertNotIn('is_public', volume_types.c) + def _check_033(self, engine, data): + """Test adding encryption_id column to encryption table.""" + encryptions = db_utils.get_table(engine, 'encryption') + self.assertIsInstance(encryptions.c.encryption_id.type, + sqlalchemy.types.VARCHAR) + + def _post_downgrade_033(self, engine): + metadata = sqlalchemy.schema.MetaData() + metadata.bind = engine + + encryptions = db_utils.get_table(engine, 'encryption') + self.assertNotIn('encryption_id', encryptions.c) + def test_walk_versions(self): self.walk_versions(True, False) diff --git a/cinder/tests/test_volume_types.py b/cinder/tests/test_volume_types.py index 59562acac..3fc7c779b 100644 --- a/cinder/tests/test_volume_types.py +++ b/cinder/tests/test_volume_types.py @@ -320,9 +320,11 @@ class VolumeTypeTestCase(test.TestCase): # And add encryption for good measure enc_keyvals1 = {'cipher': 'c1', 'key_size': 256, 'provider': 'p1', - 'control_location': 'front-end'} + 'control_location': 'front-end', + 'encryption_id': 'uuid1'} enc_keyvals2 = {'cipher': 'c1', 'key_size': 128, 'provider': 'p1', - 'control_location': 'front-end'} + 'control_location': 'front-end', + 'encryption_id': 'uuid2'} db.volume_type_encryption_create(self.ctxt, type_ref1['id'], enc_keyvals1) db.volume_type_encryption_create(self.ctxt, type_ref2['id'], @@ -350,7 +352,19 @@ class VolumeTypeTestCase(test.TestCase): 'control_location': (None, 'front-end'), 'deleted': (None, False), 'key_size': (None, 256), - 'provider': (None, 'p1')}) + 'provider': (None, 'p1'), + 'encryption_id': (None, 'uuid1')}) + + def test_encryption_create(self): + volume_type = volume_types.create(self.ctxt, "type1") + volume_type_id = volume_type.get('id') + encryption = { + 'control_location': 'front-end', + 'provider': 'fake_provider', + } + db_api.volume_type_encryption_create(self.ctxt, volume_type_id, + encryption) + self.assertTrue(volume_types.is_encrypted(self.ctxt, volume_type_id)) def test_get_volume_type_encryption(self): volume_type = volume_types.create(self.ctxt, "type1")