From 7323c18997d7dad3d7e6fea1a45f01e1528a93a7 Mon Sep 17 00:00:00 2001 From: Felix Domke Date: Mon, 14 Apr 2008 23:31:18 +0000 Subject: [PATCH] Add 'Tasks' for controlled execution of multiple external tools --- lib/python/Components/Makefile.am | 2 +- lib/python/Components/Task.py | 227 ++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 lib/python/Components/Task.py diff --git a/lib/python/Components/Makefile.am b/lib/python/Components/Makefile.am index 5236909c..f344046d 100644 --- a/lib/python/Components/Makefile.am +++ b/lib/python/Components/Makefile.am @@ -17,4 +17,4 @@ install_PYTHON = \ FIFOList.py ServiceEventTracker.py Input.py TimerSanityCheck.py FileList.py \ MultiContent.py MediaPlayer.py TunerInfo.py VideoWindow.py ChoiceList.py \ Element.py Playlist.py ParentalControl.py ParentalControlList.py \ - Ipkg.py SelectionList.py Scanner.py SystemInfo.py + Ipkg.py SelectionList.py Scanner.py SystemInfo.py Task.py diff --git a/lib/python/Components/Task.py b/lib/python/Components/Task.py new file mode 100644 index 00000000..1db19f76 --- /dev/null +++ b/lib/python/Components/Task.py @@ -0,0 +1,227 @@ +# A Job consists of many "Tasks". +# A task is the run of an external tool, with proper methods for failure handling + +from Tools.CList import CList + +class Job: + NOT_STARTED, IN_PROGRESS, FINISHED, FAILED = range(4) + def __init__(self, name): + self.tasks = [ ] + self.current_task = 0 + self.callback = None + self.name = name + self.finished = False + + self.state_changed = CList() + + self.status = self.NOT_STARTED + + # description is a dict + def fromDescription(self, description): + pass + + def createDescription(self): + return None + + def addTask(self, task): + self.tasks.append(task) + + def start(self, callback): + assert self.callback is None + self.callback = callback + self.status = self.IN_PROGRESS + self.state_changed() + self.runNext() + + def runNext(self): + if self.current_task == len(self.tasks): + self.callback(self, []) + self.status = self.FINISHED + self.state_changed() + else: + self.tasks[self.current_task].run(self.taskCallback) + self.state_changed() + + def taskCallback(self, res): + if len(res): + print ">>> Error:", res + self.status = self.FAILED + self.state_changed() + self.callback(self, res) + else: + self.current_task += 1 + self.runNext() + +class Task: + def __init__(self, name): + self.name = name + self.workspace = "/tmp" + self.immediate_preconditions = [ ] + self.global_preconditions = [ ] + self.postconditions = [ ] + self.returncode = None + self.initial_input = None + + self.cmd = None + self.args = [ ] + + def setCommandline(self, cmd, args): + self.cmd = cmd + self.args = args + + def setTool(self, tool): + self.cmd = tool + self.args = [tool] + self.global_preconditions.append(ToolExistsPrecondition()) + self.postconditions.append(ReturncodePostcondition()) + + def checkPreconditions(self, immediate = False): + not_met = [ ] + if immediate: + preconditions = self.immediate_preconditions + else: + preconditions = self.global_preconditions + for precondition in preconditions: + if not precondition.check(self): + not_met.append(precondition) + return not_met + + def run(self, callback): + failed_preconditions = self.checkPreconditions(True) + if len(failed_preconditions): + errback(failed_preconditions) + return + + self.callback = callback + from enigma import eConsoleAppContainer + self.container = eConsoleAppContainer() + self.container.appClosed.get().append(self.processFinished) + self.container.dataAvail.get().append(self.processOutput) + + assert self.cmd is not None + assert len(self.args) >= 1 + + print "execute:", self.container.execute(self.cmd, self.args), self.cmd, self.args + if self.initial_input: + self.writeInput(self.initial_input) + + def prepare(self): + pass + + def cleanup(self, failed): + pass + + def processOutput(self, data): + pass + + def processFinished(self, returncode): + self.returncode = returncode + self.finish() + + def finish(self): + self.afterRun() + not_met = [ ] + for postcondition in self.postconditions: + if not postcondition.check(self): + not_met.append(postcondition) + + self.callback(not_met) + + def afterRun(self): + pass + + def writeInput(self, input): + self.container.write(input) + +class JobManager: + def __init__(self): + self.active_jobs = [ ] + self.failed_jobs = [ ] + self.job_classes = [ ] + self.active_job = None + + def AddJob(self, job): + self.active_jobs.append(job) + self.kick() + + def kick(self): + if self.active_job is None: + if len(self.active_jobs): + self.active_job = self.active_jobs.pop(0) + self.active_job.start(self.jobDone) + + def jobDone(self, job, problems): + print "job", job, "completed with", problems + if problems: + self.failed_jobs.append(self.active_job) + + self.active_job = None + self.kick() + +# some examples: +#class PartitionExistsPostcondition: +# def __init__(self, device): +# self.device = device +# +# def check(self, task): +# import os +# return os.access(self.device + "part1", os.F_OK) +# +#class CreatePartitionTask(Task): +# def __init__(self, device): +# Task.__init__(self, _("Create Partition")) +# self.device = device +# self.setTool("/sbin/sfdisk") +# self.args += ["-f", self.device + "disc"] +# self.initial_input = "0,\n;\n;\n;\ny\n" +# self.postconditions.append(PartitionExistsPostcondition(self.device)) +# +#class CreateFilesystemTask(Task): +# def __init__(self, device, partition = 1, largefile = True): +# Task.__init__(self, _("Create Filesystem")) +# self.setTool("/sbin/mkfs.ext") +# if largefile: +# self.args += ["-T", "largefile"] +# self.args.append("-m0") +# self.args.append(device + "part%d" % partition) +# +#class FilesystemMountTask(Task): +# def __init__(self, device, partition = 1, filesystem = "ext3"): +# Task.__init__(self, _("Mounting Filesystem")) +# self.setTool("/bin/mount") +# if filesystem is not None: +# self.args += ["-t", filesystem] +# self.args.append(device + "part%d" % partition) +# +#class DiskspacePrecondition: +# def __init__(self, diskspace_required): +# self.diskspace_required = diskspace_required +# +# def check(self, task): +# return getFreeDiskspace(task.workspace) >= self.diskspace_required +# +#class ToolExistsPrecondition: +# def check(self, task): +# import os +# return os.access(task.cmd, os.X_OK) +# +#class ReturncodePostcondition: +# def check(self, task): +# return task.returncode == 0 +# +#class HDDInitJob(Job): +# def __init__(self, device): +# Job.__init__(self, _("Initialize Harddisk")) +# self.device = device +# self.fromDescription(self.createDescription()) +# +# def fromDescription(self, description): +# self.device = description["device"] +# self.addTask(CreatePartitionTask(self.device)) +# self.addTask(CreateFilesystemTask(self.device)) +# self.addTask(FilesystemMountTask(self.device)) +# +# def createDescription(self): +# return {"device": self.device} + +job_manager = JobManager() -- 2.30.2