Repository: ansible-collections/community.general

Issue:
[zypper] "msg: Zypper run command failed with return code 5." when using community.general.zypper module on MicroOS
### Summary

Using the following snippet on a freshly installed MicroOS will result in the zypper module not using `transactional-update`.

```yaml
---
- hosts: test
  tasks:
    - name: Install fail2ban
      community.general.zypper:
        name: fail2ban
        state: present
```

Failling with:

```
PLAY [test] ****************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************
ok: [test]


TASK [Install fail2ban] ***************************************************************************************************************************************
fatal: [test]: FAILED! => changed=false 
  cmd:
  - /usr/bin/zypper
  - --quiet
  - --non-interactive
  - --xmlout
  - install
  - --type
  - package
  - --auto-agree-with-licenses
  - --no-recommends
  - --
  - +fail2ban
  msg: Zypper run command failed with return code 5.
  rc: 5
  stderr: ''
  stderr_lines: <omitted>
  stdout: |-
    <?xml version='1.0'?>
    <stream>
    <message type="error">This is a transactional-server, please use transactional-update to update or modify the system.</message>
    </stream>
  stdout_lines: <omitted>
```

### Issue Type

Bug Report

### Component Name

community.general.zypper module

### Ansible Version

```console (paste below)
$ ansible --version

```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general
# /home/VENV/lib/python3.10/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 6.0.1  

```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed
CONFIG_FILE() = /etc/ansible/ansible.cfg

```


### OS / Environment

openSUSE Tumbleweed

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
- hosts: test
  tasks:
    - name: Install fail2ban
      community.general.zypper:
        name: fail2ban
        state: present
```


### Expected Results

```
TASK [Install fail2ban] ***************************************************************************************************************************************
ok: [test]
```


### Actual Results

```console (paste below)
TASK [Install fail2ban] ***************************************************************************************************************************************
fatal: [test]: FAILED! => changed=false 
  cmd:
  - /usr/bin/zypper
  - --quiet
  - --non-interactive
  - --xmlout
  - install
  - --type
  - package
  - --auto-agree-with-licenses
  - --no-recommends
  - --
  - +fail2ban
  msg: Zypper run command failed with return code 5.
  rc: 5
  stderr: ''
  stderr_lines: <omitted>
  stdout: |-
    <?xml version='1.0'?>
    <stream>
    <message type="error">This is a transactional-server, please use transactional-update to update or modify the system.</message>
    </stream>
  stdout_lines: <omitted>

```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct
[zypper] "msg: Zypper run command failed with return code 5." when using community.general.zypper module on MicroOS
### Summary

Using the following snippet on a freshly installed MicroOS will result in the zypper module not using `transactional-update`.

```yaml
---
- hosts: test
  tasks:
    - name: Install fail2ban
      community.general.zypper:
        name: fail2ban
        state: present
```

Failling with:

```
PLAY [test] ****************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************
ok: [test]


TASK [Install fail2ban] ***************************************************************************************************************************************
fatal: [test]: FAILED! => changed=false 
  cmd:
  - /usr/bin/zypper
  - --quiet
  - --non-interactive
  - --xmlout
  - install
  - --type
  - package
  - --auto-agree-with-licenses
  - --no-recommends
  - --
  - +fail2ban
  msg: Zypper run command failed with return code 5.
  rc: 5
  stderr: ''
  stderr_lines: <omitted>
  stdout: |-
    <?xml version='1.0'?>
    <stream>
    <message type="error">This is a transactional-server, please use transactional-update to update or modify the system.</message>
    </stream>
  stdout_lines: <omitted>
```

### Issue Type

Bug Report

### Component Name

community.general.zypper module

### Ansible Version

```console (paste below)
$ ansible --version

```


### Community.general Version

```console (paste below)
$ ansible-galaxy collection list community.general
# /home/VENV/lib/python3.10/site-packages/ansible_collections
Collection        Version
----------------- -------
community.general 6.0.1  

```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed
CONFIG_FILE() = /etc/ansible/ansible.cfg

```


### OS / Environment

openSUSE Tumbleweed

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
- hosts: test
  tasks:
    - name: Install fail2ban
      community.general.zypper:
        name: fail2ban
        state: present
