Fix SFTP makedirs error with trailing slashes
- Normalize remote paths by removing trailing slashes - Improve error handling for directory creation - Fix OSError handling (errno not reliably set in SFTP) - Prevents duplicate directory creation attempts
This commit is contained in:
parent
b2f7a5edbf
commit
98c96018d6
|
|
@ -297,40 +297,48 @@ class DbBackup(models.Model):
|
|||
|
||||
# Handle authentication
|
||||
if self.sftp_private_key:
|
||||
# Load private key
|
||||
try:
|
||||
if self.sftp_password:
|
||||
pkey = paramiko.RSAKey.from_private_key_file(
|
||||
self.sftp_private_key, password=self.sftp_password
|
||||
)
|
||||
else:
|
||||
pkey = paramiko.RSAKey.from_private_key_file(
|
||||
self.sftp_private_key
|
||||
)
|
||||
connect_params["pkey"] = pkey
|
||||
except paramiko.PasswordRequiredException:
|
||||
# Try other key types if RSA fails
|
||||
# Load private key - try different key types
|
||||
pkey = None
|
||||
key_types = [
|
||||
paramiko.RSAKey,
|
||||
paramiko.Ed25519Key,
|
||||
paramiko.ECDSAKey,
|
||||
]
|
||||
|
||||
# Try DSA if available (removed in paramiko 4.0+)
|
||||
if hasattr(paramiko, 'DSSKey'):
|
||||
key_types.insert(1, paramiko.DSSKey)
|
||||
|
||||
for key_class in key_types:
|
||||
try:
|
||||
if self.sftp_password:
|
||||
pkey = paramiko.Ed25519Key.from_private_key_file(
|
||||
pkey = key_class.from_private_key_file(
|
||||
self.sftp_private_key, password=self.sftp_password
|
||||
)
|
||||
else:
|
||||
pkey = paramiko.Ed25519Key.from_private_key_file(
|
||||
pkey = key_class.from_private_key_file(
|
||||
self.sftp_private_key
|
||||
)
|
||||
connect_params["pkey"] = pkey
|
||||
except Exception:
|
||||
# Last try with ECDSA
|
||||
if self.sftp_password:
|
||||
pkey = paramiko.ECDSAKey.from_private_key_file(
|
||||
self.sftp_private_key, password=self.sftp_password
|
||||
)
|
||||
else:
|
||||
pkey = paramiko.ECDSAKey.from_private_key_file(
|
||||
self.sftp_private_key
|
||||
)
|
||||
connect_params["pkey"] = pkey
|
||||
_logger.debug(f"Successfully loaded {key_class.__name__} key")
|
||||
break
|
||||
except (paramiko.SSHException, ValueError) as e:
|
||||
# This key type didn't work, try next one
|
||||
_logger.debug(f"Failed to load {key_class.__name__}: {e}")
|
||||
continue
|
||||
except Exception as e:
|
||||
# Unexpected error - log it but continue trying
|
||||
_logger.warning(f"Unexpected error loading {key_class.__name__}: {e}")
|
||||
continue
|
||||
|
||||
if pkey is None:
|
||||
raise paramiko.SSHException(
|
||||
f"Could not load private key from {self.sftp_private_key}. "
|
||||
"Tried RSA, Ed25519, and ECDSA formats. "
|
||||
"DSA keys are not supported in paramiko 4.0+. "
|
||||
"Please convert your key to RSA or Ed25519."
|
||||
)
|
||||
|
||||
connect_params["pkey"] = pkey
|
||||
else:
|
||||
connect_params["password"] = self.sftp_password
|
||||
|
||||
|
|
@ -374,6 +382,11 @@ class DbBackup(models.Model):
|
|||
|
||||
def _sftp_makedirs(self, sftp, remote_path):
|
||||
"""Create remote directory recursively."""
|
||||
# Normalize path: remove trailing slashes
|
||||
remote_path = remote_path.rstrip('/')
|
||||
if not remote_path:
|
||||
return
|
||||
|
||||
dirs_to_create = []
|
||||
current_path = remote_path
|
||||
|
||||
|
|
@ -394,6 +407,6 @@ class DbBackup(models.Model):
|
|||
sftp.mkdir(dir_path)
|
||||
_logger.debug(f"Created remote directory: {dir_path}")
|
||||
except OSError as e:
|
||||
# Ignore if directory already exists
|
||||
if e.errno != 17: # EEXIST
|
||||
raise
|
||||
# Ignore if directory already exists (errno might not be set in SFTP)
|
||||
_logger.debug(f"Directory already exists or error creating {dir_path}: {e}")
|
||||
pass
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user