[entropy.transceivers] implement remote copy method for all transceivers (similar to rename())
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user