From 1446115d33eae2e1d9f644d211596e30fcfdbe94 Mon Sep 17 00:00:00 2001 From: Fabio Erculiani Date: Sun, 21 Aug 2011 14:13:56 +0200 Subject: [PATCH] [entropy.transceivers] implement remote copy method for all transceivers (similar to rename()) --- .../plugins/interfaces/file_plugin.py | 18 ++++++++- .../plugins/interfaces/ftp_plugin.py | 38 ++++++++++++++++++- .../plugins/interfaces/ssh_plugin.py | 18 ++++++++- .../entropy/transceivers/uri_handlers/skel.py | 16 +++++++- 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/file_plugin.py b/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/file_plugin.py index 8ba7d1af8..187ae43e0 100644 --- a/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/file_plugin.py +++ b/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/file_plugin.py @@ -91,7 +91,7 @@ class EntropyFileUriHandler(EntropyUriHandler): EntropyUriHandler based FILE (local) transceiver plugin. """ - PLUGIN_API_VERSION = 2 + PLUGIN_API_VERSION = 3 @staticmethod def approve_uri(uri): @@ -170,6 +170,22 @@ class EntropyFileUriHandler(EntropyUriHandler): os.rename(tmp_remote_ptr_new, remote_ptr_new) return True + def copy(self, remote_path_old, remote_path_new): + tmp_remote_path_new = remote_path_new + \ + EntropyUriHandler.TMP_TXC_FILE_EXT + remote_ptr_old = self._setup_remote_path(remote_path_old) + remote_ptr_new = self._setup_remote_path(tmp_remote_path_new) + try: + shutil.copy2(remote_ptr_old, remote_ptr_new) + except (OSError, IOError): + self.delete(tmp_remote_path_new) + return False + # atomic rename + done = self.rename(tmp_remote_path_new, remote_path_new) + if not done: + self.delete(tmp_remote_path_new) + return done + def delete(self, remote_path): remote_str = self._setup_remote_path(remote_path) try: diff --git a/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/ftp_plugin.py b/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/ftp_plugin.py index 36ad4ed79..e5c636614 100644 --- a/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/ftp_plugin.py +++ b/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/ftp_plugin.py @@ -27,7 +27,7 @@ class EntropyFtpUriHandler(EntropyUriHandler): EntropyUriHandler based FTP transceiver plugin. """ - PLUGIN_API_VERSION = 2 + PLUGIN_API_VERSION = 3 _DEFAULT_TIMEOUT = 60 @@ -489,6 +489,42 @@ class EntropyFtpUriHandler(EntropyUriHandler): done = rc.find("250") != -1 return done + def __ftp_copy(self, fromname, toname): + """ + FTP copy is not officially part of FTP RFCs. + But for example, proftpd with mod_copy does support it. + http://www.castaglia.org/proftpd/modules/mod_copy.html + So it's worth a try. + """ + resp = self.__ftpconn.sendcmd('CPFR ' + fromname) + if resp[0] != '3': + raise ftplib.error_reply, resp + return self.__ftpconn.voidcmd('CPTO ' + toname) + + def copy(self, remote_path_old, remote_path_new): + + self.__connect_if_not() + + tmp_suffix = EntropyUriHandler.TMP_TXC_FILE_EXT + old = os.path.join(self.__ftpdir, remote_path_old) + new = os.path.join(self.__ftpdir, remote_path_new) + try: + rc = self.__ftp_copy(old, new + tmp_suffix) + except self.ftplib.Error as err: + # FTP server doesn't support non-standard copy command + return False + + done = rc.find("250") != -1 + if not done: + return False + + # then rename to final name + done = self.rename(remote_path_new + tmp_suffix, remote_path_new) + if not done: + # delete temp file, try at least + self.delete(remote_path_new + tmp_suffix) + return done + def delete(self, remote_path): self.__connect_if_not() diff --git a/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/ssh_plugin.py b/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/ssh_plugin.py index 82a8e6a64..d5e29f65c 100644 --- a/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/ssh_plugin.py +++ b/libraries/entropy/transceivers/uri_handlers/plugins/interfaces/ssh_plugin.py @@ -27,7 +27,7 @@ class EntropySshUriHandler(EntropyUriHandler): EntropyUriHandler based SSH (with pubkey) transceiver plugin. """ - PLUGIN_API_VERSION = 2 + PLUGIN_API_VERSION = 3 _DEFAULT_TIMEOUT = 60 _DEFAULT_PORT = 22 @@ -357,6 +357,22 @@ class EntropySshUriHandler(EntropyUriHandler): args += [remote_str, "mv", remote_ptr_old, remote_ptr_new] return self._exec_cmd(args)[0] == 0 + def copy(self, remote_path_old, remote_path_new): + args, remote_str = self._setup_fs_args() + tmp_remote_path_new = remote_path_new + \ + EntropyUriHandler.TMP_TXC_FILE_EXT + remote_ptr_old = os.path.join(self.__dir, remote_path_old) + remote_ptr_new = os.path.join(self.__dir, tmp_remote_path_new) + args += [remote_str, "cp", "-p", remote_ptr_old, remote_ptr_new] + if self._exec_cmd(args)[0] != 0: + self.delete(tmp_remote_path_new) + return False + # atomic rename + done = self.rename(tmp_remote_path_new, remote_path_new) + if not done: + self.delete(tmp_remote_path_new) + return done + def delete(self, remote_path): args, remote_str = self._setup_fs_args() remote_ptr = os.path.join(self.__dir, remote_path) diff --git a/libraries/entropy/transceivers/uri_handlers/skel.py b/libraries/entropy/transceivers/uri_handlers/skel.py index 338a55069..c5660c82c 100644 --- a/libraries/entropy/transceivers/uri_handlers/skel.py +++ b/libraries/entropy/transceivers/uri_handlers/skel.py @@ -23,7 +23,7 @@ class EntropyUriHandler(TextInterface): "add_uri_handler" is a EntropyTransceiver static method. """ - BASE_PLUGIN_API_VERSION = 2 + BASE_PLUGIN_API_VERSION = 3 TMP_TXC_FILE_EXT = ".tmp-entropy-txc" @@ -237,6 +237,20 @@ class EntropyUriHandler(TextInterface): """ raise NotImplementedError() + def copy(self, remote_path_old, remote_path_new): + """ + Copy URI old to URI new. + + @param remote_path_old: remote path to handle + @type remote_path_old: string + @param remote_path_new: remote path to create + @type remote_path_new: string + @return: execution status, True if done + @rtype: bool + @raise TransceiverConnectionError: if problems happen + """ + raise NotImplementedError() + def delete(self, remote_path): """ Remove the remote path (must be a file).