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

Add Powershell script to set static IPs on Windows VMs

Daniel Vincze 5 лет назад
Родитель
Сommit
294d7555cf
1 измененных файлов с 172 добавлено и 2 удалено
  1. 172 2
      coriolis/osmorphing/windows.py

+ 172 - 2
coriolis/osmorphing/windows.py

@@ -1,7 +1,10 @@
 # Copyright 2016 Cloudbase Solutions Srl
 # Copyright 2016 Cloudbase Solutions Srl
 # All Rights Reserved.
 # All Rights Reserved.
 
 
+import base64
 import copy
 import copy
+import ipaddress
+import json
 import os
 import os
 import re
 import re
 import uuid
 import uuid
@@ -68,6 +71,85 @@ CLOUDBASE_INIT_DEFAULT_METADATA_SVCS = [
 REQUIRED_DETECTED_WINDOWS_OS_FIELDS = [
 REQUIRED_DETECTED_WINDOWS_OS_FIELDS = [
     "version_number", "edition_id", "installation_type", "product_name"]
     "version_number", "edition_id", "installation_type", "product_name"]
 
 
+INTERFACES_PATH_FORMAT = (
+    "HKLM:\\%s\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces")
+
+STATIC_IP_SCRIPT_TEMPLATE = """
+$ErrorActionPreference = "Stop"
+
+function Get-InterfaceByMacAddress {
+    [CmdletBinding()]
+    Param(
+        [Parameter(Mandatory=$true)]
+        [Object]$mac_address
+    )
+
+    $win_formatted_mac_address = $mac_address.replace(':', '-').toUpper()
+    return (Get-NetAdapter | Where MacAddress -eq $win_formatted_mac_address)
+}
+
+function Set-IPAddresses {
+    [CmdletBinding()]
+    Param(
+        [Parameter(Mandatory=$true)]
+        [Object]$interfaceObj,
+
+        [Parameter(Mandatory=$true)]
+        [Array]$ip_addresses,
+
+        [Parameter(Mandatory=$true)]
+        [Array]$ips_info
+    )
+
+    foreach ($ip in $ip_addresses) {
+        $ip_info = $ips_info | Where ip_address -eq $ip
+        if ($ip_info.default_gateway) {
+            # Remove existing gateway before setting default gateway on the interface
+            Get-NetRoute | Where NextHop -eq $ip_info.default_gateway | Remove-NetRoute -Confirm:$false
+            New-NetIPAddress -InterfaceIndex $interfaceObj.ifIndex -IPAddress $ip_info.ip_address -PrefixLength $ip_info.prefix_length -DefaultGateway $ip_info.default_gateway
+        } else {
+            New-NetIPAddress -InterfaceIndex $interfaceObj.ifIndex -IPAddress $ip_info.ip_address -PrefixLength $ip_info.prefix_length
+        }
+
+        if ($ip_info.dns_addresses) {
+            # Set DNS Addresses on the interface
+            Set-DnsClientServerAddress -InterfaceIndex $interfaceObj.ifIndex -ServerAddresses $ip_info.dns_addresses
+        }
+    }
+}
+
+function Invoke-Main {
+    [CmdletBinding()]
+    Param(
+        [Parameter(Mandatory=$true)]
+        [Array]$nics_info,
+
+        [Parameter(Mandatory=$true)]
+        [Array]$ips_info
+    )
+
+    foreach ($nic in $nics_info) {
+        $interface = Get-InterfaceByMacAddress $nic.mac_address
+        if (-Not $interface) {
+            Write-Host "No interface found for MAC address: $($nic.mac_address)"
+            continue
+        }
+
+        # Removes existing IP configuration for the interface
+        Remove-NetIPAddress -InterfaceIndex $interface.ifIndex -Confirm:$false
+
+        Set-IPAddresses $interface $nic.ip_addresses $ips_info
+    }
+}
+
+$nics_info_json = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("%(nics_info)s"))
+$ips_info_json = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String("%(ips_info)s"))
+$NICS_INFO = ConvertFrom-Json $nics_info_json
+$IPS_INFO = ConvertFrom-Json $ips_info_json
+
+Invoke-Main $NICS_INFO $IPS_INFO
+""" # noqa
+
 
 
 class BaseWindowsMorphingTools(base.BaseOSMorphingTools):
 class BaseWindowsMorphingTools(base.BaseOSMorphingTools):
 
 
@@ -301,6 +383,9 @@ class BaseWindowsMorphingTools(base.BaseOSMorphingTools):
         self._set_service_start_mode(key_name, CLOUDBASEINIT_SERVICE_NAME,
         self._set_service_start_mode(key_name, CLOUDBASEINIT_SERVICE_NAME,
                                      SERVICE_START_AUTO)
                                      SERVICE_START_AUTO)
 
 
+    def _get_cbslinit_base_dir(self):
+        return "%sCloudbase-Init" % self._os_root_dir
+
     def _get_cbslinit_scripts_dir(self, base_dir):
     def _get_cbslinit_scripts_dir(self, base_dir):
         return ("%s\\LocalScripts" % base_dir)
         return ("%s\\LocalScripts" % base_dir)
 
 
@@ -381,7 +466,7 @@ class BaseWindowsMorphingTools(base.BaseOSMorphingTools):
                                 metadata_services=None, enabled_plugins=None,
                                 metadata_services=None, enabled_plugins=None,
                                 com_port="COM1"):
                                 com_port="COM1"):
         self._event_manager.progress_update("Adding cloudbase-init")
         self._event_manager.progress_update("Adding cloudbase-init")
