Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 68 additions & 1 deletion coriolis/osmorphing/windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@
INTERFACES_PATH_FORMAT = (
"HKLM:\\%s\\ControlSet001\\Services\\Tcpip\\Parameters\\Interfaces")

NET_ADAPTER_CLASS_BASE_PATH_FORMAT = (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should actually be DEVICE_CLASS_BASE_PATH_FORMAT. Please rename it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, this apperently is the wrong path.
Apologies for that, I've mistaken the Interface name with the interface description. We're looking for interface name actually, that's the thing that needs to be kept.
You will find it in:
HKLM:\SYSTEM\ControlSet001\Control\Network\{SOME_RANDOM_WINDOWS_GUID}\{NetGUID}\Connection
Key Name

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll look into finding the MAC address somewhere else as well, it seems like that's not the relevant location either.

"HKLM:\\%s\\ControlSet001\\Control\\Class")

STATIC_IP_SCRIPT_TEMPLATE = """
$ErrorActionPreference = "Stop"

Expand Down Expand Up @@ -175,6 +178,16 @@
Set-DnsClientServerAddress -InterfaceIndex $interface.IfIndex -ResetServerAddresses

Set-IPAddresses $interface $nic.ip_addresses $ips_info

if ($nic.PSObject.Properties['interface_name'] -and $nic.interface_name) {
$conflictAdapter = Get-NetAdapter -Name $nic.interface_name -ErrorAction SilentlyContinue
if ($conflictAdapter -and $conflictAdapter.IfIndex -ne $interface.IfIndex) {
Write-Host "Cannot rename adapter '$($interface.Name)' to '$($nic.interface_name)': name already in use by another adapter"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will almost always fail. We have to also delete the original NIC after we successfully gather all the information about it.
Deleting the source NIC will also help with Windows warnings like "Static IP conflict" that appears on the UI when the same static IP is set on the destination NIC.

} else {
Rename-NetAdapter -Name $interface.Name -NewName $nic.interface_name
Write-Host "Renamed network adapter '$($interface.Name)' to '$($nic.interface_name)'"
}
}
}
}

Expand Down Expand Up @@ -599,12 +612,46 @@ def _install_cloudbase_init(self, download_url,

return cloudbaseinit_base_dir

def _get_guid_to_adapter_info_map(self, key_name):
class_base_path = NET_ADAPTER_CLASS_BASE_PATH_FORMAT % key_name
ps_command = (
"$ErrorActionPreference = 'SilentlyContinue'; "
"$classBase = '%(base)s'; "
"$netClassGuid = (Get-ChildItem $classBase "
"-ErrorAction SilentlyContinue | "
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have to pass the error action twice?

"ForEach-Object { $p = Get-ItemProperty $_.PSPath "
"-ErrorAction SilentlyContinue; "
"if ($p.Class -eq 'Net') { $_.PSChildName } }); "
"Get-ChildItem -Path \"$classBase\\$netClassGuid\" "
"-ErrorAction SilentlyContinue | "
"ForEach-Object { $p = Get-ItemProperty -Path $_.PSPath "
"-ErrorAction SilentlyContinue; "
"if ($p.NetCfgInstanceId -and $p.DriverDesc) "
"{ \"$($p.NetCfgInstanceId)|$($p.DriverDesc)|"
"$($p.NetworkAddress)\" } }; "
"exit 0" % {'base': class_base_path})
result = self._conn.exec_ps_command(ps_command)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please log this result output?

guid_map = {}
for line in result.splitlines():
if '|' in line:
parts = line.split('|', 2)
guid = parts[0].strip().upper()
desc = parts[1].strip()
mac = parts[2].strip() if len(parts) > 2 else ''
guid_map[guid] = {'driver_desc': desc, 'network_address': mac}
if mac:
LOG.debug(
"Found NetworkAddress '%s' for adapter '%s' "
"(GUID: %s)", mac, desc, guid)
return guid_map

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)
guid_to_adapter_info = self._get_guid_to_adapter_info_map(key_name)
for interface in interfaces.splitlines():
reg_path = '%s\\%s' % (interfaces_reg_path, interface)
enable_dhcp = self._conn.exec_ps_command(
Expand All @@ -630,11 +677,21 @@ def _compile_static_ip_conf_from_registry(self, key_name):
prefix_lengths.append(
ipaddress.IPv4Network((0, submask)).prefixlen)

adapter_info = guid_to_adapter_info.get(interface.upper(), {})
driver_desc = adapter_info.get('driver_desc', '')
network_address = adapter_info.get('network_address', '')
if not driver_desc:
LOG.warning(
"Could not find DriverDesc for interface GUID '%s'. "
"Adapter name will not be preserved." % interface)

ip_info = {
"ip_addresses": ip_addresses.splitlines(),
"prefix_lengths": prefix_lengths,
"default_gateway": default_gateway,
"dns_addresses": name_server}
"dns_addresses": name_server,
"interface_name": driver_desc,
"network_address": network_address}
LOG.debug(
"Found static IP configuration for interface '%s': "
"%s" % (interface, ip_info))
Expand Down Expand Up @@ -678,6 +735,16 @@ def _get_static_nics_info(self, nics_info, ips_info):
f"NIC ({nic.get('mac_address')}). Skipping")
continue
static_nic['ip_addresses'] = ip_matches
for ip_info in ips_info:
if set(static_nic['ip_addresses']).intersection(
set(ip_info.get('ip_addresses', []))):
interface_name = ip_info.get('interface_name')
if interface_name:
static_nic['interface_name'] = interface_name
network_address = ip_info.get('network_address')
if network_address:
static_nic['network_address'] = network_address
break
static_nics_info.append(static_nic)

return static_nics_info
Expand Down
67 changes: 66 additions & 1 deletion coriolis/tests/osmorphing/test_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,8 +677,28 @@ def test_install_cloudbase_init_existing_service(
"HKLM\\%s" % mock_uuid4.return_value)

def test__compile_static_ip_conf_from_registry(self):
class_base_path = (windows.NET_ADAPTER_CLASS_BASE_PATH_FORMAT %
mock.sentinel.key_name)
adapter_map_ps_cmd = (
"$ErrorActionPreference = 'SilentlyContinue'; "
"$classBase = '%(base)s'; "
"$netClassGuid = (Get-ChildItem $classBase "
"-ErrorAction SilentlyContinue | "
"ForEach-Object { $p = Get-ItemProperty $_.PSPath "
"-ErrorAction SilentlyContinue; "
"if ($p.Class -eq 'Net') { $_.PSChildName } }); "
"Get-ChildItem -Path \"$classBase\\$netClassGuid\" "
"-ErrorAction SilentlyContinue | "
"ForEach-Object { $p = Get-ItemProperty -Path $_.PSPath "
"-ErrorAction SilentlyContinue; "
"if ($p.NetCfgInstanceId -and $p.DriverDesc) "
"{ \"$($p.NetCfgInstanceId)|$($p.DriverDesc)|"
"$($p.NetworkAddress)\" } }; "
"exit 0" % {'base': class_base_path})

self.conn.exec_ps_command.side_effect = [
'interface1\ninterface2',
'',
'0',
'192.168.1.254',
'8.8.8.8',
Expand All @@ -696,6 +716,7 @@ def test__compile_static_ip_conf_from_registry(self):
mock.call(
"(((Get-ChildItem -Path '%s').Name | Select-String -Pattern "
"'[^\\\\]+$').Matches).Value" % interfaces_reg_path),
mock.call(adapter_map_ps_cmd),
mock.call(
"(Get-ItemProperty -Path '%s\\interface1').EnableDHCP" %
interfaces_reg_path),
Expand All @@ -720,13 +741,35 @@ def test__compile_static_ip_conf_from_registry(self):
{"ip_addresses": ['192.168.1.1'],
"prefix_lengths": [24],
"default_gateway": '192.168.1.254',
"dns_addresses": '8.8.8.8'}
"dns_addresses": '8.8.8.8',
"interface_name": '',
"network_address": ''}
]
self.assertEqual(result, expected_ips_info)

def test_compile_static_ip_conf_from_registry_no_ip_or_subnet(self):
class_base_path = (windows.NET_ADAPTER_CLASS_BASE_PATH_FORMAT %
mock.sentinel.key_name)
adapter_map_ps_cmd = (
"$ErrorActionPreference = 'SilentlyContinue'; "
"$classBase = '%(base)s'; "
"$netClassGuid = (Get-ChildItem $classBase "
"-ErrorAction SilentlyContinue | "
"ForEach-Object { $p = Get-ItemProperty $_.PSPath "
"-ErrorAction SilentlyContinue; "
"if ($p.Class -eq 'Net') { $_.PSChildName } }); "
"Get-ChildItem -Path \"$classBase\\$netClassGuid\" "
"-ErrorAction SilentlyContinue | "
"ForEach-Object { $p = Get-ItemProperty -Path $_.PSPath "
"-ErrorAction SilentlyContinue; "
"if ($p.NetCfgInstanceId -and $p.DriverDesc) "
"{ \"$($p.NetCfgInstanceId)|$($p.DriverDesc)|"
"$($p.NetworkAddress)\" } }; "
"exit 0" % {'base': class_base_path})

self.conn.exec_ps_command.side_effect = [
'interface1',
'',
'0',
"default_gateway",
"nameservers",
Expand All @@ -745,6 +788,7 @@ def test_compile_static_ip_conf_from_registry_no_ip_or_subnet(self):
mock.call(
"(((Get-ChildItem -Path '%s').Name | Select-String -Pattern "
"'[^\\\\]+$').Matches).Value" % interfaces_reg_path),
mock.call(adapter_map_ps_cmd),
mock.call("(Get-ItemProperty -Path '%s\\interface1').EnableDHCP" %
interfaces_reg_path),
mock.call(
Expand All @@ -760,8 +804,28 @@ def test_compile_static_ip_conf_from_registry_no_ip_or_subnet(self):
])

def test_compile_static_ip_conf_from_registry_no_static_ip(self):
class_base_path = (windows.NET_ADAPTER_CLASS_BASE_PATH_FORMAT %
mock.sentinel.key_name)
adapter_map_ps_cmd = (
"$ErrorActionPreference = 'SilentlyContinue'; "
"$classBase = '%(base)s'; "
"$netClassGuid = (Get-ChildItem $classBase "
"-ErrorAction SilentlyContinue | "
"ForEach-Object { $p = Get-ItemProperty $_.PSPath "
"-ErrorAction SilentlyContinue; "
"if ($p.Class -eq 'Net') { $_.PSChildName } }); "
"Get-ChildItem -Path \"$classBase\\$netClassGuid\" "
"-ErrorAction SilentlyContinue | "
"ForEach-Object { $p = Get-ItemProperty -Path $_.PSPath "
"-ErrorAction SilentlyContinue; "
"if ($p.NetCfgInstanceId -and $p.DriverDesc) "
"{ \"$($p.NetCfgInstanceId)|$($p.DriverDesc)|"
"$($p.NetworkAddress)\" } }; "
"exit 0" % {'base': class_base_path})

self.conn.exec_ps_command.side_effect = [
'interface1',
'',
'1',
]
interfaces_reg_path = (windows.INTERFACES_PATH_FORMAT %
Expand All @@ -775,6 +839,7 @@ def test_compile_static_ip_conf_from_registry_no_static_ip(self):
mock.call(
"(((Get-ChildItem -Path '%s').Name | Select-String -Pattern "
"'[^\\\\]+$').Matches).Value" % interfaces_reg_path),
mock.call(adapter_map_ps_cmd),
mock.call("(Get-ItemProperty -Path '%s\\interface1').EnableDHCP" %
interfaces_reg_path),
])
Expand Down
Loading