mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-03-26 21:33:12 +00:00
Added support for Windows VM with Incus connection. (#11199)
* Added support for Windows VM with Incus connection. * Update changelogs/fragments/11199-incus-windows.yml Co-authored-by: Felix Fontein <felix@fontein.de> * Attempt to fix the argument splitting. * Only split on the first occurrence of the command argument * Applying nox --------- Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
committed by
GitHub
parent
d550baacfa
commit
22a4f8e272
2
changelogs/fragments/11199-incus-windows.yml
Normal file
2
changelogs/fragments/11199-incus-windows.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
minor_changes:
|
||||||
|
- incus connection plugin - add support for Windows virtual machines (https://github.com/ansible-collections/community.general/pull/11199).
|
||||||
@@ -13,6 +13,9 @@ short_description: Run tasks in Incus instances using the Incus CLI
|
|||||||
description:
|
description:
|
||||||
- Run commands or put/fetch files to an existing Incus instance using Incus CLI.
|
- Run commands or put/fetch files to an existing Incus instance using Incus CLI.
|
||||||
version_added: "8.2.0"
|
version_added: "8.2.0"
|
||||||
|
notes:
|
||||||
|
- When using this collection for Windows virtual machines, set C(ansible_shell_type) to C(powershell) or C(cmd) as a variable to the host in
|
||||||
|
the inventory.
|
||||||
options:
|
options:
|
||||||
remote_addr:
|
remote_addr:
|
||||||
description:
|
description:
|
||||||
@@ -75,6 +78,7 @@ options:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
from subprocess import call, Popen, PIPE
|
from subprocess import call, Popen, PIPE
|
||||||
|
|
||||||
from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleFileNotFound
|
from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleFileNotFound
|
||||||
@@ -97,6 +101,23 @@ class Connection(ConnectionBase):
|
|||||||
if not self._incus_cmd:
|
if not self._incus_cmd:
|
||||||
raise AnsibleError("incus command not found in PATH")
|
raise AnsibleError("incus command not found in PATH")
|
||||||
|
|
||||||
|
if getattr(self._shell, "_IS_WINDOWS", False):
|
||||||
|
# Initializing regular expression patterns to match on a PowerShell or cmd command line.
|
||||||
|
self.powershell_regex_pattern = re.compile(
|
||||||
|
r"^(?P<executable>(\"?([a-z]:)?[a-z0-9 ()\\.]+)?powershell(\.exe)?\"?|(([a-z]:)?[a-z0-9()\\.]+)?powershell(\.exe)?)\s+.*(?P<command>-c(ommand)?)\s+", # noqa: E501
|
||||||
|
re.IGNORECASE,
|
||||||
|
)
|
||||||
|
self.cmd_regex_pattern = re.compile(
|
||||||
|
r"^(?P<executable>(\"?([a-z]:)?[a-z0-9 ()\\.]+)?cmd(\.exe)?\"?|(([a-z]:)?[a-z0-9()\\.]+)?cmd(\.exe)?)\s+.*(?P<command>/c)\s+",
|
||||||
|
re.IGNORECASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Basic setup for a Windows host.
|
||||||
|
self.has_native_async = True
|
||||||
|
self.always_pipeline_modules = True
|
||||||
|
self.module_implementation_preferences = (".ps1", ".exe", "")
|
||||||
|
self.allow_executable = False
|
||||||
|
|
||||||
def _connect(self):
|
def _connect(self):
|
||||||
"""connect to Incus (nothing to do here)"""
|
"""connect to Incus (nothing to do here)"""
|
||||||
super()._connect()
|
super()._connect()
|
||||||
@@ -115,19 +136,49 @@ class Connection(ConnectionBase):
|
|||||||
"--project",
|
"--project",
|
||||||
self.get_option("project"),
|
self.get_option("project"),
|
||||||
"exec",
|
"exec",
|
||||||
|
*(["-T"] if getattr(self._shell, "_IS_WINDOWS", False) else []),
|
||||||
f"{self.get_option('remote')}:{self._instance()}",
|
f"{self.get_option('remote')}:{self._instance()}",
|
||||||
"--",
|
"--",
|
||||||
]
|
]
|
||||||
|
|
||||||
if self.get_option("remote_user") != "root":
|
if getattr(self._shell, "_IS_WINDOWS", False):
|
||||||
self._display.vvv(
|
if (
|
||||||
f"INFO: Running as non-root user: {self.get_option('remote_user')}, \
|
(regex_match := self.powershell_regex_pattern.match(cmd))
|
||||||
trying to run 'incus exec' with become method: {self.get_option('incus_become_method')}",
|
and (regex_pattern := self.powershell_regex_pattern)
|
||||||
host=self._instance(),
|
) or ((regex_match := self.cmd_regex_pattern.match(cmd)) and (regex_pattern := self.cmd_regex_pattern)):
|
||||||
)
|
self._display.vvvvvv(
|
||||||
exec_cmd.extend([self.get_option("incus_become_method"), self.get_option("remote_user"), "-c"])
|
f'Found keyword: "{regex_match.group("command")}" based on regex: {regex_pattern.pattern}',
|
||||||
|
host=self._instance(),
|
||||||
|
)
|
||||||
|
|
||||||
exec_cmd.extend([self.get_option("executable"), "-c", cmd])
|
# Split the command on the argument -c(ommand) for PowerShell or /c for cmd.
|
||||||
|
before_command_argument, after_command_argument = cmd.split(regex_match.group("command"), 1)
|
||||||
|
|
||||||
|
exec_cmd.extend(
|
||||||
|
[
|
||||||
|
# To avoid splitting on a space contained in the path, set the executable as the first argument.
|
||||||
|
regex_match.group("executable").strip('"'),
|
||||||
|
# Remove the executable path and split the rest by space.
|
||||||
|
*(before_command_argument[len(regex_match.group("executable")) :].lstrip().split(" ")),
|
||||||
|
# Set the command argument depending on cmd or powershell.
|
||||||
|
regex_match.group("command"),
|
||||||
|
# Add the rest of the command at the end.
|
||||||
|
after_command_argument,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# For anything else using -EncodedCommand or else, just split on space.
|
||||||
|
exec_cmd.extend(cmd.split(" "))
|
||||||
|
else:
|
||||||
|
if self.get_option("remote_user") != "root":
|
||||||
|
self._display.vvv(
|
||||||
|
f"INFO: Running as non-root user: {self.get_option('remote_user')}, \
|
||||||
|
trying to run 'incus exec' with become method: {self.get_option('incus_become_method')}",
|
||||||
|
host=self._instance(),
|
||||||
|
)
|
||||||
|
exec_cmd.extend([self.get_option("incus_become_method"), self.get_option("remote_user"), "-c"])
|
||||||
|
|
||||||
|
exec_cmd.extend([self.get_option("executable"), "-c", cmd])
|
||||||
|
|
||||||
return exec_cmd
|
return exec_cmd
|
||||||
|
|
||||||
@@ -200,7 +251,7 @@ class Connection(ConnectionBase):
|
|||||||
if not os.path.isfile(to_bytes(in_path, errors="surrogate_or_strict")):
|
if not os.path.isfile(to_bytes(in_path, errors="surrogate_or_strict")):
|
||||||
raise AnsibleFileNotFound(f"input path is not a file: {in_path}")
|
raise AnsibleFileNotFound(f"input path is not a file: {in_path}")
|
||||||
|
|
||||||
if self.get_option("remote_user") != "root":
|
if not getattr(self._shell, "_IS_WINDOWS", False) and self.get_option("remote_user") != "root":
|
||||||
uid, gid = self._get_remote_uid_gid()
|
uid, gid = self._get_remote_uid_gid()
|
||||||
local_cmd = [
|
local_cmd = [
|
||||||
self._incus_cmd,
|
self._incus_cmd,
|
||||||
|
|||||||
Reference in New Issue
Block a user