|
# retoor <retoor@molodetz.nl>
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
import sys
|
|
|
|
from devplacepy.config import XMLRPC_BIND, XMLRPC_PORT
|
|
from devplacepy.services.base import BaseService
|
|
|
|
XMLRPC_INTERVAL_SECONDS = 15
|
|
SERVER_MODULE = "devplacepy.services.xmlrpc.server"
|
|
TERMINATE_TIMEOUT_SECONDS = 10
|
|
|
|
|
|
class XmlrpcService(BaseService):
|
|
title = "XML-RPC Bridge"
|
|
description = (
|
|
"Forking XML-RPC server that exposes every documented REST endpoint as an "
|
|
"XML-RPC method. Reachable at /xmlrpc through the app and nginx."
|
|
)
|
|
default_enabled = True
|
|
min_interval = 5
|
|
|
|
def __init__(self) -> None:
|
|
super().__init__("xmlrpc", interval_seconds=XMLRPC_INTERVAL_SECONDS)
|
|
self._process: subprocess.Popen | None = None
|
|
|
|
def _alive(self) -> bool:
|
|
return self._process is not None and self._process.poll() is None
|
|
|
|
def _spawn(self) -> None:
|
|
self._process = subprocess.Popen(
|
|
[sys.executable, "-m", SERVER_MODULE],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
)
|
|
self.log(
|
|
f"Forking XML-RPC server started (pid {self._process.pid}) on "
|
|
f"{XMLRPC_BIND}:{XMLRPC_PORT}"
|
|
)
|
|
|
|
def _terminate(self) -> None:
|
|
if not self._alive():
|
|
self._process = None
|
|
return
|
|
self._process.terminate()
|
|
try:
|
|
self._process.wait(timeout=TERMINATE_TIMEOUT_SECONDS)
|
|
except subprocess.TimeoutExpired:
|
|
self._process.kill()
|
|
self._process.wait()
|
|
self.log("Forking XML-RPC server stopped")
|
|
self._process = None
|
|
|
|
async def on_enable(self) -> None:
|
|
if not self._alive():
|
|
self._spawn()
|
|
|
|
async def on_disable(self) -> None:
|
|
self._terminate()
|
|
|
|
async def run_once(self) -> None:
|
|
if self._alive():
|
|
self.log(f"XML-RPC server healthy (pid {self._process.pid})")
|
|
return
|
|
self.log("XML-RPC server not running, starting it")
|
|
self._spawn()
|
|
|
|
def collect_metrics(self) -> dict:
|
|
return {
|
|
"running": self._alive(),
|
|
"pid": self._process.pid if self._alive() else None,
|
|
"bind": f"{XMLRPC_BIND}:{XMLRPC_PORT}",
|
|
}
|
|
|
|
|
|
__all__ = ["XmlrpcService"]
|