-        cloudbaseinit_base_dir = "%sCloudbase-Init" % self._os_root_dir
+        cloudbaseinit_base_dir = self._get_cbslinit_base_dir()
 
 
         key_name = str(uuid.uuid4())
         key_name = str(uuid.uuid4())
         self._load_registry_hive(
         self._load_registry_hive(
@@ -433,8 +518,93 @@ class BaseWindowsMorphingTools(base.BaseOSMorphingTools):
 
 
         return cloudbaseinit_base_dir
         return cloudbaseinit_base_dir
 
 
+    def _compile_static_ip_conf_from_registry(self, key_name):
+        ips_info = []
+        interfaces_reg_path = INTERFACES_PATH_FORMAT % key_name
+        interfaces = self._conn.exec_ps_command(
+            "(((Get-ChildItem -Path '%s').Name | Select-String -Pattern "
+            "'[^\\\\]+$').Matches).Value" % interfaces_reg_path)
+        for interface in interfaces.splitlines():
+            reg_path = '%s\\%s' % (interfaces_reg_path, interface)
+            enable_dhcp = self._conn.exec_ps_command(
+                "(Get-ItemProperty -Path '%s').EnableDHCP" % reg_path)
+            if enable_dhcp == '0':
+                ip_address = self._conn.exec_ps_command(
+                    "(Get-ItemProperty -Path '%s').IPAddress" % reg_path)
+                subnet_mask = self._conn.exec_ps_command(
+                    "(Get-ItemProperty -Path '%s').SubnetMask" % reg_path)
+                if not (ip_address and subnet_mask):
+                    LOG.warning(
+                        "No IP Address or Subnet Mask found for interface: "
+                        "'%s'" % interface)
+                    continue
+                prefix_length = ipaddress.IPv4Network(
+                    (0, subnet_mask)).prefixlen
+                default_gateway = self._conn.exec_ps_command(
+                    "(Get-ItemProperty -Path '%s').DefaultGateway" % reg_path)
+                name_server = self._conn.exec_ps_command(
+                    "(Get-ItemProperty -Path '%s').NameServer" % reg_path)
+                ip_info = {
+                    "ip_address": ip_address,
+                    "prefix_length": prefix_length,
+                    "default_gateway": default_gateway,
+                    "dns_addresses": name_server}
+                LOG.debug(
+                    "Found static IP configuration for interface '%s': "
+                    "%s" % (interface, ip_info))
+                ips_info.append(ip_info)
+            else:
+                LOG.debug(
+                    "Could not find a static IP configuration for interface: "
+                    "'%s'" % interface)
+                continue
+
+        return ips_info
+
+    def _check_ips_info(self, nics_info, ips_info):
+        source_ip_addresses = set()
+        reg_ip_addresses = set()
+        for nic in nics_info:
+            source_ip_addresses.update(nic.get('ip_addresses', []))
+
+        for ip_info in ips_info:
+            if ip_info.get('ip_address'):
+                reg_ip_addresses.add(ip_info['ip_address'])
+
+        diff = source_ip_addresses - reg_ip_addresses
+        if diff:
+            raise exception.OSMorphingException(
+                "The following IP(s): %s found on the source VM's NICs were "
+                "not found in the registry. Please check whether the static "
+                "IP configurations on the source machine are properly set up "
+                "and retry the migration." % diff)
+
+    def _write_static_ip_script(self, base_dir, nics_info, ips_info):
+        scripts_dir = self._get_cbslinit_scripts_dir(base_dir)
+        script_path = "%s\\01-static-ip-config.ps1" % scripts_dir
+        nics_info_dump = json.dumps(nics_info)
+        ips_info_dump = json.dumps(ips_info)
+        contents = STATIC_IP_SCRIPT_TEMPLATE % {
+            'nics_info': base64.b64encode(nics_info_dump.encode()).decode(),
+            'ips_info': base64.b64encode(ips_info_dump.encode()).decode()}
+        utils.write_winrm_file(self._conn, script_path, contents.encode())
+
     def set_net_config(self, nics_info, dhcp):
     def set_net_config(self, nics_info, dhcp):
-        pass
+        if dhcp:
+            return
+
+        key_name = str(uuid.uuid4())
+        self._load_registry_hive(
+            "HKLM\\%s" % key_name,
+            "%sWindows\\System32\\config\\SYSTEM" % self._os_root_dir)
+        try:
+            cbslinit_base_dir = self._get_cbslinit_base_dir()
+            ips_info = self._compile_static_ip_conf_from_registry(key_name)
+            self._check_ips_info(nics_info, ips_info)
+            self._write_static_ip_script(
+                cbslinit_base_dir, nics_info, ips_info)
+        finally:
+            self._unload_registry_hive("HKLM\\%s" % key_name)
 
 
     def get_packages(self):
     def get_packages(self):
         return [], []
         return [], []