ansible-test - Add Windows remote connection option

pull/81282/merge
Matt Clay 3 months ago
parent a3ee846a64
commit 81e025b414

@ -0,0 +1,2 @@
minor_changes:
- ansible-test - Connection options can be set for ansible-test managed remote Windows instances.

@ -1,6 +1,6 @@
[windows] [windows]
{% for host in vars.groups.windows %} {% for host in vars.groups.windows %}
{{ host }} ansible_host={{ hostvars[host]['ansible_host'] }} ansible_port={{ hostvars[host]['ansible_port'] }} ansible_user={{ hostvars[host]['ansible_user'] }} ansible_password={{ hostvars[host]['ansible_password'] }} {{ host }} ansible_host={{ hostvars[host]['ansible_host'] }}{% if hostvars[host]['ansible_connection'] != 'ssh' %} ansible_port={{ hostvars[host]['ansible_port'] }}{% endif %} ansible_user={{ hostvars[host]['ansible_user'] }} ansible_password={{ hostvars[host]['ansible_password'] | default(hostvars[host]['ansible_test_connection_password']) }}
{% endfor %} {% endfor %}
[windows:vars] [windows:vars]

@ -1,6 +1,6 @@
[windows] [windows]
{% for host in vars.groups.windows %} {% for host in vars.groups.windows %}
{{ host }} ansible_host={{ hostvars[host]['ansible_host'] }} ansible_port={{ hostvars[host]['ansible_port'] }} ansible_user={{ hostvars[host]['ansible_user'] }} ansible_password={{ hostvars[host]['ansible_password'] }} {{ host }} ansible_host={{ hostvars[host]['ansible_host'] }}{% if hostvars[host]['ansible_connection'] != 'ssh' %} ansible_port={{ hostvars[host]['ansible_port'] }}{% endif %} ansible_user={{ hostvars[host]['ansible_user'] }} ansible_password={{ hostvars[host]['ansible_password'] | default(hostvars[host]['ansible_test_connection_password']) }}
{% endfor %} {% endfor %}
[windows:vars] [windows:vars]

@ -1,4 +1,4 @@
windows/2016 provider=aws arch=x86_64 windows/2016 provider=aws arch=x86_64 connection=winrm+http
windows/2019 provider=aws arch=x86_64 windows/2019 provider=aws arch=x86_64 connection=winrm+https
windows/2022 provider=aws arch=x86_64 windows/2022 provider=aws arch=x86_64 connection=winrm+https
windows provider=aws arch=x86_64 windows provider=aws arch=x86_64 connection=winrm+https

