make workspace a Job property, fix preconditions, Task will automatically add to...
[enigma2.git] / lib / python / Components / Task.py
1 # A Job consists of many "Tasks".
2 # A task is the run of an external tool, with proper methods for failure handling
3
4 from Tools.CList import CList
5
6 class Job:
7         NOT_STARTED, IN_PROGRESS, FINISHED, FAILED = range(4)
8         def __init__(self, name):
9                 self.tasks = [ ]
10                 self.workspace = "/tmp"
11                 self.current_task = 0
12                 self.callback = None
13                 self.name = name
14                 self.finished = False
15
16                 self.state_changed = CList()
17
18                 self.status = self.NOT_STARTED
19
20         # description is a dict
21         def fromDescription(self, description):
22                 pass
23
24         def createDescription(self):
25                 return None
26
27         def addTask(self, task):
28                 task.job = self
29                 self.tasks.append(task)
30
31         def start(self, callback):
32                 assert self.callback is None
33                 self.callback = callback
34                 self.status = self.IN_PROGRESS
35                 self.state_changed()
36                 self.runNext()
37
38         def runNext(self):
39                 if self.current_task == len(self.tasks):
40                         self.callback(self, [])
41                         self.status = self.FINISHED
42                         self.state_changed()
43                 else:
44                         self.tasks[self.current_task].run(self.taskCallback)
45                         self.state_changed()
46
47         def taskCallback(self, res):
48                 if len(res):
49                         print ">>> Error:", res
50                         self.status = self.FAILED
51                         self.state_changed()
52                         self.callback(self, res)
53                 else:
54                         self.current_task += 1
55                         self.runNext()
56
57 class Task:
58         def __init__(self, job, name):
59                 self.name = name
60                 self.immediate_preconditions = [ ]
61                 self.global_preconditions = [ ]
62                 self.postconditions = [ ]
63                 self.returncode = None
64                 self.initial_input = None
65                 self.job = None
66
67                 self.cmd = None
68                 self.args = [ ]
69                 job.addTask(self)
70
71         def setCommandline(self, cmd, args):
72                 self.cmd = cmd
73                 self.args = args
74
75         def setTool(self, tool):
76                 self.cmd = tool
77                 self.args = [tool]
78                 self.global_preconditions.append(ToolExistsPrecondition())
79                 self.postconditions.append(ReturncodePostcondition())
80
81         def checkPreconditions(self, immediate = False):
82                 not_met = [ ]
83                 if immediate:
84                         preconditions = self.immediate_preconditions
85                 else:
86                         preconditions = self.global_preconditions
87                 for precondition in preconditions:
88                         if not precondition.check(self):
89                                 not_met.append(precondition)
90                 return not_met
91
92         def run(self, callback):
93                 failed_preconditions = self.checkPreconditions(True) + self.checkPreconditions(False)
94                 if len(failed_preconditions):
95                         callback(failed_preconditions)
96                         return
97                 self.prepare()
98
99                 self.callback = callback
100                 from enigma import eConsoleAppContainer
101                 self.container = eConsoleAppContainer()
102                 self.container.appClosed.get().append(self.processFinished)
103                 self.container.dataAvail.get().append(self.processOutput)
104
105                 assert self.cmd is not None
106                 assert len(self.args) >= 1
107
108                 print "execute:", self.container.execute(self.cmd, self.args), self.cmd, self.args
109                 if self.initial_input:
110                         self.writeInput(self.initial_input)
111
112         def prepare(self):
113                 pass
114
115         def cleanup(self, failed):
116                 pass
117
118         def processOutput(self, data):
119                 pass
120
121         def processFinished(self, returncode):
122                 self.returncode = returncode
123                 self.finish()
124
125         def finish(self):
126                 self.afterRun()
127                 not_met = [ ]
128                 for postcondition in self.postconditions:
129                         if not postcondition.check(self):
130                                 not_met.append(postcondition)
131
132                 self.callback(not_met)
133
134         def afterRun(self):
135                 pass
136
137         def writeInput(self, input):
138                 self.container.write(input)
139
140 class JobManager:
141         def __init__(self):
142                 self.active_jobs = [ ]
143                 self.failed_jobs = [ ]
144                 self.job_classes = [ ]
145                 self.active_job = None
146
147         def AddJob(self, job):
148                 self.active_jobs.append(job)
149                 self.kick()
150
151         def kick(self):
152                 if self.active_job is None:
153                         if len(self.active_jobs):
154                                 self.active_job = self.active_jobs.pop(0)
155                                 self.active_job.start(self.jobDone)
156
157         def jobDone(self, job, problems):
158                 print "job", job, "completed with", problems
159                 if problems:
160                         self.failed_jobs.append(self.active_job)
161
162                 self.active_job = None
163                 self.kick()
164
165 # some examples:
166 #class PartitionExistsPostcondition:
167 #       def __init__(self, device):
168 #               self.device = device
169 #
170 #       def check(self, task):
171 #               import os
172 #               return os.access(self.device + "part1", os.F_OK)
173 #
174 #class CreatePartitionTask(Task):
175 #       def __init__(self, device):
176 #               Task.__init__(self, _("Create Partition"))
177 #               self.device = device
178 #               self.setTool("/sbin/sfdisk")
179 #               self.args += ["-f", self.device + "disc"]
180 #               self.initial_input = "0,\n;\n;\n;\ny\n"
181 #               self.postconditions.append(PartitionExistsPostcondition(self.device))
182 #
183 #class CreateFilesystemTask(Task):
184 #       def __init__(self, device, partition = 1, largefile = True):
185 #               Task.__init__(self, _("Create Filesystem"))
186 #               self.setTool("/sbin/mkfs.ext")
187 #               if largefile:
188 #                       self.args += ["-T", "largefile"]
189 #               self.args.append("-m0")
190 #               self.args.append(device + "part%d" % partition)
191 #
192 #class FilesystemMountTask(Task):
193 #       def __init__(self, device, partition = 1, filesystem = "ext3"):
194 #               Task.__init__(self, _("Mounting Filesystem"))
195 #               self.setTool("/bin/mount")
196 #               if filesystem is not None:
197 #                       self.args += ["-t", filesystem]
198 #               self.args.append(device + "part%d" % partition)
199 #
200 #class DiskspacePrecondition:
201 #       def __init__(self, diskspace_required):
202 #               self.diskspace_required = diskspace_required
203 #
204 #       def check(self, task):
205 #               return getFreeDiskspace(task.workspace) >= self.diskspace_required
206 #
207 class ToolExistsPrecondition:
208         def check(self, task):
209                 import os
210                 return os.access(task.cmd, os.X_OK)
211
212 class ReturncodePostcondition:
213         def check(self, task):
214                 return task.returncode == 0
215
216 #class HDDInitJob(Job):
217 #       def __init__(self, device):
218 #               Job.__init__(self, _("Initialize Harddisk"))
219 #               self.device = device
220 #               self.fromDescription(self.createDescription())
221 #
222 #       def fromDescription(self, description):
223 #               self.device = description["device"]
224 #               self.addTask(CreatePartitionTask(self.device))
225 #               self.addTask(CreateFilesystemTask(self.device))
226 #               self.addTask(FilesystemMountTask(self.device))
227 #
228 #       def createDescription(self):
229 #               return {"device": self.device}
230
231 job_manager = JobManager()