pyinfra 3.1.1__py2.py3-none-any.whl → 3.3__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. pyinfra/api/arguments.py +9 -2
  2. pyinfra/api/arguments_typed.py +4 -5
  3. pyinfra/api/command.py +22 -3
  4. pyinfra/api/config.py +5 -2
  5. pyinfra/api/deploy.py +4 -2
  6. pyinfra/api/facts.py +3 -0
  7. pyinfra/api/host.py +15 -7
  8. pyinfra/api/operation.py +2 -1
  9. pyinfra/api/state.py +1 -1
  10. pyinfra/connectors/base.py +34 -8
  11. pyinfra/connectors/chroot.py +7 -2
  12. pyinfra/connectors/docker.py +24 -8
  13. pyinfra/connectors/dockerssh.py +7 -2
  14. pyinfra/connectors/local.py +7 -2
  15. pyinfra/connectors/ssh.py +9 -2
  16. pyinfra/connectors/sshuserclient/client.py +42 -14
  17. pyinfra/connectors/sshuserclient/config.py +2 -0
  18. pyinfra/connectors/terraform.py +1 -1
  19. pyinfra/connectors/util.py +13 -9
  20. pyinfra/context.py +9 -2
  21. pyinfra/facts/apk.py +8 -1
  22. pyinfra/facts/apt.py +68 -0
  23. pyinfra/facts/brew.py +13 -0
  24. pyinfra/facts/bsdinit.py +3 -0
  25. pyinfra/facts/cargo.py +5 -0
  26. pyinfra/facts/choco.py +6 -0
  27. pyinfra/facts/crontab.py +195 -0
  28. pyinfra/facts/deb.py +10 -0
  29. pyinfra/facts/dnf.py +5 -0
  30. pyinfra/facts/docker.py +16 -0
  31. pyinfra/facts/efibootmgr.py +113 -0
  32. pyinfra/facts/files.py +112 -7
  33. pyinfra/facts/flatpak.py +7 -0
  34. pyinfra/facts/freebsd.py +75 -0
  35. pyinfra/facts/gem.py +5 -0
  36. pyinfra/facts/git.py +12 -2
  37. pyinfra/facts/gpg.py +7 -0
  38. pyinfra/facts/hardware.py +13 -0
  39. pyinfra/facts/iptables.py +9 -1
  40. pyinfra/facts/launchd.py +5 -0
  41. pyinfra/facts/lxd.py +5 -0
  42. pyinfra/facts/mysql.py +9 -2
  43. pyinfra/facts/npm.py +5 -0
  44. pyinfra/facts/openrc.py +8 -0
  45. pyinfra/facts/opkg.py +245 -0
  46. pyinfra/facts/pacman.py +9 -1
  47. pyinfra/facts/pip.py +5 -0
  48. pyinfra/facts/pipx.py +82 -0
  49. pyinfra/facts/pkg.py +4 -0
  50. pyinfra/facts/pkgin.py +5 -0
  51. pyinfra/facts/podman.py +54 -0
  52. pyinfra/facts/postgres.py +10 -2
  53. pyinfra/facts/rpm.py +11 -0
  54. pyinfra/facts/runit.py +7 -0
  55. pyinfra/facts/selinux.py +16 -0
  56. pyinfra/facts/server.py +87 -79
  57. pyinfra/facts/snap.py +7 -0
  58. pyinfra/facts/systemd.py +5 -0
  59. pyinfra/facts/sysvinit.py +4 -0
  60. pyinfra/facts/upstart.py +5 -0
  61. pyinfra/facts/util/__init__.py +4 -1
  62. pyinfra/facts/util/units.py +30 -0
  63. pyinfra/facts/vzctl.py +5 -0
  64. pyinfra/facts/xbps.py +6 -1
  65. pyinfra/facts/yum.py +5 -0
  66. pyinfra/facts/zfs.py +41 -21
  67. pyinfra/facts/zypper.py +5 -0
  68. pyinfra/local.py +3 -2
  69. pyinfra/operations/apt.py +36 -22
  70. pyinfra/operations/crontab.py +189 -0
  71. pyinfra/operations/docker.py +61 -56
  72. pyinfra/operations/files.py +65 -1
  73. pyinfra/operations/freebsd/__init__.py +12 -0
  74. pyinfra/operations/freebsd/freebsd_update.py +70 -0
  75. pyinfra/operations/freebsd/pkg.py +219 -0
  76. pyinfra/operations/freebsd/service.py +116 -0
  77. pyinfra/operations/freebsd/sysrc.py +92 -0
  78. pyinfra/operations/git.py +23 -7
  79. pyinfra/operations/opkg.py +88 -0
  80. pyinfra/operations/pip.py +3 -2
  81. pyinfra/operations/pipx.py +90 -0
  82. pyinfra/operations/postgres.py +114 -27
  83. pyinfra/operations/runit.py +2 -0
  84. pyinfra/operations/server.py +9 -181
  85. pyinfra/operations/util/docker.py +44 -22
  86. pyinfra/operations/zfs.py +3 -3
  87. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/LICENSE.md +1 -1
  88. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/METADATA +25 -25
  89. pyinfra-3.3.dist-info/RECORD +187 -0
  90. pyinfra_cli/exceptions.py +5 -0
  91. pyinfra_cli/inventory.py +26 -9
  92. pyinfra_cli/log.py +3 -0
  93. pyinfra_cli/main.py +9 -8
  94. pyinfra_cli/prints.py +19 -4
  95. pyinfra_cli/util.py +3 -0
  96. pyinfra_cli/virtualenv.py +1 -1
  97. tests/test_cli/test_cli_deploy.py +15 -13
  98. tests/test_cli/test_cli_inventory.py +53 -0
  99. tests/test_connectors/test_ssh.py +302 -182
  100. tests/test_connectors/test_sshuserclient.py +68 -1
  101. pyinfra-3.1.1.dist-info/RECORD +0 -172
  102. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/WHEEL +0 -0
  103. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/entry_points.txt +0 -0
  104. {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/top_level.txt +0 -0
pyinfra/facts/flatpak.py CHANGED
@@ -2,12 +2,15 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
 
5
+ from typing_extensions import override
6
+
5
7
  from pyinfra.api import FactBase
6
8
 
7
9
 
8
10
  class FlatpakBaseFact(FactBase):
9
11
  abstract = True
10
12
 
13
+ @override
11
14
  def requires_command(self, *args, **kwargs) -> str:
12
15
  return "flatpak"
13
16
 
@@ -32,9 +35,11 @@ class FlatpakPackage(FlatpakBaseFact):
32
35
  "version": r"^[ ]+Version:[ ]+([\w\d.-]+).*$",
33
36
  }
