94 lines
3.7 KiB
Python
94 lines
3.7 KiB
Python
|
# Any alerts produced by the player.py instances.
|
||
|
import json
|
||
|
from typing import Any, Dict, List, Optional
|
||
|
from datetime import datetime, timedelta
|
||
|
from helpers.os_environment import resolve_external_file_path
|
||
|
from helpers.alert_manager import AlertProvider
|
||
|
from baps_types.alert import CRITICAL, WARNING, Alert
|
||
|
|
||
|
MODULE = "Player" # This should match the log file, so the UI will link to the logs page.
|
||
|
|
||
|
class PlayerAlertProvider(AlertProvider):
|
||
|
|
||
|
_server_state: Dict[str, Any]
|
||
|
_states: List[Optional[Dict[str,Any]]] = []
|
||
|
_player_count: int
|
||
|
|
||
|
def __init__(self):
|
||
|
# Player count only changes after server restart, may as well just load this once.
|
||
|
with open(resolve_external_file_path("state/BAPSicleServer.json")) as file:
|
||
|
self._server_state = json.loads(file.read())
|
||
|
|
||
|
self._player_count = int(self._server_state["num_channels"])
|
||
|
self._states = [None] * self._player_count
|
||
|
|
||
|
# To simplify monitoring (and allow detection of things going super weird), we are going to read from the state file to work out the alerts.
|
||
|
def get_alerts(self):
|
||
|
for channel in range(self._player_count):
|
||
|
with open(resolve_external_file_path("state/Player{}.json".format(channel))) as file:
|
||
|
self._states[channel] = json.loads(file.read())
|
||
|
|
||
|
funcs = [self._channel_count, self._initialised, self._start_time]
|
||
|
|
||
|
alerts: List[Alert] = []
|
||
|
|
||
|
for func in funcs:
|
||
|
func_alerts = func()
|
||
|
if func_alerts:
|
||
|
alerts.extend(func_alerts)
|
||
|
|
||
|
return alerts
|
||
|
|
||
|
def _channel_count(self):
|
||
|
if self._player_count <= 0:
|
||
|
return [Alert({
|
||
|
"start_time": -1, # Now
|
||
|
"id": "no_channels",
|
||
|
"title": "There are no players configured.",
|
||
|
"description": "The number of channels configured is {}. Please set to at least 1 on the 'Server Config' page.".format(self._player_count),
|
||
|
"module": MODULE+"Handler",
|
||
|
"severity": CRITICAL
|
||
|
})]
|
||
|
|
||
|
def _initialised(self):
|
||
|
alerts: List[Alert] = []
|
||
|
for channel in range(self._player_count):
|
||
|
if self._states[channel] and not self._states[channel]["initialised"]:
|
||
|
alerts.append(Alert({
|
||
|
"start_time": -1, # Now
|
||
|
"id": "player_{}_not_initialised".format(channel),
|
||
|
"title": "Player {} is not initialised.".format(channel),
|
||
|
"description": "This typically means the player channel was not able find the configured sound output on the system. Please check the 'Player Config' and Player logs to determine the cause.",
|
||
|
"module": MODULE+str(channel),
|
||
|
"severity": CRITICAL
|
||
|
}))
|
||
|
return alerts
|
||
|
|
||
|
def _start_time(self):
|
||
|
server_start_time = self._server_state["start_time"]
|
||
|
server_start_time = datetime.fromtimestamp(server_start_time)
|
||
|
delta = timedelta(
|
||
|
seconds=30,
|
||
|
)
|
||
|
|
||
|
alerts: List[Alert] = []
|
||
|
for channel in range(self._player_count):
|
||
|
start_time = self._states[channel]["start_time"]
|
||
|
start_time = datetime.fromtimestamp(start_time)
|
||
|
if (start_time > server_start_time + delta):
|
||
|
alerts.append(Alert({
|
||
|
"start_time": -1,
|
||
|
"id": "player_{}_restarted".format(channel),
|
||
|
"title": "Player {} restarted after the server started.".format(channel),
|
||
|
"description":
|
||
|
"""Player {} last restarted at {}, after the server first started at {}, suggesting a failure.
|
||
|
|
||
|
This likely means there was an unhandled exception in the player code, causing the server to restart the player.
|
||
|
|
||
|
Please check player logs to investigate the cause. Please restart the server to clear this warning."""
|
||
|
.format(channel, str(start_time).rsplit(".",1)[0], str(server_start_time).rsplit(".",1)[0]),
|
||
|
"module": MODULE+str(channel),
|
||
|
"severity": WARNING
|
||
|
}))
|
||
|
return alerts
|