فهرست منبع

Abstract the installation of cloudbase init

The plan is to install Cloudbase-Init on all providers, regardless
of whether or not Cloudbase-init can actually be used on the target
cloud. The idea is to have a single way to run user scripts on the
migrated instance.

For clouds where Cloudbase-Init does not have a provider, and we use
is as a glorified script runner, we can set the NoCloud metadata
provider.
Gabriel-Adrian Samfira 6 سال پیش
والد
کامیت
b5f93fdb87
2فایلهای تغییر یافته به همراه302 افزوده شده و 0 حذف شده
  1. 196 0
      coriolis/osmorphing/windows.py
  2. 106 0
      coriolis/resources/bin/bring-disks-online.ps1

+ 196 - 0
coriolis/osmorphing/windows.py

@@ -19,6 +19,45 @@ SERVICE_START_MANUAL = 3
 SERVICE_START_DISABLED = 4
 
 SERVICE_PATH_FORMAT = "HKLM:\\%s\\ControlSet001\\Services\\%s"
+CLOUDBASEINIT_SERVICE_NAME = "cloudbase-init"
+CLOUDBASE_INIT_DEFAULT_PLUGINS = [
+    'cloudbaseinit.plugins.common.mtu.MTUPlugin',
+    'cloudbaseinit.plugins.windows.ntpclient'
+    '.NTPClientPlugin',
+    'cloudbaseinit.plugins.common.sethostname'
+    '.SetHostNamePlugin',
+    'cloudbaseinit.plugins.windows.createuser'
+    '.CreateUserPlugin',
+    'cloudbaseinit.plugins.common.networkconfig'
+    '.NetworkConfigPlugin',
+    'cloudbaseinit.plugins.windows.licensing'
+    '.WindowsLicensingPlugin',
+    'cloudbaseinit.plugins.common.sshpublickeys'
+    '.SetUserSSHPublicKeysPlugin',
+    'cloudbaseinit.plugins.windows.extendvolumes'
+    '.ExtendVolumesPlugin',
+    'cloudbaseinit.plugins.common.userdata.UserDataPlugin',
+    'cloudbaseinit.plugins.common.setuserpassword.'
+    'SetUserPasswordPlugin',
+    'cloudbaseinit.plugins.windows.winrmlistener.'
+    'ConfigWinRMListenerPlugin',
+    'cloudbaseinit.plugins.windows.winrmcertificateauth.'
+    'ConfigWinRMCertificateAuthPlugin',
+    'cloudbaseinit.plugins.common.localscripts'
+    '.LocalScriptsPlugin',
+]
+
+CLOUDBASE_INIT_DEFAULT_METADATA_SVCS = [
+    'cloudbaseinit.metadata.services.httpservice.HttpService',
+    'cloudbaseinit.metadata.services'
+    '.configdrive.ConfigDriveService',
+    'cloudbaseinit.metadata.services.ec2service.EC2Service',
+    'cloudbaseinit.metadata.services'
+    '.maasservice.MaaSHttpService',
+    'cloudbaseinit.metadata.services.cloudstack.CloudStack',
+    'cloudbaseinit.metadata.services'
+    '.opennebulaservice.OpenNebulaService',
+]
 
 
 class BaseWindowsMorphingTools(base.BaseOSMorphingTools):
@@ -248,3 +287,160 @@ class BaseWindowsMorphingTools(base.BaseOSMorphingTools):
         except Exception as err:
             raise exception.CoriolisException(
                 "Failed to run user script.") from err
