Updated recovery logic to fall back on older version if no image-hash is valid
This commit is contained in:
parent
cd707226f6
commit
1ed0627853
@ -73,11 +73,13 @@ def update_image(identifier, image_path, metadata_payload):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_image(identifier):
|
def get_image(identifier, version=None):
|
||||||
print("Getting image with identifier " + identifier)
|
print("Getting image with identifier " + identifier)
|
||||||
|
|
||||||
baseurl = "http://127.0.0.1:8000"
|
baseurl = "http://127.0.0.1:8000"
|
||||||
get_url = "/image/get/" + identifier
|
get_url = "/image/get/" + identifier
|
||||||
|
if version is not None:
|
||||||
|
get_url = get_url + "/version/"+str(version)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.get(baseurl + get_url)
|
response = requests.get(baseurl + get_url)
|
||||||
@ -248,6 +250,7 @@ while (command.lower() not in ["exit", "quit", "end"]):
|
|||||||
print("trigger - next image in line is sent to backend")
|
print("trigger - next image in line is sent to backend")
|
||||||
print("update - next image in line is updated in backend")
|
print("update - next image in line is updated in backend")
|
||||||
print("fetch - gets the next image from database if exists")
|
print("fetch - gets the next image from database if exists")
|
||||||
|
print("fetchversion - gets the next image with specified version from database if exists")
|
||||||
print("fetchall - get all images")
|
print("fetchall - get all images")
|
||||||
print("delete - delets the next image from db if exists")
|
print("delete - delets the next image from db if exists")
|
||||||
print("deleteall - delets all images from the service")
|
print("deleteall - delets all images from the service")
|
||||||
@ -354,6 +357,25 @@ while (command.lower() not in ["exit", "quit", "end"]):
|
|||||||
get_image(filename[:-4])
|
get_image(filename[:-4])
|
||||||
print_cursor()
|
print_cursor()
|
||||||
|
|
||||||
|
if command.lower() == "fetchversion":
|
||||||
|
if (len(attributes) < 1):
|
||||||
|
print("Error: No version supplied")
|
||||||
|
continue
|
||||||
|
if not attributes[0].isnumeric():
|
||||||
|
print("Version is no number")
|
||||||
|
continue
|
||||||
|
version = int(attributes[0])
|
||||||
|
if metadata is None:
|
||||||
|
print("No metadata loaded")
|
||||||
|
continue
|
||||||
|
if image_folder is None:
|
||||||
|
print("No image folder selected")
|
||||||
|
continue
|
||||||
|
meta_payload = metadata[index]
|
||||||
|
filename = meta_payload['filename']
|
||||||
|
get_image(filename[:-4], version)
|
||||||
|
print_cursor()
|
||||||
|
|
||||||
if command.lower() == "fetchall":
|
if command.lower() == "fetchall":
|
||||||
if metadata is None:
|
if metadata is None:
|
||||||
print("No metadata loaded")
|
print("No metadata loaded")
|
||||||
|
|||||||
@ -2,6 +2,15 @@ class StorageServiceInterface:
|
|||||||
|
|
||||||
name = "StorageServiceInterface"
|
name = "StorageServiceInterface"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_last_modified(filename: str):
|
||||||
|
"""Get last modified time of file
|
||||||
|
|
||||||
|
:param filename: filename
|
||||||
|
:return: Date of last modification or None
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check() -> bool:
|
def check() -> bool:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -9,6 +9,20 @@ from app_be.services.StorageServiceInterface import StorageServiceInterface
|
|||||||
class DropboxService(StorageServiceInterface):
|
class DropboxService(StorageServiceInterface):
|
||||||
name = "Dropbox"
|
name = "Dropbox"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_last_modified(filename: str):
|
||||||
|
"""Get last modified time of file
|
||||||
|
|
||||||
|
:param filename: filename
|
||||||
|
:return: Date of last modification or None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
dbx = dropbox.Dropbox(settings.DROPBOX_OAUTH2_ACCESS_TOKEN)
|
||||||
|
return dbx.files_get_metadata(settings.DROPBOX_IMAGE_FOLDER + filename).server_modified
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check() -> bool:
|
def check() -> bool:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import base64
|
import base64
|
||||||
import hashlib
|
import hashlib
|
||||||
|
from datetime import datetime
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
@ -12,6 +13,26 @@ from app_be.services.StorageServiceInterface import StorageServiceInterface
|
|||||||
class MinioService(StorageServiceInterface):
|
class MinioService(StorageServiceInterface):
|
||||||
name = "MinIO"
|
name = "MinIO"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_last_modified(filename: str):
|
||||||
|
"""Get last modified time of file
|
||||||
|
|
||||||
|
:param filename: filename
|
||||||
|
:return: Date of last modification or None
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
r = requests.get(settings.AWS_HOST + "?prefix="+filename)
|
||||||
|
root = ET.fromstring(r.content)
|
||||||
|
for child in root.iter('{http://s3.amazonaws.com/doc/2006-03-01/}Contents'):
|
||||||
|
if child.find('{http://s3.amazonaws.com/doc/2006-03-01/}Key').text == filename:
|
||||||
|
date_time_str = child.find('{http://s3.amazonaws.com/doc/2006-03-01/}LastModified').text
|
||||||
|
print(date_time_str)
|
||||||
|
date_time_obj = datetime.strptime(date_time_str, '%Y-%m-%dT%H:%M:%S.%fZ')
|
||||||
|
return date_time_obj
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check() -> bool:
|
def check() -> bool:
|
||||||
print("Checking MinIO availability")
|
print("Checking MinIO availability")
|
||||||
|
|||||||
@ -108,6 +108,13 @@ class MongoDBService:
|
|||||||
print("Could not delete Metadata")
|
print("Could not delete Metadata")
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def replaceHash(identifier, decoded_image):
|
||||||
|
instance = MongoManager.getInstance()
|
||||||
|
db = instance.AIC
|
||||||
|
col = db.metadata
|
||||||
|
col.update_one({"identifier": identifier}, {"$set": {"sha512": create_sha512(decoded_image)}})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def deleteAll():
|
def deleteAll():
|
||||||
instance = MongoManager.getInstance()
|
instance = MongoManager.getInstance()
|
||||||
|
|||||||
@ -25,6 +25,7 @@ urlpatterns = [
|
|||||||
url(r'^test/', TestApiClass.test_api),
|
url(r'^test/', TestApiClass.test_api),
|
||||||
url(r'^image/get/all$', ImageEndpoint.image_api_get_all),
|
url(r'^image/get/all$', ImageEndpoint.image_api_get_all),
|
||||||
url(r'^image/get/(?P<identifier>[\w-]+)$', ImageEndpoint.image_api_get_single),
|
url(r'^image/get/(?P<identifier>[\w-]+)$', ImageEndpoint.image_api_get_single),
|
||||||
|
url(r'^image/get/(?P<identifier>[\w-]+)/version/(?P<version>[\w-]+)$', ImageEndpoint.image_api_get_single_version),
|
||||||
url(r'^image/delete/all$', ImageEndpoint.image_api_delete_all),
|
url(r'^image/delete/all$', ImageEndpoint.image_api_delete_all),
|
||||||
url(r'^image/delete/(?P<identifier>[\w-]+)$', ImageEndpoint.image_api_delete),
|
url(r'^image/delete/(?P<identifier>[\w-]+)$', ImageEndpoint.image_api_delete),
|
||||||
url(r'^image/post$', ImageEndpoint.image_api_post),
|
url(r'^image/post$', ImageEndpoint.image_api_post),
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from app_be.services.dropboxservice import DropboxService
|
from app_be.services.dropboxservice import DropboxService
|
||||||
from app_be.services.hashservice import create_sha512
|
from app_be.services.hashservice import create_sha512
|
||||||
@ -36,10 +37,20 @@ class ImageEndpoint:
|
|||||||
|
|
||||||
return JsonResponse({'Result': 'success1'}, safe=False)
|
return JsonResponse({'Result': 'success1'}, safe=False)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
@api_view(['GET'])
|
||||||
|
def image_api_get_single_version(request, identifier, version):
|
||||||
|
logger.debug('Image GET single with version call: {}'.format(request))
|
||||||
|
return ImageEndpoint.get_image(identifier+'_'+version)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@api_view(['GET'])
|
@api_view(['GET'])
|
||||||
def image_api_get_single(request, identifier):
|
def image_api_get_single(request, identifier):
|
||||||
logger.debug('Image GET single call: {}'.format(request))
|
logger.debug('Image GET single call: {}'.format(request))
|
||||||
|
return ImageEndpoint.get_image(identifier)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_image(identifier):
|
||||||
|
|
||||||
# get metadata from MongoDB
|
# get metadata from MongoDB
|
||||||
metadata = MongoDBService.getSingle(identifier)
|
metadata = MongoDBService.getSingle(identifier)
|
||||||
@ -58,7 +69,6 @@ class ImageEndpoint:
|
|||||||
# check image existence and correctness for each service + retrieve valid image if possible
|
# check image existence and correctness for each service + retrieve valid image if possible
|
||||||
for service in ImageEndpoint.storageServiceList:
|
for service in ImageEndpoint.storageServiceList:
|
||||||
logger.debug('Checking recovery for service ' + service.name)
|
logger.debug('Checking recovery for service ' + service.name)
|
||||||
print('Checking recovery for service ' + service.name)
|
|
||||||
service_image_bytes = service.read_file(metadata['filename'])
|
service_image_bytes = service.read_file(metadata['filename'])
|
||||||
if service_image_bytes is not None:
|
if service_image_bytes is not None:
|
||||||
recovery_has_image[service.name] = True
|
recovery_has_image[service.name] = True
|
||||||
@ -73,22 +83,34 @@ class ImageEndpoint:
|
|||||||
recovery_has_image[service.name] = False
|
recovery_has_image[service.name] = False
|
||||||
recovery_hash_matches[service.name] = False
|
recovery_hash_matches[service.name] = False
|
||||||
|
|
||||||
# TODO: after talking with tobias about updating => replace with older version if hash is wrong
|
|
||||||
|
|
||||||
# check if any service has the image saved
|
# check if any service has the image saved
|
||||||
if not ImageEndpoint.combine_boolean_dict_or(recovery_has_image):
|
if not ImageEndpoint.combine_boolean_dict_or(recovery_has_image):
|
||||||
logger.debug('None of the storage services has the requested image saved')
|
logger.debug('None of the storage services has the requested image saved')
|
||||||
MongoDBService.deleteSingle(identifier)
|
return JsonResponse({'Result': 'Error - image is not available on any storage service',
|
||||||
return JsonResponse({'Result': 'Error - image is not available on any storage service and was deleted',
|
|
||||||
'id': identifier}, status=404, safe=False)
|
'id': identifier}, status=404, safe=False)
|
||||||
|
|
||||||
# check if any service has a valid version of the image saved
|
# check if any service has a valid version of the image saved
|
||||||
if not ImageEndpoint.combine_boolean_dict_or(recovery_hash_matches) and valid_image_bytes is None:
|
if not ImageEndpoint.combine_boolean_dict_or(recovery_hash_matches) and valid_image_bytes is None:
|
||||||
logger.debug('None of the storage services has a valid image saved')
|
logger.debug('None of the storage services has a valid image saved - assume oldest image is valid')
|
||||||
MongoDBService.deleteSingle(identifier)
|
service_with_oldest = None
|
||||||
return JsonResponse({'Result': 'Error - image is not available on any storage service and was deleted',
|
oldest_last_modified = datetime.now()
|
||||||
'id': identifier}, status=404, safe=False)
|
for service in ImageEndpoint.storageServiceList:
|
||||||
|
modified = service.get_last_modified(metadata['filename'])
|
||||||
|
if modified is not None and modified < oldest_last_modified:
|
||||||
|
oldest_last_modified = modified
|
||||||
|
service_with_oldest = service
|
||||||
|
|
||||||
|
if service_with_oldest is None: # failsave if no image is available
|
||||||
|
return JsonResponse({'Result': 'Error - image is not available on any storage service',
|
||||||
|
'id': identifier}, status=404, safe=False)
|
||||||
|
# replace image on every service with oldest available
|
||||||
|
valid_image_bytes = service_with_oldest.read_file(metadata['filename'])
|
||||||
|
MongoDBService.replaceHash(identifier, valid_image_bytes)
|
||||||
|
for service in ImageEndpoint.storageServiceList:
|
||||||
|
if service != service_with_oldest:
|
||||||
|
if not service.create_file(metadata['filename'], valid_image_bytes):
|
||||||
|
logger.error('Error recovering file in service ' + service.name)
|
||||||
|
else:
|
||||||
# at this point we know at least one valid image is available
|
# at this point we know at least one valid image is available
|
||||||
for service in ImageEndpoint.storageServiceList:
|
for service in ImageEndpoint.storageServiceList:
|
||||||
if not recovery_has_image[service.name] or not recovery_hash_matches[service.name]:
|
if not recovery_has_image[service.name] or not recovery_hash_matches[service.name]:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user