OLD | NEW |
---|---|
(Empty) | |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import logging | |
6 import random | |
7 import os | |
8 import time | |
9 import sys | |
10 import webapp2 | |
11 | |
12 from protorpc import messages | |
13 from protorpc import message_types | |
14 from protorpc import remote | |
15 | |
16 from google.appengine.api import app_identity, taskqueue | |
17 | |
18 import admin_handler | |
19 import common | |
20 | |
21 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
22 sys.path.insert(0, os.path.join(BASE_DIR, 'components', 'third_party')) | |
23 | |
24 from components import auth | |
25 | |
26 VM_MODULES = ['vm1', 'vm2', 'vm3'] | |
ghost stip (do not use)
2015/04/14 00:36:42
oppan dijkstra style:
VM_MODULE_COUNT = 3
VM_MODU
Sergey Berezin (google)
2015/04/16 04:39:07
I feel the temptation :-) But this suggests the nu
| |
27 | |
28 | |
29 def is_group_member(group_name): | |
30 """Skip authorization for dev appserver.""" | |
31 if common.is_development_server(): | |
32 auth_decorator = auth.public | |
33 else: | |
34 auth_decorator = auth.require( # pragma: no branch | |
35 lambda: auth.is_group_member(group_name)) | |
36 | |
37 def decorator(fn): | |
38 return auth_decorator(fn) | |
39 return decorator | |
40 | |
41 | |
42 class NoBackendException(Exception): | |
43 pass | |
44 | |
45 | |
46 class LoadBalancer(object): | |
47 """Balance the load among VM modules. | |
48 | |
49 TODO(sergeyberezin): take into account health checks on the | |
50 corresponding NAT boxes. Specifically, fetch the health status | |
51 from the datastore in __init__(), and update it periodically as needed. | |
52 """ | |
53 def __init__(self): | |
54 # dict: module name -> bool: whether module is healthy. | |
55 self._health = {module: True for module in VM_MODULES} | |
56 # Seconds since UNIX epoch when last health check was made. | |
ghost stip (do not use)
2015/04/14 00:36:42
probably want to remove now until we figure out ho
Sergey Berezin (google)
2015/04/16 04:39:07
Done.
| |
57 self._last_health_check = 0 | |
58 | |
59 def _healthy_modules(self): | |
60 """Return the list of names of all healthy modules.""" | |
61 # TODO(sergeyberezin): perform health checks and update the | |
62 # datastore once in a while. | |
63 return [module for module in VM_MODULES if self._health[module]] | |
64 | |
65 def choose_module(self): | |
66 """Select a module to send the data to.""" | |
67 modules = self._healthy_modules() | |
68 if not modules: | |
69 raise NoBackendException('Error: no healthy backends are available') | |
70 return random.randint(0, len(modules)-1) | |
ghost stip (do not use)
2015/04/14 00:36:42
return random.choice(modules)
Sergey Berezin (google)
2015/04/16 04:39:06
Done. Nice, thanks!
| |
71 | |
72 | |
73 def forward_data(data): | |
74 """Forwards the raw data to the backend.""" | |
75 # logging.debug('Forwarding data: %s', common.payload_stats(data)) | |
ghost stip (do not use)
2015/04/14 00:36:42
why commented out? seems legit
Sergey Berezin (google)
2015/04/16 04:39:06
It computes md5, and now that I debugged the heade
| |
76 # Task queue should work correctly both in dev and prod server. | |
77 lb = LoadBalancer() | |
78 queue_name = VM_MODULES[lb.choose_module()] | |
ghost stip (do not use)
2015/04/14 00:36:42
queue_name = lb.choose_module()
Sergey Berezin (google)
2015/04/16 04:39:07
Done.
| |
79 logging.info('Selected queue: %s', queue_name) | |
80 taskqueue.add(url='/vm', payload=data, queue_name=queue_name) | |
81 | |
82 | |
83 class MonacqHandler(auth.AuthenticatingHandler): | |
84 # Disable XSRF in local dev appserver; otherwise requests will fail. | |
85 if common.is_development_server(): | |
86 xsrf_token_enforce_on = [] # pragma: no cover | |
ghost stip (do not use)
2015/04/14 00:36:43
nit: two spaces before comment
Sergey Berezin (google)
2015/04/16 04:39:07
Done.
| |
87 | |
88 @is_group_member('service-account-monitoring-proxy') | |
89 def get(self): | |
90 self.response.headers.add_header('Content-Type', 'text/plain') | |
91 self.response.write('GET requests are not allowed.') | |
ghost stip (do not use)
2015/04/14 00:36:42
I think if you don't provide a get function to beg
Sergey Berezin (google)
2015/04/16 04:39:06
Indeed - removed.
| |
92 self.response.set_status(403) | |
ghost stip (do not use)
2015/04/14 00:36:42
at least make it 405
Sergey Berezin (google)
2015/04/16 04:39:07
N/A - removed def get().
| |
93 return | |
94 | |
95 @is_group_member('service-account-monitoring-proxy') | |
96 def post(self): | |
97 # logging.info('Received POST /monacq: %s', | |
98 # common.payload_stats(self.request.body)) | |
99 forward_data(self.request.body) | |
100 | |
101 | |
102 class MainHandler(webapp2.RequestHandler): | |
103 def get(self): | |
104 # TODO(sergeyberezin): create a meaningful welcome page with some | |
105 # documentation. | |
106 msg = 'There is nothing here yet.\n' | |
ghost stip (do not use)
2015/04/14 00:36:42
make this a little more friendly!
msg = 'Welcome
Sergey Berezin (google)
2015/04/16 04:39:06
Done - created an actual page with some useful con
| |
107 self.response.headers['Content-Type'] = 'text/plain' | |
ghost stip (do not use)
2015/04/14 00:36:42
is this needed?
Sergey Berezin (google)
2015/04/16 04:39:07
Nope - it's all gone now.
| |
108 self.response.out.write(msg) | |
109 | |
110 | |
111 | |
112 logging.basicConfig(level=logging.DEBUG) | |
113 | |
114 main_handlers = [ | |
115 (r'/', MainHandler), | |
116 (r'/monacq', MonacqHandler), | |
117 (r'/admin/(.*)', admin_handler.AdminDispatch), | |
118 ] | |
119 | |
120 app = webapp2.WSGIApplication(main_handlers, debug=True) | |
OLD | NEW |