34
37
 
38
+ @override
35
39
  def command(self, package):
36
40
  return f"flatpak info {package}"
37
41
 
42
+ @override
38
43
  def process(self, output):
39
44
  data = {}
40
45
  for line in output:
@@ -63,8 +68,10 @@ class FlatpakPackages(FlatpakBaseFact):
63
68
 
64
69
  default = list
65
70
 
71
+ @override
66
72
  def command(self):
67
73
  return "flatpak list --columns=application"
68
74
 
75
+ @override
69
76
  def process(self, output):
70
77
  return [flatpak for flatpak in output[1:]]
@@ -0,0 +1,75 @@
1
+ from __future__ import annotations
2
+
3
+ from typing_extensions import Optional, override
4
+
5
+ from pyinfra.api import FactBase
6
+ from pyinfra.api.command import QuoteString, StringCommand, make_formatted_string_command
7
+
8
+
9
+ class ServiceScript(FactBase):
10
+ @override
11
+ def command(self, srvname: str, jail: Optional[str] = None) -> StringCommand:
12
+ if jail is None:
13
+ jail = ""
14
+
15
+ return make_formatted_string_command(
16
+ (
17
+ "for service in `service -j {0} -l`; do "
18
+ 'if [ {1} = \\"$service\\" ]; '
19
+ 'then echo \\"$service\\"; '
20
+ "fi; "
21
+ "done"
22
+ ),
23
+ QuoteString(jail),
24
+ QuoteString(srvname),
25
+ )
26
+
27
+
28
+ class ServiceStatus(FactBase):
29
+ @override
30
+ def command(self, srvname: str, jail: Optional[str] = None) -> StringCommand:
31
+ if jail is None:
32
+ jail = ""
33
+
34
+ return make_formatted_string_command(
35
+ (
36
+ "service -j {0} {1} status > /dev/null 2>&1; "
37
+ "if [ $? -eq 0 ]; then "
38
+ "echo running; "
39
+ "fi"
40
+ ),
41
+ QuoteString(jail),
42
+ QuoteString(srvname),
43
+ )
44
+
45
+
46
+ class Sysrc(FactBase):
47
+ @override
48
+ def command(self, parameter: str, jail: Optional[str] = None) -> StringCommand:
49
+ if jail is None:
50
+ command = make_formatted_string_command(
51
+ ("sysrc -in -- {0} || true"), QuoteString(parameter)
52
+ )
53
+ else:
54
+ command = make_formatted_string_command(
55
+ ("sysrc -j {0} -in -- {1} || true"), QuoteString(jail), QuoteString(parameter)
56
+ )
57
+
58
+ return command
59
+
60
+
61
+ class PkgPackage(FactBase):
62
+ @override
63
+ def command(self, package: str, jail: Optional[str] = None) -> StringCommand:
64
+ if jail is None:
65
+ command = make_formatted_string_command(
66
+ ("pkg info -E -- {0} 2> /dev/null || true"), QuoteString(package)
67
+ )
68
+ else:
69
+ command = make_formatted_string_command(
70
+ ("pkg -j {0} info -E -- {1} 2> /dev/null || true"),
71
+ QuoteString(jail),
72
+ QuoteString(package),
73
+ )
74
+
75
+ return command
pyinfra/facts/gem.py CHANGED
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing_extensions import override
4
+
3
5
  from pyinfra.api import FactBase
