Windows testing - share code for creating a network device (#59847)

This commit is contained in:
Jordan Borean 2019-08-01 07:43:31 +10:00 committed by GitHub
parent dacfd72bd6
commit 093e2460b2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 206 additions and 768 deletions

View file

@ -0,0 +1,5 @@
---
- name: remove dummy network adapter device
win_device:
name: '{{ network_device_name_raw.name }}'
state: absent

View file

@ -0,0 +1,22 @@
# Creates a network adapter device for testing purposes and registers the following vars
# network_device_name: The name of the network device
# network_adapter_name: The name of the network adapter
---
- name: create dummy network adapter device
win_device:
path: '%WinDir%\Inf\netloop.inf'
hardware_id: '*msloop'
state: present
register: network_device_name_raw
notify: remove dummy network adapter device
- set_fact:
network_device_name: '{{ network_device_name_raw.name }}'
- name: get name of the dummy network adapter
win_shell: (Get-CimInstance -Class Win32_NetworkAdapter -Filter "Name='{{ network_device_name }}'").NetConnectionID
changed_when: False
register: network_adapter_name_raw
- set_fact:
network_adapter_name: '{{ network_adapter_name_raw.stdout | trim }}'

View file

@ -0,0 +1,2 @@
dependencies:
- setup_win_device

View file

@ -1,28 +1,178 @@
--- ---
- name: create dummy network adapter device
win_device:
path: '%WinDir%\Inf\netloop.inf'
hardware_id: '*msloop'
state: present
register: test_device_name
- set_fact: - set_fact:
test_device_name: '{{ test_device_name.name }}' get_ip_script: |
$adapter = Get-CimInstance -ClassName Win32_NetworkAdapter -Filter "NetConnectionID='{{ network_adapter_name }}'"
$config = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "Index=$($adapter.DeviceID)"
$ips = $config.DNSServerSearchOrder
if ($ips) {
$config.DNSServerSearchOrder[0]
$config.DNSServerSearchOrder[1]
}
- block: - name: set a single IPv4 address (check mode)
- name: get name of the dummy network adapter win_dns_client:
win_shell: (Get-CimInstance -Class Win32_NetworkAdapter -Filter "Name='{{ test_device_name }}'").NetConnectionID adapter_name: '{{ network_adapter_name }}'
changed_when: False ipv4_addresses: 192.168.34.5
register: test_adapter register: set_single_check
check_mode: yes
- set_fact: - name: get result of set a single IPv4 address (check mode)
test_adapter: '{{ test_adapter.stdout | trim }}' win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_single_actual_check
- name: run tests - name: assert set a single IPv4 address (check mode)
include_tasks: tests.yml assert:
that:
- set_single_check is changed
- set_single_actual_check.stdout_lines == []
always: - name: set a single IPv4 address
- name: remove dummy network adapter device win_dns_client:
win_device: adapter_name: '{{ network_adapter_name }}'
name: '{{ test_device_name }}' ipv4_addresses: 192.168.34.5
state: absent register: set_single
- name: get result of set a single IPv4 address
win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_single_actual
- name: assert set a single IPv4 address
assert:
that:
- set_single is changed
- set_single_actual.stdout_lines == ["192.168.34.5"]
- name: set a single IPv4 address (idempotent)
win_dns_client:
adapter_name: '{{ network_adapter_name }}'
ipv4_addresses: 192.168.34.5
register: set_single_again
- name: assert set a single IPv4 address (idempotent)
assert:
that:
- not set_single_again is changed
- name: change IPv4 address to another value (check mode)
win_dns_client:
adapter_name: '{{ network_adapter_name }}'
ipv4_addresses: 192.168.34.6
register: change_single_check
check_mode: yes
- name: get result of change IPv4 address to another value (check mode)
win_shell: '{{ get_ip_script }}'
changed_when: no
register: check_single_actual_check
- name: assert change IPv4 address to another value (check mode)
assert:
that:
- change_single_check is changed
- check_single_actual_check.stdout_lines == ["192.168.34.5"]
- name: change IPv4 address to another value
win_dns_client:
adapter_name: '{{ network_adapter_name }}'
ipv4_addresses: 192.168.34.6
register: change_single
- name: get result of change IPv4 address to another value
win_shell: '{{ get_ip_script }}'
changed_when: no
register: check_single_actual
- name: assert change IPv4 address to another value
assert:
that:
- change_single is changed
- check_single_actual.stdout_lines == ["192.168.34.6"]
- name: set multiple IPv4 addresses (check mode)
win_dns_client:
adapter_name: '{{ network_adapter_name }}'
ipv4_addresses:
- 192.168.34.7
- 192.168.34.8
register: set_multiple_check
check_mode: yes
- name: get result of set multiple IPv4 addresses (check mode)
win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_multiple_actual_check
- name: assert set multiple IPv4 addresses (check mode)
assert:
that:
- set_multiple_check is changed
- set_multiple_actual_check.stdout_lines == ["192.168.34.6"]
- name: set multiple IPv4 addresses
win_dns_client:
adapter_name: '{{ network_adapter_name }}'
ipv4_addresses:
- 192.168.34.7
- 192.168.34.8
register: set_multiple
- name: get result of set multiple IPv4 addresses
win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_multiple_actual
- name: assert set multiple IPv4 addresses
assert:
that:
- set_multiple is changed
- set_multiple_actual.stdout_lines == ["192.168.34.7", "192.168.34.8"]
- name: set multiple IPv4 addresses (idempotent)
win_dns_client:
adapter_name: '{{ network_adapter_name }}'
ipv4_addresses:
- 192.168.34.7
- 192.168.34.8
register: set_multiple_again
- name: assert set multiple IPv4 addresses (idempotent)
assert:
that:
- not set_multiple_again is changed
- name: reset IPv4 DNS back to DHCP (check mode)
win_dns_client:
adapter_name: '{{ network_adapter_name }}'
ipv4_addresses: []
register: set_dhcp_check
check_mode: yes
- name: get result of reset IPv4 DNS back to DHCP (check mode)
win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_dhcp_actual_check
- name: assert reset IPv4 DNS back to DHCP (check mode)
assert:
that:
- set_dhcp_check is changed
- set_dhcp_actual_check.stdout_lines == ["192.168.34.7", "192.168.34.8"]
- name: reset IPv4 DNS back to DHCP
win_dns_client:
adapter_name: '{{ network_adapter_name }}'
ipv4_addresses: []
register: set_dhcp
- name: get result of reset IPv4 DNS back to DHCP
win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_dhcp_actual
- name: assert reset IPv4 DNS back to DHCP
assert:
that:
- set_dhcp is changed
- set_dhcp_actual.stdout_lines == []

View file

@ -1,178 +0,0 @@
---
- set_fact:
get_ip_script: |
$adapter = Get-CimInstance -ClassName Win32_NetworkAdapter -Filter "NetConnectionID='{{ test_adapter }}'"
$config = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "Index=$($adapter.DeviceID)"
$ips = $config.DNSServerSearchOrder
if ($ips) {
$config.DNSServerSearchOrder[0]
$config.DNSServerSearchOrder[1]
}
- name: set a single IPv4 address (check mode)
win_dns_client:
adapter_name: '{{ test_adapter }}'
ipv4_addresses: 192.168.34.5
register: set_single_check
check_mode: yes
- name: get result of set a single IPv4 address (check mode)
win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_single_actual_check
- name: assert set a single IPv4 address (check mode)
assert:
that:
- set_single_check is changed
- set_single_actual_check.stdout_lines == []
- name: set a single IPv4 address
win_dns_client:
adapter_name: '{{ test_adapter }}'
ipv4_addresses: 192.168.34.5
register: set_single
- name: get result of set a single IPv4 address
win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_single_actual
- name: assert set a single IPv4 address
assert:
that:
- set_single is changed
- set_single_actual.stdout_lines == ["192.168.34.5"]
- name: set a single IPv4 address (idempotent)
win_dns_client:
adapter_name: '{{ test_adapter }}'
ipv4_addresses: 192.168.34.5
register: set_single_again
- name: assert set a single IPv4 address (idempotent)
assert:
that:
- not set_single_again is changed
- name: change IPv4 address to another value (check mode)
win_dns_client:
adapter_name: '{{ test_adapter }}'
ipv4_addresses: 192.168.34.6
register: change_single_check
check_mode: yes
- name: get result of change IPv4 address to another value (check mode)
win_shell: '{{ get_ip_script }}'
changed_when: no
register: check_single_actual_check
- name: assert change IPv4 address to another value (check mode)
assert:
that:
- change_single_check is changed
- check_single_actual_check.stdout_lines == ["192.168.34.5"]
- name: change IPv4 address to another value
win_dns_client:
adapter_name: '{{ test_adapter }}'
ipv4_addresses: 192.168.34.6
register: change_single
- name: get result of change IPv4 address to another value
win_shell: '{{ get_ip_script }}'
changed_when: no
register: check_single_actual
- name: assert change IPv4 address to another value
assert:
that:
- change_single is changed
- check_single_actual.stdout_lines == ["192.168.34.6"]
- name: set multiple IPv4 addresses (check mode)
win_dns_client:
adapter_name: '{{ test_adapter }}'
ipv4_addresses:
- 192.168.34.7
- 192.168.34.8
register: set_multiple_check
check_mode: yes
- name: get result of set multiple IPv4 addresses (check mode)
win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_multiple_actual_check
- name: assert set multiple IPv4 addresses (check mode)
assert:
that:
- set_multiple_check is changed
- set_multiple_actual_check.stdout_lines == ["192.168.34.6"]
- name: set multiple IPv4 addresses
win_dns_client:
adapter_name: '{{ test_adapter }}'
ipv4_addresses:
- 192.168.34.7
- 192.168.34.8
register: set_multiple
- name: get result of set multiple IPv4 addresses
win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_multiple_actual
- name: assert set multiple IPv4 addresses
assert:
that:
- set_multiple is changed
- set_multiple_actual.stdout_lines == ["192.168.34.7", "192.168.34.8"]
- name: set multiple IPv4 addresses (idempotent)
win_dns_client:
adapter_name: '{{ test_adapter }}'
ipv4_addresses:
- 192.168.34.7
- 192.168.34.8
register: set_multiple_again
- name: assert set multiple IPv4 addresses (idempotent)
assert:
that:
- not set_multiple_again is changed
- name: reset IPv4 DNS back to DHCP (check mode)
win_dns_client:
adapter_name: '{{ test_adapter }}'
ipv4_addresses: []
register: set_dhcp_check
check_mode: yes
- name: get result of reset IPv4 DNS back to DHCP (check mode)
win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_dhcp_actual_check
- name: assert reset IPv4 DNS back to DHCP (check mode)
assert:
that:
- set_dhcp_check is changed
- set_dhcp_actual_check.stdout_lines == ["192.168.34.7", "192.168.34.8"]
- name: reset IPv4 DNS back to DHCP
win_dns_client:
adapter_name: '{{ test_adapter }}'
ipv4_addresses: []
register: set_dhcp
- name: get result of reset IPv4 DNS back to DHCP
win_shell: '{{ get_ip_script }}'
changed_when: no
register: set_dhcp_actual
- name: assert reset IPv4 DNS back to DHCP
assert:
that:
- set_dhcp is changed
- set_dhcp_actual.stdout_lines == []

View file

@ -1,541 +0,0 @@
#!powershell
#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.AddType
$spec = @{
options = @{
hardware_id = @{ type = "str" }
name = @{ type = "str" }
path = @{ type = "path" }
state = @{ type = "str"; choices = @("absent", "present"); default = "present" }
}
required_if = @(
@("state", "present", @("path", "hardware_id"), $true),
@("state", "absent", @(,"name"))
)
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$hardware_id = $module.Params.hardware_id
$name = $module.Params.name
$path = $module.Params.path
$state = $module.Params.state
$module.Result.reboot_required = $false
Add-CSharpType -References @'
using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Text;
namespace Ansible.Device
{
public class NativeHelpers
{
[StructLayout(LayoutKind.Sequential)]
public class SP_DEVINFO_DATA
{
public UInt32 cbSize;
public Guid ClassGuid;
public UInt32 DevInst;
public IntPtr Reserved;
public SP_DEVINFO_DATA()
{
this.cbSize = (UInt32)Marshal.SizeOf(this);
this.ClassGuid = Guid.Empty;
}
}
[Flags]
public enum DeviceInfoCreationFlags : uint
{
DICD_GENERATE_ID = 0x00000001,
DICD_INHERIT_CLASSDRVS = 0x00000002,
}
public enum DeviceProperty : uint
{
SPDRP_DEVICEDESC = 0x0000000,
SPDRP_HARDWAREID = 0x0000001,
SPDRP_COMPATIBLEIDS = 0x0000002,
SPDRP_UNUSED0 = 0x0000003,
SPDRP_SERVICE = 0x0000004,
SPDRP_UNUSED1 = 0x0000005,
SPDRP_UNUSED2 = 0x0000006,
SPDRP_CLASS = 0x0000007, // Read only - tied to ClassGUID
SPDRP_CLASSGUID = 0x0000008,
SPDRP_DRIVER = 0x0000009,
SPDRP_CONFIGFLAGS = 0x000000a,
SPDRP_MFG = 0x000000b,
SPDRP_FRIENDLYNAME = 0x000000c,
SPDRP_LOCATION_INFORMATION = 0x000000d,
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0x000000e, // Read only
SPDRP_CAPABILITIES = 0x000000f, // Read only
SPDRP_UI_NUMBER = 0x0000010, // Read only
SPDRP_UPPERFILTERS = 0x0000011,
SPDRP_LOWERFILTERS = 0x0000012,
SPDRP_BUSTYPEGUID = 0x0000013, // Read only
SPDRP_LEGACYBUSTYPE = 0x0000014, // Read only
SPDRP_BUSNUMBER = 0x0000015, // Read only
SPDRP_ENUMERATOR_NAME = 0x0000016, // Read only
SPDRP_SECURITY = 0x0000017,
SPDRP_SECURITY_SDS = 0x0000018,
SPDRP_DEVTYPE = 0x0000019,
SPDRP_EXCLUSIVE = 0x000001a,
SPDRP_CHARACTERISTICS = 0x000001b,
SPDRP_ADDRESS = 0x000001c, // Read only
SPDRP_UI_NUMBER_DESC_FORMAT = 0x000001d,
SPDRP_DEVICE_POWER_DATA = 0x000001e, // Read only
SPDRP_REMOVAL_POLICY = 0x000001f, // Read only
SPDRP_REMOVAL_POLICY_HW_DEFAULT = 0x0000020, // Read only
SPDRP_REMOVAL_POLICY_OVERRIDE = 0x0000021,
SPDRP_INSTALL_STATE = 0x0000022, // Read only
SPDRP_LOCATION_PATHS = 0x0000023, // Read only
SPDRP_BASE_CONTAINERID = 0x0000024, // Read only
}
// https://docs.microsoft.com/en-us/previous-versions/ff549793%28v%3dvs.85%29
public enum DifCodes : uint
{
DIF_SELECTDIVE = 0x00000001,
DIF_INSTALLDEVICE = 0x00000002,
DIF_ASSIGNRESOURCES = 0x00000003,
DIF_PROPERTIES = 0x00000004,
DIF_REMOVE = 0x00000005,
DIF_FIRSTTIMESETUP = 0x00000006,
DIF_FOUNDDEVICE = 0x00000007,
DIF_SELECTCLASSDRIVERS = 0x00000008,
DIF_VALIDATECLASSDRIVERS = 0x00000009,
DIF_INSTALLCLASSDRIVERS = 0x0000000a,
DIF_CALCDISKSPACE = 0x0000000b,
DIF_DESTROYPRIVATEDATA = 0x0000000c,
DIF_VALIDATEDRIVER = 0x0000000d,
DIF_DETECT = 0x0000000f,
DIF_INSTALLWIZARD = 0x00000010,
DIF_DESTROYWIZARDDATA = 0x00000011,
DIF_PROPERTYCHANGE = 0x00000012,
DIF_ENABLECLASS = 0x00000013,
DIF_DETECTVERIFY = 0x00000014,
DIF_INSTALLDEVICEFILES = 0x00000015,
DIF_UNREMOVE = 0x00000016,
DIF_SELECTBESTCOMPATDRV = 0x00000017,
DIF_ALLOW_INSTALL = 0x00000018,
DIF_REGISTERDEVICE = 0x00000019,
DIF_NEWDEVICEWIZARD_PRESELECT = 0x0000001a,
DIF_NEWDEVICEWIZARD_SELECT = 0x0000001b,
DIF_NEWDEVICEWIZARD_PREANALYZE = 0x0000001c,
DIF_NEWDEVICEWIZARD_POSTANALYZE = 0x0000001d,
DIF_NEWDEVICEWIZARD_FINISHINSTALL = 0x0000001e,
DIF_UNUSED1 = 0x0000001e,
DIF_INSTALLINTERFACES = 0x00000020,
DIF_DETECTCANCEL = 0x00000021,
DIF_REGISTER_COINSTALLERS = 0x00000022,
DIF_ADDPROPERTYPAGE_ADVANCED = 0x00000023,
DIF_ADDPROPERTYPAGE_BASIC = 0x00000024,
DIF_RESERVED1 = 0x00000025,
DIF_TROUBLESHOOTER = 0x00000026,
DIF_POWERMESSAGEWAKE = 0x00000027,
DIF_ADDREMOTEPROPERTYPAGE_ADVANCED = 0x00000028,
DIF_UPDATEDRIVER_UI = 0x00000029,
DIF_FINISHINSTALL_ACTION = 0x0000002a,
}
[Flags]
public enum GetClassFlags : uint
{
DIGCF_DEFAULT = 0x00000001,
DIGCF_PRESENT = 0x00000002,
DIGCF_ALLCLASSES = 0x00000004,
DIGCF_PROFILE = 0x00000008,
DIGCF_DEVICEINTERFACE = 0x00000010,
}
[Flags]
public enum InstallFlags : uint
{
INSTALLFLAG_FORCE = 0x00000001,
INSTALLFLAG_READONLY = 0x00000002,
INSTALLFLAG_NONINTERACTIVE = 0x00000004,
INSTALLFLAG_BITS = 0x00000007,
}
}
public class NativeMethods
{
[DllImport("Setupapi.dll", SetLastError = true)]
public static extern bool SetupDiCallClassInstaller(
NativeHelpers.DifCodes InstallFunction,
SafeDeviceInfoSet DeviceInfoSet,
NativeHelpers.SP_DEVINFO_DATA DeviceInfoData);
[DllImport("Setupapi.dll", SetLastError = true)]
public static extern SafeDeviceInfoSet SetupDiCreateDeviceInfoList(
Guid ClassGuid,
IntPtr hwndParent);
[DllImport("Setupapi.dll", SetLastError = true)]
public static extern bool SetupDiCreateDeviceInfoW(
SafeDeviceInfoSet DeviceInfoSet,
[MarshalAs(UnmanagedType.LPWStr)] string DeviceName,
Guid ClassGuid,
[MarshalAs(UnmanagedType.LPWStr)] string DeviceDescription,
IntPtr hwndParent,
NativeHelpers.DeviceInfoCreationFlags CreationFlags,
NativeHelpers.SP_DEVINFO_DATA DeviceInfoData);
[DllImport("Setupapi.dll", SetLastError = true)]
public static extern bool SetupDiDestroyDeviceInfoList(
IntPtr DeviceInfoSet);
[DllImport("Setupapi.dll", SetLastError = true)]
public static extern bool SetupDiEnumDeviceInfo(
SafeDeviceInfoSet DeviceInfoSet,
UInt32 MemberIndex,
NativeHelpers.SP_DEVINFO_DATA DeviceInfoData);
[DllImport("Setupapi.dll", SetLastError = true)]
public static extern SafeDeviceInfoSet SetupDiGetClassDevsW(
Guid ClassGuid,
[MarshalAs(UnmanagedType.LPWStr)] string Enumerator,
IntPtr hwndParent,
NativeHelpers.GetClassFlags Flags);
[DllImport("Setupapi.dll", SetLastError = true)]
public static extern bool SetupDiGetDeviceRegistryPropertyW(
SafeDeviceInfoSet DeviceInfoSet,
NativeHelpers.SP_DEVINFO_DATA DeviceInfoData,
NativeHelpers.DeviceProperty Property,
out UInt32 PropertyRegDataType,
SafeMemoryBuffer PropertyBuffer,
UInt32 PropertyBufferSize,
ref UInt32 RequiredSize);
[DllImport("Setupapi.dll", SetLastError = true)]
public static extern bool SetupDiGetINFClassW(
[MarshalAs(UnmanagedType.LPWStr)] string InfName,
ref Guid ClassGuid,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder ClassName,
UInt32 ClassNameSize,
ref UInt32 RequiredSize);
[DllImport("Setupapi.dll", SetLastError = true)]
public static extern bool SetupDiSetDeviceRegistryPropertyW(
SafeDeviceInfoSet DeviceInfoSet,
NativeHelpers.SP_DEVINFO_DATA DeviceInfoData,
NativeHelpers.DeviceProperty Property,
SafeMemoryBuffer PropertyBuffer,
UInt32 PropertyBufferSize);
[DllImport("Newdev.dll", SetLastError = true)]
public static extern bool UpdateDriverForPlugAndPlayDevicesW(
IntPtr hwndParent,
[MarshalAs(UnmanagedType.LPWStr)] string HardwareId,
[MarshalAs(UnmanagedType.LPWStr)] string FullInfPath,
NativeHelpers.InstallFlags InstallFlags,
ref bool bRebootRequired);
}
public class SafeDeviceInfoSet : SafeHandleZeroOrMinusOneIsInvalid
{
public SafeDeviceInfoSet() : base(true) { }
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
return NativeMethods.SetupDiDestroyDeviceInfoList(handle);
}
}
public class SafeMemoryBuffer : SafeHandleZeroOrMinusOneIsInvalid
{
public int Length = 0;
public SafeMemoryBuffer() : base(true) { }
public SafeMemoryBuffer(int cb) : base(true)
{
Length = cb;
base.SetHandle(Marshal.AllocHGlobal(cb));
}
public SafeMemoryBuffer(string sz) : base(true)
{
Length = sz.Length * sizeof(char);
base.SetHandle(Marshal.StringToHGlobalUni(sz));
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle);
return true;
}
}
public class DeviceUtil
{
public static string GetDeviceFriendlyName(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo)
{
string friendlyName = GetDeviceStringProp(devInfoSet, devInfo, NativeHelpers.DeviceProperty.SPDRP_FRIENDLYNAME);
// Older Windows versions may not have a friendly name set. This seems to be the case when the device has
// a unique description so we fallback to that value.
if (null == friendlyName)
friendlyName = GetDeviceStringProp(devInfoSet, devInfo, NativeHelpers.DeviceProperty.SPDRP_DEVICEDESC);
return friendlyName;
}
public static void SetDeviceHardwareId(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo,
string hardwareId)
{
SetDeviceStringProp(devInfoSet, devInfo, NativeHelpers.DeviceProperty.SPDRP_HARDWAREID, hardwareId);
}
private static string GetDeviceStringProp(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo,
NativeHelpers.DeviceProperty property)
{
using (SafeMemoryBuffer memBuf = GetDeviceProperty(devInfoSet, devInfo, property))
{
if (memBuf.IsInvalid) // Property does not exist so just return null.
return null;
return Marshal.PtrToStringUni(memBuf.DangerousGetHandle());
}
}
private static SafeMemoryBuffer GetDeviceProperty(SafeDeviceInfoSet devInfoSet,
NativeHelpers.SP_DEVINFO_DATA devInfo, NativeHelpers.DeviceProperty property)
{
UInt32 requiredSize = 0;
UInt32 regDataType = 0;
if (!NativeMethods.SetupDiGetDeviceRegistryPropertyW(devInfoSet, devInfo, property,
out regDataType, new SafeMemoryBuffer(0), 0, ref requiredSize))
{
int errCode = Marshal.GetLastWin32Error();
if (errCode == 0x0000000D) // ERROR_INVALID_DATA
return new SafeMemoryBuffer(); // The FRIENDLYNAME property does not exist
else if (errCode != 0x0000007A) // ERROR_INSUFFICIENT_BUFFER
throw new Win32Exception(errCode);
}
SafeMemoryBuffer memBuf = new SafeMemoryBuffer((int)requiredSize);
if (!NativeMethods.SetupDiGetDeviceRegistryPropertyW(devInfoSet, devInfo, property,
out regDataType, memBuf, requiredSize, ref requiredSize))
{
int errCode = Marshal.GetLastWin32Error();
memBuf.Dispose();
throw new Win32Exception(errCode);
}
return memBuf;
}
private static void SetDeviceStringProp(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo,
NativeHelpers.DeviceProperty property, string value)
{
using (SafeMemoryBuffer buffer = new SafeMemoryBuffer(value))
SetDeviceProperty(devInfoSet, devInfo, property, buffer);
}
private static void SetDeviceProperty(SafeDeviceInfoSet devInfoSet, NativeHelpers.SP_DEVINFO_DATA devInfo,
NativeHelpers.DeviceProperty property, SafeMemoryBuffer buffer)
{
if (!NativeMethods.SetupDiSetDeviceRegistryPropertyW(devInfoSet, devInfo, property, buffer,
(UInt32)buffer.Length))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
'@
Function Get-Win32ErrorMessage {
Param ([System.Int32]$ErrorCode)
$exp = New-Object -TypeName System.ComponentModel.Win32Exception -ArgumentList $ErrorCode
return ("{0} (Win32 ErrorCode {1} - 0x{1:X8}" -f $exp.Message, $ErrorCode)
}
# Determine if the device is already installed
$dev_info_set = [Ansible.Device.NativeMethods]::SetupDiGetClassDevsW(
[Guid]::Empty,
[NullString]::Value,
[System.IntPtr]::Zero,
[Ansible.Device.NativeHelpers+GetClassFlags]::DIGCF_ALLCLASSES
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
try {
if ($dev_info_set.IsInvalid) {
$msg = Get-Win32ErrorMessage -ErrorCode $err
$module.FailJson("Failed to get device information set for installed devices: $msg")
}
$dev_info = $null
if ($null -ne $name) {
# Loop through the set of all devices and compare the name
$idx = 0
while ($true) {
$dev_info = New-Object -TypeName Ansible.Device.NativeHelpers+SP_DEVINFO_DATA
$res = [Ansible.Device.NativeMethods]::SetupDiEnumDeviceInfo(
$dev_info_set,
$idx,
$dev_info
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
if (-not $res) {
$dev_info = $null
if ($err -eq 0x00000103) { # ERROR_NO_MORE_ITEMS
break
}
$msg = Get-Win32ErrorMessage -ErrorCode $err
$module.FailJson("Failed to enumerate device information set at index $($idx): $msg")
}
$device_name = [Ansible.Device.DeviceUtil]::GetDeviceFriendlyName($dev_info_set, $dev_info)
if ($device_name -eq $name) {
break
}
$dev_info = $null
$idx++
}
}
if ($state -eq "absent" -and $null -ne $dev_info) {
if (-not $module.CheckMode) {
$res = [Ansible.Device.NativeMethods]::SetupDiCallClassInstaller(
[Ansible.Device.NativeHelpers+DifCodes]::DIF_REMOVE,
$dev_info_set,
$dev_info
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
if (-not $res) {
$msg = Get-Win32ErrorMessage -ErrorCode $err
$module.FailJson("Failed to remove device $($name): $msg")
}
}
$module.Result.changed = $true
} elseif ($state -eq "present" -and $null -eq $dev_info) {
# Populate the class guid and display name if the path to an inf file was set.
$class_id = [Guid]::Empty
$class_name = $null
if ($path) {
if (-not (Test-Path -LiteralPath $path)) {
$module.FailJson("Could not find the inf file specified at '$path'")
}
$class_name_sb = New-Object -TypeName System.Text.StringBuilder -ArgumentList 32 # MAX_CLASS_NAME_LEN
$required_size = 0
$res = [Ansible.Device.NativeMethods]::SetupDiGetINFClassW(
$path,
[ref]$class_id,
$class_name_sb,
$class_name_sb.Capacity,
[ref]$required_size
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
if (-not $res) {
$msg = Get-Win32ErrorMessage -ErrorCode $err
$module.FailJson("Failed to parse driver inf at '$path': $msg")
}
$class_name = $class_name_sb.ToString()
}
# When creating a new device we want to start with a blank device information set.
$dev_info_set.Dispose()
$dev_info_set = [Ansible.Device.NativeMethods]::SetupDiCreateDeviceInfoList(
$class_id,
[System.IntPtr]::Zero
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
if ($dev_info_set.IsInvalid) {
$msg = Get-Win32ErrorMessage -ErrorCode $err
$module.FailJson("Failed to create device info set for the class $($class_id): $msg")
}
# Create the new device element and add it to the device info set
$dev_info = New-Object -TypeName Ansible.Device.NativeHelpers+SP_DEVINFO_DATA
$res = [Ansible.Device.NativeMethods]::SetupDiCreateDeviceInfoW(
$dev_info_set,
$class_name,
$class_id,
$null,
[System.IntPtr]::Zero,
[Ansible.Device.NativeHelpers+DeviceInfoCreationFlags]::DICD_GENERATE_ID,
$dev_info
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
if (-not $res) {
$msg = Get-Win32ErrorMessage -ErrorCode $err
$module.FailJson("Failed to create new device element for class $($class_name): $msg")
}
# Set the hardware id of the new device so we can load the proper driver.
[Ansible.Device.DeviceUtil]::SetDeviceHardwareId($dev_info_set, $dev_info, $hardware_id)
if (-not $module.CheckMode) {
# Install the device
$res = [Ansible.Device.NativeMethods]::SetupDiCallClassInstaller(
[Ansible.Device.NativeHelpers+DifCodes]::DIF_REGISTERDEVICE,
$dev_info_set,
$dev_info
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
if (-not $res) {
$msg = Get-Win32ErrorMessage -ErrorCode $err
$module.FailJson("Failed to register new device for class $($class_name): $msg")
}
# Load the drivers for the new device
$reboot_required = $false
$res = [Ansible.Device.NativeMethods]::UpdateDriverForPlugAndPlayDevicesW(
[System.IntPtr]::Zero,
$hardware_id,
$path,
[Ansible.Device.NativeHelpers+InstallFlags]'INSTALLFLAG_FORCE, INSTALLFLAG_NONINTERACTIVE',
[ref]$reboot_required
); $err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
if (-not $res) {
# On a failure make sure we cleanup the "installed" device
[Ansible.Device.NativeMethods]::SetupDiCallClassInstaller(
[Ansible.Device.NativeHelpers+DifCodes]::DIF_REMOVE,
$dev_info_set,
$dev_info
) > $null
$msg = Get-Win32ErrorMessage -ErrorCode $err
$module.FailJson("Failed to update device driver: $msg")
}
$module.Result.reboot_required = $reboot_required
# Now get the name of the newly created device which we return back to Ansible.
$name = [Ansible.Device.DeviceUtil]::GetDeviceFriendlyName($dev_info_set, $dev_info)
} else {
# Generate random name for check mode output
$name = "Check mode generated device for $($class_name)"
}
$module.Result.changed = $true
}
} finally {
$dev_info_set.Dispose()
}
$module.Result.name = $name
$module.ExitJson()

View file

@ -0,0 +1,2 @@
dependencies:
- setup_win_device

View file

@ -16,39 +16,15 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
- name: ensure netbios is set to default to start with - name: ensure netbios is set to default to start with
win_netbios: win_netbios:
state: default state: default
- name: create dummy network adapter device
win_device:
path: '%WinDir%\Inf\netloop.inf'
hardware_id: '*msloop'
state: present
register: test_device_name
- set_fact:
test_device_name: '{{ test_device_name.name }}'
- block: - block:
- name: get name of the dummy network adapter
win_shell: (Get-CimInstance -Class Win32_NetworkAdapter -Filter "Name='{{ test_device_name }}'").NetConnectionID
changed_when: False
register: test_adapter
- set_fact:
test_adapter: '{{ test_adapter.stdout | trim }}'
- name: run tests - name: run tests
include_tasks: tests.yml include_tasks: tests.yml
always: always:
- name: remove dummy network adapter device
win_device:
name: '{{ test_device_name }}'
state: absent
- name: set netbios back to default after tests - name: set netbios back to default after tests
win_netbios: win_netbios:
state: default state: default

View file

@ -18,13 +18,13 @@
- set_fact: - set_fact:
get_netbios_script: | get_netbios_script: |
$adapter = Get-CimInstance -ClassName Win32_NetworkAdapter -Filter "NetConnectionID='{{ test_adapter }}'" $adapter = Get-CimInstance -ClassName Win32_NetworkAdapter -Filter "NetConnectionID='{{ network_adapter_name }}'"
$config = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "Index=$($adapter.DeviceID)" $config = Get-CimInstance -ClassName Win32_NetworkAdapterConfiguration -Filter "Index=$($adapter.DeviceID)"
$config.TcpipNetbiosOptions $config.TcpipNetbiosOptions
- name: disable netbios single adapter (check mode) - name: disable netbios single adapter (check mode)
win_netbios: win_netbios:
adapter_names: '{{ test_adapter }}' adapter_names: '{{ network_adapter_name }}'
state: disabled state: disabled
register: set_single_check register: set_single_check
check_mode: yes check_mode: yes
@ -42,7 +42,7 @@
- name: disable netbios single adapter - name: disable netbios single adapter
win_netbios: win_netbios:
adapter_names: '{{ test_adapter }}' adapter_names: '{{ network_adapter_name }}'
state: disabled state: disabled
register: set_single register: set_single