pyinfra icon indicating copy to clipboard operation
pyinfra copied to clipboard

systemd.service() does not support wildcards

Open NichtJens opened this issue 5 months ago • 1 comments

Is your feature request related to a problem? Please describe

Commands like systemctl stop abc* support glob-style wildcards.

However, systemd.service() does not.

Describe the solution you'd like

The obvious "user space" solution is probably something like this:


from fnmatch import fnmatch

from pyinfra import host
from pyinfra.facts.systemd import SystemdEnabled
from pyinfra.operations import systemd

def services(pattern, name=None, **kwargs):
    """
    Manage the state of all systemd services matching a glob pattern
    """
    services = get_enabled_services()

    for service in services:
        if fnmatch(service, pattern):
            iname = None if name is None else f"{name}: {service}" 

            systemd.service(
                name=iname,
                service=service,
                **kwargs
            )

def get_enabled_services():
    """
    Return a list of enabled systemd services
    """
    units = host.get_fact(SystemdEnabled)
    return [name for name, enabled in units.items() if enabled and name.endswith(".service")]

However, this works through the list of services step by step, which means it accumulates the run time. Doing the same operation via the systemctl command will perform all changes in parallel and needs only approximately the longest run time of any of the changes.

Reading the underlying code, it turns out that this basically already works. The smallest possible change that resolves the issue would be the following:

In pyinfra/pyinfra/operations/util/service.py replace line 19

is_running = statuses.get(name, None)

with

is_running = any(s for n, s in statuses.items() if fnmatch(n, name))

I.e., if any of the services that match the pattern is running, treat the pattern as running. This is compatible with the current non-wildcard name behavior since fnmatch matches non-wildcard strings to themselves.

The question is whether this is enough or if it is wished that the list of services that is interacted with is shown in the printed output. This would need a much bigger change, I fear.

Either way, I would be happy to provide a PR for the above solution (plus the needed docs changes, etc.) or work on a more complex solution as I would really like this feature to exist.

NichtJens avatar Sep 17 '24 21:09 NichtJens