<html><head><META http-equiv="Content-Type" content="text/html;charset=utf-8"></head><body><div id="content" contenteditable="true" spellcheck="true" autocorrect="true" autocapitalize="true" style="font-family: Helvetica;">Python is indent based languang, so the function will fail if we have incorrect indenting. I have tested this in real bmc machine so I believe the indenting should be fine for now. Please let Brad review this change since he is familiar with Python.<br><br><br><br><hr>在 2016年1月29日,上午8:24:58,"Cyril Bur" <cyrilbur@gmail.com> 写道:<br><div style="margin-left: 15px;" id="MaaS360PIMSDKOriginalMessageId">On Thu, 28 Jan 2016 02:00:37 -0600<br>OpenBMC Patches <openbmc-patches@stwcx.xyz> wrote:<br>> From: shgoupf <shgoupf@cn.ibm.com><br>> <br>Hi Peng,<br>So I'm don't really know python all that well but I do believe this language is<br>white space sensitive... I'll let a pythoner respond about the rest...<br>> Changes:<br>> 1) The main idea of this change is to have a streaming path as below:<br>> dbus signal -> obmc-rest capture the dbus signal -> obmc-rest notify the client of the signal receiving.<br>> 2) Replace rocket with gevent WSGI server to support multiple async accesses.<br>> 3) Use gevent queue to notify the dbus signal receiving.<br>> 4) The uri to the streaming should be in the form as below:<br>> https://<ip>/<path>/stream/<signal_name><br>> ---<br>> obmc-rest | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------<br>> 1 file changed, 104 insertions(+), 11 deletions(-)<br>> mode change 100644 => 100755 obmc-rest<br>> <br>> diff --git a/obmc-rest b/obmc-rest<br>> old mode 100644<br>> new mode 100755<br>> index c6d2949..481dafa<br>> --- a/obmc-rest<br>> +++ b/obmc-rest<br>> @@ -3,7 +3,9 @@<br>> import os<br>> import sys<br>> import dbus<br>> +import gobject<br>> import dbus.exceptions<br>> +import dbus.mainloop.glib<br>> import json<br>> import logging<br>> from xml.etree import ElementTree<br>> @@ -14,6 +16,10 @@ from OpenBMCMapper import Mapper, PathTree, IntrospectionNodeParser, ListMatch<br>> import spwd<br>> import grp<br>> import crypt<br>> +import threading<br>> +import gevent<br>> +from gevent.pywsgi import WSGIServer<br>> +from gevent.queue import Queue<br>> <br>> DBUS_UNKNOWN_INTERFACE = 'org.freedesktop.UnknownInterface'<br>> DBUS_UNKNOWN_METHOD = 'org.freedesktop.DBus.Error.UnknownMethod'<br>> @@ -59,12 +65,13 @@ def makelist(data):<br>> <br>> class RouteHandler(object):<br>> _require_auth = makelist(valid_user)<br>> - def __init__(self, app, bus, verbs, rules):<br>> + def __init__(self, app, bus, verbs, rules, skips = []):<br>> self.app = app<br>> self.bus = bus<br>> self.mapper = Mapper(bus)<br>> self._verbs = makelist(verbs)<br>> self._rules = rules<br>> + self._skips = skips<br>> <br>> def _setup(self, **kw):<br>> request.route_data = {}<br>> @@ -79,7 +86,7 @@ class RouteHandler(object):<br>> return getattr(self, 'do_' + request.method.lower())(**kw)<br>> <br>> def install(self):<br>> - self.app.route(self._rules, callback = self,<br>> + self.app.route(self._rules, callback = self, skip = self._skips,<br>> method = ['GET', 'PUT', 'PATCH', 'POST', 'DELETE'])<br>> <br>> @staticmethod<br>> @@ -108,6 +115,58 @@ class RouteHandler(object):<br>> return None<br>> raise<br>> <br>> +class SignalHandler(RouteHandler):<br>> + verbs = ['GET']<br>> + rules = '<path:path>/stream/<signal>'<br>> +<br>> + def __init__(self, app, bus):<br>> + super(SignalHandler, self).__init__(<br>> + app, bus, self.verbs, self.rules)<br>> +<br>> + def find(self, path, signal):<br>> + busses = self.try_mapper_call(self.mapper.get_object,<br>> + path = path)<br>> + for items in busses.iteritems():<br>> + s = self.find_signal_on_bus(path, signal, *items)<br>> + if s:<br>> + return s<br>> +<br>> + abort(404, _4034_msg %('signal', 'found', signal))<br>> +<br>> + def setup(self, path, signal):<br>> + request.route_data['map'] = self.find(path, signal)<br>> +<br>> + def do_get(self, path, signal):<br>> + body = Queue()<br>> + dsignal = DbusSignal(bus, request.route_data['map'][0],<br>> + request.route_data['map'][1], path)<br>> + dsignal.onData(body.put)<br>> + dsignal.onFinish(lambda: body.put(StopIteration))<br>> + dsignal.signalSnooping()<br>> + return body<br>> +<br>> + @staticmethod<br>> + def find_signal(signal, signals):<br>> + if signals is None:<br>> + return None<br>> +<br>> + signal = find_case_insensitive(signal, signals.keys())<br>> + if signal is not None:<br>> + return signal<br>> +<br>> + def find_signal_on_bus(self, path, signal, bus, interfaces):<br>> + obj = self.bus.get_object(bus, path, introspect = False)<br>> + iface = dbus.Interface(obj, dbus.INTROSPECTABLE_IFACE)<br>> + data = iface.Introspect()<br>> + parser = IntrospectionNodeParser(<br>> + ElementTree.fromstring(data),<br>> + intf_match = ListMatch(interfaces))<br>> + for x,y in parser.get_interfaces().iteritems():<br>> + s = self.find_signal(signal,<br>> + y.get('signal'))<br>> + if s:<br>> + return (x,s)<br>> +<br>> class DirectoryHandler(RouteHandler):<br>> verbs = 'GET'<br>> rules = '<path:path>/'<br>> @@ -715,7 +774,8 @@ class RestApp(Bottle):<br>> self.install(JSONPlugin(**json_kw))<br>> self.install(JsonApiErrorsPlugin(**json_kw))<br>> self.install(AuthorizationPlugin())<br>> - self.install(JsonApiResponsePlugin())<br>> + self.json_response_plugin = JsonApiResponsePlugin()<br>Indenting?<br>> + self.install(self.json_response_plugin)<br>> self.install(JsonApiRequestPlugin())<br>> self.install(JsonApiRequestTypePlugin())<br>> <br>> @@ -726,6 +786,7 @@ class RestApp(Bottle):<br>> <br>> def create_handlers(self):<br>> # create route handlers<br>> + self.signal_handler = SignalHandler(self, self.bus)<br>> self.session_handler = SessionHandler(self, self.bus)<br>> self.directory_handler = DirectoryHandler(self, self.bus)<br>> self.list_names_handler = ListNamesHandler(self, self.bus)<br>> @@ -736,6 +797,11 @@ class RestApp(Bottle):<br>> self.instance_handler = InstanceHandler(self, self.bus)<br>> <br>> def install_handlers(self):<br>> + # Skip json response for signal handler because it requires to<br>> + # return a gevent iterable which cannot be handled by json<br>> + # response plugin<br>> + self.signal_handler._skips = [self.json_response_plugin]<br>Indenting?<br>> + self.signal_handler.install()<br>> self.session_handler.install()<br>> self.directory_handler.install()<br>> self.list_names_handler.install()<br>> @@ -766,21 +832,48 @@ class RestApp(Bottle):<br>> parts = filter(bool, path.split('/'))<br>> request.environ['PATH_INFO'] = '/' + '/'.join(parts) + trailing<br>> <br>> +class DbusSignal():<br>> + def __init__(self, bus, dbus_interface, signal_name, path):<br>> + # Register the dbus recieve handler<br>> + bus.add_signal_receiver(self.signalReciever,<br>> + dbus_interface = dbus_interface,<br>> + signal_name = signal_name,<br>> + path = path)<br>> +<br>> + self.snooping = True<br>> +<br>> + def signalReciever(self, msg):<br>> + self.send("Recieved message: %s" % msg)<br>> + self.snooping = False<br>> +<br>> + def onData(self, send):<br>> + self.send = send<br>> +<br>> + def onFinish(self, f):<br>> + self.finish = f<br>> +<br>> + def signalSnooping(self):<br>> + while self.snooping:<br>> + mainloop = gobject.MainLoop()<br>> + gevent.sleep(1)<br>> + gobject.timeout_add(1, mainloop.quit)<br>> + mainloop.run()<br>> +<br>> + self.finish()<br>> +<br>> if __name__ == '__main__':<br>> log = logging.getLogger('Rocket.Errors')<br>> log.setLevel(logging.INFO)<br>> log.addHandler(logging.StreamHandler(sys.stdout))<br>> <br>> + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)<br>Indenting?<br>> bus = dbus.SystemBus()<br>> app = RestApp(bus)<br>> +<br>?<br>> default_cert = os.path.join(sys.prefix, 'share',<br>> os.path.basename(__file__), 'cert.pem')<br>> <br>> - server = Rocket(('0.0.0.0',<br>> - 443,<br>> - default_cert,<br>> - default_cert),<br>> - 'wsgi', {'wsgi_app': app},<br>> - min_threads = 1,<br>> - max_threads = 1)<br>> - server.start()<br>> + server = WSGIServer(("0.0.0.0", 443), app, keyfile = default_cert,<br>> + certfile = default_cert)<br>> +<br>> + server.serve_forever()<br>Indenting?<br></path:path></signal></path:path></signal_name></path></ip></shgoupf@cn.ibm.com></openbmc-patches@stwcx.xyz></div></div><BR>
</body></html>