]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Added UUID as primary key for Encryption model
authorIvan Kolodyazhny <e0ne@e0ne.info>
Thu, 4 Sep 2014 10:24:12 +0000 (13:24 +0300)
committerIvan Kolodyazhny <e0ne@e0ne.info>
Tue, 16 Dec 2014 21:21:33 +0000 (21:21 +0000)
volume_type_id could not be used as PK because
soft deletes are using for Encryption

Change-Id: I5c4423da4442f2458417dc3492750412a74ebe09
Closes-Bug: #1316540

cinder/db/sqlalchemy/api.py
cinder/db/sqlalchemy/migrate_repo/versions/033_add_encryption_unique_key.py [new file with mode: 0644]
cinder/db/sqlalchemy/migrate_repo/versions/033_sqlite_downgrade.sql [new file with mode: 0644]
cinder/db/sqlalchemy/models.py
cinder/tests/test_db_api.py
cinder/tests/test_migrations.py
cinder/tests/test_volume_types.py

index c53bdbb6c131b42f10f80fa45ed9a9d72f53c963..ee23660e60c026ad57929341f1d52e714be08292 100644 (file)
@@ -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 (file)
index 0000000..5075e65
--- /dev/null
@@ -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 (file)
index 0000000..996f6cd
--- /dev/null
@@ -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;
index 7acb4cacfc48d4fee84ac1fbb092e4df75b3abae..c7a53efd089460cd85a843f5881df2ddd4db659b 100644 (file)
@@ -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",
index 899a4bc1358c288a8fcd1856f3a4a4d37cdc555a..5b402c14b239c8a2df4c628db2ac0b56fb06d1e1 100644 (file)
@@ -814,6 +814,7 @@ class DBAPIEncryptionTestCase(BaseTest):
         'deleted_at',
         'created_at',
         'updated_at',
+        'encryption_id',
     ]
 
     def setUp(self):
index 0880915bd38e59cd2591f127221e187633c0fec5..94a1d96eda809947543bc7f7eb85f79a5eb15ad5 100644 (file)
@@ -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)
 
index 59562acacba2f02a3a45ec89d1eb367d4947fde5..3fc7c779ba1754506df2fdd7c0a5ae704f69aef9 100644 (file)
@@ -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")