Description: Fix issue with ansible.builtin.copy when using libvirt connection plugin The libvirt connection plugin is unable to resolve "~", leading to files copied to literal /~/ansible-tmp/, and thus many modules failing. Author: antonc42 Origin: upstream, https://github.com/ansible-collections/community.libvirt/pull/162 Bug: https://github.com/ansible-collections/community.libvirt/issues/161 Applied-Upstream: not yet Reviewed-by: Lee Garrett Last-Update: 2023-10-20 --- This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ --- /dev/null +++ b/ansible_collections/community/libvirt/changelogs/fragments/161_fix_resolve_tilde.yml @@ -0,0 +1,2 @@ +bugfixes: + - libvirt_qemu - fix path resolution of tilde (~) used to represent remote user's homedir --- a/ansible_collections/community/libvirt/plugins/connection/libvirt_qemu.py +++ b/ansible_collections/community/libvirt/plugins/connection/libvirt_qemu.py @@ -46,6 +46,7 @@ import base64 import json +import re import shlex import time import traceback @@ -99,6 +100,7 @@ super(Connection, self).__init__(play_context, new_stdin, *args, **kwargs) self._host = self._play_context.remote_addr + self._user_homedir = None # Windows operates differently from a POSIX connection/shell plugin, # we need to set various properties to ensure SSH on Windows continues @@ -147,6 +149,21 @@ display.vvv(u"ESTABLISH {0} CONNECTION".format(self.transport), host=self._host) self._connected = True + @property + def user_homedir(self): + """ the resolved user homedir on the remote """ + if self._user_homedir: + return self._user_homedir + exitcode, stdout, stderr = self.exec_command("/bin/sh -c 'getent passwd $(id -un) | cut -d: -f6'") + self._user_homedir = to_text(stdout).strip() + return self._user_homedir + + def _resolve_tilde(self, string): + """ resolve file paths or commands that begin with '~/' to the remote user's homedir """ + if re.search(r"~\/", string): + return re.sub(r"~\/", self.user_homedir + r"/", string) + return string + def exec_command(self, cmd, in_data=None, sudoable=True): """ execute a command on the virtual machine host """ super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) @@ -154,6 +171,7 @@ self._display.vvv(u"EXEC {0}".format(cmd), host=self._host) cmd_args_list = shlex.split(to_native(cmd, errors='surrogate_or_strict')) + cmd_args_list = list(map(self._resolve_tilde, cmd_args_list)) if getattr(self._shell, "_IS_WINDOWS", False): # Become method 'runas' is done in the wrapper that is executed, @@ -242,6 +260,7 @@ def put_file(self, in_path, out_path): ''' transfer a file from local to domain ''' super(Connection, self).put_file(in_path, out_path) + out_path = self._resolve_tilde(out_path) display.vvv("PUT %s TO %s" % (in_path, out_path), host=self._host) if not exists(to_bytes(in_path, errors='surrogate_or_strict')): @@ -304,6 +323,7 @@ def fetch_file(self, in_path, out_path): ''' fetch a file from domain to local ''' super(Connection, self).fetch_file(in_path, out_path) + in_path = self._resolve_tilde(in_path) display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._host) request_handle = {