Browse Source

xenapi: Attach original local disks during rescue

When rescuing an instance, a new VM is created and only the original
root disk is re-attached. Often when a user is rescuing a VM, they expect
to be able to access all of their original disks so they can potentially
salvage data.

This changes the xenapi driver to attach the original local disks
during rescue so the user can rescue all of their data.

DocImpact

Implements: blueprint rescue-attach-all-disks
Change-Id: Iba5cc85cd03d0a60f1858cf16aa31397e163df50
Partial-bug: 1223396
tags/2014.2.b3
Johannes Erdfelt 5 years ago
parent
commit
1e2c92f3f2

+ 10
- 8
nova/tests/virt/xenapi/test_vmops.py View File

@@ -224,7 +224,7 @@ class SpawnTestCase(VMOpsTestBase):
224 224
         self.mox.StubOutWithMock(self.vmops, '_attach_disks')
225 225
         self.mox.StubOutWithMock(pci_manager, 'get_instance_pci_devs')
226 226
         self.mox.StubOutWithMock(vm_utils, 'set_other_config_pci')
227
-        self.mox.StubOutWithMock(self.vmops, '_attach_orig_disk_for_rescue')
227
+        self.mox.StubOutWithMock(self.vmops, '_attach_orig_disks')
228 228
         self.mox.StubOutWithMock(self.vmops, 'inject_network_info')
229 229
         self.mox.StubOutWithMock(self.vmops, '_inject_hostname')
230 230
         self.mox.StubOutWithMock(self.vmops, '_inject_instance_metadata')
@@ -300,7 +300,7 @@ class SpawnTestCase(VMOpsTestBase):
300 300
         self.vmops._update_instance_progress(context, instance, step, steps)
301 301
 
302 302
         self.vmops._attach_disks(instance, vm_ref, name_label, vdis, di_type,
303
-                          network_info, admin_password, injected_files)
303
+                          network_info, rescue, admin_password, injected_files)
304 304
         if attach_pci_dev:
305 305
             fake_dev = {
306 306
                 'created_at': None,
@@ -346,7 +346,7 @@ class SpawnTestCase(VMOpsTestBase):
346 346
         self.vmops._update_instance_progress(context, instance, step, steps)
347 347
 
348 348
         if rescue:
349
-            self.vmops._attach_orig_disk_for_rescue(instance, vm_ref)
349
+            self.vmops._attach_orig_disks(instance, vm_ref)
350 350
             step += 1
351 351
             self.vmops._update_instance_progress(context, instance, step,
352 352
                                                  steps)
@@ -435,7 +435,7 @@ class SpawnTestCase(VMOpsTestBase):
435 435
         if resize_instance:
436 436
             self.vmops._resize_up_vdis(instance, vdis)
437 437
         self.vmops._attach_disks(instance, vm_ref, name_label, vdis, di_type,
438
-                                 network_info, None, None)
438
+                                 network_info, False, None, None)
439 439
         self.vmops._attach_mapped_block_devices(instance, block_device_info)
440 440
         pci_manager.get_instance_pci_devs(instance).AndReturn([])
441 441
 
@@ -569,21 +569,23 @@ class SpawnTestCase(VMOpsTestBase):
569 569
         self.mox.ReplayAll()
570 570
         self.vmops._wait_for_instance_to_start(instance, vm_ref)
571 571
 
572
-    def test_attach_orig_disk_for_rescue(self):
572
+    def test_attach_orig_disks(self):
573 573
         instance = {"name": "dummy"}
574 574
         vm_ref = "vm_ref"
575
+        vbd_refs = {vmops.DEVICE_ROOT: "vdi_ref"}
575 576
 
576 577
         self.mox.StubOutWithMock(vm_utils, 'lookup')
577
-        self.mox.StubOutWithMock(self.vmops, '_find_root_vdi_ref')
578
+        self.mox.StubOutWithMock(self.vmops, '_find_vdi_refs')
578 579
         self.mox.StubOutWithMock(vm_utils, 'create_vbd')
579 580
 
580 581
         vm_utils.lookup(self.vmops._session, "dummy").AndReturn("ref")
581
-        self.vmops._find_root_vdi_ref("ref").AndReturn("vdi_ref")
582
+        self.vmops._find_vdi_refs("ref", exclude_volumes=True).AndReturn(
583
+                vbd_refs)
582 584
         vm_utils.create_vbd(self.vmops._session, vm_ref, "vdi_ref",
583 585
                             vmops.DEVICE_RESCUE, bootable=False)
584 586
 
