machine.py: add support for supervisord service management
Signed-off-by: mirokymac <toufubomb@gmail.com>
This commit is contained in:
parent
e628b243c2
commit
48f3bb2189
|
@ -120,8 +120,9 @@ with system services such as systemd.
|
|||
provider: systemd_dbus
|
||||
# The provider implementation used to collect system service information
|
||||
# and run service actions (ie: start, restart, stop). This can be "none",
|
||||
# "systemd_dbus", or "systemd_cli". If the provider is set to "none" service
|
||||
# action APIs will be disabled. The default is systemd_dbus.
|
||||
# "supervisord", "systemd_dbus", or "systemd_cli". If the provider is set
|
||||
# to "none" service action APIs will be disabled.
|
||||
# The default is systemd_dbus.
|
||||
sudo_password:
|
||||
# The password for the linux user. When set Moonraker can run linux commands
|
||||
# that require elevated permissions. This option accepts Jinja2 Templates,
|
||||
|
@ -140,6 +141,10 @@ force_validation:
|
|||
# By default Moonraker will not attempt to revalidate if a previous attempt
|
||||
# at validation successfully completed. Setting this value to True will force
|
||||
# Moonraker to perform validation. The default is False.
|
||||
supervisord_config_path:
|
||||
# Path to the superrvisord config file. In case for multi supervisord instance
|
||||
# running on single machine, the default '/var/run/supervisord.sock' is occupied
|
||||
# by other services.
|
||||
```
|
||||
|
||||
!!! Note
|
||||
|
|
|
@ -108,7 +108,8 @@ class Machine:
|
|||
providers: Dict[str, type] = {
|
||||
"none": BaseProvider,
|
||||
"systemd_cli": SystemdCliProvider,
|
||||
"systemd_dbus": SystemdDbusProvider
|
||||
"systemd_dbus": SystemdDbusProvider,
|
||||
"supervisord": SuperisordProvider
|
||||
}
|
||||
self.provider_type = config.get('provider', 'systemd_dbus')
|
||||
pclass = providers.get(self.provider_type)
|
||||
|
@ -747,7 +748,7 @@ class BaseProvider:
|
|||
action: str,
|
||||
service_name: str
|
||||
) -> None:
|
||||
raise self.server.error("Serice Actions Not Available", 503)
|
||||
raise self.server.error("Service Actions Not Available", 503)
|
||||
|
||||
async def check_virt_status(self) -> Dict[str, Any]:
|
||||
return {
|
||||
|
@ -1171,6 +1172,113 @@ class SystemdDbusProvider(BaseProvider):
|
|||
processed[key] = val
|
||||
return processed
|
||||
|
||||
# for docker klipper-moonraker image multi-service managing
|
||||
# since in container, all command is launched by normal user,
|
||||
# sudo_cmd is not needed.
|
||||
class SuperisordProvider(BaseProvider):
|
||||
def __init__(self, config: ConfigHelper) -> None:
|
||||
super().__init__(config)
|
||||
self.spv_conf: str = config.get("supervisord_config_path", "")
|
||||
|
||||
async def initialize(self) -> None:
|
||||
for svc in ("klipper", "moonraker"):
|
||||
self.available_services[svc] = {
|
||||
'active_state': "none",
|
||||
'sub_state': "unknown"
|
||||
}
|
||||
self.svc_cmd = self.shell_cmd.build_shell_command(
|
||||
f"supervisorctl {self.spv_conf}"
|
||||
f"status {' '.join(list(self.available_services.keys()))}"
|
||||
)
|
||||
await self._update_service_status(0, notify=True)
|
||||
pstats: ProcStats = self.server.lookup_component('proc_stats')
|
||||
pstats.register_stat_callback(self._update_service_status)
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
raise self.server.error(
|
||||
"[machine]: Supervisord manager can not process SHUTDOWN."
|
||||
"Please try KILL container or stop Supervisord via ssh terminal."
|
||||
)
|
||||
return
|
||||
|
||||
async def reboot(self) -> None:
|
||||
raise self.server.error(
|
||||
"[machine]: Supervisord manager can not process REBOOT."
|
||||
"Please try KILL container or stop Supervisord via ssh terminal."
|
||||
)
|
||||
return
|
||||
|
||||
async def do_service_action(
|
||||
self, action: str, service_name: str
|
||||
) -> None:
|
||||
# slow reaction for supervisord, timeout set to 6.0
|
||||
await self._exec_command(
|
||||
f"supervisorctl {self.spv_conf}"
|
||||
f"{action} {service_name}",
|
||||
timeout=6.
|
||||
)
|
||||
|
||||
async def check_virt_status(self) -> Dict[str, Any]:
|
||||
virt_id = virt_type = "none"
|
||||
if (
|
||||
os.path.exists("/.dockerenv") or
|
||||
os.path.exists("/.dockerinit")
|
||||
):
|
||||
virt_id = "docker"
|
||||
virt_type = "docker"
|
||||
return {
|
||||
'virt_type': virt_type,
|
||||
'virt_identifier': virt_id
|
||||
}
|
||||
|
||||
async def _exec_command(
|
||||
self, command: str, tries: int = 1, timeout=2.
|
||||
) -> str:
|
||||
return await self.shell_cmd.exec_cmd(
|
||||
command, proc_input=None, log_complete=False, retries=tries,
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
async def _update_service_status(self,
|
||||
sequence: int,
|
||||
notify: bool = True
|
||||
) -> None:
|
||||
if sequence % 2:
|
||||
# Update every other sequence
|
||||
return
|
||||
svcs = list(self.available_services.keys())
|
||||
try:
|
||||
# slow reaction for supervisord, timeout set to 6.0
|
||||
resp = await self.svc_cmd.run_with_response(
|
||||
log_complete=False, timeout=6.
|
||||
)
|
||||
resp_l = resp.strip().split("\n") # drop lengend
|
||||
for svc, state in zip(svcs, resp_l):
|
||||
sub_state = state.split()[1].lower()
|
||||
new_state: Dict[str, str] = {
|
||||
'active_state': "active",
|
||||
'sub_state': sub_state
|
||||
}
|
||||
if self.available_services[svc] != new_state:
|
||||
self.available_services[svc] = new_state
|
||||
if notify:
|
||||
self.server.send_event(
|
||||
"machine:service_state_changed",
|
||||
{svc: new_state})
|
||||
except Exception:
|
||||
logging.exception("Error processing service state update")
|
||||
|
||||
# service files is defined since docker build.
|
||||
# not needed to implete SV.
|
||||
async def extract_service_info(
|
||||
self,
|
||||
service: str,
|
||||
pid: int,
|
||||
properties: List[str],
|
||||
raw: bool = False
|
||||
) -> Dict[str, Any]:
|
||||
return {}
|
||||
|
||||
|
||||
# Install validation
|
||||
INSTALL_VERSION = 1
|
||||
|
|
Loading…
Reference in New Issue