pyinfra 3.1.1__py2.py3-none-any.whl → 3.2__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/deploy.py +4 -2
- pyinfra/api/host.py +5 -3
- pyinfra/connectors/docker.py +17 -6
- pyinfra/connectors/sshuserclient/client.py +26 -14
- pyinfra/facts/apk.py +3 -1
- pyinfra/facts/apt.py +60 -0
- pyinfra/facts/crontab.py +190 -0
- pyinfra/facts/docker.py +6 -0
- pyinfra/facts/efibootmgr.py +108 -0
- pyinfra/facts/files.py +93 -6
- pyinfra/facts/git.py +3 -2
- pyinfra/facts/mysql.py +1 -2
- pyinfra/facts/opkg.py +233 -0
- pyinfra/facts/pipx.py +74 -0
- pyinfra/facts/podman.py +47 -0
- pyinfra/facts/postgres.py +2 -0
- pyinfra/facts/server.py +39 -77
- pyinfra/facts/util/units.py +30 -0
- pyinfra/facts/zfs.py +22 -19
- pyinfra/local.py +3 -2
- pyinfra/operations/apt.py +27 -20
- pyinfra/operations/crontab.py +189 -0
- pyinfra/operations/docker.py +13 -12
- pyinfra/operations/files.py +18 -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 +15 -11
- pyinfra/operations/runit.py +2 -0
- pyinfra/operations/server.py +3 -177
- pyinfra/operations/zfs.py +3 -3
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/METADATA +11 -12
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/RECORD +45 -36
- pyinfra_cli/inventory.py +26 -9
- pyinfra_cli/prints.py +18 -3
- pyinfra_cli/util.py +3 -0
- tests/test_cli/test_cli_deploy.py +15 -13
- tests/test_cli/test_cli_inventory.py +53 -0
- tests/test_connectors/test_sshuserclient.py +68 -1
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/LICENSE.md +0 -0
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/WHEEL +0 -0
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/entry_points.txt +0 -0
- {pyinfra-3.1.1.dist-info → pyinfra-3.2.dist-info}/top_level.txt +0 -0
pyinfra/operations/pip.py
CHANGED
|
@@ -174,12 +174,13 @@ def packages(
|
|
|
174
174
|
install_command_args.append(extra_install_args)
|
|
175
175
|
install_command = " ".join(install_command_args)
|
|
176
176
|
|
|
177
|
+
upgrade_command = "{0} --upgrade".format(install_command)
|
|
177
178
|
uninstall_command = " ".join([pip, "uninstall", "--yes"])
|
|
178
179
|
|
|
179
180
|
# (un)Install requirements
|
|
180
181
|
if requirements is not None:
|
|
181
182
|
if present:
|
|
182
|
-
yield "{0} -r {1}".format(install_command, requirements)
|
|
183
|
+
yield "{0} -r {1}".format(upgrade_command if latest else install_command, requirements)
|
|
183
184
|
else:
|
|
184
185
|
yield "{0} -r {1}".format(uninstall_command, requirements)
|
|
185
186
|
|
|
@@ -199,7 +200,7 @@ def packages(
|
|
|
199
200
|
present,
|
|
200
201
|
install_command=install_command,
|
|
201
202
|
uninstall_command=uninstall_command,
|
|
202
|
-
upgrade_command=
|
|
203
|
+
upgrade_command=upgrade_command,
|
|
203
204
|
version_join="==",
|
|
204
205
|
latest=latest,
|
|
205
206
|
)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage pipx (python) applications.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pyinfra import host
|
|
6
|
+
from pyinfra.api import operation
|
|
7
|
+
from pyinfra.facts.pipx import PipxEnvironment, PipxPackages
|
|
8
|
+
from pyinfra.facts.server import Path
|
|
9
|
+
|
|
10
|
+
from .util.packaging import ensure_packages
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@operation()
|
|
14
|
+
def packages(
|
|
15
|
+
packages=None,
|
|
16
|
+
present=True,
|
|
17
|
+
latest=False,
|
|
18
|
+
extra_args=None,
|
|
19
|
+
):
|
|
20
|
+
"""
|
|
21
|
+
Install/remove/update pipx packages.
|
|
22
|
+
|
|
23
|
+
+ packages: list of packages to ensure
|
|
24
|
+
+ present: whether the packages should be installed
|
|
25
|
+
+ latest: whether to upgrade packages without a specified version
|
|
26
|
+
+ extra_args: additional arguments to the pipx command
|
|
27
|
+
|
|
28
|
+
Versions:
|
|
29
|
+
Package versions can be pinned like pip: ``<pkg>==<version>``.
|
|
30
|
+
|
|
31
|
+
**Example:**
|
|
32
|
+
|
|
33
|
+
.. code:: python
|
|
34
|
+
|
|
35
|
+
pipx.packages(
|
|
36
|
+
name="Install ",
|
|
37
|
+
packages=["pyinfra"],
|
|
38
|
+
)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
prep_install_command = ["pipx", "install"]
|
|
42
|
+
|
|
43
|
+
if extra_args:
|
|
44
|
+
prep_install_command.append(extra_args)
|
|
45
|
+
install_command = " ".join(prep_install_command)
|
|
46
|
+
|
|
47
|
+
uninstall_command = "pipx uninstall"
|
|
48
|
+
upgrade_command = "pipx upgrade"
|
|
49
|
+
|
|
50
|
+
current_packages = host.get_fact(PipxPackages)
|
|
51
|
+
|
|
52
|
+
# pipx support only one package name at a time
|
|
53
|
+
for package in packages:
|
|
54
|
+
yield from ensure_packages(
|
|
55
|
+
host,
|
|
56
|
+
[package],
|
|
57
|
+
current_packages,
|
|
58
|
+
present,
|
|
59
|
+
install_command=install_command,
|
|
60
|
+
uninstall_command=uninstall_command,
|
|
61
|
+
upgrade_command=upgrade_command,
|
|
62
|
+
version_join="==",
|
|
63
|
+
latest=latest,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@operation()
|
|
68
|
+
def upgrade_all():
|
|
69
|
+
"""
|
|
70
|
+
Upgrade all pipx packages.
|
|
71
|
+
"""
|
|
72
|
+
yield "pipx upgrade-all"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@operation()
|
|
76
|
+
def ensure_path():
|
|
77
|
+
"""
|
|
78
|
+
Ensure pipx bin dir is in the PATH.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
# Fetch the current user's PATH
|
|
82
|
+
path = host.get_fact(Path)
|
|
83
|
+
# Fetch the pipx environment variables
|
|
84
|
+
pipx_env = host.get_fact(PipxEnvironment)
|
|
85
|
+
|
|
86
|
+
# If the pipx bin dir is already in the user's PATH, we're done
|
|
87
|
+
if "PIPX_BIN_DIR" in pipx_env and pipx_env["PIPX_BIN_DIR"] in path.split(":"):
|
|
88
|
+
host.noop("pipx bin dir is already in the PATH")
|
|
89
|
+
else:
|
|
90
|
+
yield "pipx ensurepath"
|
pyinfra/operations/postgres.py
CHANGED
|
@@ -8,6 +8,7 @@ All operations in this module take four optional arguments:
|
|
|
8
8
|
+ ``psql_password``: the password for the connecting user
|
|
9
9
|
+ ``psql_host``: the hostname of the server to connect to
|
|
10
10
|
+ ``psql_port``: the port of the server to connect to
|
|
11
|
+
+ ``psql_database``: the database on the server to connect to
|
|
11
12
|
|
|
12
13
|
See example/postgresql.py for detailed example
|
|
13
14
|
|
|
@@ -28,28 +29,27 @@ from pyinfra.facts.postgres import (
|
|
|
28
29
|
@operation(is_idempotent=False)
|
|
29
30
|
def sql(
|
|
30
31
|
sql: str,
|
|
31
|
-
database: str | None = None,
|
|
32
32
|
# Details for speaking to PostgreSQL via `psql` CLI
|
|
33
33
|
psql_user: str | None = None,
|
|
34
34
|
psql_password: str | None = None,
|
|
35
35
|
psql_host: str | None = None,
|
|
36
36
|
psql_port: int | None = None,
|
|
37
|
+
psql_database: str | None = None,
|
|
37
38
|
):
|
|
38
39
|
"""
|
|
39
40
|
Execute arbitrary SQL against PostgreSQL.
|
|
40
41
|
|
|
41
42
|
+ sql: SQL command(s) to execute
|
|
42
|
-
+ database: optional database to execute against
|
|
43
43
|
+ psql_*: global module arguments, see above
|
|
44
44
|
"""
|
|
45
45
|
|
|
46
46
|
yield make_execute_psql_command(
|
|
47
47
|
sql,
|
|
48
|
-
database=database,
|
|
49
48
|
user=psql_user,
|
|
50
49
|
password=psql_password,
|
|
51
50
|
host=psql_host,
|
|
52
51
|
port=psql_port,
|
|
52
|
+
database=psql_database,
|
|
53
53
|
)
|
|
54
54
|
|
|
55
55
|
|
|
@@ -70,6 +70,7 @@ def role(
|
|
|
70
70
|
psql_password: str | None = None,
|
|
71
71
|
psql_host: str | None = None,
|
|
72
72
|
psql_port: int | None = None,
|
|
73
|
+
psql_database: str | None = None,
|
|
73
74
|
):
|
|
74
75
|
"""
|
|
75
76
|
Add/remove PostgreSQL roles.
|
|
@@ -112,6 +113,7 @@ def role(
|
|
|
112
113
|
psql_password=psql_password,
|
|
113
114
|
psql_host=psql_host,
|
|
114
115
|
psql_port=psql_port,
|
|
116
|
+
psql_database=psql_database,
|
|
115
117
|
)
|
|
116
118
|
|
|
117
119
|
is_present = role in roles
|
|
@@ -125,6 +127,7 @@ def role(
|
|
|
125
127
|
password=psql_password,
|
|
126
128
|
host=psql_host,
|
|
127
129
|
port=psql_port,
|
|
130
|
+
database=psql_database,
|
|
128
131
|
)
|
|
129
132
|
else:
|
|
130
133
|
host.noop("postgresql role {0} does not exist".format(role))
|
|
@@ -157,6 +160,7 @@ def role(
|
|
|
157
160
|
password=psql_password,
|
|
158
161
|
host=psql_host,
|
|
159
162
|
port=psql_port,
|
|
163
|
+
database=psql_database,
|
|
160
164
|
)
|
|
161
165
|
else:
|
|
162
166
|
host.noop("postgresql role {0} exists".format(role))
|
|
@@ -178,6 +182,7 @@ def database(
|
|
|
178
182
|
psql_password: str | None = None,
|
|
179
183
|
psql_host: str | None = None,
|
|
180
184
|
psql_port: int | None = None,
|
|
185
|
+
psql_database: str | None = None,
|
|
181
186
|
):
|
|
182
187
|
"""
|
|
183
188
|
Add/remove PostgreSQL databases.
|
|
@@ -218,6 +223,7 @@ def database(
|
|
|
218
223
|
psql_password=psql_password,
|
|
219
224
|
psql_host=psql_host,
|
|
220
225
|
psql_port=psql_port,
|
|
226
|
+
psql_database=psql_database,
|
|
221
227
|
)
|
|
222
228
|
|
|
223
229
|
is_present = database in current_databases
|
|
@@ -230,6 +236,7 @@ def database(
|
|
|
230
236
|
password=psql_password,
|
|
231
237
|
host=psql_host,
|
|
232
238
|
port=psql_port,
|
|
239
|
+
database=psql_database,
|
|
233
240
|
)
|
|
234
241
|
else:
|
|
235
242
|
host.noop("postgresql database {0} does not exist".format(database))
|
|
@@ -257,6 +264,7 @@ def database(
|
|
|
257
264
|
password=psql_password,
|
|
258
265
|
host=psql_host,
|
|
259
266
|
port=psql_port,
|
|
267
|
+
database=psql_database,
|
|
260
268
|
)
|
|
261
269
|
else:
|
|
262
270
|
host.noop("postgresql database {0} exists".format(database))
|
|
@@ -265,18 +273,17 @@ def database(
|
|
|
265
273
|
@operation(is_idempotent=False)
|
|
266
274
|
def dump(
|
|
267
275
|
dest: str,
|
|
268
|
-
database: str | None = None,
|
|
269
276
|
# Details for speaking to PostgreSQL via `psql` CLI
|
|
270
277
|
psql_user: str | None = None,
|
|
271
278
|
psql_password: str | None = None,
|
|
272
279
|
psql_host: str | None = None,
|
|
273
280
|
psql_port: int | None = None,
|
|
281
|
+
psql_database: str | None = None,
|
|
274
282
|
):
|
|
275
283
|
"""
|
|
276
284
|
Dump a PostgreSQL database into a ``.sql`` file. Requires ``pg_dump``.
|
|
277
285
|
|
|
278
286
|
+ dest: name of the file to dump the SQL to
|
|
279
|
-
+ database: name of the database to dump
|
|
280
287
|
+ psql_*: global module arguments, see above
|
|
281
288
|
|
|
282
289
|
**Example:**
|
|
@@ -286,7 +293,6 @@ def dump(
|
|
|
286
293
|
postgresql.dump(
|
|
287
294
|
name="Dump the pyinfra_stuff database",
|
|
288
295
|
dest="/tmp/pyinfra_stuff.dump",
|
|
289
|
-
database="pyinfra_stuff",
|
|
290
296
|
sudo_user="postgres",
|
|
291
297
|
)
|
|
292
298
|
|
|
@@ -295,11 +301,11 @@ def dump(
|
|
|
295
301
|
yield StringCommand(
|
|
296
302
|
make_psql_command(
|
|
297
303
|
executable="pg_dump",
|
|
298
|
-
database=database,
|
|
299
304
|
user=psql_user,
|
|
300
305
|
password=psql_password,
|
|
301
306
|
host=psql_host,
|
|
302
307
|
port=psql_port,
|
|
308
|
+
database=psql_database,
|
|
303
309
|
),
|
|
304
310
|
">",
|
|
305
311
|
QuoteString(dest),
|
|
@@ -309,18 +315,17 @@ def dump(
|
|
|
309
315
|
@operation(is_idempotent=False)
|
|
310
316
|
def load(
|
|
311
317
|
src: str,
|
|
312
|
-
database: str | None = None,
|
|
313
318
|
# Details for speaking to PostgreSQL via `psql` CLI
|
|
314
319
|
psql_user: str | None = None,
|
|
315
320
|
psql_password: str | None = None,
|
|
316
321
|
psql_host: str | None = None,
|
|
317
322
|
psql_port: int | None = None,
|
|
323
|
+
psql_database: str | None = None,
|
|
318
324
|
):
|
|
319
325
|
"""
|
|
320
326
|
Load ``.sql`` file into a database.
|
|
321
327
|
|
|
322
328
|
+ src: the filename to read from
|
|
323
|
-
+ database: name of the database to import into
|
|
324
329
|
+ psql_*: global module arguments, see above
|
|
325
330
|
|
|
326
331
|
**Example:**
|
|
@@ -330,7 +335,6 @@ def load(
|
|
|
330
335
|
postgresql.load(
|
|
331
336
|
name="Import the pyinfra_stuff dump into pyinfra_stuff_copy",
|
|
332
337
|
src="/tmp/pyinfra_stuff.dump",
|
|
333
|
-
database="pyinfra_stuff_copy",
|
|
334
338
|
sudo_user="postgres",
|
|
335
339
|
)
|
|
336
340
|
|
|
@@ -338,11 +342,11 @@ def load(
|
|
|
338
342
|
|
|
339
343
|
yield StringCommand(
|
|
340
344
|
make_psql_command(
|
|
341
|
-
database=database,
|
|
342
345
|
user=psql_user,
|
|
343
346
|
password=psql_password,
|
|
344
347
|
host=psql_host,
|
|
345
348
|
port=psql_port,
|
|
349
|
+
database=psql_database,
|
|
346
350
|
),
|
|
347
351
|
"<",
|
|
348
352
|
QuoteString(src),
|
pyinfra/operations/runit.py
CHANGED
pyinfra/operations/server.py
CHANGED
|
@@ -5,7 +5,6 @@ Linux/BSD.
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
import shlex
|
|
9
8
|
from io import StringIO
|
|
10
9
|
from itertools import filterfalse, tee
|
|
11
10
|
from os import path
|
|
@@ -18,7 +17,6 @@ from pyinfra.api.util import try_int
|
|
|
18
17
|
from pyinfra.connectors.util import remove_any_sudo_askpass_file
|
|
19
18
|
from pyinfra.facts.files import Directory, FindInFile, Link
|
|
20
19
|
from pyinfra.facts.server import (
|
|
21
|
-
Crontab,
|
|
22
20
|
Groups,
|
|
23
21
|
Home,
|
|
24
22
|
Hostname,
|
|
@@ -30,6 +28,7 @@ from pyinfra.facts.server import (
|
|
|
30
28
|
Users,
|
|
31
29
|
Which,
|
|
32
30
|
)
|
|
31
|
+
from pyinfra.operations import crontab as crontab_
|
|
33
32
|
|
|
34
33
|
from . import (
|
|
35
34
|
apk,
|
|
@@ -49,7 +48,7 @@ from . import (
|
|
|
49
48
|
yum,
|
|
50
49
|
zypper,
|
|
51
50
|
)
|
|
52
|
-
from .util.files import chmod
|
|
51
|
+
from .util.files import chmod
|
|
53
52
|
|
|
54
53
|
if TYPE_CHECKING:
|
|
55
54
|
from pyinfra.api.arguments_typed import PyinfraOperation
|
|
@@ -588,180 +587,7 @@ def packages(
|
|
|
588
587
|
yield from package_operation._inner(packages=packages, present=present)
|
|
589
588
|
|
|
590
589
|
|
|
591
|
-
|
|
592
|
-
def crontab(
|
|
593
|
-
command: str,
|
|
594
|
-
present=True,
|
|
595
|
-
user: str | None = None,
|
|
596
|
-
cron_name: str | None = None,
|
|
597
|
-
minute="*",
|
|
598
|
-
hour="*",
|
|
599
|
-
month="*",
|
|
600
|
-
day_of_week="*",
|
|
601
|
-
day_of_month="*",
|
|
602
|
-
special_time: str | None = None,
|
|
603
|
-
interpolate_variables=False,
|
|
604
|
-
):
|
|
605
|
-
"""
|
|
606
|
-
Add/remove/update crontab entries.
|
|
607
|
-
|
|
608
|
-
+ command: the command for the cron
|
|
609
|
-
+ present: whether this cron command should exist
|
|
610
|
-
+ user: the user whose crontab to manage
|
|
611
|
-
+ cron_name: name the cronjob so future changes to the command will overwrite
|
|
612
|
-
+ minute: which minutes to execute the cron
|
|
613
|
-
+ hour: which hours to execute the cron
|
|
614
|
-
+ month: which months to execute the cron
|
|
615
|
-
+ day_of_week: which day of the week to execute the cron
|
|
616
|
-
+ day_of_month: which day of the month to execute the cron
|
|
617
|
-
+ special_time: cron "nickname" time (@reboot, @daily, etc), overrides others
|
|
618
|
-
+ interpolate_variables: whether to interpolate variables in ``command``
|
|
619
|
-
|
|
620
|
-
Cron commands:
|
|
621
|
-
Unless ``name`` is specified the command is used to identify crontab entries.
|
|
622
|
-
This means commands must be unique within a given users crontab. If you require
|
|
623
|
-
multiple identical commands, provide a different name argument for each.
|
|
624
|
-
|
|
625
|
-
Special times:
|
|
626
|
-
When provided, ``special_time`` will be used instead of any values passed in
|
|
627
|
-
for ``minute``/``hour``/``month``/``day_of_week``/``day_of_month``.
|
|
628
|
-
|
|
629
|
-
**Example:**
|
|
630
|
-
|
|
631
|
-
.. code:: python
|
|
632
|
-
|
|
633
|
-
# simple example for a crontab
|
|
634
|
-
server.crontab(
|
|
635
|
-
name="Backup /etc weekly",
|
|
636
|
-
command="/bin/tar cf /tmp/etc_bup.tar /etc",
|
|
637
|
-
name="backup_etc",
|
|
638
|
-
day_of_week=0,
|
|
639
|
-
hour=1,
|
|
640
|
-
minute=0,
|
|
641
|
-
)
|
|
642
|
-
"""
|
|
643
|
-
|
|
644
|
-
def comma_sep(value):
|
|
645
|
-
if isinstance(value, (list, tuple)):
|
|
646
|
-
return ",".join("{0}".format(v) for v in value)
|
|
647
|
-
return value
|
|
648
|
-
|
|
649
|
-
minute = comma_sep(minute)
|
|
650
|
-
hour = comma_sep(hour)
|
|
651
|
-
month = comma_sep(month)
|
|
652
|
-
day_of_week = comma_sep(day_of_week)
|
|
653
|
-
day_of_month = comma_sep(day_of_month)
|
|
654
|
-
|
|
655
|
-
crontab = host.get_fact(Crontab, user=user)
|
|
656
|
-
name_comment = "# pyinfra-name={0}".format(cron_name)
|
|
657
|
-
|
|
658
|
-
existing_crontab = crontab.get(command)
|
|
659
|
-
existing_crontab_command = command
|
|
660
|
-
existing_crontab_match = command
|
|
661
|
-
|
|
662
|
-
if not existing_crontab and cron_name: # find the crontab by name if provided
|
|
663
|
-
for cmd, details in crontab.items():
|
|
664
|
-
if not details["comments"]:
|
|
665
|
-
continue
|
|
666
|
-
if name_comment in details["comments"]:
|
|
667
|
-
existing_crontab = details
|
|
668
|
-
existing_crontab_match = cmd
|
|
669
|
-
existing_crontab_command = cmd
|
|
670
|
-
|
|
671
|
-
exists = existing_crontab is not None
|
|
672
|
-
|
|
673
|
-
edit_commands: list[str | StringCommand] = []
|
|
674
|
-
temp_filename = host.get_temp_filename()
|
|
675
|
-
|
|
676
|
-
if special_time:
|
|
677
|
-
new_crontab_line = "{0} {1}".format(special_time, command)
|
|
678
|
-
else:
|
|
679
|
-
new_crontab_line = "{minute} {hour} {day_of_month} {month} {day_of_week} {command}".format(
|
|
680
|
-
minute=minute,
|
|
681
|
-
hour=hour,
|
|
682
|
-
day_of_month=day_of_month,
|
|
683
|
-
month=month,
|
|
684
|
-
day_of_week=day_of_week,
|
|
685
|
-
command=command,
|
|
686
|
-
)
|
|
687
|
-
|
|
688
|
-
existing_crontab_match = ".*{0}.*".format(existing_crontab_match)
|
|
689
|
-
|
|
690
|
-
# Don't want the cron and it does exist? Remove the line
|
|
691
|
-
if not present and exists:
|
|
692
|
-
edit_commands.append(
|
|
693
|
-
sed_replace(
|
|
694
|
-
temp_filename,
|
|
695
|
-
existing_crontab_match,
|
|
696
|
-
"",
|
|
697
|
-
interpolate_variables=interpolate_variables,
|
|
698
|
-
),
|
|
699
|
-
)
|
|
700
|
-
|
|
701
|
-
# Want the cron but it doesn't exist? Append the line
|
|
702
|
-
elif present and not exists:
|
|
703
|
-
if cron_name:
|
|
704
|
-
if crontab: # append a blank line if cron entries already exist
|
|
705
|
-
edit_commands.append("echo '' >> {0}".format(temp_filename))
|
|
706
|
-
edit_commands.append(
|
|
707
|
-
"echo {0} >> {1}".format(
|
|
708
|
-
shlex.quote(name_comment),
|
|
709
|
-
temp_filename,
|
|
710
|
-
),
|
|
711
|
-
)
|
|
712
|
-
|
|
713
|
-
edit_commands.append(
|
|
714
|
-
"echo {0} >> {1}".format(
|
|
715
|
-
shlex.quote(new_crontab_line),
|
|
716
|
-
temp_filename,
|
|
717
|
-
),
|
|
718
|
-
)
|
|
719
|
-
|
|
720
|
-
# We have the cron and it exists, do it's details? If not, replace the line
|
|
721
|
-
elif present and exists:
|
|
722
|
-
assert existing_crontab is not None
|
|
723
|
-
if any(
|
|
724
|
-
(
|
|
725
|
-
special_time != existing_crontab.get("special_time"),
|
|
726
|
-
try_int(minute) != existing_crontab.get("minute"),
|
|
727
|
-
try_int(hour) != existing_crontab.get("hour"),
|
|
728
|
-
try_int(month) != existing_crontab.get("month"),
|
|
729
|
-
try_int(day_of_week) != existing_crontab.get("day_of_week"),
|
|
730
|
-
try_int(day_of_month) != existing_crontab.get("day_of_month"),
|
|
731
|
-
existing_crontab_command != command,
|
|
732
|
-
),
|
|
733
|
-
):
|
|
734
|
-
edit_commands.append(
|
|
735
|
-
sed_replace(
|
|
736
|
-
temp_filename,
|
|
737
|
-
existing_crontab_match,
|
|
738
|
-
new_crontab_line,
|
|
739
|
-
interpolate_variables=interpolate_variables,
|
|
740
|
-
),
|
|
741
|
-
)
|
|
742
|
-
|
|
743
|
-
if edit_commands:
|
|
744
|
-
crontab_args = []
|
|
745
|
-
if user:
|
|
746
|
-
crontab_args.append("-u {0}".format(user))
|
|
747
|
-
|
|
748
|
-
# List the crontab into a temporary file if it exists
|
|
749
|
-
if crontab:
|
|
750
|
-
yield "crontab -l {0} > {1}".format(" ".join(crontab_args), temp_filename)
|
|
751
|
-
|
|
752
|
-
# Now yield any edits
|
|
753
|
-
for edit_command in edit_commands:
|
|
754
|
-
yield edit_command
|
|
755
|
-
|
|
756
|
-
# Finally, use the tempfile to write a new crontab
|
|
757
|
-
yield "crontab {0} {1}".format(" ".join(crontab_args), temp_filename)
|
|
758
|
-
else:
|
|
759
|
-
host.noop(
|
|
760
|
-
"crontab {0} {1}".format(
|
|
761
|
-
command,
|
|
762
|
-
"exists" if present else "does not exist",
|
|
763
|
-
),
|
|
764
|
-
)
|
|
590
|
+
crontab = crontab_.crontab
|
|
765
591
|
|
|
766
592
|
|
|
767
593
|
@operation()
|
pyinfra/operations/zfs.py
CHANGED
|
@@ -4,7 +4,7 @@ Manage ZFS filesystems.
|
|
|
4
4
|
|
|
5
5
|
from pyinfra import host
|
|
6
6
|
from pyinfra.api import operation
|
|
7
|
-
from pyinfra.facts.zfs import
|
|
7
|
+
from pyinfra.facts.zfs import ZfsDatasets, ZfsSnapshots
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@operation()
|
|
@@ -48,7 +48,7 @@ def dataset(
|
|
|
48
48
|
|
|
49
49
|
properties.update(extra_props)
|
|
50
50
|
|
|
51
|
-
datasets = host.get_fact(
|
|
51
|
+
datasets = host.get_fact(ZfsDatasets)
|
|
52
52
|
|
|
53
53
|
existing_dataset = datasets.get(dataset_name)
|
|
54
54
|
|
|
@@ -103,7 +103,7 @@ def snapshot(snapshot_name, present=True, recursive=False, properties={}, **extr
|
|
|
103
103
|
|
|
104
104
|
"""
|
|
105
105
|
properties.update(extra_props)
|
|
106
|
-
snapshots = host.get_fact(
|
|
106
|
+
snapshots = host.get_fact(ZfsSnapshots)
|
|
107
107
|
|
|
108
108
|
if snapshot_name in snapshots or not present:
|
|
109
109
|
yield from dataset._inner(snapshot_name, present=present, properties=properties)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyinfra
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2
|
|
4
4
|
Summary: pyinfra automates/provisions/manages/deploys infrastructure.
|
|
5
5
|
Home-page: https://pyinfra.com
|
|
6
6
|
Author: Nick / Fizzadar
|
|
@@ -33,7 +33,6 @@ Requires-Dist: click >2
|
|
|
33
33
|
Requires-Dist: jinja2 <4,>2
|
|
34
34
|
Requires-Dist: python-dateutil <3,>2
|
|
35
35
|
Requires-Dist: setuptools
|
|
36
|
-
Requires-Dist: configparser
|
|
37
36
|
Requires-Dist: pywinrm
|
|
38
37
|
Requires-Dist: typeguard
|
|
39
38
|
Requires-Dist: distro <2,>=1.6
|
|
@@ -42,12 +41,12 @@ Requires-Dist: importlib-metadata >=3.6 ; python_version < "3.10"
|
|
|
42
41
|
Requires-Dist: typing-extensions ; python_version < "3.11"
|
|
43
42
|
Requires-Dist: graphlib-backport ; python_version < "3.9"
|
|
44
43
|
Provides-Extra: dev
|
|
45
|
-
Requires-Dist: pytest ==8.
|
|
46
|
-
Requires-Dist: coverage ==7.
|
|
44
|
+
Requires-Dist: pytest ==8.3.3 ; extra == 'dev'
|
|
45
|
+
Requires-Dist: coverage ==7.6.1 ; extra == 'dev'
|
|
47
46
|
Requires-Dist: pytest-cov ==5.0.0 ; extra == 'dev'
|
|
48
|
-
Requires-Dist: black ==24.
|
|
47
|
+
Requires-Dist: black ==24.8.0 ; extra == 'dev'
|
|
49
48
|
Requires-Dist: isort ==5.13.2 ; extra == 'dev'
|
|
50
|
-
Requires-Dist: flake8 ==7.
|
|
49
|
+
Requires-Dist: flake8 ==7.1.1 ; extra == 'dev'
|
|
51
50
|
Requires-Dist: flake8-black ==0.3.6 ; extra == 'dev'
|
|
52
51
|
Requires-Dist: flake8-isort ==6.1.1 ; extra == 'dev'
|
|
53
52
|
Requires-Dist: mypy ; extra == 'dev'
|
|
@@ -57,7 +56,7 @@ Requires-Dist: types-python-dateutil ; extra == 'dev'
|
|
|
57
56
|
Requires-Dist: types-PyYAML ; extra == 'dev'
|
|
58
57
|
Requires-Dist: types-setuptools ; extra == 'dev'
|
|
59
58
|
Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.16 ; extra == 'dev'
|
|
60
|
-
Requires-Dist: myst-parser ==
|
|
59
|
+
Requires-Dist: myst-parser ==3.0.1 ; extra == 'dev'
|
|
61
60
|
Requires-Dist: sphinx ==6.2.1 ; extra == 'dev'
|
|
62
61
|
Requires-Dist: wheel ; extra == 'dev'
|
|
63
62
|
Requires-Dist: twine ; extra == 'dev'
|
|
@@ -68,15 +67,15 @@ Requires-Dist: flake8-spellcheck ==0.12.1 ; extra == 'dev'
|
|
|
68
67
|
Requires-Dist: redbaron ; extra == 'dev'
|
|
69
68
|
Provides-Extra: docs
|
|
70
69
|
Requires-Dist: pyinfra-guzzle-sphinx-theme ==0.16 ; extra == 'docs'
|
|
71
|
-
Requires-Dist: myst-parser ==
|
|
70
|
+
Requires-Dist: myst-parser ==3.0.1 ; extra == 'docs'
|
|
72
71
|
Requires-Dist: sphinx ==6.2.1 ; extra == 'docs'
|
|
73
72
|
Provides-Extra: test
|
|
74
|
-
Requires-Dist: pytest ==8.
|
|
75
|
-
Requires-Dist: coverage ==7.
|
|
73
|
+
Requires-Dist: pytest ==8.3.3 ; extra == 'test'
|
|
74
|
+
Requires-Dist: coverage ==7.6.1 ; extra == 'test'
|
|
76
75
|
Requires-Dist: pytest-cov ==5.0.0 ; extra == 'test'
|
|
77
|
-
Requires-Dist: black ==24.
|
|
76
|
+
Requires-Dist: black ==24.8.0 ; extra == 'test'
|
|
78
77
|
Requires-Dist: isort ==5.13.2 ; extra == 'test'
|
|
79
|
-
Requires-Dist: flake8 ==7.
|
|
78
|
+
Requires-Dist: flake8 ==7.1.1 ; extra == 'test'
|
|
80
79
|
Requires-Dist: flake8-black ==0.3.6 ; extra == 'test'
|
|
81
80
|
Requires-Dist: flake8-isort ==6.1.1 ; extra == 'test'
|
|
82
81
|
Requires-Dist: mypy ; extra == 'test'
|