@ -17,6 +17,7 @@ from ...completion import (
from ...util import ( from ...util import (
REMOTE_ARCHITECTURES, REMOTE_ARCHITECTURES,
WINDOWS_CONNECTIONS,
) )
from ...host_configs import ( from ...host_configs import (
@ -177,6 +178,7 @@ class WindowsRemoteKeyValueParser(KeyValueParser):
return dict( return dict(
provider=ChoicesParser(REMOTE_PROVIDERS), provider=ChoicesParser(REMOTE_PROVIDERS),
arch=ChoicesParser(REMOTE_ARCHITECTURES), arch=ChoicesParser(REMOTE_ARCHITECTURES),
connection=ChoicesParser(WINDOWS_CONNECTIONS),
) )
def document(self, state: DocumentationState) -> t.Optional[str]: def document(self, state: DocumentationState) -> t.Optional[str]:
@ -186,6 +188,7 @@ class WindowsRemoteKeyValueParser(KeyValueParser):
state.sections[f'target {section_name} (comma separated):'] = '\n'.join([ state.sections[f'target {section_name} (comma separated):'] = '\n'.join([
f' provider={ChoicesParser(REMOTE_PROVIDERS).document(state)}', f' provider={ChoicesParser(REMOTE_PROVIDERS).document(state)}',
f' arch={ChoicesParser(REMOTE_ARCHITECTURES).document(state)}', f' arch={ChoicesParser(REMOTE_ARCHITECTURES).document(state)}',
f' connection={ChoicesParser(WINDOWS_CONNECTIONS).document(state)}',
]) ])
return f'{{{section_name}}}' return f'{{{section_name}}}'

@ -246,6 +246,8 @@ class PosixRemoteCompletionConfig(RemoteCompletionConfig, PythonCompletionConfig
class WindowsRemoteCompletionConfig(RemoteCompletionConfig): class WindowsRemoteCompletionConfig(RemoteCompletionConfig):
"""Configuration for remote Windows platforms.""" """Configuration for remote Windows platforms."""
connection: str = ''
TCompletionConfig = t.TypeVar('TCompletionConfig', bound=CompletionConfig) TCompletionConfig = t.TypeVar('TCompletionConfig', bound=CompletionConfig)

@ -399,10 +399,20 @@ class WindowsConfig(HostConfig, metaclass=abc.ABCMeta):
class WindowsRemoteConfig(RemoteConfig, WindowsConfig): class WindowsRemoteConfig(RemoteConfig, WindowsConfig):
"""Configuration for a remote Windows host.""" """Configuration for a remote Windows host."""
connection: t.Optional[str] = None
def get_defaults(self, context: HostContext) -> WindowsRemoteCompletionConfig: def get_defaults(self, context: HostContext) -> WindowsRemoteCompletionConfig:
"""Return the default settings.""" """Return the default settings."""
return filter_completion(windows_completion()).get(self.name) or windows_completion().get(self.platform) return filter_completion(windows_completion()).get(self.name) or windows_completion().get(self.platform)
def apply_defaults(self, context: HostContext, defaults: CompletionConfig) -> None:
"""Apply default settings."""
assert isinstance(defaults, WindowsRemoteCompletionConfig)
super().apply_defaults(context, defaults)
self.connection = self.connection or defaults.connection
@dataclasses.dataclass @dataclasses.dataclass
class WindowsInventoryConfig(InventoryConfig, WindowsConfig): class WindowsInventoryConfig(InventoryConfig, WindowsConfig):

@ -56,6 +56,7 @@ from .util import (
InternalError, InternalError,
HostConnectionError, HostConnectionError,
ANSIBLE_TEST_TARGET_ROOT, ANSIBLE_TEST_TARGET_ROOT,
WINDOWS_CONNECTION_VARIABLES,
) )
from .util_common import ( from .util_common import (
@ -1367,23 +1368,18 @@ class WindowsRemoteProfile(RemoteProfile[WindowsRemoteConfig]):
connection = core_ci.connection connection = core_ci.connection
variables: dict[str, t.Optional[t.Union[str, int]]] = dict( variables: dict[str, t.Optional[t.Union[str, int]]] = dict(
ansible_connection='winrm',
ansible_pipelining='yes',
ansible_winrm_server_cert_validation='ignore',
ansible_host=connection.hostname, ansible_host=connection.hostname,
ansible_port=connection.port, # ansible_port is intentionally not set using connection.port -- connection-specific variables can set this instead
ansible_user=connection.username, ansible_user=connection.username,
ansible_password=connection.password, ansible_ssh_private_key_file=core_ci.ssh_key.key, # required for scenarios which change the connection plugin to SSH
ansible_ssh_private_key_file=core_ci.ssh_key.key, ansible_test_connection_password=connection.password, # required for scenarios which change the connection plugin to require a password
) )
# HACK: force 2016 to use NTLM + HTTP message encryption variables.update(ansible_connection=self.config.connection.split('+')[0])
if self.config.version == '2016': variables.update(WINDOWS_CONNECTION_VARIABLES[self.config.connection])
variables.update(
ansible_winrm_transport='ntlm', if variables.pop('use_password'):
ansible_winrm_scheme='http', variables.update(ansible_password=connection.password)
ansible_port='5985',
)
return variables return variables

@ -134,6 +134,46 @@ class Architecture:
REMOTE_ARCHITECTURES = list(value for key, value in Architecture.__dict__.items() if not key.startswith('__')) REMOTE_ARCHITECTURES = list(value for key, value in Architecture.__dict__.items() if not key.startswith('__'))
WINDOWS_CONNECTION_VARIABLES: dict[str, t.Any] = {
'psrp+http': dict(
ansible_port=5985,
ansible_psrp_protocol='http',
use_password=True,
),
'psrp+https': dict(
ansible_port=5986,
ansible_psrp_protocol='https',
ansible_psrp_cert_validation='ignore',
use_password=True,
),
'ssh+key': dict(
ansible_port=22,
ansible_shell_type='powershell',
use_password=False,
),
'ssh+password': dict(
ansible_port=22,
ansible_shell_type='powershell',
use_password=True,
),
'winrm+http': dict(
ansible_port=5985,
ansible_winrm_scheme='http',
ansible_winrm_transport='ntlm',
use_password=True,
),
'winrm+https': dict(
ansible_port=5986,
ansible_winrm_scheme='https',
ansible_winrm_server_cert_validation='ignore',
use_password=True,
),
}
"""Dictionary of Windows connection types and variables required to use them."""
WINDOWS_CONNECTIONS = list(WINDOWS_CONNECTION_VARIABLES)
def is_valid_identifier(value: str) -> bool: def is_valid_identifier(value: str) -> bool:
"""Return True if the given value is a valid non-keyword Python identifier, otherwise return False.""" """Return True if the given value is a valid non-keyword Python identifier, otherwise return False."""
return value.isidentifier() and not keyword.iskeyword(value) return value.isidentifier() and not keyword.iskeyword(value)

Loading…
Cancel
Save