4
6
 
5
7
  from .util.packaging import parse_packages
@@ -18,13 +20,16 @@ class GemPackages(FactBase):
18
20
  }
19
21
  """
20
22
 
23
+ @override
21
24
  def command(self) -> str:
22
25
  return "gem list --local"
23
26
 
27
+ @override
24
28
  def requires_command(self) -> str:
25
29
  return "gem"
26
30
 
27
31
  default = dict
28
32
 
33
+ @override
29
34
  def process(self, output):
30
35
  return parse_packages(GEM_REGEX, output)
pyinfra/facts/git.py CHANGED
@@ -2,18 +2,23 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
 
5
+ from typing_extensions import override
6
+
5
7
  from pyinfra.api.facts import FactBase
6
8
 
7
9
 
8
10
  class GitFactBase(FactBase):
11
+ @override
9
12
  def requires_command(self, *args, **kwargs) -> str:
10
13
  return "git"
11
14
 
12
15
 
13
16
  class GitBranch(GitFactBase):
17
+ @override
14
18
  def command(self, repo) -> str:
15
19
  return "! test -d {0} || (cd {0} && git describe --all)".format(repo)
16
20
 
21
+ @override
17
22
  def process(self, output):
18
23
  return re.sub(r"(heads|tags)/", r"", "\n".join(output))
19
24
 
@@ -21,12 +26,15 @@ class GitBranch(GitFactBase):
21
26
  class GitConfig(GitFactBase):
22
27
  default = dict
23
28
 
24
- def command(self, repo=None) -> str:
29
+ @override
30
+ def command(self, repo=None, system=False) -> str:
25
31
  if repo is None:
26
- return "git config --global -l || true"
32
+ level = "--system" if system else "--global"
33
+ return f"git config {level} -l || true"
27
34
 
28
35
  return "! test -d {0} || (cd {0} && git config --local -l)".format(repo)
29
36
 
37
+ @override
30
38
  def process(self, output):
31
39
  items: dict[str, list[str]] = {}
32
40
 
@@ -38,9 +46,11 @@ class GitConfig(GitFactBase):
38
46
 
39
47
 
40
48
  class GitTrackingBranch(GitFactBase):
49
+ @override
41
50
  def command(self, repo) -> str:
42
51
  return r"! test -d {0} || (cd {0} && git status --branch --porcelain)".format(repo)
43
52
 
53
+ @override
44
54
  def process(self, output):
45
55
  if not output:
46
56
  return None
pyinfra/facts/gpg.py CHANGED
@@ -2,18 +2,22 @@ from __future__ import annotations
2
2
 
3
3
  from urllib.parse import urlparse
4
4
 
5
+ from typing_extensions import override
6
+
5
7
  from pyinfra.api import FactBase
6
8
 
7
9
 
8
10
  class GpgFactBase(FactBase):
9
11
  abstract = True
10
12
 
13
+ @override
11
14
  def requires_command(self, *args, **kwargs) -> str:
12
15
  return "gpg"
13
16
 
14
17
  key_record_type = "pub"
15
18
  subkey_record_type = "sub"
16
19
 
20
+ @override
17
21
  def process(self, output):
18
22
  # For details on the field values see:
19
23
  # https://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blob_plain;f=doc/DETAILS
@@ -88,6 +92,7 @@ class GpgKey(GpgFactBase):
88
92
  }
89
93
  """
