[PATCH] Adds-view-and-scripts-for-volumes-backup-restore
Esteban Arias
earias at plan.ceibal.edu.uy
Wed Jun 2 11:48:03 EDT 2010
---
bin/Makefile.am | 2 +
bin/journal-backup-volume | 65 +++++++++
bin/journal-restore-volume | 67 ++++++++++
src/jarabe/journal/Makefile.am | 3 +-
src/jarabe/journal/backup.py | 231
+++++++++++++++++++++++++++++++++
src/jarabe/model/Makefile.am | 3 +-
src/jarabe/model/processmanagement.py | 88 +++++++++++++
7 files changed, 457 insertions(+), 2 deletions(-)
create mode 100644 bin/journal-backup-volume
create mode 100644 bin/journal-restore-volume
create mode 100644 src/jarabe/journal/backup.py
create mode 100644 src/jarabe/model/processmanagement.py
diff --git a/bin/Makefile.am b/bin/Makefile.am
index 05a9215..90f157a 100644
--- a/bin/Makefile.am
+++ b/bin/Makefile.am
@@ -9,6 +9,8 @@ python_scripts =3D \
bin_SCRIPTS =3D \
sugar \
+ journal-backup-volume \
+ journal-restore-volume \
$(python_scripts)
EXTRA_DIST =3D $(python_scripts) sugar.in
diff --git a/bin/journal-backup-volume b/bin/journal-backup-volume
new file mode 100644
index 0000000..e4a070f
--- /dev/null
+++ b/bin/journal-backup-volume
@@ -0,0 +1,65 @@
+#!/bin/bash
+# Journal Backup
+# Copyright (C) 2010 Plan Ceibal
+# Copyright (C) 2010 Paraguay Educa
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Contact information: comunidad at plan.ceibal.edu.uy
+# Plan Ceibal http://www.ceibal.edu.uy
+
+function check_for_errors() {
+ if [[ $? !=3D 0 ]]; then
+ echo "$1" >&2
+ exit 1
+ fi;
+}
+
+function validate_path() {
+ if !(test -e "$1"); then
+ echo "No such file $1" >&2
+ exit 1
+ fi;
+}
+
+DATASTORE=3D"$HOME/.sugar/default/datastore/"
+validate_path $DATASTORE
+
+if [ -z "$1" ]; then
+ echo "Backup path is not valid." >&2
+ exit 1
+fi;
+
+PARAMS_PATH=3D$1
+validate_path $PARAMS_PATH
+
+if [ -z "$2" ]; then
+ echo "Backup name is not valid." >&2
+ exit 1
+fi;
+
+SERIAL=3D$2
+
+#TODO: Lets find a sugar-way to do this?
+pkill -f -x "python /usr/bin/datastore-service"
+
+BACKUP_PATH=3D$PARAMS_PATH/.backup/$SERIAL/
+mkdir -p $BACKUP_PATH
+
+cd $DATASTORE;
+tar -zcf "$BACKUP_PATH"datastore.tar.gz ./*
+
+check_for_errors
+
+exit 0
diff --git a/bin/journal-restore-volume b/bin/journal-restore-volume
new file mode 100644
index 0000000..134be1c
--- /dev/null
+++ b/bin/journal-restore-volume
@@ -0,0 +1,67 @@
+#!/bin/bash
+# Journal Restore
+# Copyright (C) 2010 Plan Ceibal
+# Copyright (C) 2010 Paraguay Educa
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Contact information: comunidad at plan.ceibal.edu.uy
+# Plan Ceibal http://www.ceibal.edu.uy
+
+function check_for_errors() {
+ if [[ $? !=3D 0 ]]; then
+ echo "$1" >&2
+ exit 1
+ fi;
+}
+
+function validate_path() {
+ if !(test -e "$1"); then
+ echo "No such file $1" >&2
+ exit 1
+ fi;
+}
+
+DATASTORE_PATH=3D"$HOME/.sugar/default/datastore/"
+validate_path $DATASTORE_PATH
+
+if [ -z "$1" ]; then
+ echo "Backup path is not valid." >&2
+ exit 1
+fi;
+
+PARAMS_PATH=3D$1
+validate_path $PARAMS_PATH
+
+if [ -z "$2" ]; then
+ echo "Backup name is not valid." >&2
+ exit 1
+fi;
+
+SERIAL=3D$2
+
+BACKUP_FILE=3D$PARAMS_PATH/.backup/$SERIAL/datastore.tar.gz
+validate_path $BACKUP_FILE
+
+#TODO: Lets find a sugar-way to do this?
+pkill -f -x "python /usr/bin/datastore-service"
+
+cd $DATASTORE_PATH
+rm -rf $DATASTORE_PATH*
+
+tar -xvzf $BACKUP_FILE
+
+check_for_errors
+
+exit 0
diff --git a/src/jarabe/journal/Makefile.am b/src/jarabe/journal/Makefile.a=
m
index f4bf273..6a3091e 100644
--- a/src/jarabe/journal/Makefile.am
+++ b/src/jarabe/journal/Makefile.am
@@ -14,4 +14,5 @@ sugar_PYTHON =3D \
model.py \
objectchooser.py \
palettes.py \
- volumestoolbar.py
+ volumestoolbar.py \
+ backup.py
diff --git a/src/jarabe/journal/backup.py b/src/jarabe/journal/backup.py
new file mode 100644
index 0000000..1b12660
--- /dev/null
+++ b/src/jarabe/journal/backup.py
@@ -0,0 +1,231 @@
+#!/usr/bin/env python
+# Copyright (C) 2010, Plan Ceibal <comunidad at plan.ceibal.edu.uy>
+# Copyright (C) 2010, Paraguay Educa <tecnologia at paraguayeduca.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import gtk
+import gobject
+import gconf
+import logging
+
+from gettext import gettext as _
+from sugar.graphics import style
+from sugar.graphics.icon import Icon
+from sugar.graphics.xocolor import XoColor
+
+from jarabe.model.session import get_session_manager
+from jarabe.model.processmanagement import ProcessManagement
+
+PATH_BACKUP =3D '/usr/bin/journal-backup-volume'
+PATH_RESTORE =3D '/usr/bin/journal-restore-volume'
+
+class Backup(gtk.Window):
+
+ __gtype_name__ =3D 'SugarJournalBackup'
+
+ def __init__(self, type_ps, mount_path):
+
+ self._type_ps =3D type_ps
+ self._mount_path =3D mount_path
+
+ if self._isValidType(self._type_ps):
+ if ((not self._mount_path =3D=3D "") and (not self._mount_path=
=3D=3D
None)):
+ self._progressBarHandler =3D None
+
+ gtk.Window.__init__(self)
+
+ self.set_border_width(style.LINE_WIDTH)
+ width =3D gtk.gdk.screen_width()
+ height =3D gtk.gdk.screen_height()
+ self.set_size_request(width, height)
+ self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
+ self.set_decorated(False)
+ self.set_resizable(False)
+ self.set_modal(True)
+
+ self._main_view =3D gtk.EventBox()
+ self._vbox =3D gtk.VBox()
+ self._vbox.set_spacing(style.DEFAULT_SPACING)
+ self._vbox.set_border_width(style.GRID_CELL_SIZE)
+ self._main_view.modify_bg(gtk.STATE_NORMAL,
+
style.COLOR_BLACK.get_gdk_color())
+ self._main_view.add(self._vbox)
+ self._vbox.show()
+
+ client =3D gconf.client_get_default()
+ color =3D
XoColor(client.get_string('/desktop/sugar/user/color'))
+
+ icon =3D Icon(icon_name=3D'activity-journal',
+ pixel_size=3Dstyle.XLARGE_ICON_SIZE,
+ xo_color=3Dcolor)
+ self._vbox.pack_start(icon, False)
+ icon.show()
+
+ self._title =3D gtk.Label()
+ self._title.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_WHITE.get_gdk_color())
+ self._title.set_markup('<b>%s</b>' % _(self._type_ps))
+ self._vbox.pack_start(self._title, False)
+ self._title.show()
+
+ if self._type_ps =3D=3D 'backup':
+ lbl_txt =3D _("Close all activities and do not remove =
the
external device during the process. \n\n"
+ "To make the backup of the Journal, a
restart is required. \n\n"
+ "The backup can only be restore from this
computer")
+ elif self._type_ps =3D=3D 'restore':
+ lbl_txt =3D _("The content you restore will overwrite =
the
current contents of the Journal. \n\n"
+ "Do not remove the external device during
the process. \n"
+ "To implement the restoration of the
Journal, a restart is required. \n\n"
+ "It will restore the backup done for this
computer ")
+ lbl_txt +=3D self._get_serial_number()
+
+ self._message =3D gtk.Label(lbl_txt)
+ self._message.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_WHITE.get_gdk_color())
+ self._message.set_line_wrap(True)
+ self._vbox.pack_start(self._message, True)
+ self._message.show()
+
+
+ vbox =3D gtk.VBox(False, 5)
+ vbox.show()
+ hbox =3D gtk.HBox(True, 3)
+ hbox.show()
+
+ valign =3D gtk.Alignment(0, 1, 0, 0)
+ valign.show()
+ vbox.pack_start(valign)
+
+ self._accept =3D gtk.Button()
+ self._accept.set_label(_('Accept'))
+ self._accept.show()
+ self._accept.connect('clicked', self.__accept_cb)
+
+ self._show_journal =3D gtk.Button()
+ self._show_journal.set_label(_('Close'))
+ self._show_journal.show()
+ self._show_journal.connect('clicked',
self.__show_journal_cb)
+
+ self._restart =3D gtk.Button()
+ self._restart.set_label(_('Restart'))
+ self._restart.hide()
+ self._restart.connect('clicked', self.__restart_cb)
+
+ hbox.add(self._accept)
+ hbox.add(self._show_journal)
+ hbox.add(self._restart)
+
+ halign =3D gtk.Alignment(1, 0, 0, 0)
+ halign.show()
+ halign.add(hbox)
+
+ vbox.pack_start(halign, False, False, 3)
+
+ self._vbox.add(vbox)
+
+ self._progress_bar =3D gtk.ProgressBar(adjustment=3DNone)
+
+ self.add(self._main_view)
+ self._main_view.show()
+
+ self.connect("realize", self.__realize_cb)
+
+ self._process_management =3D ProcessManagement()
+
self._process_management.connect('process-management-running',
self._set_update_log)
+
self._process_management.connect('process-management-started',
self._set_status_started)
+
self._process_management.connect('process-management-finished',
self._set_status_finished)
+
self._process_management.connect('process-management-failed',
self._set_status_failed)
+
+ def __realize_cb(self, widget):
+ self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
+ self.window.set_accept_focus(True)
+
+ def __show_journal_cb(self, button):
+ self.destroy()
+
+ def __accept_cb(self, *args):
+ if ((not self._mount_path =3D=3D "") and (not self._mount_path =3D=
=3D
None)):
+ if self._isValidType(self._type_ps):
+ if self._type_ps =3D=3D 'backup':
+ self._process_management.do_process([PATH_BACKUP,
self._mount_path, self._get_serial_number()])
+ elif self._type_ps =3D=3D 'restore':
+ self._process_management.do_process([PATH_RESTORE,
self._mount_path, self._get_serial_number()])
+
+ def _set_status_started(self, model, data=3DNone):
+ self._message.set_text(_("Is running, Please wait"))
+ self._accept.hide()
+ self._show_journal.hide()
+ self._showProgress()
+
+ def _set_update_log(self, model, data):
+ pass
+
+ def _set_status_finished(self, model, data=3DNone):
+ self._message.set_text(_("It is done, please restart sugar"))
+ self._progress_bar.hide()
+ self._restart.show()
+
+ def _set_status_failed(self, model, data=3DNone):
+ logging.error(data)
+ self._showMsgError(data)
+
+ def __restart_cb(self, *args):
+ session_manager =3D get_session_manager()
+ session_manager.logout()
+
+ def _showProgress(self):
+ self._progress_bar.set_fraction(0.0)
+ self._progress_bar.show()
+ self._vbox.add(self._progress_bar)
+ if (self._progressBarHandler =3D=3D None):
+ self._progressBarHandler =3D gobject.timeout_add(1000,
self._timerProgressBar)
+
+ def _timerProgressBar(self):
+ self._progress_bar.pulse()
+ return True
+
+ def _showMsgError(self, msg):
+ self._title.set_markup('<b>%s</b>' % _("Error"))
+ self._message.set_text(msg)
+ self._show_journal.show()
+ self._progress_bar.hide()
+
+ def _isValidType(self, type_ps):
+ return (type_ps =3D=3D 'backup') | (type_ps =3D=3D 'restore')
+
+ def _get_serial_number(self):
+ serial_no =3D self._read_file('/ofw/serial-number')
+ if serial_no is None:
+ serial_no =3D self._get_nick()
+ return serial_no
+
+ def _read_file(self, path):
+ if os.access(path, os.R_OK) =3D=3D 0:
+ return None
+
+ fd =3D open(path, 'r')
+ value =3D fd.read()
+ fd.close()
+ if value:
+ value =3D value.strip('\n')
+ return value
+ else:
+ logging.error('No information in file or directory: %s' % path=
)
+ return None
+
+ def _get_nick(self):
+ client =3D gconf.client_get_default()
+ return client.get_string("/desktop/sugar/user/nick")
diff --git a/src/jarabe/model/Makefile.am b/src/jarabe/model/Makefile.am
index e9f0700..a4f89a3 100644
--- a/src/jarabe/model/Makefile.am
+++ b/src/jarabe/model/Makefile.am
@@ -15,4 +15,5 @@ sugar_PYTHON =3D \
shell.py \
screen.py \
session.py \
- sound.py
+ sound.py \
+ processmanagement.py
diff --git a/src/jarabe/model/processmanagement.py
b/src/jarabe/model/processmanagement.py
new file mode 100644
index 0000000..d28ad64
--- /dev/null
+++ b/src/jarabe/model/processmanagement.py
@@ -0,0 +1,88 @@
+# Copyright (C) 2010 Paraguay Educa, Martin Abente, Bernie Innocenti
+# Copyright (C) 2010, Plan Ceibal <comunidad at plan.ceibal.edu.uy>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
USA
+
+import os
+import gobject
+import glib
+import gio
+
+from gettext import gettext as _
+
+BYTES_TO_READ =3D 100
+
+class ProcessManagement(gobject.GObject):
+
+ __gtype_name__ =3D 'ProcessManagement'
+
+ __gsignals__ =3D {
+ 'process-management-running' : (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([str])),
+ 'process-management-started' : (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
+ 'process-management-finished' : (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
+ 'process-management-failed' : (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([str]))
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+ self._running =3D False
+
+ def do_process(self, cmd):
+ self._run_cmd_async(cmd)
+
+ def _report_process_status(self, stream, result):
+ data =3D stream.read_finish(result)
+
+ if len(data):
+ self.emit('process-management-running', data)
+ stream.read_async(BYTES_TO_READ, self._report_process_status)
+
+ def _report_process_error(self, stream, result, concat_err=3D''):
+ data =3D stream.read_finish(result)
+ concat_err =3D concat_err + data
+
+ if len(data) =3D=3D 0:
+ self.emit('process-management-failed', concat_err)
+ else:
+ stream.read_async(BYTES_TO_READ, self._report_process_error,
user_data=3Dconcat_err)
+
+ def _notify_error(self, stderr):
+ stdin_stream =3D gio.unix.InputStream(stderr, True)
+ stdin_stream.read_async(BYTES_TO_READ, self._report_process_error)
+
+ def _notify_process_status(self, stdout):
+ stdin_stream =3D gio.unix.InputStream(stdout, True)
+ stdin_stream.read_async(BYTES_TO_READ, self._report_process_status=
)
+
+ def _run_cmd_async(self, cmd):
+ if self._running =3D=3D False:
+ try:
+ pid, stdin, stdout, stderr =3D glib.spawn_async(cmd,
flags=3Dglib.SPAWN_DO_NOT_REAP_CHILD, standard_output=3DTrue,
standard_error=3DTrue)
+ gobject.child_watch_add(pid, _handle_process_end, (self,
stderr))
+ except Exception:
+ self.emit('process-management-failed', _("Error - Call
process: ") + str(cmd))
+ else:
+ self._notify_process_status(stdout)
+ self._running =3D True
+ self.emit('process-management-started')
+
+def _handle_process_end(pid, condition, (myself, stderr)):
+ myself._running =3D False
+
+ if os.WIFEXITED(condition) and\
+ os.WEXITSTATUS(condition) =3D=3D 0:
+ myself.emit('process-management-finished')
+ else:
+ myself._notify_error(stderr)
--=20
1.6.2.5
More information about the Sugar-devel
mailing list