Selaa lähdekoodia

Merge pull request #197 from Dany9966/windows-dismount-fix

Use diskpart to bring disks offline
Nashwan Azhari 5 vuotta sitten
vanhempi
sitoutus
643ebacf53
3 muutettua tiedostoa jossa 65 lisäystä ja 29 poistoa
  1. 46 10
      coriolis/osmorphing/osmount/windows.py
  2. 18 18
      coriolis/osmorphing/windows.py
  3. 1 1
      coriolis/utils.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)

+ 18 - 18
coriolis/osmorphing/windows.py

@@ -426,24 +426,24 @@ class BaseWindowsMorphingTools(base.BaseOSMorphingTools):
         conf_file_path = "%s\\cloudbase-init.conf" % conf_dir
         conf_file_path = "%s\\cloudbase-init.conf" % conf_dir
 
 
         conf_content = (
         conf_content = (
-            "[DEFAULT]\n"
-            "username = Admin\n"
-            "groups = Administrators\n"
-            "verbose = true\n"
-            "bsdtar_path = %(bin_path)s\\bsdtar.exe\n"
-            "mtools_path = %(bin_path)s\n"
-            "logdir = %(log_path)s\n"
-            "local_scripts_path = %(scripts_path)s\n"
-            "stop_service_on_exit = false\n"
-            "logfile = cloudbase-init.log\n"
-            "default_log_levels = "
-            "comtypes=INFO,suds=INFO,iso8601=WARN,requests=WARN\n"
-            "allow_reboot = false\n"
-            "plugins = %(plugins)s\n"
-            "debug = true\n"
-            "san_policy = OnlineAll\n"
-            "metadata_services = %(metadata_services)s\n"
-            "logging_serial_port_settings = %(com_port)s,9600,N,8\n" %
+            "[DEFAULT]\r\n"
+            "username = Admin\r\n"
+            "groups = Administrators\r\n"
+            "verbose = true\r\n"
+            "bsdtar_path = %(bin_path)s\\bsdtar.exe\r\n"
+            "mtools_path = %(bin_path)s\r\n"
+            "logdir = %(log_path)s\r\n"
+            "local_scripts_path = %(scripts_path)s\r\n"
+            "stop_service_on_exit = false\r\n"
+            "logfile = cloudbase-init.log\r\n"
+            "default_log_levels = \r\n"
+            "comtypes=INFO,suds=INFO,iso8601=WARN,requests=WARN\r\n"
+            "allow_reboot = false\r\n"
+            "plugins = %(plugins)s\r\n"
+            "debug = true\r\n"
+            "san_policy = OnlineAll\r\n"
+            "metadata_services = %(metadata_services)s\r\n"
+            "logging_serial_port_settings = %(com_port)s,9600,N,8\r\n" %
             {"bin_path": "%s\\Bin" % local_base_dir,
             {"bin_path": "%s\\Bin" % local_base_dir,
              "log_path": "%s\\Log" % local_base_dir,
              "log_path": "%s\\Log" % local_base_dir,
              "scripts_path": "%s\\LocalScripts" % local_base_dir,
              "scripts_path": "%s\\LocalScripts" % local_base_dir,

+ 1 - 1
coriolis/utils.py

@@ -276,7 +276,7 @@ def write_winrm_file(conn, remote_path, content, overwrite=True):
             data = data.encode()
             data = data.encode()
         asb64 = base64.b64encode(data).decode()
         asb64 = base64.b64encode(data).decode()
         cmd = ("$ErrorActionPreference = 'Stop';"
         cmd = ("$ErrorActionPreference = 'Stop';"
-               "$x = [System.IO.FileStream]::new(\"%s\", "
+               "$x = New-Object System.IO.FileStream(\"%s\", "
                "[System.IO.FileMode]::Append); $bytes = "
                "[System.IO.FileMode]::Append); $bytes = "
                "[Convert]::FromBase64String('%s'); $x.Write($bytes, "
                "[Convert]::FromBase64String('%s'); $x.Write($bytes, "
                "0, $bytes.Length); $x.Close()") % (
                "0, $bytes.Length); $x.Close()") % (