Browse Source

libvirt: fix native luks encryption failure to find volume_id

Not all volume types put a 'volume_id' entry in the
connection_info['data'] dict. This change uses a new
utility method to look up the volume_id in the connection_info
data dict and if not found there, uses the 'serial' value
from the connection_info, which we know at least gets set
when the DriverVolumeBlockDevice code attaches the volume.

This also has to update pre_live_migration since the connection_info
dict doesn't have a 'volume_id' key in it. It's unclear what
this code was expecting, or if it ever really worked, but since
an attached volume represented by a BlockDeviceMapping here has
a volume_id attribute, we can just use that. As that code path
was never tested, this updates related unit tests and refactors
the tests to actually use the type of DriverVolumeBlockDevice
objects that the ComputeManager would be sending down to the
driver pre_live_migration method. The hard-to-read squashed
dicts in the tests are also re-formatted so a human can actually
read them.

Change-Id: Ie02d298cd92d5b5ebcbbcd2b0e8be01f197bfafb
Closes-Bug: #1746609
tags/17.0.0.0rc1
Matt Riedemann 1 year ago
parent
commit
cafe3d066e

+ 116
- 35
nova/tests/unit/virt/libvirt/test_driver.py View File

@@ -7127,6 +7127,40 @@ class LibvirtConnTestCase(test.NoDBTestCase,
7127 7127
         mock_encryptor.attach_volume.assert_called_once_with(self.context,
7128 7128
                                                              **encryption)
7129 7129
 
7130
+    @mock.patch.object(key_manager, 'API')
7131
+    @mock.patch('os_brick.encryptors.get_encryption_metadata')
7132
+    @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
7133
+    def test_attach_encryptor_encrypted_native_luks_serial(self,
7134
+            mock_get_encryptor, mock_get_metadata, mock_get_key_mgr):
7135
+        """Uses native luks encryption with a provider encryptor and the
7136
+        connection_info has a serial but not volume_id in the 'data'
7137
+        sub-dict.
7138
+        """
7139
+        drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
7140
+        mock_encryptor = mock.MagicMock()
7141
+        mock_get_encryptor.return_value = mock_encryptor
7142
+        encryption = {'provider': 'luks', 'control_location': 'front-end',
7143
+                      'encryption_key_id': uuids.encryption_key_id}
7144
+        connection_info = {'serial': uuids.serial, 'data': {}}
7145
+        # Mock out the key manager
7146
+        key = u'3734363537333734'
7147
+        key_encoded = binascii.unhexlify(key)
7148
+        mock_key = mock.Mock()
7149
+        mock_key_mgr = mock.Mock()
7150
+        mock_get_key_mgr.return_value = mock_key_mgr
7151
+        mock_key_mgr.get.return_value = mock_key
7152
+        mock_key.get_encoded.return_value = key_encoded
7153
+
7154
+        with mock.patch.object(drvr, '_use_native_luks', return_value=True):
7155
+            with mock.patch.object(drvr._host, 'create_secret') as crt_scrt:
7156
+                drvr._attach_encryptor(self.context, connection_info,
7157
+                                       encryption, allow_native_luks=True)
7158
+
7159
+        mock_get_metadata.assert_not_called()
7160
+        mock_get_encryptor.assert_not_called()
7161
+        crt_scrt.assert_called_once_with(
7162
+            'volume', uuids.serial, password=key)
7163
+
7130 7164
     @mock.patch('os_brick.encryptors.get_encryption_metadata')
7131 7165
     @mock.patch('nova.virt.libvirt.driver.LibvirtDriver._get_volume_encryptor')
7132 7166
     def test_detach_encryptor_connection_info_incomplete(self,
@@ -10473,15 +10507,42 @@ class LibvirtConnTestCase(test.NoDBTestCase,
10473 10507
             target_ret=None, src_supports_native_luks=True,
10474 10508
             dest_supports_native_luks=True, allow_native_luks=True):
10475 10509
         # Creating testdata
10476
-        vol = {'block_device_mapping': [
10477
-           {'connection_info': {'serial': '12345', u'data':
10478
-            {'device_path':
10479
-             u'/dev/disk/by-path/ip-1.2.3.4:3260-iqn.abc.12345.opst-lun-X'}},
10480
-             'mount_device': '/dev/sda'},
10481
-           {'connection_info': {'serial': '67890', u'data':
10482
-            {'device_path':
10483
-             u'/dev/disk/by-path/ip-1.2.3.4:3260-iqn.cde.67890.opst-lun-Z'}},
10484
-             'mount_device': '/dev/sdb'}]}
10510
+        c = context.get_admin_context()
10511
+        instance = objects.Instance(root_device_name='/dev/vda',
10512
+                                    **self.test_instance)
10513
+        bdms = objects.BlockDeviceMappingList(objects=[
10514
+            fake_block_device.fake_bdm_object(c, {
10515
+                'connection_info': jsonutils.dumps({
10516
+                    'serial': '12345',
10517
+                    'data': {
10518
+                        'device_path': '/dev/disk/by-path/ip-1.2.3.4:3260'
10519
+                                       '-iqn.abc.12345.opst-lun-X'
10520
+                    }
10521
+                }),
10522
+                'device_name': '/dev/sda',
10523
+                'volume_id': uuids.volume1,
10524
+                'source_type': 'volume',
10525
+                'destination_type': 'volume'
10526
+            }),
10527
+            fake_block_device.fake_bdm_object(c, {
10528
+                'connection_info': jsonutils.dumps({
10529
+                    'serial': '67890',
10530
+                    'data': {
10531
+                        'device_path': '/dev/disk/by-path/ip-1.2.3.4:3260'
10532
+                                       '-iqn.cde.67890.opst-lun-Z'
10533
+                    }
10534
+                }),
10535
+                'device_name': '/dev/sdb',
10536
+                'volume_id': uuids.volume2,
10537
+                'source_type': 'volume',
10538
+                'destination_type': 'volume'
10539
+            })
10540
+        ])
10541
+        # We go through get_block_device_info to simulate what the
10542
+        # ComputeManager sends to the driver (make sure we're using the
10543
+        # correct type of BDM objects since there are many of them and
10544
+        # they are super confusing).
10545
+        block_device_info = driver.get_block_device_info(instance, bdms)
10485 10546
 
10486 10547
         drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
10487 10548
 
@@ -10496,16 +10557,11 @@ class LibvirtConnTestCase(test.NoDBTestCase,
10496 10557
         self.stubs.Set(drvr, '_is_native_luks_available',
10497 10558
                         lambda: dest_supports_native_luks)
10498 10559
 
10499
-        instance = objects.Instance(**self.test_instance)
10500
-        c = context.get_admin_context()
10501 10560
         nw_info = FakeNetworkInfo()
10502 10561
 
10503 10562
         # Creating mocks
10504
-        self.mox.StubOutWithMock(driver, "block_device_info_get_mapping")
10505
-        driver.block_device_info_get_mapping(vol
10506
-            ).AndReturn(vol['block_device_mapping'])
10507 10563
         self.mox.StubOutWithMock(drvr, "_connect_volume")
10508
-        for v in vol['block_device_mapping']:
10564
+        for v in block_device_info['block_device_mapping']:
10509 10565
             drvr._connect_volume(c, v['connection_info'], instance,
10510 10566
                                  allow_native_luks=allow_native_luks)
10511 10567
         self.mox.StubOutWithMock(drvr, 'plug_vifs')
@@ -10526,14 +10582,14 @@ class LibvirtConnTestCase(test.NoDBTestCase,
10526 10582
             migrate_data.src_supports_native_luks = True
10527 10583
 
10528 10584
         result = drvr.pre_live_migration(
10529
-            c, instance, vol, nw_info, None,
10585
+            c, instance, block_device_info, nw_info, None,
10530 10586
             migrate_data=migrate_data)
10531 10587
         if not target_ret:
10532 10588
             target_ret = self._generate_target_ret()
10533 10589
         self.assertEqual(
10590
+            target_ret,
10534 10591
             result.to_legacy_dict(
10535
-                pre_migration_result=True)['pre_live_migration_result'],
10536
-            target_ret)
10592
+                pre_migration_result=True)['pre_live_migration_result'])
10537 10593
 
10538 10594
     @mock.patch.object(os, 'mkdir')
10539 10595
     @mock.patch('nova.virt.libvirt.utils.get_instance_path_at_destination')
@@ -10617,15 +10673,42 @@ class LibvirtConnTestCase(test.NoDBTestCase,
10617 10673
         # Creating testdata, using temp dir.
10618 10674
         with utils.tempdir() as tmpdir:
10619 10675
             self.flags(instances_path=tmpdir)
10620
-            vol = {'block_device_mapping': [
10621
-             {'connection_info': {'serial': '12345', u'data':
10622
-             {'device_path':
10623
-              u'/dev/disk/by-path/ip-1.2.3.4:3260-iqn.abc.12345.opst-lun-X'}},
10624
-             'mount_device': '/dev/sda'},
10625
-             {'connection_info': {'serial': '67890', u'data':
10626
-             {'device_path':
10627
-             u'/dev/disk/by-path/ip-1.2.3.4:3260-iqn.cde.67890.opst-lun-Z'}},
10628
-             'mount_device': '/dev/sdb'}]}
10676
+            c = context.get_admin_context()
10677
+            inst_ref = objects.Instance(root_device_name='/dev/vda',
10678
+                                        **self.test_instance)
10679
+            bdms = objects.BlockDeviceMappingList(objects=[
10680
+                fake_block_device.fake_bdm_object(c, {
10681
+                    'connection_info': jsonutils.dumps({
10682
+                        'serial': '12345',
10683
+                        'data': {
10684
+                            'device_path': '/dev/disk/by-path/ip-1.2.3.4:3260'
10685
+                                           '-iqn.abc.12345.opst-lun-X'
10686
+                        }
10687
+                    }),
10688
+                    'device_name': '/dev/sda',
10689
+                    'volume_id': uuids.volume1,
10690
+                    'source_type': 'volume',
10691
+                    'destination_type': 'volume'
10692
+                }),
10693
+                fake_block_device.fake_bdm_object(c, {
10694
+                    'connection_info': jsonutils.dumps({
10695
+                        'serial': '67890',
10696
+                        'data': {
10697
+                            'device_path': '/dev/disk/by-path/ip-1.2.3.4:3260'
10698
+                                           '-iqn.cde.67890.opst-lun-Z'
10699
+                        }
10700
+                    }),
10701
+                    'device_name': '/dev/sdb',
10702
+                    'volume_id': uuids.volume2,
10703
+                    'source_type': 'volume',
10704
+                    'destination_type': 'volume'
10705
+                })
10706
+            ])
10707
+            # We go through get_block_device_info to simulate what the
10708
+            # ComputeManager sends to the driver (make sure we're using the
10709
+            # correct type of BDM objects since there are many of them and
10710
+            # they are super confusing).
10711
+            block_device_info = driver.get_block_device_info(inst_ref, bdms)
10629 10712
 
10630 10713
             drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
10631 10714
 
@@ -10638,12 +10721,10 @@ class LibvirtConnTestCase(test.NoDBTestCase,
10638 10721
             class FakeNetworkInfo(object):
10639 10722
                 def fixed_ips(self):
10640 10723
                     return ["test_ip_addr"]
10641
-            inst_ref = objects.Instance(**self.test_instance)
10642
-            c = context.get_admin_context()
10643 10724
             nw_info = FakeNetworkInfo()
10644 10725
             # Creating mocks
10645 10726
             self.mox.StubOutWithMock(drvr, "_connect_volume")
10646
-            for v in vol['block_device_mapping']:
10727
+            for v in block_device_info['block_device_mapping']:
10647 10728
                 drvr._connect_volume(c, v['connection_info'], inst_ref,
10648 10729
                                      allow_native_luks=True)
10649 10730
             self.mox.StubOutWithMock(drvr, 'plug_vifs')
@@ -10661,9 +10742,9 @@ class LibvirtConnTestCase(test.NoDBTestCase,
10661 10742
                 filename='foo',
10662 10743
                 src_supports_native_luks=True,
10663 10744
             )
10664
-            ret = drvr.pre_live_migration(c, inst_ref, vol, nw_info, None,
10665
-                                          migrate_data)
10666
-            target_ret = {
10745
+            ret = drvr.pre_live_migration(
10746
+                c, inst_ref, block_device_info, nw_info, None, migrate_data)
10747
+            expected_result = {
10667 10748
             'graphics_listen_addrs': {'spice': None,
10668 10749
                                       'vnc': None},
10669 10750
             'target_connect_addr': None,
@@ -10682,8 +10763,8 @@ class LibvirtConnTestCase(test.NoDBTestCase,
10682 10763
                                     'dev': 'sdb',
10683 10764
                                     'type': 'disk'}}}}
10684 10765
             self.assertEqual(
10685
-                ret.to_legacy_dict(True)['pre_live_migration_result'],
10686
-                target_ret)
10766
+                expected_result,
10767
+                ret.to_legacy_dict(True)['pre_live_migration_result'])
10687 10768
             self.assertTrue(os.path.exists('%s/%s/' % (tmpdir,
10688 10769
                                                        inst_ref['name'])))
10689 10770
 

+ 25
- 0
nova/tests/unit/virt/test_block_device.py View File

@@ -1274,3 +1274,28 @@ class TestDriverBlockDeviceNewFlow(TestDriverBlockDevice):
1274 1274
         self.assertRaises(exception.MultiattachNotSupportedByVirtDriver,
1275 1275
                           test_bdm.attach, self.context, instance,
1276 1276
                           self.volume_api, self.virt_driver)
1277
+
1278
+
1279
+class TestGetVolumeId(test.NoDBTestCase):
1280
+
1281
+    def test_get_volume_id_none_found(self):
1282
+        self.assertIsNone(driver_block_device.get_volume_id(None))
1283
+        self.assertIsNone(driver_block_device.get_volume_id({}))
1284
+        self.assertIsNone(driver_block_device.get_volume_id({'data': {}}))
1285
+
1286
+    def test_get_volume_id_found_volume_id_no_serial(self):
1287
+        self.assertEqual(uuids.volume_id,
1288
+                         driver_block_device.get_volume_id(
1289
+                             {'data': {'volume_id': uuids.volume_id}}))
1290
+
1291
+    def test_get_volume_id_found_no_volume_id_serial(self):
1292
+        self.assertEqual(uuids.serial,
1293
+                         driver_block_device.get_volume_id(
1294
+                             {'serial': uuids.serial}))
1295
+
1296
+    def test_get_volume_id_found_both(self):
1297
+        # volume_id is taken over serial
1298
+        self.assertEqual(uuids.volume_id,
1299
+                         driver_block_device.get_volume_id(
1300
+                             {'serial': uuids.serial,
1301
+                              'data': {'volume_id': uuids.volume_id}}))

+ 10
- 0
nova/virt/block_device.py View File

@@ -880,3 +880,13 @@ def is_block_device_mapping(bdm):
880 880
     return (bdm.source_type in ('image', 'volume', 'snapshot', 'blank')
881 881
             and bdm.destination_type == 'volume'
882 882
             and is_implemented(bdm))
883
+
884
+
885
+def get_volume_id(connection_info):
886
+    if connection_info:
887
+        # Check for volume_id in 'data' and if not there, fallback to
888
+        # the 'serial' that the DriverVolumeBlockDevice adds during attach.
889
+        volume_id = connection_info.get('data', {}).get('volume_id')
890
+        if not volume_id:
891
+            volume_id = connection_info.get('serial')
892
+        return volume_id

+ 5
- 8
nova/virt/libvirt/driver.py View File

@@ -1274,8 +1274,8 @@ class LibvirtDriver(driver.ComputeDriver):
1274 1274
         """Get the encryption metadata dict if it is not provided
1275 1275
         """
1276 1276
         encryption = {}
1277
-        if connection_info.get('data', {}).get('volume_id'):
1278
-            volume_id = connection_info['data']['volume_id']
1277
+        volume_id = driver_block_device.get_volume_id(connection_info)
1278
+        if volume_id:
1279 1279
             encryption = encryptors.get_encryption_metadata(context,
1280 1280
                             self._volume_api, volume_id, connection_info)
1281 1281
         return encryption
@@ -1323,7 +1323,7 @@ class LibvirtDriver(driver.ComputeDriver):
1323 1323
             # NOTE(lyarwood): Store the passphrase as a libvirt secret locally
1324 1324
             # on the compute node. This secret is used later when generating
1325 1325
             # the volume config.
1326
-            volume_id = connection_info.get('data', {}).get('volume_id')
1326
+            volume_id = driver_block_device.get_volume_id(connection_info)
1327 1327
             self._host.create_secret('volume', volume_id, password=passphrase)
1328 1328
         elif encryption:
1329 1329
             encryptor = self._get_volume_encryptor(connection_info,
@@ -1340,7 +1340,7 @@ class LibvirtDriver(driver.ComputeDriver):
1340 1340
         If native LUKS decryption is enabled then delete previously created
1341 1341
         Libvirt volume secret from the host.
1342 1342
         """
1343
-        volume_id = connection_info.get('data', {}).get('volume_id')
1343
+        volume_id = driver_block_device.get_volume_id(connection_info)
1344 1344
         if volume_id and self._host.find_secret('volume', volume_id):
1345 1345
             return self._host.delete_secret('volume', volume_id)
1346 1346
         if encryption is None:
@@ -7427,10 +7427,7 @@ class LibvirtDriver(driver.ComputeDriver):
7427 7427
                 bdmi.type = disk_info['type']
7428 7428
                 bdmi.format = disk_info.get('format')
7429 7429
                 bdmi.boot_index = disk_info.get('boot_index')
7430
-                volume_id = connection_info.get('volume_id')
7431
-                volume_secret = None
7432
-                if volume_id:
7433
-                    volume_secret = self._host.find_secret('volume', volume_id)
7430
+                volume_secret = self._host.find_secret('volume', vol.volume_id)
7434 7431
                 if volume_secret:
7435 7432
                     bdmi.encryption_secret_uuid = volume_secret.UUIDString()
7436 7433
 

+ 2
- 1
nova/virt/libvirt/volume/volume.py View File

@@ -22,6 +22,7 @@ from oslo_log import log as logging
22 22
 import nova.conf
23 23
 from nova import exception
24 24
 from nova import profiler
25
+from nova.virt import block_device as driver_block_device
25 26
 from nova.virt.libvirt import config as vconfig
26 27
 import nova.virt.libvirt.driver
27 28
 from nova.virt.libvirt import utils as libvirt_utils
@@ -109,7 +110,7 @@ class LibvirtBaseVolumeDriver(object):
109 110
             # a shareable disk.
110 111
             conf.shareable = True
111 112
 
112
-        volume_id = connection_info.get('data', {}).get('volume_id')
113
+        volume_id = driver_block_device.get_volume_id(connection_info)
113 114
         volume_secret = None
114 115
         if volume_id:
115 116
             volume_secret = self.host.find_secret('volume', volume_id)

Loading…
Cancel
Save