netflow: Correctly track flow creation time.
[cascardo/ovs.git] / ovsdb / ovsdbmonitor / qt4reactor.py
1 # Copyright (c) 2001-2008 Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 # The referred licence file contains:
5
6 #Copyright (c) 2001-2010
7 #Allen Short
8 #Andy Gayton
9 #Andrew Bennetts
10 #Antoine Pitrou
11 #Apple Computer, Inc.
12 #Benjamin Bruheim
13 #Bob Ippolito
14 #Canonical Limited
15 #Christopher Armstrong
16 #David Reid
17 #Donovan Preston
18 #Eric Mangold
19 #Eyal Lotem
20 #Itamar Shtull-Trauring
21 #James Knight
22 #Jason A. Mobarak
23 #Jean-Paul Calderone
24 #Jessica McKellar
25 #Jonathan Jacobs
26 #Jonathan Lange
27 #Jonathan D. Simms
28 #Jurgen Hermann
29 #Kevin Horn
30 #Kevin Turner
31 #Mary Gardiner
32 #Matthew Lefkowitz
33 #Massachusetts Institute of Technology
34 #Moshe Zadka
35 #Paul Swartz
36 #Pavel Pergamenshchik
37 #Ralph Meijer
38 #Sean Riley
39 #Software Freedom Conservancy
40 #Travis B. Hartwell
41 #Thijs Triemstra
42 #Thomas Herve
43 #Timothy Allen
44 #
45 #Permission is hereby granted, free of charge, to any person obtaining
46 #a copy of this software and associated documentation files (the
47 #"Software"), to deal in the Software without restriction, including
48 #without limitation the rights to use, copy, modify, merge, publish,
49 #distribute, sublicense, and/or sell copies of the Software, and to
50 #permit persons to whom the Software is furnished to do so, subject to
51 #the following conditions:
52 #
53 #The above copyright notice and this permission notice shall be
54 #included in all copies or substantial portions of the Software.
55 #
56 #THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
57 #EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
58 #MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
59 #NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
60 #LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
61 #OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
62 #WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
63
64 """
65 This module provides support for Twisted to be driven by the Qt mainloop.
66
67 In order to use this support, simply do the following::
68     |  app = QApplication(sys.argv) # your code to init Qt
69     |  import qt4reactor
70     |  qt4reactor.install()
71     
72 alternatively:
73
74     |  from twisted.application import reactors
75     |  reactors.installReactor('qt4')
76
77 Then use twisted.internet APIs as usual.  The other methods here are not
78 intended to be called directly.
79
80 If you don't instantiate a QApplication or QCoreApplication prior to
81 installing the reactor, a QCoreApplication will be constructed
82 by the reactor.  QCoreApplication does not require a GUI so trial testing
83 can occur normally.
84
85 Twisted can be initialized after QApplication.exec_() with a call to
86 reactor.runReturn().  calling reactor.stop() will unhook twisted but
87 leave your Qt application running
88
89 API Stability: stable
90
91 Maintainer: U{Glenn H Tarbox, PhD<mailto:glenn@tarbox.org>}
92
93 Previous maintainer: U{Itamar Shtull-Trauring<mailto:twisted@itamarst.org>}
94 Original port to QT4: U{Gabe Rudy<mailto:rudy@goldenhelix.com>}
95 Subsequent port by therve
96 """
97
98 __all__ = ['install']
99
100
101 import sys, time
102
103 try:
104     from zope.interface import implements
105 except:
106     print('+++ Python Zope interface module is required\n')
107     raise
108
109
110 try:
111     from OVEStandard import globalForcePySide
112     if globalForcePySide: raise Exception()
113     from PyQt4.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication
114     from PyQt4.QtCore import QEventLoop
115 except:
116     from PySide.QtCore import QSocketNotifier, QObject, SIGNAL, QTimer, QCoreApplication
117     from PySide.QtCore import QEventLoop
118
119 try:
120     from twisted.internet.interfaces import IReactorFDSet
121     from twisted.python import log
122     from twisted.internet.posixbase import PosixReactorBase
123 except:
124     print('+++ Python Twisted Conch module is required\n')
125     raise
126     
127 class TwistedSocketNotifier(QSocketNotifier):
128     """
129     Connection between an fd event and reader/writer callbacks.
130     """
131
132     def __init__(self, reactor, watcher, type):
133         QSocketNotifier.__init__(self, watcher.fileno(), type)
134         self.reactor = reactor
135         self.watcher = watcher
136         self.fn = None
137         if type == QSocketNotifier.Read:
138             self.fn = self.read
139         elif type == QSocketNotifier.Write:
140             self.fn = self.write
141         QObject.connect(self, SIGNAL("activated(int)"), self.fn)
142
143
144     def shutdown(self):
145         QObject.disconnect(self, SIGNAL("activated(int)"), self.fn)
146         self.setEnabled(False)
147         self.fn = self.watcher = None
148         self.deleteLater()
149
150
151     def read(self, sock):
152         w = self.watcher
153         #self.setEnabled(False)    # ??? do I need this?            
154         def _read():
155             why = None
156             try:
157                 why = w.doRead()
158             except:
159                 log.err()
160                 why = sys.exc_info()[1]
161             if why:
162                 self.reactor._disconnectSelectable(w, why, True)
163             elif self.watcher:
164                 pass
165                 #self.setEnabled(True)
166         log.callWithLogger(w, _read)
167         self.reactor.reactorInvocation()
168
169     def write(self, sock):
170         w = self.watcher
171         self.setEnabled(False)
172         def _write():
173             why = None
174             try:
175                 why = w.doWrite()
176             except:
177                 log.err()
178                 why = sys.exc_info()[1]
179             if why:
180                 self.reactor._disconnectSelectable(w, why, False)
181             elif self.watcher:
182                 self.setEnabled(True)
183         log.callWithLogger(w, _write)
184         self.reactor.reactorInvocation()
185
186 class fakeApplication(QEventLoop):
187     def __init__(self):
188         QEventLoop.__init__(self)
189         
190     def exec_(self):
191         QEventLoop.exec_(self)
192         
193 class QTReactor(PosixReactorBase):
194     """
195     Qt based reactor.
196     """
197     implements(IReactorFDSet)
198
199     _timer = None
200
201     def __init__(self):
202         self._reads = {}
203         self._writes = {}
204         self._timer=QTimer()
205         self._timer.setSingleShot(True)
206         if QCoreApplication.startingUp():
207             self.qApp=QCoreApplication([])
208             self._ownApp=True
209         else:
210             self.qApp = QCoreApplication.instance()
211             self._ownApp=False
212         self._blockApp = None
213         self._readWriteQ=[]
214         
215         """ some debugging instrumentation """
216         self._doSomethingCount=0
217         
218         PosixReactorBase.__init__(self)
219
220     def addReader(self, reader):
221         if not reader in self._reads:
222             self._reads[reader] = TwistedSocketNotifier(self, reader,
223                                                        QSocketNotifier.Read)
224
225
226     def addWriter(self, writer):
227         if not writer in self._writes:
228             self._writes[writer] = TwistedSocketNotifier(self, writer,
229                                                         QSocketNotifier.Write)
230
231
232     def removeReader(self, reader):
233         if reader in self._reads:
234             #self._reads[reader].shutdown()
235             #del self._reads[reader]
236             self._reads.pop(reader).shutdown()
237
238     def removeWriter(self, writer):
239         if writer in self._writes:
240             self._writes[writer].shutdown()
241             #del self._writes[writer]
242             self._writes.pop(writer)
243
244
245     def removeAll(self):
246         return self._removeAll(self._reads, self._writes)
247
248
249     def getReaders(self):
250         return self._reads.keys()
251
252
253     def getWriters(self):
254         return self._writes.keys()
255     
256     def callLater(self,howlong, *args, **kargs):
257         rval = super(QTReactor,self).callLater(howlong, *args, **kargs)
258         self.reactorInvocation()
259         return rval
260     
261     def crash(self):
262         super(QTReactor,self).crash()
263         
264     def iterate(self,delay=0.0):
265         t=self.running # not sure I entirely get the state of running
266         self.running=True
267         self._timer.stop() # in case its not (rare?)
268         try:
269             if delay == 0.0:
270                 self.reactorInvokePrivate()
271                 self._timer.stop() # supports multiple invocations
272             else:
273                 endTime = delay + time.time()
274                 self.reactorInvokePrivate()
275                 while True:
276                     t = endTime - time.time()
277                     if t <= 0.0: return
278                     self.qApp.processEvents(QEventLoop.AllEvents | 
279                                       QEventLoop.WaitForMoreEvents,t*1010)
280         finally:
281             self.running=t
282             
283     def addReadWrite(self,t):
284         self._readWriteQ.append(t)
285         
286     def runReturn(self, installSignalHandlers=True):
287         QObject.connect(self._timer, SIGNAL("timeout()"), 
288                         self.reactorInvokePrivate)
289         self.startRunning(installSignalHandlers=installSignalHandlers)
290         self._timer.start(0)
291         
292     def run(self, installSignalHandlers=True):
293         try:
294             if self._ownApp:
295                 self._blockApp=self.qApp
296             else:
297                 self._blockApp = fakeApplication()
298             self.runReturn(installSignalHandlers)
299             self._blockApp.exec_()
300         finally:
301             self._timer.stop() # should already be stopped
302
303     def reactorInvocation(self):
304         self._timer.setInterval(0)
305         
306     def reactorInvokePrivate(self):
307         if not self.running:
308             if self._blockApp is None:
309                 # Andy's fix for Ctrl-C quit
310                 self.qApp.quit()
311             else:
312                 self._blockApp.quit()
313         self._doSomethingCount += 1
314         self.runUntilCurrent()
315         t = self.timeout()
316         if t is None: t=0.1
317         else: t = min(t,0.1)
318         self._timer.setInterval(int(t*1010))
319         self.qApp.processEvents() # could change interval
320         self._timer.start()
321                 
322     def doIteration(self):
323         assert False, "doiteration is invalid call"
324             
325 def install():
326     """
327     Configure the twisted mainloop to be run inside the qt mainloop.
328     """
329     from twisted.internet import main
330     reactor = QTReactor()
331     main.installReactor(reactor)