serverwatcher 5.27__tar.gz → 5.27.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {serverwatcher-5.27/src/serverwatcher.egg-info → serverwatcher-5.27.2}/PKG-INFO +1 -1
- {serverwatcher-5.27 → serverwatcher-5.27.2}/pyproject.toml +1 -1
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher/configclasses/config.py +15 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher/configclasses/messages.py +5 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher/configclasses/watcher.py +29 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher/defaultconfigs/messages.yaml +1 -0
- serverwatcher-5.27.2/src/serverwatcher/validator.py +52 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher/watcher.py +8 -5
- {serverwatcher-5.27 → serverwatcher-5.27.2/src/serverwatcher.egg-info}/PKG-INFO +1 -1
- serverwatcher-5.27/src/serverwatcher/validator.py +0 -121
- {serverwatcher-5.27 → serverwatcher-5.27.2}/LICENSE +0 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/README.md +0 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/setup.cfg +0 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher/__init__.py +0 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher/__main__.py +0 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher/configclasses/__init__.py +0 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher/defaultconfigs/config.yaml +0 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher/defaultconfigs/watcher.yaml +0 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher.egg-info/SOURCES.txt +0 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher.egg-info/dependency_links.txt +0 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher.egg-info/requires.txt +0 -0
- {serverwatcher-5.27 → serverwatcher-5.27.2}/src/serverwatcher.egg-info/top_level.txt +0 -0
|
@@ -56,3 +56,18 @@ class fallbacks:
|
|
|
56
56
|
|
|
57
57
|
clear_terminal = True
|
|
58
58
|
handle_keyboard_interrupt = True
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class rules:
|
|
62
|
+
panel_url = "required"
|
|
63
|
+
panel_api_key = "required"
|
|
64
|
+
server_id = "required"
|
|
65
|
+
server_domain = "required"
|
|
66
|
+
bridge_token = "required"
|
|
67
|
+
|
|
68
|
+
timezone = "recommended"
|
|
69
|
+
panel_name = "recommended"
|
|
70
|
+
server_port = "recommended"
|
|
71
|
+
bridge_port = "recommended"
|
|
72
|
+
|
|
73
|
+
# everything else defaults to optional
|
|
@@ -33,6 +33,7 @@ class MessagesConfig:
|
|
|
33
33
|
status_check: str = 'logging.status_check'
|
|
34
34
|
validation_fail: str = 'logging.validation_fail'
|
|
35
35
|
validation_ok: str = 'logging.validation_ok'
|
|
36
|
+
sampling_start: str = 'logging.sampling_start'
|
|
36
37
|
shutdown: str = 'logging.shutdown'
|
|
37
38
|
immediate_restart: str = 'logging.immediate_restart'
|
|
38
39
|
no_restart: str = 'logging.no_restart'
|
|
@@ -88,6 +89,7 @@ class fallbacks:
|
|
|
88
89
|
status_check = 'Checking server status...'
|
|
89
90
|
validation_fail = 'Validation FAILED.'
|
|
90
91
|
validation_ok = 'All validation checks succeeded.'
|
|
92
|
+
sampling_start = 'Starting sampling process...'
|
|
91
93
|
shutdown = 'Shutting down ServerWatcher.'
|
|
92
94
|
immediate_restart = 'Restarting immediately.'
|
|
93
95
|
no_restart = 'The server does not need to restart.'
|
|
@@ -113,3 +115,6 @@ class fallbacks:
|
|
|
113
115
|
server_back_online = 'Server is back online!'
|
|
114
116
|
server_back_online_log = 'ServerWatcher successfully restarted the server.'
|
|
115
117
|
server_failed_restart = 'Server failed to restart!'
|
|
118
|
+
|
|
119
|
+
class rules:
|
|
120
|
+
pass
|
|
@@ -68,3 +68,32 @@ class fallbacks:
|
|
|
68
68
|
restart_wait_seconds = 30
|
|
69
69
|
restart_timeout = 120
|
|
70
70
|
restart_online_interval = 2
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class rules:
|
|
74
|
+
threshold_ram = "required"
|
|
75
|
+
threshold_cpu = "required"
|
|
76
|
+
threshold_uptime = "required"
|
|
77
|
+
threshold_tps = "required"
|
|
78
|
+
|
|
79
|
+
restart_wait_seconds = "required"
|
|
80
|
+
restart_timeout = "required"
|
|
81
|
+
restart_online_interval = "required"
|
|
82
|
+
|
|
83
|
+
watch_interval = "recommended"
|
|
84
|
+
sample_duration = "recommended"
|
|
85
|
+
sample_interval = "recommended"
|
|
86
|
+
sample_outlier_drop = "recommended"
|
|
87
|
+
threshold_min_uptime = "recommended"
|
|
88
|
+
threshold_low_gap = "recommended"
|
|
89
|
+
snap_minutes = "recommended"
|
|
90
|
+
low_gap_minutes = "recommended"
|
|
91
|
+
high_gap_minutes = "recommended"
|
|
92
|
+
|
|
93
|
+
weight_restart_soon = "optional"
|
|
94
|
+
weight_ram = "optional"
|
|
95
|
+
weight_cpu = "optional"
|
|
96
|
+
weight_uptime = "optional"
|
|
97
|
+
weight_tps = "optional"
|
|
98
|
+
weight_low_uptime = "optional"
|
|
99
|
+
weight_per_player = "optional"
|
|
@@ -51,6 +51,7 @@ logging:
|
|
|
51
51
|
status_check: 'Checking server status...'
|
|
52
52
|
validation_fail: 'Validation FAILED.'
|
|
53
53
|
validation_ok: 'All validation checks succeeded.'
|
|
54
|
+
sampling_start: 'Starting sampling process...'
|
|
54
55
|
shutdown: 'Shutting down ServerWatcher.'
|
|
55
56
|
immediate_restart: 'Restarting immediately.'
|
|
56
57
|
no_restart: 'The server does not need to restart.'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from dataclasses import fields
|
|
3
|
+
|
|
4
|
+
from hungerlib import utils, loadConfig, Validator
|
|
5
|
+
|
|
6
|
+
from serverwatcher.configclasses.config import GlobalConfig
|
|
7
|
+
from serverwatcher.configclasses.messages import MessagesConfig
|
|
8
|
+
from serverwatcher.configclasses.watcher import WatcherConfig
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
utils.clearTerminal()
|
|
12
|
+
|
|
13
|
+
# You can customize messages here if you want
|
|
14
|
+
v = Validator()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def validate_global_config(c):
|
|
18
|
+
for f in fields(GlobalConfig):
|
|
19
|
+
if not f.name.startswith("__"):
|
|
20
|
+
v.check_field(c, f.name)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def validate_watcher_config(c):
|
|
24
|
+
for f in fields(WatcherConfig):
|
|
25
|
+
if not f.name.startswith("__"):
|
|
26
|
+
v.check_field(c, f.name)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def validate_messages_config(c):
|
|
30
|
+
for f in fields(MessagesConfig):
|
|
31
|
+
if not f.name.startswith("__"):
|
|
32
|
+
v.check_field(c, f.name)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def validate_all():
|
|
36
|
+
config = loadConfig(GlobalConfig)
|
|
37
|
+
messages = loadConfig(MessagesConfig)
|
|
38
|
+
watcher = loadConfig(WatcherConfig)
|
|
39
|
+
|
|
40
|
+
v.validate_key_types(config, GlobalConfig)
|
|
41
|
+
v.validate_key_types(messages, MessagesConfig)
|
|
42
|
+
v.validate_key_types(watcher, WatcherConfig)
|
|
43
|
+
|
|
44
|
+
validate_global_config(config)
|
|
45
|
+
validate_watcher_config(watcher)
|
|
46
|
+
validate_messages_config(messages)
|
|
47
|
+
|
|
48
|
+
return v.run(config, messages, watcher)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if __name__ == '__main__':
|
|
52
|
+
validate_all()
|
|
@@ -93,6 +93,10 @@ class ServerWatcher:
|
|
|
93
93
|
|
|
94
94
|
self.tz = ZoneInfo(self.config.timezone)
|
|
95
95
|
|
|
96
|
+
def clearTerminal(self):
|
|
97
|
+
if self.config.clear_terminal:
|
|
98
|
+
utils.clearTerminal()
|
|
99
|
+
|
|
96
100
|
def shutdown(self):
|
|
97
101
|
self.router.info(self.messages.shutdown)
|
|
98
102
|
raise SystemExit
|
|
@@ -165,8 +169,9 @@ class ServerWatcher:
|
|
|
165
169
|
self.router.error(self.messages.validation_fail)
|
|
166
170
|
self.shutdown()
|
|
167
171
|
|
|
172
|
+
self.router.info(self.messages.sampling_start)
|
|
168
173
|
self.server.refresh()
|
|
169
|
-
snap = utils.Snapshot(self.server, duration=self.watcherconfig.sample_duration, interval=self.
|
|
174
|
+
snap = utils.Snapshot(self.server, duration=self.watcherconfig.sample_duration, interval=self.watcherconfig.sample_interval, drop_outliers=self.watcherconfig.sample_outlier_drop, gb=True)
|
|
170
175
|
|
|
171
176
|
pro = 0
|
|
172
177
|
anti = 0
|
|
@@ -245,15 +250,13 @@ class ServerWatcher:
|
|
|
245
250
|
if self.config.handle_keyboard_interrupt:
|
|
246
251
|
try:
|
|
247
252
|
while True:
|
|
248
|
-
|
|
249
|
-
utils.clearTerminal()
|
|
253
|
+
self.clearTerminal()
|
|
250
254
|
self.evaluate()
|
|
251
255
|
time.sleep(self.watcherconfig.watch_interval)
|
|
252
256
|
except KeyboardInterrupt:
|
|
253
257
|
self.shutdown()
|
|
254
258
|
else:
|
|
255
259
|
while True:
|
|
256
|
-
|
|
257
|
-
utils.clearTerminal()
|
|
260
|
+
self.clearTerminal()
|
|
258
261
|
self.evaluate()
|
|
259
262
|
time.sleep(self.watcherconfig.watch_interval)
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
from dataclasses import fields
|
|
3
|
-
|
|
4
|
-
from hungerlib import utils, loadConfig, Validator
|
|
5
|
-
|
|
6
|
-
from serverwatcher.configclasses.config import GlobalConfig
|
|
7
|
-
from serverwatcher.configclasses.messages import MessagesConfig
|
|
8
|
-
from serverwatcher.configclasses.watcher import WatcherConfig
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
utils.clearTerminal()
|
|
12
|
-
v = Validator()
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def validate_global_config(config):
|
|
16
|
-
c = config
|
|
17
|
-
|
|
18
|
-
# timezone
|
|
19
|
-
v.check_field(c, "timezone")
|
|
20
|
-
if c.timezone == "":
|
|
21
|
-
v.errors.append('timezone: must not be empty')
|
|
22
|
-
|
|
23
|
-
# panel
|
|
24
|
-
v.check_field(c, "panel_name")
|
|
25
|
-
v.check_field(c, "panel_url", allow_fallback=False)
|
|
26
|
-
v.check_field(c, "panel_api_key", allow_fallback=False)
|
|
27
|
-
|
|
28
|
-
if c.panel_url and not (c.panel_url.startswith("http://") or c.panel_url.startswith("https://")):
|
|
29
|
-
v.errors.append(f'panel_url: must start with "http://" or "https://" (got "{c.panel_url}")')
|
|
30
|
-
|
|
31
|
-
if c.panel_api_key and not c.panel_api_key.startswith("ptlc_"):
|
|
32
|
-
v.errors.append(f'panel_api_key: must be a valid Pterodactyl client API key (got "{c.panel_api_key}")')
|
|
33
|
-
if c.panel_api_key and c.panel_api_key.startswith("plta_"):
|
|
34
|
-
v.errors.append(f'panel_api_key: should not be an application key (got "{c.panel_api_key}")')
|
|
35
|
-
|
|
36
|
-
# server
|
|
37
|
-
v.check_field(c, "server_name")
|
|
38
|
-
v.check_field(c, "server_id", allow_fallback=False)
|
|
39
|
-
v.check_field(c, "server_domain", allow_fallback=False)
|
|
40
|
-
v.check_field(c, "server_port")
|
|
41
|
-
|
|
42
|
-
if c.server_domain and (c.server_domain.startswith("http://") or c.server_domain.startswith("https://")):
|
|
43
|
-
v.errors.append(f'server_domain: must not contain protocol (got "{c.server_domain}")')
|
|
44
|
-
|
|
45
|
-
if c.server_port is not None and not (1 <= c.server_port <= 65535):
|
|
46
|
-
v.errors.append(f'server_port: must be 1–65535 (got "{c.server_port}")')
|
|
47
|
-
|
|
48
|
-
# hungerbridge
|
|
49
|
-
v.check_field(c, "bridge_token", allow_fallback=False)
|
|
50
|
-
v.check_field(c, "bridge_port")
|
|
51
|
-
if c.bridge_port is not None and not (1 <= c.bridge_port <= 65535):
|
|
52
|
-
v.errors.append(f'bridge_port: must be 1–65535 (got "{c.bridge_port}")')
|
|
53
|
-
|
|
54
|
-
# logger
|
|
55
|
-
for key in [
|
|
56
|
-
"enable_logging", "logger_name", "log_path",
|
|
57
|
-
"info_prefix", "warn_prefix", "error_prefix"
|
|
58
|
-
]:
|
|
59
|
-
v.check_field(c, key)
|
|
60
|
-
|
|
61
|
-
# terminal
|
|
62
|
-
v.check_field(c, "clear_terminal")
|
|
63
|
-
v.check_field(c, "handle_keyboard_interrupt")
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def validate_watcher_config(watcherconfig):
|
|
67
|
-
c = watcherconfig
|
|
68
|
-
raw = c.raw
|
|
69
|
-
fb = c.fallbacks
|
|
70
|
-
|
|
71
|
-
if c.restart_wait_seconds <= 0:
|
|
72
|
-
v.errors.append(f'restart_wait_seconds: must be > 0 (got {c.restart_wait_seconds})')
|
|
73
|
-
|
|
74
|
-
if c.threshold_cpu <= 0:
|
|
75
|
-
v.errors.append(f'threshold_cpu: must be > 0 (got {c.threshold_cpu})')
|
|
76
|
-
|
|
77
|
-
if c.threshold_ram <= 0:
|
|
78
|
-
v.errors.append(f'threshold_ram: must be > 0 (got {c.threshold_ram})')
|
|
79
|
-
|
|
80
|
-
if c.threshold_tps <= 0 or c.threshold_tps > 20:
|
|
81
|
-
v.errors.append(f'threshold_tps: must be 1–20 (got "{c.threshold_tps}")')
|
|
82
|
-
|
|
83
|
-
if raw.snap_minutes is None:
|
|
84
|
-
v.warnings.append(f'snap_minutes: key does not exist, using fallback "{fb.snap_minutes}"')
|
|
85
|
-
|
|
86
|
-
if not isinstance(c.snap_minutes, list) or not c.snap_minutes:
|
|
87
|
-
v.errors.append(f'snap_minutes: must be a non-empty list of minute marks (got {c.snap_minutes!r})')
|
|
88
|
-
else:
|
|
89
|
-
bad = [m for m in c.snap_minutes if not isinstance(m, int) or not (0 <= m <= 59)]
|
|
90
|
-
if bad:
|
|
91
|
-
v.errors.append(f'snap_minutes: all values must be integers 0–59 (bad values: {bad})')
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def validate_messages_config(messages):
|
|
95
|
-
pass # may add validation later
|
|
96
|
-
# for f in fields(MessagesConfig):
|
|
97
|
-
# if f.name.startswith("__"):
|
|
98
|
-
# continue
|
|
99
|
-
# value = getattr(messages, f.name)
|
|
100
|
-
# if not isinstance(value, str) or value.strip() == "":
|
|
101
|
-
# v.errors.append(f'MessagesConfig.{f.name}: must be a non-empty string')
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def validate_all():
|
|
105
|
-
config = loadConfig(GlobalConfig)
|
|
106
|
-
messages = loadConfig(MessagesConfig)
|
|
107
|
-
watcherconfig = loadConfig(WatcherConfig)
|
|
108
|
-
|
|
109
|
-
v.validate_key_types(config, GlobalConfig)
|
|
110
|
-
v.validate_key_types(messages, MessagesConfig)
|
|
111
|
-
v.validate_key_types(watcherconfig, WatcherConfig)
|
|
112
|
-
|
|
113
|
-
validate_global_config(config)
|
|
114
|
-
validate_watcher_config(watcherconfig)
|
|
115
|
-
validate_messages_config(messages)
|
|
116
|
-
|
|
117
|
-
return v.run(config, messages, watcherconfig)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if __name__ == '__main__':
|
|
121
|
-
validate_all()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|