Coverage for gws-app/gws/server/monitor.py: 35%
107 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 22:59 +0200
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 22:59 +0200
1import os
3import gws
4import gws.config
5import gws.lib.lock
6import gws.lib.osx
7import gws.lib.watcher
8import gws.server.uwsgi_module
10from . import control
12_LOCK_FILE = '/tmp/monitor.lock'
13_RELOAD_FILE = '/tmp/monitor.reload'
14_RECONFIGURE_FILE = '/tmp/monitor.reconfigure'
15_TICK_FREQUENCY = 3
18class Object(gws.ServerMonitor):
19 watchPaths: set[str]
20 enabled: bool
21 frequency: int
22 watcher: gws.lib.watcher.Watcher
23 dirs: list
24 files: list
25 tasks: list[gws.Node]
26 taskTime: int
28 def configure(self):
29 self.frequency = self.cfg('frequency', default=30)
30 self.dirs = []
31 self.files = []
32 self.tasks = []
33 self.taskTime = 0
35 def watch_directory(self, dirname, pattern, recursive=False):
36 self.dirs.append((dirname, pattern, recursive))
38 def watch_file(self, path):
39 self.files.append(path)
41 def register_periodic_task(self, obj):
42 self.tasks.append(obj)
44 def schedule_reload(self, with_reconfigure=False):
45 gws.log.info(f'MONITOR: reload scheduled {with_reconfigure=}')
46 os.open(_RECONFIGURE_FILE if with_reconfigure else _RELOAD_FILE, os.O_CREAT | os.O_WRONLY)
48 def start(self):
50 self._check_unlink(_LOCK_FILE)
51 self._check_unlink(_RELOAD_FILE)
52 self._check_unlink(_RECONFIGURE_FILE)
54 self.taskTime = gws.u.stime()
56 if not self.cfg('disableWatch'):
57 def notify(evt, path):
58 os.open(_RECONFIGURE_FILE, os.O_CREAT | os.O_WRONLY)
60 self.watcher = gws.lib.watcher.new(notify)
62 for d in self.dirs:
63 self.watcher.add_directory(*d)
64 for f in self.files:
65 self.watcher.add_file(f)
67 self.watcher.start()
69 uwsgi = gws.server.uwsgi_module.load()
70 uwsgi.register_signal(42, 'worker2', self._tick)
71 uwsgi.add_timer(42, _TICK_FREQUENCY)
73 gws.log.info(f'MONITOR: started')
75 def _tick(self, signo):
77 do_reconfigure = self._check_unlink(_RECONFIGURE_FILE)
78 do_reload = self._check_unlink(_RELOAD_FILE)
79 do_tasks = (gws.u.stime() - self.taskTime >= self.frequency) and self.tasks
81 if not do_reconfigure and not do_reload and not do_tasks:
82 return
84 gws.log.debug(f'MONITOR: tick {do_reconfigure=} {do_reload=} {do_tasks=}')
86 try:
87 os.open(_LOCK_FILE, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
88 except FileExistsError:
89 gws.log.debug(f'MONITOR: locked')
90 return
92 try:
93 if do_reconfigure:
94 self._reload(True)
95 elif do_reload:
96 self._reload(False)
97 elif do_tasks:
98 self._run_periodic_tasks()
99 self.taskTime = gws.u.stime()
100 finally:
101 self._check_unlink(_LOCK_FILE)
103 def _reload(self, with_reconfigure):
104 gws.log.info(f'MONITOR: reloading...')
106 if not self._reload2(with_reconfigure):
107 return
109 # ok, reload ourselves
110 gws.log.info(f'MONITOR: bye bye')
111 control.reload_app('spool')
113 def _reload2(self, with_reconfigure):
114 if with_reconfigure:
115 try:
116 control.configure_and_store()
117 except Exception as exc:
118 gws.log.exception(f'MONITOR: configuration error {exc!r}')
119 return False
121 try:
122 control.reload_app('mapproxy')
123 control.reload_app('web')
124 return True
125 except Exception as exc:
126 gws.log.exception(f'MONITOR: reload error {exc!r}')
127 return False
129 def _run_periodic_tasks(self):
130 for obj in self.tasks:
131 try:
132 obj.periodic_task()
133 except:
134 gws.log.exception(f'MONITOR: periodic task failed {obj}')
136 def _check_unlink(self, path):
137 try:
138 os.unlink(path)
139 return True
140 except FileNotFoundError:
141 return False