Skip to content

Commit d239a8c

Browse files
committed
Support both libssh and paramiko
* Swap to `get_file` and `copy_file` from `Connection` in `network_cli.py` to support both `libssh` and `paramiko` * Restore the `network_cli` check * Add diff mode support * Add check mode support
1 parent e23fbfb commit d239a8c

File tree

2 files changed

+105
-20
lines changed

2 files changed

+105
-20
lines changed

plugins/action/net_put.py

Lines changed: 103 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,55 @@
1010
import os
1111
import tempfile
1212

13+
from ansible.module_utils.common.text.converters import to_bytes, to_text
1314
from ansible.plugins.action import ActionBase
15+
from ansible.plugins.loader import lookup_loader
1416
from ansible.utils.display import Display
1517
from ansible.utils.hashing import checksum
1618

1719

1820
display = Display()
1921

22+
# def _get_binary_src_file(self, src):
23+
24+
# if not os.path.exists(source):
25+
# raise ValueError("path specified in src not found")
26+
27+
# return source
28+
29+
# def _handle_src_option(self, convert_data=True):
30+
# else:
31+
# source = self._loader.path_dwim_relative(working_path, "templates", src)
32+
# if not source:
33+
# source = self._loader.path_dwim_relative(working_path, src)
34+
35+
# if not os.path.exists(source):
36+
# raise AnsibleError("path specified in src not found")
37+
38+
# try:
39+
# with open(source, "r") as f:
40+
# template_data = to_text(f.read())
41+
# except IOError as e:
42+
# raise AnsibleError(
43+
# "unable to load src file {0}, I/O error({1}): {2}".format(
44+
# source, e.errno, e.strerror
45+
# )
46+
# )
47+
48+
# # Create a template search path in the following order:
49+
# # [working_path, self_role_path, dependent_role_paths, dirname(source)]
50+
# searchpath = [working_path]
51+
# if self._task._role is not None:
52+
# searchpath.append(self._task._role._role_path)
53+
# if hasattr(self._task, "_block:"):
54+
# dep_chain = self._task._block.get_dep_chain()
55+
# if dep_chain is not None:
56+
# for role in dep_chain:
57+
# searchpath.append(role._role_path)
58+
# searchpath.append(os.path.dirname(source))
59+
# self._templar.environment.loader.searchpath = searchpath
60+
# self._task.args["src"] = self._templar.template(template_data)
61+
2062

2163
class ActionModule(ActionBase):
2264

@@ -28,6 +70,20 @@ def __init__(self, *args, **kwargs):
2870
def run(self, tmp=None, task_vars=None):
2971
result = super(ActionModule, self).run(task_vars=task_vars)
3072

73+
if "changed" not in result:
74+
result["changed"] = False
75+
76+
persistent_connection = self._play_context.connection.split(".")[-1]
77+
if persistent_connection != "network_cli":
78+
# It is supported only with network_cli
79+
return {
80+
"failed": True,
81+
"msg": (
82+
"connection type %s is not valid for net_put module, please use fully "
83+
"qualified name of network_cli connection type" % self._play_context.connection
84+
),
85+
}
86+
3187
try:
3288
self._src = self._task.args.get("src")
3389
except KeyError as exc:
@@ -42,21 +98,31 @@ def run(self, tmp=None, task_vars=None):
4298
self._mode = self._task.args.get("mode", "binary")
4399
self._protocol = self._task.args.get("protocol", "scp")
44100

45-
self._src_real_file = self._loader.get_real_file(self._src, decrypt=self._decrypt)
101+
working_path = self._get_working_path()
102+
if os.path.isabs(self._src):
103+
self._src_real_file = self._loader.get_real_file(self._src, decrypt=self._decrypt)
104+
elif self._loader.path_dwim_relative(working_path, "templates", self._src) != "":
105+
self._src_real_file = self._loader.path_dwim_relative(
106+
working_path, "templates", self._src
107+
)
108+
elif self._loader.path_dwim_relative(working_path, self._src):
109+
self._src_real_file = self._loader.path_dwim_relative(working_path, self._src)
46110

47111
try:
48112
fetched_fd, fetched_fp = tempfile.mkstemp(prefix="")
49113
rendered_fd, rendered_fp = tempfile.mkstemp(prefix="")
50114

51-
if self._mode == "binary":
115+
if "binary" == self._mode:
52116
self._rendered_real_file = self._src_real_file
53-
elif self._mode == "text":
117+
elif "text" == self._mode:
54118
self._rendered_real_file = rendered_fp
55-
template_result = self._execute_module(
56-
module_name="ansible.builtin.template",
57-
module_args={"dest": self._rendered_real_file, "src": self._src_real_file},
58-
task_vars=task_vars,
119+
lookup = lookup_loader.get(
120+
"ansible.builtin.template", loader=self._loader, templar=self._templar
59121
)
122+
template_result = lookup.run([self._src], variables=task_vars)
123+
124+
with open(self._rendered_real_file, "wb") as fh:
125+
fh.write(to_bytes(template_result[0]))
60126
self._rendered_checksum = checksum(self._rendered_real_file)
61127

62128
display.vv(
@@ -65,29 +131,46 @@ def run(self, tmp=None, task_vars=None):
65131
)
66132

67133
try:
68-
self._connection._ssh_type_conn.fetch_file(self._dest, fetched_fp, self._protocol)
134+
self._connection.get_file(self._dest, fetched_fp, self._protocol)
69135
except Exception as exc:
136+
error = to_text(exc).lower()
70137
if not (
71-
"Error receiving information about file" in exc.message
72-
and "No such file or directory" in exc.message
138+
"error receiving information about file" in error
139+
or "no such file or directory" in error
73140
):
74141
raise exc
75-
display.vv("The file is not present on the remote device")
76-
finally:
77-
self._connection._ssh_type_conn.reset()
78-
self._dest_checksum = checksum(fetched_fp)
79-
80-
try:
81-
if self._dest_checksum != self._rendered_checksum:
82-
self._connection._ssh_type_conn.put_file(
142+
self._dest_checksum = checksum(fetched_fp)
143+
144+
if self._dest_checksum != self._rendered_checksum:
145+
result["changed"] = True
146+
147+
if self._task._diff:
148+
if "binary" == self._mode:
149+
result["diff"] = {
150+
"before": self._dest_checksum,
151+
"after": self._rendered_checksum,
152+
}
153+
elif "text" == self._mode:
154+
with open(fetched_fp, "r") as fh:
155+
result["diff"] = {
156+
"before": to_text(fh.read()),
157+
"after": template_result[0],
158+
}
159+
160+
if not self._task.check_mode:
161+
self._connection.copy_file(
83162
self._loader.get_real_file(self._rendered_real_file),
84163
self._dest,
85164
self._protocol,
86165
)
87-
finally:
88-
self._connection._ssh_type_conn.reset()
89166

90167
return result
91168
finally:
92169
os.remove(fetched_fp)
93170
os.remove(rendered_fp)
171+
172+
def _get_working_path(self):
173+
cwd = self._loader.get_basedir()
174+
if self._task._role is not None:
175+
cwd = self._task._role._role_path
176+
return cwd

plugins/connection/libssh.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,8 @@ def fetch_file(self, in_path, out_path, proto="sftp"):
630630
scp.get(in_path, out_path)
631631
except LibsshSCPException as exc:
632632
raise AnsibleError("Error transferring file from %s: %s" % (out_path, to_text(exc)))
633+
finally:
634+
self.reset()
633635
else:
634636
raise AnsibleError("Don't know how to transfer file over protocol %s" % proto)
635637

0 commit comments

Comments
 (0)