Started basic rescue script
This commit is contained in:
parent
843d282dfc
commit
9842de8859
9 changed files with 260 additions and 8 deletions
|
|
@ -1,6 +0,0 @@
|
||||||
__all__ = ["Microscope", "config", "utilities"]
|
|
||||||
__version__ = "0.1.0"
|
|
||||||
|
|
||||||
from .microscope import Microscope
|
|
||||||
from . import config
|
|
||||||
from . import utilities
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from openflexure_microscope import Microscope
|
from openflexure_microscope.microscope import Microscope
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,9 @@ def capture_from_exif(path, exif_dict):
|
||||||
try:
|
try:
|
||||||
image_metadata = exif_dict.pop("image")
|
image_metadata = exif_dict.pop("image")
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
logging.error(f"Unable to obtain OpenFlexure metadata from file {path}")
|
logging.error(
|
||||||
|
f"Unable to obtain valid 2.0 OpenFlexure metadata from file {path}"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Populate capture parameters
|
# Populate capture parameters
|
||||||
|
|
|
||||||
0
openflexure_microscope/rescue/__init__.py
Normal file
0
openflexure_microscope/rescue/__init__.py
Normal file
77
openflexure_microscope/rescue/auto.py
Normal file
77
openflexure_microscope/rescue/auto.py
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
from openflexure_microscope.paths import (
|
||||||
|
FALLBACK_OPENFLEXURE_VAR_PATH,
|
||||||
|
PREFERRED_OPENFLEXURE_VAR_PATH,
|
||||||
|
)
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from . import check_settings, check_capture_reload
|
||||||
|
|
||||||
|
# Paths for suggestions
|
||||||
|
LOGS_PATHS = [
|
||||||
|
os.path.join(PREFERRED_OPENFLEXURE_VAR_PATH, "logs"),
|
||||||
|
os.path.join(FALLBACK_OPENFLEXURE_VAR_PATH, "logs"),
|
||||||
|
]
|
||||||
|
SETTINGS_PATHS = [
|
||||||
|
os.path.join(var_path, "settings", "microscope_settings.json")
|
||||||
|
for var_path in (PREFERRED_OPENFLEXURE_VAR_PATH, FALLBACK_OPENFLEXURE_VAR_PATH)
|
||||||
|
]
|
||||||
|
CONFIG_PATHS = [
|
||||||
|
os.path.join(var_path, "settings", "microscope_configuration.json")
|
||||||
|
for var_path in (PREFERRED_OPENFLEXURE_VAR_PATH, FALLBACK_OPENFLEXURE_VAR_PATH)
|
||||||
|
]
|
||||||
|
DATA_PATHS = [
|
||||||
|
os.path.join(PREFERRED_OPENFLEXURE_VAR_PATH, "data"),
|
||||||
|
os.path.join(FALLBACK_OPENFLEXURE_VAR_PATH, "data"),
|
||||||
|
]
|
||||||
|
|
||||||
|
logger = logging.getLogger()
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
logging.debug("Testing debug logger. One two one two.")
|
||||||
|
|
||||||
|
|
||||||
|
class bcolors:
|
||||||
|
HEADER = "\033[95m"
|
||||||
|
OKBLUE = "\033[94m"
|
||||||
|
OKGREEN = "\033[92m"
|
||||||
|
WARNING = "\033[93m"
|
||||||
|
FAIL = "\033[91m"
|
||||||
|
ENDC = "\033[0m"
|
||||||
|
BOLD = "\033[1m"
|
||||||
|
UNDERLINE = "\033[4m"
|
||||||
|
|
||||||
|
|
||||||
|
error_keys = {
|
||||||
|
"config_settings_import_error": "The configuration submodule could not be imported properly. This is usually because the default config or settings files are badly broken somehow. See further errors for details",
|
||||||
|
"default_config_empty": "The default configuration file is empty, and could not be automatically populated.",
|
||||||
|
"default_settings_empty": "The default settings file is empty, and could not be automatically populated.",
|
||||||
|
"default_config_error": f"The default configuration file could not be parsed. To fix, consider backing up and deleting your configuration files from {CONFIG_PATHS} to reset the configuration.",
|
||||||
|
"default_settings_error": f"The default settings file could not be parsed. To fix, consider backing up and deleting your configuration files from {SETTINGS_PATHS} to reset the configuration.",
|
||||||
|
"capture_rebuild_timeout": f"Capture database rebuilding took a long time. This may not cause catastrophic errors, but rather will cause the server to hang for a while. To fix, consider moving your captures from {DATA_PATHS} to another location.",
|
||||||
|
}
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
spoof = False
|
||||||
|
|
||||||
|
error_sources = []
|
||||||
|
|
||||||
|
if spoof:
|
||||||
|
error_sources = list(error_keys.keys())
|
||||||
|
|
||||||
|
error_sources.extend(check_settings.main())
|
||||||
|
error_sources.extend(check_capture_reload.main())
|
||||||
|
|
||||||
|
if not error_sources:
|
||||||
|
|
||||||
|
print()
|
||||||
|
print(bcolors.OKGREEN + "No errors found!" + bcolors.ENDC)
|
||||||
|
print(
|
||||||
|
"That's not to say everything is fine, only that our automatic diagnostics couldn't find much."
|
||||||
|
)
|
||||||
|
print(f"You can check through the server logs at {LOGS_PATHS}")
|
||||||
|
|
||||||
|
else:
|
||||||
|
for err_code in error_sources:
|
||||||
|
logging.error(err_code)
|
||||||
|
if error_keys.get(err_code):
|
||||||
|
print(bcolors.FAIL + error_keys.get(err_code) + bcolors.ENDC)
|
||||||
34
openflexure_microscope/rescue/check_capture_reload.py
Normal file
34
openflexure_microscope/rescue/check_capture_reload.py
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
from openflexure_microscope.rescue.monitor_timeout import launch_timeout_test_process
|
||||||
|
from openflexure_microscope.camera.capture import build_captures_from_exif
|
||||||
|
from openflexure_microscope.camera.base import BASE_CAPTURE_PATH
|
||||||
|
from openflexure_microscope.config import user_settings
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
def check_capture_rebuild(timeout=10):
|
||||||
|
logging.info("Loading user settings...")
|
||||||
|
settings = user_settings.load()
|
||||||
|
|
||||||
|
cap_path = str(settings.get("camera", {}).get("paths", {}).get("default"))
|
||||||
|
logging.info(f"Capture path found: {cap_path}")
|
||||||
|
if not cap_path:
|
||||||
|
logging.error(
|
||||||
|
"No capture path defined in settings. This is unusual for anything other than a first-run. \nFalling back to default path."
|
||||||
|
)
|
||||||
|
cap_path = BASE_CAPTURE_PATH
|
||||||
|
|
||||||
|
logging.info("Starting capture reload with a 10 second timeout...")
|
||||||
|
passed_timeout_test = launch_timeout_test_process(
|
||||||
|
build_captures_from_exif, args=(cap_path,)
|
||||||
|
)
|
||||||
|
|
||||||
|
return passed_timeout_test
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
error_sources = []
|
||||||
|
passed_timeout = check_capture_rebuild()
|
||||||
|
if not passed_timeout:
|
||||||
|
error_sources.append("capture_rebuild_timeout")
|
||||||
|
return error_sources
|
||||||
47
openflexure_microscope/rescue/check_settings.py
Normal file
47
openflexure_microscope/rescue/check_settings.py
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
|
ERROR_SOURCES = []
|
||||||
|
|
||||||
|
|
||||||
|
def trace_config_exceptions():
|
||||||
|
error_sources = []
|
||||||
|
|
||||||
|
from openflexure_microscope.paths import (
|
||||||
|
DEFAULT_CONFIGURATION_FILE_PATH,
|
||||||
|
SETTINGS_FILE_PATH,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
default_config = json.load(DEFAULT_CONFIGURATION_FILE_PATH)
|
||||||
|
if not default_config:
|
||||||
|
error_sources.append("default_config_empty")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Error parsing config:")
|
||||||
|
logging.error(e)
|
||||||
|
error_sources.append("default_config_error")
|
||||||
|
|
||||||
|
try:
|
||||||
|
default_settings = json.load(SETTINGS_FILE_PATH)
|
||||||
|
if not default_settings:
|
||||||
|
error_sources.append("default_settings_empty")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("Error parsing settings:")
|
||||||
|
logging.error(e)
|
||||||
|
error_sources.append("default_settings_error")
|
||||||
|
|
||||||
|
return error_sources
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
error_sources = []
|
||||||
|
logging.info("Attempting default settings and config import...")
|
||||||
|
try:
|
||||||
|
from openflexure_microscope import config
|
||||||
|
except Exception as e:
|
||||||
|
error_sources.append("config_settings_import_error")
|
||||||
|
logging.error("Error importing config:")
|
||||||
|
logging.error(e)
|
||||||
|
error_sources.extend(trace_config_exceptions())
|
||||||
|
|
||||||
|
return error_sources
|
||||||
59
openflexure_microscope/rescue/monitor_service.py
Normal file
59
openflexure_microscope/rescue/monitor_service.py
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
#!/bin/python
|
||||||
|
#
|
||||||
|
# Copyright 2016 Flavio Garcia
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
# Usage: monitor_process.py <service_name>
|
||||||
|
#
|
||||||
|
# Example(crontab, every 5 minutes):
|
||||||
|
# */5 * * * * /root/bin/monitor_service.py prosody > /dev/null 2>&1
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceMonitor(object):
|
||||||
|
def __init__(self, service):
|
||||||
|
self.service = service
|
||||||
|
|
||||||
|
def is_active(self):
|
||||||
|
"""Return True if service is running"""
|
||||||
|
for line in self.status():
|
||||||
|
if "Active:" in line:
|
||||||
|
if "(running)" in line:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def status(self):
|
||||||
|
cmd = f"/bin/systemctl status {self.service}.service"
|
||||||
|
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
||||||
|
stdout_list = proc.communicate()[0].decode().split("\n")
|
||||||
|
return stdout_list
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
cmd = f"/bin/systemctl start {self.service}.service"
|
||||||
|
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
||||||
|
proc.communicate()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
cmd = f"/bin/systemctl stop {self.service}.service"
|
||||||
|
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
||||||
|
proc.communicate()
|
||||||
|
|
||||||
|
def log(self, n_lines: int = 100):
|
||||||
|
cmd = f"/bin/journalctl -u {self.service}.service -n {n_lines} --no-pager"
|
||||||
|
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
||||||
|
stdout_list = proc.communicate()[0].decode().split("\n")
|
||||||
|
return stdout_list
|
||||||
39
openflexure_microscope/rescue/monitor_timeout.py
Normal file
39
openflexure_microscope/rescue/monitor_timeout.py
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import multiprocessing
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
# bar
|
||||||
|
def test_long_fn():
|
||||||
|
for _ in range(100):
|
||||||
|
print("Tick")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
def launch_timeout_test_process(target, args=(), kwargs=None, timeout=10):
|
||||||
|
if not kwargs:
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
# Start target as a process
|
||||||
|
p = multiprocessing.Process(target=target, args=args, kwargs=kwargs)
|
||||||
|
p.start()
|
||||||
|
|
||||||
|
# Wait for 10 seconds or until process finishes
|
||||||
|
p.join(timeout)
|
||||||
|
|
||||||
|
# If thread is still active
|
||||||
|
if p.is_alive():
|
||||||
|
logging.error(
|
||||||
|
f"Function {target} reached timeout after {timeout} seconds. Terminating."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Terminate
|
||||||
|
p.terminate()
|
||||||
|
p.join()
|
||||||
|
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
launch_timeout_test_process(test_long_fn, timeout=5)
|
||||||
Loading…
Add table
Add a link
Reference in a new issue