community.general icon indicating copy to clipboard operation
community.general copied to clipboard

opkg - `_package_in_desired_state` does not handle properly `opkg list_installed` output

Open akire0ne opened this issue 1 year ago • 6 comments

Summary

Tried to integrate community.general.opkg for the first time in our ansible stack. We have some embedded devices that uses opkg for package management. My task is very trivial.

---
- name: Install vim - opkg
  community.general.opkg:
    name: "vim=9.0.1211-r0"

This results in

An exception occurred during task execution. To see the full traceback, use -vvv. The error was: ansible_collections.community.general.plugins.module_utils.mh.exceptions.ModuleHelperException
fatal: [<censored_hostname>]: FAILED! => {
    "changed": false,
    "output": {
        "diff": {}
    },
    "vars": {}
}

MSG:

failed to install vim=9.0.1211-r0

The package is installed on the remote host

root@XXXXXXXX:~# opkg list_installed vim
vim - 9.0.1211-r0

After installing the package, the modules runs the above command to confirm that it has been installed properly. This part is broken. After diging a bit, the culprit is that function:

https://github.com/ansible-collections/community.general/blob/fd811df414095c7c268e09218520dc9db03da1e8/plugins/modules/opkg.py#L177-L181

In my case: out="vim - 9.0.1211-r0\n" So when it executes has_package = out.startswith(name + " - %s" % ("" if not version else (version + " "))) - It will always return False, because there is no whitespace at the end of my out. I am not sure what is the rationale behind this condition but it clearly does not fit the out from my device. It seems like an unhandled scenario.

I fixed locally by removing the extra whitespace after version in the condtion:

has_package = out.startswith(name + " - %s" % ("" if not version else version))

I'm not sure if that approach is fine, I can't find history about that extra whitespace.

Issue Type

Bug Report

Component Name

community.general.opkg

Ansible Version

$ ansible --version
ansible [core 2.16.9]
python version = 3.10.9 (main, May  1 2024, 16:32:48)
jinja version = 3.1.4
libyaml = True

Community.general Version

$ ansible-galaxy collection list community.general
Collection        Version
----------------- -------
community.general 9.2.0

Configuration

Too sensitive environment. sorry. I don't think it's helpful for that issue.

OS / Environment

Controller: Ubuntu 22.04 LTS

Target: OS: PetaLinux 2023.1 kernel: Linux k26-lunx-tx 6.1.30-xilinx-v2023.1 #1 SMP Fri Jun 30 09:49:44 UTC 2023 aarch64 GNU/Linux opkg: version 0.6.1 (libsolv 0.7.22)

Steps to Reproduce

I think the description is self explanatory. The target device that we use is rather exotic and unfortunatelly I think it might be difficult to reproduce.

Expected Results

The package should show as "installed" and the module should not error out because of unhandled out

Actual Results

No response

Code of Conduct

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

akire0ne avatar Aug 02 '24 16:08 akire0ne

Files identified in the description:

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

ansibullbot avatar Aug 02 '24 16:08 ansibullbot

cc @skinp click here for bot help

ansibullbot avatar Aug 02 '24 16:08 ansibullbot

The condition

has_package = out.startswith(name + " - %s" % ("" if not version else version))

is not correct either, since it thinks 1.1 is installed if 1.10 is installed, for example, since 1.10 starts with 1.1. I guess the output format of opkg list_installed changed, or depends on the concrete version or whatever...

I would probably adjust the code to split into lines, and check whether the first line starts with foo - version , or equals foo - version. That way both output formats are covered.

CC @joergho who implemented version support in #5688 and might have some insight here as well...

felixfontein avatar Aug 05 '24 18:08 felixfontein

Thans for reporting the bug @akire0ne and for the analysis @felixfontein

If the Yocto version of opkg is used, the following code is executed in the end: print_pkg. This means that depending of the configuration, the output can be any of the following:

NAME - VERSION
NAME - VERSION - SIZE
NAME - VERSION - SIZE - DESCRIPTION
NAME - VERSION - DESCRIPTION

I'd therefore suggest to split the string by -:

    def _package_in_desired_state(self, name, want_installed, version=None):
        dummy, out, dummy = self.runner("state package").run(state="query", package=name)

        splitted = out.split(" - ")
        has_package = (splitted[0] == name) and (version is None or splitted[1] == version)
        return want_installed == has_package

I have done minimal testing in Python console but not yet using the whole ansible stack.

joergho avatar Aug 06 '24 07:08 joergho

If you simply split by " - ", and the input is "NAME - VERSION\n", splitted[1] ends up as "VERSION\n" with the newline added.

How about using

        lines = out.splitlines()
        splitted = lines[0].split(" - ") if lines else [None, None]

instead of splitted = out.split(" - ")?

felixfontein avatar Aug 06 '24 09:08 felixfontein

How about using (...)

Yes, this would work!

joergho avatar Aug 06 '24 11:08 joergho