90
94
 
95
+ @override
91
96
  def command(self, src):
92
97
  if urlparse(src).scheme:
93
98
  return ("(wget -O - {0} || curl -sSLf {0}) | gpg --with-colons").format(src)
@@ -109,6 +114,7 @@ class GpgKeys(GpgFactBase):
109
114
  }
110
115
  """
111
116
 
117
+ @override
112
118
  def command(self, keyring=None):
113
119
  if not keyring:
114
120
  return "gpg --list-keys --with-colons"
@@ -134,6 +140,7 @@ class GpgSecretKeys(GpgFactBase):
134
140
  key_record_type = "sec"
135
141
  subkey_record_type = "ssb"
136
142
 
143
+ @override
137
144
  def command(self, keyring=None):
138
145
  if not keyring:
139
146
  return "gpg --list-secret-keys --with-colons"
pyinfra/facts/hardware.py CHANGED
@@ -2,6 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
 
5
+ from typing_extensions import override
6
+
5
7
  from pyinfra.api import FactBase, ShortFactBase
6
8
 
7
9
 
@@ -10,9 +12,11 @@ class Cpus(FactBase[int]):
10
12
  Returns the number of CPUs on this server.
11
13
  """
12
14
 
15
+ @override
13
16
  def command(self) -> str:
14
17
  return "getconf NPROCESSORS_ONLN 2> /dev/null || getconf _NPROCESSORS_ONLN"
15
18
 
19
+ @override
16
20
  def process(self, output):
17
21
  try:
18
22
  return int(list(output)[0])
@@ -25,12 +29,15 @@ class Memory(FactBase):
25
29
  Returns the memory installed in this server, in MB.
