Add frontend and scheduler for Autotest
The attached tarball includes the new Autotest web frontend for creating
and monitoring jobs and the new scheduler for executing jobs. We've
been working hard to get these complete and stabilized, and although
they still have a long, long way to go in terms of features, we believe
they are now stable and powerful enough to be useful.
The frontend consists of two parts, the server and the client. The
server is implemented in Python using the Django web framework, and is
contained under frontend/ and frontend/afe. The client is implemented
using Google Web Toolkit and is contained under frontend/client. We
tried to use Dojo initially, as was generally agreed on, but it proved
to be too young and poorly documented of a framework, and developing in
it was just too inefficient.
The scheduler is contained entirely in the scheduler/monitor_db Python
file. It looks at the database used by the web frontend and spawns
autoserv processes to run jobs, monitoring them and recording status.
The scheduler was developed by Paul Turner, who will be sending out some
detailed documentation of it soon.
I've also included the DB migrations code for which I recently submitted
a patch to the mailing list. I've included this code because it is
necessary to create the frontend DB, but it will (hopefully) soon be
part of the SVN repository.
Lastly, there is an apache directory containing configuration files for
running the web server through Apache.
I've put instructions for installing a full Autotest server, including
the web frontend, the scheduler, and the existing "tko" results backend,
at http://test.kernel.org/autotest/AutotestServerInstall. I've also
created a brief guide to using the web frontend, with plenty of
screenshots, at http://test.kernel.org/autotest/WebFrontendHowTo.
Please let me know if you find any problems with either of these pages.
Take a look, try it out, send me comments, complaints, and bugs, and I
hope you find it useful!
Steve Howard, and the rest of the Google Autotest team
From: Steve Howard <showard@google.com>
git-svn-id: http://test.kernel.org/svn/autotest/trunk@1242 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/frontend/afe/json_rpc/serviceHandler.py b/frontend/afe/json_rpc/serviceHandler.py
new file mode 100644
index 0000000..1f6036c
--- /dev/null
+++ b/frontend/afe/json_rpc/serviceHandler.py
@@ -0,0 +1,145 @@
+
+"""
+ Copyright (c) 2007 Jan-Klaas Kollhof
+
+ This file is part of jsonrpc.
+
+ jsonrpc is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this software; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+
+import traceback
+
+from frontend.afe.simplejson import decoder, encoder
+
+def customConvertJson(value):
+ """\
+ Recursively process JSON values and do type conversions.
+ -change floats to ints
+ -change unicodes to strs
+ """
+ if isinstance(value, float):
+ return int(value)
+ elif isinstance(value, unicode):
+ return str(value)
+ elif isinstance(value, list):
+ return [customConvertJson(item) for item in value]
+ elif isinstance(value, dict):
+ new_dict = {}
+ for key, val in value.iteritems():
+ new_key = customConvertJson(key)
+ new_val = customConvertJson(val)
+ new_dict[new_key] = new_val
+ return new_dict
+ else:
+ return value
+
+json_encoder = encoder.JSONEncoder()
+json_decoder = decoder.JSONDecoder()
+
+
+def ServiceMethod(fn):
+ fn.IsServiceMethod = True
+ return fn
+
+class ServiceException(Exception):
+ pass
+
+class ServiceRequestNotTranslatable(ServiceException):
+ pass
+
+class BadServiceRequest(ServiceException):
+ pass
+
+class ServiceMethodNotFound(ServiceException):
+ pass
+
+class ServiceHandler(object):
+
+ def __init__(self, service):
+ self.service=service
+
+ def handleRequest(self, json):
+ err=None
+ result = None
+ id_=''
+
+ #print 'Request:', json
+
+ try:
+ req = self.translateRequest(json)
+ except ServiceRequestNotTranslatable, e:
+ err = e
+ req={'id':id_}
+
+ if err==None:
+ try:
+ id_ = req['id']
+ methName = req['method']
+ args = req['params']
+ except:
+ err = BadServiceRequest(json)
+
+ if err == None:
+ try:
+ meth = self.findServiceEndpoint(methName)
+ except Exception, e:
+ traceback.print_exc()
+ err = e
+
+ if err == None:
+ try:
+ result = self.invokeServiceEndpoint(meth, args)
+ except Exception, e:
+ traceback.print_exc()
+ err = e
+ resultdata = self.translateResult(result, err, id_)
+
+ return resultdata
+
+ def translateRequest(self, data):
+ try:
+ req = json_decoder.decode(data)
+ except:
+ raise ServiceRequestNotTranslatable(data)
+ req = customConvertJson(req) # -srh
+ return req
+
+ def findServiceEndpoint(self, name):
+ try:
+ meth = getattr(self.service, name)
+# -srh
+# if getattr(meth, "IsServiceMethod"):
+ return meth
+# else:
+# raise ServiceMethodNotFound(name)
+ except AttributeError:
+ raise ServiceMethodNotFound(name)
+
+ def invokeServiceEndpoint(self, meth, args):
+ return meth(*args)
+
+ def translateResult(self, rslt, err, id_):
+ if err != None:
+ err = {"name": err.__class__.__name__, "message":str(err)}
+ rslt = None
+
+ try:
+ data = json_encoder.encode({"result":rslt,"id":id_,"error":err})
+ except TypeError, e:
+ traceback.print_exc()
+ err = {"name": "JSONEncodeException", "message":"Result Object Not Serializable"}
+ data = json_encoder.encode({"result":None, "id":id_,"error":err})
+
+ return data