From a8378a4eb0d31cd0dd197e8266451e30c6a68ea7 Mon Sep 17 00:00:00 2001 From: Seddik Alaoui Ismaili <32570331+saibug@users.noreply.github.com> Date: Tue, 20 Jan 2026 22:08:47 +0100 Subject: [PATCH] nmcli idempotency connection check (#11114) * nmcli idempotency connection check * Changelog fragment and ruff reformat * Fix : change error handling * Remove odd conditions * Refactor nmcli: fix error handling and remove redundant logic * Fix code format * Fix error message to handle --- .../fragments/11114-nmcli-idempotency.yml | 3 + plugins/modules/nmcli.py | 80 ++++++++++++++----- 2 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 changelogs/fragments/11114-nmcli-idempotency.yml diff --git a/changelogs/fragments/11114-nmcli-idempotency.yml b/changelogs/fragments/11114-nmcli-idempotency.yml new file mode 100644 index 0000000000..d60f223938 --- /dev/null +++ b/changelogs/fragments/11114-nmcli-idempotency.yml @@ -0,0 +1,3 @@ +--- +minor_changes: + - nmcli - add idempotency check (https://github.com/ansible-collections/community.general/pull/11114). \ No newline at end of file diff --git a/plugins/modules/nmcli.py b/plugins/modules/nmcli.py index 9ea2a41009..09d06d4aca 100644 --- a/plugins/modules/nmcli.py +++ b/plugins/modules/nmcli.py @@ -2412,6 +2412,21 @@ class Nmcli: cmd = [self.nmcli_bin, "con", "down", self.conn_name] return self.execute_command(cmd) + def get_connection_state(self): + """Get the current state of the connection""" + cmd = [self.nmcli_bin, "--terse", "--fields", "GENERAL.STATE", "con", "show", self.conn_name] + (rc, out, err) = self.execute_command(cmd) + if rc != 0: + raise NmcliModuleError(err) + + lines = [ll.strip() for ll in out.splitlines() if "GENERAL.STATE" in ll] + return lines[0].split(":")[1] if lines else None + + def is_connection_active(self): + """Check if the connection is currently active""" + state = self.get_connection_state() + return state == "activated" + def up_connection(self): cmd = [self.nmcli_bin, "con", "up", self.conn_name] return self.execute_command(cmd) @@ -2970,31 +2985,60 @@ def main(): elif nmcli.state == "up": if nmcli.connection_exists(): - if module.check_mode: - module.exit_json(changed=True) - if nmcli.conn_reload: - (rc, out, err) = nmcli.reload_connection() - (rc, out, err) = nmcli.up_connection() - if rc != 0: - module.fail_json(name=f"Error bringing up connection named {nmcli.conn_name}", msg=err, rc=rc) + is_active = nmcli.is_connection_active() + + if is_active and not nmcli.conn_reload: + result["changed"] = False + result["msg"] = f"Connection {nmcli.conn_name} is already active" + module.exit_json(**result) + else: + if module.check_mode: + module.exit_json(changed=True, **result) + + if nmcli.conn_reload: + (rc, out, err) = nmcli.reload_connection() + if rc != 0: + module.fail_json(msg=f"Error reloading connection named {nmcli.conn_name}: {err}", rc=rc) + + (rc, out, err) = nmcli.up_connection() + if rc != 0: + module.fail_json(msg=f"Error bringing up connection named {nmcli.conn_name}: {err}", rc=rc) + result["changed"] = True + else: + module.fail_json( + name=nmcli.conn_name, + msg="Connection does not exist", + ) elif nmcli.state == "down": if nmcli.connection_exists(): - if module.check_mode: - module.exit_json(changed=True) - if nmcli.conn_reload: - (rc, out, err) = nmcli.reload_connection() - (rc, out, err) = nmcli.down_connection() - if rc != 0: - module.fail_json(name=f"Error bringing down connection named {nmcli.conn_name}", msg=err, rc=rc) + is_active = nmcli.is_connection_active() + + if not is_active and not nmcli.conn_reload: + result["changed"] = False + result["msg"] = f"Connection {nmcli.conn_name} is already inactive" + module.exit_json(**result) + else: + if module.check_mode: + module.exit_json(changed=True, **result) + + if nmcli.conn_reload: + (rc, out, err) = nmcli.reload_connection() + if rc != 0: + module.fail_json(name=f"Error reloading connection {nmcli.conn_name}", msg=err, rc=rc) + + (rc, out, err) = nmcli.down_connection() + if rc != 0: + module.fail_json(name=f"Error bringing down connection named {nmcli.conn_name}", msg=err, rc=rc) + result["changed"] = True + else: + module.fail_json(msg=f"Connection {nmcli.conn_name} does not exist") except NmcliModuleError as e: module.fail_json(name=nmcli.conn_name, msg=str(e)) - if rc is None: - result["changed"] = False - else: - result["changed"] = True + if "changed" not in result: + result["changed"] = rc is not None if out: result["stdout"] = out if err: