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.
Files changed (126) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +261 -255
  3. pyinfra/api/arguments_typed.py +77 -0
  4. pyinfra/api/command.py +66 -53
  5. pyinfra/api/config.py +27 -22
  6. pyinfra/api/connect.py +1 -1
  7. pyinfra/api/connectors.py +2 -24
  8. pyinfra/api/deploy.py +21 -52
  9. pyinfra/api/exceptions.py +33 -8
  10. pyinfra/api/facts.py +77 -113
  11. pyinfra/api/host.py +150 -82
  12. pyinfra/api/inventory.py +17 -25
  13. pyinfra/api/operation.py +232 -198
  14. pyinfra/api/operations.py +102 -148
  15. pyinfra/api/state.py +137 -79
  16. pyinfra/api/util.py +55 -70
  17. pyinfra/connectors/base.py +150 -0
  18. pyinfra/connectors/chroot.py +160 -169
  19. pyinfra/connectors/docker.py +227 -237
  20. pyinfra/connectors/dockerssh.py +231 -253
  21. pyinfra/connectors/local.py +195 -207
  22. pyinfra/connectors/ssh.py +528 -615
  23. pyinfra/connectors/ssh_util.py +114 -0
  24. pyinfra/connectors/sshuserclient/client.py +5 -3
  25. pyinfra/connectors/terraform.py +86 -65
  26. pyinfra/connectors/util.py +212 -137
  27. pyinfra/connectors/vagrant.py +55 -48
  28. pyinfra/context.py +3 -2
  29. pyinfra/facts/docker.py +1 -0
  30. pyinfra/facts/files.py +45 -32
  31. pyinfra/facts/git.py +3 -1
  32. pyinfra/facts/gpg.py +1 -1
  33. pyinfra/facts/hardware.py +4 -2
  34. pyinfra/facts/iptables.py +5 -3
  35. pyinfra/facts/mysql.py +1 -0
  36. pyinfra/facts/postgres.py +168 -0
  37. pyinfra/facts/postgresql.py +5 -161
  38. pyinfra/facts/selinux.py +3 -1
  39. pyinfra/facts/server.py +77 -30
  40. pyinfra/facts/systemd.py +29 -12
  41. pyinfra/facts/sysvinit.py +10 -10
  42. pyinfra/facts/util/packaging.py +4 -2
  43. pyinfra/local.py +4 -5
  44. pyinfra/operations/apk.py +3 -3
  45. pyinfra/operations/apt.py +25 -47
  46. pyinfra/operations/brew.py +7 -14
  47. pyinfra/operations/bsdinit.py +4 -4
  48. pyinfra/operations/cargo.py +1 -1
  49. pyinfra/operations/choco.py +1 -1
  50. pyinfra/operations/dnf.py +4 -4
  51. pyinfra/operations/files.py +108 -321
  52. pyinfra/operations/gem.py +1 -1
  53. pyinfra/operations/git.py +6 -37
  54. pyinfra/operations/iptables.py +2 -10
  55. pyinfra/operations/launchd.py +1 -1
  56. pyinfra/operations/lxd.py +1 -9
  57. pyinfra/operations/mysql.py +5 -28
  58. pyinfra/operations/npm.py +1 -1
  59. pyinfra/operations/openrc.py +1 -1
  60. pyinfra/operations/pacman.py +3 -3
  61. pyinfra/operations/pip.py +14 -15
  62. pyinfra/operations/pkg.py +1 -1
  63. pyinfra/operations/pkgin.py +3 -3
  64. pyinfra/operations/postgres.py +347 -0
  65. pyinfra/operations/postgresql.py +17 -380
  66. pyinfra/operations/python.py +2 -17
  67. pyinfra/operations/selinux.py +5 -28
  68. pyinfra/operations/server.py +59 -84
  69. pyinfra/operations/snap.py +1 -3
  70. pyinfra/operations/ssh.py +8 -23
  71. pyinfra/operations/systemd.py +7 -7
  72. pyinfra/operations/sysvinit.py +3 -12
  73. pyinfra/operations/upstart.py +4 -4
  74. pyinfra/operations/util/__init__.py +12 -0
  75. pyinfra/operations/util/files.py +2 -2
  76. pyinfra/operations/util/packaging.py +6 -24
  77. pyinfra/operations/util/service.py +18 -37
  78. pyinfra/operations/vzctl.py +2 -2
  79. pyinfra/operations/xbps.py +3 -3
  80. pyinfra/operations/yum.py +4 -4
  81. pyinfra/operations/zypper.py +4 -4
  82. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/METADATA +19 -22
  83. pyinfra-3.0b1.dist-info/RECORD +163 -0
  84. pyinfra-3.0b1.dist-info/entry_points.txt +11 -0
  85. pyinfra_cli/__main__.py +2 -0
  86. pyinfra_cli/commands.py +7 -2
  87. pyinfra_cli/exceptions.py +83 -42
  88. pyinfra_cli/inventory.py +19 -4
  89. pyinfra_cli/log.py +17 -3
  90. pyinfra_cli/main.py +133 -90
  91. pyinfra_cli/prints.py +93 -129
  92. pyinfra_cli/util.py +60 -29
  93. tests/test_api/test_api.py +2 -0
  94. tests/test_api/test_api_arguments.py +13 -13
  95. tests/test_api/test_api_deploys.py +28 -29
  96. tests/test_api/test_api_facts.py +60 -98
  97. tests/test_api/test_api_operations.py +100 -200
  98. tests/test_cli/test_cli.py +18 -49
  99. tests/test_cli/test_cli_deploy.py +11 -37
  100. tests/test_cli/test_cli_exceptions.py +50 -19
  101. tests/test_cli/util.py +1 -1
  102. tests/test_connectors/test_chroot.py +6 -6
  103. tests/test_connectors/test_docker.py +4 -4
  104. tests/test_connectors/test_dockerssh.py +38 -50
  105. tests/test_connectors/test_local.py +11 -12
  106. tests/test_connectors/test_ssh.py +66 -107
  107. tests/test_connectors/test_terraform.py +9 -15
  108. tests/test_connectors/test_util.py +24 -46
  109. tests/test_connectors/test_vagrant.py +4 -4
  110. pyinfra/api/operation.pyi +0 -117
  111. pyinfra/connectors/ansible.py +0 -171
  112. pyinfra/connectors/mech.py +0 -186
  113. pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
  114. pyinfra/connectors/winrm.py +0 -320
  115. pyinfra/facts/windows.py +0 -366
  116. pyinfra/facts/windows_files.py +0 -90
  117. pyinfra/operations/windows.py +0 -59
  118. pyinfra/operations/windows_files.py +0 -551
  119. pyinfra-2.9.2.dist-info/RECORD +0 -170
  120. pyinfra-2.9.2.dist-info/entry_points.txt +0 -14
  121. tests/test_connectors/test_ansible.py +0 -64
  122. tests/test_connectors/test_mech.py +0 -126
  123. tests/test_connectors/test_winrm.py +0 -76
  124. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/LICENSE.md +0 -0
  125. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/WHEEL +0 -0
  126. {pyinfra-2.9.2.dist-info → pyinfra-3.0b1.dist-info}/top_level.txt +0 -0
@@ -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", success_exit_codes=[0, -1]) # -1 being error/disconnected
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 = state.get_temp_filename()
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 = state.get_temp_filename("{0}{1}".format(src, data))
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 ``pyinfra`` can handle and executes the relevant operation.
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 ``pyinfra`` can handle and executes the relevant operation.
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 = state.get_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 "grep '^{0}:' /etc/group || {2} {1}".format(group, " ".join(args), group_add_command)
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 ``pyinfra`` will read.
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 ``pyinfra`` will read.
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 "grep '^{2}:' /etc/passwd || {0} -n {2} {1}".format(
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 "grep '^{2}:' /etc/passwd || {0} {1} {2}".format(
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,
@@ -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, state
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 = state.get_temp_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
- )
@@ -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 "{0} daemon-reload".format(systemctl_cmd)
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