[entropy.transceivers] implement remote copy method for all transceivers (similar to rename())

This commit is contained in:
Fabio Erculiani
2011-08-21 14:13:56 +02:00
parent 9c55829d00
commit 1446115d33
4 changed files with 86 additions and 4 deletions

View File

@@ -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:

View File

@@ -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()

View File

@@ -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)

View File

@@ -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).