+
+    def _disable_cloudbase_init(self):
+        key_name = str(uuid.uuid4())
+        self._load_registry_hive(
+            "HKLM\\%s" % key_name,
+            "%sWindows\\System32\\config\\SYSTEM" % self._os_root_dir)
+        try:
+            if self._check_cloudbase_init_exists(key_name):
+                self._event_manager.progress_update(
+                    "Disabling cloudbase-init")
+                self._set_service_start_mode(
+                    key_name, CLOUDBASEINIT_SERVICE_NAME,
+                    SERVICE_START_DISABLED)
+        finally:
+            self._unload_registry_hive("HKLM\\%s" % key_name)
+
+    def _check_cloudbase_init_exists(self, key_name):
+        reg_service_path = (SERVICE_PATH_FORMAT %
+                            (key_name, CLOUDBASEINIT_SERVICE_NAME))
+        return self._conn.exec_ps_command(
+            "Test-Path %s" % reg_service_path) == "True"
+
+    def _get_cbslinit_scripts_dir(self, base_dir):
+        return ("%s\\LocalScripts" % base_dir)
+
+    def _write_local_script(self, base_dir, script_path, priority=50):
+        scripts_dir = self._get_cbslinit_scripts_dir(base_dir)
+        script = "%s\\%d-%s" % (
+            scripts_dir, priority,
+            os.path.basename(script_path))
+
+        with open(script_path, 'r') as fd:
+            contents = fd.read()
+            utils.write_winrm_file(
+                self._conn, script, contents)
+
+    def _write_cloudbase_init_conf(self, cloudbaseinit_base_dir,
+                                   local_base_dir, com_port="COM1",
+                                   metadata_services=None, plugins=None):
+        if metadata_services is None:
+            metadata_services = CLOUDBASE_INIT_DEFAULT_METADATA_SVCS
+
+        if plugins is None:
+            plugins = CLOUDBASE_INIT_DEFAULT_PLUGINS
+        elif type(plugins) is not list:
+            raise exception.CoriolisException(
+                "Invalid plugins parameter. Must be list.")
+
+        LOG.info("Writing Cloudbase-Init configuration files")
+        conf_dir = "%s\\conf" % cloudbaseinit_base_dir
+        scripts_dir = self._get_cbslinit_scripts_dir(
+            cloudbaseinit_base_dir)
+        self._conn.exec_ps_command("mkdir '%s' -Force" % conf_dir,
+                                   ignore_stdout=True)
+        self._conn.exec_ps_command("mkdir '%s' -Force" % scripts_dir,
+                                   ignore_stdout=True)
+
+        conf_file_path = "%s\\cloudbase-init.conf" % conf_dir
+
+        conf_content = (
+            "[DEFAULT]\n"
+            "username = opc\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" %
+            {"bin_path": "%s\\Bin" % local_base_dir,
+             "log_path": "%s\\Log" % local_base_dir,
+             "scripts_path": "%s\\LocalScripts" % local_base_dir,
+             "com_port": com_port,
+             "metadata_services": ",".join(metadata_services),
+             "plugins": ",".join(plugins)})
+
+        utils.write_winrm_file(
+            self._conn,
+            conf_file_path,
+            conf_content)
+
+        disks_script = os.path.join(
+            utils.get_resources_bin_dir(),
+            "bring-disks-online.ps1")
+
+        self._write_local_script(
+            cloudbaseinit_base_dir, disks_script,
+            priority=99)
+
+    def _install_cloudbase_init(self, download_url,
+                                metadata_services=None, enabled_plugins=None,
+                                com_port="COM1"):
+        self._event_manager.progress_update("Adding cloudbase-init")
+        cloudbaseinit_base_dir = "%sCloudbase-Init" % self._os_root_dir
+
+        key_name = str(uuid.uuid4())
+        self._load_registry_hive(
+            "HKLM\\%s" % key_name,
+            "%sWindows\\System32\\config\\SYSTEM" % self._os_root_dir)
+        try:
+            if self._check_cloudbase_init_exists(key_name):
+                self._event_manager.progress_update(
+                    "Enabling cloudbase-init")
+                self._set_service_start_mode(
+                    key_name, CLOUDBASEINIT_SERVICE_NAME,
+                    SERVICE_START_AUTO)
+            else:
+                cloudbaseinit_zip_path = "c:\\cloudbaseinit.zip"
+
+                self._event_manager.progress_update(
+                    "Downloading cloudbase-init")
+                utils.retry_on_error(sleep_seconds=5)(
+                    self._conn.download_file)(
+                        download_url,
+                        cloudbaseinit_zip_path)
+
+                self._event_manager.progress_update(
+                    "Installing cloudbase-init")
+                self._expand_archive(cloudbaseinit_zip_path,
+                                     cloudbaseinit_base_dir)
+
+                log_dir = "%s\\Log" % cloudbaseinit_base_dir
+                self._conn.exec_ps_command("mkdir '%s' -Force" % log_dir,
+                                           ignore_stdout=True)
+
+                local_base_dir = "C%s" % cloudbaseinit_base_dir[1:]
+                self._write_cloudbase_init_conf(
+                    cloudbaseinit_base_dir, local_base_dir,
+                    metadata_services=metadata_services,
+                    plugins=enabled_plugins, com_port=com_port)
+
+                image_path = (
+                    '"%(path)s\\Bin\\OpenStackService.exe" '
+                    'cloudbase-init "%(path)s\\Python\\Python.exe" -c '
+                    '"from cloudbaseinit import shell;shell.main()" '
+                    '--config-file "%(path)s\\conf\\cloudbase-init.conf"'
+                    % {'path': local_base_dir})
+
+                self._create_service(
+                    key_name=key_name,
+                    service_name=CLOUDBASEINIT_SERVICE_NAME,
+                    image_path=image_path,
+                    display_name="Cloud Initialization Service",
+                    description="Service wrapper for cloudbase-init")
+        finally:
+            self._unload_registry_hive("HKLM\\%s" % key_name)
+
+        return cloudbaseinit_base_dir

