Task improvements, run fsck in background
authorMike Looijmans <milo-software@users.sourceforge.net>
Sun, 3 Oct 2010 12:19:23 +0000 (14:19 +0200)
committerMike Looijmans <milo-software@users.sourceforge.net>
Sun, 3 Oct 2010 18:58:51 +0000 (20:58 +0200)
Expect formatting in background too soon.
Needs more testing and much better error handling.

lib/python/Components/Harddisk.py
lib/python/Components/Task.py
lib/python/Screens/CopyFiles.py
lib/python/Screens/HarddiskSetup.py

index 68c666c..bc1f5a4 100755 (executable)
@@ -4,6 +4,7 @@ from Tools.CList import CList
 from SystemInfo import SystemInfo
 import time
 from Components.Console import Console
+import Task
 
 def readFile(filename):
        file = open(filename)
@@ -155,11 +156,19 @@ class Harddisk:
                                        numPart += 1
                return numPart
 
+       def _unmount(self):
+               self.unmount()
+               
        def unmount(self):
                cmd = "umount"
                for parts in getProcMounts():
                        if path.realpath(parts[0]).startswith(self.dev_path):
                                cmd = ' ' . join([cmd, parts[1]])
+                               break
+               else:
+                       # not mounted, return OK
+                       return 0
+               print "[Harddisk]", cmd
                res = system(cmd)
                return (res >> 8)
 
@@ -171,10 +180,10 @@ class Harddisk:
        def mkfs(self):
                cmd = "mkfs.ext3 "
                size = self.diskSize()
-               if size > 2 * 1024:
-                       cmd += "-T largefile -N %d " % (size * 32)
-               elif size > 16 * 1024:
+               if size > 16 * 1024:
                        cmd += "-T largefile -O sparse_super "
+               elif size > 2 * 1024:
+                       cmd += "-T largefile -N %d " % (size * 32)
                cmd += "-m0 -O dir_index " + self.partitionPath("1")
                res = system(cmd)
                return (res >> 8)
@@ -223,16 +232,45 @@ class Harddisk:
 
        def killPartition(self, n):
                part = self.partitionPath(n)
+               try:
+                       h = open(part, 'wb')
+                       zero = 512 * '\0'
+                       for i in range(3):
+                               h.write(zero)
+                       h.close()
+               except Exception, e:
+                       print "[Harddisk] Failed to write to", part, "error:", e
 
-               if access(part, 0):
-                       cmd = 'dd bs=512 count=3 if=/dev/zero of=' + part
-                       res = system(cmd)
-               else:
-                       res = 0
+       errorList = [ _("Everything is fine"), _("Creating partition failed"), _("Mkfs failed"), _("Mount failed"), _("Create movie folder failed"), _("Fsck failed"), _("Please Reboot"), _("Filesystem contains uncorrectable errors"), _("Unmount failed")]
 
-               return (res >> 8)
+       def createInitializeJob(self):
+               job = Task.Job(_("Initializing storage device..."))
 
-       errorList = [ _("Everything is fine"), _("Creating partition failed"), _("Mkfs failed"), _("Mount failed"), _("Create movie folder failed"), _("Fsck failed"), _("Please Reboot"), _("Filesystem contains uncorrectable errors"), _("Unmount failed")]
+               task = Task.PythonTask(job, _("Unmount"))
+               task.work = self.unmount
+
+               task = Task.PythonTask(job, _("Kill partition"))
+               task.work = lambda: self.killPartition("1")
+
+               task = Task.Task(job, _("Create Partition"))
+               task.setTool('sfdisk')
+               task.args.append('-f')
+               task.args.append(self.disk_path)
+               task.initial_input = "0,\n;\n;\n;\ny\n"
+               
+               task = Task.Task(job, _("Create Filesystem"))
+               task.setTool("mkfs.ext3")
+               size = self.diskSize()
+               if size > 16 * 1024:
+                       task.args += ["-T", "largefile", "-O", "sparse_super"]
+               elif size > 2 * 1024:
+                       task.args += ["-T", "largefile", "-N", str(size * 32)]
+               task.args += ["-m0", "-O", "dir_index", self.partitionPath("1")]
+
+               task = Task.PythonTask(job, _("Mount"))
+               task.work = self.mount
+
+               return job
 
        def initialize(self):
                self.unmount()