26
30
  """
27
31
 
32
+ @override
28
33
  def requires_command(self) -> str:
29
34
  return "vmstat"
30
35
 
36
+ @override
31
37
  def command(self) -> str:
32
38
  return "vmstat -s"
33
39
 
40
+ @override
34
41
  def process(self, output):
35
42
  data = {}
36
43
 
@@ -80,9 +87,11 @@ class BlockDevices(FactBase):
80
87
  regex = r"([a-zA-Z0-9\/\-_]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]{1,3})%\s+([a-zA-Z\/0-9\-_]+)" # noqa: E501
81
88
  default = dict
82
89
 
90
+ @override
83
91
  def command(self) -> str:
84
92
  return "df"
85
93
 
94
+ @override
86
95
  def process(self, output):
87
96
  devices = {}
88
97
 
@@ -177,11 +186,13 @@ class NetworkDevices(FactBase):
177
186
 
178
187
  default = dict
179
188
 
189
+ @override
180
190
  def command(self) -> str:
181
191
  return "ip addr show 2> /dev/null || ifconfig -a"
182
192
 
183
193
  # Definition of valid interface names for Linux:
184
194
  # https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/net/core/dev.c?h=v5.1.3#n1020
195
+ @override
185
196
  def process(self, output):
186
197
  def mask(value):
187
198
  try:
@@ -318,6 +329,7 @@ class Ipv4Addrs(ShortFactBase):
318
329
  fact = NetworkDevices
319
330
  ip_type = "ipv4"
320
331
 
332
+ @override
321
333
  def process_data(self, data):
322
334
  host_to_ips = {}
323
335
 
@@ -379,6 +391,7 @@ class Ipv4Addresses(ShortFactBase):
379
391
  fact = NetworkDevices
380
392
  ip_type = "ipv4"
381
393
 
394
+ @override
382
395
  def process_data(self, data):
383
396
  addresses = {}
384
397
 
pyinfra/facts/iptables.py CHANGED
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing_extensions import override
4
+
3
5
  from pyinfra.api import FactBase
4
6
 
5
7
  # Mapping for iptables code arguments to variable names
@@ -35,7 +37,7 @@ def parse_iptables_rule(line):
35
37
  args: list[str] = []
36
38
  not_arg = False
37
39
 
38
- def add_args():
40
+ def add_args() -> None:
39
41
  arg_string = " ".join(args)
40
42
 
41
43
  if key and key in IPTABLES_ARGS:
@@ -89,9 +91,11 @@ class IptablesRules(FactBase):
89
91
 
90
92
  default = list
91
93
 
94
+ @override
92
95
  def command(self, table="filter"):
93
96
  return "iptables-save -t {0}".format(table)
94
97
 
98
+ @override
95
99
  def process(self, output):
96
100
  rules = []
97
101
 
@@ -116,6 +120,7 @@ class Ip6tablesRules(IptablesRules):
116
120
  ]
117
121
  """
118
122
 
123
+ @override
119
124
  def command(self, table="filter"):
120
125
  return "ip6tables-save -t {0}".format(table)
121
126
 
@@ -133,9 +138,11 @@ class IptablesChains(FactBase):
133
138
 
134
139
  default = dict
135
140
 
141
+ @override
136
142
  def command(self, table="filter"):
137
143
  return "iptables-save -t {0}".format(table)
138
144
 
145
+ @override
139
146
  def process(self, output):
140
147
  chains = {}
141
148
 
@@ -160,5 +167,6 @@ class Ip6tablesChains(IptablesChains):
160
167
  }
161
168
  """
162
169
 
170
+ @override
163
171
  def command(self, table="filter"):
164
172
  return "ip6tables-save -t {0}".format(table)
pyinfra/facts/launchd.py CHANGED
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from typing_extensions import override
4
+
3
5
  from pyinfra.api import FactBase
4
6
 
5
7
 
@@ -8,14 +10,17 @@ class LaunchdStatus(FactBase):
8
10
  Returns a dict of name -> status for launchd managed services.
9
11
  """
10
12
 
13
+ @override
11
14
  def command(self) -> str:
12
15
  return "launchctl list"
13
16
 
17
+ @override
14
18
  def requires_command(self) -> str:
15
19
  return "launchctl"
16
20
 
17
21
  default = dict
18
22
 
23
+ @override
19
24
  def process(self, output):
20
25
  services = {}
21
26
 
pyinfra/facts/lxd.py CHANGED
@@ -2,6 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import json
4
4
 
5
+ from typing_extensions import override
6
+
5
7
  from pyinfra.api import FactBase
6
8
 
7
9
 
@@ -10,14 +12,17 @@ class LxdContainers(FactBase):
10
12
  Returns a list of running LXD containers
