Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Winship <dwinship@redhat.com>2007-07-27 16:19:36 (GMT)
committer Dan Winship <dwinship@redhat.com>2007-07-27 16:19:36 (GMT)
commit58b53633d99eb739c84e123f418e41b1a4048f90 (patch)
treee2407cd39799ce13bc8396b1bd19178a67677510
parent0dd4de95e1664329629882f635aa2177a1306e0f (diff)
Add temporary "Backup" button, for Trial-2
-rw-r--r--NEWS2
-rw-r--r--backup.py165
-rw-r--r--journaltoolbox.py20
3 files changed, 187 insertions, 0 deletions
diff --git a/NEWS b/NEWS
index d4579f5..ad17e63 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,5 @@
+* Added temporary backup capability for Trial-2 (danw)
+
35
* Fix resume icon. (tomeu)
diff --git a/backup.py b/backup.py
new file mode 100644
index 0000000..2dee6a4
--- /dev/null
+++ b/backup.py
@@ -0,0 +1,165 @@
+# Copyright (C) 2007 Red Hat, Inc.
+#
+# This library 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 of the License, or (at your option) any later version.
+#
+# This library 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 library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+import gobject
+import gtk
+import sugar.env
+import sugar.profile
+
+import os
+import popen2
+import re
+import signal
+import sys
+from threading import Thread
+
+def start_backup(verbose):
+ backup_info = sugar.profile.get_trial2_backup()
+ if not backup_info:
+ raise RuntimeError("Backup key not defined in Sugar profile")
+
+ match = re.match(r'^([^@]*)@([^:]*):(.*)$', backup_info)
+ if not match:
+ raise RuntimeError("Backup key '%s' is not user@host:path" % backup_info)
+
+ remote_user = match.group(1)
+ server = match.group(2)
+ remote_path = match.group(3)
+
+ if sugar.env.is_emulator():
+ local_path = sugar.env.get_profile_path()
+ else:
+ local_path = os.path.expanduser('~')
+
+ privkey = sugar.env.get_profile_path('owner.key')
+
+ ssh_cmd = '/usr/bin/ssh -F /dev/null -o "PasswordAuthentication no" -i "%s" -l "%s"' % (privkey, remote_user)
+ rsync_cmd = '/usr/bin/rsync -az%s -e \'%s\' %s "%s:%s"' % (verbose and 'P' or '', ssh_cmd, local_path, server, remote_path)
+
+ pipe = popen2.Popen3(rsync_cmd, True)
+ if pipe.poll() != -1:
+ raise RuntimeError('rsync failed: %s' % pipe.childerr.read())
+
+ return pipe
+
+
+class BackupThread(Thread):
+ def __init__(self, progress_cb, done_cb):
+ self._progress_cb = progress_cb
+ self._done_cb = done_cb
+ self._errmsg = None
+ self._pipe = start_backup(True)
+
+ Thread.__init__(self, target=self._backup_thread)
+
+ def _backup_thread(self):
+ for line in self._pipe.fromchild:
+ # After each file, rsync prints a line something like:
+ # 9350 100% 4.46MB/s 0:00:00 (xfer#9, to-check=7719/7769)
+ match = re.match(r'.*to-check=(\d+)/(\d+)', line)
+ if match:
+ try:
+ remaining = int(match.group(1))
+ total = int(match.group(2))
+ progress = (total - remaining) * 100 / total
+ gobject.idle_add(self._progress_cb, progress)
+ except:
+ pass
+
+ if self._pipe.poll() != 0:
+ self._errmsg = self._pipe.childerr.read()
+ gobject.idle_add(self._done_cb)
+
+ def errmsg(self):
+ return self._errmsg
+
+ def kill(self):
+ if self._pipe.poll() == -1:
+ os.kill(self._pipe.pid, signal.SIGINT)
+
+class BackupDialog(gtk.Dialog):
+ def __init__(self):
+ gtk.Dialog.__init__(self, flags=gtk.DIALOG_MODAL)
+ self.set_title('Backup')
+ self.set_has_separator(False)
+
+ label = gtk.Label('Backing up data to school server...')
+ self.vbox.pack_start(label)
+
+ self._progress_bar = gtk.ProgressBar()
+ self.vbox.pack_start(self._progress_bar)
+
+ self.vbox.show_all()
+
+ self.add_button(gtk.STOCK_STOP, gtk.RESPONSE_CLOSE)
+
+ self._thread = BackupThread(self._progress_cb, self._done_cb)
+ self._thread.start()
+ self._timeout = gobject.timeout_add(100, self._timeout_cb)
+
+ self.connect('response', self._response_cb)
+
+ def _timeout_cb(self):
+ self._progress_bar.pulse()
+ return True
+
+ def _progress_cb(self, percent):
+ self._progress_bar.set_fraction(percent / 100.0)
+ if self._timeout:
+ gobject.source_remove(self._timeout)
+ self._timeout = None
+ return False
+
+ def _done_cb(self):
+ self.response(gtk.RESPONSE_CLOSE)
+
+ def _response_cb(self, widget, response):
+ if self._timeout:
+ gobject.source_remove(self._timeout)
+
+ if self._thread.errmsg():
+ dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR,
+ gtk.BUTTONS_OK,
+ "Backup failed:\n%s" % self._thread.errmsg())
+ dlg.run()
+ elif self._thread.isAlive():
+ self._thread.kill()
+
+ self.destroy()
+
+def backup_gui():
+ try:
+ BackupDialog().run()
+ except RuntimeError, e:
+ dlg = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, \
+ gtk.BUTTONS_OK, 'Backup failed: %s' % str(e))
+ dlg.run()
+
+def backup_cron():
+ try:
+ pipe = start_backup(False)
+ sys.exit(pipe.wait())
+ except RuntimeError, e:
+ sys.stderr.write("Backup failed: %s\n" % str(e))
+ sys.exit(1)
+
+if __name__ == "__main__":
+ if os.environ.has_key('DISPLAY'):
+ gobject.threads_init()
+ backup_gui()
+ else:
+ backup_cron()
diff --git a/journaltoolbox.py b/journaltoolbox.py
index 7237fa4..dbf10c5 100644
--- a/journaltoolbox.py
+++ b/journaltoolbox.py
@@ -22,6 +22,8 @@ from datetime import datetime, timedelta
import gobject
import gtk
+import backup
+
from sugar.graphics import units
from sugar.graphics import color
from sugar.graphics.xocolor import XoColor
@@ -45,6 +47,10 @@ class JournalToolbox(Toolbox):
self.add_toolbar(_('Entry'), self.entry_toolbar)
self.entry_toolbar.show()
+ self.backup_toolbar = BackupToolbar()
+ self.add_toolbar(_('Backup'), self.backup_toolbar)
+ self.backup_toolbar.show()
+
#self.create_toolbar = CreateToolbar()
#self.add_toolbar(_('Create'), self.create_toolbar)
#self.create_toolbar.show()
@@ -342,3 +348,17 @@ class EntryToolbar(gtk.Toolbar):
self._copy.props.sensitive = False
self._resume.props.sensitive = False
+class BackupToolbar(gtk.Toolbar):
+ __gtype_name__ = 'BackupToolbar'
+
+ def __init__(self):
+ gtk.Toolbar.__init__(self)
+
+ self._backup = ToolButton('document-save')
+ self._backup.set_tooltip(_('Backup'))
+ self._backup.connect('clicked', self._backup_clicked_cb)
+ self.insert(self._backup, -1)
+ self._backup.show()
+
+ def _backup_clicked_cb(self, button):
+ backup.backup_gui()