@@ -276,6 +314,23 @@ class Harddisk:
                        return -3
 
                return 0
+               
+       def createCheckJob(self):
+               job = Task.Job(_("Check storage device..."))
+
+               task = Task.PythonTask(job, _("Unmount"))
+               task.work = self.unmount
+
+               task = Task.Task(job, "fsck")
+               task.setTool('fsck.ext3')
+               task.args.append('-f')
+               task.args.append('-p')
+               task.args.append(self.partitionPath("1"))
+
+               task = Task.PythonTask(job, _("Mount"))
+               task.work = self.mount
+
+               return job
 
        def getDeviceDir(self):
                return self.dev_path
index fef9e6f..da463b2 100644 (file)
@@ -111,6 +111,9 @@ class Job(object):
        def cancel(self):
                # some Jobs might have a better idea of how to cancel a job
                self.abort()
+               
+       def __str__(self):      
+               return "Components.Task.Job name=%s #tasks=%s" % (self.name, len(self.tasks))
 
 class Task(object):
        def __init__(self, job, name):
@@ -158,23 +161,14 @@ class Task(object):
                                not_met.append(precondition)
                return not_met
 
-       def run(self, callback):
-               failed_preconditions = self.checkPreconditions(True) + self.checkPreconditions(False)
-               if len(failed_preconditions):
-                       callback(self, failed_preconditions)
-                       return
-               self.prepare()
-
-               self.callback = callback
+       def _run(self):
                from enigma import eConsoleAppContainer
                self.container = eConsoleAppContainer()
                self.container.appClosed.append(self.processFinished)
                self.container.stdoutAvail.append(self.processStdout)
                self.container.stderrAvail.append(self.processStderr)
-
                if self.cwd is not None:
                        self.container.setCWD(self.cwd)
-
                if not self.cmd and self.cmdline:
                        print "execute:", self.container.execute(self.cmdline), self.cmdline
                else:
@@ -184,6 +178,15 @@ class Task(object):
                if self.initial_input:
                        self.writeInput(self.initial_input)
 
+       def run(self, callback):
+               failed_preconditions = self.checkPreconditions(True) + self.checkPreconditions(False)
+               if failed_preconditions:
+                       callback(self, failed_preconditions)
+                       return
+               self.prepare()
+               self.callback = callback
+               self._run()
+
        def prepare(self):
                pass
 
@@ -206,6 +209,7 @@ class Task(object):
                        self.output_line = self.output_line[i+1:]
 
        def processOutputLine(self, line):
+               print "[Task %s]" % self.name, line
                pass
 
        def processFinished(self, returncode):
@@ -249,6 +253,32 @@ class Task(object):
 
        progress = property(getProgress, setProgress)
 
+       def __str__(self):      
+               return "Components.Task.Task name=%s" % (self.name)
+
+class PythonTask(Task):
+       def _run(self):
+               from twisted.internet import threads, task
+               self.aborted = False
+               self.pos = 0
+               threads.deferToThread(self.work).addBoth(self.onComplete)
+               self.timer = task.LoopingCall(self.onTimer)
+               self.timer.start(5, False)
+       def work(self):
+               raise NotImplemented, "work"
+       def abort(self):
+               self.aborted = True
+               if self.callback is None:
+                       self.finish(aborted = True)
+       def onTimer(self):
+               self.setProgress(self.pos)
+       def onComplete(self, result):
+               self.postconditions.append(FailedPostcondition(result))
+               self.timer.stop()
+               del self.timer
+               self.finish()
+
+
 # The jobmanager will execute multiple jobs, each after another.
 # later, it will also support suspending jobs (and continuing them after reboot etc)
 # It also supports a notification when some error occured, and possibly a retry.
@@ -397,6 +427,14 @@ class ReturncodePostcondition(Condition):
        def check(self, task):
                return task.returncode == 0
 
