myarchive_dir.py

#!/usr/bin/env python3.9
# -*- coding: iso-8859-1 -*-
# FSArchiver is a system tool that allows you to save the contents of a
# file system to a compressed archive file. The file system can be restored
# on a partition which has a different size and it can be restored on a
# different file system.
# Unlike tar/dar, FSArchiver also creates the file system when it extracts
# the data to partitions.
# Everything is checksummed in the archive in order to protect the data. If
# the archive is corrupt, you just lose the current file, not the whole archive.
#
# It is possible to make consistent backups met fsarchiver if you are using LVM !
# see http://www.system-rescue-cd.org/lvm-guide-en/Making-consistent-backups-with-LVM/
#
# To start this GUI application , you need root rights !
#
"""
A GUI for fsarchiver using Python3 and Gtk3.0
Author:  Franz Ulenaers
Version: 1.0
Date: 2017/10/12
"""
import gi, os, time, threading
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk

global printdone, intervals

printdone = 1
intervals = (
    ('uren', 3600),
    ('minuten', 60),
    ('seconden', 1),
    )

def display_time(seconds, granularity=3):
    result = []
    for name, count in intervals:
        value = seconds // count
        if value:
            seconds -= value * count
            if value == 1:
                name = name.rstrip('s')
            result.append("{} {}".format(value, name))
    return ', '.join(result[:granularity])


