<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>