From c7150dd81832bc6a05958097d1fad3f907f01904 Mon Sep 17 00:00:00 2001 From: Simon Deziel Date: Wed, 25 Oct 2023 06:47:27 +0000 Subject: [PATCH] plugins/connection/lxd: convert FQDN to instance name (#7360) * plugins/connection/lxd: convert FQDN to instance name This allows to use FQDNs in the inventory and have the connection driver do the translation when talking to LXD that uses hostnames (no ".") for instance names. Those are either globally unique or unique per network/ project in LXD. ``` all: # Groups and hosts children: lxd_dmz: vars: ansible_lxd_project: dmz hosts: www01.dmz.example.com: www02.dmz.example.com: ``` ``` $ lxc list --project dmz +---------+---------+----------------+------+-----------+-----------+----------+ | NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS | LOCATION | +-------+---------+------------------+------+-----------+-----------+----------+ | www01 | RUNNING | 192.0.2.1 (eth0) | | CONTAINER | 0 | t1 | +-------+---------+------------------+------+-----------+-----------+----------+ | www02 | RUNNING | 192.0.2.2 (eth0) | | CONTAINER | 0 | t3 | +-------+---------+------------------+------+-----------+-----------+----------+ ``` Signed-off-by: Simon Deziel * plugins/connection/lxd: VMs/containers are called instances Update error string parsing to support the new format: $ lxc stop c1 -- true $ lxc exec c1 -- true Error: Instance is not running $ lxc exec does-not-exist -- true Error: Instance not found Signed-off-by: Simon Deziel * plugins/connection/lxd: add changelog fragment Signed-off-by: Simon Deziel --------- Signed-off-by: Simon Deziel --- .../fragments/7360-lxd-remote-addr-host.yml | 3 ++ plugins/connection/lxd.py | 30 +++++++++++-------- 2 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 changelogs/fragments/7360-lxd-remote-addr-host.yml diff --git a/changelogs/fragments/7360-lxd-remote-addr-host.yml b/changelogs/fragments/7360-lxd-remote-addr-host.yml new file mode 100644 index 0000000000..31a389e7a6 --- /dev/null +++ b/changelogs/fragments/7360-lxd-remote-addr-host.yml @@ -0,0 +1,3 @@ +minor_changes: + - lxd connection plugin - automatically translate ``remote_addr`` from FQDN to (short) hostname (https://github.com/ansible-collections/community.general/pull/7360). + - lxd connection plugin - update error parsing to work with newer messages mentioning instances (https://github.com/ansible-collections/community.general/pull/7360). diff --git a/plugins/connection/lxd.py b/plugins/connection/lxd.py index affb87dfd0..4ad81be399 100644 --- a/plugins/connection/lxd.py +++ b/plugins/connection/lxd.py @@ -16,7 +16,9 @@ DOCUMENTATION = ''' options: remote_addr: description: - - Container identifier. + - Instance (container/VM) identifier. + - Since community.general 8.0.0, a FQDN can be provided; in that case, the first component (the part before C(.)) + is used as the instance identifier. default: inventory_hostname vars: - name: inventory_hostname @@ -71,26 +73,30 @@ class Connection(ConnectionBase): if self._play_context.remote_user is not None and self._play_context.remote_user != 'root': self._display.warning('lxd does not support remote_user, using container default: root') + def _host(self): + """ translate remote_addr to lxd (short) hostname """ + return self.get_option("remote_addr").split(".", 1)[0] + def _connect(self): """connect to lxd (nothing to do here) """ super(Connection, self)._connect() if not self._connected: - self._display.vvv(u"ESTABLISH LXD CONNECTION FOR USER: root", host=self.get_option('remote_addr')) + self._display.vvv(u"ESTABLISH LXD CONNECTION FOR USER: root", host=self._host()) self._connected = True def exec_command(self, cmd, in_data=None, sudoable=True): """ execute a command on the lxd host """ super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable) - self._display.vvv(u"EXEC {0}".format(cmd), host=self.get_option('remote_addr')) + self._display.vvv(u"EXEC {0}".format(cmd), host=self._host()) local_cmd = [self._lxc_cmd] if self.get_option("project"): local_cmd.extend(["--project", self.get_option("project")]) local_cmd.extend([ "exec", - "%s:%s" % (self.get_option("remote"), self.get_option("remote_addr")), + "%s:%s" % (self.get_option("remote"), self._host()), "--", self.get_option("executable"), "-c", cmd ]) @@ -104,11 +110,11 @@ class Connection(ConnectionBase): stdout = to_text(stdout) stderr = to_text(stderr) - if stderr == "error: Container is not running.\n": - raise AnsibleConnectionFailure("container not running: %s" % self.get_option('remote_addr')) + if "is not running" in stderr: + raise AnsibleConnectionFailure("instance not running: %s" % self._host()) - if stderr == "error: not found\n": - raise AnsibleConnectionFailure("container not found: %s" % self.get_option('remote_addr')) + if "not found" in stderr: + raise AnsibleConnectionFailure("instance not found: %s" % self._host()) return process.returncode, stdout, stderr @@ -116,7 +122,7 @@ class Connection(ConnectionBase): """ put a file from local to lxd """ super(Connection, self).put_file(in_path, out_path) - self._display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self.get_option('remote_addr')) + self._display.vvv(u"PUT {0} TO {1}".format(in_path, out_path), host=self._host()) if not os.path.isfile(to_bytes(in_path, errors='surrogate_or_strict')): raise AnsibleFileNotFound("input path is not a file: %s" % in_path) @@ -127,7 +133,7 @@ class Connection(ConnectionBase): local_cmd.extend([ "file", "push", in_path, - "%s:%s/%s" % (self.get_option("remote"), self.get_option("remote_addr"), out_path) + "%s:%s/%s" % (self.get_option("remote"), self._host(), out_path) ]) local_cmd = [to_bytes(i, errors='surrogate_or_strict') for i in local_cmd] @@ -139,14 +145,14 @@ class Connection(ConnectionBase): """ fetch a file from lxd to local """ super(Connection, self).fetch_file(in_path, out_path) - self._display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path), host=self.get_option('remote_addr')) + self._display.vvv(u"FETCH {0} TO {1}".format(in_path, out_path), host=self._host()) local_cmd = [self._lxc_cmd] if self.get_option("project"): local_cmd.extend(["--project", self.get_option("project")]) local_cmd.extend([ "file", "pull", - "%s:%s/%s" % (self.get_option("remote"), self.get_option("remote_addr"), in_path), + "%s:%s/%s" % (self.get_option("remote"), self._host(), in_path), out_path ])