From f180025669257ed209959d5a95ddb3c0efac0165 Mon Sep 17 00:00:00 2001 From: zhangbailin Date: Mon, 24 Sep 2018 02:13:52 -0400 Subject: [PATCH] Add volume_type field to BlockDeviceMapping object Add ``volume_type`` field to BlockDeviceMapping object to be used in new instance block_device_mapping_v2.volume_type API extension. Implements: blueprint boot-instance-specific-storage-backend Change-Id: I66ff84585642ff8d40ecfefef3342349eb49f83d --- nova/block_device.py | 2 +- .../versions/391_add_volume_type_to_bdm.py | 32 +++++++++++++++++++ nova/db/sqlalchemy/models.py | 2 ++ nova/objects/block_device.py | 7 +++- nova/tests/unit/compute/test_compute_api.py | 8 ++--- nova/tests/unit/db/test_migrations.py | 5 +++ nova/tests/unit/objects/test_block_device.py | 11 +++++++ nova/tests/unit/objects/test_objects.py | 2 +- 8 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 nova/db/sqlalchemy/migrate_repo/versions/391_add_volume_type_to_bdm.py diff --git a/nova/block_device.py b/nova/block_device.py index 11644c1c684f..7ff3cde3b9ce 100644 --- a/nova/block_device.py +++ b/nova/block_device.py @@ -45,7 +45,7 @@ bdm_new_fields = set(['source_type', 'destination_type', 'guest_format', 'device_type', 'disk_bus', 'boot_index', 'device_name', 'delete_on_termination', 'snapshot_id', 'volume_id', 'volume_size', 'image_id', 'no_device', - 'connection_info', 'tag']) + 'connection_info', 'tag', 'volume_type']) bdm_db_only_fields = set(['id', 'instance_uuid', 'attachment_id', 'uuid']) diff --git a/nova/db/sqlalchemy/migrate_repo/versions/391_add_volume_type_to_bdm.py b/nova/db/sqlalchemy/migrate_repo/versions/391_add_volume_type_to_bdm.py new file mode 100644 index 000000000000..ac9132888313 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/391_add_volume_type_to_bdm.py @@ -0,0 +1,32 @@ +# 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 Column +from sqlalchemy import MetaData +from sqlalchemy import String +from sqlalchemy import Table + + +BASE_TABLE_NAME = 'block_device_mapping' +NEW_COLUMN_NAME = 'volume_type' + + +def upgrade(migrate_engine): + meta = MetaData() + meta.bind = migrate_engine + + for prefix in ('', 'shadow_'): + table = Table(prefix + BASE_TABLE_NAME, meta, autoload=True) + new_column = Column(NEW_COLUMN_NAME, String(255), nullable=True) + if not hasattr(table.c, NEW_COLUMN_NAME): + table.create_column(new_column) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index acbf7d0ed67a..7d8f7ec152b7 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -627,6 +627,8 @@ class BlockDeviceMapping(BASE, NovaBase, models.SoftDeleteMixin): volume_id = Column(String(36)) volume_size = Column(Integer) + volume_type = Column(String(255)) + image_id = Column(String(36)) # for no device to suppress devices. diff --git a/nova/objects/block_device.py b/nova/objects/block_device.py index 180dc0618657..a2cbc9c92ab5 100644 --- a/nova/objects/block_device.py +++ b/nova/objects/block_device.py @@ -69,7 +69,8 @@ class BlockDeviceMapping(base.NovaPersistentObject, base.NovaObject, # Version 1.17: Added tag field # Version 1.18: Added attachment_id # Version 1.19: Added uuid - VERSION = '1.19' + # Version 1.20: Added volume_type + VERSION = '1.20' fields = { 'id': fields.IntegerField(), @@ -93,10 +94,14 @@ class BlockDeviceMapping(base.NovaPersistentObject, base.NovaObject, 'connection_info': fields.SensitiveStringField(nullable=True), 'tag': fields.StringField(nullable=True), 'attachment_id': fields.UUIDField(nullable=True), + # volume_type field can be a volume type name or ID(UUID). + 'volume_type': fields.StringField(nullable=True), } def obj_make_compatible(self, primitive, target_version): target_version = versionutils.convert_version_to_tuple(target_version) + if target_version < (1, 20) and 'volume_type' in primitive: + del primitive['volume_type'] if target_version < (1, 19) and 'uuid' in primitive: del primitive['uuid'] if target_version < (1, 18) and 'attachment_id' in primitive: diff --git a/nova/tests/unit/compute/test_compute_api.py b/nova/tests/unit/compute/test_compute_api.py index d66dda2b3cc7..1b5af23c6c2a 100644 --- a/nova/tests/unit/compute/test_compute_api.py +++ b/nova/tests/unit/compute/test_compute_api.py @@ -3214,7 +3214,7 @@ class _ComputeAPIUnitTestMixIn(object): 'device_type': None, 'snapshot_id': '1-snapshot', 'device_name': '/dev/vda', 'destination_type': 'volume', 'delete_on_termination': False, - 'tag': None}) + 'tag': None, 'volume_type': None}) limits_patcher = mock.patch.object( self.compute_api.volume_api, 'get_absolute_limits', @@ -3258,14 +3258,14 @@ class _ComputeAPIUnitTestMixIn(object): 'disk_bus': 'ide', 'device_name': '/dev/vdf', 'delete_on_termination': True, 'snapshot_id': 'snapshot-2', 'volume_id': None, 'volume_size': 100, 'image_id': None, - 'no_device': None}])[:255]) + 'no_device': None, 'volume_type': None}])[:255]) bdm = fake_block_device.FakeDbBlockDeviceDict( {'no_device': False, 'volume_id': None, 'boot_index': -1, 'connection_info': 'inf', 'device_name': '/dev/vdh', 'source_type': 'blank', 'destination_type': 'local', 'guest_format': 'swap', 'delete_on_termination': True, - 'tag': None}) + 'tag': None, 'volume_type': None}) instance_bdms.append(bdm) # The non-volume image mapping will go at the front of the list # because the volume BDMs are processed separately. @@ -3276,7 +3276,7 @@ class _ComputeAPIUnitTestMixIn(object): 'device_type': None, 'snapshot_id': None, 'device_name': '/dev/vdh', 'destination_type': 'local', 'delete_on_termination': True, - 'tag': None}) + 'tag': None, 'volume_type': None}) quiesced = [False, False] diff --git a/nova/tests/unit/db/test_migrations.py b/nova/tests/unit/db/test_migrations.py index a29f178b5ad2..435cd2ca9e50 100644 --- a/nova/tests/unit/db/test_migrations.py +++ b/nova/tests/unit/db/test_migrations.py @@ -1009,6 +1009,11 @@ class NovaMigrationsCheckers(test_migrations.ModelsMigrationsSync, self.assertColumnExists(engine, 'shadow_instance_extra', 'trusted_certs') + def _check_391(self, engine, data): + self.assertColumnExists(engine, 'block_device_mapping', 'volume_type') + self.assertColumnExists(engine, 'shadow_block_device_mapping', + 'volume_type') + class TestNovaMigrationsSQLite(NovaMigrationsCheckers, test_base.DbTestCase, diff --git a/nova/tests/unit/objects/test_block_device.py b/nova/tests/unit/objects/test_block_device.py index 820f95fb18ed..9339600351fe 100644 --- a/nova/tests/unit/objects/test_block_device.py +++ b/nova/tests/unit/objects/test_block_device.py @@ -425,6 +425,17 @@ class _TestBlockDeviceMappingObject(object): self.assertNotIn('uuid', primitive) self.assertIn('volume_id', primitive) + def test_obj_make_compatible_pre_1_20(self): + values = {'source_type': 'volume', 'volume_id': 'fake-vol-id', + 'destination_type': 'volume', + 'instance_uuid': uuids.instance, + 'volume_type': 'fake-lvm-1'} + bdm = objects.BlockDeviceMapping(context=self.context, **values) + data = lambda x: x['nova_object.data'] + primitive = data(bdm.obj_to_primitive(target_version='1.19')) + self.assertNotIn('volume_type', primitive) + self.assertIn('volume_id', primitive) + class TestBlockDeviceMappingUUIDMigration(test.TestCase): def setUp(self): diff --git a/nova/tests/unit/objects/test_objects.py b/nova/tests/unit/objects/test_objects.py index 61f6feb3f3fc..178d8da43812 100644 --- a/nova/tests/unit/objects/test_objects.py +++ b/nova/tests/unit/objects/test_objects.py @@ -1059,7 +1059,7 @@ object_data = { 'AggregateList': '1.3-3ea55a050354e72ef3306adefa553957', 'BandwidthUsage': '1.2-c6e4c779c7f40f2407e3d70022e3cd1c', 'BandwidthUsageList': '1.2-5fe7475ada6fe62413cbfcc06ec70746', - 'BlockDeviceMapping': '1.19-407e75274f48e60a76e56283333c9dbc', + 'BlockDeviceMapping': '1.20-45a6ad666ddf14bbbedece2293af77e2', 'BlockDeviceMappingList': '1.17-1e568eecb91d06d4112db9fd656de235', 'BuildRequest': '1.3-077dee42bed93f8a5b62be77657b7152', 'BuildRequestList': '1.0-cd95608eccb89fbc702c8b52f38ec738',