```


### Expected Results

```
TASK [Install fail2ban] ***************************************************************************************************************************************
ok: [test]
```


### Actual Results

```console (paste below)
TASK [Install fail2ban] ***************************************************************************************************************************************
fatal: [test]: FAILED! => changed=false 
  cmd:
  - /usr/bin/zypper
  - --quiet
  - --non-interactive
  - --xmlout
  - install
  - --type
  - package
  - --auto-agree-with-licenses
  - --no-recommends
  - --
  - +fail2ban
  msg: Zypper run command failed with return code 5.
  rc: 5
  stderr: ''
  stderr_lines: <omitted>
  stdout: |-
    <?xml version='1.0'?>
    <stream>
    <message type="error">This is a transactional-server, please use transactional-update to update or modify the system.</message>
    </stream>
  stdout_lines: <omitted>

```


### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Files identified in the description:
* [`plugins/modules/zypper.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/zypper.py)

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @AnderEnder @alxgu @andytom @commel @dcermak @evrardjp @lrupp @sealor @toabctl
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Right now the module detects whether it should use transactional-update by looking whether `/var/lib/misc/transactional-update.state` exists. I guess that's not sufficient on MicroOS.
An additional check if transactional-update exists might help
I propose testing if `/usr/sbin/transactional-update` exists.

See https://github.com/ExNG/community.general/commit/5fd014fffbd0b069e15db797e1f4ef78a37072d5
CC @sebix who implemented support for transactional updates in #3164.
Great to see that the support for transactional-updates / MicroOS & Co. is actually used :)

> I propose testing if `/usr/sbin/transactional-update` exists.

I'm not sure if that's correct, I had that in mind when implementing it, but intentionally decided against it. Does _anything like_ `/var/lib/misc/transactional-update.state` exist on your system? Maybe they changed paths or file names.
Otherwise I'd first ask in the openSUSE community for advice to get it right and correct now.
Sorry i only yet got around testing.
So my setup isn't MicroOs per se, rather a Tumbleweed install with transactional-update.
Nonetheless the same Problem plagues fresh MicroOS installs too, `/var/lib/misc/transactional-update.state` doesnt exist on a fresh install but will after the first snapshot was created.
I tested this in blank new VMs with the Tumbleweed DVD iso and and the MicroOS VM image.
Files identified in the description:
* [`plugins/modules/zypper.py`](https://github.com/ansible-collections/community.general/blob/main/plugins/modules/zypper.py)

If these files are incorrect, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @AnderEnder @alxgu @andytom @commel @dcermak @evrardjp @lrupp @sealor @toabctl
[click here for bot help](https://github.com/ansible/ansibullbot/blob/devel/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Right now the module detects whether it should use transactional-update by looking whether `/var/lib/misc/transactional-update.state` exists. I guess that's not sufficient on MicroOS.
An additional check if transactional-update exists might help
I propose testing if `/usr/sbin/transactional-update` exists.

See https://github.com/ExNG/community.general/commit/5fd014fffbd0b069e15db797e1f4ef78a37072d5
CC @sebix who implemented support for transactional updates in #3164.
Great to see that the support for transactional-updates / MicroOS & Co. is actually used :)

> I propose testing if `/usr/sbin/transactional-update` exists.

I'm not sure if that's correct, I had that in mind when implementing it, but intentionally decided against it. Does _anything like_ `/var/lib/misc/transactional-update.state` exist on your system? Maybe they changed paths or file names.
Otherwise I'd first ask in the openSUSE community for advice to get it right and correct now.
Sorry i only yet got around testing.
So my setup isn't MicroOs per se, rather a Tumbleweed install with transactional-update.
Nonetheless the same Problem plagues fresh MicroOS installs too, `/var/lib/misc/transactional-update.state` doesnt exist on a fresh install but will after the first snapshot was created.
I tested this in blank new VMs with the Tumbleweed DVD iso and and the MicroOS VM image.

Resolving patch:
diff --git a/plugins/modules/packaging/os/zypper.py b/plugins/modules/packaging/os/zypper.py
--- a/plugins/modules/packaging/os/zypper.py
+++ b/plugins/modules/packaging/os/zypper.py
@@ -511,8 +511,21 @@ def repo_refresh(m):
     return retvals
 
 
+def get_fs_type_and_readonly_state(mount_point):
+    with open('/proc/mounts', 'r') as file:
+        for line in file.readlines():
+            fields = line.split()
+            path = fields[1]
+            if path == mount_point:
+                fs = fields[2]
+                opts = fields[3]
+                return fs, 'ro' in opts.split(',')
+    return None
+
+
 def transactional_updates():
-    return os.path.exists('/var/lib/misc/transactional-update.state')
+    return os.path.exists('/usr/sbin/transactional-update') and get_fs_type_and_readonly_state('/') == ('btrfs', True)
+
 
 # ===========================================
 # Main control flow


Test patch: