feat: add shutdown/reboot SSE events on initiation

plus:
* add some more documentation
* add error handling when the script fails to run
* a bit of refactoring

refs #70
This commit is contained in:
fusion44 2022-03-24 20:17:56 +01:00
parent 25ec9f0af5
commit efdb1c1057
No known key found for this signature in database
GPG key ID: 645FA807E935D9D5
5 changed files with 70 additions and 28 deletions

View file

@ -15,13 +15,19 @@ if PLATFORM == APIPlatform.RASPIBLITZ:
HW_INFO_YIELD_TIME,
get_hardware_info_impl,
)
from app.repositories.system_impl.raspiblitz import get_system_info_impl
from app.repositories.system_impl.raspiblitz import (
get_system_info_impl,
shutdown_impl,
)
elif PLATFORM == APIPlatform.NATIVE_PYTHON:
from app.repositories.hardware_impl.native_python import (
HW_INFO_YIELD_TIME,
get_hardware_info_impl,
)
from app.repositories.system_impl.native_python import get_system_info_impl
from app.repositories.system_impl.native_python import (
get_system_info_impl,
shutdown_impl,
)
else:
raise RuntimeError(f"Unknown platform {PLATFORM}")
@ -83,12 +89,12 @@ def password_valid(password: str):
return re.match("^[a-zA-Z0-9]*$", password)
def name_valid(password : str):
def name_valid(password: str):
if len(password) < 3:
return False
if password.find(' ') >= 0:
if password.find(" ") >= 0:
return False
return re.match('^[\.a-zA-Z0-9-_]*$', password)
return re.match("^[\.a-zA-Z0-9-_]*$", password)
async def get_system_info() -> SystemInfo:
@ -104,6 +110,15 @@ async def get_hardware_info() -> map:
return await get_hardware_info_impl()
async def shutdown(reboot: bool) -> bool:
if reboot:
await send_sse_message(SSE.SYSTEM_REBOOT_NOTICE, {"reboot": True})
else:
await send_sse_message(SSE.SYSTEM_SHUTDOWN_NOTICE, {"shutdown": True})
return await shutdown_impl(reboot=reboot)
async def subscribe_hardware_info(request: Request):
while True:
if await request.is_disconnected():

View file

@ -1,3 +1,5 @@
import logging
from decouple import config
from app.constants import API_VERSION
@ -43,3 +45,8 @@ async def get_system_info_impl() -> SystemInfo:
ssh_address=ssh_address,
chain=lninfo.chains[0].network,
)
async def shutdown_impl(reboot: bool) -> bool:
logging.info("Shutdown / reboot not supported in native_python mode.")
return False

View file

@ -1,4 +1,5 @@
import asyncio
import logging
import os
from decouple import config
@ -12,7 +13,7 @@ from app.models.system import (
SystemInfo,
)
from app.repositories.lightning import get_ln_info
from app.utils import redis_get
from app.utils import SSE, redis_get, send_sse_message
SHELL_SCRIPT_PATH = config("shell_script_path")
@ -43,7 +44,7 @@ async def get_system_info_impl() -> SystemInfo:
)
async def shutdown(reboot: bool):
async def shutdown_impl(reboot: bool) -> bool:
params = ""
if reboot:
params = "reboot"
@ -59,8 +60,19 @@ async def shutdown(reboot: bool):
stdout, stderr = await proc.communicate()
print(f"[{cmd!r} exited with {proc.returncode}]")
logging.info(f"[{cmd!r} exited with {proc.returncode}]")
if stdout:
print(f"[stdout]\n{stdout.decode()}")
logging.info(f"[stdout]\n{stdout.decode()}")
if stderr:
print(f"[stderr]\n{stderr.decode()}")
logging.error(f"[stderr]\n{stderr.decode()}")
if proc.returncode > 0:
err = stderr.decode()
if reboot:
await send_sse_message(SSE.SYSTEM_REBOOT_ERROR, {"error_message": err})
else:
await send_sse_message(SSE.SYSTEM_SHUTDOWN_ERROR, {"error_message": err})
return False
return True

View file

@ -8,25 +8,25 @@ from fastapi.params import Depends
from app.auth.auth_bearer import JWTBearer
from app.auth.auth_handler import sign_jwt
from app.external.sse_starlette import EventSourceResponse
from app.models.system import APIPlatform, LoginInput, RawDebugLogData, SystemInfo
from app.models.system import LoginInput, RawDebugLogData, SystemInfo
from app.repositories.system import (
HW_INFO_YIELD_TIME,
PLATFORM,
call_script,
get_debug_logs_raw,
get_hardware_info,
get_system_info,
parse_key_value_text,
password_valid,
shutdown,
subscribe_hardware_info,
)
from app.repositories.system_impl.raspiblitz import shutdown
from app.routers.system_docs import (
get_debug_logs_raw_desc,
get_debug_logs_raw_resp_desc,
get_debug_logs_raw_summary,
get_hw_info_json,
)
from app.utils import SSE
_PREFIX = "system"
@ -137,29 +137,33 @@ async def hw_info_sub(request: Request):
"/reboot",
name=f"{_PREFIX}.reboot",
summary="Reboots the system",
description=f"""Attempts to reboot the system.
Will send a `{SSE.SYSTEM_REBOOT_NOTICE}` SSE message immediately to
all connected clients.
""",
response_description=f"""True if successful. False on failure.
A failure will also send an error message with id `{SSE.SYSTEM_REBOOT_ERROR}`
to all connected clients.
""",
dependencies=[Depends(JWTBearer())],
)
async def reboot_system() -> bool:
if PLATFORM == APIPlatform.RASPIBLITZ:
await shutdown(True)
return True
else:
raise HTTPException(
status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented on native"
)
return await shutdown(False)
@router.post(
"/shutdown",
name=f"{_PREFIX}.shutdown",
summary="Shuts the system down",
description=f"""Attempts to shutdown the system.
Will send a `{SSE.SYSTEM_SHUTDOWN_NOTICE}` SSE message immediately to all
connected clients.
""",
response_description=f"""True if successful. False on failure.
A failure will also send an error message with id {SSE.SYSTEM_SHUTDOWN_ERROR}
to all connected clients.
""",
dependencies=[Depends(JWTBearer())],
)
async def shutdown() -> bool:
if PLATFORM == APIPlatform.RASPIBLITZ:
await shutdown(False)
return True
else:
raise HTTPException(
status.HTTP_501_NOT_IMPLEMENTED, detail="Not implemented on native"
)
async def shutdown_path() -> bool:
return await shutdown(False)

View file

@ -179,6 +179,10 @@ async def redis_get(key: str) -> str:
class SSE:
SYSTEM_INFO = "system_info"
SYSTEM_SHUTDOWN_NOTICE = "system_shutdown_initiated"
SYSTEM_SHUTDOWN_ERROR = "system_shutdown_error"
SYSTEM_REBOOT_NOTICE = "system_reboot_initiated"
SYSTEM_REBOOT_ERROR = "system_reboot_error"
HARDWARE_INFO = "hardware_info"
INSTALLED_APP_STATUS = "installed_app_status"