Swithing over to the new code
[multilist] / src / libsync.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 """
18
19 import gobject
20 import time
21 import string
22 from SimpleXMLRPCServer import SimpleXMLRPCServer 
23 import random
24 import socket 
25 socket.setdefaulttimeout(60) # Timeout auf 60 sec. setzen 
26 import xmlrpclib 
27 import select
28 #import fcntl
29 import struct
30 import gtk
31 import uuid
32 import sys
33 import logging
34
35 import libspeichern 
36  
37  
38 class ProgressDialog(gtk.Dialog):
39         
40         def pulse(self):
41                 #self.progressbar.pulse()
42                 pass
43         
44         def __init__(self,title=_("Sync process"), parent=None):
45                 gtk.Dialog.__init__(self,title,parent,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,())
46                 
47                 logging.info("ProgressDialog, init")
48                 
49                 label=gtk.Label(_("Sync process running...please wait"))
50                 self.vbox.pack_start(label, True, True, 0)
51                 label=gtk.Label(_("(this can take some minutes)"))
52                 self.vbox.pack_start(label, True, True, 0)
53                 
54                 #self.progressbar=gtk.ProgressBar()
55                 #self.vbox.pack_start(self.progressbar, True, True, 0)
56                 
57                 #self.set_keep_above(True)
58                 self.vbox.show_all()
59                 self.show()
60  
61 class Sync(gtk.VBox): 
62         
63         __gsignals__ = {
64                 'syncFinished' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_STRING,)),
65                 'syncBeforeStart' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_STRING,)),
66         }
67         
68         def changeSyncStatus(self,active,title):
69                 self.syncStatusLabel.set_text(title)
70                 if active==True:
71                         if self.progress==None:
72                                 self.progress=ProgressDialog(parent=self.parentwindow)
73                                 self.emit("syncBeforeStart","syncBeforeStart")
74                                 
75                                 
76                 else:
77                         if self.progress!=None:
78                                 self.progress.hide()            
79                                 self.progress.destroy()
80                                 self.progress=None
81                                 self.emit("syncFinished","syncFinished")
82         
83         def pulse(self):
84                 if self.progress!=None:
85                         self.progress.pulse()
86                 #if self.server!=None:
87                 #       self.server.pulse()
88                 
89         
90         def getUeberblickBox(self):
91                 frame=gtk.Frame(_("Query"))
92                 return frame
93                         
94         def handleRPC(self):
95                 try:
96                         if (self.rpcserver==None): return False
97                 except:
98                         return False
99                         
100                 while (len(self.poll.poll(0))>0):
101                         self.rpcserver.handle_request()
102                 return True
103
104         def get_ip_address(self,ifname):
105                 return socket.gethostbyname(socket.gethostname())
106                 #try:
107                 #       s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
108                 #       ip=socket.inet_ntoa(fcntl.ioctl(s.fileno(),0x8915,struct.pack('256s', ifname[:15]))[20:24])
109                 #       s.close()
110                 #except:
111                 #       ip=socket.gethostbyname(socket.gethostname())
112                 #       s.close()
113                 
114                 #return ip FixME
115                 
116         def getLastSyncDate(self,sync_uuid):
117                 sql="SELECT syncpartner,pcdatum FROM sync WHERE uuid=?"
118                 rows=self.db.ladeSQL(sql,(sync_uuid,))
119                 if (rows!=None)and(len(rows)==1): 
120                         syncpartner,pcdatum = rows[0]
121                 else:
122                         pcdatum=-1
123                 logging.info("LastSyncDatum: "+str(pcdatum)+" Jetzt "+str(int(time.time())))
124                 return pcdatum
125                 
126                 
127         def check4commit(self,newSQL,lastdate):
128                 logging.info("check4commit 1")
129                 if self.concernedRows==None:
130                         logging.info("check4commit Updatung concernedRows")
131                         sql="SELECT pcdatum,rowid FROM logtable WHERE pcdatum>? ORDER BY pcdatum DESC"
132                         self.concernedRows=self.db.ladeSQL(sql,(lastdate,))
133                         
134                         
135                 if (self.concernedRows!=None)and(len(self.concernedRows)>0):
136                         #logging.info("check4commit 2")
137                         id1, pcdatum,sql, param, host, rowid = newSQL
138                         
139                         if len(rowid)>0:
140                                 for x in self.concernedRows:
141                                         #logging.info("check4commit 3")
142                                         if (x[1]==rowid):
143                                                 if (x[0]>pcdatum):
144                                                         logging.info("newer sync entry, ignoring old one")
145                                                         #logging.info("check4commit 9.1")
146                                                         return False
147                                                 else:
148                                                         #logging.info("check4commit 9.2")
149                                                         return True
150                                                         
151                 #logging.info("check4commit 9.3")
152                 return True
153         
154         def writeSQLTupel(self,newSQLs,lastdate):
155                 if (newSQLs==None):
156                          return
157                 
158                 self.concernedRows=None
159                 pausenzaehler=0
160                 logging.info("writeSQLTupel got "+str(len(newSQLs))+" sql tupels")
161                 for newSQL in newSQLs:
162                         #print ""
163                         #print "SQL1: ",newSQL[1]
164                         #print "SQL2: ",newSQL[2]
165                         #print "SQL3: ",newSQL[3]
166                         #print "Param:",string.split(newSQL[3]," <<Tren-ner>> ")
167                         #print ""
168                         if (newSQL[3]!=""):
169                                 param=string.split(newSQL[3]," <<Tren-ner>> ")
170                         else:
171                                 param=None
172                 
173                         if (len(newSQL)>2):
174                                 commitSQL=True
175
176                                 if (newSQL[5]!=None)and(len(newSQL[5])>0):
177                                         commitSQL=self.check4commit(newSQL,lastdate)
178                                         
179                                 if (commitSQL==True):
180                                         self.db.speichereSQL(newSQL[2],param,commit=False,pcdatum=newSQL[1],rowid=newSQL[5])
181                         else: 
182                                 logging.error("writeSQLTupel: Error")
183                                 
184                         pausenzaehler+=1
185                         if (pausenzaehler % 10)==0:
186                                 self.pulse()
187                                 while (gtk.events_pending()):
188                                         gtk.main_iteration();
189                                 
190                 logging.info("Alle SQLs an sqlite geschickt, commiting now")
191                 self.db.commitSQL()
192                 logging.info("Alle SQLs commited")
193                 
194         
195         def doSync(self,sync_uuid,pcdatum,newSQLs,pcdatumjetzt):
196                 #print uuid,pcdatum,newSQLs
197                 #logging.info("doSync 0")
198                 self.changeSyncStatus(True,_("sync process running"))
199                 self.pulse()
200                 #logging.info("doSync 1")
201                 
202                 while (gtk.events_pending()):
203                         gtk.main_iteration();
204                 diff=time.time()-pcdatumjetzt
205                 if diff<0:
206                         diff=diff*(-1)
207                 if diff>30:
208                         return -1
209                 
210                 logging.info("doSync read sqls")
211                 sql="SELECT * FROM logtable WHERE pcdatum>?"
212                 rows=self.db.ladeSQL(sql,(pcdatum,))
213                 logging.info("doSync read sqls")
214                 self.writeSQLTupel(newSQLs,pcdatum)
215                 logging.info("doSync wrote "+str(len(newSQLs))+" sqls")
216                 logging.info("doSync sending "+str(len(rows))+" sqls")
217                 i=0
218                 return rows
219                 
220         def getRemoteSyncUUID(self):
221                 return self.sync_uuid
222         
223         
224         def startServer(self, widget, data=None):
225                 #Starte RPCServer
226                 self.db.speichereDirekt("syncServerIP",self.comboIP.get_child().get_text())
227                 
228                 if (widget.get_active()==True):
229                         logging.info("Starting Server")
230                         
231                         try:
232                                 ip=self.comboIP.get_child().get_text()
233                                 self.rpcserver = SimpleXMLRPCServer((ip, self.port),allow_none=True) 
234                                 self.rpcserver.register_function(pow)
235                                 self.rpcserver.register_function(self.getLastSyncDate)
236                                 self.rpcserver.register_function(self.doSync)
237                                 self.rpcserver.register_function(self.getRemoteSyncUUID)
238                                 self.rpcserver.register_function(self.doSaveFinalTime)
239                                 self.rpcserver.register_function(self.pulse)
240                                 self.poll=select.poll()
241                                 self.poll.register(self.rpcserver.fileno())
242                                 gobject.timeout_add(1000, self.handleRPC)
243                                 self.syncServerStatusLabel.set_text(_("Syncserver running..."))
244                         
245                                 #save
246                                 self.db.speichereDirekt("startSyncServer",True)
247                         
248                         except:
249                                 s=str(sys.exc_info())
250                                 logging.error("libsync: could not start server. Error: "+s)
251                                 mbox=gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_ERROR,gtk.BUTTONS_OK,_("Sync server could not start. Please check IP and port.")) #gtk.DIALOG_MODAL
252                                 mbox.set_modal(False)
253                                 response=mbox.run() 
254                                 mbox.hide() 
255                                 mbox.destroy() 
256                                 widget.set_active(False)
257                                 
258                 else:
259                         logging.info("Stopping Server")
260                         try:
261                                 del self.rpcserver      
262                         except:
263                                 pass
264                         self.syncServerStatusLabel.set_text(_("Syncserver not running..."))
265                         #save
266                         self.db.speichereDirekt("startSyncServer",False)
267                 
268         def doSaveFinalTime(self,sync_uuid,pcdatum=None):
269                 if (pcdatum==None): pcdatum=int(time.time())
270                 if (time.time()>pcdatum):
271                         pcdatum=int(time.time()) #größere Zeit nehmen
272                         
273                 self.pulse()
274                 
275                 #fime save time+uuid
276                 sql="DELETE FROM sync WHERE uuid=?"
277                 self.db.speichereSQL(sql,(sync_uuid,),log=False)
278                 sql="INSERT INTO sync (syncpartner,uuid,pcdatum) VALUES (?,?,?)"
279                 self.db.speichereSQL(sql,("x",str(sync_uuid),pcdatum),log=False)
280                 self.pulse()
281                 self.changeSyncStatus(False,_("no sync process (at the moment)"))
282                 return (self.sync_uuid,pcdatum)
283                 
284         
285         def syncButton(self, widget, data=None):
286                 logging.info("Syncing")
287                 #sql="DELETE FROM logtable WHERE sql LIKE externeStundenplanung"
288                 #self.db.speichereSQL(sql)
289                 
290                 self.changeSyncStatus(True,_("sync process running"))
291                 while (gtk.events_pending()):
292                         gtk.main_iteration();
293
294                 self.db.speichereDirekt("syncRemoteIP",self.comboRemoteIP.get_child().get_text())
295                 try:
296                         self.server = xmlrpclib.ServerProxy("http://"+self.comboRemoteIP.get_child().get_text()+":"+str(self.port),allow_none=True) 
297                         #lastDate=server.getLastSyncDate(str(self.sync_uuid))
298                         server_sync_uuid=self.server.getRemoteSyncUUID()
299                         lastDate=self.getLastSyncDate(str(server_sync_uuid))
300                         
301                         #print ("LastSyncDate: "+str(lastDate)+" Now: "+str(int(time.time())))
302                 
303                         sql="SELECT * FROM logtable WHERE pcdatum>?"
304                         rows=self.db.ladeSQL(sql,(lastDate,))
305                         
306                         logging.info("loaded concerned rows")
307                 
308                         newSQLs=self.server.doSync(self.sync_uuid,lastDate,rows,time.time())
309                 
310                         logging.info("did do sync, processing sqls now")
311                         if newSQLs!=-1:
312                                 self.writeSQLTupel(newSQLs,lastDate)
313         
314                                 sync_uuid, finalpcdatum=self.server.doSaveFinalTime(self.sync_uuid)
315                                 self.doSaveFinalTime(sync_uuid, finalpcdatum)
316                         
317                                 self.changeSyncStatus(False,_("no sync process (at the moment)"))
318                                 
319                                 mbox =  gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_INFO,gtk.BUTTONS_OK,_("Synchronization successfully completed"))
320                                 response = mbox.run() 
321                                 mbox.hide() 
322                                 mbox.destroy() 
323                         else:
324                                 logging.warning("Zeitdiff zu groß/oder anderer db-Fehler")
325                                 self.changeSyncStatus(False,_("no sync process (at the moment)"))
326                                 mbox =  gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_INFO,gtk.BUTTONS_OK,_("The clocks are not synchronized between stations"))
327                                 response = mbox.run() 
328                                 mbox.hide() 
329                                 mbox.destroy() 
330                 except:
331                                 logging.warning("Sync connect failed")
332                                 self.changeSyncStatus(False,_("no sync process (at the moment)"))
333                                 mbox =  gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_INFO,gtk.BUTTONS_OK,_("Sync failed, reason: ")+unicode(sys.exc_info()[1][1]))
334                                 response = mbox.run() 
335                                 mbox.hide() 
336                                 mbox.destroy() 
337                                 self.server=None
338                 self.server=None
339                                 
340
341                         
342         
343         def __init__(self,db,parentwindow,port):
344                 gtk.VBox.__init__(self,homogeneous=False, spacing=0)
345                 
346                 logging.info("Sync, init")
347                 self.db=db
348                 self.progress=None
349                 self.server=None
350                 self.port=int(port)
351                 self.parentwindow=parentwindow
352                 self.concernedRows=None
353                 
354                 #print "Sync, 2"
355                 #sql = "DROP TABLE sync"
356                 #self.db.speichereSQL(sql,log=False)
357                 
358                 sql = "CREATE TABLE sync (id INTEGER PRIMARY KEY, syncpartner TEXT, uuid TEXT, pcdatum INTEGER)"
359                 self.db.speichereSQL(sql,log=False)
360                 
361                 #print "Sync, 3"
362                 
363                 sql="SELECT uuid,pcdatum FROM sync WHERE syncpartner=?"
364                 rows=self.db.ladeSQL(sql,("self",)) #Eigene Id feststellen
365                 
366                 #print "Sync, 3a"
367                 if (rows==None)or(len(rows)!=1):
368                         sql="DELETE FROM sync WHERE syncpartner=?"
369                         self.db.speichereSQL(sql,("self",),log=False)
370
371                         #uuid1=uuid()
372                         #print "Sync, 3b"
373                         
374                         #print "Sync, 3bb"
375                         self.sync_uuid=str(uuid.uuid4())
376                         sql="INSERT INTO sync (syncpartner,uuid,pcdatum) VALUES (?,?,?)"
377                         self.db.speichereSQL(sql,("self",str(self.sync_uuid),int(time.time())),log=False)
378                         #print "Sync, 3c"
379                 else:
380                         sync_uuid,pcdatum = rows[0]
381                         self.sync_uuid=sync_uuid
382                 #print "x1"
383                 
384                 
385                 
386                 #print "Sync, 4"
387
388                 
389                 frame=gtk.Frame(_("Local SyncServer (port ")+str(self.port)+")")
390         
391                 
392                 
393                 self.comboIP=gtk.combo_box_entry_new_text()
394                 
395                 
396                 self.comboIP.append_text("") #self.get_ip_address("eth0"))
397                 #self.comboIP.append_text(self.get_ip_address("eth1")) #fixme
398                 #self.comboIP.append_text(self.get_ip_address("eth2"))
399                 #self.comboIP.append_text(self.get_ip_address("eth3"))
400                 #print "Sync, 4d"
401                 #self.comboIP.append_text(self.get_ip_address("wlan0"))
402                 #self.comboIP.append_text(self.get_ip_address("wlan1"))
403                 
404                 #print "Sync, 4e"
405                 
406                 frame.add(self.comboIP)
407                 serverbutton=gtk.ToggleButton(_("Start SyncServer"))
408                 serverbutton.connect("clicked",self.startServer,(None,))
409                 self.pack_start(frame, expand=False, fill=True, padding=1)
410                 self.pack_start(serverbutton, expand=False, fill=True, padding=1)
411                 self.syncServerStatusLabel=gtk.Label(_("Syncserver not running"))
412                 self.pack_start(self.syncServerStatusLabel, expand=False, fill=True, padding=1)         
413                                 
414                 frame=gtk.Frame(_("RemoteSync-Server (Port ")+str(self.port)+")")
415                 self.comboRemoteIP=gtk.combo_box_entry_new_text()
416                 self.comboRemoteIP.append_text("192.168.0.?")
417                 self.comboRemoteIP.append_text("192.168.1.?")
418                 self.comboRemoteIP.append_text("192.168.176.?")
419                 frame.add(self.comboRemoteIP)
420                 syncbutton=gtk.Button(_("Connect to remote SyncServer"))
421                 syncbutton.connect("clicked",self.syncButton,(None,))
422                 self.pack_start(frame, expand=False, fill=True, padding=1)
423                 self.pack_start(syncbutton, expand=False, fill=True, padding=1)
424                 self.syncStatusLabel=gtk.Label(_("no sync process (at the moment)"))
425                 self.pack_start(self.syncStatusLabel, expand=False, fill=True, padding=1)
426
427
428                 #self.comboRemoteIP.set_text_column("Test")
429                 self.comboRemoteIP.get_child().set_text(self.db.ladeDirekt("syncRemoteIP"))
430                 self.comboIP.get_child().set_text(self.db.ladeDirekt("syncServerIP"))
431                 
432                 #load
433                 if (self.db.ladeDirekt("startSyncServer",False)==True):
434                         serverbutton.set_active(True)
435                         
436                 #print "Sync, 9"