machine: support supervisord service info extraction
Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
5aa645f974
commit
c69441955a
|
@ -20,6 +20,7 @@ import shutil
|
||||||
import distro
|
import distro
|
||||||
import tempfile
|
import tempfile
|
||||||
import getpass
|
import getpass
|
||||||
|
import configparser
|
||||||
from confighelper import FileSourceWrapper
|
from confighelper import FileSourceWrapper
|
||||||
from utils import MOONRAKER_PATH
|
from utils import MOONRAKER_PATH
|
||||||
|
|
||||||
|
@ -756,7 +757,8 @@ class Machine:
|
||||||
if not svc_info:
|
if not svc_info:
|
||||||
return
|
return
|
||||||
name = svc_info.get("unit_name", "unknown")
|
name = svc_info.get("unit_name", "unknown")
|
||||||
msg = f"\nSystemd unit {name}:"
|
manager = svc_info.get("manager", "systemd").capitalize()
|
||||||
|
msg = f"\n{manager} unit {name}:"
|
||||||
for key, val in svc_info.items():
|
for key, val in svc_info.items():
|
||||||
if key == "properties":
|
if key == "properties":
|
||||||
msg += "\nProperties:"
|
msg += "\nProperties:"
|
||||||
|
@ -929,6 +931,7 @@ class SystemdCliProvider(BaseProvider):
|
||||||
unit_name = resp.split(maxsplit=2)[1]
|
unit_name = resp.split(maxsplit=2)[1]
|
||||||
service_info["unit_name"] = unit_name
|
service_info["unit_name"] = unit_name
|
||||||
service_info["is_default"] = True
|
service_info["is_default"] = True
|
||||||
|
service_info["manager"] = "systemd"
|
||||||
if unit_name != expected_name:
|
if unit_name != expected_name:
|
||||||
service_info["is_default"] = False
|
service_info["is_default"] = False
|
||||||
logging.info(
|
logging.info(
|
||||||
|
@ -1180,6 +1183,7 @@ class SystemdDbusProvider(BaseProvider):
|
||||||
unit_name = await unit_intf.get_id() # type: ignore
|
unit_name = await unit_intf.get_id() # type: ignore
|
||||||
service_info["unit_name"] = unit_name
|
service_info["unit_name"] = unit_name
|
||||||
service_info["is_default"] = True
|
service_info["is_default"] = True
|
||||||
|
service_info["manager"] = "systemd"
|
||||||
if unit_name != expected_name:
|
if unit_name != expected_name:
|
||||||
service_info["is_default"] = False
|
service_info["is_default"] = False
|
||||||
logging.info(
|
logging.info(
|
||||||
|
@ -1280,7 +1284,11 @@ class SupervisordProvider(BaseProvider):
|
||||||
}
|
}
|
||||||
|
|
||||||
async def _exec_supervisorctl_command(
|
async def _exec_supervisorctl_command(
|
||||||
self, args: str, tries: int = 1, timeout=2.
|
self,
|
||||||
|
args: str,
|
||||||
|
tries: int = 1,
|
||||||
|
timeout: float = 2.,
|
||||||
|
success_codes: Optional[List[int]] = None
|
||||||
) -> str:
|
) -> str:
|
||||||
if self.spv_conf:
|
if self.spv_conf:
|
||||||
cmd = f"supervisorctl -c {self.spv_conf} {args}"
|
cmd = f"supervisorctl -c {self.spv_conf} {args}"
|
||||||
|
@ -1288,12 +1296,12 @@ class SupervisordProvider(BaseProvider):
|
||||||
cmd = f"supervisorctl {args}"
|
cmd = f"supervisorctl {args}"
|
||||||
return await self.shell_cmd.exec_cmd(
|
return await self.shell_cmd.exec_cmd(
|
||||||
cmd, proc_input=None, log_complete=False, retries=tries,
|
cmd, proc_input=None, log_complete=False, retries=tries,
|
||||||
timeout=timeout
|
timeout=timeout, success_codes=success_codes
|
||||||
)
|
)
|
||||||
|
|
||||||
async def _detect_active_services(self) -> None:
|
async def _detect_active_services(self) -> None:
|
||||||
machine: Machine = self.server.lookup_component("machine")
|
machine: Machine = self.server.lookup_component("machine")
|
||||||
units: Dict[str, Any] = await self._list_processes()
|
units: Dict[str, Any] = await self._get_process_info()
|
||||||
for unit, info in units.items():
|
for unit, info in units.items():
|
||||||
if machine.is_service_allowed(unit):
|
if machine.is_service_allowed(unit):
|
||||||
self.available_services[unit] = {
|
self.available_services[unit] = {
|
||||||
|
@ -1301,20 +1309,34 @@ class SupervisordProvider(BaseProvider):
|
||||||
'sub_state': info["state"]
|
'sub_state': info["state"]
|
||||||
}
|
}
|
||||||
|
|
||||||
async def _list_processes(self) -> Dict[str, Any]:
|
async def _get_process_info(
|
||||||
|
self, process_names: Optional[List[str]] = None
|
||||||
|
) -> Dict[str, Any]:
|
||||||
units: Dict[str, Any] = {}
|
units: Dict[str, Any] = {}
|
||||||
|
cmd = "status"
|
||||||
|
if process_names is not None:
|
||||||
|
cmd = f"status {' '.join(process_names)}"
|
||||||
try:
|
try:
|
||||||
resp = await self._exec_supervisorctl_command("status", timeout=6.)
|
resp = await self._exec_supervisorctl_command(
|
||||||
|
cmd, timeout=6., success_codes=[0, 3]
|
||||||
|
)
|
||||||
lines = [line.strip() for line in resp.split("\n") if line.strip()]
|
lines = [line.strip() for line in resp.split("\n") if line.strip()]
|
||||||
except Exception:
|
except Exception:
|
||||||
return {}
|
return {}
|
||||||
for line in lines:
|
for line in lines:
|
||||||
parts = line.split()
|
parts = line.split()
|
||||||
units[parts[0]] = {
|
name: str = parts[0]
|
||||||
"state": parts[1].lower(),
|
state: str = parts[1].lower()
|
||||||
|
if state == "running" and len(parts) >= 6:
|
||||||
|
units[name] = {
|
||||||
|
"state": state,
|
||||||
"pid": int(parts[3].rstrip(",")),
|
"pid": int(parts[3].rstrip(",")),
|
||||||
"uptime": parts[5]
|
"uptime": parts[5]
|
||||||
}
|
}
|
||||||
|
else:
|
||||||
|
units[name] = {
|
||||||
|
"state": parts[1].lower()
|
||||||
|
}
|
||||||
return units
|
return units
|
||||||
|
|
||||||
async def _update_service_status(self,
|
async def _update_service_status(self,
|
||||||
|
@ -1328,7 +1350,7 @@ class SupervisordProvider(BaseProvider):
|
||||||
try:
|
try:
|
||||||
# slow reaction for supervisord, timeout set to 6.0
|
# slow reaction for supervisord, timeout set to 6.0
|
||||||
resp = await self.svc_cmd.run_with_response(
|
resp = await self.svc_cmd.run_with_response(
|
||||||
log_complete=False, timeout=6.
|
log_complete=False, timeout=6., success_codes=[0, 3]
|
||||||
)
|
)
|
||||||
resp_l = resp.strip().split("\n") # drop lengend
|
resp_l = resp.strip().split("\n") # drop lengend
|
||||||
for svc, state in zip(svcs, resp_l):
|
for svc, state in zip(svcs, resp_l):
|
||||||
|
@ -1346,8 +1368,36 @@ class SupervisordProvider(BaseProvider):
|
||||||
except Exception:
|
except Exception:
|
||||||
logging.exception("Error processing service state update")
|
logging.exception("Error processing service state update")
|
||||||
|
|
||||||
# service files is defined since docker build.
|
async def _find_service_by_pid(
|
||||||
# not needed to implete SV.
|
self, expected_name: str, pid: int
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
service_info: Dict[str, Any] = {}
|
||||||
|
for _ in range(5):
|
||||||
|
proc_info = await self._get_process_info(
|
||||||
|
list(self.available_services.keys())
|
||||||
|
)
|
||||||
|
service_info["unit_name"] = expected_name
|
||||||
|
service_info["is_default"] = True
|
||||||
|
service_info["manager"] = "supervisord"
|
||||||
|
need_retry = False
|
||||||
|
for name, info in proc_info.items():
|
||||||
|
if "pid" not in info:
|
||||||
|
need_retry |= info["state"] == "starting"
|
||||||
|
elif info["pid"] == pid:
|
||||||
|
if name != expected_name:
|
||||||
|
service_info["unit_name"] = name
|
||||||
|
service_info["is_default"] = False
|
||||||
|
logging.info(
|
||||||
|
"Detected alternate unit name for "
|
||||||
|
f"{expected_name}: {name}"
|
||||||
|
)
|
||||||
|
return service_info
|
||||||
|
if need_retry:
|
||||||
|
await asyncio.sleep(1.)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return {}
|
||||||
|
|
||||||
async def extract_service_info(
|
async def extract_service_info(
|
||||||
self,
|
self,
|
||||||
service: str,
|
service: str,
|
||||||
|
@ -1355,7 +1405,43 @@ class SupervisordProvider(BaseProvider):
|
||||||
properties: Optional[List[str]] = None,
|
properties: Optional[List[str]] = None,
|
||||||
raw: bool = False
|
raw: bool = False
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
service_info = await self._find_service_by_pid(service, pid)
|
||||||
|
if not service_info:
|
||||||
|
logging.info(
|
||||||
|
f"Unable to locate service info for {service}, pid: {pid}"
|
||||||
|
)
|
||||||
return {}
|
return {}
|
||||||
|
# locate supervisord.conf
|
||||||
|
if self.spv_conf:
|
||||||
|
spv_path = pathlib.Path(self.spv_conf)
|
||||||
|
if not spv_path.is_file():
|
||||||
|
logging.info(
|
||||||
|
f"Invalid supervisord configuration file: {self.spv_conf}"
|
||||||
|
)
|
||||||
|
return service_info
|
||||||
|
else:
|
||||||
|
default_config_locations = [
|
||||||
|
"/etc/supervisord.conf",
|
||||||
|
"/etc/supervisor/supervisord.conf"
|
||||||
|
]
|
||||||
|
for conf_path in default_config_locations:
|
||||||
|
spv_path = pathlib.Path(conf_path)
|
||||||
|
if spv_path.is_file():
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
logging.info("Failed to locate supervisord.conf")
|
||||||
|
return service_info
|
||||||
|
spv_config = configparser.ConfigParser(interpolation=None)
|
||||||
|
spv_config.read_string(spv_path.read_text())
|
||||||
|
unit = service_info["unit_name"]
|
||||||
|
section_name = f"program:{unit}"
|
||||||
|
if not spv_config.has_section(section_name):
|
||||||
|
logging.info(
|
||||||
|
f"Unable to location supervisor section {section_name}"
|
||||||
|
)
|
||||||
|
return service_info
|
||||||
|
service_info["properties"] = dict(spv_config[section_name])
|
||||||
|
return service_info
|
||||||
|
|
||||||
|
|
||||||
# Install validation
|
# Install validation
|
||||||
|
|
Loading…
Reference in New Issue