585 587
         self.mox.ReplayAll()
586
-        self.vmops._attach_orig_disk_for_rescue(instance, vm_ref)
588
+        self.vmops._attach_orig_disks(instance, vm_ref)
587 589
 
588 590
     def test_agent_update_setup(self):
589 591
         # agent updates need to occur after networking is configured

+ 17
- 5
nova/tests/virt/xenapi/test_xenapi.py View File

@@ -1231,9 +1231,16 @@ iface eth0 inet6 static
1231 1231
 
1232 1232
         swap_vdi_ref = xenapi_fake.create_vdi('swap', None)
1233 1233
         root_vdi_ref = xenapi_fake.create_vdi('root', None)
1234
+        eph1_vdi_ref = xenapi_fake.create_vdi('eph', None)
1235
+        eph2_vdi_ref = xenapi_fake.create_vdi('eph', None)
1236
+        vol_vdi_ref = xenapi_fake.create_vdi('volume', None)
1234 1237
 
1235
-        xenapi_fake.create_vbd(vm_ref, swap_vdi_ref, userdevice=1)
1238
+        xenapi_fake.create_vbd(vm_ref, swap_vdi_ref, userdevice=2)
1236 1239
         xenapi_fake.create_vbd(vm_ref, root_vdi_ref, userdevice=0)
1240
+        xenapi_fake.create_vbd(vm_ref, eph1_vdi_ref, userdevice=4)
1241
+        xenapi_fake.create_vbd(vm_ref, eph2_vdi_ref, userdevice=5)
1242
+        xenapi_fake.create_vbd(vm_ref, vol_vdi_ref, userdevice=6,
1243
+                               other_config={'osvol': True})
1237 1244
 
1238 1245
         conn = xenapi_conn.XenAPIDriver(fake.FakeVirtAPI(), False)
1239 1246
         image_meta = {'id': IMAGE_VHD,
@@ -1245,11 +1252,16 @@ iface eth0 inet6 static
1245 1252
         rescue_ref = vm_utils.lookup(session, rescue_name)
1246 1253
         rescue_vm = xenapi_fake.get_record('VM', rescue_ref)
1247 1254
 
1248
-        vdi_refs = []
1255
+        vdi_refs = {}
1249 1256
         for vbd_ref in rescue_vm['VBDs']:
1250
-            vdi_refs.append(xenapi_fake.get_record('VBD', vbd_ref)['VDI'])
1251
-        self.assertNotIn(swap_vdi_ref, vdi_refs)
1252
-        self.assertIn(root_vdi_ref, vdi_refs)
1257
+            vbd = xenapi_fake.get_record('VBD', vbd_ref)
1258
+            vdi_refs[vbd['VDI']] = vbd['userdevice']
1259
+
1260
+        self.assertEqual('1', vdi_refs[root_vdi_ref])
1261
+        self.assertEqual('2', vdi_refs[swap_vdi_ref])
1262
+        self.assertEqual('4', vdi_refs[eph1_vdi_ref])
1263
+        self.assertEqual('5', vdi_refs[eph2_vdi_ref])
1264
+        self.assertNotIn(vol_vdi_ref, vdi_refs)
1253 1265
 
1254 1266
     def test_rescue_preserve_disk_on_failure(self):
1255 1267
         # test that the original disk is preserved if rescue setup fails

+ 7
- 2
nova/virt/xenapi/fake.py View File

@@ -206,11 +206,15 @@ def after_VDI_create(vdi_ref, vdi_rec):
206 206
     vdi_rec.setdefault('VBDs', [])
207 207
 
208 208
 
209
-def create_vbd(vm_ref, vdi_ref, userdevice=0):
209
+def create_vbd(vm_ref, vdi_ref, userdevice=0, other_config=None):
210
+    if other_config is None:
211
+        other_config = {}
212
+
210 213
     vbd_rec = {'VM': vm_ref,
211 214
                'VDI': vdi_ref,
212 215
                'userdevice': str(userdevice),
213
-               'currently_attached': False}
216
+               'currently_attached': False,
217
+               'other_config': other_config}
214 218
     vbd_ref = _create_object('VBD', vbd_rec)
215 219
     after_VBD_create(vbd_ref, vbd_rec)
216 220
     return vbd_ref
