pyinfra 3.0.dev0__py2.py3-none-any.whl → 3.0.1__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/__init__.py +3 -0
- pyinfra/api/arguments.py +115 -97
- pyinfra/api/arguments_typed.py +80 -0
- pyinfra/api/command.py +5 -3
- pyinfra/api/config.py +139 -39
- pyinfra/api/connectors.py +5 -2
- pyinfra/api/deploy.py +19 -19
- pyinfra/api/exceptions.py +35 -4
- pyinfra/api/facts.py +62 -86
- pyinfra/api/host.py +102 -15
- pyinfra/api/inventory.py +4 -0
- pyinfra/api/operation.py +184 -118
- pyinfra/api/operations.py +66 -113
- pyinfra/api/state.py +53 -34
- pyinfra/api/util.py +64 -33
- pyinfra/connectors/base.py +65 -20
- pyinfra/connectors/chroot.py +15 -13
- pyinfra/connectors/docker.py +62 -72
- pyinfra/connectors/dockerssh.py +20 -19
- pyinfra/connectors/local.py +32 -22
- pyinfra/connectors/ssh.py +162 -86
- pyinfra/connectors/sshuserclient/client.py +1 -1
- pyinfra/connectors/terraform.py +57 -39
- pyinfra/connectors/util.py +26 -27
- pyinfra/connectors/vagrant.py +27 -26
- pyinfra/context.py +1 -0
- pyinfra/facts/apk.py +7 -2
- pyinfra/facts/apt.py +15 -7
- pyinfra/facts/brew.py +28 -13
- pyinfra/facts/bsdinit.py +9 -6
- pyinfra/facts/cargo.py +6 -3
- pyinfra/facts/choco.py +8 -4
- pyinfra/facts/deb.py +21 -9
- pyinfra/facts/dnf.py +11 -6
- pyinfra/facts/docker.py +30 -5
- pyinfra/facts/files.py +49 -33
- pyinfra/facts/gem.py +7 -2
- pyinfra/facts/git.py +14 -21
- pyinfra/facts/gpg.py +4 -1
- pyinfra/facts/hardware.py +186 -138
- pyinfra/facts/launchd.py +7 -2
- pyinfra/facts/lxd.py +8 -2
- pyinfra/facts/mysql.py +19 -12
- pyinfra/facts/npm.py +3 -1
- pyinfra/facts/openrc.py +8 -2
- pyinfra/facts/pacman.py +13 -5
- pyinfra/facts/pip.py +2 -0
- pyinfra/facts/pkg.py +5 -1
- pyinfra/facts/pkgin.py +7 -2
- pyinfra/facts/postgres.py +170 -0
- pyinfra/facts/postgresql.py +5 -162
- pyinfra/facts/rpm.py +21 -15
- pyinfra/facts/runit.py +70 -0
- pyinfra/facts/selinux.py +12 -4
- pyinfra/facts/server.py +240 -82
- pyinfra/facts/snap.py +8 -2
- pyinfra/facts/systemd.py +37 -13
- pyinfra/facts/sysvinit.py +7 -4
- pyinfra/facts/upstart.py +7 -2
- pyinfra/facts/util/packaging.py +3 -2
- pyinfra/facts/vzctl.py +8 -4
- pyinfra/facts/xbps.py +7 -2
- pyinfra/facts/yum.py +10 -5
- pyinfra/facts/zypper.py +9 -4
- pyinfra/operations/apk.py +5 -3
- pyinfra/operations/apt.py +28 -25
- pyinfra/operations/brew.py +60 -29
- pyinfra/operations/bsdinit.py +6 -4
- pyinfra/operations/cargo.py +3 -1
- pyinfra/operations/choco.py +3 -1
- pyinfra/operations/dnf.py +16 -20
- pyinfra/operations/docker.py +339 -0
- pyinfra/operations/files.py +187 -168
- pyinfra/operations/gem.py +3 -1
- pyinfra/operations/git.py +23 -25
- pyinfra/operations/iptables.py +33 -25
- pyinfra/operations/launchd.py +5 -6
- pyinfra/operations/lxd.py +7 -4
- pyinfra/operations/mysql.py +59 -55
- pyinfra/operations/npm.py +8 -1
- pyinfra/operations/openrc.py +5 -3
- pyinfra/operations/pacman.py +6 -7
- pyinfra/operations/pip.py +19 -12
- pyinfra/operations/pkg.py +3 -1
- pyinfra/operations/pkgin.py +5 -3
- pyinfra/operations/postgres.py +349 -0
- pyinfra/operations/postgresql.py +18 -335
- pyinfra/operations/puppet.py +3 -1
- pyinfra/operations/python.py +8 -19
- pyinfra/operations/runit.py +182 -0
- pyinfra/operations/selinux.py +47 -29
- pyinfra/operations/server.py +138 -67
- pyinfra/operations/snap.py +3 -1
- pyinfra/operations/ssh.py +18 -16
- pyinfra/operations/systemd.py +18 -12
- pyinfra/operations/sysvinit.py +7 -5
- pyinfra/operations/upstart.py +7 -5
- pyinfra/operations/util/__init__.py +12 -0
- pyinfra/operations/util/docker.py +177 -0
- pyinfra/operations/util/files.py +24 -16
- pyinfra/operations/util/packaging.py +54 -38
- pyinfra/operations/util/service.py +39 -47
- pyinfra/operations/vzctl.py +12 -10
- pyinfra/operations/xbps.py +5 -3
- pyinfra/operations/yum.py +15 -19
- pyinfra/operations/zypper.py +9 -10
- pyinfra/version.py +5 -2
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/METADATA +51 -58
- pyinfra-3.0.1.dist-info/RECORD +168 -0
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/WHEEL +1 -1
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/entry_points.txt +0 -3
- pyinfra_cli/__main__.py +4 -3
- pyinfra_cli/commands.py +3 -2
- pyinfra_cli/exceptions.py +75 -43
- pyinfra_cli/inventory.py +52 -31
- pyinfra_cli/log.py +10 -2
- pyinfra_cli/main.py +88 -65
- pyinfra_cli/prints.py +37 -109
- pyinfra_cli/util.py +15 -10
- tests/test_api/test_api.py +2 -0
- tests/test_api/test_api_arguments.py +9 -9
- tests/test_api/test_api_deploys.py +15 -19
- tests/test_api/test_api_facts.py +4 -5
- tests/test_api/test_api_operations.py +18 -20
- tests/test_api/test_api_util.py +41 -2
- tests/test_cli/test_cli.py +14 -50
- tests/test_cli/test_cli_deploy.py +10 -12
- tests/test_cli/test_cli_exceptions.py +50 -19
- tests/test_cli/test_cli_inventory.py +66 -0
- tests/test_cli/util.py +1 -1
- tests/test_connectors/test_dockerssh.py +11 -8
- tests/test_connectors/test_ssh.py +88 -23
- tests/test_connectors/test_sshuserclient.py +1 -1
- tests/test_connectors/test_terraform.py +11 -8
- tests/test_connectors/test_vagrant.py +6 -6
- pyinfra/connectors/ansible.py +0 -175
- pyinfra/connectors/mech.py +0 -189
- pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
- pyinfra/connectors/winrm.py +0 -312
- pyinfra/facts/windows.py +0 -366
- pyinfra/facts/windows_files.py +0 -90
- pyinfra/operations/windows.py +0 -59
- pyinfra/operations/windows_files.py +0 -538
- pyinfra-3.0.dev0.dist-info/RECORD +0 -170
- tests/test_connectors/test_ansible.py +0 -64
- tests/test_connectors/test_mech.py +0 -126
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/LICENSE.md +0 -0
- {pyinfra-3.0.dev0.dist-info → pyinfra-3.0.1.dist-info}/top_level.txt +0 -0
pyinfra/facts/server.py
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import os
|
|
2
4
|
import re
|
|
3
5
|
import shutil
|
|
4
6
|
from datetime import datetime
|
|
5
7
|
from tempfile import mkdtemp
|
|
8
|
+
from typing import Dict, List, Optional, Union
|
|
6
9
|
|
|
7
10
|
from dateutil.parser import parse as parse_date
|
|
8
11
|
from distro import distro
|
|
12
|
+
from typing_extensions import NotRequired, TypedDict
|
|
9
13
|
|
|
10
14
|
from pyinfra.api import FactBase, ShortFactBase
|
|
11
15
|
from pyinfra.api.util import try_int
|
|
@@ -18,15 +22,18 @@ class User(FactBase):
|
|
|
18
22
|
Returns the name of the current user.
|
|
19
23
|
"""
|
|
20
24
|
|
|
21
|
-
command
|
|
25
|
+
def command(self):
|
|
26
|
+
return "echo $USER"
|
|
22
27
|
|
|
23
28
|
|
|
24
|
-
class Home(FactBase):
|
|
29
|
+
class Home(FactBase[Optional[str]]):
|
|
25
30
|
"""
|
|
26
|
-
Returns the home directory of the current user.
|
|
31
|
+
Returns the home directory of the given user, or the current user if no user is given.
|
|
27
32
|
"""
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
@staticmethod
|
|
35
|
+
def command(user=""):
|
|
36
|
+
return f"echo ~{user}"
|
|
30
37
|
|
|
31
38
|
|
|
32
39
|
class Path(FactBase):
|
|
@@ -34,7 +41,17 @@ class Path(FactBase):
|
|
|
34
41
|
Returns the path environment variable of the current user.
|
|
35
42
|
"""
|
|
36
43
|
|
|
37
|
-
command
|
|
44
|
+
def command(self):
|
|
45
|
+
return "echo $PATH"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TmpDir(FactBase):
|
|
49
|
+
"""
|
|
50
|
+
Returns the temporary directory of the current server, if configured.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def command(self):
|
|
54
|
+
return "echo $TMPDIR"
|
|
38
55
|
|
|
39
56
|
|
|
40
57
|
class Hostname(FactBase):
|
|
@@ -42,7 +59,8 @@ class Hostname(FactBase):
|
|
|
42
59
|
Returns the current hostname of the server.
|
|
43
60
|
"""
|
|
44
61
|
|
|
45
|
-
command
|
|
62
|
+
def command(self):
|
|
63
|
+
return "uname -n"
|
|
46
64
|
|
|
47
65
|
|
|
48
66
|
class Kernel(FactBase):
|
|
@@ -50,7 +68,8 @@ class Kernel(FactBase):
|
|
|
50
68
|
Returns the kernel name according to ``uname``.
|
|
51
69
|
"""
|
|
52
70
|
|
|
53
|
-
command
|
|
71
|
+
def command(self):
|
|
72
|
+
return "uname -s"
|
|
54
73
|
|
|
55
74
|
|
|
56
75
|
class KernelVersion(FactBase):
|
|
@@ -58,11 +77,12 @@ class KernelVersion(FactBase):
|
|
|
58
77
|
Returns the kernel version according to ``uname``.
|
|
59
78
|
"""
|
|
60
79
|
|
|
61
|
-
command
|
|
80
|
+
def command(self):
|
|
81
|
+
return "uname -r"
|
|
62
82
|
|
|
63
83
|
|
|
64
84
|
# Deprecated/renamed -> Kernel
|
|
65
|
-
class Os(FactBase):
|
|
85
|
+
class Os(FactBase[str]):
|
|
66
86
|
"""
|
|
67
87
|
Returns the OS name according to ``uname``.
|
|
68
88
|
|
|
@@ -70,11 +90,12 @@ class Os(FactBase):
|
|
|
70
90
|
This fact is deprecated/renamed, please use the ``server.Kernel`` fact.
|
|
71
91
|
"""
|
|
72
92
|
|
|
73
|
-
command
|
|
93
|
+
def command(self):
|
|
94
|
+
return "uname -s"
|
|
74
95
|
|
|
75
96
|
|
|
76
97
|
# Deprecated/renamed -> KernelVersion
|
|
77
|
-
class OsVersion(FactBase):
|
|
98
|
+
class OsVersion(FactBase[str]):
|
|
78
99
|
"""
|
|
79
100
|
Returns the OS version according to ``uname``.
|
|
80
101
|
|
|
@@ -82,20 +103,22 @@ class OsVersion(FactBase):
|
|
|
82
103
|
This fact is deprecated/renamed, please use the ``server.KernelVersion`` fact.
|
|
83
104
|
"""
|
|
84
105
|
|
|
85
|
-
command
|
|
106
|
+
def command(self):
|
|
107
|
+
return "uname -r"
|
|
86
108
|
|
|
87
109
|
|
|
88
|
-
class Arch(FactBase):
|
|
110
|
+
class Arch(FactBase[str]):
|
|
89
111
|
"""
|
|
90
112
|
Returns the system architecture according to ``uname``.
|
|
91
113
|
"""
|
|
92
114
|
|
|
93
115
|
# ``uname -p`` is not portable and returns ``unknown`` on Debian.
|
|
94
116
|
# ``uname -m`` works on most Linux and BSD systems.
|
|
95
|
-
command
|
|
117
|
+
def command(self):
|
|
118
|
+
return "uname -m"
|
|
96
119
|
|
|
97
120
|
|
|
98
|
-
class Command(FactBase):
|
|
121
|
+
class Command(FactBase[str]):
|
|
99
122
|
"""
|
|
100
123
|
Returns the raw output lines of a given command.
|
|
101
124
|
"""
|
|
@@ -105,39 +128,49 @@ class Command(FactBase):
|
|
|
105
128
|
return command
|
|
106
129
|
|
|
107
130
|
|
|
108
|
-
class Which(FactBase):
|
|
131
|
+
class Which(FactBase[Optional[str]]):
|
|
109
132
|
"""
|
|
110
|
-
Returns the path of a given command
|
|
133
|
+
Returns the path of a given command according to `command -v`, if available.
|
|
111
134
|
"""
|
|
112
135
|
|
|
113
136
|
@staticmethod
|
|
114
137
|
def command(command):
|
|
115
|
-
return "
|
|
138
|
+
return "command -v {0} || true".format(command)
|
|
116
139
|
|
|
117
140
|
|
|
118
|
-
class Date(FactBase):
|
|
141
|
+
class Date(FactBase[datetime]):
|
|
119
142
|
"""
|
|
120
143
|
Returns the current datetime on the server.
|
|
121
144
|
"""
|
|
122
145
|
|
|
123
|
-
command = f"date +'{ISO_DATE_FORMAT}'"
|
|
124
146
|
default = datetime.now
|
|
125
147
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return datetime.strptime(output[0], ISO_DATE_FORMAT)
|
|
148
|
+
def command(self):
|
|
149
|
+
return f"date +'{ISO_DATE_FORMAT}'"
|
|
129
150
|
|
|
151
|
+
def process(self, output) -> datetime:
|
|
152
|
+
return datetime.strptime(list(output)[0], ISO_DATE_FORMAT)
|
|
130
153
|
|
|
131
|
-
|
|
154
|
+
|
|
155
|
+
class MacosVersion(FactBase[str]):
|
|
132
156
|
"""
|
|
133
157
|
Returns the installed MacOS version.
|
|
134
158
|
"""
|
|
135
159
|
|
|
136
|
-
|
|
137
|
-
|
|
160
|
+
def requires_command(self) -> str:
|
|
161
|
+
return "sw_vers"
|
|
162
|
+
|
|
163
|
+
def command(self):
|
|
164
|
+
return "sw_vers -productVersion"
|
|
138
165
|
|
|
139
166
|
|
|
140
|
-
class
|
|
167
|
+
class MountsDict(TypedDict):
|
|
168
|
+
device: str
|
|
169
|
+
type: str
|
|
170
|
+
options: list[str]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class Mounts(FactBase[Dict[str, MountsDict]]):
|
|
141
174
|
"""
|
|
142
175
|
Returns a dictionary of mounted filesystems and information.
|
|
143
176
|
|
|
@@ -155,12 +188,13 @@ class Mounts(FactBase):
|
|
|
155
188
|
}
|
|
156
189
|
"""
|
|
157
190
|
|
|
158
|
-
command = "mount"
|
|
159
191
|
default = dict
|
|
160
192
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
193
|
+
def command(self):
|
|
194
|
+
return "mount"
|
|
195
|
+
|
|
196
|
+
def process(self, output) -> dict[str, MountsDict]:
|
|
197
|
+
devices: dict[str, MountsDict] = {}
|
|
164
198
|
|
|
165
199
|
for line in output:
|
|
166
200
|
is_map = False
|
|
@@ -204,11 +238,12 @@ class KernelModules(FactBase):
|
|
|
204
238
|
}
|
|
205
239
|
"""
|
|
206
240
|
|
|
207
|
-
command
|
|
241
|
+
def command(self):
|
|
242
|
+
return "! test -f /proc/modules || cat /proc/modules"
|
|
243
|
+
|
|
208
244
|
default = dict
|
|
209
245
|
|
|
210
|
-
|
|
211
|
-
def process(output):
|
|
246
|
+
def process(self, output):
|
|
212
247
|
modules = {}
|
|
213
248
|
|
|
214
249
|
for line in output:
|
|
@@ -244,11 +279,13 @@ class LsbRelease(FactBase):
|
|
|
244
279
|
}
|
|
245
280
|
"""
|
|
246
281
|
|
|
247
|
-
command
|
|
248
|
-
|
|
282
|
+
def command(self):
|
|
283
|
+
return "lsb_release -ca"
|
|
249
284
|
|
|
250
|
-
|
|
251
|
-
|
|
285
|
+
def requires_command(self):
|
|
286
|
+
return "lsb_release"
|
|
287
|
+
|
|
288
|
+
def process(self, output):
|
|
252
289
|
items = {}
|
|
253
290
|
|
|
254
291
|
for line in output:
|
|
@@ -285,11 +322,14 @@ class Sysctl(FactBase):
|
|
|
285
322
|
}
|
|
286
323
|
"""
|
|
287
324
|
|
|
288
|
-
command = "sysctl -a"
|
|
289
325
|
default = dict
|
|
290
326
|
|
|
291
|
-
|
|
292
|
-
|
|
327
|
+
def command(self, keys=None):
|
|
328
|
+
if keys is None:
|
|
329
|
+
return "sysctl -a"
|
|
330
|
+
return f"sysctl {' '.join(keys)}"
|
|
331
|
+
|
|
332
|
+
def process(self, output):
|
|
293
333
|
sysctls = {}
|
|
294
334
|
|
|
295
335
|
for line in output:
|
|
@@ -317,17 +357,18 @@ class Sysctl(FactBase):
|
|
|
317
357
|
return sysctls
|
|
318
358
|
|
|
319
359
|
|
|
320
|
-
class Groups(FactBase):
|
|
360
|
+
class Groups(FactBase[List[str]]):
|
|
321
361
|
"""
|
|
322
362
|
Returns a list of groups on the system.
|
|
323
363
|
"""
|
|
324
364
|
|
|
325
|
-
command
|
|
365
|
+
def command(self):
|
|
366
|
+
return "cat /etc/group"
|
|
367
|
+
|
|
326
368
|
default = list
|
|
327
369
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
groups = []
|
|
370
|
+
def process(self, output) -> list[str]:
|
|
371
|
+
groups: list[str] = []
|
|
331
372
|
|
|
332
373
|
for line in output:
|
|
333
374
|
if ":" in line:
|
|
@@ -336,7 +377,17 @@ class Groups(FactBase):
|
|
|
336
377
|
return groups
|
|
337
378
|
|
|
338
379
|
|
|
339
|
-
class
|
|
380
|
+
class CrontabDict(TypedDict):
|
|
381
|
+
minute: NotRequired[Union[int, str]]
|
|
382
|
+
hour: NotRequired[Union[int, str]]
|
|
383
|
+
month: NotRequired[Union[int, str]]
|
|
384
|
+
day_of_month: NotRequired[Union[int, str]]
|
|
385
|
+
day_of_week: NotRequired[Union[int, str]]
|
|
386
|
+
comments: Optional[list[str]]
|
|
387
|
+
special_time: NotRequired[str]
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
class Crontab(FactBase[Dict[str, CrontabDict]]):
|
|
340
391
|
"""
|
|
341
392
|
Returns a dictionary of cron command -> execution time.
|
|
342
393
|
|
|
@@ -358,17 +409,16 @@ class Crontab(FactBase):
|
|
|
358
409
|
|
|
359
410
|
default = dict
|
|
360
411
|
|
|
361
|
-
requires_command =
|
|
412
|
+
def requires_command(self, user=None) -> str:
|
|
413
|
+
return "crontab"
|
|
362
414
|
|
|
363
|
-
|
|
364
|
-
def command(user=None):
|
|
415
|
+
def command(self, user=None):
|
|
365
416
|
if user:
|
|
366
417
|
return "crontab -l -u {0} || true".format(user)
|
|
367
418
|
return "crontab -l || true"
|
|
368
419
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
crons = {}
|
|
420
|
+
def process(self, output):
|
|
421
|
+
crons: dict[str, CrontabDict] = {}
|
|
372
422
|
current_comments = []
|
|
373
423
|
|
|
374
424
|
for line in output:
|
|
@@ -417,16 +467,20 @@ class Users(FactBase):
|
|
|
417
467
|
"uid": user_id,
|
|
418
468
|
"gid": main_user_group_id,
|
|
419
469
|
"lastlog": last_login_time,
|
|
470
|
+
"password": encrypted_password,
|
|
420
471
|
},
|
|
421
472
|
}
|
|
422
473
|
"""
|
|
423
474
|
|
|
424
|
-
command
|
|
475
|
+
def command(self):
|
|
476
|
+
return """
|
|
477
|
+
|
|
425
478
|
for i in `cat /etc/passwd | cut -d: -f1`; do
|
|
426
479
|
ENTRY=`grep ^$i: /etc/passwd`;
|
|
427
480
|
LASTLOG_RAW=`(lastlog -u $i 2> /dev/null || lastlogin $i 2> /dev/null)`;
|
|
428
481
|
LASTLOG=`echo $LASTLOG_RAW | grep ^$i | tr -s ' '`;
|
|
429
|
-
|
|
482
|
+
PASSWORD=`grep ^$i: /etc/shadow | cut -d: -f2`;
|
|
483
|
+
echo "$ENTRY|`id -gn $i`|`id -Gn $i`|$LASTLOG|$PASSWORD";
|
|
430
484
|
done
|
|
431
485
|
""".strip()
|
|
432
486
|
|
|
@@ -437,7 +491,7 @@ class Users(FactBase):
|
|
|
437
491
|
rex = r"[A-Z][a-z]{2} [A-Z][a-z]{2} {1,2}\d+ .+$"
|
|
438
492
|
|
|
439
493
|
for line in output:
|
|
440
|
-
entry, group, user_groups, lastlog = line.
|
|
494
|
+
entry, group, user_groups, lastlog, password = line.rsplit("|", 4)
|
|
441
495
|
|
|
442
496
|
if entry:
|
|
443
497
|
# Parse out the comment/home/shell
|
|
@@ -470,12 +524,20 @@ class Users(FactBase):
|
|
|
470
524
|
"gid": int(entries[3]),
|
|
471
525
|
"lastlog": raw_login_time,
|
|
472
526
|
"login_time": login_time,
|
|
527
|
+
"password": password,
|
|
473
528
|
}
|
|
474
529
|
|
|
475
530
|
return users
|
|
476
531
|
|
|
477
532
|
|
|
478
|
-
class
|
|
533
|
+
class LinuxDistributionDict(TypedDict):
|
|
534
|
+
name: Optional[str]
|
|
535
|
+
major: Optional[int]
|
|
536
|
+
minor: Optional[int]
|
|
537
|
+
release_meta: Dict
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
class LinuxDistribution(FactBase[LinuxDistributionDict]):
|
|
479
541
|
"""
|
|
480
542
|
Returns a dict of the Linux distribution version. Ubuntu, Debian, CentOS,
|
|
481
543
|
Fedora & Gentoo currently. Also contains any key/value items located in
|
|
@@ -495,11 +557,12 @@ class LinuxDistribution(FactBase):
|
|
|
495
557
|
}
|
|
496
558
|
"""
|
|
497
559
|
|
|
498
|
-
command
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
560
|
+
def command(self) -> str:
|
|
561
|
+
return (
|
|
562
|
+
"cd /etc/ && for file in $(ls -pdL *-release | grep -v /); "
|
|
563
|
+
'do echo "/etc/${file}"; cat "/etc/${file}"; echo ---; '
|
|
564
|
+
"done"
|
|
565
|
+
)
|
|
503
566
|
|
|
504
567
|
name_to_pretty_name = {
|
|
505
568
|
"alpine": "Alpine",
|
|
@@ -513,7 +576,7 @@ class LinuxDistribution(FactBase):
|
|
|
513
576
|
}
|
|
514
577
|
|
|
515
578
|
@staticmethod
|
|
516
|
-
def default():
|
|
579
|
+
def default() -> LinuxDistributionDict:
|
|
517
580
|
return {
|
|
518
581
|
"name": None,
|
|
519
582
|
"major": None,
|
|
@@ -521,7 +584,7 @@ class LinuxDistribution(FactBase):
|
|
|
521
584
|
"release_meta": {},
|
|
522
585
|
}
|
|
523
586
|
|
|
524
|
-
def process(self, output):
|
|
587
|
+
def process(self, output) -> LinuxDistributionDict:
|
|
525
588
|
parts = {}
|
|
526
589
|
for part in "\n".join(output).strip().split("---"):
|
|
527
590
|
if not part.strip():
|
|
@@ -575,7 +638,7 @@ class LinuxDistribution(FactBase):
|
|
|
575
638
|
return release_info
|
|
576
639
|
|
|
577
640
|
|
|
578
|
-
class LinuxName(ShortFactBase):
|
|
641
|
+
class LinuxName(ShortFactBase[str]):
|
|
579
642
|
"""
|
|
580
643
|
Returns the name of the Linux distribution. Shortcut for
|
|
581
644
|
``host.get_fact(LinuxDistribution)['name']``.
|
|
@@ -583,12 +646,15 @@ class LinuxName(ShortFactBase):
|
|
|
583
646
|
|
|
584
647
|
fact = LinuxDistribution
|
|
585
648
|
|
|
586
|
-
|
|
587
|
-
def process_data(data):
|
|
649
|
+
def process_data(self, data) -> str:
|
|
588
650
|
return data["name"]
|
|
589
651
|
|
|
590
652
|
|
|
591
|
-
class
|
|
653
|
+
class SelinuxDict(TypedDict):
|
|
654
|
+
mode: Optional[str]
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
class Selinux(FactBase[SelinuxDict]):
|
|
592
658
|
"""
|
|
593
659
|
Discovers the SELinux related facts on the target host.
|
|
594
660
|
|
|
@@ -599,16 +665,19 @@ class Selinux(FactBase):
|
|
|
599
665
|
}
|
|
600
666
|
"""
|
|
601
667
|
|
|
602
|
-
command
|
|
603
|
-
|
|
668
|
+
def command(self):
|
|
669
|
+
return "sestatus"
|
|
670
|
+
|
|
671
|
+
def requires_command(self) -> str:
|
|
672
|
+
return "sestatus"
|
|
604
673
|
|
|
605
674
|
@staticmethod
|
|
606
|
-
def default():
|
|
675
|
+
def default() -> SelinuxDict:
|
|
607
676
|
return {
|
|
608
677
|
"mode": None,
|
|
609
678
|
}
|
|
610
679
|
|
|
611
|
-
def process(self, output):
|
|
680
|
+
def process(self, output) -> SelinuxDict:
|
|
612
681
|
selinux_info = self.default()
|
|
613
682
|
|
|
614
683
|
match = re.match(r"^SELinux status:\s+(\S+)", "\n".join(output))
|
|
@@ -621,12 +690,14 @@ class Selinux(FactBase):
|
|
|
621
690
|
return selinux_info
|
|
622
691
|
|
|
623
692
|
|
|
624
|
-
class LinuxGui(FactBase):
|
|
693
|
+
class LinuxGui(FactBase[List[str]]):
|
|
625
694
|
"""
|
|
626
695
|
Returns a list of available Linux GUIs.
|
|
627
696
|
"""
|
|
628
697
|
|
|
629
|
-
command
|
|
698
|
+
def command(self):
|
|
699
|
+
return "ls /usr/bin/*session || true"
|
|
700
|
+
|
|
630
701
|
default = list
|
|
631
702
|
|
|
632
703
|
known_gui_binaries = {
|
|
@@ -637,7 +708,7 @@ class LinuxGui(FactBase):
|
|
|
637
708
|
"/usr/bin/xfce4-session": "XFCE 4",
|
|
638
709
|
}
|
|
639
710
|
|
|
640
|
-
def process(self, output):
|
|
711
|
+
def process(self, output) -> list[str]:
|
|
641
712
|
gui_names = []
|
|
642
713
|
|
|
643
714
|
for line in output:
|
|
@@ -648,19 +719,18 @@ class LinuxGui(FactBase):
|
|
|
648
719
|
return gui_names
|
|
649
720
|
|
|
650
721
|
|
|
651
|
-
class HasGui(ShortFactBase):
|
|
722
|
+
class HasGui(ShortFactBase[bool]):
|
|
652
723
|
"""
|
|
653
724
|
Returns a boolean indicating the remote side has GUI capabilities. Linux only.
|
|
654
725
|
"""
|
|
655
726
|
|
|
656
727
|
fact = LinuxGui
|
|
657
728
|
|
|
658
|
-
|
|
659
|
-
def process_data(data):
|
|
729
|
+
def process_data(self, data) -> bool:
|
|
660
730
|
return len(data) > 0
|
|
661
731
|
|
|
662
732
|
|
|
663
|
-
class Locales(FactBase):
|
|
733
|
+
class Locales(FactBase[List[str]]):
|
|
664
734
|
"""
|
|
665
735
|
Returns installed locales on the target host.
|
|
666
736
|
|
|
@@ -669,11 +739,99 @@ class Locales(FactBase):
|
|
|
669
739
|
["C.UTF-8", "en_US.UTF-8"]
|
|
670
740
|
"""
|
|
671
741
|
|
|
672
|
-
command
|
|
673
|
-
|
|
742
|
+
def command(self) -> str:
|
|
743
|
+
return "locale -a"
|
|
744
|
+
|
|
745
|
+
def requires_command(self) -> str:
|
|
746
|
+
return "locale"
|
|
747
|
+
|
|
674
748
|
default = list
|
|
675
749
|
|
|
676
|
-
def process(self, output):
|
|
750
|
+
def process(self, output) -> list[str]:
|
|
677
751
|
# replace utf8 with UTF-8 to match names in /etc/locale.gen
|
|
678
752
|
# return a list of enabled locales
|
|
679
753
|
return [line.replace("utf8", "UTF-8") for line in output]
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
class SecurityLimits(FactBase):
|
|
757
|
+
"""
|
|
758
|
+
Returns a list of security limits on the target host.
|
|
759
|
+
|
|
760
|
+
.. code:: python
|
|
761
|
+
|
|
762
|
+
[
|
|
763
|
+
{
|
|
764
|
+
"domain": "*",
|
|
765
|
+
"limit_type": "soft",
|
|
766
|
+
"item": "nofile",
|
|
767
|
+
"value": "1048576"
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
"domain": "*",
|
|
771
|
+
"limit_type": "hard",
|
|
772
|
+
"item": "nofile",
|
|
773
|
+
"value": "1048576"
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
"domain": "root",
|
|
777
|
+
"limit_type": "soft",
|
|
778
|
+
"item": "nofile",
|
|
779
|
+
"value": "1048576"
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
"domain": "root",
|
|
783
|
+
"limit_type": "hard",
|
|
784
|
+
"item": "nofile",
|
|
785
|
+
"value": "1048576"
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
"domain": "*",
|
|
789
|
+
"limit_type": "soft",
|
|
790
|
+
"item": "memlock",
|
|
791
|
+
"value": "unlimited"
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
"domain": "*",
|
|
795
|
+
"limit_type": "hard",
|
|
796
|
+
"item": "memlock",
|
|
797
|
+
"value": "unlimited"
|
|
798
|
+
},
|
|
799
|
+
{
|
|
800
|
+
"domain": "root",
|
|
801
|
+
"limit_type": "soft",
|
|
802
|
+
"item": "memlock",
|
|
803
|
+
"value": "unlimited"
|
|
804
|
+
},
|
|
805
|
+
{
|
|
806
|
+
"domain": "root",
|
|
807
|
+
"limit_type": "hard",
|
|
808
|
+
"item": "memlock",
|
|
809
|
+
"value": "unlimited"
|
|
810
|
+
}
|
|
811
|
+
]
|
|
812
|
+
"""
|
|
813
|
+
|
|
814
|
+
def command(self):
|
|
815
|
+
return "cat /etc/security/limits.conf"
|
|
816
|
+
|
|
817
|
+
default = list
|
|
818
|
+
|
|
819
|
+
def process(self, output):
|
|
820
|
+
limits = []
|
|
821
|
+
|
|
822
|
+
for line in output:
|
|
823
|
+
if line.startswith("#") or not len(line.strip()):
|
|
824
|
+
continue
|
|
825
|
+
|
|
826
|
+
domain, limit_type, item, value = line.split()
|
|
827
|
+
|
|
828
|
+
limits.append(
|
|
829
|
+
{
|
|
830
|
+
"domain": domain,
|
|
831
|
+
"limit_type": limit_type,
|
|
832
|
+
"item": item,
|
|
833
|
+
"value": value,
|
|
834
|
+
},
|
|
835
|
+
)
|
|
836
|
+
|
|
837
|
+
return limits
|
pyinfra/facts/snap.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import re
|
|
2
4
|
|
|
3
5
|
from pyinfra.api import FactBase
|
|
@@ -5,7 +7,9 @@ from pyinfra.api import FactBase
|
|
|
5
7
|
|
|
6
8
|
class SnapBaseFact(FactBase):
|
|
7
9
|
abstract = True
|
|
8
|
-
|
|
10
|
+
|
|
11
|
+
def requires_command(self, *args, **kwargs) -> str:
|
|
12
|
+
return "snap"
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
class SnapPackage(SnapBaseFact):
|
|
@@ -62,7 +66,9 @@ class SnapPackages(SnapBaseFact):
|
|
|
62
66
|
"""
|
|
63
67
|
|
|
64
68
|
default = list
|
|
65
|
-
|
|
69
|
+
|
|
70
|
+
def command(self) -> str:
|
|
71
|
+
return "snap list"
|
|
66
72
|
|
|
67
73
|
def process(self, output):
|
|
68
74
|
# Discard header output line from snap list command
|