about summary refs log tree commit diff
path: root/items/svc_openrc2.py
blob: 5b74f615bd7c6adb995887e7995143bd3e4eb27a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
from shlex import quote

from bundlewrap.exceptions import BundleError
from bundlewrap.items import Item
from bundlewrap.utils.text import mark_for_translation as _


def svc_start(node, svcname):
    return node.run(f"rc-service {quote(svcname)} start", may_fail=True)


def svc_running(node, svcname):
    result = node.run(f"rc-service {quote(svcname)} status", may_fail=True)
    return result.return_code == 0 and "started" in result.stdout_text


def svc_stop(node, svcname):
    return node.run(f"rc-service {quote(svcname)} stop", may_fail=True)


def svc_enable(node, svcname, runlevel):
    return node.run(f"rc-update add {quote(svcname)} {quote(runlevel)}", may_fail=True)


def svc_enabled(node, svcname, runlevel):
    result = node.run(
        f"rc-update show {quote(runlevel)} | grep -w {quote(svcname)}",
        may_fail=True,
    )
    return result.return_code == 0 and svcname in result.stdout_text


def svc_runlevel(node, svcname):
    result = node.run(
        f"rc-update show --all | grep -w {quote(svcname)} | cut -d \\| -f 2",
        may_fail=True,
    )
    return result.stdout_text.strip() if result.return_code == 0 else None


def svc_disable(node, svcname, runlevel):
    return node.run(f"rc-update del {quote(svcname)} {quote(runlevel)}", may_fail=True)


class SvcOpenRC2(Item):
    """
    A service managed by OpenRC init scripts.
    """

    BUNDLE_ATTRIBUTE_NAME = "svc_openrc2"
    ITEM_ATTRIBUTES = {
        "running": True,
        "enabled": True,
        "runlevel": "default",
    }
    ITEM_TYPE_NAME = "svc_openrc2"

    def __repr__(self):
        return "<SvcOpenRC name:{} runlevel:{} enabled:{} running:{}>".format(
            self.name,
            self.attributes["runlevel"],
            self.attributes["enabled"],
            self.attributes["running"],
        )

    def fix(self, status):
        if "enabled" in status.keys_to_fix:
            if self.attributes["enabled"]:
                svc_enable(self.node, self.name, self.attributes["runlevel"])
            else:
                svc_disable(self.node, self.name, self.attributes["runlevel"])

        if "running" in status.keys_to_fix:
            if self.attributes["running"]:
                svc_start(self.node, self.name)
            else:
                svc_stop(self.node, self.name)

        if "runlevel" in status.keys_to_fix:
            if status.sdict["runlevel"]:
                svc_disable(self.node, self.name, status.sdict["runlevel"])
            svc_enable(self.node, self.name, self.attributes["runlevel"])

    def get_canned_actions(self):
        return {
            "stop": {
                "command": f"rc-service {self.name} stop",
                "needed_by": {self.id},
            },
            "restart": {
                "command": f"rc-service {self.name} restart",
                "needs": {self.id},
            },
            "reload": {
                "command": f"rc-service {self.name} reload".format(self.name),
                "needs": {
                    # make sure we don't reload and restart simultaneously
                    f"{self.id}:restart",
                    # with only the dep on restart, we might still end
                    # up reloading if the service itself is skipped
                    # because the restart action has cascade_skip False
                    self.id,
                },
            },
        }

    def sdict(self):
        return {
            "enabled": svc_enabled(self.node, self.name, self.attributes["runlevel"]),
            "running": svc_running(self.node, self.name),
            "runlevel": svc_runlevel(self.node, self.name),
        }

    @classmethod
    def validate_attributes(cls, bundle, item_id, attributes):
        for attribute in ("enabled", "running"):
            if attributes.get(attribute, None) not in (True, False, None):
                raise BundleError(
                    _(
                        "expected boolean or None for '{attribute}' on {item} in bundle '{bundle}'"
                    ).format(
                        attribute=attribute,
                        bundle=bundle.name,
                        item=item_id,
                    )
                )