@@ -222,6 +226,7 @@ def after_VBD_create(vbd_ref, vbd_rec):
222 226
     """
223 227
     vbd_rec['currently_attached'] = False
224 228
     vbd_rec['device'] = ''
229
+    vbd_rec.setdefault('other_config', {})
225 230
 
226 231
     vm_ref = vbd_rec['VM']
227 232
     vm_rec = _db_content['VM'][vm_ref]

+ 60
- 30
nova/virt/xenapi/vmops.py View File

@@ -382,8 +382,8 @@ class VMOps(object):
382 382
                 self._resize_up_vdis(instance, vdis)
383 383
 
384 384
             self._attach_disks(instance, vm_ref, name_label, vdis,
385
-                               disk_image_type, network_info, admin_password,
386
-                               injected_files)
385
+                               disk_image_type, network_info, rescue,
386
+                               admin_password, injected_files)
387 387
             if not first_boot:
388 388
                 self._attach_mapped_block_devices(instance,
389 389
                                                   block_device_info)
@@ -440,19 +440,20 @@ class VMOps(object):
440 440
             attach_pci_devices(undo_mgr, vm_ref)
441 441
 
442 442
         if rescue:
443
-            # NOTE(johannes): Attach root disk to rescue VM now, before
444
-            # booting the VM, since we can't hotplug block devices
443
+            # NOTE(johannes): Attach disks from original VM to rescue VM now,
444
+            # before booting the VM, since we can't hotplug block devices
445 445
             # on non-PV guests
446 446
             @step
447
-            def attach_root_disk_step(undo_mgr, vm_ref):
448
-                vbd_ref = self._attach_orig_disk_for_rescue(instance, vm_ref)
447
+            def attach_orig_disks_step(undo_mgr, vm_ref):
448
+                vbd_refs = self._attach_orig_disks(instance, vm_ref)
449 449
 
450
-                def undo_attach_root_disk():
451
-                    # destroy the vbd in preparation to re-attach the VDI
450
+                def undo_attach_orig_disks():
451
+                    # Destroy the VBDs in preparation to re-attach the VDIs
452 452
                     # to its original VM.  (does not delete VDI)
453
-                    vm_utils.destroy_vbd(self._session, vbd_ref)
453
+                    for vbd_ref in vbd_refs:
454
+                        vm_utils.destroy_vbd(self._session, vbd_ref)
454 455
 
455
-                undo_mgr.undo_with(undo_attach_root_disk)
456
+                undo_mgr.undo_with(undo_attach_orig_disks)
456 457
 
457 458
         @step
458 459
         def inject_instance_data_step(undo_mgr, vm_ref, vdis):
@@ -508,7 +509,7 @@ class VMOps(object):
508 509
             setup_network_step(undo_mgr, vm_ref)
509 510
 
510 511
             if rescue:
511
-                attach_root_disk_step(undo_mgr, vm_ref)
512
+                attach_orig_disks_step(undo_mgr, vm_ref)
512 513
 
513 514
             boot_instance_step(undo_mgr, vm_ref)
514 515
 
@@ -521,11 +522,35 @@ class VMOps(object):
521 522
             msg = _("Failed to spawn, rolling back")
522 523
             undo_mgr.rollback_and_reraise(msg=msg, instance=instance)
523 524
 
524
-    def _attach_orig_disk_for_rescue(self, instance, vm_ref):
525
+    def _attach_orig_disks(self, instance, vm_ref):
525 526
         orig_vm_ref = vm_utils.lookup(self._session, instance['name'])
526
-        vdi_ref = self._find_root_vdi_ref(orig_vm_ref)
527
-        return vm_utils.create_vbd(self._session, vm_ref, vdi_ref,
528
-                                   DEVICE_RESCUE, bootable=False)
527
+        orig_vdi_refs = self._find_vdi_refs(orig_vm_ref,
528
+                                            exclude_volumes=True)
529
+
530
+        # Attach original root disk
531
+        root_vdi_ref = orig_vdi_refs.get(DEVICE_ROOT)
532
+        if not root_vdi_ref:
533
+            raise exception.NotFound(_("Unable to find root VBD/VDI for VM"))
534
+
535
+        vbd_ref = vm_utils.create_vbd(self._session, vm_ref, root_vdi_ref,
536
+                                      DEVICE_RESCUE, bootable=False)
537
+        vbd_refs = [vbd_ref]
538
+
539
+        # Attach original swap disk
540
+        swap_vdi_ref = orig_vdi_refs.get(DEVICE_SWAP)
541
+        if swap_vdi_ref:
542
+            vbd_ref = vm_utils.create_vbd(self._session, vm_ref, swap_vdi_ref,
543
+                                          DEVICE_SWAP, bootable=False)
544
+            vbd_refs.append(vbd_ref)
545
+
546
+        # Attach original ephemeral disks
547
+        for userdevice, vdi_ref in orig_vdi_refs.iteritems():
548
+            if userdevice >= DEVICE_EPHEMERAL:
549
+                vbd_ref = vm_utils.create_vbd(self._session, vm_ref, vdi_ref,
550
+                                              userdevice, bootable=False)
551
+                vbd_refs.append(vbd_ref)
552
+
553
+        return vbd_refs
529 554
 
530 555
     def _file_inject_vm_settings(self, instance, vm_ref, vdis, network_info):
531 556
         if CONF.flat_injected:
@@ -565,7 +590,7 @@ class VMOps(object):
565 590
         return vm_ref
566 591
 
567 592
     def _attach_disks(self, instance, vm_ref, name_label, vdis,
568
-                      disk_image_type, network_info,
593
+                      disk_image_type, network_info, rescue=False,
569 594
                       admin_password=None, files=None):
570 595
         flavor = flavors.extract_flavor(instance)
571 596
 
@@ -607,14 +632,17 @@ class VMOps(object):
607 632
                                 userdevice, bootable=False,
608 633
                                 osvol=vdi_info.get('osvol'))
609 634
 
635
+        # For rescue, swap and ephemeral disks get attached in
636
+        # _attach_orig_disks
637
+
610 638
         # Attach (optional) swap disk
611 639
         swap_mb = flavor['swap']
612
-        if swap_mb:
640
+        if not rescue and swap_mb:
613 641
             vm_utils.generate_swap(self._session, instance, vm_ref,
614 642
                                    DEVICE_SWAP, name_label, swap_mb)
615 643
 
616 644
         ephemeral_gb = flavor['ephemeral_gb']
617
-        if ephemeral_gb:
645
+        if not rescue and ephemeral_gb:
618 646
             ephemeral_vdis = vdis.get('ephemerals')
619 647
             if ephemeral_vdis:
620 648
                 # attach existing (migrated) ephemeral disks
@@ -1242,19 +1270,18 @@ class VMOps(object):
1242 1270
                 process_change(location, change)
1243 1271
         update_meta()
1244 1272
 
1245
-    def _find_root_vdi_ref(self, vm_ref):
1246
-        """Find and return the root vdi ref for a VM."""
1273
+    def _find_vdi_refs(self, vm_ref, exclude_volumes=False):
1274
+        """Find and return the root and ephemeral vdi refs for a VM."""
1247 1275
         if not vm_ref:
1248
-            return None
1249
-
1250
-        vbd_refs = self._session.call_xenapi("VM.get_VBDs", vm_ref)
1276
+            return {}
1251 1277
 
1252
-        for vbd_uuid in vbd_refs:
1253
-            vbd = self._session.call_xenapi("VBD.get_record", vbd_uuid)
1254
-            if vbd["userdevice"] == DEVICE_ROOT:
1255
-                return vbd["VDI"]
1278
+        vdi_refs = {}
1279
+        for vbd_ref in self._session.call_xenapi("VM.get_VBDs", vm_ref):
1280
+            vbd = self._session.call_xenapi("VBD.get_record", vbd_ref)
1281
+            if not exclude_volumes or 'osvol' not in vbd['other_config']:
1282
+                vdi_refs[vbd['userdevice']] = vbd['VDI']
1256 1283
 
1257
-        raise exception.NotFound(_("Unable to find root VBD/VDI for VM"))
1284
+        return vdi_refs
1258 1285
 
1259 1286
     def _destroy_vdis(self, instance, vm_ref):
1260 1287
         """Destroys all VDIs associated with a VM."""
@@ -1311,8 +1338,11 @@ class VMOps(object):
1311 1338
 
1312 1339
         # Destroy Rescue VDIs
1313 1340
         vdi_refs = vm_utils.lookup_vm_vdis(self._session, rescue_vm_ref)
1314
-        root_vdi_ref = self._find_root_vdi_ref(original_vm_ref)
1315
-        vdi_refs = [vdi_ref for vdi_ref in vdi_refs if vdi_ref != root_vdi_ref]
1341
+
1342
+        # Don't destroy any VDIs belonging to the original VM
1343
+        orig_vdi_refs = self._find_vdi_refs(original_vm_ref)
1344
+        vdi_refs = set(vdi_refs) - set(orig_vdi_refs.values())
1345
+
1316 1346
         vm_utils.safe_destroy_vdis(self._session, vdi_refs)
1317 1347
 
1318 1348
         # Destroy Rescue VM

Loading…
Cancel
Save