refactor: remove service id altogether
This commit is contained in:
@@ -0,0 +1,230 @@
|
||||
"""
|
||||
General Infos
|
||||
- This application is intended to be run as a service on the SLS beam dynamics server for high level applications sls-vserv-bd-hla01
|
||||
- Control of this service is handled via EPICS from the associated IOC AGEBD-CPCL-MASTER hosted on sls-vserv-bd-01
|
||||
- Monitoring of this service is handled via EPICS from the generic IOC AGEBD-CPCL-ALH hosted on sls-vserv-bd-01
|
||||
- Supervision of this service is handled via EPICS -> MASTER-SERVICE -> SYSTEMD -> This Service (wip)
|
||||
|
||||
Specific Infos
|
||||
- This service will provide the possibility to stop, start, restart and change the target version of all available SLS beam dynamics services
|
||||
|
||||
Usage: screen -S MASTER bash -c 'source /opt/gfa/python ; python AGEBD-SERVICE-MASTER.py'
|
||||
"""
|
||||
|
||||
# TODO: remove -- test cicd 9
|
||||
|
||||
import html
|
||||
import os
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from time import sleep
|
||||
|
||||
import typer
|
||||
import yaml
|
||||
|
||||
from agebd.enums import LogLevel
|
||||
from agebd.pv import PVLink as PV
|
||||
from agebd.runner import CallbackRunner
|
||||
from agebd.service.base import BaseService
|
||||
from agebd.service.pvs import BasePVs
|
||||
from agebd.utils import get_pv_class, init_logging, printgetversion
|
||||
|
||||
__version__ = printgetversion(__file__)
|
||||
|
||||
PV = get_pv_class()
|
||||
|
||||
HERE_DIR = Path(__file__).parent
|
||||
CONFIG_DIR = HERE_DIR / ".." / "config"
|
||||
HLA_NAMES_PATH = CONFIG_DIR / "hla_names.yml"
|
||||
|
||||
|
||||
def read_hla_names():
|
||||
with open(HLA_NAMES_PATH, "r") as file:
|
||||
config = yaml.safe_load(file)
|
||||
|
||||
# Access your list of apps directly
|
||||
apps_list = config.get("hla_apps", [])
|
||||
|
||||
return apps_list
|
||||
|
||||
|
||||
class PVs(BasePVs):
|
||||
# output to master panel
|
||||
logbook = PV("AGEBD-MASTER:LOG")
|
||||
logclear = PV("AGEBD-MASTER:LOG-CLEAR.VAL")
|
||||
systemdreq = PV("AGEBD-MASTER:GET-SYSTEMD-STATUS")
|
||||
|
||||
def __init__(self, service_name: str, pv_factory):
|
||||
super().__init__(service_name, pv_factory)
|
||||
PV = pv_factory
|
||||
|
||||
HLAnames = read_hla_names()
|
||||
|
||||
self.HLAs = []
|
||||
for hla in HLAnames:
|
||||
for todo in ["-START", "-RESTART", "-LOGS"]:
|
||||
pv1 = "AGEBD-MASTER:" + hla + todo
|
||||
pv2 = "AGEBD-MASTER:" + hla + todo + "-REQ"
|
||||
self.HLAs.append(["AGEBD-SERVICE-" + hla, PV(pv1), PV(pv2)])
|
||||
|
||||
# prepare sum of all start PVs on IOC for callback
|
||||
self.CallbackPV = PV("AGEBD-MASTER:CALLBACK-ANY-REQ.VAL")
|
||||
|
||||
|
||||
class Service(BaseService[PVs]):
|
||||
def __init__(
|
||||
self, name: str, pvs: PVs, version: str = __version__, sleep_interval: float = 0.1
|
||||
):
|
||||
super().__init__(name, pvs, version, sleep_interval)
|
||||
|
||||
self.systemd_services = [HLA[0] for HLA in pvs.HLAs[::2]]
|
||||
self.logs = []
|
||||
self.log_head = ["Master Service Log, " + str(datetime.now())[:-7] + "<br>"]
|
||||
|
||||
def update(self):
|
||||
# update header
|
||||
self.log_head = ["Master Service Log, " + str(datetime.now())[:-7] + "<br>"]
|
||||
|
||||
# when callback fired, write elog entry
|
||||
if self.CallbackFired:
|
||||
# Confirm run of callback
|
||||
self.CallbackFired = 0
|
||||
|
||||
# clear logs
|
||||
self.logs = []
|
||||
self.logs.append('<table border="1" style="border-collapse: collapse;">')
|
||||
|
||||
for HLA in self.pvs.HLAs[::-1]:
|
||||
# Check if request is confirmed by IOC
|
||||
if HLA[1].get():
|
||||
# Stop request
|
||||
HLA[2].put(0)
|
||||
|
||||
# Get the service name
|
||||
systemd_service = HLA[0]
|
||||
|
||||
if "RESTART" in HLA[1].pvname:
|
||||
# Check the status of the service
|
||||
self.systemctl_status(systemd_service, lines=20)
|
||||
|
||||
# Reload the unit files
|
||||
subprocess.run(["systemctl", "--user", "daemon-reload"])
|
||||
|
||||
# Restart the service
|
||||
subprocess.run(["systemctl", "--user", "restart", systemd_service])
|
||||
|
||||
# Check the status of the service
|
||||
self.systemctl_status(systemd_service, lines=20)
|
||||
|
||||
elif "START" in HLA[1].pvname:
|
||||
# Check the status of the service
|
||||
self.systemctl_status(systemd_service, lines=20)
|
||||
|
||||
# Start the service
|
||||
subprocess.run(["systemctl", "--user", "start", systemd_service])
|
||||
|
||||
# Check the status of the service
|
||||
self.systemctl_status(systemd_service, lines=20)
|
||||
|
||||
elif "LOGS" in HLA[1].pvname:
|
||||
# Check the status of the service
|
||||
self.systemctl_status(systemd_service, lines=100)
|
||||
|
||||
elif self.pvs.logclear.get():
|
||||
self.logs = []
|
||||
self.pvs.logclear.put(0)
|
||||
|
||||
elif self.pvs.systemdreq.get():
|
||||
self.logs = []
|
||||
self.logs.append(
|
||||
'<table cellpadding="15px" border="1" style="border-collapse: collapse;">'
|
||||
)
|
||||
self.pvs.systemdreq.put(0)
|
||||
|
||||
for systemd_service in self.systemd_services:
|
||||
self.systemctl_status(systemd_service)
|
||||
|
||||
self.logs.append("</table>")
|
||||
|
||||
else:
|
||||
# wait for callback(s) to fire
|
||||
sleep(0.2)
|
||||
|
||||
# Nchars = len(''.join(self.log_head + [' Chars <br>'] + self.logs))
|
||||
# logentry = self.log_head + ['{:.0f} Chars <br>'.format(Nchars)] + self.logs
|
||||
logentry = self.log_head + self.logs
|
||||
self.pvs.logbook.put("".join(logentry))
|
||||
|
||||
def systemctl_status(self, systemd_service, lines=0):
|
||||
self.logs.append("<tr><td>")
|
||||
# Check the status of the service
|
||||
os.environ["SYSTEMD_COLORS"] = "1"
|
||||
command = ["systemctl", "--user", "--lines=0", "status", systemd_service]
|
||||
output = (
|
||||
subprocess.run(command, capture_output=True, text=True)
|
||||
.stdout.encode("ascii", errors="xmlcharrefreplace")
|
||||
.decode()
|
||||
)
|
||||
output = output.replace("\x1b[0;1;39m", "<span>") # ANSI Default
|
||||
output = output.replace(
|
||||
"\x1b[0;1;32m", '<span style="color:#00aa00; font-size:14.0pt; ">'
|
||||
) # ANSI Green
|
||||
output = output.replace(
|
||||
"\x1b[0;1;31m", '<span style="color:#800000; font-size:14.0pt; ">'
|
||||
) # ANSI Red
|
||||
output = output.replace("\x1b[0m", "</span>")
|
||||
output = output.replace("\x1b]8;;\x07", "</a>") # end of hyperlink
|
||||
output = output.replace("\x1b]8;;", '<a href="') # start of hyperlink
|
||||
output = output.replace(
|
||||
"\x07/sls/bd/bin/systemd/AGEBD-SERVICE-", '">/sls/bd/bin/systemd/AGEBD-SERVICE-'
|
||||
)
|
||||
status = "<br>".join(output.splitlines())
|
||||
self.logs.append(status)
|
||||
self.logs.append("<br><br>")
|
||||
|
||||
if lines > 0:
|
||||
os.environ["SYSTEMD_COLORS"] = "0"
|
||||
command = [
|
||||
"journalctl",
|
||||
"--user",
|
||||
"--lines={:.0f}".format(lines),
|
||||
"--no-hostname",
|
||||
"--no-pager",
|
||||
"--all",
|
||||
"--unit={:}".format(systemd_service),
|
||||
]
|
||||
output = (
|
||||
subprocess.run(command, capture_output=True, text=True)
|
||||
.stdout.encode("ascii", errors="xmlcharrefreplace")
|
||||
.decode()
|
||||
)
|
||||
output = html.escape(output)
|
||||
status = "<br>".join(output.splitlines()[::-1][:-1])
|
||||
|
||||
print(command)
|
||||
self.logs.append(status)
|
||||
self.logs.append("</td></tr>")
|
||||
|
||||
|
||||
def main(
|
||||
log_level: LogLevel = typer.Option(
|
||||
LogLevel.INFO,
|
||||
"--log-level",
|
||||
"-l",
|
||||
# Looks at the OS env variable first. If empty, falls back to "INFO"
|
||||
envvar="AGEBD_LOG_LEVEL",
|
||||
# help="Set log level: DEBUG, INFO, WARNING, ERROR, CRITICAL",
|
||||
case_sensitive=False,
|
||||
),
|
||||
):
|
||||
init_logging(log_level)
|
||||
service_name = "MASTER"
|
||||
pvs = PVs(service_name=service_name, pv_factory=PV)
|
||||
service = Service(name=service_name, pvs=pvs)
|
||||
runner = CallbackRunner(service=service)
|
||||
runner.start()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
typer.run(main)
|
||||
Reference in New Issue
Block a user