Переглянути джерело

Merged in aznashwan/coriolis/win-osmount (pull request #54)

Added support for importing foreign Dynamic Disk groups in Windows OSMount tools.
Nashwan Azhari 9 роки тому
батько
коміт
eafba64081
1 змінених файлів з 82 додано та 8 видалено
  1. 82 8
      coriolis/osmorphing/osmount/windows.py

+ 82 - 8
coriolis/osmorphing/osmount/windows.py

@@ -1,6 +1,9 @@
 # Copyright 2016 Cloudbase Solutions Srl
 # All Rights Reserved.
 
+import re
+import uuid
+
 from oslo_log import log as logging
 
 from coriolis import exception
@@ -52,14 +55,82 @@ class WindowsMountTools(base.BaseOSMountTools):
         self._conn.exec_ps_command(
             "Update-HostStorageCache", ignore_stdout=True)
 
-    def _bring_all_disks_online(self):
-        LOG.info("Bringing offline disks online")
-        self._conn.exec_ps_command(
-            "Get-Disk |? IsOffline | Set-Disk -IsOffline $False",
-            ignore_stdout=True)
+    def _run_diskpart_script(self, script):
+        """ Writes the given script data to a file and runs diskpart.exe,
+        returning the output. """
+        tempdir = self._conn.exec_ps_command("$env:TEMP")
+
+        # NOTE: diskpart is interactive, so we must either pipe it its input
+        # or write a couple of one-line files and use `diskpart /s`
+        filepath = r"%s\%s.txt" % (tempdir, uuid.uuid4())
+        self._conn.write_file(filepath, bytes(script, 'utf-8'))
+
+        return self._conn.exec_ps_command("diskpart.exe /s '%s'" % filepath)
+
+    def _service_disks_with_status(
+            self, status, service_script_with_id_fmt,
+            logmsg_fmt="Operating on disk with index '%s'"):
+        """ Uses diskpart.exe to detect all disks with the given 'status', and
+        execute the given service script after formatting the disk ID in. """
+        disk_list_script = "LIST DISK\r\nEXIT"
+
+        disk_entry_re = r"\s+Disk (%s)\s+%s\s+"
+        search_disk_entry_re = disk_entry_re % ("[0-9]+", status)
+        # NOTE: some disk operations performed on one disk may have an effect
+        # on others (ex: importing a Dynamic Disk which is part of a Dynamic
+        # Disk Group also imports the other ones), so we must take care to
+        # re-status before performing any operation on any disk => O(n**2)
+        disk_list = self._run_diskpart_script(disk_list_script)
+        servicable_disk_ids = [m.group(1) for m in [
+            re.match(search_disk_entry_re, l) for l in disk_list.split("\r\n")]
+            if m is not None]
+        for disk_id in servicable_disk_ids:
+            curr_disk_entry_re = disk_entry_re % (disk_id, status)
+
+            disk_list = self._run_diskpart_script("LIST DISK\r\nEXIT")
+            for line in disk_list.split("\r\n"):
+                if re.match(curr_disk_entry_re, line):
+                    LOG.info(logmsg_fmt, disk_id)
+                    self._run_diskpart_script(
+                        service_script_with_id_fmt % disk_id)
+                    break
+
+    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
+        # group must be R/W in order to import it (importing one will
+        # trigger the importing of all of them)
+        set_rw_foreign_disk_script_fmt = (
+            "SELECT DISK %s\r\nATTRIBUTES DISK CLEAR READONLY\r\nEXIT")
+        self._service_disks_with_status(
+            "Foreign", set_rw_foreign_disk_script_fmt,
+            logmsg_fmt="Clearing R/O flag on foreign disk with ID '%s'.")
+
+    def _import_foreign_disks(self):
+        """ Uses diskpart.exe to import all disks which are foreign to the
+        worker. Needed when servicing installations on Dynamic Disks. """
+        # NOTE: foreign disks are not exposed via the APIs the PowerShell
+        # disk cmdlets use, thus any disk which is foreign is is likely
+        # still RO, which is why we must change the RO attribute as well:
+        import_disk_script_fmt = (
+            "SELECT DISK %s\r\nIMPORT\r\nEXIT")
+        self._service_disks_with_status(
+            "Foreign", import_disk_script_fmt,
+            logmsg_fmt="Importing foreign disk with ID '%s'.")
 
-    def _set_all_disks_rw_mode(self):
-        LOG.info("Setting RW mode on RO disks")
+    def _bring_all_disks_online(self):
+        online_disk_script_fmt = "SELECT DISK %s\r\nONLINE DISK\r\nEXIT"
+        self._service_disks_with_status(
+            "Offline", online_disk_script_fmt,
+            logmsg_fmt="Bringing offline disk with ID %s online.")
+
+    def _set_basic_disks_rw_mode(self):
+        # NOTE: The PowerShell cmdlets use APIs which do not expose foreign
+        # disks at all (Dynamic disks will always show up as foreign to the
+        # worker), thus this method will only work for basic disks.
+        # Dynamic Disks are set to R/W upon their importing in
+        # self._import_foreign_disks()
+        LOG.info("Setting RW mode on RO basic disks")
         self._conn.exec_ps_command(
             "Get-Disk |? IsReadOnly | Set-Disk -IsReadOnly $False",
             ignore_stdout=True)
@@ -80,7 +151,10 @@ class WindowsMountTools(base.BaseOSMountTools):
     def mount_os(self):
         self._refresh_storage()
         self._bring_all_disks_online()
-        self._set_all_disks_rw_mode()
+        self._set_basic_disks_rw_mode()
+        self._set_foreign_disks_rw_mode()
+        self._import_foreign_disks()
+        self._refresh_storage()
         fs_roots = self._get_fs_roots()
         system_drive = self._get_system_drive()