Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1782)

Unified Diff: appengine/chrome_infra_mon_proxy/main.py

Issue 928043005: Monitoring proxy for time series data (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: More fine-tuning Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: appengine/chrome_infra_mon_proxy/main.py
diff --git a/appengine/chrome_infra_mon_proxy/main.py b/appengine/chrome_infra_mon_proxy/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a59500b79d1941aca31a7f5e70083f23a0d4e5f
--- /dev/null
+++ b/appengine/chrome_infra_mon_proxy/main.py
@@ -0,0 +1,120 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import random
+import os
+import time
+import sys
+import webapp2
+
+from protorpc import messages
+from protorpc import message_types
+from protorpc import remote
+
+from google.appengine.api import app_identity, taskqueue
+
+import admin_handler
+import common
+
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, os.path.join(BASE_DIR, 'components', 'third_party'))
+
+from components import auth
+
+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
+
+
+def is_group_member(group_name):
+ """Skip authorization for dev appserver."""
+ if common.is_development_server():
+ auth_decorator = auth.public
+ else:
+ auth_decorator = auth.require( # pragma: no branch
+ lambda: auth.is_group_member(group_name))
+
+ def decorator(fn):
+ return auth_decorator(fn)
+ return decorator
+
+
+class NoBackendException(Exception):
+ pass
+
+
+class LoadBalancer(object):
+ """Balance the load among VM modules.
+
+ TODO(sergeyberezin): take into account health checks on the
+ corresponding NAT boxes. Specifically, fetch the health status
+ from the datastore in __init__(), and update it periodically as needed.
+ """
+ def __init__(self):
+ # dict: module name -> bool: whether module is healthy.
+ self._health = {module: True for module in VM_MODULES}
+ # 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.
+ self._last_health_check = 0
+
+ def _healthy_modules(self):
+ """Return the list of names of all healthy modules."""
+ # TODO(sergeyberezin): perform health checks and update the
+ # datastore once in a while.
+ return [module for module in VM_MODULES if self._health[module]]
+
+ def choose_module(self):
+ """Select a module to send the data to."""
+ modules = self._healthy_modules()
+ if not modules:
+ raise NoBackendException('Error: no healthy backends are available')
+ 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!
+
+
+def forward_data(data):
+ """Forwards the raw data to the backend."""
+ # 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
+ # Task queue should work correctly both in dev and prod server.
+ lb = LoadBalancer()
+ 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.
+ logging.info('Selected queue: %s', queue_name)
+ taskqueue.add(url='/vm', payload=data, queue_name=queue_name)
+
+
+class MonacqHandler(auth.AuthenticatingHandler):
+ # Disable XSRF in local dev appserver; otherwise requests will fail.
+ if common.is_development_server():
+ 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.
+
+ @is_group_member('service-account-monitoring-proxy')
+ def get(self):
+ self.response.headers.add_header('Content-Type', 'text/plain')
+ 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.
+ 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().
+ return
+
+ @is_group_member('service-account-monitoring-proxy')
+ def post(self):
+ # logging.info('Received POST /monacq: %s',
+ # common.payload_stats(self.request.body))
+ forward_data(self.request.body)
+
+
+class MainHandler(webapp2.RequestHandler):
+ def get(self):
+ # TODO(sergeyberezin): create a meaningful welcome page with some
+ # documentation.
+ 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
+ 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.
+ self.response.out.write(msg)
+
+
+
+logging.basicConfig(level=logging.DEBUG)
+
+main_handlers = [
+ (r'/', MainHandler),
+ (r'/monacq', MonacqHandler),
+ (r'/admin/(.*)', admin_handler.AdminDispatch),
+]
+
+app = webapp2.WSGIApplication(main_handlers, debug=True)

Powered by Google App Engine
This is Rietveld 408576698