+ 106 - 0
coriolis/resources/bin/bring-disks-online.ps1

@@ -0,0 +1,106 @@
+function Convert-RawDetailsToHashmap {
+    [CmdletBinding()]
+    Param(
+        [Parameter(Mandatory=$true)]
+        [object]$RawData
+    )
+
+    $details = @{}
+
+    foreach($i in $RawData) {
+        if ($i.Contains(":")) {
+            $kv = $i.Split(":", 2)
+            if ($kv.Count -ne 2) {
+                continue
+            }
+            $details[$kv[0].Trim()] = $kv[1].Trim()
+        }
+    }
+    return $details
+}
+
+function Invoke-DiskpartScript {
+    [CmdletBinding()]
+    Param(
+        [Parameter(Mandatory=$true)]
+        [String]$Script
+    )
+
+    $scriptName = Join-Path $env:TMP ("diskpart-script-{0}.txt" -f (Get-Random))
+    Set-Content $scriptName $Script
+    $rawDetails = diskpart /s $scriptName
+    if ($LASTEXITCODE) {
+        rm -Force $scriptName
+        Throw "Failed to run diskpart $LASTEXITCODE"
+    }
+    rm $scriptName
+    return $rawDetails
+}
+
+function Get-DiskDetails {
+    [CmdletBinding()]
+    Param(
+        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
+        [Int]$DiskIndex
+    )
+
+    $disks = gcim -ClassName Win32_DiskDrive | Where-Object {$_.Index -eq $DiskIndex}
+    if (!$disks){
+        Throw "Cannot find disk with index $DiskIndex"
+    }
+
+    $rawDetailsScript = "select disk {0}`ndetail disk" -f $DiskIndex
+    $rawDetails = Invoke-DiskpartScript $rawDetailsScript
+    $details = Convert-RawDetailsToHashmap $rawDetails
+    $details["Index"] = $DiskIndex
+    return $details
+}
+
+function Set-DiskOnline {
+    [CmdletBinding()]
+    Param(
+        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
+        [hashtable]$DiskDetails
+    )
+
+    if ($DiskDetails["Status"].Contains("Offline")) {
+        $onlineScript = "select disk {0}`nonline disk" -f $DiskDetails["Index"]
+        Invoke-DiskpartScript $onlineScript | Out-Null
+    }
+    return Get-DiskDetails $DiskDetails["Index"]
+}
+
+function Import-ForeignDisk {
+    [CmdletBinding()]
+    Param(
+        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
+        [hashtable]$DiskDetails
+    )
+
+    if($DiskDetails["Status"] -ne "Foreign") {
+        return
+    }
+
+    if($DiskDetails["Current Read-only State"] -eq "Yes" -or $DiskDetails["Read-only"] -eq "Yes") {
+        $setRW = "SELECT DISK {0}`nATTRIBUTES DISK CLEAR READONLY" -f $DiskDetails["Index"]
+        Invoke-DiskpartScript $setRW | Out-Null
+    }
+
+    $importScript = "select disk {0}`nIMPORT" -f $DiskDetails["Index"]
+    Invoke-DiskpartScript $importScript | Out-Null
+
+    return Get-DiskDetails $DiskDetails["Index"]
+}
+
+function Invoke-Main {
+    $disks = (gcim -ClassName Win32_DiskDrive).Index
+    foreach($dsk in $disks) {
+        Get-DiskDetails $dsk | Set-DiskOnline | Import-ForeignDisk | Out-Null
+    }
+    Get-Disk | Set-Disk -IsOffline:$false
+    Get-Disk | Where-Object {$_.IsBoot -eq $false} | Set-Disk -IsReadOnly:$false
+}
+
+Invoke-Main
+Start-Sleep 60
+Invoke-Main