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
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage BSD init services (``/etc/rc.d``, ``/usr/local/etc/rc.d``).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pyinfra import host
|
|
8
|
+
from pyinfra.api import operation
|
|
9
|
+
from pyinfra.facts.bsdinit import RcdStatus
|
|
10
|
+
from pyinfra.facts.server import Os
|
|
11
|
+
|
|
12
|
+
from . import files
|
|
13
|
+
from .util.service import handle_service_control
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@operation()
|
|
17
|
+
def service(
|
|
18
|
+
service: str,
|
|
19
|
+
running=True,
|
|
20
|
+
restarted=False,
|
|
21
|
+
reloaded=False,
|
|
22
|
+
command: str | None = None,
|
|
23
|
+
enabled: bool | None = None,
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Manage the state of BSD init services.
|
|
27
|
+
|
|
28
|
+
+ service: name of the service to manage
|
|
29
|
+
+ running: whether the service should be running
|
|
30
|
+
+ restarted: whether the service should be restarted
|
|
31
|
+
+ reloaded: whether the service should be reloaded
|
|
32
|
+
+ command: custom command to pass like: ``/etc/rc.d/<service> <command>``
|
|
33
|
+
+ enabled: whether this service should be enabled/disabled on boot
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
status_argument = "status"
|
|
37
|
+
if host.get_fact(Os) == "OpenBSD":
|
|
38
|
+
status_argument = "check"
|
|
39
|
+
|
|
40
|
+
yield from handle_service_control(
|
|
41
|
+
host,
|
|
42
|
+
service,
|
|
43
|
+
host.get_fact(RcdStatus),
|
|
44
|
+
"test -e /etc/rc.d/{0} && /etc/rc.d/{0} {1} || /usr/local/etc/rc.d/{0} {1}",
|
|
45
|
+
running,
|
|
46
|
+
restarted,
|
|
47
|
+
reloaded,
|
|
48
|
+
command,
|
|
49
|
+
status_argument=status_argument,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# BSD init is simple, just add/remove <service>_enabled="YES"
|
|
53
|
+
if isinstance(enabled, bool):
|
|
54
|
+
yield from files.line._inner(
|
|
55
|
+
path="/etc/rc.conf.local",
|
|
56
|
+
line="^{0}_enable=".format(service),
|
|
57
|
+
replace='{0}_enable="YES"'.format(service),
|
|
58
|
+
present=enabled,
|
|
59
|
+
)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage cargo (aka Rust) packages.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pyinfra import host
|
|
8
|
+
from pyinfra.api import operation
|
|
9
|
+
from pyinfra.facts.cargo import CargoPackages
|
|
10
|
+
|
|
11
|
+
from .util.packaging import ensure_packages
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@operation()
|
|
15
|
+
def packages(packages: str | list[str] | None = None, present=True, latest=False):
|
|
16
|
+
"""
|
|
17
|
+
Install/remove/update cargo packages.
|
|
18
|
+
|
|
19
|
+
+ packages: list of packages to ensure
|
|
20
|
+
+ present: whether the packages should be present
|
|
21
|
+
+ latest: whether to upgrade packages without a specified version
|
|
22
|
+
|
|
23
|
+
Versions:
|
|
24
|
+
Package versions can be pinned like cargo: ``<pkg>@<version>``.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
current_packages = host.get_fact(CargoPackages)
|
|
28
|
+
|
|
29
|
+
install_command = "cargo install"
|
|
30
|
+
|
|
31
|
+
uninstall_command = "cargo uninstall"
|
|
32
|
+
|
|
33
|
+
upgrade_command = "cargo install"
|
|
34
|
+
|
|
35
|
+
yield from ensure_packages(
|
|
36
|
+
host,
|
|
37
|
+
packages,
|
|
38
|
+
current_packages,
|
|
39
|
+
present,
|
|
40
|
+
install_command=install_command,
|
|
41
|
+
uninstall_command=uninstall_command,
|
|
42
|
+
upgrade_command=upgrade_command,
|
|
43
|
+
version_join="@",
|
|
44
|
+
latest=latest,
|
|
45
|
+
)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage ``choco`` (Chocolatey) packages (https://chocolatey.org).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pyinfra import host
|
|
8
|
+
from pyinfra.api import operation
|
|
9
|
+
from pyinfra.facts.choco import ChocoPackages
|
|
10
|
+
|
|
11
|
+
from .util.packaging import ensure_packages
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@operation()
|
|
15
|
+
def packages(packages: str | list[str] | None = None, present=True, latest=False):
|
|
16
|
+
"""
|
|
17
|
+
Add/remove/update ``choco`` packages.
|
|
18
|
+
|
|
19
|
+
+ packages: list of packages to ensure
|
|
20
|
+
+ present: whether the packages should be installed
|
|
21
|
+
+ latest: whether to upgrade packages without a specified version
|
|
22
|
+
|
|
23
|
+
Versions:
|
|
24
|
+
Package versions can be pinned like gem: ``<pkg>:<version>``.
|
|
25
|
+
|
|
26
|
+
**Example:**
|
|
27
|
+
|
|
28
|
+
.. code:: python
|
|
29
|
+
|
|
30
|
+
# Note: Assumes that 'choco' is installed and
|
|
31
|
+
# user has Administrator permission.
|
|
32
|
+
choco.packages(
|
|
33
|
+
name="Install Notepad++",
|
|
34
|
+
packages=["notepadplusplus"],
|
|
35
|
+
)
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
yield from ensure_packages(
|
|
39
|
+
host,
|
|
40
|
+
packages,
|
|
41
|
+
host.get_fact(ChocoPackages),
|
|
42
|
+
present,
|
|
43
|
+
install_command="choco install -y",
|
|
44
|
+
uninstall_command="choco uninstall -y -x",
|
|
45
|
+
upgrade_command="choco update -y",
|
|
46
|
+
version_join=":",
|
|
47
|
+
latest=latest,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@operation(is_idempotent=False)
|
|
52
|
+
def install():
|
|
53
|
+
"""
|
|
54
|
+
Install ``choco`` (Chocolatey).
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
yield (
|
|
58
|
+
"Set-ExecutionPolicy Bypass -Scope Process -Force ;"
|
|
59
|
+
"iex ((New-Object System.Net.WebClient).DownloadString"
|
|
60
|
+
'("https://chocolatey.org/install.ps1"))'
|
|
61
|
+
) # noqa
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import shlex
|
|
4
|
+
|
|
5
|
+
from pyinfra import logger
|
|
6
|
+
from pyinfra.api.command import StringCommand
|
|
7
|
+
from pyinfra.api.operation import operation
|
|
8
|
+
from pyinfra.api.util import try_int
|
|
9
|
+
from pyinfra.context import host
|
|
10
|
+
from pyinfra.facts.crontab import Crontab, CrontabFile
|
|
11
|
+
from pyinfra.operations.util.files import sed_delete, sed_replace
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@operation()
|
|
15
|
+
def crontab(
|
|
16
|
+
command: str,
|
|
17
|
+
present=True,
|
|
18
|
+
user: str | None = None,
|
|
19
|
+
cron_name: str | None = None,
|
|
20
|
+
minute="*",
|
|
21
|
+
hour="*",
|
|
22
|
+
month="*",
|
|
23
|
+
day_of_week="*",
|
|
24
|
+
day_of_month="*",
|
|
25
|
+
special_time: str | None = None,
|
|
26
|
+
interpolate_variables=False,
|
|
27
|
+
):
|
|
28
|
+
"""
|
|
29
|
+
Add/remove/update crontab entries.
|
|
30
|
+
|
|
31
|
+
+ command: the command for the cron
|
|
32
|
+
+ present: whether this cron command should exist
|
|
33
|
+
+ user: the user whose crontab to manage
|
|
34
|
+
+ cron_name: name the cronjob so future changes to the command will overwrite
|
|
35
|
+
+ modify_cron_name: modify the cron name
|
|
36
|
+
+ minute: which minutes to execute the cron
|
|
37
|
+
+ hour: which hours to execute the cron
|
|
38
|
+
+ month: which months to execute the cron
|
|
39
|
+
+ day_of_week: which day of the week to execute the cron
|
|
40
|
+
+ day_of_month: which day of the month to execute the cron
|
|
41
|
+
+ special_time: cron "nickname" time (@reboot, @daily, etc), overrides others
|
|
42
|
+
+ interpolate_variables: whether to interpolate variables in ``command``
|
|
43
|
+
|
|
44
|
+
Cron commands:
|
|
45
|
+
Unless ``name`` is specified the command is used to identify crontab entries.
|
|
46
|
+
This means commands must be unique within a given users crontab. If you require
|
|
47
|
+
multiple identical commands, provide a different name argument for each.
|
|
48
|
+
|
|
49
|
+
Special times:
|
|
50
|
+
When provided, ``special_time`` will be used instead of any values passed in
|
|
51
|
+
for ``minute``/``hour``/``month``/``day_of_week``/``day_of_month``.
|
|
52
|
+
|
|
53
|
+
**Example:**
|
|
54
|
+
|
|
55
|
+
.. code:: python
|
|
56
|
+
|
|
57
|
+
# simple example for a crontab
|
|
58
|
+
crontab.crontab(
|
|
59
|
+
name="Backup /etc weekly",
|
|
60
|
+
command="/bin/tar cf /tmp/etc_bup.tar /etc",
|
|
61
|
+
name="backup_etc",
|
|
62
|
+
day_of_week=0,
|
|
63
|
+
hour=1,
|
|
64
|
+
minute=0,
|
|
65
|
+
)
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def comma_sep(value):
|
|
69
|
+
if isinstance(value, (list, tuple)):
|
|
70
|
+
return ",".join("{0}".format(v) for v in value)
|
|
71
|
+
return value
|
|
72
|
+
|
|
73
|
+
minute = comma_sep(minute)
|
|
74
|
+
hour = comma_sep(hour)
|
|
75
|
+
month = comma_sep(month)
|
|
76
|
+
day_of_week = comma_sep(day_of_week)
|
|
77
|
+
day_of_month = comma_sep(day_of_month)
|
|
78
|
+
|
|
79
|
+
ctb0: CrontabFile | dict = host.get_fact(Crontab, user=user)
|
|
80
|
+
# facts from test are in dict
|
|
81
|
+
if isinstance(ctb0, dict):
|
|
82
|
+
ctb = CrontabFile(ctb0)
|
|
83
|
+
else:
|
|
84
|
+
ctb = ctb0
|
|
85
|
+
name_comment = "# pyinfra-name={0}".format(cron_name)
|
|
86
|
+
|
|
87
|
+
existing_crontab = ctb.get_command(command=command, name=cron_name)
|
|
88
|
+
existing_crontab_command = existing_crontab["command"] if existing_crontab else command
|
|
89
|
+
existing_crontab_match = existing_crontab["command"] if existing_crontab else command
|
|
90
|
+
|
|
91
|
+
exists = existing_crontab is not None
|
|
92
|
+
exists_name = existing_crontab is not None and name_comment in existing_crontab.get(
|
|
93
|
+
"comments", ""
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
edit_commands: list[str | StringCommand] = []
|
|
97
|
+
temp_filename = host.get_temp_filename()
|
|
98
|
+
|
|
99
|
+
if special_time:
|
|
100
|
+
new_crontab_line = "{0} {1}".format(special_time, command)
|
|
101
|
+
else:
|
|
102
|
+
new_crontab_line = "{minute} {hour} {day_of_month} {month} {day_of_week} {command}".format(
|
|
103
|
+
minute=minute,
|
|
104
|
+
hour=hour,
|
|
105
|
+
day_of_month=day_of_month,
|
|
106
|
+
month=month,
|
|
107
|
+
day_of_week=day_of_week,
|
|
108
|
+
command=command,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
existing_crontab_match = ".*{0}.*".format(existing_crontab_match)
|
|
112
|
+
|
|
113
|
+
# Don't want the cron and it does exist? Remove the line
|
|
114
|
+
if not present and exists:
|
|
115
|
+
edit_commands.append(
|
|
116
|
+
sed_delete(
|
|
117
|
+
temp_filename,
|
|
118
|
+
existing_crontab_match,
|
|
119
|
+
"",
|
|
120
|
+
interpolate_variables=interpolate_variables,
|
|
121
|
+
),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Want the cron but it doesn't exist? Append the line
|
|
125
|
+
elif present and not exists:
|
|
126
|
+
logger.debug(f"present: {present}, exists: {exists}")
|
|
127
|
+
if ctb: # append a blank line if cron entries already exist
|
|
128
|
+
edit_commands.append("echo '' >> {0}".format(temp_filename))
|
|
129
|
+
if cron_name:
|
|
130
|
+
edit_commands.append(
|
|
131
|
+
"echo {0} >> {1}".format(
|
|
132
|
+
shlex.quote(name_comment),
|
|
133
|
+
temp_filename,
|
|
134
|
+
),
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
edit_commands.append(
|
|
138
|
+
"echo {0} >> {1}".format(
|
|
139
|
+
shlex.quote(new_crontab_line),
|
|
140
|
+
temp_filename,
|
|
141
|
+
),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# We have the cron and it exists, do it's details? If not, replace the line
|
|
145
|
+
elif present and exists:
|
|
146
|
+
assert existing_crontab is not None
|
|
147
|
+
if any(
|
|
148
|
+
(
|
|
149
|
+
exists_name != (cron_name is not None),
|
|
150
|
+
special_time != existing_crontab.get("special_time"),
|
|
151
|
+
try_int(minute) != existing_crontab.get("minute"),
|
|
152
|
+
try_int(hour) != existing_crontab.get("hour"),
|
|
153
|
+
try_int(month) != existing_crontab.get("month"),
|
|
154
|
+
try_int(day_of_week) != existing_crontab.get("day_of_week"),
|
|
155
|
+
try_int(day_of_month) != existing_crontab.get("day_of_month"),
|
|
156
|
+
existing_crontab_command != command,
|
|
157
|
+
),
|
|
158
|
+
):
|
|
159
|
+
if not exists_name and cron_name:
|
|
160
|
+
new_crontab_line = f"{name_comment}\\n{new_crontab_line}"
|
|
161
|
+
edit_commands.append(
|
|
162
|
+
sed_replace(
|
|
163
|
+
temp_filename,
|
|
164
|
+
existing_crontab_match,
|
|
165
|
+
new_crontab_line,
|
|
166
|
+
interpolate_variables=interpolate_variables,
|
|
167
|
+
),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if edit_commands:
|
|
171
|
+
crontab_args = []
|
|
172
|
+
if user:
|
|
173
|
+
crontab_args.append("-u {0}".format(user))
|
|
174
|
+
|
|
175
|
+
# List the crontab into a temporary file if it exists
|
|
176
|
+
if ctb:
|
|
177
|
+
yield "crontab -l {0} > {1}".format(" ".join(crontab_args), temp_filename)
|
|
178
|
+
|
|
179
|
+
# Now yield any edits
|
|
180
|
+
for edit_command in edit_commands:
|
|
181
|
+
yield edit_command
|
|
182
|
+
|
|
183
|
+
# Finally, use the tempfile to write a new crontab
|
|
184
|
+
yield "crontab {0} {1}".format(" ".join(crontab_args), temp_filename)
|
|
185
|
+
else:
|
|
186
|
+
host.noop(
|
|
187
|
+
"crontab {0} {1}".format(
|
|
188
|
+
command,
|
|
189
|
+
"exists" if present else "does not exist",
|
|
190
|
+
),
|
|
191
|
+
)
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Manage dnf packages and repositories. Note that dnf package names are case-sensitive.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pyinfra import host, state
|
|
8
|
+
from pyinfra.api import operation
|
|
9
|
+
from pyinfra.facts.rpm import RpmPackageProvides, RpmPackages
|
|
10
|
+
|
|
11
|
+
from .util.packaging import ensure_packages, ensure_rpm, ensure_yum_repo
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@operation(is_idempotent=False)
|
|
15
|
+
def key(src: str):
|
|
16
|
+
"""
|
|
17
|
+
Add dnf gpg keys with ``rpm``.
|
|
18
|
+
|
|
19
|
+
+ key: filename or URL
|
|
20
|
+
|
|
21
|
+
Note:
|
|
22
|
+
always returns one command, not idempotent
|
|
23
|
+
|
|
24
|
+
**Example:**
|
|
25
|
+
|
|
26
|
+
.. code:: python
|
|
27
|
+
|
|
28
|
+
linux_id = host.get_fact(LinuxDistribution)["release_meta"].get("ID")
|
|
29
|
+
dnf.key(
|
|
30
|
+
name="Add the Docker CentOS gpg key",
|
|
31
|
+
src=f"https://download.docker.com/linux/{linux_id}/gpg",
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
yield "rpm --import {0}".format(src)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@operation()
|
|
40
|
+
def repo(
|
|
41
|
+
src: str,
|
|
42
|
+
present=True,
|
|
43
|
+
baseurl: str | None = None,
|
|
44
|
+
description: str | None = None,
|
|
45
|
+
enabled=True,
|
|
46
|
+
gpgcheck=True,
|
|
47
|
+
gpgkey: str | None = None,
|
|
48
|
+
):
|
|
49
|
+
# NOTE: if updating this docstring also update `yum.repo`
|
|
50
|
+
"""
|
|
51
|
+
Add/remove/update dnf repositories.
|
|
52
|
+
|
|
53
|
+
+ src: URL or name for the ``.repo`` file
|
|
54
|
+
+ present: whether the ``.repo`` file should be present
|
|
55
|
+
+ baseurl: the baseurl of the repo (if ``name`` is not a URL)
|
|
56
|
+
+ description: optional verbose description
|
|
57
|
+
+ enabled: whether this repo is enabled
|
|
58
|
+
+ gpgcheck: whether set ``gpgcheck=1``
|
|
59
|
+
+ gpgkey: the URL to the gpg key for this repo
|
|
60
|
+
|
|
61
|
+
``Baseurl``/``description``/``gpgcheck``/``gpgkey``:
|
|
62
|
+
These are only valid when ``name`` is a filename (ie not a URL). This is
|
|
63
|
+
for manual construction of repository files. Use a URL to download and
|
|
64
|
+
install remote repository files.
|
|
65
|
+
|
|
66
|
+
**Examples:**
|
|
67
|
+
|
|
68
|
+
.. code:: python
|
|
69
|
+
|
|
70
|
+
# Download a repository file
|
|
71
|
+
dnf.rpm(
|
|
72
|
+
name="Install Docker-CE repo via URL",
|
|
73
|
+
src="https://download.docker.com/linux/centos/docker-ce.repo",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Create the repository file from baseurl/etc
|
|
77
|
+
dnf.repo(
|
|
78
|
+
name="Add the Docker CentOS repo",
|
|
79
|
+
src="DockerCE",
|
|
80
|
+
baseurl="https://download.docker.com/linux/centos/7/$basearch/stable",
|
|
81
|
+
)
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
yield from ensure_yum_repo(
|
|
85
|
+
host,
|
|
86
|
+
src,
|
|
87
|
+
baseurl,
|
|
88
|
+
present,
|
|
89
|
+
description,
|
|
90
|
+
enabled,
|
|
91
|
+
gpgcheck,
|
|
92
|
+
gpgkey,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@operation()
|
|
97
|
+
def rpm(src: str, present=True):
|
|
98
|
+
# NOTE: if updating this docstring also update `yum.rpm`
|
|
99
|
+
"""
|
|
100
|
+
Add/remove ``.rpm`` file packages.
|
|
101
|
+
|
|
102
|
+
+ src: filename or URL of the ``.rpm`` package
|
|
103
|
+
+ present: whether ore not the package should exist on the system
|
|
104
|
+
|
|
105
|
+
URL sources with ``present=False``:
|
|
106
|
+
If the ``.rpm`` file isn't downloaded, pyinfra can't remove any existing
|
|
107
|
+
package as the file won't exist until mid-deploy.
|
|
108
|
+
|
|
109
|
+
**Example:**
|
|
110
|
+
|
|
111
|
+
.. code:: python
|
|
112
|
+
|
|
113
|
+
major_centos_version = host.get_fact(LinuxDistribution)["major"]
|
|
114
|
+
dnf.rpm(
|
|
115
|
+
name="Install EPEL rpm to enable EPEL repo",
|
|
116
|
+
src=f"https://dl.fedoraproject.org/pub/epel/epel-release-latest-{major_centos_version}.noarch.rpm",
|
|
117
|
+
)
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
yield from ensure_rpm(state, host, src, present, "dnf")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
@operation(is_idempotent=False)
|
|
124
|
+
def update():
|
|
125
|
+
"""
|
|
126
|
+
Updates all dnf packages.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
yield "dnf update -y"
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
_update = update._inner # noqa: E305 (for use below where update is a kwarg)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@operation()
|
|
136
|
+
def packages(
|
|
137
|
+
packages: str | list[str] | None = None,
|
|
138
|
+
present=True,
|
|
139
|
+
latest=False,
|
|
140
|
+
update=False,
|
|
141
|
+
clean=False,
|
|
142
|
+
nobest=False,
|
|
143
|
+
extra_install_args: str | None = None,
|
|
144
|
+
extra_uninstall_args: str | None = None,
|
|
145
|
+
):
|
|
146
|
+
"""
|
|
147
|
+
Install/remove/update dnf packages & updates.
|
|
148
|
+
|
|
149
|
+
+ packages: packages to ensure
|
|
150
|
+
+ present: whether the packages should be installed
|
|
151
|
+
+ latest: whether to upgrade packages without a specified version
|
|
152
|
+
+ update: run ``dnf update`` before installing packages
|
|
153
|
+
+ clean: run ``dnf clean`` before installing packages
|
|
154
|
+
+ nobest: add the no best option to install
|
|
155
|
+
+ extra_install_args: additional arguments to the dnf install command
|
|
156
|
+
+ extra_uninstall_args: additional arguments to the dnf uninstall command
|
|
157
|
+
|
|
158
|
+
Versions:
|
|
159
|
+
Package versions can be pinned as follows: ``<pkg>=<version>``
|
|
160
|
+
|
|
161
|
+
**Examples:**
|
|
162
|
+
|
|
163
|
+
.. code:: python
|
|
164
|
+
|
|
165
|
+
# Update package list and install packages
|
|
166
|
+
dnf.packages(
|
|
167
|
+
name='Install Vim and Vim enhanced',
|
|
168
|
+
packages=["vim-enhanced", "vim"],
|
|
169
|
+
update=True,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Install the latest versions of packages (always check)
|
|
173
|
+
dnf.packages(
|
|
174
|
+
name="Install latest Vim",
|
|
175
|
+
packages=["vim"],
|
|
176
|
+
latest=True,
|
|
177
|
+
)
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
if clean:
|
|
181
|
+
yield "dnf clean all"
|
|
182
|
+
|
|
183
|
+
if update:
|
|
184
|
+
yield from _update()
|
|
185
|
+
|
|
186
|
+
install_command = ["dnf", "install", "-y"]
|
|
187
|
+
|
|
188
|
+
if nobest:
|
|
189
|
+
install_command.append("--nobest")
|
|
190
|
+
|
|
191
|
+
if extra_install_args:
|
|
192
|
+
install_command.append(extra_install_args)
|
|
193
|
+
|
|
194
|
+
uninstall_command = ["dnf", "remove", "-y"]
|
|
195
|
+
|
|
196
|
+
if extra_uninstall_args:
|
|
197
|
+
uninstall_command.append(extra_uninstall_args)
|
|
198
|
+
|
|
199
|
+
yield from ensure_packages(
|
|
200
|
+
host,
|
|
201
|
+
packages,
|
|
202
|
+
host.get_fact(RpmPackages),
|
|
203
|
+
present,
|
|
204
|
+
install_command=" ".join(install_command),
|
|
205
|
+
uninstall_command=" ".join(uninstall_command),
|
|
206
|
+
upgrade_command="dnf update -y",
|
|
207
|
+
version_join="=",
|
|
208
|
+
latest=latest,
|
|
209
|
+
expand_package_fact=lambda package: host.get_fact(RpmPackageProvides, package=package),
|
|
210
|
+
)
|