ansible-core 2.19.0b4__py3-none-any.whl → 2.19.0b6__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.
- ansible/_internal/__init__.py +1 -1
- ansible/_internal/_ansiballz/__init__.py +0 -0
- ansible/_internal/_ansiballz/_builder.py +101 -0
- ansible/_internal/{_ansiballz.py → _ansiballz/_wrapper.py} +11 -11
- ansible/_internal/_collection_proxy.py +1 -1
- ansible/_internal/_errors/_alarm_timeout.py +66 -0
- ansible/_internal/_errors/_captured.py +25 -30
- ansible/_internal/_errors/_error_factory.py +89 -0
- ansible/_internal/_errors/_error_utils.py +240 -0
- ansible/_internal/_errors/_task_timeout.py +28 -0
- ansible/_internal/_event_formatting.py +127 -0
- ansible/_internal/_json/__init__.py +5 -5
- ansible/_internal/_json/_profiles/_cache_persistence.py +2 -0
- ansible/_internal/_json/_profiles/_inventory_legacy.py +1 -1
- ansible/_internal/_json/_profiles/_legacy.py +3 -11
- ansible/_internal/_ssh/__init__.py +0 -0
- ansible/_internal/_ssh/_agent_launch.py +91 -0
- ansible/{utils → _internal/_ssh}/_ssh_agent.py +55 -93
- ansible/_internal/_templating/__init__.py +5 -3
- ansible/_internal/_templating/_datatag.py +2 -1
- ansible/_internal/_templating/_engine.py +3 -4
- ansible/_internal/_templating/_jinja_bits.py +28 -20
- ansible/_internal/_templating/_jinja_common.py +18 -27
- ansible/_internal/_templating/_jinja_plugins.py +36 -5
- ansible/_internal/_templating/_lazy_containers.py +5 -5
- ansible/_internal/_templating/_template_vars.py +72 -0
- ansible/_internal/_templating/_transform.py +26 -19
- ansible/_internal/_templating/_utils.py +1 -1
- ansible/_internal/_yaml/_constructor.py +4 -4
- ansible/_internal/_yaml/_dumper.py +26 -18
- ansible/_internal/_yaml/_errors.py +7 -7
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/true_type.py +1 -1
- ansible/_internal/ansible_collections/ansible/_protomatter/plugins/filter/unmask.py +1 -1
- ansible/cli/__init__.py +11 -93
- ansible/cli/arguments/option_helpers.py +3 -4
- ansible/cli/console.py +1 -1
- ansible/cli/doc.py +86 -30
- ansible/cli/inventory.py +5 -7
- ansible/compat/importlib_resources.py +9 -12
- ansible/config/base.yml +46 -0
- ansible/errors/__init__.py +98 -50
- ansible/executor/module_common.py +75 -49
- ansible/executor/powershell/async_watchdog.ps1 +2 -2
- ansible/executor/powershell/async_wrapper.ps1 +3 -3
- ansible/executor/powershell/become_wrapper.ps1 +20 -2
- ansible/executor/powershell/bootstrap_wrapper.ps1 +28 -6
- ansible/executor/powershell/coverage_wrapper.ps1 +15 -6
- ansible/executor/powershell/exec_wrapper.ps1 +219 -6
- ansible/executor/powershell/module_manifest.py +52 -0
- ansible/executor/powershell/module_wrapper.ps1 +47 -21
- ansible/executor/powershell/powershell_expand_user.ps1 +20 -0
- ansible/executor/powershell/powershell_mkdtemp.ps1 +17 -0
- ansible/executor/process/worker.py +40 -115
- ansible/executor/task_executor.py +26 -61
- ansible/executor/task_result.py +2 -4
- ansible/galaxy/api.py +1 -4
- ansible/galaxy/collection/__init__.py +2 -10
- ansible/galaxy/collection/concrete_artifact_manager.py +2 -8
- ansible/galaxy/role.py +2 -2
- ansible/inventory/manager.py +1 -1
- ansible/module_utils/_internal/__init__.py +7 -7
- ansible/module_utils/_internal/_ambient_context.py +3 -3
- ansible/module_utils/_internal/_ansiballz/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/__init__.py +0 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_coverage.py +45 -0
- ansible/module_utils/_internal/_ansiballz/_extensions/_pydevd.py +62 -0
- ansible/module_utils/_internal/{_ansiballz.py → _ansiballz/_loader.py} +13 -39
- ansible/module_utils/_internal/_ansiballz/_respawn.py +32 -0
- ansible/module_utils/_internal/_ansiballz/_respawn_wrapper.py +23 -0
- ansible/module_utils/_internal/_datatag/__init__.py +43 -15
- ansible/module_utils/_internal/_datatag/_tags.py +2 -2
- ansible/module_utils/_internal/_deprecator.py +67 -55
- ansible/module_utils/_internal/_errors.py +88 -17
- ansible/module_utils/_internal/_event_utils.py +61 -0
- ansible/module_utils/_internal/_json/_profiles/__init__.py +22 -4
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_c2m.py +2 -0
- ansible/module_utils/_internal/_json/_profiles/_module_legacy_m2c.py +2 -0
- ansible/module_utils/_internal/_json/_profiles/_tagless.py +3 -1
- ansible/module_utils/{common/messages.py → _internal/_messages.py} +54 -49
- ansible/module_utils/_internal/_patches/_dataclass_annotation_patch.py +1 -3
- ansible/module_utils/_internal/_plugin_info.py +15 -2
- ansible/module_utils/_internal/_stack.py +22 -0
- ansible/module_utils/_internal/_text_utils.py +6 -0
- ansible/module_utils/_internal/_traceback.py +11 -8
- ansible/module_utils/ansible_release.py +1 -1
- ansible/module_utils/basic.py +95 -71
- ansible/module_utils/common/arg_spec.py +2 -2
- ansible/module_utils/common/collections.py +6 -0
- ansible/module_utils/common/json.py +2 -2
- ansible/module_utils/common/respawn.py +4 -41
- ansible/module_utils/common/text/converters.py +3 -3
- ansible/module_utils/common/validation.py +1 -1
- ansible/module_utils/common/warnings.py +80 -23
- ansible/module_utils/common/yaml.py +1 -1
- ansible/module_utils/connection.py +8 -11
- ansible/module_utils/datatag.py +5 -2
- ansible/module_utils/facts/hardware/linux.py +1 -1
- ansible/module_utils/facts/sysctl.py +4 -6
- ansible/module_utils/facts/system/caps.py +2 -2
- ansible/module_utils/facts/system/distribution.py +16 -3
- ansible/module_utils/facts/system/local.py +1 -1
- ansible/module_utils/facts/virtual/linux.py +2 -2
- ansible/module_utils/service.py +3 -10
- ansible/module_utils/urls.py +4 -4
- ansible/modules/apt_repository.py +17 -39
- ansible/modules/assemble.py +2 -2
- ansible/modules/async_status.py +13 -11
- ansible/modules/async_wrapper.py +12 -22
- ansible/modules/command.py +3 -3
- ansible/modules/copy.py +4 -4
- ansible/modules/cron.py +1 -1
- ansible/modules/dnf5.py +14 -22
- ansible/modules/file.py +16 -17
- ansible/modules/find.py +3 -3
- ansible/modules/get_url.py +17 -0
- ansible/modules/git.py +9 -7
- ansible/modules/hostname.py +0 -1
- ansible/modules/known_hosts.py +12 -14
- ansible/modules/package.py +6 -0
- ansible/modules/replace.py +2 -2
- ansible/modules/service.py +3 -9
- ansible/modules/slurp.py +10 -13
- ansible/modules/stat.py +5 -7
- ansible/modules/unarchive.py +6 -6
- ansible/modules/user.py +1 -1
- ansible/modules/wait_for.py +28 -30
- ansible/modules/yum_repository.py +4 -3
- ansible/parsing/ajson.py +3 -5
- ansible/parsing/dataloader.py +6 -6
- ansible/parsing/mod_args.py +1 -1
- ansible/parsing/plugin_docs.py +2 -2
- ansible/parsing/utils/yaml.py +3 -3
- ansible/parsing/vault/__init__.py +10 -14
- ansible/playbook/base.py +7 -2
- ansible/playbook/included_file.py +3 -1
- ansible/playbook/play_context.py +2 -0
- ansible/playbook/playbook_include.py +1 -1
- ansible/playbook/taggable.py +19 -8
- ansible/playbook/task.py +2 -0
- ansible/plugins/__init__.py +0 -25
- ansible/plugins/action/__init__.py +8 -31
- ansible/plugins/action/add_host.py +1 -1
- ansible/plugins/action/assemble.py +8 -16
- ansible/plugins/action/async_status.py +7 -2
- ansible/plugins/action/copy.py +8 -7
- ansible/plugins/action/fetch.py +3 -3
- ansible/plugins/action/gather_facts.py +8 -8
- ansible/plugins/action/package.py +5 -8
- ansible/plugins/action/script.py +8 -15
- ansible/plugins/action/service.py +3 -7
- ansible/plugins/action/template.py +11 -10
- ansible/plugins/action/unarchive.py +5 -15
- ansible/plugins/action/uri.py +9 -20
- ansible/plugins/cache/__init__.py +17 -19
- ansible/plugins/callback/__init__.py +4 -6
- ansible/plugins/callback/junit.py +4 -2
- ansible/plugins/callback/tree.py +5 -5
- ansible/plugins/connection/local.py +6 -6
- ansible/plugins/connection/paramiko_ssh.py +5 -5
- ansible/plugins/connection/ssh.py +25 -15
- ansible/plugins/connection/winrm.py +6 -3
- ansible/plugins/doc_fragments/constructed.py +2 -2
- ansible/plugins/filter/core.py +32 -27
- ansible/plugins/filter/encryption.py +14 -6
- ansible/plugins/inventory/__init__.py +11 -10
- ansible/plugins/inventory/script.py +1 -1
- ansible/plugins/list.py +73 -19
- ansible/plugins/loader.py +7 -7
- ansible/plugins/lookup/csvfile.py +16 -71
- ansible/plugins/lookup/first_found.py +2 -1
- ansible/plugins/lookup/template.py +9 -4
- ansible/plugins/shell/__init__.py +56 -2
- ansible/plugins/shell/powershell.py +67 -9
- ansible/plugins/shell/sh.py +10 -5
- ansible/plugins/strategy/__init__.py +3 -3
- ansible/plugins/test/core.py +22 -16
- ansible/plugins/test/finished.yml +1 -1
- ansible/plugins/test/uri.py +2 -5
- ansible/release.py +1 -1
- ansible/template/__init__.py +38 -54
- ansible/utils/collection_loader/_collection_finder.py +3 -3
- ansible/utils/display.py +124 -138
- ansible/utils/galaxy.py +2 -2
- ansible/utils/hashing.py +6 -8
- ansible/utils/listify.py +6 -4
- ansible/utils/path.py +5 -7
- ansible/utils/py3compat.py +2 -1
- ansible/utils/ssh_functions.py +3 -2
- ansible/utils/unsafe_proxy.py +1 -1
- ansible/vars/hostvars.py +1 -1
- ansible/vars/plugins.py +3 -3
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/METADATA +1 -1
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/RECORD +224 -204
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/WHEEL +1 -1
- ansible_test/_data/completion/docker.txt +3 -3
- ansible_test/_data/completion/remote.txt +1 -0
- ansible_test/_data/requirements/sanity.ansible-doc.txt +1 -1
- ansible_test/_data/requirements/sanity.changelog.txt +2 -2
- ansible_test/_data/requirements/sanity.pep8.txt +1 -1
- ansible_test/_data/requirements/sanity.pylint.txt +4 -4
- ansible_test/_data/requirements/sanity.yamllint.txt +1 -1
- ansible_test/_internal/commands/integration/coverage.py +7 -2
- ansible_test/_internal/host_profiles.py +62 -10
- ansible_test/_internal/provisioning.py +10 -4
- ansible_test/_internal/ssh.py +1 -5
- ansible_test/_internal/thread.py +2 -1
- ansible_test/_internal/timeout.py +1 -1
- ansible_test/_internal/util.py +40 -12
- ansible_test/_util/controller/sanity/pylint/config/ansible-test-target.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/ansible-test.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/code-smell.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/collection.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/config/default.cfg +1 -0
- ansible_test/_util/controller/sanity/pylint/plugins/deprecated_calls.py +61 -7
- ansible_test/_util/target/setup/bootstrap.sh +31 -0
- ansible_test/_util/target/setup/requirements.py +3 -9
- ansible/_internal/_errors/_utils.py +0 -310
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/entry_points.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/COPYING +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/Apache-License.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/BSD-3-Clause.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/MIT-license.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/PSF-license.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/licenses/licenses/simplified_bsd.txt +0 -0
- {ansible_core-2.19.0b4.dist-info → ansible_core-2.19.0b6.dist-info}/top_level.txt +0 -0
ansible/plugins/action/script.py
CHANGED
@@ -21,7 +21,7 @@ import pathlib
|
|
21
21
|
import re
|
22
22
|
import shlex
|
23
23
|
|
24
|
-
from ansible.errors import AnsibleError,
|
24
|
+
from ansible.errors import AnsibleError, AnsibleActionFail, AnsibleActionSkip
|
25
25
|
from ansible.executor.powershell import module_manifest as ps_manifest
|
26
26
|
from ansible.module_utils.common.text.converters import to_bytes, to_native, to_text
|
27
27
|
from ansible.plugins.action import ActionBase
|
@@ -53,7 +53,7 @@ class ActionModule(ActionBase):
|
|
53
53
|
mutually_exclusive=[['_raw_params', 'cmd']],
|
54
54
|
)
|
55
55
|
|
56
|
-
|
56
|
+
super(ActionModule, self).run(tmp, task_vars)
|
57
57
|
del tmp # tmp no longer has any effect
|
58
58
|
|
59
59
|
try:
|
@@ -105,16 +105,11 @@ class ActionModule(ActionBase):
|
|
105
105
|
# check mode is supported if 'creates' or 'removes' are provided
|
106
106
|
# the task has already been skipped if a change would not occur
|
107
107
|
if new_module_args['creates'] or new_module_args['removes']:
|
108
|
-
|
109
|
-
raise _AnsibleActionDone(result=result)
|
108
|
+
return dict(changed=True)
|
110
109
|
# If the script doesn't return changed in the result, it defaults to True,
|
111
110
|
# but since the script may override 'changed', just skip instead of guessing.
|
112
111
|
else:
|
113
|
-
|
114
|
-
raise AnsibleActionSkip('Check mode is not supported for this task.', result=result)
|
115
|
-
|
116
|
-
# now we execute script, always assume changed.
|
117
|
-
result['changed'] = True
|
112
|
+
raise AnsibleActionSkip('Check mode is not supported for this task.', result=dict(changed=False))
|
118
113
|
|
119
114
|
# transfer the file to a remote tmp location
|
120
115
|
tmp_src = self._connection._shell.join_path(self._connection._shell.tmpdir,
|
@@ -168,14 +163,12 @@ class ActionModule(ActionBase):
|
|
168
163
|
# full manual exec of KEEP_REMOTE_FILES
|
169
164
|
script_cmd = self._connection._shell.build_module_command(env_string='', shebang='#!powershell', cmd='')
|
170
165
|
|
171
|
-
|
166
|
+
# now we execute script, always assume changed.
|
167
|
+
result = dict(self._low_level_execute_command(cmd=script_cmd, in_data=exec_data, sudoable=True, chdir=chdir), changed=True)
|
172
168
|
|
173
169
|
if 'rc' in result and result['rc'] != 0:
|
174
|
-
raise AnsibleActionFail('non-zero return code')
|
170
|
+
raise AnsibleActionFail('non-zero return code', result=result)
|
175
171
|
|
176
|
-
|
177
|
-
result.update(e.result)
|
172
|
+
return result
|
178
173
|
finally:
|
179
174
|
self._remove_tmp_path(self._connection._shell.tmpdir)
|
180
|
-
|
181
|
-
return result
|
@@ -16,7 +16,7 @@
|
|
16
16
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
17
17
|
from __future__ import annotations
|
18
18
|
|
19
|
-
from ansible.errors import
|
19
|
+
from ansible.errors import AnsibleActionFail
|
20
20
|
from ansible.executor.module_common import _apply_action_arg_defaults
|
21
21
|
from ansible.plugins.action import ActionBase
|
22
22
|
|
@@ -39,7 +39,7 @@ class ActionModule(ActionBase):
|
|
39
39
|
self._supports_check_mode = True
|
40
40
|
self._supports_async = True
|
41
41
|
|
42
|
-
|
42
|
+
super(ActionModule, self).run(tmp, task_vars)
|
43
43
|
del tmp # tmp no longer has any effect
|
44
44
|
|
45
45
|
module = self._task.args.get('use', 'auto').lower()
|
@@ -84,14 +84,10 @@ class ActionModule(ActionBase):
|
|
84
84
|
module = 'ansible.legacy.' + module
|
85
85
|
|
86
86
|
self._display.vvvv("Running %s" % module)
|
87
|
-
|
87
|
+
return self._execute_module(module_name=module, module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val)
|
88
88
|
else:
|
89
89
|
raise AnsibleActionFail('Could not detect which service manager to use. Try gathering facts or setting the "use" option.')
|
90
90
|
|
91
|
-
except AnsibleAction as e:
|
92
|
-
result.update(e.result)
|
93
91
|
finally:
|
94
92
|
if not self._task.async_val:
|
95
93
|
self._remove_tmp_path(self._connection._shell.tmpdir)
|
96
|
-
|
97
|
-
return result
|
@@ -20,12 +20,13 @@ from jinja2.defaults import (
|
|
20
20
|
|
21
21
|
from ansible import constants as C
|
22
22
|
from ansible.config.manager import ensure_type
|
23
|
-
from ansible.errors import AnsibleError,
|
23
|
+
from ansible.errors import AnsibleError, AnsibleActionFail
|
24
24
|
from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native
|
25
25
|
from ansible.module_utils.parsing.convert_bool import boolean
|
26
26
|
from ansible.module_utils.six import string_types
|
27
27
|
from ansible.plugins.action import ActionBase
|
28
|
-
from ansible.template import
|
28
|
+
from ansible.template import trust_as_template
|
29
|
+
from ansible._internal._templating import _template_vars
|
29
30
|
|
30
31
|
|
31
32
|
class ActionModule(ActionBase):
|
@@ -39,7 +40,7 @@ class ActionModule(ActionBase):
|
|
39
40
|
if task_vars is None:
|
40
41
|
task_vars = dict()
|
41
42
|
|
42
|
-
|
43
|
+
super(ActionModule, self).run(tmp, task_vars)
|
43
44
|
del tmp # tmp no longer has any effect
|
44
45
|
|
45
46
|
# Options type validation
|
@@ -115,7 +116,12 @@ class ActionModule(ActionBase):
|
|
115
116
|
|
116
117
|
# add ansible 'template' vars
|
117
118
|
temp_vars = task_vars.copy()
|
118
|
-
temp_vars.update(generate_ansible_template_vars(
|
119
|
+
temp_vars.update(_template_vars.generate_ansible_template_vars(
|
120
|
+
path=self._task.args.get('src', None),
|
121
|
+
fullpath=source,
|
122
|
+
dest_path=dest,
|
123
|
+
include_ansible_managed='ansible_managed' not in temp_vars, # do not clobber ansible_managed when set by the user
|
124
|
+
))
|
119
125
|
|
120
126
|
overrides = dict(
|
121
127
|
block_start_string=block_start_string,
|
@@ -167,13 +173,8 @@ class ActionModule(ActionBase):
|
|
167
173
|
loader=self._loader,
|
168
174
|
templar=self._templar,
|
169
175
|
shared_loader_obj=self._shared_loader_obj)
|
170
|
-
|
176
|
+
return copy_action.run(task_vars=task_vars)
|
171
177
|
finally:
|
172
178
|
shutil.rmtree(to_bytes(local_tempdir, errors='surrogate_or_strict'))
|
173
|
-
|
174
|
-
except AnsibleAction as e:
|
175
|
-
result.update(e.result)
|
176
179
|
finally:
|
177
180
|
self._remove_tmp_path(self._connection._shell.tmpdir)
|
178
|
-
|
179
|
-
return result
|
@@ -19,8 +19,7 @@ from __future__ import annotations
|
|
19
19
|
|
20
20
|
import os
|
21
21
|
|
22
|
-
from ansible.errors import
|
23
|
-
from ansible.module_utils.common.text.converters import to_text
|
22
|
+
from ansible.errors import AnsibleActionFail, AnsibleActionSkip
|
24
23
|
from ansible.module_utils.parsing.convert_bool import boolean
|
25
24
|
from ansible.plugins.action import ActionBase
|
26
25
|
|
@@ -34,7 +33,7 @@ class ActionModule(ActionBase):
|
|
34
33
|
if task_vars is None:
|
35
34
|
task_vars = dict()
|
36
35
|
|
37
|
-
|
36
|
+
super(ActionModule, self).run(tmp, task_vars)
|
38
37
|
del tmp # tmp no longer has any effect
|
39
38
|
|
40
39
|
source = self._task.args.get('src', None)
|
@@ -68,15 +67,9 @@ class ActionModule(ActionBase):
|
|
68
67
|
source = os.path.expanduser(source)
|
69
68
|
|
70
69
|
if not remote_src:
|
71
|
-
|
72
|
-
source = self._loader.get_real_file(self._find_needle('files', source), decrypt=decrypt)
|
73
|
-
except AnsibleError as e:
|
74
|
-
raise AnsibleActionFail(to_text(e))
|
70
|
+
source = self._loader.get_real_file(self._find_needle('files', source), decrypt=decrypt)
|
75
71
|
|
76
|
-
|
77
|
-
remote_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=True)
|
78
|
-
except AnsibleError as e:
|
79
|
-
raise AnsibleActionFail(to_text(e))
|
72
|
+
remote_stat = self._execute_remote_stat(dest, all_vars=task_vars, follow=True)
|
80
73
|
|
81
74
|
if not remote_stat['exists'] or not remote_stat['isdir']:
|
82
75
|
raise AnsibleActionFail("dest '%s' must be an existing dir" % dest)
|
@@ -102,9 +95,6 @@ class ActionModule(ActionBase):
|
|
102
95
|
|
103
96
|
# execute the unarchive module now, with the updated args (using ansible.legacy prefix to eliminate collections
|
104
97
|
# collisions with local override
|
105
|
-
|
106
|
-
except AnsibleAction as e:
|
107
|
-
result.update(e.result)
|
98
|
+
return self._execute_module(module_name='ansible.legacy.unarchive', module_args=new_module_args, task_vars=task_vars)
|
108
99
|
finally:
|
109
100
|
self._remove_tmp_path(self._connection._shell.tmpdir)
|
110
|
-
return result
|
ansible/plugins/action/uri.py
CHANGED
@@ -5,11 +5,10 @@
|
|
5
5
|
|
6
6
|
from __future__ import annotations
|
7
7
|
|
8
|
+
import collections.abc as _c
|
8
9
|
import os
|
9
10
|
|
10
|
-
from ansible.errors import
|
11
|
-
from ansible.module_utils.common.text.converters import to_native
|
12
|
-
from ansible.module_utils.common.collections import Mapping, MutableMapping
|
11
|
+
from ansible.errors import AnsibleActionFail
|
13
12
|
from ansible.module_utils.parsing.convert_bool import boolean
|
14
13
|
from ansible.plugins.action import ActionBase
|
15
14
|
|
@@ -25,7 +24,7 @@ class ActionModule(ActionBase):
|
|
25
24
|
if task_vars is None:
|
26
25
|
task_vars = dict()
|
27
26
|
|
28
|
-
|
27
|
+
super(ActionModule, self).run(tmp, task_vars)
|
29
28
|
del tmp # tmp no longer has any effect
|
30
29
|
|
31
30
|
body_format = self._task.args.get('body_format', 'raw')
|
@@ -38,38 +37,31 @@ class ActionModule(ActionBase):
|
|
38
37
|
# everything is remote, so we just execute the module
|
39
38
|
# without changing any of the module arguments
|
40
39
|
# call with ansible.legacy prefix to prevent collections collisions while allowing local override
|
41
|
-
|
42
|
-
task_vars=task_vars, wrap_async=self._task.async_val))
|
40
|
+
return self._execute_module(module_name='ansible.legacy.uri', task_vars=task_vars, wrap_async=self._task.async_val)
|
43
41
|
|
44
42
|
kwargs = {}
|
45
43
|
|
46
44
|
if src:
|
47
|
-
|
48
|
-
src = self._find_needle('files', src)
|
49
|
-
except AnsibleError as e:
|
50
|
-
raise AnsibleActionFail(to_native(e))
|
45
|
+
src = self._find_needle('files', src)
|
51
46
|
|
52
47
|
tmp_src = self._connection._shell.join_path(self._connection._shell.tmpdir, os.path.basename(src))
|
53
48
|
kwargs['src'] = tmp_src
|
54
49
|
self._transfer_file(src, tmp_src)
|
55
50
|
self._fixup_perms2((self._connection._shell.tmpdir, tmp_src))
|
56
51
|
elif body_format == 'form-multipart':
|
57
|
-
if not isinstance(body, Mapping):
|
52
|
+
if not isinstance(body, _c.Mapping):
|
58
53
|
raise AnsibleActionFail(
|
59
54
|
'body must be mapping, cannot be type %s' % body.__class__.__name__
|
60
55
|
)
|
61
56
|
for field, value in body.items():
|
62
|
-
if not isinstance(value, MutableMapping):
|
57
|
+
if not isinstance(value, _c.MutableMapping):
|
63
58
|
continue
|
64
59
|
content = value.get('content')
|
65
60
|
filename = value.get('filename')
|
66
61
|
if not filename or content:
|
67
62
|
continue
|
68
63
|
|
69
|
-
|
70
|
-
filename = self._find_needle('files', filename)
|
71
|
-
except AnsibleError as e:
|
72
|
-
raise AnsibleActionFail(to_native(e))
|
64
|
+
filename = self._find_needle('files', filename)
|
73
65
|
|
74
66
|
tmp_src = self._connection._shell.join_path(
|
75
67
|
self._connection._shell.tmpdir,
|
@@ -83,10 +75,7 @@ class ActionModule(ActionBase):
|
|
83
75
|
new_module_args = self._task.args | kwargs
|
84
76
|
|
85
77
|
# call with ansible.legacy prefix to prevent collections collisions while allowing local override
|
86
|
-
|
87
|
-
except AnsibleAction as e:
|
88
|
-
result.update(e.result)
|
78
|
+
return self._execute_module('ansible.legacy.uri', module_args=new_module_args, task_vars=task_vars, wrap_async=self._task.async_val)
|
89
79
|
finally:
|
90
80
|
if not self._task.async_val:
|
91
81
|
self._remove_tmp_path(self._connection._shell.tmpdir)
|
92
|
-
return result
|
@@ -18,7 +18,6 @@
|
|
18
18
|
from __future__ import annotations
|
19
19
|
|
20
20
|
import copy
|
21
|
-
import errno
|
22
21
|
import os
|
23
22
|
import tempfile
|
24
23
|
import time
|
@@ -108,8 +107,8 @@ class BaseFileCacheModule(BaseCacheModule):
|
|
108
107
|
if not os.path.exists(self._cache_dir):
|
109
108
|
try:
|
110
109
|
os.makedirs(self._cache_dir)
|
111
|
-
except
|
112
|
-
raise AnsibleError("
|
110
|
+
except OSError as ex:
|
111
|
+
raise AnsibleError(f"Error in {self.plugin_name!r} cache plugin while trying to create cache dir {self._cache_dir!r}.") from ex
|
113
112
|
else:
|
114
113
|
for x in (os.R_OK, os.W_OK, os.X_OK):
|
115
114
|
if not os.access(self._cache_dir, x):
|
@@ -160,13 +159,13 @@ class BaseFileCacheModule(BaseCacheModule):
|
|
160
159
|
try:
|
161
160
|
try:
|
162
161
|
self._dump(value, tmpfile_path)
|
163
|
-
except
|
164
|
-
display.
|
162
|
+
except OSError as ex:
|
163
|
+
display.error_as_warning(f"Error in {self.plugin_name!r} cache plugin while trying to write to {tmpfile_path!r}.", exception=ex)
|
165
164
|
try:
|
166
165
|
os.rename(tmpfile_path, cachefile)
|
167
166
|
os.chmod(cachefile, mode=S_IRWU_RG_RO)
|
168
|
-
except
|
169
|
-
display.
|
167
|
+
except OSError as ex:
|
168
|
+
display.error_as_warning(f"Error in {self.plugin_name!r} cache plugin while trying to move {tmpfile_path!r} to {cachefile!r}.", exception=ex)
|
170
169
|
finally:
|
171
170
|
try:
|
172
171
|
os.unlink(tmpfile_path)
|
@@ -181,12 +180,12 @@ class BaseFileCacheModule(BaseCacheModule):
|
|
181
180
|
cachefile = self._get_cache_file_name(key)
|
182
181
|
try:
|
183
182
|
st = os.stat(cachefile)
|
184
|
-
except
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
183
|
+
except FileNotFoundError:
|
184
|
+
return False
|
185
|
+
except OSError as ex:
|
186
|
+
display.error_as_warning(f"Error in {self.plugin_name!r} cache plugin while trying to stat {cachefile!r}.", exception=ex)
|
187
|
+
|
188
|
+
return False
|
190
189
|
|
191
190
|
if time.time() - st.st_mtime <= self._timeout:
|
192
191
|
return False
|
@@ -223,11 +222,10 @@ class BaseFileCacheModule(BaseCacheModule):
|
|
223
222
|
try:
|
224
223
|
os.stat(cachefile)
|
225
224
|
return True
|
226
|
-
except
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
display.warning("error in '%s' cache plugin while trying to stat %s : %s" % (self.plugin_name, cachefile, to_bytes(e)))
|
225
|
+
except FileNotFoundError:
|
226
|
+
return False
|
227
|
+
except OSError as ex:
|
228
|
+
display.error_as_warning(f"Error in {self.plugin_name!r} cache plugin while trying to stat {cachefile!r}.", exception=ex)
|
231
229
|
|
232
230
|
def delete(self, key):
|
233
231
|
try:
|
@@ -236,7 +234,7 @@ class BaseFileCacheModule(BaseCacheModule):
|
|
236
234
|
pass
|
237
235
|
try:
|
238
236
|
os.remove(self._get_cache_file_name(key))
|
239
|
-
except
|
237
|
+
except OSError:
|
240
238
|
pass # TODO: only pass on non existing?
|
241
239
|
|
242
240
|
def flush(self):
|
@@ -280,8 +280,6 @@ class CallbackBase(AnsiblePlugin):
|
|
280
280
|
# that want to further modify the result, or use custom serialization
|
281
281
|
return abridged_result
|
282
282
|
|
283
|
-
# DTFIX-RELEASE: Switch to stock json/yaml serializers here? We should always have a transformed plain-types result.
|
284
|
-
|
285
283
|
if result_format == 'json':
|
286
284
|
return json.dumps(abridged_result, cls=_fallback_to_str.Encoder, indent=indent, ensure_ascii=False, sort_keys=sort_keys)
|
287
285
|
|
@@ -310,7 +308,7 @@ class CallbackBase(AnsiblePlugin):
|
|
310
308
|
' ' * (indent or 4)
|
311
309
|
)
|
312
310
|
|
313
|
-
#
|
311
|
+
# DTFIX5: add test to exercise this case
|
314
312
|
raise ValueError(f'Unsupported result_format {result_format!r}.')
|
315
313
|
|
316
314
|
def _handle_warnings(self, res: _c.MutableMapping[str, t.Any]) -> None:
|
@@ -318,7 +316,7 @@ class CallbackBase(AnsiblePlugin):
|
|
318
316
|
if res.pop('warnings', None) and self._current_task_result and (warnings := self._current_task_result.warnings):
|
319
317
|
# display warnings from the current task result if `warnings` was not removed from `result` (or made falsey)
|
320
318
|
for warning in warnings:
|
321
|
-
#
|
319
|
+
# DTFIX3: what to do about propagating wrap_text from the original display.warning call?
|
322
320
|
self._display._warning(warning, wrap_text=False)
|
323
321
|
|
324
322
|
if res.pop('deprecations', None) and self._current_task_result and (deprecations := self._current_task_result.deprecations):
|
@@ -333,7 +331,7 @@ class CallbackBase(AnsiblePlugin):
|
|
333
331
|
|
334
332
|
def _handle_warnings_and_exception(self, result: CallbackTaskResult) -> None:
|
335
333
|
"""Standardized handling of warnings/deprecations and exceptions from a task/item result."""
|
336
|
-
#
|
334
|
+
# DTFIX5: make/doc/porting-guide a public version of this method?
|
337
335
|
try:
|
338
336
|
use_stderr = self.get_option('display_failed_stderr')
|
339
337
|
except KeyError:
|
@@ -374,7 +372,7 @@ class CallbackBase(AnsiblePlugin):
|
|
374
372
|
' '
|
375
373
|
)
|
376
374
|
|
377
|
-
#
|
375
|
+
# DTFIX5: add test to exercise this case
|
378
376
|
raise ValueError(f'Unsupported result_format {result_format!r}.')
|
379
377
|
|
380
378
|
def _get_diff(self, difflist):
|
@@ -90,6 +90,8 @@ import typing as t
|
|
90
90
|
|
91
91
|
from ansible import constants
|
92
92
|
from ansible.module_utils.common.text.converters import to_bytes, to_text
|
93
|
+
from ansible.module_utils._internal import _event_utils
|
94
|
+
from ansible._internal import _event_formatting
|
93
95
|
from ansible.playbook.task import Task
|
94
96
|
from ansible.plugins.callback import CallbackBase
|
95
97
|
from ansible.executor.task_result import CallbackTaskResult
|
@@ -248,8 +250,8 @@ class CallbackModule(CallbackBase):
|
|
248
250
|
|
249
251
|
if host_data.status == 'failed':
|
250
252
|
if error_summary := task_result.exception:
|
251
|
-
message = error_summary.
|
252
|
-
output = error_summary.
|
253
|
+
message = _event_utils.format_event_brief_message(error_summary.event)
|
254
|
+
output = _event_formatting.format_event_traceback(error_summary.event)
|
253
255
|
test_case.errors.append(TestError(message=message, output=output))
|
254
256
|
elif 'msg' in res:
|
255
257
|
message = res['msg']
|
ansible/plugins/callback/tree.py
CHANGED
@@ -31,7 +31,7 @@ import os
|
|
31
31
|
|
32
32
|
from ansible.constants import TREE_DIR
|
33
33
|
from ansible.executor.task_result import CallbackTaskResult
|
34
|
-
from ansible.module_utils.common.text.converters import to_bytes
|
34
|
+
from ansible.module_utils.common.text.converters import to_bytes
|
35
35
|
from ansible.plugins.callback import CallbackBase
|
36
36
|
from ansible.utils.path import makedirs_safe, unfrackpath
|
37
37
|
from ansible.module_utils._internal import _deprecator
|
@@ -73,15 +73,15 @@ class CallbackModule(CallbackBase):
|
|
73
73
|
buf = to_bytes(buf)
|
74
74
|
try:
|
75
75
|
makedirs_safe(self.tree)
|
76
|
-
except
|
77
|
-
self._display.
|
76
|
+
except OSError as ex:
|
77
|
+
self._display.error_as_warning(f"Unable to access or create the configured directory {self.tree!r}.", exception=ex)
|
78
78
|
|
79
79
|
try:
|
80
80
|
path = to_bytes(os.path.join(self.tree, hostname))
|
81
81
|
with open(path, 'wb+') as fd:
|
82
82
|
fd.write(buf)
|
83
|
-
except
|
84
|
-
self._display.
|
83
|
+
except OSError as ex:
|
84
|
+
self._display.error_as_warning(f"Unable to write to {hostname!r}'s file.", exception=ex)
|
85
85
|
|
86
86
|
def result_to_tree(self, result: CallbackTaskResult) -> None:
|
87
87
|
self.write_tree_file(result.host.get_name(), self._dump_results(result.result))
|
@@ -101,9 +101,9 @@ class Connection(ConnectionBase):
|
|
101
101
|
display.debug("opening command with Popen()")
|
102
102
|
|
103
103
|
if isinstance(cmd, (text_type, binary_type)):
|
104
|
-
cmd =
|
104
|
+
cmd = to_text(cmd)
|
105
105
|
else:
|
106
|
-
cmd = map(
|
106
|
+
cmd = map(to_text, cmd)
|
107
107
|
|
108
108
|
pty_primary = None
|
109
109
|
stdin = subprocess.PIPE
|
@@ -114,8 +114,8 @@ class Connection(ConnectionBase):
|
|
114
114
|
# privileges or the command otherwise needs a pty.
|
115
115
|
try:
|
116
116
|
pty_primary, stdin = pty.openpty()
|
117
|
-
except
|
118
|
-
display.debug("Unable to open pty:
|
117
|
+
except OSError as ex:
|
118
|
+
display.debug(f"Unable to open pty: {ex}")
|
119
119
|
|
120
120
|
p = subprocess.Popen(
|
121
121
|
cmd,
|
@@ -271,8 +271,8 @@ class Connection(ConnectionBase):
|
|
271
271
|
shutil.copyfile(to_bytes(in_path, errors='surrogate_or_strict'), to_bytes(out_path, errors='surrogate_or_strict'))
|
272
272
|
except shutil.Error:
|
273
273
|
raise AnsibleError("failed to copy: {0} and {1} are the same".format(to_native(in_path), to_native(out_path)))
|
274
|
-
except
|
275
|
-
raise AnsibleError("
|
274
|
+
except OSError as ex:
|
275
|
+
raise AnsibleError(f"Failed to transfer file to {out_path!r}.") from ex
|
276
276
|
|
277
277
|
def fetch_file(self, in_path: str, out_path: str) -> None:
|
278
278
|
""" fetch a file from local to local -- for compatibility """
|
@@ -413,7 +413,7 @@ class Connection(ConnectionBase):
|
|
413
413
|
# TODO: check if we need to look at several possible locations, possible for loop
|
414
414
|
ssh.load_system_host_keys(ssh_known_hosts)
|
415
415
|
break
|
416
|
-
except
|
416
|
+
except OSError:
|
417
417
|
pass # file was not found, but not required to function
|
418
418
|
ssh.load_system_host_keys()
|
419
419
|
|
@@ -567,8 +567,8 @@ class Connection(ConnectionBase):
|
|
567
567
|
|
568
568
|
try:
|
569
569
|
self.sftp.put(to_bytes(in_path, errors='surrogate_or_strict'), to_bytes(out_path, errors='surrogate_or_strict'))
|
570
|
-
except
|
571
|
-
raise AnsibleError("
|
570
|
+
except OSError as ex:
|
571
|
+
raise AnsibleError(f"Failed to transfer file to {out_path!r}.") from ex
|
572
572
|
|
573
573
|
def _connect_sftp(self) -> paramiko.sftp_client.SFTPClient:
|
574
574
|
|
@@ -593,8 +593,8 @@ class Connection(ConnectionBase):
|
|
593
593
|
|
594
594
|
try:
|
595
595
|
self.sftp.get(to_bytes(in_path, errors='surrogate_or_strict'), to_bytes(out_path, errors='surrogate_or_strict'))
|
596
|
-
except
|
597
|
-
raise AnsibleError("
|
596
|
+
except OSError as ex:
|
597
|
+
raise AnsibleError(f"Failed to transfer file from {in_path!r}.") from ex
|
598
598
|
|
599
599
|
def _any_keys_added(self) -> bool:
|
600
600
|
|
@@ -332,7 +332,9 @@ DOCUMENTATION = """
|
|
332
332
|
version_added: '2.7'
|
333
333
|
sftp_batch_mode:
|
334
334
|
default: true
|
335
|
-
description:
|
335
|
+
description:
|
336
|
+
- When set to C(True), sftp will be run in batch mode, allowing detection of transfer errors.
|
337
|
+
- When set to C(False), sftp will not be run in batch mode, preventing detection of transfer errors.
|
336
338
|
env: [{name: ANSIBLE_SFTP_BATCH_MODE}]
|
337
339
|
ini:
|
338
340
|
- {key: sftp_batch_mode, section: ssh_connection}
|
@@ -398,6 +400,17 @@ DOCUMENTATION = """
|
|
398
400
|
- {key: pkcs11_provider, section: ssh_connection}
|
399
401
|
vars:
|
400
402
|
- name: ansible_ssh_pkcs11_provider
|
403
|
+
verbosity:
|
404
|
+
version_added: '2.19'
|
405
|
+
default: 0
|
406
|
+
type: int
|
407
|
+
description:
|
408
|
+
- Requested verbosity level for the SSH CLI.
|
409
|
+
env: [{name: ANSIBLE_SSH_VERBOSITY}]
|
410
|
+
ini:
|
411
|
+
- {key: verbosity, section: ssh_connection}
|
412
|
+
vars:
|
413
|
+
- name: ansible_ssh_verbosity
|
401
414
|
"""
|
402
415
|
|
403
416
|
import collections.abc as c
|
@@ -436,7 +449,7 @@ from ansible.plugins.connection import ConnectionBase, BUFSIZE
|
|
436
449
|
from ansible.plugins.shell.powershell import _replace_stderr_clixml
|
437
450
|
from ansible.utils.display import Display
|
438
451
|
from ansible.utils.path import unfrackpath, makedirs_safe
|
439
|
-
from ansible.
|
452
|
+
from ansible._internal._ssh import _ssh_agent
|
440
453
|
|
441
454
|
try:
|
442
455
|
from cryptography.hazmat.primitives import serialization
|
@@ -755,12 +768,12 @@ class Connection(ConnectionBase):
|
|
755
768
|
key_data = self.get_option('private_key')
|
756
769
|
passphrase = self.get_option('private_key_passphrase')
|
757
770
|
|
758
|
-
private_key, public_key, fingerprint =
|
771
|
+
private_key, public_key, fingerprint = _ssh_agent.key_data_into_crypto_objects(
|
759
772
|
to_bytes(key_data),
|
760
773
|
to_bytes(passphrase) if passphrase else None,
|
761
774
|
)
|
762
775
|
|
763
|
-
with SshAgentClient(auth_sock) as client:
|
776
|
+
with _ssh_agent.SshAgentClient(auth_sock) as client:
|
764
777
|
if public_key not in client:
|
765
778
|
display.vvv(f'SSH: SSH_AGENT adding {fingerprint} to agent', host=self.host)
|
766
779
|
client.add(
|
@@ -855,8 +868,8 @@ class Connection(ConnectionBase):
|
|
855
868
|
self._add_args(b_command, b_args, u'disable batch mode for password auth')
|
856
869
|
b_command += [b'-b', b'-']
|
857
870
|
|
858
|
-
if
|
859
|
-
b_command.append(b'-' + (b'v' *
|
871
|
+
if (verbosity := self.get_option('verbosity')) > 0:
|
872
|
+
b_command.append(b'-' + (b'v' * verbosity))
|
860
873
|
|
861
874
|
# Next, we add ssh_args
|
862
875
|
ssh_args = self.get_option('ssh_args')
|
@@ -879,10 +892,7 @@ class Connection(ConnectionBase):
|
|
879
892
|
try:
|
880
893
|
key = self._populate_agent()
|
881
894
|
except Exception as e:
|
882
|
-
raise AnsibleAuthenticationFailure(
|
883
|
-
'Failed to add configured private key into ssh-agent',
|
884
|
-
orig_exc=e,
|
885
|
-
)
|
895
|
+
raise AnsibleAuthenticationFailure('Failed to add configured private key into ssh-agent.') from e
|
886
896
|
b_args = (b'-o', b'IdentitiesOnly=yes', b'-o', to_bytes(f'IdentityFile="{key}"', errors='surrogate_or_strict'))
|
887
897
|
self._add_args(b_command, b_args, "ANSIBLE_PRIVATE_KEY/private_key set")
|
888
898
|
elif key := self.get_option('private_key_file'):
|
@@ -969,7 +979,7 @@ class Connection(ConnectionBase):
|
|
969
979
|
try:
|
970
980
|
fh.write(to_bytes(in_data))
|
971
981
|
fh.close()
|
972
|
-
except
|
982
|
+
except OSError as ex:
|
973
983
|
# The ssh connection may have already terminated at this point, with a more useful error
|
974
984
|
# Only raise AnsibleConnectionFailure if the ssh process is still alive
|
975
985
|
time.sleep(0.001)
|
@@ -985,7 +995,7 @@ class Connection(ConnectionBase):
|
|
985
995
|
""" Terminate a process, ignoring errors """
|
986
996
|
try:
|
987
997
|
p.terminate()
|
988
|
-
except
|
998
|
+
except OSError:
|
989
999
|
pass
|
990
1000
|
|
991
1001
|
# This is separate from _run() because we need to do the same thing for stdout
|
@@ -1126,7 +1136,7 @@ class Connection(ConnectionBase):
|
|
1126
1136
|
p = subprocess.Popen(cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **popen_kwargs)
|
1127
1137
|
stdin = os.fdopen(master, 'wb', 0)
|
1128
1138
|
os.close(slave)
|
1129
|
-
except
|
1139
|
+
except OSError:
|
1130
1140
|
p = None
|
1131
1141
|
|
1132
1142
|
if not p:
|
@@ -1134,8 +1144,8 @@ class Connection(ConnectionBase):
|
|
1134
1144
|
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
1135
1145
|
stderr=subprocess.PIPE, **popen_kwargs)
|
1136
1146
|
stdin = p.stdin # type: ignore[assignment] # stdin will be set and not None due to the calls above
|
1137
|
-
except
|
1138
|
-
raise AnsibleError('Unable to execute ssh command line on a controller
|
1147
|
+
except OSError as ex:
|
1148
|
+
raise AnsibleError('Unable to execute ssh command line on a controller.') from ex
|
1139
1149
|
|
1140
1150
|
if password_mechanism == 'sshpass' and conn_password:
|
1141
1151
|
os.close(self.sshpass_pipe[0])
|
@@ -723,8 +723,11 @@ class Connection(ConnectionBase):
|
|
723
723
|
super(Connection, self).exec_command(cmd, in_data=in_data, sudoable=sudoable)
|
724
724
|
|
725
725
|
encoded_prefix = self._shell._encode_script('', as_list=False, strict_mode=False, preserve_rc=False)
|
726
|
-
if cmd.startswith(encoded_prefix):
|
727
|
-
# Avoid double encoding the script
|
726
|
+
if cmd.startswith(encoded_prefix) or cmd.startswith("type "):
|
727
|
+
# Avoid double encoding the script, the first means we are already
|
728
|
+
# running the standard PowerShell command, the latter is used for
|
729
|
+
# the no pipeline case where it uses type to pipe the script into
|
730
|
+
# powershell which is known to work without re-encoding as pwsh.
|
728
731
|
cmd_parts = cmd.split(" ")
|
729
732
|
else:
|
730
733
|
cmd_parts = self._shell._encode_script(cmd, as_list=True, strict_mode=False, preserve_rc=False)
|
@@ -825,7 +828,7 @@ class Connection(ConnectionBase):
|
|
825
828
|
stderr = to_text(b_stderr)
|
826
829
|
|
827
830
|
if status_code != 0:
|
828
|
-
raise
|
831
|
+
raise OSError(stderr)
|
829
832
|
if stdout.strip() == '[DIR]':
|
830
833
|
data = None
|
831
834
|
else:
|