ソースを参照

Use diskpart to bring disks offline

This patch should make sure that dynamic disks are also brought offline
Daniel Vincze 5 年 前
コミット
adf460210c
1 ファイル変更46 行追加10 行削除
  1. 46 10
      coriolis/osmorphing/osmount/windows.py

+ 46 - 10
coriolis/osmorphing/osmount/windows.py

@@ -59,12 +59,15 @@ class WindowsMountTools(base.BaseOSMountTools):
 
     def _service_disks_with_status(
             self, status, service_script_with_id_fmt, skip_on_error=False,
-            logmsg_fmt="Operating on disk with index '%s'"):
+            logmsg_fmt="Operating on disk with index '%s'",
+            disk_ids_to_skip=None):
         """Executes given service script on detected disks.
 
         Uses diskpart.exe to detect all disks with the given 'status', and
         execute the given service script after formatting the disk ID in.
         """
+        if disk_ids_to_skip is None:
+            disk_ids_to_skip = []
         disk_list_script = "LIST DISK\r\nEXIT"
 
         disk_entry_re = r"\s+Disk (%s)\s+%s\s+"
@@ -81,6 +84,9 @@ class WindowsMountTools(base.BaseOSMountTools):
             "Servicing disks with status '%s' (%s) from disk list: %s",
             status, servicable_disk_ids, disk_list)
         for disk_id in servicable_disk_ids:
+            if disk_id in disk_ids_to_skip:
+                LOG.warn('Skipping disk with ID: %s', disk_id)
+                continue
             curr_disk_entry_re = disk_entry_re % (disk_id, status)
 
             disk_list = self._run_diskpart_script(disk_list_script)
@@ -105,6 +111,30 @@ class WindowsMountTools(base.BaseOSMountTools):
                             raise
                     break
 
+    def _get_disk_ids_from_drive_letters(self, drive_letters):
+        disk_ids = []
+        volume_entry_re = r"^\s+Volume ([0-9]+)\s+([A-Z]+)\s+"
+
+        def _get_disk_ids(vol_id):
+            vol_detail_script = (
+                "SELECT VOLUME %s\r\nDETAIL VOLUME\r\nEXIT" % vol_id)
+            vol_details = self._run_diskpart_script(vol_detail_script)
+            vol_disk_re = r"^\*?\s+Disk ([0-9]+)\s+"
+            return [m.group(1) for m in [
+                re.match(vol_disk_re, l) for l in vol_details.split('\r\n')]
+                if m is not None]
+
+        volume_list_script = "LIST VOLUME\r\nEXIT"
+        vol_list = self._run_diskpart_script(volume_list_script)
+        for volume in vol_list.split('\r\n'):
+            m = re.match(volume_entry_re, volume)
+            if m:
+                vol_id, letter = m.groups()
+                if letter in drive_letters:
+                    disk_ids += _get_disk_ids(vol_id)
+
+        return disk_ids
+
     def _set_foreign_disks_rw_mode(self):
         # NOTE: in case a Dynamic Disk (which will show up as 'Foreign' to
         # the worker) is part of a Dynamic Disk group, ALL disks from that
@@ -149,11 +179,16 @@ class WindowsMountTools(base.BaseOSMountTools):
             logmsg_fmt="Clearing R/O flag on disk with ID '%s'.",
             skip_on_error=True)
 
-    def _bring_disk_offline(self, drive_letter):
-        self._conn.exec_ps_command(
-            "Get-Volume |? DriveLetter -eq \"%s\" | Get-Partition | "
-            "Get-Disk | Set-Disk -IsOffline $True" % drive_letter,
-            ignore_stdout=True)
+    def _bring_disks_offline(self, drives_to_skip=None):
+        disk_ids_to_skip = None
+        if drives_to_skip:
+            disk_ids_to_skip = self._get_disk_ids_from_drive_letters(
+                drives_to_skip)
+        offline_disk_script_fmt = "SELECT DISK %s\r\nOFFLINE DISK\r\nEXIT"
+        self._service_disks_with_status(
+            "Online", offline_disk_script_fmt, skip_on_error=True,
+            logmsg_fmt="Bringing online disk with ID %s offline.",
+            disk_ids_to_skip=disk_ids_to_skip)
 
     def _get_system_drive(self):
         return self._conn.exec_ps_command("$env:SystemDrive")
@@ -182,7 +217,8 @@ class WindowsMountTools(base.BaseOSMountTools):
 
         raise exception.OperatingSystemNotFound("root partition not found")
 
-    def dismount_os(self, dirs):
-        for dir in dirs:
-            drive_letter = dir.split(":")[0]
-            self._bring_disk_offline(drive_letter)
+    def dismount_os(self, root_drive):
+        drives = self._ignore_devices.copy()
+        drives.append(self._get_system_drive())
+        drives_to_skip = [ltr.split(":")[0] for ltr in drives]
+        self._bring_disks_offline(drives_to_skip)