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
|
provider: systemd_dbus
|
||||||
# The provider implementation used to collect system service information
|
# The provider implementation used to collect system service information
|
||||||
# and run service actions (ie: start, restart, stop). This can be "none",
|
# 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
|
# "supervisord", "systemd_dbus", or "systemd_cli". If the provider is set
|
||||||
# action APIs will be disabled. The default is systemd_dbus.
|
# to "none" service action APIs will be disabled.
|
||||||
|
# The default is systemd_dbus.
|
||||||
sudo_password:
|
sudo_password:
|
||||||
# The password for the linux user. When set Moonraker can run linux commands
|
# The password for the linux user. When set Moonraker can run linux commands
|
||||||
# that require elevated permissions. This option accepts Jinja2 Templates,
|
# 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
|
# By default Moonraker will not attempt to revalidate if a previous attempt
|
||||||
# at validation successfully completed. Setting this value to True will force
|
# at validation successfully completed. Setting this value to True will force
|
||||||
# Moonraker to perform validation. The default is False.
|
# 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
|
!!! Note
|
||||||
|
|
|
@ -108,7 +108,8 @@ class Machine:
|
||||||
providers: Dict[str, type] = {
|
providers: Dict[str, type] = {
|
||||||
"none": BaseProvider,
|
"none": BaseProvider,
|
||||||
"systemd_cli": SystemdCliProvider,
|
"systemd_cli": SystemdCliProvider,
|
||||||
"systemd_dbus": SystemdDbusProvider
|
"systemd_dbus": SystemdDbusProvider,
|
||||||
|
"supervisord": SuperisordProvider
|
||||||
}
|
}
|
||||||
self.provider_type = config.get('provider', 'systemd_dbus')
|
self.provider_type = config.get('provider', 'systemd_dbus')
|
||||||
pclass = providers.get(self.provider_type)
|
pclass = providers.get(self.provider_type)
|
||||||
|
@ -747,7 +748,7 @@ class BaseProvider:
|
||||||
action: str,
|
action: str,
|
||||||
service_name: str
|
service_name: str
|
||||||
) -> None:
|
) -> 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]:
|
async def check_virt_status(self) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
|
@ -1171,6 +1172,113 @@ class SystemdDbusProvider(BaseProvider):
|
||||||
processed[key] = val
|
processed[key] = val
|
||||||
return processed
|
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 validation
|
||||||
INSTALL_VERSION = 1
|
INSTALL_VERSION = 1
|
||||||
|
|
Loading…
Reference in New Issue