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.
- pyinfra/api/arguments.py +9 -2
- pyinfra/api/arguments_typed.py +4 -5
- pyinfra/api/command.py +22 -3
- pyinfra/api/config.py +5 -2
- pyinfra/api/deploy.py +4 -2
- pyinfra/api/facts.py +3 -0
- pyinfra/api/host.py +15 -7
- pyinfra/api/operation.py +2 -1
- pyinfra/api/state.py +1 -1
- pyinfra/connectors/base.py +34 -8
- pyinfra/connectors/chroot.py +7 -2
- pyinfra/connectors/docker.py +24 -8
- pyinfra/connectors/dockerssh.py +7 -2
- pyinfra/connectors/local.py +7 -2
- pyinfra/connectors/ssh.py +9 -2
- pyinfra/connectors/sshuserclient/client.py +42 -14
- pyinfra/connectors/sshuserclient/config.py +2 -0
- pyinfra/connectors/terraform.py +1 -1
- pyinfra/connectors/util.py +13 -9
- pyinfra/context.py +9 -2
- pyinfra/facts/apk.py +8 -1
- pyinfra/facts/apt.py +68 -0
- pyinfra/facts/brew.py +13 -0
- pyinfra/facts/bsdinit.py +3 -0
- pyinfra/facts/cargo.py +5 -0
- pyinfra/facts/choco.py +6 -0
- pyinfra/facts/crontab.py +195 -0
- pyinfra/facts/deb.py +10 -0
- pyinfra/facts/dnf.py +5 -0
- pyinfra/facts/docker.py +16 -0
- pyinfra/facts/efibootmgr.py +113 -0
- pyinfra/facts/files.py +112 -7
- pyinfra/facts/flatpak.py +7 -0
- pyinfra/facts/freebsd.py +75 -0
- pyinfra/facts/gem.py +5 -0
- pyinfra/facts/git.py +12 -2
- pyinfra/facts/gpg.py +7 -0
- pyinfra/facts/hardware.py +13 -0
- pyinfra/facts/iptables.py +9 -1
- pyinfra/facts/launchd.py +5 -0
- pyinfra/facts/lxd.py +5 -0
- pyinfra/facts/mysql.py +9 -2
- pyinfra/facts/npm.py +5 -0
- pyinfra/facts/openrc.py +8 -0
- pyinfra/facts/opkg.py +245 -0
- pyinfra/facts/pacman.py +9 -1
- pyinfra/facts/pip.py +5 -0
- pyinfra/facts/pipx.py +82 -0
- pyinfra/facts/pkg.py +4 -0
- pyinfra/facts/pkgin.py +5 -0
- pyinfra/facts/podman.py +54 -0
- pyinfra/facts/postgres.py +10 -2
- pyinfra/facts/rpm.py +11 -0
- pyinfra/facts/runit.py +7 -0
- pyinfra/facts/selinux.py +16 -0
- pyinfra/facts/server.py +87 -79
- pyinfra/facts/snap.py +7 -0
- pyinfra/facts/systemd.py +5 -0
- pyinfra/facts/sysvinit.py +4 -0
- pyinfra/facts/upstart.py +5 -0
- pyinfra/facts/util/__init__.py +4 -1
- pyinfra/facts/util/units.py +30 -0
- pyinfra/facts/vzctl.py +5 -0
- pyinfra/facts/xbps.py +6 -1
- pyinfra/facts/yum.py +5 -0
- pyinfra/facts/zfs.py +41 -21
- pyinfra/facts/zypper.py +5 -0
- pyinfra/local.py +3 -2
- pyinfra/operations/apt.py +36 -22
- pyinfra/operations/crontab.py +189 -0
- pyinfra/operations/docker.py +61 -56
- pyinfra/operations/files.py +65 -1
- pyinfra/operations/freebsd/__init__.py +12 -0
- pyinfra/operations/freebsd/freebsd_update.py +70 -0
- pyinfra/operations/freebsd/pkg.py +219 -0
- pyinfra/operations/freebsd/service.py +116 -0
- pyinfra/operations/freebsd/sysrc.py +92 -0
- pyinfra/operations/git.py +23 -7
- pyinfra/operations/opkg.py +88 -0
- pyinfra/operations/pip.py +3 -2
- pyinfra/operations/pipx.py +90 -0
- pyinfra/operations/postgres.py +114 -27
- pyinfra/operations/runit.py +2 -0
- pyinfra/operations/server.py +9 -181
- pyinfra/operations/util/docker.py +44 -22
- pyinfra/operations/zfs.py +3 -3
- {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/LICENSE.md +1 -1
- {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/METADATA +25 -25
- pyinfra-3.3.dist-info/RECORD +187 -0
- pyinfra_cli/exceptions.py +5 -0
- pyinfra_cli/inventory.py +26 -9
- pyinfra_cli/log.py +3 -0
- pyinfra_cli/main.py +9 -8
- pyinfra_cli/prints.py +19 -4
- pyinfra_cli/util.py +3 -0
- pyinfra_cli/virtualenv.py +1 -1
- tests/test_cli/test_cli_deploy.py +15 -13
- tests/test_cli/test_cli_inventory.py +53 -0
- tests/test_connectors/test_ssh.py +302 -182
- tests/test_connectors/test_sshuserclient.py +68 -1
- pyinfra-3.1.1.dist-info/RECORD +0 -172
- {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/WHEEL +0 -0
- {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/entry_points.txt +0 -0
- {pyinfra-3.1.1.dist-info → pyinfra-3.3.dist-info}/top_level.txt +0 -0
pyinfra/facts/opkg.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gather the information provided by ``opkg`` on OpenWrt systems:
|
|
3
|
+
+ ``opkg`` configuration
|
|
4
|
+
+ feeds configuration
|
|
5
|
+
+ list of installed packages
|
|
6
|
+
+ list of packages with available upgrades
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
see https://openwrt.org/docs/guide-user/additional-software/opkg
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
from typing import Dict, NamedTuple, Union
|
|
14
|
+
|
|
15
|
+
from typing_extensions import override
|
|
16
|
+
|
|
17
|
+
from pyinfra import logger
|
|
18
|
+
from pyinfra.api import FactBase
|
|
19
|
+
from pyinfra.facts.util.packaging import parse_packages
|
|
20
|
+
|
|
21
|
+
# TODO - change NamedTuple to dataclass Opkgbut need to figure out how to get json serialization
|
|
22
|
+
# to work without changing core code
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class OpkgPkgUpgradeInfo(NamedTuple):
|
|
26
|
+
installed: str
|
|
27
|
+
available: str
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class OpkgConfInfo(NamedTuple):
|
|
31
|
+
paths: Dict[str, str] # list of paths, e.g. {'root':'/', 'ram':'/tmp}
|
|
32
|
+
list_dir: str # where package lists are stored, e.g. /var/opkg-lists
|
|
33
|
+
options: Dict[
|
|
34
|
+
str, Union[str, bool]
|
|
35
|
+
] # mapping from option to value, e.g. {'check_signature': True}
|
|
36
|
+
arch_cfg: Dict[str, int] # priorities for architectures
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class OpkgFeedInfo(NamedTuple):
|
|
40
|
+
url: str # url for the feed
|
|
41
|
+
fmt: str # format of the feed, e.g. "src/gz"
|
|
42
|
+
kind: str # whether it comes from the 'distribution' or is 'custom'
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class OpkgConf(FactBase):
|
|
46
|
+
"""
|
|
47
|
+
Returns a NamedTuple with the current configuration:
|
|
48
|
+
|
|
49
|
+
.. code:: python
|
|
50
|
+
|
|
51
|
+
ConfInfo(
|
|
52
|
+
paths = {
|
|
53
|
+
"root": "/",
|
|
54
|
+
"ram": "/tmp",
|
|
55
|
+
},
|
|
56
|
+
list_dir = "/opt/opkg-lists",
|
|
57
|
+
options = {
|
|
58
|
+
"overlay_root": "/overlay"
|
|
59
|
+
},
|
|
60
|
+
arch_cfg = {
|
|
61
|
+
"all": 1,
|
|
62
|
+
"noarch": 1,
|
|
63
|
+
"i386_pentium": 10
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
regex = re.compile(
|
|
70
|
+
r"""
|
|
71
|
+
^(?:\s*)
|
|
72
|
+
(?:
|
|
73
|
+
(?:arch\s+(?P<arch>\w+)\s+(?P<priority>\d+))|
|
|
74
|
+
(?:dest\s+(?P<dest>\w+)\s+(?P<dest_path>[\w/\-]+))|
|
|
75
|
+
(?:lists_dir\s+(?P<lists_dir>ext)\s+(?P<list_path>[\w/\-]+))|
|
|
76
|
+
(?:option\s+(?P<option>\w+)(?:\s+(?P<value>[^#]+))?)
|
|
77
|
+
)?
|
|
78
|
+
(?:\s*\#.*)?
|
|
79
|
+
$
|
|
80
|
+
""",
|
|
81
|
+
re.X,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def default():
|
|
86
|
+
return OpkgConfInfo({}, "", {}, {})
|
|
87
|
+
|
|
88
|
+
@override
|
|
89
|
+
def command(self) -> str:
|
|
90
|
+
return "cat /etc/opkg.conf"
|
|
91
|
+
|
|
92
|
+
@override
|
|
93
|
+
def process(self, output):
|
|
94
|
+
dest, lists_dir, options, arch_cfg = {}, "", {}, {}
|
|
95
|
+
for line in output:
|
|
96
|
+
match = self.regex.match(line)
|
|
97
|
+
|
|
98
|
+
if match is None:
|
|
99
|
+
logger.warning(f"Opkg: could not parse opkg.conf line '{line}'")
|
|
100
|
+
elif match.group("arch") is not None:
|
|
101
|
+
arch_cfg[match.group("arch")] = int(match.group("priority"))
|
|
102
|
+
elif match.group("dest") is not None:
|
|
103
|
+
dest[match.group("dest")] = match.group("dest_path")
|
|
104
|
+
elif match.group("lists_dir") is not None:
|
|
105
|
+
lists_dir = match.group("list_path")
|
|
106
|
+
elif match.group("option") is not None:
|
|
107
|
+
options[match.group("option")] = match.group("value") or True
|
|
108
|
+
|
|
109
|
+
return OpkgConfInfo(dest, lists_dir, options, arch_cfg)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class OpkgFeeds(FactBase):
|
|
113
|
+
"""
|
|
114
|
+
Returns a dictionary containing the information for the distribution-provided and
|
|
115
|
+
custom opkg feeds:
|
|
116
|
+
|
|
117
|
+
.. code:: python
|
|
118
|
+
|
|
119
|
+
{
|
|
120
|
+
'openwrt_base': FeedInfo(url='http://downloads ... /i386_pentium/base', fmt='src/gz', kind='distribution'), # noqa: E501
|
|
121
|
+
'openwrt_core': FeedInfo(url='http://downloads ... /x86/geode/packages', fmt='src/gz', kind='distribution'), # noqa: E501
|
|
122
|
+
'openwrt_luci': FeedInfo(url='http://downloads ... /i386_pentium/luci', fmt='src/gz', kind='distribution'),# noqa: E501
|
|
123
|
+
'openwrt_packages': FeedInfo(url='http://downloads ... /i386_pentium/packages', fmt='src/gz', kind='distribution'),# noqa: E501
|
|
124
|
+
'openwrt_routing': FeedInfo(url='http://downloads ... /i386_pentium/routing', fmt='src/gz', kind='distribution'),# noqa: E501
|
|
125
|
+
'openwrt_telephony': FeedInfo(url='http://downloads ... /i386_pentium/telephony', fmt='src/gz', kind='distribution') # noqa: E501
|
|
126
|
+
}
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
regex = re.compile(
|
|
130
|
+
r"^(CUSTOM)|(?:\s*(?P<fmt>[\w/]+)\s+(?P<name>[\w]+)\s+(?P<url>[\w./:]+))?(?:\s*#.*)?$"
|
|
131
|
+
)
|
|
132
|
+
default = dict
|
|
133
|
+
|
|
134
|
+
@override
|
|
135
|
+
def command(self) -> str:
|
|
136
|
+
return "cat /etc/opkg/distfeeds.conf; echo CUSTOM; cat /etc/opkg/customfeeds.conf"
|
|
137
|
+
|
|
138
|
+
@override
|
|
139
|
+
def process(self, output):
|
|
140
|
+
feeds, kind = {}, "distribution"
|
|
141
|
+
for line in output:
|
|
142
|
+
match = self.regex.match(line)
|
|
143
|
+
|
|
144
|
+
if match is None:
|
|
145
|
+
logger.warning(f"Opkg: could not parse /etc/opkg/*feeds.conf line '{line}'")
|
|
146
|
+
elif match.group(0) == "CUSTOM":
|
|
147
|
+
kind = "custom"
|
|
148
|
+
elif match.group("name") is not None:
|
|
149
|
+
feeds[match.group("name")] = OpkgFeedInfo(
|
|
150
|
+
match.group("url"), match.group("fmt"), kind
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return feeds
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class OpkgInstallableArchitectures(FactBase):
|
|
157
|
+
"""
|
|
158
|
+
Returns a dictionary containing the currently installable architectures for this system along
|
|
159
|
+
with their priority:
|
|
160
|
+
|
|
161
|
+
.. code:: python
|
|
162
|
+
|
|
163
|
+
{
|
|
164
|
+
'all': 1,
|
|
165
|
+
'i386_pentium': 10,
|
|
166
|
+
'noarch': 1
|
|
167
|
+
}
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
regex = re.compile(r"^(?:\s*arch\s+(?P<arch>[\w]+)\s+(?P<prio>\d+))?(\s*#.*)?$")
|
|
171
|
+
default = dict
|
|
172
|
+
|
|
173
|
+
@override
|
|
174
|
+
def command(self) -> str:
|
|
175
|
+
return "/bin/opkg print-architecture"
|
|
176
|
+
|
|
177
|
+
@override
|
|
178
|
+
def process(self, output):
|
|
179
|
+
arch_list = {}
|
|
180
|
+
for line in output:
|
|
181
|
+
match = self.regex.match(line)
|
|
182
|
+
|
|
183
|
+
if match is None:
|
|
184
|
+
logger.warning(f"could not parse arch line '{line}'")
|
|
185
|
+
elif match.group("arch") is not None:
|
|
186
|
+
arch_list[match.group("arch")] = int(match.group("prio"))
|
|
187
|
+
|
|
188
|
+
return arch_list
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class OpkgPackages(FactBase):
|
|
192
|
+
"""
|
|
193
|
+
Returns a dict of installed opkg packages:
|
|
194
|
+
|
|
195
|
+
.. code:: python
|
|
196
|
+
|
|
197
|
+
{
|
|
198
|
+
'package_name': ['version'],
|
|
199
|
+
...
|
|
200
|
+
}
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
regex = r"^([a-zA-Z0-9][\w\-\.]*)\s-\s([\w\-\.]+)"
|
|
204
|
+
default = dict
|
|
205
|
+
|
|
206
|
+
@override
|
|
207
|
+
def command(self) -> str:
|
|
208
|
+
return "/bin/opkg list-installed"
|
|
209
|
+
|
|
210
|
+
@override
|
|
211
|
+
def process(self, output):
|
|
212
|
+
return parse_packages(self.regex, sorted(output))
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class OpkgUpgradeablePackages(FactBase):
|
|
216
|
+
"""
|
|
217
|
+
Returns a dict of installed and upgradable opkg packages:
|
|
218
|
+
|
|
219
|
+
.. code:: python
|
|
220
|
+
|
|
221
|
+
{
|
|
222
|
+
'package_name': (installed='1.2.3', available='1.2.8')
|
|
223
|
+
...
|
|
224
|
+
}
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
regex = re.compile(r"^([a-zA-Z0-9][\w\-.]*)\s-\s([\w\-.]+)\s-\s([\w\-.]+)")
|
|
228
|
+
default = dict
|
|
229
|
+
use_default_on_error = True
|
|
230
|
+
|
|
231
|
+
@override
|
|
232
|
+
def command(self) -> str:
|
|
233
|
+
return "/bin/opkg list-upgradable" # yes, really spelled that way
|
|
234
|
+
|
|
235
|
+
@override
|
|
236
|
+
def process(self, output):
|
|
237
|
+
result = {}
|
|
238
|
+
for line in output:
|
|
239
|
+
match = self.regex.match(line)
|
|
240
|
+
if match and len(match.groups()) == 3:
|
|
241
|
+
result[match.group(1)] = OpkgPkgUpgradeInfo(match.group(2), match.group(3))
|
|
242
|
+
else:
|
|
243
|
+
logger.warning(f"Opkg: could not list-upgradable line '{line}'")
|
|
244
|
+
|
|
245
|
+
return result
|
pyinfra/facts/pacman.py
CHANGED
|
@@ -2,11 +2,13 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import shlex
|
|
4
4
|
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
5
7
|
from pyinfra.api import FactBase
|
|
6
8
|
|
|
7
9
|
from .util.packaging import parse_packages
|
|
8
10
|
|
|
9
|
-
PACMAN_REGEX = r"^([0-9a-zA-Z\-]+)\s([0-9\._+a-z
|
|
11
|
+
PACMAN_REGEX = r"^([0-9a-zA-Z\-_]+)\s([0-9\._+a-z\-:]+)"
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class PacmanUnpackGroup(FactBase):
|
|
@@ -21,15 +23,18 @@ class PacmanUnpackGroup(FactBase):
|
|
|
21
23
|
]
|
|
22
24
|
"""
|
|
23
25
|
|
|
26
|
+
@override
|
|
24
27
|
def requires_command(self, *args, **kwargs) -> str:
|
|
25
28
|
return "pacman"
|
|
26
29
|
|
|
27
30
|
default = list
|
|
28
31
|
|
|
32
|
+
@override
|
|
29
33
|
def command(self, package):
|
|
30
34
|
# Accept failure here (|| true) for invalid/unknown packages
|
|
31
35
|
return 'pacman -S --print-format "%n" {0} || true'.format(shlex.quote(package))
|
|
32
36
|
|
|
37
|
+
@override
|
|
33
38
|
def process(self, output):
|
|
34
39
|
return output
|
|
35
40
|
|
|
@@ -45,13 +50,16 @@ class PacmanPackages(FactBase):
|
|
|
45
50
|
}
|
|
46
51
|
"""
|
|
47
52
|
|
|
53
|
+
@override
|
|
48
54
|
def command(self) -> str:
|
|
49
55
|
return "pacman -Q"
|
|
50
56
|
|
|
57
|
+
@override
|
|
51
58
|
def requires_command(self, *args, **kwargs) -> str:
|
|
52
59
|
return "pacman"
|
|
53
60
|
|
|
54
61
|
default = dict
|
|
55
62
|
|
|
63
|
+
@override
|
|
56
64
|
def process(self, output):
|
|
57
65
|
return parse_packages(PACMAN_REGEX, output)
|
pyinfra/facts/pip.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
|
|
@@ -21,13 +23,16 @@ class PipPackages(FactBase):
|
|
|
21
23
|
default = dict
|
|
22
24
|
pip_command = "pip"
|
|
23
25
|
|
|
26
|
+
@override
|
|
24
27
|
def requires_command(self, pip=None):
|
|
25
28
|
return pip or self.pip_command
|
|
26
29
|
|
|
30
|
+
@override
|
|
27
31
|
def command(self, pip=None):
|
|
28
32
|
pip = pip or self.pip_command
|
|
29
33
|
return "{0} freeze --all".format(pip)
|
|
30
34
|
|
|
35
|
+
@override
|
|
31
36
|
def process(self, output):
|
|
32
37
|
return parse_packages(PIP_REGEX, output)
|
|
33
38
|
|
pyinfra/facts/pipx.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
5
|
+
from pyinfra.api import FactBase
|
|
6
|
+
|
|
7
|
+
from .util.packaging import parse_packages
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# TODO: move to an utils file
|
|
11
|
+
def parse_environment(output):
|
|
12
|
+
environment_REGEX = r"^(?P<key>[A-Z_]+)=(?P<value>.*)$"
|
|
13
|
+
environment_variables = {}
|
|
14
|
+
|
|
15
|
+
for line in output:
|
|
16
|
+
matches = re.match(environment_REGEX, line)
|
|
17
|
+
|
|
18
|
+
if matches:
|
|
19
|
+
environment_variables[matches.group("key")] = matches.group("value")
|
|
20
|
+
|
|
21
|
+
return environment_variables
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
PIPX_REGEX = r"^([a-zA-Z0-9_\-\+\.]+)\s+([0-9\.]+[a-z0-9\-]*)$"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PipxPackages(FactBase):
|
|
28
|
+
"""
|
|
29
|
+
Returns a dict of installed pipx packages:
|
|
30
|
+
|
|
31
|
+
.. code:: python
|
|
32
|
+
|
|
33
|
+
{
|
|
34
|
+
"package_name": ["version"],
|
|
35
|
+
}
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
default = dict
|
|
39
|
+
|
|
40
|
+
@override
|
|
41
|
+
def requires_command(self) -> str:
|
|
42
|
+
return "pipx"
|
|
43
|
+
|
|
44
|
+
@override
|
|
45
|
+
def command(self) -> str:
|
|
46
|
+
return "pipx list --short"
|
|
47
|
+
|
|
48
|
+
@override
|
|
49
|
+
def process(self, output):
|
|
50
|
+
return parse_packages(PIPX_REGEX, output)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class PipxEnvironment(FactBase):
|
|
54
|
+
"""
|
|
55
|
+
Returns a dict of pipx environment variables:
|
|
56
|
+
|
|
57
|
+
.. code:: python
|
|
58
|
+
|
|
59
|
+
{
|
|
60
|
+
"PIPX_HOME": "/home/doodba/.local/pipx",
|
|
61
|
+
"PIPX_BIN_DIR": "/home/doodba/.local/bin",
|
|
62
|
+
"PIPX_SHARED_LIBS": "/home/doodba/.local/pipx/shared",
|
|
63
|
+
"PIPX_LOCAL_VENVS": "/home/doodba/.local/pipx/venvs",
|
|
64
|
+
"PIPX_LOG_DIR": "/home/doodba/.local/pipx/logs",
|
|
65
|
+
"PIPX_TRASH_DIR": "/home/doodba/.local/pipx/.trash",
|
|
66
|
+
"PIPX_VENV_CACHEDIR": "/home/doodba/.local/pipx/.cache",
|
|
67
|
+
}
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
default = dict
|
|
71
|
+
|
|
72
|
+
@override
|
|
73
|
+
def requires_command(self) -> str:
|
|
74
|
+
return "pipx"
|
|
75
|
+
|
|
76
|
+
@override
|
|
77
|
+
def command(self) -> str:
|
|
78
|
+
return "pipx environment"
|
|
79
|
+
|
|
80
|
+
@override
|
|
81
|
+
def process(self, output):
|
|
82
|
+
return parse_environment(output)
|
pyinfra/facts/pkg.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
|
|
@@ -19,8 +21,10 @@ class PkgPackages(FactBase):
|
|
|
19
21
|
regex = r"^([a-zA-Z0-9_\-\+]+)\-([0-9a-z\.]+)"
|
|
20
22
|
default = dict
|
|
21
23
|
|
|
24
|
+
@override
|
|
22
25
|
def command(self) -> str:
|
|
23
26
|
return "pkg info || pkg_info || true"
|
|
24
27
|
|
|
28
|
+
@override
|
|
25
29
|
def process(self, output):
|
|
26
30
|
return parse_packages(self.regex, output)
|
pyinfra/facts/pkgin.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 PkginPackages(FactBase):
|
|
|
18
20
|
}
|
|
19
21
|
"""
|
|
20
22
|
|
|
23
|
+
@override
|
|
21
24
|
def command(self) -> str:
|
|
22
25
|
return "pkgin list"
|
|
23
26
|
|
|
27
|
+
@override
|
|
24
28
|
def requires_command(self) -> str:
|
|
25
29
|
return "pkgin"
|
|
26
30
|
|
|
27
31
|
default = dict
|
|
28
32
|
|
|
33
|
+
@override
|
|
29
34
|
def process(self, output):
|
|
30
35
|
return parse_packages(PKGIN_REGEX, output)
|
pyinfra/facts/podman.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any, Dict, Iterable, List, TypeVar
|
|
5
|
+
|
|
6
|
+
from typing_extensions import override
|
|
7
|
+
|
|
8
|
+
from pyinfra.api import FactBase
|
|
9
|
+
|
|
10
|
+
T = TypeVar("T")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PodmanFactBase(FactBase[T]):
|
|
14
|
+
"""
|
|
15
|
+
Base for facts using `podman` to retrieve
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
abstract = True
|
|
19
|
+
|
|
20
|
+
@override
|
|
21
|
+
def requires_command(self, *args, **kwargs) -> str:
|
|
22
|
+
return "podman"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PodmanSystemInfo(PodmanFactBase[Dict[str, Any]]):
|
|
26
|
+
"""
|
|
27
|
+
Output of 'podman system info'
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
@override
|
|
31
|
+
def command(self) -> str:
|
|
32
|
+
return "podman system info --format=json"
|
|
33
|
+
|
|
34
|
+
@override
|
|
35
|
+
def process(self, output: Iterable[str]) -> Dict[str, Any]:
|
|
36
|
+
output = json.loads(("").join(output))
|
|
37
|
+
assert isinstance(output, dict)
|
|
38
|
+
return output
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class PodmanPs(PodmanFactBase[List[Dict[str, Any]]]):
|
|
42
|
+
"""
|
|
43
|
+
Output of 'podman ps'
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
@override
|
|
47
|
+
def command(self) -> str:
|
|
48
|
+
return "podman ps --format=json --all"
|
|
49
|
+
|
|
50
|
+
@override
|
|
51
|
+
def process(self, output: Iterable[str]) -> List[Dict[str, Any]]:
|
|
52
|
+
output = json.loads(("").join(output))
|
|
53
|
+
assert isinstance(output, list)
|
|
54
|
+
return output # type: ignore
|
pyinfra/facts/postgres.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, MaskString, QuoteString, StringCommand
|
|
4
6
|
from pyinfra.api.util import try_int
|
|
5
7
|
|
|
@@ -49,15 +51,18 @@ class PostgresFactBase(FactBase):
|
|
|
49
51
|
|
|
50
52
|
psql_command: str
|
|
51
53
|
|
|
54
|
+
@override
|
|
52
55
|
def requires_command(self, *args, **kwargs):
|
|
53
56
|
return "psql"
|
|
54
57
|
|
|
58
|
+
@override
|
|
55
59
|
def command(
|
|
56
60
|
self,
|
|
57
61
|
psql_user=None,
|
|
58
62
|
psql_password=None,
|
|
59
63
|
psql_host=None,
|
|
60
64
|
psql_port=None,
|
|
65
|
+
psql_database=None,
|
|
61
66
|
):
|
|
62
67
|
return make_execute_psql_command(
|
|
63
68
|
self.psql_command,
|
|
@@ -65,6 +70,7 @@ class PostgresFactBase(FactBase):
|
|
|
65
70
|
password=psql_password,
|
|
66
71
|
host=psql_host,
|
|
67
72
|
port=psql_port,
|
|
73
|
+
database=psql_database,
|
|
68
74
|
)
|
|
69
75
|
|
|
70
76
|
|
|
@@ -87,6 +93,7 @@ class PostgresRoles(PostgresFactBase):
|
|
|
87
93
|
default = dict
|
|
88
94
|
psql_command = "SELECT * FROM pg_catalog.pg_roles"
|
|
89
95
|
|
|
96
|
+
@override
|
|
90
97
|
def process(self, output):
|
|
91
98
|
# Remove the last line of the output (row count)
|
|
92
99
|
output = output[:-1]
|
|
@@ -137,8 +144,9 @@ class PostgresDatabases(PostgresFactBase):
|
|
|
137
144
|
"""
|
|
138
145
|
|
|
139
146
|
default = dict
|
|
140
|
-
psql_command = "SELECT pg_catalog.pg_encoding_to_char(encoding),
|
|
147
|
+
psql_command = "SELECT pg_catalog.pg_encoding_to_char(encoding), *, pg_catalog.pg_get_userbyid(datdba) AS owner FROM pg_catalog.pg_database" # noqa: E501
|
|
141
148
|
|
|
149
|
+
@override
|
|
142
150
|
def process(self, output):
|
|
143
151
|
# Remove the last line of the output (row count)
|
|
144
152
|
output = output[:-1]
|
|
@@ -153,7 +161,7 @@ class PostgresDatabases(PostgresFactBase):
|
|
|
153
161
|
|
|
154
162
|
for details in rows:
|
|
155
163
|
details["encoding"] = details.pop("pg_encoding_to_char")
|
|
156
|
-
|
|
164
|
+
details["owner"] = details.pop("owner")
|
|
157
165
|
for key, value in list(details.items()):
|
|
158
166
|
if key.endswith("id") or key in (
|
|
159
167
|
"dba",
|
pyinfra/facts/rpm.py
CHANGED
|
@@ -3,6 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
import re
|
|
4
4
|
import shlex
|
|
5
5
|
|
|
6
|
+
from typing_extensions import override
|
|
7
|
+
|
|
6
8
|
from pyinfra.api import FactBase
|
|
7
9
|
|
|
8
10
|
from .util.packaging import parse_packages
|
|
@@ -22,14 +24,17 @@ class RpmPackages(FactBase):
|
|
|
22
24
|
}
|
|
23
25
|
"""
|
|
24
26
|
|
|
27
|
+
@override
|
|
25
28
|
def command(self) -> str:
|
|
26
29
|
return "rpm --queryformat {0} -qa".format(shlex.quote(rpm_query_format))
|
|
27
30
|
|
|
31
|
+
@override
|
|
28
32
|
def requires_command(self) -> str:
|
|
29
33
|
return "rpm"
|
|
30
34
|
|
|
31
35
|
default = dict
|
|
32
36
|
|
|
37
|
+
@override
|
|
33
38
|
def process(self, output):
|
|
34
39
|
return parse_packages(rpm_regex, output)
|
|
35
40
|
|
|
@@ -46,9 +51,11 @@ class RpmPackage(FactBase):
|
|
|
46
51
|
}
|
|
47
52
|
"""
|
|
48
53
|
|
|
54
|
+
@override
|
|
49
55
|
def requires_command(self, package) -> str:
|
|
50
56
|
return "rpm"
|
|
51
57
|
|
|
58
|
+
@override
|
|
52
59
|
def command(self, package) -> str:
|
|
53
60
|
return (
|
|
54
61
|
"rpm --queryformat {0} -q {1} || "
|
|
@@ -56,6 +63,7 @@ class RpmPackage(FactBase):
|
|
|
56
63
|
"rpm --queryformat {0} -qp {1} 2> /dev/null"
|
|
57
64
|
).format(shlex.quote(rpm_query_format), shlex.quote(package))
|
|
58
65
|
|
|
66
|
+
@override
|
|
59
67
|
def process(self, output):
|
|
60
68
|
for line in output:
|
|
61
69
|
matches = re.match(rpm_regex, line)
|
|
@@ -73,9 +81,11 @@ class RpmPackageProvides(FactBase):
|
|
|
73
81
|
|
|
74
82
|
default = list
|
|
75
83
|
|
|
84
|
+
@override
|
|
76
85
|
def requires_command(self, *args, **kwargs) -> str:
|
|
77
86
|
return "repoquery"
|
|
78
87
|
|
|
88
|
+
@override
|
|
79
89
|
def command(self, package):
|
|
80
90
|
# Accept failure here (|| true) for invalid/unknown packages
|
|
81
91
|
return "repoquery --queryformat {0} --whatprovides {1} || true".format(
|
|
@@ -83,6 +93,7 @@ class RpmPackageProvides(FactBase):
|
|
|
83
93
|
shlex.quote(package),
|
|
84
94
|
)
|
|
85
95
|
|
|
96
|
+
@override
|
|
86
97
|
def process(self, output):
|
|
87
98
|
packages = []
|
|
88
99
|
|
pyinfra/facts/runit.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing_extensions import override
|
|
2
|
+
|
|
1
3
|
from pyinfra.api import FactBase
|
|
2
4
|
|
|
3
5
|
|
|
@@ -20,9 +22,11 @@ class RunitStatus(FactBase):
|
|
|
20
22
|
|
|
21
23
|
default = dict
|
|
22
24
|
|
|
25
|
+
@override
|
|
23
26
|
def requires_command(self, *args, **kwargs) -> str:
|
|
24
27
|
return "sv"
|
|
25
28
|
|
|
29
|
+
@override
|
|
26
30
|
def command(self, service=None, svdir="/var/service") -> str:
|
|
27
31
|
if service is None:
|
|
28
32
|
return (
|
|
@@ -32,6 +36,7 @@ class RunitStatus(FactBase):
|
|
|
32
36
|
else:
|
|
33
37
|
return 'SVDIR="{0}" sv status "{1}"'.format(svdir, service)
|
|
34
38
|
|
|
39
|
+
@override
|
|
35
40
|
def process(self, output):
|
|
36
41
|
services = {}
|
|
37
42
|
for line in output:
|
|
@@ -60,11 +65,13 @@ class RunitManaged(FactBase):
|
|
|
60
65
|
|
|
61
66
|
default = set
|
|
62
67
|
|
|
68
|
+
@override
|
|
63
69
|
def command(self, service=None, svdir="/var/service"):
|
|
64
70
|
if service is None:
|
|
65
71
|
return 'cd "{0}" && find -mindepth 1 -maxdepth 1 -type l -printf "%f\n"'.format(svdir)
|
|
66
72
|
else:
|
|
67
73
|
return 'cd "{0}" && test -h "{1}" && echo "{1}" || true'.format(svdir, service)
|
|
68
74
|
|
|
75
|
+
@override
|
|
69
76
|
def process(self, output):
|
|
70
77
|
return set(output)
|