pyinfra 0.11.dev3__py3-none-any.whl → 3.5.1__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/__init__.py +9 -12
- pyinfra/__main__.py +4 -0
- pyinfra/api/__init__.py +18 -3
- pyinfra/api/arguments.py +406 -0
- pyinfra/api/arguments_typed.py +79 -0
- pyinfra/api/command.py +274 -0
- pyinfra/api/config.py +222 -28
- pyinfra/api/connect.py +33 -13
- pyinfra/api/connectors.py +27 -0
- pyinfra/api/deploy.py +65 -66
- pyinfra/api/exceptions.py +67 -18
- pyinfra/api/facts.py +253 -202
- pyinfra/api/host.py +413 -50
- pyinfra/api/inventory.py +121 -160
- pyinfra/api/operation.py +432 -262
- pyinfra/api/operations.py +273 -260
- pyinfra/api/state.py +302 -248
- pyinfra/api/util.py +291 -368
- pyinfra/connectors/base.py +173 -0
- pyinfra/connectors/chroot.py +212 -0
- pyinfra/connectors/docker.py +381 -0
- pyinfra/connectors/dockerssh.py +297 -0
- pyinfra/connectors/local.py +238 -0
- pyinfra/connectors/scp/__init__.py +1 -0
- pyinfra/connectors/scp/client.py +204 -0
- pyinfra/connectors/ssh.py +670 -0
- pyinfra/connectors/ssh_util.py +114 -0
- pyinfra/connectors/sshuserclient/client.py +309 -0
- pyinfra/connectors/sshuserclient/config.py +102 -0
- pyinfra/connectors/terraform.py +135 -0
- pyinfra/connectors/util.py +410 -0
- pyinfra/connectors/vagrant.py +183 -0
- pyinfra/context.py +145 -0
- pyinfra/facts/__init__.py +7 -6
- pyinfra/facts/apk.py +22 -7
- pyinfra/facts/apt.py +117 -60
- pyinfra/facts/brew.py +100 -15
- pyinfra/facts/bsdinit.py +23 -0
- pyinfra/facts/cargo.py +37 -0
- pyinfra/facts/choco.py +47 -0
- pyinfra/facts/crontab.py +195 -0
- pyinfra/facts/deb.py +94 -0
- pyinfra/facts/dnf.py +48 -0
- pyinfra/facts/docker.py +96 -23
- pyinfra/facts/efibootmgr.py +113 -0
- pyinfra/facts/files.py +630 -58
- pyinfra/facts/flatpak.py +77 -0
- pyinfra/facts/freebsd.py +70 -0
- pyinfra/facts/gem.py +19 -6
- pyinfra/facts/git.py +59 -14
- pyinfra/facts/gpg.py +150 -0
- pyinfra/facts/hardware.py +313 -167
- pyinfra/facts/iptables.py +72 -62
- pyinfra/facts/launchd.py +44 -0
- pyinfra/facts/lxd.py +17 -4
- pyinfra/facts/mysql.py +122 -86
- pyinfra/facts/npm.py +17 -9
- pyinfra/facts/openrc.py +71 -0
- pyinfra/facts/opkg.py +246 -0
- pyinfra/facts/pacman.py +50 -7
- pyinfra/facts/pip.py +24 -7
- pyinfra/facts/pipx.py +82 -0
- pyinfra/facts/pkg.py +15 -6
- pyinfra/facts/pkgin.py +35 -0
- pyinfra/facts/podman.py +54 -0
- pyinfra/facts/postgres.py +178 -0
- pyinfra/facts/postgresql.py +6 -147
- pyinfra/facts/rpm.py +105 -0
- pyinfra/facts/runit.py +77 -0
- pyinfra/facts/selinux.py +161 -0
- pyinfra/facts/server.py +746 -285
- pyinfra/facts/snap.py +88 -0
- pyinfra/facts/systemd.py +139 -0
- pyinfra/facts/sysvinit.py +59 -0
- pyinfra/facts/upstart.py +35 -0
- pyinfra/facts/util/__init__.py +17 -0
- pyinfra/facts/util/databases.py +4 -6
- pyinfra/facts/util/packaging.py +37 -6
- pyinfra/facts/util/units.py +30 -0
- pyinfra/facts/util/win_files.py +99 -0
- pyinfra/facts/vzctl.py +20 -13
- pyinfra/facts/xbps.py +35 -0
- pyinfra/facts/yum.py +34 -40
- pyinfra/facts/zfs.py +77 -0
- pyinfra/facts/zypper.py +42 -0
- pyinfra/local.py +45 -83
- pyinfra/operations/__init__.py +12 -0
- pyinfra/operations/apk.py +98 -0
- pyinfra/operations/apt.py +488 -0
- pyinfra/operations/brew.py +231 -0
- pyinfra/operations/bsdinit.py +59 -0
- pyinfra/operations/cargo.py +45 -0
- pyinfra/operations/choco.py +61 -0
- pyinfra/operations/crontab.py +191 -0
- pyinfra/operations/dnf.py +210 -0
- pyinfra/operations/docker.py +446 -0
- pyinfra/operations/files.py +1939 -0
- pyinfra/operations/flatpak.py +94 -0
- 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/gem.py +47 -0
- pyinfra/operations/git.py +419 -0
- pyinfra/operations/iptables.py +311 -0
- pyinfra/operations/launchd.py +45 -0
- pyinfra/operations/lxd.py +68 -0
- pyinfra/operations/mysql.py +609 -0
- pyinfra/operations/npm.py +57 -0
- pyinfra/operations/openrc.py +63 -0
- pyinfra/operations/opkg.py +88 -0
- pyinfra/operations/pacman.py +81 -0
- pyinfra/operations/pip.py +205 -0
- pyinfra/operations/pipx.py +102 -0
- pyinfra/operations/pkg.py +70 -0
- pyinfra/operations/pkgin.py +91 -0
- pyinfra/operations/postgres.py +436 -0
- pyinfra/operations/postgresql.py +30 -0
- pyinfra/operations/puppet.py +40 -0
- pyinfra/operations/python.py +72 -0
- pyinfra/operations/runit.py +184 -0
- pyinfra/operations/selinux.py +189 -0
- pyinfra/operations/server.py +1099 -0
- pyinfra/operations/snap.py +117 -0
- pyinfra/operations/ssh.py +216 -0
- pyinfra/operations/systemd.py +149 -0
- pyinfra/operations/sysvinit.py +141 -0
- pyinfra/operations/upstart.py +68 -0
- pyinfra/operations/util/__init__.py +12 -0
- pyinfra/operations/util/docker.py +251 -0
- pyinfra/operations/util/files.py +247 -0
- pyinfra/operations/util/packaging.py +336 -0
- pyinfra/operations/util/service.py +46 -0
- pyinfra/operations/vzctl.py +137 -0
- pyinfra/operations/xbps.py +77 -0
- pyinfra/operations/yum.py +210 -0
- pyinfra/operations/zfs.py +175 -0
- pyinfra/operations/zypper.py +192 -0
- pyinfra/progress.py +44 -32
- pyinfra/py.typed +0 -0
- pyinfra/version.py +9 -1
- pyinfra-3.5.1.dist-info/METADATA +141 -0
- pyinfra-3.5.1.dist-info/RECORD +159 -0
- {pyinfra-0.11.dev3.dist-info → pyinfra-3.5.1.dist-info}/WHEEL +1 -2
- pyinfra-3.5.1.dist-info/entry_points.txt +12 -0
- {pyinfra-0.11.dev3.dist-info → pyinfra-3.5.1.dist-info/licenses}/LICENSE.md +1 -1
- pyinfra_cli/__init__.py +1 -0
- pyinfra_cli/cli.py +780 -0
- pyinfra_cli/commands.py +66 -0
- pyinfra_cli/exceptions.py +155 -65
- pyinfra_cli/inventory.py +233 -89
- pyinfra_cli/log.py +39 -43
- pyinfra_cli/main.py +26 -495
- pyinfra_cli/prints.py +215 -156
- pyinfra_cli/util.py +172 -105
- pyinfra_cli/virtualenv.py +25 -20
- pyinfra/api/connectors/__init__.py +0 -21
- pyinfra/api/connectors/ansible.py +0 -99
- pyinfra/api/connectors/docker.py +0 -178
- pyinfra/api/connectors/local.py +0 -169
- pyinfra/api/connectors/ssh.py +0 -402
- pyinfra/api/connectors/sshuserclient/client.py +0 -105
- pyinfra/api/connectors/sshuserclient/config.py +0 -90
- pyinfra/api/connectors/util.py +0 -63
- pyinfra/api/connectors/vagrant.py +0 -155
- pyinfra/facts/init.py +0 -176
- pyinfra/facts/util/files.py +0 -102
- pyinfra/hook.py +0 -41
- pyinfra/modules/__init__.py +0 -11
- pyinfra/modules/apk.py +0 -64
- pyinfra/modules/apt.py +0 -272
- pyinfra/modules/brew.py +0 -122
- pyinfra/modules/files.py +0 -711
- pyinfra/modules/gem.py +0 -30
- pyinfra/modules/git.py +0 -115
- pyinfra/modules/init.py +0 -344
- pyinfra/modules/iptables.py +0 -271
- pyinfra/modules/lxd.py +0 -45
- pyinfra/modules/mysql.py +0 -347
- pyinfra/modules/npm.py +0 -47
- pyinfra/modules/pacman.py +0 -60
- pyinfra/modules/pip.py +0 -99
- pyinfra/modules/pkg.py +0 -43
- pyinfra/modules/postgresql.py +0 -245
- pyinfra/modules/puppet.py +0 -20
- pyinfra/modules/python.py +0 -37
- pyinfra/modules/server.py +0 -524
- pyinfra/modules/ssh.py +0 -150
- pyinfra/modules/util/files.py +0 -52
- pyinfra/modules/util/packaging.py +0 -118
- pyinfra/modules/vzctl.py +0 -133
- pyinfra/modules/yum.py +0 -171
- pyinfra/pseudo_modules.py +0 -64
- pyinfra-0.11.dev3.dist-info/.DS_Store +0 -0
- pyinfra-0.11.dev3.dist-info/METADATA +0 -135
- pyinfra-0.11.dev3.dist-info/RECORD +0 -95
- pyinfra-0.11.dev3.dist-info/entry_points.txt +0 -3
- pyinfra-0.11.dev3.dist-info/top_level.txt +0 -2
- pyinfra_cli/__main__.py +0 -40
- pyinfra_cli/config.py +0 -92
- /pyinfra/{modules/util → connectors}/__init__.py +0 -0
- /pyinfra/{api/connectors → connectors}/sshuserclient/__init__.py +0 -0
pyinfra/facts/openrc.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
7
|
+
from pyinfra.api import FactBase
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class OpenrcStatus(FactBase):
|
|
11
|
+
"""
|
|
12
|
+
Returns a dict of name -> status for OpenRC services for a given runlevel.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
default = dict
|
|
16
|
+
regex = (
|
|
17
|
+
r"\s+([a-zA-Z0-9\-_]+)"
|
|
18
|
+
r"\s+\[\s+"
|
|
19
|
+
r"([a-z]+)"
|
|
20
|
+
r"(?:\s(?:[0-9]+\sday\(s\)\s)?"
|
|
21
|
+
r"[0-9]+\:[0-9]+\:[0-9]+\s\([0-9]+\))?"
|
|
22
|
+
r"\s+\]"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
@override
|
|
26
|
+
def requires_command(self, runlevel="default") -> str:
|
|
27
|
+
return "rc-status"
|
|
28
|
+
|
|
29
|
+
@override
|
|
30
|
+
def command(self, runlevel="default"):
|
|
31
|
+
return "rc-status {0}".format(runlevel)
|
|
32
|
+
|
|
33
|
+
@override
|
|
34
|
+
def process(self, output):
|
|
35
|
+
services = {}
|
|
36
|
+
|
|
37
|
+
for line in output:
|
|
38
|
+
matches = re.match(self.regex, line)
|
|
39
|
+
if matches:
|
|
40
|
+
services[matches.group(1)] = matches.group(2) == "started"
|
|
41
|
+
|
|
42
|
+
return services
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class OpenrcEnabled(FactBase):
|
|
46
|
+
"""
|
|
47
|
+
Returns a dict of name -> whether enabled for OpenRC services for a given runlevel.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
default = dict
|
|
51
|
+
|
|
52
|
+
@override
|
|
53
|
+
def requires_command(self, runlevel="default") -> str:
|
|
54
|
+
return "rc-update"
|
|
55
|
+
|
|
56
|
+
@override
|
|
57
|
+
def command(self, runlevel="default"):
|
|
58
|
+
self.runlevel = runlevel
|
|
59
|
+
return "rc-update show -v"
|
|
60
|
+
|
|
61
|
+
@override
|
|
62
|
+
def process(self, output):
|
|
63
|
+
services = {}
|
|
64
|
+
|
|
65
|
+
for line in output:
|
|
66
|
+
name, levels = line.split("|", 1)
|
|
67
|
+
name = name.strip()
|
|
68
|
+
levels = levels.split()
|
|
69
|
+
services[name] = self.runlevel in levels
|
|
70
|
+
|
|
71
|
+
return services
|
pyinfra/facts/opkg.py
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
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
|
+
@override
|
|
85
|
+
@staticmethod
|
|
86
|
+
def default():
|
|
87
|
+
return OpkgConfInfo({}, "", {}, {})
|
|
88
|
+
|
|
89
|
+
@override
|
|
90
|
+
def command(self) -> str:
|
|
91
|
+
return "cat /etc/opkg.conf"
|
|
92
|
+
|
|
93
|
+
@override
|
|
94
|
+
def process(self, output):
|
|
95
|
+
dest, lists_dir, options, arch_cfg = {}, "", {}, {}
|
|
96
|
+
for line in output:
|
|
97
|
+
match = self.regex.match(line)
|
|
98
|
+
|
|
99
|
+
if match is None:
|
|
100
|
+
logger.warning(f"Opkg: could not parse opkg.conf line '{line}'")
|
|
101
|
+
elif match.group("arch") is not None:
|
|
102
|
+
arch_cfg[match.group("arch")] = int(match.group("priority"))
|
|
103
|
+
elif match.group("dest") is not None:
|
|
104
|
+
dest[match.group("dest")] = match.group("dest_path")
|
|
105
|
+
elif match.group("lists_dir") is not None:
|
|
106
|
+
lists_dir = match.group("list_path")
|
|
107
|
+
elif match.group("option") is not None:
|
|
108
|
+
options[match.group("option")] = match.group("value") or True
|
|
109
|
+
|
|
110
|
+
return OpkgConfInfo(dest, lists_dir, options, arch_cfg)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class OpkgFeeds(FactBase):
|
|
114
|
+
"""
|
|
115
|
+
Returns a dictionary containing the information for the distribution-provided and
|
|
116
|
+
custom opkg feeds:
|
|
117
|
+
|
|
118
|
+
.. code:: python
|
|
119
|
+
|
|
120
|
+
{
|
|
121
|
+
'openwrt_base': FeedInfo(url='http://downloads ... /i386_pentium/base', fmt='src/gz', kind='distribution'), # noqa: E501
|
|
122
|
+
'openwrt_core': FeedInfo(url='http://downloads ... /x86/geode/packages', fmt='src/gz', kind='distribution'), # noqa: E501
|
|
123
|
+
'openwrt_luci': FeedInfo(url='http://downloads ... /i386_pentium/luci', fmt='src/gz', kind='distribution'),# noqa: E501
|
|
124
|
+
'openwrt_packages': FeedInfo(url='http://downloads ... /i386_pentium/packages', fmt='src/gz', kind='distribution'),# noqa: E501
|
|
125
|
+
'openwrt_routing': FeedInfo(url='http://downloads ... /i386_pentium/routing', fmt='src/gz', kind='distribution'),# noqa: E501
|
|
126
|
+
'openwrt_telephony': FeedInfo(url='http://downloads ... /i386_pentium/telephony', fmt='src/gz', kind='distribution') # noqa: E501
|
|
127
|
+
}
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
regex = re.compile(
|
|
131
|
+
r"^(CUSTOM)|(?:\s*(?P<fmt>[\w/]+)\s+(?P<name>[\w]+)\s+(?P<url>[\w./:]+))?(?:\s*#.*)?$"
|
|
132
|
+
)
|
|
133
|
+
default = dict
|
|
134
|
+
|
|
135
|
+
@override
|
|
136
|
+
def command(self) -> str:
|
|
137
|
+
return "cat /etc/opkg/distfeeds.conf; echo CUSTOM; cat /etc/opkg/customfeeds.conf"
|
|
138
|
+
|
|
139
|
+
@override
|
|
140
|
+
def process(self, output):
|
|
141
|
+
feeds, kind = {}, "distribution"
|
|
142
|
+
for line in output:
|
|
143
|
+
match = self.regex.match(line)
|
|
144
|
+
|
|
145
|
+
if match is None:
|
|
146
|
+
logger.warning(f"Opkg: could not parse /etc/opkg/*feeds.conf line '{line}'")
|
|
147
|
+
elif match.group(0) == "CUSTOM":
|
|
148
|
+
kind = "custom"
|
|
149
|
+
elif match.group("name") is not None:
|
|
150
|
+
feeds[match.group("name")] = OpkgFeedInfo(
|
|
151
|
+
match.group("url"), match.group("fmt"), kind
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
return feeds
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class OpkgInstallableArchitectures(FactBase):
|
|
158
|
+
"""
|
|
159
|
+
Returns a dictionary containing the currently installable architectures for this system along
|
|
160
|
+
with their priority:
|
|
161
|
+
|
|
162
|
+
.. code:: python
|
|
163
|
+
|
|
164
|
+
{
|
|
165
|
+
'all': 1,
|
|
166
|
+
'i386_pentium': 10,
|
|
167
|
+
'noarch': 1
|
|
168
|
+
}
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
regex = re.compile(r"^(?:\s*arch\s+(?P<arch>[\w]+)\s+(?P<prio>\d+))?(\s*#.*)?$")
|
|
172
|
+
default = dict
|
|
173
|
+
|
|
174
|
+
@override
|
|
175
|
+
def command(self) -> str:
|
|
176
|
+
return "/bin/opkg print-architecture"
|
|
177
|
+
|
|
178
|
+
@override
|
|
179
|
+
def process(self, output):
|
|
180
|
+
arch_list = {}
|
|
181
|
+
for line in output:
|
|
182
|
+
match = self.regex.match(line)
|
|
183
|
+
|
|
184
|
+
if match is None:
|
|
185
|
+
logger.warning(f"could not parse arch line '{line}'")
|
|
186
|
+
elif match.group("arch") is not None:
|
|
187
|
+
arch_list[match.group("arch")] = int(match.group("prio"))
|
|
188
|
+
|
|
189
|
+
return arch_list
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class OpkgPackages(FactBase):
|
|
193
|
+
"""
|
|
194
|
+
Returns a dict of installed opkg packages:
|
|
195
|
+
|
|
196
|
+
.. code:: python
|
|
197
|
+
|
|
198
|
+
{
|
|
199
|
+
'package_name': ['version'],
|
|
200
|
+
...
|
|
201
|
+
}
|
|
202
|
+
"""
|
|
203
|
+
|
|
204
|
+
regex = r"^([a-zA-Z0-9][\w\-\.]*)\s-\s([\w\-\.]+)"
|
|
205
|
+
default = dict
|
|
206
|
+
|
|
207
|
+
@override
|
|
208
|
+
def command(self) -> str:
|
|
209
|
+
return "/bin/opkg list-installed"
|
|
210
|
+
|
|
211
|
+
@override
|
|
212
|
+
def process(self, output):
|
|
213
|
+
return parse_packages(self.regex, sorted(output))
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class OpkgUpgradeablePackages(FactBase):
|
|
217
|
+
"""
|
|
218
|
+
Returns a dict of installed and upgradable opkg packages:
|
|
219
|
+
|
|
220
|
+
.. code:: python
|
|
221
|
+
|
|
222
|
+
{
|
|
223
|
+
'package_name': (installed='1.2.3', available='1.2.8')
|
|
224
|
+
...
|
|
225
|
+
}
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
regex = re.compile(r"^([a-zA-Z0-9][\w\-.]*)\s-\s([\w\-.]+)\s-\s([\w\-.]+)")
|
|
229
|
+
default = dict
|
|
230
|
+
use_default_on_error = True
|
|
231
|
+
|
|
232
|
+
@override
|
|
233
|
+
def command(self) -> str:
|
|
234
|
+
return "/bin/opkg list-upgradable" # yes, really spelled that way
|
|
235
|
+
|
|
236
|
+
@override
|
|
237
|
+
def process(self, output):
|
|
238
|
+
result = {}
|
|
239
|
+
for line in output:
|
|
240
|
+
match = self.regex.match(line)
|
|
241
|
+
if match and len(match.groups()) == 3:
|
|
242
|
+
result[match.group(1)] = OpkgPkgUpgradeInfo(match.group(2), match.group(3))
|
|
243
|
+
else:
|
|
244
|
+
logger.warning(f"Opkg: could not list-upgradable line '{line}'")
|
|
245
|
+
|
|
246
|
+
return result
|
pyinfra/facts/pacman.py
CHANGED
|
@@ -1,22 +1,65 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shlex
|
|
4
|
+
|
|
5
|
+
from typing_extensions import override
|
|
6
|
+
|
|
1
7
|
from pyinfra.api import FactBase
|
|
2
8
|
|
|
3
9
|
from .util.packaging import parse_packages
|
|
4
10
|
|
|
5
|
-
PACMAN_REGEX = r
|
|
11
|
+
PACMAN_REGEX = r"^([0-9a-zA-Z\-_]+)\s([0-9\._+a-z\-:]+)"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PacmanUnpackGroup(FactBase):
|
|
15
|
+
"""
|
|
16
|
+
Returns a list of actual packages belonging to the provided package name,
|
|
17
|
+
expanding groups or virtual packages.
|
|
18
|
+
|
|
19
|
+
.. code:: python
|
|
20
|
+
|
|
21
|
+
[
|
|
22
|
+
"package_name",
|
|
23
|
+
]
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
@override
|
|
27
|
+
def requires_command(self, *args, **kwargs) -> str:
|
|
28
|
+
return "pacman"
|
|
29
|
+
|
|
30
|
+
default = list
|
|
31
|
+
|
|
32
|
+
@override
|
|
33
|
+
def command(self, package):
|
|
34
|
+
# Accept failure here (|| true) for invalid/unknown packages
|
|
35
|
+
return 'pacman -S --print-format "%n" {0} || true'.format(shlex.quote(package))
|
|
36
|
+
|
|
37
|
+
@override
|
|
38
|
+
def process(self, output):
|
|
39
|
+
return output
|
|
6
40
|
|
|
7
41
|
|
|
8
42
|
class PacmanPackages(FactBase):
|
|
9
|
-
|
|
43
|
+
"""
|
|
10
44
|
Returns a dict of installed pacman packages:
|
|
11
45
|
|
|
12
46
|
.. code:: python
|
|
13
47
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
48
|
+
{
|
|
49
|
+
"package_name": ["version"],
|
|
50
|
+
}
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
@override
|
|
54
|
+
def command(self) -> str:
|
|
55
|
+
return "pacman -Q"
|
|
56
|
+
|
|
57
|
+
@override
|
|
58
|
+
def requires_command(self, *args, **kwargs) -> str:
|
|
59
|
+
return "pacman"
|
|
17
60
|
|
|
18
|
-
|
|
19
|
-
deafult = dict
|
|
61
|
+
default = dict
|
|
20
62
|
|
|
63
|
+
@override
|
|
21
64
|
def process(self, output):
|
|
22
65
|
return parse_packages(PACMAN_REGEX, output)
|
pyinfra/facts/pip.py
CHANGED
|
@@ -1,24 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
1
5
|
from pyinfra.api import FactBase
|
|
2
6
|
|
|
3
7
|
from .util.packaging import parse_packages
|
|
4
8
|
|
|
5
|
-
PIP_REGEX = r
|
|
9
|
+
PIP_REGEX = r"^([a-zA-Z0-9_\-\+\.]+)==([0-9\.]+[a-z0-9\-]*)$"
|
|
6
10
|
|
|
7
11
|
|
|
8
12
|
class PipPackages(FactBase):
|
|
9
|
-
|
|
13
|
+
"""
|
|
10
14
|
Returns a dict of installed pip packages:
|
|
11
15
|
|
|
12
16
|
.. code:: python
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
{
|
|
19
|
+
"package_name": ["version"],
|
|
20
|
+
}
|
|
21
|
+
"""
|
|
17
22
|
|
|
18
23
|
default = dict
|
|
24
|
+
pip_command = "pip"
|
|
25
|
+
|
|
26
|
+
@override
|
|
27
|
+
def requires_command(self, pip=None):
|
|
28
|
+
return pip or self.pip_command
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
@override
|
|
31
|
+
def command(self, pip=None):
|
|
32
|
+
pip = pip or self.pip_command
|
|
33
|
+
return "{0} freeze --all".format(pip)
|
|
22
34
|
|
|
35
|
+
@override
|
|
23
36
|
def process(self, output):
|
|
24
37
|
return parse_packages(PIP_REGEX, output)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Pip3Packages(PipPackages):
|
|
41
|
+
pip_command = "pip3"
|
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,21 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
1
5
|
from pyinfra.api import FactBase
|
|
2
6
|
|
|
3
7
|
from .util.packaging import parse_packages
|
|
4
8
|
|
|
5
9
|
|
|
6
10
|
class PkgPackages(FactBase):
|
|
7
|
-
|
|
11
|
+
"""
|
|
8
12
|
Returns a dict of installed pkg packages:
|
|
9
13
|
|
|
10
14
|
.. code:: python
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
{
|
|
17
|
+
"package_name": ["version"],
|
|
18
|
+
}
|
|
19
|
+
"""
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
regex = r'^([a-zA-Z0-9_\-\+]+)\-([0-9a-z\.]+)'
|
|
21
|
+
regex = r"^([a-zA-Z0-9_\-\+]+)\-([0-9a-z\.]+)"
|
|
18
22
|
default = dict
|
|
19
23
|
|
|
24
|
+
@override
|
|
25
|
+
def command(self) -> str:
|
|
26
|
+
return "pkg info || pkg_info || true"
|
|
27
|
+
|
|
28
|
+
@override
|
|
20
29
|
def process(self, output):
|
|
21
30
|
return parse_packages(self.regex, output)
|
pyinfra/facts/pkgin.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing_extensions import override
|
|
4
|
+
|
|
5
|
+
from pyinfra.api import FactBase
|
|
6
|
+
|
|
7
|
+
from .util.packaging import parse_packages
|
|
8
|
+
|
|
9
|
+
PKGIN_REGEX = r"^([a-zA-Z\-0-9]+)-([0-9\.]+\-?[a-z0-9]*)\s"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class PkginPackages(FactBase):
|
|
13
|
+
"""
|
|
14
|
+
Returns a dict of installed pkgin packages:
|
|
15
|
+
|
|
16
|
+
.. code:: python
|
|
17
|
+
|
|
18
|
+
{
|
|
19
|
+
"package_name": ["version"],
|
|
20
|
+
}
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@override
|
|
24
|
+
def command(self) -> str:
|
|
25
|
+
return "pkgin list"
|
|
26
|
+
|
|
27
|
+
@override
|
|
28
|
+
def requires_command(self) -> str:
|
|
29
|
+
return "pkgin"
|
|
30
|
+
|
|
31
|
+
default = dict
|
|
32
|
+
|
|
33
|
+
@override
|
|
34
|
+
def process(self, output):
|
|
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
|