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