Просмотр исходного кода

Use diskpart to bring disks offline

This patch should make sure that dynamic disks are also brought offline
Daniel Vincze 5 лет назад
Родитель
Сommit
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(
     def _service_disks_with_status(
             self, status, service_script_with_id_fmt, skip_on_error=False,
             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.
         """Executes given service script on detected disks.
 
 
         Uses diskpart.exe to detect all disks with the given 'status', and
         Uses diskpart.exe to detect all disks with the given 'status', and
         execute the given service script after formatting the disk ID in.
         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_list_script = "LIST DISK\r\nEXIT"
 
 
         disk_entry_re = r"\s+Disk (%s)\s+%s\s+"
         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",
             "Servicing disks with status '%s' (%s) from disk list: %s",
             status, servicable_disk_ids, disk_list)
             status, servicable_disk_ids, disk_list)
         for disk_id in servicable_disk_ids:
         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)
             curr_disk_entry_re = disk_entry_re % (disk_id, status)
 
 
             disk_list = self._run_diskpart_script(disk_list_script)
             disk_list = self._run_diskpart_script(disk_list_script)
@@ -105,6 +111,30 @@ class WindowsMountTools(base.BaseOSMountTools):
                             raise
                             raise
                     break
                     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):
     def _set_foreign_disks_rw_mode(self):
         # NOTE: in case a Dynamic Disk (which will show up as 'Foreign' to
         # 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
         # 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'.",
             logmsg_fmt="Clearing R/O flag on disk with ID '%s'.",
             skip_on_error=True)
             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):
     def _get_system_drive(self):
         return self._conn.exec_ps_command("$env:SystemDrive")
         return self._conn.exec_ps_command("$env:SystemDrive")
@@ -182,7 +217,8 @@ class WindowsMountTools(base.BaseOSMountTools):
 
 
         raise exception.OperatingSystemNotFound("root partition not found")
         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)