Mercurial > templog
annotate py/tempserver.py @ 259:26eee8591f61
long polling works
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Tue, 09 Jun 2015 23:27:44 +0800 |
parents | 0a1b642e3086 |
children | 20c89630be6c |
rev | line source |
---|---|
148 | 1 #!/home/matt/templog/venv/bin/python |
141 | 2 |
3 import sys | |
4 import os | |
149
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
5 import logging |
198 | 6 import time |
7 import signal | |
228 | 8 import asyncio |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
9 import argparse |
149
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
10 |
172 | 11 import lockfile.pidlockfile |
162
d73077e8cd67
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
160
diff
changeset
|
12 import daemon |
141 | 13 |
14 import utils | |
149
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
15 from utils import L,D,EX,W |
145 | 16 import fridge |
17 import config | |
231 | 18 import sensor |
145 | 19 import params |
160 | 20 import uploader |
259 | 21 import configwaiter |
145 | 22 |
141 | 23 |
24 class Tempserver(object): | |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
25 def __init__(self, test_mode): |
141 | 26 self.readings = [] |
145 | 27 self.current = (None, None) |
149
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
28 self.fridge = None |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
29 self._wakeup = asyncio.Event() |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
30 self._test_mode = test_mode |
141 | 31 |
149
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
32 def __enter__(self): |
145 | 33 self.params = params.Params() |
149
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
34 self.fridge = fridge.Fridge(self) |
160 | 35 self.uploader = uploader.Uploader(self) |
253
0a1b642e3086
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
234
diff
changeset
|
36 self.configwaiter = configwaiter.ConfigWaiter(self) |
145 | 37 self.params.load() |
229 | 38 self.set_sensors(sensor.make_sensor(self)) |
253
0a1b642e3086
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
234
diff
changeset
|
39 asyncio.get_event_loop().add_signal_handler(signal.SIGHUP, self.reload_signal) |
149
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
40 return self |
141 | 41 |
149
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
42 def __exit__(self, exc_type, exc_value, traceback): |
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
43 L("Exiting, cleanup handler"); |
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
44 self.fridge.off() |
141 | 45 |
145 | 46 def run(self): |
149
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
47 |
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
48 if self.fridge is None: |
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
49 raise Exception("Tempserver.run() must be within 'with Tempserver() as server'") |
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
50 |
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
51 # XXX do these go here or in __enter_() ? |
d686b111dab4
working better. logging works properly, cleanup fridge.off() happens.
Matt Johnston <matt@ucc.asn.au>
parents:
148
diff
changeset
|
52 self.start_time = self.now() |
234 | 53 runloops = [ |
228 | 54 self.fridge.run(), |
55 self.sensors.run(), | |
56 self.uploader.run(), | |
253
0a1b642e3086
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
234
diff
changeset
|
57 self.configwaiter.run(), |
234 | 58 ] |
232 | 59 |
228 | 60 loop = asyncio.get_event_loop() |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
61 try: |
234 | 62 loop.run_until_complete(asyncio.gather(*runloops)) |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
63 except KeyboardInterrupt: |
234 | 64 print('\nctrl-c') |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
65 finally: |
234 | 66 # loop.close() seems necessary otherwise get warnings about signal handlers |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
67 loop.close() |
145 | 68 |
69 def now(self): | |
70 return utils.monotonic_time() | |
71 | |
144 | 72 def set_sensors(self, sensors): |
148 | 73 if hasattr(self, 'sensors'): |
144 | 74 self.sensors.kill() |
75 self.sensors = sensors | |
76 self.wort_name = sensors.wort_name() | |
77 self.fridge_name = sensors.fridge_name() | |
141 | 78 |
79 def take_readings(self): | |
80 ret = self.readings | |
81 self.readings = [] | |
82 return ret | |
83 | |
84 def pushfront(self, readings): | |
85 """ used if a caller of take_readings() fails """ | |
160 | 86 self.readings = readings + self.readings |
141 | 87 |
144 | 88 # a reading is a map of {sensorname: value}. temperatures |
89 # are float degrees | |
141 | 90 def add_reading(self, reading): |
91 """ adds a reading at the current time """ | |
155 | 92 D("add_reading(%s)" % str(reading)) |
145 | 93 self.readings.append( (reading, self.now())) |
144 | 94 self.current = (reading.get(self.wort_name, None), |
95 reading.get(self.fridge_name, None)) | |
160 | 96 if len(self.readings) > config.MAX_READINGS: |
97 self.readings = self.readings[-config.MAX_READINGS:] | |
141 | 98 |
99 def current_temps(self): | |
144 | 100 """ returns (wort_temp, fridge_temp) tuple """ |
232 | 101 D("current: %s" % str(self.current)) |
148 | 102 return self.current |
145 | 103 |
232 | 104 @asyncio.coroutine |
219
16a83e2c97a0
sleep on a semaphore so it can start/stop immediately when there's a SIGHUP
Matt Johnston <matt@ucc.asn.au>
parents:
217
diff
changeset
|
105 def sleep(self, timeout): |
16a83e2c97a0
sleep on a semaphore so it can start/stop immediately when there's a SIGHUP
Matt Johnston <matt@ucc.asn.au>
parents:
217
diff
changeset
|
106 """ sleeps for timeout seconds, though wakes if the server's config is updated """ |
232 | 107 # XXX fixme - we should wake on _wakeup but asyncio Condition with wait_for is a bit broken? |
108 # https://groups.google.com/forum/#!topic/python-tulip/eSm7rZAe9LM | |
109 # For now we just sleep, ignore the _wakeup | |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
110 try: |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
111 yield from asyncio.wait_for(self._wakeup.wait(), timeout=timeout) |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
112 except asyncio.TimeoutError: |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
113 pass |
234 | 114 |
253
0a1b642e3086
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
234
diff
changeset
|
115 def reload_signal(self, no_file = False): |
219
16a83e2c97a0
sleep on a semaphore so it can start/stop immediately when there's a SIGHUP
Matt Johnston <matt@ucc.asn.au>
parents:
217
diff
changeset
|
116 try: |
253
0a1b642e3086
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
234
diff
changeset
|
117 if not no_file: |
0a1b642e3086
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
234
diff
changeset
|
118 self.params.load() |
0a1b642e3086
long polling config updates
Matt Johnston <matt@ucc.asn.au>
parents:
234
diff
changeset
|
119 L("Reloaded.") |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
120 self._wakeup.set() |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
121 self._wakeup.clear() |
229 | 122 except Error as e: |
219
16a83e2c97a0
sleep on a semaphore so it can start/stop immediately when there's a SIGHUP
Matt Johnston <matt@ucc.asn.au>
parents:
217
diff
changeset
|
123 W("Problem reloading: %s" % str(e)) |
16a83e2c97a0
sleep on a semaphore so it can start/stop immediately when there's a SIGHUP
Matt Johnston <matt@ucc.asn.au>
parents:
217
diff
changeset
|
124 |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
125 def test_mode(self): |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
126 return self._test_mode |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
127 |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
128 def setup_logging(debug = False): |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
129 level = logging.INFO |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
130 if debug: |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
131 level = logging.DEBUG |
145 | 132 logging.basicConfig(format='%(asctime)s %(message)s', |
133 datefmt='%m/%d/%Y %I:%M:%S %p', | |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
134 level=level) |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
135 #logging.getLogger("asyncio").setLevel(logging.DEBUG) |
145 | 136 |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
137 def start(test_mode): |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
138 with Tempserver(test_mode) as server: |
162
d73077e8cd67
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
160
diff
changeset
|
139 server.run() |
d73077e8cd67
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
160
diff
changeset
|
140 |
145 | 141 def main(): |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
142 parser = argparse.ArgumentParser() |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
143 parser.add_argument('--hup', action='store_true') |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
144 parser.add_argument('--new', action='store_true') |
259 | 145 parser.add_argument('-D', '--daemon', action='store_true') |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
146 parser.add_argument('-d', '--debug', action='store_true') |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
147 parser.add_argument('-t', '--test', action='store_true') |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
148 args = parser.parse_args() |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
149 |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
150 setup_logging(args.debug) |
145 | 151 |
174
c49d87bb81b9
fix to absolute path for lockfile, --daemon does chdir("/")
Matt Johnston <matt@ucc.asn.au>
parents:
172
diff
changeset
|
152 heredir = os.path.abspath(os.path.dirname(__file__)) |
c49d87bb81b9
fix to absolute path for lockfile, --daemon does chdir("/")
Matt Johnston <matt@ucc.asn.au>
parents:
172
diff
changeset
|
153 pidpath = os.path.join(heredir, 'tempserver.pid') |
172 | 154 pidf = lockfile.pidlockfile.PIDLockFile(pidpath, threaded=False) |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
155 |
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
156 |
172 | 157 try: |
217 | 158 pidf.acquire(1) |
172 | 159 pidf.release() |
229 | 160 except (lockfile.AlreadyLocked, lockfile.LockTimeout) as e: |
172 | 161 pid = pidf.read_pid() |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
162 if args.hup: |
199 | 163 try: |
164 os.kill(pid, signal.SIGHUP) | |
230 | 165 print("Sent SIGHUP to process %d" % pid, file=sys.stderr) |
199 | 166 sys.exit(0) |
167 except OSError: | |
230 | 168 print("Process %d isn't running?" % pid, file=sys.stderr) |
199 | 169 sys.exit(1) |
170 | |
230 | 171 print("Locked by PID %d" % pid, file=sys.stderr) |
199 | 172 |
198 | 173 stale = False |
172 | 174 if pid > 0: |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
175 if args.new: |
198 | 176 try: |
177 os.kill(pid, 0) | |
178 except OSError: | |
179 stale = True | |
180 | |
181 if not stale: | |
230 | 182 print("Stopping old tempserver pid %d" % pid, file=sys.stderr) |
198 | 183 os.kill(pid, signal.SIGTERM) |
184 time.sleep(2) | |
185 pidf.acquire(0) | |
186 pidf.release() | |
187 else: | |
188 try: | |
189 os.kill(pid, 0) | |
190 # must still be running PID | |
191 raise e | |
192 except OSError: | |
193 stale = True | |
194 | |
195 if stale: | |
196 # isn't still running, steal the lock | |
230 | 197 print("Unlinking stale lockfile %s for pid %d" % (pidpath, pid), file=sys.stderr) |
198 | 198 pidf.break_lock() |
162
d73077e8cd67
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
160
diff
changeset
|
199 |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
200 if args.hup: |
230 | 201 print("Doesn't seem to be running", file=sys.stderr) |
199 | 202 sys.exit(1) |
203 | |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
204 if args.daemon: |
162
d73077e8cd67
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
160
diff
changeset
|
205 logpath = os.path.join(os.path.dirname(__file__), 'tempserver.log') |
d73077e8cd67
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
160
diff
changeset
|
206 logf = open(logpath, 'a+') |
d73077e8cd67
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
160
diff
changeset
|
207 with daemon.DaemonContext(pidfile=pidf, stdout=logf, stderr = logf): |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
208 start(args.test) |
162
d73077e8cd67
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
160
diff
changeset
|
209 else: |
d73077e8cd67
Add daemon mode with locking, add "disabled" parameter
Matt Johnston <matt@ucc.asn.au>
parents:
160
diff
changeset
|
210 with pidf: |
233
19569cb5ed46
better arg parser. seems close to ready
Matt Johnston <matt@ucc.asn.au>
parents:
232
diff
changeset
|
211 start(args.test) |
145 | 212 |
213 if __name__ == '__main__': | |
214 main() |