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
pyinfra/modules/files.py
DELETED
|
@@ -1,711 +0,0 @@
|
|
|
1
|
-
'''
|
|
2
|
-
The files module handles filesystem state, file uploads and template generation.
|
|
3
|
-
'''
|
|
4
|
-
|
|
5
|
-
import posixpath
|
|
6
|
-
import sys
|
|
7
|
-
|
|
8
|
-
from datetime import timedelta
|
|
9
|
-
from fnmatch import fnmatch
|
|
10
|
-
from io import StringIO
|
|
11
|
-
from os import makedirs, path, walk
|
|
12
|
-
|
|
13
|
-
from jinja2 import TemplateSyntaxError, UndefinedError
|
|
14
|
-
|
|
15
|
-
from pyinfra.api import operation, OperationError
|
|
16
|
-
from pyinfra.api.util import get_file_sha1, get_template
|
|
17
|
-
|
|
18
|
-
from .util.files import chmod, chown, ensure_mode_int, sed_replace
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@operation(pipeline_facts={
|
|
22
|
-
'file': 'destination',
|
|
23
|
-
})
|
|
24
|
-
def download(
|
|
25
|
-
state, host, source_url, destination,
|
|
26
|
-
user=None, group=None, mode=None, cache_time=None, force=False,
|
|
27
|
-
):
|
|
28
|
-
'''
|
|
29
|
-
Download files from remote locations.
|
|
30
|
-
|
|
31
|
-
+ source_url: source URl of the file
|
|
32
|
-
+ destination: where to save the file
|
|
33
|
-
+ user: user to own the files
|
|
34
|
-
+ group: group to own the files
|
|
35
|
-
+ mode: permissions of the files
|
|
36
|
-
+ cache_time: if the file exists already, re-download after this time (in s)
|
|
37
|
-
+ force: always download the file, even if it already exists
|
|
38
|
-
'''
|
|
39
|
-
|
|
40
|
-
# Get destination info
|
|
41
|
-
info = host.fact.file(destination)
|
|
42
|
-
|
|
43
|
-
# Destination is a directory?
|
|
44
|
-
if info is False:
|
|
45
|
-
raise OperationError(
|
|
46
|
-
'Destination {0} already exists and is not a file'.format(destination),
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
# Do we download the file? Force by default
|
|
50
|
-
download = force
|
|
51
|
-
|
|
52
|
-
# Doesn't exist, lets download it
|
|
53
|
-
if info is None:
|
|
54
|
-
download = True
|
|
55
|
-
|
|
56
|
-
# Destination file exists & cache_time: check when the file was last modified,
|
|
57
|
-
# download if old
|
|
58
|
-
elif cache_time:
|
|
59
|
-
# Time on files is not tz-aware, and will be the same tz as the server's time,
|
|
60
|
-
# so we can safely remove the tzinfo from host.fact.date before comparison.
|
|
61
|
-
cache_time = host.fact.date.replace(tzinfo=None) - timedelta(seconds=cache_time)
|
|
62
|
-
if info['mtime'] and info['mtime'] > cache_time:
|
|
63
|
-
download = True
|
|
64
|
-
|
|
65
|
-
# If we download, always do user/group/mode as SSH user may be different
|
|
66
|
-
if download:
|
|
67
|
-
yield 'wget -q {0} -O {1}'.format(source_url, destination)
|
|
68
|
-
|
|
69
|
-
if user or group:
|
|
70
|
-
yield chown(destination, user, group)
|
|
71
|
-
|
|
72
|
-
if mode:
|
|
73
|
-
yield chmod(destination, mode)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
@operation
|
|
77
|
-
def line(state, host, name, line, present=True, replace=None, flags=None):
|
|
78
|
-
'''
|
|
79
|
-
Ensure lines in files using grep to locate and sed to replace.
|
|
80
|
-
|
|
81
|
-
+ name: target remote file to edit
|
|
82
|
-
+ line: string or regex matching the target line
|
|
83
|
-
+ present: whether the line should be in the file
|
|
84
|
-
+ replace: text to replace entire matching lines when ``present=True``
|
|
85
|
-
+ flags: list of flags to pass to sed when replacing/deleting
|
|
86
|
-
|
|
87
|
-
Regex line matching:
|
|
88
|
-
Unless line matches a line (starts with ^, ends $), pyinfra will wrap it such that
|
|
89
|
-
it does, like: ``^.*LINE.*$``. This means we don't swap parts of lines out. To
|
|
90
|
-
change bits of lines, see ``files.replace``.
|
|
91
|
-
|
|
92
|
-
Regex line escaping:
|
|
93
|
-
If matching special characters (eg a crontab line containing *), remember to escape
|
|
94
|
-
it first using Python's ``re.escape``.
|
|
95
|
-
'''
|
|
96
|
-
|
|
97
|
-
match_line = line
|
|
98
|
-
|
|
99
|
-
# Ensure we're matching a whole ^line$
|
|
100
|
-
if not match_line.startswith('^'):
|
|
101
|
-
match_line = '^.*{0}'.format(match_line)
|
|
102
|
-
|
|
103
|
-
if not match_line.endswith('$'):
|
|
104
|
-
match_line = '{0}.*$'.format(match_line)
|
|
105
|
-
|
|
106
|
-
# Is there a matching line in this file?
|
|
107
|
-
present_lines = host.fact.find_in_file(name, match_line)
|
|
108
|
-
|
|
109
|
-
# If replace present, use that over the matching line
|
|
110
|
-
if replace:
|
|
111
|
-
line = replace
|
|
112
|
-
# We must provide some kind of replace to sed_replace_command below
|
|
113
|
-
else:
|
|
114
|
-
replace = ''
|
|
115
|
-
|
|
116
|
-
# Save commands for re-use in dynamic script when file not present at fact stage
|
|
117
|
-
echo_command = 'echo "{0}" >> {1}'.format(line, name)
|
|
118
|
-
sed_replace_command = sed_replace(
|
|
119
|
-
name, match_line, replace,
|
|
120
|
-
flags=flags,
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
# No line and we want it, append it
|
|
124
|
-
if not present_lines and present:
|
|
125
|
-
# If the file does not exist - it *might* be created, so we handle it
|
|
126
|
-
# dynamically with a little script.
|
|
127
|
-
if present_lines is None:
|
|
128
|
-
yield '''
|
|
129
|
-
# If the file now exists
|
|
130
|
-
if [ -f "{target}" ]; then
|
|
131
|
-
# Grep for the line, sed if matches
|
|
132
|
-
(grep "{match_line}" "{target}" && {sed_replace_command}) || \
|
|
133
|
-
# Else echo
|
|
134
|
-
{echo_command}
|
|
135
|
-
|
|
136
|
-
# No file, just echo
|
|
137
|
-
else
|
|
138
|
-
{echo_command}
|
|
139
|
-
fi
|
|
140
|
-
'''.format(
|
|
141
|
-
target=name,
|
|
142
|
-
match_line=match_line,
|
|
143
|
-
echo_command=echo_command,
|
|
144
|
-
sed_replace_command=sed_replace_command,
|
|
145
|
-
)
|
|
146
|
-
|
|
147
|
-
# Otherwise the file exists and there is no matching line, so append it
|
|
148
|
-
else:
|
|
149
|
-
yield echo_command
|
|
150
|
-
|
|
151
|
-
# Line(s) exists and we want to remove them, replace with nothing
|
|
152
|
-
elif present_lines and not present:
|
|
153
|
-
yield sed_replace(name, match_line, '', flags=flags)
|
|
154
|
-
|
|
155
|
-
# Line(s) exists and we have want to ensure they're correct
|
|
156
|
-
elif present_lines and present:
|
|
157
|
-
# If any of lines are different, sed replace them
|
|
158
|
-
if replace and any(line != replace for line in present_lines):
|
|
159
|
-
yield sed_replace_command
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
@operation
|
|
163
|
-
def replace(state, host, name, match, replace, flags=None):
|
|
164
|
-
'''
|
|
165
|
-
A simple shortcut for replacing text in files with sed.
|
|
166
|
-
|
|
167
|
-
+ name: target remote file to edit
|
|
168
|
-
+ match: text/regex to match for
|
|
169
|
-
+ replace: text to replace with
|
|
170
|
-
+ flags: list of flaggs to pass to sed
|
|
171
|
-
'''
|
|
172
|
-
|
|
173
|
-
yield sed_replace(name, match, replace, flags=flags)
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
@operation(pipeline_facts={
|
|
177
|
-
'find_files': 'destination',
|
|
178
|
-
})
|
|
179
|
-
def sync(
|
|
180
|
-
state, host, source, destination,
|
|
181
|
-
user=None, group=None, mode=None, delete=False,
|
|
182
|
-
exclude=None, exclude_dir=None, add_deploy_dir=True,
|
|
183
|
-
):
|
|
184
|
-
'''
|
|
185
|
-
Syncs a local directory with a remote one, with delete support. Note that delete will
|
|
186
|
-
remove extra files on the remote side, but not extra directories.
|
|
187
|
-
|
|
188
|
-
+ source: local directory to sync
|
|
189
|
-
+ destination: remote directory to sync to
|
|
190
|
-
+ user: user to own the files and directories
|
|
191
|
-
+ group: group to own the files and directories
|
|
192
|
-
+ mode: permissions of the files
|
|
193
|
-
+ delete: delete remote files not present locally
|
|
194
|
-
+ exclude: string or list/tuple of strings to match & exclude files (eg *.pyc)
|
|
195
|
-
+ exclude_dir: string or list/tuple of strings to match & exclude directories (eg node_modules)
|
|
196
|
-
'''
|
|
197
|
-
|
|
198
|
-
# If we don't enforce the source ending with /, remote_dirname below might start with
|
|
199
|
-
# a /, which makes the path.join cut off the destination bit.
|
|
200
|
-
if not source.endswith(path.sep):
|
|
201
|
-
source = '{0}{1}'.format(source, path.sep)
|
|
202
|
-
|
|
203
|
-
# Add deploy directory?
|
|
204
|
-
if add_deploy_dir and state.deploy_dir:
|
|
205
|
-
source = path.join(state.deploy_dir, source)
|
|
206
|
-
|
|
207
|
-
# Ensure the source directory exists
|
|
208
|
-
if not path.isdir(source):
|
|
209
|
-
raise IOError('No such directory: {0}'.format(source))
|
|
210
|
-
|
|
211
|
-
# Ensure exclude is a list/tuple
|
|
212
|
-
if exclude is not None:
|
|
213
|
-
if not isinstance(exclude, (list, tuple)):
|
|
214
|
-
exclude = [exclude]
|
|
215
|
-
|
|
216
|
-
# Ensure exclude_dir is a list/tuple
|
|
217
|
-
if exclude_dir is not None:
|
|
218
|
-
if not isinstance(exclude_dir, (list, tuple)):
|
|
219
|
-
exclude_dir = [exclude_dir]
|
|
220
|
-
|
|
221
|
-
put_files = []
|
|
222
|
-
ensure_dirnames = []
|
|
223
|
-
for dirname, _, filenames in walk(source):
|
|
224
|
-
remote_dirname = dirname.replace(source, '')
|
|
225
|
-
|
|
226
|
-
# Should we exclude this dir?
|
|
227
|
-
if exclude_dir and any(fnmatch(remote_dirname, match) for match in exclude_dir):
|
|
228
|
-
continue
|
|
229
|
-
|
|
230
|
-
if remote_dirname:
|
|
231
|
-
ensure_dirnames.append(remote_dirname)
|
|
232
|
-
|
|
233
|
-
for filename in filenames:
|
|
234
|
-
full_filename = path.join(dirname, filename)
|
|
235
|
-
|
|
236
|
-
# Should we exclude this file?
|
|
237
|
-
if exclude and any(fnmatch(full_filename, match) for match in exclude):
|
|
238
|
-
continue
|
|
239
|
-
|
|
240
|
-
put_files.append((
|
|
241
|
-
# Join local as normal (unix, win)
|
|
242
|
-
full_filename,
|
|
243
|
-
# Join remote as unix like
|
|
244
|
-
'/'.join(
|
|
245
|
-
item for item in
|
|
246
|
-
(destination, remote_dirname, filename)
|
|
247
|
-
if item
|
|
248
|
-
),
|
|
249
|
-
))
|
|
250
|
-
|
|
251
|
-
# Ensure the destination directory
|
|
252
|
-
yield directory(
|
|
253
|
-
state, host, destination,
|
|
254
|
-
user=user, group=group,
|
|
255
|
-
)
|
|
256
|
-
|
|
257
|
-
# Ensure any remote dirnames
|
|
258
|
-
for dirname in ensure_dirnames:
|
|
259
|
-
yield directory(
|
|
260
|
-
state, host,
|
|
261
|
-
'/'.join((destination, dirname)),
|
|
262
|
-
user=user, group=group,
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
# Put each file combination
|
|
266
|
-
for local_filename, remote_filename in put_files:
|
|
267
|
-
yield put(
|
|
268
|
-
state, host,
|
|
269
|
-
local_filename, remote_filename,
|
|
270
|
-
user=user, group=group, mode=mode,
|
|
271
|
-
add_deploy_dir=False,
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
# Delete any extra files
|
|
275
|
-
if delete:
|
|
276
|
-
remote_filenames = set(host.fact.find_files(destination) or [])
|
|
277
|
-
wanted_filenames = set([remote_filename for _, remote_filename in put_files])
|
|
278
|
-
files_to_delete = remote_filenames - wanted_filenames
|
|
279
|
-
for filename in files_to_delete:
|
|
280
|
-
# Should we exclude this file?
|
|
281
|
-
if exclude and any(fnmatch(filename, match) for match in exclude):
|
|
282
|
-
continue
|
|
283
|
-
|
|
284
|
-
yield file(state, host, filename, present=False)
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
def _create_remote_dir(state, host, remote_filename, user, group):
|
|
288
|
-
# Always use POSIX style path as local might be Windows, remote always *nix
|
|
289
|
-
remote_dirname = posixpath.dirname(remote_filename)
|
|
290
|
-
if remote_dirname:
|
|
291
|
-
yield directory(
|
|
292
|
-
state, host, remote_dirname,
|
|
293
|
-
user=user, group=group,
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
@operation(pipeline_facts={
|
|
298
|
-
'file': 'remote_filename',
|
|
299
|
-
'sha1_file': 'remote_filename',
|
|
300
|
-
})
|
|
301
|
-
def get(
|
|
302
|
-
state, host, remote_filename, local_filename,
|
|
303
|
-
add_deploy_dir=True, create_local_dir=False, force=False,
|
|
304
|
-
):
|
|
305
|
-
'''
|
|
306
|
-
Download a file from the remote system.
|
|
307
|
-
|
|
308
|
-
+ remote_filename: the remote filename to download
|
|
309
|
-
+ local_filename: the local filename to download the file to
|
|
310
|
-
+ add_deploy_dir: local_filename is relative to the deploy directory
|
|
311
|
-
+ create_local_dir: create the local directory if it doesn't exist
|
|
312
|
-
+ force: always download the file, even if the local copy matches
|
|
313
|
-
|
|
314
|
-
Note:
|
|
315
|
-
This operation is not suitable for large files as it may involve copying
|
|
316
|
-
the remote file before downloading it.
|
|
317
|
-
'''
|
|
318
|
-
|
|
319
|
-
if add_deploy_dir and state.deploy_dir:
|
|
320
|
-
local_filename = path.join(state.deploy_dir, local_filename)
|
|
321
|
-
|
|
322
|
-
if create_local_dir:
|
|
323
|
-
local_pathname = path.dirname(local_filename)
|
|
324
|
-
if not path.exists(local_pathname):
|
|
325
|
-
makedirs(local_pathname)
|
|
326
|
-
|
|
327
|
-
remote_file = host.fact.file(remote_filename)
|
|
328
|
-
|
|
329
|
-
# No remote file, so assume exists and download it "blind"
|
|
330
|
-
if not remote_file or force:
|
|
331
|
-
yield ('download', remote_filename, local_filename)
|
|
332
|
-
|
|
333
|
-
# Remote file exists - check if it matches our local
|
|
334
|
-
else:
|
|
335
|
-
local_sum = get_file_sha1(local_filename)
|
|
336
|
-
remote_sum = host.fact.sha1_file(remote_filename)
|
|
337
|
-
|
|
338
|
-
# Check sha1sum, upload if needed
|
|
339
|
-
if local_sum != remote_sum:
|
|
340
|
-
yield ('download', remote_filename, local_filename)
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
@operation(pipeline_facts={
|
|
344
|
-
'file': 'remote_filename',
|
|
345
|
-
'sha1_file': 'remote_filename',
|
|
346
|
-
})
|
|
347
|
-
def put(
|
|
348
|
-
state, host, local_filename, remote_filename,
|
|
349
|
-
user=None, group=None, mode=None, add_deploy_dir=True,
|
|
350
|
-
create_remote_dir=False, force=False,
|
|
351
|
-
):
|
|
352
|
-
'''
|
|
353
|
-
Upload a local file to the remote system.
|
|
354
|
-
|
|
355
|
-
+ local_filename: local filename
|
|
356
|
-
+ remote_filename: remote filename
|
|
357
|
-
+ user: user to own the files
|
|
358
|
-
+ group: group to own the files
|
|
359
|
-
+ mode: permissions of the files
|
|
360
|
-
+ add_deploy_dir: local_filename is relative to the deploy directory
|
|
361
|
-
+ create_remote_dir: create the remote directory if it doesn't exist
|
|
362
|
-
+ force: always upload the file, even if the remote copy matches
|
|
363
|
-
|
|
364
|
-
``create_remote_dir``:
|
|
365
|
-
If the remote directory does not exist it will be created using the same
|
|
366
|
-
user & group as passed to ``files.put``. The mode will *not* be copied over,
|
|
367
|
-
if this is required call ``files.directory`` separately.
|
|
368
|
-
|
|
369
|
-
Note:
|
|
370
|
-
This operation is not suitable for large files as it may involve copying
|
|
371
|
-
the file before uploading it.
|
|
372
|
-
'''
|
|
373
|
-
|
|
374
|
-
# Upload IO objects as-is
|
|
375
|
-
if hasattr(local_filename, 'read'):
|
|
376
|
-
local_file = local_filename
|
|
377
|
-
|
|
378
|
-
# Assume string filename
|
|
379
|
-
else:
|
|
380
|
-
# Add deploy directory?
|
|
381
|
-
if add_deploy_dir and state.deploy_dir:
|
|
382
|
-
local_filename = path.join(state.deploy_dir, local_filename)
|
|
383
|
-
|
|
384
|
-
local_file = local_filename
|
|
385
|
-
|
|
386
|
-
if not path.isfile(local_file):
|
|
387
|
-
raise IOError('No such file: {0}'.format(local_file))
|
|
388
|
-
|
|
389
|
-
mode = ensure_mode_int(mode)
|
|
390
|
-
remote_file = host.fact.file(remote_filename)
|
|
391
|
-
|
|
392
|
-
if create_remote_dir:
|
|
393
|
-
yield _create_remote_dir(state, host, remote_filename, user, group)
|
|
394
|
-
|
|
395
|
-
# No remote file, always upload and user/group/mode if supplied
|
|
396
|
-
if not remote_file or force:
|
|
397
|
-
yield ('upload', local_file, remote_filename)
|
|
398
|
-
|
|
399
|
-
if user or group:
|
|
400
|
-
yield chown(remote_filename, user, group)
|
|
401
|
-
|
|
402
|
-
if mode:
|
|
403
|
-
yield chmod(remote_filename, mode)
|
|
404
|
-
|
|
405
|
-
# File exists, check sum and check user/group/mode if supplied
|
|
406
|
-
else:
|
|
407
|
-
local_sum = get_file_sha1(local_filename)
|
|
408
|
-
remote_sum = host.fact.sha1_file(remote_filename)
|
|
409
|
-
|
|
410
|
-
# Check sha1sum, upload if needed
|
|
411
|
-
if local_sum != remote_sum:
|
|
412
|
-
yield ('upload', local_file, remote_filename)
|
|
413
|
-
|
|
414
|
-
if user or group:
|
|
415
|
-
yield chown(remote_filename, user, group)
|
|
416
|
-
|
|
417
|
-
if mode:
|
|
418
|
-
yield chmod(remote_filename, mode)
|
|
419
|
-
|
|
420
|
-
else:
|
|
421
|
-
# Check mode
|
|
422
|
-
if mode and remote_file['mode'] != mode:
|
|
423
|
-
yield chmod(remote_filename, mode)
|
|
424
|
-
|
|
425
|
-
# Check user/group
|
|
426
|
-
if (
|
|
427
|
-
(user and remote_file['user'] != user)
|
|
428
|
-
or (group and remote_file['group'] != group)
|
|
429
|
-
):
|
|
430
|
-
yield chown(remote_filename, user, group)
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
@operation
|
|
434
|
-
def template(
|
|
435
|
-
state, host, template_filename, remote_filename,
|
|
436
|
-
user=None, group=None, mode=None, create_remote_dir=False,
|
|
437
|
-
**data
|
|
438
|
-
):
|
|
439
|
-
'''
|
|
440
|
-
Generate a template and write it to the remote system.
|
|
441
|
-
|
|
442
|
-
+ template_filename: local template filename
|
|
443
|
-
+ remote_filename: remote filename
|
|
444
|
-
+ user: user to own the files
|
|
445
|
-
+ group: group to own the files
|
|
446
|
-
+ mode: permissions of the files
|
|
447
|
-
+ create_remote_dir: create the remote directory if it doesn't exist
|
|
448
|
-
|
|
449
|
-
``create_remote_dir``:
|
|
450
|
-
If the remote directory does not exist it will be created using the same
|
|
451
|
-
user & group as passed to ``files.put``. The mode will *not* be copied over,
|
|
452
|
-
if this is required call ``files.directory`` separately.
|
|
453
|
-
'''
|
|
454
|
-
|
|
455
|
-
if state.deploy_dir:
|
|
456
|
-
template_filename = path.join(state.deploy_dir, template_filename)
|
|
457
|
-
|
|
458
|
-
# Ensure host is always available inside templates
|
|
459
|
-
data['host'] = host
|
|
460
|
-
data['inventory'] = state.inventory
|
|
461
|
-
|
|
462
|
-
# Render and make file-like it's output
|
|
463
|
-
try:
|
|
464
|
-
output = get_template(template_filename).render(data)
|
|
465
|
-
except (TemplateSyntaxError, UndefinedError) as e:
|
|
466
|
-
_, _, trace = sys.exc_info()
|
|
467
|
-
|
|
468
|
-
# Jump through to the *second last* traceback, which contains the line number
|
|
469
|
-
# of the error within the in-memory Template object
|
|
470
|
-
while trace.tb_next:
|
|
471
|
-
if trace.tb_next.tb_next:
|
|
472
|
-
trace = trace.tb_next
|
|
473
|
-
else: # pragma: no cover
|
|
474
|
-
break
|
|
475
|
-
|
|
476
|
-
line_number = trace.tb_frame.f_lineno
|
|
477
|
-
|
|
478
|
-
# Quickly read the line in question and one above/below for nicer debugging
|
|
479
|
-
with open(template_filename, 'r') as f:
|
|
480
|
-
template_lines = f.readlines()
|
|
481
|
-
|
|
482
|
-
template_lines = [line.strip() for line in template_lines]
|
|
483
|
-
relevant_lines = template_lines[max(line_number - 2, 0):line_number + 1]
|
|
484
|
-
|
|
485
|
-
raise OperationError('Error in template: {0} (L{1}): {2}\n...\n{3}\n...'.format(
|
|
486
|
-
template_filename, line_number, e, '\n'.join(relevant_lines),
|
|
487
|
-
))
|
|
488
|
-
|
|
489
|
-
output_file = StringIO(output)
|
|
490
|
-
# Set the template attribute for nicer debugging
|
|
491
|
-
output_file.template = template_filename
|
|
492
|
-
|
|
493
|
-
# Pass to the put function
|
|
494
|
-
yield put(
|
|
495
|
-
state, host,
|
|
496
|
-
output_file, remote_filename,
|
|
497
|
-
user=user, group=group, mode=mode,
|
|
498
|
-
add_deploy_dir=False,
|
|
499
|
-
create_remote_dir=create_remote_dir,
|
|
500
|
-
)
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
@operation(pipeline_facts={
|
|
504
|
-
'link': 'name',
|
|
505
|
-
})
|
|
506
|
-
def link(
|
|
507
|
-
state, host, name,
|
|
508
|
-
target=None, present=True, assume_present=False,
|
|
509
|
-
user=None, group=None, symbolic=True,
|
|
510
|
-
create_remote_dir=False,
|
|
511
|
-
):
|
|
512
|
-
'''
|
|
513
|
-
Add/remove/update links.
|
|
514
|
-
|
|
515
|
-
+ name: the name of the link
|
|
516
|
-
+ target: the file/directory the link points to
|
|
517
|
-
+ present: whether the link should exist
|
|
518
|
-
+ assume_present: whether to assume the link exists
|
|
519
|
-
+ user: user to own the link
|
|
520
|
-
+ group: group to own the link
|
|
521
|
-
+ symbolic: whether to make a symbolic link (vs hard link)
|
|
522
|
-
+ create_remote_dir: create the remote directory if it doesn't exist
|
|
523
|
-
|
|
524
|
-
``create_remote_dir``:
|
|
525
|
-
If the remote directory does not exist it will be created using the same
|
|
526
|
-
user & group as passed to ``files.put``. The mode will *not* be copied over,
|
|
527
|
-
if this is required call ``files.directory`` separately.
|
|
528
|
-
|
|
529
|
-
Source changes:
|
|
530
|
-
If the link exists and points to a different target, pyinfra will remove it and
|
|
531
|
-
recreate a new one pointing to then new target.
|
|
532
|
-
'''
|
|
533
|
-
|
|
534
|
-
if present and not target:
|
|
535
|
-
raise OperationError('If present is True target must be provided')
|
|
536
|
-
|
|
537
|
-
info = host.fact.link(name)
|
|
538
|
-
|
|
539
|
-
# Not a link?
|
|
540
|
-
if info is False:
|
|
541
|
-
raise OperationError('{0} exists and is not a link'.format(name))
|
|
542
|
-
|
|
543
|
-
add_cmd = 'ln{0} {1} {2}'.format(
|
|
544
|
-
' -s' if symbolic else '',
|
|
545
|
-
target, name,
|
|
546
|
-
)
|
|
547
|
-
|
|
548
|
-
remove_cmd = 'rm -f {0}'.format(name)
|
|
549
|
-
|
|
550
|
-
# No link and we want it
|
|
551
|
-
if not assume_present and info is None and present:
|
|
552
|
-
if create_remote_dir:
|
|
553
|
-
yield _create_remote_dir(state, host, name, user, group)
|
|
554
|
-
|
|
555
|
-
yield add_cmd
|
|
556
|
-
if user or group:
|
|
557
|
-
yield chown(name, user, group, dereference=False)
|
|
558
|
-
|
|
559
|
-
# It exists and we don't want it
|
|
560
|
-
elif (assume_present or info) and not present:
|
|
561
|
-
yield remove_cmd
|
|
562
|
-
|
|
563
|
-
# Exists and want to ensure it's state
|
|
564
|
-
elif (assume_present or info) and present:
|
|
565
|
-
# If we have an absolute name - prepend to any non-absolute values from the fact
|
|
566
|
-
# and/or the soruce.
|
|
567
|
-
if path.isabs(name):
|
|
568
|
-
link_dirname = path.dirname(name)
|
|
569
|
-
|
|
570
|
-
if not path.isabs(target):
|
|
571
|
-
target = path.normpath('/'.join((link_dirname, target)))
|
|
572
|
-
|
|
573
|
-
if info and not path.isabs(info['link_target']):
|
|
574
|
-
info['link_target'] = path.normpath(
|
|
575
|
-
'/'.join((link_dirname, info['link_target'])),
|
|
576
|
-
)
|
|
577
|
-
|
|
578
|
-
# If the target is wrong, remove & recreate the link
|
|
579
|
-
if not info or info['link_target'] != target:
|
|
580
|
-
yield remove_cmd
|
|
581
|
-
yield add_cmd
|
|
582
|
-
|
|
583
|
-
# Check user/group
|
|
584
|
-
if (
|
|
585
|
-
(not info and (user or group))
|
|
586
|
-
or (user and info['user'] != user)
|
|
587
|
-
or (group and info['group'] != group)
|
|
588
|
-
):
|
|
589
|
-
yield chown(name, user, group, dereference=False)
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
@operation(pipeline_facts={
|
|
593
|
-
'file': 'name',
|
|
594
|
-
})
|
|
595
|
-
def file(
|
|
596
|
-
state, host, name,
|
|
597
|
-
present=True, assume_present=False,
|
|
598
|
-
user=None, group=None, mode=None, touch=False,
|
|
599
|
-
create_remote_dir=False,
|
|
600
|
-
):
|
|
601
|
-
'''
|
|
602
|
-
Add/remove/update files.
|
|
603
|
-
|
|
604
|
-
+ name: name/path of the remote file
|
|
605
|
-
+ present: whether the file should exist
|
|
606
|
-
+ assume_present: whether to assume the file exists
|
|
607
|
-
+ user: user to own the files
|
|
608
|
-
+ group: group to own the files
|
|
609
|
-
+ mode: permissions of the files as an integer, eg: 755
|
|
610
|
-
+ touch: whether to touch the file
|
|
611
|
-
+ create_remote_dir: create the remote directory if it doesn't exist
|
|
612
|
-
|
|
613
|
-
``create_remote_dir``:
|
|
614
|
-
If the remote directory does not exist it will be created using the same
|
|
615
|
-
user & group as passed to ``files.put``. The mode will *not* be copied over,
|
|
616
|
-
if this is required call ``files.directory`` separately.
|
|
617
|
-
'''
|
|
618
|
-
|
|
619
|
-
mode = ensure_mode_int(mode)
|
|
620
|
-
info = host.fact.file(name)
|
|
621
|
-
|
|
622
|
-
# Not a file?!
|
|
623
|
-
if info is False:
|
|
624
|
-
raise OperationError('{0} exists and is not a file'.format(name))
|
|
625
|
-
|
|
626
|
-
# Doesn't exist & we want it
|
|
627
|
-
if not assume_present and info is None and present:
|
|
628
|
-
if create_remote_dir:
|
|
629
|
-
yield _create_remote_dir(state, host, name, user, group)
|
|
630
|
-
|
|
631
|
-
yield 'touch {0}'.format(name)
|
|
632
|
-
|
|
633
|
-
if mode:
|
|
634
|
-
yield chmod(name, mode)
|
|
635
|
-
if user or group:
|
|
636
|
-
yield chown(name, user, group)
|
|
637
|
-
|
|
638
|
-
# It exists and we don't want it
|
|
639
|
-
elif (assume_present or info) and not present:
|
|
640
|
-
yield 'rm -f {0}'.format(name)
|
|
641
|
-
|
|
642
|
-
# It exists & we want to ensure its state
|
|
643
|
-
elif (assume_present or info) and present:
|
|
644
|
-
if touch:
|
|
645
|
-
yield 'touch {0}'.format(name)
|
|
646
|
-
|
|
647
|
-
# Check mode
|
|
648
|
-
if mode and (not info or info['mode'] != mode):
|
|
649
|
-
yield chmod(name, mode)
|
|
650
|
-
|
|
651
|
-
# Check user/group
|
|
652
|
-
if (
|
|
653
|
-
(not info and (user or group))
|
|
654
|
-
or (user and info['user'] != user)
|
|
655
|
-
or (group and info['group'] != group)
|
|
656
|
-
):
|
|
657
|
-
yield chown(name, user, group)
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
@operation(pipeline_facts={
|
|
661
|
-
'directory': 'name',
|
|
662
|
-
})
|
|
663
|
-
def directory(
|
|
664
|
-
state, host, name,
|
|
665
|
-
present=True, assume_present=False,
|
|
666
|
-
user=None, group=None, mode=None, recursive=False,
|
|
667
|
-
):
|
|
668
|
-
'''
|
|
669
|
-
Add/remove/update directories.
|
|
670
|
-
|
|
671
|
-
+ name: name/patr of the remote folder
|
|
672
|
-
+ present: whether the folder should exist
|
|
673
|
-
+ assume_present: whether to assume the directory exists
|
|
674
|
-
+ user: user to own the folder
|
|
675
|
-
+ group: group to own the folder
|
|
676
|
-
+ mode: permissions of the folder
|
|
677
|
-
+ recursive: recursively apply user/group/mode
|
|
678
|
-
'''
|
|
679
|
-
|
|
680
|
-
mode = ensure_mode_int(mode)
|
|
681
|
-
info = host.fact.directory(name)
|
|
682
|
-
|
|
683
|
-
# Not a directory?!
|
|
684
|
-
if info is False:
|
|
685
|
-
raise OperationError('{0} exists and is not a directory'.format(name))
|
|
686
|
-
|
|
687
|
-
# Doesn't exist & we want it
|
|
688
|
-
if not assume_present and info is None and present:
|
|
689
|
-
yield 'mkdir -p {0}'.format(name)
|
|
690
|
-
if mode:
|
|
691
|
-
yield chmod(name, mode, recursive=recursive)
|
|
692
|
-
if user or group:
|
|
693
|
-
yield chown(name, user, group, recursive=recursive)
|
|
694
|
-
|
|
695
|
-
# It exists and we don't want it
|
|
696
|
-
elif (assume_present or info) and not present:
|
|
697
|
-
yield 'rm -rf {0}'.format(name)
|
|
698
|
-
|
|
699
|
-
# It exists & we want to ensure its state
|
|
700
|
-
elif (assume_present or info) and present:
|
|
701
|
-
# Check mode
|
|
702
|
-
if mode and (not info or info['mode'] != mode):
|
|
703
|
-
yield chmod(name, mode, recursive=recursive)
|
|
704
|
-
|
|
705
|
-
# Check user/group
|
|
706
|
-
if (
|
|
707
|
-
(not info and (user or group))
|
|
708
|
-
or (user and info['user'] != user)
|
|
709
|
-
or (group and info['group'] != group)
|
|
710
|
-
):
|
|
711
|
-
yield chown(name, user, group, recursive=recursive)
|