pyinfra 2.9.2__py2.py3-none-any.whl → 3.0b1__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 +261 -255
- pyinfra/api/arguments_typed.py +77 -0
- pyinfra/api/command.py +66 -53
- pyinfra/api/config.py +27 -22
- pyinfra/api/connect.py +1 -1
- pyinfra/api/connectors.py +2 -24
- pyinfra/api/deploy.py +21 -52
- pyinfra/api/exceptions.py +33 -8
- pyinfra/api/facts.py +77 -113
- pyinfra/api/host.py +150 -82
- pyinfra/api/inventory.py +17 -25
- pyinfra/api/operation.py +232 -198
- pyinfra/api/operations.py +102 -148
- pyinfra/api/state.py +137 -79
- pyinfra/api/util.py +55 -70
- pyinfra/connectors/base.py +150 -0
- pyinfra/connectors/chroot.py +160 -169
- pyinfra/connectors/docker.py +227 -237
- pyinfra/connectors/dockerssh.py +231 -253
- pyinfra/connectors/local.py +195 -207
- pyinfra/connectors/ssh.py +528 -615
- pyinfra/connectors/ssh_util.py +114 -0
- pyinfra/connectors/sshuserclient/client.py +5 -3
- pyinfra/connectors/terraform.py +86 -65
- pyinfra/connectors/util.py +212 -137
- pyinfra/connectors/vagrant.py +55 -48
- pyinfra/context.py +3 -2
- pyinfra/facts/docker.py +1 -0
- pyinfra/facts/files.py +45 -32
- pyinfra/facts/git.py +3 -1
- pyinfra/facts/gpg.py +1 -1
- pyinfra/facts/hardware.py +4 -2
- pyinfra/facts/iptables.py +5 -3
- pyinfra/facts/mysql.py +1 -0
- pyinfra/facts/postgres.py +168 -0
- pyinfra/facts/postgresql.py +5 -161
- pyinfra/facts/selinux.py +3 -1
- pyinfra/facts/server.py +77 -30
- pyinfra/facts/systemd.py +29 -12
- pyinfra/facts/sysvinit.py +10 -10
- pyinfra/facts/util/packaging.py +4 -2
- pyinfra/local.py +4 -5
- pyinfra/operations/apk.py +3 -3
- pyinfra/operations/apt.py +25 -47
- pyinfra/operations/brew.py +7 -14
- pyinfra/operations/bsdinit.py +4 -4
- pyinfra/operations/cargo.py +1 -1
- pyinfra/operations/choco.py +1 -1
- pyinfra/operations/dnf.py +4 -4
- pyinfra/operations/files.py +108 -321
- pyinfra/operations/gem.py +1 -1
- pyinfra/operations/git.py +6 -37
- pyinfra/operations/iptables.py +2 -10
- pyinfra/operations/launchd.py +1 -1
- pyinfra/operations/lxd.py +1 -9
- pyinfra/operations/mysql.py +5 -28
- pyinfra/operations/npm.py +1 -1
- pyinfra/operations/openrc.py +1 -1
- pyinfra/operations/pacman.py +3 -3
- pyinfra/operations/pip.py +14 -15
- pyinfra/operations/pkg.py +1 -1
- pyinfra/operations/pkgin.py +3 -3
- pyinfra/operations/postgres.py +347 -0
- pyinfra/operations/postgresql.py +17 -380
- pyinfra/operations/python.py +2 -17
- pyinfra/operations/selinux.py +5 -28
- pyinfra/operations/server.py +59 -84
- pyinfra/operations/snap.py +1 -3
- pyinfra/operations/ssh.py +8 -23
- pyinfra/operations/systemd.py +7 -7
- pyinfra/operations/sysvinit.py +3 -12
- pyinfra/operations/upstart.py +4 -4
- pyinfra/operations/util/__init__.py +12 -0
- pyinfra/operations/util/files.py +2 -2
- pyinfra/operations/util/packaging.py +6 -24
- pyinfra/operations/util/service.py +18 -37
- pyinfra/operations/vzctl.py +2 -2
- pyinfra/operations/xbps.py +3 -3
- pyinfra/operations/yum.py +4 -4
- pyinfra/operations/zypper.py +4 -4
- {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/METADATA +19 -22
- pyinfra-3.0b1.dist-info/RECORD +163 -0
- pyinfra-3.0b1.dist-info/entry_points.txt +11 -0
- pyinfra_cli/__main__.py +2 -0
- pyinfra_cli/commands.py +7 -2
- pyinfra_cli/exceptions.py +83 -42
- pyinfra_cli/inventory.py +19 -4
- pyinfra_cli/log.py +17 -3
- pyinfra_cli/main.py +133 -90
- pyinfra_cli/prints.py +93 -129
- pyinfra_cli/util.py +60 -29
- tests/test_api/test_api.py +2 -0
- tests/test_api/test_api_arguments.py +13 -13
- tests/test_api/test_api_deploys.py +28 -29
- tests/test_api/test_api_facts.py +60 -98
- tests/test_api/test_api_operations.py +100 -200
- tests/test_cli/test_cli.py +18 -49
- tests/test_cli/test_cli_deploy.py +11 -37
- tests/test_cli/test_cli_exceptions.py +50 -19
- tests/test_cli/util.py +1 -1
- tests/test_connectors/test_chroot.py +6 -6
- tests/test_connectors/test_docker.py +4 -4
- tests/test_connectors/test_dockerssh.py +38 -50
- tests/test_connectors/test_local.py +11 -12
- tests/test_connectors/test_ssh.py +66 -107
- tests/test_connectors/test_terraform.py +9 -15
- tests/test_connectors/test_util.py +24 -46
- tests/test_connectors/test_vagrant.py +4 -4
- pyinfra/api/operation.pyi +0 -117
- pyinfra/connectors/ansible.py +0 -171
- pyinfra/connectors/mech.py +0 -186
- pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
- pyinfra/connectors/winrm.py +0 -320
- 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 -551
- pyinfra-2.9.2.dist-info/RECORD +0 -170
- pyinfra-2.9.2.dist-info/entry_points.txt +0 -14
- tests/test_connectors/test_ansible.py +0 -64
- tests/test_connectors/test_mech.py +0 -126
- tests/test_connectors/test_winrm.py +0 -76
- {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/LICENSE.md +0 -0
- {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/WHEEL +0 -0
- {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/top_level.txt +0 -0
pyinfra/operations/server.py
CHANGED
|
@@ -8,6 +8,7 @@ from io import StringIO
|
|
|
8
8
|
from itertools import filterfalse, tee
|
|
9
9
|
from os import path
|
|
10
10
|
from time import sleep
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
11
12
|
|
|
12
13
|
from pyinfra import host, logger, state
|
|
13
14
|
from pyinfra.api import FunctionCommand, OperationError, StringCommand, operation
|
|
@@ -46,6 +47,9 @@ from . import (
|
|
|
46
47
|
)
|
|
47
48
|
from .util.files import chmod, sed_replace
|
|
48
49
|
|
|
50
|
+
if TYPE_CHECKING:
|
|
51
|
+
from pyinfra.api.arguments_typed import PyinfraOperation
|
|
52
|
+
|
|
49
53
|
|
|
50
54
|
@operation(is_idempotent=False)
|
|
51
55
|
def reboot(delay=10, interval=1, reboot_timeout=300):
|
|
@@ -75,7 +79,7 @@ def reboot(delay=10, interval=1, reboot_timeout=300):
|
|
|
75
79
|
|
|
76
80
|
yield FunctionCommand(remove_any_askpass_file, (), {})
|
|
77
81
|
|
|
78
|
-
yield StringCommand("reboot",
|
|
82
|
+
yield StringCommand("reboot", _success_exit_codes=[0, -1]) # -1 being error/disconnected
|
|
79
83
|
|
|
80
84
|
def wait_and_reconnect(state, host): # pragma: no cover
|
|
81
85
|
sleep(delay)
|
|
@@ -187,8 +191,8 @@ def script(src, args=()):
|
|
|
187
191
|
)
|
|
188
192
|
"""
|
|
189
193
|
|
|
190
|
-
temp_file =
|
|
191
|
-
yield from files.put(src, temp_file)
|
|
194
|
+
temp_file = host.get_temp_filename()
|
|
195
|
+
yield from files.put._inner(src=src, dest=temp_file)
|
|
192
196
|
|
|
193
197
|
yield chmod(temp_file, "+x")
|
|
194
198
|
yield StringCommand(temp_file, *args)
|
|
@@ -217,14 +221,14 @@ def script_template(src, args=(), **data):
|
|
|
217
221
|
)
|
|
218
222
|
"""
|
|
219
223
|
|
|
220
|
-
temp_file =
|
|
221
|
-
yield from files.template(src, temp_file, **data)
|
|
224
|
+
temp_file = host.get_temp_filename("{0}{1}".format(src, data))
|
|
225
|
+
yield from files.template._inner(src, temp_file, **data)
|
|
222
226
|
|
|
223
227
|
yield chmod(temp_file, "+x")
|
|
224
228
|
yield StringCommand(temp_file, *args)
|
|
225
229
|
|
|
226
230
|
|
|
227
|
-
@operation
|
|
231
|
+
@operation()
|
|
228
232
|
def modprobe(module, present=True, force=False):
|
|
229
233
|
"""
|
|
230
234
|
Load/unload kernel modules.
|
|
@@ -259,14 +263,10 @@ def modprobe(module, present=True, force=False):
|
|
|
259
263
|
# Module is loaded and we don't want it?
|
|
260
264
|
if not present and present_mods:
|
|
261
265
|
yield "modprobe{0} -r -a {1}".format(args, " ".join(present_mods))
|
|
262
|
-
for mod in present_mods:
|
|
263
|
-
modules.pop(mod)
|
|
264
266
|
|
|
265
267
|
# Module isn't loaded and we want it?
|
|
266
268
|
elif present and missing_mods:
|
|
267
269
|
yield "modprobe{0} -a {1}".format(args, " ".join(missing_mods))
|
|
268
|
-
for mod in missing_mods:
|
|
269
|
-
modules[mod] = {}
|
|
270
270
|
|
|
271
271
|
else:
|
|
272
272
|
host.noop(
|
|
@@ -279,7 +279,7 @@ def modprobe(module, present=True, force=False):
|
|
|
279
279
|
)
|
|
280
280
|
|
|
281
281
|
|
|
282
|
-
@operation
|
|
282
|
+
@operation()
|
|
283
283
|
def mount(
|
|
284
284
|
path,
|
|
285
285
|
mounted=True,
|
|
@@ -322,13 +322,10 @@ def mount(
|
|
|
322
322
|
args.append(path)
|
|
323
323
|
|
|
324
324
|
yield StringCommand("mount", *args)
|
|
325
|
-
# Should we update facts with fs_type, device, etc?
|
|
326
|
-
mounts[path] = {"options": options}
|
|
327
325
|
|
|
328
326
|
# Want no mount but mounted?
|
|
329
327
|
elif mounted is False and is_mounted:
|
|
330
328
|
yield "umount {0}".format(path)
|
|
331
|
-
mounts.pop(path)
|
|
332
329
|
|
|
333
330
|
# Want mount and is mounted! Check the options
|
|
334
331
|
elif is_mounted and mounted and options:
|
|
@@ -336,7 +333,6 @@ def mount(
|
|
|
336
333
|
needed_options = set(options) - set(mounted_options)
|
|
337
334
|
if needed_options:
|
|
338
335
|
yield "mount -o remount,{0} {1}".format(options_string, path)
|
|
339
|
-
mounts[path]["options"] = options
|
|
340
336
|
|
|
341
337
|
else:
|
|
342
338
|
host.noop(
|
|
@@ -347,7 +343,7 @@ def mount(
|
|
|
347
343
|
)
|
|
348
344
|
|
|
349
345
|
|
|
350
|
-
@operation
|
|
346
|
+
@operation()
|
|
351
347
|
def hostname(hostname, hostname_file=None):
|
|
352
348
|
"""
|
|
353
349
|
Set the system hostname using ``hostnamectl`` or ``hostname`` on older systems.
|
|
@@ -379,7 +375,6 @@ def hostname(hostname, hostname_file=None):
|
|
|
379
375
|
if host.get_fact(Which, command="hostnamectl"):
|
|
380
376
|
if current_hostname != hostname:
|
|
381
377
|
yield "hostnamectl set-hostname {0}".format(hostname)
|
|
382
|
-
host.create_fact(Hostname, data=hostname)
|
|
383
378
|
else:
|
|
384
379
|
host.noop("hostname is set")
|
|
385
380
|
return
|
|
@@ -394,7 +389,6 @@ def hostname(hostname, hostname_file=None):
|
|
|
394
389
|
|
|
395
390
|
if current_hostname != hostname:
|
|
396
391
|
yield "hostname {0}".format(hostname)
|
|
397
|
-
host.create_fact(Hostname, data=hostname)
|
|
398
392
|
else:
|
|
399
393
|
host.noop("hostname is set")
|
|
400
394
|
|
|
@@ -403,10 +397,10 @@ def hostname(hostname, hostname_file=None):
|
|
|
403
397
|
file = StringIO("{0}\n".format(hostname))
|
|
404
398
|
|
|
405
399
|
# And ensure it exists
|
|
406
|
-
yield from files.put(file, hostname_file)
|
|
400
|
+
yield from files.put._inner(src=file, dest=hostname_file)
|
|
407
401
|
|
|
408
402
|
|
|
409
|
-
@operation
|
|
403
|
+
@operation()
|
|
410
404
|
def sysctl(
|
|
411
405
|
key,
|
|
412
406
|
value,
|
|
@@ -437,24 +431,23 @@ def sysctl(
|
|
|
437
431
|
|
|
438
432
|
value = [try_int(v) for v in value] if isinstance(value, list) else try_int(value)
|
|
439
433
|
|
|
440
|
-
existing_sysctls = host.get_fact(Sysctl)
|
|
441
|
-
|
|
434
|
+
existing_sysctls = host.get_fact(Sysctl, keys=[key])
|
|
442
435
|
existing_value = existing_sysctls.get(key)
|
|
436
|
+
|
|
443
437
|
if not existing_value or existing_value != value:
|
|
444
438
|
yield "sysctl {0}='{1}'".format(key, string_value)
|
|
445
|
-
existing_sysctls[key] = value
|
|
446
439
|
else:
|
|
447
440
|
host.noop("sysctl {0} is set to {1}".format(key, string_value))
|
|
448
441
|
|
|
449
442
|
if persist:
|
|
450
|
-
yield from files.line(
|
|
443
|
+
yield from files.line._inner(
|
|
451
444
|
path=persist_file,
|
|
452
445
|
line="{0}[[:space:]]*=[[:space:]]*{1}".format(key, string_value),
|
|
453
446
|
replace="{0} = {1}".format(key, string_value),
|
|
454
447
|
)
|
|
455
448
|
|
|
456
449
|
|
|
457
|
-
@operation
|
|
450
|
+
@operation()
|
|
458
451
|
def service(
|
|
459
452
|
service,
|
|
460
453
|
running=True,
|
|
@@ -465,7 +458,7 @@ def service(
|
|
|
465
458
|
):
|
|
466
459
|
"""
|
|
467
460
|
Manage the state of services. This command checks for the presence of all the
|
|
468
|
-
Linux init systems
|
|
461
|
+
Linux init systems pyinfra can handle and executes the relevant operation.
|
|
469
462
|
|
|
470
463
|
+ service: name of the service to manage
|
|
471
464
|
+ running: whether the service should be running
|
|
@@ -485,6 +478,8 @@ def service(
|
|
|
485
478
|
)
|
|
486
479
|
"""
|
|
487
480
|
|
|
481
|
+
service_operation: "PyinfraOperation"
|
|
482
|
+
|
|
488
483
|
if host.get_fact(Which, command="systemctl"):
|
|
489
484
|
service_operation = systemd.service
|
|
490
485
|
|
|
@@ -503,7 +498,7 @@ def service(
|
|
|
503
498
|
|
|
504
499
|
# NOTE: important that we are not Linux here because /etc/rc.d will exist but checking it's
|
|
505
500
|
# contents may trigger things (like a reboot: https://github.com/Fizzadar/pyinfra/issues/819)
|
|
506
|
-
elif host.get_fact(Os) != "Linux" and host.get_fact(Directory, path="/etc/rc.d"):
|
|
501
|
+
elif host.get_fact(Os) != "Linux" and bool(host.get_fact(Directory, path="/etc/rc.d")):
|
|
507
502
|
service_operation = bsdinit.service
|
|
508
503
|
|
|
509
504
|
else:
|
|
@@ -511,8 +506,8 @@ def service(
|
|
|
511
506
|
("No init system found " "(no systemctl, initctl, /etc/init.d or /etc/rc.d found)"),
|
|
512
507
|
)
|
|
513
508
|
|
|
514
|
-
yield from service_operation(
|
|
515
|
-
service,
|
|
509
|
+
yield from service_operation._inner(
|
|
510
|
+
service=service,
|
|
516
511
|
running=running,
|
|
517
512
|
restarted=restarted,
|
|
518
513
|
reloaded=reloaded,
|
|
@@ -521,14 +516,14 @@ def service(
|
|
|
521
516
|
)
|
|
522
517
|
|
|
523
518
|
|
|
524
|
-
@operation
|
|
519
|
+
@operation()
|
|
525
520
|
def packages(
|
|
526
521
|
packages,
|
|
527
522
|
present=True,
|
|
528
523
|
):
|
|
529
524
|
"""
|
|
530
525
|
Add or remove system packages. This command checks for the presence of all the
|
|
531
|
-
system package managers
|
|
526
|
+
system package managers pyinfra can handle and executes the relevant operation.
|
|
532
527
|
|
|
533
528
|
+ packages: list of packages to ensure
|
|
534
529
|
+ present: whether the packages should be installed
|
|
@@ -543,6 +538,8 @@ def packages(
|
|
|
543
538
|
)
|
|
544
539
|
"""
|
|
545
540
|
|
|
541
|
+
package_operation: "PyinfraOperation"
|
|
542
|
+
|
|
546
543
|
# TODO: improve this - use LinuxDistribution fact + mapping with fallback below?
|
|
547
544
|
# Here to be preferred on openSUSE which also provides aptitude
|
|
548
545
|
# See: https://github.com/Fizzadar/pyinfra/issues/799
|
|
@@ -581,10 +578,10 @@ def packages(
|
|
|
581
578
|
),
|
|
582
579
|
)
|
|
583
580
|
|
|
584
|
-
yield from package_operation(packages=packages, present=present)
|
|
581
|
+
yield from package_operation._inner(packages=packages, present=present)
|
|
585
582
|
|
|
586
583
|
|
|
587
|
-
@operation
|
|
584
|
+
@operation()
|
|
588
585
|
def crontab(
|
|
589
586
|
command,
|
|
590
587
|
present=True,
|
|
@@ -657,6 +654,8 @@ def crontab(
|
|
|
657
654
|
|
|
658
655
|
if not existing_crontab and cron_name: # find the crontab by name if provided
|
|
659
656
|
for cmd, details in crontab.items():
|
|
657
|
+
if not details["comments"]:
|
|
658
|
+
continue
|
|
660
659
|
if name_comment in details["comments"]:
|
|
661
660
|
existing_crontab = details
|
|
662
661
|
existing_crontab_match = cmd
|
|
@@ -665,7 +664,7 @@ def crontab(
|
|
|
665
664
|
exists = existing_crontab is not None
|
|
666
665
|
|
|
667
666
|
edit_commands = []
|
|
668
|
-
temp_filename =
|
|
667
|
+
temp_filename = host.get_temp_filename()
|
|
669
668
|
|
|
670
669
|
if special_time:
|
|
671
670
|
new_crontab_line = "{0} {1}".format(special_time, command)
|
|
@@ -713,6 +712,7 @@ def crontab(
|
|
|
713
712
|
|
|
714
713
|
# We have the cron and it exists, do it's details? If not, replace the line
|
|
715
714
|
elif present and exists:
|
|
715
|
+
assert existing_crontab is not None
|
|
716
716
|
if any(
|
|
717
717
|
(
|
|
718
718
|
special_time != existing_crontab.get("special_time"),
|
|
@@ -748,20 +748,6 @@ def crontab(
|
|
|
748
748
|
|
|
749
749
|
# Finally, use the tempfile to write a new crontab
|
|
750
750
|
yield "crontab {0} {1}".format(" ".join(crontab_args), temp_filename)
|
|
751
|
-
|
|
752
|
-
# Update the crontab fact
|
|
753
|
-
if present:
|
|
754
|
-
crontab[command] = {
|
|
755
|
-
"special_time": special_time,
|
|
756
|
-
"minute": minute,
|
|
757
|
-
"hour": hour,
|
|
758
|
-
"month": month,
|
|
759
|
-
"day_of_week": day_of_week,
|
|
760
|
-
"day_of_month": day_of_month,
|
|
761
|
-
"comments": [cron_name] if cron_name else [],
|
|
762
|
-
}
|
|
763
|
-
else:
|
|
764
|
-
crontab.pop(command)
|
|
765
751
|
else:
|
|
766
752
|
host.noop(
|
|
767
753
|
"crontab {0} {1}".format(
|
|
@@ -771,7 +757,7 @@ def crontab(
|
|
|
771
757
|
)
|
|
772
758
|
|
|
773
759
|
|
|
774
|
-
@operation
|
|
760
|
+
@operation()
|
|
775
761
|
def group(group, present=True, system=False, gid=None):
|
|
776
762
|
"""
|
|
777
763
|
Add/remove system groups.
|
|
@@ -811,7 +797,6 @@ def group(group, present=True, system=False, gid=None):
|
|
|
811
797
|
yield "pw groupdel -n {0}".format(group)
|
|
812
798
|
else:
|
|
813
799
|
yield "groupdel {0}".format(group)
|
|
814
|
-
groups.remove(group)
|
|
815
800
|
|
|
816
801
|
# Group doesn't exist and we want it?
|
|
817
802
|
elif present and not is_present:
|
|
@@ -837,11 +822,10 @@ def group(group, present=True, system=False, gid=None):
|
|
|
837
822
|
group_add_command = "groupadd"
|
|
838
823
|
if os_type == "FreeBSD":
|
|
839
824
|
group_add_command = "pw groupadd"
|
|
840
|
-
yield "
|
|
841
|
-
groups.append(group)
|
|
825
|
+
yield "{0} {1}".format(group_add_command, " ".join(args))
|
|
842
826
|
|
|
843
827
|
|
|
844
|
-
@operation
|
|
828
|
+
@operation()
|
|
845
829
|
def user_authorized_keys(
|
|
846
830
|
user,
|
|
847
831
|
public_keys,
|
|
@@ -860,7 +844,7 @@ def user_authorized_keys(
|
|
|
860
844
|
|
|
861
845
|
Public keys:
|
|
862
846
|
These can be provided as strings containing the public key or as a path to
|
|
863
|
-
a public key file which
|
|
847
|
+
a public key file which pyinfra will read.
|
|
864
848
|
|
|
865
849
|
**Examples:**
|
|
866
850
|
|
|
@@ -872,6 +856,7 @@ def user_authorized_keys(
|
|
|
872
856
|
public_keys=["ed25519..."],
|
|
873
857
|
)
|
|
874
858
|
"""
|
|
859
|
+
|
|
875
860
|
if not authorized_key_directory:
|
|
876
861
|
authorized_key_directory = f"/home/{user}/.ssh/"
|
|
877
862
|
if not authorized_key_filename:
|
|
@@ -889,15 +874,15 @@ def user_authorized_keys(
|
|
|
889
874
|
with open(try_path, "r") as f:
|
|
890
875
|
return f.read().strip()
|
|
891
876
|
|
|
892
|
-
return key
|
|
877
|
+
return key.strip()
|
|
893
878
|
|
|
894
879
|
public_keys = list(map(read_any_pub_key_file, public_keys))
|
|
895
880
|
|
|
896
881
|
# Ensure .ssh directory
|
|
897
882
|
# note that this always outputs commands unless the SSH user has access to the
|
|
898
883
|
# authorized_keys file, ie the SSH user is the user defined in this function
|
|
899
|
-
yield from files.directory(
|
|
900
|
-
authorized_key_directory,
|
|
884
|
+
yield from files.directory._inner(
|
|
885
|
+
path=authorized_key_directory,
|
|
901
886
|
user=user,
|
|
902
887
|
group=group or user,
|
|
903
888
|
mode=700,
|
|
@@ -914,7 +899,7 @@ def user_authorized_keys(
|
|
|
914
899
|
)
|
|
915
900
|
|
|
916
901
|
# And ensure it exists
|
|
917
|
-
yield from files.put(
|
|
902
|
+
yield from files.put._inner(
|
|
918
903
|
src=keys_file,
|
|
919
904
|
dest=authorized_key_file,
|
|
920
905
|
user=user,
|
|
@@ -924,7 +909,7 @@ def user_authorized_keys(
|
|
|
924
909
|
|
|
925
910
|
else:
|
|
926
911
|
# Ensure authorized_keys exists
|
|
927
|
-
yield from files.file(
|
|
912
|
+
yield from files.file._inner(
|
|
928
913
|
path=authorized_key_file,
|
|
929
914
|
user=user,
|
|
930
915
|
group=group or user,
|
|
@@ -933,10 +918,10 @@ def user_authorized_keys(
|
|
|
933
918
|
|
|
934
919
|
# And every public key is present
|
|
935
920
|
for key in public_keys:
|
|
936
|
-
yield from files.line(path=authorized_key_file, line=key, ensure_newline=True)
|
|
921
|
+
yield from files.line._inner(path=authorized_key_file, line=key, ensure_newline=True)
|
|
937
922
|
|
|
938
923
|
|
|
939
|
-
@operation
|
|
924
|
+
@operation()
|
|
940
925
|
def user(
|
|
941
926
|
user,
|
|
942
927
|
present=True,
|
|
@@ -983,7 +968,7 @@ def user(
|
|
|
983
968
|
|
|
984
969
|
Public keys:
|
|
985
970
|
These can be provided as strings containing the public key or as a path to
|
|
986
|
-
a public key file which
|
|
971
|
+
a public key file which pyinfra will read.
|
|
987
972
|
|
|
988
973
|
**Examples:**
|
|
989
974
|
|
|
@@ -1029,7 +1014,6 @@ def user(
|
|
|
1029
1014
|
yield "pw userdel -n {0}".format(user)
|
|
1030
1015
|
else:
|
|
1031
1016
|
yield "userdel {0}".format(user)
|
|
1032
|
-
users.pop(user)
|
|
1033
1017
|
return
|
|
1034
1018
|
|
|
1035
1019
|
# User doesn't exist but we want them?
|
|
@@ -1079,29 +1063,20 @@ def user(
|
|
|
1079
1063
|
|
|
1080
1064
|
# Users are often added by other operations (package installs), so check
|
|
1081
1065
|
# for the user at runtime before adding.
|
|
1082
|
-
|
|
1083
1066
|
add_user_command = "useradd"
|
|
1084
1067
|
if os_type == "FreeBSD":
|
|
1085
1068
|
add_user_command = "pw useradd"
|
|
1086
|
-
yield "
|
|
1069
|
+
yield "{0} -n {2} {1}".format(
|
|
1087
1070
|
add_user_command,
|
|
1088
1071
|
" ".join(args),
|
|
1089
1072
|
user,
|
|
1090
1073
|
)
|
|
1091
1074
|
else:
|
|
1092
|
-
yield "
|
|
1075
|
+
yield "{0} {1} {2}".format(
|
|
1093
1076
|
add_user_command,
|
|
1094
1077
|
" ".join(args),
|
|
1095
1078
|
user,
|
|
1096
1079
|
)
|
|
1097
|
-
users[user] = {
|
|
1098
|
-
"comment": comment,
|
|
1099
|
-
"home": home,
|
|
1100
|
-
"shell": shell,
|
|
1101
|
-
"group": group,
|
|
1102
|
-
"groups": groups,
|
|
1103
|
-
"password": password,
|
|
1104
|
-
}
|
|
1105
1080
|
|
|
1106
1081
|
# User exists and we want them, check home/shell/keys/password
|
|
1107
1082
|
else:
|
|
@@ -1150,8 +1125,8 @@ def user(
|
|
|
1150
1125
|
|
|
1151
1126
|
# Ensure home directory ownership
|
|
1152
1127
|
if ensure_home:
|
|
1153
|
-
yield from files.directory(
|
|
1154
|
-
home,
|
|
1128
|
+
yield from files.directory._inner(
|
|
1129
|
+
path=home,
|
|
1155
1130
|
user=user,
|
|
1156
1131
|
group=group or user,
|
|
1157
1132
|
# Don't fail if the home directory exists as a link
|
|
@@ -1160,9 +1135,9 @@ def user(
|
|
|
1160
1135
|
|
|
1161
1136
|
# Add SSH keys
|
|
1162
1137
|
if public_keys is not None:
|
|
1163
|
-
yield from user_authorized_keys(
|
|
1164
|
-
user,
|
|
1165
|
-
public_keys,
|
|
1138
|
+
yield from user_authorized_keys._inner(
|
|
1139
|
+
user=user,
|
|
1140
|
+
public_keys=public_keys,
|
|
1166
1141
|
group=group,
|
|
1167
1142
|
delete_keys=delete_keys,
|
|
1168
1143
|
authorized_key_directory="{0}/.ssh".format(home),
|
|
@@ -1170,7 +1145,7 @@ def user(
|
|
|
1170
1145
|
)
|
|
1171
1146
|
|
|
1172
1147
|
|
|
1173
|
-
@operation
|
|
1148
|
+
@operation()
|
|
1174
1149
|
def locale(
|
|
1175
1150
|
locale,
|
|
1176
1151
|
present=True,
|
|
@@ -1221,7 +1196,7 @@ def locale(
|
|
|
1221
1196
|
if not present and locale in locales:
|
|
1222
1197
|
logger.debug(f"Removing locale {locale}")
|
|
1223
1198
|
|
|
1224
|
-
yield from files.line(
|
|
1199
|
+
yield from files.line._inner(
|
|
1225
1200
|
path=locales_definitions_file, line=f"^{matching_line}$", replace=f"# {matching_line}"
|
|
1226
1201
|
)
|
|
1227
1202
|
|
|
@@ -1231,7 +1206,7 @@ def locale(
|
|
|
1231
1206
|
if present and locale not in locales:
|
|
1232
1207
|
logger.debug(f"Adding locale {locale}")
|
|
1233
1208
|
|
|
1234
|
-
yield from files.replace(
|
|
1209
|
+
yield from files.replace._inner(
|
|
1235
1210
|
path=locales_definitions_file,
|
|
1236
1211
|
text=f"^{matching_line}$",
|
|
1237
1212
|
replace=f"{matching_line}".replace("# ", ""),
|
|
@@ -1240,7 +1215,7 @@ def locale(
|
|
|
1240
1215
|
yield "locale-gen"
|
|
1241
1216
|
|
|
1242
1217
|
|
|
1243
|
-
@operation
|
|
1218
|
+
@operation()
|
|
1244
1219
|
def security_limit(
|
|
1245
1220
|
domain,
|
|
1246
1221
|
limit_type,
|
|
@@ -1270,7 +1245,7 @@ def security_limit(
|
|
|
1270
1245
|
|
|
1271
1246
|
line_format = f"{domain}\t{limit_type}\t{item}\t{value}"
|
|
1272
1247
|
|
|
1273
|
-
yield from files.line(
|
|
1248
|
+
yield from files.line._inner(
|
|
1274
1249
|
path="/etc/security/limits.conf",
|
|
1275
1250
|
line=f"^{domain}[[:space:]]+{limit_type}[[:space:]]+{item}",
|
|
1276
1251
|
replace=line_format,
|
pyinfra/operations/snap.py
CHANGED
|
@@ -7,7 +7,7 @@ from pyinfra.api import operation
|
|
|
7
7
|
from pyinfra.facts.snap import SnapPackage, SnapPackages
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
@operation
|
|
10
|
+
@operation()
|
|
11
11
|
def package(
|
|
12
12
|
packages=None,
|
|
13
13
|
channel="latest/stable",
|
|
@@ -91,14 +91,12 @@ def package(
|
|
|
91
91
|
else:
|
|
92
92
|
# we don't want it
|
|
93
93
|
remove_packages.append(package)
|
|
94
|
-
snap_packages.remove(package)
|
|
95
94
|
|
|
96
95
|
# it's not installed
|
|
97
96
|
if package not in snap_packages:
|
|
98
97
|
# we want it
|
|
99
98
|
if present:
|
|
100
99
|
install_packages.append(package)
|
|
101
|
-
snap_packages.append(package)
|
|
102
100
|
|
|
103
101
|
# we don't want it
|
|
104
102
|
else:
|
pyinfra/operations/ssh.py
CHANGED
|
@@ -6,7 +6,7 @@ Eg: ``pyinfra -> inventory-host.net <-> another-host.net``
|
|
|
6
6
|
|
|
7
7
|
import shlex
|
|
8
8
|
|
|
9
|
-
from pyinfra import host
|
|
9
|
+
from pyinfra import host
|
|
10
10
|
from pyinfra.api import OperationError, operation
|
|
11
11
|
from pyinfra.facts.files import File, FindInFile
|
|
12
12
|
from pyinfra.facts.server import Home
|
|
@@ -14,7 +14,7 @@ from pyinfra.facts.server import Home
|
|
|
14
14
|
from . import files
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
@operation
|
|
17
|
+
@operation()
|
|
18
18
|
def keyscan(hostname, force=False, port=22):
|
|
19
19
|
"""
|
|
20
20
|
Check/add hosts to the ``~/.ssh/known_hosts`` file.
|
|
@@ -34,7 +34,7 @@ def keyscan(hostname, force=False, port=22):
|
|
|
34
34
|
|
|
35
35
|
homedir = host.get_fact(Home)
|
|
36
36
|
|
|
37
|
-
yield from files.directory(
|
|
37
|
+
yield from files.directory._inner(
|
|
38
38
|
"{0}/.ssh".format(homedir),
|
|
39
39
|
mode=700,
|
|
40
40
|
)
|
|
@@ -45,7 +45,6 @@ def keyscan(hostname, force=False, port=22):
|
|
|
45
45
|
pattern=hostname,
|
|
46
46
|
)
|
|
47
47
|
|
|
48
|
-
did_keyscan = False
|
|
49
48
|
keyscan_command = "ssh-keyscan -p {0} {1} >> {2}/.ssh/known_hosts".format(
|
|
50
49
|
port,
|
|
51
50
|
hostname,
|
|
@@ -54,23 +53,14 @@ def keyscan(hostname, force=False, port=22):
|
|
|
54
53
|
|
|
55
54
|
if not hostname_present:
|
|
56
55
|
yield keyscan_command
|
|
57
|
-
did_keyscan = True
|
|
58
56
|
|
|
59
57
|
elif force:
|
|
60
58
|
yield "ssh-keygen -R {0}".format(hostname)
|
|
61
59
|
yield keyscan_command
|
|
62
|
-
did_keyscan = True
|
|
63
60
|
|
|
64
61
|
else:
|
|
65
62
|
host.noop("host key for {0} already exists".format(hostname))
|
|
66
63
|
|
|
67
|
-
if did_keyscan:
|
|
68
|
-
host.create_fact(
|
|
69
|
-
FindInFile,
|
|
70
|
-
kwargs={"path": "{0}/.ssh/known_hosts".format(homedir), "pattern": hostname},
|
|
71
|
-
data=["{0} unknown unknown".format(hostname)],
|
|
72
|
-
)
|
|
73
|
-
|
|
74
64
|
|
|
75
65
|
@operation(is_idempotent=False)
|
|
76
66
|
def command(hostname, command, user=None, port=22):
|
|
@@ -133,7 +123,7 @@ def upload(
|
|
|
133
123
|
connection_target = "@".join((user, hostname))
|
|
134
124
|
|
|
135
125
|
if ssh_keyscan:
|
|
136
|
-
yield from keyscan(hostname)
|
|
126
|
+
yield from keyscan._inner(hostname)
|
|
137
127
|
|
|
138
128
|
# If we're not using sudo on the remote side, just scp the file over
|
|
139
129
|
if not use_remote_sudo:
|
|
@@ -146,7 +136,7 @@ def upload(
|
|
|
146
136
|
|
|
147
137
|
else:
|
|
148
138
|
# Otherwise - we need a temporary location for the file
|
|
149
|
-
temp_remote_filename =
|
|
139
|
+
temp_remote_filename = host.get_temp_filename()
|
|
150
140
|
|
|
151
141
|
# scp it to the temporary location
|
|
152
142
|
upload_cmd = "scp -P {0} {1} {2}:{3}".format(
|
|
@@ -159,7 +149,7 @@ def upload(
|
|
|
159
149
|
yield upload_cmd
|
|
160
150
|
|
|
161
151
|
# And sudo sudo to move it
|
|
162
|
-
yield from command(
|
|
152
|
+
yield from command._inner(
|
|
163
153
|
hostname=hostname,
|
|
164
154
|
command="sudo mv {0} {1}".format(temp_remote_filename, remote_filename),
|
|
165
155
|
port=port,
|
|
@@ -167,7 +157,7 @@ def upload(
|
|
|
167
157
|
)
|
|
168
158
|
|
|
169
159
|
|
|
170
|
-
@operation
|
|
160
|
+
@operation()
|
|
171
161
|
def download(
|
|
172
162
|
hostname,
|
|
173
163
|
filename,
|
|
@@ -213,7 +203,7 @@ def download(
|
|
|
213
203
|
connection_target = "@".join((user, hostname))
|
|
214
204
|
|
|
215
205
|
if ssh_keyscan:
|
|
216
|
-
yield from keyscan(hostname)
|
|
206
|
+
yield from keyscan._inner(hostname)
|
|
217
207
|
|
|
218
208
|
# Download the file with scp
|
|
219
209
|
yield "scp -P {0} {1}:{2} {3}".format(
|
|
@@ -222,8 +212,3 @@ def download(
|
|
|
222
212
|
filename,
|
|
223
213
|
local_filename,
|
|
224
214
|
)
|
|
225
|
-
host.create_fact(
|
|
226
|
-
File,
|
|
227
|
-
kwargs={"path": local_filename},
|
|
228
|
-
data={"mode": None, "group": None, "user": user, "mtime": None},
|
|
229
|
-
)
|
pyinfra/operations/systemd.py
CHANGED
|
@@ -3,7 +3,7 @@ Manage systemd services.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from pyinfra import host
|
|
6
|
-
from pyinfra.api import operation
|
|
6
|
+
from pyinfra.api import StringCommand, operation
|
|
7
7
|
from pyinfra.facts.systemd import SystemdEnabled, SystemdStatus, _make_systemctl_cmd
|
|
8
8
|
|
|
9
9
|
from .util.service import handle_service_control
|
|
@@ -25,13 +25,13 @@ def daemon_reload(user_mode=False, machine=None, user_name=None):
|
|
|
25
25
|
user_name=user_name,
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
-
yield "
|
|
28
|
+
yield StringCommand(systemctl_cmd, "daemon-reload")
|
|
29
29
|
|
|
30
30
|
|
|
31
|
-
_daemon_reload = daemon_reload # noqa: E305
|
|
31
|
+
_daemon_reload = daemon_reload._inner # noqa: E305
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
@operation
|
|
34
|
+
@operation()
|
|
35
35
|
def service(
|
|
36
36
|
service,
|
|
37
37
|
running=True,
|
|
@@ -117,8 +117,9 @@ def service(
|
|
|
117
117
|
user_mode=user_mode,
|
|
118
118
|
machine=machine,
|
|
119
119
|
user_name=user_name,
|
|
120
|
+
services=[service],
|
|
120
121
|
),
|
|
121
|
-
" ".join([systemctl_cmd, "{1}", "{0}"]),
|
|
122
|
+
" ".join([systemctl_cmd.get_raw_value(), "{1}", "{0}"]),
|
|
122
123
|
running,
|
|
123
124
|
restarted,
|
|
124
125
|
reloaded,
|
|
@@ -131,15 +132,14 @@ def service(
|
|
|
131
132
|
user_mode=user_mode,
|
|
132
133
|
machine=machine,
|
|
133
134
|
user_name=user_name,
|
|
135
|
+
services=[service],
|
|
134
136
|
)
|
|
135
137
|
is_enabled = systemd_enabled.get(service, False)
|
|
136
138
|
|
|
137
139
|
# Isn't enabled and want enabled?
|
|
138
140
|
if not is_enabled and enabled is True:
|
|
139
141
|
yield "{0} enable {1}".format(systemctl_cmd, service)
|
|
140
|
-
systemd_enabled[service] = True
|
|
141
142
|
|
|
142
143
|
# Is enabled and want disabled?
|
|
143
144
|
elif is_enabled and enabled is False:
|
|
144
145
|
yield "{0} disable {1}".format(systemctl_cmd, service)
|
|
145
|
-
systemd_enabled[service] = False
|