11
13
  """
12
14
 
15
+ @override
13
16
  def command(self) -> str:
14
17
  return "lxc list --format json --fast"
15
18
 
19
+ @override
16
20
  def requires_command(self) -> str:
17
21
  return "lxc"
18
22
 
19
23
  default = list
20
24
 
25
+ @override
21
26
  def process(self, output):
22
27
  output = list(output)
23
28
  assert len(output) == 1
pyinfra/facts/mysql.py CHANGED
@@ -3,6 +3,8 @@ from __future__ import annotations
3
3
  import re
4
4
  from collections import defaultdict
5
5
 
6
+ from typing_extensions import override
7
+
6
8
  from pyinfra.api import FactBase, MaskString, QuoteString, StringCommand
7
9
  from pyinfra.api.util import try_int
8
10
 
@@ -62,9 +64,11 @@ class MysqlFactBase(FactBase):
62
64
  mysql_command: str
63
65
  ignore_errors = False
64
66
 
67
+ @override
65
68
  def requires_command(self, *args, **kwargs) -> str:
66
69
  return "mysql"
67
70
 
71
+ @override
68
72
  def command(
69
73
  self,
70
74
  # Details for speaking to MySQL via `mysql` CLI via `mysql` CLI
@@ -100,6 +104,7 @@ class MysqlDatabases(MysqlFactBase):
100
104
  default = dict
101
105
  mysql_command = "SELECT * FROM information_schema.SCHEMATA"
102
106
 
107
+ @override
103
108
  def process(self, output):
104
109
  rows = parse_columns_and_rows(
105
110
  output,
@@ -136,6 +141,7 @@ class MysqlUsers(MysqlFactBase):
136
141
  default = dict
137
142
  mysql_command = "SELECT * FROM mysql.user"
138
143
 
144
+ @override
139
145
  def process(self, output):
140
146
  rows = parse_columns_and_rows(output, "\t")
141
147
 
@@ -195,6 +201,7 @@ class MysqlUserGrants(MysqlFactBase):
195
201
  # Ignore errors as SHOW GRANTS will error if the user does not exist
196
202
  ignore_errors = True
197
203
 
204
+ @override
198
205
  def command( # type: ignore[override]
199
206
  self,
200
207
  user,
@@ -214,8 +221,8 @@ class MysqlUserGrants(MysqlFactBase):
214
221
  mysql_port,
215
222
  )
216
223
 
217
- @staticmethod
218
- def process(output):
224
+ @override
225
+ def process(self, output):
219
226
  database_table_privileges = defaultdict(set)
220
227
 
221
228
  for line in output:
pyinfra/facts/npm.py CHANGED
@@ -1,6 +1,8 @@
1
1
  # encoding: utf8
2
2
  from __future__ import annotations
3
3
 
4
+ from typing_extensions import override
5
+
4
6
  from pyinfra.api import FactBase
5
7
 
6
8
  from .util.packaging import parse_packages
@@ -21,13 +23,16 @@ class NpmPackages(FactBase):
21
23
 
22
24
  default = dict
23
25
 
26
+ @override
24
27
  def requires_command(self, directory=None) -> str:
25
28
  return "npm"
26
29
 
30
+ @override
27
31
  def command(self, directory=None):
28
32
  if directory:
29
33
  return ("cd {0} && npm list -g --depth=0").format(directory)
30
34
  return "npm list -g --depth=0"
31
35
 
36
+ @override
32
37
  def process(self, output):
33
38
  return parse_packages(NPM_REGEX, output)
pyinfra/facts/openrc.py CHANGED
@@ -2,6 +2,8 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
 
5
+ from typing_extensions import override
6
+
5
7
  from pyinfra.api import FactBase
6
8
 
7
9
 
@@ -20,12 +22,15 @@ class OpenrcStatus(FactBase):
20
22
  r"\s+\]"
21
23
  )
22
24
 
25
+ @override
23
26
  def requires_command(self, runlevel="default") -> str:
24
27
  return "rc-status"
25
28
 
29
+ @override
26
30
  def command(self, runlevel="default"):
27
31
  return "rc-status {0}".format(runlevel)
28
32
 
33
+ @override
29
34
  def process(self, output):
30
35
  services = {}
31
36
 
@@ -44,13 +49,16 @@ class OpenrcEnabled(FactBase):
44
49
 
45
50
  default = dict
46
51
 
52
+ @override
47
53
  def requires_command(self, runlevel="default") -> str:
48
54
  return "rc-update"
49
55
 
56
+ @override
50
57
  def command(self, runlevel="default"):
51
58
  self.runlevel = runlevel
52
59
  return "rc-update show -v"
53
60
 
61
+ @override
54
62
  def process(self, output):
55
63
  services = {}
56
64