pyinfra 2.9.2__py2.py3-none-any.whl → 3.0__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 (156) hide show
  1. pyinfra/api/__init__.py +3 -0
  2. pyinfra/api/arguments.py +265 -253
  3. pyinfra/api/arguments_typed.py +80 -0
  4. pyinfra/api/command.py +68 -53
  5. pyinfra/api/config.py +139 -32
  6. pyinfra/api/connect.py +1 -1
  7. pyinfra/api/connectors.py +7 -26
  8. pyinfra/api/deploy.py +21 -52
  9. pyinfra/api/exceptions.py +33 -8
  10. pyinfra/api/facts.py +102 -137
  11. pyinfra/api/host.py +150 -82
  12. pyinfra/api/inventory.py +21 -25
  13. pyinfra/api/operation.py +240 -198
  14. pyinfra/api/operations.py +102 -148
  15. pyinfra/api/state.py +137 -79
  16. pyinfra/api/util.py +79 -86
  17. pyinfra/connectors/base.py +147 -0
  18. pyinfra/connectors/chroot.py +160 -169
  19. pyinfra/connectors/docker.py +220 -237
  20. pyinfra/connectors/dockerssh.py +231 -253
  21. pyinfra/connectors/local.py +196 -208
  22. pyinfra/connectors/ssh.py +530 -613
  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 +211 -137
  27. pyinfra/connectors/vagrant.py +60 -53
  28. pyinfra/context.py +4 -2
  29. pyinfra/facts/apk.py +2 -0
  30. pyinfra/facts/apt.py +2 -0
  31. pyinfra/facts/brew.py +2 -0
  32. pyinfra/facts/bsdinit.py +2 -0
  33. pyinfra/facts/cargo.py +2 -0
  34. pyinfra/facts/choco.py +2 -0
  35. pyinfra/facts/deb.py +7 -2
  36. pyinfra/facts/dnf.py +2 -0
  37. pyinfra/facts/docker.py +19 -0
  38. pyinfra/facts/files.py +47 -32
  39. pyinfra/facts/gem.py +2 -0
  40. pyinfra/facts/git.py +3 -1
  41. pyinfra/facts/gpg.py +3 -1
  42. pyinfra/facts/hardware.py +34 -24
  43. pyinfra/facts/iptables.py +5 -3
  44. pyinfra/facts/launchd.py +2 -0
  45. pyinfra/facts/lxd.py +2 -0
  46. pyinfra/facts/mysql.py +13 -6
  47. pyinfra/facts/npm.py +1 -0
  48. pyinfra/facts/openrc.py +2 -0
  49. pyinfra/facts/pacman.py +6 -2
  50. pyinfra/facts/pip.py +2 -0
  51. pyinfra/facts/pkg.py +2 -0
  52. pyinfra/facts/pkgin.py +2 -0
  53. pyinfra/facts/postgres.py +168 -0
  54. pyinfra/facts/postgresql.py +6 -160
  55. pyinfra/facts/rpm.py +12 -9
  56. pyinfra/facts/runit.py +68 -0
  57. pyinfra/facts/selinux.py +3 -1
  58. pyinfra/facts/server.py +80 -36
  59. pyinfra/facts/snap.py +2 -0
  60. pyinfra/facts/systemd.py +31 -12
  61. pyinfra/facts/sysvinit.py +10 -10
  62. pyinfra/facts/upstart.py +2 -0
  63. pyinfra/facts/util/packaging.py +7 -4
  64. pyinfra/facts/vzctl.py +2 -0
  65. pyinfra/facts/xbps.py +2 -0
  66. pyinfra/facts/yum.py +2 -0
  67. pyinfra/facts/zypper.py +2 -0
  68. pyinfra/local.py +4 -5
  69. pyinfra/operations/apk.py +6 -4
  70. pyinfra/operations/apt.py +46 -65
  71. pyinfra/operations/brew.py +17 -22
  72. pyinfra/operations/bsdinit.py +9 -7
  73. pyinfra/operations/cargo.py +4 -2
  74. pyinfra/operations/choco.py +4 -2
  75. pyinfra/operations/dnf.py +19 -23
  76. pyinfra/operations/docker.py +339 -0
  77. pyinfra/operations/files.py +188 -386
  78. pyinfra/operations/gem.py +4 -2
  79. pyinfra/operations/git.py +24 -53
  80. pyinfra/operations/iptables.py +29 -35
  81. pyinfra/operations/launchd.py +6 -7
  82. pyinfra/operations/lxd.py +8 -13
  83. pyinfra/operations/mysql.py +62 -81
  84. pyinfra/operations/npm.py +9 -2
  85. pyinfra/operations/openrc.py +6 -4
  86. pyinfra/operations/pacman.py +7 -8
  87. pyinfra/operations/pip.py +25 -24
  88. pyinfra/operations/pkg.py +4 -2
  89. pyinfra/operations/pkgin.py +6 -4
  90. pyinfra/operations/postgres.py +349 -0
  91. pyinfra/operations/postgresql.py +18 -379
  92. pyinfra/operations/puppet.py +3 -1
  93. pyinfra/operations/python.py +8 -19
  94. pyinfra/operations/runit.py +182 -0
  95. pyinfra/operations/selinux.py +47 -44
  96. pyinfra/operations/server.py +111 -127
  97. pyinfra/operations/snap.py +4 -4
  98. pyinfra/operations/ssh.py +20 -33
  99. pyinfra/operations/systemd.py +19 -15
  100. pyinfra/operations/sysvinit.py +9 -16
  101. pyinfra/operations/upstart.py +9 -7
  102. pyinfra/operations/util/__init__.py +12 -0
  103. pyinfra/operations/util/docker.py +177 -0
  104. pyinfra/operations/util/files.py +24 -16
  105. pyinfra/operations/util/packaging.py +55 -57
  106. pyinfra/operations/util/service.py +39 -51
  107. pyinfra/operations/vzctl.py +12 -10
  108. pyinfra/operations/xbps.py +6 -4
  109. pyinfra/operations/yum.py +18 -22
  110. pyinfra/operations/zypper.py +12 -13
  111. pyinfra/version.py +5 -2
  112. {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/METADATA +40 -41
  113. pyinfra-3.0.dist-info/RECORD +167 -0
  114. {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/WHEEL +1 -1
  115. pyinfra-3.0.dist-info/entry_points.txt +11 -0
  116. pyinfra_cli/__main__.py +4 -3
  117. pyinfra_cli/commands.py +7 -2
  118. pyinfra_cli/exceptions.py +78 -42
  119. pyinfra_cli/inventory.py +40 -6
  120. pyinfra_cli/log.py +17 -3
  121. pyinfra_cli/main.py +133 -90
  122. pyinfra_cli/prints.py +95 -127
  123. pyinfra_cli/util.py +62 -29
  124. tests/test_api/test_api.py +2 -0
  125. tests/test_api/test_api_arguments.py +13 -13
  126. tests/test_api/test_api_deploys.py +28 -29
  127. tests/test_api/test_api_facts.py +60 -98
  128. tests/test_api/test_api_operations.py +101 -201
  129. tests/test_cli/test_cli.py +18 -49
  130. tests/test_cli/test_cli_deploy.py +11 -37
  131. tests/test_cli/test_cli_exceptions.py +50 -19
  132. tests/test_cli/util.py +1 -1
  133. tests/test_connectors/test_chroot.py +6 -6
  134. tests/test_connectors/test_docker.py +4 -4
  135. tests/test_connectors/test_dockerssh.py +38 -50
  136. tests/test_connectors/test_local.py +11 -12
  137. tests/test_connectors/test_ssh.py +105 -93
  138. tests/test_connectors/test_terraform.py +9 -15
  139. tests/test_connectors/test_util.py +24 -46
  140. tests/test_connectors/test_vagrant.py +7 -7
  141. pyinfra/api/operation.pyi +0 -117
  142. pyinfra/connectors/ansible.py +0 -171
  143. pyinfra/connectors/mech.py +0 -186
  144. pyinfra/connectors/pyinfrawinrmsession/__init__.py +0 -28
  145. pyinfra/connectors/winrm.py +0 -320
  146. pyinfra/facts/windows.py +0 -366
  147. pyinfra/facts/windows_files.py +0 -90
  148. pyinfra/operations/windows.py +0 -59
  149. pyinfra/operations/windows_files.py +0 -551
  150. pyinfra-2.9.2.dist-info/RECORD +0 -170
  151. pyinfra-2.9.2.dist-info/entry_points.txt +0 -14
  152. tests/test_connectors/test_ansible.py +0 -64
  153. tests/test_connectors/test_mech.py +0 -126
  154. tests/test_connectors/test_winrm.py +0 -76
  155. {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/LICENSE.md +0 -0
  156. {pyinfra-2.9.2.dist-info → pyinfra-3.0.dist-info}/top_level.txt +0 -0
@@ -1,551 +0,0 @@
1
- """
2
- The windows_files module handles windows filesystem state, file uploads and template generation.
3
- """
4
-
5
- import ntpath
6
- import os
7
- from datetime import timedelta
8
-
9
- from pyinfra import host, state
10
- from pyinfra.api import FileUploadCommand, OperationError, OperationTypeError, operation
11
- from pyinfra.api.util import get_file_sha1
12
- from pyinfra.facts.windows import Date
13
- from pyinfra.facts.windows_files import Directory, File, Link, Md5File, Sha1File, Sha256File
14
-
15
- from .util.files import ensure_mode_int
16
-
17
-
18
- @operation(
19
- pipeline_facts={"file": "dest"},
20
- )
21
- def download(
22
- src,
23
- dest,
24
- user=None,
25
- group=None,
26
- mode=None,
27
- cache_time=None,
28
- force=False,
29
- sha256sum=None,
30
- sha1sum=None,
31
- md5sum=None,
32
- ):
33
- """
34
- Download files from remote locations using curl or wget.
35
-
36
- + src: source URL of the file
37
- + dest: where to save the file
38
- + user: user to own the files
39
- + group: group to own the files
40
- + mode: permissions of the files
41
- + cache_time: if the file exists already, re-download after this time (in seconds)
42
- + force: always download the file, even if it already exists
43
- + sha256sum: sha256 hash to checksum the downloaded file against
44
- + sha1sum: sha1 hash to checksum the downloaded file against
45
- + md5sum: md5 hash to checksum the downloaded file against
46
-
47
- **Example:**
48
-
49
- .. code:: python
50
-
51
- windows_files.download(
52
- name="Download the Docker repo file",
53
- src="https://download.docker.com/linux/centos/docker-ce.repo",
54
- dest="C:\\docker",
55
- )
56
- """
57
-
58
- info = host.get_fact(File, path=dest)
59
- # Destination is a directory?
60
- if info is False:
61
- raise OperationError(
62
- "Destination {0} already exists and is not a file".format(dest),
63
- )
64
-
65
- # Do we download the file? Force by default
66
- download = force
67
-
68
- # Doesn't exist, lets download it
69
- if info is None:
70
- download = True
71
-
72
- # Destination file exists & cache_time: check when the file was last modified,
73
- # download if old
74
- else:
75
- if cache_time:
76
- # Time on files is not tz-aware, and will be the same tz as the server's time,
77
- # so we can safely remove the tzinfo from Date before comparison.
78
- cache_time = host.get_fact(Date).replace(tzinfo=None) - timedelta(seconds=cache_time)
79
- if info["mtime"] and info["mtime"] > cache_time:
80
- download = True
81
-
82
- if sha1sum:
83
- if sha1sum != host.get_fact(Sha1File, path=dest):
84
- download = True
85
-
86
- if sha256sum:
87
- if sha256sum != host.get_fact(Sha256File, path=dest):
88
- download = True
89
-
90
- if md5sum:
91
- if md5sum != host.get_fact(Md5File, path=dest):
92
- download = True
93
-
94
- # If we download, always do user/group/mode as SSH user may be different
95
- if download:
96
- yield (
97
- '$ProgressPreference = "SilentlyContinue"; ' "Invoke-WebRequest -Uri {0} -OutFile {1}"
98
- ).format(src, dest)
99
-
100
- # if user or group:
101
- # yield chown(dest, user, group)
102
-
103
- # if mode:
104
- # yield chmod(dest, mode)
105
-
106
- if sha1sum:
107
- yield (
108
- 'if ((Get-FileHash -Algorithm SHA1 "{0}").hash -ne {1}) {{ '
109
- 'Write-Error "SHA1 did not match!" '
110
- "}}"
111
- ).format(dest, sha1sum)
112
-
113
- if sha256sum:
114
- yield (
115
- 'if ((Get-FileHash -Algorithm SHA256 "{0}").hash -ne {1}) {{ '
116
- 'Write-Error "SHA256 did not match!" '
117
- "}}"
118
- ).format(dest, sha256sum)
119
-
120
- if md5sum:
121
- yield (
122
- 'if ((Get-FileHash -Algorithm MD5 "{0}").hash -ne {1}) {{ '
123
- 'Write-Error "MD5 did not match!" '
124
- "}}"
125
- ).format(dest, md5sum)
126
-
127
- else:
128
- host.noop("file {0} has already been downloaded".format(dest))
129
-
130
-
131
- @operation(
132
- pipeline_facts={
133
- "file": "dest",
134
- "sha1_file": "dest",
135
- },
136
- )
137
- def put(
138
- src,
139
- dest,
140
- user=None,
141
- group=None,
142
- mode=None,
143
- add_deploy_dir=True,
144
- create_remote_dir=True,
145
- force=False,
146
- assume_exists=False,
147
- ):
148
- """
149
- Upload a local file to the remote system.
150
-
151
- + src: local filename to upload
152
- + dest: remote filename to upload to
153
- + user: user to own the files
154
- + group: group to own the files
155
- + mode: permissions of the files
156
- + add_deploy_dir: src is relative to the deploy directory
157
- + create_remote_dir: create the remote directory if it doesn't exist
158
- + force: always upload the file, even if the remote copy matches
159
- + assume_exists: whether to assume the local file exists
160
-
161
- ``create_remote_dir``:
162
- If the remote directory does not exist it will be created using the same
163
- user & group as passed to ``files.put``. The mode will *not* be copied over,
164
- if this is required call ``files.directory`` separately.
165
-
166
- Note:
167
- This operation is not suitable for large files as it may involve copying
168
- the file before uploading it.
169
-
170
- **Examples:**
171
-
172
- .. code:: python
173
-
174
- # Note: This requires a 'files/motd' file on the local filesystem
175
- files.put(
176
- name="Update the message of the day file",
177
- src="data/content.json",
178
- dest="C:\\data\\content.json",
179
- )
180
- """
181
-
182
- # Upload IO objects as-is
183
- if hasattr(src, "read"):
184
- local_file = src
185
-
186
- # Assume string filename
187
- else:
188
- # Add deploy directory?
189
- if add_deploy_dir and state.cwd:
190
- src = os.path.join(state.cwd, src)
191
-
192
- local_file = src
193
-
194
- if not assume_exists and not os.path.isfile(local_file):
195
- raise IOError("No such file: {0}".format(local_file))
196
-
197
- mode = ensure_mode_int(mode)
198
- remote_file = host.get_fact(File, path=dest)
199
-
200
- if create_remote_dir:
201
- yield from _create_remote_dir(state, host, dest, user, group)
202
-
203
- # No remote file, always upload and user/group/mode if supplied
204
- if not remote_file or force:
205
- yield FileUploadCommand(
206
- local_file,
207
- dest,
208
- remote_temp_filename=state.get_temp_filename(dest),
209
- )
210
-
211
- # if user or group:
212
- # yield chown(dest, user, group)
213
-
214
- # if mode:
215
- # yield chmod(dest, mode)
216
-
217
- # File exists, check sum and check user/group/mode if supplied
218
- else:
219
- local_sum = get_file_sha1(src)
220
- remote_sum = host.get_fact(Sha1File, path=dest)
221
-
222
- # Check sha1sum, upload if needed
223
- if local_sum != remote_sum:
224
- yield FileUploadCommand(
225
- local_file,
226
- dest,
227
- remote_temp_filename=state.get_temp_filename(dest),
228
- )
229
-
230
- # if user or group:
231
- # yield chown(dest, user, group)
232
-
233
- # if mode:
234
- # yield chmod(dest, mode)
235
-
236
- else:
237
- changed = False
238
-
239
- # Check mode
240
- # if mode and remote_file['mode'] != mode:
241
- # yield chmod(dest, mode)
242
- # changed = True
243
-
244
- # Check user/group
245
- # if (
246
- # (user and remote_file['user'] != user)
247
- # or (group and remote_file['group'] != group)
248
- # ):
249
- # yield chown(dest, user, group)
250
- # changed = True
251
-
252
- if not changed:
253
- host.noop("file {0} is already uploaded".format(dest))
254
-
255
-
256
- @operation(
257
- pipeline_facts={"windows_file": "name"},
258
- )
259
- def file(
260
- path,
261
- present=True,
262
- assume_present=False,
263
- user=None,
264
- group=None,
265
- mode=None,
266
- touch=False,
267
- create_remote_dir=True,
268
- ):
269
- """
270
- Add/remove/update files.
271
-
272
- + path: path of the remote file
273
- + present: whether the file should exist
274
- + assume_present: whether to assume the file exists
275
- + TODO: user: user to own the files
276
- + TODO: group: group to own the files
277
- + TODO: mode: permissions of the files as an integer, eg: 755
278
- + touch: whether to touch the file
279
- + create_remote_dir: create the remote directory if it doesn't exist
280
-
281
- ``create_remote_dir``:
282
- If the remote directory does not exist it will be created using the same
283
- user & group as passed to ``files.put``. The mode will *not* be copied over,
284
- if this is required call ``files.directory`` separately.
285
-
286
- **Example:**
287
-
288
- .. code:: python
289
-
290
- files.file(
291
- name="Create c:\\temp\\hello.txt",
292
- path="c:\\temp\\hello.txt",
293
- touch=True,
294
- )
295
- """
296
-
297
- if not isinstance(path, str):
298
- raise OperationTypeError("Name must be a string")
299
-
300
- # mode = ensure_mode_int(mode)
301
- info = host.get_fact(File, path=path)
302
-
303
- # Not a file?!
304
- if info is False:
305
- raise OperationError("{0} exists and is not a file".format(path))
306
-
307
- # Doesn't exist & we want it
308
- if not assume_present and info is None and present:
309
- if create_remote_dir:
310
- yield from _create_remote_dir(state, host, path, user, group)
311
-
312
- yield "New-Item -ItemType file {0}".format(path)
313
-
314
- # if mode:
315
- # yield chmod(path, mode)
316
- # if user or group:
317
- # yield chown(path, user, group)
318
-
319
- # It exists and we don't want it
320
- elif (assume_present or info) and not present:
321
- yield "Remove-Item {0}".format(path)
322
-
323
-
324
- # # It exists & we want to ensure its state
325
- # elif (assume_present or info) and present:
326
- # if touch:
327
- # yield 'New-Item -ItemType file {0}'.format(path)
328
- #
329
- # # Check mode
330
- # if mode and (not info or info['mode'] != mode):
331
- # yield chmod(path, mode)
332
- #
333
- # # Check user/group
334
- # if (
335
- # (not info and (user or group))
336
- # or (user and info['user'] != user)
337
- # or (group and info['group'] != group)
338
- # ):
339
- # yield chown(path, user, group)
340
-
341
-
342
- def _create_remote_dir(state, host, remote_filename, user, group):
343
- # Always use POSIX style path as local might be Windows, remote always *nix
344
- remote_dirname = ntpath.dirname(remote_filename)
345
- if remote_dirname:
346
- yield from directory(
347
- remote_dirname,
348
- state=state,
349
- host=host,
350
- user=user,
351
- group=group,
352
- )
353
-
354
-
355
- @operation(
356
- pipeline_facts={"windows_directory": "name"},
357
- )
358
- def directory(
359
- path,
360
- present=True,
361
- assume_present=False,
362
- user=None,
363
- group=None,
364
- mode=None,
365
- recursive=False,
366
- ):
367
- """
368
- Add/remove/update directories.
369
-
370
- + path: path of the remote folder
371
- + present: whether the folder should exist
372
- + assume_present: whether to assume the directory exists
373
- + TODO: user: user to own the folder
374
- + TODO: group: group to own the folder
375
- + TODO: mode: permissions of the folder
376
- + TODO: recursive: recursively apply user/group/mode
377
-
378
- **Examples:**
379
-
380
- .. code:: python
381
-
382
- files.directory(
383
- name="Ensure the c:\\temp\\dir_that_we_want_removed is removed",
384
- path="c:\\temp\\dir_that_we_want_removed",
385
- present=False,
386
- )
387
-
388
- files.directory(
389
- name="Ensure c:\\temp\\foo\\foo_dir exists",
390
- path="c:\\temp\\foo\\foo_dir",
391
- recursive=True,
392
- )
393
-
394
- # multiple directories
395
- dirs = ["c:\\temp\\foo_dir1", "c:\\temp\\foo_dir2"]
396
- for dir in dirs:
397
- files.directory(
398
- name="Ensure the directory `{}` exists".format(dir),
399
- path=dir,
400
- )
401
-
402
- """
403
-
404
- if not isinstance(path, str):
405
- raise OperationTypeError("Name must be a string")
406
-
407
- info = host.get_fact(Directory, path=path)
408
-
409
- # Not a directory?!
410
- if info is False:
411
- raise OperationError("{0} exists and is not a directory".format(path))
412
-
413
- # Doesn't exist & we want it
414
- if not assume_present and info is None and present:
415
- yield "New-Item -Path {0} -ItemType Directory".format(path)
416
- # if mode:
417
- # yield chmod(path, mode, recursive=recursive)
418
- # if user or group:
419
- # yield chown(path, user, group, recursive=recursive)
420
- #
421
- # Somewhat bare fact, should flesh out more
422
- host.create_fact(
423
- Date,
424
- kwargs={"path": path},
425
- data={"type": "directory"},
426
- )
427
-
428
- # It exists and we don't want it
429
- elif (assume_present or info) and not present:
430
- # TODO: how to ensure we use 'ps'?
431
- # remove anything in the directory
432
- yield "Get-ChildItem {0} -Recurse | Remove-Item".format(path)
433
- # remove directory
434
- yield "Remove-Item {0}".format(path)
435
-
436
- # It exists & we want to ensure its state
437
-
438
-
439
- # elif (assume_present or info) and present:
440
- # # Check mode
441
- # if mode and (not info or info['mode'] != mode):
442
- # yield chmod(path, mode, recursive=recursive)
443
- #
444
- # # Check user/group
445
- # if (
446
- # (not info and (user or group))
447
- # or (user and info['user'] != user)
448
- # or (group and info['group'] != group)
449
- # ):
450
- # yield chown(path, user, group, recursive=recursive)
451
-
452
-
453
- def _validate_path(path):
454
- try:
455
- path = os.fspath(path)
456
- except TypeError:
457
- raise OperationTypeError("`path` must be a string or `os.PathLike` object")
458
-
459
-
460
- @operation(
461
- pipeline_facts={"link": "path"},
462
- )
463
- def link(
464
- path,
465
- target=None,
466
- present=True,
467
- assume_present=False,
468
- user=None,
469
- group=None,
470
- symbolic=True,
471
- force=True,
472
- create_remote_dir=True,
473
- ):
474
- """
475
- Add/remove/update links.
476
-
477
- + path: the name of the link
478
- + target: the file/directory the link points to
479
- + present: whether the link should exist
480
- + assume_present: whether to assume the link exists
481
- + user: user to own the link
482
- + group: group to own the link
483
- + symbolic: whether to make a symbolic link (vs hard link)
484
- + create_remote_dir: create the remote directory if it doesn't exist
485
-
486
- ``create_remote_dir``:
487
- If the remote directory does not exist it will be created using the same
488
- user & group as passed to ``files.put``. The mode will *not* be copied over,
489
- if this is required call ``files.directory`` separately.
490
-
491
- Source changes:
492
- If the link exists and points to a different target, pyinfra will remove it and
493
- recreate a new one pointing to then new target.
494
-
495
- **Examples:**
496
-
497
- .. code:: python
498
-
499
- # simple example showing how to link to a file
500
- files.link(
501
- name=r"Create link C:\\issue2 that points to C:\\issue",
502
- path=r"C:\\issue2",
503
- target=r"C\\issue",
504
- )
505
- """
506
-
507
- _validate_path(path)
508
-
509
- if present and not target:
510
- raise OperationError("If present is True target must be provided")
511
-
512
- info = host.get_fact(Link, path=path)
513
-
514
- # Not a link?
515
- if info is not None and not info:
516
- raise OperationError("{0} exists and is not a link".format(path))
517
-
518
- add_cmd = "New-Item -ItemType {0} -Path {1} -Target {2} {3}".format(
519
- "SymbolicLink" if symbolic else "HardLink",
520
- path,
521
- target,
522
- "-Force" if force else "",
523
- )
524
-
525
- remove_cmd = "(Get-Item {0}).Delete()".format(path)
526
-
527
- # We will attempt to link regardless of current existence
528
- # since we know by now the path is either a link already
529
- # or does not exist
530
- if (info is None or force) and present:
531
- if create_remote_dir:
532
- yield from _create_remote_dir(state, host, path, user, group)
533
-
534
- yield add_cmd
535
-
536
- # if user or group:
537
- # yield chown(path, user, group, dereference=False)
538
-
539
- # host.create_fact(
540
- # WindowsLink,
541
- # kwargs={'name': path},
542
- # data={'link_target': target, 'group': group, 'user': user},
543
- # )
544
-
545
- # It exists and we don't want it
546
- elif (assume_present or info) and not present:
547
- yield remove_cmd
548
- # host.delete_fact(WindowsLink, kwargs={'name': path})
549
-
550
- else:
551
- host.noop("link {0} already exists and force=False".format(path))