+class FailedPostcondition(Condition):
+       def __init__(self, exception):
+               self.exception = exception
+       def getErrorMessage(self, task):
+               return str(self.exception)
+       def check(self, task):
+               return (self.exception is None) or (self.exception == 0) 
+
 #class HDDInitJob(Job):
 #      def __init__(self, device):
 #              Job.__init__(self, _("Initialize Harddisk"))
index e0fde56..3f33a6e 100644 (file)
@@ -10,10 +10,10 @@ class FailedPostcondition(Components.Task.Condition):
        def check(self, task):
                return self.exception is None
 
-
-class CopyFileTask(Components.Task.Task):
+class CopyFileTask(Components.Task.PythonTask):
        def openFiles(self, fileList):
                self.callback = None
+               self.fileList = fileList
                self.handles = [(open(fn[0], 'rb'), open(fn[1], 'wb')) for fn in fileList]
                self.end = 0
                for src,dst in fileList:
@@ -24,55 +24,34 @@ class CopyFileTask(Components.Task.Task):
                if not self.end:
                        self.end = 1
                print "[CopyFileTask] size:", self.end
-       def run(self, callback):
-               print "[CopyFileTask] run"
-               self.callback = callback
-               self.aborted = False
-               self.pos = 0
-               threads.deferToThread(self.copyHandles).addBoth(self.onComplete)
-               self.timer = task.LoopingCall(self.onTimer)
-               self.timer.start(5, False)
-       def copyHandles(self):
-               print "copyHandles: ", len(self.handles)
+       def work(self):
+               print "[CopyFileTask] handles ", len(self.handles)
                try:
                        for src, dst in self.handles:
                                while 1:
                                        if self.aborted:
+                                               print "[CopyFileTask] aborting"
                                                raise Exception, "Aborted"
                                        d = src.read(65536)
                                        if not d:
+                                               src.close()
+                                               dst.close()
                                                # EOF
                                                break
                                        dst.write(d)
                                        self.pos += len(d)
-               finally:
+               except:
                        # In any event, close all handles
                        for src, dst in self.handles:
                                src.close()
                                dst.close()
-       def abort(self):
-               print "[CopyFileTask] abort!"
-               self.aborted = True
-               if self.callback is None:
-                       self.finish(aborted = True)
-       def onTimer(self):
-               self.setProgress(self.pos)
-       def onComplete(self, result):
-               #callback from reactor, result=None or Failure.
-               print "[CopyFileTask] onComplete", result
-               self.postconditions.append(FailedPostcondition(result))
-               self.timer.stop()
-               del self.timer
-               if result is None:
-                       print "[CopyFileTask] done, okay"
-               else:
-                       for s,d in fileList:
+                       for s,d in self.fileList:
                                # Remove incomplete data.
                                try:
                                        os.unlink(d)
                                except:
                                        pass
-               self.finish()
+                       raise
 
 
 def copyFiles(fileList, name):
index 28e987f..6a8accd 100644 (file)
@@ -5,6 +5,7 @@ from Components.MenuList import MenuList
 from Components.Label import Label
 from Components.Pixmap import Pixmap
 from Screens.MessageBox import MessageBox
+import Components.Task
 from enigma import eTimer
 
 class HarddiskWait(Screen):
@@ -15,8 +16,8 @@ class HarddiskWait(Screen):
 
        def doCheck(self):
                self.timer.stop()
-               result = self.hdd.check()
-               self.close(result)
+               Components.Task.job_manager.AddJob(self.hdd.createCheckJob())
+               self.close(None)
 
        def __init__(self, session, hdd, type):
                Screen.__init__(self, session)
@@ -68,7 +69,10 @@ class HarddiskSetup(Screen):
 
        def hddReady(self, result):
                print "Result: " + str(result)
-               if (result != 0):
+               if result is None:
+                       # todo: Notify about background task?
+                       self.close()
+               elif (result != 0):
                        if self.type == self.HARDDISK_INITIALIZE:
                                message = _("Unable to initialize device.\nError: ")
                        else: