New alert service
This commit is contained in:
@@ -340,27 +340,85 @@ class ArtifactRepository(TrainingBaseRepository):
|
||||
}
|
||||
|
||||
async def verify_artifact_integrity(self, artifact_id: int) -> Dict[str, Any]:
|
||||
"""Verify artifact file integrity (placeholder for file system checks)"""
|
||||
"""Verify artifact file integrity with actual file system checks"""
|
||||
try:
|
||||
import os
|
||||
import hashlib
|
||||
|
||||
artifact = await self.get_by_id(artifact_id)
|
||||
if not artifact:
|
||||
return {"exists": False, "error": "Artifact not found"}
|
||||
|
||||
# This is a placeholder - in a real implementation, you would:
|
||||
# 1. Check if the file exists at artifact.file_path
|
||||
# 2. Calculate current checksum and compare with stored checksum
|
||||
# 3. Verify file size matches stored file_size_bytes
|
||||
|
||||
return {
|
||||
|
||||
# Check if file exists
|
||||
file_exists = os.path.exists(artifact.file_path)
|
||||
if not file_exists:
|
||||
return {
|
||||
"artifact_id": artifact_id,
|
||||
"file_path": artifact.file_path,
|
||||
"exists": False,
|
||||
"checksum_valid": False,
|
||||
"size_valid": False,
|
||||
"storage_location": artifact.storage_location,
|
||||
"last_verified": datetime.now().isoformat(),
|
||||
"error": "File does not exist on disk"
|
||||
}
|
||||
|
||||
# Verify file size
|
||||
actual_size = os.path.getsize(artifact.file_path)
|
||||
size_valid = True
|
||||
if artifact.file_size_bytes:
|
||||
size_valid = (actual_size == artifact.file_size_bytes)
|
||||
|
||||
# Verify checksum if stored
|
||||
checksum_valid = True
|
||||
actual_checksum = None
|
||||
if artifact.checksum:
|
||||
# Calculate checksum of actual file
|
||||
sha256_hash = hashlib.sha256()
|
||||
try:
|
||||
with open(artifact.file_path, "rb") as f:
|
||||
# Read file in chunks to handle large files
|
||||
for byte_block in iter(lambda: f.read(4096), b""):
|
||||
sha256_hash.update(byte_block)
|
||||
actual_checksum = sha256_hash.hexdigest()
|
||||
checksum_valid = (actual_checksum == artifact.checksum)
|
||||
except Exception as checksum_error:
|
||||
logger.error(f"Failed to calculate checksum: {checksum_error}")
|
||||
checksum_valid = False
|
||||
actual_checksum = None
|
||||
|
||||
# Overall integrity status
|
||||
integrity_valid = file_exists and size_valid and checksum_valid
|
||||
|
||||
result = {
|
||||
"artifact_id": artifact_id,
|
||||
"file_path": artifact.file_path,
|
||||
"exists": True, # Would check actual file existence
|
||||
"checksum_valid": True, # Would verify actual checksum
|
||||
"size_valid": True, # Would verify actual file size
|
||||
"exists": file_exists,
|
||||
"checksum_valid": checksum_valid,
|
||||
"size_valid": size_valid,
|
||||
"integrity_valid": integrity_valid,
|
||||
"storage_location": artifact.storage_location,
|
||||
"last_verified": datetime.now().isoformat()
|
||||
"last_verified": datetime.now().isoformat(),
|
||||
"details": {
|
||||
"stored_size_bytes": artifact.file_size_bytes,
|
||||
"actual_size_bytes": actual_size if file_exists else None,
|
||||
"stored_checksum": artifact.checksum,
|
||||
"actual_checksum": actual_checksum
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if not integrity_valid:
|
||||
issues = []
|
||||
if not file_exists:
|
||||
issues.append("file_missing")
|
||||
if not size_valid:
|
||||
issues.append("size_mismatch")
|
||||
if not checksum_valid:
|
||||
issues.append("checksum_mismatch")
|
||||
result["issues"] = issues
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to verify artifact integrity",
|
||||
artifact_id=artifact_id,
|
||||
@@ -374,55 +432,124 @@ class ArtifactRepository(TrainingBaseRepository):
|
||||
self,
|
||||
from_location: str,
|
||||
to_location: str,
|
||||
tenant_id: str = None
|
||||
tenant_id: str = None,
|
||||
copy_only: bool = False,
|
||||
verify: bool = True
|
||||
) -> Dict[str, Any]:
|
||||
"""Migrate artifacts from one storage location to another (placeholder)"""
|
||||
"""Migrate artifacts from one storage location to another with actual file operations"""
|
||||
try:
|
||||
import os
|
||||
import shutil
|
||||
import hashlib
|
||||
|
||||
# Get artifacts to migrate
|
||||
artifacts = await self.get_artifacts_by_storage_location(from_location, tenant_id)
|
||||
|
||||
|
||||
migrated_count = 0
|
||||
failed_count = 0
|
||||
|
||||
# This is a placeholder - in a real implementation, you would:
|
||||
# 1. Copy files from old location to new location
|
||||
# 2. Update file paths in database
|
||||
# 3. Verify successful migration
|
||||
# 4. Clean up old files
|
||||
|
||||
failed_artifacts = []
|
||||
verified_count = 0
|
||||
|
||||
for artifact in artifacts:
|
||||
try:
|
||||
# Placeholder migration logic
|
||||
new_file_path = artifact.file_path.replace(from_location, to_location)
|
||||
|
||||
# Determine new file path
|
||||
new_file_path = artifact.file_path.replace(from_location, to_location, 1)
|
||||
|
||||
# Create destination directory if it doesn't exist
|
||||
dest_dir = os.path.dirname(new_file_path)
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
|
||||
# Check if source file exists
|
||||
if not os.path.exists(artifact.file_path):
|
||||
logger.warning(f"Source file not found: {artifact.file_path}")
|
||||
failed_count += 1
|
||||
failed_artifacts.append({
|
||||
"artifact_id": artifact.id,
|
||||
"file_path": artifact.file_path,
|
||||
"reason": "source_file_not_found"
|
||||
})
|
||||
continue
|
||||
|
||||
# Copy or move file
|
||||
if copy_only:
|
||||
shutil.copy2(artifact.file_path, new_file_path)
|
||||
logger.debug(f"Copied file from {artifact.file_path} to {new_file_path}")
|
||||
else:
|
||||
shutil.move(artifact.file_path, new_file_path)
|
||||
logger.debug(f"Moved file from {artifact.file_path} to {new_file_path}")
|
||||
|
||||
# Verify file was copied/moved successfully
|
||||
if verify and os.path.exists(new_file_path):
|
||||
# Verify file size
|
||||
new_size = os.path.getsize(new_file_path)
|
||||
if artifact.file_size_bytes and new_size != artifact.file_size_bytes:
|
||||
logger.warning(f"File size mismatch after migration: {new_file_path}")
|
||||
failed_count += 1
|
||||
failed_artifacts.append({
|
||||
"artifact_id": artifact.id,
|
||||
"file_path": new_file_path,
|
||||
"reason": "size_mismatch_after_migration"
|
||||
})
|
||||
continue
|
||||
|
||||
# Verify checksum if available
|
||||
if artifact.checksum:
|
||||
sha256_hash = hashlib.sha256()
|
||||
with open(new_file_path, "rb") as f:
|
||||
for byte_block in iter(lambda: f.read(4096), b""):
|
||||
sha256_hash.update(byte_block)
|
||||
new_checksum = sha256_hash.hexdigest()
|
||||
|
||||
if new_checksum != artifact.checksum:
|
||||
logger.warning(f"Checksum mismatch after migration: {new_file_path}")
|
||||
failed_count += 1
|
||||
failed_artifacts.append({
|
||||
"artifact_id": artifact.id,
|
||||
"file_path": new_file_path,
|
||||
"reason": "checksum_mismatch_after_migration"
|
||||
})
|
||||
continue
|
||||
|
||||
verified_count += 1
|
||||
|
||||
# Update database with new location
|
||||
await self.update(artifact.id, {
|
||||
"storage_location": to_location,
|
||||
"file_path": new_file_path
|
||||
})
|
||||
|
||||
|
||||
migrated_count += 1
|
||||
|
||||
|
||||
except Exception as migration_error:
|
||||
logger.error("Failed to migrate artifact",
|
||||
artifact_id=artifact.id,
|
||||
error=str(migration_error))
|
||||
failed_count += 1
|
||||
|
||||
failed_artifacts.append({
|
||||
"artifact_id": artifact.id,
|
||||
"file_path": artifact.file_path,
|
||||
"reason": str(migration_error)
|
||||
})
|
||||
|
||||
logger.info("Artifact migration completed",
|
||||
from_location=from_location,
|
||||
to_location=to_location,
|
||||
migrated_count=migrated_count,
|
||||
failed_count=failed_count)
|
||||
|
||||
failed_count=failed_count,
|
||||
verified_count=verified_count)
|
||||
|
||||
return {
|
||||
"from_location": from_location,
|
||||
"to_location": to_location,
|
||||
"total_artifacts": len(artifacts),
|
||||
"migrated_count": migrated_count,
|
||||
"failed_count": failed_count,
|
||||
"success_rate": round((migrated_count / len(artifacts)) * 100, 2) if artifacts else 100
|
||||
"verified_count": verified_count if verify else None,
|
||||
"success_rate": round((migrated_count / len(artifacts)) * 100, 2) if artifacts else 100,
|
||||
"copy_only": copy_only,
|
||||
"failed_artifacts": failed_artifacts if failed_artifacts else None
|
||||
}
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to migrate artifacts",
|
||||
from_location=from_location,
|
||||
|
||||
Reference in New Issue
Block a user