class ArchiverGUI(object):
    def __init__(self):
        window = Gtk.Window()
        window.connect("destroy", Gtk.main_quit)
        vbox = Gtk.VBox(homogeneous=False, spacing=3, expand=False)

        g = Gtk.Grid()
        g.set_border_width(10)
        g.set_row_spacing(5)
        g.set_column_spacing(5)
        vbox.add(g)

        offset = 0
        frame = self.new_frame(g, offset, 7)
        label = Gtk.Label()
        label.set_markup('<span font="12" weight="bold">Backup een bestandsfolder met fsarchiver</span>')
        g.attach(label, 0, 0, 2, 1)
        labels = ["<b> Foldernaam te archiveren:</b>",
            "<b> Foldernaam voor gearchiveerd bestand:</b>",
            "<b> Gearchiveerd bestandsnaam:</b>",
            "<b> Compressie nummer:</b>",
            "<b> Aantal cpus/threads:</b>",
            "<b> Splitsen in aantal blokken van 1G</b>"]
        for i in range(6):
            label = Gtk.Label()
            label.set_markup(labels[i])
            g.attach(label, 0, 1 + i, 1, 1)

        # self.dev_list = self.get_device_labels()

        self.fn_to_archive = Gtk.FileChooserButton()
        self.fn_to_archive.set_title("Directory te archiveren")
        self.fn_to_archive.set_current_folder("/")
        self.fn_to_archive.set_action(Gtk.FileChooserAction.SELECT_FOLDER)
        g.attach(self.fn_to_archive, 1, 1, 1, 1)

        self.archive_file_path = Gtk.FileChooserButton()
        self.archive_file_path.set_title("Directory in which to save archive")
        self.archive_file_path.set_current_folder("/")
        self.archive_file_path.set_action(Gtk.FileChooserAction.SELECT_FOLDER)
        g.attach(self.archive_file_path, 1, 2, 1, 1)

        self.create_archive_filename = Gtk.Entry()
        g.attach(self.create_archive_filename, 1, 3, 1, 1)

        self.compression_level = Gtk.ComboBoxText()
        for i in range(1, 23):
            self.compression_level.append_text(str(i))
        g.attach(self.compression_level, 1, 4, 1, 1)

        self.number_of_threads = Gtk.ComboBoxText()
        for i in range(1, 6):
            self.number_of_threads.append_text(str(i))
        g.attach(self.number_of_threads, 1, 5, 1, 1)

        self.number_of_blocks = Gtk.ComboBoxText()
        for i in range(1, 7):
            self.number_of_blocks.append_text(str(i))
        g.attach(self.number_of_blocks, 1, 6, 1, 1)
        #============================

        offset = 8
        frame = self.new_frame(g, offset, 5)

        label = Gtk.Label()
        label.set_markup('<span font="12" weight="bold">Terugzetten gearchiveerd bestandssysteem</span>')

        g.attach(label, 0, offset, 2, 1)

        labels = ["<b>Archiveer bestandsnaam:</b>",
            "<b>Terugzetten naar foldernaam:</b>"]
        for i in range(2):
            label = Gtk.Label()
            label.set_markup(labels[i])
            g.attach(label, 0, offset + 1 + i, 1, 1)

        self.restore_archive_filename = Gtk.FileChooserButton()
        self.restore_archive_filename.set_title("Bestand bevat gearchiveerd bestandssysteem:")
        self.restore_archive_filename.set_current_folder("/")
        self.restore_archive_filename.connect("file-set", self.show_info_window)
        g.attach(self.restore_archive_filename, 1, offset + 1, 1, 1)

        # self.archive_position = Gtk.ComboBoxText.new_with_entry()
        # self.archive_position.append_text("0")
        # g.attach(self.archive_position, 1, offset + 2, 1, 1)

        self.destination_filesystem = Gtk.FileChooserButton()
        self.destination_filesystem.set_title("Directory in which to save archive")
        self.destination_filesystem.set_current_folder("/")
        self.destination_filesystem.set_action(Gtk.FileChooserAction.SELECT_FOLDER)
        g.attach(self.destination_filesystem, 1, offset + 2, 1, 1)
        #============================

        offset = 13
        frame = self.new_frame(g, offset, 3)

        btn_box = Gtk.HButtonBox()
        frame.add(btn_box)

        btn_box.set_layout(Gtk.ButtonBoxStyle.SPREAD)
        btn = Gtk.Button.new_from_stock(Gtk.STOCK_QUIT)
        btn.connect("clicked", Gtk.main_quit)
        btn_box.add(btn)
        btn = Gtk.Button.new_from_stock(Gtk.STOCK_EXECUTE)
        btn.connect("clicked", self.gather_the_info)
        btn_box.add(btn)

        #============================

        offset = 16
        #frame = self.new_frame(g,offset,3)

        self.status_label = Gtk.Label(label="Hier komt de status")
        #frame.add(self.status_label)
        g.attach(self.status_label, 0, 16, 4, 1)

        #============================

        window.add(vbox)
        window.show_all()

        self.info_window = Gtk.Dialog("Gearchiveerde Informatie")
        c = self.info_window.get_content_area()
        self.archive_info = Gtk.TextView()
        c.add(self.archive_info)
        a = self.info_window.get_action_area()
        btn = Gtk.Button.new_from_stock(Gtk.STOCK_CLOSE)
        btn.connect("clicked", lambda x: self.info_window.hide())
        a.add(btn)

    # ---------------------------------

    def display_time(seconds, granularity=3):
        result = []
        for name, count in intervals:
            value = seconds // count
            if value:
                seconds -= value * count
                if value == 1:
                    name = name.rstrip('s')
                result.append("{} {}".format(value, name))
        return ', '.join(result[:granularity])

    # ----------------------------------

    def new_frame(self, grid, top, height):
        frame = Gtk.Frame()
        # frame.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
        grid.attach(frame, 0, top, 3, height)
        return frame

    # ---------------------------------

    def gather_the_info(self, widget):
        global printdone
        global display_time
        starttime = time.strftime('%H:%M:%S', time.localtime())

        archive_name = ""
        archive_name = self.restore_archive_filename.get_filename()
        print(archive_name)
        if not(archive_name):
            # destination = self.destination_filesystem.get_destination()
            # if not(destination):
            path2 = self.fn_to_archive.get_filename()
            path = self.archive_file_path.get_filename()
            filename = self.create_archive_filename.get_text() + "-" + time.strftime("%Y%m%d-%Hh%M") + ".fsa"
            compression_level = self.compression_level.get_active() + 1
            thread_count = self.number_of_threads.get_active() + 1
            block_count = (self.number_of_blocks.get_active() + 1)*1000
            my_cmd = "sudo fsarchiver -A -Z " + str(compression_level) + \
                " -s " + str(block_count) + \
                " -j " + str(thread_count) + " savedir " + path + \
                "/" + filename + " " + path2
        #print ("my_cmd = ",my_cmd)
        else:
            destination = self.destination_filesystem.get_filename()
            my_cmd = "sudo fsarchiver restdir " + archive_name + \
                        " " + destination
        print ("my_cmd = ",my_cmd)
        self.my_cmd = my_cmd
        self.archiver_busy_flag = 1
        t = threading.Thread(target=self.do_my_cmd)
        t.start()
        start_time = time.strftime('%H:%M:%S')
        num_sec = 0
        while self.archiver_busy_flag:
            while Gtk.events_pending():
                Gtk.main_iteration_do(False)
            self.status_label.set_text("Start: " + start_time + " Run time: " + display_time(num_sec, 3) + " running")
            time.sleep(1)
            num_sec += 1

        if self.archiver_busy_flag == 0:
            self.status_label.set_text("Start: " + start_time + " Run time: " + display_time(num_sec, 3) + " Done !")
            time.sleep(1)
            while printdone == 0:
                print ("fsarchiver commando : " + self.status_label.get_text())
                printdone = 1
        #

    # ---------------------------------

    def do_my_cmd(self):
        global printdone
        os.system(self.my_cmd)
        self.archiver_busy_flag = 0
        printdone = 0

    # ---------------------------------

    def show_info_window(self, widget):
        target = self.restore_archive_filename.get_filename()
        print ("Gearchiveerde informatie van bestand = ", target)
        my_cmd = 'sudo fsarchiver archinfo ' + target + ' 2>&1'
        archinfo_msg = ''
        print ("archinfo cmd = ", my_cmd)
        archinfo = os.popen(my_cmd, 'r')
        line = archinfo.readline()
        print (line)
        knt = 1
        while line:
            archinfo_msg = archinfo_msg + line
            line = archinfo.readline()
            print (line)
            knt += 1
            if knt == 3:
                words = line.split(' ')
                try:
                    archive_filecount = int(words[2])
                    for i in range(archive_filecount):
                        self.archive_position.append_text(str(i + 100))
                except:
                    continue
        b = self.archive_info.get_buffer()
        b.set_text(archinfo_msg)
        self.info_window.show_all()

    # ---------------------------------


    # ---------------------------------



    # ===============================


if __name__ == "__main__":
    app = ArchiverGUI()
    Gtk.main()