diff options
author | Adrian Dudau <adrian.dudau@enea.com> | 2014-06-26 14:36:22 +0200 |
---|---|---|
committer | Adrian Dudau <adrian.dudau@enea.com> | 2014-06-26 15:32:53 +0200 |
commit | f4cf9fe05bb3f32fabea4e54dd92d368967a80da (patch) | |
tree | 487180fa9866985ea7b28e625651765d86f515c3 /scripts/lib | |
download | poky-f4cf9fe05bb3f32fabea4e54dd92d368967a80da.tar.gz |
initial commit for Enea Linux 4.0
Migrated from the internal git server on the daisy-enea branch
Signed-off-by: Adrian Dudau <adrian.dudau@enea.com>
Diffstat (limited to 'scripts/lib')
291 files changed, 39563 insertions, 0 deletions
diff --git a/scripts/lib/bsp/__init__.py b/scripts/lib/bsp/__init__.py new file mode 100644 index 0000000000..8bbb6e1530 --- /dev/null +++ b/scripts/lib/bsp/__init__.py | |||
@@ -0,0 +1,22 @@ | |||
1 | # | ||
2 | # Yocto BSP tools library | ||
3 | # | ||
4 | # Copyright (c) 2012, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # AUTHORS | ||
21 | # Tom Zanussi <tom.zanussi (at] intel.com> | ||
22 | # | ||
diff --git a/scripts/lib/bsp/engine.py b/scripts/lib/bsp/engine.py new file mode 100644 index 0000000000..681720d20a --- /dev/null +++ b/scripts/lib/bsp/engine.py | |||
@@ -0,0 +1,1780 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2012, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This module implements the templating engine used by 'yocto-bsp' to | ||
22 | # create BSPs. The BSP templates are simply the set of files expected | ||
23 | # to appear in a generated BSP, marked up with a small set of tags | ||
24 | # used to customize the output. The engine parses through the | ||
25 | # templates and generates a Python program containing all the logic | ||
26 | # and input elements needed to display and retrieve BSP-specific | ||
27 | # information from the user. The resulting program uses those results | ||
28 | # to generate the final BSP files. | ||
29 | # | ||
30 | # AUTHORS | ||
31 | # Tom Zanussi <tom.zanussi (at] intel.com> | ||
32 | # | ||
33 | |||
34 | import os | ||
35 | import sys | ||
36 | from abc import ABCMeta, abstractmethod | ||
37 | from tags import * | ||
38 | import shlex | ||
39 | import json | ||
40 | import subprocess | ||
41 | import shutil | ||
42 | |||
43 | class Line(): | ||
44 | """ | ||
45 | Generic (abstract) container representing a line that will appear | ||
46 | in the BSP-generating program. | ||
47 | """ | ||
48 | __metaclass__ = ABCMeta | ||
49 | |||
50 | def __init__(self, line): | ||
51 | self.line = line | ||
52 | self.generated_line = "" | ||
53 | self.prio = sys.maxint | ||
54 | self.discard = False | ||
55 | |||
56 | @abstractmethod | ||
57 | def gen(self, context = None): | ||
58 | """ | ||
59 | Generate the final executable line that will appear in the | ||
60 | BSP-generation program. | ||
61 | """ | ||
62 | pass | ||
63 | |||
64 | def escape(self, line): | ||
65 | """ | ||
66 | Escape single and double quotes and backslashes until I find | ||
67 | something better (re.escape() escapes way too much). | ||
68 | """ | ||
69 | return line.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "\\'") | ||
70 | |||
71 | def parse_error(self, msg, lineno, line): | ||
72 | raise SyntaxError("%s: %s" % (msg, line)) | ||
73 | |||
74 | |||
75 | class NormalLine(Line): | ||
76 | """ | ||
77 | Container for normal (non-tag) lines. | ||
78 | """ | ||
79 | def __init__(self, line): | ||
80 | Line.__init__(self, line) | ||
81 | self.is_filename = False | ||
82 | self.is_dirname = False | ||
83 | self.out_filebase = None | ||
84 | |||
85 | def gen(self, context = None): | ||
86 | if self.is_filename: | ||
87 | line = "current_file = \"" + os.path.join(self.out_filebase, self.escape(self.line)) + "\"; of = open(current_file, \"w\")" | ||
88 | elif self.is_dirname: | ||
89 | dirname = os.path.join(self.out_filebase, self.escape(self.line)) | ||
90 | line = "if not os.path.exists(\"" + dirname + "\"): os.mkdir(\"" + dirname + "\")" | ||
91 | else: | ||
92 | line = "of.write(\"" + self.escape(self.line) + "\\n\")" | ||
93 | return line | ||
94 | |||
95 | |||
96 | class CodeLine(Line): | ||
97 | """ | ||
98 | Container for Python code tag lines. | ||
99 | """ | ||
100 | def __init__(self, line): | ||
101 | Line.__init__(self, line) | ||
102 | |||
103 | def gen(self, context = None): | ||
104 | return self.line | ||
105 | |||
106 | |||
107 | class Assignment: | ||
108 | """ | ||
109 | Representation of everything we know about {{=name }} tags. | ||
110 | Instances of these are used by Assignment lines. | ||
111 | """ | ||
112 | def __init__(self, start, end, name): | ||
113 | self.start = start | ||
114 | self.end = end | ||
115 | self.name = name | ||
116 | |||
117 | |||
118 | class AssignmentLine(NormalLine): | ||
119 | """ | ||
120 | Container for normal lines containing assignment tags. Assignment | ||
121 | tags must be in ascending order of 'start' value. | ||
122 | """ | ||
123 | def __init__(self, line): | ||
124 | NormalLine.__init__(self, line) | ||
125 | self.assignments = [] | ||
126 | |||
127 | def add_assignment(self, start, end, name): | ||
128 | self.assignments.append(Assignment(start, end, name)) | ||
129 | |||
130 | def gen(self, context = None): | ||
131 | line = self.escape(self.line) | ||
132 | |||
133 | for assignment in self.assignments: | ||
134 | replacement = "\" + " + assignment.name + " + \"" | ||
135 | idx = line.find(ASSIGN_TAG) | ||
136 | line = line[:idx] + replacement + line[idx + assignment.end - assignment.start:] | ||
137 | if self.is_filename: | ||
138 | return "current_file = \"" + os.path.join(self.out_filebase, line) + "\"; of = open(current_file, \"w\")" | ||
139 | elif self.is_dirname: | ||
140 | dirname = os.path.join(self.out_filebase, line) | ||
141 | return "if not os.path.exists(\"" + dirname + "\"): os.mkdir(\"" + dirname + "\")" | ||
142 | else: | ||
143 | return "of.write(\"" + line + "\\n\")" | ||
144 | |||
145 | |||
146 | class InputLine(Line): | ||
147 | """ | ||
148 | Base class for Input lines. | ||
149 | """ | ||
150 | def __init__(self, props, tag, lineno): | ||
151 | Line.__init__(self, tag) | ||
152 | self.props = props | ||
153 | self.lineno = lineno | ||
154 | |||
155 | try: | ||
156 | self.prio = int(props["prio"]) | ||
157 | except KeyError: | ||
158 | self.prio = sys.maxint | ||
159 | |||
160 | def gen(self, context = None): | ||
161 | try: | ||
162 | depends_on = self.props["depends-on"] | ||
163 | try: | ||
164 | depends_on_val = self.props["depends-on-val"] | ||
165 | except KeyError: | ||
166 | self.parse_error("No 'depends-on-val' for 'depends-on' property", | ||
167 | self.lineno, self.line) | ||
168 | except KeyError: | ||
169 | pass | ||
170 | |||
171 | |||
172 | class EditBoxInputLine(InputLine): | ||
173 | """ | ||
174 | Base class for 'editbox' Input lines. | ||
175 | |||
176 | props: | ||
177 | name: example - "Load address" | ||
178 | msg: example - "Please enter the load address" | ||
179 | result: | ||
180 | Sets the value of the variable specified by 'name' to | ||
181 | whatever the user typed. | ||
182 | """ | ||
183 | def __init__(self, props, tag, lineno): | ||
184 | InputLine.__init__(self, props, tag, lineno) | ||
185 | |||
186 | def gen(self, context = None): | ||
187 | InputLine.gen(self, context) | ||
188 | name = self.props["name"] | ||
189 | if not name: | ||
190 | self.parse_error("No input 'name' property found", | ||
191 | self.lineno, self.line) | ||
192 | msg = self.props["msg"] | ||
193 | if not msg: | ||
194 | self.parse_error("No input 'msg' property found", | ||
195 | self.lineno, self.line) | ||
196 | |||
197 | try: | ||
198 | default_choice = self.props["default"] | ||
199 | except KeyError: | ||
200 | default_choice = "" | ||
201 | |||
202 | msg += " [default: " + default_choice + "]" | ||
203 | |||
204 | line = name + " = default(raw_input(\"" + msg + " \"), " + name + ")" | ||
205 | |||
206 | return line | ||
207 | |||
208 | |||
209 | class GitRepoEditBoxInputLine(EditBoxInputLine): | ||
210 | """ | ||
211 | Base class for 'editbox' Input lines for user input of remote git | ||
212 | repos. This class verifies the existence and connectivity of the | ||
213 | specified git repo. | ||
214 | |||
215 | props: | ||
216 | name: example - "Load address" | ||
217 | msg: example - "Please enter the load address" | ||
218 | result: | ||
219 | Sets the value of the variable specified by 'name' to | ||
220 | whatever the user typed. | ||
221 | """ | ||
222 | def __init__(self, props, tag, lineno): | ||
223 | EditBoxInputLine.__init__(self, props, tag, lineno) | ||
224 | |||
225 | def gen(self, context = None): | ||
226 | EditBoxInputLine.gen(self, context) | ||
227 | name = self.props["name"] | ||
228 | if not name: | ||
229 | self.parse_error("No input 'name' property found", | ||
230 | self.lineno, self.line) | ||
231 | msg = self.props["msg"] | ||
232 | if not msg: | ||
233 | self.parse_error("No input 'msg' property found", | ||
234 | self.lineno, self.line) | ||
235 | |||
236 | try: | ||
237 | default_choice = self.props["default"] | ||
238 | except KeyError: | ||
239 | default_choice = "" | ||
240 | |||
241 | msg += " [default: " + default_choice + "]" | ||
242 | |||
243 | line = name + " = get_verified_git_repo(\"" + msg + "\"," + name + ")" | ||
244 | |||
245 | return line | ||
246 | |||
247 | |||
248 | class FileEditBoxInputLine(EditBoxInputLine): | ||
249 | """ | ||
250 | Base class for 'editbox' Input lines for user input of existing | ||
251 | files. This class verifies the existence of the specified file. | ||
252 | |||
253 | props: | ||
254 | name: example - "Load address" | ||
255 | msg: example - "Please enter the load address" | ||
256 | result: | ||
257 | Sets the value of the variable specified by 'name' to | ||
258 | whatever the user typed. | ||
259 | """ | ||
260 | def __init__(self, props, tag, lineno): | ||
261 | EditBoxInputLine.__init__(self, props, tag, lineno) | ||
262 | |||
263 | def gen(self, context = None): | ||
264 | EditBoxInputLine.gen(self, context) | ||
265 | name = self.props["name"] | ||
266 | if not name: | ||
267 | self.parse_error("No input 'name' property found", | ||
268 | self.lineno, self.line) | ||
269 | msg = self.props["msg"] | ||
270 | if not msg: | ||
271 | self.parse_error("No input 'msg' property found", | ||
272 | self.lineno, self.line) | ||
273 | |||
274 | try: | ||
275 | default_choice = self.props["default"] | ||
276 | except KeyError: | ||
277 | default_choice = "" | ||
278 | |||
279 | msg += " [default: " + default_choice + "]" | ||
280 | |||
281 | line = name + " = get_verified_file(\"" + msg + "\"," + name + ", True)" | ||
282 | |||
283 | return line | ||
284 | |||
285 | |||
286 | class BooleanInputLine(InputLine): | ||
287 | """ | ||
288 | Base class for boolean Input lines. | ||
289 | props: | ||
290 | name: example - "keyboard" | ||
291 | msg: example - "Got keyboard?" | ||
292 | result: | ||
293 | Sets the value of the variable specified by 'name' to "yes" or "no" | ||
294 | example - keyboard = "yes" | ||
295 | """ | ||
296 | def __init__(self, props, tag, lineno): | ||
297 | InputLine.__init__(self, props, tag, lineno) | ||
298 | |||
299 | def gen(self, context = None): | ||
300 | InputLine.gen(self, context) | ||
301 | name = self.props["name"] | ||
302 | if not name: | ||
303 | self.parse_error("No input 'name' property found", | ||
304 | self.lineno, self.line) | ||
305 | msg = self.props["msg"] | ||
306 | if not msg: | ||
307 | self.parse_error("No input 'msg' property found", | ||
308 | self.lineno, self.line) | ||
309 | |||
310 | try: | ||
311 | default_choice = self.props["default"] | ||
312 | except KeyError: | ||
313 | default_choice = "" | ||
314 | |||
315 | msg += " [default: " + default_choice + "]" | ||
316 | |||
317 | line = name + " = boolean(raw_input(\"" + msg + " \"), " + name + ")" | ||
318 | |||
319 | return line | ||
320 | |||
321 | |||
322 | class ListInputLine(InputLine): | ||
323 | """ | ||
324 | Base class for List-based Input lines. e.g. Choicelist, Checklist. | ||
325 | """ | ||
326 | __metaclass__ = ABCMeta | ||
327 | |||
328 | def __init__(self, props, tag, lineno): | ||
329 | InputLine.__init__(self, props, tag, lineno) | ||
330 | self.choices = [] | ||
331 | |||
332 | def gen_choicepair_list(self): | ||
333 | """Generate a list of 2-item val:desc lists from self.choices.""" | ||
334 | if not self.choices: | ||
335 | return None | ||
336 | |||
337 | choicepair_list = list() | ||
338 | |||
339 | for choice in self.choices: | ||
340 | choicepair = [] | ||
341 | choicepair.append(choice.val) | ||
342 | choicepair.append(choice.desc) | ||
343 | choicepair_list.append(choicepair) | ||
344 | |||
345 | return choicepair_list | ||
346 | |||
347 | def gen_degenerate_choicepair_list(self, choices): | ||
348 | """Generate a list of 2-item val:desc with val=desc from passed-in choices.""" | ||
349 | choicepair_list = list() | ||
350 | |||
351 | for choice in choices: | ||
352 | choicepair = [] | ||
353 | choicepair.append(choice) | ||
354 | choicepair.append(choice) | ||
355 | choicepair_list.append(choicepair) | ||
356 | |||
357 | return choicepair_list | ||
358 | |||
359 | def exec_listgen_fn(self, context = None): | ||
360 | """ | ||
361 | Execute the list-generating function contained as a string in | ||
362 | the "gen" property. | ||
363 | """ | ||
364 | retval = None | ||
365 | try: | ||
366 | fname = self.props["gen"] | ||
367 | modsplit = fname.split('.') | ||
368 | mod_fn = modsplit.pop() | ||
369 | mod = '.'.join(modsplit) | ||
370 | |||
371 | __import__(mod) | ||
372 | # python 2.7 has a better way to do this using importlib.import_module | ||
373 | m = sys.modules[mod] | ||
374 | |||
375 | fn = getattr(m, mod_fn) | ||
376 | if not fn: | ||
377 | self.parse_error("couldn't load function specified for 'gen' property ", | ||
378 | self.lineno, self.line) | ||
379 | retval = fn(context) | ||
380 | if not retval: | ||
381 | self.parse_error("function specified for 'gen' property returned nothing ", | ||
382 | self.lineno, self.line) | ||
383 | except KeyError: | ||
384 | pass | ||
385 | |||
386 | return retval | ||
387 | |||
388 | def gen_choices_str(self, choicepairs): | ||
389 | """ | ||
390 | Generate a numbered list of choices from a list of choicepairs | ||
391 | for display to the user. | ||
392 | """ | ||
393 | choices_str = "" | ||
394 | |||
395 | for i, choicepair in enumerate(choicepairs): | ||
396 | choices_str += "\t" + str(i + 1) + ") " + choicepair[1] + "\n" | ||
397 | |||
398 | return choices_str | ||
399 | |||
400 | def gen_choices_val_str(self, choicepairs): | ||
401 | """ | ||
402 | Generate an array of choice values corresponding to the | ||
403 | numbered list generated by gen_choices_str(). | ||
404 | """ | ||
405 | choices_val_list = "[" | ||
406 | |||
407 | for i, choicepair in enumerate(choicepairs): | ||
408 | choices_val_list += "\"" + choicepair[0] + "\"," | ||
409 | choices_val_list += "]" | ||
410 | |||
411 | return choices_val_list | ||
412 | |||
413 | def gen_choices_val_list(self, choicepairs): | ||
414 | """ | ||
415 | Generate an array of choice values corresponding to the | ||
416 | numbered list generated by gen_choices_str(). | ||
417 | """ | ||
418 | choices_val_list = [] | ||
419 | |||
420 | for i, choicepair in enumerate(choicepairs): | ||
421 | choices_val_list.append(choicepair[0]) | ||
422 | |||
423 | return choices_val_list | ||
424 | |||
425 | def gen_choices_list(self, context = None, checklist = False): | ||
426 | """ | ||
427 | Generate an array of choice values corresponding to the | ||
428 | numbered list generated by gen_choices_str(). | ||
429 | """ | ||
430 | choices = self.exec_listgen_fn(context) | ||
431 | if choices: | ||
432 | if len(choices) == 0: | ||
433 | self.parse_error("No entries available for input list", | ||
434 | self.lineno, self.line) | ||
435 | choicepairs = self.gen_degenerate_choicepair_list(choices) | ||
436 | else: | ||
437 | if len(self.choices) == 0: | ||
438 | self.parse_error("No entries available for input list", | ||
439 | self.lineno, self.line) | ||
440 | choicepairs = self.gen_choicepair_list() | ||
441 | |||
442 | return choicepairs | ||
443 | |||
444 | def gen_choices(self, context = None, checklist = False): | ||
445 | """ | ||
446 | Generate an array of choice values corresponding to the | ||
447 | numbered list generated by gen_choices_str(), display it to | ||
448 | the user, and process the result. | ||
449 | """ | ||
450 | msg = self.props["msg"] | ||
451 | if not msg: | ||
452 | self.parse_error("No input 'msg' property found", | ||
453 | self.lineno, self.line) | ||
454 | |||
455 | try: | ||
456 | default_choice = self.props["default"] | ||
457 | except KeyError: | ||
458 | default_choice = "" | ||
459 | |||
460 | msg += " [default: " + default_choice + "]" | ||
461 | |||
462 | choicepairs = self.gen_choices_list(context, checklist) | ||
463 | |||
464 | choices_str = self.gen_choices_str(choicepairs) | ||
465 | choices_val_list = self.gen_choices_val_list(choicepairs) | ||
466 | if checklist: | ||
467 | choiceval = default(find_choicevals(raw_input(msg + "\n" + choices_str), choices_val_list), default_choice) | ||
468 | else: | ||
469 | choiceval = default(find_choiceval(raw_input(msg + "\n" + choices_str), choices_val_list), default_choice) | ||
470 | |||
471 | return choiceval | ||
472 | |||
473 | |||
474 | def find_choiceval(choice_str, choice_list): | ||
475 | """ | ||
476 | Take number as string and return val string from choice_list, | ||
477 | empty string if oob. choice_list is a simple python list. | ||
478 | """ | ||
479 | choice_val = "" | ||
480 | |||
481 | try: | ||
482 | choice_idx = int(choice_str) | ||
483 | if choice_idx <= len(choice_list): | ||
484 | choice_idx -= 1 | ||
485 | choice_val = choice_list[choice_idx] | ||
486 | except ValueError: | ||
487 | pass | ||
488 | |||
489 | return choice_val | ||
490 | |||
491 | |||
492 | def find_choicevals(choice_str, choice_list): | ||
493 | """ | ||
494 | Take numbers as space-separated string and return vals list from | ||
495 | choice_list, empty list if oob. choice_list is a simple python | ||
496 | list. | ||
497 | """ | ||
498 | choice_vals = [] | ||
499 | |||
500 | choices = choice_str.split() | ||
501 | for choice in choices: | ||
502 | choice_vals.append(find_choiceval(choice, choice_list)) | ||
503 | |||
504 | return choice_vals | ||
505 | |||
506 | |||
507 | def default(input_str, name): | ||
508 | """ | ||
509 | Return default if no input_str, otherwise stripped input_str. | ||
510 | """ | ||
511 | if not input_str: | ||
512 | return name | ||
513 | |||
514 | return input_str.strip() | ||
515 | |||
516 | |||
517 | def verify_git_repo(giturl): | ||
518 | """ | ||
519 | Verify that the giturl passed in can be connected to. This can be | ||
520 | used as a check for the existence of the given repo and/or basic | ||
521 | git remote connectivity. | ||
522 | |||
523 | Returns True if the connection was successful, fals otherwise | ||
524 | """ | ||
525 | if not giturl: | ||
526 | return False | ||
527 | |||
528 | gitcmd = "git ls-remote %s > /dev/null 2>&1" % (giturl) | ||
529 | rc = subprocess.call(gitcmd, shell=True) | ||
530 | if rc == 0: | ||
531 | return True | ||
532 | |||
533 | return False | ||
534 | |||
535 | |||
536 | def get_verified_git_repo(input_str, name): | ||
537 | """ | ||
538 | Return git repo if verified, otherwise loop forever asking user | ||
539 | for filename. | ||
540 | """ | ||
541 | msg = input_str.strip() + " " | ||
542 | |||
543 | giturl = default(raw_input(msg), name) | ||
544 | |||
545 | while True: | ||
546 | if verify_git_repo(giturl): | ||
547 | return giturl | ||
548 | giturl = default(raw_input(msg), name) | ||
549 | |||
550 | |||
551 | def get_verified_file(input_str, name, filename_can_be_null): | ||
552 | """ | ||
553 | Return filename if the file exists, otherwise loop forever asking | ||
554 | user for filename. | ||
555 | """ | ||
556 | msg = input_str.strip() + " " | ||
557 | |||
558 | filename = default(raw_input(msg), name) | ||
559 | |||
560 | while True: | ||
561 | if not filename and filename_can_be_null: | ||
562 | return filename | ||
563 | if os.path.isfile(filename): | ||
564 | return filename | ||
565 | filename = default(raw_input(msg), name) | ||
566 | |||
567 | |||
568 | def replace_file(replace_this, with_this): | ||
569 | """ | ||
570 | Replace the given file with the contents of filename, retaining | ||
571 | the original filename. | ||
572 | """ | ||
573 | try: | ||
574 | replace_this.close() | ||
575 | shutil.copy(with_this, replace_this.name) | ||
576 | except IOError: | ||
577 | pass | ||
578 | |||
579 | |||
580 | def boolean(input_str, name): | ||
581 | """ | ||
582 | Return lowercase version of first char in string, or value in name. | ||
583 | """ | ||
584 | if not input_str: | ||
585 | return name | ||
586 | |||
587 | str = input_str.lower().strip() | ||
588 | if str and str[0] == "y" or str[0] == "n": | ||
589 | return str[0] | ||
590 | else: | ||
591 | return name | ||
592 | |||
593 | |||
594 | def strip_base(input_str): | ||
595 | """ | ||
596 | strip '/base' off the end of input_str, so we can use 'base' in | ||
597 | the branch names we present to the user. | ||
598 | """ | ||
599 | if input_str and input_str.endswith("/base"): | ||
600 | return input_str[:-len("/base")] | ||
601 | return input_str.strip() | ||
602 | |||
603 | |||
604 | deferred_choices = {} | ||
605 | |||
606 | def gen_choices_defer(input_line, context, checklist = False): | ||
607 | """ | ||
608 | Save the context hashed the name of the input item, which will be | ||
609 | passed to the gen function later. | ||
610 | """ | ||
611 | name = input_line.props["name"] | ||
612 | |||
613 | try: | ||
614 | nameappend = input_line.props["nameappend"] | ||
615 | except KeyError: | ||
616 | nameappend = "" | ||
617 | |||
618 | try: | ||
619 | branches_base = input_line.props["branches_base"] | ||
620 | except KeyError: | ||
621 | branches_base = "" | ||
622 | |||
623 | filename = input_line.props["filename"] | ||
624 | |||
625 | closetag_start = filename.find(CLOSE_TAG) | ||
626 | |||
627 | if closetag_start != -1: | ||
628 | filename = filename[closetag_start + len(CLOSE_TAG):] | ||
629 | |||
630 | filename = filename.strip() | ||
631 | filename = os.path.splitext(filename)[0] | ||
632 | |||
633 | captured_context = capture_context(context) | ||
634 | context["filename"] = filename | ||
635 | captured_context["filename"] = filename | ||
636 | context["nameappend"] = nameappend | ||
637 | captured_context["nameappend"] = nameappend | ||
638 | context["branches_base"] = branches_base | ||
639 | captured_context["branches_base"] = branches_base | ||
640 | |||
641 | deferred_choice = (input_line, captured_context, checklist) | ||
642 | key = name + "_" + filename + "_" + nameappend | ||
643 | deferred_choices[key] = deferred_choice | ||
644 | |||
645 | |||
646 | def invoke_deferred_choices(name): | ||
647 | """ | ||
648 | Invoke the choice generation function using the context hashed by | ||
649 | 'name'. | ||
650 | """ | ||
651 | deferred_choice = deferred_choices[name] | ||
652 | input_line = deferred_choice[0] | ||
653 | context = deferred_choice[1] | ||
654 | checklist = deferred_choice[2] | ||
655 | |||
656 | context["name"] = name | ||
657 | |||
658 | choices = input_line.gen_choices(context, checklist) | ||
659 | |||
660 | return choices | ||
661 | |||
662 | |||
663 | class ChoicelistInputLine(ListInputLine): | ||
664 | """ | ||
665 | Base class for choicelist Input lines. | ||
666 | props: | ||
667 | name: example - "xserver_choice" | ||
668 | msg: example - "Please select an xserver for this machine" | ||
669 | result: | ||
670 | Sets the value of the variable specified by 'name' to whichever Choice was chosen | ||
671 | example - xserver_choice = "xserver_vesa" | ||
672 | """ | ||
673 | def __init__(self, props, tag, lineno): | ||
674 | ListInputLine.__init__(self, props, tag, lineno) | ||
675 | |||
676 | def gen(self, context = None): | ||
677 | InputLine.gen(self, context) | ||
678 | |||
679 | gen_choices_defer(self, context) | ||
680 | name = self.props["name"] | ||
681 | nameappend = context["nameappend"] | ||
682 | filename = context["filename"] | ||
683 | |||
684 | try: | ||
685 | default_choice = self.props["default"] | ||
686 | except KeyError: | ||
687 | default_choice = "" | ||
688 | |||
689 | line = name + " = default(invoke_deferred_choices(\"" + name + "_" + filename + "_" + nameappend + "\"), \"" + default_choice + "\")" | ||
690 | |||
691 | return line | ||
692 | |||
693 | |||
694 | class ListValInputLine(InputLine): | ||
695 | """ | ||
696 | Abstract base class for choice and checkbox Input lines. | ||
697 | """ | ||
698 | def __init__(self, props, tag, lineno): | ||
699 | InputLine.__init__(self, props, tag, lineno) | ||
700 | |||
701 | try: | ||
702 | self.val = self.props["val"] | ||
703 | except KeyError: | ||
704 | self.parse_error("No input 'val' property found", self.lineno, self.line) | ||
705 | |||
706 | try: | ||
707 | self.desc = self.props["msg"] | ||
708 | except KeyError: | ||
709 | self.parse_error("No input 'msg' property found", self.lineno, self.line) | ||
710 | |||
711 | |||
712 | class ChoiceInputLine(ListValInputLine): | ||
713 | """ | ||
714 | Base class for choicelist item Input lines. | ||
715 | """ | ||
716 | def __init__(self, props, tag, lineno): | ||
717 | ListValInputLine.__init__(self, props, tag, lineno) | ||
718 | |||
719 | def gen(self, context = None): | ||
720 | return None | ||
721 | |||
722 | |||
723 | class ChecklistInputLine(ListInputLine): | ||
724 | """ | ||
725 | Base class for checklist Input lines. | ||
726 | """ | ||
727 | def __init__(self, props, tag, lineno): | ||
728 | ListInputLine.__init__(self, props, tag, lineno) | ||
729 | |||
730 | def gen(self, context = None): | ||
731 | InputLine.gen(self, context) | ||
732 | |||
733 | gen_choices_defer(self, context, True) | ||
734 | name = self.props["name"] | ||
735 | nameappend = context["nameappend"] | ||
736 | filename = context["filename"] | ||
737 | |||
738 | try: | ||
739 | default_choice = self.props["default"] | ||
740 | except KeyError: | ||
741 | default_choice = "" | ||
742 | |||
743 | line = name + " = default(invoke_deferred_choices(\"" + name + "_" + filename + "_" + nameappend + "\"), \"" + default_choice + "\")" | ||
744 | |||
745 | return line | ||
746 | |||
747 | |||
748 | class CheckInputLine(ListValInputLine): | ||
749 | """ | ||
750 | Base class for checklist item Input lines. | ||
751 | """ | ||
752 | def __init__(self, props, tag, lineno): | ||
753 | ListValInputLine.__init__(self, props, tag, lineno) | ||
754 | |||
755 | def gen(self, context = None): | ||
756 | return None | ||
757 | |||
758 | |||
759 | class SubstrateBase(object): | ||
760 | """ | ||
761 | Base class for both expanded and unexpanded file and dir container | ||
762 | objects. | ||
763 | """ | ||
764 | def __init__(self, filename, filebase, out_filebase): | ||
765 | self.filename = filename | ||
766 | self.filebase = filebase | ||
767 | self.out_filebase = out_filebase | ||
768 | self.raw_lines = [] | ||
769 | self.expanded_lines = [] | ||
770 | self.prev_choicelist = None | ||
771 | |||
772 | def parse_error(self, msg, lineno, line): | ||
773 | raise SyntaxError("%s: [%s: %d]: %s" % (msg, self.filename, lineno, line)) | ||
774 | |||
775 | def expand_input_tag(self, tag, lineno): | ||
776 | """ | ||
777 | Input tags consist of the word 'input' at the beginning, | ||
778 | followed by name:value property pairs which are converted into | ||
779 | a dictionary. | ||
780 | """ | ||
781 | propstr = tag[len(INPUT_TAG):] | ||
782 | |||
783 | props = dict(prop.split(":", 1) for prop in shlex.split(propstr)) | ||
784 | props["filename"] = self.filename | ||
785 | |||
786 | input_type = props[INPUT_TYPE_PROPERTY] | ||
787 | if not props[INPUT_TYPE_PROPERTY]: | ||
788 | self.parse_error("No input 'type' property found", lineno, tag) | ||
789 | |||
790 | if input_type == "boolean": | ||
791 | return BooleanInputLine(props, tag, lineno) | ||
792 | if input_type == "edit": | ||
793 | return EditBoxInputLine(props, tag, lineno) | ||
794 | if input_type == "edit-git-repo": | ||
795 | return GitRepoEditBoxInputLine(props, tag, lineno) | ||
796 | if input_type == "edit-file": | ||
797 | return FileEditBoxInputLine(props, tag, lineno) | ||
798 | elif input_type == "choicelist": | ||
799 | self.prev_choicelist = ChoicelistInputLine(props, tag, lineno) | ||
800 | return self.prev_choicelist | ||
801 | elif input_type == "choice": | ||
802 | if not self.prev_choicelist: | ||
803 | self.parse_error("Found 'choice' input tag but no previous choicelist", | ||
804 | lineno, tag) | ||
805 | choice = ChoiceInputLine(props, tag, lineno) | ||
806 | self.prev_choicelist.choices.append(choice) | ||
807 | return choice | ||
808 | elif input_type == "checklist": | ||
809 | return ChecklistInputLine(props, tag, lineno) | ||
810 | elif input_type == "check": | ||
811 | return CheckInputLine(props, tag, lineno) | ||
812 | |||
813 | def expand_assignment_tag(self, start, line, lineno): | ||
814 | """ | ||
815 | Expand all tags in a line. | ||
816 | """ | ||
817 | expanded_line = AssignmentLine(line.rstrip()) | ||
818 | |||
819 | while start != -1: | ||
820 | end = line.find(CLOSE_TAG, start) | ||
821 | if end == -1: | ||
822 | self.parse_error("No close tag found for assignment tag", lineno, line) | ||
823 | else: | ||
824 | name = line[start + len(ASSIGN_TAG):end].strip() | ||
825 | expanded_line.add_assignment(start, end + len(CLOSE_TAG), name) | ||
826 | start = line.find(ASSIGN_TAG, end) | ||
827 | |||
828 | return expanded_line | ||
829 | |||
830 | def expand_tag(self, line, lineno): | ||
831 | """ | ||
832 | Returns a processed tag line, or None if there was no tag | ||
833 | |||
834 | The rules for tags are very simple: | ||
835 | - No nested tags | ||
836 | - Tags start with {{ and end with }} | ||
837 | - An assign tag, {{=, can appear anywhere and will | ||
838 | be replaced with what the assignment evaluates to | ||
839 | - Any other tag occupies the whole line it is on | ||
840 | - if there's anything else on the tag line, it's an error | ||
841 | - if it starts with 'input', it's an input tag and | ||
842 | will only be used for prompting and setting variables | ||
843 | - anything else is straight Python | ||
844 | - tags are in effect only until the next blank line or tag or 'pass' tag | ||
845 | - we don't have indentation in tags, but we need some way to end a block | ||
846 | forcefully without blank lines or other tags - that's the 'pass' tag | ||
847 | - todo: implement pass tag | ||
848 | - directories and filenames can have tags as well, but only assignment | ||
849 | and 'if' code lines | ||
850 | - directories and filenames are the only case where normal tags can | ||
851 | coexist with normal text on the same 'line' | ||
852 | """ | ||
853 | start = line.find(ASSIGN_TAG) | ||
854 | if start != -1: | ||
855 | return self.expand_assignment_tag(start, line, lineno) | ||
856 | |||
857 | start = line.find(OPEN_TAG) | ||
858 | if start == -1: | ||
859 | return None | ||
860 | |||
861 | end = line.find(CLOSE_TAG, 0) | ||
862 | if end == -1: | ||
863 | self.parse_error("No close tag found for open tag", lineno, line) | ||
864 | |||
865 | tag = line[start + len(OPEN_TAG):end].strip() | ||
866 | |||
867 | if not tag.lstrip().startswith(INPUT_TAG): | ||
868 | return CodeLine(tag) | ||
869 | |||
870 | return self.expand_input_tag(tag, lineno) | ||
871 | |||
872 | def expand_file_or_dir_name(self): | ||
873 | """ | ||
874 | Expand file or dir names into codeline. Dirnames and | ||
875 | filenames can only have assignments or if statements. First | ||
876 | translate if statements into CodeLine + (dirname or filename | ||
877 | creation). | ||
878 | """ | ||
879 | lineno = 0 | ||
880 | |||
881 | line = self.filename[len(self.filebase):] | ||
882 | if line.startswith("/"): | ||
883 | line = line[1:] | ||
884 | opentag_start = -1 | ||
885 | |||
886 | start = line.find(OPEN_TAG) | ||
887 | while start != -1: | ||
888 | if not line[start:].startswith(ASSIGN_TAG): | ||
889 | opentag_start = start | ||
890 | break | ||
891 | start += len(ASSIGN_TAG) | ||
892 | start = line.find(OPEN_TAG, start) | ||
893 | |||
894 | if opentag_start != -1: | ||
895 | end = line.find(CLOSE_TAG, opentag_start) | ||
896 | if end == -1: | ||
897 | self.parse_error("No close tag found for open tag", lineno, line) | ||
898 | # we have a {{ tag i.e. code | ||
899 | tag = line[opentag_start + len(OPEN_TAG):end].strip() | ||
900 | |||
901 | if not tag.lstrip().startswith(IF_TAG): | ||
902 | self.parse_error("Only 'if' tags are allowed in file or directory names", | ||
903 | lineno, line) | ||
904 | self.expanded_lines.append(CodeLine(tag)) | ||
905 | |||
906 | # everything after }} is the actual filename (possibly with assignments) | ||
907 | # everything before is the pathname | ||
908 | line = line[:opentag_start] + line[end + len(CLOSE_TAG):].strip() | ||
909 | |||
910 | assign_start = line.find(ASSIGN_TAG) | ||
911 | if assign_start != -1: | ||
912 | assignment_tag = self.expand_assignment_tag(assign_start, line, lineno) | ||
913 | if isinstance(self, SubstrateFile): | ||
914 | assignment_tag.is_filename = True | ||
915 | assignment_tag.out_filebase = self.out_filebase | ||
916 | elif isinstance(self, SubstrateDir): | ||
917 | assignment_tag.is_dirname = True | ||
918 | assignment_tag.out_filebase = self.out_filebase | ||
919 | self.expanded_lines.append(assignment_tag) | ||
920 | return | ||
921 | |||
922 | normal_line = NormalLine(line) | ||
923 | if isinstance(self, SubstrateFile): | ||
924 | normal_line.is_filename = True | ||
925 | normal_line.out_filebase = self.out_filebase | ||
926 | elif isinstance(self, SubstrateDir): | ||
927 | normal_line.is_dirname = True | ||
928 | normal_line.out_filebase = self.out_filebase | ||
929 | self.expanded_lines.append(normal_line) | ||
930 | |||
931 | def expand(self): | ||
932 | """ | ||
933 | Expand the file or dir name first, eventually this ends up | ||
934 | creating the file or dir. | ||
935 | """ | ||
936 | self.expand_file_or_dir_name() | ||
937 | |||
938 | |||
939 | class SubstrateFile(SubstrateBase): | ||
940 | """ | ||
941 | Container for both expanded and unexpanded substrate files. | ||
942 | """ | ||
943 | def __init__(self, filename, filebase, out_filebase): | ||
944 | SubstrateBase.__init__(self, filename, filebase, out_filebase) | ||
945 | |||
946 | def read(self): | ||
947 | if self.raw_lines: | ||
948 | return | ||
949 | f = open(self.filename) | ||
950 | self.raw_lines = f.readlines() | ||
951 | |||
952 | def expand(self): | ||
953 | """Expand the contents of all template tags in the file.""" | ||
954 | SubstrateBase.expand(self) | ||
955 | self.read() | ||
956 | |||
957 | for lineno, line in enumerate(self.raw_lines): | ||
958 | expanded_line = self.expand_tag(line, lineno + 1) # humans not 0-based | ||
959 | if not expanded_line: | ||
960 | expanded_line = NormalLine(line.rstrip()) | ||
961 | self.expanded_lines.append(expanded_line) | ||
962 | |||
963 | def gen(self, context = None): | ||
964 | """Generate the code that generates the BSP.""" | ||
965 | base_indent = 0 | ||
966 | |||
967 | indent = new_indent = base_indent | ||
968 | |||
969 | for line in self.expanded_lines: | ||
970 | genline = line.gen(context) | ||
971 | if not genline: | ||
972 | continue | ||
973 | if isinstance(line, InputLine): | ||
974 | line.generated_line = genline | ||
975 | continue | ||
976 | if genline.startswith(OPEN_START): | ||
977 | if indent == 1: | ||
978 | base_indent = 1 | ||
979 | if indent: | ||
980 | if genline == BLANKLINE_STR or (not genline.startswith(NORMAL_START) | ||
981 | and not genline.startswith(OPEN_START)): | ||
982 | indent = new_indent = base_indent | ||
983 | if genline.endswith(":"): | ||
984 | new_indent = base_indent + 1 | ||
985 | line.generated_line = (indent * INDENT_STR) + genline | ||
986 | indent = new_indent | ||
987 | |||
988 | |||
989 | class SubstrateDir(SubstrateBase): | ||
990 | """ | ||
991 | Container for both expanded and unexpanded substrate dirs. | ||
992 | """ | ||
993 | def __init__(self, filename, filebase, out_filebase): | ||
994 | SubstrateBase.__init__(self, filename, filebase, out_filebase) | ||
995 | |||
996 | def expand(self): | ||
997 | SubstrateBase.expand(self) | ||
998 | |||
999 | def gen(self, context = None): | ||
1000 | """Generate the code that generates the BSP.""" | ||
1001 | indent = new_indent = 0 | ||
1002 | for line in self.expanded_lines: | ||
1003 | genline = line.gen(context) | ||
1004 | if not genline: | ||
1005 | continue | ||
1006 | if genline.endswith(":"): | ||
1007 | new_indent = 1 | ||
1008 | else: | ||
1009 | new_indent = 0 | ||
1010 | line.generated_line = (indent * INDENT_STR) + genline | ||
1011 | indent = new_indent | ||
1012 | |||
1013 | |||
1014 | def expand_target(target, all_files, out_filebase): | ||
1015 | """ | ||
1016 | Expand the contents of all template tags in the target. This | ||
1017 | means removing tags and categorizing or creating lines so that | ||
1018 | future passes can process and present input lines and generate the | ||
1019 | corresponding lines of the Python program that will be exec'ed to | ||
1020 | actually produce the final BSP. 'all_files' includes directories. | ||
1021 | """ | ||
1022 | for root, dirs, files in os.walk(target): | ||
1023 | for file in files: | ||
1024 | if file.endswith("~") or file.endswith("#"): | ||
1025 | continue | ||
1026 | f = os.path.join(root, file) | ||
1027 | sfile = SubstrateFile(f, target, out_filebase) | ||
1028 | sfile.expand() | ||
1029 | all_files.append(sfile) | ||
1030 | |||
1031 | for dir in dirs: | ||
1032 | d = os.path.join(root, dir) | ||
1033 | sdir = SubstrateDir(d, target, out_filebase) | ||
1034 | sdir.expand() | ||
1035 | all_files.append(sdir) | ||
1036 | |||
1037 | |||
1038 | def gen_program_machine_lines(machine, program_lines): | ||
1039 | """ | ||
1040 | Use the input values we got from the command line. | ||
1041 | """ | ||
1042 | line = "machine = \"" + machine + "\"" | ||
1043 | program_lines.append(line) | ||
1044 | |||
1045 | line = "layer_name = \"" + machine + "\"" | ||
1046 | program_lines.append(line) | ||
1047 | |||
1048 | |||
1049 | def sort_inputlines(input_lines): | ||
1050 | """Sort input lines according to priority (position).""" | ||
1051 | input_lines.sort(key = lambda l: l.prio) | ||
1052 | |||
1053 | |||
1054 | def find_parent_dependency(lines, depends_on): | ||
1055 | for i, line in lines: | ||
1056 | if isinstance(line, CodeLine): | ||
1057 | continue | ||
1058 | if line.props["name"] == depends_on: | ||
1059 | return i | ||
1060 | |||
1061 | return -1 | ||
1062 | |||
1063 | |||
1064 | def process_inputline_dependencies(input_lines, all_inputlines): | ||
1065 | """If any input lines depend on others, put the others first.""" | ||
1066 | for line in input_lines: | ||
1067 | if isinstance(line, InputLineGroup): | ||
1068 | group_inputlines = [] | ||
1069 | process_inputline_dependencies(line.group, group_inputlines) | ||
1070 | line.group = group_inputlines | ||
1071 | all_inputlines.append(line) | ||
1072 | continue | ||
1073 | |||
1074 | if isinstance(line, CodeLine) or isinstance(line, NormalLine): | ||
1075 | all_inputlines.append(line) | ||
1076 | continue | ||
1077 | |||
1078 | try: | ||
1079 | depends_on = line.props["depends-on"] | ||
1080 | depends_codeline = "if " + line.props["depends-on"] + " == \"" + line.props["depends-on-val"] + "\":" | ||
1081 | all_inputlines.append(CodeLine(depends_codeline)) | ||
1082 | all_inputlines.append(line) | ||
1083 | except KeyError: | ||
1084 | all_inputlines.append(line) | ||
1085 | |||
1086 | |||
1087 | def conditional_filename(filename): | ||
1088 | """ | ||
1089 | Check if the filename itself contains a conditional statement. If | ||
1090 | so, return a codeline for it. | ||
1091 | """ | ||
1092 | opentag_start = filename.find(OPEN_TAG) | ||
1093 | |||
1094 | if opentag_start != -1: | ||
1095 | if filename[opentag_start:].startswith(ASSIGN_TAG): | ||
1096 | return None | ||
1097 | end = filename.find(CLOSE_TAG, opentag_start) | ||
1098 | if end == -1: | ||
1099 | print "No close tag found for open tag in filename %s" % filename | ||
1100 | sys.exit(1) | ||
1101 | |||
1102 | # we have a {{ tag i.e. code | ||
1103 | tag = filename[opentag_start + len(OPEN_TAG):end].strip() | ||
1104 | if not tag.lstrip().startswith(IF_TAG): | ||
1105 | print "Only 'if' tags are allowed in file or directory names, filename: %s" % filename | ||
1106 | sys.exit(1) | ||
1107 | |||
1108 | return CodeLine(tag) | ||
1109 | |||
1110 | return None | ||
1111 | |||
1112 | |||
1113 | class InputLineGroup(InputLine): | ||
1114 | """ | ||
1115 | InputLine that does nothing but group other input lines | ||
1116 | corresponding to all the input lines in a SubstrateFile so they | ||
1117 | can be generated as a group. prio is the only property used. | ||
1118 | """ | ||
1119 | def __init__(self, codeline): | ||
1120 | InputLine.__init__(self, {}, "", 0) | ||
1121 | self.group = [] | ||
1122 | self.prio = sys.maxint | ||
1123 | self.group.append(codeline) | ||
1124 | |||
1125 | def append(self, line): | ||
1126 | self.group.append(line) | ||
1127 | if line.prio < self.prio: | ||
1128 | self.prio = line.prio | ||
1129 | |||
1130 | def len(self): | ||
1131 | return len(self.group) | ||
1132 | |||
1133 | |||
1134 | def gather_inputlines(files): | ||
1135 | """ | ||
1136 | Gather all the InputLines - we want to generate them first. | ||
1137 | """ | ||
1138 | all_inputlines = [] | ||
1139 | input_lines = [] | ||
1140 | |||
1141 | for file in files: | ||
1142 | if isinstance(file, SubstrateFile): | ||
1143 | group = None | ||
1144 | basename = os.path.basename(file.filename) | ||
1145 | |||
1146 | codeline = conditional_filename(basename) | ||
1147 | if codeline: | ||
1148 | group = InputLineGroup(codeline) | ||
1149 | |||
1150 | have_condition = False | ||
1151 | condition_to_write = None | ||
1152 | for line in file.expanded_lines: | ||
1153 | if isinstance(line, CodeLine): | ||
1154 | have_condition = True | ||
1155 | condition_to_write = line | ||
1156 | continue | ||
1157 | if isinstance(line, InputLine): | ||
1158 | if group: | ||
1159 | if condition_to_write: | ||
1160 | condition_to_write.prio = line.prio | ||
1161 | condition_to_write.discard = True | ||
1162 | group.append(condition_to_write) | ||
1163 | condition_to_write = None | ||
1164 | group.append(line) | ||
1165 | else: | ||
1166 | if condition_to_write: | ||
1167 | condition_to_write.prio = line.prio | ||
1168 | condition_to_write.discard = True | ||
1169 | input_lines.append(condition_to_write) | ||
1170 | condition_to_write = None | ||
1171 | input_lines.append(line) | ||
1172 | else: | ||
1173 | if condition_to_write: | ||
1174 | condition_to_write = None | ||
1175 | if have_condition: | ||
1176 | if not line.line.strip(): | ||
1177 | line.discard = True | ||
1178 | input_lines.append(line) | ||
1179 | have_condition = False | ||
1180 | |||
1181 | if group and group.len() > 1: | ||
1182 | input_lines.append(group) | ||
1183 | |||
1184 | sort_inputlines(input_lines) | ||
1185 | process_inputline_dependencies(input_lines, all_inputlines) | ||
1186 | |||
1187 | return all_inputlines | ||
1188 | |||
1189 | |||
1190 | def run_program_lines(linelist, codedump): | ||
1191 | """ | ||
1192 | For a single file, print all the python code into a buf and execute it. | ||
1193 | """ | ||
1194 | buf = "\n".join(linelist) | ||
1195 | |||
1196 | if codedump: | ||
1197 | of = open("bspgen.out", "w") | ||
1198 | of.write(buf) | ||
1199 | of.close() | ||
1200 | exec buf | ||
1201 | |||
1202 | |||
1203 | def gen_target(files, context = None): | ||
1204 | """ | ||
1205 | Generate the python code for each file. | ||
1206 | """ | ||
1207 | for file in files: | ||
1208 | file.gen(context) | ||
1209 | |||
1210 | |||
1211 | def gen_program_header_lines(program_lines): | ||
1212 | """ | ||
1213 | Generate any imports we need. | ||
1214 | """ | ||
1215 | program_lines.append("current_file = \"\"") | ||
1216 | |||
1217 | |||
1218 | def gen_supplied_property_vals(properties, program_lines): | ||
1219 | """ | ||
1220 | Generate user-specified entries for input values instead of | ||
1221 | generating input prompts. | ||
1222 | """ | ||
1223 | for name, val in properties.iteritems(): | ||
1224 | program_line = name + " = \"" + val + "\"" | ||
1225 | program_lines.append(program_line) | ||
1226 | |||
1227 | |||
1228 | def gen_initial_property_vals(input_lines, program_lines): | ||
1229 | """ | ||
1230 | Generate null or default entries for input values, so we don't | ||
1231 | have undefined variables. | ||
1232 | """ | ||
1233 | for line in input_lines: | ||
1234 | if isinstance(line, InputLineGroup): | ||
1235 | gen_initial_property_vals(line.group, program_lines) | ||
1236 | continue | ||
1237 | |||
1238 | if isinstance(line, InputLine): | ||
1239 | try: | ||
1240 | name = line.props["name"] | ||
1241 | try: | ||
1242 | default_val = "\"" + line.props["default"] + "\"" | ||
1243 | except: | ||
1244 | default_val = "\"\"" | ||
1245 | program_line = name + " = " + default_val | ||
1246 | program_lines.append(program_line) | ||
1247 | except KeyError: | ||
1248 | pass | ||
1249 | |||
1250 | |||
1251 | def gen_program_input_lines(input_lines, program_lines, context, in_group = False): | ||
1252 | """ | ||
1253 | Generate only the input lines used for prompting the user. For | ||
1254 | that, we only have input lines and CodeLines that affect the next | ||
1255 | input line. | ||
1256 | """ | ||
1257 | indent = new_indent = 0 | ||
1258 | |||
1259 | for line in input_lines: | ||
1260 | if isinstance(line, InputLineGroup): | ||
1261 | gen_program_input_lines(line.group, program_lines, context, True) | ||
1262 | continue | ||
1263 | if not line.line.strip(): | ||
1264 | continue | ||
1265 | |||
1266 | genline = line.gen(context) | ||
1267 | if not genline: | ||
1268 | continue | ||
1269 | if genline.endswith(":"): | ||
1270 | new_indent += 1 | ||
1271 | else: | ||
1272 | if indent > 1 or (not in_group and indent): | ||
1273 | new_indent -= 1 | ||
1274 | |||
1275 | line.generated_line = (indent * INDENT_STR) + genline | ||
1276 | program_lines.append(line.generated_line) | ||
1277 | |||
1278 | indent = new_indent | ||
1279 | |||
1280 | |||
1281 | def gen_program_lines(target_files, program_lines): | ||
1282 | """ | ||
1283 | Generate the program lines that make up the BSP generation | ||
1284 | program. This appends the generated lines of all target_files to | ||
1285 | program_lines, and skips input lines, which are dealt with | ||
1286 | separately, or omitted. | ||
1287 | """ | ||
1288 | for file in target_files: | ||
1289 | if file.filename.endswith("noinstall"): | ||
1290 | continue | ||
1291 | |||
1292 | for line in file.expanded_lines: | ||
1293 | if isinstance(line, InputLine): | ||
1294 | continue | ||
1295 | if line.discard: | ||
1296 | continue | ||
1297 | |||
1298 | program_lines.append(line.generated_line) | ||
1299 | |||
1300 | |||
1301 | def create_context(machine, arch, scripts_path): | ||
1302 | """ | ||
1303 | Create a context object for use in deferred function invocation. | ||
1304 | """ | ||
1305 | context = {} | ||
1306 | |||
1307 | context["machine"] = machine | ||
1308 | context["arch"] = arch | ||
1309 | context["scripts_path"] = scripts_path | ||
1310 | |||
1311 | return context | ||
1312 | |||
1313 | |||
1314 | def capture_context(context): | ||
1315 | """ | ||
1316 | Create a context object for use in deferred function invocation. | ||
1317 | """ | ||
1318 | captured_context = {} | ||
1319 | |||
1320 | captured_context["machine"] = context["machine"] | ||
1321 | captured_context["arch"] = context["arch"] | ||
1322 | captured_context["scripts_path"] = context["scripts_path"] | ||
1323 | |||
1324 | return captured_context | ||
1325 | |||
1326 | |||
1327 | def expand_targets(context, bsp_output_dir, expand_common=True): | ||
1328 | """ | ||
1329 | Expand all the tags in both the common and machine-specific | ||
1330 | 'targets'. | ||
1331 | |||
1332 | If expand_common is False, don't expand the common target (this | ||
1333 | option is used to create special-purpose layers). | ||
1334 | """ | ||
1335 | target_files = [] | ||
1336 | |||
1337 | machine = context["machine"] | ||
1338 | arch = context["arch"] | ||
1339 | scripts_path = context["scripts_path"] | ||
1340 | |||
1341 | lib_path = scripts_path + '/lib' | ||
1342 | bsp_path = lib_path + '/bsp' | ||
1343 | arch_path = bsp_path + '/substrate/target/arch' | ||
1344 | |||
1345 | if expand_common: | ||
1346 | common = os.path.join(arch_path, "common") | ||
1347 | expand_target(common, target_files, bsp_output_dir) | ||
1348 | |||
1349 | arches = os.listdir(arch_path) | ||
1350 | if arch not in arches or arch == "common": | ||
1351 | print "Invalid karch, exiting\n" | ||
1352 | sys.exit(1) | ||
1353 | |||
1354 | target = os.path.join(arch_path, arch) | ||
1355 | expand_target(target, target_files, bsp_output_dir) | ||
1356 | |||
1357 | gen_target(target_files, context) | ||
1358 | |||
1359 | return target_files | ||
1360 | |||
1361 | |||
1362 | def yocto_common_create(machine, target, scripts_path, layer_output_dir, codedump, properties_file, properties_str="", expand_common=True): | ||
1363 | """ | ||
1364 | Common layer-creation code | ||
1365 | |||
1366 | machine - user-defined machine name (if needed, will generate 'machine' var) | ||
1367 | target - the 'target' the layer will be based on, must be one in | ||
1368 | scripts/lib/bsp/substrate/target/arch | ||
1369 | scripts_path - absolute path to yocto /scripts dir | ||
1370 | layer_output_dir - dirname to create for layer | ||
1371 | codedump - dump generated code to bspgen.out | ||
1372 | properties_file - use values from this file if nonempty i.e no prompting | ||
1373 | properties_str - use values from this string if nonempty i.e no prompting | ||
1374 | expand_common - boolean, use the contents of (for bsp layers) arch/common | ||
1375 | """ | ||
1376 | if os.path.exists(layer_output_dir): | ||
1377 | print "\nlayer output dir already exists, exiting. (%s)" % layer_output_dir | ||
1378 | sys.exit(1) | ||
1379 | |||
1380 | properties = None | ||
1381 | |||
1382 | if properties_file: | ||
1383 | try: | ||
1384 | infile = open(properties_file, "r") | ||
1385 | except IOError: | ||
1386 | print "Couldn't open properties file %s for reading, exiting" % properties_file | ||
1387 | sys.exit(1) | ||
1388 | |||
1389 | properties = json.load(infile) | ||
1390 | |||
1391 | if properties_str and not properties: | ||
1392 | properties = json.loads(properties_str) | ||
1393 | |||
1394 | os.mkdir(layer_output_dir) | ||
1395 | |||
1396 | context = create_context(machine, target, scripts_path) | ||
1397 | target_files = expand_targets(context, layer_output_dir, expand_common) | ||
1398 | |||
1399 | input_lines = gather_inputlines(target_files) | ||
1400 | |||
1401 | program_lines = [] | ||
1402 | |||
1403 | gen_program_header_lines(program_lines) | ||
1404 | |||
1405 | gen_initial_property_vals(input_lines, program_lines) | ||
1406 | |||
1407 | if properties: | ||
1408 | gen_supplied_property_vals(properties, program_lines) | ||
1409 | |||
1410 | gen_program_machine_lines(machine, program_lines) | ||
1411 | |||
1412 | if not properties: | ||
1413 | gen_program_input_lines(input_lines, program_lines, context) | ||
1414 | |||
1415 | gen_program_lines(target_files, program_lines) | ||
1416 | |||
1417 | run_program_lines(program_lines, codedump) | ||
1418 | |||
1419 | |||
1420 | def yocto_layer_create(layer_name, scripts_path, layer_output_dir, codedump, properties_file, properties=""): | ||
1421 | """ | ||
1422 | Create yocto layer | ||
1423 | |||
1424 | layer_name - user-defined layer name | ||
1425 | scripts_path - absolute path to yocto /scripts dir | ||
1426 | layer_output_dir - dirname to create for layer | ||
1427 | codedump - dump generated code to bspgen.out | ||
1428 | properties_file - use values from this file if nonempty i.e no prompting | ||
1429 | properties - use values from this string if nonempty i.e no prompting | ||
1430 | """ | ||
1431 | yocto_common_create(layer_name, "layer", scripts_path, layer_output_dir, codedump, properties_file, properties, False) | ||
1432 | |||
1433 | print "\nNew layer created in %s.\n" % (layer_output_dir) | ||
1434 | print "Don't forget to add it to your BBLAYERS (for details see %s\README)." % (layer_output_dir) | ||
1435 | |||
1436 | |||
1437 | def yocto_bsp_create(machine, arch, scripts_path, bsp_output_dir, codedump, properties_file, properties=None): | ||
1438 | """ | ||
1439 | Create bsp | ||
1440 | |||
1441 | machine - user-defined machine name | ||
1442 | arch - the arch the bsp will be based on, must be one in | ||
1443 | scripts/lib/bsp/substrate/target/arch | ||
1444 | scripts_path - absolute path to yocto /scripts dir | ||
1445 | bsp_output_dir - dirname to create for BSP | ||
1446 | codedump - dump generated code to bspgen.out | ||
1447 | properties_file - use values from this file if nonempty i.e no prompting | ||
1448 | properties - use values from this string if nonempty i.e no prompting | ||
1449 | """ | ||
1450 | yocto_common_create(machine, arch, scripts_path, bsp_output_dir, codedump, properties_file, properties) | ||
1451 | |||
1452 | print "\nNew %s BSP created in %s" % (arch, bsp_output_dir) | ||
1453 | |||
1454 | |||
1455 | def print_dict(items, indent = 0): | ||
1456 | """ | ||
1457 | Print the values in a possibly nested dictionary. | ||
1458 | """ | ||
1459 | for key, val in items.iteritems(): | ||
1460 | print " "*indent + "\"%s\" :" % key, | ||
1461 | if type(val) == dict: | ||
1462 | print "{" | ||
1463 | print_dict(val, indent + 1) | ||
1464 | print " "*indent + "}" | ||
1465 | else: | ||
1466 | print "%s" % val | ||
1467 | |||
1468 | |||
1469 | def get_properties(input_lines): | ||
1470 | """ | ||
1471 | Get the complete set of properties for all the input items in the | ||
1472 | BSP, as a possibly nested dictionary. | ||
1473 | """ | ||
1474 | properties = {} | ||
1475 | |||
1476 | for line in input_lines: | ||
1477 | if isinstance(line, InputLineGroup): | ||
1478 | statement = line.group[0].line | ||
1479 | group_properties = get_properties(line.group) | ||
1480 | properties[statement] = group_properties | ||
1481 | continue | ||
1482 | |||
1483 | if not isinstance(line, InputLine): | ||
1484 | continue | ||
1485 | |||
1486 | if isinstance(line, ChoiceInputLine): | ||
1487 | continue | ||
1488 | |||
1489 | props = line.props | ||
1490 | item = {} | ||
1491 | name = props["name"] | ||
1492 | for key, val in props.items(): | ||
1493 | if not key == "name": | ||
1494 | item[key] = val | ||
1495 | properties[name] = item | ||
1496 | |||
1497 | return properties | ||
1498 | |||
1499 | |||
1500 | def yocto_layer_list_properties(arch, scripts_path, properties_file, expand_common=True): | ||
1501 | """ | ||
1502 | List the complete set of properties for all the input items in the | ||
1503 | layer. If properties_file is non-null, write the complete set of | ||
1504 | properties as a nested JSON object corresponding to a possibly | ||
1505 | nested dictionary. | ||
1506 | """ | ||
1507 | context = create_context("unused", arch, scripts_path) | ||
1508 | target_files = expand_targets(context, "unused", expand_common) | ||
1509 | |||
1510 | input_lines = gather_inputlines(target_files) | ||
1511 | |||
1512 | properties = get_properties(input_lines) | ||
1513 | if properties_file: | ||
1514 | try: | ||
1515 | of = open(properties_file, "w") | ||
1516 | except IOError: | ||
1517 | print "Couldn't open properties file %s for writing, exiting" % properties_file | ||
1518 | sys.exit(1) | ||
1519 | |||
1520 | json.dump(properties, of) | ||
1521 | |||
1522 | print_dict(properties) | ||
1523 | |||
1524 | |||
1525 | def split_nested_property(property): | ||
1526 | """ | ||
1527 | A property name of the form x.y describes a nested property | ||
1528 | i.e. the property y is contained within x and can be addressed | ||
1529 | using standard JSON syntax for nested properties. Note that if a | ||
1530 | property name itself contains '.', it should be contained in | ||
1531 | double quotes. | ||
1532 | """ | ||
1533 | splittable_property = "" | ||
1534 | in_quotes = False | ||
1535 | for c in property: | ||
1536 | if c == '.' and not in_quotes: | ||
1537 | splittable_property += '\n' | ||
1538 | continue | ||
1539 | if c == '"': | ||
1540 | in_quotes = not in_quotes | ||
1541 | splittable_property += c | ||
1542 | |||
1543 | split_properties = splittable_property.split('\n') | ||
1544 | |||
1545 | if len(split_properties) > 1: | ||
1546 | return split_properties | ||
1547 | |||
1548 | return None | ||
1549 | |||
1550 | |||
1551 | def find_input_line_group(substring, input_lines): | ||
1552 | """ | ||
1553 | Find and return the InputLineGroup containing the specified substring. | ||
1554 | """ | ||
1555 | for line in input_lines: | ||
1556 | if isinstance(line, InputLineGroup): | ||
1557 | if substring in line.group[0].line: | ||
1558 | return line | ||
1559 | |||
1560 | return None | ||
1561 | |||
1562 | |||
1563 | def find_input_line(name, input_lines): | ||
1564 | """ | ||
1565 | Find the input line with the specified name. | ||
1566 | """ | ||
1567 | for line in input_lines: | ||
1568 | if isinstance(line, InputLineGroup): | ||
1569 | l = find_input_line(name, line.group) | ||
1570 | if l: | ||
1571 | return l | ||
1572 | |||
1573 | if isinstance(line, InputLine): | ||
1574 | try: | ||
1575 | if line.props["name"] == name: | ||
1576 | return line | ||
1577 | if line.props["name"] + "_" + line.props["nameappend"] == name: | ||
1578 | return line | ||
1579 | except KeyError: | ||
1580 | pass | ||
1581 | |||
1582 | return None | ||
1583 | |||
1584 | |||
1585 | def print_values(type, values_list): | ||
1586 | """ | ||
1587 | Print the values in the given list of values. | ||
1588 | """ | ||
1589 | if type == "choicelist": | ||
1590 | for value in values_list: | ||
1591 | print "[\"%s\", \"%s\"]" % (value[0], value[1]) | ||
1592 | elif type == "boolean": | ||
1593 | for value in values_list: | ||
1594 | print "[\"%s\", \"%s\"]" % (value[0], value[1]) | ||
1595 | |||
1596 | |||
1597 | def yocto_layer_list_property_values(arch, property, scripts_path, properties_file, expand_common=True): | ||
1598 | """ | ||
1599 | List the possible values for a given input property. If | ||
1600 | properties_file is non-null, write the complete set of properties | ||
1601 | as a JSON object corresponding to an array of possible values. | ||
1602 | """ | ||
1603 | context = create_context("unused", arch, scripts_path) | ||
1604 | context["name"] = property | ||
1605 | |||
1606 | target_files = expand_targets(context, "unused", expand_common) | ||
1607 | |||
1608 | input_lines = gather_inputlines(target_files) | ||
1609 | |||
1610 | properties = get_properties(input_lines) | ||
1611 | |||
1612 | nested_properties = split_nested_property(property) | ||
1613 | if nested_properties: | ||
1614 | # currently the outer property of a nested property always | ||
1615 | # corresponds to an input line group | ||
1616 | input_line_group = find_input_line_group(nested_properties[0], input_lines) | ||
1617 | if input_line_group: | ||
1618 | input_lines[:] = input_line_group.group[1:] | ||
1619 | # The inner property of a nested property name is the | ||
1620 | # actual property name we want, so reset to that | ||
1621 | property = nested_properties[1] | ||
1622 | |||
1623 | input_line = find_input_line(property, input_lines) | ||
1624 | if not input_line: | ||
1625 | print "Couldn't find values for property %s" % property | ||
1626 | return | ||
1627 | |||
1628 | values_list = [] | ||
1629 | |||
1630 | type = input_line.props["type"] | ||
1631 | if type == "boolean": | ||
1632 | values_list.append(["y", "n"]) | ||
1633 | elif type == "choicelist" or type == "checklist": | ||
1634 | try: | ||
1635 | gen_fn = input_line.props["gen"] | ||
1636 | if nested_properties: | ||
1637 | context["filename"] = nested_properties[0] | ||
1638 | try: | ||
1639 | context["branches_base"] = input_line.props["branches_base"] | ||
1640 | except KeyError: | ||
1641 | context["branches_base"] = None | ||
1642 | values_list = input_line.gen_choices_list(context, False) | ||
1643 | except KeyError: | ||
1644 | for choice in input_line.choices: | ||
1645 | choicepair = [] | ||
1646 | choicepair.append(choice.val) | ||
1647 | choicepair.append(choice.desc) | ||
1648 | values_list.append(choicepair) | ||
1649 | |||
1650 | if properties_file: | ||
1651 | try: | ||
1652 | of = open(properties_file, "w") | ||
1653 | except IOError: | ||
1654 | print "Couldn't open properties file %s for writing, exiting" % properties_file | ||
1655 | sys.exit(1) | ||
1656 | |||
1657 | json.dump(values_list, of) | ||
1658 | |||
1659 | print_values(type, values_list) | ||
1660 | |||
1661 | |||
1662 | def yocto_bsp_list(args, scripts_path, properties_file): | ||
1663 | """ | ||
1664 | Print available architectures, or the complete list of properties | ||
1665 | defined by the BSP, or the possible values for a particular BSP | ||
1666 | property. | ||
1667 | """ | ||
1668 | if len(args) < 1: | ||
1669 | return False | ||
1670 | |||
1671 | if args[0] == "karch": | ||
1672 | lib_path = scripts_path + '/lib' | ||
1673 | bsp_path = lib_path + '/bsp' | ||
1674 | arch_path = bsp_path + '/substrate/target/arch' | ||
1675 | print "Architectures available:" | ||
1676 | for arch in os.listdir(arch_path): | ||
1677 | if arch == "common" or arch == "layer": | ||
1678 | continue | ||
1679 | print " %s" % arch | ||
1680 | return True | ||
1681 | else: | ||
1682 | arch = args[0] | ||
1683 | |||
1684 | if len(args) < 2 or len(args) > 3: | ||
1685 | return False | ||
1686 | |||
1687 | if len(args) == 2: | ||
1688 | if args[1] == "properties": | ||
1689 | yocto_layer_list_properties(arch, scripts_path, properties_file) | ||
1690 | else: | ||
1691 | return False | ||
1692 | |||
1693 | if len(args) == 3: | ||
1694 | if args[1] == "property": | ||
1695 | yocto_layer_list_property_values(arch, args[2], scripts_path, properties_file) | ||
1696 | else: | ||
1697 | return False | ||
1698 | |||
1699 | return True | ||
1700 | |||
1701 | |||
1702 | def yocto_layer_list(args, scripts_path, properties_file): | ||
1703 | """ | ||
1704 | Print the complete list of input properties defined by the layer, | ||
1705 | or the possible values for a particular layer property. | ||
1706 | """ | ||
1707 | if len(args) < 1: | ||
1708 | return False | ||
1709 | |||
1710 | if len(args) < 1 or len(args) > 2: | ||
1711 | return False | ||
1712 | |||
1713 | if len(args) == 1: | ||
1714 | if args[0] == "properties": | ||
1715 | yocto_layer_list_properties("layer", scripts_path, properties_file, False) | ||
1716 | else: | ||
1717 | return False | ||
1718 | |||
1719 | if len(args) == 2: | ||
1720 | if args[0] == "property": | ||
1721 | yocto_layer_list_property_values("layer", args[1], scripts_path, properties_file, False) | ||
1722 | else: | ||
1723 | return False | ||
1724 | |||
1725 | return True | ||
1726 | |||
1727 | |||
1728 | def map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch): | ||
1729 | """ | ||
1730 | Return the linux-yocto bsp branch to use with the specified | ||
1731 | kbranch. This handles the -standard variants for 3.4 and 3.8; the | ||
1732 | other variants don't need mappings. | ||
1733 | """ | ||
1734 | if need_new_kbranch == "y": | ||
1735 | kbranch = new_kbranch | ||
1736 | else: | ||
1737 | kbranch = existing_kbranch | ||
1738 | |||
1739 | if kbranch.startswith("standard/common-pc-64"): | ||
1740 | return "bsp/common-pc-64/common-pc-64-standard.scc" | ||
1741 | if kbranch.startswith("standard/common-pc"): | ||
1742 | return "bsp/common-pc/common-pc-standard.scc" | ||
1743 | else: | ||
1744 | return "ktypes/standard/standard.scc" | ||
1745 | |||
1746 | |||
1747 | def map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch): | ||
1748 | """ | ||
1749 | Return the linux-yocto bsp branch to use with the specified | ||
1750 | kbranch. This handles the -preempt-rt variants for 3.4 and 3.8; | ||
1751 | the other variants don't need mappings. | ||
1752 | """ | ||
1753 | if need_new_kbranch == "y": | ||
1754 | kbranch = new_kbranch | ||
1755 | else: | ||
1756 | kbranch = existing_kbranch | ||
1757 | |||
1758 | if kbranch.startswith("standard/preempt-rt/common-pc-64"): | ||
1759 | return "bsp/common-pc-64/common-pc-64-preempt-rt.scc" | ||
1760 | if kbranch.startswith("standard/preempt-rt/common-pc"): | ||
1761 | return "bsp/common-pc/common-pc-preempt-rt.scc" | ||
1762 | else: | ||
1763 | return "ktypes/preempt-rt/preempt-rt.scc" | ||
1764 | |||
1765 | |||
1766 | def map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch): | ||
1767 | """ | ||
1768 | Return the linux-yocto bsp branch to use with the specified | ||
1769 | kbranch. This handles the -tiny variants for 3.4 and 3.8; the | ||
1770 | other variants don't need mappings. | ||
1771 | """ | ||
1772 | if need_new_kbranch == "y": | ||
1773 | kbranch = new_kbranch | ||
1774 | else: | ||
1775 | kbranch = existing_kbranch | ||
1776 | |||
1777 | if kbranch.startswith("standard/tiny/common-pc"): | ||
1778 | return "bsp/common-pc/common-pc-tiny.scc" | ||
1779 | else: | ||
1780 | return "ktypes/tiny/tiny.scc" | ||
diff --git a/scripts/lib/bsp/help.py b/scripts/lib/bsp/help.py new file mode 100644 index 0000000000..7c436d6be0 --- /dev/null +++ b/scripts/lib/bsp/help.py | |||
@@ -0,0 +1,1043 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2012, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This module implements some basic help invocation functions along | ||
22 | # with the bulk of the help topic text for the Yocto BSP Tools. | ||
23 | # | ||
24 | # AUTHORS | ||
25 | # Tom Zanussi <tom.zanussi (at] intel.com> | ||
26 | # | ||
27 | |||
28 | import subprocess | ||
29 | import logging | ||
30 | |||
31 | |||
32 | def subcommand_error(args): | ||
33 | logging.info("invalid subcommand %s" % args[0]) | ||
34 | |||
35 | |||
36 | def display_help(subcommand, subcommands): | ||
37 | """ | ||
38 | Display help for subcommand. | ||
39 | """ | ||
40 | if subcommand not in subcommands: | ||
41 | return False | ||
42 | |||
43 | help = subcommands.get(subcommand, subcommand_error)[2] | ||
44 | pager = subprocess.Popen('less', stdin=subprocess.PIPE) | ||
45 | pager.communicate(help) | ||
46 | |||
47 | return True | ||
48 | |||
49 | |||
50 | def yocto_help(args, usage_str, subcommands): | ||
51 | """ | ||
52 | Subcommand help dispatcher. | ||
53 | """ | ||
54 | if len(args) == 1 or not display_help(args[1], subcommands): | ||
55 | print(usage_str) | ||
56 | |||
57 | |||
58 | def invoke_subcommand(args, parser, main_command_usage, subcommands): | ||
59 | """ | ||
60 | Dispatch to subcommand handler borrowed from combo-layer. | ||
61 | Should use argparse, but has to work in 2.6. | ||
62 | """ | ||
63 | if not args: | ||
64 | logging.error("No subcommand specified, exiting") | ||
65 | parser.print_help() | ||
66 | elif args[0] == "help": | ||
67 | yocto_help(args, main_command_usage, subcommands) | ||
68 | elif args[0] not in subcommands: | ||
69 | logging.error("Unsupported subcommand %s, exiting\n" % (args[0])) | ||
70 | parser.print_help() | ||
71 | else: | ||
72 | usage = subcommands.get(args[0], subcommand_error)[1] | ||
73 | subcommands.get(args[0], subcommand_error)[0](args[1:], usage) | ||
74 | |||
75 | |||
76 | ## | ||
77 | # yocto-bsp help and usage strings | ||
78 | ## | ||
79 | |||
80 | yocto_bsp_usage = """ | ||
81 | |||
82 | Create a customized Yocto BSP layer. | ||
83 | |||
84 | usage: yocto-bsp [--version] [--help] COMMAND [ARGS] | ||
85 | |||
86 | Current 'yocto-bsp' commands are: | ||
87 | create Create a new Yocto BSP | ||
88 | list List available values for options and BSP properties | ||
89 | |||
90 | See 'yocto-bsp help COMMAND' for more information on a specific command. | ||
91 | """ | ||
92 | |||
93 | yocto_bsp_help_usage = """ | ||
94 | |||
95 | usage: yocto-bsp help <subcommand> | ||
96 | |||
97 | This command displays detailed help for the specified subcommand. | ||
98 | """ | ||
99 | |||
100 | yocto_bsp_create_usage = """ | ||
101 | |||
102 | Create a new Yocto BSP | ||
103 | |||
104 | usage: yocto-bsp create <bsp-name> <karch> [-o <DIRNAME> | --outdir <DIRNAME>] | ||
105 | [-i <JSON PROPERTY FILE> | --infile <JSON PROPERTY_FILE>] | ||
106 | |||
107 | This command creates a Yocto BSP based on the specified parameters. | ||
108 | The new BSP will be a new Yocto BSP layer contained by default within | ||
109 | the top-level directory specified as 'meta-bsp-name'. The -o option | ||
110 | can be used to place the BSP layer in a directory with a different | ||
111 | name and location. | ||
112 | |||
113 | The value of the 'karch' parameter determines the set of files that | ||
114 | will be generated for the BSP, along with the specific set of | ||
115 | 'properties' that will be used to fill out the BSP-specific portions | ||
116 | of the BSP. The possible values for the 'karch' paramter can be | ||
117 | listed via 'yocto-bsp list karch'. | ||
118 | |||
119 | NOTE: Once created, you should add your new layer to your | ||
120 | bblayers.conf file in order for it to be subsequently seen and | ||
121 | modified by the yocto-kernel tool. | ||
122 | |||
123 | See 'yocto bsp help create' for more detailed instructions. | ||
124 | """ | ||
125 | |||
126 | yocto_bsp_create_help = """ | ||
127 | |||
128 | NAME | ||
129 | yocto-bsp create - Create a new Yocto BSP | ||
130 | |||
131 | SYNOPSIS | ||
132 | yocto-bsp create <bsp-name> <karch> [-o <DIRNAME> | --outdir <DIRNAME>] | ||
133 | [-i <JSON PROPERTY FILE> | --infile <JSON PROPERTY_FILE>] | ||
134 | |||
135 | DESCRIPTION | ||
136 | This command creates a Yocto BSP based on the specified | ||
137 | parameters. The new BSP will be a new Yocto BSP layer contained | ||
138 | by default within the top-level directory specified as | ||
139 | 'meta-bsp-name'. The -o option can be used to place the BSP layer | ||
140 | in a directory with a different name and location. | ||
141 | |||
142 | The value of the 'karch' parameter determines the set of files | ||
143 | that will be generated for the BSP, along with the specific set of | ||
144 | 'properties' that will be used to fill out the BSP-specific | ||
145 | portions of the BSP. The possible values for the 'karch' paramter | ||
146 | can be listed via 'yocto-bsp list karch'. | ||
147 | |||
148 | The BSP-specific properties that define the values that will be | ||
149 | used to generate a particular BSP can be specified on the | ||
150 | command-line using the -i option and supplying a JSON object | ||
151 | consisting of the set of name:value pairs needed by the BSP. | ||
152 | |||
153 | If the -i option is not used, the user will be interactively | ||
154 | prompted for each of the required property values, which will then | ||
155 | be used as values for BSP generation. | ||
156 | |||
157 | The set of properties available for a given architecture can be | ||
158 | listed using the 'yocto-bsp list' command. | ||
159 | |||
160 | Specifying -c causes the Python code generated and executed to | ||
161 | create the BSP to be dumped to the 'bspgen.out' file in the | ||
162 | current directory, and is useful for debugging. | ||
163 | |||
164 | NOTE: Once created, you should add your new layer to your | ||
165 | bblayers.conf file in order for it to be subsequently seen and | ||
166 | modified by the yocto-kernel tool. | ||
167 | |||
168 | For example, assuming your poky repo is at /path/to/poky, your new | ||
169 | BSP layer is at /path/to/poky/meta-mybsp, and your build directory | ||
170 | is /path/to/build: | ||
171 | |||
172 | $ gedit /path/to/build/conf/bblayers.conf | ||
173 | |||
174 | BBLAYERS ?= " \\ | ||
175 | /path/to/poky/meta \\ | ||
176 | /path/to/poky/meta-yocto \\ | ||
177 | /path/to/poky/meta-mybsp \\ | ||
178 | " | ||
179 | """ | ||
180 | |||
181 | yocto_bsp_list_usage = """ | ||
182 | |||
183 | usage: yocto-bsp list karch | ||
184 | yocto-bsp list <karch> properties | ||
185 | [-o <JSON PROPERTY FILE> | --outfile <JSON PROPERTY_FILE>] | ||
186 | yocto-bsp list <karch> property <xxx> | ||
187 | [-o <JSON PROPERTY FILE> | --outfile <JSON PROPERTY_FILE>] | ||
188 | |||
189 | This command enumerates the complete set of possible values for a | ||
190 | specified option or property needed by the BSP creation process. | ||
191 | |||
192 | The first form enumerates all the possible values that exist and can | ||
193 | be specified for the 'karch' parameter to the 'yocto bsp create' | ||
194 | command. | ||
195 | |||
196 | The second form enumerates all the possible properties that exist and | ||
197 | must have values specified for them in the 'yocto bsp create' command | ||
198 | for the given 'karch'. | ||
199 | |||
200 | The third form enumerates all the possible values that exist and can | ||
201 | be specified for any of the enumerable properties of the given | ||
202 | 'karch' in the 'yocto bsp create' command. | ||
203 | |||
204 | See 'yocto-bsp help list' for more details. | ||
205 | """ | ||
206 | |||
207 | yocto_bsp_list_help = """ | ||
208 | |||
209 | NAME | ||
210 | yocto-bsp list - List available values for options and BSP properties | ||
211 | |||
212 | SYNOPSIS | ||
213 | yocto-bsp list karch | ||
214 | yocto-bsp list <karch> properties | ||
215 | [--o <JSON PROPERTY FILE> | -outfile <JSON PROPERTY_FILE>] | ||
216 | yocto-bsp list <karch> property <xxx> | ||
217 | [--o <JSON PROPERTY FILE> | -outfile <JSON PROPERTY_FILE>] | ||
218 | |||
219 | DESCRIPTION | ||
220 | This command enumerates the complete set of possible values for a | ||
221 | specified option or property needed by the BSP creation process. | ||
222 | |||
223 | The first form enumerates all the possible values that exist and | ||
224 | can be specified for the 'karch' parameter to the 'yocto bsp | ||
225 | create' command. Example output for the 'list karch' command: | ||
226 | |||
227 | $ yocto-bsp list karch | ||
228 | Architectures available: | ||
229 | arm | ||
230 | powerpc | ||
231 | i386 | ||
232 | mips | ||
233 | x86_64 | ||
234 | qemu | ||
235 | |||
236 | The second form enumerates all the possible properties that exist | ||
237 | and must have values specified for them in the 'yocto bsp create' | ||
238 | command for the given 'karch'. This command is mainly meant to | ||
239 | allow the development user interface alternatives to the default | ||
240 | text-based prompting interface. If the -o option is specified, | ||
241 | the list of properties, in addition to being displayed, will be | ||
242 | written to the specified file as a JSON object. In this case, the | ||
243 | object will consist of the set of name:value pairs corresponding | ||
244 | to the (possibly nested) dictionary of properties defined by the | ||
245 | input statements used by the BSP. Some example output for the | ||
246 | 'list properties' command: | ||
247 | |||
248 | $ yocto-bsp list arm properties | ||
249 | "touchscreen" : { | ||
250 | "msg" : Does your BSP have a touchscreen? (y/N) | ||
251 | "default" : n | ||
252 | "type" : boolean | ||
253 | } | ||
254 | "uboot_loadaddress" : { | ||
255 | "msg" : Please specify a value for UBOOT_LOADADDRESS. | ||
256 | "default" : 0x80008000 | ||
257 | "type" : edit | ||
258 | "prio" : 40 | ||
259 | } | ||
260 | "kernel_choice" : { | ||
261 | "prio" : 10 | ||
262 | "default" : linux-yocto_3.2 | ||
263 | "depends-on" : use_default_kernel | ||
264 | "depends-on-val" : n | ||
265 | "msg" : Please choose the kernel to use in this BSP => | ||
266 | "type" : choicelist | ||
267 | "gen" : bsp.kernel.kernels | ||
268 | } | ||
269 | "if kernel_choice == "linux-yocto_3.0":" : { | ||
270 | "base_kbranch_linux_yocto_3_0" : { | ||
271 | "prio" : 20 | ||
272 | "default" : yocto/standard | ||
273 | "depends-on" : new_kbranch_linux_yocto_3_0 | ||
274 | "depends-on-val" : y | ||
275 | "msg" : Please choose a machine branch to base this BSP on => | ||
276 | "type" : choicelist | ||
277 | "gen" : bsp.kernel.all_branches | ||
278 | } | ||
279 | . | ||
280 | . | ||
281 | . | ||
282 | |||
283 | Each entry in the output consists of the name of the input element | ||
284 | e.g. "touchscreen", followed by the properties defined for that | ||
285 | element enclosed in braces. This information should provide | ||
286 | sufficient information to create a complete user interface with. | ||
287 | Two features of the scheme provide for conditional input. First, | ||
288 | if a Python "if" statement appears in place of an input element | ||
289 | name, the set of enclosed input elements apply and should be | ||
290 | presented to the user only if the 'if' statement evaluates to | ||
291 | true. The test in the if statement will always reference another | ||
292 | input element in the list, which means that the element being | ||
293 | tested should be presented to the user before the elements | ||
294 | enclosed by the if block. Secondly, in a similar way, some | ||
295 | elements contain "depends-on" and depends-on-val" tags, which mean | ||
296 | that the affected input element should only be presented to the | ||
297 | user if the element it depends on has already been presented to | ||
298 | the user and the user has selected the specified value for that | ||
299 | element. | ||
300 | |||
301 | The third form enumerates all the possible values that exist and | ||
302 | can be specified for any of the enumerable properties of the given | ||
303 | 'karch' in the 'yocto bsp create' command. If the -o option is | ||
304 | specified, the list of values for the given property, in addition | ||
305 | to being displayed, will be written to the specified file as a | ||
306 | JSON object. In this case, the object will consist of the set of | ||
307 | name:value pairs corresponding to the array of property values | ||
308 | associated with the property. | ||
309 | |||
310 | $ yocto-bsp list i386 property xserver_choice | ||
311 | ["xserver_vesa", "VESA xserver support"] | ||
312 | ["xserver_i915", "i915 xserver support"] | ||
313 | |||
314 | $ yocto-bsp list arm property base_kbranch_linux_yocto_3_0 | ||
315 | Getting branches from remote repo git://git.yoctoproject.org/linux-yocto-3.0... | ||
316 | ["yocto/base", "yocto/base"] | ||
317 | ["yocto/eg20t", "yocto/eg20t"] | ||
318 | ["yocto/gma500", "yocto/gma500"] | ||
319 | ["yocto/pvr", "yocto/pvr"] | ||
320 | ["yocto/standard/arm-versatile-926ejs", "yocto/standard/arm-versatile-926ejs"] | ||
321 | ["yocto/standard/base", "yocto/standard/base"] | ||
322 | ["yocto/standard/cedartrail", "yocto/standard/cedartrail"] | ||
323 | . | ||
324 | . | ||
325 | . | ||
326 | ["yocto/standard/qemu-ppc32", "yocto/standard/qemu-ppc32"] | ||
327 | ["yocto/standard/routerstationpro", "yocto/standard/routerstationpro"] | ||
328 | |||
329 | The third form as well is meant mainly for developers of | ||
330 | alternative interfaces - it allows the developer to fetch the | ||
331 | possible values for a given input element on-demand. This | ||
332 | on-demand capability is especially valuable for elements that | ||
333 | require relatively expensive remote operations to fulfill, such as | ||
334 | the example that returns the set of branches available in a remote | ||
335 | git tree above. | ||
336 | |||
337 | """ | ||
338 | |||
339 | ## | ||
340 | # yocto-kernel help and usage strings | ||
341 | ## | ||
342 | |||
343 | yocto_kernel_usage = """ | ||
344 | |||
345 | Modify and list Yocto BSP kernel config items and patches. | ||
346 | |||
347 | usage: yocto-kernel [--version] [--help] COMMAND [ARGS] | ||
348 | |||
349 | Current 'yocto-kernel' commands are: | ||
350 | config list List the modifiable set of bare kernel config options for a BSP | ||
351 | config add Add or modify bare kernel config options for a BSP | ||
352 | config rm Remove bare kernel config options from a BSP | ||
353 | patch list List the patches associated with a BSP | ||
354 | patch add Patch the Yocto kernel for a BSP | ||
355 | patch rm Remove patches from a BSP | ||
356 | feature list List the features used by a BSP | ||
357 | feature add Have a BSP use a feature | ||
358 | feature rm Have a BSP stop using a feature | ||
359 | features list List the features available to BSPs | ||
360 | feature describe Describe a particular feature | ||
361 | feature create Create a new BSP-local feature | ||
362 | feature destroy Remove a BSP-local feature | ||
363 | |||
364 | See 'yocto-kernel help COMMAND' for more information on a specific command. | ||
365 | |||
366 | """ | ||
367 | |||
368 | |||
369 | yocto_kernel_help_usage = """ | ||
370 | |||
371 | usage: yocto-kernel help <subcommand> | ||
372 | |||
373 | This command displays detailed help for the specified subcommand. | ||
374 | """ | ||
375 | |||
376 | yocto_kernel_config_list_usage = """ | ||
377 | |||
378 | List the modifiable set of bare kernel config options for a BSP | ||
379 | |||
380 | usage: yocto-kernel config list <bsp-name> | ||
381 | |||
382 | This command lists the 'modifiable' config items for a BSP i.e. the | ||
383 | items which are eligible for modification or removal by other | ||
384 | yocto-kernel commands. | ||
385 | |||
386 | 'modifiable' config items are the config items contained a BSP's | ||
387 | user-config.cfg base config. | ||
388 | """ | ||
389 | |||
390 | |||
391 | yocto_kernel_config_list_help = """ | ||
392 | |||
393 | NAME | ||
394 | yocto-kernel config list - List the modifiable set of bare kernel | ||
395 | config options for a BSP | ||
396 | |||
397 | SYNOPSIS | ||
398 | yocto-kernel config list <bsp-name> | ||
399 | |||
400 | DESCRIPTION | ||
401 | This command lists the 'modifiable' config items for a BSP | ||
402 | i.e. the items which are eligible for modification or removal by | ||
403 | other yocto-kernel commands. | ||
404 | """ | ||
405 | |||
406 | |||
407 | yocto_kernel_config_add_usage = """ | ||
408 | |||
409 | Add or modify bare kernel config options for a BSP | ||
410 | |||
411 | usage: yocto-kernel config add <bsp-name> [<CONFIG_XXX=x> ...] | ||
412 | |||
413 | This command adds one or more CONFIG_XXX=x items to a BSP's user-config.cfg | ||
414 | base config. | ||
415 | """ | ||
416 | |||
417 | |||
418 | yocto_kernel_config_add_help = """ | ||
419 | |||
420 | NAME | ||
421 | yocto-kernel config add - Add or modify bare kernel config options | ||
422 | for a BSP | ||
423 | |||
424 | SYNOPSIS | ||
425 | yocto-kernel config add <bsp-name> [<CONFIG_XXX=x> ...] | ||
426 | |||
427 | DESCRIPTION | ||
428 | This command adds one or more CONFIG_XXX=x items to a BSP's | ||
429 | foo.cfg base config. | ||
430 | |||
431 | NOTE: It's up to the user to determine whether or not the config | ||
432 | options being added make sense or not - this command does no | ||
433 | sanity checking or verification of any kind to ensure that a | ||
434 | config option really makes sense and will actually be set in in | ||
435 | the final config. For example, if a config option depends on | ||
436 | other config options, it will be turned off by kconfig if the | ||
437 | other options aren't set correctly. | ||
438 | """ | ||
439 | |||
440 | |||
441 | yocto_kernel_config_rm_usage = """ | ||
442 | |||
443 | Remove bare kernel config options from a BSP | ||
444 | |||
445 | usage: yocto-kernel config rm <bsp-name> | ||
446 | |||
447 | This command removes (turns off) one or more CONFIG_XXX items from a | ||
448 | BSP's user-config.cfg base config. | ||
449 | |||
450 | The set of config items available to be removed by this command for a | ||
451 | BSP is listed and the user prompted for the specific items to remove. | ||
452 | """ | ||
453 | |||
454 | |||
455 | yocto_kernel_config_rm_help = """ | ||
456 | |||
457 | NAME | ||
458 | yocto-kernel config rm - Remove bare kernel config options from a | ||
459 | BSP | ||
460 | |||
461 | SYNOPSIS | ||
462 | yocto-kernel config rm <bsp-name> | ||
463 | |||
464 | DESCRIPTION | ||
465 | This command removes (turns off) one or more CONFIG_XXX items from a | ||
466 | BSP's user-config.cfg base config. | ||
467 | |||
468 | The set of config items available to be removed by this command | ||
469 | for a BSP is listed and the user prompted for the specific items | ||
470 | to remove. | ||
471 | """ | ||
472 | |||
473 | |||
474 | yocto_kernel_patch_list_usage = """ | ||
475 | |||
476 | List the patches associated with the kernel for a BSP | ||
477 | |||
478 | usage: yocto-kernel patch list <bsp-name> | ||
479 | |||
480 | This command lists the patches associated with a BSP. | ||
481 | |||
482 | NOTE: this only applies to patches listed in the kernel recipe's | ||
483 | user-patches.scc file (and currently repeated in its SRC_URI). | ||
484 | """ | ||
485 | |||
486 | |||
487 | yocto_kernel_patch_list_help = """ | ||
488 | |||
489 | NAME | ||
490 | yocto-kernel patch list - List the patches associated with the kernel | ||
491 | for a BSP | ||
492 | |||
493 | SYNOPSIS | ||
494 | yocto-kernel patch list <bsp-name> | ||
495 | |||
496 | DESCRIPTION | ||
497 | This command lists the patches associated with a BSP. | ||
498 | |||
499 | NOTE: this only applies to patches listed in the kernel recipe's | ||
500 | user-patches.scc file (and currently repeated in its SRC_URI). | ||
501 | """ | ||
502 | |||
503 | |||
504 | yocto_kernel_patch_add_usage = """ | ||
505 | |||
506 | Patch the Yocto kernel for a specific BSP | ||
507 | |||
508 | usage: yocto-kernel patch add <bsp-name> [<PATCH> ...] | ||
509 | |||
510 | This command adds one or more patches to a BSP's machine branch. The | ||
511 | patch will be added to the BSP's linux-yocto kernel user-patches.scc | ||
512 | file (and currently repeated in its SRC_URI) and will be guaranteed | ||
513 | to be applied in the order specified. | ||
514 | """ | ||
515 | |||
516 | |||
517 | yocto_kernel_patch_add_help = """ | ||
518 | |||
519 | NAME | ||
520 | yocto-kernel patch add - Patch the Yocto kernel for a specific BSP | ||
521 | |||
522 | SYNOPSIS | ||
523 | yocto-kernel patch add <bsp-name> [<PATCH> ...] | ||
524 | |||
525 | DESCRIPTION | ||
526 | This command adds one or more patches to a BSP's machine branch. | ||
527 | The patch will be added to the BSP's linux-yocto kernel | ||
528 | user-patches.scc file (and currently repeated in its SRC_URI) and | ||
529 | will be guaranteed to be applied in the order specified. | ||
530 | |||
531 | NOTE: It's up to the user to determine whether or not the patches | ||
532 | being added makes sense or not - this command does no sanity | ||
533 | checking or verification of any kind to ensure that a patch can | ||
534 | actually be applied to the BSP's kernel branch; it's assumed that | ||
535 | the user has already done that. | ||
536 | """ | ||
537 | |||
538 | |||
539 | yocto_kernel_patch_rm_usage = """ | ||
540 | |||
541 | Remove a patch from the Yocto kernel for a specific BSP | ||
542 | |||
543 | usage: yocto-kernel patch rm <bsp-name> | ||
544 | |||
545 | This command removes one or more patches from a BSP's machine branch. | ||
546 | The patch will be removed from the BSP's linux-yocto kernel | ||
547 | user-patches.scc file (and currently repeated in its SRC_URI) and | ||
548 | kernel SRC_URI dir. | ||
549 | |||
550 | The set of patches available to be removed by this command for a BSP | ||
551 | is listed and the user prompted for the specific patches to remove. | ||
552 | """ | ||
553 | |||
554 | |||
555 | yocto_kernel_patch_rm_help = """ | ||
556 | |||
557 | NAME | ||
558 | yocto-kernel patch rm - Remove a patch from the Yocto kernel for a specific BSP | ||
559 | |||
560 | SYNOPSIS | ||
561 | yocto-kernel patch rm <bsp-name> | ||
562 | |||
563 | DESCRIPTION | ||
564 | This command removes one or more patches from a BSP's machine | ||
565 | branch. The patch will be removed from the BSP's linux-yocto | ||
566 | kernel user-patches.scc file (and currently repeated in its | ||
567 | SRC_URI). | ||
568 | |||
569 | The set of patches available to be removed by this command for a | ||
570 | BSP is listed and the user prompted for the specific patches to | ||
571 | remove. | ||
572 | """ | ||
573 | |||
574 | yocto_kernel_feature_list_usage = """ | ||
575 | |||
576 | List the BSP features that are being used by a BSP | ||
577 | |||
578 | usage: yocto-kernel feature list <bsp-name> | ||
579 | |||
580 | This command lists the features being used by a BSP i.e. the features | ||
581 | which are eligible for modification or removal by other yocto-kernel | ||
582 | commands. | ||
583 | |||
584 | 'modifiable' features are the features listed in a BSP's | ||
585 | user-features.scc file. | ||
586 | """ | ||
587 | |||
588 | |||
589 | yocto_kernel_feature_list_help = """ | ||
590 | |||
591 | NAME | ||
592 | yocto-kernel feature list - List the modifiable set of features | ||
593 | being used by a BSP | ||
594 | |||
595 | SYNOPSIS | ||
596 | yocto-kernel feature list <bsp-name> | ||
597 | |||
598 | DESCRIPTION | ||
599 | This command lists the 'modifiable' features being used by a BSP | ||
600 | i.e. the features which are eligible for modification or removal | ||
601 | by other yocto-kernel commands. | ||
602 | """ | ||
603 | |||
604 | |||
605 | yocto_kernel_feature_add_usage = """ | ||
606 | |||
607 | Add to or modify the list of features being used for a BSP | ||
608 | |||
609 | usage: yocto-kernel feature add <bsp-name> [/xxxx/yyyy/feature.scc ...] | ||
610 | |||
611 | This command adds one or more feature items to a BSP's kernel | ||
612 | user-features.scc file, which is the file used to manage features in | ||
613 | a yocto-bsp-generated BSP. Features to be added must be specified as | ||
614 | fully-qualified feature names. | ||
615 | """ | ||
616 | |||
617 | |||
618 | yocto_kernel_feature_add_help = """ | ||
619 | |||
620 | NAME | ||
621 | yocto-kernel feature add - Add to or modify the list of features | ||
622 | being used for a BSP | ||
623 | |||
624 | SYNOPSIS | ||
625 | yocto-kernel feature add <bsp-name> [/xxxx/yyyy/feature.scc ...] | ||
626 | |||
627 | DESCRIPTION | ||
628 | This command adds one or more feature items to a BSP's | ||
629 | user-features.scc file, which is the file used to manage features | ||
630 | in a yocto-bsp-generated BSP. Features to be added must be | ||
631 | specified as fully-qualified feature names. | ||
632 | """ | ||
633 | |||
634 | |||
635 | yocto_kernel_feature_rm_usage = """ | ||
636 | |||
637 | Remove a feature from the list of features being used for a BSP | ||
638 | |||
639 | usage: yocto-kernel feature rm <bsp-name> | ||
640 | |||
641 | This command removes (turns off) one or more features from a BSP's | ||
642 | user-features.scc file, which is the file used to manage features in | ||
643 | a yocto-bsp-generated BSP. | ||
644 | |||
645 | The set of features available to be removed by this command for a BSP | ||
646 | is listed and the user prompted for the specific items to remove. | ||
647 | """ | ||
648 | |||
649 | |||
650 | yocto_kernel_feature_rm_help = """ | ||
651 | |||
652 | NAME | ||
653 | yocto-kernel feature rm - Remove a feature from the list of | ||
654 | features being used for a BSP | ||
655 | |||
656 | SYNOPSIS | ||
657 | yocto-kernel feature rm <bsp-name> | ||
658 | |||
659 | DESCRIPTION | ||
660 | This command removes (turns off) one or more features from a BSP's | ||
661 | user-features.scc file, which is the file used to manage features | ||
662 | in a yocto-bsp-generated BSP. | ||
663 | |||
664 | The set of features available to be removed by this command for a | ||
665 | BSP is listed and the user prompted for the specific items to | ||
666 | remove. | ||
667 | """ | ||
668 | |||
669 | |||
670 | yocto_kernel_available_features_list_usage = """ | ||
671 | |||
672 | List the set of kernel features available to a BSP | ||
673 | |||
674 | usage: yocto-kernel features list <bsp-name> | ||
675 | |||
676 | This command lists the complete set of kernel features available to a | ||
677 | BSP. This includes the features contained in linux-yocto meta | ||
678 | branches as well as recipe-space features defined locally to the BSP. | ||
679 | """ | ||
680 | |||
681 | |||
682 | yocto_kernel_available_features_list_help = """ | ||
683 | |||
684 | NAME | ||
685 | yocto-kernel features list - List the set of kernel features | ||
686 | available to a BSP | ||
687 | |||
688 | SYNOPSIS | ||
689 | yocto-kernel features list <bsp-name> | ||
690 | |||
691 | DESCRIPTION | ||
692 | This command lists the complete set of kernel features available | ||
693 | to a BSP. This includes the features contained in linux-yocto | ||
694 | meta branches as well as recipe-space features defined locally to | ||
695 | the BSP. | ||
696 | """ | ||
697 | |||
698 | |||
699 | yocto_kernel_feature_describe_usage = """ | ||
700 | |||
701 | Print the description and compatibility information for a given kernel feature | ||
702 | |||
703 | usage: yocto-kernel feature describe <bsp-name> [/xxxx/yyyy/feature.scc ...] | ||
704 | |||
705 | This command prints the description and compatibility of a specific | ||
706 | feature in the format 'description [compatibility]. | ||
707 | """ | ||
708 | |||
709 | |||
710 | yocto_kernel_feature_describe_help = """ | ||
711 | |||
712 | NAME | ||
713 | yocto-kernel feature describe - print the description and | ||
714 | compatibility information for a given kernel feature | ||
715 | |||
716 | SYNOPSIS | ||
717 | yocto-kernel feature describe <bsp-name> [/xxxx/yyyy/feature.scc ...] | ||
718 | |||
719 | DESCRIPTION | ||
720 | This command prints the description and compatibility of a | ||
721 | specific feature in the format 'description [compatibility]. If | ||
722 | the feature doesn't define a description or compatibility, a | ||
723 | string with generic unknown values will be printed. | ||
724 | """ | ||
725 | |||
726 | |||
727 | yocto_kernel_feature_create_usage = """ | ||
728 | |||
729 | Create a recipe-space kernel feature in a BSP | ||
730 | |||
731 | usage: yocto-kernel feature create <bsp-name> newfeature.scc \ | ||
732 | "Feature Description" capabilities [<CONFIG_XXX=x> ...] [<PATCH> ...] | ||
733 | |||
734 | This command creates a new kernel feature from the bare config | ||
735 | options and patches specified on the command-line. | ||
736 | """ | ||
737 | |||
738 | |||
739 | yocto_kernel_feature_create_help = """ | ||
740 | |||
741 | NAME | ||
742 | yocto-kernel feature create - create a recipe-space kernel feature | ||
743 | in a BSP | ||
744 | |||
745 | SYNOPSIS | ||
746 | yocto-kernel feature create <bsp-name> newfeature.scc \ | ||
747 | "Feature Description" capabilities [<CONFIG_XXX=x> ...] [<PATCH> ...] | ||
748 | |||
749 | DESCRIPTION | ||
750 | This command creates a new kernel feature from the bare config | ||
751 | options and patches specified on the command-line. The new | ||
752 | feature will be created in recipe-space, specifically in either | ||
753 | the kernel .bbappend's /files/cfg or /files/features subdirectory, | ||
754 | depending on whether or not the feature contains config items only | ||
755 | or config items along with patches. The named feature must end | ||
756 | with .scc and must not contain a feature directory to contain the | ||
757 | feature (this will be determined automatically), and a feature | ||
758 | decription in double-quotes along with a capabilities string | ||
759 | (which for the time being can be one of: 'all' or 'board'). | ||
760 | """ | ||
761 | |||
762 | |||
763 | yocto_kernel_feature_destroy_usage = """ | ||
764 | |||
765 | Destroy a recipe-space kernel feature in a BSP | ||
766 | |||
767 | usage: yocto-kernel feature destroy <bsp-name> feature.scc | ||
768 | |||
769 | This command destroys a kernel feature defined in the specified BSP's | ||
770 | recipe-space kernel definition. | ||
771 | """ | ||
772 | |||
773 | |||
774 | yocto_kernel_feature_destroy_help = """ | ||
775 | |||
776 | NAME | ||
777 | yocto-kernel feature destroy <bsp-name> feature.scc - destroy a | ||
778 | recipe-space kernel feature in a BSP | ||
779 | |||
780 | SYNOPSIS | ||
781 | yocto-kernel feature destroy <bsp-name> feature.scc | ||
782 | |||
783 | DESCRIPTION | ||
784 | This command destroys a kernel feature defined in the specified | ||
785 | BSP's recipe-space kernel definition. The named feature must end | ||
786 | with .scc and must not contain a feature directory to contain the | ||
787 | feature (this will be determined automatically). If the kernel | ||
788 | feature is in use by a BSP, it can't be removed until the BSP | ||
789 | stops using it (see yocto-kernel feature rm to stop using it). | ||
790 | """ | ||
791 | |||
792 | ## | ||
793 | # yocto-layer help and usage strings | ||
794 | ## | ||
795 | |||
796 | yocto_layer_usage = """ | ||
797 | |||
798 | Create a generic Yocto layer. | ||
799 | |||
800 | usage: yocto-layer [--version] [--help] COMMAND [ARGS] | ||
801 | |||
802 | Current 'yocto-layer' commands are: | ||
803 | create Create a new generic Yocto layer | ||
804 | list List available values for input options and properties | ||
805 | |||
806 | See 'yocto-layer help COMMAND' for more information on a specific command. | ||
807 | """ | ||
808 | |||
809 | yocto_layer_help_usage = """ | ||
810 | |||
811 | usage: yocto-layer help <subcommand> | ||
812 | |||
813 | This command displays detailed help for the specified subcommand. | ||
814 | """ | ||
815 | |||
816 | yocto_layer_create_usage = """ | ||
817 | |||
818 | Create a new generic Yocto layer | ||
819 | |||
820 | usage: yocto-layer create <layer-name> [layer_priority] | ||
821 | [-o <DIRNAME> | --outdir <DIRNAME>] | ||
822 | [-i <JSON PROPERTY FILE> | --infile <JSON PROPERTY_FILE>] | ||
823 | |||
824 | This command creates a generic Yocto layer based on the specified | ||
825 | parameters. The new layer will be a new Yocto layer contained by | ||
826 | default within the top-level directory specified as | ||
827 | 'meta-layer-name'. The -o option can be used to place the layer in a | ||
828 | directory with a different name and location. | ||
829 | |||
830 | If layer_priority is specified, a simple layer will be created using | ||
831 | the given layer priority, and the user will not be prompted for | ||
832 | further input. | ||
833 | |||
834 | NOTE: Once created, you should add your new layer to your | ||
835 | bblayers.conf file in order for it to be subsequently seen and | ||
836 | modified by the yocto-kernel tool. Instructions for doing this can | ||
837 | be found in the README file generated in the layer's top-level | ||
838 | directory. | ||
839 | |||
840 | See 'yocto layer help create' for more detailed instructions. | ||
841 | """ | ||
842 | |||
843 | yocto_layer_create_help = """ | ||
844 | |||
845 | NAME | ||
846 | yocto-layer create - Create a new generic Yocto layer | ||
847 | |||
848 | SYNOPSIS | ||
849 | yocto-layer create <layer-name> [layer_priority] | ||
850 | [-o <DIRNAME> | --outdir <DIRNAME>] | ||
851 | [-i <JSON PROPERTY FILE> | --infile <JSON PROPERTY_FILE>] | ||
852 | |||
853 | DESCRIPTION | ||
854 | This command creates a generic Yocto layer based on the specified | ||
855 | parameters. The new layer will be a new Yocto layer contained by | ||
856 | default within the top-level directory specified as | ||
857 | 'meta-layer-name'. The -o option can be used to place the layer | ||
858 | in a directory with a different name and location. | ||
859 | |||
860 | If layer_priority is specified, a simple layer will be created | ||
861 | using the given layer priority, and the user will not be prompted | ||
862 | for further input. | ||
863 | |||
864 | The layer-specific properties that define the values that will be | ||
865 | used to generate the layer can be specified on the command-line | ||
866 | using the -i option and supplying a JSON object consisting of the | ||
867 | set of name:value pairs needed by the layer. | ||
868 | |||
869 | If the -i option is not used, the user will be interactively | ||
870 | prompted for each of the required property values, which will then | ||
871 | be used as values for layer generation. | ||
872 | |||
873 | The set of properties available can be listed using the | ||
874 | 'yocto-layer list' command. | ||
875 | |||
876 | Specifying -c causes the Python code generated and executed to | ||
877 | create the layer to be dumped to the 'bspgen.out' file in the | ||
878 | current directory, and is useful for debugging. | ||
879 | |||
880 | NOTE: Once created, you should add your new layer to your | ||
881 | bblayers.conf file in order for it to be subsequently seen and | ||
882 | modified by the yocto-kernel tool. Instructions for doing this | ||
883 | can be found in the README file generated in the layer's top-level | ||
884 | directory. | ||
885 | |||
886 | For example, assuming your poky repo is at /path/to/poky, your new | ||
887 | layer is at /path/to/poky/meta-mylayer, and your build directory | ||
888 | is /path/to/build: | ||
889 | |||
890 | $ gedit /path/to/build/conf/bblayers.conf | ||
891 | |||
892 | BBLAYERS ?= " \\ | ||
893 | /path/to/poky/meta \\ | ||
894 | /path/to/poky/meta-yocto \\ | ||
895 | /path/to/poky/meta-mylayer \\ | ||
896 | " | ||
897 | """ | ||
898 | |||
899 | yocto_layer_list_usage = """ | ||
900 | |||
901 | usage: yocto-layer list properties | ||
902 | [-o <JSON PROPERTY FILE> | --outfile <JSON PROPERTY_FILE>] | ||
903 | yocto-layer list property <xxx> | ||
904 | [-o <JSON PROPERTY FILE> | --outfile <JSON PROPERTY_FILE>] | ||
905 | |||
906 | This command enumerates the complete set of possible values for a | ||
907 | specified option or property needed by the layer creation process. | ||
908 | |||
909 | The first form enumerates all the possible properties that exist and | ||
910 | must have values specified for them in the 'yocto-layer create' | ||
911 | command. | ||
912 | |||
913 | The second form enumerates all the possible values that exist and can | ||
914 | be specified for any of the enumerable properties in the 'yocto-layer | ||
915 | create' command. | ||
916 | |||
917 | See 'yocto-layer help list' for more details. | ||
918 | """ | ||
919 | |||
920 | yocto_layer_list_help = """ | ||
921 | |||
922 | NAME | ||
923 | yocto-layer list - List available values for layer input options and properties | ||
924 | |||
925 | SYNOPSIS | ||
926 | yocto-layer list properties | ||
927 | [--o <JSON PROPERTY FILE> | -outfile <JSON PROPERTY_FILE>] | ||
928 | yocto-layer list property <xxx> | ||
929 | [--o <JSON PROPERTY FILE> | -outfile <JSON PROPERTY_FILE>] | ||
930 | |||
931 | DESCRIPTION | ||
932 | This command enumerates the complete set of possible values for a | ||
933 | specified option or property needed by the layer creation process. | ||
934 | |||
935 | The first form enumerates all the possible properties that exist | ||
936 | and must have values specified for them in the 'yocto-layer | ||
937 | create' command. This command is mainly meant to aid the | ||
938 | development of user interface alternatives to the default | ||
939 | text-based prompting interface. If the -o option is specified, | ||
940 | the list of properties, in addition to being displayed, will be | ||
941 | written to the specified file as a JSON object. In this case, the | ||
942 | object will consist of the set of name:value pairs corresponding | ||
943 | to the (possibly nested) dictionary of properties defined by the | ||
944 | input statements used by the BSP. Some example output for the | ||
945 | 'list properties' command: | ||
946 | |||
947 | $ yocto-layer list properties | ||
948 | "example_bbappend_name" : { | ||
949 | "default" : example | ||
950 | "msg" : Please enter the name you'd like to use for your bbappend file: | ||
951 | "type" : edit | ||
952 | "prio" : 20 | ||
953 | "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall | ||
954 | } | ||
955 | "create_example_recipe" : { | ||
956 | "default" : n | ||
957 | "msg" : Would you like to have an example recipe created? (y/n) | ||
958 | "type" : boolean | ||
959 | "prio" : 20 | ||
960 | "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall | ||
961 | } | ||
962 | "example_recipe_name" : { | ||
963 | "default" : example | ||
964 | "msg" : Please enter the name you'd like to use for your example recipe: | ||
965 | "type" : edit | ||
966 | "prio" : 20 | ||
967 | "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall | ||
968 | } | ||
969 | "layer_priority" : { | ||
970 | "default" : 6 | ||
971 | "msg" : Please enter the layer priority you'd like to use for the layer: | ||
972 | "type" : edit | ||
973 | "prio" : 20 | ||
974 | "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall | ||
975 | } | ||
976 | "create_example_bbappend" : { | ||
977 | "default" : n | ||
978 | "msg" : Would you like to have an example bbappend file created? (y/n) | ||
979 | "type" : boolean | ||
980 | "prio" : 20 | ||
981 | "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall | ||
982 | } | ||
983 | "example_bbappend_version" : { | ||
984 | "default" : 0.1 | ||
985 | "msg" : Please enter the version number you'd like to use for your bbappend file (this should match the recipe you're appending to): | ||
986 | "type" : edit | ||
987 | "prio" : 20 | ||
988 | "filename" : /home/trz/yocto/yocto-layer-dev/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall | ||
989 | } | ||
990 | |||
991 | Each entry in the output consists of the name of the input element | ||
992 | e.g. "layer_priority", followed by the properties defined for that | ||
993 | element enclosed in braces. This information should provide | ||
994 | sufficient information to create a complete user interface. Two | ||
995 | features of the scheme provide for conditional input. First, if a | ||
996 | Python "if" statement appears in place of an input element name, | ||
997 | the set of enclosed input elements apply and should be presented | ||
998 | to the user only if the 'if' statement evaluates to true. The | ||
999 | test in the if statement will always reference another input | ||
1000 | element in the list, which means that the element being tested | ||
1001 | should be presented to the user before the elements enclosed by | ||
1002 | the if block. Secondly, in a similar way, some elements contain | ||
1003 | "depends-on" and depends-on-val" tags, which mean that the | ||
1004 | affected input element should only be presented to the user if the | ||
1005 | element it depends on has already been presented to the user and | ||
1006 | the user has selected the specified value for that element. | ||
1007 | |||
1008 | The second form enumerates all the possible values that exist and | ||
1009 | can be specified for any of the enumerable properties in the | ||
1010 | 'yocto-layer create' command. If the -o option is specified, the | ||
1011 | list of values for the given property, in addition to being | ||
1012 | displayed, will be written to the specified file as a JSON object. | ||
1013 | In this case, the object will consist of the set of name:value | ||
1014 | pairs corresponding to the array of property values associated | ||
1015 | with the property. | ||
1016 | |||
1017 | $ yocto-layer list property layer_priority | ||
1018 | [no output - layer_priority is a text field that has no enumerable values] | ||
1019 | |||
1020 | The second form as well is meant mainly for developers of | ||
1021 | alternative interfaces - it allows the developer to fetch the | ||
1022 | possible values for a given input element on-demand. This | ||
1023 | on-demand capability is especially valuable for elements that | ||
1024 | require relatively expensive remote operations to fulfill, such as | ||
1025 | the example that returns the set of branches available in a remote | ||
1026 | git tree above. | ||
1027 | |||
1028 | """ | ||
1029 | |||
1030 | ## | ||
1031 | # test code | ||
1032 | ## | ||
1033 | |||
1034 | test_bsp_properties = { | ||
1035 | 'smp': 'yes', | ||
1036 | 'touchscreen': 'yes', | ||
1037 | 'keyboard': 'no', | ||
1038 | 'xserver': 'yes', | ||
1039 | 'xserver_choice': 'xserver-i915', | ||
1040 | 'features': ['goodfeature', 'greatfeature'], | ||
1041 | 'tunefile': 'tune-quark', | ||
1042 | } | ||
1043 | |||
diff --git a/scripts/lib/bsp/kernel.py b/scripts/lib/bsp/kernel.py new file mode 100644 index 0000000000..ba68b60fcb --- /dev/null +++ b/scripts/lib/bsp/kernel.py | |||
@@ -0,0 +1,1071 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2012, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This module implements the kernel-related functions used by | ||
22 | # 'yocto-kernel' to manage kernel config items and patches for Yocto | ||
23 | # BSPs. | ||
24 | # | ||
25 | # AUTHORS | ||
26 | # Tom Zanussi <tom.zanussi (at] intel.com> | ||
27 | # | ||
28 | |||
29 | import sys | ||
30 | import os | ||
31 | import shutil | ||
32 | from tags import * | ||
33 | import glob | ||
34 | import subprocess | ||
35 | from engine import create_context | ||
36 | |||
37 | |||
38 | def find_bblayers(): | ||
39 | """ | ||
40 | Find and return a sanitized list of the layers found in BBLAYERS. | ||
41 | """ | ||
42 | try: | ||
43 | builddir = os.environ["BUILDDIR"] | ||
44 | except KeyError: | ||
45 | print "BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)" | ||
46 | sys.exit(1) | ||
47 | bblayers_conf = os.path.join(builddir, "conf/bblayers.conf") | ||
48 | |||
49 | layers = [] | ||
50 | |||
51 | bitbake_env_cmd = "bitbake -e" | ||
52 | bitbake_env_lines = subprocess.Popen(bitbake_env_cmd, shell=True, | ||
53 | stdout=subprocess.PIPE).stdout.read() | ||
54 | |||
55 | if not bitbake_env_lines: | ||
56 | print "Couldn't get '%s' output, exiting." % bitbake_env_cmd | ||
57 | sys.exit(1) | ||
58 | |||
59 | for line in bitbake_env_lines.split('\n'): | ||
60 | bblayers = get_line_val(line, "BBLAYERS") | ||
61 | if (bblayers): | ||
62 | break | ||
63 | |||
64 | if not bblayers: | ||
65 | print "Couldn't find BBLAYERS in %s output, exiting." % \ | ||
66 | bitbake_env_cmd | ||
67 | sys.exit(1) | ||
68 | |||
69 | raw_layers = bblayers.split() | ||
70 | |||
71 | for layer in raw_layers: | ||
72 | if layer == 'BBLAYERS' or '=' in layer: | ||
73 | continue | ||
74 | layers.append(layer) | ||
75 | |||
76 | return layers | ||
77 | |||
78 | |||
79 | def get_line_val(line, key): | ||
80 | """ | ||
81 | Extract the value from the VAR="val" string | ||
82 | """ | ||
83 | if line.startswith(key + "="): | ||
84 | stripped_line = line.split('=')[1] | ||
85 | stripped_line = stripped_line.replace('\"', '') | ||
86 | return stripped_line | ||
87 | return None | ||
88 | |||
89 | |||
90 | def find_meta_layer(): | ||
91 | """ | ||
92 | Find and return the meta layer in BBLAYERS. | ||
93 | """ | ||
94 | layers = find_bblayers() | ||
95 | |||
96 | for layer in layers: | ||
97 | if layer.endswith("meta"): | ||
98 | return layer | ||
99 | |||
100 | return None | ||
101 | |||
102 | |||
103 | def find_bsp_layer(machine): | ||
104 | """ | ||
105 | Find and return a machine's BSP layer in BBLAYERS. | ||
106 | """ | ||
107 | layers = find_bblayers() | ||
108 | |||
109 | for layer in layers: | ||
110 | if layer.endswith(machine): | ||
111 | return layer | ||
112 | |||
113 | print "Unable to find the BSP layer for machine %s." % machine | ||
114 | print "Please make sure it is listed in bblayers.conf" | ||
115 | sys.exit(1) | ||
116 | |||
117 | |||
118 | def gen_choices_str(choices): | ||
119 | """ | ||
120 | Generate a numbered list of choices from a list of choices for | ||
121 | display to the user. | ||
122 | """ | ||
123 | choices_str = "" | ||
124 | |||
125 | for i, choice in enumerate(choices): | ||
126 | choices_str += "\t" + str(i + 1) + ") " + choice + "\n" | ||
127 | |||
128 | return choices_str | ||
129 | |||
130 | |||
131 | def open_user_file(scripts_path, machine, userfile, mode): | ||
132 | """ | ||
133 | Find one of the user files (user-config.cfg, user-patches.scc) | ||
134 | associated with the machine (could be in files/, | ||
135 | linux-yocto-custom/, etc). Returns the open file if found, None | ||
136 | otherwise. | ||
137 | |||
138 | The caller is responsible for closing the file returned. | ||
139 | """ | ||
140 | layer = find_bsp_layer(machine) | ||
141 | linuxdir = os.path.join(layer, "recipes-kernel/linux") | ||
142 | linuxdir_list = os.listdir(linuxdir) | ||
143 | for fileobj in linuxdir_list: | ||
144 | fileobj_path = os.path.join(linuxdir, fileobj) | ||
145 | if os.path.isdir(fileobj_path): | ||
146 | userfile_name = os.path.join(fileobj_path, userfile) | ||
147 | try: | ||
148 | f = open(userfile_name, mode) | ||
149 | return f | ||
150 | except IOError: | ||
151 | continue | ||
152 | return None | ||
153 | |||
154 | |||
155 | def read_config_items(scripts_path, machine): | ||
156 | """ | ||
157 | Find and return a list of config items (CONFIG_XXX) in a machine's | ||
158 | user-defined config fragment [${machine}-user-config.cfg]. | ||
159 | """ | ||
160 | config_items = [] | ||
161 | |||
162 | f = open_user_file(scripts_path, machine, machine+"-user-config.cfg", "r") | ||
163 | lines = f.readlines() | ||
164 | for line in lines: | ||
165 | s = line.strip() | ||
166 | if s and not s.startswith("#"): | ||
167 | config_items.append(s) | ||
168 | f.close() | ||
169 | |||
170 | return config_items | ||
171 | |||
172 | |||
173 | def write_config_items(scripts_path, machine, config_items): | ||
174 | """ | ||
175 | Write (replace) the list of config items (CONFIG_XXX) in a | ||
176 | machine's user-defined config fragment [${machine}=user-config.cfg]. | ||
177 | """ | ||
178 | f = open_user_file(scripts_path, machine, machine+"-user-config.cfg", "w") | ||
179 | for item in config_items: | ||
180 | f.write(item + "\n") | ||
181 | f.close() | ||
182 | |||
183 | kernel_contents_changed(scripts_path, machine) | ||
184 | |||
185 | |||
186 | def yocto_kernel_config_list(scripts_path, machine): | ||
187 | """ | ||
188 | Display the list of config items (CONFIG_XXX) in a machine's | ||
189 | user-defined config fragment [${machine}-user-config.cfg]. | ||
190 | """ | ||
191 | config_items = read_config_items(scripts_path, machine) | ||
192 | |||
193 | print "The current set of machine-specific kernel config items for %s is:" % machine | ||
194 | print gen_choices_str(config_items) | ||
195 | |||
196 | |||
197 | def yocto_kernel_config_rm(scripts_path, machine): | ||
198 | """ | ||
199 | Display the list of config items (CONFIG_XXX) in a machine's | ||
200 | user-defined config fragment [${machine}-user-config.cfg], prompt the user | ||
201 | for one or more to remove, and remove them. | ||
202 | """ | ||
203 | config_items = read_config_items(scripts_path, machine) | ||
204 | |||
205 | print "Specify the kernel config items to remove:" | ||
206 | input = raw_input(gen_choices_str(config_items)) | ||
207 | rm_choices = input.split() | ||
208 | rm_choices.sort() | ||
209 | |||
210 | removed = [] | ||
211 | |||
212 | for choice in reversed(rm_choices): | ||
213 | try: | ||
214 | idx = int(choice) - 1 | ||
215 | except ValueError: | ||
216 | print "Invalid choice (%s), exiting" % choice | ||
217 | sys.exit(1) | ||
218 | if idx < 0 or idx >= len(config_items): | ||
219 | print "Invalid choice (%d), exiting" % (idx + 1) | ||
220 | sys.exit(1) | ||
221 | removed.append(config_items.pop(idx)) | ||
222 | |||
223 | write_config_items(scripts_path, machine, config_items) | ||
224 | |||
225 | print "Removed items:" | ||
226 | for r in removed: | ||
227 | print "\t%s" % r | ||
228 | |||
229 | |||
230 | def yocto_kernel_config_add(scripts_path, machine, config_items): | ||
231 | """ | ||
232 | Add one or more config items (CONFIG_XXX) to a machine's | ||
233 | user-defined config fragment [${machine}-user-config.cfg]. | ||
234 | """ | ||
235 | new_items = [] | ||
236 | dup_items = [] | ||
237 | |||
238 | cur_items = read_config_items(scripts_path, machine) | ||
239 | |||
240 | for item in config_items: | ||
241 | if not item.startswith("CONFIG") or (not "=y" in item and not "=m" in item): | ||
242 | print "Invalid config item (%s), exiting" % item | ||
243 | sys.exit(1) | ||
244 | if item not in cur_items and item not in new_items: | ||
245 | new_items.append(item) | ||
246 | else: | ||
247 | dup_items.append(item) | ||
248 | |||
249 | if len(new_items) > 0: | ||
250 | cur_items.extend(new_items) | ||
251 | write_config_items(scripts_path, machine, cur_items) | ||
252 | print "Added item%s:" % ("" if len(new_items)==1 else "s") | ||
253 | for n in new_items: | ||
254 | print "\t%s" % n | ||
255 | |||
256 | if len(dup_items) > 0: | ||
257 | output="The following item%s already exist%s in the current configuration, ignoring %s:" % \ | ||
258 | (("","s", "it") if len(dup_items)==1 else ("s", "", "them" )) | ||
259 | print output | ||
260 | for n in dup_items: | ||
261 | print "\t%s" % n | ||
262 | |||
263 | def find_current_kernel(bsp_layer, machine): | ||
264 | """ | ||
265 | Determine the kernel and version currently being used in the BSP. | ||
266 | """ | ||
267 | machine_conf = os.path.join(bsp_layer, "conf/machine/" + machine + ".conf") | ||
268 | |||
269 | preferred_kernel = preferred_kernel_version = preferred_version_varname = None | ||
270 | |||
271 | f = open(machine_conf, "r") | ||
272 | lines = f.readlines() | ||
273 | for line in lines: | ||
274 | if line.strip().startswith("PREFERRED_PROVIDER_virtual/kernel"): | ||
275 | preferred_kernel = line.split()[-1] | ||
276 | preferred_kernel = preferred_kernel.replace('\"','') | ||
277 | preferred_version_varname = "PREFERRED_VERSION_" + preferred_kernel | ||
278 | if preferred_version_varname and line.strip().startswith(preferred_version_varname): | ||
279 | preferred_kernel_version = line.split()[-1] | ||
280 | preferred_kernel_version = preferred_kernel_version.replace('\"','') | ||
281 | preferred_kernel_version = preferred_kernel_version.replace('%','') | ||
282 | |||
283 | if preferred_kernel and preferred_kernel_version: | ||
284 | return preferred_kernel + "_" + preferred_kernel_version | ||
285 | elif preferred_kernel: | ||
286 | return preferred_kernel | ||
287 | |||
288 | |||
289 | def find_filesdir(scripts_path, machine): | ||
290 | """ | ||
291 | Find the name of the 'files' dir associated with the machine | ||
292 | (could be in files/, linux-yocto-custom/, etc). Returns the name | ||
293 | of the files dir if found, None otherwise. | ||
294 | """ | ||
295 | layer = find_bsp_layer(machine) | ||
296 | filesdir = None | ||
297 | linuxdir = os.path.join(layer, "recipes-kernel/linux") | ||
298 | linuxdir_list = os.listdir(linuxdir) | ||
299 | for fileobj in linuxdir_list: | ||
300 | fileobj_path = os.path.join(linuxdir, fileobj) | ||
301 | if os.path.isdir(fileobj_path): | ||
302 | # this could be files/ or linux-yocto-custom/, we have no way of distinguishing | ||
303 | # so we take the first (and normally only) dir we find as the 'filesdir' | ||
304 | filesdir = fileobj_path | ||
305 | |||
306 | return filesdir | ||
307 | |||
308 | |||
309 | def read_patch_items(scripts_path, machine): | ||
310 | """ | ||
311 | Find and return a list of patch items in a machine's user-defined | ||
312 | patch list [${machine}-user-patches.scc]. | ||
313 | """ | ||
314 | patch_items = [] | ||
315 | |||
316 | f = open_user_file(scripts_path, machine, machine+"-user-patches.scc", "r") | ||
317 | lines = f.readlines() | ||
318 | for line in lines: | ||
319 | s = line.strip() | ||
320 | if s and not s.startswith("#"): | ||
321 | fields = s.split() | ||
322 | if not fields[0] == "patch": | ||
323 | continue | ||
324 | patch_items.append(fields[1]) | ||
325 | f.close() | ||
326 | |||
327 | return patch_items | ||
328 | |||
329 | |||
330 | def write_patch_items(scripts_path, machine, patch_items): | ||
331 | """ | ||
332 | Write (replace) the list of patches in a machine's user-defined | ||
333 | patch list [${machine}-user-patches.scc]. | ||
334 | """ | ||
335 | f = open_user_file(scripts_path, machine, machine+"-user-patches.scc", "w") | ||
336 | for item in patch_items: | ||
337 | f.write("patch " + item + "\n") | ||
338 | f.close() | ||
339 | |||
340 | kernel_contents_changed(scripts_path, machine) | ||
341 | |||
342 | |||
343 | def yocto_kernel_patch_list(scripts_path, machine): | ||
344 | """ | ||
345 | Display the list of patches in a machine's user-defined patch list | ||
346 | [${machine}-user-patches.scc]. | ||
347 | """ | ||
348 | patches = read_patch_items(scripts_path, machine) | ||
349 | |||
350 | print "The current set of machine-specific patches for %s is:" % machine | ||
351 | print gen_choices_str(patches) | ||
352 | |||
353 | |||
354 | def yocto_kernel_patch_rm(scripts_path, machine): | ||
355 | """ | ||
356 | Remove one or more patches from a machine's user-defined patch | ||
357 | list [${machine}-user-patches.scc]. | ||
358 | """ | ||
359 | patches = read_patch_items(scripts_path, machine) | ||
360 | |||
361 | print "Specify the patches to remove:" | ||
362 | input = raw_input(gen_choices_str(patches)) | ||
363 | rm_choices = input.split() | ||
364 | rm_choices.sort() | ||
365 | |||
366 | removed = [] | ||
367 | |||
368 | filesdir = find_filesdir(scripts_path, machine) | ||
369 | if not filesdir: | ||
370 | print "Couldn't rm patch(es) since we couldn't find a 'files' dir" | ||
371 | sys.exit(1) | ||
372 | |||
373 | for choice in reversed(rm_choices): | ||
374 | try: | ||
375 | idx = int(choice) - 1 | ||
376 | except ValueError: | ||
377 | print "Invalid choice (%s), exiting" % choice | ||
378 | sys.exit(1) | ||
379 | if idx < 0 or idx >= len(patches): | ||
380 | print "Invalid choice (%d), exiting" % (idx + 1) | ||
381 | sys.exit(1) | ||
382 | filesdir_patch = os.path.join(filesdir, patches[idx]) | ||
383 | if os.path.isfile(filesdir_patch): | ||
384 | os.remove(filesdir_patch) | ||
385 | removed.append(patches[idx]) | ||
386 | patches.pop(idx) | ||
387 | |||
388 | write_patch_items(scripts_path, machine, patches) | ||
389 | |||
390 | print "Removed patches:" | ||
391 | for r in removed: | ||
392 | print "\t%s" % r | ||
393 | |||
394 | |||
395 | def yocto_kernel_patch_add(scripts_path, machine, patches): | ||
396 | """ | ||
397 | Add one or more patches to a machine's user-defined patch list | ||
398 | [${machine}-user-patches.scc]. | ||
399 | """ | ||
400 | existing_patches = read_patch_items(scripts_path, machine) | ||
401 | |||
402 | for patch in patches: | ||
403 | if os.path.basename(patch) in existing_patches: | ||
404 | print "Couldn't add patch (%s) since it's already been added" % os.path.basename(patch) | ||
405 | sys.exit(1) | ||
406 | |||
407 | filesdir = find_filesdir(scripts_path, machine) | ||
408 | if not filesdir: | ||
409 | print "Couldn't add patch (%s) since we couldn't find a 'files' dir to add it to" % os.path.basename(patch) | ||
410 | sys.exit(1) | ||
411 | |||
412 | new_patches = [] | ||
413 | |||
414 | for patch in patches: | ||
415 | if not os.path.isfile(patch): | ||
416 | print "Couldn't find patch (%s), exiting" % patch | ||
417 | sys.exit(1) | ||
418 | basename = os.path.basename(patch) | ||
419 | filesdir_patch = os.path.join(filesdir, basename) | ||
420 | shutil.copyfile(patch, filesdir_patch) | ||
421 | new_patches.append(basename) | ||
422 | |||
423 | cur_items = read_patch_items(scripts_path, machine) | ||
424 | cur_items.extend(new_patches) | ||
425 | write_patch_items(scripts_path, machine, cur_items) | ||
426 | |||
427 | print "Added patches:" | ||
428 | for n in new_patches: | ||
429 | print "\t%s" % n | ||
430 | |||
431 | |||
432 | def inc_pr(line): | ||
433 | """ | ||
434 | Add 1 to the PR value in the given bbappend PR line. For the PR | ||
435 | lines in kernel .bbappends after modifications. Handles PRs of | ||
436 | the form PR := "${PR}.1" as well as PR = "r0". | ||
437 | """ | ||
438 | idx = line.find("\"") | ||
439 | |||
440 | pr_str = line[idx:] | ||
441 | pr_str = pr_str.replace('\"','') | ||
442 | fields = pr_str.split('.') | ||
443 | if len(fields) > 1: | ||
444 | fields[1] = str(int(fields[1]) + 1) | ||
445 | pr_str = "\"" + '.'.join(fields) + "\"\n" | ||
446 | else: | ||
447 | pr_val = pr_str[1:] | ||
448 | pr_str = "\"" + "r" + str(int(pr_val) + 1) + "\"\n" | ||
449 | idx2 = line.find("\"", idx + 1) | ||
450 | line = line[:idx] + pr_str | ||
451 | |||
452 | return line | ||
453 | |||
454 | |||
455 | def kernel_contents_changed(scripts_path, machine): | ||
456 | """ | ||
457 | Do what we need to do to notify the system that the kernel | ||
458 | recipe's contents have changed. | ||
459 | """ | ||
460 | layer = find_bsp_layer(machine) | ||
461 | |||
462 | kernel = find_current_kernel(layer, machine) | ||
463 | if not kernel: | ||
464 | print "Couldn't determine the kernel for this BSP, exiting." | ||
465 | sys.exit(1) | ||
466 | |||
467 | kernel_bbfile = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bbappend") | ||
468 | if not os.path.isfile(kernel_bbfile): | ||
469 | kernel_bbfile = os.path.join(layer, "recipes-kernel/linux/" + kernel + ".bb") | ||
470 | if not os.path.isfile(kernel_bbfile): | ||
471 | return | ||
472 | kernel_bbfile_prev = kernel_bbfile + ".prev" | ||
473 | shutil.copyfile(kernel_bbfile, kernel_bbfile_prev) | ||
474 | |||
475 | ifile = open(kernel_bbfile_prev, "r") | ||
476 | ofile = open(kernel_bbfile, "w") | ||
477 | ifile_lines = ifile.readlines() | ||
478 | for ifile_line in ifile_lines: | ||
479 | if ifile_line.strip().startswith("PR"): | ||
480 | ifile_line = inc_pr(ifile_line) | ||
481 | ofile.write(ifile_line) | ||
482 | ofile.close() | ||
483 | ifile.close() | ||
484 | |||
485 | |||
486 | def kernels(context): | ||
487 | """ | ||
488 | Return the list of available kernels in the BSP i.e. corresponding | ||
489 | to the kernel .bbappends found in the layer. | ||
490 | """ | ||
491 | archdir = os.path.join(context["scripts_path"], "lib/bsp/substrate/target/arch/" + context["arch"]) | ||
492 | kerndir = os.path.join(archdir, "recipes-kernel/linux") | ||
493 | bbglob = os.path.join(kerndir, "*.bbappend") | ||
494 | |||
495 | bbappends = glob.glob(bbglob) | ||
496 | |||
497 | kernels = [] | ||
498 | |||
499 | for kernel in bbappends: | ||
500 | filename = os.path.splitext(os.path.basename(kernel))[0] | ||
501 | idx = filename.find(CLOSE_TAG) | ||
502 | if idx != -1: | ||
503 | filename = filename[idx + len(CLOSE_TAG):].strip() | ||
504 | kernels.append(filename) | ||
505 | |||
506 | kernels.append("custom") | ||
507 | |||
508 | return kernels | ||
509 | |||
510 | |||
511 | def extract_giturl(file): | ||
512 | """ | ||
513 | Extract the git url of the kernel repo from the kernel recipe's | ||
514 | SRC_URI. | ||
515 | """ | ||
516 | url = None | ||
517 | f = open(file, "r") | ||
518 | lines = f.readlines() | ||
519 | for line in lines: | ||
520 | line = line.strip() | ||
521 | if line.startswith("SRC_URI"): | ||
522 | line = line[len("SRC_URI"):].strip() | ||
523 | if line.startswith("="): | ||
524 | line = line[1:].strip() | ||
525 | if line.startswith("\""): | ||
526 | line = line[1:].strip() | ||
527 | prot = "git" | ||
528 | for s in line.split(";"): | ||
529 | if s.startswith("git://"): | ||
530 | url = s | ||
531 | if s.startswith("protocol="): | ||
532 | prot = s.split("=")[1] | ||
533 | if url: | ||
534 | url = prot + url[3:] | ||
535 | return url | ||
536 | |||
537 | |||
538 | def find_giturl(context): | ||
539 | """ | ||
540 | Find the git url of the kernel repo from the kernel recipe's | ||
541 | SRC_URI. | ||
542 | """ | ||
543 | name = context["name"] | ||
544 | filebase = context["filename"] | ||
545 | scripts_path = context["scripts_path"] | ||
546 | |||
547 | meta_layer = find_meta_layer() | ||
548 | |||
549 | kerndir = os.path.join(meta_layer, "recipes-kernel/linux") | ||
550 | bbglob = os.path.join(kerndir, "*.bb") | ||
551 | bbs = glob.glob(bbglob) | ||
552 | for kernel in bbs: | ||
553 | filename = os.path.splitext(os.path.basename(kernel))[0] | ||
554 | if filename in filebase: | ||
555 | giturl = extract_giturl(kernel) | ||
556 | return giturl | ||
557 | |||
558 | return None | ||
559 | |||
560 | |||
561 | def read_features(scripts_path, machine): | ||
562 | """ | ||
563 | Find and return a list of features in a machine's user-defined | ||
564 | features fragment [${machine}-user-features.scc]. | ||
565 | """ | ||
566 | features = [] | ||
567 | |||
568 | f = open_user_file(scripts_path, machine, machine+"-user-features.scc", "r") | ||
569 | lines = f.readlines() | ||
570 | for line in lines: | ||
571 | s = line.strip() | ||
572 | if s and not s.startswith("#"): | ||
573 | feature_include = s.split() | ||
574 | features.append(feature_include[1].strip()) | ||
575 | f.close() | ||
576 | |||
577 | return features | ||
578 | |||
579 | |||
580 | def write_features(scripts_path, machine, features): | ||
581 | """ | ||
582 | Write (replace) the list of feature items in a | ||
583 | machine's user-defined features fragment [${machine}=user-features.cfg]. | ||
584 | """ | ||
585 | f = open_user_file(scripts_path, machine, machine+"-user-features.scc", "w") | ||
586 | for item in features: | ||
587 | f.write("include " + item + "\n") | ||
588 | f.close() | ||
589 | |||
590 | kernel_contents_changed(scripts_path, machine) | ||
591 | |||
592 | |||
593 | def yocto_kernel_feature_list(scripts_path, machine): | ||
594 | """ | ||
595 | Display the list of features used in a machine's user-defined | ||
596 | features fragment [${machine}-user-features.scc]. | ||
597 | """ | ||
598 | features = read_features(scripts_path, machine) | ||
599 | |||
600 | print "The current set of machine-specific features for %s is:" % machine | ||
601 | print gen_choices_str(features) | ||
602 | |||
603 | |||
604 | def yocto_kernel_feature_rm(scripts_path, machine): | ||
605 | """ | ||
606 | Display the list of features used in a machine's user-defined | ||
607 | features fragment [${machine}-user-features.scc], prompt the user | ||
608 | for one or more to remove, and remove them. | ||
609 | """ | ||
610 | features = read_features(scripts_path, machine) | ||
611 | |||
612 | print "Specify the features to remove:" | ||
613 | input = raw_input(gen_choices_str(features)) | ||
614 | rm_choices = input.split() | ||
615 | rm_choices.sort() | ||
616 | |||
617 | removed = [] | ||
618 | |||
619 | for choice in reversed(rm_choices): | ||
620 | try: | ||
621 | idx = int(choice) - 1 | ||
622 | except ValueError: | ||
623 | print "Invalid choice (%s), exiting" % choice | ||
624 | sys.exit(1) | ||
625 | if idx < 0 or idx >= len(features): | ||
626 | print "Invalid choice (%d), exiting" % (idx + 1) | ||
627 | sys.exit(1) | ||
628 | removed.append(features.pop(idx)) | ||
629 | |||
630 | write_features(scripts_path, machine, features) | ||
631 | |||
632 | print "Removed features:" | ||
633 | for r in removed: | ||
634 | print "\t%s" % r | ||
635 | |||
636 | |||
637 | def yocto_kernel_feature_add(scripts_path, machine, features): | ||
638 | """ | ||
639 | Add one or more features a machine's user-defined features | ||
640 | fragment [${machine}-user-features.scc]. | ||
641 | """ | ||
642 | new_items = [] | ||
643 | |||
644 | for item in features: | ||
645 | if not item.endswith(".scc"): | ||
646 | print "Invalid feature (%s), exiting" % item | ||
647 | sys.exit(1) | ||
648 | new_items.append(item) | ||
649 | |||
650 | cur_items = read_features(scripts_path, machine) | ||
651 | cur_items.extend(new_items) | ||
652 | |||
653 | write_features(scripts_path, machine, cur_items) | ||
654 | |||
655 | print "Added features:" | ||
656 | for n in new_items: | ||
657 | print "\t%s" % n | ||
658 | |||
659 | |||
660 | def find_feature_url(git_url): | ||
661 | """ | ||
662 | Find the url of the kern-features.rc kernel for the kernel repo | ||
663 | specified from the BSP's kernel recipe SRC_URI. | ||
664 | """ | ||
665 | feature_url = "" | ||
666 | if git_url.startswith("git://"): | ||
667 | git_url = git_url[len("git://"):].strip() | ||
668 | s = git_url.split("/") | ||
669 | if s[1].endswith(".git"): | ||
670 | s[1] = s[1][:len(s[1]) - len(".git")] | ||
671 | feature_url = "http://" + s[0] + "/cgit/cgit.cgi/" + s[1] + \ | ||
672 | "/plain/meta/cfg/kern-features.rc?h=meta" | ||
673 | |||
674 | return feature_url | ||
675 | |||
676 | |||
677 | def find_feature_desc(lines): | ||
678 | """ | ||
679 | Find the feature description and compatibility in the passed-in | ||
680 | set of lines. Returns a string string of the form 'desc | ||
681 | [compat]'. | ||
682 | """ | ||
683 | desc = "no description available" | ||
684 | compat = "unknown" | ||
685 | |||
686 | for line in lines: | ||
687 | idx = line.find("KFEATURE_DESCRIPTION") | ||
688 | if idx != -1: | ||
689 | desc = line[idx + len("KFEATURE_DESCRIPTION"):].strip() | ||
690 | if desc.startswith("\""): | ||
691 | desc = desc[1:] | ||
692 | if desc.endswith("\""): | ||
693 | desc = desc[:-1] | ||
694 | else: | ||
695 | idx = line.find("KFEATURE_COMPATIBILITY") | ||
696 | if idx != -1: | ||
697 | compat = line[idx + len("KFEATURE_COMPATIBILITY"):].strip() | ||
698 | |||
699 | return desc + " [" + compat + "]" | ||
700 | |||
701 | |||
702 | def print_feature_descs(layer, feature_dir): | ||
703 | """ | ||
704 | Print the feature descriptions for the features in feature_dir. | ||
705 | """ | ||
706 | kernel_files_features = os.path.join(layer, "recipes-kernel/linux/files/" + | ||
707 | feature_dir) | ||
708 | for root, dirs, files in os.walk(kernel_files_features): | ||
709 | for file in files: | ||
710 | if file.endswith("~") or file.endswith("#"): | ||
711 | continue | ||
712 | if file.endswith(".scc"): | ||
713 | fullpath = os.path.join(layer, "recipes-kernel/linux/files/" + | ||
714 | feature_dir + "/" + file) | ||
715 | f = open(fullpath) | ||
716 | feature_desc = find_feature_desc(f.readlines()) | ||
717 | print feature_dir + "/" + file + ": " + feature_desc | ||
718 | |||
719 | |||
720 | def yocto_kernel_available_features_list(scripts_path, machine): | ||
721 | """ | ||
722 | Display the list of all the kernel features available for use in | ||
723 | BSPs, as gathered from the set of feature sources. | ||
724 | """ | ||
725 | layer = find_bsp_layer(machine) | ||
726 | kernel = find_current_kernel(layer, machine) | ||
727 | if not kernel: | ||
728 | print "Couldn't determine the kernel for this BSP, exiting." | ||
729 | sys.exit(1) | ||
730 | |||
731 | context = create_context(machine, "arch", scripts_path) | ||
732 | context["name"] = "name" | ||
733 | context["filename"] = kernel | ||
734 | giturl = find_giturl(context) | ||
735 | feature_url = find_feature_url(giturl) | ||
736 | |||
737 | feature_cmd = "wget -q -O - " + feature_url | ||
738 | tmp = subprocess.Popen(feature_cmd, shell=True, stdout=subprocess.PIPE).stdout.read() | ||
739 | |||
740 | print "The current set of kernel features available to %s is:\n" % machine | ||
741 | |||
742 | if tmp: | ||
743 | tmpline = tmp.split("\n") | ||
744 | in_kernel_options = False | ||
745 | for line in tmpline: | ||
746 | if not "=" in line: | ||
747 | if in_kernel_options: | ||
748 | break | ||
749 | if "kernel-options" in line: | ||
750 | in_kernel_options = True | ||
751 | continue | ||
752 | if in_kernel_options: | ||
753 | feature_def = line.split("=") | ||
754 | feature_type = feature_def[0].strip() | ||
755 | feature = feature_def[1].strip() | ||
756 | desc = get_feature_desc(giturl, feature) | ||
757 | print "%s: %s" % (feature, desc) | ||
758 | |||
759 | print "[local]" | ||
760 | |||
761 | print_feature_descs(layer, "cfg") | ||
762 | print_feature_descs(layer, "features") | ||
763 | |||
764 | |||
765 | def find_feature_desc_url(git_url, feature): | ||
766 | """ | ||
767 | Find the url of the kernel feature in the kernel repo specified | ||
768 | from the BSP's kernel recipe SRC_URI. | ||
769 | """ | ||
770 | feature_desc_url = "" | ||
771 | if git_url.startswith("git://"): | ||
772 | git_url = git_url[len("git://"):].strip() | ||
773 | s = git_url.split("/") | ||
774 | if s[1].endswith(".git"): | ||
775 | s[1] = s[1][:len(s[1]) - len(".git")] | ||
776 | feature_desc_url = "http://" + s[0] + "/cgit/cgit.cgi/" + s[1] + \ | ||
777 | "/plain/meta/cfg/kernel-cache/" + feature + "?h=meta" | ||
778 | |||
779 | return feature_desc_url | ||
780 | |||
781 | |||
782 | def get_feature_desc(git_url, feature): | ||
783 | """ | ||
784 | Return a feature description of the form 'description [compatibility] | ||
785 | BSPs, as gathered from the set of feature sources. | ||
786 | """ | ||
787 | feature_desc_url = find_feature_desc_url(git_url, feature) | ||
788 | feature_desc_cmd = "wget -q -O - " + feature_desc_url | ||
789 | tmp = subprocess.Popen(feature_desc_cmd, shell=True, stdout=subprocess.PIPE).stdout.read() | ||
790 | |||
791 | return find_feature_desc(tmp.split("\n")) | ||
792 | |||
793 | |||
794 | def yocto_kernel_feature_describe(scripts_path, machine, feature): | ||
795 | """ | ||
796 | Display the description of a specific kernel feature available for | ||
797 | use in a BSP. | ||
798 | """ | ||
799 | layer = find_bsp_layer(machine) | ||
800 | |||
801 | kernel = find_current_kernel(layer, machine) | ||
802 | if not kernel: | ||
803 | print "Couldn't determine the kernel for this BSP, exiting." | ||
804 | sys.exit(1) | ||
805 | |||
806 | context = create_context(machine, "arch", scripts_path) | ||
807 | context["name"] = "name" | ||
808 | context["filename"] = kernel | ||
809 | giturl = find_giturl(context) | ||
810 | |||
811 | desc = get_feature_desc(giturl, feature) | ||
812 | |||
813 | print desc | ||
814 | |||
815 | |||
816 | def check_feature_name(feature_name): | ||
817 | """ | ||
818 | Sanity-check the feature name for create/destroy. Return False if not OK. | ||
819 | """ | ||
820 | if not feature_name.endswith(".scc"): | ||
821 | print "Invalid feature name (must end with .scc) [%s], exiting" % feature_name | ||
822 | return False | ||
823 | |||
824 | if "/" in feature_name: | ||
825 | print "Invalid feature name (don't specify directory) [%s], exiting" % feature_name | ||
826 | return False | ||
827 | |||
828 | return True | ||
829 | |||
830 | |||
831 | def check_create_input(feature_items): | ||
832 | """ | ||
833 | Sanity-check the create input. Return False if not OK. | ||
834 | """ | ||
835 | if not check_feature_name(feature_items[0]): | ||
836 | return False | ||
837 | |||
838 | if feature_items[1].endswith(".patch") or feature_items[1].startswith("CONFIG_"): | ||
839 | print "Missing description and/or compatibilty [%s], exiting" % feature_items[1] | ||
840 | return False | ||
841 | |||
842 | if feature_items[2].endswith(".patch") or feature_items[2].startswith("CONFIG_"): | ||
843 | print "Missing description and/or compatibility [%s], exiting" % feature_items[1] | ||
844 | return False | ||
845 | |||
846 | return True | ||
847 | |||
848 | |||
849 | def yocto_kernel_feature_create(scripts_path, machine, feature_items): | ||
850 | """ | ||
851 | Create a recipe-space kernel feature in a BSP. | ||
852 | """ | ||
853 | if not check_create_input(feature_items): | ||
854 | sys.exit(1) | ||
855 | |||
856 | feature = feature_items[0] | ||
857 | feature_basename = feature.split(".")[0] | ||
858 | feature_description = feature_items[1] | ||
859 | feature_compat = feature_items[2] | ||
860 | |||
861 | patches = [] | ||
862 | cfg_items = [] | ||
863 | |||
864 | for item in feature_items[3:]: | ||
865 | if item.endswith(".patch"): | ||
866 | patches.append(item) | ||
867 | elif item.startswith("CONFIG"): | ||
868 | if ("=y" in item or "=m" in item): | ||
869 | cfg_items.append(item) | ||
870 | else: | ||
871 | print "Invalid feature item (must be .patch or CONFIG_*) [%s], exiting" % item | ||
872 | sys.exit(1) | ||
873 | |||
874 | feature_dirname = "cfg" | ||
875 | if patches: | ||
876 | feature_dirname = "features" | ||
877 | |||
878 | filesdir = find_filesdir(scripts_path, machine) | ||
879 | if not filesdir: | ||
880 | print "Couldn't add feature (%s), no 'files' dir found" % feature | ||
881 | sys.exit(1) | ||
882 | |||
883 | featdir = os.path.join(filesdir, feature_dirname) | ||
884 | if not os.path.exists(featdir): | ||
885 | os.mkdir(featdir) | ||
886 | |||
887 | for patch in patches: | ||
888 | if not os.path.isfile(patch): | ||
889 | print "Couldn't find patch (%s), exiting" % patch | ||
890 | sys.exit(1) | ||
891 | basename = os.path.basename(patch) | ||
892 | featdir_patch = os.path.join(featdir, basename) | ||
893 | shutil.copyfile(patch, featdir_patch) | ||
894 | |||
895 | new_cfg_filename = os.path.join(featdir, feature_basename + ".cfg") | ||
896 | new_cfg_file = open(new_cfg_filename, "w") | ||
897 | for cfg_item in cfg_items: | ||
898 | new_cfg_file.write(cfg_item + "\n") | ||
899 | new_cfg_file.close() | ||
900 | |||
901 | new_feature_filename = os.path.join(featdir, feature_basename + ".scc") | ||
902 | new_feature_file = open(new_feature_filename, "w") | ||
903 | new_feature_file.write("define KFEATURE_DESCRIPTION \"" + feature_description + "\"\n") | ||
904 | new_feature_file.write("define KFEATURE_COMPATIBILITY " + feature_compat + "\n\n") | ||
905 | |||
906 | for patch in patches: | ||
907 | patch_dir, patch_file = os.path.split(patch) | ||
908 | new_feature_file.write("patch " + patch_file + "\n") | ||
909 | |||
910 | new_feature_file.write("kconf non-hardware " + feature_basename + ".cfg\n") | ||
911 | new_feature_file.close() | ||
912 | |||
913 | print "Added feature:" | ||
914 | print "\t%s" % feature_dirname + "/" + feature | ||
915 | |||
916 | |||
917 | def feature_in_use(scripts_path, machine, feature): | ||
918 | """ | ||
919 | Determine whether the specified feature is in use by the BSP. | ||
920 | Return True if so, False otherwise. | ||
921 | """ | ||
922 | features = read_features(scripts_path, machine) | ||
923 | for f in features: | ||
924 | if f == feature: | ||
925 | return True | ||
926 | return False | ||
927 | |||
928 | |||
929 | def feature_remove(scripts_path, machine, feature): | ||
930 | """ | ||
931 | Remove the specified feature from the available recipe-space | ||
932 | features defined for the BSP. | ||
933 | """ | ||
934 | features = read_features(scripts_path, machine) | ||
935 | new_features = [] | ||
936 | for f in features: | ||
937 | if f == feature: | ||
938 | continue | ||
939 | new_features.append(f) | ||
940 | write_features(scripts_path, machine, new_features) | ||
941 | |||
942 | |||
943 | def yocto_kernel_feature_destroy(scripts_path, machine, feature): | ||
944 | """ | ||
945 | Remove a recipe-space kernel feature from a BSP. | ||
946 | """ | ||
947 | if not check_feature_name(feature): | ||
948 | sys.exit(1) | ||
949 | |||
950 | if feature_in_use(scripts_path, machine, "features/" + feature) or \ | ||
951 | feature_in_use(scripts_path, machine, "cfg/" + feature): | ||
952 | print "Feature %s is in use (use 'feature rm' to un-use it first), exiting" % feature | ||
953 | sys.exit(1) | ||
954 | |||
955 | filesdir = find_filesdir(scripts_path, machine) | ||
956 | if not filesdir: | ||
957 | print "Couldn't destroy feature (%s), no 'files' dir found" % feature | ||
958 | sys.exit(1) | ||
959 | |||
960 | feature_dirname = "features" | ||
961 | featdir = os.path.join(filesdir, feature_dirname) | ||
962 | if not os.path.exists(featdir): | ||
963 | print "Couldn't find feature directory (%s)" % feature_dirname | ||
964 | sys.exit(1) | ||
965 | |||
966 | feature_fqn = os.path.join(featdir, feature) | ||
967 | if not os.path.exists(feature_fqn): | ||
968 | feature_dirname = "cfg" | ||
969 | featdir = os.path.join(filesdir, feature_dirname) | ||
970 | if not os.path.exists(featdir): | ||
971 | print "Couldn't find feature directory (%s)" % feature_dirname | ||
972 | sys.exit(1) | ||
973 | feature_fqn = os.path.join(featdir, feature_filename) | ||
974 | if not os.path.exists(feature_fqn): | ||
975 | print "Couldn't find feature (%s)" % feature | ||
976 | sys.exit(1) | ||
977 | |||
978 | f = open(feature_fqn, "r") | ||
979 | lines = f.readlines() | ||
980 | for line in lines: | ||
981 | s = line.strip() | ||
982 | if s.startswith("patch ") or s.startswith("kconf "): | ||
983 | split_line = s.split() | ||
984 | filename = os.path.join(featdir, split_line[-1]) | ||
985 | if os.path.exists(filename): | ||
986 | os.remove(filename) | ||
987 | f.close() | ||
988 | os.remove(feature_fqn) | ||
989 | |||
990 | feature_remove(scripts_path, machine, feature) | ||
991 | |||
992 | print "Removed feature:" | ||
993 | print "\t%s" % feature_dirname + "/" + feature | ||
994 | |||
995 | |||
996 | def base_branches(context): | ||
997 | """ | ||
998 | Return a list of the base branches found in the kernel git repo. | ||
999 | """ | ||
1000 | giturl = find_giturl(context) | ||
1001 | |||
1002 | print "Getting branches from remote repo %s..." % giturl | ||
1003 | |||
1004 | gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl) | ||
1005 | tmp = subprocess.Popen(gitcmd, shell=True, stdout=subprocess.PIPE).stdout.read() | ||
1006 | |||
1007 | branches = [] | ||
1008 | |||
1009 | if tmp: | ||
1010 | tmpline = tmp.split("\n") | ||
1011 | for line in tmpline: | ||
1012 | if len(line)==0: | ||
1013 | break; | ||
1014 | if not line.endswith("base"): | ||
1015 | continue; | ||
1016 | idx = line.find("refs/heads/") | ||
1017 | kbranch = line[idx + len("refs/heads/"):] | ||
1018 | if kbranch.find("/") == -1 and kbranch.find("base") == -1: | ||
1019 | continue | ||
1020 | idx = kbranch.find("base") | ||
1021 | branches.append(kbranch[:idx - 1]) | ||
1022 | |||
1023 | return branches | ||
1024 | |||
1025 | |||
1026 | def all_branches(context): | ||
1027 | """ | ||
1028 | Return a list of all the branches found in the kernel git repo. | ||
1029 | """ | ||
1030 | giturl = find_giturl(context) | ||
1031 | |||
1032 | print "Getting branches from remote repo %s..." % giturl | ||
1033 | |||
1034 | gitcmd = "git ls-remote %s *heads* 2>&1" % (giturl) | ||
1035 | tmp = subprocess.Popen(gitcmd, shell=True, stdout=subprocess.PIPE).stdout.read() | ||
1036 | |||
1037 | branches = [] | ||
1038 | |||
1039 | base_prefixes = None | ||
1040 | |||
1041 | try: | ||
1042 | branches_base = context["branches_base"] | ||
1043 | if branches_base: | ||
1044 | base_prefixes = branches_base.split(":") | ||
1045 | except KeyError: | ||
1046 | pass | ||
1047 | |||
1048 | arch = context["arch"] | ||
1049 | |||
1050 | if tmp: | ||
1051 | tmpline = tmp.split("\n") | ||
1052 | for line in tmpline: | ||
1053 | if len(line)==0: | ||
1054 | break; | ||
1055 | idx = line.find("refs/heads/") | ||
1056 | kbranch = line[idx + len("refs/heads/"):] | ||
1057 | kbranch_prefix = kbranch.rsplit("/", 1)[0] | ||
1058 | |||
1059 | if base_prefixes: | ||
1060 | for base_prefix in base_prefixes: | ||
1061 | if kbranch_prefix == base_prefix: | ||
1062 | branches.append(kbranch) | ||
1063 | continue | ||
1064 | |||
1065 | if (kbranch.find("/") != -1 and | ||
1066 | (kbranch.find("standard") != -1 or kbranch.find("base") != -1) or | ||
1067 | kbranch == "base"): | ||
1068 | branches.append(kbranch) | ||
1069 | continue | ||
1070 | |||
1071 | return branches | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/.gitignore b/scripts/lib/bsp/substrate/target/arch/arm/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/.gitignore | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/arm/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..44a80d226c --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/conf/machine/{{=machine}}.conf | |||
@@ -0,0 +1,105 @@ | |||
1 | #@TYPE: Machine | ||
2 | #@NAME: {{=machine}} | ||
3 | |||
4 | #@DESCRIPTION: Machine configuration for {{=machine}} systems | ||
5 | |||
6 | {{ input type:"boolean" name:"xserver" prio:"50" msg:"Do you need support for X? (y/n)" default:"y" }} | ||
7 | {{ if xserver == "y": }} | ||
8 | PREFERRED_PROVIDER_virtual/xserver ?= "xserver-xorg" | ||
9 | XSERVER ?= "xserver-xorg \ | ||
10 | xf86-input-evdev \ | ||
11 | xf86-input-mouse \ | ||
12 | xf86-video-omapfb \ | ||
13 | xf86-input-keyboard" | ||
14 | |||
15 | # Ship all kernel modules by default | ||
16 | MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" | ||
17 | |||
18 | # Allow for MMC booting (required by the NAND-less Beagleboard XM) | ||
19 | EXTRA_IMAGEDEPENDS += "u-boot" | ||
20 | |||
21 | # Uncomment the following line to enable the hard floating point abi. Note that | ||
22 | # this breaks some binary libraries and 3D (neither of which ship with | ||
23 | # meta-yocto). For maximum compatibility, leave this disabled. | ||
24 | #DEFAULTTUNE ?= "cortexa8hf-neon" | ||
25 | {{ input type:"choicelist" name:"tunefile" prio:"40" msg:"Which machine tuning would you like to use?" default:"tune_cortexa8" }} | ||
26 | {{ input type:"choice" val:"tune_arm1136jf_s" msg:"arm1136jf-s tuning optimizations" }} | ||
27 | {{ input type:"choice" val:"tune_arm920t" msg:"arm920t tuning optimizations" }} | ||
28 | {{ input type:"choice" val:"tune_arm926ejs" msg:"arm926ejs tuning optimizations" }} | ||
29 | {{ input type:"choice" val:"tune_arm9tdmi" msg:"arm9tdmi tuning optimizations" }} | ||
30 | {{ input type:"choice" val:"tune_cortexa5" msg:"cortexa5 tuning optimizations" }} | ||
31 | {{ input type:"choice" val:"tune_cortexa7" msg:"cortexa7 tuning optimizations" }} | ||
32 | {{ input type:"choice" val:"tune_cortexa8" msg:"cortexa8 tuning optimizations" }} | ||
33 | {{ input type:"choice" val:"tune_cortexa9" msg:"cortexa9 tuning optimizations" }} | ||
34 | {{ input type:"choice" val:"tune_cortexa15" msg:"cortexa15 tuning optimizations" }} | ||
35 | {{ input type:"choice" val:"tune_cortexm1" msg:"cortexm1 tuning optimizations" }} | ||
36 | {{ input type:"choice" val:"tune_cortexm3" msg:"cortexm3 tuning optimizations" }} | ||
37 | {{ input type:"choice" val:"tune_cortexr4" msg:"cortexr4 tuning optimizations" }} | ||
38 | {{ input type:"choice" val:"tune_ep9312" msg:"ep9312 tuning optimizations" }} | ||
39 | {{ input type:"choice" val:"tune_iwmmxt" msg:"iwmmxt tuning optimizations" }} | ||
40 | {{ input type:"choice" val:"tune_strongarm1100" msg:"strongarm1100 tuning optimizations" }} | ||
41 | {{ input type:"choice" val:"tune_xscale" msg:"xscale tuning optimizations" }} | ||
42 | {{ if tunefile == "tune_arm1136jf_s": }} | ||
43 | include conf/machine/include/tune-arm1136jf-s.inc | ||
44 | {{ if tunefile == "tune_arm920t": }} | ||
45 | include conf/machine/include/tune-arm920t.inc | ||
46 | {{ if tunefile == "tune_arm926ejs": }} | ||
47 | include conf/machine/include/tune-arm926ejs.inc | ||
48 | {{ if tunefile == "tune_arm9tdmi": }} | ||
49 | include conf/machine/include/tune-arm9tdmi.inc | ||
50 | {{ if tunefile == "tune_cortexa5": }} | ||
51 | include conf/machine/include/tune-cortexa5.inc | ||
52 | {{ if tunefile == "tune_cortexa7": }} | ||
53 | include conf/machine/include/tune-cortexa7.inc | ||
54 | {{ if tunefile == "tune_cortexa8": }} | ||
55 | include conf/machine/include/tune-cortexa8.inc | ||
56 | {{ if tunefile == "tune_cortexa9": }} | ||
57 | include conf/machine/include/tune-cortexa9.inc | ||
58 | {{ if tunefile == "tune_cortexa15": }} | ||
59 | include conf/machine/include/tune-cortexa15.inc | ||
60 | {{ if tunefile == "tune_cortexm1": }} | ||
61 | include conf/machine/include/tune-cortexm1.inc | ||
62 | {{ if tunefile == "tune_cortexm3": }} | ||
63 | include conf/machine/include/tune-cortexm3.inc | ||
64 | {{ if tunefile == "tune_cortexr4": }} | ||
65 | include conf/machine/include/tune-cortexr4.inc | ||
66 | {{ if tunefile == "tune_ep9312": }} | ||
67 | include conf/machine/include/tune-ep9312.inc | ||
68 | {{ if tunefile == "tune_iwmmxt": }} | ||
69 | include conf/machine/include/tune-iwmmxt.inc | ||
70 | {{ if tunefile == "tune_strongarm1100": }} | ||
71 | include conf/machine/include/tune-strongarm1100.inc | ||
72 | {{ if tunefile == "tune_xscale": }} | ||
73 | include conf/machine/include/tune-xscale.inc | ||
74 | |||
75 | IMAGE_FSTYPES += "tar.bz2 jffs2" | ||
76 | EXTRA_IMAGECMD_jffs2 = "-lnp " | ||
77 | |||
78 | # 2.6.37 and later kernels use OMAP_SERIAL, ttyO2 | ||
79 | # earlier kernels use ttyS2 | ||
80 | SERIAL_CONSOLE = "115200 ttyO2" | ||
81 | |||
82 | {{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} | ||
83 | {{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} | ||
84 | {{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} | ||
85 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
86 | |||
87 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} | ||
88 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} | ||
89 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} | ||
90 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
91 | PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" | ||
92 | |||
93 | KERNEL_IMAGETYPE = "zImage" | ||
94 | KERNEL_DEVICETREE = "${S}/arch/arm/boot/dts/omap3-beagle.dts ${S}/arch/arm/boot/dts/omap3-beagle-xm.dts" | ||
95 | |||
96 | SPL_BINARY = "MLO" | ||
97 | UBOOT_SUFFIX = "img" | ||
98 | {{ input type:"edit" name:"uboot_machine" prio:"40" msg:"Please specify a value for UBOOT_MACHINE:" default:"omap3_beagle_config" }} | ||
99 | UBOOT_MACHINE = "{{=uboot_machine}}" | ||
100 | {{ input type:"edit" name:"uboot_entrypoint" prio:"40" msg:"Please specify a value for UBOOT_ENTRYPOINT:" default:"0x80008000" }} | ||
101 | UBOOT_ENTRYPOINT = "{{=uboot_entrypoint}}" | ||
102 | {{ input type:"edit" name:"uboot_loadaddress" prio:"40" msg:"Please specify a value for UBOOT_LOADADDRESS:" default:"0x80008000" }} | ||
103 | UBOOT_LOADADDRESS = "{{=uboot_loadaddress}}" | ||
104 | |||
105 | MACHINE_FEATURES = "usbgadget usbhost vfat alsa" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == "y": }} xorg.conf b/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == "y": }} xorg.conf new file mode 100644 index 0000000000..264f3c91ad --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == "y": }} xorg.conf | |||
@@ -0,0 +1,33 @@ | |||
1 | Section "Module" | ||
2 | Load "extmod" | ||
3 | Load "dbe" | ||
4 | Load "glx" | ||
5 | Load "freetype" | ||
6 | Load "type1" | ||
7 | Load "record" | ||
8 | Load "dri" | ||
9 | EndSection | ||
10 | |||
11 | Section "Monitor" | ||
12 | Identifier "Builtin Default Monitor" | ||
13 | EndSection | ||
14 | |||
15 | Section "Device" | ||
16 | Identifier "Builtin Default fbdev Device 0" | ||
17 | Driver "omapfb" | ||
18 | EndSection | ||
19 | |||
20 | Section "Screen" | ||
21 | Identifier "Builtin Default fbdev Screen 0" | ||
22 | Device "Builtin Default fbdev Device 0" | ||
23 | Monitor "Builtin Default Monitor" | ||
24 | EndSection | ||
25 | |||
26 | Section "ServerLayout" | ||
27 | Identifier "Builtin Default Layout" | ||
28 | Screen "Builtin Default fbdev Screen 0" | ||
29 | EndSection | ||
30 | |||
31 | Section "ServerFlags" | ||
32 | Option "DontZap" "0" | ||
33 | EndSection | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/{{ if xserver == "y": }} xserver-xf86-config_0.1.bbappend b/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/{{ if xserver == "y": }} xserver-xf86-config_0.1.bbappend new file mode 100644 index 0000000000..72d991c7e5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-graphics/xorg-xserver/{{ if xserver == "y": }} xserver-xf86-config_0.1.bbappend | |||
@@ -0,0 +1 @@ | |||
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/kernel-list.noinstall | |||
@@ -0,0 +1,5 @@ | |||
1 | {{ if kernel_choice != "custom": }} | ||
2 | {{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} | ||
3 | |||
4 | {{ if kernel_choice != "custom" and use_default_kernel == "n": }} | ||
5 | {{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-non_hardware.cfg b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-non_hardware.cfg new file mode 100644 index 0000000000..361343bb58 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-non_hardware.cfg | |||
@@ -0,0 +1,30 @@ | |||
1 | # | ||
2 | # Miscellaneous filesystems | ||
3 | # | ||
4 | CONFIG_NFS_DEF_FILE_IO_SIZE=1024 | ||
5 | |||
6 | # | ||
7 | # Multiple Device Support | ||
8 | # | ||
9 | # CONFIG_MD is not set | ||
10 | |||
11 | # Kernel Features | ||
12 | # | ||
13 | CONFIG_NO_HZ=y | ||
14 | |||
15 | # | ||
16 | # CPUIdle | ||
17 | # | ||
18 | CONFIG_CPU_IDLE=y | ||
19 | CONFIG_CPU_IDLE_GOV_LADDER=y | ||
20 | CONFIG_CPU_IDLE_GOV_MENU=y | ||
21 | |||
22 | # | ||
23 | # Kernel hacking | ||
24 | # | ||
25 | CONFIG_DEBUG_FS=y | ||
26 | |||
27 | # | ||
28 | # Power management options | ||
29 | # | ||
30 | CONFIG_PM_DEBUG=y | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc new file mode 100644 index 0000000000..56f7f0f1e3 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc | |||
@@ -0,0 +1,13 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE preempt-rt | ||
3 | define KARCH arm | ||
4 | |||
5 | include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
10 | |||
11 | # default policy for preempt-rt kernels | ||
12 | include features/latencytop/latencytop.scc | ||
13 | include features/profiling/profiling.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc new file mode 100644 index 0000000000..80640db4a2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc | |||
@@ -0,0 +1,13 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE standard | ||
3 | define KARCH arm | ||
4 | |||
5 | include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
10 | |||
11 | # default policy for standard kernels | ||
12 | include features/latencytop/latencytop.scc | ||
13 | include features/profiling/profiling.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc new file mode 100644 index 0000000000..51eaf2d32c --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE tiny | ||
3 | define KARCH arm | ||
4 | |||
5 | include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg new file mode 100644 index 0000000000..10134c81f5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg | |||
@@ -0,0 +1,320 @@ | |||
1 | # | ||
2 | # System Type | ||
3 | # | ||
4 | CONFIG_ARCH_OMAP=y | ||
5 | |||
6 | # | ||
7 | # TI OMAP Implementations | ||
8 | # | ||
9 | # CONFIG_ARCH_OMAP2 is not set | ||
10 | CONFIG_ARCH_OMAP3=y | ||
11 | |||
12 | # | ||
13 | # TI OMAP Common Features | ||
14 | # | ||
15 | CONFIG_ARCH_OMAP2PLUS=y | ||
16 | |||
17 | # | ||
18 | # OMAP Feature Selections | ||
19 | # | ||
20 | CONFIG_OMAP_32K_TIMER=y | ||
21 | CONFIG_OMAP_32K_TIMER_HZ=128 | ||
22 | CONFIG_OMAP_DM_TIMER=y | ||
23 | CONFIG_OMAP_RESET_CLOCKS=y | ||
24 | CONFIG_OMAP_SMARTREFLEX=y | ||
25 | CONFIG_OMAP_SMARTREFLEX_CLASS3=y | ||
26 | CONFIG_OMAP_MBOX_FWK=m | ||
27 | CONFIG_OMAP_MBOX_KFIFO_SIZE=256 | ||
28 | |||
29 | # | ||
30 | # OMAP Board Type | ||
31 | # | ||
32 | CONFIG_MACH_OMAP3_BEAGLE=y | ||
33 | |||
34 | # | ||
35 | # Processor Features | ||
36 | # | ||
37 | CONFIG_ARM_THUMBEE=y | ||
38 | CONFIG_ARM_ERRATA_430973=y | ||
39 | |||
40 | # | ||
41 | # Kernel Features | ||
42 | # | ||
43 | CONFIG_LEDS=y | ||
44 | |||
45 | |||
46 | # | ||
47 | # Serial drivers | ||
48 | # | ||
49 | CONFIG_SERIAL_OMAP=y | ||
50 | CONFIG_SERIAL_OMAP_CONSOLE=y | ||
51 | |||
52 | # | ||
53 | # At least one emulation must be selected | ||
54 | # | ||
55 | CONFIG_VFP=y | ||
56 | CONFIG_NEON=y | ||
57 | |||
58 | # | ||
59 | # Power management options | ||
60 | # | ||
61 | CONFIG_PM=y | ||
62 | CONFIG_PM_RUNTIME=y | ||
63 | |||
64 | # | ||
65 | # Generic Driver Options | ||
66 | # | ||
67 | CONFIG_MTD=y | ||
68 | CONFIG_MTD_CMDLINE_PARTS=y | ||
69 | # | ||
70 | # User Modules And Translation Layers | ||
71 | # | ||
72 | CONFIG_MTD_BLKDEVS=y | ||
73 | CONFIG_MTD_BLOCK=y | ||
74 | |||
75 | # | ||
76 | # RAM/ROM/Flash chip drivers | ||
77 | # | ||
78 | CONFIG_MTD_CFI=y | ||
79 | CONFIG_MTD_CFI_INTELEXT=y | ||
80 | |||
81 | # | ||
82 | # Disk-On-Chip Device Drivers | ||
83 | # | ||
84 | CONFIG_MTD_NAND=y | ||
85 | |||
86 | CONFIG_MTD_NAND_OMAP2=y | ||
87 | |||
88 | CONFIG_MTD_UBI=y | ||
89 | |||
90 | # | ||
91 | # SCSI device support | ||
92 | # | ||
93 | CONFIG_SCSI=y | ||
94 | |||
95 | # | ||
96 | # SCSI support type (disk, tape, CD-ROM) | ||
97 | # | ||
98 | CONFIG_BLK_DEV_SD=y | ||
99 | |||
100 | # | ||
101 | # Ethernet (10 or 100Mbit) | ||
102 | # | ||
103 | CONFIG_SMSC911X=y | ||
104 | CONFIG_USB_NET_SMSC95XX=y | ||
105 | |||
106 | # | ||
107 | # Userland interfaces | ||
108 | # | ||
109 | CONFIG_INPUT_EVDEV=y | ||
110 | |||
111 | # | ||
112 | # Input Device Drivers | ||
113 | # | ||
114 | CONFIG_KEYBOARD_TWL4030=y | ||
115 | CONFIG_INPUT_TOUCHSCREEN=y | ||
116 | CONFIG_TOUCHSCREEN_ADS7846=y | ||
117 | |||
118 | # | ||
119 | # Miscellaneous I2C Chip support | ||
120 | # | ||
121 | CONFIG_I2C=y | ||
122 | CONFIG_I2C_OMAP=y | ||
123 | CONFIG_SPI=y | ||
124 | CONFIG_SPI_MASTER=y | ||
125 | CONFIG_SPI_OMAP24XX=y | ||
126 | |||
127 | # | ||
128 | # I2C GPIO expanders: | ||
129 | # | ||
130 | CONFIG_GPIO_TWL4030=y | ||
131 | |||
132 | # | ||
133 | # SPI GPIO expanders: | ||
134 | # | ||
135 | CONFIG_OMAP_WATCHDOG=y | ||
136 | CONFIG_WATCHDOG_NOWAYOUT=y | ||
137 | |||
138 | # | ||
139 | # Multifunction device drivers | ||
140 | # | ||
141 | CONFIG_TWL4030_CORE=y | ||
142 | CONFIG_REGULATOR=y | ||
143 | CONFIG_REGULATOR_DUMMY=y | ||
144 | CONFIG_REGULATOR_TWL4030=y | ||
145 | |||
146 | # | ||
147 | # Graphics support | ||
148 | # | ||
149 | CONFIG_FB=y | ||
150 | CONFIG_DRM=m | ||
151 | # CONFIG_VGASTATE is not set | ||
152 | # CONFIG_VIDEO_OUTPUT_CONTROL is not set | ||
153 | # CONFIG_FIRMWARE_EDID is not set | ||
154 | # CONFIG_FB_DDC is not set | ||
155 | # CONFIG_FB_BOOT_VESA_SUPPORT is not set | ||
156 | CONFIG_FB_CFB_FILLRECT=y | ||
157 | CONFIG_FB_CFB_COPYAREA=y | ||
158 | CONFIG_FB_CFB_IMAGEBLIT=y | ||
159 | # CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set | ||
160 | # CONFIG_FB_SYS_FILLRECT is not set | ||
161 | # CONFIG_FB_SYS_COPYAREA is not set | ||
162 | # CONFIG_FB_SYS_IMAGEBLIT is not set | ||
163 | # CONFIG_FB_FOREIGN_ENDIAN is not set | ||
164 | # CONFIG_FB_SYS_FOPS is not set | ||
165 | # CONFIG_FB_SVGALIB is not set | ||
166 | # CONFIG_FB_MACMODES is not set | ||
167 | # CONFIG_FB_BACKLIGHT is not set | ||
168 | CONFIG_FB_MODE_HELPERS=y | ||
169 | # CONFIG_FB_TILEBLITTING is not set | ||
170 | |||
171 | # | ||
172 | # Frame buffer hardware drivers | ||
173 | # | ||
174 | # CONFIG_FB_S1D13XXX is not set | ||
175 | # CONFIG_FB_TMIO is not set | ||
176 | # CONFIG_FB_VIRTUAL is not set | ||
177 | # CONFIG_FB_METRONOME is not set | ||
178 | # CONFIG_FB_MB862XX is not set | ||
179 | # CONFIG_FB_BROADSHEET is not set | ||
180 | # CONFIG_FB_OMAP_BOOTLOADER_INIT is not set | ||
181 | CONFIG_OMAP2_VRAM=y | ||
182 | CONFIG_OMAP2_VRFB=y | ||
183 | CONFIG_OMAP2_DSS=y | ||
184 | CONFIG_OMAP2_VRAM_SIZE=14 | ||
185 | CONFIG_OMAP2_DSS_DEBUG_SUPPORT=y | ||
186 | # CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS is not set | ||
187 | CONFIG_OMAP2_DSS_DPI=y | ||
188 | # CONFIG_OMAP2_DSS_RFBI is not set | ||
189 | CONFIG_OMAP2_DSS_VENC=y | ||
190 | # CONFIG_OMAP2_DSS_SDI is not set | ||
191 | CONFIG_OMAP2_DSS_DSI=y | ||
192 | # CONFIG_OMAP2_DSS_FAKE_VSYNC is not set | ||
193 | CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=0 | ||
194 | CONFIG_FB_OMAP2=y | ||
195 | CONFIG_FB_OMAP2_DEBUG_SUPPORT=y | ||
196 | CONFIG_FB_OMAP2_NUM_FBS=2 | ||
197 | |||
198 | # | ||
199 | # OMAP2/3 Display Device Drivers | ||
200 | # | ||
201 | CONFIG_PANEL_GENERIC_DPI=y | ||
202 | CONFIG_PANEL_DVI=y | ||
203 | CONFIG_PANEL_SHARP_LS037V7DW01=y | ||
204 | # CONFIG_PANEL_LGPHILIPS_LB035Q02 is not set | ||
205 | # CONFIG_PANEL_TAAL is not set | ||
206 | CONFIG_PANEL_TPO_TD043MTEA1=m | ||
207 | # CONFIG_BACKLIGHT_LCD_SUPPORT is not set | ||
208 | CONFIG_BACKLIGHT_CLASS_DEVICE=y | ||
209 | |||
210 | # | ||
211 | # Display device support | ||
212 | # | ||
213 | CONFIG_DISPLAY_SUPPORT=y | ||
214 | CONFIG_DUMMY_CONSOLE=y | ||
215 | # CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set | ||
216 | CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y | ||
217 | # CONFIG_FONTS is not set | ||
218 | CONFIG_FONT_8x8=y | ||
219 | CONFIG_FONT_8x16=y | ||
220 | # CONFIG_LOGO_LINUX_MONO is not set | ||
221 | # CONFIG_LOGO_LINUX_VGA16 is not set | ||
222 | |||
223 | # | ||
224 | # Console display driver support | ||
225 | # | ||
226 | CONFIG_FRAMEBUFFER_CONSOLE=y | ||
227 | CONFIG_LOGO=y | ||
228 | # CONFIG_VGA_CONSOLE is not set | ||
229 | |||
230 | # DMA Devices | ||
231 | CONFIG_DMADEVICES=y | ||
232 | CONFIG_DMA_OMAP=y | ||
233 | CONFIG_DMA_OF=y | ||
234 | |||
235 | CONFIG_SOUND=y | ||
236 | CONFIG_SND=y | ||
237 | CONFIG_SND_SOC=y | ||
238 | CONFIG_SND_OMAP_SOC=y | ||
239 | CONFIG_SND_OMAP_SOC_OMAP_TWL4030=y | ||
240 | |||
241 | # | ||
242 | # USB Input Devices | ||
243 | # | ||
244 | CONFIG_USB=y | ||
245 | CONFIG_USB_SUPPORT=y | ||
246 | |||
247 | # | ||
248 | # Miscellaneous USB options | ||
249 | # | ||
250 | CONFIG_USB_OTG=y | ||
251 | # CONFIG_USB_OTG_WHITELIST is not set | ||
252 | |||
253 | # | ||
254 | # USB Host Controller Drivers | ||
255 | # | ||
256 | CONFIG_USB_EHCI_HCD=y | ||
257 | CONFIG_USB_EHCI_TT_NEWSCHED=y | ||
258 | CONFIG_USB_EHCI_ROOT_HUB_TT=y | ||
259 | CONFIG_USB_MUSB_HDRC=y | ||
260 | CONFIG_USB_MUSB_OMAP2PLUS=y | ||
261 | CONFIG_USB_OMAP=y | ||
262 | |||
263 | # | ||
264 | # OMAP 343x high speed USB support | ||
265 | # | ||
266 | CONFIG_USB_MUSB_OTG=y | ||
267 | CONFIG_USB_GADGET_MUSB_HDRC=y | ||
268 | CONFIG_USB_MUSB_HDRC_HCD=y | ||
269 | CONFIG_USB_INVENTRA_DMA=y | ||
270 | |||
271 | # | ||
272 | # NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' | ||
273 | # | ||
274 | |||
275 | # | ||
276 | # may also be needed; see USB_STORAGE Help for more information | ||
277 | # | ||
278 | CONFIG_USB_STORAGE=y | ||
279 | |||
280 | # | ||
281 | # USB Miscellaneous drivers | ||
282 | # | ||
283 | CONFIG_USB_GADGET=y | ||
284 | CONFIG_USB_GADGET_DUALSPEED=y | ||
285 | CONFIG_USB_OTG_UTILS=y | ||
286 | CONFIG_TWL4030_USB=y | ||
287 | |||
288 | # USB gadget modules | ||
289 | CONFIG_USB_G_NCM=y | ||
290 | CONFIG_USB_MASS_STORAGE=y | ||
291 | |||
292 | CONFIG_MMC=y | ||
293 | |||
294 | # | ||
295 | # MMC/SD Host Controller Drivers | ||
296 | # | ||
297 | CONFIG_MMC_OMAP_HS=y | ||
298 | |||
299 | # | ||
300 | # Real Time Clock | ||
301 | # | ||
302 | CONFIG_RTC_LIB=y | ||
303 | CONFIG_RTC_CLASS=y | ||
304 | CONFIG_RTC_DRV_TWL4030=y | ||
305 | |||
306 | # | ||
307 | # DOS/FAT/NT Filesystems | ||
308 | # | ||
309 | CONFIG_VFAT_FS=y | ||
310 | |||
311 | # | ||
312 | # Multimedia core support | ||
313 | # | ||
314 | |||
315 | # CONFIG_VIDEO_HELPER_CHIPS_AUTO is not set | ||
316 | |||
317 | # | ||
318 | # Advanced Power Management Emulation support | ||
319 | # | ||
320 | CONFIG_APM_EMULATION=y | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc new file mode 100644 index 0000000000..24196e6f67 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc | |||
@@ -0,0 +1,7 @@ | |||
1 | kconf hardware {{=machine}}.cfg | ||
2 | kconf non-hardware {{machine}}-non_hardware.cfg | ||
3 | |||
4 | include features/usb-net/usb-net.scc | ||
5 | |||
6 | kconf hardware {{=machine}}-user-config.cfg | ||
7 | include {{=machine}}-user-patches.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend new file mode 100644 index 0000000000..25c87a85ac --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend | |||
@@ -0,0 +1,25 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
7 | |||
8 | {{ if need_new_kbranch == "y": }} | ||
9 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
10 | |||
11 | {{ if need_new_kbranch == "n": }} | ||
12 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
13 | |||
14 | {{ if need_new_kbranch == "n": }} | ||
15 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
16 | |||
17 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} | ||
18 | {{ if smp == "y": }} | ||
19 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
20 | |||
21 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
22 | file://{{=machine}}-user-config.cfg \ | ||
23 | file://{{=machine}}-user-patches.scc \ | ||
24 | file://{{=machine}}-user-features.scc \ | ||
25 | " | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend new file mode 100644 index 0000000000..08b1f88d1b --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-preempt-rt.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" | ||
31 | #SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" | ||
32 | #LINUX_VERSION = "3.10.9" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend new file mode 100644 index 0000000000..bc6968d832 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.10.9" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend new file mode 100644 index 0000000000..d221d5f2a4 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.14" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend new file mode 100644 index 0000000000..1e814c54d7 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "19f7e43b54aef08d58135ed2a897d77b624b320a" | ||
31 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "459165c1dd61c4e843c36e6a1abeb30949a20ba7" | ||
32 | #LINUX_VERSION = "3.10.9" \ No newline at end of file | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend new file mode 100644 index 0000000000..ca7f8c5978 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/arm/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.14" \ No newline at end of file | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/COPYING.MIT b/scripts/lib/bsp/substrate/target/arch/common/COPYING.MIT new file mode 100644 index 0000000000..fb950dc69f --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/COPYING.MIT | |||
@@ -0,0 +1,17 @@ | |||
1 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
2 | of this software and associated documentation files (the "Software"), to deal | ||
3 | in the Software without restriction, including without limitation the rights | ||
4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
5 | copies of the Software, and to permit persons to whom the Software is | ||
6 | furnished to do so, subject to the following conditions: | ||
7 | |||
8 | The above copyright notice and this permission notice shall be included in | ||
9 | all copies or substantial portions of the Software. | ||
10 | |||
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
17 | THE SOFTWARE. | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/README b/scripts/lib/bsp/substrate/target/arch/common/README new file mode 100644 index 0000000000..928659f302 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/README | |||
@@ -0,0 +1,118 @@ | |||
1 | This README file contains information on building the meta-{{=machine}} | ||
2 | BSP layer, and booting the images contained in the /binary directory. | ||
3 | Please see the corresponding sections below for details. | ||
4 | |||
5 | |||
6 | Dependencies | ||
7 | ============ | ||
8 | |||
9 | This layer depends on: | ||
10 | |||
11 | URI: git://git.openembedded.org/bitbake | ||
12 | branch: master | ||
13 | |||
14 | URI: git://git.openembedded.org/openembedded-core | ||
15 | layers: meta | ||
16 | branch: master | ||
17 | |||
18 | URI: git://git.yoctoproject.org/xxxx | ||
19 | layers: xxxx | ||
20 | branch: master | ||
21 | |||
22 | |||
23 | Patches | ||
24 | ======= | ||
25 | |||
26 | Please submit any patches against this BSP to the Yocto mailing list | ||
27 | (yocto@yoctoproject.org) and cc: the maintainer: | ||
28 | |||
29 | Maintainer: XXX YYYYYY <xxx.yyyyyy@zzzzz.com> | ||
30 | |||
31 | Please see the meta-xxxx/MAINTAINERS file for more details. | ||
32 | |||
33 | |||
34 | Table of Contents | ||
35 | ================= | ||
36 | |||
37 | I. Building the meta-{{=machine}} BSP layer | ||
38 | II. Booting the images in /binary | ||
39 | |||
40 | |||
41 | I. Building the meta-{{=machine}} BSP layer | ||
42 | ======================================== | ||
43 | |||
44 | --- replace with specific instructions for your layer --- | ||
45 | |||
46 | In order to build an image with BSP support for a given release, you | ||
47 | need to download the corresponding BSP tarball from the 'Board Support | ||
48 | Package (BSP) Downloads' page of the Yocto Project website. | ||
49 | |||
50 | Having done that, and assuming you extracted the BSP tarball contents | ||
51 | at the top-level of your yocto build tree, you can build a | ||
52 | {{=machine}} image by adding the location of the meta-{{=machine}} | ||
53 | layer to bblayers.conf, along with any other layers needed (to access | ||
54 | common metadata shared between BSPs) e.g.: | ||
55 | |||
56 | yocto/meta-xxxx \ | ||
57 | yocto/meta-xxxx/meta-{{=machine}} \ | ||
58 | |||
59 | To enable the {{=machine}} layer, add the {{=machine}} MACHINE to local.conf: | ||
60 | |||
61 | MACHINE ?= "{{=machine}}" | ||
62 | |||
63 | You should then be able to build a {{=machine}} image as such: | ||
64 | |||
65 | $ source oe-init-build-env | ||
66 | $ bitbake core-image-sato | ||
67 | |||
68 | At the end of a successful build, you should have a live image that | ||
69 | you can boot from a USB flash drive (see instructions on how to do | ||
70 | that below, in the section 'Booting the images from /binary'). | ||
71 | |||
72 | As an alternative to downloading the BSP tarball, you can also work | ||
73 | directly from the meta-xxxx git repository. For each BSP in the | ||
74 | 'meta-xxxx' repository, there are multiple branches, one corresponding | ||
75 | to each major release starting with 'laverne' (0.90), in addition to | ||
76 | the latest code which tracks the current master (note that not all | ||
77 | BSPs are present in every release). Instead of extracting a BSP | ||
78 | tarball at the top level of your yocto build tree, you can | ||
79 | equivalently check out the appropriate branch from the meta-xxxx | ||
80 | repository at the same location. | ||
81 | |||
82 | |||
83 | II. Booting the images in /binary | ||
84 | ================================= | ||
85 | |||
86 | --- replace with specific instructions for your platform --- | ||
87 | |||
88 | This BSP contains bootable live images, which can be used to directly | ||
89 | boot Yocto off of a USB flash drive. | ||
90 | |||
91 | Under Linux, insert a USB flash drive. Assuming the USB flash drive | ||
92 | takes device /dev/sdf, use dd to copy the live image to it. For | ||
93 | example: | ||
94 | |||
95 | # dd if=core-image-sato-{{=machine}}-20101207053738.hddimg of=/dev/sdf | ||
96 | # sync | ||
97 | # eject /dev/sdf | ||
98 | |||
99 | This should give you a bootable USB flash device. Insert the device | ||
100 | into a bootable USB socket on the target, and power on. This should | ||
101 | result in a system booted to the Sato graphical desktop. | ||
102 | |||
103 | If you want a terminal, use the arrows at the top of the UI to move to | ||
104 | different pages of available applications, one of which is named | ||
105 | 'Terminal'. Clicking that should give you a root terminal. | ||
106 | |||
107 | If you want to ssh into the system, you can use the root terminal to | ||
108 | ifconfig the IP address and use that to ssh in. The root password is | ||
109 | empty, so to log in type 'root' for the user name and hit 'Enter' at | ||
110 | the Password prompt: and you should be in. | ||
111 | |||
112 | ---- | ||
113 | |||
114 | If you find you're getting corrupt images on the USB (it doesn't show | ||
115 | the syslinux boot: prompt, or the boot: prompt contains strange | ||
116 | characters), try doing this first: | ||
117 | |||
118 | # dd if=/dev/zero of=/dev/sdf bs=1M count=512 | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/README.sources b/scripts/lib/bsp/substrate/target/arch/common/README.sources new file mode 100644 index 0000000000..3c4cb7b435 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/README.sources | |||
@@ -0,0 +1,17 @@ | |||
1 | The sources for the packages comprising the images shipped with this | ||
2 | BSP can be found at the following location: | ||
3 | |||
4 | http://downloads.yoctoproject.org/mirror/sources/ | ||
5 | |||
6 | The metadata used to generate the images shipped with this BSP, in | ||
7 | addition to the code contained in this BSP, can be found at the | ||
8 | following location: | ||
9 | |||
10 | http://www.yoctoproject.org/downloads/yocto-1.1/poky-edison-6.0.tar.bz2 | ||
11 | |||
12 | The metadata used to generate the images shipped with this BSP, in | ||
13 | addition to the code contained in this BSP, can also be found at the | ||
14 | following locations: | ||
15 | |||
16 | git://git.yoctoproject.org/poky.git | ||
17 | git://git.yoctoproject.org/meta-xxxx | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/binary/.gitignore b/scripts/lib/bsp/substrate/target/arch/common/binary/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/binary/.gitignore | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/conf/layer.conf b/scripts/lib/bsp/substrate/target/arch/common/conf/layer.conf new file mode 100644 index 0000000000..5529f45954 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/conf/layer.conf | |||
@@ -0,0 +1,10 @@ | |||
1 | # We have a conf and classes directory, add to BBPATH | ||
2 | BBPATH .= ":${LAYERDIR}" | ||
3 | |||
4 | # We have a recipes-* directories, add to BBFILES | ||
5 | BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \ | ||
6 | ${LAYERDIR}/recipes-*/*/*.bbappend" | ||
7 | |||
8 | BBFILE_COLLECTIONS += "{{=machine}}" | ||
9 | BBFILE_PATTERN_{{=machine}} = "^${LAYERDIR}/" | ||
10 | BBFILE_PRIORITY_{{=machine}} = "6" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor/{{=machine}}/machconfig b/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor/{{=machine}}/machconfig new file mode 100644 index 0000000000..3b85d3821f --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor/{{=machine}}/machconfig | |||
@@ -0,0 +1,5 @@ | |||
1 | # Assume a USB mouse and keyboard are connected | ||
2 | {{ input type:"boolean" name:"touchscreen" msg:"Does your BSP have a touchscreen? (y/n)" default:"n" }} | ||
3 | HAVE_TOUCHSCREEN={{=touchscreen}} | ||
4 | {{ input type:"boolean" name:"keyboard" msg:"Does your BSP have a keyboard? (y/n)" default:"y" }} | ||
5 | HAVE_KEYBOARD={{=keyboard}} | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor_0.0.bbappend b/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor_0.0.bbappend new file mode 100644 index 0000000000..6d4804d127 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-bsp/formfactor/formfactor_0.0.bbappend | |||
@@ -0,0 +1,2 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" | ||
2 | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..03b7d84ec2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/kernel-list.noinstall | |||
@@ -0,0 +1,26 @@ | |||
1 | {{ if kernel_choice == "custom": }} | ||
2 | {{ input type:"boolean" name:"custom_kernel_remote" prio:"20" msg:"Is the custom kernel you'd like to use in a remote git repo? (y/n)" default:"y"}} | ||
3 | |||
4 | {{ if kernel_choice == "custom" and custom_kernel_remote == "y": }} | ||
5 | {{ input type:"edit-git-repo" name:"custom_kernel_remote_path" prio:"20" msg:"Please enter the full URI to the remote git repo (the default corresponds to linux-stable v3.13.9)" default:"git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git"}} | ||
6 | |||
7 | {{ if kernel_choice == "custom" and custom_kernel_remote == "n": }} | ||
8 | {{ input type:"edit-git-repo" name:"custom_kernel_local_path" prio:"20" msg:"You've indicated that you're not using a remote git repo. Please enter the full path to the local git repo you want to use (the default assumes a local linux-stable v3.13.9)" default:"/home/trz/yocto/kernels/linux-stable.git"}} | ||
9 | |||
10 | {{ if kernel_choice == "custom": }} | ||
11 | {{ input type:"boolean" name:"custom_kernel_need_kbranch" prio:"20" msg:"Do you need to use a specific (non-master) branch? (y/n)" default:"n"}} | ||
12 | |||
13 | {{ if kernel_choice == "custom" and custom_kernel_need_kbranch == "y": }} | ||
14 | {{ input type:"edit" name:"custom_kernel_kbranch" prio:"20" msg:"Please enter the branch you want to use (the default branch corresponds to the linux-stable 'linux-3.13.y' branch):" default:"linux-3.13.y"}} | ||
15 | |||
16 | {{ if kernel_choice == "custom": }} | ||
17 | {{ input type:"edit" name:"custom_kernel_srcrev" prio:"20" msg:"Please enter the SRCREV (commit id) you'd like to use (use '${AUTOREV}' to track the current HEAD):" default:"${AUTOREV}"}} | ||
18 | |||
19 | {{ if kernel_choice == "custom": }} | ||
20 | {{ input type:"edit" name:"custom_kernel_linux_version" prio:"20" msg:"Please enter the Linux version of the kernel you've specified:" default:"3.13.9"}} | ||
21 | |||
22 | {{ if kernel_choice == "custom": }} | ||
23 | {{ input type:"edit" name:"custom_kernel_linux_version_extension" prio:"20" msg:"Please enter a Linux version extension if you want (it will show up at the end of the kernel name shown by uname):" default:"-custom"}} | ||
24 | |||
25 | {{ if kernel_choice == "custom": }} | ||
26 | {{ input type:"edit-file" name:"custom_kernel_defconfig" prio:"20" msg:"It's recommended (but not required) that custom kernels be built using a defconfig. Please enter the full path to the defconfig for your kernel (NOTE: if you don't specify a defconfig the kernel probably won't build or boot):" default:""}} | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom.bb b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom.bb new file mode 100644 index 0000000000..6d3cc6f743 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom.bb | |||
@@ -0,0 +1,57 @@ | |||
1 | # This file was derived from the linux-yocto-custom.bb recipe in | ||
2 | # oe-core. | ||
3 | # | ||
4 | # linux-yocto-custom.bb: | ||
5 | # | ||
6 | # A yocto-bsp-generated kernel recipe that uses the linux-yocto and | ||
7 | # oe-core kernel classes to apply a subset of yocto kernel | ||
8 | # management to git managed kernel repositories. | ||
9 | # | ||
10 | # Warning: | ||
11 | # | ||
12 | # Building this kernel without providing a defconfig or BSP | ||
13 | # configuration will result in build or boot errors. This is not a | ||
14 | # bug. | ||
15 | # | ||
16 | # Notes: | ||
17 | # | ||
18 | # patches: patches can be merged into to the source git tree itself, | ||
19 | # added via the SRC_URI, or controlled via a BSP | ||
20 | # configuration. | ||
21 | # | ||
22 | # example configuration addition: | ||
23 | # SRC_URI += "file://smp.cfg" | ||
24 | # example patch addition: | ||
25 | # SRC_URI += "file://0001-linux-version-tweak.patch | ||
26 | # example feature addition: | ||
27 | # SRC_URI += "file://feature.scc" | ||
28 | # | ||
29 | |||
30 | inherit kernel | ||
31 | require recipes-kernel/linux/linux-yocto.inc | ||
32 | |||
33 | {{ if kernel_choice == "custom" and custom_kernel_remote == "y": }} | ||
34 | SRC_URI = "{{=custom_kernel_remote_path}};protocol=git;bareclone=1" | ||
35 | {{ if kernel_choice == "custom" and custom_kernel_remote == "n": }} | ||
36 | SRC_URI = "git://{{=custom_kernel_local_path}};protocol=file;bareclone=1" | ||
37 | |||
38 | SRC_URI += "file://defconfig" | ||
39 | |||
40 | SRC_URI += "file://{{=machine}}.scc \ | ||
41 | file://{{=machine}}.cfg \ | ||
42 | file://{{=machine}}-user-config.cfg \ | ||
43 | file://{{=machine}}-user-patches.scc \ | ||
44 | " | ||
45 | |||
46 | {{ if kernel_choice == "custom" and custom_kernel_need_kbranch == "y" and custom_kernel_kbranch and custom_kernel_kbranch != "master": }} | ||
47 | KBRANCH = "{{=custom_kernel_kbranch}}" | ||
48 | |||
49 | LINUX_VERSION ?= "{{=custom_kernel_linux_version}}" | ||
50 | LINUX_VERSION_EXTENSION ?= "{{=custom_kernel_linux_version_extension}}" | ||
51 | |||
52 | SRCREV="{{=custom_kernel_srcrev}}" | ||
53 | |||
54 | PR = "r0" | ||
55 | PV = "${LINUX_VERSION}+git${SRCPV}" | ||
56 | |||
57 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/defconfig b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/defconfig new file mode 100644 index 0000000000..ceb0ffa30c --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/defconfig | |||
@@ -0,0 +1,5 @@ | |||
1 | # | ||
2 | # Placeholder for custom default kernel configuration. yocto-bsp will | ||
3 | # replace this file with a user-specified defconfig. | ||
4 | # | ||
5 | {{ if custom_kernel_defconfig: replace_file(of, custom_kernel_defconfig) }} | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}-user-config.cfg b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}-user-config.cfg new file mode 100644 index 0000000000..17c8b503da --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}-user-config.cfg | |||
@@ -0,0 +1,8 @@ | |||
1 | # | ||
2 | # Used by yocto-kernel to manage config options. | ||
3 | # | ||
4 | # yocto-kernel may change the contents of this file in any | ||
5 | # way it sees fit, including removing comments like this, | ||
6 | # so don't manually make any modifications you don't want | ||
7 | # to lose. | ||
8 | # | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}-user-patches.scc b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}-user-patches.scc new file mode 100644 index 0000000000..7a598d9118 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}-user-patches.scc | |||
@@ -0,0 +1,8 @@ | |||
1 | # | ||
2 | # Used by yocto-kernel to manage patches. | ||
3 | # | ||
4 | # yocto-kernel may change the contents of this file in any | ||
5 | # way it sees fit, including removing comments like this, | ||
6 | # so don't manually make any modifications you don't want | ||
7 | # to lose. | ||
8 | # | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}.cfg b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}.cfg new file mode 100644 index 0000000000..95170b12eb --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}.cfg | |||
@@ -0,0 +1,3 @@ | |||
1 | # | ||
2 | # A convenient place to add config options, nothing more. | ||
3 | # | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}.scc b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}.scc new file mode 100644 index 0000000000..2e3ca90793 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/common/recipes-kernel/linux/{{ if kernel_choice == "custom": }} linux-yocto-custom/{{=machine}}.scc | |||
@@ -0,0 +1,17 @@ | |||
1 | # | ||
2 | # The top-level 'feature' for the {{=machine}} custom kernel. | ||
3 | # | ||
4 | # Essentially this is a convenient top-level container or starting | ||
5 | # point for adding lower-level config fragements and features. | ||
6 | # | ||
7 | |||
8 | # {{=machine}}.cfg in the linux-yocto-custom subdir is just a | ||
9 | # convenient place for adding random config fragments. | ||
10 | |||
11 | kconf hardware {{=machine}}.cfg | ||
12 | |||
13 | # These are used by yocto-kernel to add config fragments and features. | ||
14 | # Don't remove if you plan on using yocto-kernel with this BSP. | ||
15 | |||
16 | kconf hardware {{=machine}}-user-config.cfg | ||
17 | include {{=machine}}-user-patches.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/i386/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..932fd79bb9 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/conf/machine/{{=machine}}.conf | |||
@@ -0,0 +1,68 @@ | |||
1 | #@TYPE: Machine | ||
2 | #@NAME: {{=machine}} | ||
3 | |||
4 | #@DESCRIPTION: Machine configuration for {{=machine}} systems | ||
5 | |||
6 | {{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} | ||
7 | {{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} | ||
8 | {{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} | ||
9 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
10 | |||
11 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} | ||
12 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} | ||
13 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} | ||
14 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
15 | PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" | ||
16 | |||
17 | {{ input type:"choicelist" name:"tunefile" prio:"40" msg:"Which machine tuning would you like to use?" default:"tune_core2" }} | ||
18 | {{ input type:"choice" val:"tune_i586" msg:"i586 tuning optimizations" }} | ||
19 | {{ input type:"choice" val:"tune_atom" msg:"Atom tuning optimizations" }} | ||
20 | {{ input type:"choice" val:"tune_core2" msg:"Core2 tuning optimizations" }} | ||
21 | {{ if tunefile == "tune_i586": }} | ||
22 | require conf/machine/include/tune-i586.inc | ||
23 | {{ if tunefile == "tune_atom": }} | ||
24 | require conf/machine/include/tune-atom.inc | ||
25 | {{ if tunefile == "tune_core2": }} | ||
26 | DEFAULTTUNE="core2-32" | ||
27 | require conf/machine/include/tune-core2.inc | ||
28 | |||
29 | require conf/machine/include/x86-base.inc | ||
30 | |||
31 | MACHINE_FEATURES += "wifi efi pcbios" | ||
32 | |||
33 | {{ input type:"boolean" name:"xserver" prio:"50" msg:"Do you need support for X? (y/n)" default:"y" }} | ||
34 | |||
35 | {{ if xserver == "y" and (kernel_choice == "linux-yocto_3.14" or kernel_choice == "linux-yocto_3.10"): }} | ||
36 | {{ input type:"choicelist" name:"xserver_choice" prio:"50" msg:"Please select an xserver for this machine:" default:"xserver_i915" }} | ||
37 | {{ input type:"choice" val:"xserver_vesa" msg:"VESA xserver support" }} | ||
38 | {{ input type:"choice" val:"xserver_i915" msg:"i915 xserver support" }} | ||
39 | {{ input type:"choice" val:"xserver_i965" msg:"i965 xserver support" }} | ||
40 | |||
41 | {{ if xserver == "y" and kernel_choice == "custom": }} | ||
42 | {{ input type:"choicelist" name:"xserver_choice" prio:"50" msg:"Please select an xserver for this machine:" default:"xserver_i915" }} | ||
43 | {{ input type:"choice" val:"xserver_vesa" msg:"VESA xserver support" }} | ||
44 | {{ input type:"choice" val:"xserver_i915" msg:"i915 xserver support" }} | ||
45 | {{ input type:"choice" val:"xserver_i965" msg:"i965 xserver support" }} | ||
46 | |||
47 | {{ if xserver == "y" and kernel_choice != "linux-yocto_3.14" and kernel_choice != "linux-yocto_3.10" and kernel_choice != "custom": xserver_choice = "xserver_i915" }} | ||
48 | |||
49 | {{ if xserver == "y": }} | ||
50 | XSERVER ?= "${XSERVER_X86_BASE} \ | ||
51 | ${XSERVER_X86_EXT} \ | ||
52 | {{ if xserver == "y" and xserver_choice == "xserver_vesa": }} | ||
53 | ${XSERVER_X86_VESA} \ | ||
54 | {{ if xserver == "y" and xserver_choice == "xserver_i915": }} | ||
55 | ${XSERVER_X86_I915} \ | ||
56 | {{ if xserver == "y" and xserver_choice == "xserver_i965": }} | ||
57 | ${XSERVER_X86_I965} \ | ||
58 | {{ if xserver == "y": }} | ||
59 | " | ||
60 | |||
61 | MACHINE_EXTRA_RRECOMMENDS += "linux-firmware v86d" | ||
62 | |||
63 | GLIBC_ADDONS = "nptl" | ||
64 | |||
65 | EXTRA_OECONF_append_pn-matchbox-panel-2 = " --with-battery=acpi" | ||
66 | |||
67 | {{ if xserver == "y" and xserver_choice == "xserver_vesa": }} | ||
68 | APPEND += "video=vesafb vga=0x318" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == "y": }} xorg.conf b/scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == "y": }} xorg.conf new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == "y": }} xorg.conf | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/{{ if xserver == "y": }} xserver-xf86-config_0.1.bbappend b/scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/{{ if xserver == "y": }} xserver-xf86-config_0.1.bbappend new file mode 100644 index 0000000000..72d991c7e5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-graphics/xorg-xserver/{{ if xserver == "y": }} xserver-xf86-config_0.1.bbappend | |||
@@ -0,0 +1 @@ | |||
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/kernel-list.noinstall | |||
@@ -0,0 +1,5 @@ | |||
1 | {{ if kernel_choice != "custom": }} | ||
2 | {{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} | ||
3 | |||
4 | {{ if kernel_choice != "custom" and use_default_kernel == "n": }} | ||
5 | {{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc new file mode 100644 index 0000000000..bfefb0d0a0 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc | |||
@@ -0,0 +1,15 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE preempt-rt | ||
3 | define KARCH i386 | ||
4 | |||
5 | include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
10 | |||
11 | # default policy for preempt-rt kernels | ||
12 | include cfg/usb-mass-storage.scc | ||
13 | include cfg/boot-live.scc | ||
14 | include features/latencytop/latencytop.scc | ||
15 | include features/profiling/profiling.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc new file mode 100644 index 0000000000..60b670dffc --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc | |||
@@ -0,0 +1,15 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE standard | ||
3 | define KARCH i386 | ||
4 | |||
5 | include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
10 | |||
11 | # default policy for standard kernels | ||
12 | include cfg/usb-mass-storage.scc | ||
13 | include cfg/boot-live.scc | ||
14 | include features/latencytop/latencytop.scc | ||
15 | include features/profiling/profiling.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc new file mode 100644 index 0000000000..ec44ef9485 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE tiny | ||
3 | define KARCH i386 | ||
4 | |||
5 | include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg new file mode 100644 index 0000000000..e93c0b8a08 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg | |||
@@ -0,0 +1,54 @@ | |||
1 | CONFIG_X86_32=y | ||
2 | CONFIG_MATOM=y | ||
3 | CONFIG_PRINTK=y | ||
4 | |||
5 | # Basic hardware support for the box - network, USB, PCI, sound | ||
6 | CONFIG_NETDEVICES=y | ||
7 | CONFIG_ATA=y | ||
8 | CONFIG_ATA_GENERIC=y | ||
9 | CONFIG_ATA_SFF=y | ||
10 | CONFIG_PCI=y | ||
11 | CONFIG_MMC=y | ||
12 | CONFIG_MMC_SDHCI=y | ||
13 | CONFIG_USB_SUPPORT=y | ||
14 | CONFIG_USB=y | ||
15 | CONFIG_USB_ARCH_HAS_EHCI=y | ||
16 | CONFIG_R8169=y | ||
17 | CONFIG_PATA_SCH=y | ||
18 | CONFIG_MMC_SDHCI_PCI=y | ||
19 | CONFIG_USB_EHCI_HCD=y | ||
20 | CONFIG_PCIEPORTBUS=y | ||
21 | CONFIG_NET=y | ||
22 | CONFIG_USB_UHCI_HCD=y | ||
23 | CONFIG_USB_OHCI_HCD=y | ||
24 | CONFIG_BLK_DEV_SD=y | ||
25 | CONFIG_CHR_DEV_SG=y | ||
26 | CONFIG_SOUND=y | ||
27 | CONFIG_SND=y | ||
28 | CONFIG_SND_HDA_INTEL=y | ||
29 | CONFIG_SATA_AHCI=y | ||
30 | CONFIG_AGP=y | ||
31 | CONFIG_PM=y | ||
32 | CONFIG_ACPI=y | ||
33 | CONFIG_BACKLIGHT_LCD_SUPPORT=y | ||
34 | CONFIG_BACKLIGHT_CLASS_DEVICE=y | ||
35 | CONFIG_INPUT=y | ||
36 | |||
37 | # Make sure these are on, otherwise the bootup won't be fun | ||
38 | CONFIG_EXT3_FS=y | ||
39 | CONFIG_UNIX=y | ||
40 | CONFIG_INET=y | ||
41 | CONFIG_MODULES=y | ||
42 | CONFIG_SHMEM=y | ||
43 | CONFIG_TMPFS=y | ||
44 | CONFIG_PACKET=y | ||
45 | |||
46 | # Needed for booting (and using) USB memory sticks | ||
47 | CONFIG_BLK_DEV_LOOP=y | ||
48 | CONFIG_NLS_CODEPAGE_437=y | ||
49 | CONFIG_NLS_ISO8859_1=y | ||
50 | |||
51 | CONFIG_RD_GZIP=y | ||
52 | |||
53 | # Needed for booting (and using) CD images | ||
54 | CONFIG_BLK_DEV_SR=y | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc new file mode 100644 index 0000000000..eda1d62f11 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc | |||
@@ -0,0 +1,20 @@ | |||
1 | kconf hardware {{=machine}}.cfg | ||
2 | |||
3 | include features/intel-e1xxxx/intel-e100.scc | ||
4 | include features/intel-e1xxxx/intel-e1xxxx.scc | ||
5 | |||
6 | {{ if xserver == "y" and xserver_choice == "xserver_i915" or xserver_choice == "xserver_i965": }} | ||
7 | include features/i915/i915.scc | ||
8 | |||
9 | include features/serial/8250.scc | ||
10 | include features/ericsson-3g/f5521gw.scc | ||
11 | |||
12 | {{ if xserver == "y" and xserver_choice == "xserver_vesa": }} | ||
13 | include cfg/vesafb.scc | ||
14 | |||
15 | include cfg/usb-mass-storage.scc | ||
16 | include cfg/boot-live.scc | ||
17 | include features/power/intel.scc | ||
18 | |||
19 | kconf hardware {{=machine}}-user-config.cfg | ||
20 | include {{=machine}}-user-patches.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend new file mode 100644 index 0000000000..25c87a85ac --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend | |||
@@ -0,0 +1,25 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
7 | |||
8 | {{ if need_new_kbranch == "y": }} | ||
9 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
10 | |||
11 | {{ if need_new_kbranch == "n": }} | ||
12 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
13 | |||
14 | {{ if need_new_kbranch == "n": }} | ||
15 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
16 | |||
17 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} | ||
18 | {{ if smp == "y": }} | ||
19 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
20 | |||
21 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
22 | file://{{=machine}}-user-config.cfg \ | ||
23 | file://{{=machine}}-user-patches.scc \ | ||
24 | file://{{=machine}}-user-features.scc \ | ||
25 | " | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend new file mode 100644 index 0000000000..08b1f88d1b --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-preempt-rt.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" | ||
31 | #SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" | ||
32 | #LINUX_VERSION = "3.10.9" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend new file mode 100644 index 0000000000..bc6968d832 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.10.9" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend new file mode 100644 index 0000000000..d221d5f2a4 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.14" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend new file mode 100644 index 0000000000..c1f26540a7 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "19f7e43b54aef08d58135ed2a897d77b624b320a" | ||
31 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "459165c1dd61c4e843c36e6a1abeb30949a20ba7" | ||
32 | #LINUX_VERSION = "3.10.9" \ No newline at end of file | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend new file mode 100644 index 0000000000..948d568cd1 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/i386/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.14" \ No newline at end of file | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/layer/COPYING.MIT b/scripts/lib/bsp/substrate/target/arch/layer/COPYING.MIT new file mode 100644 index 0000000000..89de354795 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/COPYING.MIT | |||
@@ -0,0 +1,17 @@ | |||
1 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
2 | of this software and associated documentation files (the "Software"), to deal | ||
3 | in the Software without restriction, including without limitation the rights | ||
4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
5 | copies of the Software, and to permit persons to whom the Software is | ||
6 | furnished to do so, subject to the following conditions: | ||
7 | |||
8 | The above copyright notice and this permission notice shall be included in | ||
9 | all copies or substantial portions of the Software. | ||
10 | |||
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
17 | THE SOFTWARE. | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/layer/README b/scripts/lib/bsp/substrate/target/arch/layer/README new file mode 100644 index 0000000000..943dfc4412 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/README | |||
@@ -0,0 +1,64 @@ | |||
1 | This README file contains information on the contents of the | ||
2 | {{=layer_name}} layer. | ||
3 | |||
4 | Please see the corresponding sections below for details. | ||
5 | |||
6 | |||
7 | Dependencies | ||
8 | ============ | ||
9 | |||
10 | This layer depends on: | ||
11 | |||
12 | URI: git://git.openembedded.org/bitbake | ||
13 | branch: master | ||
14 | |||
15 | URI: git://git.openembedded.org/openembedded-core | ||
16 | layers: meta | ||
17 | branch: master | ||
18 | |||
19 | URI: git://git.yoctoproject.org/xxxx | ||
20 | layers: xxxx | ||
21 | branch: master | ||
22 | |||
23 | |||
24 | Patches | ||
25 | ======= | ||
26 | |||
27 | Please submit any patches against the {{=layer_name}} layer to the | ||
28 | xxxx mailing list (xxxx@zzzz.org) and cc: the maintainer: | ||
29 | |||
30 | Maintainer: XXX YYYYYY <xxx.yyyyyy@zzzzz.com> | ||
31 | |||
32 | |||
33 | Table of Contents | ||
34 | ================= | ||
35 | |||
36 | I. Adding the {{=layer_name}} layer to your build | ||
37 | II. Misc | ||
38 | |||
39 | |||
40 | I. Adding the {{=layer_name}} layer to your build | ||
41 | ================================================= | ||
42 | |||
43 | --- replace with specific instructions for the {{=layer_name}} layer --- | ||
44 | |||
45 | In order to use this layer, you need to make the build system aware of | ||
46 | it. | ||
47 | |||
48 | Assuming the {{=layer_name}} layer exists at the top-level of your | ||
49 | yocto build tree, you can add it to the build system by adding the | ||
50 | location of the {{=layer_name}} layer to bblayers.conf, along with any | ||
51 | other layers needed. e.g.: | ||
52 | |||
53 | BBLAYERS ?= " \ | ||
54 | /path/to/yocto/meta \ | ||
55 | /path/to/yocto/meta-yocto \ | ||
56 | /path/to/yocto/meta-yocto-bsp \ | ||
57 | /path/to/yocto/meta-{{=layer_name}} \ | ||
58 | " | ||
59 | |||
60 | |||
61 | II. Misc | ||
62 | ======== | ||
63 | |||
64 | --- replace with specific information about the {{=layer_name}} layer --- | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/layer/conf/layer.conf b/scripts/lib/bsp/substrate/target/arch/layer/conf/layer.conf new file mode 100644 index 0000000000..bdffe17195 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/conf/layer.conf | |||
@@ -0,0 +1,10 @@ | |||
1 | # We have a conf and classes directory, add to BBPATH | ||
2 | BBPATH .= ":${LAYERDIR}" | ||
3 | |||
4 | # We have recipes-* directories, add to BBFILES | ||
5 | BBFILES += "${LAYERDIR}/recipes-*/*/*.bb \ | ||
6 | ${LAYERDIR}/recipes-*/*/*.bbappend" | ||
7 | |||
8 | BBFILE_COLLECTIONS += "{{=layer_name}}" | ||
9 | BBFILE_PATTERN_{{=layer_name}} = "^${LAYERDIR}/" | ||
10 | BBFILE_PRIORITY_{{=layer_name}} = "{{=layer_priority}}" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall b/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall new file mode 100644 index 0000000000..e2a89c3b5d --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/layer-questions.noinstall | |||
@@ -0,0 +1,14 @@ | |||
1 | {{ input type:"edit" name:"layer_priority" prio:"20" msg:"Please enter the layer priority you'd like to use for the layer:" default:"6"}} | ||
2 | |||
3 | {{ input type:"boolean" name:"create_example_recipe" prio:"20" msg:"Would you like to have an example recipe created? (y/n)" default:"n"}} | ||
4 | |||
5 | {{ if create_example_recipe == "y": }} | ||
6 | {{ input type:"edit" name:"example_recipe_name" prio:"20" msg:"Please enter the name you'd like to use for your example recipe:" default:"example"}} | ||
7 | |||
8 | {{ input type:"boolean" name:"create_example_bbappend" prio:"20" msg:"Would you like to have an example bbappend file created? (y/n)" default:"n"}} | ||
9 | |||
10 | {{ if create_example_bbappend == "y": }} | ||
11 | {{ input type:"edit" name:"example_bbappend_name" prio:"20" msg:"Please enter the name you'd like to use for your bbappend file:" default:"example"}} | ||
12 | |||
13 | {{ if create_example_bbappend == "y": }} | ||
14 | {{ input type:"edit" name:"example_bbappend_version" prio:"20" msg:"Please enter the version number you'd like to use for your bbappend file (this should match the recipe you're appending to):" default:"0.1"}} | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == "y": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}-{{=example_bbappend_version}}/example.patch b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == "y": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}-{{=example_bbappend_version}}/example.patch new file mode 100644 index 0000000000..2000a34da5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == "y": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}-{{=example_bbappend_version}}/example.patch | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # This is a non-functional placeholder file, here for example purposes | ||
3 | # only. | ||
4 | # | ||
5 | # If you had a patch for your recipe, you'd put it in this directory | ||
6 | # and reference it from your recipe's SRC_URI: | ||
7 | # | ||
8 | # SRC_URI += "file://example.patch" | ||
9 | # | ||
10 | # Note that you could also rename the directory containing this patch | ||
11 | # to remove the version number or simply rename it 'files'. Doing so | ||
12 | # allows you to use the same directory for multiple recipes. | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == "y": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}_{{=example_bbappend_version}}.bbappend b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == "y": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}_{{=example_bbappend_version}}.bbappend new file mode 100644 index 0000000000..2e50ff668d --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_bbappend == "y": }} recipes-example-bbappend/example-bbappend/{{=example_bbappend_name}}_{{=example_bbappend_version}}.bbappend | |||
@@ -0,0 +1,8 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}-${PV}:" | ||
2 | |||
3 | # | ||
4 | # This .bbappend doesn't yet do anything - replace this text with | ||
5 | # modifications to the example_0.1.bb recipe, or whatever recipe it is | ||
6 | # that you want to modify with this .bbappend (make sure you change | ||
7 | # the recipe name (PN) and version (PV) to match). | ||
8 | # | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == "y": }} recipes-example/example/{{=example_recipe_name}}-0.1/example.patch b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == "y": }} recipes-example/example/{{=example_recipe_name}}-0.1/example.patch new file mode 100644 index 0000000000..2000a34da5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == "y": }} recipes-example/example/{{=example_recipe_name}}-0.1/example.patch | |||
@@ -0,0 +1,12 @@ | |||
1 | # | ||
2 | # This is a non-functional placeholder file, here for example purposes | ||
3 | # only. | ||
4 | # | ||
5 | # If you had a patch for your recipe, you'd put it in this directory | ||
6 | # and reference it from your recipe's SRC_URI: | ||
7 | # | ||
8 | # SRC_URI += "file://example.patch" | ||
9 | # | ||
10 | # Note that you could also rename the directory containing this patch | ||
11 | # to remove the version number or simply rename it 'files'. Doing so | ||
12 | # allows you to use the same directory for multiple recipes. | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == "y": }} recipes-example/example/{{=example_recipe_name}}-0.1/helloworld.c b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == "y": }} recipes-example/example/{{=example_recipe_name}}-0.1/helloworld.c new file mode 100644 index 0000000000..71f2e46b4e --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == "y": }} recipes-example/example/{{=example_recipe_name}}-0.1/helloworld.c | |||
@@ -0,0 +1,8 @@ | |||
1 | #include <stdio.h> | ||
2 | |||
3 | int main(int argc, char **argv) | ||
4 | { | ||
5 | printf("Hello World!\n"); | ||
6 | |||
7 | return 0; | ||
8 | } | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == "y": }} recipes-example/example/{{=example_recipe_name}}_0.1.bb b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == "y": }} recipes-example/example/{{=example_recipe_name}}_0.1.bb new file mode 100644 index 0000000000..14bf344da5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/layer/{{ if create_example_recipe == "y": }} recipes-example/example/{{=example_recipe_name}}_0.1.bb | |||
@@ -0,0 +1,23 @@ | |||
1 | # | ||
2 | # This file was derived from the 'Hello World!' example recipe in the | ||
3 | # Yocto Project Development Manual. | ||
4 | # | ||
5 | |||
6 | DESCRIPTION = "Simple helloworld application" | ||
7 | SECTION = "examples" | ||
8 | LICENSE = "MIT" | ||
9 | LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302" | ||
10 | PR = "r0" | ||
11 | |||
12 | SRC_URI = "file://helloworld.c" | ||
13 | |||
14 | S = "${WORKDIR}" | ||
15 | |||
16 | do_compile() { | ||
17 | ${CC} helloworld.c -o helloworld | ||
18 | } | ||
19 | |||
20 | do_install() { | ||
21 | install -d ${D}${bindir} | ||
22 | install -m 0755 helloworld ${D}${bindir} | ||
23 | } | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/.gitignore b/scripts/lib/bsp/substrate/target/arch/mips/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/.gitignore | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/mips/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..2e704263e1 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/conf/machine/{{=machine}}.conf | |||
@@ -0,0 +1,38 @@ | |||
1 | #@TYPE: Machine | ||
2 | #@NAME: {{=machine}} | ||
3 | |||
4 | #@DESCRIPTION: Machine configuration for {{=machine}} systems | ||
5 | |||
6 | require conf/machine/include/tune-mips32.inc | ||
7 | |||
8 | MACHINE_FEATURES = "screen keyboard pci usbhost ext2 ext3 serial" | ||
9 | |||
10 | KERNEL_IMAGETYPE = "vmlinux" | ||
11 | KERNEL_ALT_IMAGETYPE = "vmlinux.bin" | ||
12 | KERNEL_IMAGE_STRIP_EXTRA_SECTIONS = ".comment" | ||
13 | |||
14 | {{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} | ||
15 | {{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} | ||
16 | {{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} | ||
17 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
18 | |||
19 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} | ||
20 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} | ||
21 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} | ||
22 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
23 | PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" | ||
24 | |||
25 | {{ input type:"boolean" name:"xserver" prio:"50" msg:"Do you need support for X? (y/n)" default:"y" }} | ||
26 | {{ if xserver == "y": }} | ||
27 | PREFERRED_PROVIDER_virtual/xserver ?= "xserver-xorg" | ||
28 | XSERVER ?= "xserver-xorg \ | ||
29 | xf86-input-evdev \ | ||
30 | xf86-video-fbdev" | ||
31 | |||
32 | SERIAL_CONSOLE = "115200 ttyS0" | ||
33 | USE_VT ?= "0" | ||
34 | |||
35 | MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" | ||
36 | |||
37 | IMAGE_FSTYPES ?= "jffs2 tar.bz2" | ||
38 | JFFS2_ERASEBLOCK = "0x10000" \ No newline at end of file | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/kernel-list.noinstall | |||
@@ -0,0 +1,5 @@ | |||
1 | {{ if kernel_choice != "custom": }} | ||
2 | {{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} | ||
3 | |||
4 | {{ if kernel_choice != "custom" and use_default_kernel == "n": }} | ||
5 | {{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc new file mode 100644 index 0000000000..b0fb63ac6a --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE preempt-rt | ||
3 | define KARCH mips | ||
4 | |||
5 | include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc new file mode 100644 index 0000000000..326663a509 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE standard | ||
3 | define KARCH mips | ||
4 | |||
5 | include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc new file mode 100644 index 0000000000..4514765eb3 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE tiny | ||
3 | define KARCH mips | ||
4 | |||
5 | include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg new file mode 100644 index 0000000000..a1b333ca56 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg | |||
@@ -0,0 +1 @@ | |||
CONFIG_MIPS=y | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc new file mode 100644 index 0000000000..1ef01b6e3c --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc | |||
@@ -0,0 +1,7 @@ | |||
1 | kconf hardware {{=machine}}.cfg | ||
2 | |||
3 | include cfg/usb-mass-storage.scc | ||
4 | include cfg/fs/vfat.scc | ||
5 | |||
6 | kconf hardware {{=machine}}-user-config.cfg | ||
7 | include {{=machine}}-user-patches.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend new file mode 100644 index 0000000000..25c87a85ac --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend | |||
@@ -0,0 +1,25 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
7 | |||
8 | {{ if need_new_kbranch == "y": }} | ||
9 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
10 | |||
11 | {{ if need_new_kbranch == "n": }} | ||
12 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
13 | |||
14 | {{ if need_new_kbranch == "n": }} | ||
15 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
16 | |||
17 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} | ||
18 | {{ if smp == "y": }} | ||
19 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
20 | |||
21 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
22 | file://{{=machine}}-user-config.cfg \ | ||
23 | file://{{=machine}}-user-patches.scc \ | ||
24 | file://{{=machine}}-user-features.scc \ | ||
25 | " | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend new file mode 100644 index 0000000000..08b1f88d1b --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-preempt-rt.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" | ||
31 | #SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" | ||
32 | #LINUX_VERSION = "3.10.9" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend new file mode 100644 index 0000000000..bc6968d832 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.10.9" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend new file mode 100644 index 0000000000..85544e812c --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.14" \ No newline at end of file | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend new file mode 100644 index 0000000000..1e814c54d7 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "19f7e43b54aef08d58135ed2a897d77b624b320a" | ||
31 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "459165c1dd61c4e843c36e6a1abeb30949a20ba7" | ||
32 | #LINUX_VERSION = "3.10.9" \ No newline at end of file | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend new file mode 100644 index 0000000000..ca7f8c5978 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/mips/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.14" \ No newline at end of file | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/.gitignore b/scripts/lib/bsp/substrate/target/arch/powerpc/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/.gitignore | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/powerpc/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..78fb5db22b --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/conf/machine/{{=machine}}.conf | |||
@@ -0,0 +1,74 @@ | |||
1 | #@TYPE: Machine | ||
2 | #@NAME: {{=machine}} | ||
3 | |||
4 | #@DESCRIPTION: Machine configuration for {{=machine}} systems | ||
5 | |||
6 | TARGET_FPU = "" | ||
7 | |||
8 | {{ input type:"choicelist" name:"tunefile" prio:"40" msg:"Which machine tuning would you like to use?" default:"tune_ppce300c3" }} | ||
9 | {{ input type:"choice" val:"tune_ppc476" msg:"ppc476 tuning optimizations" }} | ||
10 | {{ input type:"choice" val:"tune_ppc603e" msg:"ppc603e tuning optimizations" }} | ||
11 | {{ input type:"choice" val:"tune_ppc7400" msg:"ppc7400 tuning optimizations" }} | ||
12 | {{ input type:"choice" val:"tune_ppce300c2" msg:"ppce300c2 tuning optimizations" }} | ||
13 | {{ input type:"choice" val:"tune_ppce300c3" msg:"ppce300c3 tuning optimizations" }} | ||
14 | {{ input type:"choice" val:"tune_ppce500" msg:"ppce500 tuning optimizations" }} | ||
15 | {{ input type:"choice" val:"tune_ppce500mc" msg:"ppce500mc tuning optimizations" }} | ||
16 | {{ input type:"choice" val:"tune_ppce500v2" msg:"ppce500v2 tuning optimizations" }} | ||
17 | {{ input type:"choice" val:"tune_ppce5500" msg:"ppce5500 tuning optimizations" }} | ||
18 | {{ input type:"choice" val:"tune_ppce6500" msg:"ppce6500 tuning optimizations" }} | ||
19 | {{ if tunefile == "tune_ppc476": }} | ||
20 | include conf/machine/include/tune-ppc476.inc | ||
21 | {{ if tunefile == "tune_ppc603e": }} | ||
22 | include conf/machine/include/tune-ppc603e.inc | ||
23 | {{ if tunefile == "tune_ppc7400": }} | ||
24 | include conf/machine/include/tune-ppc7400.inc | ||
25 | {{ if tunefile == "tune_ppce300c2": }} | ||
26 | include conf/machine/include/tune-ppce300c2.inc | ||
27 | {{ if tunefile == "tune_ppce300c3": }} | ||
28 | include conf/machine/include/tune-ppce300c3.inc | ||
29 | {{ if tunefile == "tune_ppce500": }} | ||
30 | include conf/machine/include/tune-ppce500.inc | ||
31 | {{ if tunefile == "tune_ppce500mc": }} | ||
32 | include conf/machine/include/tune-ppce500mc.inc | ||
33 | {{ if tunefile == "tune_ppce500v2": }} | ||
34 | include conf/machine/include/tune-ppce500v2.inc | ||
35 | {{ if tunefile == "tune_ppce5500": }} | ||
36 | include conf/machine/include/tune-ppce5500.inc | ||
37 | {{ if tunefile == "tune_ppce6500": }} | ||
38 | include conf/machine/include/tune-ppce6500.inc | ||
39 | |||
40 | KERNEL_IMAGETYPE = "uImage" | ||
41 | |||
42 | EXTRA_IMAGEDEPENDS += "u-boot" | ||
43 | UBOOT_MACHINE_{{=machine}} = "MPC8315ERDB_config" | ||
44 | |||
45 | SERIAL_CONSOLE = "115200 ttyS0" | ||
46 | |||
47 | MACHINE_FEATURES = "keyboard pci ext2 ext3 serial" | ||
48 | |||
49 | {{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} | ||
50 | {{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} | ||
51 | {{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} | ||
52 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
53 | |||
54 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} | ||
55 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} | ||
56 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} | ||
57 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
58 | PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" | ||
59 | |||
60 | {{ input type:"boolean" name:"xserver" prio:"50" msg:"Do you need support for X? (y/n)" default:"y" }} | ||
61 | {{ if xserver == "y": }} | ||
62 | PREFERRED_PROVIDER_virtual/xserver ?= "xserver-xorg" | ||
63 | XSERVER ?= "xserver-xorg \ | ||
64 | xf86-input-evdev \ | ||
65 | xf86-video-fbdev" | ||
66 | |||
67 | PREFERRED_VERSION_u-boot ?= "v2013.07%" | ||
68 | {{ input type:"edit" name:"uboot_entrypoint" prio:"40" msg:"Please specify a value for UBOOT_ENTRYPOINT:" default:"0x00000000" }} | ||
69 | UBOOT_ENTRYPOINT = "{{=uboot_entrypoint}}" | ||
70 | |||
71 | {{ input type:"edit" name:"kernel_devicetree" prio:"40" msg:"Please specify a [arch/powerpc/boot/dts/xxx] value for KERNEL_DEVICETREE:" default:"mpc8315erdb.dts" }} | ||
72 | KERNEL_DEVICETREE = "${S}/arch/powerpc/boot/dts/{{=kernel_devicetree}}" | ||
73 | |||
74 | MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/kernel-list.noinstall | |||
@@ -0,0 +1,5 @@ | |||
1 | {{ if kernel_choice != "custom": }} | ||
2 | {{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} | ||
3 | |||
4 | {{ if kernel_choice != "custom" and use_default_kernel == "n": }} | ||
5 | {{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc new file mode 100644 index 0000000000..1da7b0c892 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE preempt-rt | ||
3 | define KARCH powerpc | ||
4 | |||
5 | include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc new file mode 100644 index 0000000000..53a74a6ca2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE standard | ||
3 | define KARCH powerpc | ||
4 | |||
5 | include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc new file mode 100644 index 0000000000..4ca6224774 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE tiny | ||
3 | define KARCH powerpc | ||
4 | |||
5 | include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg new file mode 100644 index 0000000000..9f37d07553 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg | |||
@@ -0,0 +1,163 @@ | |||
1 | .......................................................................... | ||
2 | . WARNING | ||
3 | . | ||
4 | . This file is a kernel configuration fragment, and not a full kernel | ||
5 | . configuration file. The final kernel configuration is made up of | ||
6 | . an assembly of processed fragments, each of which is designed to | ||
7 | . capture a specific part of the final configuration (e.g. platform | ||
8 | . configuration, feature configuration, and board specific hardware | ||
9 | . configuration). For more information on kernel configuration, please | ||
10 | . consult the product documentation. | ||
11 | . | ||
12 | .......................................................................... | ||
13 | CONFIG_PPC32=y | ||
14 | CONFIG_PPC_OF=y | ||
15 | CONFIG_PPC_UDBG_16550=y | ||
16 | |||
17 | # | ||
18 | # Processor support | ||
19 | # | ||
20 | CONFIG_PPC_83xx=y | ||
21 | |||
22 | # | ||
23 | # Platform support | ||
24 | # | ||
25 | CONFIG_MPC831x_RDB=y | ||
26 | # CONFIG_PPC_CHRP is not set | ||
27 | # CONFIG_PPC_PMAC is not set | ||
28 | |||
29 | # | ||
30 | # Bus options | ||
31 | # | ||
32 | CONFIG_PCI=y | ||
33 | |||
34 | # | ||
35 | # Memory Technology Devices (MTD) | ||
36 | # | ||
37 | CONFIG_MTD=y | ||
38 | CONFIG_MTD_PARTITIONS=y | ||
39 | CONFIG_MTD_CMDLINE_PARTS=y | ||
40 | CONFIG_MTD_OF_PARTS=y | ||
41 | |||
42 | # | ||
43 | # User Modules And Translation Layers | ||
44 | # | ||
45 | CONFIG_MTD_CHAR=y | ||
46 | CONFIG_MTD_BLOCK=y | ||
47 | |||
48 | # | ||
49 | # RAM/ROM/Flash chip drivers | ||
50 | # | ||
51 | CONFIG_MTD_CFI=y | ||
52 | CONFIG_MTD_CFI_AMDSTD=y | ||
53 | |||
54 | # | ||
55 | # Mapping drivers for chip access | ||
56 | # | ||
57 | CONFIG_MTD_PHYSMAP_OF=y | ||
58 | |||
59 | # | ||
60 | # NAND Flash Device Drivers | ||
61 | # | ||
62 | CONFIG_MTD_NAND=y | ||
63 | |||
64 | # | ||
65 | # Ethernet (1000 Mbit) | ||
66 | # | ||
67 | CONFIG_GIANFAR=y | ||
68 | |||
69 | # | ||
70 | # Serial drivers | ||
71 | # | ||
72 | CONFIG_SERIAL_8250=y | ||
73 | CONFIG_SERIAL_8250_CONSOLE=y | ||
74 | CONFIG_SERIAL_8250_NR_UARTS=2 | ||
75 | |||
76 | # | ||
77 | # Watchdog Device Drivers | ||
78 | # | ||
79 | CONFIG_8xxx_WDT=y | ||
80 | |||
81 | # | ||
82 | # I2C support | ||
83 | # | ||
84 | CONFIG_I2C=y | ||
85 | CONFIG_I2C_CHARDEV=y | ||
86 | |||
87 | # | ||
88 | # I2C Hardware Bus support | ||
89 | # | ||
90 | CONFIG_I2C_MPC=y | ||
91 | |||
92 | CONFIG_SENSORS_LM75=y | ||
93 | |||
94 | CONFIG_MISC_DEVICES=y | ||
95 | |||
96 | # | ||
97 | # Miscellaneous I2C Chip support | ||
98 | # | ||
99 | CONFIG_EEPROM_AT24=y | ||
100 | |||
101 | # | ||
102 | # SPI support | ||
103 | # | ||
104 | CONFIG_SPI=y | ||
105 | # CONFIG_SPI_DEBUG is not set | ||
106 | CONFIG_SPI_MASTER=y | ||
107 | |||
108 | # | ||
109 | # SPI Master Controller Drivers | ||
110 | # | ||
111 | CONFIG_SPI_MPC8xxx=y | ||
112 | |||
113 | # | ||
114 | # SPI Protocol Masters | ||
115 | # | ||
116 | CONFIG_HWMON=y | ||
117 | |||
118 | # | ||
119 | # SCSI device support | ||
120 | # | ||
121 | CONFIG_SCSI=y | ||
122 | CONFIG_BLK_DEV_SD=y | ||
123 | CONFIG_CHR_DEV_SG=y | ||
124 | CONFIG_SCSI_LOGGING=y | ||
125 | |||
126 | CONFIG_ATA=y | ||
127 | CONFIG_ATA_VERBOSE_ERROR=y | ||
128 | CONFIG_SATA_FSL=y | ||
129 | CONFIG_ATA_SFF=y | ||
130 | |||
131 | # | ||
132 | # USB support | ||
133 | # | ||
134 | CONFIG_USB=m | ||
135 | CONFIG_USB_DEVICEFS=y | ||
136 | |||
137 | # | ||
138 | # USB Host Controller Drivers | ||
139 | # | ||
140 | CONFIG_USB_EHCI_HCD=m | ||
141 | CONFIG_USB_EHCI_FSL=y | ||
142 | CONFIG_USB_STORAGE=m | ||
143 | |||
144 | # | ||
145 | # Real Time Clock | ||
146 | # | ||
147 | CONFIG_RTC_CLASS=y | ||
148 | |||
149 | # | ||
150 | # I2C RTC drivers | ||
151 | # | ||
152 | CONFIG_RTC_DRV_DS1307=y | ||
153 | |||
154 | CONFIG_KGDB_8250=m | ||
155 | |||
156 | CONFIG_CRYPTO_DEV_TALITOS=m | ||
157 | |||
158 | CONFIG_FSL_DMA=y | ||
159 | |||
160 | CONFIG_MMC=y | ||
161 | CONFIG_MMC_SPI=m | ||
162 | |||
163 | CONFIG_USB_FSL_MPH_DR_OF=y | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc new file mode 100644 index 0000000000..c9fd468180 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | kconf hardware {{=machine}}.cfg | ||
2 | |||
3 | include cfg/usb-mass-storage.scc | ||
4 | include cfg/fs/vfat.scc | ||
5 | |||
6 | include cfg/dmaengine.scc | ||
7 | |||
8 | kconf hardware {{=machine}}-user-config.cfg | ||
9 | include {{=machine}}-user-patches.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend new file mode 100644 index 0000000000..25c87a85ac --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend | |||
@@ -0,0 +1,25 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
7 | |||
8 | {{ if need_new_kbranch == "y": }} | ||
9 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
10 | |||
11 | {{ if need_new_kbranch == "n": }} | ||
12 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
13 | |||
14 | {{ if need_new_kbranch == "n": }} | ||
15 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
16 | |||
17 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} | ||
18 | {{ if smp == "y": }} | ||
19 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
20 | |||
21 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
22 | file://{{=machine}}-user-config.cfg \ | ||
23 | file://{{=machine}}-user-patches.scc \ | ||
24 | file://{{=machine}}-user-features.scc \ | ||
25 | " | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend new file mode 100644 index 0000000000..00c8c68933 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-preempt-rt.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" | ||
31 | #SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" | ||
32 | #LINUX_VERSION = "3.10.9" \ No newline at end of file | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend new file mode 100644 index 0000000000..bc6968d832 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.10.9" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend new file mode 100644 index 0000000000..d221d5f2a4 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.14" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend new file mode 100644 index 0000000000..a61f5ccb80 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "19f7e43b54aef08d58135ed2a897d77b624b320a" | ||
31 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "459165c1dd61c4e843c36e6a1abeb30949a20ba7" | ||
32 | #LINUX_VERSION = "3.10.9" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend new file mode 100644 index 0000000000..aebda9b3a5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/powerpc/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.14" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/qemu/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..782ac217d9 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/conf/machine/{{=machine}}.conf | |||
@@ -0,0 +1,68 @@ | |||
1 | #@TYPE: Machine | ||
2 | #@NAME: {{=machine}} | ||
3 | |||
4 | #@DESCRIPTION: Machine configuration for {{=machine}} systems | ||
5 | |||
6 | {{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} | ||
7 | {{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} | ||
8 | {{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} | ||
9 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
10 | |||
11 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} | ||
12 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} | ||
13 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} | ||
14 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
15 | PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" | ||
16 | |||
17 | {{ if qemuarch == "i386" or qemuarch == "x86_64": }} | ||
18 | PREFERRED_PROVIDER_virtual/xserver ?= "xserver-xorg" | ||
19 | PREFERRED_PROVIDER_virtual/libgl ?= "mesa" | ||
20 | PREFERRED_PROVIDER_virtual/libgles1 ?= "mesa" | ||
21 | PREFERRED_PROVIDER_virtual/libgles2 ?= "mesa" | ||
22 | |||
23 | {{ input type:"choicelist" name:"qemuarch" prio:"5" msg:"Which qemu architecture would you like to use?" default:"i386" }} | ||
24 | {{ input type:"choice" val:"i386" msg:"i386 (32-bit)" }} | ||
25 | {{ input type:"choice" val:"x86_64" msg:"x86_64 (64-bit)" }} | ||
26 | {{ input type:"choice" val:"arm" msg:"ARM (32-bit)" }} | ||
27 | {{ input type:"choice" val:"powerpc" msg:"PowerPC (32-bit)" }} | ||
28 | {{ input type:"choice" val:"mips" msg:"MIPS (32-bit)" }} | ||
29 | {{ if qemuarch == "i386": }} | ||
30 | require conf/machine/include/qemu.inc | ||
31 | require conf/machine/include/tune-i586.inc | ||
32 | {{ if qemuarch == "x86_64": }} | ||
33 | require conf/machine/include/qemu.inc | ||
34 | require conf/machine/include/tune-x86_64.inc | ||
35 | {{ if qemuarch == "arm": }} | ||
36 | require conf/machine/include/qemu.inc | ||
37 | require conf/machine/include/tune-arm926ejs.inc | ||
38 | {{ if qemuarch == "powerpc": }} | ||
39 | require conf/machine/include/qemu.inc | ||
40 | require conf/machine/include/tune-ppc603e.inc | ||
41 | {{ if qemuarch == "mips": }} | ||
42 | require conf/machine/include/qemu.inc | ||
43 | require conf/machine/include/tune-mips32.inc | ||
44 | |||
45 | {{ if qemuarch == "i386" or qemuarch == "x86_64": }} | ||
46 | MACHINE_FEATURES += "x86" | ||
47 | KERNEL_IMAGETYPE = "bzImage" | ||
48 | SERIAL_CONSOLE = "115200 ttyS0" | ||
49 | XSERVER = "xserver-xorg \ | ||
50 | ${@base_contains('DISTRO_FEATURES', 'opengl', 'mesa-driver-swrast', '', d)} \ | ||
51 | xf86-input-vmmouse \ | ||
52 | xf86-input-keyboard \ | ||
53 | xf86-input-evdev \ | ||
54 | xf86-video-vmware" | ||
55 | |||
56 | {{ if qemuarch == "arm": }} | ||
57 | KERNEL_IMAGETYPE = "zImage" | ||
58 | SERIAL_CONSOLE = "115200 ttyAMA0" | ||
59 | |||
60 | {{ if qemuarch == "powerpc": }} | ||
61 | KERNEL_IMAGETYPE = "vmlinux" | ||
62 | SERIAL_CONSOLE = "115200 ttyS0" | ||
63 | |||
64 | {{ if qemuarch == "mips": }} | ||
65 | KERNEL_IMAGETYPE = "vmlinux" | ||
66 | KERNEL_ALT_IMAGETYPE = "vmlinux.bin" | ||
67 | SERIAL_CONSOLE = "115200 ttyS0" | ||
68 | MACHINE_EXTRA_RRECOMMENDS = " kernel-modules" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown/{{=machine}}/interfaces b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown/{{=machine}}/interfaces new file mode 100644 index 0000000000..16967763e5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown/{{=machine}}/interfaces | |||
@@ -0,0 +1,5 @@ | |||
1 | # /etc/network/interfaces -- configuration file for ifup(8), ifdown(8) | ||
2 | |||
3 | # The loopback interface | ||
4 | auto lo | ||
5 | iface lo inet loopback | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown_1.0.bbappend b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown_1.0.bbappend new file mode 100644 index 0000000000..72d991c7e5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-core/init-ifupdown/init-ifupdown_1.0.bbappend | |||
@@ -0,0 +1 @@ | |||
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/xorg.conf b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/xorg.conf new file mode 100644 index 0000000000..13519804bc --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/xorg.conf | |||
@@ -0,0 +1,77 @@ | |||
1 | |||
2 | Section "Files" | ||
3 | EndSection | ||
4 | |||
5 | Section "InputDevice" | ||
6 | Identifier "Generic Keyboard" | ||
7 | Driver "evdev" | ||
8 | Option "CoreKeyboard" | ||
9 | Option "Device" "/dev/input/by-path/platform-i8042-serio-0-event-kbd" | ||
10 | Option "XkbRules" "xorg" | ||
11 | Option "XkbModel" "evdev" | ||
12 | Option "XkbLayout" "us" | ||
13 | EndSection | ||
14 | |||
15 | Section "InputDevice" | ||
16 | Identifier "Configured Mouse" | ||
17 | {{ if qemuarch == "arm" or qemuarch == "powerpc" or qemuarch == "mips": }} | ||
18 | Driver "mouse" | ||
19 | {{ if qemuarch == "i386" or qemuarch == "x86_64": }} | ||
20 | Driver "vmmouse" | ||
21 | |||
22 | Option "CorePointer" | ||
23 | Option "Device" "/dev/input/mice" | ||
24 | Option "Protocol" "ImPS/2" | ||
25 | Option "ZAxisMapping" "4 5" | ||
26 | Option "Emulate3Buttons" "true" | ||
27 | EndSection | ||
28 | |||
29 | Section "InputDevice" | ||
30 | Identifier "Qemu Tablet" | ||
31 | Driver "evdev" | ||
32 | Option "CorePointer" | ||
33 | Option "Device" "/dev/input/touchscreen0" | ||
34 | Option "USB" "on" | ||
35 | EndSection | ||
36 | |||
37 | Section "Device" | ||
38 | Identifier "Graphics Controller" | ||
39 | {{ if qemuarch == "arm" or qemuarch == "powerpc" or qemuarch == "mips": }} | ||
40 | Driver "fbdev" | ||
41 | {{ if qemuarch == "i386" or qemuarch == "x86_64": }} | ||
42 | Driver "vmware" | ||
43 | |||
44 | EndSection | ||
45 | |||
46 | Section "Monitor" | ||
47 | Identifier "Generic Monitor" | ||
48 | Option "DPMS" | ||
49 | # 1024x600 59.85 Hz (CVT) hsync: 37.35 kHz; pclk: 49.00 MHz | ||
50 | Modeline "1024x600_60.00" 49.00 1024 1072 1168 1312 600 603 613 624 -hsync +vsync | ||
51 | # 640x480 @ 60Hz (Industry standard) hsync: 31.5kHz | ||
52 | ModeLine "640x480" 25.2 640 656 752 800 480 490 492 525 -hsync -vsync | ||
53 | # 640x480 @ 72Hz (VESA) hsync: 37.9kHz | ||
54 | ModeLine "640x480" 31.5 640 664 704 832 480 489 491 520 -hsync -vsync | ||
55 | # 640x480 @ 75Hz (VESA) hsync: 37.5kHz | ||
56 | ModeLine "640x480" 31.5 640 656 720 840 480 481 484 500 -hsync -vsync | ||
57 | # 640x480 @ 85Hz (VESA) hsync: 43.3kHz | ||
58 | ModeLine "640x480" 36.0 640 696 752 832 480 481 484 509 -hsync -vsync | ||
59 | EndSection | ||
60 | |||
61 | Section "Screen" | ||
62 | Identifier "Default Screen" | ||
63 | Device "Graphics Controller" | ||
64 | Monitor "Generic Monitor" | ||
65 | SubSection "Display" | ||
66 | Modes "640x480" | ||
67 | EndSubSection | ||
68 | EndSection | ||
69 | |||
70 | Section "ServerLayout" | ||
71 | Identifier "Default Layout" | ||
72 | Screen "Default Screen" | ||
73 | InputDevice "Generic Keyboard" | ||
74 | # InputDevice "Configured Mouse" | ||
75 | InputDevice "QEMU Tablet" | ||
76 | Option "AllowEmptyInput" "no" | ||
77 | EndSection | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend new file mode 100644 index 0000000000..72d991c7e5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-graphics/xorg-xserver/xserver-xf86-config_0.1.bbappend | |||
@@ -0,0 +1 @@ | |||
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/kernel-list.noinstall | |||
@@ -0,0 +1,5 @@ | |||
1 | {{ if kernel_choice != "custom": }} | ||
2 | {{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} | ||
3 | |||
4 | {{ if kernel_choice != "custom" and use_default_kernel == "n": }} | ||
5 | {{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc new file mode 100644 index 0000000000..af34437d0a --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE preempt-rt | ||
3 | define KARCH {{=qemuarch}} | ||
4 | |||
5 | include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc new file mode 100644 index 0000000000..0e20023764 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc | |||
@@ -0,0 +1,16 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE standard | ||
3 | define KARCH {{=qemuarch}} | ||
4 | |||
5 | {{ if qemuarch == "i386" or qemuarch == "x86_64": }} | ||
6 | include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
7 | {{ if qemuarch == "arm": }} | ||
8 | include bsp/arm-versatile-926ejs/arm-versatile-926ejs-standard | ||
9 | {{ if qemuarch == "powerpc": }} | ||
10 | include bsp/qemu-ppc32/qemu-ppc32-standard | ||
11 | {{ if qemuarch == "mips": }} | ||
12 | include bsp/mti-malta32/mti-malta32-be-standard | ||
13 | {{ if need_new_kbranch == "y": }} | ||
14 | branch {{=machine}} | ||
15 | |||
16 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc new file mode 100644 index 0000000000..10c4dac44d --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE tiny | ||
3 | define KARCH {{=qemuarch}} | ||
4 | |||
5 | include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc new file mode 100644 index 0000000000..f3739be1e6 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc | |||
@@ -0,0 +1,4 @@ | |||
1 | kconf hardware {{=machine}}.cfg | ||
2 | |||
3 | kconf hardware {{=machine}}-user-config.cfg | ||
4 | include {{=machine}}-user-patches.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend new file mode 100644 index 0000000000..7599ecb0a5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend | |||
@@ -0,0 +1,49 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
7 | |||
8 | {{ if need_new_kbranch == "y" and qemuarch == "arm": }} | ||
9 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base your new BSP branch on:" default:"standard/base" }} | ||
10 | |||
11 | {{ if need_new_kbranch == "n" and qemuarch == "arm": }} | ||
12 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose an existing machine branch to use for this BSP:" default:"standard/arm-versatile-926ejs" }} | ||
13 | |||
14 | {{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} | ||
15 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
16 | |||
17 | {{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} | ||
18 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/qemuppc" }} | ||
19 | |||
20 | {{ if need_new_kbranch == "y" and qemuarch == "i386": }} | ||
21 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} | ||
22 | |||
23 | {{ if need_new_kbranch == "n" and qemuarch == "i386": }} | ||
24 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} | ||
25 | |||
26 | {{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} | ||
27 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} | ||
28 | |||
29 | {{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} | ||
30 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} | ||
31 | |||
32 | {{ if need_new_kbranch == "y" and qemuarch == "mips": }} | ||
33 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
34 | |||
35 | {{ if need_new_kbranch == "n" and qemuarch == "mips": }} | ||
36 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/mti-malta32" }} | ||
37 | |||
38 | {{ if need_new_kbranch == "n": }} | ||
39 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
40 | |||
41 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} | ||
42 | {{ if smp == "y": }} | ||
43 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
44 | |||
45 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
46 | file://{{=machine}}-user-config.cfg \ | ||
47 | file://{{=machine}}-user-patches.scc \ | ||
48 | file://{{=machine}}-user-features.scc \ | ||
49 | " | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend new file mode 100644 index 0000000000..73b6e34839 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend | |||
@@ -0,0 +1,55 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
7 | |||
8 | {{ if need_new_kbranch == "y" and qemuarch == "arm": }} | ||
9 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
10 | |||
11 | {{ if need_new_kbranch == "n" and qemuarch == "arm": }} | ||
12 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
13 | |||
14 | {{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} | ||
15 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
16 | |||
17 | {{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} | ||
18 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/qemuppc" }} | ||
19 | |||
20 | {{ if need_new_kbranch == "y" and qemuarch == "i386": }} | ||
21 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
22 | |||
23 | {{ if need_new_kbranch == "n" and qemuarch == "i386": }} | ||
24 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
25 | |||
26 | {{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} | ||
27 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
28 | |||
29 | {{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} | ||
30 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
31 | |||
32 | {{ if need_new_kbranch == "y" and qemuarch == "mips": }} | ||
33 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
34 | |||
35 | {{ if need_new_kbranch == "n" and qemuarch == "mips": }} | ||
36 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
37 | |||
38 | {{ if need_new_kbranch == "n": }} | ||
39 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
40 | |||
41 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
42 | {{ if smp == "y": }} | ||
43 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
44 | |||
45 | SRC_URI += "file://{{=machine}}-preempt-rt.scc \ | ||
46 | file://{{=machine}}-user-config.cfg \ | ||
47 | file://{{=machine}}-user-patches.scc \ | ||
48 | file://{{=machine}}-user-features.scc \ | ||
49 | " | ||
50 | |||
51 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
52 | # the appropriate changes committed to the upstream linux-yocto repo | ||
53 | #SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" | ||
54 | #SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" | ||
55 | #LINUX_VERSION = "3.10.35" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend new file mode 100644 index 0000000000..da4e61ef83 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend | |||
@@ -0,0 +1,55 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
7 | |||
8 | {{ if need_new_kbranch == "y" and qemuarch == "arm": }} | ||
9 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
10 | |||
11 | {{ if need_new_kbranch == "n" and qemuarch == "arm": }} | ||
12 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
13 | |||
14 | {{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} | ||
15 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
16 | |||
17 | {{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} | ||
18 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
19 | |||
20 | {{ if need_new_kbranch == "y" and qemuarch == "i386": }} | ||
21 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
22 | |||
23 | {{ if need_new_kbranch == "n" and qemuarch == "i386": }} | ||
24 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/common-pc" }} | ||
25 | |||
26 | {{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} | ||
27 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
28 | |||
29 | {{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} | ||
30 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
31 | |||
32 | {{ if need_new_kbranch == "y" and qemuarch == "mips": }} | ||
33 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
34 | |||
35 | {{ if need_new_kbranch == "n" and qemuarch == "mips": }} | ||
36 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
37 | |||
38 | {{ if need_new_kbranch == "n": }} | ||
39 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
40 | |||
41 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
42 | {{ if smp == "y": }} | ||
43 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
44 | |||
45 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
46 | file://{{=machine}}-user-config.cfg \ | ||
47 | file://{{=machine}}-user-patches.scc \ | ||
48 | file://{{=machine}}-user-features.scc \ | ||
49 | " | ||
50 | |||
51 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
52 | # the appropriate changes committed to the upstream linux-yocto repo | ||
53 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
54 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
55 | #LINUX_VERSION = "3.10.35" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.4.bbappend b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.4.bbappend new file mode 100644 index 0000000000..013883ffeb --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.4.bbappend | |||
@@ -0,0 +1,55 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
7 | |||
8 | {{ if need_new_kbranch == "y" and qemuarch == "arm": }} | ||
9 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
10 | |||
11 | {{ if need_new_kbranch == "n" and qemuarch == "arm": }} | ||
12 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"arm" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
13 | |||
14 | {{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} | ||
15 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
16 | |||
17 | {{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} | ||
18 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
19 | |||
20 | {{ if need_new_kbranch == "y" and qemuarch == "i386": }} | ||
21 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
22 | |||
23 | {{ if need_new_kbranch == "n" and qemuarch == "i386": }} | ||
24 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/common-pc" }} | ||
25 | |||
26 | {{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} | ||
27 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
28 | |||
29 | {{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} | ||
30 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
31 | |||
32 | {{ if need_new_kbranch == "y" and qemuarch == "mips": }} | ||
33 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
34 | |||
35 | {{ if need_new_kbranch == "n" and qemuarch == "mips": }} | ||
36 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
37 | |||
38 | {{ if need_new_kbranch == "n": }} | ||
39 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
40 | |||
41 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
42 | {{ if smp == "y": }} | ||
43 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
44 | |||
45 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
46 | file://{{=machine}}-user-config.cfg \ | ||
47 | file://{{=machine}}-user-patches.scc \ | ||
48 | file://{{=machine}}-user-features.scc \ | ||
49 | " | ||
50 | |||
51 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
52 | # the appropriate changes committed to the upstream linux-yocto repo | ||
53 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "0143c6ebb4a2d63b241df5f608b19f483f7eb9e0" | ||
54 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "8f55bee2403176a50cc0dd41811aa60fcf07243c" | ||
55 | #LINUX_VERSION = "3.14" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend new file mode 100644 index 0000000000..392ace6694 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend | |||
@@ -0,0 +1,55 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
7 | |||
8 | {{ if need_new_kbranch == "y" and qemuarch == "arm": }} | ||
9 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base your new BSP branch on:" default:"standard/base" }} | ||
10 | |||
11 | {{ if need_new_kbranch == "n" and qemuarch == "arm": }} | ||
12 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose an existing machine branch to use for this BSP:" default:"standard/arm-versatile-926ejs" }} | ||
13 | |||
14 | {{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} | ||
15 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
16 | |||
17 | {{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} | ||
18 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/qemuppc" }} | ||
19 | |||
20 | {{ if need_new_kbranch == "y" and qemuarch == "i386": }} | ||
21 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} | ||
22 | |||
23 | {{ if need_new_kbranch == "n" and qemuarch == "i386": }} | ||
24 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} | ||
25 | |||
26 | {{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} | ||
27 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} | ||
28 | |||
29 | {{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} | ||
30 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} | ||
31 | |||
32 | {{ if need_new_kbranch == "y" and qemuarch == "mips": }} | ||
33 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
34 | |||
35 | {{ if need_new_kbranch == "n" and qemuarch == "mips": }} | ||
36 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/mti-malta32" }} | ||
37 | |||
38 | {{ if need_new_kbranch == "n": }} | ||
39 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
40 | |||
41 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} | ||
42 | {{ if smp == "y": }} | ||
43 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
44 | |||
45 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
46 | file://{{=machine}}-user-config.cfg \ | ||
47 | file://{{=machine}}-user-patches.scc \ | ||
48 | file://{{=machine}}-user-features.scc \ | ||
49 | " | ||
50 | |||
51 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
52 | # the appropriate changes committed to the upstream linux-yocto repo | ||
53 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "b170394a475b96ecc92cbc9e4b002bed0a9f69c5" | ||
54 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "c2ed0f16fdec628242a682897d5d86df4547cf24" | ||
55 | #LINUX_VERSION = "3.10.35" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend new file mode 100644 index 0000000000..2cc9b87cf2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/qemu/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend | |||
@@ -0,0 +1,55 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
7 | |||
8 | {{ if need_new_kbranch == "y" and qemuarch == "arm": }} | ||
9 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base your new BSP branch on:" default:"standard/base" }} | ||
10 | |||
11 | {{ if need_new_kbranch == "n" and qemuarch == "arm": }} | ||
12 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose an existing machine branch to use for this BSP:" default:"standard/arm-versatile-926ejs" }} | ||
13 | |||
14 | {{ if need_new_kbranch == "y" and qemuarch == "powerpc": }} | ||
15 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
16 | |||
17 | {{ if need_new_kbranch == "n" and qemuarch == "powerpc": }} | ||
18 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"powerpc" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/qemuppc" }} | ||
19 | |||
20 | {{ if need_new_kbranch == "y" and qemuarch == "i386": }} | ||
21 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} | ||
22 | |||
23 | {{ if need_new_kbranch == "n" and qemuarch == "i386": }} | ||
24 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc/base" }} | ||
25 | |||
26 | {{ if need_new_kbranch == "y" and qemuarch == "x86_64": }} | ||
27 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} | ||
28 | |||
29 | {{ if need_new_kbranch == "n" and qemuarch == "x86_64": }} | ||
30 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"x86_64" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} | ||
31 | |||
32 | {{ if need_new_kbranch == "y" and qemuarch == "mips": }} | ||
33 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
34 | |||
35 | {{ if need_new_kbranch == "n" and qemuarch == "mips": }} | ||
36 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"mips" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/mti-malta32" }} | ||
37 | |||
38 | {{ if need_new_kbranch == "n": }} | ||
39 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
40 | |||
41 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} | ||
42 | {{ if smp == "y": }} | ||
43 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
44 | |||
45 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
46 | file://{{=machine}}-user-config.cfg \ | ||
47 | file://{{=machine}}-user-patches.scc \ | ||
48 | file://{{=machine}}-user-features.scc \ | ||
49 | " | ||
50 | |||
51 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
52 | # the appropriate changes committed to the upstream linux-yocto repo | ||
53 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "0143c6ebb4a2d63b241df5f608b19f483f7eb9e0" | ||
54 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "8f55bee2403176a50cc0dd41811aa60fcf07243c" | ||
55 | #LINUX_VERSION = "3.14" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/.gitignore b/scripts/lib/bsp/substrate/target/arch/x86_64/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/.gitignore | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/conf/machine/{{=machine}}.conf b/scripts/lib/bsp/substrate/target/arch/x86_64/conf/machine/{{=machine}}.conf new file mode 100644 index 0000000000..53e8e92e6d --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/conf/machine/{{=machine}}.conf | |||
@@ -0,0 +1,58 @@ | |||
1 | #@TYPE: Machine | ||
2 | #@NAME: {{=machine}} | ||
3 | |||
4 | #@DESCRIPTION: Machine configuration for {{=machine}} systems | ||
5 | |||
6 | {{ if kernel_choice == "custom": preferred_kernel = "linux-yocto-custom" }} | ||
7 | {{ if kernel_choice == "linux-yocto-dev": preferred_kernel = "linux-yocto-dev" }} | ||
8 | {{ if kernel_choice == "custom" or kernel_choice == "linux-yocto-dev" : }} | ||
9 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
10 | |||
11 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel = kernel_choice.split('_')[0] }} | ||
12 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": preferred_kernel_version = kernel_choice.split('_')[1] }} | ||
13 | {{ if kernel_choice != "custom" and kernel_choice != "linux-yocto-dev": }} | ||
14 | PREFERRED_PROVIDER_virtual/kernel ?= "{{=preferred_kernel}}" | ||
15 | PREFERRED_VERSION_{{=preferred_kernel}} ?= "{{=preferred_kernel_version}}%" | ||
16 | |||
17 | {{ input type:"choicelist" name:"tunefile" prio:"40" msg:"Which machine tuning would you like to use?" default:"tune_core2" }} | ||
18 | {{ input type:"choice" val:"tune_core2" msg:"Core2 tuning optimizations" }} | ||
19 | {{ input type:"choice" val:"tune_corei7" msg:"Corei7 tuning optimizations" }} | ||
20 | {{ if tunefile == "tune_core2": }} | ||
21 | DEFAULTTUNE ?= "core2-64" | ||
22 | require conf/machine/include/tune-core2.inc | ||
23 | {{ if tunefile == "tune_corei7": }} | ||
24 | DEFAULTTUNE ?= "corei7-64" | ||
25 | require conf/machine/include/tune-corei7.inc | ||
26 | |||
27 | require conf/machine/include/x86-base.inc | ||
28 | |||
29 | MACHINE_FEATURES += "wifi efi pcbios" | ||
30 | |||
31 | {{ input type:"boolean" name:"xserver" prio:"50" msg:"Do you need support for X? (y/n)" default:"y" }} | ||
32 | |||
33 | {{ if xserver == "y": }} | ||
34 | {{ input type:"choicelist" name:"xserver_choice" prio:"50" msg:"Please select an xserver for this machine:" default:"xserver_i915" }} | ||
35 | |||
36 | {{ input type:"choice" val:"xserver_vesa" msg:"VESA xserver support" }} | ||
37 | {{ input type:"choice" val:"xserver_i915" msg:"i915 xserver support" }} | ||
38 | {{ input type:"choice" val:"xserver_i965" msg:"i965 xserver support" }} | ||
39 | {{ if xserver == "y": }} | ||
40 | XSERVER ?= "${XSERVER_X86_BASE} \ | ||
41 | ${XSERVER_X86_EXT} \ | ||
42 | {{ if xserver == "y" and xserver_choice == "xserver_vesa": }} | ||
43 | ${XSERVER_X86_VESA} \ | ||
44 | {{ if xserver == "y" and xserver_choice == "xserver_i915": }} | ||
45 | ${XSERVER_X86_I915} \ | ||
46 | {{ if xserver == "y" and xserver_choice == "xserver_i965": }} | ||
47 | ${XSERVER_X86_I965} \ | ||
48 | {{ if xserver == "y": }} | ||
49 | " | ||
50 | |||
51 | MACHINE_EXTRA_RRECOMMENDS += "linux-firmware v86d" | ||
52 | |||
53 | GLIBC_ADDONS = "nptl" | ||
54 | |||
55 | EXTRA_OECONF_append_pn-matchbox-panel-2 = " --with-battery=acpi" | ||
56 | |||
57 | {{ if xserver == "y" and xserver_choice == "xserver_vesa": }} | ||
58 | APPEND += "video=vesafb vga=0x318" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == "y": }} xorg.conf b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == "y": }} xorg.conf new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/xserver-xf86-config/{{=machine}}/{{ if xserver == "y": }} xorg.conf | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/{{ if xserver == "y": }} xserver-xf86-config_0.1.bbappend b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/{{ if xserver == "y": }} xserver-xf86-config_0.1.bbappend new file mode 100644 index 0000000000..72d991c7e5 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-graphics/xorg-xserver/{{ if xserver == "y": }} xserver-xf86-config_0.1.bbappend | |||
@@ -0,0 +1 @@ | |||
FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/kernel-list.noinstall b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/kernel-list.noinstall new file mode 100644 index 0000000000..a04e6c7852 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/kernel-list.noinstall | |||
@@ -0,0 +1,5 @@ | |||
1 | {{ if kernel_choice != "custom": }} | ||
2 | {{ input type:"boolean" name:"use_default_kernel" prio:"10" msg:"Would you like to use the default (3.14) kernel? (y/n)" default:"y"}} | ||
3 | |||
4 | {{ if kernel_choice != "custom" and use_default_kernel == "n": }} | ||
5 | {{ input type:"choicelist" name:"kernel_choice" gen:"bsp.kernel.kernels" prio:"10" msg:"Please choose the kernel to use in this BSP:" default:"linux-yocto_3.14"}} | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc new file mode 100644 index 0000000000..c9882590a8 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-preempt-rt.scc | |||
@@ -0,0 +1,15 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE preempt-rt | ||
3 | define KARCH x86_64 | ||
4 | |||
5 | include {{=map_preempt_rt_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
10 | |||
11 | # default policy for preempt-rt kernels | ||
12 | include cfg/usb-mass-storage.scc | ||
13 | include cfg/boot-live.scc | ||
14 | include features/latencytop/latencytop.scc | ||
15 | include features/profiling/profiling.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc new file mode 100644 index 0000000000..e500bad4b2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-standard.scc | |||
@@ -0,0 +1,15 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE standard | ||
3 | define KARCH x86_64 | ||
4 | |||
5 | include {{=map_standard_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
10 | |||
11 | # default policy for standard kernels | ||
12 | include cfg/usb-mass-storage.scc | ||
13 | include cfg/boot-live.scc | ||
14 | include features/latencytop/latencytop.scc | ||
15 | include features/profiling/profiling.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc new file mode 100644 index 0000000000..e8e3c1c04d --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-tiny.scc | |||
@@ -0,0 +1,9 @@ | |||
1 | define KMACHINE {{=machine}} | ||
2 | define KTYPE tiny | ||
3 | define KARCH x86_64 | ||
4 | |||
5 | include {{=map_tiny_kbranch(need_new_kbranch, new_kbranch, existing_kbranch)}} | ||
6 | {{ if need_new_kbranch == "y": }} | ||
7 | branch {{=machine}} | ||
8 | |||
9 | include {{=machine}}.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-config.cfg | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-features.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}-user-patches.scc | |||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg new file mode 100644 index 0000000000..b4b82d7ca0 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.cfg | |||
@@ -0,0 +1,47 @@ | |||
1 | CONFIG_PRINTK=y | ||
2 | |||
3 | # Basic hardware support for the box - network, USB, PCI, sound | ||
4 | CONFIG_NETDEVICES=y | ||
5 | CONFIG_ATA=y | ||
6 | CONFIG_ATA_GENERIC=y | ||
7 | CONFIG_ATA_SFF=y | ||
8 | CONFIG_PCI=y | ||
9 | CONFIG_MMC=y | ||
10 | CONFIG_MMC_SDHCI=y | ||
11 | CONFIG_USB_SUPPORT=y | ||
12 | CONFIG_USB=y | ||
13 | CONFIG_USB_ARCH_HAS_EHCI=y | ||
14 | CONFIG_R8169=y | ||
15 | CONFIG_PATA_SCH=y | ||
16 | CONFIG_MMC_SDHCI_PCI=y | ||
17 | CONFIG_USB_EHCI_HCD=y | ||
18 | CONFIG_PCIEPORTBUS=y | ||
19 | CONFIG_NET=y | ||
20 | CONFIG_USB_UHCI_HCD=y | ||
21 | CONFIG_BLK_DEV_SD=y | ||
22 | CONFIG_CHR_DEV_SG=y | ||
23 | CONFIG_SOUND=y | ||
24 | CONFIG_SND=y | ||
25 | CONFIG_SND_HDA_INTEL=y | ||
26 | |||
27 | # Make sure these are on, otherwise the bootup won't be fun | ||
28 | CONFIG_EXT3_FS=y | ||
29 | CONFIG_UNIX=y | ||
30 | CONFIG_INET=y | ||
31 | CONFIG_MODULES=y | ||
32 | CONFIG_SHMEM=y | ||
33 | CONFIG_TMPFS=y | ||
34 | CONFIG_PACKET=y | ||
35 | |||
36 | CONFIG_I2C=y | ||
37 | CONFIG_AGP=y | ||
38 | CONFIG_PM=y | ||
39 | CONFIG_ACPI=y | ||
40 | CONFIG_INPUT=y | ||
41 | |||
42 | # Needed for booting (and using) USB memory sticks | ||
43 | CONFIG_BLK_DEV_LOOP=y | ||
44 | CONFIG_NLS_CODEPAGE_437=y | ||
45 | CONFIG_NLS_ISO8859_1=y | ||
46 | |||
47 | CONFIG_RD_GZIP=y | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc new file mode 100644 index 0000000000..db45140381 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice != "custom": }} files/{{=machine}}.scc | |||
@@ -0,0 +1,13 @@ | |||
1 | kconf hardware {{=machine}}.cfg | ||
2 | |||
3 | include features/serial/8250.scc | ||
4 | {{ if xserver == "y" and xserver_choice == "xserver_vesa": }} | ||
5 | include cfg/vesafb.scc | ||
6 | {{ if xserver == "y" and xserver_choice == "xserver_i915" or xserver_choice == "xserver_i965": }} | ||
7 | include features/i915/i915.scc | ||
8 | |||
9 | include cfg/usb-mass-storage.scc | ||
10 | include features/power/intel.scc | ||
11 | |||
12 | kconf hardware {{=machine}}-user-config.cfg | ||
13 | include {{=machine}}-user-patches.scc | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend new file mode 100644 index 0000000000..25c87a85ac --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-dev": }} linux-yocto-dev.bbappend | |||
@@ -0,0 +1,25 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
7 | |||
8 | {{ if need_new_kbranch == "y": }} | ||
9 | {{ input type:"choicelist" name:"new_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
10 | |||
11 | {{ if need_new_kbranch == "n": }} | ||
12 | {{ input type:"choicelist" name:"existing_kbranch" nameappend:"i386" gen:"bsp.kernel.all_branches" branches_base:"standard" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/base" }} | ||
13 | |||
14 | {{ if need_new_kbranch == "n": }} | ||
15 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
16 | |||
17 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Would you like SMP support? (y/n)" default:"y"}} | ||
18 | {{ if smp == "y": }} | ||
19 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
20 | |||
21 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
22 | file://{{=machine}}-user-config.cfg \ | ||
23 | file://{{=machine}}-user-patches.scc \ | ||
24 | file://{{=machine}}-user-features.scc \ | ||
25 | " | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend new file mode 100644 index 0000000000..00c8c68933 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-rt_3.10": }} linux-yocto-rt_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/preempt-rt" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/preempt-rt/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-preempt-rt.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-rt_{{=machine}} ?= "f35992f80c81dc5fa1a97165dfd5cbb84661f7cb" | ||
31 | #SRCREV_meta_pn-linux-yocto-rt_{{=machine}} ?= "1b534b2f8bbe9b8a773268cfa30a4850346f6f5f" | ||
32 | #LINUX_VERSION = "3.10.9" \ No newline at end of file | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend new file mode 100644 index 0000000000..bc6968d832 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.10": }} linux-yocto-tiny_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.10.9" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend new file mode 100644 index 0000000000..d221d5f2a4 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto-tiny_3.14": }} linux-yocto-tiny_3.14.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard/tiny" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/tiny/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-tiny.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto-tiny_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto-tiny_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.14" | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend new file mode 100644 index 0000000000..162348114f --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.10": }} linux-yocto_3.10.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "b170394a475b96ecc92cbc9e4b002bed0a9f69c5" | ||
31 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "c2ed0f16fdec628242a682897d5d86df4547cf24" | ||
32 | #LINUX_VERSION = "3.10.9" \ No newline at end of file | ||
diff --git a/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend new file mode 100644 index 0000000000..81e528bc33 --- /dev/null +++ b/scripts/lib/bsp/substrate/target/arch/x86_64/recipes-kernel/linux/{{ if kernel_choice == "linux-yocto_3.14": }} linux-yocto_3.14.bbappend | |||
@@ -0,0 +1,32 @@ | |||
1 | FILESEXTRAPATHS_prepend := "${THISDIR}/files:" | ||
2 | |||
3 | PR := "${PR}.1" | ||
4 | |||
5 | COMPATIBLE_MACHINE_{{=machine}} = "{{=machine}}" | ||
6 | |||
7 | {{ input type:"boolean" name:"need_new_kbranch" prio:"20" msg:"Do you need a new machine branch for this BSP (the alternative is to re-use an existing branch)? [y/n]" default:"y" }} | ||
8 | |||
9 | {{ if need_new_kbranch == "y": }} | ||
10 | {{ input type:"choicelist" name:"new_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} | ||
11 | |||
12 | {{ if need_new_kbranch == "n": }} | ||
13 | {{ input type:"choicelist" name:"existing_kbranch" gen:"bsp.kernel.all_branches" branches_base:"standard:standard/common-pc-64" prio:"20" msg:"Please choose a machine branch to base this BSP on:" default:"standard/common-pc-64/base" }} | ||
14 | |||
15 | {{ if need_new_kbranch == "n": }} | ||
16 | KBRANCH_{{=machine}} = "{{=existing_kbranch}}" | ||
17 | |||
18 | {{ input type:"boolean" name:"smp" prio:"30" msg:"Do you need SMP support? (y/n)" default:"y"}} | ||
19 | {{ if smp == "y": }} | ||
20 | KERNEL_FEATURES_append_{{=machine}} += " cfg/smp.scc" | ||
21 | |||
22 | SRC_URI += "file://{{=machine}}-standard.scc \ | ||
23 | file://{{=machine}}-user-config.cfg \ | ||
24 | file://{{=machine}}-user-patches.scc \ | ||
25 | file://{{=machine}}-user-features.scc \ | ||
26 | " | ||
27 | |||
28 | # uncomment and replace these SRCREVs with the real commit ids once you've had | ||
29 | # the appropriate changes committed to the upstream linux-yocto repo | ||
30 | #SRCREV_machine_pn-linux-yocto_{{=machine}} ?= "840bb8c059418c4753415df56c9aff1c0d5354c8" | ||
31 | #SRCREV_meta_pn-linux-yocto_{{=machine}} ?= "4fd76cc4f33e0afd8f906b1e8f231b6d13b6c993" | ||
32 | #LINUX_VERSION = "3.14" | ||
diff --git a/scripts/lib/bsp/tags.py b/scripts/lib/bsp/tags.py new file mode 100644 index 0000000000..6d5feb0a59 --- /dev/null +++ b/scripts/lib/bsp/tags.py | |||
@@ -0,0 +1,47 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2012, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This module provides a place to define common constants for the | ||
22 | # Yocto BSP Tools. | ||
23 | # | ||
24 | # AUTHORS | ||
25 | # Tom Zanussi <tom.zanussi (at] intel.com> | ||
26 | # | ||
27 | |||
28 | OPEN_TAG = "{{" | ||
29 | CLOSE_TAG = "}}" | ||
30 | ASSIGN_TAG = "{{=" | ||
31 | INPUT_TAG = "input" | ||
32 | IF_TAG = "if" | ||
33 | |||
34 | INDENT_STR = " " | ||
35 | |||
36 | BLANKLINE_STR = "of.write(\"\\n\")" | ||
37 | NORMAL_START = "of.write" | ||
38 | OPEN_START = "current_file =" | ||
39 | |||
40 | INPUT_TYPE_PROPERTY = "type" | ||
41 | |||
42 | SRC_URI_FILE = "file://" | ||
43 | |||
44 | GIT_CHECK_URI = "git://git.yoctoproject.org/linux-yocto-dev.git" | ||
45 | |||
46 | |||
47 | |||
diff --git a/scripts/lib/image/__init__.py b/scripts/lib/image/__init__.py new file mode 100644 index 0000000000..1ff814e761 --- /dev/null +++ b/scripts/lib/image/__init__.py | |||
@@ -0,0 +1,22 @@ | |||
1 | # | ||
2 | # OpenEmbedded Image tools library | ||
3 | # | ||
4 | # Copyright (c) 2013, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # AUTHORS | ||
21 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
22 | # | ||
diff --git a/scripts/lib/image/canned-wks/directdisk.wks b/scripts/lib/image/canned-wks/directdisk.wks new file mode 100644 index 0000000000..397a929c74 --- /dev/null +++ b/scripts/lib/image/canned-wks/directdisk.wks | |||
@@ -0,0 +1,10 @@ | |||
1 | # short-description: Create a 'pcbios' direct disk image | ||
2 | # long-description: Creates a partitioned legacy BIOS disk image that the user | ||
3 | # can directly dd to boot media. | ||
4 | |||
5 | |||
6 | part /boot --source bootimg-pcbios --ondisk sda --fstype=msdos --label boot --active --align 1024 | ||
7 | part / --source rootfs --ondisk sda --fstype=ext3 --label platform --align 1024 | ||
8 | |||
9 | bootloader --timeout=0 --append="rootwait rootfstype=ext3 video=vesafb vga=0x318 console=tty0" | ||
10 | |||
diff --git a/scripts/lib/image/canned-wks/mkefidisk.wks b/scripts/lib/image/canned-wks/mkefidisk.wks new file mode 100644 index 0000000000..e976bc80dd --- /dev/null +++ b/scripts/lib/image/canned-wks/mkefidisk.wks | |||
@@ -0,0 +1,11 @@ | |||
1 | # short-description: Create an EFI disk image | ||
2 | # long-description: Creates a partitioned EFI disk image that the user | ||
3 | # can directly dd to boot media. | ||
4 | |||
5 | part /boot --source bootimg-efi --ondisk sda --fstype=msdos --label msdos --active --align 1024 | ||
6 | |||
7 | part / --source rootfs --ondisk sda --fstype=ext3 --label platform --align 1024 | ||
8 | |||
9 | part swap --ondisk sda --size 44 --label swap1 --fstype=swap | ||
10 | |||
11 | bootloader --timeout=10 --append="rootwait rootfstype=ext3 console=ttyPCH0,115200 console=tty0 vmalloc=256MB snd-hda-intel.enable_msi=0" | ||
diff --git a/scripts/lib/image/canned-wks/uboot.wks b/scripts/lib/image/canned-wks/uboot.wks new file mode 100644 index 0000000000..7de0572d0f --- /dev/null +++ b/scripts/lib/image/canned-wks/uboot.wks | |||
@@ -0,0 +1,17 @@ | |||
1 | # short-description: . Create a ramdisk image for U-Boot | ||
2 | # long-description: Creates a ramdisk image for U-Boot that user | ||
3 | # can directly load it into ram through tftp | ||
4 | # | ||
5 | # part - is a wic command that drive the process of generating a valid file system | ||
6 | # - --source=uboot : wic plugin that generates a ramdisk image for U-Boot | ||
7 | # - --fstype=ext2 : file system type( ext2 / ext3 / ext 4) | ||
8 | # | ||
9 | # %packages %end - option to provide a list of packages that will be installed | ||
10 | # into rootfs. All packages dependencies will be installed by | ||
11 | # package manager(default opkg). | ||
12 | |||
13 | |||
14 | part / --source=uboot --fstype=ext2 --label imageName --align 1024 | ||
15 | |||
16 | %packages | ||
17 | %end | ||
diff --git a/scripts/lib/image/config/wic.conf b/scripts/lib/image/config/wic.conf new file mode 100644 index 0000000000..2a2750b4ee --- /dev/null +++ b/scripts/lib/image/config/wic.conf | |||
@@ -0,0 +1,11 @@ | |||
1 | [common] | ||
2 | ; general settings | ||
3 | distro_name = OpenEmbedded | ||
4 | |||
5 | [create] | ||
6 | ; settings for create subcommand | ||
7 | ; repourl=http://linux.com/ipk/all http://linux.com/ipk/target http://linux.com/ipk/arch | ||
8 | arch=powerpc | ||
9 | pkgmgr=opkg | ||
10 | runtime=native | ||
11 | install_pkgs=source | ||
diff --git a/scripts/lib/image/engine.py b/scripts/lib/image/engine.py new file mode 100644 index 0000000000..0643780f1a --- /dev/null +++ b/scripts/lib/image/engine.py | |||
@@ -0,0 +1,287 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2013, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | |||
22 | # This module implements the image creation engine used by 'wic' to | ||
23 | # create images. The engine parses through the OpenEmbedded kickstart | ||
24 | # (wks) file specified and generates images that can then be directly | ||
25 | # written onto media. | ||
26 | # | ||
27 | # AUTHORS | ||
28 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
29 | # | ||
30 | |||
31 | import os | ||
32 | import sys | ||
33 | from abc import ABCMeta, abstractmethod | ||
34 | import shlex | ||
35 | import json | ||
36 | import subprocess | ||
37 | import shutil | ||
38 | |||
39 | import os, sys, errno | ||
40 | from mic import msger, creator | ||
41 | from mic.utils import cmdln, misc, errors | ||
42 | from mic.conf import configmgr | ||
43 | from mic.plugin import pluginmgr | ||
44 | from mic.__version__ import VERSION | ||
45 | from mic.utils.oe.misc import * | ||
46 | |||
47 | |||
48 | def verify_build_env(): | ||
49 | """ | ||
50 | Verify that the build environment is sane. | ||
51 | |||
52 | Returns True if it is, false otherwise | ||
53 | """ | ||
54 | try: | ||
55 | builddir = os.environ["BUILDDIR"] | ||
56 | except KeyError: | ||
57 | print "BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)" | ||
58 | sys.exit(1) | ||
59 | |||
60 | return True | ||
61 | |||
62 | |||
63 | def find_bitbake_env_lines(image_name): | ||
64 | """ | ||
65 | If image_name is empty, plugins might still be able to use the | ||
66 | environment, so set it regardless. | ||
67 | """ | ||
68 | if image_name: | ||
69 | bitbake_env_cmd = "bitbake -e %s" % image_name | ||
70 | else: | ||
71 | bitbake_env_cmd = "bitbake -e" | ||
72 | rc, bitbake_env_lines = exec_cmd(bitbake_env_cmd) | ||
73 | if rc != 0: | ||
74 | print "Couldn't get '%s' output." % bitbake_env_cmd | ||
75 | return None | ||
76 | |||
77 | return bitbake_env_lines | ||
78 | |||
79 | |||
80 | def find_artifacts(image_name): | ||
81 | """ | ||
82 | Gather the build artifacts for the current image (the image_name | ||
83 | e.g. core-image-minimal) for the current MACHINE set in local.conf | ||
84 | """ | ||
85 | bitbake_env_lines = get_bitbake_env_lines() | ||
86 | |||
87 | rootfs_dir = kernel_dir = hdddir = staging_data_dir = native_sysroot = "" | ||
88 | |||
89 | for line in bitbake_env_lines.split('\n'): | ||
90 | if (get_line_val(line, "IMAGE_ROOTFS")): | ||
91 | rootfs_dir = get_line_val(line, "IMAGE_ROOTFS") | ||
92 | continue | ||
93 | if (get_line_val(line, "STAGING_KERNEL_DIR")): | ||
94 | kernel_dir = get_line_val(line, "STAGING_KERNEL_DIR") | ||
95 | continue | ||
96 | if (get_line_val(line, "HDDDIR")): | ||
97 | hdddir = get_line_val(line, "HDDDIR") | ||
98 | continue | ||
99 | if (get_line_val(line, "STAGING_DATADIR")): | ||
100 | staging_data_dir = get_line_val(line, "STAGING_DATADIR") | ||
101 | continue | ||
102 | if (get_line_val(line, "STAGING_DIR_NATIVE")): | ||
103 | native_sysroot = get_line_val(line, "STAGING_DIR_NATIVE") | ||
104 | continue | ||
105 | |||
106 | return (rootfs_dir, kernel_dir, hdddir, staging_data_dir, native_sysroot) | ||
107 | |||
108 | |||
109 | CANNED_IMAGE_DIR = "lib/image/canned-wks" # relative to scripts | ||
110 | |||
111 | def find_canned_image(scripts_path, wks_file): | ||
112 | """ | ||
113 | Find a .wks file with the given name in the canned files dir. | ||
114 | |||
115 | Return False if not found | ||
116 | """ | ||
117 | canned_wks_dir = os.path.join(scripts_path, CANNED_IMAGE_DIR) | ||
118 | |||
119 | for root, dirs, files in os.walk(canned_wks_dir): | ||
120 | for file in files: | ||
121 | if file.endswith("~") or file.endswith("#"): | ||
122 | continue | ||
123 | if file.endswith(".wks") and wks_file + ".wks" == file: | ||
124 | fullpath = os.path.join(canned_wks_dir, file) | ||
125 | return fullpath | ||
126 | return None | ||
127 | |||
128 | |||
129 | def list_canned_images(scripts_path): | ||
130 | """ | ||
131 | List the .wks files in the canned image dir, minus the extension. | ||
132 | """ | ||
133 | canned_wks_dir = os.path.join(scripts_path, CANNED_IMAGE_DIR) | ||
134 | |||
135 | for root, dirs, files in os.walk(canned_wks_dir): | ||
136 | for file in files: | ||
137 | if file.endswith("~") or file.endswith("#"): | ||
138 | continue | ||
139 | if file.endswith(".wks"): | ||
140 | fullpath = os.path.join(canned_wks_dir, file) | ||
141 | f = open(fullpath, "r") | ||
142 | lines = f.readlines() | ||
143 | for line in lines: | ||
144 | desc = "" | ||
145 | idx = line.find("short-description:") | ||
146 | if idx != -1: | ||
147 | desc = line[idx + len("short-description:"):].strip() | ||
148 | break | ||
149 | basename = os.path.splitext(file)[0] | ||
150 | print " %s\t\t%s" % (basename, desc) | ||
151 | |||
152 | |||
153 | def list_canned_image_help(scripts_path, fullpath): | ||
154 | """ | ||
155 | List the help and params in the specified canned image. | ||
156 | """ | ||
157 | canned_wks_dir = os.path.join(scripts_path, CANNED_IMAGE_DIR) | ||
158 | |||
159 | f = open(fullpath, "r") | ||
160 | lines = f.readlines() | ||
161 | found = False | ||
162 | for line in lines: | ||
163 | if not found: | ||
164 | idx = line.find("long-description:") | ||
165 | if idx != -1: | ||
166 | |||
167 | print line[idx + len("long-description:"):].strip() | ||
168 | found = True | ||
169 | continue | ||
170 | if not line.strip(): | ||
171 | break | ||
172 | idx = line.find("#") | ||
173 | if idx != -1: | ||
174 | print line[idx + len("#:"):].rstrip() | ||
175 | else: | ||
176 | break | ||
177 | |||
178 | |||
179 | def wic_create(args, wks_file, rootfs_dir, bootimg_dir, kernel_dir, | ||
180 | native_sysroot, hdddir, staging_data_dir, scripts_path, | ||
181 | image_output_dir, debug, properties_file, properties=None): | ||
182 | """ | ||
183 | Create image | ||
184 | |||
185 | wks_file - user-defined OE kickstart file | ||
186 | rootfs_dir - absolute path to the build's /rootfs dir | ||
187 | bootimg_dir - absolute path to the build's boot artifacts directory | ||
188 | kernel_dir - absolute path to the build's kernel directory | ||
189 | native_sysroot - absolute path to the build's native sysroots dir | ||
190 | hdddir - absolute path to the build's HDDDIR dir | ||
191 | staging_data_dir - absolute path to the build's STAGING_DATA_DIR dir | ||
192 | scripts_path - absolute path to /scripts dir | ||
193 | image_output_dir - dirname to create for image | ||
194 | properties_file - use values from this file if nonempty i.e no prompting | ||
195 | properties - use values from this string if nonempty i.e no prompting | ||
196 | |||
197 | Normally, the values for the build artifacts values are determined | ||
198 | by 'wic -e' from the output of the 'bitbake -e' command given an | ||
199 | image name e.g. 'core-image-minimal' and a given machine set in | ||
200 | local.conf. If that's the case, the variables get the following | ||
201 | values from the output of 'bitbake -e': | ||
202 | |||
203 | rootfs_dir: IMAGE_ROOTFS | ||
204 | kernel_dir: STAGING_KERNEL_DIR | ||
205 | native_sysroot: STAGING_DIR_NATIVE | ||
206 | hdddir: HDDDIR | ||
207 | staging_data_dir: STAGING_DATA_DIR | ||
208 | |||
209 | In the above case, bootimg_dir remains unset and the image | ||
210 | creation code determines which of the passed-in directories to | ||
211 | use. | ||
212 | |||
213 | In the case where the values are passed in explicitly i.e 'wic -e' | ||
214 | is not used but rather the individual 'wic' options are used to | ||
215 | explicitly specify these values, hdddir and staging_data_dir will | ||
216 | be unset, but bootimg_dir must be explicit i.e. explicitly set to | ||
217 | either hdddir or staging_data_dir, depending on the image being | ||
218 | generated. The other values (rootfs_dir, kernel_dir, and | ||
219 | native_sysroot) correspond to the same values found above via | ||
220 | 'bitbake -e'). | ||
221 | |||
222 | """ | ||
223 | try: | ||
224 | oe_builddir = os.environ["BUILDDIR"] | ||
225 | except KeyError: | ||
226 | print "BUILDDIR not found, exiting. (Did you forget to source oe-init-build-env?)" | ||
227 | sys.exit(1) | ||
228 | |||
229 | direct_args = list() | ||
230 | direct_args.insert(0, oe_builddir) | ||
231 | direct_args.insert(0, image_output_dir) | ||
232 | direct_args.insert(0, wks_file) | ||
233 | direct_args.insert(0, rootfs_dir) | ||
234 | direct_args.insert(0, bootimg_dir) | ||
235 | direct_args.insert(0, kernel_dir) | ||
236 | direct_args.insert(0, native_sysroot) | ||
237 | direct_args.insert(0, hdddir) | ||
238 | direct_args.insert(0, staging_data_dir) | ||
239 | direct_args.insert(0, "direct") | ||
240 | |||
241 | if debug: | ||
242 | msger.set_loglevel('debug') | ||
243 | |||
244 | cr = creator.Creator() | ||
245 | |||
246 | cr.main(direct_args) | ||
247 | |||
248 | print "\nThe image(s) were created using OE kickstart file:\n %s" % wks_file | ||
249 | |||
250 | |||
251 | def wic_list(args, scripts_path, properties_file): | ||
252 | """ | ||
253 | Print the complete list of properties defined by the image, or the | ||
254 | possible values for a particular image property. | ||
255 | """ | ||
256 | if len(args) < 1: | ||
257 | return False | ||
258 | |||
259 | if len(args) == 1: | ||
260 | if args[0] == "images": | ||
261 | list_canned_images(scripts_path) | ||
262 | return True | ||
263 | elif args[0] == "properties": | ||
264 | return True | ||
265 | else: | ||
266 | return False | ||
267 | |||
268 | if len(args) == 2: | ||
269 | if args[0] == "properties": | ||
270 | wks_file = args[1] | ||
271 | print "print properties contained in wks file: %s" % wks_file | ||
272 | return True | ||
273 | elif args[0] == "property": | ||
274 | print "print property values for property: %s" % args[1] | ||
275 | return True | ||
276 | elif args[1] == "help": | ||
277 | wks_file = args[0] | ||
278 | fullpath = find_canned_image(scripts_path, wks_file) | ||
279 | if not fullpath: | ||
280 | print "No image named %s found, exiting. (Use 'wic list images' to list available images, or specify a fully-qualified OE kickstart (.wks) filename)\n" % wks_file | ||
281 | sys.exit(1) | ||
282 | list_canned_image_help(scripts_path, fullpath) | ||
283 | return True | ||
284 | else: | ||
285 | return False | ||
286 | |||
287 | return False | ||
diff --git a/scripts/lib/image/help.py b/scripts/lib/image/help.py new file mode 100644 index 0000000000..cb3112cf08 --- /dev/null +++ b/scripts/lib/image/help.py | |||
@@ -0,0 +1,311 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2013, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This module implements some basic help invocation functions along | ||
22 | # with the bulk of the help topic text for the OE Core Image Tools. | ||
23 | # | ||
24 | # AUTHORS | ||
25 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
26 | # | ||
27 | |||
28 | import subprocess | ||
29 | import logging | ||
30 | |||
31 | |||
32 | def subcommand_error(args): | ||
33 | logging.info("invalid subcommand %s" % args[0]) | ||
34 | |||
35 | |||
36 | def display_help(subcommand, subcommands): | ||
37 | """ | ||
38 | Display help for subcommand. | ||
39 | """ | ||
40 | if subcommand not in subcommands: | ||
41 | return False | ||
42 | |||
43 | help = subcommands.get(subcommand, subcommand_error)[2] | ||
44 | pager = subprocess.Popen('less', stdin=subprocess.PIPE) | ||
45 | pager.communicate(help) | ||
46 | |||
47 | return True | ||
48 | |||
49 | |||
50 | def wic_help(args, usage_str, subcommands): | ||
51 | """ | ||
52 | Subcommand help dispatcher. | ||
53 | """ | ||
54 | if len(args) == 1 or not display_help(args[1], subcommands): | ||
55 | print(usage_str) | ||
56 | |||
57 | |||
58 | def invoke_subcommand(args, parser, main_command_usage, subcommands): | ||
59 | """ | ||
60 | Dispatch to subcommand handler borrowed from combo-layer. | ||
61 | Should use argparse, but has to work in 2.6. | ||
62 | """ | ||
63 | if not args: | ||
64 | logging.error("No subcommand specified, exiting") | ||
65 | parser.print_help() | ||
66 | elif args[0] == "help": | ||
67 | wic_help(args, main_command_usage, subcommands) | ||
68 | elif args[0] not in subcommands: | ||
69 | logging.error("Unsupported subcommand %s, exiting\n" % (args[0])) | ||
70 | parser.print_help() | ||
71 | else: | ||
72 | usage = subcommands.get(args[0], subcommand_error)[1] | ||
73 | subcommands.get(args[0], subcommand_error)[0](args[1:], usage) | ||
74 | |||
75 | |||
76 | ## | ||
77 | # wic help and usage strings | ||
78 | ## | ||
79 | |||
80 | wic_usage = """ | ||
81 | |||
82 | Create a customized OpenEmbedded image | ||
83 | |||
84 | usage: wic [--version] [--help] COMMAND [ARGS] | ||
85 | |||
86 | Current 'wic' commands are: | ||
87 | create Create a new OpenEmbedded image | ||
88 | list List available values for options and image properties | ||
89 | |||
90 | See 'wic help COMMAND' for more information on a specific command. | ||
91 | """ | ||
92 | |||
93 | wic_help_usage = """ | ||
94 | |||
95 | usage: wic help <subcommand> | ||
96 | |||
97 | This command displays detailed help for the specified subcommand. | ||
98 | """ | ||
99 | |||
100 | wic_create_usage = """ | ||
101 | |||
102 | Create a new OpenEmbedded image | ||
103 | |||
104 | usage: wic create <wks file or image name> [-o <DIRNAME> | --outdir <DIRNAME>] | ||
105 | [-i <JSON PROPERTY FILE> | --infile <JSON PROPERTY_FILE>] | ||
106 | [-e | --image-name] [-r, --rootfs-dir] [-b, --bootimg-dir] | ||
107 | [-k, --kernel-dir] [-n, --native-sysroot] [-s, --skip-build-check] | ||
108 | |||
109 | This command creates an OpenEmbedded image based on the 'OE kickstart | ||
110 | commands' found in the <wks file>. | ||
111 | |||
112 | The -o option can be used to place the image in a directory with a | ||
113 | different name and location. | ||
114 | |||
115 | See 'wic help create' for more detailed instructions. | ||
116 | """ | ||
117 | |||
118 | wic_create_help = """ | ||
119 | |||
120 | NAME | ||
121 | wic create - Create a new OpenEmbedded image | ||
122 | |||
123 | SYNOPSIS | ||
124 | wic create <wks file or image name> [-o <DIRNAME> | --outdir <DIRNAME>] | ||
125 | [-i <JSON PROPERTY FILE> | --infile <JSON PROPERTY_FILE>] | ||
126 | [-e | --image-name] [-r, --rootfs-dir] [-b, --bootimg-dir] | ||
127 | [-k, --kernel-dir] [-n, --native-sysroot] [-s, --skip-build-check] | ||
128 | |||
129 | DESCRIPTION | ||
130 | This command creates an OpenEmbedded image based on the 'OE | ||
131 | kickstart commands' found in the <wks file>. | ||
132 | |||
133 | In order to do this, wic needs to know the locations of the | ||
134 | various build artifacts required to build the image. | ||
135 | |||
136 | Users can explicitly specify the build artifact locations using | ||
137 | the -r, -b, -k, and -n options. See below for details on where | ||
138 | the corresponding artifacts are typically found in a normal | ||
139 | OpenEmbedded build. | ||
140 | |||
141 | Alternatively, users can use the -e option to have 'mic' determine | ||
142 | those locations for a given image. If the -e option is used, the | ||
143 | user needs to have set the appropriate MACHINE variable in | ||
144 | local.conf, and have sourced the build environment. | ||
145 | |||
146 | The -e option is used to specify the name of the image to use the | ||
147 | artifacts from e.g. core-image-sato. | ||
148 | |||
149 | The -r option is used to specify the path to the /rootfs dir to | ||
150 | use as the .wks rootfs source. | ||
151 | |||
152 | The -b option is used to specify the path to the dir containing | ||
153 | the boot artifacts (e.g. /EFI or /syslinux dirs) to use as the | ||
154 | .wks bootimg source. | ||
155 | |||
156 | The -k option is used to specify the path to the dir containing | ||
157 | the kernel to use in the .wks bootimg. | ||
158 | |||
159 | The -n option is used to specify the path to the native sysroot | ||
160 | containing the tools to use to build the image. | ||
161 | |||
162 | The -s option is used to skip the build check. The build check is | ||
163 | a simple sanity check used to determine whether the user has | ||
164 | sourced the build environment so that the -e option can operate | ||
165 | correctly. If the user has specified the build artifact locations | ||
166 | explicitly, 'wic' assumes the user knows what he or she is doing | ||
167 | and skips the build check. | ||
168 | |||
169 | When 'wic -e' is used, the locations for the build artifacts | ||
170 | values are determined by 'wic -e' from the output of the 'bitbake | ||
171 | -e' command given an image name e.g. 'core-image-minimal' and a | ||
172 | given machine set in local.conf. In that case, the image is | ||
173 | created as if the following 'bitbake -e' variables were used: | ||
174 | |||
175 | -r: IMAGE_ROOTFS | ||
176 | -k: STAGING_KERNEL_DIR | ||
177 | -n: STAGING_DIR_NATIVE | ||
178 | -b: HDDDIR and STAGING_DATA_DIR (handlers decide which to use) | ||
179 | |||
180 | If 'wic -e' is not used, the user needs to select the appropriate | ||
181 | value for -b (as well as -r, -k, and -n). | ||
182 | |||
183 | The -o option can be used to place the image in a directory with a | ||
184 | different name and location. | ||
185 | |||
186 | As an alternative to the wks file, the image-specific properties | ||
187 | that define the values that will be used to generate a particular | ||
188 | image can be specified on the command-line using the -i option and | ||
189 | supplying a JSON object consisting of the set of name:value pairs | ||
190 | needed by image creation. | ||
191 | |||
192 | The set of properties available for a given image type can be | ||
193 | listed using the 'wic list' command. | ||
194 | """ | ||
195 | |||
196 | wic_list_usage = """ | ||
197 | |||
198 | List available OpenEmbedded image properties and values | ||
199 | |||
200 | usage: wic list images | ||
201 | wic list <image> help | ||
202 | wic list properties | ||
203 | wic list properties <wks file> | ||
204 | wic list property <property> | ||
205 | [-o <JSON PROPERTY FILE> | --outfile <JSON PROPERTY_FILE>] | ||
206 | |||
207 | This command enumerates the set of available canned images as well as | ||
208 | help for those images. It also can be used to enumerate the complete | ||
209 | set of possible values for a specified option or property needed by | ||
210 | the image creation process. | ||
211 | |||
212 | The first form enumerates all the available 'canned' images. | ||
213 | |||
214 | The second form lists the detailed help information for a specific | ||
215 | 'canned' image. | ||
216 | |||
217 | The third form enumerates all the possible values that exist and can | ||
218 | be specified in an OE kickstart (wks) file. | ||
219 | |||
220 | The fourth form enumerates all the possible options that exist for | ||
221 | the set of properties specified in a given OE kickstart (ks) file. | ||
222 | |||
223 | The final form enumerates all the possible values that exist and can | ||
224 | be specified for any given OE kickstart (wks) property. | ||
225 | |||
226 | See 'wic help list' for more details. | ||
227 | """ | ||
228 | |||
229 | wic_list_help = """ | ||
230 | |||
231 | NAME | ||
232 | wic list - List available OpenEmbedded image properties and values | ||
233 | |||
234 | SYNOPSIS | ||
235 | wic list images | ||
236 | wic list <image> help | ||
237 | wic list properties | ||
238 | wic list properties <wks file> | ||
239 | wic list property <property> | ||
240 | [-o <JSON PROPERTY FILE> | --outfile <JSON PROPERTY_FILE>] | ||
241 | |||
242 | DESCRIPTION | ||
243 | This command enumerates the complete set of possible values for a | ||
244 | specified option or property needed by the image creation process. | ||
245 | |||
246 | This command enumerates the set of available canned images as well | ||
247 | as help for those images. It also can be used to enumerate the | ||
248 | complete set of possible values for a specified option or property | ||
249 | needed by the image creation process. | ||
250 | |||
251 | The first form enumerates all the available 'canned' images. | ||
252 | These are actually just the set of .wks files that have been moved | ||
253 | into the /scripts/lib/image/canned-wks directory). | ||
254 | |||
255 | The second form lists the detailed help information for a specific | ||
256 | 'canned' image. | ||
257 | |||
258 | The third form enumerates all the possible values that exist and | ||
259 | can be specified in a OE kickstart (wks) file. The output of this | ||
260 | can be used by the third form to print the description and | ||
261 | possible values of a specific property. | ||
262 | |||
263 | The fourth form enumerates all the possible options that exist for | ||
264 | the set of properties specified in a given OE kickstart (wks) | ||
265 | file. If the -o option is specified, the list of properties, in | ||
266 | addition to being displayed, will be written to the specified file | ||
267 | as a JSON object. In this case, the object will consist of the | ||
268 | set of name:value pairs corresponding to the (possibly nested) | ||
269 | dictionary of properties defined by the input statements used by | ||
270 | the image. Some example output for the 'list <wks file>' command: | ||
271 | |||
272 | $ wic list test.ks | ||
273 | "part" : { | ||
274 | "mountpoint" : "/" | ||
275 | "fstype" : "ext3" | ||
276 | } | ||
277 | "part" : { | ||
278 | "mountpoint" : "/home" | ||
279 | "fstype" : "ext3" | ||
280 | "offset" : "10000" | ||
281 | } | ||
282 | "bootloader" : { | ||
283 | "type" : "efi" | ||
284 | } | ||
285 | . | ||
286 | . | ||
287 | . | ||
288 | |||
289 | Each entry in the output consists of the name of the input element | ||
290 | e.g. "part", followed by the properties defined for that | ||
291 | element enclosed in braces. This information should provide | ||
292 | sufficient information to create a complete user interface with. | ||
293 | |||
294 | The final form enumerates all the possible values that exist and | ||
295 | can be specified for any given OE kickstart (wks) property. If | ||
296 | the -o option is specified, the list of values for the given | ||
297 | property, in addition to being displayed, will be written to the | ||
298 | specified file as a JSON object. In this case, the object will | ||
299 | consist of the set of name:value pairs corresponding to the array | ||
300 | of property values associated with the property. | ||
301 | |||
302 | $ wic list property part | ||
303 | ["mountpoint", "where the partition should be mounted"] | ||
304 | ["fstype", "filesytem type of the partition"] | ||
305 | ["ext3"] | ||
306 | ["ext4"] | ||
307 | ["btrfs"] | ||
308 | ["swap"] | ||
309 | ["offset", "offset of the partition within the image"] | ||
310 | |||
311 | """ | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/__init__.py | |||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/base.py b/scripts/lib/mic/3rdparty/pykickstart/base.py new file mode 100644 index 0000000000..e6c8f56f9d --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/base.py | |||
@@ -0,0 +1,466 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2006, 2007, 2008 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | """ | ||
21 | Base classes for creating commands and syntax version object. | ||
22 | |||
23 | This module exports several important base classes: | ||
24 | |||
25 | BaseData - The base abstract class for all data objects. Data objects | ||
26 | are contained within a BaseHandler object. | ||
27 | |||
28 | BaseHandler - The base abstract class from which versioned kickstart | ||
29 | handler are derived. Subclasses of BaseHandler hold | ||
30 | BaseData and KickstartCommand objects. | ||
31 | |||
32 | DeprecatedCommand - An abstract subclass of KickstartCommand that should | ||
33 | be further subclassed by users of this module. When | ||
34 | a subclass is used, a warning message will be | ||
35 | printed. | ||
36 | |||
37 | KickstartCommand - The base abstract class for all kickstart commands. | ||
38 | Command objects are contained within a BaseHandler | ||
39 | object. | ||
40 | """ | ||
41 | import gettext | ||
42 | gettext.textdomain("pykickstart") | ||
43 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
44 | |||
45 | import types | ||
46 | import warnings | ||
47 | from pykickstart.errors import * | ||
48 | from pykickstart.ko import * | ||
49 | from pykickstart.parser import Packages | ||
50 | from pykickstart.version import versionToString | ||
51 | |||
52 | ### | ||
53 | ### COMMANDS | ||
54 | ### | ||
55 | class KickstartCommand(KickstartObject): | ||
56 | """The base class for all kickstart commands. This is an abstract class.""" | ||
57 | removedKeywords = [] | ||
58 | removedAttrs = [] | ||
59 | |||
60 | def __init__(self, writePriority=0, *args, **kwargs): | ||
61 | """Create a new KickstartCommand instance. This method must be | ||
62 | provided by all subclasses, but subclasses must call | ||
63 | KickstartCommand.__init__ first. Instance attributes: | ||
64 | |||
65 | currentCmd -- The name of the command in the input file that | ||
66 | caused this handler to be run. | ||
67 | currentLine -- The current unprocessed line from the input file | ||
68 | that caused this handler to be run. | ||
69 | handler -- A reference to the BaseHandler subclass this | ||
70 | command is contained withing. This is needed to | ||
71 | allow referencing of Data objects. | ||
72 | lineno -- The current line number in the input file. | ||
73 | writePriority -- An integer specifying when this command should be | ||
74 | printed when iterating over all commands' __str__ | ||
75 | methods. The higher the number, the later this | ||
76 | command will be written. All commands with the | ||
77 | same priority will be written alphabetically. | ||
78 | """ | ||
79 | |||
80 | # We don't want people using this class by itself. | ||
81 | if self.__class__ is KickstartCommand: | ||
82 | raise TypeError, "KickstartCommand is an abstract class." | ||
83 | |||
84 | KickstartObject.__init__(self, *args, **kwargs) | ||
85 | |||
86 | self.writePriority = writePriority | ||
87 | |||
88 | # These will be set by the dispatcher. | ||
89 | self.currentCmd = "" | ||
90 | self.currentLine = "" | ||
91 | self.handler = None | ||
92 | self.lineno = 0 | ||
93 | |||
94 | # If a subclass provides a removedKeywords list, remove all the | ||
95 | # members from the kwargs list before we start processing it. This | ||
96 | # ensures that subclasses don't continue to recognize arguments that | ||
97 | # were removed. | ||
98 | for arg in filter(kwargs.has_key, self.removedKeywords): | ||
99 | kwargs.pop(arg) | ||
100 | |||
101 | def __call__(self, *args, **kwargs): | ||
102 | """Set multiple attributes on a subclass of KickstartCommand at once | ||
103 | via keyword arguments. Valid attributes are anything specified in | ||
104 | a subclass, but unknown attributes will be ignored. | ||
105 | """ | ||
106 | for (key, val) in kwargs.items(): | ||
107 | # Ignore setting attributes that were removed in a subclass, as | ||
108 | # if they were unknown attributes. | ||
109 | if key in self.removedAttrs: | ||
110 | continue | ||
111 | |||
112 | if hasattr(self, key): | ||
113 | setattr(self, key, val) | ||
114 | |||
115 | def __str__(self): | ||
116 | """Return a string formatted for output to a kickstart file. This | ||
117 | method must be provided by all subclasses. | ||
118 | """ | ||
119 | return KickstartObject.__str__(self) | ||
120 | |||
121 | def parse(self, args): | ||
122 | """Parse the list of args and set data on the KickstartCommand object. | ||
123 | This method must be provided by all subclasses. | ||
124 | """ | ||
125 | raise TypeError, "parse() not implemented for KickstartCommand" | ||
126 | |||
127 | def apply(self, instroot="/"): | ||
128 | """Write out the configuration related to the KickstartCommand object. | ||
129 | Subclasses which do not provide this method will not have their | ||
130 | configuration written out. | ||
131 | """ | ||
132 | return | ||
133 | |||
134 | def dataList(self): | ||
135 | """For commands that can occur multiple times in a single kickstart | ||
136 | file (like network, part, etc.), return the list that we should | ||
137 | append more data objects to. | ||
138 | """ | ||
139 | return None | ||
140 | |||
141 | def deleteRemovedAttrs(self): | ||
142 | """Remove all attributes from self that are given in the removedAttrs | ||
143 | list. This method should be called from __init__ in a subclass, | ||
144 | but only after the superclass's __init__ method has been called. | ||
145 | """ | ||
146 | for attr in filter(lambda k: hasattr(self, k), self.removedAttrs): | ||
147 | delattr(self, attr) | ||
148 | |||
149 | # Set the contents of the opts object (an instance of optparse.Values | ||
150 | # returned by parse_args) as attributes on the KickstartCommand object. | ||
151 | # It's useful to call this from KickstartCommand subclasses after parsing | ||
152 | # the arguments. | ||
153 | def _setToSelf(self, optParser, opts): | ||
154 | self._setToObj(optParser, opts, self) | ||
155 | |||
156 | # Sets the contents of the opts object (an instance of optparse.Values | ||
157 | # returned by parse_args) as attributes on the provided object obj. It's | ||
158 | # useful to call this from KickstartCommand subclasses that handle lists | ||
159 | # of objects (like partitions, network devices, etc.) and need to populate | ||
160 | # a Data object. | ||
161 | def _setToObj(self, optParser, opts, obj): | ||
162 | for key in filter (lambda k: getattr(opts, k) != None, optParser.keys()): | ||
163 | setattr(obj, key, getattr(opts, key)) | ||
164 | |||
165 | class DeprecatedCommand(KickstartCommand): | ||
166 | """Specify that a command is deprecated and no longer has any function. | ||
167 | Any command that is deprecated should be subclassed from this class, | ||
168 | only specifying an __init__ method that calls the superclass's __init__. | ||
169 | This is an abstract class. | ||
170 | """ | ||
171 | def __init__(self, writePriority=None, *args, **kwargs): | ||
172 | # We don't want people using this class by itself. | ||
173 | if self.__class__ is KickstartCommand: | ||
174 | raise TypeError, "DeprecatedCommand is an abstract class." | ||
175 | |||
176 | # Create a new DeprecatedCommand instance. | ||
177 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
178 | |||
179 | def __str__(self): | ||
180 | """Placeholder since DeprecatedCommands don't work anymore.""" | ||
181 | return "" | ||
182 | |||
183 | def parse(self, args): | ||
184 | """Print a warning message if the command is seen in the input file.""" | ||
185 | mapping = {"lineno": self.lineno, "cmd": self.currentCmd} | ||
186 | warnings.warn(_("Ignoring deprecated command on line %(lineno)s: The %(cmd)s command has been deprecated and no longer has any effect. It may be removed from future releases, which will result in a fatal error from kickstart. Please modify your kickstart file to remove this command.") % mapping, DeprecationWarning) | ||
187 | |||
188 | |||
189 | ### | ||
190 | ### HANDLERS | ||
191 | ### | ||
192 | class BaseHandler(KickstartObject): | ||
193 | """Each version of kickstart syntax is provided by a subclass of this | ||
194 | class. These subclasses are what users will interact with for parsing, | ||
195 | extracting data, and writing out kickstart files. This is an abstract | ||
196 | class. | ||
197 | |||
198 | version -- The version this syntax handler supports. This is set by | ||
199 | a class attribute of a BaseHandler subclass and is used to | ||
200 | set up the command dict. It is for read-only use. | ||
201 | """ | ||
202 | version = None | ||
203 | |||
204 | def __init__(self, mapping=None, dataMapping=None, commandUpdates=None, | ||
205 | dataUpdates=None, *args, **kwargs): | ||
206 | """Create a new BaseHandler instance. This method must be provided by | ||
207 | all subclasses, but subclasses must call BaseHandler.__init__ first. | ||
208 | |||
209 | mapping -- A custom map from command strings to classes, | ||
210 | useful when creating your own handler with | ||
211 | special command objects. It is otherwise unused | ||
212 | and rarely needed. If you give this argument, | ||
213 | the mapping takes the place of the default one | ||
214 | and so must include all commands you want | ||
215 | recognized. | ||
216 | dataMapping -- This is the same as mapping, but for data | ||
217 | objects. All the same comments apply. | ||
218 | commandUpdates -- This is similar to mapping, but does not take | ||
219 | the place of the defaults entirely. Instead, | ||
220 | this mapping is applied after the defaults and | ||
221 | updates it with just the commands you want to | ||
222 | modify. | ||
223 | dataUpdates -- This is the same as commandUpdates, but for | ||
224 | data objects. | ||
225 | |||
226 | |||
227 | Instance attributes: | ||
228 | |||
229 | commands -- A mapping from a string command to a KickstartCommand | ||
230 | subclass object that handles it. Multiple strings can | ||
231 | map to the same object, but only one instance of the | ||
232 | command object should ever exist. Most users should | ||
233 | never have to deal with this directly, as it is | ||
234 | manipulated internally and called through dispatcher. | ||
235 | currentLine -- The current unprocessed line from the input file | ||
236 | that caused this handler to be run. | ||
237 | packages -- An instance of pykickstart.parser.Packages which | ||
238 | describes the packages section of the input file. | ||
239 | platform -- A string describing the hardware platform, which is | ||
240 | needed only by system-config-kickstart. | ||
241 | scripts -- A list of pykickstart.parser.Script instances, which is | ||
242 | populated by KickstartParser.addScript and describes the | ||
243 | %pre/%post/%traceback script section of the input file. | ||
244 | """ | ||
245 | |||
246 | # We don't want people using this class by itself. | ||
247 | if self.__class__ is BaseHandler: | ||
248 | raise TypeError, "BaseHandler is an abstract class." | ||
249 | |||
250 | KickstartObject.__init__(self, *args, **kwargs) | ||
251 | |||
252 | # This isn't really a good place for these, but it's better than | ||
253 | # everything else I can think of. | ||
254 | self.scripts = [] | ||
255 | self.packages = Packages() | ||
256 | self.platform = "" | ||
257 | |||
258 | # These will be set by the dispatcher. | ||
259 | self.commands = {} | ||
260 | self.currentLine = 0 | ||
261 | |||
262 | # A dict keyed by an integer priority number, with each value being a | ||
263 | # list of KickstartCommand subclasses. This dict is maintained by | ||
264 | # registerCommand and used in __str__. No one else should be touching | ||
265 | # it. | ||
266 | self._writeOrder = {} | ||
267 | |||
268 | self._registerCommands(mapping, dataMapping, commandUpdates, dataUpdates) | ||
269 | |||
270 | def __str__(self): | ||
271 | """Return a string formatted for output to a kickstart file.""" | ||
272 | retval = "" | ||
273 | |||
274 | if self.platform != "": | ||
275 | retval += "#platform=%s\n" % self.platform | ||
276 | |||
277 | retval += "#version=%s\n" % versionToString(self.version) | ||
278 | |||
279 | lst = self._writeOrder.keys() | ||
280 | lst.sort() | ||
281 | |||
282 | for prio in lst: | ||
283 | for obj in self._writeOrder[prio]: | ||
284 | retval += obj.__str__() | ||
285 | |||
286 | for script in self.scripts: | ||
287 | retval += script.__str__() | ||
288 | |||
289 | retval += self.packages.__str__() | ||
290 | |||
291 | return retval | ||
292 | |||
293 | def _insertSorted(self, lst, obj): | ||
294 | length = len(lst) | ||
295 | i = 0 | ||
296 | |||
297 | while i < length: | ||
298 | # If the two classes have the same name, it's because we are | ||
299 | # overriding an existing class with one from a later kickstart | ||
300 | # version, so remove the old one in favor of the new one. | ||
301 | if obj.__class__.__name__ > lst[i].__class__.__name__: | ||
302 | i += 1 | ||
303 | elif obj.__class__.__name__ == lst[i].__class__.__name__: | ||
304 | lst[i] = obj | ||
305 | return | ||
306 | elif obj.__class__.__name__ < lst[i].__class__.__name__: | ||
307 | break | ||
308 | |||
309 | if i >= length: | ||
310 | lst.append(obj) | ||
311 | else: | ||
312 | lst.insert(i, obj) | ||
313 | |||
314 | def _setCommand(self, cmdObj): | ||
315 | # Add an attribute on this version object. We need this to provide a | ||
316 | # way for clients to access the command objects. We also need to strip | ||
317 | # off the version part from the front of the name. | ||
318 | if cmdObj.__class__.__name__.find("_") != -1: | ||
319 | name = unicode(cmdObj.__class__.__name__.split("_", 1)[1]) | ||
320 | else: | ||
321 | name = unicode(cmdObj.__class__.__name__).lower() | ||
322 | |||
323 | setattr(self, name.lower(), cmdObj) | ||
324 | |||
325 | # Also, add the object into the _writeOrder dict in the right place. | ||
326 | if cmdObj.writePriority is not None: | ||
327 | if self._writeOrder.has_key(cmdObj.writePriority): | ||
328 | self._insertSorted(self._writeOrder[cmdObj.writePriority], cmdObj) | ||
329 | else: | ||
330 | self._writeOrder[cmdObj.writePriority] = [cmdObj] | ||
331 | |||
332 | def _registerCommands(self, mapping=None, dataMapping=None, commandUpdates=None, | ||
333 | dataUpdates=None): | ||
334 | if mapping == {} or mapping == None: | ||
335 | from pykickstart.handlers.control import commandMap | ||
336 | cMap = commandMap[self.version] | ||
337 | else: | ||
338 | cMap = mapping | ||
339 | |||
340 | if dataMapping == {} or dataMapping == None: | ||
341 | from pykickstart.handlers.control import dataMap | ||
342 | dMap = dataMap[self.version] | ||
343 | else: | ||
344 | dMap = dataMapping | ||
345 | |||
346 | if type(commandUpdates) == types.DictType: | ||
347 | cMap.update(commandUpdates) | ||
348 | |||
349 | if type(dataUpdates) == types.DictType: | ||
350 | dMap.update(dataUpdates) | ||
351 | |||
352 | for (cmdName, cmdClass) in cMap.iteritems(): | ||
353 | # First make sure we haven't instantiated this command handler | ||
354 | # already. If we have, we just need to make another mapping to | ||
355 | # it in self.commands. | ||
356 | cmdObj = None | ||
357 | |||
358 | for (key, val) in self.commands.iteritems(): | ||
359 | if val.__class__.__name__ == cmdClass.__name__: | ||
360 | cmdObj = val | ||
361 | break | ||
362 | |||
363 | # If we didn't find an instance in self.commands, create one now. | ||
364 | if cmdObj == None: | ||
365 | cmdObj = cmdClass() | ||
366 | self._setCommand(cmdObj) | ||
367 | |||
368 | # Finally, add the mapping to the commands dict. | ||
369 | self.commands[cmdName] = cmdObj | ||
370 | self.commands[cmdName].handler = self | ||
371 | |||
372 | # We also need to create attributes for the various data objects. | ||
373 | # No checks here because dMap is a bijection. At least, that's what | ||
374 | # the comment says. Hope no one screws that up. | ||
375 | for (dataName, dataClass) in dMap.iteritems(): | ||
376 | setattr(self, dataName, dataClass) | ||
377 | |||
378 | def dispatcher(self, args, lineno): | ||
379 | """Call the appropriate KickstartCommand handler for the current line | ||
380 | in the kickstart file. A handler for the current command should | ||
381 | be registered, though a handler of None is not an error. Returns | ||
382 | the data object returned by KickstartCommand.parse. | ||
383 | |||
384 | args -- A list of arguments to the current command | ||
385 | lineno -- The line number in the file, for error reporting | ||
386 | """ | ||
387 | cmd = args[0] | ||
388 | |||
389 | if not self.commands.has_key(cmd): | ||
390 | raise KickstartParseError, formatErrorMsg(lineno, msg=_("Unknown command: %s" % cmd)) | ||
391 | elif self.commands[cmd] != None: | ||
392 | self.commands[cmd].currentCmd = cmd | ||
393 | self.commands[cmd].currentLine = self.currentLine | ||
394 | self.commands[cmd].lineno = lineno | ||
395 | |||
396 | # The parser returns the data object that was modified. This could | ||
397 | # be a BaseData subclass that should be put into a list, or it | ||
398 | # could be the command handler object itself. | ||
399 | obj = self.commands[cmd].parse(args[1:]) | ||
400 | lst = self.commands[cmd].dataList() | ||
401 | if lst is not None: | ||
402 | lst.append(obj) | ||
403 | |||
404 | return obj | ||
405 | |||
406 | def maskAllExcept(self, lst): | ||
407 | """Set all entries in the commands dict to None, except the ones in | ||
408 | the lst. All other commands will not be processed. | ||
409 | """ | ||
410 | self._writeOrder = {} | ||
411 | |||
412 | for (key, val) in self.commands.iteritems(): | ||
413 | if not key in lst: | ||
414 | self.commands[key] = None | ||
415 | |||
416 | def hasCommand(self, cmd): | ||
417 | """Return true if there is a handler for the string cmd.""" | ||
418 | return hasattr(self, cmd) | ||
419 | |||
420 | |||
421 | ### | ||
422 | ### DATA | ||
423 | ### | ||
424 | class BaseData(KickstartObject): | ||
425 | """The base class for all data objects. This is an abstract class.""" | ||
426 | removedKeywords = [] | ||
427 | removedAttrs = [] | ||
428 | |||
429 | def __init__(self, *args, **kwargs): | ||
430 | """Create a new BaseData instance. | ||
431 | |||
432 | lineno -- Line number in the ks-file where this object was defined | ||
433 | """ | ||
434 | |||
435 | # We don't want people using this class by itself. | ||
436 | if self.__class__ is BaseData: | ||
437 | raise TypeError, "BaseData is an abstract class." | ||
438 | |||
439 | KickstartObject.__init__(self, *args, **kwargs) | ||
440 | self.lineno = 0 | ||
441 | |||
442 | def __str__(self): | ||
443 | """Return a string formatted for output to a kickstart file.""" | ||
444 | return "" | ||
445 | |||
446 | def __call__(self, *args, **kwargs): | ||
447 | """Set multiple attributes on a subclass of BaseData at once via | ||
448 | keyword arguments. Valid attributes are anything specified in a | ||
449 | subclass, but unknown attributes will be ignored. | ||
450 | """ | ||
451 | for (key, val) in kwargs.items(): | ||
452 | # Ignore setting attributes that were removed in a subclass, as | ||
453 | # if they were unknown attributes. | ||
454 | if key in self.removedAttrs: | ||
455 | continue | ||
456 | |||
457 | if hasattr(self, key): | ||
458 | setattr(self, key, val) | ||
459 | |||
460 | def deleteRemovedAttrs(self): | ||
461 | """Remove all attributes from self that are given in the removedAttrs | ||
462 | list. This method should be called from __init__ in a subclass, | ||
463 | but only after the superclass's __init__ method has been called. | ||
464 | """ | ||
465 | for attr in filter(lambda k: hasattr(self, k), self.removedAttrs): | ||
466 | delattr(self, attr) | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/commands/__init__.py new file mode 100644 index 0000000000..da48ff50d5 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/__init__.py | |||
@@ -0,0 +1,26 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2009 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | import authconfig, autopart, autostep, bootloader, clearpart, device | ||
21 | import deviceprobe, displaymode, dmraid, driverdisk, fcoe, firewall, firstboot | ||
22 | import group, ignoredisk, interactive, iscsi, iscsiname, key, keyboard, lang | ||
23 | import langsupport, lilocheck, logging, logvol, mediacheck, method, monitor | ||
24 | import mouse, multipath, network, partition, raid, reboot, repo, rescue, rootpw | ||
25 | import selinux, services, skipx, sshpw, timezone, updates, upgrade, user, vnc | ||
26 | import volgroup, xconfig, zerombr, zfcp | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/authconfig.py b/scripts/lib/mic/3rdparty/pykickstart/commands/authconfig.py new file mode 100644 index 0000000000..9af9c0ff14 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/authconfig.py | |||
@@ -0,0 +1,40 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | |||
22 | class FC3_Authconfig(KickstartCommand): | ||
23 | removedKeywords = KickstartCommand.removedKeywords | ||
24 | removedAttrs = KickstartCommand.removedAttrs | ||
25 | |||
26 | def __init__(self, writePriority=0, *args, **kwargs): | ||
27 | KickstartCommand.__init__(self, *args, **kwargs) | ||
28 | self.authconfig = kwargs.get("authconfig", "") | ||
29 | |||
30 | def __str__(self): | ||
31 | retval = KickstartCommand.__str__(self) | ||
32 | |||
33 | if self.authconfig: | ||
34 | retval += "# System authorization information\nauth %s\n" % self.authconfig | ||
35 | |||
36 | return retval | ||
37 | |||
38 | def parse(self, args): | ||
39 | self.authconfig = self.currentLine[len(self.currentCmd):].strip() | ||
40 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/autopart.py b/scripts/lib/mic/3rdparty/pykickstart/commands/autopart.py new file mode 100644 index 0000000000..cf28b5c7f7 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/autopart.py | |||
@@ -0,0 +1,119 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007, 2008 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_AutoPart(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=100, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.autopart = kwargs.get("autopart", False) | ||
34 | |||
35 | def __str__(self): | ||
36 | retval = KickstartCommand.__str__(self) | ||
37 | |||
38 | if self.autopart: | ||
39 | retval += "autopart\n" | ||
40 | |||
41 | return retval | ||
42 | |||
43 | def parse(self, args): | ||
44 | if len(args) > 0: | ||
45 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "autopart") | ||
46 | |||
47 | self.autopart = True | ||
48 | return self | ||
49 | |||
50 | class F9_AutoPart(FC3_AutoPart): | ||
51 | removedKeywords = FC3_AutoPart.removedKeywords | ||
52 | removedAttrs = FC3_AutoPart.removedAttrs | ||
53 | |||
54 | def __init__(self, writePriority=100, *args, **kwargs): | ||
55 | FC3_AutoPart.__init__(self, writePriority=writePriority, *args, **kwargs) | ||
56 | self.encrypted = kwargs.get("encrypted", False) | ||
57 | self.passphrase = kwargs.get("passphrase", "") | ||
58 | |||
59 | self.op = self._getParser() | ||
60 | |||
61 | def __str__(self): | ||
62 | retval = KickstartCommand.__str__(self) | ||
63 | |||
64 | if self.autopart: | ||
65 | retval += "autopart" | ||
66 | |||
67 | if self.encrypted: | ||
68 | retval += " --encrypted" | ||
69 | |||
70 | if self.passphrase != "": | ||
71 | retval += " --passphrase=\"%s\""% self.passphrase | ||
72 | |||
73 | if retval != "": | ||
74 | retval += "\n" | ||
75 | |||
76 | return retval | ||
77 | |||
78 | def _getParser(self): | ||
79 | op = KSOptionParser() | ||
80 | op.add_option("--encrypted", action="store_true", default=False) | ||
81 | op.add_option("--passphrase") | ||
82 | return op | ||
83 | |||
84 | def parse(self, args): | ||
85 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
86 | self._setToSelf(self.op, opts) | ||
87 | self.autopart = True | ||
88 | return self | ||
89 | |||
90 | class F12_AutoPart(F9_AutoPart): | ||
91 | removedKeywords = F9_AutoPart.removedKeywords | ||
92 | removedAttrs = F9_AutoPart.removedAttrs | ||
93 | |||
94 | def __init__(self, writePriority=100, *args, **kwargs): | ||
95 | F9_AutoPart.__init__(self, writePriority=writePriority, *args, **kwargs) | ||
96 | |||
97 | self.escrowcert = kwargs.get("escrowcert", "") | ||
98 | self.backuppassphrase = kwargs.get("backuppassphrase", False) | ||
99 | |||
100 | def __str__(self): | ||
101 | retval = F9_AutoPart.__str__(self) | ||
102 | |||
103 | if self.encrypted and self.escrowcert != "": | ||
104 | retval = retval.strip() | ||
105 | |||
106 | retval += " --escrowcert=\"%s\"" % self.escrowcert | ||
107 | |||
108 | if self.backuppassphrase: | ||
109 | retval += " --backuppassphrase" | ||
110 | |||
111 | retval += "\n" | ||
112 | |||
113 | return retval | ||
114 | |||
115 | def _getParser(self): | ||
116 | op = F9_AutoPart._getParser(self) | ||
117 | op.add_option("--escrowcert") | ||
118 | op.add_option("--backuppassphrase", action="store_true", default=False) | ||
119 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/autostep.py b/scripts/lib/mic/3rdparty/pykickstart/commands/autostep.py new file mode 100644 index 0000000000..e6ae71cefc --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/autostep.py | |||
@@ -0,0 +1,55 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.options import * | ||
22 | |||
23 | class FC3_AutoStep(KickstartCommand): | ||
24 | removedKeywords = KickstartCommand.removedKeywords | ||
25 | removedAttrs = KickstartCommand.removedAttrs | ||
26 | |||
27 | def __init__(self, writePriority=0, *args, **kwargs): | ||
28 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
29 | self.op = self._getParser() | ||
30 | |||
31 | self.autostep = kwargs.get("autostep", False) | ||
32 | self.autoscreenshot = kwargs.get("autoscreenshot", False) | ||
33 | |||
34 | def __str__(self): | ||
35 | retval = KickstartCommand.__str__(self) | ||
36 | |||
37 | if self.autostep: | ||
38 | if self.autoscreenshot: | ||
39 | retval += "autostep --autoscreenshot\n" | ||
40 | else: | ||
41 | retval += "autostep\n" | ||
42 | |||
43 | return retval | ||
44 | |||
45 | def _getParser(self): | ||
46 | op = KSOptionParser() | ||
47 | op.add_option("--autoscreenshot", dest="autoscreenshot", | ||
48 | action="store_true", default=False) | ||
49 | return op | ||
50 | |||
51 | def parse(self, args): | ||
52 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
53 | self._setToSelf(self.op, opts) | ||
54 | self.autostep = True | ||
55 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/bootloader.py b/scripts/lib/mic/3rdparty/pykickstart/commands/bootloader.py new file mode 100644 index 0000000000..b227fac3be --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/bootloader.py | |||
@@ -0,0 +1,265 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.options import * | ||
22 | |||
23 | class FC3_Bootloader(KickstartCommand): | ||
24 | removedKeywords = KickstartCommand.removedKeywords | ||
25 | removedAttrs = KickstartCommand.removedAttrs | ||
26 | |||
27 | def __init__(self, writePriority=10, *args, **kwargs): | ||
28 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
29 | self.op = self._getParser() | ||
30 | |||
31 | self.driveorder = kwargs.get("driveorder", []) | ||
32 | self.appendLine = kwargs.get("appendLine", "") | ||
33 | self.forceLBA = kwargs.get("forceLBA", False) | ||
34 | self.linear = kwargs.get("linear", True) | ||
35 | self.location = kwargs.get("location", "") | ||
36 | self.md5pass = kwargs.get("md5pass", "") | ||
37 | self.password = kwargs.get("password", "") | ||
38 | self.upgrade = kwargs.get("upgrade", False) | ||
39 | self.useLilo = kwargs.get("useLilo", False) | ||
40 | |||
41 | self.deleteRemovedAttrs() | ||
42 | |||
43 | def _getArgsAsStr(self): | ||
44 | retval = "" | ||
45 | |||
46 | if self.appendLine != "": | ||
47 | retval += " --append=\"%s\"" % self.appendLine | ||
48 | if self.linear: | ||
49 | retval += " --linear" | ||
50 | if self.location: | ||
51 | retval += " --location=%s" % self.location | ||
52 | if hasattr(self, "forceLBA") and self.forceLBA: | ||
53 | retval += " --lba32" | ||
54 | if self.password != "": | ||
55 | retval += " --password=\"%s\"" % self.password | ||
56 | if self.md5pass != "": | ||
57 | retval += " --md5pass=\"%s\"" % self.md5pass | ||
58 | if self.upgrade: | ||
59 | retval += " --upgrade" | ||
60 | if self.useLilo: | ||
61 | retval += " --useLilo" | ||
62 | if len(self.driveorder) > 0: | ||
63 | retval += " --driveorder=\"%s\"" % ",".join(self.driveorder) | ||
64 | |||
65 | return retval | ||
66 | |||
67 | def __str__(self): | ||
68 | retval = KickstartCommand.__str__(self) | ||
69 | |||
70 | if self.location != "": | ||
71 | retval += "# System bootloader configuration\nbootloader" | ||
72 | retval += self._getArgsAsStr() + "\n" | ||
73 | |||
74 | return retval | ||
75 | |||
76 | def _getParser(self): | ||
77 | def driveorder_cb (option, opt_str, value, parser): | ||
78 | for d in value.split(','): | ||
79 | parser.values.ensure_value(option.dest, []).append(d) | ||
80 | |||
81 | op = KSOptionParser() | ||
82 | op.add_option("--append", dest="appendLine") | ||
83 | op.add_option("--linear", dest="linear", action="store_true", | ||
84 | default=True) | ||
85 | op.add_option("--nolinear", dest="linear", action="store_false") | ||
86 | op.add_option("--location", dest="location", type="choice", | ||
87 | default="mbr", | ||
88 | choices=["mbr", "partition", "none", "boot"]) | ||
89 | op.add_option("--lba32", dest="forceLBA", action="store_true", | ||
90 | default=False) | ||
91 | op.add_option("--password", dest="password", default="") | ||
92 | op.add_option("--md5pass", dest="md5pass", default="") | ||
93 | op.add_option("--upgrade", dest="upgrade", action="store_true", | ||
94 | default=False) | ||
95 | op.add_option("--useLilo", dest="useLilo", action="store_true", | ||
96 | default=False) | ||
97 | op.add_option("--driveorder", dest="driveorder", action="callback", | ||
98 | callback=driveorder_cb, nargs=1, type="string") | ||
99 | return op | ||
100 | |||
101 | def parse(self, args): | ||
102 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
103 | self._setToSelf(self.op, opts) | ||
104 | |||
105 | if self.currentCmd == "lilo": | ||
106 | self.useLilo = True | ||
107 | |||
108 | return self | ||
109 | |||
110 | class FC4_Bootloader(FC3_Bootloader): | ||
111 | removedKeywords = FC3_Bootloader.removedKeywords + ["linear", "useLilo"] | ||
112 | removedAttrs = FC3_Bootloader.removedAttrs + ["linear", "useLilo"] | ||
113 | |||
114 | def __init__(self, writePriority=10, *args, **kwargs): | ||
115 | FC3_Bootloader.__init__(self, writePriority, *args, **kwargs) | ||
116 | |||
117 | def _getArgsAsStr(self): | ||
118 | retval = "" | ||
119 | if self.appendLine != "": | ||
120 | retval += " --append=\"%s\"" % self.appendLine | ||
121 | if self.location: | ||
122 | retval += " --location=%s" % self.location | ||
123 | if hasattr(self, "forceLBA") and self.forceLBA: | ||
124 | retval += " --lba32" | ||
125 | if self.password != "": | ||
126 | retval += " --password=\"%s\"" % self.password | ||
127 | if self.md5pass != "": | ||
128 | retval += " --md5pass=\"%s\"" % self.md5pass | ||
129 | if self.upgrade: | ||
130 | retval += " --upgrade" | ||
131 | if len(self.driveorder) > 0: | ||
132 | retval += " --driveorder=\"%s\"" % ",".join(self.driveorder) | ||
133 | return retval | ||
134 | |||
135 | def _getParser(self): | ||
136 | op = FC3_Bootloader._getParser(self) | ||
137 | op.remove_option("--linear") | ||
138 | op.remove_option("--nolinear") | ||
139 | op.remove_option("--useLilo") | ||
140 | return op | ||
141 | |||
142 | def parse(self, args): | ||
143 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
144 | self._setToSelf(self.op, opts) | ||
145 | return self | ||
146 | |||
147 | class F8_Bootloader(FC4_Bootloader): | ||
148 | removedKeywords = FC4_Bootloader.removedKeywords | ||
149 | removedAttrs = FC4_Bootloader.removedAttrs | ||
150 | |||
151 | def __init__(self, writePriority=10, *args, **kwargs): | ||
152 | FC4_Bootloader.__init__(self, writePriority, *args, **kwargs) | ||
153 | |||
154 | self.timeout = kwargs.get("timeout", None) | ||
155 | self.default = kwargs.get("default", "") | ||
156 | |||
157 | def _getArgsAsStr(self): | ||
158 | ret = FC4_Bootloader._getArgsAsStr(self) | ||
159 | |||
160 | if self.timeout is not None: | ||
161 | ret += " --timeout=%d" %(self.timeout,) | ||
162 | if self.default: | ||
163 | ret += " --default=%s" %(self.default,) | ||
164 | |||
165 | return ret | ||
166 | |||
167 | def _getParser(self): | ||
168 | op = FC4_Bootloader._getParser(self) | ||
169 | op.add_option("--timeout", dest="timeout", type="int") | ||
170 | op.add_option("--default", dest="default") | ||
171 | return op | ||
172 | |||
173 | class F12_Bootloader(F8_Bootloader): | ||
174 | removedKeywords = F8_Bootloader.removedKeywords | ||
175 | removedAttrs = F8_Bootloader.removedAttrs | ||
176 | |||
177 | def _getParser(self): | ||
178 | op = F8_Bootloader._getParser(self) | ||
179 | op.add_option("--lba32", dest="forceLBA", deprecated=1, action="store_true") | ||
180 | return op | ||
181 | |||
182 | class F14_Bootloader(F12_Bootloader): | ||
183 | removedKeywords = F12_Bootloader.removedKeywords + ["forceLBA"] | ||
184 | removedAttrs = F12_Bootloader.removedKeywords + ["forceLBA"] | ||
185 | |||
186 | def _getParser(self): | ||
187 | op = F12_Bootloader._getParser(self) | ||
188 | op.remove_option("--lba32") | ||
189 | return op | ||
190 | |||
191 | class F15_Bootloader(F14_Bootloader): | ||
192 | removedKeywords = F14_Bootloader.removedKeywords | ||
193 | removedAttrs = F14_Bootloader.removedAttrs | ||
194 | |||
195 | def __init__(self, writePriority=10, *args, **kwargs): | ||
196 | F14_Bootloader.__init__(self, writePriority, *args, **kwargs) | ||
197 | |||
198 | self.isCrypted = kwargs.get("isCrypted", False) | ||
199 | |||
200 | def _getArgsAsStr(self): | ||
201 | ret = F14_Bootloader._getArgsAsStr(self) | ||
202 | |||
203 | if self.isCrypted: | ||
204 | ret += " --iscrypted" | ||
205 | |||
206 | return ret | ||
207 | |||
208 | def _getParser(self): | ||
209 | def password_cb(option, opt_str, value, parser): | ||
210 | parser.values.isCrypted = True | ||
211 | parser.values.password = value | ||
212 | |||
213 | op = F14_Bootloader._getParser(self) | ||
214 | op.add_option("--iscrypted", dest="isCrypted", action="store_true", default=False) | ||
215 | op.add_option("--md5pass", action="callback", callback=password_cb, nargs=1, type="string") | ||
216 | return op | ||
217 | |||
218 | class RHEL5_Bootloader(FC4_Bootloader): | ||
219 | removedKeywords = FC4_Bootloader.removedKeywords | ||
220 | removedAttrs = FC4_Bootloader.removedAttrs | ||
221 | |||
222 | def __init__(self, writePriority=10, *args, **kwargs): | ||
223 | FC4_Bootloader.__init__(self, writePriority, *args, **kwargs) | ||
224 | |||
225 | self.hvArgs = kwargs.get("hvArgs", "") | ||
226 | |||
227 | def _getArgsAsStr(self): | ||
228 | ret = FC4_Bootloader._getArgsAsStr(self) | ||
229 | |||
230 | if self.hvArgs: | ||
231 | ret += " --hvargs=\"%s\"" %(self.hvArgs,) | ||
232 | |||
233 | return ret | ||
234 | |||
235 | def _getParser(self): | ||
236 | op = FC4_Bootloader._getParser(self) | ||
237 | op.add_option("--hvargs", dest="hvArgs", type="string") | ||
238 | return op | ||
239 | |||
240 | class RHEL6_Bootloader(F12_Bootloader): | ||
241 | removedKeywords = F12_Bootloader.removedKeywords | ||
242 | removedAttrs = F12_Bootloader.removedAttrs | ||
243 | |||
244 | def __init__(self, writePriority=10, *args, **kwargs): | ||
245 | F12_Bootloader.__init__(self, writePriority, *args, **kwargs) | ||
246 | |||
247 | self.isCrypted = kwargs.get("isCrypted", False) | ||
248 | |||
249 | def _getArgsAsStr(self): | ||
250 | ret = F12_Bootloader._getArgsAsStr(self) | ||
251 | |||
252 | if self.isCrypted: | ||
253 | ret += " --iscrypted" | ||
254 | |||
255 | return ret | ||
256 | |||
257 | def _getParser(self): | ||
258 | def password_cb(option, opt_str, value, parser): | ||
259 | parser.values.isCrypted = True | ||
260 | parser.values.password = value | ||
261 | |||
262 | op = F12_Bootloader._getParser(self) | ||
263 | op.add_option("--iscrypted", dest="isCrypted", action="store_true", default=False) | ||
264 | op.add_option("--md5pass", action="callback", callback=password_cb, nargs=1, type="string") | ||
265 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/clearpart.py b/scripts/lib/mic/3rdparty/pykickstart/commands/clearpart.py new file mode 100644 index 0000000000..a8089fcb99 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/clearpart.py | |||
@@ -0,0 +1,86 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.constants import * | ||
22 | from pykickstart.errors import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | class FC3_ClearPart(KickstartCommand): | ||
26 | removedKeywords = KickstartCommand.removedKeywords | ||
27 | removedAttrs = KickstartCommand.removedAttrs | ||
28 | |||
29 | def __init__(self, writePriority=120, *args, **kwargs): | ||
30 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
31 | self.op = self._getParser() | ||
32 | |||
33 | self.drives = kwargs.get("drives", []) | ||
34 | self.initAll = kwargs.get("initAll", False) | ||
35 | self.type = kwargs.get("type", None) | ||
36 | |||
37 | def __str__(self): | ||
38 | retval = KickstartCommand.__str__(self) | ||
39 | |||
40 | if self.type is None: | ||
41 | return retval | ||
42 | |||
43 | if self.type == CLEARPART_TYPE_NONE: | ||
44 | clearstr = "--none" | ||
45 | elif self.type == CLEARPART_TYPE_LINUX: | ||
46 | clearstr = "--linux" | ||
47 | elif self.type == CLEARPART_TYPE_ALL: | ||
48 | clearstr = "--all" | ||
49 | else: | ||
50 | clearstr = "" | ||
51 | |||
52 | if self.initAll: | ||
53 | initstr = "--initlabel" | ||
54 | else: | ||
55 | initstr = "" | ||
56 | |||
57 | if len(self.drives) > 0: | ||
58 | drivestr = "--drives=" + ",".join(self.drives) | ||
59 | else: | ||
60 | drivestr = "" | ||
61 | |||
62 | retval += "# Partition clearing information\nclearpart %s %s %s\n" % (clearstr, initstr, drivestr) | ||
63 | return retval | ||
64 | |||
65 | def _getParser(self): | ||
66 | def drive_cb (option, opt_str, value, parser): | ||
67 | for d in value.split(','): | ||
68 | parser.values.ensure_value(option.dest, []).append(d) | ||
69 | |||
70 | op = KSOptionParser() | ||
71 | op.add_option("--all", dest="type", action="store_const", | ||
72 | const=CLEARPART_TYPE_ALL) | ||
73 | op.add_option("--drives", dest="drives", action="callback", | ||
74 | callback=drive_cb, nargs=1, type="string") | ||
75 | op.add_option("--initlabel", dest="initAll", action="store_true", | ||
76 | default=False) | ||
77 | op.add_option("--linux", dest="type", action="store_const", | ||
78 | const=CLEARPART_TYPE_LINUX) | ||
79 | op.add_option("--none", dest="type", action="store_const", | ||
80 | const=CLEARPART_TYPE_NONE) | ||
81 | return op | ||
82 | |||
83 | def parse(self, args): | ||
84 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
85 | self._setToSelf(self.op, opts) | ||
86 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/device.py b/scripts/lib/mic/3rdparty/pykickstart/commands/device.py new file mode 100644 index 0000000000..321410e2e2 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/device.py | |||
@@ -0,0 +1,125 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.options import * | ||
22 | |||
23 | import gettext | ||
24 | import warnings | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class F8_DeviceData(BaseData): | ||
28 | removedKeywords = BaseData.removedKeywords | ||
29 | removedAttrs = BaseData.removedAttrs | ||
30 | |||
31 | def __init__(self, *args, **kwargs): | ||
32 | BaseData.__init__(self, *args, **kwargs) | ||
33 | self.moduleName = kwargs.get("moduleName", "") | ||
34 | self.moduleOpts = kwargs.get("moduleOpts", "") | ||
35 | |||
36 | def __eq__(self, y): | ||
37 | return self.moduleName == y.moduleName | ||
38 | |||
39 | def __str__(self): | ||
40 | retval = BaseData.__str__(self) | ||
41 | |||
42 | if self.moduleName != "": | ||
43 | retval += "device %s" % self.moduleName | ||
44 | |||
45 | if self.moduleOpts != "": | ||
46 | retval += " --opts=\"%s\"" % self.moduleOpts | ||
47 | |||
48 | return retval + "\n" | ||
49 | |||
50 | class FC3_Device(KickstartCommand): | ||
51 | removedKeywords = KickstartCommand.removedKeywords | ||
52 | removedAttrs = KickstartCommand.removedAttrs | ||
53 | |||
54 | def __init__(self, writePriority=0, *args, **kwargs): | ||
55 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
56 | self.op = self._getParser() | ||
57 | |||
58 | self.type = kwargs.get("type", "") | ||
59 | self.moduleName = kwargs.get("moduleName", "") | ||
60 | self.moduleOpts = kwargs.get("moduleOpts", "") | ||
61 | |||
62 | def __eq__(self, y): | ||
63 | return self.moduleName == y.moduleName | ||
64 | |||
65 | def __str__(self): | ||
66 | retval = KickstartCommand.__str__(self) | ||
67 | |||
68 | if self.moduleName != "": | ||
69 | retval += "device %s %s" % (self.type, self.moduleName) | ||
70 | |||
71 | if self.moduleOpts != "": | ||
72 | retval += " --opts=\"%s\"" % self.moduleOpts | ||
73 | |||
74 | return retval + "\n" | ||
75 | |||
76 | def _getParser(self): | ||
77 | op = KSOptionParser() | ||
78 | op.add_option("--opts", dest="moduleOpts", default="") | ||
79 | return op | ||
80 | |||
81 | def parse(self, args): | ||
82 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
83 | |||
84 | if len(extra) != 2: | ||
85 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("device command requires two arguments: module type and name")) | ||
86 | |||
87 | self.moduleOpts = opts.moduleOpts | ||
88 | self.type = extra[0] | ||
89 | self.moduleName = extra[1] | ||
90 | return self | ||
91 | |||
92 | class F8_Device(FC3_Device): | ||
93 | removedKeywords = FC3_Device.removedKeywords | ||
94 | removedAttrs = FC3_Device.removedAttrs | ||
95 | |||
96 | def __init__(self, writePriority=0, *args, **kwargs): | ||
97 | FC3_Device.__init__(self, writePriority, *args, **kwargs) | ||
98 | self.deviceList = kwargs.get("deviceList", []) | ||
99 | |||
100 | def __str__(self): | ||
101 | retval = "" | ||
102 | for device in self.deviceList: | ||
103 | retval += device.__str__() | ||
104 | |||
105 | return retval | ||
106 | |||
107 | def parse(self, args): | ||
108 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
109 | |||
110 | if len(extra) != 1: | ||
111 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("%s command requires a single argument: %s") % ("device", "module name")) | ||
112 | |||
113 | dd = F8_DeviceData() | ||
114 | self._setToObj(self.op, opts, dd) | ||
115 | dd.lineno = self.lineno | ||
116 | dd.moduleName = extra[0] | ||
117 | |||
118 | # Check for duplicates in the data list. | ||
119 | if dd in self.dataList(): | ||
120 | warnings.warn(_("A module with the name %s has already been defined.") % dd.moduleName) | ||
121 | |||
122 | return dd | ||
123 | |||
124 | def dataList(self): | ||
125 | return self.deviceList | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/deviceprobe.py b/scripts/lib/mic/3rdparty/pykickstart/commands/deviceprobe.py new file mode 100644 index 0000000000..9f462fdff7 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/deviceprobe.py | |||
@@ -0,0 +1,40 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | |||
22 | class FC3_DeviceProbe(KickstartCommand): | ||
23 | removedKeywords = KickstartCommand.removedKeywords | ||
24 | removedAttrs = KickstartCommand.removedAttrs | ||
25 | |||
26 | def __init__(self, writePriority=0, *args, **kwargs): | ||
27 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
28 | self.deviceprobe = kwargs.get("deviceprobe", "") | ||
29 | |||
30 | def __str__(self): | ||
31 | retval = KickstartCommand.__str__(self) | ||
32 | |||
33 | if self.deviceprobe != "": | ||
34 | retval += "deviceprobe %s\n" % self.deviceprobe | ||
35 | |||
36 | return retval | ||
37 | |||
38 | def parse(self, args): | ||
39 | self.deviceprobe = " ".join(args) | ||
40 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/displaymode.py b/scripts/lib/mic/3rdparty/pykickstart/commands/displaymode.py new file mode 100644 index 0000000000..6a12d58ec2 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/displaymode.py | |||
@@ -0,0 +1,68 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.constants import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_DisplayMode(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | self.displayMode = kwargs.get("displayMode", None) | ||
35 | |||
36 | def __str__(self): | ||
37 | retval = KickstartCommand.__str__(self) | ||
38 | |||
39 | if self.displayMode is None: | ||
40 | return retval | ||
41 | |||
42 | if self.displayMode == DISPLAY_MODE_CMDLINE: | ||
43 | retval += "cmdline\n" | ||
44 | elif self.displayMode == DISPLAY_MODE_GRAPHICAL: | ||
45 | retval += "# Use graphical install\ngraphical\n" | ||
46 | elif self.displayMode == DISPLAY_MODE_TEXT: | ||
47 | retval += "# Use text mode install\ntext\n" | ||
48 | |||
49 | return retval | ||
50 | |||
51 | def _getParser(self): | ||
52 | op = KSOptionParser() | ||
53 | return op | ||
54 | |||
55 | def parse(self, args): | ||
56 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
57 | |||
58 | if len(extra) > 0: | ||
59 | raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % self.currentCmd) | ||
60 | |||
61 | if self.currentCmd == "cmdline": | ||
62 | self.displayMode = DISPLAY_MODE_CMDLINE | ||
63 | elif self.currentCmd == "graphical": | ||
64 | self.displayMode = DISPLAY_MODE_GRAPHICAL | ||
65 | elif self.currentCmd == "text": | ||
66 | self.displayMode = DISPLAY_MODE_TEXT | ||
67 | |||
68 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/dmraid.py b/scripts/lib/mic/3rdparty/pykickstart/commands/dmraid.py new file mode 100644 index 0000000000..993575a041 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/dmraid.py | |||
@@ -0,0 +1,91 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # Peter Jones <pjones@redhat.com> | ||
4 | # | ||
5 | # Copyright 2006, 2007 Red Hat, Inc. | ||
6 | # | ||
7 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
8 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
9 | # General Public License v.2. This program is distributed in the hope that it | ||
10 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
11 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
12 | # See the GNU General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along with | ||
15 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
16 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
17 | # trademarks that are incorporated in the source code or documentation are not | ||
18 | # subject to the GNU General Public License and may only be used or replicated | ||
19 | # with the express permission of Red Hat, Inc. | ||
20 | # | ||
21 | from pykickstart.base import * | ||
22 | from pykickstart.errors import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | import gettext | ||
26 | import warnings | ||
27 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
28 | |||
29 | class FC6_DmRaidData(BaseData): | ||
30 | removedKeywords = BaseData.removedKeywords | ||
31 | removedAttrs = BaseData.removedAttrs | ||
32 | |||
33 | def __init__(self, *args, **kwargs): | ||
34 | BaseData.__init__(self, *args, **kwargs) | ||
35 | |||
36 | self.name = kwargs.get("name", "") | ||
37 | self.devices = kwargs.get("devices", []) | ||
38 | self.dmset = kwargs.get("dmset", None) | ||
39 | |||
40 | def __eq__(self, y): | ||
41 | return self.name == y.name and self.devices == y.devices | ||
42 | |||
43 | def __str__(self): | ||
44 | retval = BaseData.__str__(self) | ||
45 | retval += "dmraid --name=%s" % self.name | ||
46 | |||
47 | for dev in self.devices: | ||
48 | retval += " --dev=\"%s\"" % dev | ||
49 | |||
50 | return retval + "\n" | ||
51 | |||
52 | class FC6_DmRaid(KickstartCommand): | ||
53 | removedKeywords = KickstartCommand.removedKeywords | ||
54 | removedAttrs = KickstartCommand.removedAttrs | ||
55 | |||
56 | def __init__(self, writePriority=60, *args, **kwargs): | ||
57 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
58 | self.op = self._getParser() | ||
59 | |||
60 | self.dmraids = kwargs.get("dmraids", []) | ||
61 | |||
62 | def __str__(self): | ||
63 | retval = "" | ||
64 | for dm in self.dmraids: | ||
65 | retval += dm.__str__() | ||
66 | |||
67 | return retval | ||
68 | |||
69 | def _getParser(self): | ||
70 | op = KSOptionParser() | ||
71 | op.add_option("--name", dest="name", action="store", type="string", | ||
72 | required=1) | ||
73 | op.add_option("--dev", dest="devices", action="append", type="string", | ||
74 | required=1) | ||
75 | return op | ||
76 | |||
77 | def parse(self, args): | ||
78 | dm = FC6_DmRaidData() | ||
79 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
80 | dm.name = dm.name.split('/')[-1] | ||
81 | self._setToObj(self.op, opts, dm) | ||
82 | dm.lineno = self.lineno | ||
83 | |||
84 | # Check for duplicates in the data list. | ||
85 | if dm in self.dataList(): | ||
86 | warnings.warn(_("A DM RAID device with the name %s and devices %s has already been defined.") % (dm.name, dm.devices)) | ||
87 | |||
88 | return dm | ||
89 | |||
90 | def dataList(self): | ||
91 | return self.dmraids | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/driverdisk.py b/scripts/lib/mic/3rdparty/pykickstart/commands/driverdisk.py new file mode 100644 index 0000000000..82a58c0e28 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/driverdisk.py | |||
@@ -0,0 +1,184 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007, 2008 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.options import * | ||
22 | |||
23 | import gettext | ||
24 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
25 | |||
26 | class FC3_DriverDiskData(BaseData): | ||
27 | removedKeywords = BaseData.removedKeywords | ||
28 | removedAttrs = BaseData.removedAttrs | ||
29 | |||
30 | def __init__(self, writePriority=0, *args, **kwargs): | ||
31 | BaseData.__init__(self, *args, **kwargs) | ||
32 | |||
33 | self.partition = kwargs.get("partition", "") | ||
34 | self.source = kwargs.get("source", "") | ||
35 | self.type = kwargs.get("type", "") | ||
36 | |||
37 | def _getArgsAsStr(self): | ||
38 | retval = "" | ||
39 | |||
40 | if self.partition: | ||
41 | retval += "%s" % self.partition | ||
42 | |||
43 | if hasattr(self, "type") and self.type: | ||
44 | retval += " --type=%s" % self.type | ||
45 | elif self.source: | ||
46 | retval += "--source=%s" % self.source | ||
47 | return retval | ||
48 | |||
49 | def __str__(self): | ||
50 | retval = BaseData.__str__(self) | ||
51 | retval += "driverdisk %s\n" % self._getArgsAsStr() | ||
52 | return retval | ||
53 | |||
54 | class FC4_DriverDiskData(FC3_DriverDiskData): | ||
55 | removedKeywords = FC3_DriverDiskData.removedKeywords | ||
56 | removedAttrs = FC3_DriverDiskData.removedAttrs | ||
57 | |||
58 | def __init__(self, writePriority=0, *args, **kwargs): | ||
59 | FC3_DriverDiskData.__init__(self, *args, **kwargs) | ||
60 | self.deleteRemovedAttrs() | ||
61 | |||
62 | self.biospart = kwargs.get("biospart", "") | ||
63 | |||
64 | def _getArgsAsStr(self): | ||
65 | retval = "" | ||
66 | |||
67 | if self.partition: | ||
68 | retval += "%s" % self.partition | ||
69 | |||
70 | if hasattr(self, "type") and self.type: | ||
71 | retval += " --type=%s" % self.type | ||
72 | elif self.source: | ||
73 | retval += "--source=%s" % self.source | ||
74 | elif self.biospart: | ||
75 | retval += "--biospart=%s" % self.biospart | ||
76 | |||
77 | return retval | ||
78 | |||
79 | class F12_DriverDiskData(FC4_DriverDiskData): | ||
80 | removedKeywords = FC4_DriverDiskData.removedKeywords + ["type"] | ||
81 | removedAttrs = FC4_DriverDiskData.removedAttrs + ["type"] | ||
82 | |||
83 | def __init__(self, *args, **kwargs): | ||
84 | FC4_DriverDiskData.__init__(self, *args, **kwargs) | ||
85 | self.deleteRemovedAttrs() | ||
86 | |||
87 | F14_DriverDiskData = F12_DriverDiskData | ||
88 | |||
89 | class FC3_DriverDisk(KickstartCommand): | ||
90 | removedKeywords = KickstartCommand.removedKeywords | ||
91 | removedAttrs = KickstartCommand.removedAttrs | ||
92 | |||
93 | def __init__(self, writePriority=0, *args, **kwargs): | ||
94 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
95 | self.op = self._getParser() | ||
96 | |||
97 | self.driverdiskList = kwargs.get("driverdiskList", []) | ||
98 | |||
99 | def __str__(self): | ||
100 | retval = "" | ||
101 | for dd in self.driverdiskList: | ||
102 | retval += dd.__str__() | ||
103 | |||
104 | return retval | ||
105 | |||
106 | def _getParser(self): | ||
107 | op = KSOptionParser() | ||
108 | op.add_option("--source") | ||
109 | op.add_option("--type") | ||
110 | return op | ||
111 | |||
112 | def parse(self, args): | ||
113 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
114 | |||
115 | if len(extra) > 1: | ||
116 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one partition may be specified for driverdisk command.")) | ||
117 | |||
118 | if len(extra) == 1 and opts.source: | ||
119 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --source and partition may be specified for driverdisk command.")) | ||
120 | |||
121 | if not extra and not opts.source: | ||
122 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --source or partition must be specified for driverdisk command.")) | ||
123 | |||
124 | ddd = self.handler.DriverDiskData() | ||
125 | self._setToObj(self.op, opts, ddd) | ||
126 | ddd.lineno = self.lineno | ||
127 | if len(extra) == 1: | ||
128 | ddd.partition = extra[0] | ||
129 | |||
130 | return ddd | ||
131 | |||
132 | def dataList(self): | ||
133 | return self.driverdiskList | ||
134 | |||
135 | class FC4_DriverDisk(FC3_DriverDisk): | ||
136 | removedKeywords = FC3_DriverDisk.removedKeywords | ||
137 | removedAttrs = FC3_DriverDisk.removedKeywords | ||
138 | |||
139 | def _getParser(self): | ||
140 | op = FC3_DriverDisk._getParser(self) | ||
141 | op.add_option("--biospart") | ||
142 | return op | ||
143 | |||
144 | def parse(self, args): | ||
145 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
146 | |||
147 | if len(extra) > 1: | ||
148 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one partition may be specified for driverdisk command.")) | ||
149 | |||
150 | if len(extra) == 1 and opts.source: | ||
151 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --source and partition may be specified for driverdisk command.")) | ||
152 | elif len(extra) == 1 and opts.biospart: | ||
153 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --biospart and partition may be specified for driverdisk command.")) | ||
154 | elif opts.source and opts.biospart: | ||
155 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --biospart and --source may be specified for driverdisk command.")) | ||
156 | |||
157 | if not extra and not opts.source and not opts.biospart: | ||
158 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --source, --biospart, or partition must be specified for driverdisk command.")) | ||
159 | |||
160 | ddd = self.handler.DriverDiskData() | ||
161 | self._setToObj(self.op, opts, ddd) | ||
162 | ddd.lineno = self.lineno | ||
163 | if len(extra) == 1: | ||
164 | ddd.partition = extra[0] | ||
165 | |||
166 | return ddd | ||
167 | |||
168 | class F12_DriverDisk(FC4_DriverDisk): | ||
169 | removedKeywords = FC4_DriverDisk.removedKeywords | ||
170 | removedAttrs = FC4_DriverDisk.removedKeywords | ||
171 | |||
172 | def _getParser(self): | ||
173 | op = FC4_DriverDisk._getParser(self) | ||
174 | op.add_option("--type", deprecated=1) | ||
175 | return op | ||
176 | |||
177 | class F14_DriverDisk(F12_DriverDisk): | ||
178 | removedKeywords = F12_DriverDisk.removedKeywords | ||
179 | removedAttrs = F12_DriverDisk.removedKeywords | ||
180 | |||
181 | def _getParser(self): | ||
182 | op = F12_DriverDisk._getParser(self) | ||
183 | op.remove_option("--type") | ||
184 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/fcoe.py b/scripts/lib/mic/3rdparty/pykickstart/commands/fcoe.py new file mode 100644 index 0000000000..33208499b3 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/fcoe.py | |||
@@ -0,0 +1,114 @@ | |||
1 | # | ||
2 | # Hans de Goede <hdegoede@redhat.com> | ||
3 | # | ||
4 | # Copyright 2009 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.options import * | ||
22 | |||
23 | import gettext | ||
24 | import warnings | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class F12_FcoeData(BaseData): | ||
28 | removedKeywords = BaseData.removedKeywords | ||
29 | removedAttrs = BaseData.removedAttrs | ||
30 | |||
31 | def __init__(self, *args, **kwargs): | ||
32 | BaseData.__init__(self, *args, **kwargs) | ||
33 | self.nic = kwargs.get("nic", None) | ||
34 | |||
35 | def __eq__(self, y): | ||
36 | return self.nic == y.nic | ||
37 | |||
38 | def _getArgsAsStr(self): | ||
39 | retval = "" | ||
40 | |||
41 | if self.nic: | ||
42 | retval += " --nic=%s" % self.nic | ||
43 | |||
44 | return retval | ||
45 | |||
46 | def __str__(self): | ||
47 | retval = BaseData.__str__(self) | ||
48 | retval += "fcoe%s\n" % self._getArgsAsStr() | ||
49 | return retval | ||
50 | |||
51 | class F13_FcoeData(F12_FcoeData): | ||
52 | removedKeywords = F12_FcoeData.removedKeywords | ||
53 | removedAttrs = F12_FcoeData.removedAttrs | ||
54 | |||
55 | def __init__(self, *args, **kwargs): | ||
56 | F12_FcoeData.__init__(self, *args, **kwargs) | ||
57 | self.dcb = kwargs.get("dcb", False) | ||
58 | |||
59 | def _getArgsAsStr(self): | ||
60 | retval = F12_FcoeData._getArgsAsStr(self) | ||
61 | |||
62 | if self.dcb: | ||
63 | retval += " --dcb" | ||
64 | |||
65 | return retval | ||
66 | |||
67 | class F12_Fcoe(KickstartCommand): | ||
68 | removedKeywords = KickstartCommand.removedKeywords | ||
69 | removedAttrs = KickstartCommand.removedAttrs | ||
70 | |||
71 | def __init__(self, writePriority=71, *args, **kwargs): | ||
72 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
73 | self.op = self._getParser() | ||
74 | self.fcoe = kwargs.get("fcoe", []) | ||
75 | |||
76 | def __str__(self): | ||
77 | retval = "" | ||
78 | for fcoe in self.fcoe: | ||
79 | retval += fcoe.__str__() | ||
80 | |||
81 | return retval | ||
82 | |||
83 | def _getParser(self): | ||
84 | op = KSOptionParser() | ||
85 | op.add_option("--nic", dest="nic", required=1) | ||
86 | return op | ||
87 | |||
88 | def parse(self, args): | ||
89 | zd = self.handler.FcoeData() | ||
90 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
91 | if len(extra) > 0: | ||
92 | mapping = {"command": "fcoe", "options": extra} | ||
93 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping) | ||
94 | |||
95 | self._setToObj(self.op, opts, zd) | ||
96 | zd.lineno = self.lineno | ||
97 | |||
98 | # Check for duplicates in the data list. | ||
99 | if zd in self.dataList(): | ||
100 | warnings.warn(_("A FCOE device with the name %s has already been defined.") % zd.nic) | ||
101 | |||
102 | return zd | ||
103 | |||
104 | def dataList(self): | ||
105 | return self.fcoe | ||
106 | |||
107 | class F13_Fcoe(F12_Fcoe): | ||
108 | removedKeywords = F12_Fcoe.removedKeywords | ||
109 | removedAttrs = F12_Fcoe.removedAttrs | ||
110 | |||
111 | def _getParser(self): | ||
112 | op = F12_Fcoe._getParser(self) | ||
113 | op.add_option("--dcb", dest="dcb", action="store_true", default=False) | ||
114 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/firewall.py b/scripts/lib/mic/3rdparty/pykickstart/commands/firewall.py new file mode 100644 index 0000000000..24a01bd610 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/firewall.py | |||
@@ -0,0 +1,193 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_Firewall(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | |||
35 | self.enabled = kwargs.get("enabled", None) | ||
36 | self.ports = kwargs.get("ports", []) | ||
37 | self.trusts = kwargs.get("trusts", []) | ||
38 | |||
39 | def __str__(self): | ||
40 | extra = [] | ||
41 | filteredPorts = [] | ||
42 | |||
43 | retval = KickstartCommand.__str__(self) | ||
44 | |||
45 | if self.enabled is None: | ||
46 | return retval | ||
47 | |||
48 | if self.enabled: | ||
49 | # It's possible we have words in the ports list instead of | ||
50 | # port:proto (s-c-kickstart may do this). So, filter those | ||
51 | # out into their own list leaving what we expect. | ||
52 | for port in self.ports: | ||
53 | if port == "ssh": | ||
54 | extra.append(" --ssh") | ||
55 | elif port == "telnet": | ||
56 | extra.append(" --telnet") | ||
57 | elif port == "smtp": | ||
58 | extra.append(" --smtp") | ||
59 | elif port == "http": | ||
60 | extra.append(" --http") | ||
61 | elif port == "ftp": | ||
62 | extra.append(" --ftp") | ||
63 | else: | ||
64 | filteredPorts.append(port) | ||
65 | |||
66 | # All the port:proto strings go into a comma-separated list. | ||
67 | portstr = ",".join(filteredPorts) | ||
68 | if len(portstr) > 0: | ||
69 | portstr = " --port=" + portstr | ||
70 | else: | ||
71 | portstr = "" | ||
72 | |||
73 | extrastr = "".join(extra) | ||
74 | truststr = ",".join(self.trusts) | ||
75 | |||
76 | if len(truststr) > 0: | ||
77 | truststr = " --trust=" + truststr | ||
78 | |||
79 | # The output port list consists only of port:proto for | ||
80 | # everything that we don't recognize, and special options for | ||
81 | # those that we do. | ||
82 | retval += "# Firewall configuration\nfirewall --enabled%s%s%s\n" % (extrastr, portstr, truststr) | ||
83 | else: | ||
84 | retval += "# Firewall configuration\nfirewall --disabled\n" | ||
85 | |||
86 | return retval | ||
87 | |||
88 | def _getParser(self): | ||
89 | def firewall_port_cb (option, opt_str, value, parser): | ||
90 | for p in value.split(","): | ||
91 | p = p.strip() | ||
92 | if p.find(":") == -1: | ||
93 | p = "%s:tcp" % p | ||
94 | parser.values.ensure_value(option.dest, []).append(p) | ||
95 | |||
96 | op = KSOptionParser(mapping={"ssh":["22:tcp"], "telnet":["23:tcp"], | ||
97 | "smtp":["25:tcp"], "http":["80:tcp", "443:tcp"], | ||
98 | "ftp":["21:tcp"]}) | ||
99 | |||
100 | op.add_option("--disable", "--disabled", dest="enabled", | ||
101 | action="store_false") | ||
102 | op.add_option("--enable", "--enabled", dest="enabled", | ||
103 | action="store_true", default=True) | ||
104 | op.add_option("--ftp", "--http", "--smtp", "--ssh", "--telnet", | ||
105 | dest="ports", action="map_extend") | ||
106 | op.add_option("--high", deprecated=1) | ||
107 | op.add_option("--medium", deprecated=1) | ||
108 | op.add_option("--port", dest="ports", action="callback", | ||
109 | callback=firewall_port_cb, nargs=1, type="string") | ||
110 | op.add_option("--trust", dest="trusts", action="append") | ||
111 | return op | ||
112 | |||
113 | def parse(self, args): | ||
114 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
115 | |||
116 | if len(extra) != 0: | ||
117 | mapping = {"command": "firewall", "options": extra} | ||
118 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping) | ||
119 | |||
120 | self._setToSelf(self.op, opts) | ||
121 | return self | ||
122 | |||
123 | class F9_Firewall(FC3_Firewall): | ||
124 | removedKeywords = FC3_Firewall.removedKeywords | ||
125 | removedAttrs = FC3_Firewall.removedAttrs | ||
126 | |||
127 | def _getParser(self): | ||
128 | op = FC3_Firewall._getParser(self) | ||
129 | op.remove_option("--high") | ||
130 | op.remove_option("--medium") | ||
131 | return op | ||
132 | |||
133 | class F10_Firewall(F9_Firewall): | ||
134 | removedKeywords = F9_Firewall.removedKeywords | ||
135 | removedAttrs = F9_Firewall.removedAttrs | ||
136 | |||
137 | def __init__(self, writePriority=0, *args, **kwargs): | ||
138 | F9_Firewall.__init__(self, writePriority, *args, **kwargs) | ||
139 | self.services = kwargs.get("services", []) | ||
140 | |||
141 | def __str__(self): | ||
142 | if self.enabled is None: | ||
143 | return "" | ||
144 | |||
145 | retval = F9_Firewall.__str__(self) | ||
146 | if self.enabled: | ||
147 | retval = retval.strip() | ||
148 | |||
149 | svcstr = ",".join(self.services) | ||
150 | if len(svcstr) > 0: | ||
151 | svcstr = " --service=" + svcstr | ||
152 | else: | ||
153 | svcstr = "" | ||
154 | |||
155 | return retval + "%s\n" % svcstr | ||
156 | else: | ||
157 | return retval | ||
158 | |||
159 | def _getParser(self): | ||
160 | def service_cb (option, opt_str, value, parser): | ||
161 | # python2.4 does not support action="append_const" that we were | ||
162 | # using for these options. Instead, we have to fake it by | ||
163 | # appending whatever the option string is to the service list. | ||
164 | if not value: | ||
165 | parser.values.ensure_value(option.dest, []).append(opt_str[2:]) | ||
166 | return | ||
167 | |||
168 | for p in value.split(","): | ||
169 | p = p.strip() | ||
170 | parser.values.ensure_value(option.dest, []).append(p) | ||
171 | |||
172 | op = F9_Firewall._getParser(self) | ||
173 | op.add_option("--service", dest="services", action="callback", | ||
174 | callback=service_cb, nargs=1, type="string") | ||
175 | op.add_option("--ftp", dest="services", action="callback", | ||
176 | callback=service_cb) | ||
177 | op.add_option("--http", dest="services", action="callback", | ||
178 | callback=service_cb) | ||
179 | op.add_option("--smtp", dest="services", action="callback", | ||
180 | callback=service_cb) | ||
181 | op.add_option("--ssh", dest="services", action="callback", | ||
182 | callback=service_cb) | ||
183 | op.add_option("--telnet", deprecated=1) | ||
184 | return op | ||
185 | |||
186 | class F14_Firewall(F10_Firewall): | ||
187 | removedKeywords = F10_Firewall.removedKeywords + ["telnet"] | ||
188 | removedAttrs = F10_Firewall.removedAttrs + ["telnet"] | ||
189 | |||
190 | def _getParser(self): | ||
191 | op = F10_Firewall._getParser(self) | ||
192 | op.remove_option("--telnet") | ||
193 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/firstboot.py b/scripts/lib/mic/3rdparty/pykickstart/commands/firstboot.py new file mode 100644 index 0000000000..05c0ac11c6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/firstboot.py | |||
@@ -0,0 +1,62 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.constants import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | class FC3_Firstboot(KickstartCommand): | ||
25 | removedKeywords = KickstartCommand.removedKeywords | ||
26 | removedAttrs = KickstartCommand.removedAttrs | ||
27 | |||
28 | def __init__(self, writePriority=0, *args, **kwargs): | ||
29 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
30 | self.op = self._getParser() | ||
31 | |||
32 | self.firstboot = kwargs.get("firstboot", None) | ||
33 | |||
34 | def __str__(self): | ||
35 | retval = KickstartCommand.__str__(self) | ||
36 | |||
37 | if self.firstboot is None: | ||
38 | return retval | ||
39 | |||
40 | if self.firstboot == FIRSTBOOT_SKIP: | ||
41 | retval += "firstboot --disable\n" | ||
42 | elif self.firstboot == FIRSTBOOT_DEFAULT: | ||
43 | retval += "# Run the Setup Agent on first boot\nfirstboot --enable\n" | ||
44 | elif self.firstboot == FIRSTBOOT_RECONFIG: | ||
45 | retval += "# Run the Setup Agent on first boot\nfirstboot --reconfig\n" | ||
46 | |||
47 | return retval | ||
48 | |||
49 | def _getParser(self): | ||
50 | op = KSOptionParser() | ||
51 | op.add_option("--disable", "--disabled", dest="firstboot", | ||
52 | action="store_const", const=FIRSTBOOT_SKIP) | ||
53 | op.add_option("--enable", "--enabled", dest="firstboot", | ||
54 | action="store_const", const=FIRSTBOOT_DEFAULT) | ||
55 | op.add_option("--reconfig", dest="firstboot", action="store_const", | ||
56 | const=FIRSTBOOT_RECONFIG) | ||
57 | return op | ||
58 | |||
59 | def parse(self, args): | ||
60 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
61 | self.firstboot = opts.firstboot | ||
62 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/group.py b/scripts/lib/mic/3rdparty/pykickstart/commands/group.py new file mode 100644 index 0000000000..80ba5bdca6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/group.py | |||
@@ -0,0 +1,88 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2009 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.constants import * | ||
22 | from pykickstart.errors import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | import gettext | ||
26 | import warnings | ||
27 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
28 | |||
29 | class F12_GroupData(BaseData): | ||
30 | removedKeywords = BaseData.removedKeywords | ||
31 | removedAttrs = BaseData.removedAttrs | ||
32 | |||
33 | def __init__(self, *args, **kwargs): | ||
34 | BaseData.__init__(self, *args, **kwargs) | ||
35 | self.name = kwargs.get("name", "") | ||
36 | self.gid = kwargs.get("gid", None) | ||
37 | |||
38 | def __eq__(self, y): | ||
39 | return self.name == y.name | ||
40 | |||
41 | def __str__(self): | ||
42 | retval = BaseData.__str__(self) | ||
43 | retval += "group" | ||
44 | |||
45 | if self.name: | ||
46 | retval += " --name=%s" % self.name | ||
47 | if self.gid: | ||
48 | retval += " --gid=%s" % self.gid | ||
49 | |||
50 | return retval + "\n" | ||
51 | |||
52 | class F12_Group(KickstartCommand): | ||
53 | removedKeywords = KickstartCommand.removedKeywords | ||
54 | removedAttrs = KickstartCommand.removedAttrs | ||
55 | |||
56 | def __init__(self, writePriority=0, *args, **kwargs): | ||
57 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
58 | self.op = self._getParser() | ||
59 | |||
60 | self.groupList = kwargs.get("groupList", []) | ||
61 | |||
62 | def __str__(self): | ||
63 | retval = "" | ||
64 | for user in self.groupList: | ||
65 | retval += user.__str__() | ||
66 | |||
67 | return retval | ||
68 | |||
69 | def _getParser(self): | ||
70 | op = KSOptionParser() | ||
71 | op.add_option("--name", required=1) | ||
72 | op.add_option("--gid", type="int") | ||
73 | return op | ||
74 | |||
75 | def parse(self, args): | ||
76 | gd = self.handler.GroupData() | ||
77 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
78 | self._setToObj(self.op, opts, gd) | ||
79 | gd.lineno = self.lineno | ||
80 | |||
81 | # Check for duplicates in the data list. | ||
82 | if gd in self.dataList(): | ||
83 | warnings.warn(_("A group with the name %s has already been defined.") % gd.name) | ||
84 | |||
85 | return gd | ||
86 | |||
87 | def dataList(self): | ||
88 | return self.groupList | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/ignoredisk.py b/scripts/lib/mic/3rdparty/pykickstart/commands/ignoredisk.py new file mode 100644 index 0000000000..676d080836 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/ignoredisk.py | |||
@@ -0,0 +1,139 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007, 2009 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.options import * | ||
22 | |||
23 | import gettext | ||
24 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
25 | |||
26 | class FC3_IgnoreDisk(KickstartCommand): | ||
27 | removedKeywords = KickstartCommand.removedKeywords | ||
28 | removedAttrs = KickstartCommand.removedAttrs | ||
29 | |||
30 | def __init__(self, writePriority=0, *args, **kwargs): | ||
31 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
32 | self.op = self._getParser() | ||
33 | |||
34 | self.ignoredisk = kwargs.get("ignoredisk", []) | ||
35 | |||
36 | def __str__(self): | ||
37 | retval = KickstartCommand.__str__(self) | ||
38 | |||
39 | if len(self.ignoredisk) > 0: | ||
40 | retval += "ignoredisk --drives=%s\n" % ",".join(self.ignoredisk) | ||
41 | |||
42 | return retval | ||
43 | |||
44 | def _getParser(self): | ||
45 | def drive_cb (option, opt_str, value, parser): | ||
46 | for d in value.split(','): | ||
47 | parser.values.ensure_value(option.dest, []).append(d) | ||
48 | |||
49 | op = KSOptionParser() | ||
50 | op.add_option("--drives", dest="ignoredisk", action="callback", | ||
51 | callback=drive_cb, nargs=1, type="string", required=1) | ||
52 | return op | ||
53 | |||
54 | def parse(self, args): | ||
55 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
56 | self._setToSelf(self.op, opts) | ||
57 | return self | ||
58 | |||
59 | class F8_IgnoreDisk(FC3_IgnoreDisk): | ||
60 | removedKeywords = FC3_IgnoreDisk.removedKeywords | ||
61 | removedAttrs = FC3_IgnoreDisk.removedAttrs | ||
62 | |||
63 | def __init__(self, writePriority=0, *args, **kwargs): | ||
64 | FC3_IgnoreDisk.__init__(self, writePriority, *args, **kwargs) | ||
65 | |||
66 | self.onlyuse = kwargs.get("onlyuse", []) | ||
67 | |||
68 | def __str__(self): | ||
69 | retval = KickstartCommand.__str__(self) | ||
70 | |||
71 | if len(self.ignoredisk) > 0: | ||
72 | retval += "ignoredisk --drives=%s\n" % ",".join(self.ignoredisk) | ||
73 | elif len(self.onlyuse) > 0: | ||
74 | retval += "ignoredisk --only-use=%s\n" % ",".join(self.onlyuse) | ||
75 | |||
76 | return retval | ||
77 | |||
78 | def parse(self, args, errorCheck=True): | ||
79 | retval = FC3_IgnoreDisk.parse(self, args) | ||
80 | |||
81 | if errorCheck: | ||
82 | if (len(self.ignoredisk) == 0 and len(self.onlyuse) == 0) or (len(self.ignoredisk) > 0 and (len(self.onlyuse) > 0)): | ||
83 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --drives or --only-use must be specified for ignoredisk command.")) | ||
84 | |||
85 | return retval | ||
86 | |||
87 | def _getParser(self): | ||
88 | def drive_cb (option, opt_str, value, parser): | ||
89 | for d in value.split(','): | ||
90 | parser.values.ensure_value(option.dest, []).append(d) | ||
91 | |||
92 | op = FC3_IgnoreDisk._getParser(self) | ||
93 | op.add_option("--drives", dest="ignoredisk", action="callback", | ||
94 | callback=drive_cb, nargs=1, type="string") | ||
95 | op.add_option("--only-use", dest="onlyuse", action="callback", | ||
96 | callback=drive_cb, nargs=1, type="string") | ||
97 | return op | ||
98 | |||
99 | class RHEL6_IgnoreDisk(F8_IgnoreDisk): | ||
100 | removedKeywords = F8_IgnoreDisk.removedKeywords | ||
101 | removedAttrs = F8_IgnoreDisk.removedAttrs | ||
102 | |||
103 | def __init__(self, writePriority=0, *args, **kwargs): | ||
104 | F8_IgnoreDisk.__init__(self, writePriority, *args, **kwargs) | ||
105 | |||
106 | self.interactive = kwargs.get("interactive", False) | ||
107 | if self.interactive: | ||
108 | self.ignoredisk = [] | ||
109 | |||
110 | def __str__(self): | ||
111 | retval = F8_IgnoreDisk.__str__(self) | ||
112 | |||
113 | if self.interactive: | ||
114 | retval = "ignoredisk --interactive\n" | ||
115 | |||
116 | return retval | ||
117 | |||
118 | def parse(self, args): | ||
119 | retval = F8_IgnoreDisk.parse(self, args, errorCheck=False) | ||
120 | |||
121 | howmany = 0 | ||
122 | if len(self.ignoredisk) > 0: | ||
123 | howmany += 1 | ||
124 | if len(self.onlyuse) > 0: | ||
125 | howmany += 1 | ||
126 | if self.interactive: | ||
127 | howmany += 1 | ||
128 | if howmany != 1: | ||
129 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --drives , --only-use , or --interactive must be specified for ignoredisk command.")) | ||
130 | |||
131 | return retval | ||
132 | |||
133 | def _getParser(self): | ||
134 | op = F8_IgnoreDisk._getParser(self) | ||
135 | op.add_option("--interactive", dest="interactive", action="store_true", | ||
136 | default=False) | ||
137 | return op | ||
138 | |||
139 | F14_IgnoreDisk = RHEL6_IgnoreDisk | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/interactive.py b/scripts/lib/mic/3rdparty/pykickstart/commands/interactive.py new file mode 100644 index 0000000000..fa3dc025b1 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/interactive.py | |||
@@ -0,0 +1,58 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_Interactive(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | self.interactive = kwargs.get("interactive", False) | ||
35 | |||
36 | def __str__(self): | ||
37 | retval = KickstartCommand.__str__(self) | ||
38 | |||
39 | if self.interactive: | ||
40 | retval += "# Use interactive kickstart installation method\ninteractive\n" | ||
41 | |||
42 | return retval | ||
43 | |||
44 | def _getParser(self): | ||
45 | op = KSOptionParser() | ||
46 | return op | ||
47 | |||
48 | def parse(self, args): | ||
49 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
50 | if len(extra) > 0: | ||
51 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "interactive") | ||
52 | |||
53 | self.interactive = True | ||
54 | return self | ||
55 | |||
56 | class F14_Interactive(DeprecatedCommand): | ||
57 | def __init__(self): | ||
58 | DeprecatedCommand.__init__(self) | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/iscsi.py b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsi.py new file mode 100644 index 0000000000..da5a544e86 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsi.py | |||
@@ -0,0 +1,133 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # Peter Jones <pjones@redhat.com> | ||
4 | # | ||
5 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
6 | # | ||
7 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
8 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
9 | # General Public License v.2. This program is distributed in the hope that it | ||
10 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
11 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
12 | # See the GNU General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along with | ||
15 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
16 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
17 | # trademarks that are incorporated in the source code or documentation are not | ||
18 | # subject to the GNU General Public License and may only be used or replicated | ||
19 | # with the express permission of Red Hat, Inc. | ||
20 | # | ||
21 | from pykickstart.base import * | ||
22 | from pykickstart.errors import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | import gettext | ||
26 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
27 | |||
28 | class FC6_IscsiData(BaseData): | ||
29 | removedKeywords = BaseData.removedKeywords | ||
30 | removedAttrs = BaseData.removedAttrs | ||
31 | |||
32 | def __init__(self, *args, **kwargs): | ||
33 | BaseData.__init__(self, *args, **kwargs) | ||
34 | self.ipaddr = kwargs.get("ipaddr", "") | ||
35 | self.port = kwargs.get("port", "3260") | ||
36 | self.target = kwargs.get("target", "") | ||
37 | self.user = kwargs.get("user", None) | ||
38 | self.password = kwargs.get("password", None) | ||
39 | |||
40 | def _getArgsAsStr(self): | ||
41 | retval = "" | ||
42 | |||
43 | if self.target != "": | ||
44 | retval += " --target=%s" % self.target | ||
45 | if self.ipaddr != "": | ||
46 | retval += " --ipaddr=%s" % self.ipaddr | ||
47 | if self.port != "3260": | ||
48 | retval += " --port=%s" % self.port | ||
49 | if self.user is not None: | ||
50 | retval += " --user=%s" % self.user | ||
51 | if self.password is not None: | ||
52 | retval += " --password=%s" % self.password | ||
53 | |||
54 | return retval | ||
55 | |||
56 | def __str__(self): | ||
57 | retval = BaseData.__str__(self) | ||
58 | retval += "iscsi%s\n" % self._getArgsAsStr() | ||
59 | return retval | ||
60 | |||
61 | class F10_IscsiData(FC6_IscsiData): | ||
62 | removedKeywords = FC6_IscsiData.removedKeywords | ||
63 | removedAttrs = FC6_IscsiData.removedAttrs | ||
64 | |||
65 | def __init__(self, *args, **kwargs): | ||
66 | FC6_IscsiData.__init__(self, *args, **kwargs) | ||
67 | self.user_in = kwargs.get("user_in", None) | ||
68 | self.password_in = kwargs.get("password_in", None) | ||
69 | |||
70 | def _getArgsAsStr(self): | ||
71 | retval = FC6_IscsiData._getArgsAsStr(self) | ||
72 | |||
73 | if self.user_in is not None: | ||
74 | retval += " --reverse-user=%s" % self.user_in | ||
75 | if self.password_in is not None: | ||
76 | retval += " --reverse-password=%s" % self.password_in | ||
77 | |||
78 | return retval | ||
79 | |||
80 | class FC6_Iscsi(KickstartCommand): | ||
81 | removedKeywords = KickstartCommand.removedKeywords | ||
82 | removedAttrs = KickstartCommand.removedAttrs | ||
83 | |||
84 | def __init__(self, writePriority=71, *args, **kwargs): | ||
85 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
86 | self.op = self._getParser() | ||
87 | |||
88 | self.iscsi = kwargs.get("iscsi", []) | ||
89 | |||
90 | def __str__(self): | ||
91 | retval = "" | ||
92 | for iscsi in self.iscsi: | ||
93 | retval += iscsi.__str__() | ||
94 | |||
95 | return retval | ||
96 | |||
97 | def _getParser(self): | ||
98 | op = KSOptionParser() | ||
99 | op.add_option("--target", dest="target", action="store", type="string") | ||
100 | op.add_option("--ipaddr", dest="ipaddr", action="store", type="string", | ||
101 | required=1) | ||
102 | op.add_option("--port", dest="port", action="store", type="string") | ||
103 | op.add_option("--user", dest="user", action="store", type="string") | ||
104 | op.add_option("--password", dest="password", action="store", | ||
105 | type="string") | ||
106 | return op | ||
107 | |||
108 | def parse(self, args): | ||
109 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
110 | |||
111 | if len(extra) != 0: | ||
112 | mapping = {"command": "iscsi", "options": extra} | ||
113 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping) | ||
114 | |||
115 | dd = self.handler.IscsiData() | ||
116 | self._setToObj(self.op, opts, dd) | ||
117 | dd.lineno = self.lineno | ||
118 | return dd | ||
119 | |||
120 | def dataList(self): | ||
121 | return self.iscsi | ||
122 | |||
123 | class F10_Iscsi(FC6_Iscsi): | ||
124 | removedKeywords = FC6_Iscsi.removedKeywords | ||
125 | removedAttrs = FC6_Iscsi.removedAttrs | ||
126 | |||
127 | def _getParser(self): | ||
128 | op = FC6_Iscsi._getParser(self) | ||
129 | op.add_option("--reverse-user", dest="user_in", action="store", | ||
130 | type="string") | ||
131 | op.add_option("--reverse-password", dest="password_in", action="store", | ||
132 | type="string") | ||
133 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/iscsiname.py b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsiname.py new file mode 100644 index 0000000000..a87d0637d6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/iscsiname.py | |||
@@ -0,0 +1,54 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # Peter Jones <pjones@redhat.com> | ||
4 | # | ||
5 | # Copyright 2006, 2007 Red Hat, Inc. | ||
6 | # | ||
7 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
8 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
9 | # General Public License v.2. This program is distributed in the hope that it | ||
10 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
11 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
12 | # See the GNU General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along with | ||
15 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
16 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
17 | # trademarks that are incorporated in the source code or documentation are not | ||
18 | # subject to the GNU General Public License and may only be used or replicated | ||
19 | # with the express permission of Red Hat, Inc. | ||
20 | # | ||
21 | from pykickstart.base import * | ||
22 | from pykickstart.errors import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | import gettext | ||
26 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
27 | |||
28 | class FC6_IscsiName(KickstartCommand): | ||
29 | removedKeywords = KickstartCommand.removedKeywords | ||
30 | removedAttrs = KickstartCommand.removedAttrs | ||
31 | |||
32 | def __init__(self, writePriority=70, *args, **kwargs): | ||
33 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
34 | self.op = self._getParser() | ||
35 | self.iscsiname = kwargs.get("iscsiname", "") | ||
36 | |||
37 | def __str__(self): | ||
38 | retval = KickstartCommand.__str__(self) | ||
39 | |||
40 | if self.iscsiname != "": | ||
41 | retval += "iscsiname %s\n" % self.iscsiname | ||
42 | |||
43 | return retval | ||
44 | |||
45 | def _getParser(self): | ||
46 | op = KSOptionParser() | ||
47 | return op | ||
48 | |||
49 | def parse(self, args): | ||
50 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
51 | if len(extra) != 1: | ||
52 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "iscsiname") | ||
53 | self.iscsiname = extra[0] | ||
54 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/key.py b/scripts/lib/mic/3rdparty/pykickstart/commands/key.py new file mode 100644 index 0000000000..c20c4231f6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/key.py | |||
@@ -0,0 +1,64 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.constants import * | ||
22 | from pykickstart.errors import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | import gettext | ||
26 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
27 | |||
28 | class RHEL5_Key(KickstartCommand): | ||
29 | removedKeywords = KickstartCommand.removedKeywords | ||
30 | removedAttrs = KickstartCommand.removedAttrs | ||
31 | |||
32 | def __init__(self, writePriority=0, *args, **kwargs): | ||
33 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
34 | self.op = self._getParser() | ||
35 | self.key = kwargs.get("key", "") | ||
36 | self.skip = kwargs.get("skip", False) | ||
37 | |||
38 | def __str__(self): | ||
39 | retval = KickstartCommand.__str__(self) | ||
40 | |||
41 | if self.key == KS_INSTKEY_SKIP: | ||
42 | retval += "key --skip\n" | ||
43 | elif self.key != "": | ||
44 | retval += "key %s\n" % self.key | ||
45 | |||
46 | return retval | ||
47 | |||
48 | def _getParser(self): | ||
49 | op = KSOptionParser() | ||
50 | op.add_option("--skip", action="store_true", default=False) | ||
51 | return op | ||
52 | |||
53 | def parse(self, args): | ||
54 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
55 | self._setToSelf(self.op, opts) | ||
56 | |||
57 | if self.skip: | ||
58 | self.key = KS_INSTKEY_SKIP | ||
59 | elif len(extra) != 1: | ||
60 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "key") | ||
61 | else: | ||
62 | self.key = extra[0] | ||
63 | |||
64 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/keyboard.py b/scripts/lib/mic/3rdparty/pykickstart/commands/keyboard.py new file mode 100644 index 0000000000..babc2acd4c --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/keyboard.py | |||
@@ -0,0 +1,55 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_Keyboard(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | self.keyboard = kwargs.get("keyboard", "") | ||
35 | |||
36 | def __str__(self): | ||
37 | retval = KickstartCommand.__str__(self) | ||
38 | |||
39 | if self.keyboard != "": | ||
40 | retval += "# System keyboard\nkeyboard %s\n" % self.keyboard | ||
41 | |||
42 | return retval | ||
43 | |||
44 | def _getParser(self): | ||
45 | op = KSOptionParser() | ||
46 | return op | ||
47 | |||
48 | def parse(self, args): | ||
49 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
50 | |||
51 | if len(extra) != 1: | ||
52 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "keyboard") | ||
53 | |||
54 | self.keyboard = extra[0] | ||
55 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/lang.py b/scripts/lib/mic/3rdparty/pykickstart/commands/lang.py new file mode 100644 index 0000000000..cf5e46cda7 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/lang.py | |||
@@ -0,0 +1,60 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_Lang(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | self.lang = kwargs.get("lang", "") | ||
35 | |||
36 | def __str__(self): | ||
37 | retval = KickstartCommand.__str__(self) | ||
38 | |||
39 | if self.lang != "": | ||
40 | retval += "# System language\nlang %s\n" % self.lang | ||
41 | |||
42 | return retval | ||
43 | |||
44 | def _getParser(self): | ||
45 | op = KSOptionParser() | ||
46 | return op | ||
47 | |||
48 | def parse(self, args): | ||
49 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
50 | if len(extra) != 1: | ||
51 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "lang") | ||
52 | |||
53 | self.lang = extra[0] | ||
54 | return self | ||
55 | |||
56 | def apply(self, instroot="/"): | ||
57 | if self.lang == "": return | ||
58 | f = open(instroot + "/etc/sysconfig/i18n", "w+") | ||
59 | f.write("LANG=\"%s\"\n" %(self.lang,)) | ||
60 | f.close() | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/langsupport.py b/scripts/lib/mic/3rdparty/pykickstart/commands/langsupport.py new file mode 100644 index 0000000000..73a9e537a9 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/langsupport.py | |||
@@ -0,0 +1,58 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.options import * | ||
22 | |||
23 | class FC3_LangSupport(KickstartCommand): | ||
24 | removedKeywords = KickstartCommand.removedKeywords | ||
25 | removedAttrs = KickstartCommand.removedAttrs | ||
26 | |||
27 | def __init__(self, writePriority=0, *args, **kwargs): | ||
28 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
29 | self.op = self._getParser() | ||
30 | |||
31 | self.deflang = kwargs.get("deflang", "") | ||
32 | self.supported = kwargs.get("supported", []) | ||
33 | |||
34 | def __str__(self): | ||
35 | retval = KickstartCommand.__str__(self) | ||
36 | |||
37 | if self.deflang: | ||
38 | retval += "langsupport --default=%s" % self.deflang | ||
39 | |||
40 | if self.supported: | ||
41 | retval += " %s" % " ".join(self.supported) | ||
42 | |||
43 | return retval + "\n" | ||
44 | |||
45 | def _getParser(self): | ||
46 | op = KSOptionParser() | ||
47 | op.add_option("--default", dest="deflang", default="en_US.UTF-8") | ||
48 | return op | ||
49 | |||
50 | def parse(self, args): | ||
51 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
52 | self._setToSelf(self.op, opts) | ||
53 | self.supported = extra | ||
54 | return self | ||
55 | |||
56 | class FC5_LangSupport(DeprecatedCommand): | ||
57 | def __init__(self): | ||
58 | DeprecatedCommand.__init__(self) | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/lilocheck.py b/scripts/lib/mic/3rdparty/pykickstart/commands/lilocheck.py new file mode 100644 index 0000000000..92b3f930b6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/lilocheck.py | |||
@@ -0,0 +1,54 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_LiloCheck(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | self.check = kwargs.get("check", False) | ||
35 | |||
36 | def __str__(self): | ||
37 | retval = KickstartCommand.__str__(self) | ||
38 | |||
39 | if self.check: | ||
40 | retval += "lilocheck\n" | ||
41 | |||
42 | return retval | ||
43 | |||
44 | def _getParser(self): | ||
45 | op = KSOptionParser() | ||
46 | return op | ||
47 | |||
48 | def parse(self, args): | ||
49 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
50 | if len(extra) > 0: | ||
51 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "lilocheck") | ||
52 | |||
53 | self.check = True | ||
54 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/logging.py b/scripts/lib/mic/3rdparty/pykickstart/commands/logging.py new file mode 100644 index 0000000000..698561994d --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/logging.py | |||
@@ -0,0 +1,66 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007, 2009 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC6_Logging(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | |||
35 | self.host = kwargs.get("host", "") | ||
36 | self.level = kwargs.get("level", "info") | ||
37 | self.port = kwargs.get("port", "") | ||
38 | |||
39 | def __str__(self): | ||
40 | retval = KickstartCommand.__str__(self) | ||
41 | retval += "# Installation logging level\nlogging --level=%s" % self.level | ||
42 | |||
43 | if self.host != "": | ||
44 | retval += " --host=%s" % self.host | ||
45 | |||
46 | if self.port != "": | ||
47 | retval += " --port=%s" % self.port | ||
48 | |||
49 | return retval + "\n" | ||
50 | |||
51 | def _getParser(self): | ||
52 | op = KSOptionParser() | ||
53 | op.add_option("--host") | ||
54 | op.add_option("--level", type="choice", default="info", | ||
55 | choices=["debug", "info", "warning", "error", "critical"]) | ||
56 | op.add_option("--port") | ||
57 | return op | ||
58 | |||
59 | def parse(self, args): | ||
60 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
61 | |||
62 | if opts.port and not opts.host: | ||
63 | raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("Can't specify --port without --host.")) | ||
64 | |||
65 | self._setToSelf(self.op, opts) | ||
66 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/logvol.py b/scripts/lib/mic/3rdparty/pykickstart/commands/logvol.py new file mode 100644 index 0000000000..c1b9cc3a61 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/logvol.py | |||
@@ -0,0 +1,304 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007, 2008 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | import warnings | ||
26 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
27 | |||
28 | class FC3_LogVolData(BaseData): | ||
29 | removedKeywords = BaseData.removedKeywords | ||
30 | removedAttrs = BaseData.removedAttrs | ||
31 | |||
32 | def __init__(self, *args, **kwargs): | ||
33 | BaseData.__init__(self, *args, **kwargs) | ||
34 | self.fstype = kwargs.get("fstype", "") | ||
35 | self.grow = kwargs.get("grow", False) | ||
36 | self.maxSizeMB = kwargs.get("maxSizeMB", 0) | ||
37 | self.name = kwargs.get("name", "") | ||
38 | self.format = kwargs.get("format", True) | ||
39 | self.percent = kwargs.get("percent", 0) | ||
40 | self.recommended = kwargs.get("recommended", False) | ||
41 | self.size = kwargs.get("size", None) | ||
42 | self.preexist = kwargs.get("preexist", False) | ||
43 | self.vgname = kwargs.get("vgname", "") | ||
44 | self.mountpoint = kwargs.get("mountpoint", "") | ||
45 | |||
46 | def __eq__(self, y): | ||
47 | return self.vgname == y.vgname and self.name == y.name | ||
48 | |||
49 | def _getArgsAsStr(self): | ||
50 | retval = "" | ||
51 | |||
52 | if self.fstype != "": | ||
53 | retval += " --fstype=\"%s\"" % self.fstype | ||
54 | if self.grow: | ||
55 | retval += " --grow" | ||
56 | if self.maxSizeMB > 0: | ||
57 | retval += " --maxsize=%d" % self.maxSizeMB | ||
58 | if not self.format: | ||
59 | retval += " --noformat" | ||
60 | if self.percent > 0: | ||
61 | retval += " --percent=%d" % self.percent | ||
62 | if self.recommended: | ||
63 | retval += " --recommended" | ||
64 | if self.size > 0: | ||
65 | retval += " --size=%d" % self.size | ||
66 | if self.preexist: | ||
67 | retval += " --useexisting" | ||
68 | |||
69 | return retval | ||
70 | |||
71 | def __str__(self): | ||
72 | retval = BaseData.__str__(self) | ||
73 | retval += "logvol %s %s --name=%s --vgname=%s\n" % (self.mountpoint, self._getArgsAsStr(), self.name, self.vgname) | ||
74 | return retval | ||
75 | |||
76 | class FC4_LogVolData(FC3_LogVolData): | ||
77 | removedKeywords = FC3_LogVolData.removedKeywords | ||
78 | removedAttrs = FC3_LogVolData.removedAttrs | ||
79 | |||
80 | def __init__(self, *args, **kwargs): | ||
81 | FC3_LogVolData.__init__(self, *args, **kwargs) | ||
82 | self.bytesPerInode = kwargs.get("bytesPerInode", 4096) | ||
83 | self.fsopts = kwargs.get("fsopts", "") | ||
84 | |||
85 | def _getArgsAsStr(self): | ||
86 | retval = FC3_LogVolData._getArgsAsStr(self) | ||
87 | |||
88 | if hasattr(self, "bytesPerInode") and self.bytesPerInode != 0: | ||
89 | retval += " --bytes-per-inode=%d" % self.bytesPerInode | ||
90 | if self.fsopts != "": | ||
91 | retval += " --fsoptions=\"%s\"" % self.fsopts | ||
92 | |||
93 | return retval | ||
94 | |||
95 | class RHEL5_LogVolData(FC4_LogVolData): | ||
96 | removedKeywords = FC4_LogVolData.removedKeywords | ||
97 | removedAttrs = FC4_LogVolData.removedAttrs | ||
98 | |||
99 | def __init__(self, *args, **kwargs): | ||
100 | FC4_LogVolData.__init__(self, *args, **kwargs) | ||
101 | self.encrypted = kwargs.get("encrypted", False) | ||
102 | self.passphrase = kwargs.get("passphrase", "") | ||
103 | |||
104 | def _getArgsAsStr(self): | ||
105 | retval = FC4_LogVolData._getArgsAsStr(self) | ||
106 | |||
107 | if self.encrypted: | ||
108 | retval += " --encrypted" | ||
109 | |||
110 | if self.passphrase != "": | ||
111 | retval += " --passphrase=\"%s\"" % self.passphrase | ||
112 | |||
113 | return retval | ||
114 | |||
115 | class F9_LogVolData(FC4_LogVolData): | ||
116 | removedKeywords = FC4_LogVolData.removedKeywords + ["bytesPerInode"] | ||
117 | removedAttrs = FC4_LogVolData.removedAttrs + ["bytesPerInode"] | ||
118 | |||
119 | def __init__(self, *args, **kwargs): | ||
120 | FC4_LogVolData.__init__(self, *args, **kwargs) | ||
121 | self.deleteRemovedAttrs() | ||
122 | |||
123 | self.fsopts = kwargs.get("fsopts", "") | ||
124 | self.fsprofile = kwargs.get("fsprofile", "") | ||
125 | self.encrypted = kwargs.get("encrypted", False) | ||
126 | self.passphrase = kwargs.get("passphrase", "") | ||
127 | |||
128 | def _getArgsAsStr(self): | ||
129 | retval = FC4_LogVolData._getArgsAsStr(self) | ||
130 | |||
131 | if self.fsprofile != "": | ||
132 | retval += " --fsprofile=\"%s\"" % self.fsprofile | ||
133 | if self.encrypted: | ||
134 | retval += " --encrypted" | ||
135 | |||
136 | if self.passphrase != "": | ||
137 | retval += " --passphrase=\"%s\"" % self.passphrase | ||
138 | |||
139 | return retval | ||
140 | |||
141 | class F12_LogVolData(F9_LogVolData): | ||
142 | removedKeywords = F9_LogVolData.removedKeywords | ||
143 | removedAttrs = F9_LogVolData.removedAttrs | ||
144 | |||
145 | def __init__(self, *args, **kwargs): | ||
146 | F9_LogVolData.__init__(self, *args, **kwargs) | ||
147 | self.deleteRemovedAttrs() | ||
148 | |||
149 | self.escrowcert = kwargs.get("escrowcert", "") | ||
150 | self.backuppassphrase = kwargs.get("backuppassphrase", False) | ||
151 | |||
152 | def _getArgsAsStr(self): | ||
153 | retval = F9_LogVolData._getArgsAsStr(self) | ||
154 | |||
155 | if self.encrypted and self.escrowcert != "": | ||
156 | retval += " --escrowcert=\"%s\"" % self.escrowcert | ||
157 | |||
158 | if self.backuppassphrase: | ||
159 | retval += " --backuppassphrase" | ||
160 | |||
161 | return retval | ||
162 | |||
163 | F14_LogVolData = F12_LogVolData | ||
164 | |||
165 | class F15_LogVolData(F14_LogVolData): | ||
166 | removedKeywords = F14_LogVolData.removedKeywords | ||
167 | removedAttrs = F14_LogVolData.removedAttrs | ||
168 | |||
169 | def __init__(self, *args, **kwargs): | ||
170 | F14_LogVolData.__init__(self, *args, **kwargs) | ||
171 | self.label = kwargs.get("label", "") | ||
172 | |||
173 | def _getArgsAsStr(self): | ||
174 | retval = F14_LogVolData._getArgsAsStr(self) | ||
175 | |||
176 | if self.label != "": | ||
177 | retval += " --label=\"%s\"" % self.label | ||
178 | |||
179 | return retval | ||
180 | |||
181 | class FC3_LogVol(KickstartCommand): | ||
182 | removedKeywords = KickstartCommand.removedKeywords | ||
183 | removedAttrs = KickstartCommand.removedAttrs | ||
184 | |||
185 | def __init__(self, writePriority=133, *args, **kwargs): | ||
186 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
187 | self.op = self._getParser() | ||
188 | |||
189 | self.lvList = kwargs.get("lvList", []) | ||
190 | |||
191 | def __str__(self): | ||
192 | retval = "" | ||
193 | |||
194 | for part in self.lvList: | ||
195 | retval += part.__str__() | ||
196 | |||
197 | return retval | ||
198 | |||
199 | def _getParser(self): | ||
200 | def lv_cb (option, opt_str, value, parser): | ||
201 | parser.values.format = False | ||
202 | parser.values.preexist = True | ||
203 | |||
204 | op = KSOptionParser() | ||
205 | op.add_option("--fstype", dest="fstype") | ||
206 | op.add_option("--grow", dest="grow", action="store_true", | ||
207 | default=False) | ||
208 | op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int", | ||
209 | nargs=1) | ||
210 | op.add_option("--name", dest="name", required=1) | ||
211 | op.add_option("--noformat", action="callback", callback=lv_cb, | ||
212 | dest="format", default=True, nargs=0) | ||
213 | op.add_option("--percent", dest="percent", action="store", type="int", | ||
214 | nargs=1) | ||
215 | op.add_option("--recommended", dest="recommended", action="store_true", | ||
216 | default=False) | ||
217 | op.add_option("--size", dest="size", action="store", type="int", | ||
218 | nargs=1) | ||
219 | op.add_option("--useexisting", dest="preexist", action="store_true", | ||
220 | default=False) | ||
221 | op.add_option("--vgname", dest="vgname", required=1) | ||
222 | return op | ||
223 | |||
224 | def parse(self, args): | ||
225 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
226 | |||
227 | if len(extra) == 0: | ||
228 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "logvol") | ||
229 | |||
230 | lvd = self.handler.LogVolData() | ||
231 | self._setToObj(self.op, opts, lvd) | ||
232 | lvd.lineno = self.lineno | ||
233 | lvd.mountpoint=extra[0] | ||
234 | |||
235 | # Check for duplicates in the data list. | ||
236 | if lvd in self.dataList(): | ||
237 | warnings.warn(_("A logical volume with the name %s has already been defined in volume group %s.") % (lvd.device, lvd.vgname)) | ||
238 | |||
239 | return lvd | ||
240 | |||
241 | def dataList(self): | ||
242 | return self.lvList | ||
243 | |||
244 | class FC4_LogVol(FC3_LogVol): | ||
245 | removedKeywords = FC3_LogVol.removedKeywords | ||
246 | removedAttrs = FC3_LogVol.removedAttrs | ||
247 | |||
248 | def _getParser(self): | ||
249 | op = FC3_LogVol._getParser(self) | ||
250 | op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store", | ||
251 | type="int", nargs=1) | ||
252 | op.add_option("--fsoptions", dest="fsopts") | ||
253 | return op | ||
254 | |||
255 | class RHEL5_LogVol(FC4_LogVol): | ||
256 | removedKeywords = FC4_LogVol.removedKeywords | ||
257 | removedAttrs = FC4_LogVol.removedAttrs | ||
258 | |||
259 | def _getParser(self): | ||
260 | op = FC4_LogVol._getParser(self) | ||
261 | op.add_option("--encrypted", action="store_true", default=False) | ||
262 | op.add_option("--passphrase") | ||
263 | return op | ||
264 | |||
265 | class F9_LogVol(FC4_LogVol): | ||
266 | removedKeywords = FC4_LogVol.removedKeywords | ||
267 | removedAttrs = FC4_LogVol.removedAttrs | ||
268 | |||
269 | def _getParser(self): | ||
270 | op = FC4_LogVol._getParser(self) | ||
271 | op.add_option("--bytes-per-inode", deprecated=1) | ||
272 | op.add_option("--fsprofile", dest="fsprofile", action="store", | ||
273 | type="string", nargs=1) | ||
274 | op.add_option("--encrypted", action="store_true", default=False) | ||
275 | op.add_option("--passphrase") | ||
276 | return op | ||
277 | |||
278 | class F12_LogVol(F9_LogVol): | ||
279 | removedKeywords = F9_LogVol.removedKeywords | ||
280 | removedAttrs = F9_LogVol.removedAttrs | ||
281 | |||
282 | def _getParser(self): | ||
283 | op = F9_LogVol._getParser(self) | ||
284 | op.add_option("--escrowcert") | ||
285 | op.add_option("--backuppassphrase", action="store_true", default=False) | ||
286 | return op | ||
287 | |||
288 | class F14_LogVol(F12_LogVol): | ||
289 | removedKeywords = F12_LogVol.removedKeywords | ||
290 | removedAttrs = F12_LogVol.removedAttrs | ||
291 | |||
292 | def _getParser(self): | ||
293 | op = F12_LogVol._getParser(self) | ||
294 | op.remove_option("--bytes-per-inode") | ||
295 | return op | ||
296 | |||
297 | class F15_LogVol(F14_LogVol): | ||
298 | removedKeywords = F14_LogVol.removedKeywords | ||
299 | removedAttrs = F14_LogVol.removedAttrs | ||
300 | |||
301 | def _getParser(self): | ||
302 | op = F14_LogVol._getParser(self) | ||
303 | op.add_option("--label") | ||
304 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/mediacheck.py b/scripts/lib/mic/3rdparty/pykickstart/commands/mediacheck.py new file mode 100644 index 0000000000..388823a839 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/mediacheck.py | |||
@@ -0,0 +1,53 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC4_MediaCheck(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | self.mediacheck = kwargs.get("mediacheck", False) | ||
35 | |||
36 | def __str__(self): | ||
37 | retval = KickstartCommand.__str__(self) | ||
38 | if self.mediacheck: | ||
39 | retval += "mediacheck\n" | ||
40 | |||
41 | return retval | ||
42 | |||
43 | def _getParser(self): | ||
44 | op = KSOptionParser() | ||
45 | return op | ||
46 | |||
47 | def parse(self, args): | ||
48 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
49 | if len(extra) > 0: | ||
50 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "mediacheck") | ||
51 | |||
52 | self.mediacheck = True | ||
53 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/method.py b/scripts/lib/mic/3rdparty/pykickstart/commands/method.py new file mode 100644 index 0000000000..e21064acda --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/method.py | |||
@@ -0,0 +1,186 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007, 2009 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_Method(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.method = kwargs.get("method", "") | ||
34 | |||
35 | # Set all these attributes so calls to this command's __call__ | ||
36 | # method can set them. However we don't want to provide them as | ||
37 | # arguments to __init__ because method is special. | ||
38 | self.biospart = None | ||
39 | self.partition = None | ||
40 | self.server = None | ||
41 | self.dir = None | ||
42 | self.url = None | ||
43 | |||
44 | def __str__(self): | ||
45 | retval = KickstartCommand.__str__(self) | ||
46 | |||
47 | if self.method == "cdrom": | ||
48 | retval += "# Use CDROM installation media\ncdrom\n" | ||
49 | elif self.method == "harddrive": | ||
50 | msg = "# Use hard drive installation media\nharddrive --dir=%s" % self.dir | ||
51 | |||
52 | if self.biospart is not None: | ||
53 | retval += msg + " --biospart=%s\n" % self.biospart | ||
54 | else: | ||
55 | retval += msg + " --partition=%s\n" % self.partition | ||
56 | elif self.method == "nfs": | ||
57 | retval += "# Use NFS installation media\nnfs --server=%s --dir=%s\n" % (self.server, self.dir) | ||
58 | elif self.method == "url": | ||
59 | retval += "# Use network installation\nurl --url=\"%s\"\n" % self.url | ||
60 | |||
61 | return retval | ||
62 | |||
63 | def _getParser(self): | ||
64 | op = KSOptionParser() | ||
65 | |||
66 | # method = "cdrom" falls through to the return | ||
67 | if self.currentCmd == "harddrive": | ||
68 | op.add_option("--biospart", dest="biospart") | ||
69 | op.add_option("--partition", dest="partition") | ||
70 | op.add_option("--dir", dest="dir", required=1) | ||
71 | elif self.currentCmd == "nfs": | ||
72 | op.add_option("--server", dest="server", required=1) | ||
73 | op.add_option("--dir", dest="dir", required=1) | ||
74 | elif self.currentCmd == "url": | ||
75 | op.add_option("--url", dest="url", required=1) | ||
76 | |||
77 | return op | ||
78 | |||
79 | def parse(self, args): | ||
80 | self.method = self.currentCmd | ||
81 | |||
82 | op = self._getParser() | ||
83 | (opts, extra) = op.parse_args(args=args, lineno=self.lineno) | ||
84 | self._setToSelf(op, opts) | ||
85 | |||
86 | if self.currentCmd == "harddrive": | ||
87 | if self.biospart is None and self.partition is None or \ | ||
88 | self.biospart is not None and self.partition is not None: | ||
89 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of biospart or partition options must be specified.")) | ||
90 | |||
91 | return self | ||
92 | |||
93 | class FC6_Method(FC3_Method): | ||
94 | removedKeywords = FC3_Method.removedKeywords | ||
95 | removedAttrs = FC3_Method.removedAttrs | ||
96 | |||
97 | def __init__(self, writePriority=0, *args, **kwargs): | ||
98 | FC3_Method.__init__(self, writePriority, *args, **kwargs) | ||
99 | |||
100 | # Same reason for this attribute as the comment in FC3_Method. | ||
101 | self.opts = None | ||
102 | |||
103 | def __str__(self): | ||
104 | retval = KickstartCommand.__str__(self) | ||
105 | |||
106 | if self.method == "cdrom": | ||
107 | retval += "# Use CDROM installation media\ncdrom\n" | ||
108 | elif self.method == "harddrive": | ||
109 | msg = "# Use hard drive installation media\nharddrive --dir=%s" % self.dir | ||
110 | |||
111 | if self.biospart is not None: | ||
112 | retval += msg + " --biospart=%s\n" % self.biospart | ||
113 | else: | ||
114 | retval += msg + " --partition=%s\n" % self.partition | ||
115 | elif self.method == "nfs": | ||
116 | retval += "# Use NFS installation media\nnfs --server=%s --dir=%s" % (self.server, self.dir) | ||
117 | if self.opts is not None: | ||
118 | retval += " --opts=\"%s\"" % self.opts | ||
119 | retval += "\n" | ||
120 | elif self.method == "url": | ||
121 | retval += "# Use network installation\nurl --url=\"%s\"\n" % self.url | ||
122 | |||
123 | return retval | ||
124 | |||
125 | def _getParser(self): | ||
126 | op = FC3_Method._getParser(self) | ||
127 | |||
128 | if self.currentCmd == "nfs": | ||
129 | op.add_option("--opts", dest="opts") | ||
130 | |||
131 | return op | ||
132 | |||
133 | class F13_Method(FC6_Method): | ||
134 | removedKeywords = FC6_Method.removedKeywords | ||
135 | removedAttrs = FC6_Method.removedAttrs | ||
136 | |||
137 | def __init__(self, *args, **kwargs): | ||
138 | FC6_Method.__init__(self, *args, **kwargs) | ||
139 | |||
140 | # And same as all the other __init__ methods. | ||
141 | self.proxy = "" | ||
142 | |||
143 | def __str__(self): | ||
144 | retval = FC6_Method.__str__(self) | ||
145 | |||
146 | if self.method == "url" and self.proxy: | ||
147 | retval = retval.strip() | ||
148 | retval += " --proxy=\"%s\"\n" % self.proxy | ||
149 | |||
150 | return retval | ||
151 | |||
152 | def _getParser(self): | ||
153 | op = FC6_Method._getParser(self) | ||
154 | |||
155 | if self.currentCmd == "url": | ||
156 | op.add_option("--proxy") | ||
157 | |||
158 | return op | ||
159 | |||
160 | class F14_Method(F13_Method): | ||
161 | removedKeywords = F13_Method.removedKeywords | ||
162 | removedAttrs = F13_Method.removedAttrs | ||
163 | |||
164 | def __init__(self, *args, **kwargs): | ||
165 | F13_Method.__init__(self, *args, **kwargs) | ||
166 | |||
167 | self.noverifyssl = False | ||
168 | |||
169 | def __str__(self): | ||
170 | retval = F13_Method.__str__(self) | ||
171 | |||
172 | if self.method == "url" and self.noverifyssl: | ||
173 | retval = retval.strip() | ||
174 | retval += " --noverifyssl\n" | ||
175 | |||
176 | return retval | ||
177 | |||
178 | def _getParser(self): | ||
179 | op = F13_Method._getParser(self) | ||
180 | |||
181 | if self.currentCmd == "url": | ||
182 | op.add_option("--noverifyssl", action="store_true", default=False) | ||
183 | |||
184 | return op | ||
185 | |||
186 | RHEL6_Method = F14_Method | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/monitor.py b/scripts/lib/mic/3rdparty/pykickstart/commands/monitor.py new file mode 100644 index 0000000000..8c8c2c4fc9 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/monitor.py | |||
@@ -0,0 +1,106 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007, 2008 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_Monitor(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | |||
35 | self.hsync = kwargs.get("hsync", "") | ||
36 | self.monitor = kwargs.get("monitor", "") | ||
37 | self.vsync = kwargs.get("vsync", "") | ||
38 | |||
39 | def __str__(self): | ||
40 | retval = KickstartCommand.__str__(self) | ||
41 | retval += "monitor" | ||
42 | |||
43 | if self.hsync != "": | ||
44 | retval += " --hsync=%s" % self.hsync | ||
45 | if self.monitor != "": | ||
46 | retval += " --monitor=\"%s\"" % self.monitor | ||
47 | if self.vsync != "": | ||
48 | retval += " --vsync=%s" % self.vsync | ||
49 | |||
50 | if retval != "monitor": | ||
51 | return retval + "\n" | ||
52 | else: | ||
53 | return "" | ||
54 | |||
55 | def _getParser(self): | ||
56 | op = KSOptionParser() | ||
57 | op.add_option("--hsync") | ||
58 | op.add_option("--monitor") | ||
59 | op.add_option("--vsync") | ||
60 | return op | ||
61 | |||
62 | def parse(self, args): | ||
63 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
64 | |||
65 | if extra: | ||
66 | mapping = {"cmd": "monitor", "options": extra} | ||
67 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(cmd)s command: %(options)s") % mapping) | ||
68 | |||
69 | self._setToSelf(self.op, opts) | ||
70 | return self | ||
71 | |||
72 | class FC6_Monitor(FC3_Monitor): | ||
73 | removedKeywords = FC3_Monitor.removedKeywords | ||
74 | removedAttrs = FC3_Monitor.removedAttrs | ||
75 | |||
76 | def __init__(self, writePriority=0, *args, **kwargs): | ||
77 | FC3_Monitor.__init__(self, writePriority, *args, **kwargs) | ||
78 | self.probe = kwargs.get("probe", True) | ||
79 | |||
80 | def __str__(self): | ||
81 | retval = KickstartCommand.__str__(self) | ||
82 | retval += "monitor" | ||
83 | |||
84 | if self.hsync != "": | ||
85 | retval += " --hsync=%s" % self.hsync | ||
86 | if self.monitor != "": | ||
87 | retval += " --monitor=\"%s\"" % self.monitor | ||
88 | if not self.probe: | ||
89 | retval += " --noprobe" | ||
90 | if self.vsync != "": | ||
91 | retval += " --vsync=%s" % self.vsync | ||
92 | |||
93 | if retval != "monitor": | ||
94 | return retval + "\n" | ||
95 | else: | ||
96 | return "" | ||
97 | |||
98 | def _getParser(self): | ||
99 | op = FC3_Monitor._getParser(self) | ||
100 | op.add_option("--noprobe", dest="probe", action="store_false", | ||
101 | default=True) | ||
102 | return op | ||
103 | |||
104 | class F10_Monitor(DeprecatedCommand): | ||
105 | def __init__(self): | ||
106 | DeprecatedCommand.__init__(self) | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/mouse.py b/scripts/lib/mic/3rdparty/pykickstart/commands/mouse.py new file mode 100644 index 0000000000..c643bcedc3 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/mouse.py | |||
@@ -0,0 +1,70 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class RHEL3_Mouse(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | |||
35 | self.device = kwargs.get("device", "") | ||
36 | self.emulthree = kwargs.get("emulthree", False) | ||
37 | self.mouse = kwargs.get("mouse", "") | ||
38 | |||
39 | def __str__(self): | ||
40 | retval = KickstartCommand.__str__(self) | ||
41 | |||
42 | opts = "" | ||
43 | if self.device: | ||
44 | opts += "--device=%s " % self.device | ||
45 | if self.emulthree: | ||
46 | opts += "--emulthree " | ||
47 | |||
48 | if self.mouse: | ||
49 | retval += "# System mouse\nmouse %s%s\n" % (opts, self.mouse) | ||
50 | return retval | ||
51 | |||
52 | def _getParser(self): | ||
53 | op = KSOptionParser() | ||
54 | op.add_option("--device", dest="device", default="") | ||
55 | op.add_option("--emulthree", dest="emulthree", default=False, action="store_true") | ||
56 | return op | ||
57 | |||
58 | def parse(self, args): | ||
59 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
60 | self._setToSelf(self.op, opts) | ||
61 | |||
62 | if len(extra) != 1: | ||
63 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s requires one argument") % "mouse") | ||
64 | |||
65 | self.mouse = extra[0] | ||
66 | return self | ||
67 | |||
68 | class FC3_Mouse(DeprecatedCommand): | ||
69 | def __init__(self): | ||
70 | DeprecatedCommand.__init__(self) | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/multipath.py b/scripts/lib/mic/3rdparty/pykickstart/commands/multipath.py new file mode 100644 index 0000000000..84ba755e68 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/multipath.py | |||
@@ -0,0 +1,111 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # Peter Jones <pjones@redhat.com> | ||
4 | # | ||
5 | # Copyright 2006, 2007 Red Hat, Inc. | ||
6 | # | ||
7 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
8 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
9 | # General Public License v.2. This program is distributed in the hope that it | ||
10 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
11 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
12 | # See the GNU General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along with | ||
15 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
16 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
17 | # trademarks that are incorporated in the source code or documentation are not | ||
18 | # subject to the GNU General Public License and may only be used or replicated | ||
19 | # with the express permission of Red Hat, Inc. | ||
20 | # | ||
21 | from pykickstart.base import * | ||
22 | from pykickstart.errors import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | import gettext | ||
26 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
27 | |||
28 | class FC6_MpPathData(BaseData): | ||
29 | removedKeywords = BaseData.removedKeywords | ||
30 | removedAttrs = BaseData.removedAttrs | ||
31 | |||
32 | def __init__(self, *args, **kwargs): | ||
33 | BaseData.__init__(self, *args, **kwargs) | ||
34 | self.mpdev = kwargs.get("mpdev", "") | ||
35 | self.device = kwargs.get("device", "") | ||
36 | self.rule = kwargs.get("rule", "") | ||
37 | |||
38 | def __str__(self): | ||
39 | return " --device=%s --rule=\"%s\"" % (self.device, self.rule) | ||
40 | |||
41 | class FC6_MultiPathData(BaseData): | ||
42 | removedKeywords = BaseData.removedKeywords | ||
43 | removedAttrs = BaseData.removedAttrs | ||
44 | |||
45 | def __init__(self, *args, **kwargs): | ||
46 | BaseData.__init__(self, *args, **kwargs) | ||
47 | self.name = kwargs.get("name", "") | ||
48 | self.paths = kwargs.get("paths", []) | ||
49 | |||
50 | def __str__(self): | ||
51 | retval = BaseData.__str__(self) | ||
52 | |||
53 | for path in self.paths: | ||
54 | retval += "multipath --mpdev=%s %s\n" % (self.name, path.__str__()) | ||
55 | |||
56 | return retval | ||
57 | |||
58 | class FC6_MultiPath(KickstartCommand): | ||
59 | removedKeywords = KickstartCommand.removedKeywords | ||
60 | removedAttrs = KickstartCommand.removedAttrs | ||
61 | |||
62 | def __init__(self, writePriority=50, *args, **kwargs): | ||
63 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
64 | self.op = self._getParser() | ||
65 | |||
66 | self.mpaths = kwargs.get("mpaths", []) | ||
67 | |||
68 | def __str__(self): | ||
69 | retval = "" | ||
70 | for mpath in self.mpaths: | ||
71 | retval += mpath.__str__() | ||
72 | |||
73 | return retval | ||
74 | |||
75 | def _getParser(self): | ||
76 | op = KSOptionParser() | ||
77 | op.add_option("--name", dest="name", action="store", type="string", | ||
78 | required=1) | ||
79 | op.add_option("--device", dest="device", action="store", type="string", | ||
80 | required=1) | ||
81 | op.add_option("--rule", dest="rule", action="store", type="string", | ||
82 | required=1) | ||
83 | return op | ||
84 | |||
85 | def parse(self, args): | ||
86 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
87 | dd = FC6_MpPathData() | ||
88 | self._setToObj(self.op, opts, dd) | ||
89 | dd.lineno = self.lineno | ||
90 | dd.mpdev = dd.mpdev.split('/')[-1] | ||
91 | |||
92 | parent = None | ||
93 | for x in range(0, len(self.mpaths)): | ||
94 | mpath = self.mpaths[x] | ||
95 | for path in mpath.paths: | ||
96 | if path.device == dd.device: | ||
97 | mapping = {"device": path.device, "multipathdev": path.mpdev} | ||
98 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Device '%(device)s' is already used in multipath '%(multipathdev)s'") % mapping) | ||
99 | if mpath.name == dd.mpdev: | ||
100 | parent = x | ||
101 | |||
102 | if parent is None: | ||
103 | mpath = FC6_MultiPathData() | ||
104 | return mpath | ||
105 | else: | ||
106 | mpath = self.mpaths[parent] | ||
107 | |||
108 | return dd | ||
109 | |||
110 | def dataList(self): | ||
111 | return self.mpaths | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/network.py b/scripts/lib/mic/3rdparty/pykickstart/commands/network.py new file mode 100644 index 0000000000..9b67f92831 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/network.py | |||
@@ -0,0 +1,363 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007, 2008 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.constants import * | ||
22 | from pykickstart.errors import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | import gettext | ||
26 | import warnings | ||
27 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
28 | |||
29 | class FC3_NetworkData(BaseData): | ||
30 | removedKeywords = BaseData.removedKeywords | ||
31 | removedAttrs = BaseData.removedAttrs | ||
32 | |||
33 | def __init__(self, *args, **kwargs): | ||
34 | BaseData.__init__(self, *args, **kwargs) | ||
35 | self.bootProto = kwargs.get("bootProto", BOOTPROTO_DHCP) | ||
36 | self.dhcpclass = kwargs.get("dhcpclass", "") | ||
37 | self.device = kwargs.get("device", "") | ||
38 | self.essid = kwargs.get("essid", "") | ||
39 | self.ethtool = kwargs.get("ethtool", "") | ||
40 | self.gateway = kwargs.get("gateway", "") | ||
41 | self.hostname = kwargs.get("hostname", "") | ||
42 | self.ip = kwargs.get("ip", "") | ||
43 | self.mtu = kwargs.get("mtu", "") | ||
44 | self.nameserver = kwargs.get("nameserver", "") | ||
45 | self.netmask = kwargs.get("netmask", "") | ||
46 | self.nodns = kwargs.get("nodns", False) | ||
47 | self.onboot = kwargs.get("onboot", True) | ||
48 | self.wepkey = kwargs.get("wepkey", "") | ||
49 | |||
50 | def __eq__(self, y): | ||
51 | return self.device and self.device == y.device | ||
52 | |||
53 | def _getArgsAsStr(self): | ||
54 | retval = "" | ||
55 | |||
56 | if self.bootProto != "": | ||
57 | retval += " --bootproto=%s" % self.bootProto | ||
58 | if self.dhcpclass != "": | ||
59 | retval += " --dhcpclass=%s" % self.dhcpclass | ||
60 | if self.device != "": | ||
61 | retval += " --device=%s" % self.device | ||
62 | if self.essid != "": | ||
63 | retval += " --essid=\"%s\"" % self.essid | ||
64 | if self.ethtool != "": | ||
65 | retval += " --ethtool=\"%s\"" % self.ethtool | ||
66 | if self.gateway != "": | ||
67 | retval += " --gateway=%s" % self.gateway | ||
68 | if self.hostname != "": | ||
69 | retval += " --hostname=%s" % self.hostname | ||
70 | if self.ip != "": | ||
71 | retval += " --ip=%s" % self.ip | ||
72 | if self.mtu != "": | ||
73 | retval += " --mtu=%s" % self.mtu | ||
74 | if self.nameserver != "": | ||
75 | retval += " --nameserver=%s" % self.nameserver | ||
76 | if self.netmask != "": | ||
77 | retval += " --netmask=%s" % self.netmask | ||
78 | if self.nodns: | ||
79 | retval += " --nodns" | ||
80 | if not self.onboot: | ||
81 | retval += " --onboot=off" | ||
82 | if self.wepkey != "": | ||
83 | retval += " --wepkey=%s" % self.wepkey | ||
84 | |||
85 | return retval | ||
86 | |||
87 | def __str__(self): | ||
88 | retval = BaseData.__str__(self) | ||
89 | retval += "network %s\n" % self._getArgsAsStr() | ||
90 | return retval | ||
91 | |||
92 | class FC4_NetworkData(FC3_NetworkData): | ||
93 | removedKeywords = FC3_NetworkData.removedKeywords | ||
94 | removedAttrs = FC3_NetworkData.removedAttrs | ||
95 | |||
96 | def __init__(self, *args, **kwargs): | ||
97 | FC3_NetworkData.__init__(self, *args, **kwargs) | ||
98 | self.notksdevice = kwargs.get("notksdevice", False) | ||
99 | |||
100 | def _getArgsAsStr(self): | ||
101 | retval = FC3_NetworkData._getArgsAsStr(self) | ||
102 | |||
103 | if self.notksdevice: | ||
104 | retval += " --notksdevice" | ||
105 | |||
106 | return retval | ||
107 | |||
108 | class FC6_NetworkData(FC4_NetworkData): | ||
109 | removedKeywords = FC4_NetworkData.removedKeywords | ||
110 | removedAttrs = FC4_NetworkData.removedAttrs | ||
111 | |||
112 | def __init__(self, *args, **kwargs): | ||
113 | FC4_NetworkData.__init__(self, *args, **kwargs) | ||
114 | self.noipv4 = kwargs.get("noipv4", False) | ||
115 | self.noipv6 = kwargs.get("noipv6", False) | ||
116 | |||
117 | def _getArgsAsStr(self): | ||
118 | retval = FC4_NetworkData._getArgsAsStr(self) | ||
119 | |||
120 | if self.noipv4: | ||
121 | retval += " --noipv4" | ||
122 | if self.noipv6: | ||
123 | retval += " --noipv6" | ||
124 | |||
125 | return retval | ||
126 | |||
127 | class F8_NetworkData(FC6_NetworkData): | ||
128 | removedKeywords = FC6_NetworkData.removedKeywords | ||
129 | removedAttrs = FC6_NetworkData.removedAttrs | ||
130 | |||
131 | def __init__(self, *args, **kwargs): | ||
132 | FC6_NetworkData.__init__(self, *args, **kwargs) | ||
133 | self.ipv6 = kwargs.get("ipv6", "") | ||
134 | |||
135 | def _getArgsAsStr(self): | ||
136 | retval = FC6_NetworkData._getArgsAsStr(self) | ||
137 | |||
138 | if self.ipv6 != "": | ||
139 | retval += " --ipv6" % self.ipv6 | ||
140 | |||
141 | return retval | ||
142 | |||
143 | class F16_NetworkData(F8_NetworkData): | ||
144 | removedKeywords = F8_NetworkData.removedKeywords | ||
145 | removedAttrs = F8_NetworkData.removedAttrs | ||
146 | |||
147 | def __init__(self, *args, **kwargs): | ||
148 | F8_NetworkData.__init__(self, *args, **kwargs) | ||
149 | self.activate = kwargs.get("activate", False) | ||
150 | self.nodefroute = kwargs.get("nodefroute", False) | ||
151 | self.wpakey = kwargs.get("wpakey", "") | ||
152 | |||
153 | def _getArgsAsStr(self): | ||
154 | retval = F8_NetworkData._getArgsAsStr(self) | ||
155 | |||
156 | if self.activate: | ||
157 | retval += " --activate" | ||
158 | if self.nodefroute: | ||
159 | retval += " --nodefroute" | ||
160 | if self.wpakey != "": | ||
161 | retval += "--wpakey=%s" % self.wpakey | ||
162 | |||
163 | return retval | ||
164 | |||
165 | class RHEL4_NetworkData(FC3_NetworkData): | ||
166 | removedKeywords = FC3_NetworkData.removedKeywords | ||
167 | removedAttrs = FC3_NetworkData.removedAttrs | ||
168 | |||
169 | def __init__(self, *args, **kwargs): | ||
170 | FC3_NetworkData.__init__(self, *args, **kwargs) | ||
171 | self.notksdevice = kwargs.get("notksdevice", False) | ||
172 | |||
173 | def _getArgsAsStr(self): | ||
174 | retval = FC3_NetworkData._getArgsAsStr(self) | ||
175 | |||
176 | if self.notksdevice: | ||
177 | retval += " --notksdevice" | ||
178 | |||
179 | return retval | ||
180 | |||
181 | class RHEL6_NetworkData(F8_NetworkData): | ||
182 | removedKeywords = F8_NetworkData.removedKeywords | ||
183 | removedAttrs = F8_NetworkData.removedAttrs | ||
184 | |||
185 | def __init__(self, *args, **kwargs): | ||
186 | F8_NetworkData.__init__(self, *args, **kwargs) | ||
187 | self.activate = kwargs.get("activate", False) | ||
188 | self.nodefroute = kwargs.get("nodefroute", False) | ||
189 | |||
190 | def _getArgsAsStr(self): | ||
191 | retval = F8_NetworkData._getArgsAsStr(self) | ||
192 | |||
193 | if self.activate: | ||
194 | retval += " --activate" | ||
195 | if self.nodefroute: | ||
196 | retval += " --nodefroute" | ||
197 | |||
198 | return retval | ||
199 | |||
200 | class FC3_Network(KickstartCommand): | ||
201 | removedKeywords = KickstartCommand.removedKeywords | ||
202 | removedAttrs = KickstartCommand.removedAttrs | ||
203 | |||
204 | def __init__(self, writePriority=0, *args, **kwargs): | ||
205 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
206 | self.bootprotoList = [BOOTPROTO_DHCP, BOOTPROTO_BOOTP, | ||
207 | BOOTPROTO_STATIC] | ||
208 | |||
209 | self.op = self._getParser() | ||
210 | |||
211 | self.network = kwargs.get("network", []) | ||
212 | |||
213 | def __str__(self): | ||
214 | retval = "" | ||
215 | |||
216 | for nic in self.network: | ||
217 | retval += nic.__str__() | ||
218 | |||
219 | if retval != "": | ||
220 | return "# Network information\n" + retval | ||
221 | else: | ||
222 | return "" | ||
223 | |||
224 | def _getParser(self): | ||
225 | op = KSOptionParser() | ||
226 | op.add_option("--bootproto", dest="bootProto", | ||
227 | default=BOOTPROTO_DHCP, | ||
228 | choices=self.bootprotoList) | ||
229 | op.add_option("--dhcpclass", dest="dhcpclass") | ||
230 | op.add_option("--device", dest="device") | ||
231 | op.add_option("--essid", dest="essid") | ||
232 | op.add_option("--ethtool", dest="ethtool") | ||
233 | op.add_option("--gateway", dest="gateway") | ||
234 | op.add_option("--hostname", dest="hostname") | ||
235 | op.add_option("--ip", dest="ip") | ||
236 | op.add_option("--mtu", dest="mtu") | ||
237 | op.add_option("--nameserver", dest="nameserver") | ||
238 | op.add_option("--netmask", dest="netmask") | ||
239 | op.add_option("--nodns", dest="nodns", action="store_true", | ||
240 | default=False) | ||
241 | op.add_option("--onboot", dest="onboot", action="store", | ||
242 | type="ksboolean") | ||
243 | op.add_option("--wepkey", dest="wepkey") | ||
244 | return op | ||
245 | |||
246 | def parse(self, args): | ||
247 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
248 | nd = self.handler.NetworkData() | ||
249 | self._setToObj(self.op, opts, nd) | ||
250 | nd.lineno = self.lineno | ||
251 | |||
252 | # Check for duplicates in the data list. | ||
253 | if nd in self.dataList(): | ||
254 | warnings.warn(_("A network device with the name %s has already been defined.") % nd.device) | ||
255 | |||
256 | return nd | ||
257 | |||
258 | def dataList(self): | ||
259 | return self.network | ||
260 | |||
261 | class FC4_Network(FC3_Network): | ||
262 | removedKeywords = FC3_Network.removedKeywords | ||
263 | removedAttrs = FC3_Network.removedAttrs | ||
264 | |||
265 | def _getParser(self): | ||
266 | op = FC3_Network._getParser(self) | ||
267 | op.add_option("--notksdevice", dest="notksdevice", action="store_true", | ||
268 | default=False) | ||
269 | return op | ||
270 | |||
271 | class FC6_Network(FC4_Network): | ||
272 | removedKeywords = FC4_Network.removedKeywords | ||
273 | removedAttrs = FC4_Network.removedAttrs | ||
274 | |||
275 | def _getParser(self): | ||
276 | op = FC4_Network._getParser(self) | ||
277 | op.add_option("--noipv4", dest="noipv4", action="store_true", | ||
278 | default=False) | ||
279 | op.add_option("--noipv6", dest="noipv6", action="store_true", | ||
280 | default=False) | ||
281 | return op | ||
282 | |||
283 | class F8_Network(FC6_Network): | ||
284 | removedKeywords = FC6_Network.removedKeywords | ||
285 | removedAttrs = FC6_Network.removedAttrs | ||
286 | |||
287 | def _getParser(self): | ||
288 | op = FC6_Network._getParser(self) | ||
289 | op.add_option("--ipv6", dest="ipv6") | ||
290 | return op | ||
291 | |||
292 | class F9_Network(F8_Network): | ||
293 | removedKeywords = F8_Network.removedKeywords | ||
294 | removedAttrs = F8_Network.removedAttrs | ||
295 | |||
296 | def __init__(self, writePriority=0, *args, **kwargs): | ||
297 | F8_Network.__init__(self, writePriority, *args, **kwargs) | ||
298 | self.bootprotoList.append(BOOTPROTO_QUERY) | ||
299 | |||
300 | def _getParser(self): | ||
301 | op = F8_Network._getParser(self) | ||
302 | op.add_option("--bootproto", dest="bootProto", | ||
303 | default=BOOTPROTO_DHCP, | ||
304 | choices=self.bootprotoList) | ||
305 | return op | ||
306 | |||
307 | class F16_Network(F9_Network): | ||
308 | removedKeywords = F9_Network.removedKeywords | ||
309 | removedAttrs = F9_Network.removedAttrs | ||
310 | |||
311 | def __init__(self, writePriority=0, *args, **kwargs): | ||
312 | F9_Network.__init__(self, writePriority, *args, **kwargs) | ||
313 | self.bootprotoList.append(BOOTPROTO_IBFT) | ||
314 | |||
315 | def _getParser(self): | ||
316 | op = F9_Network._getParser(self) | ||
317 | op.add_option("--activate", dest="activate", action="store_true", | ||
318 | default=False) | ||
319 | op.add_option("--nodefroute", dest="nodefroute", action="store_true", | ||
320 | default=False) | ||
321 | op.add_option("--wpakey", dest="wpakey", action="store", default="") | ||
322 | return op | ||
323 | |||
324 | class RHEL4_Network(FC3_Network): | ||
325 | removedKeywords = FC3_Network.removedKeywords | ||
326 | removedAttrs = FC3_Network.removedAttrs | ||
327 | |||
328 | def _getParser(self): | ||
329 | op = FC3_Network._getParser(self) | ||
330 | op.add_option("--notksdevice", dest="notksdevice", action="store_true", | ||
331 | default=False) | ||
332 | return op | ||
333 | |||
334 | class RHEL5_Network(FC6_Network): | ||
335 | removedKeywords = FC6_Network.removedKeywords | ||
336 | removedAttrs = FC6_Network.removedAttrs | ||
337 | |||
338 | def __init__(self, writePriority=0, *args, **kwargs): | ||
339 | FC6_Network.__init__(self, writePriority, *args, **kwargs) | ||
340 | self.bootprotoList.append(BOOTPROTO_QUERY) | ||
341 | |||
342 | def _getParser(self): | ||
343 | op = FC6_Network._getParser(self) | ||
344 | op.add_option("--bootproto", dest="bootProto", | ||
345 | default=BOOTPROTO_DHCP, | ||
346 | choices=self.bootprotoList) | ||
347 | return op | ||
348 | |||
349 | class RHEL6_Network(F9_Network): | ||
350 | removedKeywords = F9_Network.removedKeywords | ||
351 | removedAttrs = F9_Network.removedAttrs | ||
352 | |||
353 | def __init__(self, writePriority=0, *args, **kwargs): | ||
354 | F9_Network.__init__(self, writePriority, *args, **kwargs) | ||
355 | self.bootprotoList.append(BOOTPROTO_IBFT) | ||
356 | |||
357 | def _getParser(self): | ||
358 | op = F9_Network._getParser(self) | ||
359 | op.add_option("--activate", dest="activate", action="store_true", | ||
360 | default=False) | ||
361 | op.add_option("--nodefroute", dest="nodefroute", action="store_true", | ||
362 | default=False) | ||
363 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/partition.py b/scripts/lib/mic/3rdparty/pykickstart/commands/partition.py new file mode 100644 index 0000000000..e65e012d02 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/partition.py | |||
@@ -0,0 +1,353 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007, 2008 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | import warnings | ||
26 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
27 | |||
28 | class FC3_PartData(BaseData): | ||
29 | removedKeywords = BaseData.removedKeywords | ||
30 | removedAttrs = BaseData.removedAttrs | ||
31 | |||
32 | def __init__(self, *args, **kwargs): | ||
33 | BaseData.__init__(self, *args, **kwargs) | ||
34 | self.active = kwargs.get("active", False) | ||
35 | self.primOnly = kwargs.get("primOnly", False) | ||
36 | self.end = kwargs.get("end", 0) | ||
37 | self.fstype = kwargs.get("fstype", "") | ||
38 | self.grow = kwargs.get("grow", False) | ||
39 | self.maxSizeMB = kwargs.get("maxSizeMB", 0) | ||
40 | self.format = kwargs.get("format", True) | ||
41 | self.onbiosdisk = kwargs.get("onbiosdisk", "") | ||
42 | self.disk = kwargs.get("disk", "") | ||
43 | self.onPart = kwargs.get("onPart", "") | ||
44 | self.recommended = kwargs.get("recommended", False) | ||
45 | self.size = kwargs.get("size", None) | ||
46 | self.start = kwargs.get("start", 0) | ||
47 | self.mountpoint = kwargs.get("mountpoint", "") | ||
48 | |||
49 | def __eq__(self, y): | ||
50 | if self.mountpoint: | ||
51 | return self.mountpoint == y.mountpoint | ||
52 | else: | ||
53 | return False | ||
54 | |||
55 | def _getArgsAsStr(self): | ||
56 | retval = "" | ||
57 | |||
58 | if self.active: | ||
59 | retval += " --active" | ||
60 | if self.primOnly: | ||
61 | retval += " --asprimary" | ||
62 | if hasattr(self, "end") and self.end != 0: | ||
63 | retval += " --end=%s" % self.end | ||
64 | if self.fstype != "": | ||
65 | retval += " --fstype=\"%s\"" % self.fstype | ||
66 | if self.grow: | ||
67 | retval += " --grow" | ||
68 | if self.maxSizeMB > 0: | ||
69 | retval += " --maxsize=%d" % self.maxSizeMB | ||
70 | if not self.format: | ||
71 | retval += " --noformat" | ||
72 | if self.onbiosdisk != "": | ||
73 | retval += " --onbiosdisk=%s" % self.onbiosdisk | ||
74 | if self.disk != "": | ||
75 | retval += " --ondisk=%s" % self.disk | ||
76 | if self.onPart != "": | ||
77 | retval += " --onpart=%s" % self.onPart | ||
78 | if self.recommended: | ||
79 | retval += " --recommended" | ||
80 | if self.size and self.size != 0: | ||
81 | retval += " --size=%s" % self.size | ||
82 | if hasattr(self, "start") and self.start != 0: | ||
83 | retval += " --start=%s" % self.start | ||
84 | |||
85 | return retval | ||
86 | |||
87 | def __str__(self): | ||
88 | retval = BaseData.__str__(self) | ||
89 | if self.mountpoint: | ||
90 | mountpoint_str = "%s" % self.mountpoint | ||
91 | else: | ||
92 | mountpoint_str = "(No mount point)" | ||
93 | retval += "part %s%s\n" % (mountpoint_str, self._getArgsAsStr()) | ||
94 | return retval | ||
95 | |||
96 | class FC4_PartData(FC3_PartData): | ||
97 | removedKeywords = FC3_PartData.removedKeywords | ||
98 | removedAttrs = FC3_PartData.removedAttrs | ||
99 | |||
100 | def __init__(self, *args, **kwargs): | ||
101 | FC3_PartData.__init__(self, *args, **kwargs) | ||
102 | self.bytesPerInode = kwargs.get("bytesPerInode", 4096) | ||
103 | self.fsopts = kwargs.get("fsopts", "") | ||
104 | self.label = kwargs.get("label", "") | ||
105 | |||
106 | def _getArgsAsStr(self): | ||
107 | retval = FC3_PartData._getArgsAsStr(self) | ||
108 | |||
109 | if hasattr(self, "bytesPerInode") and self.bytesPerInode != 0: | ||
110 | retval += " --bytes-per-inode=%d" % self.bytesPerInode | ||
111 | if self.fsopts != "": | ||
112 | retval += " --fsoptions=\"%s\"" % self.fsopts | ||
113 | if self.label != "": | ||
114 | retval += " --label=%s" % self.label | ||
115 | |||
116 | return retval | ||
117 | |||
118 | class RHEL5_PartData(FC4_PartData): | ||
119 | removedKeywords = FC4_PartData.removedKeywords | ||
120 | removedAttrs = FC4_PartData.removedAttrs | ||
121 | |||
122 | def __init__(self, *args, **kwargs): | ||
123 | FC4_PartData.__init__(self, *args, **kwargs) | ||
124 | self.encrypted = kwargs.get("encrypted", False) | ||
125 | self.passphrase = kwargs.get("passphrase", "") | ||
126 | |||
127 | def _getArgsAsStr(self): | ||
128 | retval = FC4_PartData._getArgsAsStr(self) | ||
129 | |||
130 | if self.encrypted: | ||
131 | retval += " --encrypted" | ||
132 | |||
133 | if self.passphrase != "": | ||
134 | retval += " --passphrase=\"%s\"" % self.passphrase | ||
135 | |||
136 | return retval | ||
137 | |||
138 | class F9_PartData(FC4_PartData): | ||
139 | removedKeywords = FC4_PartData.removedKeywords + ["bytesPerInode"] | ||
140 | removedAttrs = FC4_PartData.removedAttrs + ["bytesPerInode"] | ||
141 | |||
142 | def __init__(self, *args, **kwargs): | ||
143 | FC4_PartData.__init__(self, *args, **kwargs) | ||
144 | self.deleteRemovedAttrs() | ||
145 | |||
146 | self.fsopts = kwargs.get("fsopts", "") | ||
147 | self.label = kwargs.get("label", "") | ||
148 | self.fsprofile = kwargs.get("fsprofile", "") | ||
149 | self.encrypted = kwargs.get("encrypted", False) | ||
150 | self.passphrase = kwargs.get("passphrase", "") | ||
151 | |||
152 | def _getArgsAsStr(self): | ||
153 | retval = FC4_PartData._getArgsAsStr(self) | ||
154 | |||
155 | if self.fsprofile != "": | ||
156 | retval += " --fsprofile=\"%s\"" % self.fsprofile | ||
157 | if self.encrypted: | ||
158 | retval += " --encrypted" | ||
159 | |||
160 | if self.passphrase != "": | ||
161 | retval += " --passphrase=\"%s\"" % self.passphrase | ||
162 | |||
163 | return retval | ||
164 | |||
165 | class F11_PartData(F9_PartData): | ||
166 | removedKeywords = F9_PartData.removedKeywords + ["start", "end"] | ||
167 | removedAttrs = F9_PartData.removedAttrs + ["start", "end"] | ||
168 | |||
169 | class F12_PartData(F11_PartData): | ||
170 | removedKeywords = F11_PartData.removedKeywords | ||
171 | removedAttrs = F11_PartData.removedAttrs | ||
172 | |||
173 | def __init__(self, *args, **kwargs): | ||
174 | F11_PartData.__init__(self, *args, **kwargs) | ||
175 | |||
176 | self.escrowcert = kwargs.get("escrowcert", "") | ||
177 | self.backuppassphrase = kwargs.get("backuppassphrase", False) | ||
178 | |||
179 | def _getArgsAsStr(self): | ||
180 | retval = F11_PartData._getArgsAsStr(self) | ||
181 | |||
182 | if self.encrypted and self.escrowcert != "": | ||
183 | retval += " --escrowcert=\"%s\"" % self.escrowcert | ||
184 | |||
185 | if self.backuppassphrase: | ||
186 | retval += " --backuppassphrase" | ||
187 | |||
188 | return retval | ||
189 | |||
190 | F14_PartData = F12_PartData | ||
191 | |||
192 | class FC3_Partition(KickstartCommand): | ||
193 | removedKeywords = KickstartCommand.removedKeywords | ||
194 | removedAttrs = KickstartCommand.removedAttrs | ||
195 | |||
196 | def __init__(self, writePriority=130, *args, **kwargs): | ||
197 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
198 | self.op = self._getParser() | ||
199 | |||
200 | self.partitions = kwargs.get("partitions", []) | ||
201 | |||
202 | def __str__(self): | ||
203 | retval = "" | ||
204 | |||
205 | for part in self.partitions: | ||
206 | retval += part.__str__() | ||
207 | |||
208 | if retval != "": | ||
209 | return "# Disk partitioning information\n" + retval | ||
210 | else: | ||
211 | return "" | ||
212 | |||
213 | def _getParser(self): | ||
214 | def part_cb (option, opt_str, value, parser): | ||
215 | if value.startswith("/dev/"): | ||
216 | parser.values.ensure_value(option.dest, value[5:]) | ||
217 | else: | ||
218 | parser.values.ensure_value(option.dest, value) | ||
219 | |||
220 | op = KSOptionParser() | ||
221 | op.add_option("--active", dest="active", action="store_true", | ||
222 | default=False) | ||
223 | op.add_option("--asprimary", dest="primOnly", action="store_true", | ||
224 | default=False) | ||
225 | op.add_option("--end", dest="end", action="store", type="int", | ||
226 | nargs=1) | ||
227 | op.add_option("--fstype", "--type", dest="fstype") | ||
228 | op.add_option("--grow", dest="grow", action="store_true", default=False) | ||
229 | op.add_option("--maxsize", dest="maxSizeMB", action="store", type="int", | ||
230 | nargs=1) | ||
231 | op.add_option("--noformat", dest="format", action="store_false", | ||
232 | default=True) | ||
233 | op.add_option("--onbiosdisk", dest="onbiosdisk") | ||
234 | op.add_option("--ondisk", "--ondrive", dest="disk") | ||
235 | op.add_option("--onpart", "--usepart", dest="onPart", action="callback", | ||
236 | callback=part_cb, nargs=1, type="string") | ||
237 | op.add_option("--recommended", dest="recommended", action="store_true", | ||
238 | default=False) | ||
239 | op.add_option("--size", dest="size", action="store", type="int", | ||
240 | nargs=1) | ||
241 | op.add_option("--start", dest="start", action="store", type="int", | ||
242 | nargs=1) | ||
243 | return op | ||
244 | |||
245 | def parse(self, args): | ||
246 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
247 | |||
248 | pd = self.handler.PartData() | ||
249 | self._setToObj(self.op, opts, pd) | ||
250 | pd.lineno = self.lineno | ||
251 | if extra: | ||
252 | pd.mountpoint = extra[0] | ||
253 | if pd in self.dataList(): | ||
254 | warnings.warn(_("A partition with the mountpoint %s has already been defined.") % pd.mountpoint) | ||
255 | else: | ||
256 | pd.mountpoint = None | ||
257 | |||
258 | return pd | ||
259 | |||
260 | def dataList(self): | ||
261 | return self.partitions | ||
262 | |||
263 | class FC4_Partition(FC3_Partition): | ||
264 | removedKeywords = FC3_Partition.removedKeywords | ||
265 | removedAttrs = FC3_Partition.removedAttrs | ||
266 | |||
267 | def __init__(self, writePriority=130, *args, **kwargs): | ||
268 | FC3_Partition.__init__(self, writePriority, *args, **kwargs) | ||
269 | |||
270 | def part_cb (option, opt_str, value, parser): | ||
271 | if value.startswith("/dev/"): | ||
272 | parser.values.ensure_value(option.dest, value[5:]) | ||
273 | else: | ||
274 | parser.values.ensure_value(option.dest, value) | ||
275 | |||
276 | def _getParser(self): | ||
277 | op = FC3_Partition._getParser(self) | ||
278 | op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store", | ||
279 | type="int", nargs=1) | ||
280 | op.add_option("--fsoptions", dest="fsopts") | ||
281 | op.add_option("--label", dest="label") | ||
282 | return op | ||
283 | |||
284 | class RHEL5_Partition(FC4_Partition): | ||
285 | removedKeywords = FC4_Partition.removedKeywords | ||
286 | removedAttrs = FC4_Partition.removedAttrs | ||
287 | |||
288 | def __init__(self, writePriority=130, *args, **kwargs): | ||
289 | FC4_Partition.__init__(self, writePriority, *args, **kwargs) | ||
290 | |||
291 | def part_cb (option, opt_str, value, parser): | ||
292 | if value.startswith("/dev/"): | ||
293 | parser.values.ensure_value(option.dest, value[5:]) | ||
294 | else: | ||
295 | parser.values.ensure_value(option.dest, value) | ||
296 | |||
297 | def _getParser(self): | ||
298 | op = FC4_Partition._getParser(self) | ||
299 | op.add_option("--encrypted", action="store_true", default=False) | ||
300 | op.add_option("--passphrase") | ||
301 | return op | ||
302 | |||
303 | class F9_Partition(FC4_Partition): | ||
304 | removedKeywords = FC4_Partition.removedKeywords | ||
305 | removedAttrs = FC4_Partition.removedAttrs | ||
306 | |||
307 | def __init__(self, writePriority=130, *args, **kwargs): | ||
308 | FC4_Partition.__init__(self, writePriority, *args, **kwargs) | ||
309 | |||
310 | def part_cb (option, opt_str, value, parser): | ||
311 | if value.startswith("/dev/"): | ||
312 | parser.values.ensure_value(option.dest, value[5:]) | ||
313 | else: | ||
314 | parser.values.ensure_value(option.dest, value) | ||
315 | |||
316 | def _getParser(self): | ||
317 | op = FC4_Partition._getParser(self) | ||
318 | op.add_option("--bytes-per-inode", deprecated=1) | ||
319 | op.add_option("--fsprofile") | ||
320 | op.add_option("--encrypted", action="store_true", default=False) | ||
321 | op.add_option("--passphrase") | ||
322 | return op | ||
323 | |||
324 | class F11_Partition(F9_Partition): | ||
325 | removedKeywords = F9_Partition.removedKeywords | ||
326 | removedAttrs = F9_Partition.removedAttrs | ||
327 | |||
328 | def _getParser(self): | ||
329 | op = F9_Partition._getParser(self) | ||
330 | op.add_option("--start", deprecated=1) | ||
331 | op.add_option("--end", deprecated=1) | ||
332 | return op | ||
333 | |||
334 | class F12_Partition(F11_Partition): | ||
335 | removedKeywords = F11_Partition.removedKeywords | ||
336 | removedAttrs = F11_Partition.removedAttrs | ||
337 | |||
338 | def _getParser(self): | ||
339 | op = F11_Partition._getParser(self) | ||
340 | op.add_option("--escrowcert") | ||
341 | op.add_option("--backuppassphrase", action="store_true", default=False) | ||
342 | return op | ||
343 | |||
344 | class F14_Partition(F12_Partition): | ||
345 | removedKeywords = F12_Partition.removedKeywords | ||
346 | removedAttrs = F12_Partition.removedAttrs | ||
347 | |||
348 | def _getParser(self): | ||
349 | op = F12_Partition._getParser(self) | ||
350 | op.remove_option("--bytes-per-inode") | ||
351 | op.remove_option("--start") | ||
352 | op.remove_option("--end") | ||
353 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/raid.py b/scripts/lib/mic/3rdparty/pykickstart/commands/raid.py new file mode 100644 index 0000000000..0f4c92a107 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/raid.py | |||
@@ -0,0 +1,365 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007, 2008, 2011 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | import warnings | ||
26 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
27 | |||
28 | class FC3_RaidData(BaseData): | ||
29 | removedKeywords = BaseData.removedKeywords | ||
30 | removedAttrs = BaseData.removedAttrs | ||
31 | |||
32 | def __init__(self, *args, **kwargs): | ||
33 | BaseData.__init__(self, *args, **kwargs) | ||
34 | self.device = kwargs.get("device", None) | ||
35 | self.fstype = kwargs.get("fstype", "") | ||
36 | self.level = kwargs.get("level", "") | ||
37 | self.format = kwargs.get("format", True) | ||
38 | self.spares = kwargs.get("spares", 0) | ||
39 | self.preexist = kwargs.get("preexist", False) | ||
40 | self.mountpoint = kwargs.get("mountpoint", "") | ||
41 | self.members = kwargs.get("members", []) | ||
42 | |||
43 | def __eq__(self, y): | ||
44 | return self.device == y.device | ||
45 | |||
46 | def _getArgsAsStr(self): | ||
47 | retval = "" | ||
48 | |||
49 | if self.device != "": | ||
50 | retval += " --device=%s" % self.device | ||
51 | if self.fstype != "": | ||
52 | retval += " --fstype=\"%s\"" % self.fstype | ||
53 | if self.level != "": | ||
54 | retval += " --level=%s" % self.level | ||
55 | if not self.format: | ||
56 | retval += " --noformat" | ||
57 | if self.spares != 0: | ||
58 | retval += " --spares=%d" % self.spares | ||
59 | if self.preexist: | ||
60 | retval += " --useexisting" | ||
61 | |||
62 | return retval | ||
63 | |||
64 | def __str__(self): | ||
65 | retval = BaseData.__str__(self) | ||
66 | retval += "raid %s%s %s\n" % (self.mountpoint, self._getArgsAsStr(), | ||
67 | " ".join(self.members)) | ||
68 | return retval | ||
69 | |||
70 | class FC4_RaidData(FC3_RaidData): | ||
71 | removedKeywords = FC3_RaidData.removedKeywords | ||
72 | removedAttrs = FC3_RaidData.removedAttrs | ||
73 | |||
74 | def __init__(self, *args, **kwargs): | ||
75 | FC3_RaidData.__init__(self, *args, **kwargs) | ||
76 | self.fsopts = kwargs.get("fsopts", "") | ||
77 | |||
78 | def _getArgsAsStr(self): | ||
79 | retval = FC3_RaidData._getArgsAsStr(self) | ||
80 | |||
81 | if self.fsopts != "": | ||
82 | retval += " --fsoptions=\"%s\"" % self.fsopts | ||
83 | |||
84 | return retval | ||
85 | |||
86 | class FC5_RaidData(FC4_RaidData): | ||
87 | removedKeywords = FC4_RaidData.removedKeywords | ||
88 | removedAttrs = FC4_RaidData.removedAttrs | ||
89 | |||
90 | def __init__(self, *args, **kwargs): | ||
91 | FC4_RaidData.__init__(self, *args, **kwargs) | ||
92 | self.bytesPerInode = kwargs.get("bytesPerInode", 4096) | ||
93 | |||
94 | def _getArgsAsStr(self): | ||
95 | retval = FC4_RaidData._getArgsAsStr(self) | ||
96 | |||
97 | if hasattr(self, "bytesPerInode") and self.bytesPerInode != 0: | ||
98 | retval += " --bytes-per-inode=%d" % self.bytesPerInode | ||
99 | |||
100 | return retval | ||
101 | |||
102 | class RHEL5_RaidData(FC5_RaidData): | ||
103 | removedKeywords = FC5_RaidData.removedKeywords | ||
104 | removedAttrs = FC5_RaidData.removedAttrs | ||
105 | |||
106 | def __init__(self, *args, **kwargs): | ||
107 | FC5_RaidData.__init__(self, *args, **kwargs) | ||
108 | self.encrypted = kwargs.get("encrypted", False) | ||
109 | self.passphrase = kwargs.get("passphrase", "") | ||
110 | |||
111 | def _getArgsAsStr(self): | ||
112 | retval = FC5_RaidData._getArgsAsStr(self) | ||
113 | |||
114 | if self.encrypted: | ||
115 | retval += " --encrypted" | ||
116 | |||
117 | if self.passphrase != "": | ||
118 | retval += " --passphrase=\"%s\"" % self.passphrase | ||
119 | |||
120 | return retval | ||
121 | |||
122 | F7_RaidData = FC5_RaidData | ||
123 | |||
124 | class F9_RaidData(FC5_RaidData): | ||
125 | removedKeywords = FC5_RaidData.removedKeywords + ["bytesPerInode"] | ||
126 | removedAttrs = FC5_RaidData.removedAttrs + ["bytesPerInode"] | ||
127 | |||
128 | def __init__(self, *args, **kwargs): | ||
129 | FC5_RaidData.__init__(self, *args, **kwargs) | ||
130 | self.deleteRemovedAttrs() | ||
131 | |||
132 | self.fsprofile = kwargs.get("fsprofile", "") | ||
133 | self.encrypted = kwargs.get("encrypted", False) | ||
134 | self.passphrase = kwargs.get("passphrase", "") | ||
135 | |||
136 | def _getArgsAsStr(self): | ||
137 | retval = FC5_RaidData._getArgsAsStr(self) | ||
138 | |||
139 | if self.fsprofile != "": | ||
140 | retval += " --fsprofile=\"%s\"" % self.fsprofile | ||
141 | if self.encrypted: | ||
142 | retval += " --encrypted" | ||
143 | |||
144 | if self.passphrase != "": | ||
145 | retval += " --passphrase=\"%s\"" % self.passphrase | ||
146 | |||
147 | return retval | ||
148 | |||
149 | class F12_RaidData(F9_RaidData): | ||
150 | removedKeywords = F9_RaidData.removedKeywords | ||
151 | removedAttrs = F9_RaidData.removedAttrs | ||
152 | |||
153 | def __init__(self, *args, **kwargs): | ||
154 | F9_RaidData.__init__(self, *args, **kwargs) | ||
155 | self.deleteRemovedAttrs() | ||
156 | |||
157 | self.escrowcert = kwargs.get("escrowcert", "") | ||
158 | self.backuppassphrase = kwargs.get("backuppassphrase", False) | ||
159 | |||
160 | def _getArgsAsStr(self): | ||
161 | retval = F9_RaidData._getArgsAsStr(self) | ||
162 | |||
163 | if self.encrypted and self.escrowcert != "": | ||
164 | retval += " --escrowcert=\"%s\"" % self.escrowcert | ||
165 | |||
166 | if self.backuppassphrase: | ||
167 | retval += " --backuppassphrase" | ||
168 | return retval | ||
169 | |||
170 | F13_RaidData = F12_RaidData | ||
171 | |||
172 | F14_RaidData = F13_RaidData | ||
173 | |||
174 | class F15_RaidData(F14_RaidData): | ||
175 | removedKeywords = F14_RaidData.removedKeywords | ||
176 | removedAttrs = F14_RaidData.removedAttrs | ||
177 | |||
178 | def __init__(self, *args, **kwargs): | ||
179 | F14_RaidData.__init__(self, *args, **kwargs) | ||
180 | self.deleteRemovedAttrs() | ||
181 | |||
182 | self.label = kwargs.get("label", "") | ||
183 | |||
184 | def _getArgsAsStr(self): | ||
185 | retval = F14_RaidData._getArgsAsStr(self) | ||
186 | |||
187 | if self.label != "": | ||
188 | retval += " --label=%s" % self.label | ||
189 | |||
190 | return retval | ||
191 | |||
192 | class FC3_Raid(KickstartCommand): | ||
193 | removedKeywords = KickstartCommand.removedKeywords | ||
194 | removedAttrs = KickstartCommand.removedAttrs | ||
195 | |||
196 | def __init__(self, writePriority=131, *args, **kwargs): | ||
197 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
198 | self.op = self._getParser() | ||
199 | |||
200 | # A dict of all the RAID levels we support. This means that if we | ||
201 | # support more levels in the future, subclasses don't have to | ||
202 | # duplicate too much. | ||
203 | self.levelMap = { "RAID0": "RAID0", "0": "RAID0", | ||
204 | "RAID1": "RAID1", "1": "RAID1", | ||
205 | "RAID5": "RAID5", "5": "RAID5", | ||
206 | "RAID6": "RAID6", "6": "RAID6" } | ||
207 | |||
208 | self.raidList = kwargs.get("raidList", []) | ||
209 | |||
210 | def __str__(self): | ||
211 | retval = "" | ||
212 | |||
213 | for raid in self.raidList: | ||
214 | retval += raid.__str__() | ||
215 | |||
216 | return retval | ||
217 | |||
218 | def _getParser(self): | ||
219 | def raid_cb (option, opt_str, value, parser): | ||
220 | parser.values.format = False | ||
221 | parser.values.preexist = True | ||
222 | |||
223 | def device_cb (option, opt_str, value, parser): | ||
224 | if value[0:2] == "md": | ||
225 | parser.values.ensure_value(option.dest, value[2:]) | ||
226 | else: | ||
227 | parser.values.ensure_value(option.dest, value) | ||
228 | |||
229 | def level_cb (option, opt_str, value, parser): | ||
230 | if self.levelMap.has_key(value): | ||
231 | parser.values.ensure_value(option.dest, self.levelMap[value]) | ||
232 | |||
233 | op = KSOptionParser() | ||
234 | op.add_option("--device", action="callback", callback=device_cb, | ||
235 | dest="device", type="string", nargs=1, required=1) | ||
236 | op.add_option("--fstype", dest="fstype") | ||
237 | op.add_option("--level", dest="level", action="callback", | ||
238 | callback=level_cb, type="string", nargs=1) | ||
239 | op.add_option("--noformat", action="callback", callback=raid_cb, | ||
240 | dest="format", default=True, nargs=0) | ||
241 | op.add_option("--spares", dest="spares", action="store", type="int", | ||
242 | nargs=1, default=0) | ||
243 | op.add_option("--useexisting", dest="preexist", action="store_true", | ||
244 | default=False) | ||
245 | return op | ||
246 | |||
247 | def parse(self, args): | ||
248 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
249 | |||
250 | if len(extra) == 0: | ||
251 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Mount point required for %s") % "raid") | ||
252 | if len(extra) == 1: | ||
253 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Partitions required for %s") % "raid") | ||
254 | |||
255 | rd = self.handler.RaidData() | ||
256 | self._setToObj(self.op, opts, rd) | ||
257 | rd.lineno = self.lineno | ||
258 | |||
259 | # --device can't just take an int in the callback above, because it | ||
260 | # could be specificed as "mdX", which causes optparse to error when | ||
261 | # it runs int(). | ||
262 | rd.device = int(rd.device) | ||
263 | rd.mountpoint = extra[0] | ||
264 | rd.members = extra[1:] | ||
265 | |||
266 | # Check for duplicates in the data list. | ||
267 | if rd in self.dataList(): | ||
268 | warnings.warn(_("A RAID device with the name %s has already been defined.") % rd.device) | ||
269 | |||
270 | return rd | ||
271 | |||
272 | def dataList(self): | ||
273 | return self.raidList | ||
274 | |||
275 | class FC4_Raid(FC3_Raid): | ||
276 | removedKeywords = FC3_Raid.removedKeywords | ||
277 | removedAttrs = FC3_Raid.removedAttrs | ||
278 | |||
279 | def _getParser(self): | ||
280 | op = FC3_Raid._getParser(self) | ||
281 | op.add_option("--fsoptions", dest="fsopts") | ||
282 | return op | ||
283 | |||
284 | class FC5_Raid(FC4_Raid): | ||
285 | removedKeywords = FC4_Raid.removedKeywords | ||
286 | removedAttrs = FC4_Raid.removedAttrs | ||
287 | |||
288 | def _getParser(self): | ||
289 | op = FC4_Raid._getParser(self) | ||
290 | op.add_option("--bytes-per-inode", dest="bytesPerInode", action="store", | ||
291 | type="int", nargs=1) | ||
292 | return op | ||
293 | |||
294 | class RHEL5_Raid(FC5_Raid): | ||
295 | removedKeywords = FC5_Raid.removedKeywords | ||
296 | removedAttrs = FC5_Raid.removedAttrs | ||
297 | |||
298 | def __init__(self, writePriority=131, *args, **kwargs): | ||
299 | FC5_Raid.__init__(self, writePriority, *args, **kwargs) | ||
300 | |||
301 | self.levelMap.update({"RAID10": "RAID10", "10": "RAID10"}) | ||
302 | |||
303 | def _getParser(self): | ||
304 | op = FC5_Raid._getParser(self) | ||
305 | op.add_option("--encrypted", action="store_true", default=False) | ||
306 | op.add_option("--passphrase") | ||
307 | return op | ||
308 | |||
309 | class F7_Raid(FC5_Raid): | ||
310 | removedKeywords = FC5_Raid.removedKeywords | ||
311 | removedAttrs = FC5_Raid.removedAttrs | ||
312 | |||
313 | def __init__(self, writePriority=131, *args, **kwargs): | ||
314 | FC5_Raid.__init__(self, writePriority, *args, **kwargs) | ||
315 | |||
316 | self.levelMap.update({"RAID10": "RAID10", "10": "RAID10"}) | ||
317 | |||
318 | class F9_Raid(F7_Raid): | ||
319 | removedKeywords = F7_Raid.removedKeywords | ||
320 | removedAttrs = F7_Raid.removedAttrs | ||
321 | |||
322 | def _getParser(self): | ||
323 | op = F7_Raid._getParser(self) | ||
324 | op.add_option("--bytes-per-inode", deprecated=1) | ||
325 | op.add_option("--fsprofile") | ||
326 | op.add_option("--encrypted", action="store_true", default=False) | ||
327 | op.add_option("--passphrase") | ||
328 | return op | ||
329 | |||
330 | class F12_Raid(F9_Raid): | ||
331 | removedKeywords = F9_Raid.removedKeywords | ||
332 | removedAttrs = F9_Raid.removedAttrs | ||
333 | |||
334 | def _getParser(self): | ||
335 | op = F9_Raid._getParser(self) | ||
336 | op.add_option("--escrowcert") | ||
337 | op.add_option("--backuppassphrase", action="store_true", default=False) | ||
338 | return op | ||
339 | |||
340 | class F13_Raid(F12_Raid): | ||
341 | removedKeywords = F12_Raid.removedKeywords | ||
342 | removedAttrs = F12_Raid.removedAttrs | ||
343 | |||
344 | def __init__(self, writePriority=131, *args, **kwargs): | ||
345 | F12_Raid.__init__(self, writePriority, *args, **kwargs) | ||
346 | |||
347 | self.levelMap.update({"RAID4": "RAID4", "4": "RAID4"}) | ||
348 | |||
349 | class F14_Raid(F13_Raid): | ||
350 | removedKeywords = F13_Raid.removedKeywords | ||
351 | removedAttrs = F13_Raid.removedAttrs | ||
352 | |||
353 | def _getParser(self): | ||
354 | op = F13_Raid._getParser(self) | ||
355 | op.remove_option("--bytes-per-inode") | ||
356 | return op | ||
357 | |||
358 | class F15_Raid(F14_Raid): | ||
359 | removedKeywords = F14_Raid.removedKeywords | ||
360 | removedAttrs = F14_Raid.removedAttrs | ||
361 | |||
362 | def _getParser(self): | ||
363 | op = F14_Raid._getParser(self) | ||
364 | op.add_option("--label") | ||
365 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/reboot.py b/scripts/lib/mic/3rdparty/pykickstart/commands/reboot.py new file mode 100644 index 0000000000..391af14c22 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/reboot.py | |||
@@ -0,0 +1,79 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.constants import * | ||
22 | from pykickstart.errors import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | class FC3_Reboot(KickstartCommand): | ||
26 | removedKeywords = KickstartCommand.removedKeywords | ||
27 | removedAttrs = KickstartCommand.removedAttrs | ||
28 | |||
29 | def __init__(self, writePriority=0, *args, **kwargs): | ||
30 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
31 | self.action = kwargs.get("action", None) | ||
32 | |||
33 | def __str__(self): | ||
34 | retval = KickstartCommand.__str__(self) | ||
35 | |||
36 | if self.action == KS_REBOOT: | ||
37 | retval += "# Reboot after installation\nreboot\n" | ||
38 | elif self.action == KS_SHUTDOWN: | ||
39 | retval += "# Shutdown after installation\nshutdown\n" | ||
40 | |||
41 | return retval | ||
42 | |||
43 | def parse(self, args): | ||
44 | if self.currentCmd == "reboot": | ||
45 | self.action = KS_REBOOT | ||
46 | else: | ||
47 | self.action = KS_SHUTDOWN | ||
48 | |||
49 | return self | ||
50 | |||
51 | class FC6_Reboot(FC3_Reboot): | ||
52 | removedKeywords = FC3_Reboot.removedKeywords | ||
53 | removedAttrs = FC3_Reboot.removedAttrs | ||
54 | |||
55 | def __init__(self, writePriority=0, *args, **kwargs): | ||
56 | FC3_Reboot.__init__(self, writePriority, *args, **kwargs) | ||
57 | self.op = self._getParser() | ||
58 | |||
59 | self.eject = kwargs.get("eject", False) | ||
60 | |||
61 | def __str__(self): | ||
62 | retval = FC3_Reboot.__str__(self).rstrip() | ||
63 | |||
64 | if self.eject: | ||
65 | retval += " --eject" | ||
66 | |||
67 | return retval + "\n" | ||
68 | |||
69 | def _getParser(self): | ||
70 | op = KSOptionParser() | ||
71 | op.add_option("--eject", dest="eject", action="store_true", | ||
72 | default=False) | ||
73 | return op | ||
74 | |||
75 | def parse(self, args): | ||
76 | FC3_Reboot.parse(self, args) | ||
77 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
78 | self._setToSelf(self.op, opts) | ||
79 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/repo.py b/scripts/lib/mic/3rdparty/pykickstart/commands/repo.py new file mode 100644 index 0000000000..543ef947c1 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/repo.py | |||
@@ -0,0 +1,249 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007, 2008, 2009 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.constants import * | ||
22 | from pykickstart.errors import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | import gettext | ||
26 | import warnings | ||
27 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
28 | |||
29 | class FC6_RepoData(BaseData): | ||
30 | removedKeywords = BaseData.removedKeywords | ||
31 | removedAttrs = BaseData.removedAttrs | ||
32 | |||
33 | def __init__(self, *args, **kwargs): | ||
34 | BaseData.__init__(self, *args, **kwargs) | ||
35 | self.baseurl = kwargs.get("baseurl", "") | ||
36 | self.mirrorlist = kwargs.get("mirrorlist", None) | ||
37 | self.name = kwargs.get("name", "") | ||
38 | |||
39 | def __eq__(self, y): | ||
40 | return self.name == y.name | ||
41 | |||
42 | def _getArgsAsStr(self): | ||
43 | retval = "" | ||
44 | |||
45 | if self.baseurl: | ||
46 | retval += "--baseurl=%s" % self.baseurl | ||
47 | elif self.mirrorlist: | ||
48 | retval += "--mirrorlist=%s" % self.mirrorlist | ||
49 | |||
50 | return retval | ||
51 | |||
52 | def __str__(self): | ||
53 | retval = BaseData.__str__(self) | ||
54 | retval += "repo --name=\"%s\" %s\n" % (self.name, self._getArgsAsStr()) | ||
55 | return retval | ||
56 | |||
57 | class F8_RepoData(FC6_RepoData): | ||
58 | removedKeywords = FC6_RepoData.removedKeywords | ||
59 | removedAttrs = FC6_RepoData.removedAttrs | ||
60 | |||
61 | def __init__(self, *args, **kwargs): | ||
62 | FC6_RepoData.__init__(self, *args, **kwargs) | ||
63 | self.cost = kwargs.get("cost", None) | ||
64 | self.includepkgs = kwargs.get("includepkgs", []) | ||
65 | self.excludepkgs = kwargs.get("excludepkgs", []) | ||
66 | |||
67 | def _getArgsAsStr(self): | ||
68 | retval = FC6_RepoData._getArgsAsStr(self) | ||
69 | |||
70 | if self.cost: | ||
71 | retval += " --cost=%s" % self.cost | ||
72 | if self.includepkgs: | ||
73 | retval += " --includepkgs=\"%s\"" % ",".join(self.includepkgs) | ||
74 | if self.excludepkgs: | ||
75 | retval += " --excludepkgs=\"%s\"" % ",".join(self.excludepkgs) | ||
76 | |||
77 | return retval | ||
78 | |||
79 | class F11_RepoData(F8_RepoData): | ||
80 | removedKeywords = F8_RepoData.removedKeywords | ||
81 | removedAttrs = F8_RepoData.removedAttrs | ||
82 | |||
83 | def __init__(self, *args, **kwargs): | ||
84 | F8_RepoData.__init__(self, *args, **kwargs) | ||
85 | self.ignoregroups = kwargs.get("ignoregroups", None) | ||
86 | |||
87 | def _getArgsAsStr(self): | ||
88 | retval = F8_RepoData._getArgsAsStr(self) | ||
89 | |||
90 | if self.ignoregroups: | ||
91 | retval += " --ignoregroups=true" | ||
92 | return retval | ||
93 | |||
94 | class F13_RepoData(F11_RepoData): | ||
95 | removedKeywords = F11_RepoData.removedKeywords | ||
96 | removedAttrs = F11_RepoData.removedAttrs | ||
97 | |||
98 | def __init__(self, *args, **kwargs): | ||
99 | F11_RepoData.__init__(self, *args, **kwargs) | ||
100 | self.proxy = kwargs.get("proxy", "") | ||
101 | |||
102 | def _getArgsAsStr(self): | ||
103 | retval = F11_RepoData._getArgsAsStr(self) | ||
104 | |||
105 | if self.proxy: | ||
106 | retval += " --proxy=\"%s\"" % self.proxy | ||
107 | |||
108 | return retval | ||
109 | |||
110 | class F14_RepoData(F13_RepoData): | ||
111 | removedKeywords = F13_RepoData.removedKeywords | ||
112 | removedAttrs = F13_RepoData.removedAttrs | ||
113 | |||
114 | def __init__(self, *args, **kwargs): | ||
115 | F13_RepoData.__init__(self, *args, **kwargs) | ||
116 | self.noverifyssl = kwargs.get("noverifyssl", False) | ||
117 | |||
118 | def _getArgsAsStr(self): | ||
119 | retval = F13_RepoData._getArgsAsStr(self) | ||
120 | |||
121 | if self.noverifyssl: | ||
122 | retval += " --noverifyssl" | ||
123 | |||
124 | return retval | ||
125 | |||
126 | RHEL6_RepoData = F14_RepoData | ||
127 | |||
128 | F15_RepoData = F14_RepoData | ||
129 | |||
130 | class FC6_Repo(KickstartCommand): | ||
131 | removedKeywords = KickstartCommand.removedKeywords | ||
132 | removedAttrs = KickstartCommand.removedAttrs | ||
133 | |||
134 | urlRequired = True | ||
135 | |||
136 | def __init__(self, writePriority=0, *args, **kwargs): | ||
137 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
138 | self.op = self._getParser() | ||
139 | |||
140 | self.repoList = kwargs.get("repoList", []) | ||
141 | |||
142 | def __str__(self): | ||
143 | retval = "" | ||
144 | for repo in self.repoList: | ||
145 | retval += repo.__str__() | ||
146 | |||
147 | return retval | ||
148 | |||
149 | def _getParser(self): | ||
150 | op = KSOptionParser() | ||
151 | op.add_option("--name", dest="name", required=1) | ||
152 | op.add_option("--baseurl") | ||
153 | op.add_option("--mirrorlist") | ||
154 | return op | ||
155 | |||
156 | def parse(self, args): | ||
157 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
158 | |||
159 | if len(extra) != 0: | ||
160 | mapping = {"command": "repo", "options": extra} | ||
161 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping) | ||
162 | |||
163 | # This is lame, but I can't think of a better way to make sure only | ||
164 | # one of these two is specified. | ||
165 | if opts.baseurl and opts.mirrorlist: | ||
166 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --baseurl and --mirrorlist may be specified for repo command.")) | ||
167 | |||
168 | if self.urlRequired and not opts.baseurl and not opts.mirrorlist: | ||
169 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("One of --baseurl or --mirrorlist must be specified for repo command.")) | ||
170 | |||
171 | rd = self.handler.RepoData() | ||
172 | self._setToObj(self.op, opts, rd) | ||
173 | rd.lineno = self.lineno | ||
174 | |||
175 | # Check for duplicates in the data list. | ||
176 | if rd in self.dataList(): | ||
177 | warnings.warn(_("A repo with the name %s has already been defined.") % rd.name) | ||
178 | |||
179 | return rd | ||
180 | |||
181 | def dataList(self): | ||
182 | return self.repoList | ||
183 | |||
184 | class F8_Repo(FC6_Repo): | ||
185 | removedKeywords = FC6_Repo.removedKeywords | ||
186 | removedAttrs = FC6_Repo.removedAttrs | ||
187 | |||
188 | def __str__(self): | ||
189 | retval = "" | ||
190 | for repo in self.repoList: | ||
191 | retval += repo.__str__() | ||
192 | |||
193 | return retval | ||
194 | |||
195 | def _getParser(self): | ||
196 | def list_cb (option, opt_str, value, parser): | ||
197 | for d in value.split(','): | ||
198 | parser.values.ensure_value(option.dest, []).append(d) | ||
199 | |||
200 | op = FC6_Repo._getParser(self) | ||
201 | op.add_option("--cost", action="store", type="int") | ||
202 | op.add_option("--excludepkgs", action="callback", callback=list_cb, | ||
203 | nargs=1, type="string") | ||
204 | op.add_option("--includepkgs", action="callback", callback=list_cb, | ||
205 | nargs=1, type="string") | ||
206 | return op | ||
207 | |||
208 | def methodToRepo(self): | ||
209 | if not self.handler.method.url: | ||
210 | raise KickstartError, formatErrorMsg(self.handler.method.lineno, msg=_("Method must be a url to be added to the repo list.")) | ||
211 | reponame = "ks-method-url" | ||
212 | repourl = self.handler.method.url | ||
213 | rd = self.handler.RepoData(name=reponame, baseurl=repourl) | ||
214 | return rd | ||
215 | |||
216 | class F11_Repo(F8_Repo): | ||
217 | removedKeywords = F8_Repo.removedKeywords | ||
218 | removedAttrs = F8_Repo.removedAttrs | ||
219 | |||
220 | def _getParser(self): | ||
221 | op = F8_Repo._getParser(self) | ||
222 | op.add_option("--ignoregroups", action="store", type="ksboolean") | ||
223 | return op | ||
224 | |||
225 | class F13_Repo(F11_Repo): | ||
226 | removedKeywords = F11_Repo.removedKeywords | ||
227 | removedAttrs = F11_Repo.removedAttrs | ||
228 | |||
229 | def _getParser(self): | ||
230 | op = F11_Repo._getParser(self) | ||
231 | op.add_option("--proxy") | ||
232 | return op | ||
233 | |||
234 | class F14_Repo(F13_Repo): | ||
235 | removedKeywords = F13_Repo.removedKeywords | ||
236 | removedAttrs = F13_Repo.removedAttrs | ||
237 | |||
238 | def _getParser(self): | ||
239 | op = F13_Repo._getParser(self) | ||
240 | op.add_option("--noverifyssl", action="store_true", default=False) | ||
241 | return op | ||
242 | |||
243 | RHEL6_Repo = F14_Repo | ||
244 | |||
245 | class F15_Repo(F14_Repo): | ||
246 | removedKeywords = F14_Repo.removedKeywords | ||
247 | removedAttrs = F14_Repo.removedAttrs | ||
248 | |||
249 | urlRequired = False | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/rescue.py b/scripts/lib/mic/3rdparty/pykickstart/commands/rescue.py new file mode 100644 index 0000000000..1893d4ea49 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/rescue.py | |||
@@ -0,0 +1,68 @@ | |||
1 | # | ||
2 | # Alexander Todorov <atodorov@redhat.com> | ||
3 | # | ||
4 | # Copyright 2008 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class F10_Rescue(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | |||
35 | self.rescue = False | ||
36 | self.nomount = kwargs.get("nomount", False) | ||
37 | self.romount = kwargs.get("romount", False) | ||
38 | |||
39 | def __str__(self): | ||
40 | retval = KickstartCommand.__str__(self) | ||
41 | |||
42 | if self.rescue: | ||
43 | retval += "rescue" | ||
44 | |||
45 | if self.nomount: | ||
46 | retval += " --nomount" | ||
47 | if self.romount: | ||
48 | retval += " --romount" | ||
49 | |||
50 | retval = "# Start rescue mode\n%s\n" % retval | ||
51 | |||
52 | return retval | ||
53 | |||
54 | def _getParser(self): | ||
55 | op = KSOptionParser() | ||
56 | op.add_option("--nomount", dest="nomount", action="store_true", default=False) | ||
57 | op.add_option("--romount", dest="romount", action="store_true", default=False) | ||
58 | return op | ||
59 | |||
60 | def parse(self, args): | ||
61 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
62 | |||
63 | if opts.nomount and opts.romount: | ||
64 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Only one of --nomount and --romount may be specified for rescue command.")) | ||
65 | |||
66 | self._setToSelf(self.op, opts) | ||
67 | self.rescue = True | ||
68 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/rootpw.py b/scripts/lib/mic/3rdparty/pykickstart/commands/rootpw.py new file mode 100644 index 0000000000..e038b4525d --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/rootpw.py | |||
@@ -0,0 +1,93 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_RootPw(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | |||
35 | self.isCrypted = kwargs.get("isCrypted", False) | ||
36 | self.password = kwargs.get("password", "") | ||
37 | |||
38 | def _getArgsAsStr(self): | ||
39 | retval = "" | ||
40 | |||
41 | if self.isCrypted: | ||
42 | retval += " --iscrypted" | ||
43 | |||
44 | return retval | ||
45 | |||
46 | def __str__(self): | ||
47 | retval = KickstartCommand.__str__(self) | ||
48 | |||
49 | if self.password != "": | ||
50 | retval += "# Root password\nrootpw%s %s\n" % (self._getArgsAsStr(), self.password) | ||
51 | |||
52 | return retval | ||
53 | |||
54 | def _getParser(self): | ||
55 | op = KSOptionParser() | ||
56 | op.add_option("--iscrypted", dest="isCrypted", action="store_true", | ||
57 | default=False) | ||
58 | return op | ||
59 | |||
60 | def parse(self, args): | ||
61 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
62 | self._setToSelf(self.op, opts) | ||
63 | |||
64 | if len(extra) != 1: | ||
65 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "rootpw") | ||
66 | |||
67 | self.password = extra[0] | ||
68 | return self | ||
69 | |||
70 | class F8_RootPw(FC3_RootPw): | ||
71 | removedKeywords = FC3_RootPw.removedKeywords | ||
72 | removedAttrs = FC3_RootPw.removedAttrs | ||
73 | |||
74 | def __init__(self, writePriority=0, *args, **kwargs): | ||
75 | FC3_RootPw.__init__(self, writePriority, *args, **kwargs) | ||
76 | self.lock = kwargs.get("lock", False) | ||
77 | |||
78 | def _getArgsAsStr(self): | ||
79 | retval = FC3_RootPw._getArgsAsStr(self) | ||
80 | |||
81 | if self.lock: | ||
82 | retval += " --lock" | ||
83 | |||
84 | if not self.isCrypted: | ||
85 | retval += " --plaintext" | ||
86 | |||
87 | return retval | ||
88 | |||
89 | def _getParser(self): | ||
90 | op = FC3_RootPw._getParser(self) | ||
91 | op.add_option("--lock", dest="lock", action="store_true", default=False) | ||
92 | op.add_option("--plaintext", dest="isCrypted", action="store_false") | ||
93 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/selinux.py b/scripts/lib/mic/3rdparty/pykickstart/commands/selinux.py new file mode 100644 index 0000000000..9f8059c76b --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/selinux.py | |||
@@ -0,0 +1,64 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.constants import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | class FC3_SELinux(KickstartCommand): | ||
25 | removedKeywords = KickstartCommand.removedKeywords | ||
26 | removedAttrs = KickstartCommand.removedAttrs | ||
27 | |||
28 | def __init__(self, writePriority=0, *args, **kwargs): | ||
29 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
30 | self.op = self._getParser() | ||
31 | |||
32 | self.selinux = kwargs.get("selinux", None) | ||
33 | |||
34 | def __str__(self): | ||
35 | retval = KickstartCommand.__str__(self) | ||
36 | |||
37 | if not retval and self.selinux is None: | ||
38 | return "" | ||
39 | |||
40 | retval += "# SELinux configuration\n" | ||
41 | |||
42 | if self.selinux == SELINUX_DISABLED: | ||
43 | retval += "selinux --disabled\n" | ||
44 | elif self.selinux == SELINUX_ENFORCING: | ||
45 | retval += "selinux --enforcing\n" | ||
46 | elif self.selinux == SELINUX_PERMISSIVE: | ||
47 | retval += "selinux --permissive\n" | ||
48 | |||
49 | return retval | ||
50 | |||
51 | def _getParser(self): | ||
52 | op = KSOptionParser() | ||
53 | op.add_option("--disabled", dest="selinux", action="store_const", | ||
54 | const=SELINUX_DISABLED) | ||
55 | op.add_option("--enforcing", dest="selinux", action="store_const", | ||
56 | const=SELINUX_ENFORCING) | ||
57 | op.add_option("--permissive", dest="selinux", action="store_const", | ||
58 | const=SELINUX_PERMISSIVE) | ||
59 | return op | ||
60 | |||
61 | def parse(self, args): | ||
62 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
63 | self._setToSelf(self.op, opts) | ||
64 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/services.py b/scripts/lib/mic/3rdparty/pykickstart/commands/services.py new file mode 100644 index 0000000000..2e0eab8007 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/services.py | |||
@@ -0,0 +1,71 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC6_Services(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | |||
35 | self.disabled = kwargs.get("disabled", []) | ||
36 | self.enabled = kwargs.get("enabled", []) | ||
37 | |||
38 | def __str__(self): | ||
39 | retval = KickstartCommand.__str__(self) | ||
40 | args = "" | ||
41 | |||
42 | if len(self.disabled) > 0: | ||
43 | args += " --disabled=\"%s\"" % ",".join(self.disabled) | ||
44 | if len(self.enabled) > 0: | ||
45 | args += " --enabled=\"%s\"" % ",".join(self.enabled) | ||
46 | |||
47 | if args != "": | ||
48 | retval += "# System services\nservices%s\n" % args | ||
49 | |||
50 | return retval | ||
51 | |||
52 | def _getParser(self): | ||
53 | def services_cb (option, opt_str, value, parser): | ||
54 | for d in value.split(','): | ||
55 | parser.values.ensure_value(option.dest, []).append(d.strip()) | ||
56 | |||
57 | op = KSOptionParser() | ||
58 | op.add_option("--disabled", dest="disabled", action="callback", | ||
59 | callback=services_cb, nargs=1, type="string") | ||
60 | op.add_option("--enabled", dest="enabled", action="callback", | ||
61 | callback=services_cb, nargs=1, type="string") | ||
62 | return op | ||
63 | |||
64 | def parse(self, args): | ||
65 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
66 | self._setToSelf(self.op, opts) | ||
67 | |||
68 | if len(self.disabled) == 0 and len(self.enabled) == 0: | ||
69 | raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("One of --disabled or --enabled must be provided.")) | ||
70 | |||
71 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/skipx.py b/scripts/lib/mic/3rdparty/pykickstart/commands/skipx.py new file mode 100644 index 0000000000..36d1a8d5ba --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/skipx.py | |||
@@ -0,0 +1,54 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_SkipX(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | self.skipx = kwargs.get("skipx", False) | ||
35 | |||
36 | def __str__(self): | ||
37 | retval = KickstartCommand.__str__(self) | ||
38 | |||
39 | if self.skipx: | ||
40 | retval += "# Do not configure the X Window System\nskipx\n" | ||
41 | |||
42 | return retval | ||
43 | |||
44 | def _getParser(self): | ||
45 | op = KSOptionParser() | ||
46 | return op | ||
47 | |||
48 | def parse(self, args): | ||
49 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
50 | if len(extra) > 0: | ||
51 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "skipx") | ||
52 | |||
53 | self.skipx = True | ||
54 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/sshpw.py b/scripts/lib/mic/3rdparty/pykickstart/commands/sshpw.py new file mode 100644 index 0000000000..e7867ebfb2 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/sshpw.py | |||
@@ -0,0 +1,105 @@ | |||
1 | # | ||
2 | # Peter Jones <pjones@redhat.com> | ||
3 | # | ||
4 | # Copyright 2009 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class F13_SshPwData(BaseData): | ||
28 | removedKeywords = BaseData.removedKeywords | ||
29 | removedAttrs = BaseData.removedAttrs | ||
30 | |||
31 | def __init__(self, *args, **kwargs): | ||
32 | BaseData.__init__(self, *args, **kwargs) | ||
33 | self.username = kwargs.get("username", None) | ||
34 | self.isCrypted = kwargs.get("isCrypted", False) | ||
35 | self.password = kwargs.get("password", "") | ||
36 | self.lock = kwargs.get("lock", False) | ||
37 | |||
38 | def __eq__(self, y): | ||
39 | return self.username == y.username | ||
40 | |||
41 | def __str__(self): | ||
42 | retval = BaseData.__str__(self) | ||
43 | |||
44 | retval += "sshpw" | ||
45 | retval += self._getArgsAsStr() + '\n' | ||
46 | |||
47 | return retval | ||
48 | |||
49 | def _getArgsAsStr(self): | ||
50 | retval = "" | ||
51 | |||
52 | retval += " --username=%s" % self.username | ||
53 | if self.lock: | ||
54 | retval += " --lock" | ||
55 | if self.isCrypted: | ||
56 | retval += " --iscrypted" | ||
57 | else: | ||
58 | retval += " --plaintext" | ||
59 | |||
60 | retval += " %s" % self.password | ||
61 | return retval | ||
62 | |||
63 | class F13_SshPw(KickstartCommand): | ||
64 | removedKeywords = KickstartCommand.removedKeywords | ||
65 | removedAttrs = KickstartCommand.removedAttrs | ||
66 | |||
67 | def __init__(self, writePriority=0, *args, **kwargs): | ||
68 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
69 | self.op = self._getParser() | ||
70 | |||
71 | self.sshUserList = kwargs.get("sshUserList", []) | ||
72 | |||
73 | def __str__(self): | ||
74 | retval = "" | ||
75 | for user in self.sshUserList: | ||
76 | retval += user.__str__() | ||
77 | |||
78 | return retval | ||
79 | |||
80 | def _getParser(self): | ||
81 | op = KSOptionParser() | ||
82 | op.add_option("--username", dest="username", required=True) | ||
83 | op.add_option("--iscrypted", dest="isCrypted", action="store_true", | ||
84 | default=False) | ||
85 | op.add_option("--plaintext", dest="isCrypted", action="store_false") | ||
86 | op.add_option("--lock", dest="lock", action="store_true", default=False) | ||
87 | return op | ||
88 | |||
89 | def parse(self, args): | ||
90 | ud = self.handler.SshPwData() | ||
91 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
92 | self._setToObj(self.op, opts, ud) | ||
93 | ud.lineno = self.lineno | ||
94 | |||
95 | if len(extra) != 1: | ||
96 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "sshpw") | ||
97 | ud.password = extra[0] | ||
98 | |||
99 | if ud in self.dataList(): | ||
100 | warnings.warn(_("An ssh user with the name %s has already been defined.") % ud.name) | ||
101 | |||
102 | return ud | ||
103 | |||
104 | def dataList(self): | ||
105 | return self.sshUserList | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/timezone.py b/scripts/lib/mic/3rdparty/pykickstart/commands/timezone.py new file mode 100644 index 0000000000..f5441de593 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/timezone.py | |||
@@ -0,0 +1,86 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_Timezone(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | |||
35 | self.isUtc = kwargs.get("isUtc", False) | ||
36 | self.timezone = kwargs.get("timezone", "") | ||
37 | |||
38 | def __str__(self): | ||
39 | retval = KickstartCommand.__str__(self) | ||
40 | |||
41 | if self.timezone != "": | ||
42 | if self.isUtc: | ||
43 | utc = "--utc" | ||
44 | else: | ||
45 | utc = "" | ||
46 | |||
47 | retval += "# System timezone\ntimezone %s %s\n" %(utc, self.timezone) | ||
48 | |||
49 | return retval | ||
50 | |||
51 | def _getParser(self): | ||
52 | op = KSOptionParser() | ||
53 | op.add_option("--utc", dest="isUtc", action="store_true", default=False) | ||
54 | return op | ||
55 | |||
56 | def parse(self, args): | ||
57 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
58 | self._setToSelf(self.op, opts) | ||
59 | |||
60 | if len(extra) != 1: | ||
61 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("A single argument is expected for the %s command") % "timezone") | ||
62 | |||
63 | self.timezone = extra[0] | ||
64 | return self | ||
65 | |||
66 | class FC6_Timezone(FC3_Timezone): | ||
67 | removedKeywords = FC3_Timezone.removedKeywords | ||
68 | removedAttrs = FC3_Timezone.removedAttrs | ||
69 | |||
70 | def __str__(self): | ||
71 | retval = KickstartCommand.__str__(self) | ||
72 | |||
73 | if self.timezone != "": | ||
74 | if self.isUtc: | ||
75 | utc = "--isUtc" | ||
76 | else: | ||
77 | utc = "" | ||
78 | |||
79 | retval += "# System timezone\ntimezone %s %s\n" %(utc, self.timezone) | ||
80 | |||
81 | return retval | ||
82 | |||
83 | def _getParser(self): | ||
84 | op = FC3_Timezone._getParser(self) | ||
85 | op.add_option("--utc", "--isUtc", dest="isUtc", action="store_true", default=False) | ||
86 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/updates.py b/scripts/lib/mic/3rdparty/pykickstart/commands/updates.py new file mode 100644 index 0000000000..53ec49f7b8 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/updates.py | |||
@@ -0,0 +1,60 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class F7_Updates(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | self.url = kwargs.get("url", "") | ||
35 | |||
36 | def __str__(self): | ||
37 | retval = KickstartCommand.__str__(self) | ||
38 | |||
39 | if self.url == "floppy": | ||
40 | retval += "updates\n" | ||
41 | elif self.url != "": | ||
42 | retval += "updates %s\n" % self.url | ||
43 | |||
44 | return retval | ||
45 | |||
46 | def _getParser(self): | ||
47 | op = KSOptionParser() | ||
48 | return op | ||
49 | |||
50 | def parse(self, args): | ||
51 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
52 | |||
53 | if len(extra) > 1: | ||
54 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s only takes one argument") % "updates") | ||
55 | elif len(extra) == 0: | ||
56 | self.url = "floppy" | ||
57 | else: | ||
58 | self.url = extra[0] | ||
59 | |||
60 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/upgrade.py b/scripts/lib/mic/3rdparty/pykickstart/commands/upgrade.py new file mode 100644 index 0000000000..a68a82d378 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/upgrade.py | |||
@@ -0,0 +1,106 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_Upgrade(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.upgrade = kwargs.get("upgrade", None) | ||
34 | self.op = self._getParser() | ||
35 | |||
36 | def __str__(self): | ||
37 | retval = KickstartCommand.__str__(self) | ||
38 | |||
39 | if self.upgrade is None: | ||
40 | return retval | ||
41 | |||
42 | if self.upgrade: | ||
43 | retval += "# Upgrade existing installation\nupgrade\n" | ||
44 | else: | ||
45 | retval += "# Install OS instead of upgrade\ninstall\n" | ||
46 | |||
47 | return retval | ||
48 | |||
49 | def _getParser(self): | ||
50 | op = KSOptionParser() | ||
51 | return op | ||
52 | |||
53 | def parse(self, args): | ||
54 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
55 | |||
56 | if len(extra) > 0: | ||
57 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "upgrade") | ||
58 | |||
59 | if self.currentCmd == "upgrade": | ||
60 | self.upgrade = True | ||
61 | else: | ||
62 | self.upgrade = False | ||
63 | |||
64 | return self | ||
65 | |||
66 | class F11_Upgrade(FC3_Upgrade): | ||
67 | removedKeywords = FC3_Upgrade.removedKeywords | ||
68 | removedAttrs = FC3_Upgrade.removedAttrs | ||
69 | |||
70 | def __init__(self, writePriority=0, *args, **kwargs): | ||
71 | FC3_Upgrade.__init__(self, writePriority, *args, **kwargs) | ||
72 | |||
73 | self.op = self._getParser() | ||
74 | self.root_device = kwargs.get("root_device", None) | ||
75 | |||
76 | def __str__(self): | ||
77 | if self.upgrade and (self.root_device is not None): | ||
78 | retval = KickstartCommand.__str__(self) | ||
79 | retval += "# Upgrade existing installation\nupgrade --root-device=%s\n" % self.root_device | ||
80 | else: | ||
81 | retval = FC3_Upgrade.__str__(self) | ||
82 | |||
83 | return retval | ||
84 | |||
85 | def _getParser(self): | ||
86 | op = KSOptionParser() | ||
87 | op.add_option("--root-device", dest="root_device") | ||
88 | return op | ||
89 | |||
90 | def parse(self, args): | ||
91 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
92 | |||
93 | if len(extra) > 0: | ||
94 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "upgrade") | ||
95 | |||
96 | if (opts.root_device is not None) and (opts.root_device == ""): | ||
97 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not accept empty parameter %s") % ("upgrade", "--root-device")) | ||
98 | else: | ||
99 | self.root_device = opts.root_device | ||
100 | |||
101 | if self.currentCmd == "upgrade": | ||
102 | self.upgrade = True | ||
103 | else: | ||
104 | self.upgrade = False | ||
105 | |||
106 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/user.py b/scripts/lib/mic/3rdparty/pykickstart/commands/user.py new file mode 100644 index 0000000000..189dc7585f --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/user.py | |||
@@ -0,0 +1,173 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.constants import * | ||
22 | from pykickstart.errors import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | import gettext | ||
26 | import warnings | ||
27 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
28 | |||
29 | class FC6_UserData(BaseData): | ||
30 | removedKeywords = BaseData.removedKeywords | ||
31 | removedAttrs = BaseData.removedAttrs | ||
32 | |||
33 | def __init__(self, *args, **kwargs): | ||
34 | BaseData.__init__(self, *args, **kwargs) | ||
35 | self.groups = kwargs.get("groups", []) | ||
36 | self.homedir = kwargs.get("homedir", "") | ||
37 | self.isCrypted = kwargs.get("isCrypted", False) | ||
38 | self.name = kwargs.get("name", "") | ||
39 | self.password = kwargs.get("password", "") | ||
40 | self.shell = kwargs.get("shell", "") | ||
41 | self.uid = kwargs.get("uid", None) | ||
42 | |||
43 | def __eq__(self, y): | ||
44 | return self.name == y.name | ||
45 | |||
46 | def __str__(self): | ||
47 | retval = BaseData.__str__(self) | ||
48 | |||
49 | if self.uid != "": | ||
50 | retval += "user" | ||
51 | retval += self._getArgsAsStr() + "\n" | ||
52 | |||
53 | return retval | ||
54 | |||
55 | def _getArgsAsStr(self): | ||
56 | retval = "" | ||
57 | |||
58 | if len(self.groups) > 0: | ||
59 | retval += " --groups=%s" % ",".join(self.groups) | ||
60 | if self.homedir: | ||
61 | retval += " --homedir=%s" % self.homedir | ||
62 | if self.name: | ||
63 | retval += " --name=%s" % self.name | ||
64 | if self.password: | ||
65 | retval += " --password=%s" % self.password | ||
66 | if self.isCrypted: | ||
67 | retval += " --iscrypted" | ||
68 | if self.shell: | ||
69 | retval += " --shell=%s" % self.shell | ||
70 | if self.uid: | ||
71 | retval += " --uid=%s" % self.uid | ||
72 | |||
73 | return retval | ||
74 | |||
75 | class F8_UserData(FC6_UserData): | ||
76 | removedKeywords = FC6_UserData.removedKeywords | ||
77 | removedAttrs = FC6_UserData.removedAttrs | ||
78 | |||
79 | def __init__(self, *args, **kwargs): | ||
80 | FC6_UserData.__init__(self, *args, **kwargs) | ||
81 | self.lock = kwargs.get("lock", False) | ||
82 | |||
83 | def _getArgsAsStr(self): | ||
84 | retval = FC6_UserData._getArgsAsStr(self) | ||
85 | |||
86 | if self.lock: | ||
87 | retval += " --lock" | ||
88 | |||
89 | return retval | ||
90 | |||
91 | class F12_UserData(F8_UserData): | ||
92 | removedKeywords = F8_UserData.removedKeywords | ||
93 | removedAttrs = F8_UserData.removedAttrs | ||
94 | |||
95 | def __init__(self, *args, **kwargs): | ||
96 | F8_UserData.__init__(self, *args, **kwargs) | ||
97 | self.gecos = kwargs.get("gecos", "") | ||
98 | |||
99 | def _getArgsAsStr(self): | ||
100 | retval = F8_UserData._getArgsAsStr(self) | ||
101 | |||
102 | if self.gecos: | ||
103 | retval += " --gecos=\"%s\"" % (self.gecos,) | ||
104 | |||
105 | return retval | ||
106 | |||
107 | class FC6_User(KickstartCommand): | ||
108 | removedKeywords = KickstartCommand.removedKeywords | ||
109 | removedAttrs = KickstartCommand.removedAttrs | ||
110 | |||
111 | def __init__(self, writePriority=0, *args, **kwargs): | ||
112 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
113 | self.op = self._getParser() | ||
114 | |||
115 | self.userList = kwargs.get("userList", []) | ||
116 | |||
117 | def __str__(self): | ||
118 | retval = "" | ||
119 | for user in self.userList: | ||
120 | retval += user.__str__() | ||
121 | |||
122 | return retval | ||
123 | |||
124 | def _getParser(self): | ||
125 | def groups_cb (option, opt_str, value, parser): | ||
126 | for d in value.split(','): | ||
127 | parser.values.ensure_value(option.dest, []).append(d) | ||
128 | |||
129 | op = KSOptionParser() | ||
130 | op.add_option("--groups", dest="groups", action="callback", | ||
131 | callback=groups_cb, nargs=1, type="string") | ||
132 | op.add_option("--homedir") | ||
133 | op.add_option("--iscrypted", dest="isCrypted", action="store_true", | ||
134 | default=False) | ||
135 | op.add_option("--name", required=1) | ||
136 | op.add_option("--password") | ||
137 | op.add_option("--shell") | ||
138 | op.add_option("--uid", type="int") | ||
139 | return op | ||
140 | |||
141 | def parse(self, args): | ||
142 | ud = self.handler.UserData() | ||
143 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
144 | self._setToObj(self.op, opts, ud) | ||
145 | ud.lineno = self.lineno | ||
146 | |||
147 | # Check for duplicates in the data list. | ||
148 | if ud in self.dataList(): | ||
149 | warnings.warn(_("A user with the name %s has already been defined.") % ud.name) | ||
150 | |||
151 | return ud | ||
152 | |||
153 | def dataList(self): | ||
154 | return self.userList | ||
155 | |||
156 | class F8_User(FC6_User): | ||
157 | removedKeywords = FC6_User.removedKeywords | ||
158 | removedAttrs = FC6_User.removedAttrs | ||
159 | |||
160 | def _getParser(self): | ||
161 | op = FC6_User._getParser(self) | ||
162 | op.add_option("--lock", action="store_true", default=False) | ||
163 | op.add_option("--plaintext", dest="isCrypted", action="store_false") | ||
164 | return op | ||
165 | |||
166 | class F12_User(F8_User): | ||
167 | removedKeywords = F8_User.removedKeywords | ||
168 | removedAttrs = F8_User.removedAttrs | ||
169 | |||
170 | def _getParser(self): | ||
171 | op = F8_User._getParser(self) | ||
172 | op.add_option("--gecos", type="string") | ||
173 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/vnc.py b/scripts/lib/mic/3rdparty/pykickstart/commands/vnc.py new file mode 100644 index 0000000000..200ccfba2e --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/vnc.py | |||
@@ -0,0 +1,114 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | class FC3_Vnc(KickstartCommand): | ||
25 | removedKeywords = KickstartCommand.removedKeywords | ||
26 | removedAttrs = KickstartCommand.removedAttrs | ||
27 | |||
28 | def __init__(self, writePriority=0, *args, **kwargs): | ||
29 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
30 | self.op = self._getParser() | ||
31 | |||
32 | self.enabled = kwargs.get("enabled", False) | ||
33 | self.password = kwargs.get("password", "") | ||
34 | self.connect = kwargs.get("connect", "") | ||
35 | |||
36 | def __str__(self): | ||
37 | retval = KickstartCommand.__str__(self) | ||
38 | |||
39 | if not self.enabled: | ||
40 | return retval | ||
41 | |||
42 | retval += "vnc" | ||
43 | |||
44 | if self.connect != "": | ||
45 | retval += " --connect=%s" % self.connect | ||
46 | if self.password != "": | ||
47 | retval += " --password=%s" % self.password | ||
48 | |||
49 | return retval + "\n" | ||
50 | |||
51 | def _getParser(self): | ||
52 | op = KSOptionParser() | ||
53 | op.add_option("--connect") | ||
54 | op.add_option("--password", dest="password") | ||
55 | return op | ||
56 | |||
57 | def parse(self, args): | ||
58 | self.enabled = True | ||
59 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
60 | self._setToSelf(self.op, opts) | ||
61 | return self | ||
62 | |||
63 | class FC6_Vnc(FC3_Vnc): | ||
64 | removedKeywords = FC3_Vnc.removedKeywords + ["connect"] | ||
65 | removedAttrs = FC3_Vnc.removedAttrs + ["connect"] | ||
66 | |||
67 | def __init__(self, writePriority=0, host="", port="", *args, **kwargs): | ||
68 | FC3_Vnc.__init__(self, writePriority, *args, **kwargs) | ||
69 | self.deleteRemovedAttrs() | ||
70 | |||
71 | self.host = kwargs.get("host", "") | ||
72 | self.port = kwargs.get("port", "") | ||
73 | |||
74 | def __str__(self): | ||
75 | retval = KickstartCommand.__str__(self) | ||
76 | |||
77 | if not self.enabled: | ||
78 | return retval | ||
79 | |||
80 | retval += "vnc" | ||
81 | |||
82 | if self.host != "": | ||
83 | retval += " --host=%s" % self.host | ||
84 | |||
85 | if self.port != "": | ||
86 | retval += " --port=%s" % self.port | ||
87 | if self.password != "": | ||
88 | retval += " --password=%s" % self.password | ||
89 | |||
90 | return retval + "\n" | ||
91 | |||
92 | def _getParser(self): | ||
93 | def connect_cb (option, opt_str, value, parser): | ||
94 | cargs = value.split(":") | ||
95 | parser.values.ensure_value("host", cargs[0]) | ||
96 | |||
97 | if len(cargs) > 1: | ||
98 | parser.values.ensure_value("port", cargs[1]) | ||
99 | |||
100 | op = FC3_Vnc._getParser(self) | ||
101 | op.add_option("--connect", action="callback", callback=connect_cb, | ||
102 | nargs=1, type="string") | ||
103 | op.add_option("--host", dest="host") | ||
104 | op.add_option("--port", dest="port") | ||
105 | return op | ||
106 | |||
107 | class F9_Vnc(FC6_Vnc): | ||
108 | removedKeywords = FC6_Vnc.removedKeywords | ||
109 | removedAttrs = FC6_Vnc.removedAttrs | ||
110 | |||
111 | def _getParser(self): | ||
112 | op = FC6_Vnc._getParser(self) | ||
113 | op.remove_option("--connect") | ||
114 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/volgroup.py b/scripts/lib/mic/3rdparty/pykickstart/commands/volgroup.py new file mode 100644 index 0000000000..255c47f0ae --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/volgroup.py | |||
@@ -0,0 +1,102 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.options import * | ||
22 | |||
23 | import gettext | ||
24 | import warnings | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_VolGroupData(BaseData): | ||
28 | removedKeywords = BaseData.removedKeywords | ||
29 | removedAttrs = BaseData.removedAttrs | ||
30 | |||
31 | def __init__(self, *args, **kwargs): | ||
32 | BaseData.__init__(self, *args, **kwargs) | ||
33 | self.format = kwargs.get("format", True) | ||
34 | self.pesize = kwargs.get("pesize", 32768) | ||
35 | self.preexist = kwargs.get("preexist", False) | ||
36 | self.vgname = kwargs.get("vgname", "") | ||
37 | self.physvols = kwargs.get("physvols", []) | ||
38 | |||
39 | def __eq__(self, y): | ||
40 | return self.vgname == y.vgname | ||
41 | |||
42 | def __str__(self): | ||
43 | retval = BaseData.__str__(self) | ||
44 | retval += "volgroup %s" % self.vgname | ||
45 | |||
46 | if not self.format: | ||
47 | retval += " --noformat" | ||
48 | if self.pesize != 0: | ||
49 | retval += " --pesize=%d" % self.pesize | ||
50 | if self.preexist: | ||
51 | retval += " --useexisting" | ||
52 | |||
53 | return retval + " " + " ".join(self.physvols) + "\n" | ||
54 | |||
55 | class FC3_VolGroup(KickstartCommand): | ||
56 | removedKeywords = KickstartCommand.removedKeywords | ||
57 | removedAttrs = KickstartCommand.removedAttrs | ||
58 | |||
59 | def __init__(self, writePriority=132, *args, **kwargs): | ||
60 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
61 | self.op = self._getParser() | ||
62 | |||
63 | self.vgList = kwargs.get("vgList", []) | ||
64 | |||
65 | def __str__(self): | ||
66 | retval = "" | ||
67 | for vg in self.vgList: | ||
68 | retval += vg.__str__() | ||
69 | |||
70 | return retval | ||
71 | |||
72 | def _getParser(self): | ||
73 | # Have to be a little more complicated to set two values. | ||
74 | def vg_cb (option, opt_str, value, parser): | ||
75 | parser.values.format = False | ||
76 | parser.values.preexist = True | ||
77 | |||
78 | op = KSOptionParser() | ||
79 | op.add_option("--noformat", action="callback", callback=vg_cb, | ||
80 | dest="format", default=True, nargs=0) | ||
81 | op.add_option("--pesize", dest="pesize", type="int", nargs=1, | ||
82 | default=32768) | ||
83 | op.add_option("--useexisting", dest="preexist", action="store_true", | ||
84 | default=False) | ||
85 | return op | ||
86 | |||
87 | def parse(self, args): | ||
88 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
89 | vg = self.handler.VolGroupData() | ||
90 | self._setToObj(self.op, opts, vg) | ||
91 | vg.lineno = self.lineno | ||
92 | vg.vgname = extra[0] | ||
93 | vg.physvols = extra[1:] | ||
94 | |||
95 | # Check for duplicates in the data list. | ||
96 | if vg in self.dataList(): | ||
97 | warnings.warn(_("A volgroup with the name %s has already been defined.") % vg.vgname) | ||
98 | |||
99 | return vg | ||
100 | |||
101 | def dataList(self): | ||
102 | return self.vgList | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/xconfig.py b/scripts/lib/mic/3rdparty/pykickstart/commands/xconfig.py new file mode 100644 index 0000000000..644ee86743 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/xconfig.py | |||
@@ -0,0 +1,184 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007, 2008 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | import gettext | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_XConfig(KickstartCommand): | ||
28 | removedKeywords = KickstartCommand.removedKeywords | ||
29 | removedAttrs = KickstartCommand.removedAttrs | ||
30 | |||
31 | def __init__(self, writePriority=0, *args, **kwargs): | ||
32 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
33 | self.op = self._getParser() | ||
34 | |||
35 | self.card = kwargs.get("card", "") | ||
36 | self.defaultdesktop = kwargs.get("defaultdesktop", "") | ||
37 | self.depth = kwargs.get("depth", 0) | ||
38 | self.hsync = kwargs.get("hsync", "") | ||
39 | self.monitor = kwargs.get("monitor", "") | ||
40 | self.noProbe = kwargs.get("noProbe", False) | ||
41 | self.resolution = kwargs.get("resolution", "") | ||
42 | self.server = kwargs.get("server", "") | ||
43 | self.startX = kwargs.get("startX", False) | ||
44 | self.videoRam = kwargs.get("videoRam", "") | ||
45 | self.vsync = kwargs.get("vsync", "") | ||
46 | |||
47 | def __str__(self): | ||
48 | retval = KickstartCommand.__str__(self) | ||
49 | |||
50 | if self.card != "": | ||
51 | retval += " --card=%s" % self.card | ||
52 | if self.defaultdesktop != "": | ||
53 | retval += " --defaultdesktop=%s" % self.defaultdesktop | ||
54 | if self.depth != 0: | ||
55 | retval += " --depth=%d" % self.depth | ||
56 | if self.hsync != "": | ||
57 | retval += " --hsync=%s" % self.hsync | ||
58 | if self.monitor != "": | ||
59 | retval += " --monitor=%s" % self.monitor | ||
60 | if self.noProbe: | ||
61 | retval += " --noprobe" | ||
62 | if self.resolution != "": | ||
63 | retval += " --resolution=%s" % self.resolution | ||
64 | if self.server != "": | ||
65 | retval += " --server=%s" % self.server | ||
66 | if self.startX: | ||
67 | retval += " --startxonboot" | ||
68 | if self.videoRam != "": | ||
69 | retval += " --videoram=%s" % self.videoRam | ||
70 | if self.vsync != "": | ||
71 | retval += " --vsync=%s" % self.vsync | ||
72 | |||
73 | if retval != "": | ||
74 | retval = "# X Window System configuration information\nxconfig %s\n" % retval | ||
75 | |||
76 | return retval | ||
77 | |||
78 | def _getParser(self): | ||
79 | op = KSOptionParser() | ||
80 | op.add_option("--card") | ||
81 | op.add_option("--defaultdesktop") | ||
82 | op.add_option("--depth", action="store", type="int", nargs=1) | ||
83 | op.add_option("--hsync") | ||
84 | op.add_option("--monitor") | ||
85 | op.add_option("--noprobe", dest="noProbe", action="store_true", | ||
86 | default=False) | ||
87 | op.add_option("--resolution") | ||
88 | op.add_option("--server") | ||
89 | op.add_option("--startxonboot", dest="startX", action="store_true", | ||
90 | default=False) | ||
91 | op.add_option("--videoram", dest="videoRam") | ||
92 | op.add_option("--vsync") | ||
93 | return op | ||
94 | |||
95 | def parse(self, args): | ||
96 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
97 | if extra: | ||
98 | mapping = {"command": "xconfig", "options": extra} | ||
99 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=_("Unexpected arguments to %(command)s command: %(options)s") % mapping) | ||
100 | |||
101 | self._setToSelf(self.op, opts) | ||
102 | return self | ||
103 | |||
104 | class FC6_XConfig(FC3_XConfig): | ||
105 | removedKeywords = FC3_XConfig.removedKeywords + ["card", "hsync", "monitor", "noProbe", "vsync"] | ||
106 | removedAttrs = FC3_XConfig.removedAttrs + ["card", "hsync", "monitor", "noProbe", "vsync"] | ||
107 | |||
108 | def __init__(self, writePriority=0, *args, **kwargs): | ||
109 | FC3_XConfig.__init__(self, writePriority, *args, **kwargs) | ||
110 | self.deleteRemovedAttrs() | ||
111 | |||
112 | self.driver = kwargs.get("driver", "") | ||
113 | |||
114 | def __str__(self): | ||
115 | retval = KickstartCommand.__str__(self) | ||
116 | |||
117 | if hasattr(self, "driver") and self.driver != "": | ||
118 | retval += " --driver=%s" % self.driver | ||
119 | if self.defaultdesktop != "": | ||
120 | retval += " --defaultdesktop=%s" % self.defaultdesktop | ||
121 | if self.depth != 0: | ||
122 | retval += " --depth=%d" % self.depth | ||
123 | if hasattr(self, "resolution") and self.resolution != "": | ||
124 | retval += " --resolution=%s" % self.resolution | ||
125 | if self.startX: | ||
126 | retval += " --startxonboot" | ||
127 | if hasattr(self, "videoRam") and self.videoRam != "": | ||
128 | retval += " --videoram=%s" % self.videoRam | ||
129 | |||
130 | if retval != "": | ||
131 | retval = "# X Window System configuration information\nxconfig %s\n" % retval | ||
132 | |||
133 | return retval | ||
134 | |||
135 | def _getParser(self): | ||
136 | op = FC3_XConfig._getParser(self) | ||
137 | op.add_option("--card", deprecated=1) | ||
138 | op.add_option("--driver", dest="driver") | ||
139 | op.add_option("--hsync", deprecated=1) | ||
140 | op.add_option("--monitor", deprecated=1) | ||
141 | op.add_option("--noprobe", deprecated=1) | ||
142 | op.add_option("--vsync", deprecated=1) | ||
143 | return op | ||
144 | |||
145 | class F9_XConfig(FC6_XConfig): | ||
146 | removedKeywords = FC6_XConfig.removedKeywords | ||
147 | removedAttrs = FC6_XConfig.removedAttrs | ||
148 | |||
149 | def _getParser(self): | ||
150 | op = FC6_XConfig._getParser(self) | ||
151 | op.remove_option("--card") | ||
152 | op.remove_option("--hsync") | ||
153 | op.remove_option("--monitor") | ||
154 | op.remove_option("--noprobe") | ||
155 | op.remove_option("--vsync") | ||
156 | return op | ||
157 | |||
158 | class F10_XConfig(F9_XConfig): | ||
159 | removedKeywords = F9_XConfig.removedKeywords + ["driver", "resolution", "videoRam"] | ||
160 | removedAttrs = F9_XConfig.removedAttrs + ["driver", "resolution", "videoRam"] | ||
161 | |||
162 | def __init__(self, writePriority=0, *args, **kwargs): | ||
163 | F9_XConfig.__init__(self, writePriority, *args, **kwargs) | ||
164 | self.deleteRemovedAttrs() | ||
165 | |||
166 | def _getParser(self): | ||
167 | op = F9_XConfig._getParser(self) | ||
168 | op.add_option("--driver", deprecated=1) | ||
169 | op.add_option("--depth", deprecated=1) | ||
170 | op.add_option("--resolution", deprecated=1) | ||
171 | op.add_option("--videoram", deprecated=1) | ||
172 | return op | ||
173 | |||
174 | class F14_XConfig(F10_XConfig): | ||
175 | removedKeywords = F10_XConfig.removedKeywords | ||
176 | removedAttrs = F10_XConfig.removedAttrs | ||
177 | |||
178 | def _getParser(self): | ||
179 | op = F10_XConfig._getParser(self) | ||
180 | op.remove_option("--driver") | ||
181 | op.remove_option("--depth") | ||
182 | op.remove_option("--resolution") | ||
183 | op.remove_option("--videoram") | ||
184 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/zerombr.py b/scripts/lib/mic/3rdparty/pykickstart/commands/zerombr.py new file mode 100644 index 0000000000..79555a9b27 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/zerombr.py | |||
@@ -0,0 +1,69 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | import warnings | ||
21 | |||
22 | from pykickstart.base import * | ||
23 | from pykickstart.options import * | ||
24 | |||
25 | import gettext | ||
26 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
27 | |||
28 | class FC3_ZeroMbr(KickstartCommand): | ||
29 | removedKeywords = KickstartCommand.removedKeywords | ||
30 | removedAttrs = KickstartCommand.removedAttrs | ||
31 | |||
32 | def __init__(self, writePriority=110, *args, **kwargs): | ||
33 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
34 | self.op = self._getParser() | ||
35 | self.zerombr = kwargs.get("zerombr", False) | ||
36 | |||
37 | def __str__(self): | ||
38 | retval = KickstartCommand.__str__(self) | ||
39 | |||
40 | if self.zerombr: | ||
41 | retval += "# Clear the Master Boot Record\nzerombr\n" | ||
42 | |||
43 | return retval | ||
44 | |||
45 | def _getParser(self): | ||
46 | op = KSOptionParser() | ||
47 | return op | ||
48 | |||
49 | def parse(self, args): | ||
50 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
51 | |||
52 | if len(extra) > 0: | ||
53 | warnings.warn(_("Ignoring deprecated option on line %s: The zerombr command no longer takes any options. In future releases, this will result in a fatal error from kickstart. Please modify your kickstart file to remove any options.") % self.lineno, DeprecationWarning) | ||
54 | |||
55 | self.zerombr = True | ||
56 | return self | ||
57 | |||
58 | class F9_ZeroMbr(FC3_ZeroMbr): | ||
59 | removedKeywords = FC3_ZeroMbr.removedKeywords | ||
60 | removedAttrs = FC3_ZeroMbr.removedAttrs | ||
61 | |||
62 | def parse(self, args): | ||
63 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
64 | |||
65 | if len(extra) > 0: | ||
66 | raise KickstartParseError, formatErrorMsg(self.lineno, msg=_("Kickstart command %s does not take any arguments") % "zerombr") | ||
67 | |||
68 | self.zerombr = True | ||
69 | return self | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/commands/zfcp.py b/scripts/lib/mic/3rdparty/pykickstart/commands/zfcp.py new file mode 100644 index 0000000000..1ed2694c89 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/commands/zfcp.py | |||
@@ -0,0 +1,134 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.options import * | ||
22 | |||
23 | import gettext | ||
24 | import warnings | ||
25 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
26 | |||
27 | class FC3_ZFCPData(BaseData): | ||
28 | removedKeywords = BaseData.removedKeywords | ||
29 | removedAttrs = BaseData.removedAttrs | ||
30 | |||
31 | def __init__(self, *args, **kwargs): | ||
32 | BaseData.__init__(self, *args, **kwargs) | ||
33 | self.devnum = kwargs.get("devnum", "") | ||
34 | self.wwpn = kwargs.get("wwpn", "") | ||
35 | self.fcplun = kwargs.get("fcplun", "") | ||
36 | self.scsiid = kwargs.get("scsiid", "") | ||
37 | self.scsilun = kwargs.get("scsilun", "") | ||
38 | |||
39 | def __eq__(self, y): | ||
40 | return self.devnum == y.devnum and self.wwpn == y.wwpn and \ | ||
41 | self.fcplun == y.fcplun and self.scsiid == y.scsiid and \ | ||
42 | self.scsilun == y.scsilun | ||
43 | |||
44 | def __str__(self): | ||
45 | retval = BaseData.__str__(self) | ||
46 | retval += "zfcp" | ||
47 | |||
48 | if self.devnum != "": | ||
49 | retval += " --devnum=%s" % self.devnum | ||
50 | if self.wwpn != "": | ||
51 | retval += " --wwpn=%s" % self.wwpn | ||
52 | if self.fcplun != "": | ||
53 | retval += " --fcplun=%s" % self.fcplun | ||
54 | if hasattr(self, "scsiid") and self.scsiid != "": | ||
55 | retval += " --scsiid=%s" % self.scsiid | ||
56 | if hasattr(self, "scsilun") and self.scsilun != "": | ||
57 | retval += " --scsilun=%s" % self.scsilun | ||
58 | |||
59 | return retval + "\n" | ||
60 | |||
61 | class F12_ZFCPData(FC3_ZFCPData): | ||
62 | removedKeywords = FC3_ZFCPData.removedKeywords + ["scsiid", "scsilun"] | ||
63 | removedAttrs = FC3_ZFCPData.removedAttrs + ["scsiid", "scsilun"] | ||
64 | |||
65 | def __init__(self, *args, **kwargs): | ||
66 | FC3_ZFCPData.__init__(self, *args, **kwargs) | ||
67 | self.deleteRemovedAttrs() | ||
68 | |||
69 | F14_ZFCPData = F12_ZFCPData | ||
70 | |||
71 | class FC3_ZFCP(KickstartCommand): | ||
72 | removedKeywords = KickstartCommand.removedKeywords | ||
73 | removedAttrs = KickstartCommand.removedAttrs | ||
74 | |||
75 | def __init__(self, writePriority=71, *args, **kwargs): | ||
76 | KickstartCommand.__init__(self, writePriority, *args, **kwargs) | ||
77 | self.op = self._getParser() | ||
78 | |||
79 | self.zfcp = kwargs.get("zfcp", []) | ||
80 | |||
81 | def __str__(self): | ||
82 | retval = "" | ||
83 | for zfcp in self.zfcp: | ||
84 | retval += zfcp.__str__() | ||
85 | |||
86 | return retval | ||
87 | |||
88 | def _getParser(self): | ||
89 | op = KSOptionParser() | ||
90 | op.add_option("--devnum", dest="devnum", required=1) | ||
91 | op.add_option("--fcplun", dest="fcplun", required=1) | ||
92 | op.add_option("--scsiid", dest="scsiid", required=1) | ||
93 | op.add_option("--scsilun", dest="scsilun", required=1) | ||
94 | op.add_option("--wwpn", dest="wwpn", required=1) | ||
95 | return op | ||
96 | |||
97 | def parse(self, args): | ||
98 | zd = self.handler.ZFCPData() | ||
99 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
100 | self._setToObj(self.op, opts, zd) | ||
101 | zd.lineno = self.lineno | ||
102 | |||
103 | # Check for duplicates in the data list. | ||
104 | if zd in self.dataList(): | ||
105 | warnings.warn(_("A zfcp with this information has already been defined.")) | ||
106 | |||
107 | return zd | ||
108 | |||
109 | def dataList(self): | ||
110 | return self.zfcp | ||
111 | |||
112 | class F12_ZFCP(FC3_ZFCP): | ||
113 | removedKeywords = FC3_ZFCP.removedKeywords | ||
114 | removedAttrs = FC3_ZFCP.removedAttrs + ["scsiid", "scsilun"] | ||
115 | |||
116 | def __init__(self, *args, **kwargs): | ||
117 | FC3_ZFCP.__init__(self, *args, **kwargs) | ||
118 | self.deleteRemovedAttrs() | ||
119 | |||
120 | def _getParser(self): | ||
121 | op = FC3_ZFCP._getParser(self) | ||
122 | op.add_option("--scsiid", deprecated=1) | ||
123 | op.add_option("--scsilun", deprecated=1) | ||
124 | return op | ||
125 | |||
126 | class F14_ZFCP(F12_ZFCP): | ||
127 | removedKeywords = F12_ZFCP.removedKeywords | ||
128 | removedAttrs = F12_ZFCP.removedAttrs | ||
129 | |||
130 | def _getParser(self): | ||
131 | op = F12_ZFCP._getParser(self) | ||
132 | op.remove_option("--scsiid") | ||
133 | op.remove_option("--scsilun") | ||
134 | return op | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/constants.py b/scripts/lib/mic/3rdparty/pykickstart/constants.py new file mode 100644 index 0000000000..5e12fc80ec --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/constants.py | |||
@@ -0,0 +1,57 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005-2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | CLEARPART_TYPE_LINUX = 0 | ||
21 | CLEARPART_TYPE_ALL = 1 | ||
22 | CLEARPART_TYPE_NONE = 2 | ||
23 | |||
24 | DISPLAY_MODE_CMDLINE = 0 | ||
25 | DISPLAY_MODE_GRAPHICAL = 1 | ||
26 | DISPLAY_MODE_TEXT = 2 | ||
27 | |||
28 | FIRSTBOOT_DEFAULT = 0 | ||
29 | FIRSTBOOT_SKIP = 1 | ||
30 | FIRSTBOOT_RECONFIG = 2 | ||
31 | |||
32 | KS_MISSING_PROMPT = 0 | ||
33 | KS_MISSING_IGNORE = 1 | ||
34 | |||
35 | SELINUX_DISABLED = 0 | ||
36 | SELINUX_ENFORCING = 1 | ||
37 | SELINUX_PERMISSIVE = 2 | ||
38 | |||
39 | KS_SCRIPT_PRE = 0 | ||
40 | KS_SCRIPT_POST = 1 | ||
41 | KS_SCRIPT_TRACEBACK = 2 | ||
42 | |||
43 | KS_WAIT = 0 | ||
44 | KS_REBOOT = 1 | ||
45 | KS_SHUTDOWN = 2 | ||
46 | |||
47 | KS_INSTKEY_SKIP = -99 | ||
48 | |||
49 | BOOTPROTO_DHCP = "dhcp" | ||
50 | BOOTPROTO_BOOTP = "bootp" | ||
51 | BOOTPROTO_STATIC = "static" | ||
52 | BOOTPROTO_QUERY = "query" | ||
53 | BOOTPROTO_IBFT = "ibft" | ||
54 | |||
55 | GROUP_REQUIRED = 0 | ||
56 | GROUP_DEFAULT = 1 | ||
57 | GROUP_ALL = 2 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/errors.py b/scripts/lib/mic/3rdparty/pykickstart/errors.py new file mode 100644 index 0000000000..a234d99d43 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/errors.py | |||
@@ -0,0 +1,103 @@ | |||
1 | # | ||
2 | # errors.py: Kickstart error handling. | ||
3 | # | ||
4 | # Chris Lumens <clumens@redhat.com> | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | """ | ||
21 | Error handling classes and functions. | ||
22 | |||
23 | This module exports a single function: | ||
24 | |||
25 | formatErrorMsg - Properly formats an error message. | ||
26 | |||
27 | It also exports several exception classes: | ||
28 | |||
29 | KickstartError - A generic exception class. | ||
30 | |||
31 | KickstartParseError - An exception for errors relating to parsing. | ||
32 | |||
33 | KickstartValueError - An exception for errors relating to option | ||
34 | processing. | ||
35 | |||
36 | KickstartVersionError - An exception for errors relating to unsupported | ||
37 | syntax versions. | ||
38 | """ | ||
39 | import gettext | ||
40 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
41 | |||
42 | def formatErrorMsg(lineno, msg=""): | ||
43 | """Properly format the error message msg for inclusion in an exception.""" | ||
44 | if msg != "": | ||
45 | mapping = {"lineno": lineno, "msg": msg} | ||
46 | return _("The following problem occurred on line %(lineno)s of the kickstart file:\n\n%(msg)s\n") % mapping | ||
47 | else: | ||
48 | return _("There was a problem reading from line %s of the kickstart file") % lineno | ||
49 | |||
50 | class KickstartError(Exception): | ||
51 | """A generic exception class for unspecific error conditions.""" | ||
52 | def __init__(self, val = ""): | ||
53 | """Create a new KickstartError exception instance with the descriptive | ||
54 | message val. val should be the return value of formatErrorMsg. | ||
55 | """ | ||
56 | Exception.__init__(self) | ||
57 | self.value = val | ||
58 | |||
59 | def __str__ (self): | ||
60 | return self.value | ||
61 | |||
62 | class KickstartParseError(KickstartError): | ||
63 | """An exception class for errors when processing the input file, such as | ||
64 | unknown options, commands, or sections. | ||
65 | """ | ||
66 | def __init__(self, msg): | ||
67 | """Create a new KickstartParseError exception instance with the | ||
68 | descriptive message val. val should be the return value of | ||
69 | formatErrorMsg. | ||
70 | """ | ||
71 | KickstartError.__init__(self, msg) | ||
72 | |||
73 | def __str__(self): | ||
74 | return self.value | ||
75 | |||
76 | class KickstartValueError(KickstartError): | ||
77 | """An exception class for errors when processing arguments to commands, | ||
78 | such as too many arguments, too few arguments, or missing required | ||
79 | arguments. | ||
80 | """ | ||
81 | def __init__(self, msg): | ||
82 | """Create a new KickstartValueError exception instance with the | ||
83 | descriptive message val. val should be the return value of | ||
84 | formatErrorMsg. | ||
85 | """ | ||
86 | KickstartError.__init__(self, msg) | ||
87 | |||
88 | def __str__ (self): | ||
89 | return self.value | ||
90 | |||
91 | class KickstartVersionError(KickstartError): | ||
92 | """An exception class for errors related to using an incorrect version of | ||
93 | kickstart syntax. | ||
94 | """ | ||
95 | def __init__(self, msg): | ||
96 | """Create a new KickstartVersionError exception instance with the | ||
97 | descriptive message val. val should be the return value of | ||
98 | formatErrorMsg. | ||
99 | """ | ||
100 | KickstartError.__init__(self, msg) | ||
101 | |||
102 | def __str__ (self): | ||
103 | return self.value | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/__init__.py | |||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/control.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/control.py new file mode 100644 index 0000000000..d8c8f2b899 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/control.py | |||
@@ -0,0 +1,1307 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007, 2008, 2009, 2010 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.version import * | ||
21 | from pykickstart.commands import * | ||
22 | |||
23 | # This map is keyed on kickstart syntax version as provided by | ||
24 | # pykickstart.version. Within each sub-dict is a mapping from command name | ||
25 | # to the class that handles it. This is an onto mapping - that is, multiple | ||
26 | # command names can map to the same class. However, the Handler will ensure | ||
27 | # that only one instance of each class ever exists. | ||
28 | commandMap = { | ||
29 | FC3: { | ||
30 | "auth": authconfig.FC3_Authconfig, | ||
31 | "authconfig": authconfig.FC3_Authconfig, | ||
32 | "autopart": autopart.FC3_AutoPart, | ||
33 | "autostep": autostep.FC3_AutoStep, | ||
34 | "bootloader": bootloader.FC3_Bootloader, | ||
35 | "cdrom": method.FC3_Method, | ||
36 | "clearpart": clearpart.FC3_ClearPart, | ||
37 | "cmdline": displaymode.FC3_DisplayMode, | ||
38 | "device": device.FC3_Device, | ||
39 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
40 | "driverdisk": driverdisk.FC3_DriverDisk, | ||
41 | "firewall": firewall.FC3_Firewall, | ||
42 | "firstboot": firstboot.FC3_Firstboot, | ||
43 | "graphical": displaymode.FC3_DisplayMode, | ||
44 | "halt": reboot.FC3_Reboot, | ||
45 | "harddrive": method.FC3_Method, | ||
46 | "ignoredisk": ignoredisk.FC3_IgnoreDisk, | ||
47 | "install": upgrade.FC3_Upgrade, | ||
48 | "interactive": interactive.FC3_Interactive, | ||
49 | "keyboard": keyboard.FC3_Keyboard, | ||
50 | "lang": lang.FC3_Lang, | ||
51 | "langsupport": langsupport.FC3_LangSupport, | ||
52 | "lilo": bootloader.FC3_Bootloader, | ||
53 | "lilocheck": lilocheck.FC3_LiloCheck, | ||
54 | "logvol": logvol.FC3_LogVol, | ||
55 | "monitor": monitor.FC3_Monitor, | ||
56 | "mouse": mouse.FC3_Mouse, | ||
57 | "network": network.FC3_Network, | ||
58 | "nfs": method.FC3_Method, | ||
59 | "part": partition.FC3_Partition, | ||
60 | "partition": partition.FC3_Partition, | ||
61 | "poweroff": reboot.FC3_Reboot, | ||
62 | "raid": raid.FC3_Raid, | ||
63 | "reboot": reboot.FC3_Reboot, | ||
64 | "rootpw": rootpw.FC3_RootPw, | ||
65 | "selinux": selinux.FC3_SELinux, | ||
66 | "shutdown": reboot.FC3_Reboot, | ||
67 | "skipx": skipx.FC3_SkipX, | ||
68 | "text": displaymode.FC3_DisplayMode, | ||
69 | "timezone": timezone.FC3_Timezone, | ||
70 | "upgrade": upgrade.FC3_Upgrade, | ||
71 | "url": method.FC3_Method, | ||
72 | "vnc": vnc.FC3_Vnc, | ||
73 | "volgroup": volgroup.FC3_VolGroup, | ||
74 | "xconfig": xconfig.FC3_XConfig, | ||
75 | "zerombr": zerombr.FC3_ZeroMbr, | ||
76 | "zfcp": zfcp.FC3_ZFCP, | ||
77 | }, | ||
78 | |||
79 | # based on fc3 | ||
80 | FC4: { | ||
81 | "auth": authconfig.FC3_Authconfig, | ||
82 | "authconfig": authconfig.FC3_Authconfig, | ||
83 | "autopart": autopart.FC3_AutoPart, | ||
84 | "autostep": autostep.FC3_AutoStep, | ||
85 | "bootloader": bootloader.FC4_Bootloader, | ||
86 | "cdrom": method.FC3_Method, | ||
87 | "clearpart": clearpart.FC3_ClearPart, | ||
88 | "cmdline": displaymode.FC3_DisplayMode, | ||
89 | "device": device.FC3_Device, | ||
90 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
91 | "driverdisk": driverdisk.FC4_DriverDisk, | ||
92 | "firewall": firewall.FC3_Firewall, | ||
93 | "firstboot": firstboot.FC3_Firstboot, | ||
94 | "graphical": displaymode.FC3_DisplayMode, | ||
95 | "halt": reboot.FC3_Reboot, | ||
96 | "harddrive": method.FC3_Method, | ||
97 | "ignoredisk": ignoredisk.FC3_IgnoreDisk, | ||
98 | "install": upgrade.FC3_Upgrade, | ||
99 | "interactive": interactive.FC3_Interactive, | ||
100 | "keyboard": keyboard.FC3_Keyboard, | ||
101 | "lang": lang.FC3_Lang, | ||
102 | "langsupport": langsupport.FC3_LangSupport, | ||
103 | "logvol": logvol.FC4_LogVol, | ||
104 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
105 | "monitor": monitor.FC3_Monitor, | ||
106 | "mouse": mouse.FC3_Mouse, | ||
107 | "network": network.FC4_Network, | ||
108 | "nfs": method.FC3_Method, | ||
109 | "part": partition.FC4_Partition, | ||
110 | "partition": partition.FC4_Partition, | ||
111 | "poweroff": reboot.FC3_Reboot, | ||
112 | "raid": raid.FC4_Raid, | ||
113 | "reboot": reboot.FC3_Reboot, | ||
114 | "rootpw": rootpw.FC3_RootPw, | ||
115 | "selinux": selinux.FC3_SELinux, | ||
116 | "shutdown": reboot.FC3_Reboot, | ||
117 | "skipx": skipx.FC3_SkipX, | ||
118 | "text": displaymode.FC3_DisplayMode, | ||
119 | "timezone": timezone.FC3_Timezone, | ||
120 | "upgrade": upgrade.FC3_Upgrade, | ||
121 | "url": method.FC3_Method, | ||
122 | "vnc": vnc.FC3_Vnc, | ||
123 | "volgroup": volgroup.FC3_VolGroup, | ||
124 | "xconfig": xconfig.FC3_XConfig, | ||
125 | "zerombr": zerombr.FC3_ZeroMbr, | ||
126 | "zfcp": zfcp.FC3_ZFCP, | ||
127 | }, | ||
128 | |||
129 | # based on fc4 | ||
130 | FC5: { | ||
131 | "auth": authconfig.FC3_Authconfig, | ||
132 | "authconfig": authconfig.FC3_Authconfig, | ||
133 | "autopart": autopart.FC3_AutoPart, | ||
134 | "autostep": autostep.FC3_AutoStep, | ||
135 | "bootloader": bootloader.FC4_Bootloader, | ||
136 | "cdrom": method.FC3_Method, | ||
137 | "clearpart": clearpart.FC3_ClearPart, | ||
138 | "cmdline": displaymode.FC3_DisplayMode, | ||
139 | "device": device.FC3_Device, | ||
140 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
141 | "driverdisk": driverdisk.FC4_DriverDisk, | ||
142 | "firewall": firewall.FC3_Firewall, | ||
143 | "firstboot": firstboot.FC3_Firstboot, | ||
144 | "graphical": displaymode.FC3_DisplayMode, | ||
145 | "halt": reboot.FC3_Reboot, | ||
146 | "harddrive": method.FC3_Method, | ||
147 | "ignoredisk": ignoredisk.FC3_IgnoreDisk, | ||
148 | "install": upgrade.FC3_Upgrade, | ||
149 | "interactive": interactive.FC3_Interactive, | ||
150 | "keyboard": keyboard.FC3_Keyboard, | ||
151 | "lang": lang.FC3_Lang, | ||
152 | "langsupport": langsupport.FC5_LangSupport, | ||
153 | "logvol": logvol.FC4_LogVol, | ||
154 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
155 | "monitor": monitor.FC3_Monitor, | ||
156 | "mouse": mouse.FC3_Mouse, | ||
157 | "network": network.FC4_Network, | ||
158 | "nfs": method.FC3_Method, | ||
159 | "part": partition.FC4_Partition, | ||
160 | "partition": partition.FC4_Partition, | ||
161 | "poweroff": reboot.FC3_Reboot, | ||
162 | "raid": raid.FC5_Raid, | ||
163 | "reboot": reboot.FC3_Reboot, | ||
164 | "rootpw": rootpw.FC3_RootPw, | ||
165 | "selinux": selinux.FC3_SELinux, | ||
166 | "shutdown": reboot.FC3_Reboot, | ||
167 | "skipx": skipx.FC3_SkipX, | ||
168 | "text": displaymode.FC3_DisplayMode, | ||
169 | "timezone": timezone.FC3_Timezone, | ||
170 | "upgrade": upgrade.FC3_Upgrade, | ||
171 | "url": method.FC3_Method, | ||
172 | "vnc": vnc.FC3_Vnc, | ||
173 | "volgroup": volgroup.FC3_VolGroup, | ||
174 | "xconfig": xconfig.FC3_XConfig, | ||
175 | "zerombr": zerombr.FC3_ZeroMbr, | ||
176 | "zfcp": zfcp.FC3_ZFCP, | ||
177 | }, | ||
178 | |||
179 | # based on fc5 | ||
180 | FC6: { | ||
181 | "auth": authconfig.FC3_Authconfig, | ||
182 | "authconfig": authconfig.FC3_Authconfig, | ||
183 | "autopart": autopart.FC3_AutoPart, | ||
184 | "autostep": autostep.FC3_AutoStep, | ||
185 | "bootloader": bootloader.FC4_Bootloader, | ||
186 | "cdrom": method.FC6_Method, | ||
187 | "clearpart": clearpart.FC3_ClearPart, | ||
188 | "cmdline": displaymode.FC3_DisplayMode, | ||
189 | "device": device.FC3_Device, | ||
190 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
191 | "dmraid": dmraid.FC6_DmRaid, | ||
192 | "driverdisk": driverdisk.FC4_DriverDisk, | ||
193 | "firewall": firewall.FC3_Firewall, | ||
194 | "firstboot": firstboot.FC3_Firstboot, | ||
195 | "graphical": displaymode.FC3_DisplayMode, | ||
196 | "halt": reboot.FC6_Reboot, | ||
197 | "harddrive": method.FC6_Method, | ||
198 | "ignoredisk": ignoredisk.FC3_IgnoreDisk, | ||
199 | "install": upgrade.FC3_Upgrade, | ||
200 | "interactive": interactive.FC3_Interactive, | ||
201 | "iscsi": iscsi.FC6_Iscsi, | ||
202 | "iscsiname": iscsiname.FC6_IscsiName, | ||
203 | "keyboard": keyboard.FC3_Keyboard, | ||
204 | "lang": lang.FC3_Lang, | ||
205 | "langsupport": langsupport.FC5_LangSupport, | ||
206 | "logging": logging.FC6_Logging, | ||
207 | "logvol": logvol.FC4_LogVol, | ||
208 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
209 | "monitor": monitor.FC6_Monitor, | ||
210 | "mouse": mouse.FC3_Mouse, | ||
211 | "multipath": multipath.FC6_MultiPath, | ||
212 | "network": network.FC6_Network, | ||
213 | "nfs": method.FC6_Method, | ||
214 | "part": partition.FC4_Partition, | ||
215 | "partition": partition.FC4_Partition, | ||
216 | "poweroff": reboot.FC6_Reboot, | ||
217 | "raid": raid.FC5_Raid, | ||
218 | "reboot": reboot.FC6_Reboot, | ||
219 | "repo": repo.FC6_Repo, | ||
220 | "rootpw": rootpw.FC3_RootPw, | ||
221 | "selinux": selinux.FC3_SELinux, | ||
222 | "services": services.FC6_Services, | ||
223 | "shutdown": reboot.FC6_Reboot, | ||
224 | "skipx": skipx.FC3_SkipX, | ||
225 | "text": displaymode.FC3_DisplayMode, | ||
226 | "timezone": timezone.FC6_Timezone, | ||
227 | "upgrade": upgrade.FC3_Upgrade, | ||
228 | "user": user.FC6_User, | ||
229 | "url": method.FC6_Method, | ||
230 | "vnc": vnc.FC6_Vnc, | ||
231 | "volgroup": volgroup.FC3_VolGroup, | ||
232 | "xconfig": xconfig.FC6_XConfig, | ||
233 | "zerombr": zerombr.FC3_ZeroMbr, | ||
234 | "zfcp": zfcp.FC3_ZFCP, | ||
235 | }, | ||
236 | |||
237 | # based on fc6 | ||
238 | F7: { | ||
239 | "auth": authconfig.FC3_Authconfig, | ||
240 | "authconfig": authconfig.FC3_Authconfig, | ||
241 | "autopart": autopart.FC3_AutoPart, | ||
242 | "autostep": autostep.FC3_AutoStep, | ||
243 | "bootloader": bootloader.FC4_Bootloader, | ||
244 | "cdrom": method.FC6_Method, | ||
245 | "clearpart": clearpart.FC3_ClearPart, | ||
246 | "cmdline": displaymode.FC3_DisplayMode, | ||
247 | "device": device.FC3_Device, | ||
248 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
249 | "dmraid": dmraid.FC6_DmRaid, | ||
250 | "driverdisk": driverdisk.FC4_DriverDisk, | ||
251 | "firewall": firewall.FC3_Firewall, | ||
252 | "firstboot": firstboot.FC3_Firstboot, | ||
253 | "graphical": displaymode.FC3_DisplayMode, | ||
254 | "halt": reboot.FC6_Reboot, | ||
255 | "harddrive": method.FC6_Method, | ||
256 | "ignoredisk": ignoredisk.FC3_IgnoreDisk, | ||
257 | "install": upgrade.FC3_Upgrade, | ||
258 | "interactive": interactive.FC3_Interactive, | ||
259 | "iscsi": iscsi.FC6_Iscsi, | ||
260 | "iscsiname": iscsiname.FC6_IscsiName, | ||
261 | "keyboard": keyboard.FC3_Keyboard, | ||
262 | "lang": lang.FC3_Lang, | ||
263 | "logging": logging.FC6_Logging, | ||
264 | "logvol": logvol.FC4_LogVol, | ||
265 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
266 | "monitor": monitor.FC6_Monitor, | ||
267 | "multipath": multipath.FC6_MultiPath, | ||
268 | "network": network.FC6_Network, | ||
269 | "nfs": method.FC6_Method, | ||
270 | "part": partition.FC4_Partition, | ||
271 | "partition": partition.FC4_Partition, | ||
272 | "poweroff": reboot.FC6_Reboot, | ||
273 | "raid": raid.F7_Raid, | ||
274 | "reboot": reboot.FC6_Reboot, | ||
275 | "repo": repo.FC6_Repo, | ||
276 | "rootpw": rootpw.FC3_RootPw, | ||
277 | "selinux": selinux.FC3_SELinux, | ||
278 | "services": services.FC6_Services, | ||
279 | "shutdown": reboot.FC6_Reboot, | ||
280 | "skipx": skipx.FC3_SkipX, | ||
281 | "text": displaymode.FC3_DisplayMode, | ||
282 | "timezone": timezone.FC6_Timezone, | ||
283 | "updates": updates.F7_Updates, | ||
284 | "upgrade": upgrade.FC3_Upgrade, | ||
285 | "url": method.FC6_Method, | ||
286 | "user": user.FC6_User, | ||
287 | "vnc": vnc.FC6_Vnc, | ||
288 | "volgroup": volgroup.FC3_VolGroup, | ||
289 | "xconfig": xconfig.FC6_XConfig, | ||
290 | "zerombr": zerombr.FC3_ZeroMbr, | ||
291 | "zfcp": zfcp.FC3_ZFCP, | ||
292 | }, | ||
293 | |||
294 | # based on f7 | ||
295 | F8: { | ||
296 | "auth": authconfig.FC3_Authconfig, | ||
297 | "authconfig": authconfig.FC3_Authconfig, | ||
298 | "autopart": autopart.FC3_AutoPart, | ||
299 | "autostep": autostep.FC3_AutoStep, | ||
300 | "bootloader": bootloader.F8_Bootloader, | ||
301 | "cdrom": method.FC6_Method, | ||
302 | "clearpart": clearpart.FC3_ClearPart, | ||
303 | "cmdline": displaymode.FC3_DisplayMode, | ||
304 | "device": device.F8_Device, | ||
305 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
306 | "dmraid": dmraid.FC6_DmRaid, | ||
307 | "driverdisk": driverdisk.FC4_DriverDisk, | ||
308 | "firewall": firewall.FC3_Firewall, | ||
309 | "firstboot": firstboot.FC3_Firstboot, | ||
310 | "graphical": displaymode.FC3_DisplayMode, | ||
311 | "halt": reboot.FC6_Reboot, | ||
312 | "harddrive": method.FC6_Method, | ||
313 | "ignoredisk": ignoredisk.F8_IgnoreDisk, | ||
314 | "install": upgrade.FC3_Upgrade, | ||
315 | "interactive": interactive.FC3_Interactive, | ||
316 | "iscsi": iscsi.FC6_Iscsi, | ||
317 | "iscsiname": iscsiname.FC6_IscsiName, | ||
318 | "keyboard": keyboard.FC3_Keyboard, | ||
319 | "lang": lang.FC3_Lang, | ||
320 | "logging": logging.FC6_Logging, | ||
321 | "logvol": logvol.FC4_LogVol, | ||
322 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
323 | "monitor": monitor.FC6_Monitor, | ||
324 | "multipath": multipath.FC6_MultiPath, | ||
325 | "network": network.F8_Network, | ||
326 | "nfs": method.FC6_Method, | ||
327 | "part": partition.FC4_Partition, | ||
328 | "partition": partition.FC4_Partition, | ||
329 | "poweroff": reboot.FC6_Reboot, | ||
330 | "raid": raid.F7_Raid, | ||
331 | "reboot": reboot.FC6_Reboot, | ||
332 | "repo": repo.F8_Repo, | ||
333 | "rootpw": rootpw.F8_RootPw, | ||
334 | "selinux": selinux.FC3_SELinux, | ||
335 | "services": services.FC6_Services, | ||
336 | "shutdown": reboot.FC6_Reboot, | ||
337 | "skipx": skipx.FC3_SkipX, | ||
338 | "text": displaymode.FC3_DisplayMode, | ||
339 | "timezone": timezone.FC6_Timezone, | ||
340 | "updates": updates.F7_Updates, | ||
341 | "upgrade": upgrade.FC3_Upgrade, | ||
342 | "url": method.FC6_Method, | ||
343 | "user": user.F8_User, | ||
344 | "vnc": vnc.FC6_Vnc, | ||
345 | "volgroup": volgroup.FC3_VolGroup, | ||
346 | "xconfig": xconfig.FC6_XConfig, | ||
347 | "zerombr": zerombr.FC3_ZeroMbr, | ||
348 | "zfcp": zfcp.FC3_ZFCP, | ||
349 | }, | ||
350 | |||
351 | # based on f8 | ||
352 | F9: { | ||
353 | "auth": authconfig.FC3_Authconfig, | ||
354 | "authconfig": authconfig.FC3_Authconfig, | ||
355 | "autopart": autopart.F9_AutoPart, | ||
356 | "autostep": autostep.FC3_AutoStep, | ||
357 | "bootloader": bootloader.F8_Bootloader, | ||
358 | "cdrom": method.FC6_Method, | ||
359 | "clearpart": clearpart.FC3_ClearPart, | ||
360 | "cmdline": displaymode.FC3_DisplayMode, | ||
361 | "device": device.F8_Device, | ||
362 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
363 | "dmraid": dmraid.FC6_DmRaid, | ||
364 | "driverdisk": driverdisk.FC4_DriverDisk, | ||
365 | "firewall": firewall.F9_Firewall, | ||
366 | "firstboot": firstboot.FC3_Firstboot, | ||
367 | "graphical": displaymode.FC3_DisplayMode, | ||
368 | "halt": reboot.FC6_Reboot, | ||
369 | "harddrive": method.FC6_Method, | ||
370 | "ignoredisk": ignoredisk.F8_IgnoreDisk, | ||
371 | "install": upgrade.FC3_Upgrade, | ||
372 | "interactive": interactive.FC3_Interactive, | ||
373 | "iscsi": iscsi.FC6_Iscsi, | ||
374 | "iscsiname": iscsiname.FC6_IscsiName, | ||
375 | "keyboard": keyboard.FC3_Keyboard, | ||
376 | "lang": lang.FC3_Lang, | ||
377 | "logging": logging.FC6_Logging, | ||
378 | "logvol": logvol.F9_LogVol, | ||
379 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
380 | "monitor": monitor.FC6_Monitor, | ||
381 | "multipath": multipath.FC6_MultiPath, | ||
382 | "network": network.F9_Network, | ||
383 | "nfs": method.FC6_Method, | ||
384 | "part": partition.F9_Partition, | ||
385 | "partition": partition.F9_Partition, | ||
386 | "poweroff": reboot.FC6_Reboot, | ||
387 | "raid": raid.F9_Raid, | ||
388 | "reboot": reboot.FC6_Reboot, | ||
389 | "repo": repo.F8_Repo, | ||
390 | "rootpw": rootpw.F8_RootPw, | ||
391 | "selinux": selinux.FC3_SELinux, | ||
392 | "services": services.FC6_Services, | ||
393 | "shutdown": reboot.FC6_Reboot, | ||
394 | "skipx": skipx.FC3_SkipX, | ||
395 | "text": displaymode.FC3_DisplayMode, | ||
396 | "timezone": timezone.FC6_Timezone, | ||
397 | "updates": updates.F7_Updates, | ||
398 | "upgrade": upgrade.FC3_Upgrade, | ||
399 | "url": method.FC6_Method, | ||
400 | "user": user.F8_User, | ||
401 | "vnc": vnc.F9_Vnc, | ||
402 | "volgroup": volgroup.FC3_VolGroup, | ||
403 | "xconfig": xconfig.F9_XConfig, | ||
404 | "zerombr": zerombr.F9_ZeroMbr, | ||
405 | "zfcp": zfcp.FC3_ZFCP, | ||
406 | }, | ||
407 | |||
408 | # based on f9 | ||
409 | F10: { | ||
410 | "auth": authconfig.FC3_Authconfig, | ||
411 | "authconfig": authconfig.FC3_Authconfig, | ||
412 | "autopart": autopart.F9_AutoPart, | ||
413 | "autostep": autostep.FC3_AutoStep, | ||
414 | "bootloader": bootloader.F8_Bootloader, | ||
415 | "cdrom": method.FC6_Method, | ||
416 | "clearpart": clearpart.FC3_ClearPart, | ||
417 | "cmdline": displaymode.FC3_DisplayMode, | ||
418 | "device": device.F8_Device, | ||
419 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
420 | "dmraid": dmraid.FC6_DmRaid, | ||
421 | "driverdisk": driverdisk.FC4_DriverDisk, | ||
422 | "firewall": firewall.F10_Firewall, | ||
423 | "firstboot": firstboot.FC3_Firstboot, | ||
424 | "graphical": displaymode.FC3_DisplayMode, | ||
425 | "halt": reboot.FC6_Reboot, | ||
426 | "harddrive": method.FC6_Method, | ||
427 | "ignoredisk": ignoredisk.F8_IgnoreDisk, | ||
428 | "install": upgrade.FC3_Upgrade, | ||
429 | "interactive": interactive.FC3_Interactive, | ||
430 | "iscsi": iscsi.F10_Iscsi, | ||
431 | "iscsiname": iscsiname.FC6_IscsiName, | ||
432 | "keyboard": keyboard.FC3_Keyboard, | ||
433 | "lang": lang.FC3_Lang, | ||
434 | "logging": logging.FC6_Logging, | ||
435 | "logvol": logvol.F9_LogVol, | ||
436 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
437 | "monitor": monitor.F10_Monitor, | ||
438 | "multipath": multipath.FC6_MultiPath, | ||
439 | "network": network.F9_Network, | ||
440 | "nfs": method.FC6_Method, | ||
441 | "part": partition.F9_Partition, | ||
442 | "partition": partition.F9_Partition, | ||
443 | "poweroff": reboot.FC6_Reboot, | ||
444 | "raid": raid.F9_Raid, | ||
445 | "reboot": reboot.FC6_Reboot, | ||
446 | "repo": repo.F8_Repo, | ||
447 | "rescue": rescue.F10_Rescue, | ||
448 | "rootpw": rootpw.F8_RootPw, | ||
449 | "selinux": selinux.FC3_SELinux, | ||
450 | "services": services.FC6_Services, | ||
451 | "shutdown": reboot.FC6_Reboot, | ||
452 | "skipx": skipx.FC3_SkipX, | ||
453 | "text": displaymode.FC3_DisplayMode, | ||
454 | "timezone": timezone.FC6_Timezone, | ||
455 | "updates": updates.F7_Updates, | ||
456 | "upgrade": upgrade.FC3_Upgrade, | ||
457 | "url": method.FC6_Method, | ||
458 | "user": user.F8_User, | ||
459 | "vnc": vnc.F9_Vnc, | ||
460 | "volgroup": volgroup.FC3_VolGroup, | ||
461 | "xconfig": xconfig.F10_XConfig, | ||
462 | "zerombr": zerombr.F9_ZeroMbr, | ||
463 | "zfcp": zfcp.FC3_ZFCP, | ||
464 | }, | ||
465 | |||
466 | # based on f10 | ||
467 | F11: { | ||
468 | "auth": authconfig.FC3_Authconfig, | ||
469 | "authconfig": authconfig.FC3_Authconfig, | ||
470 | "autopart": autopart.F9_AutoPart, | ||
471 | "autostep": autostep.FC3_AutoStep, | ||
472 | "bootloader": bootloader.F8_Bootloader, | ||
473 | "cdrom": method.FC6_Method, | ||
474 | "clearpart": clearpart.FC3_ClearPart, | ||
475 | "cmdline": displaymode.FC3_DisplayMode, | ||
476 | "device": device.F8_Device, | ||
477 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
478 | "dmraid": dmraid.FC6_DmRaid, | ||
479 | "driverdisk": driverdisk.FC4_DriverDisk, | ||
480 | "firewall": firewall.F10_Firewall, | ||
481 | "firstboot": firstboot.FC3_Firstboot, | ||
482 | "graphical": displaymode.FC3_DisplayMode, | ||
483 | "halt": reboot.FC6_Reboot, | ||
484 | "harddrive": method.FC6_Method, | ||
485 | "ignoredisk": ignoredisk.F8_IgnoreDisk, | ||
486 | "install": upgrade.F11_Upgrade, | ||
487 | "interactive": interactive.FC3_Interactive, | ||
488 | "iscsi": iscsi.F10_Iscsi, | ||
489 | "iscsiname": iscsiname.FC6_IscsiName, | ||
490 | "keyboard": keyboard.FC3_Keyboard, | ||
491 | "lang": lang.FC3_Lang, | ||
492 | "logging": logging.FC6_Logging, | ||
493 | "logvol": logvol.F9_LogVol, | ||
494 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
495 | "monitor": monitor.F10_Monitor, | ||
496 | "multipath": multipath.FC6_MultiPath, | ||
497 | "network": network.F9_Network, | ||
498 | "nfs": method.FC6_Method, | ||
499 | "part": partition.F11_Partition, | ||
500 | "partition": partition.F11_Partition, | ||
501 | "poweroff": reboot.FC6_Reboot, | ||
502 | "raid": raid.F9_Raid, | ||
503 | "reboot": reboot.FC6_Reboot, | ||
504 | "repo": repo.F11_Repo, | ||
505 | "rescue": rescue.F10_Rescue, | ||
506 | "rootpw": rootpw.F8_RootPw, | ||
507 | "selinux": selinux.FC3_SELinux, | ||
508 | "services": services.FC6_Services, | ||
509 | "shutdown": reboot.FC6_Reboot, | ||
510 | "skipx": skipx.FC3_SkipX, | ||
511 | "text": displaymode.FC3_DisplayMode, | ||
512 | "timezone": timezone.FC6_Timezone, | ||
513 | "updates": updates.F7_Updates, | ||
514 | "upgrade": upgrade.F11_Upgrade, | ||
515 | "url": method.FC6_Method, | ||
516 | "user": user.F8_User, | ||
517 | "vnc": vnc.F9_Vnc, | ||
518 | "volgroup": volgroup.FC3_VolGroup, | ||
519 | "xconfig": xconfig.F10_XConfig, | ||
520 | "zerombr": zerombr.F9_ZeroMbr, | ||
521 | "zfcp": zfcp.FC3_ZFCP, | ||
522 | }, | ||
523 | |||
524 | # based on f11 | ||
525 | F12: { | ||
526 | "auth": authconfig.FC3_Authconfig, | ||
527 | "authconfig": authconfig.FC3_Authconfig, | ||
528 | "autopart": autopart.F12_AutoPart, | ||
529 | "autostep": autostep.FC3_AutoStep, | ||
530 | "bootloader": bootloader.F12_Bootloader, | ||
531 | "cdrom": method.FC6_Method, | ||
532 | "clearpart": clearpart.FC3_ClearPart, | ||
533 | "cmdline": displaymode.FC3_DisplayMode, | ||
534 | "device": device.F8_Device, | ||
535 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
536 | "dmraid": dmraid.FC6_DmRaid, | ||
537 | "driverdisk": driverdisk.F12_DriverDisk, | ||
538 | "fcoe": fcoe.F12_Fcoe, | ||
539 | "firewall": firewall.F10_Firewall, | ||
540 | "firstboot": firstboot.FC3_Firstboot, | ||
541 | "graphical": displaymode.FC3_DisplayMode, | ||
542 | "group": group.F12_Group, | ||
543 | "halt": reboot.FC6_Reboot, | ||
544 | "harddrive": method.FC6_Method, | ||
545 | "ignoredisk": ignoredisk.F8_IgnoreDisk, | ||
546 | "install": upgrade.F11_Upgrade, | ||
547 | "interactive": interactive.FC3_Interactive, | ||
548 | "iscsi": iscsi.F10_Iscsi, | ||
549 | "iscsiname": iscsiname.FC6_IscsiName, | ||
550 | "keyboard": keyboard.FC3_Keyboard, | ||
551 | "lang": lang.FC3_Lang, | ||
552 | "logging": logging.FC6_Logging, | ||
553 | "logvol": logvol.F12_LogVol, | ||
554 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
555 | "monitor": monitor.F10_Monitor, | ||
556 | "multipath": multipath.FC6_MultiPath, | ||
557 | "network": network.F9_Network, | ||
558 | "nfs": method.FC6_Method, | ||
559 | "part": partition.F12_Partition, | ||
560 | "partition": partition.F12_Partition, | ||
561 | "poweroff": reboot.FC6_Reboot, | ||
562 | "raid": raid.F12_Raid, | ||
563 | "reboot": reboot.FC6_Reboot, | ||
564 | "repo": repo.F11_Repo, | ||
565 | "rescue": rescue.F10_Rescue, | ||
566 | "rootpw": rootpw.F8_RootPw, | ||
567 | "selinux": selinux.FC3_SELinux, | ||
568 | "services": services.FC6_Services, | ||
569 | "shutdown": reboot.FC6_Reboot, | ||
570 | "skipx": skipx.FC3_SkipX, | ||
571 | "text": displaymode.FC3_DisplayMode, | ||
572 | "timezone": timezone.FC6_Timezone, | ||
573 | "updates": updates.F7_Updates, | ||
574 | "upgrade": upgrade.F11_Upgrade, | ||
575 | "url": method.FC6_Method, | ||
576 | "user": user.F12_User, | ||
577 | "vnc": vnc.F9_Vnc, | ||
578 | "volgroup": volgroup.FC3_VolGroup, | ||
579 | "xconfig": xconfig.F10_XConfig, | ||
580 | "zerombr": zerombr.F9_ZeroMbr, | ||
581 | "zfcp": zfcp.F12_ZFCP, | ||
582 | }, | ||
583 | |||
584 | # based on f12 | ||
585 | F13: { | ||
586 | "auth": authconfig.FC3_Authconfig, | ||
587 | "authconfig": authconfig.FC3_Authconfig, | ||
588 | "autopart": autopart.F12_AutoPart, | ||
589 | "autostep": autostep.FC3_AutoStep, | ||
590 | "bootloader": bootloader.F12_Bootloader, | ||
591 | "cdrom": method.F13_Method, | ||
592 | "clearpart": clearpart.FC3_ClearPart, | ||
593 | "cmdline": displaymode.FC3_DisplayMode, | ||
594 | "device": device.F8_Device, | ||
595 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
596 | "dmraid": dmraid.FC6_DmRaid, | ||
597 | "driverdisk": driverdisk.F12_DriverDisk, | ||
598 | "fcoe": fcoe.F13_Fcoe, | ||
599 | "firewall": firewall.F10_Firewall, | ||
600 | "firstboot": firstboot.FC3_Firstboot, | ||
601 | "graphical": displaymode.FC3_DisplayMode, | ||
602 | "group": group.F12_Group, | ||
603 | "halt": reboot.FC6_Reboot, | ||
604 | "harddrive": method.F13_Method, | ||
605 | "ignoredisk": ignoredisk.F8_IgnoreDisk, | ||
606 | "install": upgrade.F11_Upgrade, | ||
607 | "interactive": interactive.FC3_Interactive, | ||
608 | "iscsi": iscsi.F10_Iscsi, | ||
609 | "iscsiname": iscsiname.FC6_IscsiName, | ||
610 | "keyboard": keyboard.FC3_Keyboard, | ||
611 | "lang": lang.FC3_Lang, | ||
612 | "logging": logging.FC6_Logging, | ||
613 | "logvol": logvol.F12_LogVol, | ||
614 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
615 | "monitor": monitor.F10_Monitor, | ||
616 | "multipath": multipath.FC6_MultiPath, | ||
617 | "network": network.F9_Network, | ||
618 | "nfs": method.F13_Method, | ||
619 | "part": partition.F12_Partition, | ||
620 | "partition": partition.F12_Partition, | ||
621 | "poweroff": reboot.FC6_Reboot, | ||
622 | "raid": raid.F13_Raid, | ||
623 | "reboot": reboot.FC6_Reboot, | ||
624 | "repo": repo.F13_Repo, | ||
625 | "rescue": rescue.F10_Rescue, | ||
626 | "rootpw": rootpw.F8_RootPw, | ||
627 | "selinux": selinux.FC3_SELinux, | ||
628 | "services": services.FC6_Services, | ||
629 | "shutdown": reboot.FC6_Reboot, | ||
630 | "skipx": skipx.FC3_SkipX, | ||
631 | "sshpw": sshpw.F13_SshPw, | ||
632 | "text": displaymode.FC3_DisplayMode, | ||
633 | "timezone": timezone.FC6_Timezone, | ||
634 | "updates": updates.F7_Updates, | ||
635 | "upgrade": upgrade.F11_Upgrade, | ||
636 | "url": method.F13_Method, | ||
637 | "user": user.F12_User, | ||
638 | "vnc": vnc.F9_Vnc, | ||
639 | "volgroup": volgroup.FC3_VolGroup, | ||
640 | "xconfig": xconfig.F10_XConfig, | ||
641 | "zerombr": zerombr.F9_ZeroMbr, | ||
642 | "zfcp": zfcp.F12_ZFCP, | ||
643 | }, | ||
644 | |||
645 | # based on f13 | ||
646 | F14: { | ||
647 | "auth": authconfig.FC3_Authconfig, | ||
648 | "authconfig": authconfig.FC3_Authconfig, | ||
649 | "autopart": autopart.F12_AutoPart, | ||
650 | "autostep": autostep.FC3_AutoStep, | ||
651 | "bootloader": bootloader.F14_Bootloader, | ||
652 | "cdrom": method.F14_Method, | ||
653 | "clearpart": clearpart.FC3_ClearPart, | ||
654 | "cmdline": displaymode.FC3_DisplayMode, | ||
655 | "device": device.F8_Device, | ||
656 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
657 | "dmraid": dmraid.FC6_DmRaid, | ||
658 | "driverdisk": driverdisk.F14_DriverDisk, | ||
659 | "fcoe": fcoe.F13_Fcoe, | ||
660 | "firewall": firewall.F14_Firewall, | ||
661 | "firstboot": firstboot.FC3_Firstboot, | ||
662 | "graphical": displaymode.FC3_DisplayMode, | ||
663 | "group": group.F12_Group, | ||
664 | "halt": reboot.FC6_Reboot, | ||
665 | "harddrive": method.F14_Method, | ||
666 | "ignoredisk": ignoredisk.F14_IgnoreDisk, | ||
667 | "install": upgrade.F11_Upgrade, | ||
668 | "interactive": interactive.F14_Interactive, | ||
669 | "iscsi": iscsi.F10_Iscsi, | ||
670 | "iscsiname": iscsiname.FC6_IscsiName, | ||
671 | "keyboard": keyboard.FC3_Keyboard, | ||
672 | "lang": lang.FC3_Lang, | ||
673 | "logging": logging.FC6_Logging, | ||
674 | "logvol": logvol.F14_LogVol, | ||
675 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
676 | "monitor": monitor.F10_Monitor, | ||
677 | "multipath": multipath.FC6_MultiPath, | ||
678 | "network": network.F9_Network, | ||
679 | "nfs": method.F14_Method, | ||
680 | "part": partition.F14_Partition, | ||
681 | "partition": partition.F14_Partition, | ||
682 | "poweroff": reboot.FC6_Reboot, | ||
683 | "raid": raid.F14_Raid, | ||
684 | "reboot": reboot.FC6_Reboot, | ||
685 | "repo": repo.F14_Repo, | ||
686 | "rescue": rescue.F10_Rescue, | ||
687 | "rootpw": rootpw.F8_RootPw, | ||
688 | "selinux": selinux.FC3_SELinux, | ||
689 | "services": services.FC6_Services, | ||
690 | "shutdown": reboot.FC6_Reboot, | ||
691 | "skipx": skipx.FC3_SkipX, | ||
692 | "sshpw": sshpw.F13_SshPw, | ||
693 | "text": displaymode.FC3_DisplayMode, | ||
694 | "timezone": timezone.FC6_Timezone, | ||
695 | "updates": updates.F7_Updates, | ||
696 | "upgrade": upgrade.F11_Upgrade, | ||
697 | "url": method.F14_Method, | ||
698 | "user": user.F12_User, | ||
699 | "vnc": vnc.F9_Vnc, | ||
700 | "volgroup": volgroup.FC3_VolGroup, | ||
701 | "xconfig": xconfig.F14_XConfig, | ||
702 | "zerombr": zerombr.F9_ZeroMbr, | ||
703 | "zfcp": zfcp.F14_ZFCP, | ||
704 | }, | ||
705 | |||
706 | # based on f14 | ||
707 | F15: { | ||
708 | "auth": authconfig.FC3_Authconfig, | ||
709 | "authconfig": authconfig.FC3_Authconfig, | ||
710 | "autopart": autopart.F12_AutoPart, | ||
711 | "autostep": autostep.FC3_AutoStep, | ||
712 | "bootloader": bootloader.F15_Bootloader, | ||
713 | "cdrom": method.F14_Method, | ||
714 | "clearpart": clearpart.FC3_ClearPart, | ||
715 | "cmdline": displaymode.FC3_DisplayMode, | ||
716 | "device": device.F8_Device, | ||
717 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
718 | "dmraid": dmraid.FC6_DmRaid, | ||
719 | "driverdisk": driverdisk.F14_DriverDisk, | ||
720 | "fcoe": fcoe.F13_Fcoe, | ||
721 | "firewall": firewall.F14_Firewall, | ||
722 | "firstboot": firstboot.FC3_Firstboot, | ||
723 | "graphical": displaymode.FC3_DisplayMode, | ||
724 | "group": group.F12_Group, | ||
725 | "halt": reboot.FC6_Reboot, | ||
726 | "harddrive": method.F14_Method, | ||
727 | "ignoredisk": ignoredisk.F14_IgnoreDisk, | ||
728 | "install": upgrade.F11_Upgrade, | ||
729 | "iscsi": iscsi.F10_Iscsi, | ||
730 | "iscsiname": iscsiname.FC6_IscsiName, | ||
731 | "keyboard": keyboard.FC3_Keyboard, | ||
732 | "lang": lang.FC3_Lang, | ||
733 | "logging": logging.FC6_Logging, | ||
734 | "logvol": logvol.F15_LogVol, | ||
735 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
736 | "monitor": monitor.F10_Monitor, | ||
737 | "multipath": multipath.FC6_MultiPath, | ||
738 | "network": network.F9_Network, | ||
739 | "nfs": method.F14_Method, | ||
740 | "part": partition.F14_Partition, | ||
741 | "partition": partition.F14_Partition, | ||
742 | "poweroff": reboot.FC6_Reboot, | ||
743 | "raid": raid.F15_Raid, | ||
744 | "reboot": reboot.FC6_Reboot, | ||
745 | "repo": repo.F15_Repo, | ||
746 | "rescue": rescue.F10_Rescue, | ||
747 | "rootpw": rootpw.F8_RootPw, | ||
748 | "selinux": selinux.FC3_SELinux, | ||
749 | "services": services.FC6_Services, | ||
750 | "shutdown": reboot.FC6_Reboot, | ||
751 | "skipx": skipx.FC3_SkipX, | ||
752 | "sshpw": sshpw.F13_SshPw, | ||
753 | "text": displaymode.FC3_DisplayMode, | ||
754 | "timezone": timezone.FC6_Timezone, | ||
755 | "updates": updates.F7_Updates, | ||
756 | "upgrade": upgrade.F11_Upgrade, | ||
757 | "url": method.F14_Method, | ||
758 | "user": user.F12_User, | ||
759 | "vnc": vnc.F9_Vnc, | ||
760 | "volgroup": volgroup.FC3_VolGroup, | ||
761 | "xconfig": xconfig.F14_XConfig, | ||
762 | "zerombr": zerombr.F9_ZeroMbr, | ||
763 | "zfcp": zfcp.F14_ZFCP, | ||
764 | }, | ||
765 | |||
766 | # based on f15 | ||
767 | F16: { | ||
768 | "auth": authconfig.FC3_Authconfig, | ||
769 | "authconfig": authconfig.FC3_Authconfig, | ||
770 | "autopart": autopart.F12_AutoPart, | ||
771 | "autostep": autostep.FC3_AutoStep, | ||
772 | "bootloader": bootloader.F15_Bootloader, | ||
773 | "cdrom": method.F14_Method, | ||
774 | "clearpart": clearpart.FC3_ClearPart, | ||
775 | "cmdline": displaymode.FC3_DisplayMode, | ||
776 | "device": device.F8_Device, | ||
777 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
778 | "dmraid": dmraid.FC6_DmRaid, | ||
779 | "driverdisk": driverdisk.F14_DriverDisk, | ||
780 | "fcoe": fcoe.F13_Fcoe, | ||
781 | "firewall": firewall.F14_Firewall, | ||
782 | "firstboot": firstboot.FC3_Firstboot, | ||
783 | "graphical": displaymode.FC3_DisplayMode, | ||
784 | "group": group.F12_Group, | ||
785 | "halt": reboot.FC6_Reboot, | ||
786 | "harddrive": method.F14_Method, | ||
787 | "ignoredisk": ignoredisk.F14_IgnoreDisk, | ||
788 | "install": upgrade.F11_Upgrade, | ||
789 | "iscsi": iscsi.F10_Iscsi, | ||
790 | "iscsiname": iscsiname.FC6_IscsiName, | ||
791 | "keyboard": keyboard.FC3_Keyboard, | ||
792 | "lang": lang.FC3_Lang, | ||
793 | "logging": logging.FC6_Logging, | ||
794 | "logvol": logvol.F15_LogVol, | ||
795 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
796 | "monitor": monitor.F10_Monitor, | ||
797 | "multipath": multipath.FC6_MultiPath, | ||
798 | "network": network.F16_Network, | ||
799 | "nfs": method.F14_Method, | ||
800 | "part": partition.F14_Partition, | ||
801 | "partition": partition.F14_Partition, | ||
802 | "poweroff": reboot.FC6_Reboot, | ||
803 | "raid": raid.F15_Raid, | ||
804 | "reboot": reboot.FC6_Reboot, | ||
805 | "repo": repo.F15_Repo, | ||
806 | "rescue": rescue.F10_Rescue, | ||
807 | "rootpw": rootpw.F8_RootPw, | ||
808 | "selinux": selinux.FC3_SELinux, | ||
809 | "services": services.FC6_Services, | ||
810 | "shutdown": reboot.FC6_Reboot, | ||
811 | "skipx": skipx.FC3_SkipX, | ||
812 | "sshpw": sshpw.F13_SshPw, | ||
813 | "text": displaymode.FC3_DisplayMode, | ||
814 | "timezone": timezone.FC6_Timezone, | ||
815 | "updates": updates.F7_Updates, | ||
816 | "upgrade": upgrade.F11_Upgrade, | ||
817 | "url": method.F14_Method, | ||
818 | "user": user.F12_User, | ||
819 | "vnc": vnc.F9_Vnc, | ||
820 | "volgroup": volgroup.FC3_VolGroup, | ||
821 | "xconfig": xconfig.F14_XConfig, | ||
822 | "zerombr": zerombr.F9_ZeroMbr, | ||
823 | "zfcp": zfcp.F14_ZFCP, | ||
824 | }, | ||
825 | |||
826 | # based on fc1 | ||
827 | RHEL3: { | ||
828 | "auth": authconfig.FC3_Authconfig, | ||
829 | "authconfig": authconfig.FC3_Authconfig, | ||
830 | "autopart": autopart.FC3_AutoPart, | ||
831 | "autostep": autostep.FC3_AutoStep, | ||
832 | "bootloader": bootloader.FC3_Bootloader, | ||
833 | "cdrom": method.FC3_Method, | ||
834 | "clearpart": clearpart.FC3_ClearPart, | ||
835 | "cmdline": displaymode.FC3_DisplayMode, | ||
836 | "device": device.FC3_Device, | ||
837 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
838 | "driverdisk": driverdisk.FC3_DriverDisk, | ||
839 | "firewall": firewall.FC3_Firewall, | ||
840 | "firstboot": firstboot.FC3_Firstboot, | ||
841 | "graphical": displaymode.FC3_DisplayMode, | ||
842 | "halt": reboot.FC3_Reboot, | ||
843 | "harddrive": method.FC3_Method, | ||
844 | "ignoredisk": ignoredisk.FC3_IgnoreDisk, | ||
845 | "install": upgrade.FC3_Upgrade, | ||
846 | "interactive": interactive.FC3_Interactive, | ||
847 | "keyboard": keyboard.FC3_Keyboard, | ||
848 | "lang": lang.FC3_Lang, | ||
849 | "langsupport": langsupport.FC3_LangSupport, | ||
850 | "lilo": bootloader.FC3_Bootloader, | ||
851 | "lilocheck": lilocheck.FC3_LiloCheck, | ||
852 | "logvol": logvol.FC3_LogVol, | ||
853 | "monitor": monitor.FC3_Monitor, | ||
854 | "mouse": mouse.RHEL3_Mouse, | ||
855 | "network": network.FC3_Network, | ||
856 | "nfs": method.FC3_Method, | ||
857 | "part": partition.FC3_Partition, | ||
858 | "partition": partition.FC3_Partition, | ||
859 | "poweroff": reboot.FC3_Reboot, | ||
860 | "raid": raid.FC3_Raid, | ||
861 | "reboot": reboot.FC3_Reboot, | ||
862 | "rootpw": rootpw.FC3_RootPw, | ||
863 | "shutdown": reboot.FC3_Reboot, | ||
864 | "skipx": skipx.FC3_SkipX, | ||
865 | "text": displaymode.FC3_DisplayMode, | ||
866 | "timezone": timezone.FC3_Timezone, | ||
867 | "upgrade": upgrade.FC3_Upgrade, | ||
868 | "url": method.FC3_Method, | ||
869 | "vnc": vnc.FC3_Vnc, | ||
870 | "volgroup": volgroup.FC3_VolGroup, | ||
871 | "xconfig": xconfig.FC3_XConfig, | ||
872 | "zerombr": zerombr.FC3_ZeroMbr, | ||
873 | }, | ||
874 | |||
875 | # based on fc3 | ||
876 | RHEL4: { | ||
877 | "auth": authconfig.FC3_Authconfig, | ||
878 | "authconfig": authconfig.FC3_Authconfig, | ||
879 | "autopart": autopart.FC3_AutoPart, | ||
880 | "autostep": autostep.FC3_AutoStep, | ||
881 | "bootloader": bootloader.FC3_Bootloader, | ||
882 | "cdrom": method.FC3_Method, | ||
883 | "clearpart": clearpart.FC3_ClearPart, | ||
884 | "cmdline": displaymode.FC3_DisplayMode, | ||
885 | "device": device.FC3_Device, | ||
886 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
887 | "driverdisk": driverdisk.FC4_DriverDisk, | ||
888 | "firewall": firewall.FC3_Firewall, | ||
889 | "firstboot": firstboot.FC3_Firstboot, | ||
890 | "graphical": displaymode.FC3_DisplayMode, | ||
891 | "halt": reboot.FC3_Reboot, | ||
892 | "harddrive": method.FC3_Method, | ||
893 | "ignoredisk": ignoredisk.F8_IgnoreDisk, | ||
894 | "install": upgrade.FC3_Upgrade, | ||
895 | "interactive": interactive.FC3_Interactive, | ||
896 | "keyboard": keyboard.FC3_Keyboard, | ||
897 | "lang": lang.FC3_Lang, | ||
898 | "langsupport": langsupport.FC3_LangSupport, | ||
899 | "lilo": bootloader.FC3_Bootloader, | ||
900 | "lilocheck": lilocheck.FC3_LiloCheck, | ||
901 | "logvol": logvol.FC3_LogVol, | ||
902 | "monitor": monitor.FC3_Monitor, | ||
903 | "mouse": mouse.FC3_Mouse, | ||
904 | "network": network.RHEL4_Network, | ||
905 | "nfs": method.FC3_Method, | ||
906 | "part": partition.FC3_Partition, | ||
907 | "partition": partition.FC3_Partition, | ||
908 | "poweroff": reboot.FC3_Reboot, | ||
909 | "raid": raid.FC3_Raid, | ||
910 | "reboot": reboot.FC3_Reboot, | ||
911 | "rootpw": rootpw.FC3_RootPw, | ||
912 | "selinux": selinux.FC3_SELinux, | ||
913 | "shutdown": reboot.FC3_Reboot, | ||
914 | "skipx": skipx.FC3_SkipX, | ||
915 | "text": displaymode.FC3_DisplayMode, | ||
916 | "timezone": timezone.FC3_Timezone, | ||
917 | "upgrade": upgrade.FC3_Upgrade, | ||
918 | "url": method.FC3_Method, | ||
919 | "vnc": vnc.FC3_Vnc, | ||
920 | "volgroup": volgroup.FC3_VolGroup, | ||
921 | "xconfig": xconfig.FC3_XConfig, | ||
922 | "zerombr": zerombr.FC3_ZeroMbr, | ||
923 | "zfcp": zfcp.FC3_ZFCP, | ||
924 | }, | ||
925 | |||
926 | # based on fc6 | ||
927 | RHEL5: { | ||
928 | "auth": authconfig.FC3_Authconfig, | ||
929 | "authconfig": authconfig.FC3_Authconfig, | ||
930 | "autopart": autopart.F9_AutoPart, | ||
931 | "autostep": autostep.FC3_AutoStep, | ||
932 | "bootloader": bootloader.RHEL5_Bootloader, | ||
933 | "cdrom": method.FC6_Method, | ||
934 | "clearpart": clearpart.FC3_ClearPart, | ||
935 | "cmdline": displaymode.FC3_DisplayMode, | ||
936 | "device": device.FC3_Device, | ||
937 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
938 | "dmraid": dmraid.FC6_DmRaid, | ||
939 | "driverdisk": driverdisk.F12_DriverDisk, | ||
940 | "firewall": firewall.FC3_Firewall, | ||
941 | "firstboot": firstboot.FC3_Firstboot, | ||
942 | "graphical": displaymode.FC3_DisplayMode, | ||
943 | "halt": reboot.FC6_Reboot, | ||
944 | "harddrive": method.FC6_Method, | ||
945 | "ignoredisk": ignoredisk.F8_IgnoreDisk, | ||
946 | "install": upgrade.FC3_Upgrade, | ||
947 | "interactive": interactive.FC3_Interactive, | ||
948 | "iscsi": iscsi.FC6_Iscsi, | ||
949 | "iscsiname": iscsiname.FC6_IscsiName, | ||
950 | "key": key.RHEL5_Key, | ||
951 | "keyboard": keyboard.FC3_Keyboard, | ||
952 | "lang": lang.FC3_Lang, | ||
953 | "langsupport": langsupport.FC5_LangSupport, | ||
954 | "logging": logging.FC6_Logging, | ||
955 | "logvol": logvol.RHEL5_LogVol, | ||
956 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
957 | "monitor": monitor.FC6_Monitor, | ||
958 | "mouse": mouse.FC3_Mouse, | ||
959 | "multipath": multipath.FC6_MultiPath, | ||
960 | "network": network.RHEL5_Network, | ||
961 | "nfs": method.FC6_Method, | ||
962 | "part": partition.RHEL5_Partition, | ||
963 | "partition": partition.RHEL5_Partition, | ||
964 | "poweroff": reboot.FC6_Reboot, | ||
965 | "raid": raid.RHEL5_Raid, | ||
966 | "reboot": reboot.FC6_Reboot, | ||
967 | "repo": repo.FC6_Repo, | ||
968 | "rootpw": rootpw.FC3_RootPw, | ||
969 | "services": services.FC6_Services, | ||
970 | "selinux": selinux.FC3_SELinux, | ||
971 | "shutdown": reboot.FC6_Reboot, | ||
972 | "skipx": skipx.FC3_SkipX, | ||
973 | "text": displaymode.FC3_DisplayMode, | ||
974 | "timezone": timezone.FC6_Timezone, | ||
975 | "upgrade": upgrade.FC3_Upgrade, | ||
976 | "user": user.FC6_User, | ||
977 | "url": method.FC6_Method, | ||
978 | "vnc": vnc.FC6_Vnc, | ||
979 | "volgroup": volgroup.FC3_VolGroup, | ||
980 | "xconfig": xconfig.FC6_XConfig, | ||
981 | "zerombr": zerombr.FC3_ZeroMbr, | ||
982 | "zfcp": zfcp.FC3_ZFCP, | ||
983 | }, | ||
984 | |||
985 | # based on f13ish | ||
986 | RHEL6: { | ||
987 | "auth": authconfig.FC3_Authconfig, | ||
988 | "authconfig": authconfig.FC3_Authconfig, | ||
989 | "autopart": autopart.F12_AutoPart, | ||
990 | "autostep": autostep.FC3_AutoStep, | ||
991 | "bootloader": bootloader.RHEL6_Bootloader, | ||
992 | "cdrom": method.RHEL6_Method, | ||
993 | "clearpart": clearpart.FC3_ClearPart, | ||
994 | "cmdline": displaymode.FC3_DisplayMode, | ||
995 | "device": device.F8_Device, | ||
996 | "deviceprobe": deviceprobe.FC3_DeviceProbe, | ||
997 | "dmraid": dmraid.FC6_DmRaid, | ||
998 | "driverdisk": driverdisk.F12_DriverDisk, | ||
999 | "fcoe": fcoe.F13_Fcoe, | ||
1000 | "firewall": firewall.F10_Firewall, | ||
1001 | "firstboot": firstboot.FC3_Firstboot, | ||
1002 | "graphical": displaymode.FC3_DisplayMode, | ||
1003 | "group": group.F12_Group, | ||
1004 | "halt": reboot.FC6_Reboot, | ||
1005 | "harddrive": method.RHEL6_Method, | ||
1006 | "ignoredisk": ignoredisk.RHEL6_IgnoreDisk, | ||
1007 | "install": upgrade.F11_Upgrade, | ||
1008 | "interactive": interactive.FC3_Interactive, | ||
1009 | "iscsi": iscsi.F10_Iscsi, | ||
1010 | "iscsiname": iscsiname.FC6_IscsiName, | ||
1011 | "keyboard": keyboard.FC3_Keyboard, | ||
1012 | "lang": lang.FC3_Lang, | ||
1013 | "logging": logging.FC6_Logging, | ||
1014 | "logvol": logvol.F12_LogVol, | ||
1015 | "mediacheck": mediacheck.FC4_MediaCheck, | ||
1016 | "monitor": monitor.F10_Monitor, | ||
1017 | "multipath": multipath.FC6_MultiPath, | ||
1018 | "network": network.RHEL6_Network, | ||
1019 | "nfs": method.RHEL6_Method, | ||
1020 | "part": partition.F12_Partition, | ||
1021 | "partition": partition.F12_Partition, | ||
1022 | "poweroff": reboot.FC6_Reboot, | ||
1023 | "raid": raid.F13_Raid, | ||
1024 | "reboot": reboot.FC6_Reboot, | ||
1025 | "repo": repo.RHEL6_Repo, | ||
1026 | "rescue": rescue.F10_Rescue, | ||
1027 | "rootpw": rootpw.F8_RootPw, | ||
1028 | "selinux": selinux.FC3_SELinux, | ||
1029 | "services": services.FC6_Services, | ||
1030 | "shutdown": reboot.FC6_Reboot, | ||
1031 | "skipx": skipx.FC3_SkipX, | ||
1032 | "sshpw": sshpw.F13_SshPw, | ||
1033 | "text": displaymode.FC3_DisplayMode, | ||
1034 | "timezone": timezone.FC6_Timezone, | ||
1035 | "updates": updates.F7_Updates, | ||
1036 | "upgrade": upgrade.F11_Upgrade, | ||
1037 | "url": method.RHEL6_Method, | ||
1038 | "user": user.F12_User, | ||
1039 | "vnc": vnc.F9_Vnc, | ||
1040 | "volgroup": volgroup.FC3_VolGroup, | ||
1041 | "xconfig": xconfig.F10_XConfig, | ||
1042 | "zerombr": zerombr.F9_ZeroMbr, | ||
1043 | "zfcp": zfcp.F12_ZFCP, | ||
1044 | } | ||
1045 | } | ||
1046 | |||
1047 | # This map is keyed on kickstart syntax version as provided by | ||
1048 | # pykickstart.version. Within each sub-dict is a mapping from a data object | ||
1049 | # name to the class that provides it. This is a bijective mapping - that is, | ||
1050 | # each name maps to exactly one data class and all data classes have a name. | ||
1051 | # More than one instance of each class is allowed to exist, however. | ||
1052 | dataMap = { | ||
1053 | FC3: { | ||
1054 | "DriverDiskData": driverdisk.FC3_DriverDiskData, | ||
1055 | "LogVolData": logvol.FC3_LogVolData, | ||
1056 | "NetworkData": network.FC3_NetworkData, | ||
1057 | "PartData": partition.FC3_PartData, | ||
1058 | "RaidData": raid.FC3_RaidData, | ||
1059 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1060 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1061 | }, | ||
1062 | FC4: { | ||
1063 | "DriverDiskData": driverdisk.FC4_DriverDiskData, | ||
1064 | "LogVolData": logvol.FC4_LogVolData, | ||
1065 | "NetworkData": network.FC4_NetworkData, | ||
1066 | "PartData": partition.FC4_PartData, | ||
1067 | "RaidData": raid.FC4_RaidData, | ||
1068 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1069 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1070 | }, | ||
1071 | FC5: { | ||
1072 | "DriverDiskData": driverdisk.FC4_DriverDiskData, | ||
1073 | "LogVolData": logvol.FC4_LogVolData, | ||
1074 | "NetworkData": network.FC4_NetworkData, | ||
1075 | "PartData": partition.FC4_PartData, | ||
1076 | "RaidData": raid.FC5_RaidData, | ||
1077 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1078 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1079 | }, | ||
1080 | FC6: { | ||
1081 | "DriverDiskData": driverdisk.FC4_DriverDiskData, | ||
1082 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1083 | "IscsiData": iscsi.FC6_IscsiData, | ||
1084 | "LogVolData": logvol.FC4_LogVolData, | ||
1085 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1086 | "NetworkData": network.FC6_NetworkData, | ||
1087 | "PartData": partition.FC4_PartData, | ||
1088 | "RaidData": raid.FC5_RaidData, | ||
1089 | "RepoData": repo.FC6_RepoData, | ||
1090 | "UserData": user.FC6_UserData, | ||
1091 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1092 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1093 | }, | ||
1094 | F7: { | ||
1095 | "DriverDiskData": driverdisk.FC4_DriverDiskData, | ||
1096 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1097 | "IscsiData": iscsi.FC6_IscsiData, | ||
1098 | "LogVolData": logvol.FC4_LogVolData, | ||
1099 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1100 | "NetworkData": network.FC6_NetworkData, | ||
1101 | "PartData": partition.FC4_PartData, | ||
1102 | "RaidData": raid.F7_RaidData, | ||
1103 | "RepoData": repo.FC6_RepoData, | ||
1104 | "UserData": user.FC6_UserData, | ||
1105 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1106 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1107 | }, | ||
1108 | F8: { | ||
1109 | "DriverDiskData": driverdisk.FC4_DriverDiskData, | ||
1110 | "DeviceData": device.F8_DeviceData, | ||
1111 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1112 | "IscsiData": iscsi.FC6_IscsiData, | ||
1113 | "LogVolData": logvol.FC4_LogVolData, | ||
1114 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1115 | "NetworkData": network.F8_NetworkData, | ||
1116 | "PartData": partition.FC4_PartData, | ||
1117 | "RaidData": raid.F7_RaidData, | ||
1118 | "RepoData": repo.F8_RepoData, | ||
1119 | "UserData": user.F8_UserData, | ||
1120 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1121 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1122 | }, | ||
1123 | F9: { | ||
1124 | "DriverDiskData": driverdisk.FC4_DriverDiskData, | ||
1125 | "DeviceData": device.F8_DeviceData, | ||
1126 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1127 | "IscsiData": iscsi.FC6_IscsiData, | ||
1128 | "LogVolData": logvol.F9_LogVolData, | ||
1129 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1130 | "NetworkData": network.F8_NetworkData, | ||
1131 | "PartData": partition.F9_PartData, | ||
1132 | "RaidData": raid.F9_RaidData, | ||
1133 | "RepoData": repo.F8_RepoData, | ||
1134 | "UserData": user.F8_UserData, | ||
1135 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1136 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1137 | }, | ||
1138 | F10: { | ||
1139 | "DriverDiskData": driverdisk.FC4_DriverDiskData, | ||
1140 | "DeviceData": device.F8_DeviceData, | ||
1141 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1142 | "IscsiData": iscsi.F10_IscsiData, | ||
1143 | "LogVolData": logvol.F9_LogVolData, | ||
1144 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1145 | "NetworkData": network.F8_NetworkData, | ||
1146 | "PartData": partition.F9_PartData, | ||
1147 | "RaidData": raid.F9_RaidData, | ||
1148 | "RepoData": repo.F8_RepoData, | ||
1149 | "UserData": user.F8_UserData, | ||
1150 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1151 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1152 | }, | ||
1153 | F11: { | ||
1154 | "DriverDiskData": driverdisk.FC4_DriverDiskData, | ||
1155 | "DeviceData": device.F8_DeviceData, | ||
1156 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1157 | "IscsiData": iscsi.F10_IscsiData, | ||
1158 | "LogVolData": logvol.F9_LogVolData, | ||
1159 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1160 | "NetworkData": network.F8_NetworkData, | ||
1161 | "PartData": partition.F11_PartData, | ||
1162 | "RaidData": raid.F9_RaidData, | ||
1163 | "RepoData": repo.F11_RepoData, | ||
1164 | "UserData": user.F8_UserData, | ||
1165 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1166 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1167 | }, | ||
1168 | F12: { | ||
1169 | "DriverDiskData": driverdisk.F12_DriverDiskData, | ||
1170 | "DeviceData": device.F8_DeviceData, | ||
1171 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1172 | "FcoeData": fcoe.F12_FcoeData, | ||
1173 | "GroupData": group.F12_GroupData, | ||
1174 | "IscsiData": iscsi.F10_IscsiData, | ||
1175 | "LogVolData": logvol.F12_LogVolData, | ||
1176 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1177 | "NetworkData": network.F8_NetworkData, | ||
1178 | "PartData": partition.F12_PartData, | ||
1179 | "RaidData": raid.F12_RaidData, | ||
1180 | "RepoData": repo.F11_RepoData, | ||
1181 | "UserData": user.F12_UserData, | ||
1182 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1183 | "ZFCPData": zfcp.F12_ZFCPData, | ||
1184 | }, | ||
1185 | F13: { | ||
1186 | "DriverDiskData": driverdisk.F12_DriverDiskData, | ||
1187 | "DeviceData": device.F8_DeviceData, | ||
1188 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1189 | "FcoeData": fcoe.F13_FcoeData, | ||
1190 | "GroupData": group.F12_GroupData, | ||
1191 | "IscsiData": iscsi.F10_IscsiData, | ||
1192 | "LogVolData": logvol.F12_LogVolData, | ||
1193 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1194 | "NetworkData": network.F8_NetworkData, | ||
1195 | "PartData": partition.F12_PartData, | ||
1196 | "RaidData": raid.F13_RaidData, | ||
1197 | "RepoData": repo.F13_RepoData, | ||
1198 | "SshPwData": sshpw.F13_SshPwData, | ||
1199 | "UserData": user.F12_UserData, | ||
1200 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1201 | "ZFCPData": zfcp.F12_ZFCPData, | ||
1202 | }, | ||
1203 | F14: { | ||
1204 | "DriverDiskData": driverdisk.F14_DriverDiskData, | ||
1205 | "DeviceData": device.F8_DeviceData, | ||
1206 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1207 | "FcoeData": fcoe.F13_FcoeData, | ||
1208 | "GroupData": group.F12_GroupData, | ||
1209 | "IscsiData": iscsi.F10_IscsiData, | ||
1210 | "LogVolData": logvol.F14_LogVolData, | ||
1211 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1212 | "NetworkData": network.F8_NetworkData, | ||
1213 | "PartData": partition.F14_PartData, | ||
1214 | "RaidData": raid.F14_RaidData, | ||
1215 | "RepoData": repo.F14_RepoData, | ||
1216 | "SshPwData": sshpw.F13_SshPwData, | ||
1217 | "UserData": user.F12_UserData, | ||
1218 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1219 | "ZFCPData": zfcp.F14_ZFCPData, | ||
1220 | }, | ||
1221 | F15: { | ||
1222 | "DriverDiskData": driverdisk.F14_DriverDiskData, | ||
1223 | "DeviceData": device.F8_DeviceData, | ||
1224 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1225 | "FcoeData": fcoe.F13_FcoeData, | ||
1226 | "GroupData": group.F12_GroupData, | ||
1227 | "IscsiData": iscsi.F10_IscsiData, | ||
1228 | "LogVolData": logvol.F15_LogVolData, | ||
1229 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1230 | "NetworkData": network.F8_NetworkData, | ||
1231 | "PartData": partition.F14_PartData, | ||
1232 | "RaidData": raid.F15_RaidData, | ||
1233 | "RepoData": repo.F15_RepoData, | ||
1234 | "SshPwData": sshpw.F13_SshPwData, | ||
1235 | "UserData": user.F12_UserData, | ||
1236 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1237 | "ZFCPData": zfcp.F14_ZFCPData, | ||
1238 | }, | ||
1239 | F16: { | ||
1240 | "DriverDiskData": driverdisk.F14_DriverDiskData, | ||
1241 | "DeviceData": device.F8_DeviceData, | ||
1242 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1243 | "FcoeData": fcoe.F13_FcoeData, | ||
1244 | "GroupData": group.F12_GroupData, | ||
1245 | "IscsiData": iscsi.F10_IscsiData, | ||
1246 | "LogVolData": logvol.F15_LogVolData, | ||
1247 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1248 | "NetworkData": network.F16_NetworkData, | ||
1249 | "PartData": partition.F14_PartData, | ||
1250 | "RaidData": raid.F15_RaidData, | ||
1251 | "RepoData": repo.F15_RepoData, | ||
1252 | "SshPwData": sshpw.F13_SshPwData, | ||
1253 | "UserData": user.F12_UserData, | ||
1254 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1255 | "ZFCPData": zfcp.F14_ZFCPData, | ||
1256 | }, | ||
1257 | RHEL3: { | ||
1258 | "DriverDiskData": driverdisk.FC3_DriverDiskData, | ||
1259 | "LogVolData": logvol.FC3_LogVolData, | ||
1260 | "NetworkData": network.RHEL4_NetworkData, | ||
1261 | "PartData": partition.FC3_PartData, | ||
1262 | "RaidData": raid.FC3_RaidData, | ||
1263 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1264 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1265 | }, | ||
1266 | RHEL4: { | ||
1267 | "DriverDiskData": driverdisk.FC4_DriverDiskData, | ||
1268 | "LogVolData": logvol.FC3_LogVolData, | ||
1269 | "NetworkData": network.RHEL4_NetworkData, | ||
1270 | "PartData": partition.FC3_PartData, | ||
1271 | "RaidData": raid.FC3_RaidData, | ||
1272 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1273 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1274 | }, | ||
1275 | RHEL5: { | ||
1276 | "DriverDiskData": driverdisk.F12_DriverDiskData, | ||
1277 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1278 | "IscsiData": iscsi.FC6_IscsiData, | ||
1279 | "LogVolData": logvol.RHEL5_LogVolData, | ||
1280 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1281 | "NetworkData": network.FC6_NetworkData, | ||
1282 | "PartData": partition.RHEL5_PartData, | ||
1283 | "RaidData": raid.RHEL5_RaidData, | ||
1284 | "RepoData": repo.FC6_RepoData, | ||
1285 | "UserData": user.FC6_UserData, | ||
1286 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1287 | "ZFCPData": zfcp.FC3_ZFCPData, | ||
1288 | }, | ||
1289 | RHEL6: { | ||
1290 | "DriverDiskData": driverdisk.F12_DriverDiskData, | ||
1291 | "DeviceData": device.F8_DeviceData, | ||
1292 | "DmRaidData": dmraid.FC6_DmRaidData, | ||
1293 | "FcoeData": fcoe.F13_FcoeData, | ||
1294 | "GroupData": group.F12_GroupData, | ||
1295 | "IscsiData": iscsi.F10_IscsiData, | ||
1296 | "LogVolData": logvol.F12_LogVolData, | ||
1297 | "MultiPathData": multipath.FC6_MultiPathData, | ||
1298 | "NetworkData": network.RHEL6_NetworkData, | ||
1299 | "PartData": partition.F12_PartData, | ||
1300 | "RaidData": raid.F13_RaidData, | ||
1301 | "RepoData": repo.RHEL6_RepoData, | ||
1302 | "SshPwData": sshpw.F13_SshPwData, | ||
1303 | "UserData": user.F12_UserData, | ||
1304 | "VolGroupData": volgroup.FC3_VolGroupData, | ||
1305 | "ZFCPData": zfcp.F12_ZFCPData, | ||
1306 | } | ||
1307 | } | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f10.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f10.py new file mode 100644 index 0000000000..17c8211bbf --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f10.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2008 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class F10Handler(BaseHandler): | ||
24 | version = F10 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f11.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f11.py new file mode 100644 index 0000000000..d21aee3e8b --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f11.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2008 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class F11Handler(BaseHandler): | ||
24 | version = F11 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f12.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f12.py new file mode 100644 index 0000000000..cea3ecef6b --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f12.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2009 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class F12Handler(BaseHandler): | ||
24 | version = F12 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f13.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f13.py new file mode 100644 index 0000000000..b94c738f79 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f13.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2009 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class F13Handler(BaseHandler): | ||
24 | version = F13 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f14.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f14.py new file mode 100644 index 0000000000..478f75d15e --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f14.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2010 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class F14Handler(BaseHandler): | ||
24 | version = F14 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f15.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f15.py new file mode 100644 index 0000000000..12aecb4c1a --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f15.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2010 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class F15Handler(BaseHandler): | ||
24 | version = F15 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f16.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f16.py new file mode 100644 index 0000000000..3c52f8d754 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f16.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2011 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class F16Handler(BaseHandler): | ||
24 | version = F16 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f7.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f7.py new file mode 100644 index 0000000000..5e856ea983 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f7.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class F7Handler(BaseHandler): | ||
24 | version = F7 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f8.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f8.py new file mode 100644 index 0000000000..1a978810f4 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f8.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class F8Handler(BaseHandler): | ||
24 | version = F8 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/f9.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/f9.py new file mode 100644 index 0000000000..116f1b57c9 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/f9.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class F9Handler(BaseHandler): | ||
24 | version = F9 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/fc3.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc3.py new file mode 100644 index 0000000000..a115dc2646 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc3.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class FC3Handler(BaseHandler): | ||
24 | version = FC3 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/fc4.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc4.py new file mode 100644 index 0000000000..fd47b732ef --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc4.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class FC4Handler(BaseHandler): | ||
24 | version = FC4 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/fc5.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc5.py new file mode 100644 index 0000000000..bcdc29d23a --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc5.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class FC5Handler(BaseHandler): | ||
24 | version = FC5 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/fc6.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc6.py new file mode 100644 index 0000000000..c83a929f84 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/fc6.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class FC6Handler(BaseHandler): | ||
24 | version = FC6 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel3.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel3.py new file mode 100644 index 0000000000..131763c2a8 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel3.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class RHEL3Handler(BaseHandler): | ||
24 | version = RHEL3 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel4.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel4.py new file mode 100644 index 0000000000..3496c43ea5 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel4.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class RHEL4Handler(BaseHandler): | ||
24 | version = RHEL4 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel5.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel5.py new file mode 100644 index 0000000000..abb7a8d36c --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel5.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class RHEL5Handler(BaseHandler): | ||
24 | version = RHEL5 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel6.py b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel6.py new file mode 100644 index 0000000000..7202419780 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/handlers/rhel6.py | |||
@@ -0,0 +1,24 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2010 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | from pykickstart.base import * | ||
21 | from pykickstart.version import * | ||
22 | |||
23 | class RHEL6Handler(BaseHandler): | ||
24 | version = RHEL6 | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/ko.py b/scripts/lib/mic/3rdparty/pykickstart/ko.py new file mode 100644 index 0000000000..1350d19c70 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/ko.py | |||
@@ -0,0 +1,37 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2009 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | """ | ||
21 | Base classes for internal pykickstart use. | ||
22 | |||
23 | The module exports the following important classes: | ||
24 | |||
25 | KickstartObject - The base class for all classes in pykickstart | ||
26 | """ | ||
27 | |||
28 | class KickstartObject(object): | ||
29 | """The base class for all other classes in pykickstart.""" | ||
30 | def __init__(self, *args, **kwargs): | ||
31 | """Create a new KickstartObject instance. All other classes in | ||
32 | pykickstart should be derived from this one. Instance attributes: | ||
33 | """ | ||
34 | pass | ||
35 | |||
36 | def __str__(self): | ||
37 | return "" | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/options.py b/scripts/lib/mic/3rdparty/pykickstart/options.py new file mode 100644 index 0000000000..341c5d7298 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/options.py | |||
@@ -0,0 +1,204 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2005, 2006, 2007 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | """ | ||
21 | Specialized option handling. | ||
22 | |||
23 | This module exports two classes: | ||
24 | |||
25 | KSOptionParser - A specialized subclass of OptionParser to be used | ||
26 | in BaseHandler subclasses. | ||
27 | |||
28 | KSOption - A specialized subclass of Option. | ||
29 | """ | ||
30 | import warnings | ||
31 | from copy import copy | ||
32 | from optparse import * | ||
33 | |||
34 | from constants import * | ||
35 | from errors import * | ||
36 | from version import * | ||
37 | |||
38 | import gettext | ||
39 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
40 | |||
41 | class KSOptionParser(OptionParser): | ||
42 | """A specialized subclass of optparse.OptionParser to handle extra option | ||
43 | attribute checking, work error reporting into the KickstartParseError | ||
44 | framework, and to turn off the default help. | ||
45 | """ | ||
46 | def exit(self, status=0, msg=None): | ||
47 | pass | ||
48 | |||
49 | def error(self, msg): | ||
50 | if self.lineno != None: | ||
51 | raise KickstartParseError, formatErrorMsg(self.lineno, msg=msg) | ||
52 | else: | ||
53 | raise KickstartParseError, msg | ||
54 | |||
55 | def keys(self): | ||
56 | retval = [] | ||
57 | |||
58 | for opt in self.option_list: | ||
59 | if opt not in retval: | ||
60 | retval.append(opt.dest) | ||
61 | |||
62 | return retval | ||
63 | |||
64 | def _init_parsing_state (self): | ||
65 | OptionParser._init_parsing_state(self) | ||
66 | self.option_seen = {} | ||
67 | |||
68 | def check_values (self, values, args): | ||
69 | def seen(self, option): | ||
70 | return self.option_seen.has_key(option) | ||
71 | |||
72 | def usedTooNew(self, option): | ||
73 | return option.introduced and option.introduced > self.version | ||
74 | |||
75 | def usedDeprecated(self, option): | ||
76 | return option.deprecated | ||
77 | |||
78 | def usedRemoved(self, option): | ||
79 | return option.removed and option.removed <= self.version | ||
80 | |||
81 | for option in filter(lambda o: isinstance(o, Option), self.option_list): | ||
82 | if option.required and not seen(self, option): | ||
83 | raise KickstartValueError, formatErrorMsg(self.lineno, _("Option %s is required") % option) | ||
84 | elif seen(self, option) and usedTooNew(self, option): | ||
85 | mapping = {"option": option, "intro": versionToString(option.introduced), | ||
86 | "version": versionToString(self.version)} | ||
87 | self.error(_("The %(option)s option was introduced in version %(intro)s, but you are using kickstart syntax version %(version)s.") % mapping) | ||
88 | elif seen(self, option) and usedRemoved(self, option): | ||
89 | mapping = {"option": option, "removed": versionToString(option.removed), | ||
90 | "version": versionToString(self.version)} | ||
91 | |||
92 | if option.removed == self.version: | ||
93 | self.error(_("The %(option)s option is no longer supported.") % mapping) | ||
94 | else: | ||
95 | self.error(_("The %(option)s option was removed in version %(removed)s, but you are using kickstart syntax version %(version)s.") % mapping) | ||
96 | elif seen(self, option) and usedDeprecated(self, option): | ||
97 | mapping = {"lineno": self.lineno, "option": option} | ||
98 | warnings.warn(_("Ignoring deprecated option on line %(lineno)s: The %(option)s option has been deprecated and no longer has any effect. It may be removed from future releases, which will result in a fatal error from kickstart. Please modify your kickstart file to remove this option.") % mapping, DeprecationWarning) | ||
99 | |||
100 | return (values, args) | ||
101 | |||
102 | def parse_args(self, *args, **kwargs): | ||
103 | if kwargs.has_key("lineno"): | ||
104 | self.lineno = kwargs.pop("lineno") | ||
105 | |||
106 | return OptionParser.parse_args(self, **kwargs) | ||
107 | |||
108 | def __init__(self, mapping=None, version=None): | ||
109 | """Create a new KSOptionParser instance. Each KickstartCommand | ||
110 | subclass should create one instance of KSOptionParser, providing | ||
111 | at least the lineno attribute. mapping and version are not required. | ||
112 | Instance attributes: | ||
113 | |||
114 | mapping -- A mapping from option strings to different values. | ||
115 | version -- The version of the kickstart syntax we are checking | ||
116 | against. | ||
117 | """ | ||
118 | OptionParser.__init__(self, option_class=KSOption, | ||
119 | add_help_option=False, | ||
120 | conflict_handler="resolve") | ||
121 | if mapping is None: | ||
122 | self.map = {} | ||
123 | else: | ||
124 | self.map = mapping | ||
125 | |||
126 | self.lineno = None | ||
127 | self.option_seen = {} | ||
128 | self.version = version | ||
129 | |||
130 | def _check_ksboolean(option, opt, value): | ||
131 | if value.lower() in ("on", "yes", "true", "1"): | ||
132 | return True | ||
133 | elif value.lower() in ("off", "no", "false", "0"): | ||
134 | return False | ||
135 | else: | ||
136 | mapping = {"opt": opt, "value": value} | ||
137 | raise OptionValueError(_("Option %(opt)s: invalid boolean value: %(value)r") % mapping) | ||
138 | |||
139 | def _check_string(option, opt, value): | ||
140 | if len(value) > 2 and value.startswith("--"): | ||
141 | mapping = {"opt": opt, "value": value} | ||
142 | raise OptionValueError(_("Option %(opt)s: invalid string value: %(value)r") % mapping) | ||
143 | else: | ||
144 | return value | ||
145 | |||
146 | # Creates a new Option class that supports several new attributes: | ||
147 | # - required: any option with this attribute must be supplied or an exception | ||
148 | # is thrown | ||
149 | # - introduced: the kickstart syntax version that this option first appeared | ||
150 | # in - an exception will be raised if the option is used and | ||
151 | # the specified syntax version is less than the value of this | ||
152 | # attribute | ||
153 | # - deprecated: the kickstart syntax version that this option was deprecated | ||
154 | # in - a DeprecationWarning will be thrown if the option is | ||
155 | # used and the specified syntax version is greater than the | ||
156 | # value of this attribute | ||
157 | # - removed: the kickstart syntax version that this option was removed in - an | ||
158 | # exception will be raised if the option is used and the specified | ||
159 | # syntax version is greated than the value of this attribute | ||
160 | # Also creates a new type: | ||
161 | # - ksboolean: support various kinds of boolean values on an option | ||
162 | # And two new actions: | ||
163 | # - map : allows you to define an opt -> val mapping such that dest gets val | ||
164 | # when opt is seen | ||
165 | # - map_extend: allows you to define an opt -> [val1, ... valn] mapping such | ||
166 | # that dest gets a list of vals built up when opt is seen | ||
167 | class KSOption (Option): | ||
168 | ATTRS = Option.ATTRS + ['introduced', 'deprecated', 'removed', 'required'] | ||
169 | ACTIONS = Option.ACTIONS + ("map", "map_extend",) | ||
170 | STORE_ACTIONS = Option.STORE_ACTIONS + ("map", "map_extend",) | ||
171 | |||
172 | TYPES = Option.TYPES + ("ksboolean", "string") | ||
173 | TYPE_CHECKER = copy(Option.TYPE_CHECKER) | ||
174 | TYPE_CHECKER["ksboolean"] = _check_ksboolean | ||
175 | TYPE_CHECKER["string"] = _check_string | ||
176 | |||
177 | def _check_required(self): | ||
178 | if self.required and not self.takes_value(): | ||
179 | raise OptionError(_("Required flag set for option that doesn't take a value"), self) | ||
180 | |||
181 | # Make sure _check_required() is called from the constructor! | ||
182 | CHECK_METHODS = Option.CHECK_METHODS + [_check_required] | ||
183 | |||
184 | def process (self, opt, value, values, parser): | ||
185 | Option.process(self, opt, value, values, parser) | ||
186 | parser.option_seen[self] = 1 | ||
187 | |||
188 | # Override default take_action method to handle our custom actions. | ||
189 | def take_action(self, action, dest, opt, value, values, parser): | ||
190 | if action == "map": | ||
191 | values.ensure_value(dest, parser.map[opt.lstrip('-')]) | ||
192 | elif action == "map_extend": | ||
193 | values.ensure_value(dest, []).extend(parser.map[opt.lstrip('-')]) | ||
194 | else: | ||
195 | Option.take_action(self, action, dest, opt, value, values, parser) | ||
196 | |||
197 | def takes_value(self): | ||
198 | # Deprecated options don't take a value. | ||
199 | return Option.takes_value(self) and not self.deprecated | ||
200 | |||
201 | def __init__(self, *args, **kwargs): | ||
202 | self.deprecated = False | ||
203 | self.required = False | ||
204 | Option.__init__(self, *args, **kwargs) | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/parser.py b/scripts/lib/mic/3rdparty/pykickstart/parser.py new file mode 100644 index 0000000000..840a448673 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/parser.py | |||
@@ -0,0 +1,702 @@ | |||
1 | # | ||
2 | # parser.py: Kickstart file parser. | ||
3 | # | ||
4 | # Chris Lumens <clumens@redhat.com> | ||
5 | # | ||
6 | # Copyright 2005, 2006, 2007, 2008, 2011 Red Hat, Inc. | ||
7 | # | ||
8 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
9 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
10 | # General Public License v.2. This program is distributed in the hope that it | ||
11 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
12 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | # See the GNU General Public License for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License along with | ||
16 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
17 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
18 | # trademarks that are incorporated in the source code or documentation are not | ||
19 | # subject to the GNU General Public License and may only be used or replicated | ||
20 | # with the express permission of Red Hat, Inc. | ||
21 | # | ||
22 | """ | ||
23 | Main kickstart file processing module. | ||
24 | |||
25 | This module exports several important classes: | ||
26 | |||
27 | Script - Representation of a single %pre, %post, or %traceback script. | ||
28 | |||
29 | Packages - Representation of the %packages section. | ||
30 | |||
31 | KickstartParser - The kickstart file parser state machine. | ||
32 | """ | ||
33 | |||
34 | from collections import Iterator | ||
35 | import os | ||
36 | import shlex | ||
37 | import sys | ||
38 | import tempfile | ||
39 | from copy import copy | ||
40 | from optparse import * | ||
41 | from urlgrabber import urlread | ||
42 | import urlgrabber.grabber as grabber | ||
43 | |||
44 | import constants | ||
45 | from errors import KickstartError, KickstartParseError, KickstartValueError, formatErrorMsg | ||
46 | from ko import KickstartObject | ||
47 | from sections import * | ||
48 | import version | ||
49 | |||
50 | import gettext | ||
51 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
52 | |||
53 | STATE_END = "end" | ||
54 | STATE_COMMANDS = "commands" | ||
55 | |||
56 | ver = version.DEVEL | ||
57 | |||
58 | def _preprocessStateMachine (lineIter): | ||
59 | l = None | ||
60 | lineno = 0 | ||
61 | |||
62 | # Now open an output kickstart file that we are going to write to one | ||
63 | # line at a time. | ||
64 | (outF, outName) = tempfile.mkstemp("-ks.cfg", "", "/tmp") | ||
65 | |||
66 | while True: | ||
67 | try: | ||
68 | l = lineIter.next() | ||
69 | except StopIteration: | ||
70 | break | ||
71 | |||
72 | # At the end of the file? | ||
73 | if l == "": | ||
74 | break | ||
75 | |||
76 | lineno += 1 | ||
77 | url = None | ||
78 | |||
79 | ll = l.strip() | ||
80 | if not ll.startswith("%ksappend"): | ||
81 | os.write(outF, l) | ||
82 | continue | ||
83 | |||
84 | # Try to pull down the remote file. | ||
85 | try: | ||
86 | ksurl = ll.split(' ')[1] | ||
87 | except: | ||
88 | raise KickstartParseError, formatErrorMsg(lineno, msg=_("Illegal url for %%ksappend: %s") % ll) | ||
89 | |||
90 | try: | ||
91 | url = grabber.urlopen(ksurl) | ||
92 | except grabber.URLGrabError, e: | ||
93 | raise KickstartError, formatErrorMsg(lineno, msg=_("Unable to open %%ksappend file: %s") % e.strerror) | ||
94 | else: | ||
95 | # Sanity check result. Sometimes FTP doesn't catch a file | ||
96 | # is missing. | ||
97 | try: | ||
98 | if url.size < 1: | ||
99 | raise KickstartError, formatErrorMsg(lineno, msg=_("Unable to open %%ksappend file")) | ||
100 | except: | ||
101 | raise KickstartError, formatErrorMsg(lineno, msg=_("Unable to open %%ksappend file")) | ||
102 | |||
103 | # If that worked, write the remote file to the output kickstart | ||
104 | # file in one burst. Then close everything up to get ready to | ||
105 | # read ahead in the input file. This allows multiple %ksappend | ||
106 | # lines to exist. | ||
107 | if url is not None: | ||
108 | os.write(outF, url.read()) | ||
109 | url.close() | ||
110 | |||
111 | # All done - close the temp file and return its location. | ||
112 | os.close(outF) | ||
113 | return outName | ||
114 | |||
115 | def preprocessFromString (s): | ||
116 | """Preprocess the kickstart file, provided as the string str. This | ||
117 | method is currently only useful for handling %ksappend lines, | ||
118 | which need to be fetched before the real kickstart parser can be | ||
119 | run. Returns the location of the complete kickstart file. | ||
120 | """ | ||
121 | i = iter(s.splitlines(True) + [""]) | ||
122 | rc = _preprocessStateMachine (i.next) | ||
123 | return rc | ||
124 | |||
125 | def preprocessKickstart (f): | ||
126 | """Preprocess the kickstart file, given by the filename file. This | ||
127 | method is currently only useful for handling %ksappend lines, | ||
128 | which need to be fetched before the real kickstart parser can be | ||
129 | run. Returns the location of the complete kickstart file. | ||
130 | """ | ||
131 | try: | ||
132 | fh = urlopen(f) | ||
133 | except grabber.URLGrabError, e: | ||
134 | raise KickstartError, formatErrorMsg(0, msg=_("Unable to open input kickstart file: %s") % e.strerror) | ||
135 | |||
136 | rc = _preprocessStateMachine (iter(fh.readlines())) | ||
137 | fh.close() | ||
138 | return rc | ||
139 | |||
140 | class PutBackIterator(Iterator): | ||
141 | def __init__(self, iterable): | ||
142 | self._iterable = iter(iterable) | ||
143 | self._buf = None | ||
144 | |||
145 | def __iter__(self): | ||
146 | return self | ||
147 | |||
148 | def put(self, s): | ||
149 | self._buf = s | ||
150 | |||
151 | def next(self): | ||
152 | if self._buf: | ||
153 | retval = self._buf | ||
154 | self._buf = None | ||
155 | return retval | ||
156 | else: | ||
157 | return self._iterable.next() | ||
158 | |||
159 | ### | ||
160 | ### SCRIPT HANDLING | ||
161 | ### | ||
162 | class Script(KickstartObject): | ||
163 | """A class representing a single kickstart script. If functionality beyond | ||
164 | just a data representation is needed (for example, a run method in | ||
165 | anaconda), Script may be subclassed. Although a run method is not | ||
166 | provided, most of the attributes of Script have to do with running the | ||
167 | script. Instances of Script are held in a list by the Version object. | ||
168 | """ | ||
169 | def __init__(self, script, *args , **kwargs): | ||
170 | """Create a new Script instance. Instance attributes: | ||
171 | |||
172 | errorOnFail -- If execution of the script fails, should anaconda | ||
173 | stop, display an error, and then reboot without | ||
174 | running any other scripts? | ||
175 | inChroot -- Does the script execute in anaconda's chroot | ||
176 | environment or not? | ||
177 | interp -- The program that should be used to interpret this | ||
178 | script. | ||
179 | lineno -- The line number this script starts on. | ||
180 | logfile -- Where all messages from the script should be logged. | ||
181 | script -- A string containing all the lines of the script. | ||
182 | type -- The type of the script, which can be KS_SCRIPT_* from | ||
183 | pykickstart.constants. | ||
184 | """ | ||
185 | KickstartObject.__init__(self, *args, **kwargs) | ||
186 | self.script = "".join(script) | ||
187 | |||
188 | self.interp = kwargs.get("interp", "/bin/sh") | ||
189 | self.inChroot = kwargs.get("inChroot", False) | ||
190 | self.lineno = kwargs.get("lineno", None) | ||
191 | self.logfile = kwargs.get("logfile", None) | ||
192 | self.errorOnFail = kwargs.get("errorOnFail", False) | ||
193 | self.type = kwargs.get("type", constants.KS_SCRIPT_PRE) | ||
194 | |||
195 | def __str__(self): | ||
196 | """Return a string formatted for output to a kickstart file.""" | ||
197 | retval = "" | ||
198 | |||
199 | if self.type == constants.KS_SCRIPT_PRE: | ||
200 | retval += '\n%pre' | ||
201 | elif self.type == constants.KS_SCRIPT_POST: | ||
202 | retval += '\n%post' | ||
203 | elif self.type == constants.KS_SCRIPT_TRACEBACK: | ||
204 | retval += '\n%traceback' | ||
205 | |||
206 | if self.interp != "/bin/sh" and self.interp != "": | ||
207 | retval += " --interpreter=%s" % self.interp | ||
208 | if self.type == constants.KS_SCRIPT_POST and not self.inChroot: | ||
209 | retval += " --nochroot" | ||
210 | if self.logfile != None: | ||
211 | retval += " --logfile %s" % self.logfile | ||
212 | if self.errorOnFail: | ||
213 | retval += " --erroronfail" | ||
214 | |||
215 | if self.script.endswith("\n"): | ||
216 | if ver >= version.F8: | ||
217 | return retval + "\n%s%%end\n" % self.script | ||
218 | else: | ||
219 | return retval + "\n%s\n" % self.script | ||
220 | else: | ||
221 | if ver >= version.F8: | ||
222 | return retval + "\n%s\n%%end\n" % self.script | ||
223 | else: | ||
224 | return retval + "\n%s\n" % self.script | ||
225 | |||
226 | |||
227 | ## | ||
228 | ## PACKAGE HANDLING | ||
229 | ## | ||
230 | class Group: | ||
231 | """A class representing a single group in the %packages section.""" | ||
232 | def __init__(self, name="", include=constants.GROUP_DEFAULT): | ||
233 | """Create a new Group instance. Instance attributes: | ||
234 | |||
235 | name -- The group's identifier | ||
236 | include -- The level of how much of the group should be included. | ||
237 | Values can be GROUP_* from pykickstart.constants. | ||
238 | """ | ||
239 | self.name = name | ||
240 | self.include = include | ||
241 | |||
242 | def __str__(self): | ||
243 | """Return a string formatted for output to a kickstart file.""" | ||
244 | if self.include == constants.GROUP_REQUIRED: | ||
245 | return "@%s --nodefaults" % self.name | ||
246 | elif self.include == constants.GROUP_ALL: | ||
247 | return "@%s --optional" % self.name | ||
248 | else: | ||
249 | return "@%s" % self.name | ||
250 | |||
251 | def __cmp__(self, other): | ||
252 | if self.name < other.name: | ||
253 | return -1 | ||
254 | elif self.name > other.name: | ||
255 | return 1 | ||
256 | return 0 | ||
257 | |||
258 | class Packages(KickstartObject): | ||
259 | """A class representing the %packages section of the kickstart file.""" | ||
260 | def __init__(self, *args, **kwargs): | ||
261 | """Create a new Packages instance. Instance attributes: | ||
262 | |||
263 | addBase -- Should the Base group be installed even if it is | ||
264 | not specified? | ||
265 | default -- Should the default package set be selected? | ||
266 | excludedList -- A list of all the packages marked for exclusion in | ||
267 | the %packages section, without the leading minus | ||
268 | symbol. | ||
269 | excludeDocs -- Should documentation in each package be excluded? | ||
270 | groupList -- A list of Group objects representing all the groups | ||
271 | specified in the %packages section. Names will be | ||
272 | stripped of the leading @ symbol. | ||
273 | excludedGroupList -- A list of Group objects representing all the | ||
274 | groups specified for removal in the %packages | ||
275 | section. Names will be stripped of the leading | ||
276 | -@ symbols. | ||
277 | handleMissing -- If unknown packages are specified in the %packages | ||
278 | section, should it be ignored or not? Values can | ||
279 | be KS_MISSING_* from pykickstart.constants. | ||
280 | packageList -- A list of all the packages specified in the | ||
281 | %packages section. | ||
282 | instLangs -- A list of languages to install. | ||
283 | """ | ||
284 | KickstartObject.__init__(self, *args, **kwargs) | ||
285 | |||
286 | self.addBase = True | ||
287 | self.default = False | ||
288 | self.excludedList = [] | ||
289 | self.excludedGroupList = [] | ||
290 | self.excludeDocs = False | ||
291 | self.groupList = [] | ||
292 | self.handleMissing = constants.KS_MISSING_PROMPT | ||
293 | self.packageList = [] | ||
294 | self.instLangs = None | ||
295 | |||
296 | def __str__(self): | ||
297 | """Return a string formatted for output to a kickstart file.""" | ||
298 | pkgs = "" | ||
299 | |||
300 | if not self.default: | ||
301 | grps = self.groupList | ||
302 | grps.sort() | ||
303 | for grp in grps: | ||
304 | pkgs += "%s\n" % grp.__str__() | ||
305 | |||
306 | p = self.packageList | ||
307 | p.sort() | ||
308 | for pkg in p: | ||
309 | pkgs += "%s\n" % pkg | ||
310 | |||
311 | grps = self.excludedGroupList | ||
312 | grps.sort() | ||
313 | for grp in grps: | ||
314 | pkgs += "-%s\n" % grp.__str__() | ||
315 | |||
316 | p = self.excludedList | ||
317 | p.sort() | ||
318 | for pkg in p: | ||
319 | pkgs += "-%s\n" % pkg | ||
320 | |||
321 | if pkgs == "": | ||
322 | return "" | ||
323 | |||
324 | retval = "\n%packages" | ||
325 | |||
326 | if self.default: | ||
327 | retval += " --default" | ||
328 | if self.excludeDocs: | ||
329 | retval += " --excludedocs" | ||
330 | if not self.addBase: | ||
331 | retval += " --nobase" | ||
332 | if self.handleMissing == constants.KS_MISSING_IGNORE: | ||
333 | retval += " --ignoremissing" | ||
334 | if self.instLangs: | ||
335 | retval += " --instLangs=%s" % self.instLangs | ||
336 | |||
337 | if ver >= version.F8: | ||
338 | return retval + "\n" + pkgs + "\n%end\n" | ||
339 | else: | ||
340 | return retval + "\n" + pkgs + "\n" | ||
341 | |||
342 | def _processGroup (self, line): | ||
343 | op = OptionParser() | ||
344 | op.add_option("--nodefaults", action="store_true", default=False) | ||
345 | op.add_option("--optional", action="store_true", default=False) | ||
346 | |||
347 | (opts, extra) = op.parse_args(args=line.split()) | ||
348 | |||
349 | if opts.nodefaults and opts.optional: | ||
350 | raise KickstartValueError, _("Group cannot specify both --nodefaults and --optional") | ||
351 | |||
352 | # If the group name has spaces in it, we have to put it back together | ||
353 | # now. | ||
354 | grp = " ".join(extra) | ||
355 | |||
356 | if opts.nodefaults: | ||
357 | self.groupList.append(Group(name=grp, include=constants.GROUP_REQUIRED)) | ||
358 | elif opts.optional: | ||
359 | self.groupList.append(Group(name=grp, include=constants.GROUP_ALL)) | ||
360 | else: | ||
361 | self.groupList.append(Group(name=grp, include=constants.GROUP_DEFAULT)) | ||
362 | |||
363 | def add (self, pkgList): | ||
364 | """Given a list of lines from the input file, strip off any leading | ||
365 | symbols and add the result to the appropriate list. | ||
366 | """ | ||
367 | existingExcludedSet = set(self.excludedList) | ||
368 | existingPackageSet = set(self.packageList) | ||
369 | newExcludedSet = set() | ||
370 | newPackageSet = set() | ||
371 | |||
372 | excludedGroupList = [] | ||
373 | |||
374 | for pkg in pkgList: | ||
375 | stripped = pkg.strip() | ||
376 | |||
377 | if stripped[0] == "@": | ||
378 | self._processGroup(stripped[1:]) | ||
379 | elif stripped[0] == "-": | ||
380 | if stripped[1] == "@": | ||
381 | excludedGroupList.append(Group(name=stripped[2:])) | ||
382 | else: | ||
383 | newExcludedSet.add(stripped[1:]) | ||
384 | else: | ||
385 | newPackageSet.add(stripped) | ||
386 | |||
387 | # Groups have to be excluded in two different ways (note: can't use | ||
388 | # sets here because we have to store objects): | ||
389 | excludedGroupNames = map(lambda g: g.name, excludedGroupList) | ||
390 | |||
391 | # First, an excluded group may be cancelling out a previously given | ||
392 | # one. This is often the case when using %include. So there we should | ||
393 | # just remove the group from the list. | ||
394 | self.groupList = filter(lambda g: g.name not in excludedGroupNames, self.groupList) | ||
395 | |||
396 | # Second, the package list could have included globs which are not | ||
397 | # processed by pykickstart. In that case we need to preserve a list of | ||
398 | # excluded groups so whatever tool doing package/group installation can | ||
399 | # take appropriate action. | ||
400 | self.excludedGroupList.extend(excludedGroupList) | ||
401 | |||
402 | existingPackageSet = (existingPackageSet - newExcludedSet) | newPackageSet | ||
403 | existingExcludedSet = (existingExcludedSet - existingPackageSet) | newExcludedSet | ||
404 | |||
405 | self.packageList = list(existingPackageSet) | ||
406 | self.excludedList = list(existingExcludedSet) | ||
407 | |||
408 | |||
409 | ### | ||
410 | ### PARSER | ||
411 | ### | ||
412 | class KickstartParser: | ||
413 | """The kickstart file parser class as represented by a basic state | ||
414 | machine. To create a specialized parser, make a subclass and override | ||
415 | any of the methods you care about. Methods that don't need to do | ||
416 | anything may just pass. However, _stateMachine should never be | ||
417 | overridden. | ||
418 | """ | ||
419 | def __init__ (self, handler, followIncludes=True, errorsAreFatal=True, | ||
420 | missingIncludeIsFatal=True): | ||
421 | """Create a new KickstartParser instance. Instance attributes: | ||
422 | |||
423 | errorsAreFatal -- Should errors cause processing to halt, or | ||
424 | just print a message to the screen? This | ||
425 | is most useful for writing syntax checkers | ||
426 | that may want to continue after an error is | ||
427 | encountered. | ||
428 | followIncludes -- If %include is seen, should the included | ||
429 | file be checked as well or skipped? | ||
430 | handler -- An instance of a BaseHandler subclass. If | ||
431 | None, the input file will still be parsed | ||
432 | but no data will be saved and no commands | ||
433 | will be executed. | ||
434 | missingIncludeIsFatal -- Should missing include files be fatal, even | ||
435 | if errorsAreFatal is False? | ||
436 | """ | ||
437 | self.errorsAreFatal = errorsAreFatal | ||
438 | self.followIncludes = followIncludes | ||
439 | self.handler = handler | ||
440 | self.currentdir = {} | ||
441 | self.missingIncludeIsFatal = missingIncludeIsFatal | ||
442 | |||
443 | self._state = STATE_COMMANDS | ||
444 | self._includeDepth = 0 | ||
445 | self._line = "" | ||
446 | |||
447 | self.version = self.handler.version | ||
448 | |||
449 | global ver | ||
450 | ver = self.version | ||
451 | |||
452 | self._sections = {} | ||
453 | self.setupSections() | ||
454 | |||
455 | def _reset(self): | ||
456 | """Reset the internal variables of the state machine for a new kickstart file.""" | ||
457 | self._state = STATE_COMMANDS | ||
458 | self._includeDepth = 0 | ||
459 | |||
460 | def getSection(self, s): | ||
461 | """Return a reference to the requested section (s must start with '%'s), | ||
462 | or raise KeyError if not found. | ||
463 | """ | ||
464 | return self._sections[s] | ||
465 | |||
466 | def handleCommand (self, lineno, args): | ||
467 | """Given the list of command and arguments, call the Version's | ||
468 | dispatcher method to handle the command. Returns the command or | ||
469 | data object returned by the dispatcher. This method may be | ||
470 | overridden in a subclass if necessary. | ||
471 | """ | ||
472 | if self.handler: | ||
473 | self.handler.currentCmd = args[0] | ||
474 | self.handler.currentLine = self._line | ||
475 | retval = self.handler.dispatcher(args, lineno) | ||
476 | |||
477 | return retval | ||
478 | |||
479 | def registerSection(self, obj): | ||
480 | """Given an instance of a Section subclass, register the new section | ||
481 | with the parser. Calling this method means the parser will | ||
482 | recognize your new section and dispatch into the given object to | ||
483 | handle it. | ||
484 | """ | ||
485 | if not obj.sectionOpen: | ||
486 | raise TypeError, "no sectionOpen given for section %s" % obj | ||
487 | |||
488 | if not obj.sectionOpen.startswith("%"): | ||
489 | raise TypeError, "section %s tag does not start with a %%" % obj.sectionOpen | ||
490 | |||
491 | self._sections[obj.sectionOpen] = obj | ||
492 | |||
493 | def _finalize(self, obj): | ||
494 | """Called at the close of a kickstart section to take any required | ||
495 | actions. Internally, this is used to add scripts once we have the | ||
496 | whole body read. | ||
497 | """ | ||
498 | obj.finalize() | ||
499 | self._state = STATE_COMMANDS | ||
500 | |||
501 | def _handleSpecialComments(self, line): | ||
502 | """Kickstart recognizes a couple special comments.""" | ||
503 | if self._state != STATE_COMMANDS: | ||
504 | return | ||
505 | |||
506 | # Save the platform for s-c-kickstart. | ||
507 | if line[:10] == "#platform=": | ||
508 | self.handler.platform = self._line[11:] | ||
509 | |||
510 | def _readSection(self, lineIter, lineno): | ||
511 | obj = self._sections[self._state] | ||
512 | |||
513 | while True: | ||
514 | try: | ||
515 | line = lineIter.next() | ||
516 | if line == "": | ||
517 | # This section ends at the end of the file. | ||
518 | if self.version >= version.F8: | ||
519 | raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end.")) | ||
520 | |||
521 | self._finalize(obj) | ||
522 | except StopIteration: | ||
523 | break | ||
524 | |||
525 | lineno += 1 | ||
526 | |||
527 | # Throw away blank lines and comments, unless the section wants all | ||
528 | # lines. | ||
529 | if self._isBlankOrComment(line) and not obj.allLines: | ||
530 | continue | ||
531 | |||
532 | if line.startswith("%"): | ||
533 | args = shlex.split(line) | ||
534 | |||
535 | if args and args[0] == "%end": | ||
536 | # This is a properly terminated section. | ||
537 | self._finalize(obj) | ||
538 | break | ||
539 | elif args and args[0] == "%ksappend": | ||
540 | continue | ||
541 | elif args and (self._validState(args[0]) or args[0] in ["%include", "%ksappend"]): | ||
542 | # This is an unterminated section. | ||
543 | if self.version >= version.F8: | ||
544 | raise KickstartParseError, formatErrorMsg(lineno, msg=_("Section does not end with %%end.")) | ||
545 | |||
546 | # Finish up. We do not process the header here because | ||
547 | # kicking back out to STATE_COMMANDS will ensure that happens. | ||
548 | lineIter.put(line) | ||
549 | lineno -= 1 | ||
550 | self._finalize(obj) | ||
551 | break | ||
552 | else: | ||
553 | # This is just a line within a section. Pass it off to whatever | ||
554 | # section handles it. | ||
555 | obj.handleLine(line) | ||
556 | |||
557 | return lineno | ||
558 | |||
559 | def _validState(self, st): | ||
560 | """Is the given section tag one that has been registered with the parser?""" | ||
561 | return st in self._sections.keys() | ||
562 | |||
563 | def _tryFunc(self, fn): | ||
564 | """Call the provided function (which doesn't take any arguments) and | ||
565 | do the appropriate error handling. If errorsAreFatal is False, this | ||
566 | function will just print the exception and keep going. | ||
567 | """ | ||
568 | try: | ||
569 | fn() | ||
570 | except Exception, msg: | ||
571 | if self.errorsAreFatal: | ||
572 | raise | ||
573 | else: | ||
574 | print msg | ||
575 | |||
576 | def _isBlankOrComment(self, line): | ||
577 | return line.isspace() or line == "" or line.lstrip()[0] == '#' | ||
578 | |||
579 | def _stateMachine(self, lineIter): | ||
580 | # For error reporting. | ||
581 | lineno = 0 | ||
582 | |||
583 | while True: | ||
584 | # Get the next line out of the file, quitting if this is the last line. | ||
585 | try: | ||
586 | self._line = lineIter.next() | ||
587 | if self._line == "": | ||
588 | break | ||
589 | except StopIteration: | ||
590 | break | ||
591 | |||
592 | lineno += 1 | ||
593 | |||
594 | # Eliminate blank lines, whitespace-only lines, and comments. | ||
595 | if self._isBlankOrComment(self._line): | ||
596 | self._handleSpecialComments(self._line) | ||
597 | continue | ||
598 | |||
599 | # Remove any end-of-line comments. | ||
600 | sanitized = self._line.split("#")[0] | ||
601 | |||
602 | # Then split the line. | ||
603 | args = shlex.split(sanitized.rstrip()) | ||
604 | |||
605 | if args[0] == "%include": | ||
606 | # This case comes up primarily in ksvalidator. | ||
607 | if not self.followIncludes: | ||
608 | continue | ||
609 | |||
610 | if len(args) == 1 or not args[1]: | ||
611 | raise KickstartParseError, formatErrorMsg(lineno) | ||
612 | |||
613 | self._includeDepth += 1 | ||
614 | |||
615 | try: | ||
616 | self.readKickstart(args[1], reset=False) | ||
617 | except KickstartError: | ||
618 | # Handle the include file being provided over the | ||
619 | # network in a %pre script. This case comes up in the | ||
620 | # early parsing in anaconda. | ||
621 | if self.missingIncludeIsFatal: | ||
622 | raise | ||
623 | |||
624 | self._includeDepth -= 1 | ||
625 | continue | ||
626 | |||
627 | # Now on to the main event. | ||
628 | if self._state == STATE_COMMANDS: | ||
629 | if args[0] == "%ksappend": | ||
630 | # This is handled by the preprocess* functions, so continue. | ||
631 | continue | ||
632 | elif args[0][0] == '%': | ||
633 | # This is the beginning of a new section. Handle its header | ||
634 | # here. | ||
635 | newSection = args[0] | ||
636 | if not self._validState(newSection): | ||
637 | raise KickstartParseError, formatErrorMsg(lineno, msg=_("Unknown kickstart section: %s" % newSection)) | ||
638 | |||
639 | self._state = newSection | ||
640 | obj = self._sections[self._state] | ||
641 | self._tryFunc(lambda: obj.handleHeader(lineno, args)) | ||
642 | |||
643 | # This will handle all section processing, kicking us back | ||
644 | # out to STATE_COMMANDS at the end with the current line | ||
645 | # being the next section header, etc. | ||
646 | lineno = self._readSection(lineIter, lineno) | ||
647 | else: | ||
648 | # This is a command in the command section. Dispatch to it. | ||
649 | self._tryFunc(lambda: self.handleCommand(lineno, args)) | ||
650 | elif self._state == STATE_END: | ||
651 | break | ||
652 | |||
653 | def readKickstartFromString (self, s, reset=True): | ||
654 | """Process a kickstart file, provided as the string str.""" | ||
655 | if reset: | ||
656 | self._reset() | ||
657 | |||
658 | # Add a "" to the end of the list so the string reader acts like the | ||
659 | # file reader and we only get StopIteration when we're after the final | ||
660 | # line of input. | ||
661 | i = PutBackIterator(s.splitlines(True) + [""]) | ||
662 | self._stateMachine (i) | ||
663 | |||
664 | def readKickstart(self, f, reset=True): | ||
665 | """Process a kickstart file, given by the filename f.""" | ||
666 | if reset: | ||
667 | self._reset() | ||
668 | |||
669 | # an %include might not specify a full path. if we don't try to figure | ||
670 | # out what the path should have been, then we're unable to find it | ||
671 | # requiring full path specification, though, sucks. so let's make | ||
672 | # the reading "smart" by keeping track of what the path is at each | ||
673 | # include depth. | ||
674 | if not os.path.exists(f): | ||
675 | if self.currentdir.has_key(self._includeDepth - 1): | ||
676 | if os.path.exists(os.path.join(self.currentdir[self._includeDepth - 1], f)): | ||
677 | f = os.path.join(self.currentdir[self._includeDepth - 1], f) | ||
678 | |||
679 | cd = os.path.dirname(f) | ||
680 | if not cd.startswith("/"): | ||
681 | cd = os.path.abspath(cd) | ||
682 | self.currentdir[self._includeDepth] = cd | ||
683 | |||
684 | try: | ||
685 | s = urlread(f) | ||
686 | except grabber.URLGrabError, e: | ||
687 | raise KickstartError, formatErrorMsg(0, msg=_("Unable to open input kickstart file: %s") % e.strerror) | ||
688 | |||
689 | self.readKickstartFromString(s, reset=False) | ||
690 | |||
691 | def setupSections(self): | ||
692 | """Install the sections all kickstart files support. You may override | ||
693 | this method in a subclass, but should avoid doing so unless you know | ||
694 | what you're doing. | ||
695 | """ | ||
696 | self._sections = {} | ||
697 | |||
698 | # Install the sections all kickstart files support. | ||
699 | self.registerSection(PreScriptSection(self.handler, dataObj=Script)) | ||
700 | self.registerSection(PostScriptSection(self.handler, dataObj=Script)) | ||
701 | self.registerSection(TracebackScriptSection(self.handler, dataObj=Script)) | ||
702 | self.registerSection(PackageSection(self.handler)) | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/sections.py b/scripts/lib/mic/3rdparty/pykickstart/sections.py new file mode 100644 index 0000000000..44df856b8d --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/sections.py | |||
@@ -0,0 +1,244 @@ | |||
1 | # | ||
2 | # sections.py: Kickstart file sections. | ||
3 | # | ||
4 | # Chris Lumens <clumens@redhat.com> | ||
5 | # | ||
6 | # Copyright 2011 Red Hat, Inc. | ||
7 | # | ||
8 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
9 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
10 | # General Public License v.2. This program is distributed in the hope that it | ||
11 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
12 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | # See the GNU General Public License for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License along with | ||
16 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
17 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
18 | # trademarks that are incorporated in the source code or documentation are not | ||
19 | # subject to the GNU General Public License and may only be used or replicated | ||
20 | # with the express permission of Red Hat, Inc. | ||
21 | # | ||
22 | """ | ||
23 | This module exports the classes that define a section of a kickstart file. A | ||
24 | section is a chunk of the file starting with a %tag and ending with a %end. | ||
25 | Examples of sections include %packages, %pre, and %post. | ||
26 | |||
27 | You may use this module to define your own custom sections which will be | ||
28 | treated just the same as a predefined one by the kickstart parser. All that | ||
29 | is necessary is to create a new subclass of Section and call | ||
30 | parser.registerSection with an instance of your new class. | ||
31 | """ | ||
32 | from constants import * | ||
33 | from options import KSOptionParser | ||
34 | from version import * | ||
35 | |||
36 | class Section(object): | ||
37 | """The base class for defining kickstart sections. You are free to | ||
38 | subclass this as appropriate. | ||
39 | |||
40 | Class attributes: | ||
41 | |||
42 | allLines -- Does this section require the parser to call handleLine | ||
43 | for every line in the section, even blanks and comments? | ||
44 | sectionOpen -- The string that denotes the start of this section. You | ||
45 | must start your tag with a percent sign. | ||
46 | timesSeen -- This attribute is for informational purposes only. It is | ||
47 | incremented every time handleHeader is called to keep | ||
48 | track of the number of times a section of this type is | ||
49 | seen. | ||
50 | """ | ||
51 | allLines = False | ||
52 | sectionOpen = "" | ||
53 | timesSeen = 0 | ||
54 | |||
55 | def __init__(self, handler, **kwargs): | ||
56 | """Create a new Script instance. At the least, you must pass in an | ||
57 | instance of a baseHandler subclass. | ||
58 | |||
59 | Valid kwargs: | ||
60 | |||
61 | dataObj -- | ||
62 | """ | ||
63 | self.handler = handler | ||
64 | |||
65 | self.version = self.handler.version | ||
66 | |||
67 | self.dataObj = kwargs.get("dataObj", None) | ||
68 | |||
69 | def finalize(self): | ||
70 | """This method is called when the %end tag for a section is seen. It | ||
71 | is not required to be provided. | ||
72 | """ | ||
73 | pass | ||
74 | |||
75 | def handleLine(self, line): | ||
76 | """This method is called for every line of a section. Take whatever | ||
77 | action is appropriate. While this method is not required to be | ||
78 | provided, not providing it does not make a whole lot of sense. | ||
79 | |||
80 | Arguments: | ||
81 | |||
82 | line -- The complete line, with any trailing newline. | ||
83 | """ | ||
84 | pass | ||
85 | |||
86 | def handleHeader(self, lineno, args): | ||
87 | """This method is called when the opening tag for a section is seen. | ||
88 | Not all sections will need this method, though all provided with | ||
89 | kickstart include one. | ||
90 | |||
91 | Arguments: | ||
92 | |||
93 | args -- A list of all strings passed as arguments to the section | ||
94 | opening tag. | ||
95 | """ | ||
96 | self.timesSeen += 1 | ||
97 | |||
98 | class NullSection(Section): | ||
99 | """This defines a section that pykickstart will recognize but do nothing | ||
100 | with. If the parser runs across a %section that has no object registered, | ||
101 | it will raise an error. Sometimes, you may want to simply ignore those | ||
102 | sections instead. This class is useful for that purpose. | ||
103 | """ | ||
104 | def __init__(self, *args, **kwargs): | ||
105 | """Create a new NullSection instance. You must pass a sectionOpen | ||
106 | parameter (including a leading '%') for the section you wish to | ||
107 | ignore. | ||
108 | """ | ||
109 | Section.__init__(self, *args, **kwargs) | ||
110 | self.sectionOpen = kwargs.get("sectionOpen") | ||
111 | |||
112 | class ScriptSection(Section): | ||
113 | allLines = True | ||
114 | |||
115 | def __init__(self, *args, **kwargs): | ||
116 | Section.__init__(self, *args, **kwargs) | ||
117 | self._script = {} | ||
118 | self._resetScript() | ||
119 | |||
120 | def _getParser(self): | ||
121 | op = KSOptionParser(self.version) | ||
122 | op.add_option("--erroronfail", dest="errorOnFail", action="store_true", | ||
123 | default=False) | ||
124 | op.add_option("--interpreter", dest="interpreter", default="/bin/sh") | ||
125 | op.add_option("--log", "--logfile", dest="log") | ||
126 | return op | ||
127 | |||
128 | def _resetScript(self): | ||
129 | self._script = {"interp": "/bin/sh", "log": None, "errorOnFail": False, | ||
130 | "lineno": None, "chroot": False, "body": []} | ||
131 | |||
132 | def handleLine(self, line): | ||
133 | self._script["body"].append(line) | ||
134 | |||
135 | def finalize(self): | ||
136 | if " ".join(self._script["body"]).strip() == "": | ||
137 | return | ||
138 | |||
139 | kwargs = {"interp": self._script["interp"], | ||
140 | "inChroot": self._script["chroot"], | ||
141 | "lineno": self._script["lineno"], | ||
142 | "logfile": self._script["log"], | ||
143 | "errorOnFail": self._script["errorOnFail"], | ||
144 | "type": self._script["type"]} | ||
145 | |||
146 | s = self.dataObj (self._script["body"], **kwargs) | ||
147 | self._resetScript() | ||
148 | |||
149 | if self.handler: | ||
150 | self.handler.scripts.append(s) | ||
151 | |||
152 | def handleHeader(self, lineno, args): | ||
153 | """Process the arguments to a %pre/%post/%traceback header for later | ||
154 | setting on a Script instance once the end of the script is found. | ||
155 | This method may be overridden in a subclass if necessary. | ||
156 | """ | ||
157 | Section.handleHeader(self, lineno, args) | ||
158 | op = self._getParser() | ||
159 | |||
160 | (opts, extra) = op.parse_args(args=args[1:], lineno=lineno) | ||
161 | |||
162 | self._script["interp"] = opts.interpreter | ||
163 | self._script["lineno"] = lineno | ||
164 | self._script["log"] = opts.log | ||
165 | self._script["errorOnFail"] = opts.errorOnFail | ||
166 | if hasattr(opts, "nochroot"): | ||
167 | self._script["chroot"] = not opts.nochroot | ||
168 | |||
169 | class PreScriptSection(ScriptSection): | ||
170 | sectionOpen = "%pre" | ||
171 | |||
172 | def _resetScript(self): | ||
173 | ScriptSection._resetScript(self) | ||
174 | self._script["type"] = KS_SCRIPT_PRE | ||
175 | |||
176 | class PostScriptSection(ScriptSection): | ||
177 | sectionOpen = "%post" | ||
178 | |||
179 | def _getParser(self): | ||
180 | op = ScriptSection._getParser(self) | ||
181 | op.add_option("--nochroot", dest="nochroot", action="store_true", | ||
182 | default=False) | ||
183 | return op | ||
184 | |||
185 | def _resetScript(self): | ||
186 | ScriptSection._resetScript(self) | ||
187 | self._script["chroot"] = True | ||
188 | self._script["type"] = KS_SCRIPT_POST | ||
189 | |||
190 | class TracebackScriptSection(ScriptSection): | ||
191 | sectionOpen = "%traceback" | ||
192 | |||
193 | def _resetScript(self): | ||
194 | ScriptSection._resetScript(self) | ||
195 | self._script["type"] = KS_SCRIPT_TRACEBACK | ||
196 | |||
197 | class PackageSection(Section): | ||
198 | sectionOpen = "%packages" | ||
199 | |||
200 | def handleLine(self, line): | ||
201 | if not self.handler: | ||
202 | return | ||
203 | |||
204 | (h, s, t) = line.partition('#') | ||
205 | line = h.rstrip() | ||
206 | |||
207 | self.handler.packages.add([line]) | ||
208 | |||
209 | def handleHeader(self, lineno, args): | ||
210 | """Process the arguments to the %packages header and set attributes | ||
211 | on the Version's Packages instance appropriate. This method may be | ||
212 | overridden in a subclass if necessary. | ||
213 | """ | ||
214 | Section.handleHeader(self, lineno, args) | ||
215 | op = KSOptionParser(version=self.version) | ||
216 | op.add_option("--excludedocs", dest="excludedocs", action="store_true", | ||
217 | default=False) | ||
218 | op.add_option("--ignoremissing", dest="ignoremissing", | ||
219 | action="store_true", default=False) | ||
220 | op.add_option("--nobase", dest="nobase", action="store_true", | ||
221 | default=False) | ||
222 | op.add_option("--ignoredeps", dest="resolveDeps", action="store_false", | ||
223 | deprecated=FC4, removed=F9) | ||
224 | op.add_option("--resolvedeps", dest="resolveDeps", action="store_true", | ||
225 | deprecated=FC4, removed=F9) | ||
226 | op.add_option("--default", dest="defaultPackages", action="store_true", | ||
227 | default=False, introduced=F7) | ||
228 | op.add_option("--instLangs", dest="instLangs", type="string", | ||
229 | default="", introduced=F9) | ||
230 | |||
231 | (opts, extra) = op.parse_args(args=args[1:], lineno=lineno) | ||
232 | |||
233 | self.handler.packages.excludeDocs = opts.excludedocs | ||
234 | self.handler.packages.addBase = not opts.nobase | ||
235 | if opts.ignoremissing: | ||
236 | self.handler.packages.handleMissing = KS_MISSING_IGNORE | ||
237 | else: | ||
238 | self.handler.packages.handleMissing = KS_MISSING_PROMPT | ||
239 | |||
240 | if opts.defaultPackages: | ||
241 | self.handler.packages.default = True | ||
242 | |||
243 | if opts.instLangs: | ||
244 | self.handler.packages.instLangs = opts.instLangs | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/__init__.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/__init__.py new file mode 100644 index 0000000000..7bcd9d5541 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/__init__.py | |||
@@ -0,0 +1,53 @@ | |||
1 | # This program is free software; you can redistribute it and/or modify | ||
2 | # it under the terms of the GNU General Public License as published by | ||
3 | # the Free Software Foundation; either version 2 of the License, or | ||
4 | # (at your option) any later version. | ||
5 | # | ||
6 | # This program is distributed in the hope that it will be useful, | ||
7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
9 | # GNU Library General Public License for more details. | ||
10 | # | ||
11 | # You should have received a copy of the GNU General Public License | ||
12 | # along with this program; if not, write to the Free Software | ||
13 | # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
14 | |||
15 | # Copyright 2002-2006 Michael D. Stenner, Ryan Tomayko | ||
16 | |||
17 | # $Id: __init__.py,v 1.20 2006/09/22 00:58:55 mstenner Exp $ | ||
18 | |||
19 | """A high-level cross-protocol url-grabber. | ||
20 | |||
21 | Using urlgrabber, data can be fetched in three basic ways: | ||
22 | |||
23 | urlgrab(url) copy the file to the local filesystem | ||
24 | urlopen(url) open the remote file and return a file object | ||
25 | (like urllib2.urlopen) | ||
26 | urlread(url) return the contents of the file as a string | ||
27 | |||
28 | When using these functions (or methods), urlgrabber supports the | ||
29 | following features: | ||
30 | |||
31 | * identical behavior for http://, ftp://, and file:// urls | ||
32 | * http keepalive - faster downloads of many files by using | ||
33 | only a single connection | ||
34 | * byte ranges - fetch only a portion of the file | ||
35 | * reget - for a urlgrab, resume a partial download | ||
36 | * progress meters - the ability to report download progress | ||
37 | automatically, even when using urlopen! | ||
38 | * throttling - restrict bandwidth usage | ||
39 | * retries - automatically retry a download if it fails. The | ||
40 | number of retries and failure types are configurable. | ||
41 | * authenticated server access for http and ftp | ||
42 | * proxy support - support for authenticated http and ftp proxies | ||
43 | * mirror groups - treat a list of mirrors as a single source, | ||
44 | automatically switching mirrors if there is a failure. | ||
45 | """ | ||
46 | |||
47 | __version__ = '3.1.0' | ||
48 | __date__ = '2006/09/21' | ||
49 | __author__ = 'Michael D. Stenner <mstenner@linux.duke.edu>, ' \ | ||
50 | 'Ryan Tomayko <rtomayko@naeblis.cx>' | ||
51 | __url__ = 'http://linux.duke.edu/projects/urlgrabber/' | ||
52 | |||
53 | from grabber import urlgrab, urlopen, urlread | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/byterange.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/byterange.py new file mode 100644 index 0000000000..001b4e32d6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/byterange.py | |||
@@ -0,0 +1,463 @@ | |||
1 | # This library is free software; you can redistribute it and/or | ||
2 | # modify it under the terms of the GNU Lesser General Public | ||
3 | # License as published by the Free Software Foundation; either | ||
4 | # version 2.1 of the License, or (at your option) any later version. | ||
5 | # | ||
6 | # This library is distributed in the hope that it will be useful, | ||
7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
9 | # Lesser General Public License for more details. | ||
10 | # | ||
11 | # You should have received a copy of the GNU Lesser General Public | ||
12 | # License along with this library; if not, write to the | ||
13 | # Free Software Foundation, Inc., | ||
14 | # 59 Temple Place, Suite 330, | ||
15 | # Boston, MA 02111-1307 USA | ||
16 | |||
17 | # This file is part of urlgrabber, a high-level cross-protocol url-grabber | ||
18 | # Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko | ||
19 | |||
20 | # $Id: byterange.py,v 1.12 2006/07/20 20:15:58 mstenner Exp $ | ||
21 | |||
22 | import os | ||
23 | import stat | ||
24 | import urllib | ||
25 | import urllib2 | ||
26 | import rfc822 | ||
27 | |||
28 | DEBUG = None | ||
29 | |||
30 | try: | ||
31 | from cStringIO import StringIO | ||
32 | except ImportError, msg: | ||
33 | from StringIO import StringIO | ||
34 | |||
35 | class RangeError(IOError): | ||
36 | """Error raised when an unsatisfiable range is requested.""" | ||
37 | pass | ||
38 | |||
39 | class HTTPRangeHandler(urllib2.BaseHandler): | ||
40 | """Handler that enables HTTP Range headers. | ||
41 | |||
42 | This was extremely simple. The Range header is a HTTP feature to | ||
43 | begin with so all this class does is tell urllib2 that the | ||
44 | "206 Partial Content" reponse from the HTTP server is what we | ||
45 | expected. | ||
46 | |||
47 | Example: | ||
48 | import urllib2 | ||
49 | import byterange | ||
50 | |||
51 | range_handler = range.HTTPRangeHandler() | ||
52 | opener = urllib2.build_opener(range_handler) | ||
53 | |||
54 | # install it | ||
55 | urllib2.install_opener(opener) | ||
56 | |||
57 | # create Request and set Range header | ||
58 | req = urllib2.Request('http://www.python.org/') | ||
59 | req.header['Range'] = 'bytes=30-50' | ||
60 | f = urllib2.urlopen(req) | ||
61 | """ | ||
62 | |||
63 | def http_error_206(self, req, fp, code, msg, hdrs): | ||
64 | # 206 Partial Content Response | ||
65 | r = urllib.addinfourl(fp, hdrs, req.get_full_url()) | ||
66 | r.code = code | ||
67 | r.msg = msg | ||
68 | return r | ||
69 | |||
70 | def http_error_416(self, req, fp, code, msg, hdrs): | ||
71 | # HTTP's Range Not Satisfiable error | ||
72 | raise RangeError('Requested Range Not Satisfiable') | ||
73 | |||
74 | class HTTPSRangeHandler(HTTPRangeHandler): | ||
75 | """ Range Header support for HTTPS. """ | ||
76 | |||
77 | def https_error_206(self, req, fp, code, msg, hdrs): | ||
78 | return self.http_error_206(req, fp, code, msg, hdrs) | ||
79 | |||
80 | def https_error_416(self, req, fp, code, msg, hdrs): | ||
81 | self.https_error_416(req, fp, code, msg, hdrs) | ||
82 | |||
83 | class RangeableFileObject: | ||
84 | """File object wrapper to enable raw range handling. | ||
85 | This was implemented primarilary for handling range | ||
86 | specifications for file:// urls. This object effectively makes | ||
87 | a file object look like it consists only of a range of bytes in | ||
88 | the stream. | ||
89 | |||
90 | Examples: | ||
91 | # expose 10 bytes, starting at byte position 20, from | ||
92 | # /etc/aliases. | ||
93 | >>> fo = RangeableFileObject(file('/etc/passwd', 'r'), (20,30)) | ||
94 | # seek seeks within the range (to position 23 in this case) | ||
95 | >>> fo.seek(3) | ||
96 | # tell tells where your at _within the range_ (position 3 in | ||
97 | # this case) | ||
98 | >>> fo.tell() | ||
99 | # read EOFs if an attempt is made to read past the last | ||
100 | # byte in the range. the following will return only 7 bytes. | ||
101 | >>> fo.read(30) | ||
102 | """ | ||
103 | |||
104 | def __init__(self, fo, rangetup): | ||
105 | """Create a RangeableFileObject. | ||
106 | fo -- a file like object. only the read() method need be | ||
107 | supported but supporting an optimized seek() is | ||
108 | preferable. | ||
109 | rangetup -- a (firstbyte,lastbyte) tuple specifying the range | ||
110 | to work over. | ||
111 | The file object provided is assumed to be at byte offset 0. | ||
112 | """ | ||
113 | self.fo = fo | ||
114 | (self.firstbyte, self.lastbyte) = range_tuple_normalize(rangetup) | ||
115 | self.realpos = 0 | ||
116 | self._do_seek(self.firstbyte) | ||
117 | |||
118 | def __getattr__(self, name): | ||
119 | """This effectively allows us to wrap at the instance level. | ||
120 | Any attribute not found in _this_ object will be searched for | ||
121 | in self.fo. This includes methods.""" | ||
122 | if hasattr(self.fo, name): | ||
123 | return getattr(self.fo, name) | ||
124 | raise AttributeError, name | ||
125 | |||
126 | def tell(self): | ||
127 | """Return the position within the range. | ||
128 | This is different from fo.seek in that position 0 is the | ||
129 | first byte position of the range tuple. For example, if | ||
130 | this object was created with a range tuple of (500,899), | ||
131 | tell() will return 0 when at byte position 500 of the file. | ||
132 | """ | ||
133 | return (self.realpos - self.firstbyte) | ||
134 | |||
135 | def seek(self,offset,whence=0): | ||
136 | """Seek within the byte range. | ||
137 | Positioning is identical to that described under tell(). | ||
138 | """ | ||
139 | assert whence in (0, 1, 2) | ||
140 | if whence == 0: # absolute seek | ||
141 | realoffset = self.firstbyte + offset | ||
142 | elif whence == 1: # relative seek | ||
143 | realoffset = self.realpos + offset | ||
144 | elif whence == 2: # absolute from end of file | ||
145 | # XXX: are we raising the right Error here? | ||
146 | raise IOError('seek from end of file not supported.') | ||
147 | |||
148 | # do not allow seek past lastbyte in range | ||
149 | if self.lastbyte and (realoffset >= self.lastbyte): | ||
150 | realoffset = self.lastbyte | ||
151 | |||
152 | self._do_seek(realoffset - self.realpos) | ||
153 | |||
154 | def read(self, size=-1): | ||
155 | """Read within the range. | ||
156 | This method will limit the size read based on the range. | ||
157 | """ | ||
158 | size = self._calc_read_size(size) | ||
159 | rslt = self.fo.read(size) | ||
160 | self.realpos += len(rslt) | ||
161 | return rslt | ||
162 | |||
163 | def readline(self, size=-1): | ||
164 | """Read lines within the range. | ||
165 | This method will limit the size read based on the range. | ||
166 | """ | ||
167 | size = self._calc_read_size(size) | ||
168 | rslt = self.fo.readline(size) | ||
169 | self.realpos += len(rslt) | ||
170 | return rslt | ||
171 | |||
172 | def _calc_read_size(self, size): | ||
173 | """Handles calculating the amount of data to read based on | ||
174 | the range. | ||
175 | """ | ||
176 | if self.lastbyte: | ||
177 | if size > -1: | ||
178 | if ((self.realpos + size) >= self.lastbyte): | ||
179 | size = (self.lastbyte - self.realpos) | ||
180 | else: | ||
181 | size = (self.lastbyte - self.realpos) | ||
182 | return size | ||
183 | |||
184 | def _do_seek(self,offset): | ||
185 | """Seek based on whether wrapped object supports seek(). | ||
186 | offset is relative to the current position (self.realpos). | ||
187 | """ | ||
188 | assert offset >= 0 | ||
189 | if not hasattr(self.fo, 'seek'): | ||
190 | self._poor_mans_seek(offset) | ||
191 | else: | ||
192 | self.fo.seek(self.realpos + offset) | ||
193 | self.realpos+= offset | ||
194 | |||
195 | def _poor_mans_seek(self,offset): | ||
196 | """Seek by calling the wrapped file objects read() method. | ||
197 | This is used for file like objects that do not have native | ||
198 | seek support. The wrapped objects read() method is called | ||
199 | to manually seek to the desired position. | ||
200 | offset -- read this number of bytes from the wrapped | ||
201 | file object. | ||
202 | raise RangeError if we encounter EOF before reaching the | ||
203 | specified offset. | ||
204 | """ | ||
205 | pos = 0 | ||
206 | bufsize = 1024 | ||
207 | while pos < offset: | ||
208 | if (pos + bufsize) > offset: | ||
209 | bufsize = offset - pos | ||
210 | buf = self.fo.read(bufsize) | ||
211 | if len(buf) != bufsize: | ||
212 | raise RangeError('Requested Range Not Satisfiable') | ||
213 | pos+= bufsize | ||
214 | |||
215 | class FileRangeHandler(urllib2.FileHandler): | ||
216 | """FileHandler subclass that adds Range support. | ||
217 | This class handles Range headers exactly like an HTTP | ||
218 | server would. | ||
219 | """ | ||
220 | def open_local_file(self, req): | ||
221 | import mimetypes | ||
222 | import mimetools | ||
223 | host = req.get_host() | ||
224 | file = req.get_selector() | ||
225 | localfile = urllib.url2pathname(file) | ||
226 | stats = os.stat(localfile) | ||
227 | size = stats[stat.ST_SIZE] | ||
228 | modified = rfc822.formatdate(stats[stat.ST_MTIME]) | ||
229 | mtype = mimetypes.guess_type(file)[0] | ||
230 | if host: | ||
231 | host, port = urllib.splitport(host) | ||
232 | if port or socket.gethostbyname(host) not in self.get_names(): | ||
233 | raise urllib2.URLError('file not on local host') | ||
234 | fo = open(localfile,'rb') | ||
235 | brange = req.headers.get('Range',None) | ||
236 | brange = range_header_to_tuple(brange) | ||
237 | assert brange != () | ||
238 | if brange: | ||
239 | (fb,lb) = brange | ||
240 | if lb == '': lb = size | ||
241 | if fb < 0 or fb > size or lb > size: | ||
242 | raise RangeError('Requested Range Not Satisfiable') | ||
243 | size = (lb - fb) | ||
244 | fo = RangeableFileObject(fo, (fb,lb)) | ||
245 | headers = mimetools.Message(StringIO( | ||
246 | 'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' % | ||
247 | (mtype or 'text/plain', size, modified))) | ||
248 | return urllib.addinfourl(fo, headers, 'file:'+file) | ||
249 | |||
250 | |||
251 | # FTP Range Support | ||
252 | # Unfortunately, a large amount of base FTP code had to be copied | ||
253 | # from urllib and urllib2 in order to insert the FTP REST command. | ||
254 | # Code modifications for range support have been commented as | ||
255 | # follows: | ||
256 | # -- range support modifications start/end here | ||
257 | |||
258 | from urllib import splitport, splituser, splitpasswd, splitattr, \ | ||
259 | unquote, addclosehook, addinfourl | ||
260 | import ftplib | ||
261 | import socket | ||
262 | import sys | ||
263 | import ftplib | ||
264 | import mimetypes | ||
265 | import mimetools | ||
266 | |||
267 | class FTPRangeHandler(urllib2.FTPHandler): | ||
268 | def ftp_open(self, req): | ||
269 | host = req.get_host() | ||
270 | if not host: | ||
271 | raise IOError, ('ftp error', 'no host given') | ||
272 | host, port = splitport(host) | ||
273 | if port is None: | ||
274 | port = ftplib.FTP_PORT | ||
275 | |||
276 | # username/password handling | ||
277 | user, host = splituser(host) | ||
278 | if user: | ||
279 | user, passwd = splitpasswd(user) | ||
280 | else: | ||
281 | passwd = None | ||
282 | host = unquote(host) | ||
283 | user = unquote(user or '') | ||
284 | passwd = unquote(passwd or '') | ||
285 | |||
286 | try: | ||
287 | host = socket.gethostbyname(host) | ||
288 | except socket.error, msg: | ||
289 | raise urllib2.URLError(msg) | ||
290 | path, attrs = splitattr(req.get_selector()) | ||
291 | dirs = path.split('/') | ||
292 | dirs = map(unquote, dirs) | ||
293 | dirs, file = dirs[:-1], dirs[-1] | ||
294 | if dirs and not dirs[0]: | ||
295 | dirs = dirs[1:] | ||
296 | try: | ||
297 | fw = self.connect_ftp(user, passwd, host, port, dirs) | ||
298 | type = file and 'I' or 'D' | ||
299 | for attr in attrs: | ||
300 | attr, value = splitattr(attr) | ||
301 | if attr.lower() == 'type' and \ | ||
302 | value in ('a', 'A', 'i', 'I', 'd', 'D'): | ||
303 | type = value.upper() | ||
304 | |||
305 | # -- range support modifications start here | ||
306 | rest = None | ||
307 | range_tup = range_header_to_tuple(req.headers.get('Range',None)) | ||
308 | assert range_tup != () | ||
309 | if range_tup: | ||
310 | (fb,lb) = range_tup | ||
311 | if fb > 0: rest = fb | ||
312 | # -- range support modifications end here | ||
313 | |||
314 | fp, retrlen = fw.retrfile(file, type, rest) | ||
315 | |||
316 | # -- range support modifications start here | ||
317 | if range_tup: | ||
318 | (fb,lb) = range_tup | ||
319 | if lb == '': | ||
320 | if retrlen is None or retrlen == 0: | ||
321 | raise RangeError('Requested Range Not Satisfiable due to unobtainable file length.') | ||
322 | lb = retrlen | ||
323 | retrlen = lb - fb | ||
324 | if retrlen < 0: | ||
325 | # beginning of range is larger than file | ||
326 | raise RangeError('Requested Range Not Satisfiable') | ||
327 | else: | ||
328 | retrlen = lb - fb | ||
329 | fp = RangeableFileObject(fp, (0,retrlen)) | ||
330 | # -- range support modifications end here | ||
331 | |||
332 | headers = "" | ||
333 | mtype = mimetypes.guess_type(req.get_full_url())[0] | ||
334 | if mtype: | ||
335 | headers += "Content-Type: %s\n" % mtype | ||
336 | if retrlen is not None and retrlen >= 0: | ||
337 | headers += "Content-Length: %d\n" % retrlen | ||
338 | sf = StringIO(headers) | ||
339 | headers = mimetools.Message(sf) | ||
340 | return addinfourl(fp, headers, req.get_full_url()) | ||
341 | except ftplib.all_errors, msg: | ||
342 | raise IOError, ('ftp error', msg), sys.exc_info()[2] | ||
343 | |||
344 | def connect_ftp(self, user, passwd, host, port, dirs): | ||
345 | fw = ftpwrapper(user, passwd, host, port, dirs) | ||
346 | return fw | ||
347 | |||
348 | class ftpwrapper(urllib.ftpwrapper): | ||
349 | # range support note: | ||
350 | # this ftpwrapper code is copied directly from | ||
351 | # urllib. The only enhancement is to add the rest | ||
352 | # argument and pass it on to ftp.ntransfercmd | ||
353 | def retrfile(self, file, type, rest=None): | ||
354 | self.endtransfer() | ||
355 | if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1 | ||
356 | else: cmd = 'TYPE ' + type; isdir = 0 | ||
357 | try: | ||
358 | self.ftp.voidcmd(cmd) | ||
359 | except ftplib.all_errors: | ||
360 | self.init() | ||
361 | self.ftp.voidcmd(cmd) | ||
362 | conn = None | ||
363 | if file and not isdir: | ||
364 | # Use nlst to see if the file exists at all | ||
365 | try: | ||
366 | self.ftp.nlst(file) | ||
367 | except ftplib.error_perm, reason: | ||
368 | raise IOError, ('ftp error', reason), sys.exc_info()[2] | ||
369 | # Restore the transfer mode! | ||
370 | self.ftp.voidcmd(cmd) | ||
371 | # Try to retrieve as a file | ||
372 | try: | ||
373 | cmd = 'RETR ' + file | ||
374 | conn = self.ftp.ntransfercmd(cmd, rest) | ||
375 | except ftplib.error_perm, reason: | ||
376 | if str(reason)[:3] == '501': | ||
377 | # workaround for REST not supported error | ||
378 | fp, retrlen = self.retrfile(file, type) | ||
379 | fp = RangeableFileObject(fp, (rest,'')) | ||
380 | return (fp, retrlen) | ||
381 | elif str(reason)[:3] != '550': | ||
382 | raise IOError, ('ftp error', reason), sys.exc_info()[2] | ||
383 | if not conn: | ||
384 | # Set transfer mode to ASCII! | ||
385 | self.ftp.voidcmd('TYPE A') | ||
386 | # Try a directory listing | ||
387 | if file: cmd = 'LIST ' + file | ||
388 | else: cmd = 'LIST' | ||
389 | conn = self.ftp.ntransfercmd(cmd) | ||
390 | self.busy = 1 | ||
391 | # Pass back both a suitably decorated object and a retrieval length | ||
392 | return (addclosehook(conn[0].makefile('rb'), | ||
393 | self.endtransfer), conn[1]) | ||
394 | |||
395 | |||
396 | #################################################################### | ||
397 | # Range Tuple Functions | ||
398 | # XXX: These range tuple functions might go better in a class. | ||
399 | |||
400 | _rangere = None | ||
401 | def range_header_to_tuple(range_header): | ||
402 | """Get a (firstbyte,lastbyte) tuple from a Range header value. | ||
403 | |||
404 | Range headers have the form "bytes=<firstbyte>-<lastbyte>". This | ||
405 | function pulls the firstbyte and lastbyte values and returns | ||
406 | a (firstbyte,lastbyte) tuple. If lastbyte is not specified in | ||
407 | the header value, it is returned as an empty string in the | ||
408 | tuple. | ||
409 | |||
410 | Return None if range_header is None | ||
411 | Return () if range_header does not conform to the range spec | ||
412 | pattern. | ||
413 | |||
414 | """ | ||
415 | global _rangere | ||
416 | if range_header is None: return None | ||
417 | if _rangere is None: | ||
418 | import re | ||
419 | _rangere = re.compile(r'^bytes=(\d{1,})-(\d*)') | ||
420 | match = _rangere.match(range_header) | ||
421 | if match: | ||
422 | tup = range_tuple_normalize(match.group(1,2)) | ||
423 | if tup and tup[1]: | ||
424 | tup = (tup[0],tup[1]+1) | ||
425 | return tup | ||
426 | return () | ||
427 | |||
428 | def range_tuple_to_header(range_tup): | ||
429 | """Convert a range tuple to a Range header value. | ||
430 | Return a string of the form "bytes=<firstbyte>-<lastbyte>" or None | ||
431 | if no range is needed. | ||
432 | """ | ||
433 | if range_tup is None: return None | ||
434 | range_tup = range_tuple_normalize(range_tup) | ||
435 | if range_tup: | ||
436 | if range_tup[1]: | ||
437 | range_tup = (range_tup[0],range_tup[1] - 1) | ||
438 | return 'bytes=%s-%s' % range_tup | ||
439 | |||
440 | def range_tuple_normalize(range_tup): | ||
441 | """Normalize a (first_byte,last_byte) range tuple. | ||
442 | Return a tuple whose first element is guaranteed to be an int | ||
443 | and whose second element will be '' (meaning: the last byte) or | ||
444 | an int. Finally, return None if the normalized tuple == (0,'') | ||
445 | as that is equivelant to retrieving the entire file. | ||
446 | """ | ||
447 | if range_tup is None: return None | ||
448 | # handle first byte | ||
449 | fb = range_tup[0] | ||
450 | if fb in (None,''): fb = 0 | ||
451 | else: fb = int(fb) | ||
452 | # handle last byte | ||
453 | try: lb = range_tup[1] | ||
454 | except IndexError: lb = '' | ||
455 | else: | ||
456 | if lb is None: lb = '' | ||
457 | elif lb != '': lb = int(lb) | ||
458 | # check if range is over the entire file | ||
459 | if (fb,lb) == (0,''): return None | ||
460 | # check that the range is valid | ||
461 | if lb < fb: raise RangeError('Invalid byte range: %s-%s' % (fb,lb)) | ||
462 | return (fb,lb) | ||
463 | |||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/grabber.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/grabber.py new file mode 100644 index 0000000000..fefdab36f6 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/grabber.py | |||
@@ -0,0 +1,1477 @@ | |||
1 | # This library is free software; you can redistribute it and/or | ||
2 | # modify it under the terms of the GNU Lesser General Public | ||
3 | # License as published by the Free Software Foundation; either | ||
4 | # version 2.1 of the License, or (at your option) any later version. | ||
5 | # | ||
6 | # This library is distributed in the hope that it will be useful, | ||
7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
9 | # Lesser General Public License for more details. | ||
10 | # | ||
11 | # You should have received a copy of the GNU Lesser General Public | ||
12 | # License along with this library; if not, write to the | ||
13 | # Free Software Foundation, Inc., | ||
14 | # 59 Temple Place, Suite 330, | ||
15 | # Boston, MA 02111-1307 USA | ||
16 | |||
17 | # This file is part of urlgrabber, a high-level cross-protocol url-grabber | ||
18 | # Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko | ||
19 | |||
20 | """A high-level cross-protocol url-grabber. | ||
21 | |||
22 | GENERAL ARGUMENTS (kwargs) | ||
23 | |||
24 | Where possible, the module-level default is indicated, and legal | ||
25 | values are provided. | ||
26 | |||
27 | copy_local = 0 [0|1] | ||
28 | |||
29 | ignored except for file:// urls, in which case it specifies | ||
30 | whether urlgrab should still make a copy of the file, or simply | ||
31 | point to the existing copy. The module level default for this | ||
32 | option is 0. | ||
33 | |||
34 | close_connection = 0 [0|1] | ||
35 | |||
36 | tells URLGrabber to close the connection after a file has been | ||
37 | transfered. This is ignored unless the download happens with the | ||
38 | http keepalive handler (keepalive=1). Otherwise, the connection | ||
39 | is left open for further use. The module level default for this | ||
40 | option is 0 (keepalive connections will not be closed). | ||
41 | |||
42 | keepalive = 1 [0|1] | ||
43 | |||
44 | specifies whether keepalive should be used for HTTP/1.1 servers | ||
45 | that support it. The module level default for this option is 1 | ||
46 | (keepalive is enabled). | ||
47 | |||
48 | progress_obj = None | ||
49 | |||
50 | a class instance that supports the following methods: | ||
51 | po.start(filename, url, basename, length, text) | ||
52 | # length will be None if unknown | ||
53 | po.update(read) # read == bytes read so far | ||
54 | po.end() | ||
55 | |||
56 | text = None | ||
57 | |||
58 | specifies an alternativ text item in the beginning of the progress | ||
59 | bar line. If not given, the basename of the file is used. | ||
60 | |||
61 | throttle = 1.0 | ||
62 | |||
63 | a number - if it's an int, it's the bytes/second throttle limit. | ||
64 | If it's a float, it is first multiplied by bandwidth. If throttle | ||
65 | == 0, throttling is disabled. If None, the module-level default | ||
66 | (which can be set on default_grabber.throttle) is used. See | ||
67 | BANDWIDTH THROTTLING for more information. | ||
68 | |||
69 | timeout = None | ||
70 | |||
71 | a positive float expressing the number of seconds to wait for socket | ||
72 | operations. If the value is None or 0.0, socket operations will block | ||
73 | forever. Setting this option causes urlgrabber to call the settimeout | ||
74 | method on the Socket object used for the request. See the Python | ||
75 | documentation on settimeout for more information. | ||
76 | http://www.python.org/doc/current/lib/socket-objects.html | ||
77 | |||
78 | bandwidth = 0 | ||
79 | |||
80 | the nominal max bandwidth in bytes/second. If throttle is a float | ||
81 | and bandwidth == 0, throttling is disabled. If None, the | ||
82 | module-level default (which can be set on | ||
83 | default_grabber.bandwidth) is used. See BANDWIDTH THROTTLING for | ||
84 | more information. | ||
85 | |||
86 | range = None | ||
87 | |||
88 | a tuple of the form (first_byte, last_byte) describing a byte | ||
89 | range to retrieve. Either or both of the values may set to | ||
90 | None. If first_byte is None, byte offset 0 is assumed. If | ||
91 | last_byte is None, the last byte available is assumed. Note that | ||
92 | the range specification is python-like in that (0,10) will yeild | ||
93 | the first 10 bytes of the file. | ||
94 | |||
95 | If set to None, no range will be used. | ||
96 | |||
97 | reget = None [None|'simple'|'check_timestamp'] | ||
98 | |||
99 | whether to attempt to reget a partially-downloaded file. Reget | ||
100 | only applies to .urlgrab and (obviously) only if there is a | ||
101 | partially downloaded file. Reget has two modes: | ||
102 | |||
103 | 'simple' -- the local file will always be trusted. If there | ||
104 | are 100 bytes in the local file, then the download will always | ||
105 | begin 100 bytes into the requested file. | ||
106 | |||
107 | 'check_timestamp' -- the timestamp of the server file will be | ||
108 | compared to the timestamp of the local file. ONLY if the | ||
109 | local file is newer than or the same age as the server file | ||
110 | will reget be used. If the server file is newer, or the | ||
111 | timestamp is not returned, the entire file will be fetched. | ||
112 | |||
113 | NOTE: urlgrabber can do very little to verify that the partial | ||
114 | file on disk is identical to the beginning of the remote file. | ||
115 | You may want to either employ a custom "checkfunc" or simply avoid | ||
116 | using reget in situations where corruption is a concern. | ||
117 | |||
118 | user_agent = 'urlgrabber/VERSION' | ||
119 | |||
120 | a string, usually of the form 'AGENT/VERSION' that is provided to | ||
121 | HTTP servers in the User-agent header. The module level default | ||
122 | for this option is "urlgrabber/VERSION". | ||
123 | |||
124 | http_headers = None | ||
125 | |||
126 | a tuple of 2-tuples, each containing a header and value. These | ||
127 | will be used for http and https requests only. For example, you | ||
128 | can do | ||
129 | http_headers = (('Pragma', 'no-cache'),) | ||
130 | |||
131 | ftp_headers = None | ||
132 | |||
133 | this is just like http_headers, but will be used for ftp requests. | ||
134 | |||
135 | proxies = None | ||
136 | |||
137 | a dictionary that maps protocol schemes to proxy hosts. For | ||
138 | example, to use a proxy server on host "foo" port 3128 for http | ||
139 | and https URLs: | ||
140 | proxies={ 'http' : 'http://foo:3128', 'https' : 'http://foo:3128' } | ||
141 | note that proxy authentication information may be provided using | ||
142 | normal URL constructs: | ||
143 | proxies={ 'http' : 'http://user:host@foo:3128' } | ||
144 | Lastly, if proxies is None, the default environment settings will | ||
145 | be used. | ||
146 | |||
147 | prefix = None | ||
148 | |||
149 | a url prefix that will be prepended to all requested urls. For | ||
150 | example: | ||
151 | g = URLGrabber(prefix='http://foo.com/mirror/') | ||
152 | g.urlgrab('some/file.txt') | ||
153 | ## this will fetch 'http://foo.com/mirror/some/file.txt' | ||
154 | This option exists primarily to allow identical behavior to | ||
155 | MirrorGroup (and derived) instances. Note: a '/' will be inserted | ||
156 | if necessary, so you cannot specify a prefix that ends with a | ||
157 | partial file or directory name. | ||
158 | |||
159 | opener = None | ||
160 | |||
161 | Overrides the default urllib2.OpenerDirector provided to urllib2 | ||
162 | when making requests. This option exists so that the urllib2 | ||
163 | handler chain may be customized. Note that the range, reget, | ||
164 | proxy, and keepalive features require that custom handlers be | ||
165 | provided to urllib2 in order to function properly. If an opener | ||
166 | option is provided, no attempt is made by urlgrabber to ensure | ||
167 | chain integrity. You are responsible for ensuring that any | ||
168 | extension handlers are present if said features are required. | ||
169 | |||
170 | data = None | ||
171 | |||
172 | Only relevant for the HTTP family (and ignored for other | ||
173 | protocols), this allows HTTP POSTs. When the data kwarg is | ||
174 | present (and not None), an HTTP request will automatically become | ||
175 | a POST rather than GET. This is done by direct passthrough to | ||
176 | urllib2. If you use this, you may also want to set the | ||
177 | 'Content-length' and 'Content-type' headers with the http_headers | ||
178 | option. Note that python 2.2 handles the case of these | ||
179 | badly and if you do not use the proper case (shown here), your | ||
180 | values will be overridden with the defaults. | ||
181 | |||
182 | |||
183 | RETRY RELATED ARGUMENTS | ||
184 | |||
185 | retry = None | ||
186 | |||
187 | the number of times to retry the grab before bailing. If this is | ||
188 | zero, it will retry forever. This was intentional... really, it | ||
189 | was :). If this value is not supplied or is supplied but is None | ||
190 | retrying does not occur. | ||
191 | |||
192 | retrycodes = [-1,2,4,5,6,7] | ||
193 | |||
194 | a sequence of errorcodes (values of e.errno) for which it should | ||
195 | retry. See the doc on URLGrabError for more details on this. You | ||
196 | might consider modifying a copy of the default codes rather than | ||
197 | building yours from scratch so that if the list is extended in the | ||
198 | future (or one code is split into two) you can still enjoy the | ||
199 | benefits of the default list. You can do that with something like | ||
200 | this: | ||
201 | |||
202 | retrycodes = urlgrabber.grabber.URLGrabberOptions().retrycodes | ||
203 | if 12 not in retrycodes: | ||
204 | retrycodes.append(12) | ||
205 | |||
206 | checkfunc = None | ||
207 | |||
208 | a function to do additional checks. This defaults to None, which | ||
209 | means no additional checking. The function should simply return | ||
210 | on a successful check. It should raise URLGrabError on an | ||
211 | unsuccessful check. Raising of any other exception will be | ||
212 | considered immediate failure and no retries will occur. | ||
213 | |||
214 | If it raises URLGrabError, the error code will determine the retry | ||
215 | behavior. Negative error numbers are reserved for use by these | ||
216 | passed in functions, so you can use many negative numbers for | ||
217 | different types of failure. By default, -1 results in a retry, | ||
218 | but this can be customized with retrycodes. | ||
219 | |||
220 | If you simply pass in a function, it will be given exactly one | ||
221 | argument: a CallbackObject instance with the .url attribute | ||
222 | defined and either .filename (for urlgrab) or .data (for urlread). | ||
223 | For urlgrab, .filename is the name of the local file. For | ||
224 | urlread, .data is the actual string data. If you need other | ||
225 | arguments passed to the callback (program state of some sort), you | ||
226 | can do so like this: | ||
227 | |||
228 | checkfunc=(function, ('arg1', 2), {'kwarg': 3}) | ||
229 | |||
230 | if the downloaded file has filename /tmp/stuff, then this will | ||
231 | result in this call (for urlgrab): | ||
232 | |||
233 | function(obj, 'arg1', 2, kwarg=3) | ||
234 | # obj.filename = '/tmp/stuff' | ||
235 | # obj.url = 'http://foo.com/stuff' | ||
236 | |||
237 | NOTE: both the "args" tuple and "kwargs" dict must be present if | ||
238 | you use this syntax, but either (or both) can be empty. | ||
239 | |||
240 | failure_callback = None | ||
241 | |||
242 | The callback that gets called during retries when an attempt to | ||
243 | fetch a file fails. The syntax for specifying the callback is | ||
244 | identical to checkfunc, except for the attributes defined in the | ||
245 | CallbackObject instance. The attributes for failure_callback are: | ||
246 | |||
247 | exception = the raised exception | ||
248 | url = the url we're trying to fetch | ||
249 | tries = the number of tries so far (including this one) | ||
250 | retry = the value of the retry option | ||
251 | |||
252 | The callback is present primarily to inform the calling program of | ||
253 | the failure, but if it raises an exception (including the one it's | ||
254 | passed) that exception will NOT be caught and will therefore cause | ||
255 | future retries to be aborted. | ||
256 | |||
257 | The callback is called for EVERY failure, including the last one. | ||
258 | On the last try, the callback can raise an alternate exception, | ||
259 | but it cannot (without severe trickiness) prevent the exception | ||
260 | from being raised. | ||
261 | |||
262 | interrupt_callback = None | ||
263 | |||
264 | This callback is called if KeyboardInterrupt is received at any | ||
265 | point in the transfer. Basically, this callback can have three | ||
266 | impacts on the fetch process based on the way it exits: | ||
267 | |||
268 | 1) raise no exception: the current fetch will be aborted, but | ||
269 | any further retries will still take place | ||
270 | |||
271 | 2) raise a URLGrabError: if you're using a MirrorGroup, then | ||
272 | this will prompt a failover to the next mirror according to | ||
273 | the behavior of the MirrorGroup subclass. It is recommended | ||
274 | that you raise URLGrabError with code 15, 'user abort'. If | ||
275 | you are NOT using a MirrorGroup subclass, then this is the | ||
276 | same as (3). | ||
277 | |||
278 | 3) raise some other exception (such as KeyboardInterrupt), which | ||
279 | will not be caught at either the grabber or mirror levels. | ||
280 | That is, it will be raised up all the way to the caller. | ||
281 | |||
282 | This callback is very similar to failure_callback. They are | ||
283 | passed the same arguments, so you could use the same function for | ||
284 | both. | ||
285 | |||
286 | urlparser = URLParser() | ||
287 | |||
288 | The URLParser class handles pre-processing of URLs, including | ||
289 | auth-handling for user/pass encoded in http urls, file handing | ||
290 | (that is, filenames not sent as a URL), and URL quoting. If you | ||
291 | want to override any of this behavior, you can pass in a | ||
292 | replacement instance. See also the 'quote' option. | ||
293 | |||
294 | quote = None | ||
295 | |||
296 | Whether or not to quote the path portion of a url. | ||
297 | quote = 1 -> quote the URLs (they're not quoted yet) | ||
298 | quote = 0 -> do not quote them (they're already quoted) | ||
299 | quote = None -> guess what to do | ||
300 | |||
301 | This option only affects proper urls like 'file:///etc/passwd'; it | ||
302 | does not affect 'raw' filenames like '/etc/passwd'. The latter | ||
303 | will always be quoted as they are converted to URLs. Also, only | ||
304 | the path part of a url is quoted. If you need more fine-grained | ||
305 | control, you should probably subclass URLParser and pass it in via | ||
306 | the 'urlparser' option. | ||
307 | |||
308 | BANDWIDTH THROTTLING | ||
309 | |||
310 | urlgrabber supports throttling via two values: throttle and | ||
311 | bandwidth Between the two, you can either specify and absolute | ||
312 | throttle threshold or specify a theshold as a fraction of maximum | ||
313 | available bandwidth. | ||
314 | |||
315 | throttle is a number - if it's an int, it's the bytes/second | ||
316 | throttle limit. If it's a float, it is first multiplied by | ||
317 | bandwidth. If throttle == 0, throttling is disabled. If None, the | ||
318 | module-level default (which can be set with set_throttle) is used. | ||
319 | |||
320 | bandwidth is the nominal max bandwidth in bytes/second. If throttle | ||
321 | is a float and bandwidth == 0, throttling is disabled. If None, the | ||
322 | module-level default (which can be set with set_bandwidth) is used. | ||
323 | |||
324 | THROTTLING EXAMPLES: | ||
325 | |||
326 | Lets say you have a 100 Mbps connection. This is (about) 10^8 bits | ||
327 | per second, or 12,500,000 Bytes per second. You have a number of | ||
328 | throttling options: | ||
329 | |||
330 | *) set_bandwidth(12500000); set_throttle(0.5) # throttle is a float | ||
331 | |||
332 | This will limit urlgrab to use half of your available bandwidth. | ||
333 | |||
334 | *) set_throttle(6250000) # throttle is an int | ||
335 | |||
336 | This will also limit urlgrab to use half of your available | ||
337 | bandwidth, regardless of what bandwidth is set to. | ||
338 | |||
339 | *) set_throttle(6250000); set_throttle(1.0) # float | ||
340 | |||
341 | Use half your bandwidth | ||
342 | |||
343 | *) set_throttle(6250000); set_throttle(2.0) # float | ||
344 | |||
345 | Use up to 12,500,000 Bytes per second (your nominal max bandwidth) | ||
346 | |||
347 | *) set_throttle(6250000); set_throttle(0) # throttle = 0 | ||
348 | |||
349 | Disable throttling - this is more efficient than a very large | ||
350 | throttle setting. | ||
351 | |||
352 | *) set_throttle(0); set_throttle(1.0) # throttle is float, bandwidth = 0 | ||
353 | |||
354 | Disable throttling - this is the default when the module is loaded. | ||
355 | |||
356 | SUGGESTED AUTHOR IMPLEMENTATION (THROTTLING) | ||
357 | |||
358 | While this is flexible, it's not extremely obvious to the user. I | ||
359 | suggest you implement a float throttle as a percent to make the | ||
360 | distinction between absolute and relative throttling very explicit. | ||
361 | |||
362 | Also, you may want to convert the units to something more convenient | ||
363 | than bytes/second, such as kbps or kB/s, etc. | ||
364 | |||
365 | """ | ||
366 | |||
367 | # $Id: grabber.py,v 1.48 2006/09/22 00:58:05 mstenner Exp $ | ||
368 | |||
369 | import os | ||
370 | import os.path | ||
371 | import sys | ||
372 | import urlparse | ||
373 | import rfc822 | ||
374 | import time | ||
375 | import string | ||
376 | import urllib | ||
377 | import urllib2 | ||
378 | from stat import * # S_* and ST_* | ||
379 | |||
380 | ######################################################################## | ||
381 | # MODULE INITIALIZATION | ||
382 | ######################################################################## | ||
383 | try: | ||
384 | exec('from ' + (__name__.split('.'))[0] + ' import __version__') | ||
385 | except: | ||
386 | __version__ = '???' | ||
387 | |||
388 | import sslfactory | ||
389 | |||
390 | auth_handler = urllib2.HTTPBasicAuthHandler( \ | ||
391 | urllib2.HTTPPasswordMgrWithDefaultRealm()) | ||
392 | |||
393 | try: | ||
394 | from i18n import _ | ||
395 | except ImportError, msg: | ||
396 | def _(st): return st | ||
397 | |||
398 | try: | ||
399 | from httplib import HTTPException | ||
400 | except ImportError, msg: | ||
401 | HTTPException = None | ||
402 | |||
403 | try: | ||
404 | # This is a convenient way to make keepalive optional. | ||
405 | # Just rename the module so it can't be imported. | ||
406 | import keepalive | ||
407 | from keepalive import HTTPHandler, HTTPSHandler | ||
408 | have_keepalive = True | ||
409 | except ImportError, msg: | ||
410 | have_keepalive = False | ||
411 | |||
412 | try: | ||
413 | # add in range support conditionally too | ||
414 | import byterange | ||
415 | from byterange import HTTPRangeHandler, HTTPSRangeHandler, \ | ||
416 | FileRangeHandler, FTPRangeHandler, range_tuple_normalize, \ | ||
417 | range_tuple_to_header, RangeError | ||
418 | except ImportError, msg: | ||
419 | range_handlers = () | ||
420 | RangeError = None | ||
421 | have_range = 0 | ||
422 | else: | ||
423 | range_handlers = (HTTPRangeHandler(), HTTPSRangeHandler(), | ||
424 | FileRangeHandler(), FTPRangeHandler()) | ||
425 | have_range = 1 | ||
426 | |||
427 | |||
428 | # check whether socket timeout support is available (Python >= 2.3) | ||
429 | import socket | ||
430 | try: | ||
431 | TimeoutError = socket.timeout | ||
432 | have_socket_timeout = True | ||
433 | except AttributeError: | ||
434 | TimeoutError = None | ||
435 | have_socket_timeout = False | ||
436 | |||
437 | ######################################################################## | ||
438 | # functions for debugging output. These functions are here because they | ||
439 | # are also part of the module initialization. | ||
440 | DEBUG = None | ||
441 | def set_logger(DBOBJ): | ||
442 | """Set the DEBUG object. This is called by _init_default_logger when | ||
443 | the environment variable URLGRABBER_DEBUG is set, but can also be | ||
444 | called by a calling program. Basically, if the calling program uses | ||
445 | the logging module and would like to incorporate urlgrabber logging, | ||
446 | then it can do so this way. It's probably not necessary as most | ||
447 | internal logging is only for debugging purposes. | ||
448 | |||
449 | The passed-in object should be a logging.Logger instance. It will | ||
450 | be pushed into the keepalive and byterange modules if they're | ||
451 | being used. The mirror module pulls this object in on import, so | ||
452 | you will need to manually push into it. In fact, you may find it | ||
453 | tidier to simply push your logging object (or objects) into each | ||
454 | of these modules independently. | ||
455 | """ | ||
456 | |||
457 | global DEBUG | ||
458 | DEBUG = DBOBJ | ||
459 | if have_keepalive and keepalive.DEBUG is None: | ||
460 | keepalive.DEBUG = DBOBJ | ||
461 | if have_range and byterange.DEBUG is None: | ||
462 | byterange.DEBUG = DBOBJ | ||
463 | if sslfactory.DEBUG is None: | ||
464 | sslfactory.DEBUG = DBOBJ | ||
465 | |||
466 | def _init_default_logger(): | ||
467 | '''Examines the environment variable URLGRABBER_DEBUG and creates | ||
468 | a logging object (logging.logger) based on the contents. It takes | ||
469 | the form | ||
470 | |||
471 | URLGRABBER_DEBUG=level,filename | ||
472 | |||
473 | where "level" can be either an integer or a log level from the | ||
474 | logging module (DEBUG, INFO, etc). If the integer is zero or | ||
475 | less, logging will be disabled. Filename is the filename where | ||
476 | logs will be sent. If it is "-", then stdout will be used. If | ||
477 | the filename is empty or missing, stderr will be used. If the | ||
478 | variable cannot be processed or the logging module cannot be | ||
479 | imported (python < 2.3) then logging will be disabled. Here are | ||
480 | some examples: | ||
481 | |||
482 | URLGRABBER_DEBUG=1,debug.txt # log everything to debug.txt | ||
483 | URLGRABBER_DEBUG=WARNING,- # log warning and higher to stdout | ||
484 | URLGRABBER_DEBUG=INFO # log info and higher to stderr | ||
485 | |||
486 | This funtion is called during module initialization. It is not | ||
487 | intended to be called from outside. The only reason it is a | ||
488 | function at all is to keep the module-level namespace tidy and to | ||
489 | collect the code into a nice block.''' | ||
490 | |||
491 | try: | ||
492 | dbinfo = os.environ['URLGRABBER_DEBUG'].split(',') | ||
493 | import logging | ||
494 | level = logging._levelNames.get(dbinfo[0], int(dbinfo[0])) | ||
495 | if level < 1: raise ValueError() | ||
496 | |||
497 | formatter = logging.Formatter('%(asctime)s %(message)s') | ||
498 | if len(dbinfo) > 1: filename = dbinfo[1] | ||
499 | else: filename = '' | ||
500 | if filename == '': handler = logging.StreamHandler(sys.stderr) | ||
501 | elif filename == '-': handler = logging.StreamHandler(sys.stdout) | ||
502 | else: handler = logging.FileHandler(filename) | ||
503 | handler.setFormatter(formatter) | ||
504 | DBOBJ = logging.getLogger('urlgrabber') | ||
505 | DBOBJ.addHandler(handler) | ||
506 | DBOBJ.setLevel(level) | ||
507 | except (KeyError, ImportError, ValueError): | ||
508 | DBOBJ = None | ||
509 | set_logger(DBOBJ) | ||
510 | |||
511 | _init_default_logger() | ||
512 | ######################################################################## | ||
513 | # END MODULE INITIALIZATION | ||
514 | ######################################################################## | ||
515 | |||
516 | |||
517 | |||
518 | class URLGrabError(IOError): | ||
519 | """ | ||
520 | URLGrabError error codes: | ||
521 | |||
522 | URLGrabber error codes (0 -- 255) | ||
523 | 0 - everything looks good (you should never see this) | ||
524 | 1 - malformed url | ||
525 | 2 - local file doesn't exist | ||
526 | 3 - request for non-file local file (dir, etc) | ||
527 | 4 - IOError on fetch | ||
528 | 5 - OSError on fetch | ||
529 | 6 - no content length header when we expected one | ||
530 | 7 - HTTPException | ||
531 | 8 - Exceeded read limit (for urlread) | ||
532 | 9 - Requested byte range not satisfiable. | ||
533 | 10 - Byte range requested, but range support unavailable | ||
534 | 11 - Illegal reget mode | ||
535 | 12 - Socket timeout | ||
536 | 13 - malformed proxy url | ||
537 | 14 - HTTPError (includes .code and .exception attributes) | ||
538 | 15 - user abort | ||
539 | |||
540 | MirrorGroup error codes (256 -- 511) | ||
541 | 256 - No more mirrors left to try | ||
542 | |||
543 | Custom (non-builtin) classes derived from MirrorGroup (512 -- 767) | ||
544 | [ this range reserved for application-specific error codes ] | ||
545 | |||
546 | Retry codes (< 0) | ||
547 | -1 - retry the download, unknown reason | ||
548 | |||
549 | Note: to test which group a code is in, you can simply do integer | ||
550 | division by 256: e.errno / 256 | ||
551 | |||
552 | Negative codes are reserved for use by functions passed in to | ||
553 | retrygrab with checkfunc. The value -1 is built in as a generic | ||
554 | retry code and is already included in the retrycodes list. | ||
555 | Therefore, you can create a custom check function that simply | ||
556 | returns -1 and the fetch will be re-tried. For more customized | ||
557 | retries, you can use other negative number and include them in | ||
558 | retry-codes. This is nice for outputting useful messages about | ||
559 | what failed. | ||
560 | |||
561 | You can use these error codes like so: | ||
562 | try: urlgrab(url) | ||
563 | except URLGrabError, e: | ||
564 | if e.errno == 3: ... | ||
565 | # or | ||
566 | print e.strerror | ||
567 | # or simply | ||
568 | print e #### print '[Errno %i] %s' % (e.errno, e.strerror) | ||
569 | """ | ||
570 | pass | ||
571 | |||
572 | class CallbackObject: | ||
573 | """Container for returned callback data. | ||
574 | |||
575 | This is currently a dummy class into which urlgrabber can stuff | ||
576 | information for passing to callbacks. This way, the prototype for | ||
577 | all callbacks is the same, regardless of the data that will be | ||
578 | passed back. Any function that accepts a callback function as an | ||
579 | argument SHOULD document what it will define in this object. | ||
580 | |||
581 | It is possible that this class will have some greater | ||
582 | functionality in the future. | ||
583 | """ | ||
584 | def __init__(self, **kwargs): | ||
585 | self.__dict__.update(kwargs) | ||
586 | |||
587 | def urlgrab(url, filename=None, **kwargs): | ||
588 | """grab the file at <url> and make a local copy at <filename> | ||
589 | If filename is none, the basename of the url is used. | ||
590 | urlgrab returns the filename of the local file, which may be different | ||
591 | from the passed-in filename if the copy_local kwarg == 0. | ||
592 | |||
593 | See module documentation for a description of possible kwargs. | ||
594 | """ | ||
595 | return default_grabber.urlgrab(url, filename, **kwargs) | ||
596 | |||
597 | def urlopen(url, **kwargs): | ||
598 | """open the url and return a file object | ||
599 | If a progress object or throttle specifications exist, then | ||
600 | a special file object will be returned that supports them. | ||
601 | The file object can be treated like any other file object. | ||
602 | |||
603 | See module documentation for a description of possible kwargs. | ||
604 | """ | ||
605 | return default_grabber.urlopen(url, **kwargs) | ||
606 | |||
607 | def urlread(url, limit=None, **kwargs): | ||
608 | """read the url into a string, up to 'limit' bytes | ||
609 | If the limit is exceeded, an exception will be thrown. Note that urlread | ||
610 | is NOT intended to be used as a way of saying "I want the first N bytes" | ||
611 | but rather 'read the whole file into memory, but don't use too much' | ||
612 | |||
613 | See module documentation for a description of possible kwargs. | ||
614 | """ | ||
615 | return default_grabber.urlread(url, limit, **kwargs) | ||
616 | |||
617 | |||
618 | class URLParser: | ||
619 | """Process the URLs before passing them to urllib2. | ||
620 | |||
621 | This class does several things: | ||
622 | |||
623 | * add any prefix | ||
624 | * translate a "raw" file to a proper file: url | ||
625 | * handle any http or https auth that's encoded within the url | ||
626 | * quote the url | ||
627 | |||
628 | Only the "parse" method is called directly, and it calls sub-methods. | ||
629 | |||
630 | An instance of this class is held in the options object, which | ||
631 | means that it's easy to change the behavior by sub-classing and | ||
632 | passing the replacement in. It need only have a method like: | ||
633 | |||
634 | url, parts = urlparser.parse(url, opts) | ||
635 | """ | ||
636 | |||
637 | def parse(self, url, opts): | ||
638 | """parse the url and return the (modified) url and its parts | ||
639 | |||
640 | Note: a raw file WILL be quoted when it's converted to a URL. | ||
641 | However, other urls (ones which come with a proper scheme) may | ||
642 | or may not be quoted according to opts.quote | ||
643 | |||
644 | opts.quote = 1 --> quote it | ||
645 | opts.quote = 0 --> do not quote it | ||
646 | opts.quote = None --> guess | ||
647 | """ | ||
648 | quote = opts.quote | ||
649 | |||
650 | if opts.prefix: | ||
651 | url = self.add_prefix(url, opts.prefix) | ||
652 | |||
653 | parts = urlparse.urlparse(url) | ||
654 | (scheme, host, path, parm, query, frag) = parts | ||
655 | |||
656 | if not scheme or (len(scheme) == 1 and scheme in string.letters): | ||
657 | # if a scheme isn't specified, we guess that it's "file:" | ||
658 | if url[0] not in '/\\': url = os.path.abspath(url) | ||
659 | url = 'file:' + urllib.pathname2url(url) | ||
660 | parts = urlparse.urlparse(url) | ||
661 | quote = 0 # pathname2url quotes, so we won't do it again | ||
662 | |||
663 | if scheme in ['http', 'https']: | ||
664 | parts = self.process_http(parts) | ||
665 | |||
666 | if quote is None: | ||
667 | quote = self.guess_should_quote(parts) | ||
668 | if quote: | ||
669 | parts = self.quote(parts) | ||
670 | |||
671 | url = urlparse.urlunparse(parts) | ||
672 | return url, parts | ||
673 | |||
674 | def add_prefix(self, url, prefix): | ||
675 | if prefix[-1] == '/' or url[0] == '/': | ||
676 | url = prefix + url | ||
677 | else: | ||
678 | url = prefix + '/' + url | ||
679 | return url | ||
680 | |||
681 | def process_http(self, parts): | ||
682 | (scheme, host, path, parm, query, frag) = parts | ||
683 | |||
684 | if '@' in host and auth_handler: | ||
685 | try: | ||
686 | user_pass, host = host.split('@', 1) | ||
687 | if ':' in user_pass: | ||
688 | user, password = user_pass.split(':', 1) | ||
689 | except ValueError, e: | ||
690 | raise URLGrabError(1, _('Bad URL: %s') % url) | ||
691 | if DEBUG: DEBUG.info('adding HTTP auth: %s, XXXXXXXX', user) | ||
692 | auth_handler.add_password(None, host, user, password) | ||
693 | |||
694 | return (scheme, host, path, parm, query, frag) | ||
695 | |||
696 | def quote(self, parts): | ||
697 | """quote the URL | ||
698 | |||
699 | This method quotes ONLY the path part. If you need to quote | ||
700 | other parts, you should override this and pass in your derived | ||
701 | class. The other alternative is to quote other parts before | ||
702 | passing into urlgrabber. | ||
703 | """ | ||
704 | (scheme, host, path, parm, query, frag) = parts | ||
705 | path = urllib.quote(path) | ||
706 | return (scheme, host, path, parm, query, frag) | ||
707 | |||
708 | hexvals = '0123456789ABCDEF' | ||
709 | def guess_should_quote(self, parts): | ||
710 | """ | ||
711 | Guess whether we should quote a path. This amounts to | ||
712 | guessing whether it's already quoted. | ||
713 | |||
714 | find ' ' -> 1 | ||
715 | find '%' -> 1 | ||
716 | find '%XX' -> 0 | ||
717 | else -> 1 | ||
718 | """ | ||
719 | (scheme, host, path, parm, query, frag) = parts | ||
720 | if ' ' in path: | ||
721 | return 1 | ||
722 | ind = string.find(path, '%') | ||
723 | if ind > -1: | ||
724 | while ind > -1: | ||
725 | if len(path) < ind+3: | ||
726 | return 1 | ||
727 | code = path[ind+1:ind+3].upper() | ||
728 | if code[0] not in self.hexvals or \ | ||
729 | code[1] not in self.hexvals: | ||
730 | return 1 | ||
731 | ind = string.find(path, '%', ind+1) | ||
732 | return 0 | ||
733 | return 1 | ||
734 | |||
735 | class URLGrabberOptions: | ||
736 | """Class to ease kwargs handling.""" | ||
737 | |||
738 | def __init__(self, delegate=None, **kwargs): | ||
739 | """Initialize URLGrabberOptions object. | ||
740 | Set default values for all options and then update options specified | ||
741 | in kwargs. | ||
742 | """ | ||
743 | self.delegate = delegate | ||
744 | if delegate is None: | ||
745 | self._set_defaults() | ||
746 | self._set_attributes(**kwargs) | ||
747 | |||
748 | def __getattr__(self, name): | ||
749 | if self.delegate and hasattr(self.delegate, name): | ||
750 | return getattr(self.delegate, name) | ||
751 | raise AttributeError, name | ||
752 | |||
753 | def raw_throttle(self): | ||
754 | """Calculate raw throttle value from throttle and bandwidth | ||
755 | values. | ||
756 | """ | ||
757 | if self.throttle <= 0: | ||
758 | return 0 | ||
759 | elif type(self.throttle) == type(0): | ||
760 | return float(self.throttle) | ||
761 | else: # throttle is a float | ||
762 | return self.bandwidth * self.throttle | ||
763 | |||
764 | def derive(self, **kwargs): | ||
765 | """Create a derived URLGrabberOptions instance. | ||
766 | This method creates a new instance and overrides the | ||
767 | options specified in kwargs. | ||
768 | """ | ||
769 | return URLGrabberOptions(delegate=self, **kwargs) | ||
770 | |||
771 | def _set_attributes(self, **kwargs): | ||
772 | """Update object attributes with those provided in kwargs.""" | ||
773 | self.__dict__.update(kwargs) | ||
774 | if have_range and kwargs.has_key('range'): | ||
775 | # normalize the supplied range value | ||
776 | self.range = range_tuple_normalize(self.range) | ||
777 | if not self.reget in [None, 'simple', 'check_timestamp']: | ||
778 | raise URLGrabError(11, _('Illegal reget mode: %s') \ | ||
779 | % (self.reget, )) | ||
780 | |||
781 | def _set_defaults(self): | ||
782 | """Set all options to their default values. | ||
783 | When adding new options, make sure a default is | ||
784 | provided here. | ||
785 | """ | ||
786 | self.progress_obj = None | ||
787 | self.throttle = 1.0 | ||
788 | self.bandwidth = 0 | ||
789 | self.retry = None | ||
790 | self.retrycodes = [-1,2,4,5,6,7] | ||
791 | self.checkfunc = None | ||
792 | self.copy_local = 0 | ||
793 | self.close_connection = 0 | ||
794 | self.range = None | ||
795 | self.user_agent = 'urlgrabber/%s' % __version__ | ||
796 | self.keepalive = 1 | ||
797 | self.proxies = None | ||
798 | self.reget = None | ||
799 | self.failure_callback = None | ||
800 | self.interrupt_callback = None | ||
801 | self.prefix = None | ||
802 | self.opener = None | ||
803 | self.cache_openers = True | ||
804 | self.timeout = None | ||
805 | self.text = None | ||
806 | self.http_headers = None | ||
807 | self.ftp_headers = None | ||
808 | self.data = None | ||
809 | self.urlparser = URLParser() | ||
810 | self.quote = None | ||
811 | self.ssl_ca_cert = None | ||
812 | self.ssl_context = None | ||
813 | |||
814 | class URLGrabber: | ||
815 | """Provides easy opening of URLs with a variety of options. | ||
816 | |||
817 | All options are specified as kwargs. Options may be specified when | ||
818 | the class is created and may be overridden on a per request basis. | ||
819 | |||
820 | New objects inherit default values from default_grabber. | ||
821 | """ | ||
822 | |||
823 | def __init__(self, **kwargs): | ||
824 | self.opts = URLGrabberOptions(**kwargs) | ||
825 | |||
826 | def _retry(self, opts, func, *args): | ||
827 | tries = 0 | ||
828 | while 1: | ||
829 | # there are only two ways out of this loop. The second has | ||
830 | # several "sub-ways" | ||
831 | # 1) via the return in the "try" block | ||
832 | # 2) by some exception being raised | ||
833 | # a) an excepton is raised that we don't "except" | ||
834 | # b) a callback raises ANY exception | ||
835 | # c) we're not retry-ing or have run out of retries | ||
836 | # d) the URLGrabError code is not in retrycodes | ||
837 | # beware of infinite loops :) | ||
838 | tries = tries + 1 | ||
839 | exception = None | ||
840 | retrycode = None | ||
841 | callback = None | ||
842 | if DEBUG: DEBUG.info('attempt %i/%s: %s', | ||
843 | tries, opts.retry, args[0]) | ||
844 | try: | ||
845 | r = apply(func, (opts,) + args, {}) | ||
846 | if DEBUG: DEBUG.info('success') | ||
847 | return r | ||
848 | except URLGrabError, e: | ||
849 | exception = e | ||
850 | callback = opts.failure_callback | ||
851 | retrycode = e.errno | ||
852 | except KeyboardInterrupt, e: | ||
853 | exception = e | ||
854 | callback = opts.interrupt_callback | ||
855 | |||
856 | if DEBUG: DEBUG.info('exception: %s', exception) | ||
857 | if callback: | ||
858 | if DEBUG: DEBUG.info('calling callback: %s', callback) | ||
859 | cb_func, cb_args, cb_kwargs = self._make_callback(callback) | ||
860 | obj = CallbackObject(exception=exception, url=args[0], | ||
861 | tries=tries, retry=opts.retry) | ||
862 | cb_func(obj, *cb_args, **cb_kwargs) | ||
863 | |||
864 | if (opts.retry is None) or (tries == opts.retry): | ||
865 | if DEBUG: DEBUG.info('retries exceeded, re-raising') | ||
866 | raise | ||
867 | |||
868 | if (retrycode is not None) and (retrycode not in opts.retrycodes): | ||
869 | if DEBUG: DEBUG.info('retrycode (%i) not in list %s, re-raising', | ||
870 | retrycode, opts.retrycodes) | ||
871 | raise | ||
872 | |||
873 | def urlopen(self, url, **kwargs): | ||
874 | """open the url and return a file object | ||
875 | If a progress object or throttle value specified when this | ||
876 | object was created, then a special file object will be | ||
877 | returned that supports them. The file object can be treated | ||
878 | like any other file object. | ||
879 | """ | ||
880 | opts = self.opts.derive(**kwargs) | ||
881 | (url,parts) = opts.urlparser.parse(url, opts) | ||
882 | def retryfunc(opts, url): | ||
883 | return URLGrabberFileObject(url, filename=None, opts=opts) | ||
884 | return self._retry(opts, retryfunc, url) | ||
885 | |||
886 | def urlgrab(self, url, filename=None, **kwargs): | ||
887 | """grab the file at <url> and make a local copy at <filename> | ||
888 | If filename is none, the basename of the url is used. | ||
889 | urlgrab returns the filename of the local file, which may be | ||
890 | different from the passed-in filename if copy_local == 0. | ||
891 | """ | ||
892 | opts = self.opts.derive(**kwargs) | ||
893 | (url,parts) = opts.urlparser.parse(url, opts) | ||
894 | (scheme, host, path, parm, query, frag) = parts | ||
895 | if filename is None: | ||
896 | filename = os.path.basename( urllib.unquote(path) ) | ||
897 | if scheme == 'file' and not opts.copy_local: | ||
898 | # just return the name of the local file - don't make a | ||
899 | # copy currently | ||
900 | path = urllib.url2pathname(path) | ||
901 | if host: | ||
902 | path = os.path.normpath('//' + host + path) | ||
903 | if not os.path.exists(path): | ||
904 | raise URLGrabError(2, | ||
905 | _('Local file does not exist: %s') % (path, )) | ||
906 | elif not os.path.isfile(path): | ||
907 | raise URLGrabError(3, | ||
908 | _('Not a normal file: %s') % (path, )) | ||
909 | elif not opts.range: | ||
910 | return path | ||
911 | |||
912 | def retryfunc(opts, url, filename): | ||
913 | fo = URLGrabberFileObject(url, filename, opts) | ||
914 | try: | ||
915 | fo._do_grab() | ||
916 | if not opts.checkfunc is None: | ||
917 | cb_func, cb_args, cb_kwargs = \ | ||
918 | self._make_callback(opts.checkfunc) | ||
919 | obj = CallbackObject() | ||
920 | obj.filename = filename | ||
921 | obj.url = url | ||
922 | apply(cb_func, (obj, )+cb_args, cb_kwargs) | ||
923 | finally: | ||
924 | fo.close() | ||
925 | return filename | ||
926 | |||
927 | return self._retry(opts, retryfunc, url, filename) | ||
928 | |||
929 | def urlread(self, url, limit=None, **kwargs): | ||
930 | """read the url into a string, up to 'limit' bytes | ||
931 | If the limit is exceeded, an exception will be thrown. Note | ||
932 | that urlread is NOT intended to be used as a way of saying | ||
933 | "I want the first N bytes" but rather 'read the whole file | ||
934 | into memory, but don't use too much' | ||
935 | """ | ||
936 | opts = self.opts.derive(**kwargs) | ||
937 | (url,parts) = opts.urlparser.parse(url, opts) | ||
938 | if limit is not None: | ||
939 | limit = limit + 1 | ||
940 | |||
941 | def retryfunc(opts, url, limit): | ||
942 | fo = URLGrabberFileObject(url, filename=None, opts=opts) | ||
943 | s = '' | ||
944 | try: | ||
945 | # this is an unfortunate thing. Some file-like objects | ||
946 | # have a default "limit" of None, while the built-in (real) | ||
947 | # file objects have -1. They each break the other, so for | ||
948 | # now, we just force the default if necessary. | ||
949 | if limit is None: s = fo.read() | ||
950 | else: s = fo.read(limit) | ||
951 | |||
952 | if not opts.checkfunc is None: | ||
953 | cb_func, cb_args, cb_kwargs = \ | ||
954 | self._make_callback(opts.checkfunc) | ||
955 | obj = CallbackObject() | ||
956 | obj.data = s | ||
957 | obj.url = url | ||
958 | apply(cb_func, (obj, )+cb_args, cb_kwargs) | ||
959 | finally: | ||
960 | fo.close() | ||
961 | return s | ||
962 | |||
963 | s = self._retry(opts, retryfunc, url, limit) | ||
964 | if limit and len(s) > limit: | ||
965 | raise URLGrabError(8, | ||
966 | _('Exceeded limit (%i): %s') % (limit, url)) | ||
967 | return s | ||
968 | |||
969 | def _make_callback(self, callback_obj): | ||
970 | if callable(callback_obj): | ||
971 | return callback_obj, (), {} | ||
972 | else: | ||
973 | return callback_obj | ||
974 | |||
975 | # create the default URLGrabber used by urlXXX functions. | ||
976 | # NOTE: actual defaults are set in URLGrabberOptions | ||
977 | default_grabber = URLGrabber() | ||
978 | |||
979 | class URLGrabberFileObject: | ||
980 | """This is a file-object wrapper that supports progress objects | ||
981 | and throttling. | ||
982 | |||
983 | This exists to solve the following problem: lets say you want to | ||
984 | drop-in replace a normal open with urlopen. You want to use a | ||
985 | progress meter and/or throttling, but how do you do that without | ||
986 | rewriting your code? Answer: urlopen will return a wrapped file | ||
987 | object that does the progress meter and-or throttling internally. | ||
988 | """ | ||
989 | |||
990 | def __init__(self, url, filename, opts): | ||
991 | self.url = url | ||
992 | self.filename = filename | ||
993 | self.opts = opts | ||
994 | self.fo = None | ||
995 | self._rbuf = '' | ||
996 | self._rbufsize = 1024*8 | ||
997 | self._ttime = time.time() | ||
998 | self._tsize = 0 | ||
999 | self._amount_read = 0 | ||
1000 | self._opener = None | ||
1001 | self._do_open() | ||
1002 | |||
1003 | def __getattr__(self, name): | ||
1004 | """This effectively allows us to wrap at the instance level. | ||
1005 | Any attribute not found in _this_ object will be searched for | ||
1006 | in self.fo. This includes methods.""" | ||
1007 | if hasattr(self.fo, name): | ||
1008 | return getattr(self.fo, name) | ||
1009 | raise AttributeError, name | ||
1010 | |||
1011 | def _get_opener(self): | ||
1012 | """Build a urllib2 OpenerDirector based on request options.""" | ||
1013 | if self.opts.opener: | ||
1014 | return self.opts.opener | ||
1015 | elif self._opener is None: | ||
1016 | handlers = [] | ||
1017 | need_keepalive_handler = (have_keepalive and self.opts.keepalive) | ||
1018 | need_range_handler = (range_handlers and \ | ||
1019 | (self.opts.range or self.opts.reget)) | ||
1020 | # if you specify a ProxyHandler when creating the opener | ||
1021 | # it _must_ come before all other handlers in the list or urllib2 | ||
1022 | # chokes. | ||
1023 | if self.opts.proxies: | ||
1024 | handlers.append( CachedProxyHandler(self.opts.proxies) ) | ||
1025 | |||
1026 | # ------------------------------------------------------- | ||
1027 | # OK, these next few lines are a serious kludge to get | ||
1028 | # around what I think is a bug in python 2.2's | ||
1029 | # urllib2. The basic idea is that default handlers | ||
1030 | # get applied first. If you override one (like a | ||
1031 | # proxy handler), then the default gets pulled, but | ||
1032 | # the replacement goes on the end. In the case of | ||
1033 | # proxies, this means the normal handler picks it up | ||
1034 | # first and the proxy isn't used. Now, this probably | ||
1035 | # only happened with ftp or non-keepalive http, so not | ||
1036 | # many folks saw it. The simple approach to fixing it | ||
1037 | # is just to make sure you override the other | ||
1038 | # conflicting defaults as well. I would LOVE to see | ||
1039 | # these go way or be dealt with more elegantly. The | ||
1040 | # problem isn't there after 2.2. -MDS 2005/02/24 | ||
1041 | if not need_keepalive_handler: | ||
1042 | handlers.append( urllib2.HTTPHandler() ) | ||
1043 | if not need_range_handler: | ||
1044 | handlers.append( urllib2.FTPHandler() ) | ||
1045 | # ------------------------------------------------------- | ||
1046 | |||
1047 | ssl_factory = sslfactory.get_factory(self.opts.ssl_ca_cert, | ||
1048 | self.opts.ssl_context) | ||
1049 | |||
1050 | if need_keepalive_handler: | ||
1051 | handlers.append(HTTPHandler()) | ||
1052 | handlers.append(HTTPSHandler(ssl_factory)) | ||
1053 | if need_range_handler: | ||
1054 | handlers.extend( range_handlers ) | ||
1055 | handlers.append( auth_handler ) | ||
1056 | if self.opts.cache_openers: | ||
1057 | self._opener = CachedOpenerDirector(ssl_factory, *handlers) | ||
1058 | else: | ||
1059 | self._opener = ssl_factory.create_opener(*handlers) | ||
1060 | # OK, I don't like to do this, but otherwise, we end up with | ||
1061 | # TWO user-agent headers. | ||
1062 | self._opener.addheaders = [] | ||
1063 | return self._opener | ||
1064 | |||
1065 | def _do_open(self): | ||
1066 | opener = self._get_opener() | ||
1067 | |||
1068 | req = urllib2.Request(self.url, self.opts.data) # build request object | ||
1069 | self._add_headers(req) # add misc headers that we need | ||
1070 | self._build_range(req) # take care of reget and byterange stuff | ||
1071 | |||
1072 | fo, hdr = self._make_request(req, opener) | ||
1073 | if self.reget_time and self.opts.reget == 'check_timestamp': | ||
1074 | # do this if we have a local file with known timestamp AND | ||
1075 | # we're in check_timestamp reget mode. | ||
1076 | fetch_again = 0 | ||
1077 | try: | ||
1078 | modified_tuple = hdr.getdate_tz('last-modified') | ||
1079 | modified_stamp = rfc822.mktime_tz(modified_tuple) | ||
1080 | if modified_stamp > self.reget_time: fetch_again = 1 | ||
1081 | except (TypeError,): | ||
1082 | fetch_again = 1 | ||
1083 | |||
1084 | if fetch_again: | ||
1085 | # the server version is newer than the (incomplete) local | ||
1086 | # version, so we should abandon the version we're getting | ||
1087 | # and fetch the whole thing again. | ||
1088 | fo.close() | ||
1089 | self.opts.reget = None | ||
1090 | del req.headers['Range'] | ||
1091 | self._build_range(req) | ||
1092 | fo, hdr = self._make_request(req, opener) | ||
1093 | |||
1094 | (scheme, host, path, parm, query, frag) = urlparse.urlparse(self.url) | ||
1095 | path = urllib.unquote(path) | ||
1096 | if not (self.opts.progress_obj or self.opts.raw_throttle() \ | ||
1097 | or self.opts.timeout): | ||
1098 | # if we're not using the progress_obj, throttling, or timeout | ||
1099 | # we can get a performance boost by going directly to | ||
1100 | # the underlying fileobject for reads. | ||
1101 | self.read = fo.read | ||
1102 | if hasattr(fo, 'readline'): | ||
1103 | self.readline = fo.readline | ||
1104 | elif self.opts.progress_obj: | ||
1105 | try: | ||
1106 | length = int(hdr['Content-Length']) | ||
1107 | length = length + self._amount_read # Account for regets | ||
1108 | except (KeyError, ValueError, TypeError): | ||
1109 | length = None | ||
1110 | |||
1111 | self.opts.progress_obj.start(str(self.filename), | ||
1112 | urllib.unquote(self.url), | ||
1113 | os.path.basename(path), | ||
1114 | length, text=self.opts.text) | ||
1115 | self.opts.progress_obj.update(0) | ||
1116 | (self.fo, self.hdr) = (fo, hdr) | ||
1117 | |||
1118 | def _add_headers(self, req): | ||
1119 | if self.opts.user_agent: | ||
1120 | req.add_header('User-agent', self.opts.user_agent) | ||
1121 | try: req_type = req.get_type() | ||
1122 | except ValueError: req_type = None | ||
1123 | if self.opts.http_headers and req_type in ('http', 'https'): | ||
1124 | for h, v in self.opts.http_headers: | ||
1125 | req.add_header(h, v) | ||
1126 | if self.opts.ftp_headers and req_type == 'ftp': | ||
1127 | for h, v in self.opts.ftp_headers: | ||
1128 | req.add_header(h, v) | ||
1129 | |||
1130 | def _build_range(self, req): | ||
1131 | self.reget_time = None | ||
1132 | self.append = 0 | ||
1133 | reget_length = 0 | ||
1134 | rt = None | ||
1135 | if have_range and self.opts.reget and type(self.filename) == type(''): | ||
1136 | # we have reget turned on and we're dumping to a file | ||
1137 | try: | ||
1138 | s = os.stat(self.filename) | ||
1139 | except OSError: | ||
1140 | pass | ||
1141 | else: | ||
1142 | self.reget_time = s[ST_MTIME] | ||
1143 | reget_length = s[ST_SIZE] | ||
1144 | |||
1145 | # Set initial length when regetting | ||
1146 | self._amount_read = reget_length | ||
1147 | |||
1148 | rt = reget_length, '' | ||
1149 | self.append = 1 | ||
1150 | |||
1151 | if self.opts.range: | ||
1152 | if not have_range: | ||
1153 | raise URLGrabError(10, _('Byte range requested but range '\ | ||
1154 | 'support unavailable')) | ||
1155 | rt = self.opts.range | ||
1156 | if rt[0]: rt = (rt[0] + reget_length, rt[1]) | ||
1157 | |||
1158 | if rt: | ||
1159 | header = range_tuple_to_header(rt) | ||
1160 | if header: req.add_header('Range', header) | ||
1161 | |||
1162 | def _make_request(self, req, opener): | ||
1163 | try: | ||
1164 | if have_socket_timeout and self.opts.timeout: | ||
1165 | old_to = socket.getdefaulttimeout() | ||
1166 | socket.setdefaulttimeout(self.opts.timeout) | ||
1167 | try: | ||
1168 | fo = opener.open(req) | ||
1169 | finally: | ||
1170 | socket.setdefaulttimeout(old_to) | ||
1171 | else: | ||
1172 | fo = opener.open(req) | ||
1173 | hdr = fo.info() | ||
1174 | except ValueError, e: | ||
1175 | raise URLGrabError(1, _('Bad URL: %s') % (e, )) | ||
1176 | except RangeError, e: | ||
1177 | raise URLGrabError(9, str(e)) | ||
1178 | except urllib2.HTTPError, e: | ||
1179 | new_e = URLGrabError(14, str(e)) | ||
1180 | new_e.code = e.code | ||
1181 | new_e.exception = e | ||
1182 | raise new_e | ||
1183 | except IOError, e: | ||
1184 | if hasattr(e, 'reason') and have_socket_timeout and \ | ||
1185 | isinstance(e.reason, TimeoutError): | ||
1186 | raise URLGrabError(12, _('Timeout: %s') % (e, )) | ||
1187 | else: | ||
1188 | raise URLGrabError(4, _('IOError: %s') % (e, )) | ||
1189 | except OSError, e: | ||
1190 | raise URLGrabError(5, _('OSError: %s') % (e, )) | ||
1191 | except HTTPException, e: | ||
1192 | raise URLGrabError(7, _('HTTP Exception (%s): %s') % \ | ||
1193 | (e.__class__.__name__, e)) | ||
1194 | else: | ||
1195 | return (fo, hdr) | ||
1196 | |||
1197 | def _do_grab(self): | ||
1198 | """dump the file to self.filename.""" | ||
1199 | if self.append: new_fo = open(self.filename, 'ab') | ||
1200 | else: new_fo = open(self.filename, 'wb') | ||
1201 | bs = 1024*8 | ||
1202 | size = 0 | ||
1203 | |||
1204 | block = self.read(bs) | ||
1205 | size = size + len(block) | ||
1206 | while block: | ||
1207 | new_fo.write(block) | ||
1208 | block = self.read(bs) | ||
1209 | size = size + len(block) | ||
1210 | |||
1211 | new_fo.close() | ||
1212 | try: | ||
1213 | modified_tuple = self.hdr.getdate_tz('last-modified') | ||
1214 | modified_stamp = rfc822.mktime_tz(modified_tuple) | ||
1215 | os.utime(self.filename, (modified_stamp, modified_stamp)) | ||
1216 | except (TypeError,), e: pass | ||
1217 | |||
1218 | return size | ||
1219 | |||
1220 | def _fill_buffer(self, amt=None): | ||
1221 | """fill the buffer to contain at least 'amt' bytes by reading | ||
1222 | from the underlying file object. If amt is None, then it will | ||
1223 | read until it gets nothing more. It updates the progress meter | ||
1224 | and throttles after every self._rbufsize bytes.""" | ||
1225 | # the _rbuf test is only in this first 'if' for speed. It's not | ||
1226 | # logically necessary | ||
1227 | if self._rbuf and not amt is None: | ||
1228 | L = len(self._rbuf) | ||
1229 | if amt > L: | ||
1230 | amt = amt - L | ||
1231 | else: | ||
1232 | return | ||
1233 | |||
1234 | # if we've made it here, then we don't have enough in the buffer | ||
1235 | # and we need to read more. | ||
1236 | |||
1237 | buf = [self._rbuf] | ||
1238 | bufsize = len(self._rbuf) | ||
1239 | while amt is None or amt: | ||
1240 | # first, delay if necessary for throttling reasons | ||
1241 | if self.opts.raw_throttle(): | ||
1242 | diff = self._tsize/self.opts.raw_throttle() - \ | ||
1243 | (time.time() - self._ttime) | ||
1244 | if diff > 0: time.sleep(diff) | ||
1245 | self._ttime = time.time() | ||
1246 | |||
1247 | # now read some data, up to self._rbufsize | ||
1248 | if amt is None: readamount = self._rbufsize | ||
1249 | else: readamount = min(amt, self._rbufsize) | ||
1250 | try: | ||
1251 | new = self.fo.read(readamount) | ||
1252 | except socket.error, e: | ||
1253 | raise URLGrabError(4, _('Socket Error: %s') % (e, )) | ||
1254 | except TimeoutError, e: | ||
1255 | raise URLGrabError(12, _('Timeout: %s') % (e, )) | ||
1256 | except IOError, e: | ||
1257 | raise URLGrabError(4, _('IOError: %s') %(e,)) | ||
1258 | newsize = len(new) | ||
1259 | if not newsize: break # no more to read | ||
1260 | |||
1261 | if amt: amt = amt - newsize | ||
1262 | buf.append(new) | ||
1263 | bufsize = bufsize + newsize | ||
1264 | self._tsize = newsize | ||
1265 | self._amount_read = self._amount_read + newsize | ||
1266 | if self.opts.progress_obj: | ||
1267 | self.opts.progress_obj.update(self._amount_read) | ||
1268 | |||
1269 | self._rbuf = string.join(buf, '') | ||
1270 | return | ||
1271 | |||
1272 | def read(self, amt=None): | ||
1273 | self._fill_buffer(amt) | ||
1274 | if amt is None: | ||
1275 | s, self._rbuf = self._rbuf, '' | ||
1276 | else: | ||
1277 | s, self._rbuf = self._rbuf[:amt], self._rbuf[amt:] | ||
1278 | return s | ||
1279 | |||
1280 | def readline(self, limit=-1): | ||
1281 | i = string.find(self._rbuf, '\n') | ||
1282 | while i < 0 and not (0 < limit <= len(self._rbuf)): | ||
1283 | L = len(self._rbuf) | ||
1284 | self._fill_buffer(L + self._rbufsize) | ||
1285 | if not len(self._rbuf) > L: break | ||
1286 | i = string.find(self._rbuf, '\n', L) | ||
1287 | |||
1288 | if i < 0: i = len(self._rbuf) | ||
1289 | else: i = i+1 | ||
1290 | if 0 <= limit < len(self._rbuf): i = limit | ||
1291 | |||
1292 | s, self._rbuf = self._rbuf[:i], self._rbuf[i:] | ||
1293 | return s | ||
1294 | |||
1295 | def close(self): | ||
1296 | if self.opts.progress_obj: | ||
1297 | self.opts.progress_obj.end(self._amount_read) | ||
1298 | self.fo.close() | ||
1299 | if self.opts.close_connection: | ||
1300 | try: self.fo.close_connection() | ||
1301 | except: pass | ||
1302 | |||
1303 | _handler_cache = [] | ||
1304 | def CachedOpenerDirector(ssl_factory = None, *handlers): | ||
1305 | for (cached_handlers, opener) in _handler_cache: | ||
1306 | if cached_handlers == handlers: | ||
1307 | for handler in opener.handlers: | ||
1308 | handler.add_parent(opener) | ||
1309 | return opener | ||
1310 | if not ssl_factory: | ||
1311 | ssl_factory = sslfactory.get_factory() | ||
1312 | opener = ssl_factory.create_opener(*handlers) | ||
1313 | _handler_cache.append( (handlers, opener) ) | ||
1314 | return opener | ||
1315 | |||
1316 | _proxy_cache = [] | ||
1317 | def CachedProxyHandler(proxies): | ||
1318 | for (pdict, handler) in _proxy_cache: | ||
1319 | if pdict == proxies: | ||
1320 | if DEBUG: DEBUG.debug('re-using proxy settings: %s', proxies) | ||
1321 | break | ||
1322 | else: | ||
1323 | for k, v in proxies.items(): | ||
1324 | utype, url = urllib.splittype(v) | ||
1325 | host, other = urllib.splithost(url) | ||
1326 | if (utype is None) or (host is None): | ||
1327 | raise URLGrabError(13, _('Bad proxy URL: %s') % v) | ||
1328 | |||
1329 | if DEBUG: DEBUG.info('creating new proxy handler: %s', proxies) | ||
1330 | handler = urllib2.ProxyHandler(proxies) | ||
1331 | _proxy_cache.append( (proxies, handler) ) | ||
1332 | return handler | ||
1333 | |||
1334 | ##################################################################### | ||
1335 | # DEPRECATED FUNCTIONS | ||
1336 | def set_throttle(new_throttle): | ||
1337 | """Deprecated. Use: default_grabber.throttle = new_throttle""" | ||
1338 | default_grabber.throttle = new_throttle | ||
1339 | |||
1340 | def set_bandwidth(new_bandwidth): | ||
1341 | """Deprecated. Use: default_grabber.bandwidth = new_bandwidth""" | ||
1342 | default_grabber.bandwidth = new_bandwidth | ||
1343 | |||
1344 | def set_progress_obj(new_progress_obj): | ||
1345 | """Deprecated. Use: default_grabber.progress_obj = new_progress_obj""" | ||
1346 | default_grabber.progress_obj = new_progress_obj | ||
1347 | |||
1348 | def set_user_agent(new_user_agent): | ||
1349 | """Deprecated. Use: default_grabber.user_agent = new_user_agent""" | ||
1350 | default_grabber.user_agent = new_user_agent | ||
1351 | |||
1352 | def retrygrab(url, filename=None, copy_local=0, close_connection=0, | ||
1353 | progress_obj=None, throttle=None, bandwidth=None, | ||
1354 | numtries=3, retrycodes=[-1,2,4,5,6,7], checkfunc=None): | ||
1355 | """Deprecated. Use: urlgrab() with the retry arg instead""" | ||
1356 | kwargs = {'copy_local' : copy_local, | ||
1357 | 'close_connection' : close_connection, | ||
1358 | 'progress_obj' : progress_obj, | ||
1359 | 'throttle' : throttle, | ||
1360 | 'bandwidth' : bandwidth, | ||
1361 | 'retry' : numtries, | ||
1362 | 'retrycodes' : retrycodes, | ||
1363 | 'checkfunc' : checkfunc | ||
1364 | } | ||
1365 | return urlgrab(url, filename, **kwargs) | ||
1366 | |||
1367 | |||
1368 | ##################################################################### | ||
1369 | # TESTING | ||
1370 | def _main_test(): | ||
1371 | import sys | ||
1372 | try: url, filename = sys.argv[1:3] | ||
1373 | except ValueError: | ||
1374 | print 'usage:', sys.argv[0], \ | ||
1375 | '<url> <filename> [copy_local=0|1] [close_connection=0|1]' | ||
1376 | sys.exit() | ||
1377 | |||
1378 | kwargs = {} | ||
1379 | for a in sys.argv[3:]: | ||
1380 | k, v = string.split(a, '=', 1) | ||
1381 | kwargs[k] = int(v) | ||
1382 | |||
1383 | set_throttle(1.0) | ||
1384 | set_bandwidth(32 * 1024) | ||
1385 | print "throttle: %s, throttle bandwidth: %s B/s" % (default_grabber.throttle, | ||
1386 | default_grabber.bandwidth) | ||
1387 | |||
1388 | try: from progress import text_progress_meter | ||
1389 | except ImportError, e: pass | ||
1390 | else: kwargs['progress_obj'] = text_progress_meter() | ||
1391 | |||
1392 | try: name = apply(urlgrab, (url, filename), kwargs) | ||
1393 | except URLGrabError, e: print e | ||
1394 | else: print 'LOCAL FILE:', name | ||
1395 | |||
1396 | |||
1397 | def _retry_test(): | ||
1398 | import sys | ||
1399 | try: url, filename = sys.argv[1:3] | ||
1400 | except ValueError: | ||
1401 | print 'usage:', sys.argv[0], \ | ||
1402 | '<url> <filename> [copy_local=0|1] [close_connection=0|1]' | ||
1403 | sys.exit() | ||
1404 | |||
1405 | kwargs = {} | ||
1406 | for a in sys.argv[3:]: | ||
1407 | k, v = string.split(a, '=', 1) | ||
1408 | kwargs[k] = int(v) | ||
1409 | |||
1410 | try: from progress import text_progress_meter | ||
1411 | except ImportError, e: pass | ||
1412 | else: kwargs['progress_obj'] = text_progress_meter() | ||
1413 | |||
1414 | def cfunc(filename, hello, there='foo'): | ||
1415 | print hello, there | ||
1416 | import random | ||
1417 | rnum = random.random() | ||
1418 | if rnum < .5: | ||
1419 | print 'forcing retry' | ||
1420 | raise URLGrabError(-1, 'forcing retry') | ||
1421 | if rnum < .75: | ||
1422 | print 'forcing failure' | ||
1423 | raise URLGrabError(-2, 'forcing immediate failure') | ||
1424 | print 'success' | ||
1425 | return | ||
1426 | |||
1427 | kwargs['checkfunc'] = (cfunc, ('hello',), {'there':'there'}) | ||
1428 | try: name = apply(retrygrab, (url, filename), kwargs) | ||
1429 | except URLGrabError, e: print e | ||
1430 | else: print 'LOCAL FILE:', name | ||
1431 | |||
1432 | def _file_object_test(filename=None): | ||
1433 | import random, cStringIO, sys | ||
1434 | if filename is None: | ||
1435 | filename = __file__ | ||
1436 | print 'using file "%s" for comparisons' % filename | ||
1437 | fo = open(filename) | ||
1438 | s_input = fo.read() | ||
1439 | fo.close() | ||
1440 | |||
1441 | for testfunc in [_test_file_object_smallread, | ||
1442 | _test_file_object_readall, | ||
1443 | _test_file_object_readline, | ||
1444 | _test_file_object_readlines]: | ||
1445 | fo_input = cStringIO.StringIO(s_input) | ||
1446 | fo_output = cStringIO.StringIO() | ||
1447 | wrapper = URLGrabberFileObject(fo_input, None, 0) | ||
1448 | print 'testing %-30s ' % testfunc.__name__, | ||
1449 | testfunc(wrapper, fo_output) | ||
1450 | s_output = fo_output.getvalue() | ||
1451 | if s_output == s_input: print 'passed' | ||
1452 | else: print 'FAILED' | ||
1453 | |||
1454 | def _test_file_object_smallread(wrapper, fo_output): | ||
1455 | while 1: | ||
1456 | s = wrapper.read(23) | ||
1457 | fo_output.write(s) | ||
1458 | if not s: return | ||
1459 | |||
1460 | def _test_file_object_readall(wrapper, fo_output): | ||
1461 | s = wrapper.read() | ||
1462 | fo_output.write(s) | ||
1463 | |||
1464 | def _test_file_object_readline(wrapper, fo_output): | ||
1465 | while 1: | ||
1466 | s = wrapper.readline() | ||
1467 | fo_output.write(s) | ||
1468 | if not s: return | ||
1469 | |||
1470 | def _test_file_object_readlines(wrapper, fo_output): | ||
1471 | li = wrapper.readlines() | ||
1472 | fo_output.write(string.join(li, '')) | ||
1473 | |||
1474 | if __name__ == '__main__': | ||
1475 | _main_test() | ||
1476 | _retry_test() | ||
1477 | _file_object_test('test') | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/keepalive.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/keepalive.py new file mode 100644 index 0000000000..71393e2b8d --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/keepalive.py | |||
@@ -0,0 +1,617 @@ | |||
1 | # This library is free software; you can redistribute it and/or | ||
2 | # modify it under the terms of the GNU Lesser General Public | ||
3 | # License as published by the Free Software Foundation; either | ||
4 | # version 2.1 of the License, or (at your option) any later version. | ||
5 | # | ||
6 | # This library is distributed in the hope that it will be useful, | ||
7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
9 | # Lesser General Public License for more details. | ||
10 | # | ||
11 | # You should have received a copy of the GNU Lesser General Public | ||
12 | # License along with this library; if not, write to the | ||
13 | # Free Software Foundation, Inc., | ||
14 | # 59 Temple Place, Suite 330, | ||
15 | # Boston, MA 02111-1307 USA | ||
16 | |||
17 | # This file is part of urlgrabber, a high-level cross-protocol url-grabber | ||
18 | # Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko | ||
19 | |||
20 | """An HTTP handler for urllib2 that supports HTTP 1.1 and keepalive. | ||
21 | |||
22 | >>> import urllib2 | ||
23 | >>> from keepalive import HTTPHandler | ||
24 | >>> keepalive_handler = HTTPHandler() | ||
25 | >>> opener = urllib2.build_opener(keepalive_handler) | ||
26 | >>> urllib2.install_opener(opener) | ||
27 | >>> | ||
28 | >>> fo = urllib2.urlopen('http://www.python.org') | ||
29 | |||
30 | If a connection to a given host is requested, and all of the existing | ||
31 | connections are still in use, another connection will be opened. If | ||
32 | the handler tries to use an existing connection but it fails in some | ||
33 | way, it will be closed and removed from the pool. | ||
34 | |||
35 | To remove the handler, simply re-run build_opener with no arguments, and | ||
36 | install that opener. | ||
37 | |||
38 | You can explicitly close connections by using the close_connection() | ||
39 | method of the returned file-like object (described below) or you can | ||
40 | use the handler methods: | ||
41 | |||
42 | close_connection(host) | ||
43 | close_all() | ||
44 | open_connections() | ||
45 | |||
46 | NOTE: using the close_connection and close_all methods of the handler | ||
47 | should be done with care when using multiple threads. | ||
48 | * there is nothing that prevents another thread from creating new | ||
49 | connections immediately after connections are closed | ||
50 | * no checks are done to prevent in-use connections from being closed | ||
51 | |||
52 | >>> keepalive_handler.close_all() | ||
53 | |||
54 | EXTRA ATTRIBUTES AND METHODS | ||
55 | |||
56 | Upon a status of 200, the object returned has a few additional | ||
57 | attributes and methods, which should not be used if you want to | ||
58 | remain consistent with the normal urllib2-returned objects: | ||
59 | |||
60 | close_connection() - close the connection to the host | ||
61 | readlines() - you know, readlines() | ||
62 | status - the return status (ie 404) | ||
63 | reason - english translation of status (ie 'File not found') | ||
64 | |||
65 | If you want the best of both worlds, use this inside an | ||
66 | AttributeError-catching try: | ||
67 | |||
68 | >>> try: status = fo.status | ||
69 | >>> except AttributeError: status = None | ||
70 | |||
71 | Unfortunately, these are ONLY there if status == 200, so it's not | ||
72 | easy to distinguish between non-200 responses. The reason is that | ||
73 | urllib2 tries to do clever things with error codes 301, 302, 401, | ||
74 | and 407, and it wraps the object upon return. | ||
75 | |||
76 | For python versions earlier than 2.4, you can avoid this fancy error | ||
77 | handling by setting the module-level global HANDLE_ERRORS to zero. | ||
78 | You see, prior to 2.4, it's the HTTP Handler's job to determine what | ||
79 | to handle specially, and what to just pass up. HANDLE_ERRORS == 0 | ||
80 | means "pass everything up". In python 2.4, however, this job no | ||
81 | longer belongs to the HTTP Handler and is now done by a NEW handler, | ||
82 | HTTPErrorProcessor. Here's the bottom line: | ||
83 | |||
84 | python version < 2.4 | ||
85 | HANDLE_ERRORS == 1 (default) pass up 200, treat the rest as | ||
86 | errors | ||
87 | HANDLE_ERRORS == 0 pass everything up, error processing is | ||
88 | left to the calling code | ||
89 | python version >= 2.4 | ||
90 | HANDLE_ERRORS == 1 pass up 200, treat the rest as errors | ||
91 | HANDLE_ERRORS == 0 (default) pass everything up, let the | ||
92 | other handlers (specifically, | ||
93 | HTTPErrorProcessor) decide what to do | ||
94 | |||
95 | In practice, setting the variable either way makes little difference | ||
96 | in python 2.4, so for the most consistent behavior across versions, | ||
97 | you probably just want to use the defaults, which will give you | ||
98 | exceptions on errors. | ||
99 | |||
100 | """ | ||
101 | |||
102 | # $Id: keepalive.py,v 1.16 2006/09/22 00:58:05 mstenner Exp $ | ||
103 | |||
104 | import urllib2 | ||
105 | import httplib | ||
106 | import socket | ||
107 | import thread | ||
108 | |||
109 | DEBUG = None | ||
110 | |||
111 | import sslfactory | ||
112 | |||
113 | import sys | ||
114 | if sys.version_info < (2, 4): HANDLE_ERRORS = 1 | ||
115 | else: HANDLE_ERRORS = 0 | ||
116 | |||
117 | class ConnectionManager: | ||
118 | """ | ||
119 | The connection manager must be able to: | ||
120 | * keep track of all existing | ||
121 | """ | ||
122 | def __init__(self): | ||
123 | self._lock = thread.allocate_lock() | ||
124 | self._hostmap = {} # map hosts to a list of connections | ||
125 | self._connmap = {} # map connections to host | ||
126 | self._readymap = {} # map connection to ready state | ||
127 | |||
128 | def add(self, host, connection, ready): | ||
129 | self._lock.acquire() | ||
130 | try: | ||
131 | if not self._hostmap.has_key(host): self._hostmap[host] = [] | ||
132 | self._hostmap[host].append(connection) | ||
133 | self._connmap[connection] = host | ||
134 | self._readymap[connection] = ready | ||
135 | finally: | ||
136 | self._lock.release() | ||
137 | |||
138 | def remove(self, connection): | ||
139 | self._lock.acquire() | ||
140 | try: | ||
141 | try: | ||
142 | host = self._connmap[connection] | ||
143 | except KeyError: | ||
144 | pass | ||
145 | else: | ||
146 | del self._connmap[connection] | ||
147 | del self._readymap[connection] | ||
148 | self._hostmap[host].remove(connection) | ||
149 | if not self._hostmap[host]: del self._hostmap[host] | ||
150 | finally: | ||
151 | self._lock.release() | ||
152 | |||
153 | def set_ready(self, connection, ready): | ||
154 | try: self._readymap[connection] = ready | ||
155 | except KeyError: pass | ||
156 | |||
157 | def get_ready_conn(self, host): | ||
158 | conn = None | ||
159 | self._lock.acquire() | ||
160 | try: | ||
161 | if self._hostmap.has_key(host): | ||
162 | for c in self._hostmap[host]: | ||
163 | if self._readymap[c]: | ||
164 | self._readymap[c] = 0 | ||
165 | conn = c | ||
166 | break | ||
167 | finally: | ||
168 | self._lock.release() | ||
169 | return conn | ||
170 | |||
171 | def get_all(self, host=None): | ||
172 | if host: | ||
173 | return list(self._hostmap.get(host, [])) | ||
174 | else: | ||
175 | return dict(self._hostmap) | ||
176 | |||
177 | class KeepAliveHandler: | ||
178 | def __init__(self): | ||
179 | self._cm = ConnectionManager() | ||
180 | |||
181 | #### Connection Management | ||
182 | def open_connections(self): | ||
183 | """return a list of connected hosts and the number of connections | ||
184 | to each. [('foo.com:80', 2), ('bar.org', 1)]""" | ||
185 | return [(host, len(li)) for (host, li) in self._cm.get_all().items()] | ||
186 | |||
187 | def close_connection(self, host): | ||
188 | """close connection(s) to <host> | ||
189 | host is the host:port spec, as in 'www.cnn.com:8080' as passed in. | ||
190 | no error occurs if there is no connection to that host.""" | ||
191 | for h in self._cm.get_all(host): | ||
192 | self._cm.remove(h) | ||
193 | h.close() | ||
194 | |||
195 | def close_all(self): | ||
196 | """close all open connections""" | ||
197 | for host, conns in self._cm.get_all().items(): | ||
198 | for h in conns: | ||
199 | self._cm.remove(h) | ||
200 | h.close() | ||
201 | |||
202 | def _request_closed(self, request, host, connection): | ||
203 | """tells us that this request is now closed and the the | ||
204 | connection is ready for another request""" | ||
205 | self._cm.set_ready(connection, 1) | ||
206 | |||
207 | def _remove_connection(self, host, connection, close=0): | ||
208 | if close: connection.close() | ||
209 | self._cm.remove(connection) | ||
210 | |||
211 | #### Transaction Execution | ||
212 | def do_open(self, req): | ||
213 | host = req.get_host() | ||
214 | if not host: | ||
215 | raise urllib2.URLError('no host given') | ||
216 | |||
217 | try: | ||
218 | h = self._cm.get_ready_conn(host) | ||
219 | while h: | ||
220 | r = self._reuse_connection(h, req, host) | ||
221 | |||
222 | # if this response is non-None, then it worked and we're | ||
223 | # done. Break out, skipping the else block. | ||
224 | if r: break | ||
225 | |||
226 | # connection is bad - possibly closed by server | ||
227 | # discard it and ask for the next free connection | ||
228 | h.close() | ||
229 | self._cm.remove(h) | ||
230 | h = self._cm.get_ready_conn(host) | ||
231 | else: | ||
232 | # no (working) free connections were found. Create a new one. | ||
233 | h = self._get_connection(host) | ||
234 | if DEBUG: DEBUG.info("creating new connection to %s (%d)", | ||
235 | host, id(h)) | ||
236 | self._cm.add(host, h, 0) | ||
237 | self._start_transaction(h, req) | ||
238 | r = h.getresponse() | ||
239 | except (socket.error, httplib.HTTPException), err: | ||
240 | raise urllib2.URLError(err) | ||
241 | |||
242 | # if not a persistent connection, don't try to reuse it | ||
243 | if r.will_close: self._cm.remove(h) | ||
244 | |||
245 | if DEBUG: DEBUG.info("STATUS: %s, %s", r.status, r.reason) | ||
246 | r._handler = self | ||
247 | r._host = host | ||
248 | r._url = req.get_full_url() | ||
249 | r._connection = h | ||
250 | r.code = r.status | ||
251 | r.headers = r.msg | ||
252 | r.msg = r.reason | ||
253 | |||
254 | if r.status == 200 or not HANDLE_ERRORS: | ||
255 | return r | ||
256 | else: | ||
257 | return self.parent.error('http', req, r, | ||
258 | r.status, r.msg, r.headers) | ||
259 | |||
260 | def _reuse_connection(self, h, req, host): | ||
261 | """start the transaction with a re-used connection | ||
262 | return a response object (r) upon success or None on failure. | ||
263 | This DOES not close or remove bad connections in cases where | ||
264 | it returns. However, if an unexpected exception occurs, it | ||
265 | will close and remove the connection before re-raising. | ||
266 | """ | ||
267 | try: | ||
268 | self._start_transaction(h, req) | ||
269 | r = h.getresponse() | ||
270 | # note: just because we got something back doesn't mean it | ||
271 | # worked. We'll check the version below, too. | ||
272 | except (socket.error, httplib.HTTPException): | ||
273 | r = None | ||
274 | except: | ||
275 | # adding this block just in case we've missed | ||
276 | # something we will still raise the exception, but | ||
277 | # lets try and close the connection and remove it | ||
278 | # first. We previously got into a nasty loop | ||
279 | # where an exception was uncaught, and so the | ||
280 | # connection stayed open. On the next try, the | ||
281 | # same exception was raised, etc. The tradeoff is | ||
282 | # that it's now possible this call will raise | ||
283 | # a DIFFERENT exception | ||
284 | if DEBUG: DEBUG.error("unexpected exception - closing " + \ | ||
285 | "connection to %s (%d)", host, id(h)) | ||
286 | self._cm.remove(h) | ||
287 | h.close() | ||
288 | raise | ||
289 | |||
290 | if r is None or r.version == 9: | ||
291 | # httplib falls back to assuming HTTP 0.9 if it gets a | ||
292 | # bad header back. This is most likely to happen if | ||
293 | # the socket has been closed by the server since we | ||
294 | # last used the connection. | ||
295 | if DEBUG: DEBUG.info("failed to re-use connection to %s (%d)", | ||
296 | host, id(h)) | ||
297 | r = None | ||
298 | else: | ||
299 | if DEBUG: DEBUG.info("re-using connection to %s (%d)", host, id(h)) | ||
300 | |||
301 | return r | ||
302 | |||
303 | def _start_transaction(self, h, req): | ||
304 | try: | ||
305 | if req.has_data(): | ||
306 | data = req.get_data() | ||
307 | h.putrequest('POST', req.get_selector()) | ||
308 | if not req.headers.has_key('Content-type'): | ||
309 | h.putheader('Content-type', | ||
310 | 'application/x-www-form-urlencoded') | ||
311 | if not req.headers.has_key('Content-length'): | ||
312 | h.putheader('Content-length', '%d' % len(data)) | ||
313 | else: | ||
314 | h.putrequest('GET', req.get_selector()) | ||
315 | except (socket.error, httplib.HTTPException), err: | ||
316 | raise urllib2.URLError(err) | ||
317 | |||
318 | for args in self.parent.addheaders: | ||
319 | h.putheader(*args) | ||
320 | for k, v in req.headers.items(): | ||
321 | h.putheader(k, v) | ||
322 | h.endheaders() | ||
323 | if req.has_data(): | ||
324 | h.send(data) | ||
325 | |||
326 | def _get_connection(self, host): | ||
327 | return NotImplementedError | ||
328 | |||
329 | class HTTPHandler(KeepAliveHandler, urllib2.HTTPHandler): | ||
330 | def __init__(self): | ||
331 | KeepAliveHandler.__init__(self) | ||
332 | |||
333 | def http_open(self, req): | ||
334 | return self.do_open(req) | ||
335 | |||
336 | def _get_connection(self, host): | ||
337 | return HTTPConnection(host) | ||
338 | |||
339 | class HTTPSHandler(KeepAliveHandler, urllib2.HTTPSHandler): | ||
340 | def __init__(self, ssl_factory=None): | ||
341 | KeepAliveHandler.__init__(self) | ||
342 | if not ssl_factory: | ||
343 | ssl_factory = sslfactory.get_factory() | ||
344 | self._ssl_factory = ssl_factory | ||
345 | |||
346 | def https_open(self, req): | ||
347 | return self.do_open(req) | ||
348 | |||
349 | def _get_connection(self, host): | ||
350 | return self._ssl_factory.get_https_connection(host) | ||
351 | |||
352 | class HTTPResponse(httplib.HTTPResponse): | ||
353 | # we need to subclass HTTPResponse in order to | ||
354 | # 1) add readline() and readlines() methods | ||
355 | # 2) add close_connection() methods | ||
356 | # 3) add info() and geturl() methods | ||
357 | |||
358 | # in order to add readline(), read must be modified to deal with a | ||
359 | # buffer. example: readline must read a buffer and then spit back | ||
360 | # one line at a time. The only real alternative is to read one | ||
361 | # BYTE at a time (ick). Once something has been read, it can't be | ||
362 | # put back (ok, maybe it can, but that's even uglier than this), | ||
363 | # so if you THEN do a normal read, you must first take stuff from | ||
364 | # the buffer. | ||
365 | |||
366 | # the read method wraps the original to accomodate buffering, | ||
367 | # although read() never adds to the buffer. | ||
368 | # Both readline and readlines have been stolen with almost no | ||
369 | # modification from socket.py | ||
370 | |||
371 | |||
372 | def __init__(self, sock, debuglevel=0, strict=0, method=None): | ||
373 | if method: # the httplib in python 2.3 uses the method arg | ||
374 | httplib.HTTPResponse.__init__(self, sock, debuglevel, method) | ||
375 | else: # 2.2 doesn't | ||
376 | httplib.HTTPResponse.__init__(self, sock, debuglevel) | ||
377 | self.fileno = sock.fileno | ||
378 | self.code = None | ||
379 | self._rbuf = '' | ||
380 | self._rbufsize = 8096 | ||
381 | self._handler = None # inserted by the handler later | ||
382 | self._host = None # (same) | ||
383 | self._url = None # (same) | ||
384 | self._connection = None # (same) | ||
385 | |||
386 | _raw_read = httplib.HTTPResponse.read | ||
387 | |||
388 | def close(self): | ||
389 | if self.fp: | ||
390 | self.fp.close() | ||
391 | self.fp = None | ||
392 | if self._handler: | ||
393 | self._handler._request_closed(self, self._host, | ||
394 | self._connection) | ||
395 | |||
396 | def close_connection(self): | ||
397 | self._handler._remove_connection(self._host, self._connection, close=1) | ||
398 | self.close() | ||
399 | |||
400 | def info(self): | ||
401 | return self.headers | ||
402 | |||
403 | def geturl(self): | ||
404 | return self._url | ||
405 | |||
406 | def read(self, amt=None): | ||
407 | # the _rbuf test is only in this first if for speed. It's not | ||
408 | # logically necessary | ||
409 | if self._rbuf and not amt is None: | ||
410 | L = len(self._rbuf) | ||
411 | if amt > L: | ||
412 | amt -= L | ||
413 | else: | ||
414 | s = self._rbuf[:amt] | ||
415 | self._rbuf = self._rbuf[amt:] | ||
416 | return s | ||
417 | |||
418 | s = self._rbuf + self._raw_read(amt) | ||
419 | self._rbuf = '' | ||
420 | return s | ||
421 | |||
422 | def readline(self, limit=-1): | ||
423 | data = "" | ||
424 | i = self._rbuf.find('\n') | ||
425 | while i < 0 and not (0 < limit <= len(self._rbuf)): | ||
426 | new = self._raw_read(self._rbufsize) | ||
427 | if not new: break | ||
428 | i = new.find('\n') | ||
429 | if i >= 0: i = i + len(self._rbuf) | ||
430 | self._rbuf = self._rbuf + new | ||
431 | if i < 0: i = len(self._rbuf) | ||
432 | else: i = i+1 | ||
433 | if 0 <= limit < len(self._rbuf): i = limit | ||
434 | data, self._rbuf = self._rbuf[:i], self._rbuf[i:] | ||
435 | return data | ||
436 | |||
437 | def readlines(self, sizehint = 0): | ||
438 | total = 0 | ||
439 | list = [] | ||
440 | while 1: | ||
441 | line = self.readline() | ||
442 | if not line: break | ||
443 | list.append(line) | ||
444 | total += len(line) | ||
445 | if sizehint and total >= sizehint: | ||
446 | break | ||
447 | return list | ||
448 | |||
449 | |||
450 | class HTTPConnection(httplib.HTTPConnection): | ||
451 | # use the modified response class | ||
452 | response_class = HTTPResponse | ||
453 | |||
454 | class HTTPSConnection(httplib.HTTPSConnection): | ||
455 | response_class = HTTPResponse | ||
456 | |||
457 | ######################################################################### | ||
458 | ##### TEST FUNCTIONS | ||
459 | ######################################################################### | ||
460 | |||
461 | def error_handler(url): | ||
462 | global HANDLE_ERRORS | ||
463 | orig = HANDLE_ERRORS | ||
464 | keepalive_handler = HTTPHandler() | ||
465 | opener = urllib2.build_opener(keepalive_handler) | ||
466 | urllib2.install_opener(opener) | ||
467 | pos = {0: 'off', 1: 'on'} | ||
468 | for i in (0, 1): | ||
469 | print " fancy error handling %s (HANDLE_ERRORS = %i)" % (pos[i], i) | ||
470 | HANDLE_ERRORS = i | ||
471 | try: | ||
472 | fo = urllib2.urlopen(url) | ||
473 | foo = fo.read() | ||
474 | fo.close() | ||
475 | try: status, reason = fo.status, fo.reason | ||
476 | except AttributeError: status, reason = None, None | ||
477 | except IOError, e: | ||
478 | print " EXCEPTION: %s" % e | ||
479 | raise | ||
480 | else: | ||
481 | print " status = %s, reason = %s" % (status, reason) | ||
482 | HANDLE_ERRORS = orig | ||
483 | hosts = keepalive_handler.open_connections() | ||
484 | print "open connections:", hosts | ||
485 | keepalive_handler.close_all() | ||
486 | |||
487 | def continuity(url): | ||
488 | import md5 | ||
489 | format = '%25s: %s' | ||
490 | |||
491 | # first fetch the file with the normal http handler | ||
492 | opener = urllib2.build_opener() | ||
493 | urllib2.install_opener(opener) | ||
494 | fo = urllib2.urlopen(url) | ||
495 | foo = fo.read() | ||
496 | fo.close() | ||
497 | m = md5.new(foo) | ||
498 | print format % ('normal urllib', m.hexdigest()) | ||
499 | |||
500 | # now install the keepalive handler and try again | ||
501 | opener = urllib2.build_opener(HTTPHandler()) | ||
502 | urllib2.install_opener(opener) | ||
503 | |||
504 | fo = urllib2.urlopen(url) | ||
505 | foo = fo.read() | ||
506 | fo.close() | ||
507 | m = md5.new(foo) | ||
508 | print format % ('keepalive read', m.hexdigest()) | ||
509 | |||
510 | fo = urllib2.urlopen(url) | ||
511 | foo = '' | ||
512 | while 1: | ||
513 | f = fo.readline() | ||
514 | if f: foo = foo + f | ||
515 | else: break | ||
516 | fo.close() | ||
517 | m = md5.new(foo) | ||
518 | print format % ('keepalive readline', m.hexdigest()) | ||
519 | |||
520 | def comp(N, url): | ||
521 | print ' making %i connections to:\n %s' % (N, url) | ||
522 | |||
523 | sys.stdout.write(' first using the normal urllib handlers') | ||
524 | # first use normal opener | ||
525 | opener = urllib2.build_opener() | ||
526 | urllib2.install_opener(opener) | ||
527 | t1 = fetch(N, url) | ||
528 | print ' TIME: %.3f s' % t1 | ||
529 | |||
530 | sys.stdout.write(' now using the keepalive handler ') | ||
531 | # now install the keepalive handler and try again | ||
532 | opener = urllib2.build_opener(HTTPHandler()) | ||
533 | urllib2.install_opener(opener) | ||
534 | t2 = fetch(N, url) | ||
535 | print ' TIME: %.3f s' % t2 | ||
536 | print ' improvement factor: %.2f' % (t1/t2, ) | ||
537 | |||
538 | def fetch(N, url, delay=0): | ||
539 | import time | ||
540 | lens = [] | ||
541 | starttime = time.time() | ||
542 | for i in range(N): | ||
543 | if delay and i > 0: time.sleep(delay) | ||
544 | fo = urllib2.urlopen(url) | ||
545 | foo = fo.read() | ||
546 | fo.close() | ||
547 | lens.append(len(foo)) | ||
548 | diff = time.time() - starttime | ||
549 | |||
550 | j = 0 | ||
551 | for i in lens[1:]: | ||
552 | j = j + 1 | ||
553 | if not i == lens[0]: | ||
554 | print "WARNING: inconsistent length on read %i: %i" % (j, i) | ||
555 | |||
556 | return diff | ||
557 | |||
558 | def test_timeout(url): | ||
559 | global DEBUG | ||
560 | dbbackup = DEBUG | ||
561 | class FakeLogger: | ||
562 | def debug(self, msg, *args): print msg % args | ||
563 | info = warning = error = debug | ||
564 | DEBUG = FakeLogger() | ||
565 | print " fetching the file to establish a connection" | ||
566 | fo = urllib2.urlopen(url) | ||
567 | data1 = fo.read() | ||
568 | fo.close() | ||
569 | |||
570 | i = 20 | ||
571 | print " waiting %i seconds for the server to close the connection" % i | ||
572 | while i > 0: | ||
573 | sys.stdout.write('\r %2i' % i) | ||
574 | sys.stdout.flush() | ||
575 | time.sleep(1) | ||
576 | i -= 1 | ||
577 | sys.stderr.write('\r') | ||
578 | |||
579 | print " fetching the file a second time" | ||
580 | fo = urllib2.urlopen(url) | ||
581 | data2 = fo.read() | ||
582 | fo.close() | ||
583 | |||
584 | if data1 == data2: | ||
585 | print ' data are identical' | ||
586 | else: | ||
587 | print ' ERROR: DATA DIFFER' | ||
588 | |||
589 | DEBUG = dbbackup | ||
590 | |||
591 | |||
592 | def test(url, N=10): | ||
593 | print "checking error hander (do this on a non-200)" | ||
594 | try: error_handler(url) | ||
595 | except IOError, e: | ||
596 | print "exiting - exception will prevent further tests" | ||
597 | sys.exit() | ||
598 | |||
599 | print "performing continuity test (making sure stuff isn't corrupted)" | ||
600 | continuity(url) | ||
601 | |||
602 | print "performing speed comparison" | ||
603 | comp(N, url) | ||
604 | |||
605 | print "performing dropped-connection check" | ||
606 | test_timeout(url) | ||
607 | |||
608 | if __name__ == '__main__': | ||
609 | import time | ||
610 | import sys | ||
611 | try: | ||
612 | N = int(sys.argv[1]) | ||
613 | url = sys.argv[2] | ||
614 | except: | ||
615 | print "%s <integer> <url>" % sys.argv[0] | ||
616 | else: | ||
617 | test(url, N) | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/mirror.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/mirror.py new file mode 100644 index 0000000000..9664c6b5c5 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/mirror.py | |||
@@ -0,0 +1,458 @@ | |||
1 | # This library is free software; you can redistribute it and/or | ||
2 | # modify it under the terms of the GNU Lesser General Public | ||
3 | # License as published by the Free Software Foundation; either | ||
4 | # version 2.1 of the License, or (at your option) any later version. | ||
5 | # | ||
6 | # This library is distributed in the hope that it will be useful, | ||
7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
9 | # Lesser General Public License for more details. | ||
10 | # | ||
11 | # You should have received a copy of the GNU Lesser General Public | ||
12 | # License along with this library; if not, write to the | ||
13 | # Free Software Foundation, Inc., | ||
14 | # 59 Temple Place, Suite 330, | ||
15 | # Boston, MA 02111-1307 USA | ||
16 | |||
17 | # This file is part of urlgrabber, a high-level cross-protocol url-grabber | ||
18 | # Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko | ||
19 | |||
20 | """Module for downloading files from a pool of mirrors | ||
21 | |||
22 | DESCRIPTION | ||
23 | |||
24 | This module provides support for downloading files from a pool of | ||
25 | mirrors with configurable failover policies. To a large extent, the | ||
26 | failover policy is chosen by using different classes derived from | ||
27 | the main class, MirrorGroup. | ||
28 | |||
29 | Instances of MirrorGroup (and cousins) act very much like URLGrabber | ||
30 | instances in that they have urlread, urlgrab, and urlopen methods. | ||
31 | They can therefore, be used in very similar ways. | ||
32 | |||
33 | from urlgrabber.grabber import URLGrabber | ||
34 | from urlgrabber.mirror import MirrorGroup | ||
35 | gr = URLGrabber() | ||
36 | mg = MirrorGroup(gr, ['http://foo.com/some/directory/', | ||
37 | 'http://bar.org/maybe/somewhere/else/', | ||
38 | 'ftp://baz.net/some/other/place/entirely/'] | ||
39 | mg.urlgrab('relative/path.zip') | ||
40 | |||
41 | The assumption is that all mirrors are identical AFTER the base urls | ||
42 | specified, so that any mirror can be used to fetch any file. | ||
43 | |||
44 | FAILOVER | ||
45 | |||
46 | The failover mechanism is designed to be customized by subclassing | ||
47 | from MirrorGroup to change the details of the behavior. In general, | ||
48 | the classes maintain a master mirror list and a "current mirror" | ||
49 | index. When a download is initiated, a copy of this list and index | ||
50 | is created for that download only. The specific failover policy | ||
51 | depends on the class used, and so is documented in the class | ||
52 | documentation. Note that ANY behavior of the class can be | ||
53 | overridden, so any failover policy at all is possible (although | ||
54 | you may need to change the interface in extreme cases). | ||
55 | |||
56 | CUSTOMIZATION | ||
57 | |||
58 | Most customization of a MirrorGroup object is done at instantiation | ||
59 | time (or via subclassing). There are four major types of | ||
60 | customization: | ||
61 | |||
62 | 1) Pass in a custom urlgrabber - The passed in urlgrabber will be | ||
63 | used (by default... see #2) for the grabs, so options to it | ||
64 | apply for the url-fetching | ||
65 | |||
66 | 2) Custom mirror list - Mirror lists can simply be a list of | ||
67 | stings mirrors (as shown in the example above) but each can | ||
68 | also be a dict, allowing for more options. For example, the | ||
69 | first mirror in the list above could also have been: | ||
70 | |||
71 | {'mirror': 'http://foo.com/some/directory/', | ||
72 | 'grabber': <a custom grabber to be used for this mirror>, | ||
73 | 'kwargs': { <a dict of arguments passed to the grabber> }} | ||
74 | |||
75 | All mirrors are converted to this format internally. If | ||
76 | 'grabber' is omitted, the default grabber will be used. If | ||
77 | kwargs are omitted, then (duh) they will not be used. | ||
78 | |||
79 | 3) Pass keyword arguments when instantiating the mirror group. | ||
80 | See, for example, the failure_callback argument. | ||
81 | |||
82 | 4) Finally, any kwargs passed in for the specific file (to the | ||
83 | urlgrab method, for example) will be folded in. The options | ||
84 | passed into the grabber's urlXXX methods will override any | ||
85 | options specified in a custom mirror dict. | ||
86 | |||
87 | """ | ||
88 | |||
89 | # $Id: mirror.py,v 1.14 2006/02/22 18:26:46 mstenner Exp $ | ||
90 | |||
91 | import random | ||
92 | import thread # needed for locking to make this threadsafe | ||
93 | |||
94 | from grabber import URLGrabError, CallbackObject, DEBUG | ||
95 | |||
96 | try: | ||
97 | from i18n import _ | ||
98 | except ImportError, msg: | ||
99 | def _(st): return st | ||
100 | |||
101 | class GrabRequest: | ||
102 | """This is a dummy class used to hold information about the specific | ||
103 | request. For example, a single file. By maintaining this information | ||
104 | separately, we can accomplish two things: | ||
105 | |||
106 | 1) make it a little easier to be threadsafe | ||
107 | 2) have request-specific parameters | ||
108 | """ | ||
109 | pass | ||
110 | |||
111 | class MirrorGroup: | ||
112 | """Base Mirror class | ||
113 | |||
114 | Instances of this class are built with a grabber object and a list | ||
115 | of mirrors. Then all calls to urlXXX should be passed relative urls. | ||
116 | The requested file will be searched for on the first mirror. If the | ||
117 | grabber raises an exception (possibly after some retries) then that | ||
118 | mirror will be removed from the list, and the next will be attempted. | ||
119 | If all mirrors are exhausted, then an exception will be raised. | ||
120 | |||
121 | MirrorGroup has the following failover policy: | ||
122 | |||
123 | * downloads begin with the first mirror | ||
124 | |||
125 | * by default (see default_action below) a failure (after retries) | ||
126 | causes it to increment the local AND master indices. Also, | ||
127 | the current mirror is removed from the local list (but NOT the | ||
128 | master list - the mirror can potentially be used for other | ||
129 | files) | ||
130 | |||
131 | * if the local list is ever exhausted, a URLGrabError will be | ||
132 | raised (errno=256, no more mirrors) | ||
133 | |||
134 | OPTIONS | ||
135 | |||
136 | In addition to the required arguments "grabber" and "mirrors", | ||
137 | MirrorGroup also takes the following optional arguments: | ||
138 | |||
139 | default_action | ||
140 | |||
141 | A dict that describes the actions to be taken upon failure | ||
142 | (after retries). default_action can contain any of the | ||
143 | following keys (shown here with their default values): | ||
144 | |||
145 | default_action = {'increment': 1, | ||
146 | 'increment_master': 1, | ||
147 | 'remove': 1, | ||
148 | 'remove_master': 0, | ||
149 | 'fail': 0} | ||
150 | |||
151 | In this context, 'increment' means "use the next mirror" and | ||
152 | 'remove' means "never use this mirror again". The two | ||
153 | 'master' values refer to the instance-level mirror list (used | ||
154 | for all files), whereas the non-master values refer to the | ||
155 | current download only. | ||
156 | |||
157 | The 'fail' option will cause immediate failure by re-raising | ||
158 | the exception and no further attempts to get the current | ||
159 | download. | ||
160 | |||
161 | This dict can be set at instantiation time, | ||
162 | mg = MirrorGroup(grabber, mirrors, default_action={'fail':1}) | ||
163 | at method-execution time (only applies to current fetch), | ||
164 | filename = mg.urlgrab(url, default_action={'increment': 0}) | ||
165 | or by returning an action dict from the failure_callback | ||
166 | return {'fail':0} | ||
167 | in increasing precedence. | ||
168 | |||
169 | If all three of these were done, the net result would be: | ||
170 | {'increment': 0, # set in method | ||
171 | 'increment_master': 1, # class default | ||
172 | 'remove': 1, # class default | ||
173 | 'remove_master': 0, # class default | ||
174 | 'fail': 0} # set at instantiation, reset | ||
175 | # from callback | ||
176 | |||
177 | failure_callback | ||
178 | |||
179 | this is a callback that will be called when a mirror "fails", | ||
180 | meaning the grabber raises some URLGrabError. If this is a | ||
181 | tuple, it is interpreted to be of the form (cb, args, kwargs) | ||
182 | where cb is the actual callable object (function, method, | ||
183 | etc). Otherwise, it is assumed to be the callable object | ||
184 | itself. The callback will be passed a grabber.CallbackObject | ||
185 | instance along with args and kwargs (if present). The following | ||
186 | attributes are defined withing the instance: | ||
187 | |||
188 | obj.exception = < exception that was raised > | ||
189 | obj.mirror = < the mirror that was tried > | ||
190 | obj.relative_url = < url relative to the mirror > | ||
191 | obj.url = < full url that failed > | ||
192 | # .url is just the combination of .mirror | ||
193 | # and .relative_url | ||
194 | |||
195 | The failure callback can return an action dict, as described | ||
196 | above. | ||
197 | |||
198 | Like default_action, the failure_callback can be set at | ||
199 | instantiation time or when the urlXXX method is called. In | ||
200 | the latter case, it applies only for that fetch. | ||
201 | |||
202 | The callback can re-raise the exception quite easily. For | ||
203 | example, this is a perfectly adequate callback function: | ||
204 | |||
205 | def callback(obj): raise obj.exception | ||
206 | |||
207 | WARNING: do not save the exception object (or the | ||
208 | CallbackObject instance). As they contain stack frame | ||
209 | references, they can lead to circular references. | ||
210 | |||
211 | Notes: | ||
212 | * The behavior can be customized by deriving and overriding the | ||
213 | 'CONFIGURATION METHODS' | ||
214 | * The 'grabber' instance is kept as a reference, not copied. | ||
215 | Therefore, the grabber instance can be modified externally | ||
216 | and changes will take effect immediately. | ||
217 | """ | ||
218 | |||
219 | # notes on thread-safety: | ||
220 | |||
221 | # A GrabRequest should never be shared by multiple threads because | ||
222 | # it's never saved inside the MG object and never returned outside it. | ||
223 | # therefore, it should be safe to access/modify grabrequest data | ||
224 | # without a lock. However, accessing the mirrors and _next attributes | ||
225 | # of the MG itself must be done when locked to prevent (for example) | ||
226 | # removal of the wrong mirror. | ||
227 | |||
228 | ############################################################## | ||
229 | # CONFIGURATION METHODS - intended to be overridden to | ||
230 | # customize behavior | ||
231 | def __init__(self, grabber, mirrors, **kwargs): | ||
232 | """Initialize the MirrorGroup object. | ||
233 | |||
234 | REQUIRED ARGUMENTS | ||
235 | |||
236 | grabber - URLGrabber instance | ||
237 | mirrors - a list of mirrors | ||
238 | |||
239 | OPTIONAL ARGUMENTS | ||
240 | |||
241 | failure_callback - callback to be used when a mirror fails | ||
242 | default_action - dict of failure actions | ||
243 | |||
244 | See the module-level and class level documentation for more | ||
245 | details. | ||
246 | """ | ||
247 | |||
248 | # OVERRIDE IDEAS: | ||
249 | # shuffle the list to randomize order | ||
250 | self.grabber = grabber | ||
251 | self.mirrors = self._parse_mirrors(mirrors) | ||
252 | self._next = 0 | ||
253 | self._lock = thread.allocate_lock() | ||
254 | self.default_action = None | ||
255 | self._process_kwargs(kwargs) | ||
256 | |||
257 | # if these values are found in **kwargs passed to one of the urlXXX | ||
258 | # methods, they will be stripped before getting passed on to the | ||
259 | # grabber | ||
260 | options = ['default_action', 'failure_callback'] | ||
261 | |||
262 | def _process_kwargs(self, kwargs): | ||
263 | self.failure_callback = kwargs.get('failure_callback') | ||
264 | self.default_action = kwargs.get('default_action') | ||
265 | |||
266 | def _parse_mirrors(self, mirrors): | ||
267 | parsed_mirrors = [] | ||
268 | for m in mirrors: | ||
269 | if type(m) == type(''): m = {'mirror': m} | ||
270 | parsed_mirrors.append(m) | ||
271 | return parsed_mirrors | ||
272 | |||
273 | def _load_gr(self, gr): | ||
274 | # OVERRIDE IDEAS: | ||
275 | # shuffle gr list | ||
276 | self._lock.acquire() | ||
277 | gr.mirrors = list(self.mirrors) | ||
278 | gr._next = self._next | ||
279 | self._lock.release() | ||
280 | |||
281 | def _get_mirror(self, gr): | ||
282 | # OVERRIDE IDEAS: | ||
283 | # return a random mirror so that multiple mirrors get used | ||
284 | # even without failures. | ||
285 | if not gr.mirrors: | ||
286 | raise URLGrabError(256, _('No more mirrors to try.')) | ||
287 | return gr.mirrors[gr._next] | ||
288 | |||
289 | def _failure(self, gr, cb_obj): | ||
290 | # OVERRIDE IDEAS: | ||
291 | # inspect the error - remove=1 for 404, remove=2 for connection | ||
292 | # refused, etc. (this can also be done via | ||
293 | # the callback) | ||
294 | cb = gr.kw.get('failure_callback') or self.failure_callback | ||
295 | if cb: | ||
296 | if type(cb) == type( () ): | ||
297 | cb, args, kwargs = cb | ||
298 | else: | ||
299 | args, kwargs = (), {} | ||
300 | action = cb(cb_obj, *args, **kwargs) or {} | ||
301 | else: | ||
302 | action = {} | ||
303 | # XXXX - decide - there are two ways to do this | ||
304 | # the first is action-overriding as a whole - use the entire action | ||
305 | # or fall back on module level defaults | ||
306 | #action = action or gr.kw.get('default_action') or self.default_action | ||
307 | # the other is to fall through for each element in the action dict | ||
308 | a = dict(self.default_action or {}) | ||
309 | a.update(gr.kw.get('default_action', {})) | ||
310 | a.update(action) | ||
311 | action = a | ||
312 | self.increment_mirror(gr, action) | ||
313 | if action and action.get('fail', 0): raise | ||
314 | |||
315 | def increment_mirror(self, gr, action={}): | ||
316 | """Tell the mirror object increment the mirror index | ||
317 | |||
318 | This increments the mirror index, which amounts to telling the | ||
319 | mirror object to use a different mirror (for this and future | ||
320 | downloads). | ||
321 | |||
322 | This is a SEMI-public method. It will be called internally, | ||
323 | and you may never need to call it. However, it is provided | ||
324 | (and is made public) so that the calling program can increment | ||
325 | the mirror choice for methods like urlopen. For example, with | ||
326 | urlopen, there's no good way for the mirror group to know that | ||
327 | an error occurs mid-download (it's already returned and given | ||
328 | you the file object). | ||
329 | |||
330 | remove --- can have several values | ||
331 | 0 do not remove the mirror from the list | ||
332 | 1 remove the mirror for this download only | ||
333 | 2 remove the mirror permanently | ||
334 | |||
335 | beware of remove=0 as it can lead to infinite loops | ||
336 | """ | ||
337 | badmirror = gr.mirrors[gr._next] | ||
338 | |||
339 | self._lock.acquire() | ||
340 | try: | ||
341 | ind = self.mirrors.index(badmirror) | ||
342 | except ValueError: | ||
343 | pass | ||
344 | else: | ||
345 | if action.get('remove_master', 0): | ||
346 | del self.mirrors[ind] | ||
347 | elif self._next == ind and action.get('increment_master', 1): | ||
348 | self._next += 1 | ||
349 | if self._next >= len(self.mirrors): self._next = 0 | ||
350 | self._lock.release() | ||
351 | |||
352 | if action.get('remove', 1): | ||
353 | del gr.mirrors[gr._next] | ||
354 | elif action.get('increment', 1): | ||
355 | gr._next += 1 | ||
356 | if gr._next >= len(gr.mirrors): gr._next = 0 | ||
357 | |||
358 | if DEBUG: | ||
359 | grm = [m['mirror'] for m in gr.mirrors] | ||
360 | DEBUG.info('GR mirrors: [%s] %i', ' '.join(grm), gr._next) | ||
361 | selfm = [m['mirror'] for m in self.mirrors] | ||
362 | DEBUG.info('MAIN mirrors: [%s] %i', ' '.join(selfm), self._next) | ||
363 | |||
364 | ##################################################################### | ||
365 | # NON-CONFIGURATION METHODS | ||
366 | # these methods are designed to be largely workhorse methods that | ||
367 | # are not intended to be overridden. That doesn't mean you can't; | ||
368 | # if you want to, feel free, but most things can be done by | ||
369 | # by overriding the configuration methods :) | ||
370 | |||
371 | def _join_url(self, base_url, rel_url): | ||
372 | if base_url.endswith('/') or rel_url.startswith('/'): | ||
373 | return base_url + rel_url | ||
374 | else: | ||
375 | return base_url + '/' + rel_url | ||
376 | |||
377 | def _mirror_try(self, func, url, kw): | ||
378 | gr = GrabRequest() | ||
379 | gr.func = func | ||
380 | gr.url = url | ||
381 | gr.kw = dict(kw) | ||
382 | self._load_gr(gr) | ||
383 | |||
384 | for k in self.options: | ||
385 | try: del kw[k] | ||
386 | except KeyError: pass | ||
387 | |||
388 | while 1: | ||
389 | mirrorchoice = self._get_mirror(gr) | ||
390 | fullurl = self._join_url(mirrorchoice['mirror'], gr.url) | ||
391 | kwargs = dict(mirrorchoice.get('kwargs', {})) | ||
392 | kwargs.update(kw) | ||
393 | grabber = mirrorchoice.get('grabber') or self.grabber | ||
394 | func_ref = getattr(grabber, func) | ||
395 | if DEBUG: DEBUG.info('MIRROR: trying %s -> %s', url, fullurl) | ||
396 | try: | ||
397 | return func_ref( *(fullurl,), **kwargs ) | ||
398 | except URLGrabError, e: | ||
399 | if DEBUG: DEBUG.info('MIRROR: failed') | ||
400 | obj = CallbackObject() | ||
401 | obj.exception = e | ||
402 | obj.mirror = mirrorchoice['mirror'] | ||
403 | obj.relative_url = gr.url | ||
404 | obj.url = fullurl | ||
405 | self._failure(gr, obj) | ||
406 | |||
407 | def urlgrab(self, url, filename=None, **kwargs): | ||
408 | kw = dict(kwargs) | ||
409 | kw['filename'] = filename | ||
410 | func = 'urlgrab' | ||
411 | return self._mirror_try(func, url, kw) | ||
412 | |||
413 | def urlopen(self, url, **kwargs): | ||
414 | kw = dict(kwargs) | ||
415 | func = 'urlopen' | ||
416 | return self._mirror_try(func, url, kw) | ||
417 | |||
418 | def urlread(self, url, limit=None, **kwargs): | ||
419 | kw = dict(kwargs) | ||
420 | kw['limit'] = limit | ||
421 | func = 'urlread' | ||
422 | return self._mirror_try(func, url, kw) | ||
423 | |||
424 | |||
425 | class MGRandomStart(MirrorGroup): | ||
426 | """A mirror group that starts at a random mirror in the list. | ||
427 | |||
428 | This behavior of this class is identical to MirrorGroup, except that | ||
429 | it starts at a random location in the mirror list. | ||
430 | """ | ||
431 | |||
432 | def __init__(self, grabber, mirrors, **kwargs): | ||
433 | """Initialize the object | ||
434 | |||
435 | The arguments for intialization are the same as for MirrorGroup | ||
436 | """ | ||
437 | MirrorGroup.__init__(self, grabber, mirrors, **kwargs) | ||
438 | self._next = random.randrange(len(mirrors)) | ||
439 | |||
440 | class MGRandomOrder(MirrorGroup): | ||
441 | """A mirror group that uses mirrors in a random order. | ||
442 | |||
443 | This behavior of this class is identical to MirrorGroup, except that | ||
444 | it uses the mirrors in a random order. Note that the order is set at | ||
445 | initialization time and fixed thereafter. That is, it does not pick a | ||
446 | random mirror after each failure. | ||
447 | """ | ||
448 | |||
449 | def __init__(self, grabber, mirrors, **kwargs): | ||
450 | """Initialize the object | ||
451 | |||
452 | The arguments for intialization are the same as for MirrorGroup | ||
453 | """ | ||
454 | MirrorGroup.__init__(self, grabber, mirrors, **kwargs) | ||
455 | random.shuffle(self.mirrors) | ||
456 | |||
457 | if __name__ == '__main__': | ||
458 | pass | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/progress.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/progress.py new file mode 100644 index 0000000000..02db524e76 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/progress.py | |||
@@ -0,0 +1,530 @@ | |||
1 | # This library is free software; you can redistribute it and/or | ||
2 | # modify it under the terms of the GNU Lesser General Public | ||
3 | # License as published by the Free Software Foundation; either | ||
4 | # version 2.1 of the License, or (at your option) any later version. | ||
5 | # | ||
6 | # This library is distributed in the hope that it will be useful, | ||
7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
9 | # Lesser General Public License for more details. | ||
10 | # | ||
11 | # You should have received a copy of the GNU Lesser General Public | ||
12 | # License along with this library; if not, write to the | ||
13 | # Free Software Foundation, Inc., | ||
14 | # 59 Temple Place, Suite 330, | ||
15 | # Boston, MA 02111-1307 USA | ||
16 | |||
17 | # This file is part of urlgrabber, a high-level cross-protocol url-grabber | ||
18 | # Copyright 2002-2004 Michael D. Stenner, Ryan Tomayko | ||
19 | |||
20 | # $Id: progress.py,v 1.7 2005/08/19 21:59:07 mstenner Exp $ | ||
21 | |||
22 | import sys | ||
23 | import time | ||
24 | import math | ||
25 | import thread | ||
26 | |||
27 | class BaseMeter: | ||
28 | def __init__(self): | ||
29 | self.update_period = 0.3 # seconds | ||
30 | |||
31 | self.filename = None | ||
32 | self.url = None | ||
33 | self.basename = None | ||
34 | self.text = None | ||
35 | self.size = None | ||
36 | self.start_time = None | ||
37 | self.last_amount_read = 0 | ||
38 | self.last_update_time = None | ||
39 | self.re = RateEstimator() | ||
40 | |||
41 | def start(self, filename=None, url=None, basename=None, | ||
42 | size=None, now=None, text=None): | ||
43 | self.filename = filename | ||
44 | self.url = url | ||
45 | self.basename = basename | ||
46 | self.text = text | ||
47 | |||
48 | #size = None ######### TESTING | ||
49 | self.size = size | ||
50 | if not size is None: self.fsize = format_number(size) + 'B' | ||
51 | |||
52 | if now is None: now = time.time() | ||
53 | self.start_time = now | ||
54 | self.re.start(size, now) | ||
55 | self.last_amount_read = 0 | ||
56 | self.last_update_time = now | ||
57 | self._do_start(now) | ||
58 | |||
59 | def _do_start(self, now=None): | ||
60 | pass | ||
61 | |||
62 | def update(self, amount_read, now=None): | ||
63 | # for a real gui, you probably want to override and put a call | ||
64 | # to your mainloop iteration function here | ||
65 | if now is None: now = time.time() | ||
66 | if (now >= self.last_update_time + self.update_period) or \ | ||
67 | not self.last_update_time: | ||
68 | self.re.update(amount_read, now) | ||
69 | self.last_amount_read = amount_read | ||
70 | self.last_update_time = now | ||
71 | self._do_update(amount_read, now) | ||
72 | |||
73 | def _do_update(self, amount_read, now=None): | ||
74 | pass | ||
75 | |||
76 | def end(self, amount_read, now=None): | ||
77 | if now is None: now = time.time() | ||
78 | self.re.update(amount_read, now) | ||
79 | self.last_amount_read = amount_read | ||
80 | self.last_update_time = now | ||
81 | self._do_end(amount_read, now) | ||
82 | |||
83 | def _do_end(self, amount_read, now=None): | ||
84 | pass | ||
85 | |||
86 | class TextMeter(BaseMeter): | ||
87 | def __init__(self, fo=sys.stderr): | ||
88 | BaseMeter.__init__(self) | ||
89 | self.fo = fo | ||
90 | |||
91 | def _do_update(self, amount_read, now=None): | ||
92 | etime = self.re.elapsed_time() | ||
93 | fetime = format_time(etime) | ||
94 | fread = format_number(amount_read) | ||
95 | #self.size = None | ||
96 | if self.text is not None: | ||
97 | text = self.text | ||
98 | else: | ||
99 | text = self.basename | ||
100 | if self.size is None: | ||
101 | out = '\r%-60.60s %5sB %s ' % \ | ||
102 | (text, fread, fetime) | ||
103 | else: | ||
104 | rtime = self.re.remaining_time() | ||
105 | frtime = format_time(rtime) | ||
106 | frac = self.re.fraction_read() | ||
107 | bar = '='*int(25 * frac) | ||
108 | |||
109 | out = '\r%-25.25s %3i%% |%-25.25s| %5sB %8s ETA ' % \ | ||
110 | (text, frac*100, bar, fread, frtime) | ||
111 | |||
112 | self.fo.write(out) | ||
113 | self.fo.flush() | ||
114 | |||
115 | def _do_end(self, amount_read, now=None): | ||
116 | total_time = format_time(self.re.elapsed_time()) | ||
117 | total_size = format_number(amount_read) | ||
118 | if self.text is not None: | ||
119 | text = self.text | ||
120 | else: | ||
121 | text = self.basename | ||
122 | if self.size is None: | ||
123 | out = '\r%-60.60s %5sB %s ' % \ | ||
124 | (text, total_size, total_time) | ||
125 | else: | ||
126 | bar = '='*25 | ||
127 | out = '\r%-25.25s %3i%% |%-25.25s| %5sB %8s ' % \ | ||
128 | (text, 100, bar, total_size, total_time) | ||
129 | self.fo.write(out + '\n') | ||
130 | self.fo.flush() | ||
131 | |||
132 | text_progress_meter = TextMeter | ||
133 | |||
134 | class MultiFileHelper(BaseMeter): | ||
135 | def __init__(self, master): | ||
136 | BaseMeter.__init__(self) | ||
137 | self.master = master | ||
138 | |||
139 | def _do_start(self, now): | ||
140 | self.master.start_meter(self, now) | ||
141 | |||
142 | def _do_update(self, amount_read, now): | ||
143 | # elapsed time since last update | ||
144 | self.master.update_meter(self, now) | ||
145 | |||
146 | def _do_end(self, amount_read, now): | ||
147 | self.ftotal_time = format_time(now - self.start_time) | ||
148 | self.ftotal_size = format_number(self.last_amount_read) | ||
149 | self.master.end_meter(self, now) | ||
150 | |||
151 | def failure(self, message, now=None): | ||
152 | self.master.failure_meter(self, message, now) | ||
153 | |||
154 | def message(self, message): | ||
155 | self.master.message_meter(self, message) | ||
156 | |||
157 | class MultiFileMeter: | ||
158 | helperclass = MultiFileHelper | ||
159 | def __init__(self): | ||
160 | self.meters = [] | ||
161 | self.in_progress_meters = [] | ||
162 | self._lock = thread.allocate_lock() | ||
163 | self.update_period = 0.3 # seconds | ||
164 | |||
165 | self.numfiles = None | ||
166 | self.finished_files = 0 | ||
167 | self.failed_files = 0 | ||
168 | self.open_files = 0 | ||
169 | self.total_size = None | ||
170 | self.failed_size = 0 | ||
171 | self.start_time = None | ||
172 | self.finished_file_size = 0 | ||
173 | self.last_update_time = None | ||
174 | self.re = RateEstimator() | ||
175 | |||
176 | def start(self, numfiles=None, total_size=None, now=None): | ||
177 | if now is None: now = time.time() | ||
178 | self.numfiles = numfiles | ||
179 | self.finished_files = 0 | ||
180 | self.failed_files = 0 | ||
181 | self.open_files = 0 | ||
182 | self.total_size = total_size | ||
183 | self.failed_size = 0 | ||
184 | self.start_time = now | ||
185 | self.finished_file_size = 0 | ||
186 | self.last_update_time = now | ||
187 | self.re.start(total_size, now) | ||
188 | self._do_start(now) | ||
189 | |||
190 | def _do_start(self, now): | ||
191 | pass | ||
192 | |||
193 | def end(self, now=None): | ||
194 | if now is None: now = time.time() | ||
195 | self._do_end(now) | ||
196 | |||
197 | def _do_end(self, now): | ||
198 | pass | ||
199 | |||
200 | def lock(self): self._lock.acquire() | ||
201 | def unlock(self): self._lock.release() | ||
202 | |||
203 | ########################################################### | ||
204 | # child meter creation and destruction | ||
205 | def newMeter(self): | ||
206 | newmeter = self.helperclass(self) | ||
207 | self.meters.append(newmeter) | ||
208 | return newmeter | ||
209 | |||
210 | def removeMeter(self, meter): | ||
211 | self.meters.remove(meter) | ||
212 | |||
213 | ########################################################### | ||
214 | # child functions - these should only be called by helpers | ||
215 | def start_meter(self, meter, now): | ||
216 | if not meter in self.meters: | ||
217 | raise ValueError('attempt to use orphaned meter') | ||
218 | self._lock.acquire() | ||
219 | try: | ||
220 | if not meter in self.in_progress_meters: | ||
221 | self.in_progress_meters.append(meter) | ||
222 | self.open_files += 1 | ||
223 | finally: | ||
224 | self._lock.release() | ||
225 | self._do_start_meter(meter, now) | ||
226 | |||
227 | def _do_start_meter(self, meter, now): | ||
228 | pass | ||
229 | |||
230 | def update_meter(self, meter, now): | ||
231 | if not meter in self.meters: | ||
232 | raise ValueError('attempt to use orphaned meter') | ||
233 | if (now >= self.last_update_time + self.update_period) or \ | ||
234 | not self.last_update_time: | ||
235 | self.re.update(self._amount_read(), now) | ||
236 | self.last_update_time = now | ||
237 | self._do_update_meter(meter, now) | ||
238 | |||
239 | def _do_update_meter(self, meter, now): | ||
240 | pass | ||
241 | |||
242 | def end_meter(self, meter, now): | ||
243 | if not meter in self.meters: | ||
244 | raise ValueError('attempt to use orphaned meter') | ||
245 | self._lock.acquire() | ||
246 | try: | ||
247 | try: self.in_progress_meters.remove(meter) | ||
248 | except ValueError: pass | ||
249 | self.open_files -= 1 | ||
250 | self.finished_files += 1 | ||
251 | self.finished_file_size += meter.last_amount_read | ||
252 | finally: | ||
253 | self._lock.release() | ||
254 | self._do_end_meter(meter, now) | ||
255 | |||
256 | def _do_end_meter(self, meter, now): | ||
257 | pass | ||
258 | |||
259 | def failure_meter(self, meter, message, now): | ||
260 | if not meter in self.meters: | ||
261 | raise ValueError('attempt to use orphaned meter') | ||
262 | self._lock.acquire() | ||
263 | try: | ||
264 | try: self.in_progress_meters.remove(meter) | ||
265 | except ValueError: pass | ||
266 | self.open_files -= 1 | ||
267 | self.failed_files += 1 | ||
268 | if meter.size and self.failed_size is not None: | ||
269 | self.failed_size += meter.size | ||
270 | else: | ||
271 | self.failed_size = None | ||
272 | finally: | ||
273 | self._lock.release() | ||
274 | self._do_failure_meter(meter, message, now) | ||
275 | |||
276 | def _do_failure_meter(self, meter, message, now): | ||
277 | pass | ||
278 | |||
279 | def message_meter(self, meter, message): | ||
280 | pass | ||
281 | |||
282 | ######################################################## | ||
283 | # internal functions | ||
284 | def _amount_read(self): | ||
285 | tot = self.finished_file_size | ||
286 | for m in self.in_progress_meters: | ||
287 | tot += m.last_amount_read | ||
288 | return tot | ||
289 | |||
290 | |||
291 | class TextMultiFileMeter(MultiFileMeter): | ||
292 | def __init__(self, fo=sys.stderr): | ||
293 | self.fo = fo | ||
294 | MultiFileMeter.__init__(self) | ||
295 | |||
296 | # files: ###/### ###% data: ######/###### ###% time: ##:##:##/##:##:## | ||
297 | def _do_update_meter(self, meter, now): | ||
298 | self._lock.acquire() | ||
299 | try: | ||
300 | format = "files: %3i/%-3i %3i%% data: %6.6s/%-6.6s %3i%% " \ | ||
301 | "time: %8.8s/%8.8s" | ||
302 | df = self.finished_files | ||
303 | tf = self.numfiles or 1 | ||
304 | pf = 100 * float(df)/tf + 0.49 | ||
305 | dd = self.re.last_amount_read | ||
306 | td = self.total_size | ||
307 | pd = 100 * (self.re.fraction_read() or 0) + 0.49 | ||
308 | dt = self.re.elapsed_time() | ||
309 | rt = self.re.remaining_time() | ||
310 | if rt is None: tt = None | ||
311 | else: tt = dt + rt | ||
312 | |||
313 | fdd = format_number(dd) + 'B' | ||
314 | ftd = format_number(td) + 'B' | ||
315 | fdt = format_time(dt, 1) | ||
316 | ftt = format_time(tt, 1) | ||
317 | |||
318 | out = '%-79.79s' % (format % (df, tf, pf, fdd, ftd, pd, fdt, ftt)) | ||
319 | self.fo.write('\r' + out) | ||
320 | self.fo.flush() | ||
321 | finally: | ||
322 | self._lock.release() | ||
323 | |||
324 | def _do_end_meter(self, meter, now): | ||
325 | self._lock.acquire() | ||
326 | try: | ||
327 | format = "%-30.30s %6.6s %8.8s %9.9s" | ||
328 | fn = meter.basename | ||
329 | size = meter.last_amount_read | ||
330 | fsize = format_number(size) + 'B' | ||
331 | et = meter.re.elapsed_time() | ||
332 | fet = format_time(et, 1) | ||
333 | frate = format_number(size / et) + 'B/s' | ||
334 | |||
335 | out = '%-79.79s' % (format % (fn, fsize, fet, frate)) | ||
336 | self.fo.write('\r' + out + '\n') | ||
337 | finally: | ||
338 | self._lock.release() | ||
339 | self._do_update_meter(meter, now) | ||
340 | |||
341 | def _do_failure_meter(self, meter, message, now): | ||
342 | self._lock.acquire() | ||
343 | try: | ||
344 | format = "%-30.30s %6.6s %s" | ||
345 | fn = meter.basename | ||
346 | if type(message) in (type(''), type(u'')): | ||
347 | message = message.splitlines() | ||
348 | if not message: message = [''] | ||
349 | out = '%-79s' % (format % (fn, 'FAILED', message[0] or '')) | ||
350 | self.fo.write('\r' + out + '\n') | ||
351 | for m in message[1:]: self.fo.write(' ' + m + '\n') | ||
352 | self._lock.release() | ||
353 | finally: | ||
354 | self._do_update_meter(meter, now) | ||
355 | |||
356 | def message_meter(self, meter, message): | ||
357 | self._lock.acquire() | ||
358 | try: | ||
359 | pass | ||
360 | finally: | ||
361 | self._lock.release() | ||
362 | |||
363 | def _do_end(self, now): | ||
364 | self._do_update_meter(None, now) | ||
365 | self._lock.acquire() | ||
366 | try: | ||
367 | self.fo.write('\n') | ||
368 | self.fo.flush() | ||
369 | finally: | ||
370 | self._lock.release() | ||
371 | |||
372 | ###################################################################### | ||
373 | # support classes and functions | ||
374 | |||
375 | class RateEstimator: | ||
376 | def __init__(self, timescale=5.0): | ||
377 | self.timescale = timescale | ||
378 | |||
379 | def start(self, total=None, now=None): | ||
380 | if now is None: now = time.time() | ||
381 | self.total = total | ||
382 | self.start_time = now | ||
383 | self.last_update_time = now | ||
384 | self.last_amount_read = 0 | ||
385 | self.ave_rate = None | ||
386 | |||
387 | def update(self, amount_read, now=None): | ||
388 | if now is None: now = time.time() | ||
389 | if amount_read == 0: | ||
390 | # if we just started this file, all bets are off | ||
391 | self.last_update_time = now | ||
392 | self.last_amount_read = 0 | ||
393 | self.ave_rate = None | ||
394 | return | ||
395 | |||
396 | #print 'times', now, self.last_update_time | ||
397 | time_diff = now - self.last_update_time | ||
398 | read_diff = amount_read - self.last_amount_read | ||
399 | self.last_update_time = now | ||
400 | self.last_amount_read = amount_read | ||
401 | self.ave_rate = self._temporal_rolling_ave(\ | ||
402 | time_diff, read_diff, self.ave_rate, self.timescale) | ||
403 | #print 'results', time_diff, read_diff, self.ave_rate | ||
404 | |||
405 | ##################################################################### | ||
406 | # result methods | ||
407 | def average_rate(self): | ||
408 | "get the average transfer rate (in bytes/second)" | ||
409 | return self.ave_rate | ||
410 | |||
411 | def elapsed_time(self): | ||
412 | "the time between the start of the transfer and the most recent update" | ||
413 | return self.last_update_time - self.start_time | ||
414 | |||
415 | def remaining_time(self): | ||
416 | "estimated time remaining" | ||
417 | if not self.ave_rate or not self.total: return None | ||
418 | return (self.total - self.last_amount_read) / self.ave_rate | ||
419 | |||
420 | def fraction_read(self): | ||
421 | """the fraction of the data that has been read | ||
422 | (can be None for unknown transfer size)""" | ||
423 | if self.total is None: return None | ||
424 | elif self.total == 0: return 1.0 | ||
425 | else: return float(self.last_amount_read)/self.total | ||
426 | |||
427 | ######################################################################### | ||
428 | # support methods | ||
429 | def _temporal_rolling_ave(self, time_diff, read_diff, last_ave, timescale): | ||
430 | """a temporal rolling average performs smooth averaging even when | ||
431 | updates come at irregular intervals. This is performed by scaling | ||
432 | the "epsilon" according to the time since the last update. | ||
433 | Specifically, epsilon = time_diff / timescale | ||
434 | |||
435 | As a general rule, the average will take on a completely new value | ||
436 | after 'timescale' seconds.""" | ||
437 | epsilon = time_diff / timescale | ||
438 | if epsilon > 1: epsilon = 1.0 | ||
439 | return self._rolling_ave(time_diff, read_diff, last_ave, epsilon) | ||
440 | |||
441 | def _rolling_ave(self, time_diff, read_diff, last_ave, epsilon): | ||
442 | """perform a "rolling average" iteration | ||
443 | a rolling average "folds" new data into an existing average with | ||
444 | some weight, epsilon. epsilon must be between 0.0 and 1.0 (inclusive) | ||
445 | a value of 0.0 means only the old value (initial value) counts, | ||
446 | and a value of 1.0 means only the newest value is considered.""" | ||
447 | |||
448 | try: | ||
449 | recent_rate = read_diff / time_diff | ||
450 | except ZeroDivisionError: | ||
451 | recent_rate = None | ||
452 | if last_ave is None: return recent_rate | ||
453 | elif recent_rate is None: return last_ave | ||
454 | |||
455 | # at this point, both last_ave and recent_rate are numbers | ||
456 | return epsilon * recent_rate + (1 - epsilon) * last_ave | ||
457 | |||
458 | def _round_remaining_time(self, rt, start_time=15.0): | ||
459 | """round the remaining time, depending on its size | ||
460 | If rt is between n*start_time and (n+1)*start_time round downward | ||
461 | to the nearest multiple of n (for any counting number n). | ||
462 | If rt < start_time, round down to the nearest 1. | ||
463 | For example (for start_time = 15.0): | ||
464 | 2.7 -> 2.0 | ||
465 | 25.2 -> 25.0 | ||
466 | 26.4 -> 26.0 | ||
467 | 35.3 -> 34.0 | ||
468 | 63.6 -> 60.0 | ||
469 | """ | ||
470 | |||
471 | if rt < 0: return 0.0 | ||
472 | shift = int(math.log(rt/start_time)/math.log(2)) | ||
473 | rt = int(rt) | ||
474 | if shift <= 0: return rt | ||
475 | return float(int(rt) >> shift << shift) | ||
476 | |||
477 | |||
478 | def format_time(seconds, use_hours=0): | ||
479 | if seconds is None or seconds < 0: | ||
480 | if use_hours: return '--:--:--' | ||
481 | else: return '--:--' | ||
482 | else: | ||
483 | seconds = int(seconds) | ||
484 | minutes = seconds / 60 | ||
485 | seconds = seconds % 60 | ||
486 | if use_hours: | ||
487 | hours = minutes / 60 | ||
488 | minutes = minutes % 60 | ||
489 | return '%02i:%02i:%02i' % (hours, minutes, seconds) | ||
490 | else: | ||
491 | return '%02i:%02i' % (minutes, seconds) | ||
492 | |||
493 | def format_number(number, SI=0, space=' '): | ||
494 | """Turn numbers into human-readable metric-like numbers""" | ||
495 | symbols = ['', # (none) | ||
496 | 'k', # kilo | ||
497 | 'M', # mega | ||
498 | 'G', # giga | ||
499 | 'T', # tera | ||
500 | 'P', # peta | ||
501 | 'E', # exa | ||
502 | 'Z', # zetta | ||
503 | 'Y'] # yotta | ||
504 | |||
505 | if SI: step = 1000.0 | ||
506 | else: step = 1024.0 | ||
507 | |||
508 | thresh = 999 | ||
509 | depth = 0 | ||
510 | max_depth = len(symbols) - 1 | ||
511 | |||
512 | # we want numbers between 0 and thresh, but don't exceed the length | ||
513 | # of our list. In that event, the formatting will be screwed up, | ||
514 | # but it'll still show the right number. | ||
515 | while number > thresh and depth < max_depth: | ||
516 | depth = depth + 1 | ||
517 | number = number / step | ||
518 | |||
519 | if type(number) == type(1) or type(number) == type(1L): | ||
520 | # it's an int or a long, which means it didn't get divided, | ||
521 | # which means it's already short enough | ||
522 | format = '%i%s%s' | ||
523 | elif number < 9.95: | ||
524 | # must use 9.95 for proper sizing. For example, 9.99 will be | ||
525 | # rounded to 10.0 with the .1f format string (which is too long) | ||
526 | format = '%.1f%s%s' | ||
527 | else: | ||
528 | format = '%.0f%s%s' | ||
529 | |||
530 | return(format % (float(number or 0), space, symbols[depth])) | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py new file mode 100644 index 0000000000..07848dac7c --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/urlgrabber/sslfactory.py | |||
@@ -0,0 +1,90 @@ | |||
1 | # This library is free software; you can redistribute it and/or | ||
2 | # modify it under the terms of the GNU Lesser General Public | ||
3 | # License as published by the Free Software Foundation; either | ||
4 | # version 2.1 of the License, or (at your option) any later version. | ||
5 | # | ||
6 | # This library is distributed in the hope that it will be useful, | ||
7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
9 | # Lesser General Public License for more details. | ||
10 | # | ||
11 | # You should have received a copy of the GNU Lesser General Public | ||
12 | # License along with this library; if not, write to the | ||
13 | # Free Software Foundation, Inc., | ||
14 | # 59 Temple Place, Suite 330, | ||
15 | # Boston, MA 02111-1307 USA | ||
16 | |||
17 | # This file is part of urlgrabber, a high-level cross-protocol url-grabber | ||
18 | |||
19 | import httplib | ||
20 | import urllib2 | ||
21 | |||
22 | try: | ||
23 | from M2Crypto import SSL | ||
24 | from M2Crypto import httpslib | ||
25 | from M2Crypto import m2urllib2 | ||
26 | |||
27 | SSL.Connection.clientPostConnectionCheck = None | ||
28 | have_m2crypto = True | ||
29 | except ImportError: | ||
30 | have_m2crypto = False | ||
31 | |||
32 | DEBUG = None | ||
33 | |||
34 | if have_m2crypto: | ||
35 | |||
36 | class M2SSLFactory: | ||
37 | |||
38 | def __init__(self, ssl_ca_cert, ssl_context): | ||
39 | self.ssl_context = self._get_ssl_context(ssl_ca_cert, ssl_context) | ||
40 | |||
41 | def _get_ssl_context(self, ssl_ca_cert, ssl_context): | ||
42 | """ | ||
43 | Create an ssl context using the CA cert file or ssl context. | ||
44 | |||
45 | The CA cert is used first if it was passed as an option. If not, | ||
46 | then the supplied ssl context is used. If no ssl context was supplied, | ||
47 | None is returned. | ||
48 | """ | ||
49 | if ssl_ca_cert: | ||
50 | context = SSL.Context() | ||
51 | context.load_verify_locations(ssl_ca_cert) | ||
52 | context.set_verify(SSL.verify_none, -1) | ||
53 | return context | ||
54 | else: | ||
55 | return ssl_context | ||
56 | |||
57 | def create_https_connection(self, host, response_class = None): | ||
58 | connection = httplib.HTTPSConnection(host, self.ssl_context) | ||
59 | if response_class: | ||
60 | connection.response_class = response_class | ||
61 | return connection | ||
62 | |||
63 | def create_opener(self, *handlers): | ||
64 | return m2urllib2.build_opener(self.ssl_context, *handlers) | ||
65 | |||
66 | |||
67 | class SSLFactory: | ||
68 | |||
69 | def create_https_connection(self, host, response_class = None): | ||
70 | connection = httplib.HTTPSConnection(host) | ||
71 | if response_class: | ||
72 | connection.response_class = response_class | ||
73 | return connection | ||
74 | |||
75 | def create_opener(self, *handlers): | ||
76 | return urllib2.build_opener(*handlers) | ||
77 | |||
78 | |||
79 | |||
80 | def get_factory(ssl_ca_cert = None, ssl_context = None): | ||
81 | """ Return an SSLFactory, based on if M2Crypto is available. """ | ||
82 | if have_m2crypto: | ||
83 | return M2SSLFactory(ssl_ca_cert, ssl_context) | ||
84 | else: | ||
85 | # Log here if someone provides the args but we don't use them. | ||
86 | if ssl_ca_cert or ssl_context: | ||
87 | if DEBUG: | ||
88 | DEBUG.warning("SSL arguments supplied, but M2Crypto is not available. " | ||
89 | "Using Python SSL.") | ||
90 | return SSLFactory() | ||
diff --git a/scripts/lib/mic/3rdparty/pykickstart/version.py b/scripts/lib/mic/3rdparty/pykickstart/version.py new file mode 100644 index 0000000000..102cc37d80 --- /dev/null +++ b/scripts/lib/mic/3rdparty/pykickstart/version.py | |||
@@ -0,0 +1,197 @@ | |||
1 | # | ||
2 | # Chris Lumens <clumens@redhat.com> | ||
3 | # | ||
4 | # Copyright 2006, 2007, 2008, 2009, 2010 Red Hat, Inc. | ||
5 | # | ||
6 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
7 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
8 | # General Public License v.2. This program is distributed in the hope that it | ||
9 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
10 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | # See the GNU General Public License for more details. | ||
12 | # | ||
13 | # You should have received a copy of the GNU General Public License along with | ||
14 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
15 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Any Red Hat | ||
16 | # trademarks that are incorporated in the source code or documentation are not | ||
17 | # subject to the GNU General Public License and may only be used or replicated | ||
18 | # with the express permission of Red Hat, Inc. | ||
19 | # | ||
20 | """ | ||
21 | Methods for working with kickstart versions. | ||
22 | |||
23 | This module defines several symbolic constants that specify kickstart syntax | ||
24 | versions. Each version corresponds roughly to one release of Red Hat Linux, | ||
25 | Red Hat Enterprise Linux, or Fedora Core as these are where most syntax | ||
26 | changes take place. | ||
27 | |||
28 | This module also exports several functions: | ||
29 | |||
30 | makeVersion - Given a version number, return an instance of the | ||
31 | matching handler class. | ||
32 | |||
33 | returnClassForVersion - Given a version number, return the matching | ||
34 | handler class. This does not return an | ||
35 | instance of that class, however. | ||
36 | |||
37 | stringToVersion - Convert a string representation of a version number | ||
38 | into the symbolic constant. | ||
39 | |||
40 | versionToString - Perform the reverse mapping. | ||
41 | |||
42 | versionFromFile - Read a kickstart file and determine the version of | ||
43 | syntax it uses. This requires the kickstart file to | ||
44 | have a version= comment in it. | ||
45 | """ | ||
46 | import imputil, re, sys | ||
47 | from urlgrabber import urlopen | ||
48 | |||
49 | import gettext | ||
50 | _ = lambda x: gettext.ldgettext("pykickstart", x) | ||
51 | |||
52 | from pykickstart.errors import KickstartVersionError | ||
53 | |||
54 | # Symbolic names for internal version numbers. | ||
55 | RHEL3 = 900 | ||
56 | FC3 = 1000 | ||
57 | RHEL4 = 1100 | ||
58 | FC4 = 2000 | ||
59 | FC5 = 3000 | ||
60 | FC6 = 4000 | ||
61 | RHEL5 = 4100 | ||
62 | F7 = 5000 | ||
63 | F8 = 6000 | ||
64 | F9 = 7000 | ||
65 | F10 = 8000 | ||
66 | F11 = 9000 | ||
67 | F12 = 10000 | ||
68 | F13 = 11000 | ||
69 | RHEL6 = 11100 | ||
70 | F14 = 12000 | ||
71 | F15 = 13000 | ||
72 | F16 = 14000 | ||
73 | |||
74 | # This always points at the latest version and is the default. | ||
75 | DEVEL = F16 | ||
76 | |||
77 | # A one-to-one mapping from string representations to version numbers. | ||
78 | versionMap = { | ||
79 | "DEVEL": DEVEL, | ||
80 | "FC3": FC3, "FC4": FC4, "FC5": FC5, "FC6": FC6, "F7": F7, "F8": F8, | ||
81 | "F9": F9, "F10": F10, "F11": F11, "F12": F12, "F13": F13, | ||
82 | "F14": F14, "F15": F15, "F16": F16, | ||
83 | "RHEL3": RHEL3, "RHEL4": RHEL4, "RHEL5": RHEL5, "RHEL6": RHEL6 | ||
84 | } | ||
85 | |||
86 | def stringToVersion(s): | ||
87 | """Convert string into one of the provided version constants. Raises | ||
88 | KickstartVersionError if string does not match anything. | ||
89 | """ | ||
90 | # First try these short forms. | ||
91 | try: | ||
92 | return versionMap[s.upper()] | ||
93 | except KeyError: | ||
94 | pass | ||
95 | |||
96 | # Now try the Fedora versions. | ||
97 | m = re.match("^fedora.* (\d+)$", s, re.I) | ||
98 | |||
99 | if m and m.group(1): | ||
100 | if versionMap.has_key("FC" + m.group(1)): | ||
101 | return versionMap["FC" + m.group(1)] | ||
102 | elif versionMap.has_key("F" + m.group(1)): | ||
103 | return versionMap["F" + m.group(1)] | ||
104 | else: | ||
105 | raise KickstartVersionError(_("Unsupported version specified: %s") % s) | ||
106 | |||
107 | # Now try the RHEL versions. | ||
108 | m = re.match("^red hat enterprise linux.* (\d+)([\.\d]*)$", s, re.I) | ||
109 | |||
110 | if m and m.group(1): | ||
111 | if versionMap.has_key("RHEL" + m.group(1)): | ||
112 | return versionMap["RHEL" + m.group(1)] | ||
113 | else: | ||
114 | raise KickstartVersionError(_("Unsupported version specified: %s") % s) | ||
115 | |||
116 | # If nothing else worked, we're out of options. | ||
117 | raise KickstartVersionError(_("Unsupported version specified: %s") % s) | ||
118 | |||
119 | def versionToString(version, skipDevel=False): | ||
120 | """Convert version into a string representation of the version number. | ||
121 | This is the reverse operation of stringToVersion. Raises | ||
122 | KickstartVersionError if version does not match anything. | ||
123 | """ | ||
124 | if not skipDevel and version == versionMap["DEVEL"]: | ||
125 | return "DEVEL" | ||
126 | |||
127 | for (key, val) in versionMap.iteritems(): | ||
128 | if key == "DEVEL": | ||
129 | continue | ||
130 | elif val == version: | ||
131 | return key | ||
132 | |||
133 | raise KickstartVersionError(_("Unsupported version specified: %s") % version) | ||
134 | |||
135 | def versionFromFile(f): | ||
136 | """Given a file or URL, look for a line starting with #version= and | ||
137 | return the version number. If no version is found, return DEVEL. | ||
138 | """ | ||
139 | v = DEVEL | ||
140 | |||
141 | fh = urlopen(f) | ||
142 | |||
143 | while True: | ||
144 | try: | ||
145 | l = fh.readline() | ||
146 | except StopIteration: | ||
147 | break | ||
148 | |||
149 | # At the end of the file? | ||
150 | if l == "": | ||
151 | break | ||
152 | |||
153 | if l.isspace() or l.strip() == "": | ||
154 | continue | ||
155 | |||
156 | if l[:9] == "#version=": | ||
157 | v = stringToVersion(l[9:].rstrip()) | ||
158 | break | ||
159 | |||
160 | fh.close() | ||
161 | return v | ||
162 | |||
163 | def returnClassForVersion(version=DEVEL): | ||
164 | """Return the class of the syntax handler for version. version can be | ||
165 | either a string or the matching constant. Raises KickstartValueError | ||
166 | if version does not match anything. | ||
167 | """ | ||
168 | try: | ||
169 | version = int(version) | ||
170 | module = "%s" % versionToString(version, skipDevel=True) | ||
171 | except ValueError: | ||
172 | module = "%s" % version | ||
173 | version = stringToVersion(version) | ||
174 | |||
175 | module = module.lower() | ||
176 | |||
177 | try: | ||
178 | import pykickstart.handlers | ||
179 | sys.path.extend(pykickstart.handlers.__path__) | ||
180 | found = imputil.imp.find_module(module) | ||
181 | loaded = imputil.imp.load_module(module, found[0], found[1], found[2]) | ||
182 | |||
183 | for (k, v) in loaded.__dict__.iteritems(): | ||
184 | if k.lower().endswith("%shandler" % module): | ||
185 | return v | ||
186 | except: | ||
187 | raise KickstartVersionError(_("Unsupported version specified: %s") % version) | ||
188 | |||
189 | def makeVersion(version=DEVEL): | ||
190 | """Return a new instance of the syntax handler for version. version can be | ||
191 | either a string or the matching constant. This function is useful for | ||
192 | standalone programs which just need to handle a specific version of | ||
193 | kickstart syntax (as provided by a command line argument, for example) | ||
194 | and need to instantiate the correct object. | ||
195 | """ | ||
196 | cl = returnClassForVersion(version) | ||
197 | return cl() | ||
diff --git a/scripts/lib/mic/__init__.py b/scripts/lib/mic/__init__.py new file mode 100644 index 0000000000..63c1d9c846 --- /dev/null +++ b/scripts/lib/mic/__init__.py | |||
@@ -0,0 +1,4 @@ | |||
1 | import os, sys | ||
2 | |||
3 | cur_path = os.path.dirname(__file__) or '.' | ||
4 | sys.path.insert(0, cur_path + '/3rdparty') | ||
diff --git a/scripts/lib/mic/__version__.py b/scripts/lib/mic/__version__.py new file mode 100644 index 0000000000..60d7626cac --- /dev/null +++ b/scripts/lib/mic/__version__.py | |||
@@ -0,0 +1 @@ | |||
VERSION = "0.14" | |||
diff --git a/scripts/lib/mic/bootstrap.py b/scripts/lib/mic/bootstrap.py new file mode 100644 index 0000000000..66c291b0a8 --- /dev/null +++ b/scripts/lib/mic/bootstrap.py | |||
@@ -0,0 +1,279 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2009, 2010, 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | from __future__ import with_statement | ||
19 | import os | ||
20 | import sys | ||
21 | import tempfile | ||
22 | import shutil | ||
23 | import subprocess | ||
24 | import rpm | ||
25 | from mic import msger | ||
26 | from mic.utils import errors, proxy, misc | ||
27 | from mic.utils.rpmmisc import readRpmHeader, RPMInstallCallback | ||
28 | from mic.chroot import cleanup_mounts, setup_chrootenv, cleanup_chrootenv | ||
29 | |||
30 | PATH_BOOTSTRAP = "/usr/sbin:/usr/bin:/sbin:/bin" | ||
31 | |||
32 | RPMTRANS_FLAGS = [ | ||
33 | rpm.RPMTRANS_FLAG_ALLFILES, | ||
34 | rpm.RPMTRANS_FLAG_NOSCRIPTS, | ||
35 | rpm.RPMTRANS_FLAG_NOTRIGGERS, | ||
36 | ] | ||
37 | |||
38 | RPMVSF_FLAGS = [ | ||
39 | rpm._RPMVSF_NOSIGNATURES, | ||
40 | rpm._RPMVSF_NODIGESTS | ||
41 | ] | ||
42 | |||
43 | RPMPROB_FLAGS = [ | ||
44 | rpm.RPMPROB_FILTER_OLDPACKAGE, | ||
45 | rpm.RPMPROB_FILTER_REPLACEPKG, | ||
46 | rpm.RPMPROB_FILTER_IGNOREARCH | ||
47 | ] | ||
48 | |||
49 | class MiniBackend(object): | ||
50 | def __init__(self, rootdir, arch=None, repomd=None): | ||
51 | self._ts = None | ||
52 | self.rootdir = os.path.abspath(rootdir) | ||
53 | self.arch = arch | ||
54 | self.repomd = repomd | ||
55 | self.dlpkgs = [] | ||
56 | self.localpkgs = {} | ||
57 | self.optionals = [] | ||
58 | self.preins = {} | ||
59 | self.postins = {} | ||
60 | self.scriptlets = False | ||
61 | |||
62 | def __del__(self): | ||
63 | try: | ||
64 | del self.ts | ||
65 | except: | ||
66 | pass | ||
67 | |||
68 | def get_ts(self): | ||
69 | if not self._ts: | ||
70 | self._ts = rpm.TransactionSet(self.rootdir) | ||
71 | self._ts.setFlags(reduce(lambda x, y: x|y, RPMTRANS_FLAGS)) | ||
72 | self._ts.setVSFlags(reduce(lambda x, y: x|y, RPMVSF_FLAGS)) | ||
73 | self._ts.setProbFilter(reduce(lambda x, y: x|y, RPMPROB_FLAGS)) | ||
74 | |||
75 | return self._ts | ||
76 | |||
77 | def del_ts(self): | ||
78 | if self._ts: | ||
79 | self._ts.closeDB() | ||
80 | self._ts = None | ||
81 | |||
82 | ts = property(fget = lambda self: self.get_ts(), | ||
83 | fdel = lambda self: self.del_ts(), | ||
84 | doc="TransactionSet object") | ||
85 | |||
86 | def selectPackage(self, pkg): | ||
87 | if not pkg in self.dlpkgs: | ||
88 | self.dlpkgs.append(pkg) | ||
89 | |||
90 | def runInstall(self): | ||
91 | # FIXME: check space | ||
92 | self.downloadPkgs() | ||
93 | self.installPkgs() | ||
94 | |||
95 | if not self.scriptlets: | ||
96 | return | ||
97 | |||
98 | for pkg in self.preins.keys(): | ||
99 | prog, script = self.preins[pkg] | ||
100 | self.run_pkg_script(pkg, prog, script, '0') | ||
101 | for pkg in self.postins.keys(): | ||
102 | prog, script = self.postins[pkg] | ||
103 | self.run_pkg_script(pkg, prog, script, '1') | ||
104 | |||
105 | def downloadPkgs(self): | ||
106 | nonexist = [] | ||
107 | for pkg in self.dlpkgs: | ||
108 | localpth = misc.get_package(pkg, self.repomd, self.arch) | ||
109 | if localpth: | ||
110 | self.localpkgs[pkg] = localpth | ||
111 | elif pkg in self.optionals: | ||
112 | # skip optional rpm | ||
113 | continue | ||
114 | else: | ||
115 | # mark nonexist rpm | ||
116 | nonexist.append(pkg) | ||
117 | |||
118 | if nonexist: | ||
119 | raise errors.BootstrapError("Can't get rpm binary: %s" % | ||
120 | ','.join(nonexist)) | ||
121 | |||
122 | def installPkgs(self): | ||
123 | for pkg in self.localpkgs.keys(): | ||
124 | rpmpath = self.localpkgs[pkg] | ||
125 | |||
126 | hdr = readRpmHeader(self.ts, rpmpath) | ||
127 | |||
128 | # save prein and postin scripts | ||
129 | self.preins[pkg] = (hdr['PREINPROG'], hdr['PREIN']) | ||
130 | self.postins[pkg] = (hdr['POSTINPROG'], hdr['POSTIN']) | ||
131 | |||
132 | # mark pkg as install | ||
133 | self.ts.addInstall(hdr, rpmpath, 'u') | ||
134 | |||
135 | # run transaction | ||
136 | self.ts.order() | ||
137 | cb = RPMInstallCallback(self.ts) | ||
138 | self.ts.run(cb.callback, '') | ||
139 | |||
140 | def run_pkg_script(self, pkg, prog, script, arg): | ||
141 | mychroot = lambda: os.chroot(self.rootdir) | ||
142 | |||
143 | if not script: | ||
144 | return | ||
145 | |||
146 | if prog == "<lua>": | ||
147 | prog = "/usr/bin/lua" | ||
148 | |||
149 | tmpdir = os.path.join(self.rootdir, "tmp") | ||
150 | if not os.path.exists(tmpdir): | ||
151 | os.makedirs(tmpdir) | ||
152 | tmpfd, tmpfp = tempfile.mkstemp(dir=tmpdir, prefix="%s.pre-" % pkg) | ||
153 | script = script.replace('\r', '') | ||
154 | os.write(tmpfd, script) | ||
155 | os.close(tmpfd) | ||
156 | os.chmod(tmpfp, 0700) | ||
157 | |||
158 | try: | ||
159 | script_fp = os.path.join('/tmp', os.path.basename(tmpfp)) | ||
160 | subprocess.call([prog, script_fp, arg], preexec_fn=mychroot) | ||
161 | except (OSError, IOError), err: | ||
162 | msger.warning(str(err)) | ||
163 | finally: | ||
164 | os.unlink(tmpfp) | ||
165 | |||
166 | class Bootstrap(object): | ||
167 | def __init__(self, rootdir, distro, arch=None): | ||
168 | self.rootdir = misc.mkdtemp(dir=rootdir, prefix=distro) | ||
169 | self.distro = distro | ||
170 | self.arch = arch | ||
171 | self.logfile = None | ||
172 | self.pkgslist = [] | ||
173 | self.repomd = None | ||
174 | |||
175 | def __del__(self): | ||
176 | self.cleanup() | ||
177 | |||
178 | def get_rootdir(self): | ||
179 | if os.path.exists(self.rootdir): | ||
180 | shutil.rmtree(self.rootdir, ignore_errors=True) | ||
181 | os.makedirs(self.rootdir) | ||
182 | return self.rootdir | ||
183 | |||
184 | def dirsetup(self, rootdir=None): | ||
185 | _path = lambda pth: os.path.join(rootdir, pth.lstrip('/')) | ||
186 | |||
187 | if not rootdir: | ||
188 | rootdir = self.rootdir | ||
189 | |||
190 | try: | ||
191 | # make /tmp and /etc path | ||
192 | tmpdir = _path('/tmp') | ||
193 | if not os.path.exists(tmpdir): | ||
194 | os.makedirs(tmpdir) | ||
195 | etcdir = _path('/etc') | ||
196 | if not os.path.exists(etcdir): | ||
197 | os.makedirs(etcdir) | ||
198 | |||
199 | # touch distro file | ||
200 | tzdist = _path('/etc/%s-release' % self.distro) | ||
201 | if not os.path.exists(tzdist): | ||
202 | with open(tzdist, 'w') as wf: | ||
203 | wf.write("bootstrap") | ||
204 | except: | ||
205 | pass | ||
206 | |||
207 | def create(self, repomd, pkglist, optlist=()): | ||
208 | try: | ||
209 | pkgmgr = MiniBackend(self.get_rootdir()) | ||
210 | pkgmgr.arch = self.arch | ||
211 | pkgmgr.repomd = repomd | ||
212 | pkgmgr.optionals = list(optlist) | ||
213 | map(pkgmgr.selectPackage, pkglist + list(optlist)) | ||
214 | pkgmgr.runInstall() | ||
215 | except (OSError, IOError, errors.CreatorError), err: | ||
216 | raise errors.BootstrapError("%s" % err) | ||
217 | |||
218 | def run(self, cmd, chdir, rootdir=None, bindmounts=None): | ||
219 | def mychroot(): | ||
220 | os.chroot(rootdir) | ||
221 | os.chdir(chdir) | ||
222 | |||
223 | def sync_timesetting(rootdir): | ||
224 | try: | ||
225 | # sync time and zone info to bootstrap | ||
226 | if os.path.exists(rootdir + "/etc/localtime"): | ||
227 | os.unlink(rootdir + "/etc/localtime") | ||
228 | shutil.copyfile("/etc/localtime", rootdir + "/etc/localtime") | ||
229 | except: | ||
230 | pass | ||
231 | |||
232 | def sync_passwdfile(rootdir): | ||
233 | try: | ||
234 | # sync passwd file to bootstrap, saving the user info | ||
235 | if os.path.exists(rootdir + "/etc/passwd"): | ||
236 | os.unlink(rootdir + "/etc/passwd") | ||
237 | shutil.copyfile("/etc/passwd", rootdir + "/etc/passwd") | ||
238 | except: | ||
239 | pass | ||
240 | |||
241 | if not rootdir: | ||
242 | rootdir = self.rootdir | ||
243 | |||
244 | if isinstance(cmd, list): | ||
245 | shell = False | ||
246 | else: | ||
247 | shell = True | ||
248 | |||
249 | env = os.environ | ||
250 | env['PATH'] = "%s:%s" % (PATH_BOOTSTRAP, env['PATH']) | ||
251 | |||
252 | retcode = 0 | ||
253 | gloablmounts = None | ||
254 | try: | ||
255 | proxy.set_proxy_environ() | ||
256 | gloablmounts = setup_chrootenv(rootdir, bindmounts, False) | ||
257 | sync_timesetting(rootdir) | ||
258 | sync_passwdfile(rootdir) | ||
259 | retcode = subprocess.call(cmd, preexec_fn=mychroot, env=env, shell=shell) | ||
260 | except (OSError, IOError): | ||
261 | # add additional information to original exception | ||
262 | value, tb = sys.exc_info()[1:] | ||
263 | value = '%s: %s' % (value, ' '.join(cmd)) | ||
264 | raise RuntimeError, value, tb | ||
265 | finally: | ||
266 | if self.logfile and os.path.isfile(self.logfile): | ||
267 | msger.log(file(self.logfile).read()) | ||
268 | cleanup_chrootenv(rootdir, bindmounts, gloablmounts) | ||
269 | proxy.unset_proxy_environ() | ||
270 | return retcode | ||
271 | |||
272 | def cleanup(self): | ||
273 | try: | ||
274 | # clean mounts | ||
275 | cleanup_mounts(self.rootdir) | ||
276 | # remove rootdir | ||
277 | shutil.rmtree(self.rootdir, ignore_errors=True) | ||
278 | except: | ||
279 | pass | ||
diff --git a/scripts/lib/mic/chroot.py b/scripts/lib/mic/chroot.py new file mode 100644 index 0000000000..99fb9a2c17 --- /dev/null +++ b/scripts/lib/mic/chroot.py | |||
@@ -0,0 +1,343 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2009, 2010, 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | from __future__ import with_statement | ||
19 | import os | ||
20 | import shutil | ||
21 | import subprocess | ||
22 | |||
23 | from mic import msger | ||
24 | from mic.conf import configmgr | ||
25 | from mic.utils import misc, errors, runner, fs_related | ||
26 | |||
27 | chroot_lockfd = -1 | ||
28 | chroot_lock = "" | ||
29 | BIND_MOUNTS = ( | ||
30 | "/proc", | ||
31 | "/proc/sys/fs/binfmt_misc", | ||
32 | "/sys", | ||
33 | "/dev", | ||
34 | "/dev/pts", | ||
35 | "/dev/shm", | ||
36 | "/var/lib/dbus", | ||
37 | "/var/run/dbus", | ||
38 | "/var/lock", | ||
39 | ) | ||
40 | |||
41 | def cleanup_after_chroot(targettype,imgmount,tmpdir,tmpmnt): | ||
42 | if imgmount and targettype == "img": | ||
43 | imgmount.cleanup() | ||
44 | |||
45 | if tmpdir: | ||
46 | shutil.rmtree(tmpdir, ignore_errors = True) | ||
47 | |||
48 | if tmpmnt: | ||
49 | shutil.rmtree(tmpmnt, ignore_errors = True) | ||
50 | |||
51 | def check_bind_mounts(chrootdir, bindmounts): | ||
52 | chrootmounts = [] | ||
53 | for mount in bindmounts.split(";"): | ||
54 | if not mount: | ||
55 | continue | ||
56 | |||
57 | srcdst = mount.split(":") | ||
58 | if len(srcdst) == 1: | ||
59 | srcdst.append("none") | ||
60 | |||
61 | if not os.path.isdir(srcdst[0]): | ||
62 | return False | ||
63 | |||
64 | if srcdst[1] == "" or srcdst[1] == "none": | ||
65 | srcdst[1] = None | ||
66 | |||
67 | if srcdst[0] in BIND_MOUNTS or srcdst[0] == '/': | ||
68 | continue | ||
69 | |||
70 | if chrootdir: | ||
71 | if not srcdst[1]: | ||
72 | srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[0])) | ||
73 | else: | ||
74 | srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1])) | ||
75 | |||
76 | tmpdir = chrootdir + "/" + srcdst[1] | ||
77 | if os.path.isdir(tmpdir): | ||
78 | msger.warning("Warning: dir %s has existed." % tmpdir) | ||
79 | |||
80 | return True | ||
81 | |||
82 | def cleanup_mounts(chrootdir): | ||
83 | umountcmd = misc.find_binary_path("umount") | ||
84 | abs_chrootdir = os.path.abspath(chrootdir) | ||
85 | mounts = open('/proc/mounts').readlines() | ||
86 | for line in reversed(mounts): | ||
87 | if abs_chrootdir not in line: | ||
88 | continue | ||
89 | |||
90 | point = line.split()[1] | ||
91 | |||
92 | # '/' to avoid common name prefix | ||
93 | if abs_chrootdir == point or point.startswith(abs_chrootdir + '/'): | ||
94 | args = [ umountcmd, "-l", point ] | ||
95 | ret = runner.quiet(args) | ||
96 | if ret != 0: | ||
97 | msger.warning("failed to unmount %s" % point) | ||
98 | |||
99 | return 0 | ||
100 | |||
101 | def setup_chrootenv(chrootdir, bindmounts = None, mountparent = True): | ||
102 | global chroot_lockfd, chroot_lock | ||
103 | |||
104 | def get_bind_mounts(chrootdir, bindmounts, mountparent = True): | ||
105 | chrootmounts = [] | ||
106 | if bindmounts in ("", None): | ||
107 | bindmounts = "" | ||
108 | |||
109 | for mount in bindmounts.split(";"): | ||
110 | if not mount: | ||
111 | continue | ||
112 | |||
113 | srcdst = mount.split(":") | ||
114 | srcdst[0] = os.path.abspath(os.path.expanduser(srcdst[0])) | ||
115 | if len(srcdst) == 1: | ||
116 | srcdst.append("none") | ||
117 | |||
118 | # if some bindmount is not existed, but it's created inside | ||
119 | # chroot, this is not expected | ||
120 | if not os.path.exists(srcdst[0]): | ||
121 | os.makedirs(srcdst[0]) | ||
122 | |||
123 | if not os.path.isdir(srcdst[0]): | ||
124 | continue | ||
125 | |||
126 | if srcdst[0] in BIND_MOUNTS or srcdst[0] == '/': | ||
127 | msger.verbose("%s will be mounted by default." % srcdst[0]) | ||
128 | continue | ||
129 | |||
130 | if srcdst[1] == "" or srcdst[1] == "none": | ||
131 | srcdst[1] = None | ||
132 | else: | ||
133 | srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1])) | ||
134 | if os.path.isdir(chrootdir + "/" + srcdst[1]): | ||
135 | msger.warning("%s has existed in %s , skip it."\ | ||
136 | % (srcdst[1], chrootdir)) | ||
137 | continue | ||
138 | |||
139 | chrootmounts.append(fs_related.BindChrootMount(srcdst[0], | ||
140 | chrootdir, | ||
141 | srcdst[1])) | ||
142 | |||
143 | """Default bind mounts""" | ||
144 | for pt in BIND_MOUNTS: | ||
145 | if not os.path.exists(pt): | ||
146 | continue | ||
147 | chrootmounts.append(fs_related.BindChrootMount(pt, | ||
148 | chrootdir, | ||
149 | None)) | ||
150 | |||
151 | if mountparent: | ||
152 | chrootmounts.append(fs_related.BindChrootMount("/", | ||
153 | chrootdir, | ||
154 | "/parentroot", | ||
155 | "ro")) | ||
156 | |||
157 | for kernel in os.listdir("/lib/modules"): | ||
158 | chrootmounts.append(fs_related.BindChrootMount( | ||
159 | "/lib/modules/"+kernel, | ||
160 | chrootdir, | ||
161 | None, | ||
162 | "ro")) | ||
163 | |||
164 | return chrootmounts | ||
165 | |||
166 | def bind_mount(chrootmounts): | ||
167 | for b in chrootmounts: | ||
168 | msger.verbose("bind_mount: %s -> %s" % (b.src, b.dest)) | ||
169 | b.mount() | ||
170 | |||
171 | def setup_resolv(chrootdir): | ||
172 | try: | ||
173 | shutil.copyfile("/etc/resolv.conf", chrootdir + "/etc/resolv.conf") | ||
174 | except: | ||
175 | pass | ||
176 | |||
177 | globalmounts = get_bind_mounts(chrootdir, bindmounts, mountparent) | ||
178 | bind_mount(globalmounts) | ||
179 | |||
180 | setup_resolv(chrootdir) | ||
181 | |||
182 | mtab = "/etc/mtab" | ||
183 | dstmtab = chrootdir + mtab | ||
184 | if not os.path.islink(dstmtab): | ||
185 | shutil.copyfile(mtab, dstmtab) | ||
186 | |||
187 | chroot_lock = os.path.join(chrootdir, ".chroot.lock") | ||
188 | chroot_lockfd = open(chroot_lock, "w") | ||
189 | |||
190 | return globalmounts | ||
191 | |||
192 | def cleanup_chrootenv(chrootdir, bindmounts=None, globalmounts=()): | ||
193 | global chroot_lockfd, chroot_lock | ||
194 | |||
195 | def bind_unmount(chrootmounts): | ||
196 | for b in reversed(chrootmounts): | ||
197 | msger.verbose("bind_unmount: %s -> %s" % (b.src, b.dest)) | ||
198 | b.unmount() | ||
199 | |||
200 | def cleanup_resolv(chrootdir): | ||
201 | try: | ||
202 | fd = open(chrootdir + "/etc/resolv.conf", "w") | ||
203 | fd.truncate(0) | ||
204 | fd.close() | ||
205 | except: | ||
206 | pass | ||
207 | |||
208 | def kill_processes(chrootdir): | ||
209 | import glob | ||
210 | for fp in glob.glob("/proc/*/root"): | ||
211 | try: | ||
212 | if os.readlink(fp) == chrootdir: | ||
213 | pid = int(fp.split("/")[2]) | ||
214 | os.kill(pid, 9) | ||
215 | except: | ||
216 | pass | ||
217 | |||
218 | def cleanup_mountdir(chrootdir, bindmounts): | ||
219 | if bindmounts == "" or bindmounts == None: | ||
220 | return | ||
221 | chrootmounts = [] | ||
222 | for mount in bindmounts.split(";"): | ||
223 | if not mount: | ||
224 | continue | ||
225 | |||
226 | srcdst = mount.split(":") | ||
227 | |||
228 | if len(srcdst) == 1: | ||
229 | srcdst.append("none") | ||
230 | |||
231 | if srcdst[0] == "/": | ||
232 | continue | ||
233 | |||
234 | if srcdst[1] == "" or srcdst[1] == "none": | ||
235 | srcdst[1] = srcdst[0] | ||
236 | |||
237 | srcdst[1] = os.path.abspath(os.path.expanduser(srcdst[1])) | ||
238 | tmpdir = chrootdir + "/" + srcdst[1] | ||
239 | if os.path.isdir(tmpdir): | ||
240 | if len(os.listdir(tmpdir)) == 0: | ||
241 | shutil.rmtree(tmpdir, ignore_errors = True) | ||
242 | else: | ||
243 | msger.warning("Warning: dir %s isn't empty." % tmpdir) | ||
244 | |||
245 | chroot_lockfd.close() | ||
246 | bind_unmount(globalmounts) | ||
247 | |||
248 | if not fs_related.my_fuser(chroot_lock): | ||
249 | tmpdir = chrootdir + "/parentroot" | ||
250 | if os.path.exists(tmpdir) and len(os.listdir(tmpdir)) == 0: | ||
251 | shutil.rmtree(tmpdir, ignore_errors = True) | ||
252 | |||
253 | cleanup_resolv(chrootdir) | ||
254 | |||
255 | if os.path.exists(chrootdir + "/etc/mtab"): | ||
256 | os.unlink(chrootdir + "/etc/mtab") | ||
257 | |||
258 | kill_processes(chrootdir) | ||
259 | |||
260 | cleanup_mountdir(chrootdir, bindmounts) | ||
261 | |||
262 | def chroot(chrootdir, bindmounts = None, execute = "/bin/bash"): | ||
263 | def mychroot(): | ||
264 | os.chroot(chrootdir) | ||
265 | os.chdir("/") | ||
266 | |||
267 | if configmgr.chroot['saveto']: | ||
268 | savefs = True | ||
269 | saveto = configmgr.chroot['saveto'] | ||
270 | wrnmsg = "Can't save chroot fs for dir %s exists" % saveto | ||
271 | if saveto == chrootdir: | ||
272 | savefs = False | ||
273 | wrnmsg = "Dir %s is being used to chroot" % saveto | ||
274 | elif os.path.exists(saveto): | ||
275 | if msger.ask("Dir %s already exists, cleanup and continue?" % | ||
276 | saveto): | ||
277 | shutil.rmtree(saveto, ignore_errors = True) | ||
278 | savefs = True | ||
279 | else: | ||
280 | savefs = False | ||
281 | |||
282 | if savefs: | ||
283 | msger.info("Saving image to directory %s" % saveto) | ||
284 | fs_related.makedirs(os.path.dirname(os.path.abspath(saveto))) | ||
285 | runner.quiet("cp -af %s %s" % (chrootdir, saveto)) | ||
286 | devs = ['dev/fd', | ||
287 | 'dev/stdin', | ||
288 | 'dev/stdout', | ||
289 | 'dev/stderr', | ||
290 | 'etc/mtab'] | ||
291 | ignlst = [os.path.join(saveto, x) for x in devs] | ||
292 | map(os.unlink, filter(os.path.exists, ignlst)) | ||
293 | else: | ||
294 | msger.warning(wrnmsg) | ||
295 | |||
296 | dev_null = os.open("/dev/null", os.O_WRONLY) | ||
297 | files_to_check = ["/bin/bash", "/sbin/init"] | ||
298 | |||
299 | architecture_found = False | ||
300 | |||
301 | """ Register statically-linked qemu-arm if it is an ARM fs """ | ||
302 | qemu_emulator = None | ||
303 | |||
304 | for ftc in files_to_check: | ||
305 | ftc = "%s/%s" % (chrootdir,ftc) | ||
306 | |||
307 | # Return code of 'file' is "almost always" 0 based on some man pages | ||
308 | # so we need to check the file existance first. | ||
309 | if not os.path.exists(ftc): | ||
310 | continue | ||
311 | |||
312 | for line in runner.outs(['file', ftc]).splitlines(): | ||
313 | if 'ARM' in line: | ||
314 | qemu_emulator = misc.setup_qemu_emulator(chrootdir, "arm") | ||
315 | architecture_found = True | ||
316 | break | ||
317 | |||
318 | if 'Intel' in line: | ||
319 | architecture_found = True | ||
320 | break | ||
321 | |||
322 | if architecture_found: | ||
323 | break | ||
324 | |||
325 | os.close(dev_null) | ||
326 | if not architecture_found: | ||
327 | raise errors.CreatorError("Failed to get architecture from any of the " | ||
328 | "following files %s from chroot." \ | ||
329 | % files_to_check) | ||
330 | |||
331 | try: | ||
332 | msger.info("Launching shell. Exit to continue.\n" | ||
333 | "----------------------------------") | ||
334 | globalmounts = setup_chrootenv(chrootdir, bindmounts) | ||
335 | subprocess.call(execute, preexec_fn = mychroot, shell=True) | ||
336 | |||
337 | except OSError, err: | ||
338 | raise errors.CreatorError("chroot err: %s" % str(err)) | ||
339 | |||
340 | finally: | ||
341 | cleanup_chrootenv(chrootdir, bindmounts, globalmounts) | ||
342 | if qemu_emulator: | ||
343 | os.unlink(chrootdir + qemu_emulator) | ||
diff --git a/scripts/lib/mic/conf.py b/scripts/lib/mic/conf.py new file mode 100644 index 0000000000..b850d80520 --- /dev/null +++ b/scripts/lib/mic/conf.py | |||
@@ -0,0 +1,197 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os, sys, re | ||
19 | import ConfigParser | ||
20 | |||
21 | from mic import msger | ||
22 | from mic import kickstart | ||
23 | from mic.utils import misc, runner, proxy, errors | ||
24 | |||
25 | |||
26 | def get_siteconf(): | ||
27 | mic_path = os.path.dirname(__file__) | ||
28 | eos = mic_path.find('scripts') + len('scripts') | ||
29 | scripts_path = mic_path[:eos] | ||
30 | |||
31 | return scripts_path + "/lib/image/config/wic.conf" | ||
32 | |||
33 | class ConfigMgr(object): | ||
34 | prefer_backends = ["zypp", "yum"] | ||
35 | |||
36 | DEFAULTS = {'common': { | ||
37 | "distro_name": "Default Distribution", | ||
38 | "plugin_dir": "/usr/lib/wic/plugins", # TODO use prefix also? | ||
39 | }, | ||
40 | 'create': { | ||
41 | "tmpdir": '/var/tmp/wic', | ||
42 | "cachedir": '/var/tmp/wic/cache', | ||
43 | "outdir": './wic-output', | ||
44 | |||
45 | "arch": None, # None means auto-detect | ||
46 | "pkgmgr": "auto", | ||
47 | "name": "output", | ||
48 | "ksfile": None, | ||
49 | "ks": None, | ||
50 | "repomd": None, | ||
51 | "local_pkgs_path": None, | ||
52 | "release": None, | ||
53 | "logfile": None, | ||
54 | "record_pkgs": [], | ||
55 | "pack_to": None, | ||
56 | "name_prefix": None, | ||
57 | "name_suffix": None, | ||
58 | "proxy": None, | ||
59 | "no_proxy": None, | ||
60 | "copy_kernel": False, | ||
61 | "install_pkgs": None, | ||
62 | "repourl": {}, | ||
63 | "localrepos": [], # save localrepos | ||
64 | "runtime": "bootstrap", | ||
65 | }, | ||
66 | 'chroot': { | ||
67 | "saveto": None, | ||
68 | }, | ||
69 | 'convert': { | ||
70 | "shell": False, | ||
71 | }, | ||
72 | 'bootstrap': { | ||
73 | "rootdir": '/var/tmp/wic-bootstrap', | ||
74 | "packages": [], | ||
75 | }, | ||
76 | } | ||
77 | |||
78 | # make the manager class as singleton | ||
79 | _instance = None | ||
80 | def __new__(cls, *args, **kwargs): | ||
81 | if not cls._instance: | ||
82 | cls._instance = super(ConfigMgr, cls).__new__(cls, *args, **kwargs) | ||
83 | |||
84 | return cls._instance | ||
85 | |||
86 | def __init__(self, ksconf=None, siteconf=None): | ||
87 | # reset config options | ||
88 | self.reset() | ||
89 | |||
90 | if not siteconf: | ||
91 | siteconf = get_siteconf() | ||
92 | |||
93 | # initial options from siteconf | ||
94 | self._siteconf = siteconf | ||
95 | |||
96 | if ksconf: | ||
97 | self._ksconf = ksconf | ||
98 | |||
99 | def reset(self): | ||
100 | self.__ksconf = None | ||
101 | self.__siteconf = None | ||
102 | |||
103 | # initialize the values with defaults | ||
104 | for sec, vals in self.DEFAULTS.iteritems(): | ||
105 | setattr(self, sec, vals) | ||
106 | |||
107 | def __set_siteconf(self, siteconf): | ||
108 | try: | ||
109 | self.__siteconf = siteconf | ||
110 | self._parse_siteconf(siteconf) | ||
111 | except ConfigParser.Error, error: | ||
112 | raise errors.ConfigError("%s" % error) | ||
113 | def __get_siteconf(self): | ||
114 | return self.__siteconf | ||
115 | _siteconf = property(__get_siteconf, __set_siteconf) | ||
116 | |||
117 | def __set_ksconf(self, ksconf): | ||
118 | if not os.path.isfile(ksconf): | ||
119 | msger.error('Cannot find ks file: %s' % ksconf) | ||
120 | |||
121 | self.__ksconf = ksconf | ||
122 | self._parse_kickstart(ksconf) | ||
123 | def __get_ksconf(self): | ||
124 | return self.__ksconf | ||
125 | _ksconf = property(__get_ksconf, __set_ksconf) | ||
126 | |||
127 | def _parse_siteconf(self, siteconf): | ||
128 | if not siteconf: | ||
129 | return | ||
130 | |||
131 | if not os.path.exists(siteconf): | ||
132 | msger.warning("cannot read config file: %s" % siteconf) | ||
133 | return | ||
134 | |||
135 | parser = ConfigParser.SafeConfigParser() | ||
136 | parser.read(siteconf) | ||
137 | |||
138 | for section in parser.sections(): | ||
139 | if section in self.DEFAULTS: | ||
140 | getattr(self, section).update(dict(parser.items(section))) | ||
141 | |||
142 | # append common section items to other sections | ||
143 | for section in self.DEFAULTS.keys(): | ||
144 | if section != "common": | ||
145 | getattr(self, section).update(self.common) | ||
146 | |||
147 | # check and normalize the scheme of proxy url | ||
148 | if self.create['proxy']: | ||
149 | m = re.match('^(\w+)://.*', self.create['proxy']) | ||
150 | if m: | ||
151 | scheme = m.group(1) | ||
152 | if scheme not in ('http', 'https', 'ftp', 'socks'): | ||
153 | msger.error("%s: proxy scheme is incorrect" % siteconf) | ||
154 | else: | ||
155 | msger.warning("%s: proxy url w/o scheme, use http as default" | ||
156 | % siteconf) | ||
157 | self.create['proxy'] = "http://" + self.create['proxy'] | ||
158 | |||
159 | proxy.set_proxies(self.create['proxy'], self.create['no_proxy']) | ||
160 | |||
161 | # bootstrap option handling | ||
162 | self.set_runtime(self.create['runtime']) | ||
163 | if isinstance(self.bootstrap['packages'], basestring): | ||
164 | packages = self.bootstrap['packages'].replace('\n', ' ') | ||
165 | if packages.find(',') != -1: | ||
166 | packages = packages.split(',') | ||
167 | else: | ||
168 | packages = packages.split() | ||
169 | self.bootstrap['packages'] = packages | ||
170 | |||
171 | def _parse_kickstart(self, ksconf=None): | ||
172 | if not ksconf: | ||
173 | return | ||
174 | |||
175 | ksconf = misc.normalize_ksfile(ksconf, | ||
176 | self.create['release'], | ||
177 | self.create['arch']) | ||
178 | |||
179 | ks = kickstart.read_kickstart(ksconf) | ||
180 | |||
181 | self.create['ks'] = ks | ||
182 | self.create['name'] = os.path.splitext(os.path.basename(ksconf))[0] | ||
183 | |||
184 | self.create['name'] = misc.build_name(ksconf, | ||
185 | self.create['release'], | ||
186 | self.create['name_prefix'], | ||
187 | self.create['name_suffix']) | ||
188 | |||
189 | def set_runtime(self, runtime): | ||
190 | if runtime not in ("bootstrap", "native"): | ||
191 | msger.error("Invalid runtime mode: %s" % runtime) | ||
192 | |||
193 | if misc.get_distro()[0] in ("tizen", "Tizen"): | ||
194 | runtime = "native" | ||
195 | self.create['runtime'] = runtime | ||
196 | |||
197 | configmgr = ConfigMgr() | ||
diff --git a/scripts/lib/mic/creator.py b/scripts/lib/mic/creator.py new file mode 100644 index 0000000000..267928f877 --- /dev/null +++ b/scripts/lib/mic/creator.py | |||
@@ -0,0 +1,351 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os, sys, re | ||
19 | from optparse import SUPPRESS_HELP | ||
20 | |||
21 | from mic import msger | ||
22 | from mic.utils import cmdln, errors | ||
23 | from mic.conf import configmgr | ||
24 | from mic.plugin import pluginmgr | ||
25 | |||
26 | |||
27 | class Creator(cmdln.Cmdln): | ||
28 | """${name}: create an image | ||
29 | |||
30 | Usage: | ||
31 | ${name} SUBCOMMAND <ksfile> [OPTS] | ||
32 | |||
33 | ${command_list} | ||
34 | ${option_list} | ||
35 | """ | ||
36 | |||
37 | name = 'mic create(cr)' | ||
38 | |||
39 | def __init__(self, *args, **kwargs): | ||
40 | cmdln.Cmdln.__init__(self, *args, **kwargs) | ||
41 | self._subcmds = [] | ||
42 | |||
43 | # get cmds from pluginmgr | ||
44 | # mix-in do_subcmd interface | ||
45 | for subcmd, klass in pluginmgr.get_plugins('imager').iteritems(): | ||
46 | if not hasattr(klass, 'do_create'): | ||
47 | msger.warning("Unsurpport subcmd: %s" % subcmd) | ||
48 | continue | ||
49 | |||
50 | func = getattr(klass, 'do_create') | ||
51 | setattr(self.__class__, "do_"+subcmd, func) | ||
52 | self._subcmds.append(subcmd) | ||
53 | |||
54 | def get_optparser(self): | ||
55 | optparser = cmdln.CmdlnOptionParser(self) | ||
56 | optparser.add_option('-d', '--debug', action='store_true', | ||
57 | dest='debug', | ||
58 | help=SUPPRESS_HELP) | ||
59 | optparser.add_option('-v', '--verbose', action='store_true', | ||
60 | dest='verbose', | ||
61 | help=SUPPRESS_HELP) | ||
62 | optparser.add_option('', '--logfile', type='string', dest='logfile', | ||
63 | default=None, | ||
64 | help='Path of logfile') | ||
65 | optparser.add_option('-c', '--config', type='string', dest='config', | ||
66 | default=None, | ||
67 | help='Specify config file for mic') | ||
68 | optparser.add_option('-k', '--cachedir', type='string', action='store', | ||
69 | dest='cachedir', default=None, | ||
70 | help='Cache directory to store the downloaded') | ||
71 | optparser.add_option('-o', '--outdir', type='string', action='store', | ||
72 | dest='outdir', default=None, | ||
73 | help='Output directory') | ||
74 | optparser.add_option('-A', '--arch', type='string', dest='arch', | ||
75 | default=None, | ||
76 | help='Specify repo architecture') | ||
77 | optparser.add_option('', '--release', type='string', dest='release', | ||
78 | default=None, metavar='RID', | ||
79 | help='Generate a release of RID with all necessary' | ||
80 | ' files, when @BUILD_ID@ is contained in ' | ||
81 | 'kickstart file, it will be replaced by RID') | ||
82 | optparser.add_option("", "--record-pkgs", type="string", | ||
83 | dest="record_pkgs", default=None, | ||
84 | help='Record the info of installed packages, ' | ||
85 | 'multiple values can be specified which ' | ||
86 | 'joined by ",", valid values: "name", ' | ||
87 | '"content", "license", "vcs"') | ||
88 | optparser.add_option('', '--pkgmgr', type='string', dest='pkgmgr', | ||
89 | default=None, | ||
90 | help='Specify backend package manager') | ||
91 | optparser.add_option('', '--local-pkgs-path', type='string', | ||
92 | dest='local_pkgs_path', default=None, | ||
93 | help='Path for local pkgs(rpms) to be installed') | ||
94 | optparser.add_option('', '--runtime', type='string', | ||
95 | dest='runtime', default=None, | ||
96 | help='Specify runtime mode, avaiable: bootstrap, native') | ||
97 | # --taring-to is alias to --pack-to | ||
98 | optparser.add_option('', '--taring-to', type='string', | ||
99 | dest='pack_to', default=None, | ||
100 | help=SUPPRESS_HELP) | ||
101 | optparser.add_option('', '--pack-to', type='string', | ||
102 | dest='pack_to', default=None, | ||
103 | help='Pack the images together into the specified' | ||
104 | ' achive, extension supported: .zip, .tar, ' | ||
105 | '.tar.gz, .tar.bz2, etc. by default, .tar ' | ||
106 | 'will be used') | ||
107 | optparser.add_option('', '--copy-kernel', action='store_true', | ||
108 | dest='copy_kernel', | ||
109 | help='Copy kernel files from image /boot directory' | ||
110 | ' to the image output directory.') | ||
111 | optparser.add_option('', '--install-pkgs', type='string', action='store', | ||
112 | dest='install_pkgs', default=None, | ||
113 | help='Specify what type of packages to be installed,' | ||
114 | ' valid: source, debuginfo, debugsource') | ||
115 | optparser.add_option('', '--tmpfs', action='store_true', dest='enabletmpfs', | ||
116 | help='Setup tmpdir as tmpfs to accelerate, experimental' | ||
117 | ' feature, use it if you have more than 4G memory') | ||
118 | optparser.add_option('', '--repourl', action='append', | ||
119 | dest='repourl', default=[], | ||
120 | help=SUPPRESS_HELP) | ||
121 | return optparser | ||
122 | |||
123 | def preoptparse(self, argv): | ||
124 | optparser = self.get_optparser() | ||
125 | |||
126 | largs = [] | ||
127 | rargs = [] | ||
128 | while argv: | ||
129 | arg = argv.pop(0) | ||
130 | |||
131 | if arg in ('-h', '--help'): | ||
132 | rargs.append(arg) | ||
133 | |||
134 | elif optparser.has_option(arg): | ||
135 | largs.append(arg) | ||
136 | |||
137 | if optparser.get_option(arg).takes_value(): | ||
138 | try: | ||
139 | largs.append(argv.pop(0)) | ||
140 | except IndexError: | ||
141 | raise errors.Usage("option %s requires arguments" % arg) | ||
142 | |||
143 | else: | ||
144 | if arg.startswith("--"): | ||
145 | if "=" in arg: | ||
146 | opt = arg.split("=")[0] | ||
147 | else: | ||
148 | opt = None | ||
149 | elif arg.startswith("-") and len(arg) > 2: | ||
150 | opt = arg[0:2] | ||
151 | else: | ||
152 | opt = None | ||
153 | |||
154 | if opt and optparser.has_option(opt): | ||
155 | largs.append(arg) | ||
156 | else: | ||
157 | rargs.append(arg) | ||
158 | |||
159 | return largs + rargs | ||
160 | |||
161 | def postoptparse(self): | ||
162 | abspath = lambda pth: os.path.abspath(os.path.expanduser(pth)) | ||
163 | |||
164 | if self.options.verbose: | ||
165 | msger.set_loglevel('verbose') | ||
166 | if self.options.debug: | ||
167 | msger.set_loglevel('debug') | ||
168 | |||
169 | if self.options.logfile: | ||
170 | logfile_abs_path = abspath(self.options.logfile) | ||
171 | if os.path.isdir(logfile_abs_path): | ||
172 | raise errors.Usage("logfile's path %s should be file" | ||
173 | % self.options.logfile) | ||
174 | if not os.path.exists(os.path.dirname(logfile_abs_path)): | ||
175 | os.makedirs(os.path.dirname(logfile_abs_path)) | ||
176 | msger.set_interactive(False) | ||
177 | msger.set_logfile(logfile_abs_path) | ||
178 | configmgr.create['logfile'] = self.options.logfile | ||
179 | |||
180 | if self.options.config: | ||
181 | configmgr.reset() | ||
182 | configmgr._siteconf = self.options.config | ||
183 | |||
184 | if self.options.outdir is not None: | ||
185 | configmgr.create['outdir'] = abspath(self.options.outdir) | ||
186 | if self.options.cachedir is not None: | ||
187 | configmgr.create['cachedir'] = abspath(self.options.cachedir) | ||
188 | os.environ['ZYPP_LOCKFILE_ROOT'] = configmgr.create['cachedir'] | ||
189 | |||
190 | for cdir in ('outdir', 'cachedir'): | ||
191 | if os.path.exists(configmgr.create[cdir]) \ | ||
192 | and not os.path.isdir(configmgr.create[cdir]): | ||
193 | msger.error('Invalid directory specified: %s' \ | ||
194 | % configmgr.create[cdir]) | ||
195 | |||
196 | if self.options.local_pkgs_path is not None: | ||
197 | if not os.path.exists(self.options.local_pkgs_path): | ||
198 | msger.error('Local pkgs directory: \'%s\' not exist' \ | ||
199 | % self.options.local_pkgs_path) | ||
200 | configmgr.create['local_pkgs_path'] = self.options.local_pkgs_path | ||
201 | |||
202 | if self.options.release: | ||
203 | configmgr.create['release'] = self.options.release.rstrip('/') | ||
204 | |||
205 | if self.options.record_pkgs: | ||
206 | configmgr.create['record_pkgs'] = [] | ||
207 | for infotype in self.options.record_pkgs.split(','): | ||
208 | if infotype not in ('name', 'content', 'license', 'vcs'): | ||
209 | raise errors.Usage('Invalid pkg recording: %s, valid ones:' | ||
210 | ' "name", "content", "license", "vcs"' \ | ||
211 | % infotype) | ||
212 | |||
213 | configmgr.create['record_pkgs'].append(infotype) | ||
214 | |||
215 | if self.options.arch is not None: | ||
216 | supported_arch = sorted(rpmmisc.archPolicies.keys(), reverse=True) | ||
217 | if self.options.arch in supported_arch: | ||
218 | configmgr.create['arch'] = self.options.arch | ||
219 | else: | ||
220 | raise errors.Usage('Invalid architecture: "%s".\n' | ||
221 | ' Supported architectures are: \n' | ||
222 | ' %s' % (self.options.arch, | ||
223 | ', '.join(supported_arch))) | ||
224 | |||
225 | if self.options.pkgmgr is not None: | ||
226 | configmgr.create['pkgmgr'] = self.options.pkgmgr | ||
227 | |||
228 | if self.options.runtime: | ||
229 | configmgr.set_runtime(self.options.runtime) | ||
230 | |||
231 | if self.options.pack_to is not None: | ||
232 | configmgr.create['pack_to'] = self.options.pack_to | ||
233 | |||
234 | if self.options.copy_kernel: | ||
235 | configmgr.create['copy_kernel'] = self.options.copy_kernel | ||
236 | |||
237 | if self.options.install_pkgs: | ||
238 | configmgr.create['install_pkgs'] = [] | ||
239 | for pkgtype in self.options.install_pkgs.split(','): | ||
240 | if pkgtype not in ('source', 'debuginfo', 'debugsource'): | ||
241 | raise errors.Usage('Invalid parameter specified: "%s", ' | ||
242 | 'valid values: source, debuginfo, ' | ||
243 | 'debusource' % pkgtype) | ||
244 | |||
245 | configmgr.create['install_pkgs'].append(pkgtype) | ||
246 | |||
247 | if self.options.enabletmpfs: | ||
248 | configmgr.create['enabletmpfs'] = self.options.enabletmpfs | ||
249 | |||
250 | if self.options.repourl: | ||
251 | for item in self.options.repourl: | ||
252 | try: | ||
253 | key, val = item.split('=') | ||
254 | except: | ||
255 | continue | ||
256 | configmgr.create['repourl'][key] = val | ||
257 | |||
258 | def main(self, argv=None): | ||
259 | if argv is None: | ||
260 | argv = sys.argv | ||
261 | else: | ||
262 | argv = argv[:] # don't modify caller's list | ||
263 | |||
264 | self.optparser = self.get_optparser() | ||
265 | if self.optparser: | ||
266 | try: | ||
267 | argv = self.preoptparse(argv) | ||
268 | self.options, args = self.optparser.parse_args(argv) | ||
269 | |||
270 | except cmdln.CmdlnUserError, ex: | ||
271 | msg = "%s: %s\nTry '%s help' for info.\n"\ | ||
272 | % (self.name, ex, self.name) | ||
273 | msger.error(msg) | ||
274 | |||
275 | except cmdln.StopOptionProcessing, ex: | ||
276 | return 0 | ||
277 | else: | ||
278 | # optparser=None means no process for opts | ||
279 | self.options, args = None, argv[1:] | ||
280 | |||
281 | if not args: | ||
282 | return self.emptyline() | ||
283 | |||
284 | self.postoptparse() | ||
285 | |||
286 | return self.cmd(args) | ||
287 | |||
288 | def precmd(self, argv): # check help before cmd | ||
289 | |||
290 | if '-h' in argv or '?' in argv or '--help' in argv or 'help' in argv: | ||
291 | return argv | ||
292 | |||
293 | if len(argv) == 1: | ||
294 | return ['help', argv[0]] | ||
295 | |||
296 | return argv | ||
297 | |||
298 | def do_auto(self, subcmd, opts, *args): | ||
299 | """${cmd_name}: auto detect image type from magic header | ||
300 | |||
301 | Usage: | ||
302 | ${name} ${cmd_name} <ksfile> | ||
303 | |||
304 | ${cmd_option_list} | ||
305 | """ | ||
306 | def parse_magic_line(re_str, pstr, ptype='mic'): | ||
307 | ptn = re.compile(re_str) | ||
308 | m = ptn.match(pstr) | ||
309 | if not m or not m.groups(): | ||
310 | return None | ||
311 | |||
312 | inline_argv = m.group(1).strip() | ||
313 | if ptype == 'mic': | ||
314 | m2 = re.search('(?P<format>\w+)', inline_argv) | ||
315 | elif ptype == 'mic2': | ||
316 | m2 = re.search('(-f|--format(=)?)\s*(?P<format>\w+)', | ||
317 | inline_argv) | ||
318 | else: | ||
319 | return None | ||
320 | |||
321 | if m2: | ||
322 | cmdname = m2.group('format') | ||
323 | inline_argv = inline_argv.replace(m2.group(0), '') | ||
324 | return (cmdname, inline_argv) | ||
325 | |||
326 | return None | ||
327 | |||
328 | if len(args) != 1: | ||
329 | raise errors.Usage("Extra arguments given") | ||
330 | |||
331 | if not os.path.exists(args[0]): | ||
332 | raise errors.CreatorError("Can't find the file: %s" % args[0]) | ||
333 | |||
334 | with open(args[0], 'r') as rf: | ||
335 | first_line = rf.readline() | ||
336 | |||
337 | mic_re = '^#\s*-\*-mic-options-\*-\s+(.*)\s+-\*-mic-options-\*-' | ||
338 | mic2_re = '^#\s*-\*-mic2-options-\*-\s+(.*)\s+-\*-mic2-options-\*-' | ||
339 | |||
340 | result = parse_magic_line(mic_re, first_line, 'mic') \ | ||
341 | or parse_magic_line(mic2_re, first_line, 'mic2') | ||
342 | if not result: | ||
343 | raise errors.KsError("Invalid magic line in file: %s" % args[0]) | ||
344 | |||
345 | if result[0] not in self._subcmds: | ||
346 | raise errors.KsError("Unsupport format '%s' in %s" | ||
347 | % (result[0], args[0])) | ||
348 | |||
349 | argv = ' '.join(result + args).split() | ||
350 | self.main(argv) | ||
351 | |||
diff --git a/scripts/lib/mic/imager/__init__.py b/scripts/lib/mic/imager/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/mic/imager/__init__.py | |||
diff --git a/scripts/lib/mic/imager/baseimager.py b/scripts/lib/mic/imager/baseimager.py new file mode 100644 index 0000000000..b7212493b4 --- /dev/null +++ b/scripts/lib/mic/imager/baseimager.py | |||
@@ -0,0 +1,1263 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2007 Red Hat Inc. | ||
4 | # Copyright (c) 2009, 2010, 2011 Intel, Inc. | ||
5 | # | ||
6 | # This program is free software; you can redistribute it and/or modify it | ||
7 | # under the terms of the GNU General Public License as published by the Free | ||
8 | # Software Foundation; version 2 of the License | ||
9 | # | ||
10 | # This program is distributed in the hope that it will be useful, but | ||
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
12 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
13 | # for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License along | ||
16 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
17 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | |||
19 | from __future__ import with_statement | ||
20 | import os, sys | ||
21 | import stat | ||
22 | import tempfile | ||
23 | import shutil | ||
24 | import subprocess | ||
25 | import re | ||
26 | import tarfile | ||
27 | import glob | ||
28 | |||
29 | from mic import kickstart | ||
30 | from mic import msger | ||
31 | from mic.utils.errors import CreatorError, Abort | ||
32 | from mic.utils import misc, runner, fs_related as fs | ||
33 | |||
34 | class BaseImageCreator(object): | ||
35 | """Installs a system to a chroot directory. | ||
36 | |||
37 | ImageCreator is the simplest creator class available; it will install and | ||
38 | configure a system image according to the supplied kickstart file. | ||
39 | |||
40 | e.g. | ||
41 | |||
42 | import mic.imgcreate as imgcreate | ||
43 | ks = imgcreate.read_kickstart("foo.ks") | ||
44 | imgcreate.ImageCreator(ks, "foo").create() | ||
45 | |||
46 | """ | ||
47 | |||
48 | def __del__(self): | ||
49 | self.cleanup() | ||
50 | |||
51 | def __init__(self, createopts = None, pkgmgr = None): | ||
52 | """Initialize an ImageCreator instance. | ||
53 | |||
54 | ks -- a pykickstart.KickstartParser instance; this instance will be | ||
55 | used to drive the install by e.g. providing the list of packages | ||
56 | to be installed, the system configuration and %post scripts | ||
57 | |||
58 | name -- a name for the image; used for e.g. image filenames or | ||
59 | filesystem labels | ||
60 | """ | ||
61 | |||
62 | self.pkgmgr = pkgmgr | ||
63 | |||
64 | self.__builddir = None | ||
65 | self.__bindmounts = [] | ||
66 | |||
67 | self.ks = None | ||
68 | self.name = "target" | ||
69 | self.tmpdir = "/var/tmp/wic" | ||
70 | self.cachedir = "/var/tmp/wic/cache" | ||
71 | self.workdir = "/var/tmp/wic/build" | ||
72 | |||
73 | self.destdir = "." | ||
74 | self.installerfw_prefix = "INSTALLERFW_" | ||
75 | self.target_arch = "noarch" | ||
76 | self._local_pkgs_path = None | ||
77 | self.pack_to = None | ||
78 | self.repourl = {} | ||
79 | |||
80 | # If the kernel is save to the destdir when copy_kernel cmd is called. | ||
81 | self._need_copy_kernel = False | ||
82 | # setup tmpfs tmpdir when enabletmpfs is True | ||
83 | self.enabletmpfs = False | ||
84 | |||
85 | if createopts: | ||
86 | # Mapping table for variables that have different names. | ||
87 | optmap = {"pkgmgr" : "pkgmgr_name", | ||
88 | "outdir" : "destdir", | ||
89 | "arch" : "target_arch", | ||
90 | "local_pkgs_path" : "_local_pkgs_path", | ||
91 | "copy_kernel" : "_need_copy_kernel", | ||
92 | } | ||
93 | |||
94 | # update setting from createopts | ||
95 | for key in createopts.keys(): | ||
96 | if key in optmap: | ||
97 | option = optmap[key] | ||
98 | else: | ||
99 | option = key | ||
100 | setattr(self, option, createopts[key]) | ||
101 | |||
102 | self.destdir = os.path.abspath(os.path.expanduser(self.destdir)) | ||
103 | |||
104 | if 'release' in createopts and createopts['release']: | ||
105 | self.name = createopts['release'] + '_' + self.name | ||
106 | |||
107 | if self.pack_to: | ||
108 | if '@NAME@' in self.pack_to: | ||
109 | self.pack_to = self.pack_to.replace('@NAME@', self.name) | ||
110 | (tar, ext) = os.path.splitext(self.pack_to) | ||
111 | if ext in (".gz", ".bz2") and tar.endswith(".tar"): | ||
112 | ext = ".tar" + ext | ||
113 | if ext not in misc.pack_formats: | ||
114 | self.pack_to += ".tar" | ||
115 | |||
116 | self._dep_checks = ["ls", "bash", "cp", "echo", "modprobe"] | ||
117 | |||
118 | # Output image file names | ||
119 | self.outimage = [] | ||
120 | |||
121 | # A flag to generate checksum | ||
122 | self._genchecksum = False | ||
123 | |||
124 | self._alt_initrd_name = None | ||
125 | |||
126 | self._recording_pkgs = [] | ||
127 | |||
128 | # available size in root fs, init to 0 | ||
129 | self._root_fs_avail = 0 | ||
130 | |||
131 | # Name of the disk image file that is created. | ||
132 | self._img_name = None | ||
133 | |||
134 | self.image_format = None | ||
135 | |||
136 | # Save qemu emulator file name in order to clean up it finally | ||
137 | self.qemu_emulator = None | ||
138 | |||
139 | # No ks provided when called by convertor, so skip the dependency check | ||
140 | if self.ks: | ||
141 | # If we have btrfs partition we need to check necessary tools | ||
142 | for part in self.ks.handler.partition.partitions: | ||
143 | if part.fstype and part.fstype == "btrfs": | ||
144 | self._dep_checks.append("mkfs.btrfs") | ||
145 | break | ||
146 | |||
147 | if self.target_arch and self.target_arch.startswith("arm"): | ||
148 | for dep in self._dep_checks: | ||
149 | if dep == "extlinux": | ||
150 | self._dep_checks.remove(dep) | ||
151 | |||
152 | if not os.path.exists("/usr/bin/qemu-arm") or \ | ||
153 | not misc.is_statically_linked("/usr/bin/qemu-arm"): | ||
154 | self._dep_checks.append("qemu-arm-static") | ||
155 | |||
156 | if os.path.exists("/proc/sys/vm/vdso_enabled"): | ||
157 | vdso_fh = open("/proc/sys/vm/vdso_enabled","r") | ||
158 | vdso_value = vdso_fh.read().strip() | ||
159 | vdso_fh.close() | ||
160 | if (int)(vdso_value) == 1: | ||
161 | msger.warning("vdso is enabled on your host, which might " | ||
162 | "cause problems with arm emulations.\n" | ||
163 | "\tYou can disable vdso with following command before " | ||
164 | "starting image build:\n" | ||
165 | "\techo 0 | sudo tee /proc/sys/vm/vdso_enabled") | ||
166 | |||
167 | # make sure the specified tmpdir and cachedir exist | ||
168 | if not os.path.exists(self.tmpdir): | ||
169 | os.makedirs(self.tmpdir) | ||
170 | if not os.path.exists(self.cachedir): | ||
171 | os.makedirs(self.cachedir) | ||
172 | |||
173 | |||
174 | # | ||
175 | # Properties | ||
176 | # | ||
177 | def __get_instroot(self): | ||
178 | if self.__builddir is None: | ||
179 | raise CreatorError("_instroot is not valid before calling mount()") | ||
180 | return self.__builddir + "/install_root" | ||
181 | _instroot = property(__get_instroot) | ||
182 | """The location of the install root directory. | ||
183 | |||
184 | This is the directory into which the system is installed. Subclasses may | ||
185 | mount a filesystem image here or copy files to/from here. | ||
186 | |||
187 | Note, this directory does not exist before ImageCreator.mount() is called. | ||
188 | |||
189 | Note also, this is a read-only attribute. | ||
190 | |||
191 | """ | ||
192 | |||
193 | def __get_outdir(self): | ||
194 | if self.__builddir is None: | ||
195 | raise CreatorError("_outdir is not valid before calling mount()") | ||
196 | return self.__builddir + "/out" | ||
197 | _outdir = property(__get_outdir) | ||
198 | """The staging location for the final image. | ||
199 | |||
200 | This is where subclasses should stage any files that are part of the final | ||
201 | image. ImageCreator.package() will copy any files found here into the | ||
202 | requested destination directory. | ||
203 | |||
204 | Note, this directory does not exist before ImageCreator.mount() is called. | ||
205 | |||
206 | Note also, this is a read-only attribute. | ||
207 | |||
208 | """ | ||
209 | |||
210 | |||
211 | # | ||
212 | # Hooks for subclasses | ||
213 | # | ||
214 | def _mount_instroot(self, base_on = None): | ||
215 | """Mount or prepare the install root directory. | ||
216 | |||
217 | This is the hook where subclasses may prepare the install root by e.g. | ||
218 | mounting creating and loopback mounting a filesystem image to | ||
219 | _instroot. | ||
220 | |||
221 | There is no default implementation. | ||
222 | |||
223 | base_on -- this is the value passed to mount() and can be interpreted | ||
224 | as the subclass wishes; it might e.g. be the location of | ||
225 | a previously created ISO containing a system image. | ||
226 | |||
227 | """ | ||
228 | pass | ||
229 | |||
230 | def _unmount_instroot(self): | ||
231 | """Undo anything performed in _mount_instroot(). | ||
232 | |||
233 | This is the hook where subclasses must undo anything which was done | ||
234 | in _mount_instroot(). For example, if a filesystem image was mounted | ||
235 | onto _instroot, it should be unmounted here. | ||
236 | |||
237 | There is no default implementation. | ||
238 | |||
239 | """ | ||
240 | pass | ||
241 | |||
242 | def _create_bootconfig(self): | ||
243 | """Configure the image so that it's bootable. | ||
244 | |||
245 | This is the hook where subclasses may prepare the image for booting by | ||
246 | e.g. creating an initramfs and bootloader configuration. | ||
247 | |||
248 | This hook is called while the install root is still mounted, after the | ||
249 | packages have been installed and the kickstart configuration has been | ||
250 | applied, but before the %post scripts have been executed. | ||
251 | |||
252 | There is no default implementation. | ||
253 | |||
254 | """ | ||
255 | pass | ||
256 | |||
257 | def _stage_final_image(self): | ||
258 | """Stage the final system image in _outdir. | ||
259 | |||
260 | This is the hook where subclasses should place the image in _outdir | ||
261 | so that package() can copy it to the requested destination directory. | ||
262 | |||
263 | By default, this moves the install root into _outdir. | ||
264 | |||
265 | """ | ||
266 | shutil.move(self._instroot, self._outdir + "/" + self.name) | ||
267 | |||
268 | def get_installed_packages(self): | ||
269 | return self._pkgs_content.keys() | ||
270 | |||
271 | def _save_recording_pkgs(self, destdir): | ||
272 | """Save the list or content of installed packages to file. | ||
273 | """ | ||
274 | pkgs = self._pkgs_content.keys() | ||
275 | pkgs.sort() # inplace op | ||
276 | |||
277 | if not os.path.exists(destdir): | ||
278 | os.makedirs(destdir) | ||
279 | |||
280 | content = None | ||
281 | if 'vcs' in self._recording_pkgs: | ||
282 | vcslst = ["%s %s" % (k, v) for (k, v) in self._pkgs_vcsinfo.items()] | ||
283 | content = '\n'.join(sorted(vcslst)) | ||
284 | elif 'name' in self._recording_pkgs: | ||
285 | content = '\n'.join(pkgs) | ||
286 | if content: | ||
287 | namefile = os.path.join(destdir, self.name + '.packages') | ||
288 | f = open(namefile, "w") | ||
289 | f.write(content) | ||
290 | f.close() | ||
291 | self.outimage.append(namefile); | ||
292 | |||
293 | # if 'content', save more details | ||
294 | if 'content' in self._recording_pkgs: | ||
295 | contfile = os.path.join(destdir, self.name + '.files') | ||
296 | f = open(contfile, "w") | ||
297 | |||
298 | for pkg in pkgs: | ||
299 | content = pkg + '\n' | ||
300 | |||
301 | pkgcont = self._pkgs_content[pkg] | ||
302 | content += ' ' | ||
303 | content += '\n '.join(pkgcont) | ||
304 | content += '\n' | ||
305 | |||
306 | content += '\n' | ||
307 | f.write(content) | ||
308 | f.close() | ||
309 | self.outimage.append(contfile) | ||
310 | |||
311 | if 'license' in self._recording_pkgs: | ||
312 | licensefile = os.path.join(destdir, self.name + '.license') | ||
313 | f = open(licensefile, "w") | ||
314 | |||
315 | f.write('Summary:\n') | ||
316 | for license in reversed(sorted(self._pkgs_license, key=\ | ||
317 | lambda license: len(self._pkgs_license[license]))): | ||
318 | f.write(" - %s: %s\n" \ | ||
319 | % (license, len(self._pkgs_license[license]))) | ||
320 | |||
321 | f.write('\nDetails:\n') | ||
322 | for license in reversed(sorted(self._pkgs_license, key=\ | ||
323 | lambda license: len(self._pkgs_license[license]))): | ||
324 | f.write(" - %s:\n" % (license)) | ||
325 | for pkg in sorted(self._pkgs_license[license]): | ||
326 | f.write(" - %s\n" % (pkg)) | ||
327 | f.write('\n') | ||
328 | |||
329 | f.close() | ||
330 | self.outimage.append(licensefile) | ||
331 | |||
332 | def _get_required_packages(self): | ||
333 | """Return a list of required packages. | ||
334 | |||
335 | This is the hook where subclasses may specify a set of packages which | ||
336 | it requires to be installed. | ||
337 | |||
338 | This returns an empty list by default. | ||
339 | |||
340 | Note, subclasses should usually chain up to the base class | ||
341 | implementation of this hook. | ||
342 | |||
343 | """ | ||
344 | return [] | ||
345 | |||
346 | def _get_excluded_packages(self): | ||
347 | """Return a list of excluded packages. | ||
348 | |||
349 | This is the hook where subclasses may specify a set of packages which | ||
350 | it requires _not_ to be installed. | ||
351 | |||
352 | This returns an empty list by default. | ||
353 | |||
354 | Note, subclasses should usually chain up to the base class | ||
355 | implementation of this hook. | ||
356 | |||
357 | """ | ||
358 | return [] | ||
359 | |||
360 | def _get_local_packages(self): | ||
361 | """Return a list of rpm path to be local installed. | ||
362 | |||
363 | This is the hook where subclasses may specify a set of rpms which | ||
364 | it requires to be installed locally. | ||
365 | |||
366 | This returns an empty list by default. | ||
367 | |||
368 | Note, subclasses should usually chain up to the base class | ||
369 | implementation of this hook. | ||
370 | |||
371 | """ | ||
372 | if self._local_pkgs_path: | ||
373 | if os.path.isdir(self._local_pkgs_path): | ||
374 | return glob.glob( | ||
375 | os.path.join(self._local_pkgs_path, '*.rpm')) | ||
376 | elif os.path.splitext(self._local_pkgs_path)[-1] == '.rpm': | ||
377 | return [self._local_pkgs_path] | ||
378 | |||
379 | return [] | ||
380 | |||
381 | def _get_fstab(self): | ||
382 | """Return the desired contents of /etc/fstab. | ||
383 | |||
384 | This is the hook where subclasses may specify the contents of | ||
385 | /etc/fstab by returning a string containing the desired contents. | ||
386 | |||
387 | A sensible default implementation is provided. | ||
388 | |||
389 | """ | ||
390 | s = "/dev/root / %s %s 0 0\n" \ | ||
391 | % (self._fstype, | ||
392 | "defaults,noatime" if not self._fsopts else self._fsopts) | ||
393 | s += self._get_fstab_special() | ||
394 | return s | ||
395 | |||
396 | def _get_fstab_special(self): | ||
397 | s = "devpts /dev/pts devpts gid=5,mode=620 0 0\n" | ||
398 | s += "tmpfs /dev/shm tmpfs defaults 0 0\n" | ||
399 | s += "proc /proc proc defaults 0 0\n" | ||
400 | s += "sysfs /sys sysfs defaults 0 0\n" | ||
401 | return s | ||
402 | |||
403 | def _set_part_env(self, pnum, prop, value): | ||
404 | """ This is a helper function which generates an environment variable | ||
405 | for a property "prop" with value "value" of a partition number "pnum". | ||
406 | |||
407 | The naming convention is: | ||
408 | * Variables start with INSTALLERFW_PART | ||
409 | * Then goes the partition number, the order is the same as | ||
410 | specified in the KS file | ||
411 | * Then goes the property name | ||
412 | """ | ||
413 | |||
414 | if value == None: | ||
415 | value = "" | ||
416 | else: | ||
417 | value = str(value) | ||
418 | |||
419 | name = self.installerfw_prefix + ("PART%d_" % pnum) + prop | ||
420 | return { name : value } | ||
421 | |||
422 | def _get_post_scripts_env(self, in_chroot): | ||
423 | """Return an environment dict for %post scripts. | ||
424 | |||
425 | This is the hook where subclasses may specify some environment | ||
426 | variables for %post scripts by return a dict containing the desired | ||
427 | environment. | ||
428 | |||
429 | in_chroot -- whether this %post script is to be executed chroot()ed | ||
430 | into _instroot. | ||
431 | """ | ||
432 | |||
433 | env = {} | ||
434 | pnum = 0 | ||
435 | |||
436 | for p in kickstart.get_partitions(self.ks): | ||
437 | env.update(self._set_part_env(pnum, "SIZE", p.size)) | ||
438 | env.update(self._set_part_env(pnum, "MOUNTPOINT", p.mountpoint)) | ||
439 | env.update(self._set_part_env(pnum, "FSTYPE", p.fstype)) | ||
440 | env.update(self._set_part_env(pnum, "LABEL", p.label)) | ||
441 | env.update(self._set_part_env(pnum, "FSOPTS", p.fsopts)) | ||
442 | env.update(self._set_part_env(pnum, "BOOTFLAG", p.active)) | ||
443 | env.update(self._set_part_env(pnum, "ALIGN", p.align)) | ||
444 | env.update(self._set_part_env(pnum, "TYPE_ID", p.part_type)) | ||
445 | env.update(self._set_part_env(pnum, "DEVNODE", | ||
446 | "/dev/%s%d" % (p.disk, pnum + 1))) | ||
447 | pnum += 1 | ||
448 | |||
449 | # Count of paritions | ||
450 | env[self.installerfw_prefix + "PART_COUNT"] = str(pnum) | ||
451 | |||
452 | # Partition table format | ||
453 | ptable_format = self.ks.handler.bootloader.ptable | ||
454 | env[self.installerfw_prefix + "PTABLE_FORMAT"] = ptable_format | ||
455 | |||
456 | # The kerned boot parameters | ||
457 | kernel_opts = self.ks.handler.bootloader.appendLine | ||
458 | env[self.installerfw_prefix + "KERNEL_OPTS"] = kernel_opts | ||
459 | |||
460 | # Name of the distribution | ||
461 | env[self.installerfw_prefix + "DISTRO_NAME"] = self.distro_name | ||
462 | |||
463 | # Name of the image creation tool | ||
464 | env[self.installerfw_prefix + "INSTALLER_NAME"] = "wic" | ||
465 | |||
466 | # The real current location of the mounted file-systems | ||
467 | if in_chroot: | ||
468 | mount_prefix = "/" | ||
469 | else: | ||
470 | mount_prefix = self._instroot | ||
471 | env[self.installerfw_prefix + "MOUNT_PREFIX"] = mount_prefix | ||
472 | |||
473 | # These are historical variables which lack the common name prefix | ||
474 | if not in_chroot: | ||
475 | env["INSTALL_ROOT"] = self._instroot | ||
476 | env["IMG_NAME"] = self._name | ||
477 | |||
478 | return env | ||
479 | |||
480 | def __get_imgname(self): | ||
481 | return self.name | ||
482 | _name = property(__get_imgname) | ||
483 | """The name of the image file. | ||
484 | |||
485 | """ | ||
486 | |||
487 | def _get_kernel_versions(self): | ||
488 | """Return a dict detailing the available kernel types/versions. | ||
489 | |||
490 | This is the hook where subclasses may override what kernel types and | ||
491 | versions should be available for e.g. creating the booloader | ||
492 | configuration. | ||
493 | |||
494 | A dict should be returned mapping the available kernel types to a list | ||
495 | of the available versions for those kernels. | ||
496 | |||
497 | The default implementation uses rpm to iterate over everything | ||
498 | providing 'kernel', finds /boot/vmlinuz-* and returns the version | ||
499 | obtained from the vmlinuz filename. (This can differ from the kernel | ||
500 | RPM's n-v-r in the case of e.g. xen) | ||
501 | |||
502 | """ | ||
503 | def get_kernel_versions(instroot): | ||
504 | ret = {} | ||
505 | versions = set() | ||
506 | files = glob.glob(instroot + "/boot/vmlinuz-*") | ||
507 | for file in files: | ||
508 | version = os.path.basename(file)[8:] | ||
509 | if version is None: | ||
510 | continue | ||
511 | versions.add(version) | ||
512 | ret["kernel"] = list(versions) | ||
513 | return ret | ||
514 | |||
515 | def get_version(header): | ||
516 | version = None | ||
517 | for f in header['filenames']: | ||
518 | if f.startswith('/boot/vmlinuz-'): | ||
519 | version = f[14:] | ||
520 | return version | ||
521 | |||
522 | if self.ks is None: | ||
523 | return get_kernel_versions(self._instroot) | ||
524 | |||
525 | ts = rpm.TransactionSet(self._instroot) | ||
526 | |||
527 | ret = {} | ||
528 | for header in ts.dbMatch('provides', 'kernel'): | ||
529 | version = get_version(header) | ||
530 | if version is None: | ||
531 | continue | ||
532 | |||
533 | name = header['name'] | ||
534 | if not name in ret: | ||
535 | ret[name] = [version] | ||
536 | elif not version in ret[name]: | ||
537 | ret[name].append(version) | ||
538 | |||
539 | return ret | ||
540 | |||
541 | |||
542 | # | ||
543 | # Helpers for subclasses | ||
544 | # | ||
545 | def _do_bindmounts(self): | ||
546 | """Mount various system directories onto _instroot. | ||
547 | |||
548 | This method is called by mount(), but may also be used by subclasses | ||
549 | in order to re-mount the bindmounts after modifying the underlying | ||
550 | filesystem. | ||
551 | |||
552 | """ | ||
553 | for b in self.__bindmounts: | ||
554 | b.mount() | ||
555 | |||
556 | def _undo_bindmounts(self): | ||
557 | """Unmount the bind-mounted system directories from _instroot. | ||
558 | |||
559 | This method is usually only called by unmount(), but may also be used | ||
560 | by subclasses in order to gain access to the filesystem obscured by | ||
561 | the bindmounts - e.g. in order to create device nodes on the image | ||
562 | filesystem. | ||
563 | |||
564 | """ | ||
565 | self.__bindmounts.reverse() | ||
566 | for b in self.__bindmounts: | ||
567 | b.unmount() | ||
568 | |||
569 | def _chroot(self): | ||
570 | """Chroot into the install root. | ||
571 | |||
572 | This method may be used by subclasses when executing programs inside | ||
573 | the install root e.g. | ||
574 | |||
575 | subprocess.call(["/bin/ls"], preexec_fn = self.chroot) | ||
576 | |||
577 | """ | ||
578 | os.chroot(self._instroot) | ||
579 | os.chdir("/") | ||
580 | |||
581 | def _mkdtemp(self, prefix = "tmp-"): | ||
582 | """Create a temporary directory. | ||
583 | |||
584 | This method may be used by subclasses to create a temporary directory | ||
585 | for use in building the final image - e.g. a subclass might create | ||
586 | a temporary directory in order to bundle a set of files into a package. | ||
587 | |||
588 | The subclass may delete this directory if it wishes, but it will be | ||
589 | automatically deleted by cleanup(). | ||
590 | |||
591 | The absolute path to the temporary directory is returned. | ||
592 | |||
593 | Note, this method should only be called after mount() has been called. | ||
594 | |||
595 | prefix -- a prefix which should be used when creating the directory; | ||
596 | defaults to "tmp-". | ||
597 | |||
598 | """ | ||
599 | self.__ensure_builddir() | ||
600 | return tempfile.mkdtemp(dir = self.__builddir, prefix = prefix) | ||
601 | |||
602 | def _mkstemp(self, prefix = "tmp-"): | ||
603 | """Create a temporary file. | ||
604 | |||
605 | This method may be used by subclasses to create a temporary file | ||
606 | for use in building the final image - e.g. a subclass might need | ||
607 | a temporary location to unpack a compressed file. | ||
608 | |||
609 | The subclass may delete this file if it wishes, but it will be | ||
610 | automatically deleted by cleanup(). | ||
611 | |||
612 | A tuple containing a file descriptor (returned from os.open() and the | ||
613 | absolute path to the temporary directory is returned. | ||
614 | |||
615 | Note, this method should only be called after mount() has been called. | ||
616 | |||
617 | prefix -- a prefix which should be used when creating the file; | ||
618 | defaults to "tmp-". | ||
619 | |||
620 | """ | ||
621 | self.__ensure_builddir() | ||
622 | return tempfile.mkstemp(dir = self.__builddir, prefix = prefix) | ||
623 | |||
624 | def _mktemp(self, prefix = "tmp-"): | ||
625 | """Create a temporary file. | ||
626 | |||
627 | This method simply calls _mkstemp() and closes the returned file | ||
628 | descriptor. | ||
629 | |||
630 | The absolute path to the temporary file is returned. | ||
631 | |||
632 | Note, this method should only be called after mount() has been called. | ||
633 | |||
634 | prefix -- a prefix which should be used when creating the file; | ||
635 | defaults to "tmp-". | ||
636 | |||
637 | """ | ||
638 | |||
639 | (f, path) = self._mkstemp(prefix) | ||
640 | os.close(f) | ||
641 | return path | ||
642 | |||
643 | |||
644 | # | ||
645 | # Actual implementation | ||
646 | # | ||
647 | def __ensure_builddir(self): | ||
648 | if not self.__builddir is None: | ||
649 | return | ||
650 | |||
651 | try: | ||
652 | self.workdir = os.path.join(self.tmpdir, "build") | ||
653 | if not os.path.exists(self.workdir): | ||
654 | os.makedirs(self.workdir) | ||
655 | self.__builddir = tempfile.mkdtemp(dir = self.workdir, | ||
656 | prefix = "imgcreate-") | ||
657 | except OSError, (err, msg): | ||
658 | raise CreatorError("Failed create build directory in %s: %s" % | ||
659 | (self.tmpdir, msg)) | ||
660 | |||
661 | def get_cachedir(self, cachedir = None): | ||
662 | if self.cachedir: | ||
663 | return self.cachedir | ||
664 | |||
665 | self.__ensure_builddir() | ||
666 | if cachedir: | ||
667 | self.cachedir = cachedir | ||
668 | else: | ||
669 | self.cachedir = self.__builddir + "/wic-cache" | ||
670 | fs.makedirs(self.cachedir) | ||
671 | return self.cachedir | ||
672 | |||
673 | def __sanity_check(self): | ||
674 | """Ensure that the config we've been given is sane.""" | ||
675 | if not (kickstart.get_packages(self.ks) or | ||
676 | kickstart.get_groups(self.ks)): | ||
677 | raise CreatorError("No packages or groups specified") | ||
678 | |||
679 | kickstart.convert_method_to_repo(self.ks) | ||
680 | |||
681 | if not kickstart.get_repos(self.ks): | ||
682 | raise CreatorError("No repositories specified") | ||
683 | |||
684 | def __write_fstab(self): | ||
685 | fstab_contents = self._get_fstab() | ||
686 | if fstab_contents: | ||
687 | fstab = open(self._instroot + "/etc/fstab", "w") | ||
688 | fstab.write(fstab_contents) | ||
689 | fstab.close() | ||
690 | |||
691 | def __create_minimal_dev(self): | ||
692 | """Create a minimal /dev so that we don't corrupt the host /dev""" | ||
693 | origumask = os.umask(0000) | ||
694 | devices = (('null', 1, 3, 0666), | ||
695 | ('urandom',1, 9, 0666), | ||
696 | ('random', 1, 8, 0666), | ||
697 | ('full', 1, 7, 0666), | ||
698 | ('ptmx', 5, 2, 0666), | ||
699 | ('tty', 5, 0, 0666), | ||
700 | ('zero', 1, 5, 0666)) | ||
701 | |||
702 | links = (("/proc/self/fd", "/dev/fd"), | ||
703 | ("/proc/self/fd/0", "/dev/stdin"), | ||
704 | ("/proc/self/fd/1", "/dev/stdout"), | ||
705 | ("/proc/self/fd/2", "/dev/stderr")) | ||
706 | |||
707 | for (node, major, minor, perm) in devices: | ||
708 | if not os.path.exists(self._instroot + "/dev/" + node): | ||
709 | os.mknod(self._instroot + "/dev/" + node, | ||
710 | perm | stat.S_IFCHR, | ||
711 | os.makedev(major,minor)) | ||
712 | |||
713 | for (src, dest) in links: | ||
714 | if not os.path.exists(self._instroot + dest): | ||
715 | os.symlink(src, self._instroot + dest) | ||
716 | |||
717 | os.umask(origumask) | ||
718 | |||
719 | def __setup_tmpdir(self): | ||
720 | if not self.enabletmpfs: | ||
721 | return | ||
722 | |||
723 | runner.show('mount -t tmpfs -o size=4G tmpfs %s' % self.workdir) | ||
724 | |||
725 | def __clean_tmpdir(self): | ||
726 | if not self.enabletmpfs: | ||
727 | return | ||
728 | |||
729 | runner.show('umount -l %s' % self.workdir) | ||
730 | |||
731 | def mount(self, base_on = None, cachedir = None): | ||
732 | """Setup the target filesystem in preparation for an install. | ||
733 | |||
734 | This function sets up the filesystem which the ImageCreator will | ||
735 | install into and configure. The ImageCreator class merely creates an | ||
736 | install root directory, bind mounts some system directories (e.g. /dev) | ||
737 | and writes out /etc/fstab. Other subclasses may also e.g. create a | ||
738 | sparse file, format it and loopback mount it to the install root. | ||
739 | |||
740 | base_on -- a previous install on which to base this install; defaults | ||
741 | to None, causing a new image to be created | ||
742 | |||
743 | cachedir -- a directory in which to store the Yum cache; defaults to | ||
744 | None, causing a new cache to be created; by setting this | ||
745 | to another directory, the same cache can be reused across | ||
746 | multiple installs. | ||
747 | |||
748 | """ | ||
749 | self.__setup_tmpdir() | ||
750 | self.__ensure_builddir() | ||
751 | |||
752 | self._mount_instroot(base_on) | ||
753 | |||
754 | def unmount(self): | ||
755 | """Unmounts the target filesystem. | ||
756 | |||
757 | The ImageCreator class detaches the system from the install root, but | ||
758 | other subclasses may also detach the loopback mounted filesystem image | ||
759 | from the install root. | ||
760 | |||
761 | """ | ||
762 | self._unmount_instroot() | ||
763 | |||
764 | |||
765 | def cleanup(self): | ||
766 | """Unmounts the target filesystem and deletes temporary files. | ||
767 | |||
768 | This method calls unmount() and then deletes any temporary files and | ||
769 | directories that were created on the host system while building the | ||
770 | image. | ||
771 | |||
772 | Note, make sure to call this method once finished with the creator | ||
773 | instance in order to ensure no stale files are left on the host e.g.: | ||
774 | |||
775 | creator = ImageCreator(ks, name) | ||
776 | try: | ||
777 | creator.create() | ||
778 | finally: | ||
779 | creator.cleanup() | ||
780 | |||
781 | """ | ||
782 | if not self.__builddir: | ||
783 | return | ||
784 | |||
785 | self.unmount() | ||
786 | |||
787 | shutil.rmtree(self.__builddir, ignore_errors = True) | ||
788 | self.__builddir = None | ||
789 | |||
790 | self.__clean_tmpdir() | ||
791 | |||
792 | def __is_excluded_pkg(self, pkg): | ||
793 | if pkg in self._excluded_pkgs: | ||
794 | self._excluded_pkgs.remove(pkg) | ||
795 | return True | ||
796 | |||
797 | for xpkg in self._excluded_pkgs: | ||
798 | if xpkg.endswith('*'): | ||
799 | if pkg.startswith(xpkg[:-1]): | ||
800 | return True | ||
801 | elif xpkg.startswith('*'): | ||
802 | if pkg.endswith(xpkg[1:]): | ||
803 | return True | ||
804 | |||
805 | return None | ||
806 | |||
807 | def __select_packages(self, pkg_manager): | ||
808 | skipped_pkgs = [] | ||
809 | for pkg in self._required_pkgs: | ||
810 | e = pkg_manager.selectPackage(pkg) | ||
811 | if e: | ||
812 | if kickstart.ignore_missing(self.ks): | ||
813 | skipped_pkgs.append(pkg) | ||
814 | elif self.__is_excluded_pkg(pkg): | ||
815 | skipped_pkgs.append(pkg) | ||
816 | else: | ||
817 | raise CreatorError("Failed to find package '%s' : %s" % | ||
818 | (pkg, e)) | ||
819 | |||
820 | for pkg in skipped_pkgs: | ||
821 | msger.warning("Skipping missing package '%s'" % (pkg,)) | ||
822 | |||
823 | def __select_groups(self, pkg_manager): | ||
824 | skipped_groups = [] | ||
825 | for group in self._required_groups: | ||
826 | e = pkg_manager.selectGroup(group.name, group.include) | ||
827 | if e: | ||
828 | if kickstart.ignore_missing(self.ks): | ||
829 | skipped_groups.append(group) | ||
830 | else: | ||
831 | raise CreatorError("Failed to find group '%s' : %s" % | ||
832 | (group.name, e)) | ||
833 | |||
834 | for group in skipped_groups: | ||
835 | msger.warning("Skipping missing group '%s'" % (group.name,)) | ||
836 | |||
837 | def __deselect_packages(self, pkg_manager): | ||
838 | for pkg in self._excluded_pkgs: | ||
839 | pkg_manager.deselectPackage(pkg) | ||
840 | |||
841 | def __localinst_packages(self, pkg_manager): | ||
842 | for rpm_path in self._get_local_packages(): | ||
843 | pkg_manager.installLocal(rpm_path) | ||
844 | |||
845 | def __preinstall_packages(self, pkg_manager): | ||
846 | if not self.ks: | ||
847 | return | ||
848 | |||
849 | self._preinstall_pkgs = kickstart.get_pre_packages(self.ks) | ||
850 | for pkg in self._preinstall_pkgs: | ||
851 | pkg_manager.preInstall(pkg) | ||
852 | |||
853 | def __attachment_packages(self, pkg_manager): | ||
854 | if not self.ks: | ||
855 | return | ||
856 | |||
857 | self._attachment = [] | ||
858 | for item in kickstart.get_attachment(self.ks): | ||
859 | if item.startswith('/'): | ||
860 | fpaths = os.path.join(self._instroot, item.lstrip('/')) | ||
861 | for fpath in glob.glob(fpaths): | ||
862 | self._attachment.append(fpath) | ||
863 | continue | ||
864 | |||
865 | filelist = pkg_manager.getFilelist(item) | ||
866 | if filelist: | ||
867 | # found rpm in rootfs | ||
868 | for pfile in pkg_manager.getFilelist(item): | ||
869 | fpath = os.path.join(self._instroot, pfile.lstrip('/')) | ||
870 | self._attachment.append(fpath) | ||
871 | continue | ||
872 | |||
873 | # try to retrieve rpm file | ||
874 | (url, proxies) = pkg_manager.package_url(item) | ||
875 | if not url: | ||
876 | msger.warning("Can't get url from repo for %s" % item) | ||
877 | continue | ||
878 | fpath = os.path.join(self.cachedir, os.path.basename(url)) | ||
879 | if not os.path.exists(fpath): | ||
880 | # download pkgs | ||
881 | try: | ||
882 | fpath = grabber.myurlgrab(url, fpath, proxies, None) | ||
883 | except CreatorError: | ||
884 | raise | ||
885 | |||
886 | tmpdir = self._mkdtemp() | ||
887 | misc.extract_rpm(fpath, tmpdir) | ||
888 | for (root, dirs, files) in os.walk(tmpdir): | ||
889 | for fname in files: | ||
890 | fpath = os.path.join(root, fname) | ||
891 | self._attachment.append(fpath) | ||
892 | |||
893 | def install(self, repo_urls=None): | ||
894 | """Install packages into the install root. | ||
895 | |||
896 | This function installs the packages listed in the supplied kickstart | ||
897 | into the install root. By default, the packages are installed from the | ||
898 | repository URLs specified in the kickstart. | ||
899 | |||
900 | repo_urls -- a dict which maps a repository name to a repository URL; | ||
901 | if supplied, this causes any repository URLs specified in | ||
902 | the kickstart to be overridden. | ||
903 | |||
904 | """ | ||
905 | |||
906 | # initialize pkg list to install | ||
907 | if self.ks: | ||
908 | self.__sanity_check() | ||
909 | |||
910 | self._required_pkgs = \ | ||
911 | kickstart.get_packages(self.ks, self._get_required_packages()) | ||
912 | self._excluded_pkgs = \ | ||
913 | kickstart.get_excluded(self.ks, self._get_excluded_packages()) | ||
914 | self._required_groups = kickstart.get_groups(self.ks) | ||
915 | else: | ||
916 | self._required_pkgs = None | ||
917 | self._excluded_pkgs = None | ||
918 | self._required_groups = None | ||
919 | |||
920 | pkg_manager = self.get_pkg_manager() | ||
921 | pkg_manager.setup() | ||
922 | |||
923 | if hasattr(self, 'install_pkgs') and self.install_pkgs: | ||
924 | if 'debuginfo' in self.install_pkgs: | ||
925 | pkg_manager.install_debuginfo = True | ||
926 | |||
927 | for repo in kickstart.get_repos(self.ks, repo_urls): | ||
928 | (name, baseurl, mirrorlist, inc, exc, | ||
929 | proxy, proxy_username, proxy_password, debuginfo, | ||
930 | source, gpgkey, disable, ssl_verify, nocache, | ||
931 | cost, priority) = repo | ||
932 | |||
933 | yr = pkg_manager.addRepository(name, baseurl, mirrorlist, proxy, | ||
934 | proxy_username, proxy_password, inc, exc, ssl_verify, | ||
935 | nocache, cost, priority) | ||
936 | |||
937 | if kickstart.exclude_docs(self.ks): | ||
938 | rpm.addMacro("_excludedocs", "1") | ||
939 | rpm.addMacro("_dbpath", "/var/lib/rpm") | ||
940 | rpm.addMacro("__file_context_path", "%{nil}") | ||
941 | if kickstart.inst_langs(self.ks) != None: | ||
942 | rpm.addMacro("_install_langs", kickstart.inst_langs(self.ks)) | ||
943 | |||
944 | try: | ||
945 | self.__preinstall_packages(pkg_manager) | ||
946 | self.__select_packages(pkg_manager) | ||
947 | self.__select_groups(pkg_manager) | ||
948 | self.__deselect_packages(pkg_manager) | ||
949 | self.__localinst_packages(pkg_manager) | ||
950 | |||
951 | BOOT_SAFEGUARD = 256L * 1024 * 1024 # 256M | ||
952 | checksize = self._root_fs_avail | ||
953 | if checksize: | ||
954 | checksize -= BOOT_SAFEGUARD | ||
955 | if self.target_arch: | ||
956 | pkg_manager._add_prob_flags(rpm.RPMPROB_FILTER_IGNOREARCH) | ||
957 | pkg_manager.runInstall(checksize) | ||
958 | except CreatorError, e: | ||
959 | raise | ||
960 | except KeyboardInterrupt: | ||
961 | raise | ||
962 | else: | ||
963 | self._pkgs_content = pkg_manager.getAllContent() | ||
964 | self._pkgs_license = pkg_manager.getPkgsLicense() | ||
965 | self._pkgs_vcsinfo = pkg_manager.getVcsInfo() | ||
966 | self.__attachment_packages(pkg_manager) | ||
967 | finally: | ||
968 | pkg_manager.close() | ||
969 | |||
970 | # hook post install | ||
971 | self.postinstall() | ||
972 | |||
973 | # do some clean up to avoid lvm info leakage. this sucks. | ||
974 | for subdir in ("cache", "backup", "archive"): | ||
975 | lvmdir = self._instroot + "/etc/lvm/" + subdir | ||
976 | try: | ||
977 | for f in os.listdir(lvmdir): | ||
978 | os.unlink(lvmdir + "/" + f) | ||
979 | except: | ||
980 | pass | ||
981 | |||
982 | def postinstall(self): | ||
983 | self.copy_attachment() | ||
984 | |||
985 | def __run_post_scripts(self): | ||
986 | msger.info("Running scripts ...") | ||
987 | if os.path.exists(self._instroot + "/tmp"): | ||
988 | shutil.rmtree(self._instroot + "/tmp") | ||
989 | os.mkdir (self._instroot + "/tmp", 0755) | ||
990 | for s in kickstart.get_post_scripts(self.ks): | ||
991 | (fd, path) = tempfile.mkstemp(prefix = "ks-script-", | ||
992 | dir = self._instroot + "/tmp") | ||
993 | |||
994 | s.script = s.script.replace("\r", "") | ||
995 | os.write(fd, s.script) | ||
996 | os.close(fd) | ||
997 | os.chmod(path, 0700) | ||
998 | |||
999 | env = self._get_post_scripts_env(s.inChroot) | ||
1000 | |||
1001 | if not s.inChroot: | ||
1002 | preexec = None | ||
1003 | script = path | ||
1004 | else: | ||
1005 | preexec = self._chroot | ||
1006 | script = "/tmp/" + os.path.basename(path) | ||
1007 | |||
1008 | try: | ||
1009 | try: | ||
1010 | subprocess.call([s.interp, script], | ||
1011 | preexec_fn = preexec, | ||
1012 | env = env, | ||
1013 | stdout = sys.stdout, | ||
1014 | stderr = sys.stderr) | ||
1015 | except OSError, (err, msg): | ||
1016 | raise CreatorError("Failed to execute %%post script " | ||
1017 | "with '%s' : %s" % (s.interp, msg)) | ||
1018 | finally: | ||
1019 | os.unlink(path) | ||
1020 | |||
1021 | def __save_repo_keys(self, repodata): | ||
1022 | if not repodata: | ||
1023 | return None | ||
1024 | |||
1025 | gpgkeydir = "/etc/pki/rpm-gpg" | ||
1026 | fs.makedirs(self._instroot + gpgkeydir) | ||
1027 | for repo in repodata: | ||
1028 | if repo["repokey"]: | ||
1029 | repokey = gpgkeydir + "/RPM-GPG-KEY-%s" % repo["name"] | ||
1030 | shutil.copy(repo["repokey"], self._instroot + repokey) | ||
1031 | |||
1032 | def configure(self, repodata = None): | ||
1033 | """Configure the system image according to the kickstart. | ||
1034 | |||
1035 | This method applies the (e.g. keyboard or network) configuration | ||
1036 | specified in the kickstart and executes the kickstart %post scripts. | ||
1037 | |||
1038 | If necessary, it also prepares the image to be bootable by e.g. | ||
1039 | creating an initrd and bootloader configuration. | ||
1040 | |||
1041 | """ | ||
1042 | ksh = self.ks.handler | ||
1043 | |||
1044 | msger.info('Applying configurations ...') | ||
1045 | try: | ||
1046 | kickstart.LanguageConfig(self._instroot).apply(ksh.lang) | ||
1047 | kickstart.KeyboardConfig(self._instroot).apply(ksh.keyboard) | ||
1048 | kickstart.TimezoneConfig(self._instroot).apply(ksh.timezone) | ||
1049 | #kickstart.AuthConfig(self._instroot).apply(ksh.authconfig) | ||
1050 | kickstart.FirewallConfig(self._instroot).apply(ksh.firewall) | ||
1051 | kickstart.RootPasswordConfig(self._instroot).apply(ksh.rootpw) | ||
1052 | kickstart.UserConfig(self._instroot).apply(ksh.user) | ||
1053 | kickstart.ServicesConfig(self._instroot).apply(ksh.services) | ||
1054 | kickstart.XConfig(self._instroot).apply(ksh.xconfig) | ||
1055 | kickstart.NetworkConfig(self._instroot).apply(ksh.network) | ||
1056 | kickstart.RPMMacroConfig(self._instroot).apply(self.ks) | ||
1057 | kickstart.DesktopConfig(self._instroot).apply(ksh.desktop) | ||
1058 | self.__save_repo_keys(repodata) | ||
1059 | kickstart.MoblinRepoConfig(self._instroot).apply(ksh.repo, repodata, self.repourl) | ||
1060 | except: | ||
1061 | msger.warning("Failed to apply configuration to image") | ||
1062 | raise | ||
1063 | |||
1064 | self._create_bootconfig() | ||
1065 | self.__run_post_scripts() | ||
1066 | |||
1067 | def launch_shell(self, launch): | ||
1068 | """Launch a shell in the install root. | ||
1069 | |||
1070 | This method is launches a bash shell chroot()ed in the install root; | ||
1071 | this can be useful for debugging. | ||
1072 | |||
1073 | """ | ||
1074 | if launch: | ||
1075 | msger.info("Launching shell. Exit to continue.") | ||
1076 | subprocess.call(["/bin/bash"], preexec_fn = self._chroot) | ||
1077 | |||
1078 | def do_genchecksum(self, image_name): | ||
1079 | if not self._genchecksum: | ||
1080 | return | ||
1081 | |||
1082 | md5sum = misc.get_md5sum(image_name) | ||
1083 | with open(image_name + ".md5sum", "w") as f: | ||
1084 | f.write("%s %s" % (md5sum, os.path.basename(image_name))) | ||
1085 | self.outimage.append(image_name+".md5sum") | ||
1086 | |||
1087 | def package(self, destdir = "."): | ||
1088 | """Prepares the created image for final delivery. | ||
1089 | |||
1090 | In its simplest form, this method merely copies the install root to the | ||
1091 | supplied destination directory; other subclasses may choose to package | ||
1092 | the image by e.g. creating a bootable ISO containing the image and | ||
1093 | bootloader configuration. | ||
1094 | |||
1095 | destdir -- the directory into which the final image should be moved; | ||
1096 | this defaults to the current directory. | ||
1097 | |||
1098 | """ | ||
1099 | self._stage_final_image() | ||
1100 | |||
1101 | if not os.path.exists(destdir): | ||
1102 | fs.makedirs(destdir) | ||
1103 | |||
1104 | if self._recording_pkgs: | ||
1105 | self._save_recording_pkgs(destdir) | ||
1106 | |||
1107 | # For image formats with two or multiple image files, it will be | ||
1108 | # better to put them under a directory | ||
1109 | if self.image_format in ("raw", "vmdk", "vdi", "nand", "mrstnand"): | ||
1110 | destdir = os.path.join(destdir, "%s-%s" \ | ||
1111 | % (self.name, self.image_format)) | ||
1112 | msger.debug("creating destination dir: %s" % destdir) | ||
1113 | fs.makedirs(destdir) | ||
1114 | |||
1115 | # Ensure all data is flushed to _outdir | ||
1116 | runner.quiet('sync') | ||
1117 | |||
1118 | misc.check_space_pre_cp(self._outdir, destdir) | ||
1119 | for f in os.listdir(self._outdir): | ||
1120 | shutil.move(os.path.join(self._outdir, f), | ||
1121 | os.path.join(destdir, f)) | ||
1122 | self.outimage.append(os.path.join(destdir, f)) | ||
1123 | self.do_genchecksum(os.path.join(destdir, f)) | ||
1124 | |||
1125 | def print_outimage_info(self): | ||
1126 | msg = "The new image can be found here:\n" | ||
1127 | self.outimage.sort() | ||
1128 | for file in self.outimage: | ||
1129 | msg += ' %s\n' % os.path.abspath(file) | ||
1130 | |||
1131 | msger.info(msg) | ||
1132 | |||
1133 | def check_depend_tools(self): | ||
1134 | for tool in self._dep_checks: | ||
1135 | fs.find_binary_path(tool) | ||
1136 | |||
1137 | def package_output(self, image_format, destdir = ".", package="none"): | ||
1138 | if not package or package == "none": | ||
1139 | return | ||
1140 | |||
1141 | destdir = os.path.abspath(os.path.expanduser(destdir)) | ||
1142 | (pkg, comp) = os.path.splitext(package) | ||
1143 | if comp: | ||
1144 | comp=comp.lstrip(".") | ||
1145 | |||
1146 | if pkg == "tar": | ||
1147 | if comp: | ||
1148 | dst = "%s/%s-%s.tar.%s" %\ | ||
1149 | (destdir, self.name, image_format, comp) | ||
1150 | else: | ||
1151 | dst = "%s/%s-%s.tar" %\ | ||
1152 | (destdir, self.name, image_format) | ||
1153 | |||
1154 | msger.info("creating %s" % dst) | ||
1155 | tar = tarfile.open(dst, "w:" + comp) | ||
1156 | |||
1157 | for file in self.outimage: | ||
1158 | msger.info("adding %s to %s" % (file, dst)) | ||
1159 | tar.add(file, | ||
1160 | arcname=os.path.join("%s-%s" \ | ||
1161 | % (self.name, image_format), | ||
1162 | os.path.basename(file))) | ||
1163 | if os.path.isdir(file): | ||
1164 | shutil.rmtree(file, ignore_errors = True) | ||
1165 | else: | ||
1166 | os.remove(file) | ||
1167 | |||
1168 | tar.close() | ||
1169 | |||
1170 | '''All the file in outimage has been packaged into tar.* file''' | ||
1171 | self.outimage = [dst] | ||
1172 | |||
1173 | def release_output(self, config, destdir, release): | ||
1174 | """ Create release directory and files | ||
1175 | """ | ||
1176 | |||
1177 | def _rpath(fn): | ||
1178 | """ release path """ | ||
1179 | return os.path.join(destdir, fn) | ||
1180 | |||
1181 | outimages = self.outimage | ||
1182 | |||
1183 | # new ks | ||
1184 | new_kspath = _rpath(self.name+'.ks') | ||
1185 | with open(config) as fr: | ||
1186 | with open(new_kspath, "w") as wf: | ||
1187 | # When building a release we want to make sure the .ks | ||
1188 | # file generates the same build even when --release not used. | ||
1189 | wf.write(fr.read().replace("@BUILD_ID@", release)) | ||
1190 | outimages.append(new_kspath) | ||
1191 | |||
1192 | # save log file, logfile is only available in creator attrs | ||
1193 | if hasattr(self, 'logfile') and not self.logfile: | ||
1194 | log_path = _rpath(self.name + ".log") | ||
1195 | # touch the log file, else outimages will filter it out | ||
1196 | with open(log_path, 'w') as wf: | ||
1197 | wf.write('') | ||
1198 | msger.set_logfile(log_path) | ||
1199 | outimages.append(_rpath(self.name + ".log")) | ||
1200 | |||
1201 | # rename iso and usbimg | ||
1202 | for f in os.listdir(destdir): | ||
1203 | if f.endswith(".iso"): | ||
1204 | newf = f[:-4] + '.img' | ||
1205 | elif f.endswith(".usbimg"): | ||
1206 | newf = f[:-7] + '.img' | ||
1207 | else: | ||
1208 | continue | ||
1209 | os.rename(_rpath(f), _rpath(newf)) | ||
1210 | outimages.append(_rpath(newf)) | ||
1211 | |||
1212 | # generate MD5SUMS | ||
1213 | with open(_rpath("MD5SUMS"), "w") as wf: | ||
1214 | for f in os.listdir(destdir): | ||
1215 | if f == "MD5SUMS": | ||
1216 | continue | ||
1217 | |||
1218 | if os.path.isdir(os.path.join(destdir, f)): | ||
1219 | continue | ||
1220 | |||
1221 | md5sum = misc.get_md5sum(_rpath(f)) | ||
1222 | # There needs to be two spaces between the sum and | ||
1223 | # filepath to match the syntax with md5sum. | ||
1224 | # This way also md5sum -c MD5SUMS can be used by users | ||
1225 | wf.write("%s *%s\n" % (md5sum, f)) | ||
1226 | |||
1227 | outimages.append("%s/MD5SUMS" % destdir) | ||
1228 | |||
1229 | # Filter out the nonexist file | ||
1230 | for fp in outimages[:]: | ||
1231 | if not os.path.exists("%s" % fp): | ||
1232 | outimages.remove(fp) | ||
1233 | |||
1234 | def copy_kernel(self): | ||
1235 | """ Copy kernel files to the outimage directory. | ||
1236 | NOTE: This needs to be called before unmounting the instroot. | ||
1237 | """ | ||
1238 | |||
1239 | if not self._need_copy_kernel: | ||
1240 | return | ||
1241 | |||
1242 | if not os.path.exists(self.destdir): | ||
1243 | os.makedirs(self.destdir) | ||
1244 | |||
1245 | for kernel in glob.glob("%s/boot/vmlinuz-*" % self._instroot): | ||
1246 | kernelfilename = "%s/%s-%s" % (self.destdir, | ||
1247 | self.name, | ||
1248 | os.path.basename(kernel)) | ||
1249 | msger.info('copy kernel file %s as %s' % (os.path.basename(kernel), | ||
1250 | kernelfilename)) | ||
1251 | shutil.copy(kernel, kernelfilename) | ||
1252 | self.outimage.append(kernelfilename) | ||
1253 | |||
1254 | def copy_attachment(self): | ||
1255 | """ Subclass implement it to handle attachment files | ||
1256 | NOTE: This needs to be called before unmounting the instroot. | ||
1257 | """ | ||
1258 | pass | ||
1259 | |||
1260 | def get_pkg_manager(self): | ||
1261 | return self.pkgmgr(target_arch = self.target_arch, | ||
1262 | instroot = self._instroot, | ||
1263 | cachedir = self.cachedir) | ||
diff --git a/scripts/lib/mic/imager/direct.py b/scripts/lib/mic/imager/direct.py new file mode 100644 index 0000000000..fef9d0ed32 --- /dev/null +++ b/scripts/lib/mic/imager/direct.py | |||
@@ -0,0 +1,384 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2013, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This implements the 'direct' image creator class for 'wic', based | ||
22 | # loosely on the raw image creator from 'mic' | ||
23 | # | ||
24 | # AUTHORS | ||
25 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
26 | # | ||
27 | |||
28 | import os | ||
29 | import stat | ||
30 | import shutil | ||
31 | |||
32 | from mic import kickstart, msger | ||
33 | from mic.utils import fs_related, runner, misc | ||
34 | from mic.utils.partitionedfs import PartitionedMount | ||
35 | from mic.utils.errors import CreatorError, MountError | ||
36 | from mic.imager.baseimager import BaseImageCreator | ||
37 | from mic.utils.oe.misc import * | ||
38 | from mic.plugin import pluginmgr | ||
39 | |||
40 | disk_methods = { | ||
41 | "do_install_disk":None, | ||
42 | } | ||
43 | |||
44 | class DirectImageCreator(BaseImageCreator): | ||
45 | """ | ||
46 | Installs a system into a file containing a partitioned disk image. | ||
47 | |||
48 | DirectImageCreator is an advanced ImageCreator subclass; an image | ||
49 | file is formatted with a partition table, each partition created | ||
50 | from a rootfs or other OpenEmbedded build artifact and dd'ed into | ||
51 | the virtual disk. The disk image can subsequently be dd'ed onto | ||
52 | media and used on actual hardware. | ||
53 | """ | ||
54 | |||
55 | def __init__(self, oe_builddir, image_output_dir, rootfs_dir, bootimg_dir, | ||
56 | kernel_dir, native_sysroot, hdddir, staging_data_dir, | ||
57 | creatoropts=None, pkgmgr=None, compress_image=None, | ||
58 | generate_bmap=None, fstab_entry="uuid"): | ||
59 | """ | ||
60 | Initialize a DirectImageCreator instance. | ||
61 | |||
62 | This method takes the same arguments as ImageCreator.__init__() | ||
63 | """ | ||
64 | BaseImageCreator.__init__(self, creatoropts, pkgmgr) | ||
65 | |||
66 | self.__instimage = None | ||
67 | self.__imgdir = None | ||
68 | self.__disks = {} | ||
69 | self.__disk_format = "direct" | ||
70 | self._disk_names = [] | ||
71 | self._ptable_format = self.ks.handler.bootloader.ptable | ||
72 | self.use_uuid = fstab_entry == "uuid" | ||
73 | self.compress_image = compress_image | ||
74 | self.bmap_needed = generate_bmap | ||
75 | |||
76 | self.oe_builddir = oe_builddir | ||
77 | if image_output_dir: | ||
78 | self.tmpdir = image_output_dir | ||
79 | self.cachedir = "%s/cache" % image_output_dir | ||
80 | self.rootfs_dir = rootfs_dir | ||
81 | self.bootimg_dir = bootimg_dir | ||
82 | self.kernel_dir = kernel_dir | ||
83 | self.native_sysroot = native_sysroot | ||
84 | self.hdddir = hdddir | ||
85 | self.staging_data_dir = staging_data_dir | ||
86 | |||
87 | def __write_fstab(self, image_rootfs): | ||
88 | """overriden to generate fstab (temporarily) in rootfs. This | ||
89 | is called from mount_instroot, make sure it doesn't get called | ||
90 | from BaseImage.mount()""" | ||
91 | if image_rootfs is None: | ||
92 | return None | ||
93 | |||
94 | fstab = image_rootfs + "/etc/fstab" | ||
95 | if not os.path.isfile(fstab): | ||
96 | return None | ||
97 | |||
98 | parts = self._get_parts() | ||
99 | |||
100 | self._save_fstab(fstab) | ||
101 | fstab_lines = self._get_fstab(fstab, parts) | ||
102 | self._update_fstab(fstab_lines, parts) | ||
103 | self._write_fstab(fstab, fstab_lines) | ||
104 | |||
105 | return fstab | ||
106 | |||
107 | def _update_fstab(self, fstab_lines, parts): | ||
108 | """Assume partition order same as in wks""" | ||
109 | for num, p in enumerate(parts, 1): | ||
110 | if not p.mountpoint or p.mountpoint == "/" or p.mountpoint == "/boot": | ||
111 | continue | ||
112 | if self._ptable_format == 'msdos' and num > 3: | ||
113 | device_name = "/dev/" + p.disk + str(num + 1) | ||
114 | else: | ||
115 | device_name = "/dev/" + p.disk + str(num) | ||
116 | fstab_entry = device_name + "\t" + p.mountpoint + "\t" + p.fstype + "\tdefaults\t0\t0\n" | ||
117 | fstab_lines.append(fstab_entry) | ||
118 | |||
119 | def _write_fstab(self, fstab, fstab_lines): | ||
120 | fstab = open(fstab, "w") | ||
121 | for line in fstab_lines: | ||
122 | fstab.write(line) | ||
123 | fstab.close() | ||
124 | |||
125 | def _save_fstab(self, fstab): | ||
126 | """Save the current fstab in rootfs""" | ||
127 | shutil.copyfile(fstab, fstab + ".orig") | ||
128 | |||
129 | def _restore_fstab(self, fstab): | ||
130 | """Restore the saved fstab in rootfs""" | ||
131 | if fstab is None: | ||
132 | return | ||
133 | shutil.move(fstab + ".orig", fstab) | ||
134 | |||
135 | def _get_fstab(self, fstab, parts): | ||
136 | """Return the desired contents of /etc/fstab.""" | ||
137 | f = open(fstab, "r") | ||
138 | fstab_contents = f.readlines() | ||
139 | f.close() | ||
140 | |||
141 | return fstab_contents | ||
142 | |||
143 | def set_bootimg_dir(self, bootimg_dir): | ||
144 | """ | ||
145 | Accessor for bootimg_dir, the actual location used for the source | ||
146 | of the bootimg. Should be set by source plugins (only if they | ||
147 | change the default bootimg source) so the correct info gets | ||
148 | displayed for print_outimage_info(). | ||
149 | """ | ||
150 | self.bootimg_dir = bootimg_dir | ||
151 | |||
152 | def _get_parts(self): | ||
153 | if not self.ks: | ||
154 | raise CreatorError("Failed to get partition info, " | ||
155 | "please check your kickstart setting.") | ||
156 | |||
157 | # Set a default partition if no partition is given out | ||
158 | if not self.ks.handler.partition.partitions: | ||
159 | partstr = "part / --size 1900 --ondisk sda --fstype=ext3" | ||
160 | args = partstr.split() | ||
161 | pd = self.ks.handler.partition.parse(args[1:]) | ||
162 | if pd not in self.ks.handler.partition.partitions: | ||
163 | self.ks.handler.partition.partitions.append(pd) | ||
164 | |||
165 | # partitions list from kickstart file | ||
166 | return kickstart.get_partitions(self.ks) | ||
167 | |||
168 | def get_disk_names(self): | ||
169 | """ Returns a list of physical target disk names (e.g., 'sdb') which | ||
170 | will be created. """ | ||
171 | |||
172 | if self._disk_names: | ||
173 | return self._disk_names | ||
174 | |||
175 | #get partition info from ks handler | ||
176 | parts = self._get_parts() | ||
177 | |||
178 | for i in range(len(parts)): | ||
179 | if parts[i].disk: | ||
180 | disk_name = parts[i].disk | ||
181 | else: | ||
182 | raise CreatorError("Failed to create disks, no --ondisk " | ||
183 | "specified in partition line of ks file") | ||
184 | |||
185 | if parts[i].mountpoint and not parts[i].fstype: | ||
186 | raise CreatorError("Failed to create disks, no --fstype " | ||
187 | "specified for partition with mountpoint " | ||
188 | "'%s' in the ks file") | ||
189 | |||
190 | self._disk_names.append(disk_name) | ||
191 | |||
192 | return self._disk_names | ||
193 | |||
194 | def _full_name(self, name, extention): | ||
195 | """ Construct full file name for a file we generate. """ | ||
196 | return "%s-%s.%s" % (self.name, name, extention) | ||
197 | |||
198 | def _full_path(self, path, name, extention): | ||
199 | """ Construct full file path to a file we generate. """ | ||
200 | return os.path.join(path, self._full_name(name, extention)) | ||
201 | |||
202 | def get_default_source_plugin(self): | ||
203 | """ | ||
204 | The default source plugin i.e. the plugin that's consulted for | ||
205 | overall image generation tasks outside of any particular | ||
206 | partition. For convenience, we just hang it off the | ||
207 | bootloader handler since it's the one non-partition object in | ||
208 | any setup. By default the default plugin is set to the same | ||
209 | plugin as the /boot partition; since we hang it off the | ||
210 | bootloader object, the default can be explicitly set using the | ||
211 | --source bootloader param. | ||
212 | """ | ||
213 | return self.ks.handler.bootloader.source | ||
214 | |||
215 | # | ||
216 | # Actual implemention | ||
217 | # | ||
218 | def _mount_instroot(self, base_on = None): | ||
219 | """ | ||
220 | For 'wic', we already have our build artifacts and don't want | ||
221 | to loop mount anything to install into, we just create | ||
222 | filesystems from the artifacts directly and combine them into | ||
223 | a partitioned image. | ||
224 | |||
225 | We still want to reuse as much of the basic mic machinery | ||
226 | though; despite the fact that we don't actually do loop or any | ||
227 | other kind of mounting we still want to do many of the same | ||
228 | things to prepare images, so we basically just adapt to the | ||
229 | basic framework and reinterpret what 'mounting' means in our | ||
230 | context. | ||
231 | |||
232 | _instroot would normally be something like | ||
233 | /var/tmp/wic/build/imgcreate-s_9AKQ/install_root, for | ||
234 | installing packages, etc. We don't currently need to do that, | ||
235 | so we simplify life by just using /var/tmp/wic/build as our | ||
236 | workdir. | ||
237 | """ | ||
238 | parts = self._get_parts() | ||
239 | |||
240 | self.__instimage = PartitionedMount(self._instroot) | ||
241 | |||
242 | for p in parts: | ||
243 | # as a convenience, set source to the boot partition source | ||
244 | # instead of forcing it to be set via bootloader --source | ||
245 | if not self.ks.handler.bootloader.source and p.mountpoint == "/boot": | ||
246 | self.ks.handler.bootloader.source = p.source | ||
247 | |||
248 | for p in parts: | ||
249 | # need to create the filesystems in order to get their | ||
250 | # sizes before we can add them and do the layout. | ||
251 | # PartitionedMount.mount() actually calls __format_disks() | ||
252 | # to create the disk images and carve out the partitions, | ||
253 | # then self.install() calls PartitionedMount.install() | ||
254 | # which calls __install_partitition() for each partition | ||
255 | # to dd the fs into the partitions. It would be nice to | ||
256 | # be able to use e.g. ExtDiskMount etc to create the | ||
257 | # filesystems, since that's where existing e.g. mkfs code | ||
258 | # is, but those are only created after __format_disks() | ||
259 | # which needs the partition sizes so needs them created | ||
260 | # before its called. Well, the existing setup is geared | ||
261 | # to installing packages into mounted filesystems - maybe | ||
262 | # when/if we need to actually do package selection we | ||
263 | # should modify things to use those objects, but for now | ||
264 | # we can avoid that. | ||
265 | |||
266 | p.install_pkgs(self, self.workdir, self.oe_builddir, self.rootfs_dir, | ||
267 | self.bootimg_dir, self.kernel_dir, self.native_sysroot) | ||
268 | |||
269 | p.prepare(self, self.workdir, self.oe_builddir, self.rootfs_dir, | ||
270 | self.bootimg_dir, self.kernel_dir, self.native_sysroot) | ||
271 | |||
272 | fstab = self.__write_fstab(p.get_rootfs()) | ||
273 | self._restore_fstab(fstab) | ||
274 | |||
275 | self.__instimage.add_partition(int(p.size), | ||
276 | p.disk, | ||
277 | p.mountpoint, | ||
278 | p.source_file, | ||
279 | p.fstype, | ||
280 | p.label, | ||
281 | fsopts = p.fsopts, | ||
282 | boot = p.active, | ||
283 | align = p.align, | ||
284 | part_type = p.part_type) | ||
285 | self.__instimage.layout_partitions(self._ptable_format) | ||
286 | |||
287 | self.__imgdir = self.workdir | ||
288 | for disk_name, disk in self.__instimage.disks.items(): | ||
289 | full_path = self._full_path(self.__imgdir, disk_name, "direct") | ||
290 | msger.debug("Adding disk %s as %s with size %s bytes" \ | ||
291 | % (disk_name, full_path, disk['min_size'])) | ||
292 | disk_obj = fs_related.DiskImage(full_path, disk['min_size']) | ||
293 | self.__disks[disk_name] = disk_obj | ||
294 | self.__instimage.add_disk(disk_name, disk_obj) | ||
295 | |||
296 | self.__instimage.mount() | ||
297 | |||
298 | def install(self, repo_urls=None): | ||
299 | """ | ||
300 | Install fs images into partitions | ||
301 | """ | ||
302 | for disk_name, disk in self.__instimage.disks.items(): | ||
303 | full_path = self._full_path(self.__imgdir, disk_name, "direct") | ||
304 | msger.debug("Installing disk %s as %s with size %s bytes" \ | ||
305 | % (disk_name, full_path, disk['min_size'])) | ||
306 | self.__instimage.install(full_path) | ||
307 | |||
308 | def configure(self, repodata = None): | ||
309 | """ | ||
310 | Configure the system image according to kickstart. | ||
311 | |||
312 | For now, it just prepares the image to be bootable by e.g. | ||
313 | creating and installing a bootloader configuration. | ||
314 | """ | ||
315 | source_plugin = self.get_default_source_plugin() | ||
316 | if source_plugin: | ||
317 | self._source_methods = pluginmgr.get_source_plugin_methods(source_plugin, disk_methods) | ||
318 | for disk_name, disk in self.__instimage.disks.items(): | ||
319 | self._source_methods["do_install_disk"](disk, disk_name, self, | ||
320 | self.workdir, | ||
321 | self.oe_builddir, | ||
322 | self.bootimg_dir, | ||
323 | self.kernel_dir, | ||
324 | self.native_sysroot) | ||
325 | |||
326 | def print_outimage_info(self): | ||
327 | """ | ||
328 | Print the image(s) and artifacts used, for the user. | ||
329 | """ | ||
330 | msg = "The new image(s) can be found here:\n" | ||
331 | |||
332 | parts = self._get_parts() | ||
333 | |||
334 | for disk_name, disk in self.__instimage.disks.items(): | ||
335 | full_path = self._full_path(self.__imgdir, disk_name, "direct") | ||
336 | msg += ' %s\n\n' % full_path | ||
337 | |||
338 | msg += 'The following build artifacts were used to create the image(s):\n' | ||
339 | for p in parts: | ||
340 | if p.get_rootfs() is None: | ||
341 | continue | ||
342 | if p.mountpoint == '/': | ||
343 | str = ':' | ||
344 | else: | ||
345 | str = '["%s"]:' % p.label | ||
346 | msg += ' ROOTFS_DIR%s%s\n' % (str.ljust(20), p.get_rootfs()) | ||
347 | |||
348 | msg += ' BOOTIMG_DIR: %s\n' % self.bootimg_dir | ||
349 | msg += ' KERNEL_DIR: %s\n' % self.kernel_dir | ||
350 | msg += ' NATIVE_SYSROOT: %s\n' % self.native_sysroot | ||
351 | |||
352 | msger.info(msg) | ||
353 | |||
354 | def _get_boot_config(self): | ||
355 | """ | ||
356 | Return the rootdev/root_part_uuid (if specified by | ||
357 | --part-type) | ||
358 | |||
359 | Assume partition order same as in wks | ||
360 | """ | ||
361 | rootdev = None | ||
362 | root_part_uuid = None | ||
363 | parts = self._get_parts() | ||
364 | for num, p in enumerate(parts, 1): | ||
365 | if p.mountpoint == "/": | ||
366 | part = '' | ||
367 | if p.disk.startswith('mmcblk'): | ||
368 | part = 'p' | ||
369 | |||
370 | if self._ptable_format == 'msdos' and num > 3: | ||
371 | rootdev = "/dev/%s%s%-d" % (p.disk, part, num + 1) | ||
372 | else: | ||
373 | rootdev = "/dev/%s%s%-d" % (p.disk, part, num) | ||
374 | root_part_uuid = p.part_type | ||
375 | |||
376 | return (rootdev, root_part_uuid) | ||
377 | |||
378 | def _unmount_instroot(self): | ||
379 | if not self.__instimage is None: | ||
380 | try: | ||
381 | self.__instimage.cleanup() | ||
382 | except MountError, err: | ||
383 | msger.warning("%s" % err) | ||
384 | |||
diff --git a/scripts/lib/mic/imager/fs.py b/scripts/lib/mic/imager/fs.py new file mode 100644 index 0000000000..d53b29cb47 --- /dev/null +++ b/scripts/lib/mic/imager/fs.py | |||
@@ -0,0 +1,99 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | |||
20 | from mic import msger | ||
21 | from mic.utils import runner, misc | ||
22 | from mic.utils.errors import CreatorError | ||
23 | from mic.utils.fs_related import find_binary_path | ||
24 | from mic.imager.baseimager import BaseImageCreator | ||
25 | |||
26 | class FsImageCreator(BaseImageCreator): | ||
27 | def __init__(self, cfgmgr = None, pkgmgr = None): | ||
28 | self.zips = { | ||
29 | "tar.bz2" : "" | ||
30 | } | ||
31 | BaseImageCreator.__init__(self, cfgmgr, pkgmgr) | ||
32 | self._fstype = None | ||
33 | self._fsopts = None | ||
34 | self._include_src = False | ||
35 | |||
36 | def package(self, destdir = "."): | ||
37 | |||
38 | ignores = ["/dev/fd", | ||
39 | "/dev/stdin", | ||
40 | "/dev/stdout", | ||
41 | "/dev/stderr", | ||
42 | "/etc/mtab"] | ||
43 | |||
44 | if not os.path.exists(destdir): | ||
45 | os.makedirs(destdir) | ||
46 | |||
47 | if self._recording_pkgs: | ||
48 | self._save_recording_pkgs(destdir) | ||
49 | |||
50 | if not self.pack_to: | ||
51 | fsdir = os.path.join(destdir, self.name) | ||
52 | |||
53 | misc.check_space_pre_cp(self._instroot, destdir) | ||
54 | msger.info("Copying %s to %s ..." % (self._instroot, fsdir)) | ||
55 | runner.show(['cp', "-af", self._instroot, fsdir]) | ||
56 | |||
57 | for exclude in ignores: | ||
58 | if os.path.exists(fsdir + exclude): | ||
59 | os.unlink(fsdir + exclude) | ||
60 | |||
61 | self.outimage.append(fsdir) | ||
62 | |||
63 | else: | ||
64 | (tar, comp) = os.path.splitext(self.pack_to) | ||
65 | try: | ||
66 | tarcreat = {'.tar': '-cf', | ||
67 | '.gz': '-czf', | ||
68 | '.bz2': '-cjf', | ||
69 | '.tgz': '-czf', | ||
70 | '.tbz': '-cjf'}[comp] | ||
71 | except KeyError: | ||
72 | raise CreatorError("Unsupported comression for this image type:" | ||
73 | " '%s', try '.tar', '.tar.gz', etc" % comp) | ||
74 | |||
75 | dst = os.path.join(destdir, self.pack_to) | ||
76 | msger.info("Pack rootfs to %s. Please wait..." % dst) | ||
77 | |||
78 | tar = find_binary_path('tar') | ||
79 | tar_cmdline = [tar, "--numeric-owner", | ||
80 | "--preserve-permissions", | ||
81 | "--preserve-order", | ||
82 | "--one-file-system", | ||
83 | "--directory", | ||
84 | self._instroot] | ||
85 | for ignore_entry in ignores: | ||
86 | if ignore_entry.startswith('/'): | ||
87 | ignore_entry = ignore_entry[1:] | ||
88 | |||
89 | tar_cmdline.append("--exclude=%s" % (ignore_entry)) | ||
90 | |||
91 | tar_cmdline.extend([tarcreat, dst, "."]) | ||
92 | |||
93 | rc = runner.show(tar_cmdline) | ||
94 | if rc: | ||
95 | raise CreatorError("Failed compress image with tar.bz2. " | ||
96 | "Cmdline: %s" % (" ".join(tar_cmdline))) | ||
97 | |||
98 | self.outimage.append(dst) | ||
99 | |||
diff --git a/scripts/lib/mic/imager/livecd.py b/scripts/lib/mic/imager/livecd.py new file mode 100644 index 0000000000..e36f4a76c6 --- /dev/null +++ b/scripts/lib/mic/imager/livecd.py | |||
@@ -0,0 +1,750 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os, sys | ||
19 | import glob | ||
20 | import shutil | ||
21 | |||
22 | from mic import kickstart, msger | ||
23 | from mic.utils import fs_related, runner, misc | ||
24 | from mic.utils.errors import CreatorError | ||
25 | from mic.imager.loop import LoopImageCreator | ||
26 | |||
27 | |||
28 | class LiveImageCreatorBase(LoopImageCreator): | ||
29 | """A base class for LiveCD image creators. | ||
30 | |||
31 | This class serves as a base class for the architecture-specific LiveCD | ||
32 | image creator subclass, LiveImageCreator. | ||
33 | |||
34 | LiveImageCreator creates a bootable ISO containing the system image, | ||
35 | bootloader, bootloader configuration, kernel and initramfs. | ||
36 | """ | ||
37 | |||
38 | def __init__(self, creatoropts = None, pkgmgr = None): | ||
39 | """Initialise a LiveImageCreator instance. | ||
40 | |||
41 | This method takes the same arguments as ImageCreator.__init__(). | ||
42 | """ | ||
43 | LoopImageCreator.__init__(self, creatoropts, pkgmgr) | ||
44 | |||
45 | #Controls whether to use squashfs to compress the image. | ||
46 | self.skip_compression = False | ||
47 | |||
48 | #Controls whether an image minimizing snapshot should be created. | ||
49 | # | ||
50 | #This snapshot can be used when copying the system image from the ISO in | ||
51 | #order to minimize the amount of data that needs to be copied; simply, | ||
52 | #it makes it possible to create a version of the image's filesystem with | ||
53 | #no spare space. | ||
54 | self.skip_minimize = False | ||
55 | |||
56 | #A flag which indicates i act as a convertor default false | ||
57 | self.actasconvertor = False | ||
58 | |||
59 | #The bootloader timeout from kickstart. | ||
60 | if self.ks: | ||
61 | self._timeout = kickstart.get_timeout(self.ks, 10) | ||
62 | else: | ||
63 | self._timeout = 10 | ||
64 | |||
65 | #The default kernel type from kickstart. | ||
66 | if self.ks: | ||
67 | self._default_kernel = kickstart.get_default_kernel(self.ks, | ||
68 | "kernel") | ||
69 | else: | ||
70 | self._default_kernel = None | ||
71 | |||
72 | if self.ks: | ||
73 | parts = kickstart.get_partitions(self.ks) | ||
74 | if len(parts) > 1: | ||
75 | raise CreatorError("Can't support multi partitions in ks file " | ||
76 | "for this image type") | ||
77 | # FIXME: rename rootfs img to self.name, | ||
78 | # else can't find files when create iso | ||
79 | self._instloops[0]['name'] = self.name + ".img" | ||
80 | |||
81 | self.__isodir = None | ||
82 | |||
83 | self.__modules = ["=ata", | ||
84 | "sym53c8xx", | ||
85 | "aic7xxx", | ||
86 | "=usb", | ||
87 | "=firewire", | ||
88 | "=mmc", | ||
89 | "=pcmcia", | ||
90 | "mptsas"] | ||
91 | if self.ks: | ||
92 | self.__modules.extend(kickstart.get_modules(self.ks)) | ||
93 | |||
94 | self._dep_checks.extend(["isohybrid", | ||
95 | "unsquashfs", | ||
96 | "mksquashfs", | ||
97 | "dd", | ||
98 | "genisoimage"]) | ||
99 | |||
100 | # | ||
101 | # Hooks for subclasses | ||
102 | # | ||
103 | def _configure_bootloader(self, isodir): | ||
104 | """Create the architecture specific booloader configuration. | ||
105 | |||
106 | This is the hook where subclasses must create the booloader | ||
107 | configuration in order to allow a bootable ISO to be built. | ||
108 | |||
109 | isodir -- the directory where the contents of the ISO are to | ||
110 | be staged | ||
111 | """ | ||
112 | raise CreatorError("Bootloader configuration is arch-specific, " | ||
113 | "but not implemented for this arch!") | ||
114 | def _get_menu_options(self): | ||
115 | """Return a menu options string for syslinux configuration. | ||
116 | """ | ||
117 | if self.ks is None: | ||
118 | return "liveinst autoinst" | ||
119 | r = kickstart.get_menu_args(self.ks) | ||
120 | return r | ||
121 | |||
122 | def _get_kernel_options(self): | ||
123 | """Return a kernel options string for bootloader configuration. | ||
124 | |||
125 | This is the hook where subclasses may specify a set of kernel | ||
126 | options which should be included in the images bootloader | ||
127 | configuration. | ||
128 | |||
129 | A sensible default implementation is provided. | ||
130 | """ | ||
131 | |||
132 | if self.ks is None: | ||
133 | r = "ro rd.live.image" | ||
134 | else: | ||
135 | r = kickstart.get_kernel_args(self.ks) | ||
136 | |||
137 | return r | ||
138 | |||
139 | def _get_mkisofs_options(self, isodir): | ||
140 | """Return the architecture specific mkisosfs options. | ||
141 | |||
142 | This is the hook where subclasses may specify additional arguments | ||
143 | to mkisofs, e.g. to enable a bootable ISO to be built. | ||
144 | |||
145 | By default, an empty list is returned. | ||
146 | """ | ||
147 | return [] | ||
148 | |||
149 | # | ||
150 | # Helpers for subclasses | ||
151 | # | ||
152 | def _has_checkisomd5(self): | ||
153 | """Check whether checkisomd5 is available in the install root.""" | ||
154 | def _exists(path): | ||
155 | return os.path.exists(self._instroot + path) | ||
156 | |||
157 | if _exists("/usr/bin/checkisomd5") and os.path.exists("/usr/bin/implantisomd5"): | ||
158 | return True | ||
159 | |||
160 | return False | ||
161 | |||
162 | def __restore_file(self,path): | ||
163 | try: | ||
164 | os.unlink(path) | ||
165 | except: | ||
166 | pass | ||
167 | if os.path.exists(path + '.rpmnew'): | ||
168 | os.rename(path + '.rpmnew', path) | ||
169 | |||
170 | def _mount_instroot(self, base_on = None): | ||
171 | LoopImageCreator._mount_instroot(self, base_on) | ||
172 | self.__write_initrd_conf(self._instroot + "/etc/sysconfig/mkinitrd") | ||
173 | self.__write_dracut_conf(self._instroot + "/etc/dracut.conf.d/02livecd.conf") | ||
174 | |||
175 | def _unmount_instroot(self): | ||
176 | self.__restore_file(self._instroot + "/etc/sysconfig/mkinitrd") | ||
177 | self.__restore_file(self._instroot + "/etc/dracut.conf.d/02livecd.conf") | ||
178 | LoopImageCreator._unmount_instroot(self) | ||
179 | |||
180 | def __ensure_isodir(self): | ||
181 | if self.__isodir is None: | ||
182 | self.__isodir = self._mkdtemp("iso-") | ||
183 | return self.__isodir | ||
184 | |||
185 | def _get_isodir(self): | ||
186 | return self.__ensure_isodir() | ||
187 | |||
188 | def _set_isodir(self, isodir = None): | ||
189 | self.__isodir = isodir | ||
190 | |||
191 | def _create_bootconfig(self): | ||
192 | """Configure the image so that it's bootable.""" | ||
193 | self._configure_bootloader(self.__ensure_isodir()) | ||
194 | |||
195 | def _get_post_scripts_env(self, in_chroot): | ||
196 | env = LoopImageCreator._get_post_scripts_env(self, in_chroot) | ||
197 | |||
198 | if not in_chroot: | ||
199 | env["LIVE_ROOT"] = self.__ensure_isodir() | ||
200 | |||
201 | return env | ||
202 | def __write_dracut_conf(self, path): | ||
203 | if not os.path.exists(os.path.dirname(path)): | ||
204 | fs_related.makedirs(os.path.dirname(path)) | ||
205 | f = open(path, "a") | ||
206 | f.write('add_dracutmodules+=" dmsquash-live pollcdrom "') | ||
207 | f.close() | ||
208 | |||
209 | def __write_initrd_conf(self, path): | ||
210 | content = "" | ||
211 | if not os.path.exists(os.path.dirname(path)): | ||
212 | fs_related.makedirs(os.path.dirname(path)) | ||
213 | f = open(path, "w") | ||
214 | |||
215 | content += 'LIVEOS="yes"\n' | ||
216 | content += 'PROBE="no"\n' | ||
217 | content += 'MODULES+="squashfs ext3 ext2 vfat msdos "\n' | ||
218 | content += 'MODULES+="sr_mod sd_mod ide-cd cdrom "\n' | ||
219 | |||
220 | for module in self.__modules: | ||
221 | if module == "=usb": | ||
222 | content += 'MODULES+="ehci_hcd uhci_hcd ohci_hcd "\n' | ||
223 | content += 'MODULES+="usb_storage usbhid "\n' | ||
224 | elif module == "=firewire": | ||
225 | content += 'MODULES+="firewire-sbp2 firewire-ohci "\n' | ||
226 | content += 'MODULES+="sbp2 ohci1394 ieee1394 "\n' | ||
227 | elif module == "=mmc": | ||
228 | content += 'MODULES+="mmc_block sdhci sdhci-pci "\n' | ||
229 | elif module == "=pcmcia": | ||
230 | content += 'MODULES+="pata_pcmcia "\n' | ||
231 | else: | ||
232 | content += 'MODULES+="' + module + ' "\n' | ||
233 | f.write(content) | ||
234 | f.close() | ||
235 | |||
236 | def __create_iso(self, isodir): | ||
237 | iso = self._outdir + "/" + self.name + ".iso" | ||
238 | genisoimage = fs_related.find_binary_path("genisoimage") | ||
239 | args = [genisoimage, | ||
240 | "-J", "-r", | ||
241 | "-hide-rr-moved", "-hide-joliet-trans-tbl", | ||
242 | "-V", self.fslabel, | ||
243 | "-o", iso] | ||
244 | |||
245 | args.extend(self._get_mkisofs_options(isodir)) | ||
246 | |||
247 | args.append(isodir) | ||
248 | |||
249 | if runner.show(args) != 0: | ||
250 | raise CreatorError("ISO creation failed!") | ||
251 | |||
252 | """ It should be ok still even if you haven't isohybrid """ | ||
253 | isohybrid = None | ||
254 | try: | ||
255 | isohybrid = fs_related.find_binary_path("isohybrid") | ||
256 | except: | ||
257 | pass | ||
258 | |||
259 | if isohybrid: | ||
260 | args = [isohybrid, "-partok", iso ] | ||
261 | if runner.show(args) != 0: | ||
262 | raise CreatorError("Hybrid ISO creation failed!") | ||
263 | |||
264 | self.__implant_md5sum(iso) | ||
265 | |||
266 | def __implant_md5sum(self, iso): | ||
267 | """Implant an isomd5sum.""" | ||
268 | if os.path.exists("/usr/bin/implantisomd5"): | ||
269 | implantisomd5 = "/usr/bin/implantisomd5" | ||
270 | else: | ||
271 | msger.warning("isomd5sum not installed; not setting up mediacheck") | ||
272 | implantisomd5 = "" | ||
273 | return | ||
274 | |||
275 | runner.show([implantisomd5, iso]) | ||
276 | |||
277 | def _stage_final_image(self): | ||
278 | try: | ||
279 | fs_related.makedirs(self.__ensure_isodir() + "/LiveOS") | ||
280 | |||
281 | minimal_size = self._resparse() | ||
282 | |||
283 | if not self.skip_minimize: | ||
284 | fs_related.create_image_minimizer(self.__isodir + \ | ||
285 | "/LiveOS/osmin.img", | ||
286 | self._image, | ||
287 | minimal_size) | ||
288 | |||
289 | if self.skip_compression: | ||
290 | shutil.move(self._image, self.__isodir + "/LiveOS/ext3fs.img") | ||
291 | else: | ||
292 | fs_related.makedirs(os.path.join( | ||
293 | os.path.dirname(self._image), | ||
294 | "LiveOS")) | ||
295 | shutil.move(self._image, | ||
296 | os.path.join(os.path.dirname(self._image), | ||
297 | "LiveOS", "ext3fs.img")) | ||
298 | fs_related.mksquashfs(os.path.dirname(self._image), | ||
299 | self.__isodir + "/LiveOS/squashfs.img") | ||
300 | |||
301 | self.__create_iso(self.__isodir) | ||
302 | |||
303 | if self.pack_to: | ||
304 | isoimg = os.path.join(self._outdir, self.name + ".iso") | ||
305 | packimg = os.path.join(self._outdir, self.pack_to) | ||
306 | misc.packing(packimg, isoimg) | ||
307 | os.unlink(isoimg) | ||
308 | |||
309 | finally: | ||
310 | shutil.rmtree(self.__isodir, ignore_errors = True) | ||
311 | self.__isodir = None | ||
312 | |||
313 | class x86LiveImageCreator(LiveImageCreatorBase): | ||
314 | """ImageCreator for x86 machines""" | ||
315 | def _get_mkisofs_options(self, isodir): | ||
316 | return [ "-b", "isolinux/isolinux.bin", | ||
317 | "-c", "isolinux/boot.cat", | ||
318 | "-no-emul-boot", "-boot-info-table", | ||
319 | "-boot-load-size", "4" ] | ||
320 | |||
321 | def _get_required_packages(self): | ||
322 | return ["syslinux", "syslinux-extlinux"] + \ | ||
323 | LiveImageCreatorBase._get_required_packages(self) | ||
324 | |||
325 | def _get_isolinux_stanzas(self, isodir): | ||
326 | return "" | ||
327 | |||
328 | def __find_syslinux_menu(self): | ||
329 | for menu in ["vesamenu.c32", "menu.c32"]: | ||
330 | if os.path.isfile(self._instroot + "/usr/share/syslinux/" + menu): | ||
331 | return menu | ||
332 | |||
333 | raise CreatorError("syslinux not installed : " | ||
334 | "no suitable /usr/share/syslinux/*menu.c32 found") | ||
335 | |||
336 | def __find_syslinux_mboot(self): | ||
337 | # | ||
338 | # We only need the mboot module if we have any xen hypervisors | ||
339 | # | ||
340 | if not glob.glob(self._instroot + "/boot/xen.gz*"): | ||
341 | return None | ||
342 | |||
343 | return "mboot.c32" | ||
344 | |||
345 | def __copy_syslinux_files(self, isodir, menu, mboot = None): | ||
346 | files = ["isolinux.bin", menu] | ||
347 | if mboot: | ||
348 | files += [mboot] | ||
349 | |||
350 | for f in files: | ||
351 | path = self._instroot + "/usr/share/syslinux/" + f | ||
352 | |||
353 | if not os.path.isfile(path): | ||
354 | raise CreatorError("syslinux not installed : " | ||
355 | "%s not found" % path) | ||
356 | |||
357 | shutil.copy(path, isodir + "/isolinux/") | ||
358 | |||
359 | def __copy_syslinux_background(self, isodest): | ||
360 | background_path = self._instroot + \ | ||
361 | "/usr/share/branding/default/syslinux/syslinux-vesa-splash.jpg" | ||
362 | |||
363 | if not os.path.exists(background_path): | ||
364 | return False | ||
365 | |||
366 | shutil.copyfile(background_path, isodest) | ||
367 | |||
368 | return True | ||
369 | |||
370 | def __copy_kernel_and_initramfs(self, isodir, version, index): | ||
371 | bootdir = self._instroot + "/boot" | ||
372 | isDracut = False | ||
373 | |||
374 | if self._alt_initrd_name: | ||
375 | src_initrd_path = os.path.join(bootdir, self._alt_initrd_name) | ||
376 | else: | ||
377 | if os.path.exists(bootdir + "/initramfs-" + version + ".img"): | ||
378 | src_initrd_path = os.path.join(bootdir, "initramfs-" +version+ ".img") | ||
379 | isDracut = True | ||
380 | else: | ||
381 | src_initrd_path = os.path.join(bootdir, "initrd-" +version+ ".img") | ||
382 | |||
383 | try: | ||
384 | msger.debug("copy %s to %s" % (bootdir + "/vmlinuz-" + version, isodir + "/isolinux/vmlinuz" + index)) | ||
385 | shutil.copyfile(bootdir + "/vmlinuz-" + version, | ||
386 | isodir + "/isolinux/vmlinuz" + index) | ||
387 | |||
388 | msger.debug("copy %s to %s" % (src_initrd_path, isodir + "/isolinux/initrd" + index + ".img")) | ||
389 | shutil.copyfile(src_initrd_path, | ||
390 | isodir + "/isolinux/initrd" + index + ".img") | ||
391 | except: | ||
392 | raise CreatorError("Unable to copy valid kernels or initrds, " | ||
393 | "please check the repo.") | ||
394 | |||
395 | is_xen = False | ||
396 | if os.path.exists(bootdir + "/xen.gz-" + version[:-3]): | ||
397 | shutil.copyfile(bootdir + "/xen.gz-" + version[:-3], | ||
398 | isodir + "/isolinux/xen" + index + ".gz") | ||
399 | is_xen = True | ||
400 | |||
401 | return (is_xen,isDracut) | ||
402 | |||
403 | def __is_default_kernel(self, kernel, kernels): | ||
404 | if len(kernels) == 1: | ||
405 | return True | ||
406 | |||
407 | if kernel == self._default_kernel: | ||
408 | return True | ||
409 | |||
410 | if kernel.startswith("kernel-") and kernel[7:] == self._default_kernel: | ||
411 | return True | ||
412 | |||
413 | return False | ||
414 | |||
415 | def __get_basic_syslinux_config(self, **args): | ||
416 | return """ | ||
417 | default %(menu)s | ||
418 | timeout %(timeout)d | ||
419 | |||
420 | %(background)s | ||
421 | menu title Welcome to %(distroname)s! | ||
422 | menu color border 0 #ffffffff #00000000 | ||
423 | menu color sel 7 #ff000000 #ffffffff | ||
424 | menu color title 0 #ffffffff #00000000 | ||
425 | menu color tabmsg 0 #ffffffff #00000000 | ||
426 | menu color unsel 0 #ffffffff #00000000 | ||
427 | menu color hotsel 0 #ff000000 #ffffffff | ||
428 | menu color hotkey 7 #ffffffff #ff000000 | ||
429 | menu color timeout_msg 0 #ffffffff #00000000 | ||
430 | menu color timeout 0 #ffffffff #00000000 | ||
431 | menu color cmdline 0 #ffffffff #00000000 | ||
432 | menu hidden | ||
433 | menu clear | ||
434 | """ % args | ||
435 | |||
436 | def __get_image_stanza(self, is_xen, isDracut, **args): | ||
437 | if isDracut: | ||
438 | args["rootlabel"] = "live:CDLABEL=%(fslabel)s" % args | ||
439 | else: | ||
440 | args["rootlabel"] = "CDLABEL=%(fslabel)s" % args | ||
441 | if not is_xen: | ||
442 | template = """label %(short)s | ||
443 | menu label %(long)s | ||
444 | kernel vmlinuz%(index)s | ||
445 | append initrd=initrd%(index)s.img root=%(rootlabel)s rootfstype=iso9660 %(liveargs)s %(extra)s | ||
446 | """ | ||
447 | else: | ||
448 | template = """label %(short)s | ||
449 | menu label %(long)s | ||
450 | kernel mboot.c32 | ||
451 | append xen%(index)s.gz --- vmlinuz%(index)s root=%(rootlabel)s rootfstype=iso9660 %(liveargs)s %(extra)s --- initrd%(index)s.img | ||
452 | """ | ||
453 | return template % args | ||
454 | |||
455 | def __get_image_stanzas(self, isodir): | ||
456 | versions = [] | ||
457 | kernels = self._get_kernel_versions() | ||
458 | for kernel in kernels: | ||
459 | for version in kernels[kernel]: | ||
460 | versions.append(version) | ||
461 | |||
462 | if not versions: | ||
463 | raise CreatorError("Unable to find valid kernels, " | ||
464 | "please check the repo") | ||
465 | |||
466 | kernel_options = self._get_kernel_options() | ||
467 | |||
468 | """ menu can be customized highly, the format is: | ||
469 | |||
470 | short_name1:long_name1:extra_opts1;short_name2:long_name2:extra_opts2 | ||
471 | |||
472 | e.g.: autoinst:InstallationOnly:systemd.unit=installer-graphical.service | ||
473 | but in order to keep compatible with old format, these are still ok: | ||
474 | |||
475 | liveinst autoinst | ||
476 | liveinst;autoinst | ||
477 | liveinst::;autoinst:: | ||
478 | """ | ||
479 | oldmenus = {"basic": { | ||
480 | "short": "basic", | ||
481 | "long": "Installation Only (Text based)", | ||
482 | "extra": "basic nosplash 4" | ||
483 | }, | ||
484 | "liveinst": { | ||
485 | "short": "liveinst", | ||
486 | "long": "Installation Only", | ||
487 | "extra": "liveinst nosplash 4" | ||
488 | }, | ||
489 | "autoinst": { | ||
490 | "short": "autoinst", | ||
491 | "long": "Autoinstall (Deletes all existing content)", | ||
492 | "extra": "autoinst nosplash 4" | ||
493 | }, | ||
494 | "netinst": { | ||
495 | "short": "netinst", | ||
496 | "long": "Network Installation", | ||
497 | "extra": "netinst 4" | ||
498 | }, | ||
499 | "verify": { | ||
500 | "short": "check", | ||
501 | "long": "Verify and", | ||
502 | "extra": "check" | ||
503 | } | ||
504 | } | ||
505 | menu_options = self._get_menu_options() | ||
506 | menus = menu_options.split(";") | ||
507 | for i in range(len(menus)): | ||
508 | menus[i] = menus[i].split(":") | ||
509 | if len(menus) == 1 and len(menus[0]) == 1: | ||
510 | """ Keep compatible with the old usage way """ | ||
511 | menus = menu_options.split() | ||
512 | for i in range(len(menus)): | ||
513 | menus[i] = [menus[i]] | ||
514 | |||
515 | cfg = "" | ||
516 | |||
517 | default_version = None | ||
518 | default_index = None | ||
519 | index = "0" | ||
520 | netinst = None | ||
521 | for version in versions: | ||
522 | (is_xen, isDracut) = self.__copy_kernel_and_initramfs(isodir, version, index) | ||
523 | if index == "0": | ||
524 | self._isDracut = isDracut | ||
525 | |||
526 | default = self.__is_default_kernel(kernel, kernels) | ||
527 | |||
528 | if default: | ||
529 | long = "Boot %s" % self.distro_name | ||
530 | elif kernel.startswith("kernel-"): | ||
531 | long = "Boot %s(%s)" % (self.name, kernel[7:]) | ||
532 | else: | ||
533 | long = "Boot %s(%s)" % (self.name, kernel) | ||
534 | |||
535 | oldmenus["verify"]["long"] = "%s %s" % (oldmenus["verify"]["long"], | ||
536 | long) | ||
537 | # tell dracut not to ask for LUKS passwords or activate mdraid sets | ||
538 | if isDracut: | ||
539 | kern_opts = kernel_options + " rd.luks=0 rd.md=0 rd.dm=0" | ||
540 | else: | ||
541 | kern_opts = kernel_options | ||
542 | |||
543 | cfg += self.__get_image_stanza(is_xen, isDracut, | ||
544 | fslabel = self.fslabel, | ||
545 | liveargs = kern_opts, | ||
546 | long = long, | ||
547 | short = "linux" + index, | ||
548 | extra = "", | ||
549 | index = index) | ||
550 | |||
551 | if default: | ||
552 | cfg += "menu default\n" | ||
553 | default_version = version | ||
554 | default_index = index | ||
555 | |||
556 | for menu in menus: | ||
557 | if not menu[0]: | ||
558 | continue | ||
559 | short = menu[0] + index | ||
560 | |||
561 | if len(menu) >= 2: | ||
562 | long = menu[1] | ||
563 | else: | ||
564 | if menu[0] in oldmenus.keys(): | ||
565 | if menu[0] == "verify" and not self._has_checkisomd5(): | ||
566 | continue | ||
567 | if menu[0] == "netinst": | ||
568 | netinst = oldmenus[menu[0]] | ||
569 | continue | ||
570 | long = oldmenus[menu[0]]["long"] | ||
571 | extra = oldmenus[menu[0]]["extra"] | ||
572 | else: | ||
573 | long = short.upper() + " X" + index | ||
574 | extra = "" | ||
575 | |||
576 | if len(menu) >= 3: | ||
577 | extra = menu[2] | ||
578 | |||
579 | cfg += self.__get_image_stanza(is_xen, isDracut, | ||
580 | fslabel = self.fslabel, | ||
581 | liveargs = kernel_options, | ||
582 | long = long, | ||
583 | short = short, | ||
584 | extra = extra, | ||
585 | index = index) | ||
586 | |||
587 | index = str(int(index) + 1) | ||
588 | |||
589 | if not default_version: | ||
590 | default_version = versions[0] | ||
591 | if not default_index: | ||
592 | default_index = "0" | ||
593 | |||
594 | if netinst: | ||
595 | cfg += self.__get_image_stanza(is_xen, isDracut, | ||
596 | fslabel = self.fslabel, | ||
597 | liveargs = kernel_options, | ||
598 | long = netinst["long"], | ||
599 | short = netinst["short"], | ||
600 | extra = netinst["extra"], | ||
601 | index = default_index) | ||
602 | |||
603 | return cfg | ||
604 | |||
605 | def __get_memtest_stanza(self, isodir): | ||
606 | memtest = glob.glob(self._instroot + "/boot/memtest86*") | ||
607 | if not memtest: | ||
608 | return "" | ||
609 | |||
610 | shutil.copyfile(memtest[0], isodir + "/isolinux/memtest") | ||
611 | |||
612 | return """label memtest | ||
613 | menu label Memory Test | ||
614 | kernel memtest | ||
615 | """ | ||
616 | |||
617 | def __get_local_stanza(self, isodir): | ||
618 | return """label local | ||
619 | menu label Boot from local drive | ||
620 | localboot 0xffff | ||
621 | """ | ||
622 | |||
623 | def _configure_syslinux_bootloader(self, isodir): | ||
624 | """configure the boot loader""" | ||
625 | fs_related.makedirs(isodir + "/isolinux") | ||
626 | |||
627 | menu = self.__find_syslinux_menu() | ||
628 | |||
629 | self.__copy_syslinux_files(isodir, menu, | ||
630 | self.__find_syslinux_mboot()) | ||
631 | |||
632 | background = "" | ||
633 | if self.__copy_syslinux_background(isodir + "/isolinux/splash.jpg"): | ||
634 | background = "menu background splash.jpg" | ||
635 | |||
636 | cfg = self.__get_basic_syslinux_config(menu = menu, | ||
637 | background = background, | ||
638 | name = self.name, | ||
639 | timeout = self._timeout * 10, | ||
640 | distroname = self.distro_name) | ||
641 | |||
642 | cfg += self.__get_image_stanzas(isodir) | ||
643 | cfg += self.__get_memtest_stanza(isodir) | ||
644 | cfg += self.__get_local_stanza(isodir) | ||
645 | cfg += self._get_isolinux_stanzas(isodir) | ||
646 | |||
647 | cfgf = open(isodir + "/isolinux/isolinux.cfg", "w") | ||
648 | cfgf.write(cfg) | ||
649 | cfgf.close() | ||
650 | |||
651 | def __copy_efi_files(self, isodir): | ||
652 | if not os.path.exists(self._instroot + "/boot/efi/EFI/redhat/grub.efi"): | ||
653 | return False | ||
654 | shutil.copy(self._instroot + "/boot/efi/EFI/redhat/grub.efi", | ||
655 | isodir + "/EFI/boot/grub.efi") | ||
656 | shutil.copy(self._instroot + "/boot/grub/splash.xpm.gz", | ||
657 | isodir + "/EFI/boot/splash.xpm.gz") | ||
658 | |||
659 | return True | ||
660 | |||
661 | def __get_basic_efi_config(self, **args): | ||
662 | return """ | ||
663 | default=0 | ||
664 | splashimage=/EFI/boot/splash.xpm.gz | ||
665 | timeout %(timeout)d | ||
666 | hiddenmenu | ||
667 | |||
668 | """ %args | ||
669 | |||
670 | def __get_efi_image_stanza(self, **args): | ||
671 | return """title %(long)s | ||
672 | kernel /EFI/boot/vmlinuz%(index)s root=CDLABEL=%(fslabel)s rootfstype=iso9660 %(liveargs)s %(extra)s | ||
673 | initrd /EFI/boot/initrd%(index)s.img | ||
674 | """ %args | ||
675 | |||
676 | def __get_efi_image_stanzas(self, isodir, name): | ||
677 | # FIXME: this only supports one kernel right now... | ||
678 | |||
679 | kernel_options = self._get_kernel_options() | ||
680 | checkisomd5 = self._has_checkisomd5() | ||
681 | |||
682 | cfg = "" | ||
683 | |||
684 | for index in range(0, 9): | ||
685 | # we don't support xen kernels | ||
686 | if os.path.exists("%s/EFI/boot/xen%d.gz" %(isodir, index)): | ||
687 | continue | ||
688 | cfg += self.__get_efi_image_stanza(fslabel = self.fslabel, | ||
689 | liveargs = kernel_options, | ||
690 | long = name, | ||
691 | extra = "", index = index) | ||
692 | if checkisomd5: | ||
693 | cfg += self.__get_efi_image_stanza( | ||
694 | fslabel = self.fslabel, | ||
695 | liveargs = kernel_options, | ||
696 | long = "Verify and Boot " + name, | ||
697 | extra = "check", | ||
698 | index = index) | ||
699 | break | ||
700 | |||
701 | return cfg | ||
702 | |||
703 | def _configure_efi_bootloader(self, isodir): | ||
704 | """Set up the configuration for an EFI bootloader""" | ||
705 | fs_related.makedirs(isodir + "/EFI/boot") | ||
706 | |||
707 | if not self.__copy_efi_files(isodir): | ||
708 | shutil.rmtree(isodir + "/EFI") | ||
709 | return | ||
710 | |||
711 | for f in os.listdir(isodir + "/isolinux"): | ||
712 | os.link("%s/isolinux/%s" %(isodir, f), | ||
713 | "%s/EFI/boot/%s" %(isodir, f)) | ||
714 | |||
715 | |||
716 | cfg = self.__get_basic_efi_config(name = self.name, | ||
717 | timeout = self._timeout) | ||
718 | cfg += self.__get_efi_image_stanzas(isodir, self.name) | ||
719 | |||
720 | cfgf = open(isodir + "/EFI/boot/grub.conf", "w") | ||
721 | cfgf.write(cfg) | ||
722 | cfgf.close() | ||
723 | |||
724 | # first gen mactel machines get the bootloader name wrong apparently | ||
725 | if rpmmisc.getBaseArch() == "i386": | ||
726 | os.link(isodir + "/EFI/boot/grub.efi", | ||
727 | isodir + "/EFI/boot/boot.efi") | ||
728 | os.link(isodir + "/EFI/boot/grub.conf", | ||
729 | isodir + "/EFI/boot/boot.conf") | ||
730 | |||
731 | # for most things, we want them named boot$efiarch | ||
732 | efiarch = {"i386": "ia32", "x86_64": "x64"} | ||
733 | efiname = efiarch[rpmmisc.getBaseArch()] | ||
734 | os.rename(isodir + "/EFI/boot/grub.efi", | ||
735 | isodir + "/EFI/boot/boot%s.efi" %(efiname,)) | ||
736 | os.link(isodir + "/EFI/boot/grub.conf", | ||
737 | isodir + "/EFI/boot/boot%s.conf" %(efiname,)) | ||
738 | |||
739 | |||
740 | def _configure_bootloader(self, isodir): | ||
741 | self._configure_syslinux_bootloader(isodir) | ||
742 | self._configure_efi_bootloader(isodir) | ||
743 | |||
744 | arch = "i386" | ||
745 | if arch in ("i386", "x86_64"): | ||
746 | LiveCDImageCreator = x86LiveImageCreator | ||
747 | elif arch.startswith("arm"): | ||
748 | LiveCDImageCreator = LiveImageCreatorBase | ||
749 | else: | ||
750 | raise CreatorError("Architecture not supported!") | ||
diff --git a/scripts/lib/mic/imager/liveusb.py b/scripts/lib/mic/imager/liveusb.py new file mode 100644 index 0000000000..a909928a4c --- /dev/null +++ b/scripts/lib/mic/imager/liveusb.py | |||
@@ -0,0 +1,308 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import shutil | ||
20 | import re | ||
21 | |||
22 | from mic import msger | ||
23 | from mic.utils import misc, fs_related, runner | ||
24 | from mic.utils.errors import CreatorError, MountError | ||
25 | from mic.utils.partitionedfs import PartitionedMount | ||
26 | from mic.imager.livecd import LiveCDImageCreator | ||
27 | |||
28 | |||
29 | class LiveUSBImageCreator(LiveCDImageCreator): | ||
30 | def __init__(self, *args): | ||
31 | LiveCDImageCreator.__init__(self, *args) | ||
32 | |||
33 | self._dep_checks.extend(["kpartx", "parted"]) | ||
34 | |||
35 | # remove dependency of genisoimage in parent class | ||
36 | if "genisoimage" in self._dep_checks: | ||
37 | self._dep_checks.remove("genisoimage") | ||
38 | |||
39 | def _create_usbimg(self, isodir): | ||
40 | overlaysizemb = 64 #default | ||
41 | #skipcompress = self.skip_compression? | ||
42 | fstype = "vfat" | ||
43 | homesizemb=0 | ||
44 | swapsizemb=0 | ||
45 | homefile="home.img" | ||
46 | plussize=128 | ||
47 | kernelargs=None | ||
48 | |||
49 | if fstype == 'vfat': | ||
50 | if overlaysizemb > 2047: | ||
51 | raise CreatorError("Can't have an overlay of 2048MB or " | ||
52 | "greater on VFAT") | ||
53 | |||
54 | if homesizemb > 2047: | ||
55 | raise CreatorError("Can't have an home overlay of 2048MB or " | ||
56 | "greater on VFAT") | ||
57 | |||
58 | if swapsizemb > 2047: | ||
59 | raise CreatorError("Can't have an swap overlay of 2048MB or " | ||
60 | "greater on VFAT") | ||
61 | |||
62 | livesize = misc.get_file_size(isodir + "/LiveOS") | ||
63 | |||
64 | usbimgsize = (overlaysizemb + \ | ||
65 | homesizemb + \ | ||
66 | swapsizemb + \ | ||
67 | livesize + \ | ||
68 | plussize) * 1024L * 1024L | ||
69 | |||
70 | disk = fs_related.SparseLoopbackDisk("%s/%s.usbimg" \ | ||
71 | % (self._outdir, self.name), | ||
72 | usbimgsize) | ||
73 | usbmnt = self._mkdtemp("usb-mnt") | ||
74 | usbloop = PartitionedMount(usbmnt) | ||
75 | usbloop.add_disk('/dev/sdb', disk) | ||
76 | |||
77 | usbloop.add_partition(usbimgsize/1024/1024, | ||
78 | "/dev/sdb", | ||
79 | "/", | ||
80 | fstype, | ||
81 | boot=True) | ||
82 | |||
83 | usbloop.mount() | ||
84 | |||
85 | try: | ||
86 | fs_related.makedirs(usbmnt + "/LiveOS") | ||
87 | |||
88 | if os.path.exists(isodir + "/LiveOS/squashfs.img"): | ||
89 | shutil.copyfile(isodir + "/LiveOS/squashfs.img", | ||
90 | usbmnt + "/LiveOS/squashfs.img") | ||
91 | else: | ||
92 | fs_related.mksquashfs(os.path.dirname(self._image), | ||
93 | usbmnt + "/LiveOS/squashfs.img") | ||
94 | |||
95 | if os.path.exists(isodir + "/LiveOS/osmin.img"): | ||
96 | shutil.copyfile(isodir + "/LiveOS/osmin.img", | ||
97 | usbmnt + "/LiveOS/osmin.img") | ||
98 | |||
99 | if fstype == "vfat" or fstype == "msdos": | ||
100 | uuid = usbloop.partitions[0]['mount'].uuid | ||
101 | label = usbloop.partitions[0]['mount'].fslabel | ||
102 | usblabel = "UUID=%s-%s" % (uuid[0:4], uuid[4:8]) | ||
103 | overlaysuffix = "-%s-%s-%s" % (label, uuid[0:4], uuid[4:8]) | ||
104 | else: | ||
105 | diskmount = usbloop.partitions[0]['mount'] | ||
106 | usblabel = "UUID=%s" % diskmount.uuid | ||
107 | overlaysuffix = "-%s-%s" % (diskmount.fslabel, diskmount.uuid) | ||
108 | |||
109 | args = ['cp', "-Rf", isodir + "/isolinux", usbmnt + "/syslinux"] | ||
110 | rc = runner.show(args) | ||
111 | if rc: | ||
112 | raise CreatorError("Can't copy isolinux directory %s" \ | ||
113 | % (isodir + "/isolinux/*")) | ||
114 | |||
115 | if os.path.isfile("/usr/share/syslinux/isolinux.bin"): | ||
116 | syslinux_path = "/usr/share/syslinux" | ||
117 | elif os.path.isfile("/usr/lib/syslinux/isolinux.bin"): | ||
118 | syslinux_path = "/usr/lib/syslinux" | ||
119 | else: | ||
120 | raise CreatorError("syslinux not installed : " | ||
121 | "cannot find syslinux installation path") | ||
122 | |||
123 | for f in ("isolinux.bin", "vesamenu.c32"): | ||
124 | path = os.path.join(syslinux_path, f) | ||
125 | if os.path.isfile(path): | ||
126 | args = ['cp', path, usbmnt + "/syslinux/"] | ||
127 | rc = runner.show(args) | ||
128 | if rc: | ||
129 | raise CreatorError("Can't copy syslinux file " + path) | ||
130 | else: | ||
131 | raise CreatorError("syslinux not installed: " | ||
132 | "syslinux file %s not found" % path) | ||
133 | |||
134 | fd = open(isodir + "/isolinux/isolinux.cfg", "r") | ||
135 | text = fd.read() | ||
136 | fd.close() | ||
137 | pattern = re.compile('CDLABEL=[^ ]*') | ||
138 | text = pattern.sub(usblabel, text) | ||
139 | pattern = re.compile('rootfstype=[^ ]*') | ||
140 | text = pattern.sub("rootfstype=" + fstype, text) | ||
141 | if kernelargs: | ||
142 | text = text.replace("rd.live.image", "rd.live.image " + kernelargs) | ||
143 | |||
144 | if overlaysizemb > 0: | ||
145 | msger.info("Initializing persistent overlay file") | ||
146 | overfile = "overlay" + overlaysuffix | ||
147 | if fstype == "vfat": | ||
148 | args = ['dd', | ||
149 | "if=/dev/zero", | ||
150 | "of=" + usbmnt + "/LiveOS/" + overfile, | ||
151 | "count=%d" % overlaysizemb, | ||
152 | "bs=1M"] | ||
153 | else: | ||
154 | args = ['dd', | ||
155 | "if=/dev/null", | ||
156 | "of=" + usbmnt + "/LiveOS/" + overfile, | ||
157 | "count=1", | ||
158 | "bs=1M", | ||
159 | "seek=%d" % overlaysizemb] | ||
160 | rc = runner.show(args) | ||
161 | if rc: | ||
162 | raise CreatorError("Can't create overlay file") | ||
163 | text = text.replace("rd.live.image", "rd.live.image rd.live.overlay=" + usblabel) | ||
164 | text = text.replace(" ro ", " rw ") | ||
165 | |||
166 | if swapsizemb > 0: | ||
167 | msger.info("Initializing swap file") | ||
168 | swapfile = usbmnt + "/LiveOS/" + "swap.img" | ||
169 | args = ['dd', | ||
170 | "if=/dev/zero", | ||
171 | "of=" + swapfile, | ||
172 | "count=%d" % swapsizemb, | ||
173 | "bs=1M"] | ||
174 | rc = runner.show(args) | ||
175 | if rc: | ||
176 | raise CreatorError("Can't create swap file") | ||
177 | args = ["mkswap", "-f", swapfile] | ||
178 | rc = runner.show(args) | ||
179 | if rc: | ||
180 | raise CreatorError("Can't mkswap on swap file") | ||
181 | |||
182 | if homesizemb > 0: | ||
183 | msger.info("Initializing persistent /home") | ||
184 | homefile = usbmnt + "/LiveOS/" + homefile | ||
185 | if fstype == "vfat": | ||
186 | args = ['dd', | ||
187 | "if=/dev/zero", | ||
188 | "of=" + homefile, | ||
189 | "count=%d" % homesizemb, | ||
190 | "bs=1M"] | ||
191 | else: | ||
192 | args = ['dd', | ||
193 | "if=/dev/null", | ||
194 | "of=" + homefile, | ||
195 | "count=1", | ||
196 | "bs=1M", | ||
197 | "seek=%d" % homesizemb] | ||
198 | rc = runner.show(args) | ||
199 | if rc: | ||
200 | raise CreatorError("Can't create home file") | ||
201 | |||
202 | mkfscmd = fs_related.find_binary_path("/sbin/mkfs." + fstype) | ||
203 | if fstype == "ext2" or fstype == "ext3": | ||
204 | args = [mkfscmd, "-F", "-j", homefile] | ||
205 | else: | ||
206 | args = [mkfscmd, homefile] | ||
207 | rc = runner.show(args) | ||
208 | if rc: | ||
209 | raise CreatorError("Can't mke2fs home file") | ||
210 | if fstype == "ext2" or fstype == "ext3": | ||
211 | tune2fs = fs_related.find_binary_path("tune2fs") | ||
212 | args = [tune2fs, | ||
213 | "-c0", | ||
214 | "-i0", | ||
215 | "-ouser_xattr,acl", | ||
216 | homefile] | ||
217 | rc = runner.show(args) | ||
218 | if rc: | ||
219 | raise CreatorError("Can't tune2fs home file") | ||
220 | |||
221 | if fstype == "vfat" or fstype == "msdos": | ||
222 | syslinuxcmd = fs_related.find_binary_path("syslinux") | ||
223 | syslinuxcfg = usbmnt + "/syslinux/syslinux.cfg" | ||
224 | args = [syslinuxcmd, | ||
225 | "-d", | ||
226 | "syslinux", | ||
227 | usbloop.partitions[0]["device"]] | ||
228 | |||
229 | elif fstype == "ext2" or fstype == "ext3": | ||
230 | extlinuxcmd = fs_related.find_binary_path("extlinux") | ||
231 | syslinuxcfg = usbmnt + "/syslinux/extlinux.conf" | ||
232 | args = [extlinuxcmd, | ||
233 | "-i", | ||
234 | usbmnt + "/syslinux"] | ||
235 | |||
236 | else: | ||
237 | raise CreatorError("Invalid file system type: %s" % (fstype)) | ||
238 | |||
239 | os.unlink(usbmnt + "/syslinux/isolinux.cfg") | ||
240 | fd = open(syslinuxcfg, "w") | ||
241 | fd.write(text) | ||
242 | fd.close() | ||
243 | rc = runner.show(args) | ||
244 | if rc: | ||
245 | raise CreatorError("Can't install boot loader.") | ||
246 | |||
247 | finally: | ||
248 | usbloop.unmount() | ||
249 | usbloop.cleanup() | ||
250 | |||
251 | # Need to do this after image is unmounted and device mapper is closed | ||
252 | msger.info("set MBR") | ||
253 | mbrfile = "/usr/lib/syslinux/mbr.bin" | ||
254 | if not os.path.exists(mbrfile): | ||
255 | mbrfile = "/usr/share/syslinux/mbr.bin" | ||
256 | if not os.path.exists(mbrfile): | ||
257 | raise CreatorError("mbr.bin file didn't exist.") | ||
258 | mbrsize = os.path.getsize(mbrfile) | ||
259 | outimg = "%s/%s.usbimg" % (self._outdir, self.name) | ||
260 | |||
261 | args = ['dd', | ||
262 | "if=" + mbrfile, | ||
263 | "of=" + outimg, | ||
264 | "seek=0", | ||
265 | "conv=notrunc", | ||
266 | "bs=1", | ||
267 | "count=%d" % (mbrsize)] | ||
268 | rc = runner.show(args) | ||
269 | if rc: | ||
270 | raise CreatorError("Can't set MBR.") | ||
271 | |||
272 | def _stage_final_image(self): | ||
273 | try: | ||
274 | isodir = self._get_isodir() | ||
275 | fs_related.makedirs(isodir + "/LiveOS") | ||
276 | |||
277 | minimal_size = self._resparse() | ||
278 | |||
279 | if not self.skip_minimize: | ||
280 | fs_related.create_image_minimizer(isodir + "/LiveOS/osmin.img", | ||
281 | self._image, | ||
282 | minimal_size) | ||
283 | |||
284 | if self.skip_compression: | ||
285 | shutil.move(self._image, | ||
286 | isodir + "/LiveOS/ext3fs.img") | ||
287 | else: | ||
288 | fs_related.makedirs(os.path.join( | ||
289 | os.path.dirname(self._image), | ||
290 | "LiveOS")) | ||
291 | shutil.move(self._image, | ||
292 | os.path.join(os.path.dirname(self._image), | ||
293 | "LiveOS", "ext3fs.img")) | ||
294 | fs_related.mksquashfs(os.path.dirname(self._image), | ||
295 | isodir + "/LiveOS/squashfs.img") | ||
296 | |||
297 | self._create_usbimg(isodir) | ||
298 | |||
299 | if self.pack_to: | ||
300 | usbimg = os.path.join(self._outdir, self.name + ".usbimg") | ||
301 | packimg = os.path.join(self._outdir, self.pack_to) | ||
302 | misc.packing(packimg, usbimg) | ||
303 | os.unlink(usbimg) | ||
304 | |||
305 | finally: | ||
306 | shutil.rmtree(isodir, ignore_errors = True) | ||
307 | self._set_isodir(None) | ||
308 | |||
diff --git a/scripts/lib/mic/imager/loop.py b/scripts/lib/mic/imager/loop.py new file mode 100644 index 0000000000..4d05ef271d --- /dev/null +++ b/scripts/lib/mic/imager/loop.py | |||
@@ -0,0 +1,418 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import glob | ||
20 | import shutil | ||
21 | |||
22 | from mic import kickstart, msger | ||
23 | from mic.utils.errors import CreatorError, MountError | ||
24 | from mic.utils import misc, runner, fs_related as fs | ||
25 | from mic.imager.baseimager import BaseImageCreator | ||
26 | |||
27 | |||
28 | # The maximum string length supported for LoopImageCreator.fslabel | ||
29 | FSLABEL_MAXLEN = 32 | ||
30 | |||
31 | |||
32 | def save_mountpoints(fpath, loops, arch = None): | ||
33 | """Save mount points mapping to file | ||
34 | |||
35 | :fpath, the xml file to store partition info | ||
36 | :loops, dict of partition info | ||
37 | :arch, image arch | ||
38 | """ | ||
39 | |||
40 | if not fpath or not loops: | ||
41 | return | ||
42 | |||
43 | from xml.dom import minidom | ||
44 | doc = minidom.Document() | ||
45 | imgroot = doc.createElement("image") | ||
46 | doc.appendChild(imgroot) | ||
47 | if arch: | ||
48 | imgroot.setAttribute('arch', arch) | ||
49 | for loop in loops: | ||
50 | part = doc.createElement("partition") | ||
51 | imgroot.appendChild(part) | ||
52 | for (key, val) in loop.items(): | ||
53 | if isinstance(val, fs.Mount): | ||
54 | continue | ||
55 | part.setAttribute(key, str(val)) | ||
56 | |||
57 | with open(fpath, 'w') as wf: | ||
58 | wf.write(doc.toprettyxml(indent=' ')) | ||
59 | |||
60 | return | ||
61 | |||
62 | def load_mountpoints(fpath): | ||
63 | """Load mount points mapping from file | ||
64 | |||
65 | :fpath, file path to load | ||
66 | """ | ||
67 | |||
68 | if not fpath: | ||
69 | return | ||
70 | |||
71 | from xml.dom import minidom | ||
72 | mount_maps = [] | ||
73 | with open(fpath, 'r') as rf: | ||
74 | dom = minidom.parse(rf) | ||
75 | imgroot = dom.documentElement | ||
76 | for part in imgroot.getElementsByTagName("partition"): | ||
77 | p = dict(part.attributes.items()) | ||
78 | |||
79 | try: | ||
80 | mp = (p['mountpoint'], p['label'], p['name'], | ||
81 | int(p['size']), p['fstype']) | ||
82 | except KeyError: | ||
83 | msger.warning("Wrong format line in file: %s" % fpath) | ||
84 | except ValueError: | ||
85 | msger.warning("Invalid size '%s' in file: %s" % (p['size'], fpath)) | ||
86 | else: | ||
87 | mount_maps.append(mp) | ||
88 | |||
89 | return mount_maps | ||
90 | |||
91 | class LoopImageCreator(BaseImageCreator): | ||
92 | """Installs a system into a loopback-mountable filesystem image. | ||
93 | |||
94 | LoopImageCreator is a straightforward ImageCreator subclass; the system | ||
95 | is installed into an ext3 filesystem on a sparse file which can be | ||
96 | subsequently loopback-mounted. | ||
97 | |||
98 | When specifying multiple partitions in kickstart file, each partition | ||
99 | will be created as a separated loop image. | ||
100 | """ | ||
101 | |||
102 | def __init__(self, creatoropts=None, pkgmgr=None, | ||
103 | compress_image=None, | ||
104 | shrink_image=False): | ||
105 | """Initialize a LoopImageCreator instance. | ||
106 | |||
107 | This method takes the same arguments as ImageCreator.__init__() | ||
108 | with the addition of: | ||
109 | |||
110 | fslabel -- A string used as a label for any filesystems created. | ||
111 | """ | ||
112 | |||
113 | BaseImageCreator.__init__(self, creatoropts, pkgmgr) | ||
114 | |||
115 | self.compress_image = compress_image | ||
116 | self.shrink_image = shrink_image | ||
117 | |||
118 | self.__fslabel = None | ||
119 | self.fslabel = self.name | ||
120 | |||
121 | self.__blocksize = 4096 | ||
122 | if self.ks: | ||
123 | self.__fstype = kickstart.get_image_fstype(self.ks, | ||
124 | "ext3") | ||
125 | self.__fsopts = kickstart.get_image_fsopts(self.ks, | ||
126 | "defaults,noatime") | ||
127 | |||
128 | allloops = [] | ||
129 | for part in sorted(kickstart.get_partitions(self.ks), | ||
130 | key=lambda p: p.mountpoint): | ||
131 | if part.fstype == "swap": | ||
132 | continue | ||
133 | |||
134 | label = part.label | ||
135 | mp = part.mountpoint | ||
136 | if mp == '/': | ||
137 | # the base image | ||
138 | if not label: | ||
139 | label = self.name | ||
140 | else: | ||
141 | mp = mp.rstrip('/') | ||
142 | if not label: | ||
143 | msger.warning('no "label" specified for loop img at %s' | ||
144 | ', use the mountpoint as the name' % mp) | ||
145 | label = mp.split('/')[-1] | ||
146 | |||
147 | imgname = misc.strip_end(label, '.img') + '.img' | ||
148 | allloops.append({ | ||
149 | 'mountpoint': mp, | ||
150 | 'label': label, | ||
151 | 'name': imgname, | ||
152 | 'size': part.size or 4096L * 1024 * 1024, | ||
153 | 'fstype': part.fstype or 'ext3', | ||
154 | 'extopts': part.extopts or None, | ||
155 | 'loop': None, # to be created in _mount_instroot | ||
156 | }) | ||
157 | self._instloops = allloops | ||
158 | |||
159 | else: | ||
160 | self.__fstype = None | ||
161 | self.__fsopts = None | ||
162 | self._instloops = [] | ||
163 | |||
164 | self.__imgdir = None | ||
165 | |||
166 | if self.ks: | ||
167 | self.__image_size = kickstart.get_image_size(self.ks, | ||
168 | 4096L * 1024 * 1024) | ||
169 | else: | ||
170 | self.__image_size = 0 | ||
171 | |||
172 | self._img_name = self.name + ".img" | ||
173 | |||
174 | def get_image_names(self): | ||
175 | if not self._instloops: | ||
176 | return None | ||
177 | |||
178 | return [lo['name'] for lo in self._instloops] | ||
179 | |||
180 | def _set_fstype(self, fstype): | ||
181 | self.__fstype = fstype | ||
182 | |||
183 | def _set_image_size(self, imgsize): | ||
184 | self.__image_size = imgsize | ||
185 | |||
186 | |||
187 | # | ||
188 | # Properties | ||
189 | # | ||
190 | def __get_fslabel(self): | ||
191 | if self.__fslabel is None: | ||
192 | return self.name | ||
193 | else: | ||
194 | return self.__fslabel | ||
195 | def __set_fslabel(self, val): | ||
196 | if val is None: | ||
197 | self.__fslabel = None | ||
198 | else: | ||
199 | self.__fslabel = val[:FSLABEL_MAXLEN] | ||
200 | #A string used to label any filesystems created. | ||
201 | # | ||
202 | #Some filesystems impose a constraint on the maximum allowed size of the | ||
203 | #filesystem label. In the case of ext3 it's 16 characters, but in the case | ||
204 | #of ISO9660 it's 32 characters. | ||
205 | # | ||
206 | #mke2fs silently truncates the label, but mkisofs aborts if the label is | ||
207 | #too long. So, for convenience sake, any string assigned to this attribute | ||
208 | #is silently truncated to FSLABEL_MAXLEN (32) characters. | ||
209 | fslabel = property(__get_fslabel, __set_fslabel) | ||
210 | |||
211 | def __get_image(self): | ||
212 | if self.__imgdir is None: | ||
213 | raise CreatorError("_image is not valid before calling mount()") | ||
214 | return os.path.join(self.__imgdir, self._img_name) | ||
215 | #The location of the image file. | ||
216 | # | ||
217 | #This is the path to the filesystem image. Subclasses may use this path | ||
218 | #in order to package the image in _stage_final_image(). | ||
219 | # | ||
220 | #Note, this directory does not exist before ImageCreator.mount() is called. | ||
221 | # | ||
222 | #Note also, this is a read-only attribute. | ||
223 | _image = property(__get_image) | ||
224 | |||
225 | def __get_blocksize(self): | ||
226 | return self.__blocksize | ||
227 | def __set_blocksize(self, val): | ||
228 | if self._instloops: | ||
229 | raise CreatorError("_blocksize must be set before calling mount()") | ||
230 | try: | ||
231 | self.__blocksize = int(val) | ||
232 | except ValueError: | ||
233 | raise CreatorError("'%s' is not a valid integer value " | ||
234 | "for _blocksize" % val) | ||
235 | #The block size used by the image's filesystem. | ||
236 | # | ||
237 | #This is the block size used when creating the filesystem image. Subclasses | ||
238 | #may change this if they wish to use something other than a 4k block size. | ||
239 | # | ||
240 | #Note, this attribute may only be set before calling mount(). | ||
241 | _blocksize = property(__get_blocksize, __set_blocksize) | ||
242 | |||
243 | def __get_fstype(self): | ||
244 | return self.__fstype | ||
245 | def __set_fstype(self, val): | ||
246 | if val != "ext2" and val != "ext3": | ||
247 | raise CreatorError("Unknown _fstype '%s' supplied" % val) | ||
248 | self.__fstype = val | ||
249 | #The type of filesystem used for the image. | ||
250 | # | ||
251 | #This is the filesystem type used when creating the filesystem image. | ||
252 | #Subclasses may change this if they wish to use something other ext3. | ||
253 | # | ||
254 | #Note, only ext2 and ext3 are currently supported. | ||
255 | # | ||
256 | #Note also, this attribute may only be set before calling mount(). | ||
257 | _fstype = property(__get_fstype, __set_fstype) | ||
258 | |||
259 | def __get_fsopts(self): | ||
260 | return self.__fsopts | ||
261 | def __set_fsopts(self, val): | ||
262 | self.__fsopts = val | ||
263 | #Mount options of filesystem used for the image. | ||
264 | # | ||
265 | #This can be specified by --fsoptions="xxx,yyy" in part command in | ||
266 | #kickstart file. | ||
267 | _fsopts = property(__get_fsopts, __set_fsopts) | ||
268 | |||
269 | |||
270 | # | ||
271 | # Helpers for subclasses | ||
272 | # | ||
273 | def _resparse(self, size=None): | ||
274 | """Rebuild the filesystem image to be as sparse as possible. | ||
275 | |||
276 | This method should be used by subclasses when staging the final image | ||
277 | in order to reduce the actual space taken up by the sparse image file | ||
278 | to be as little as possible. | ||
279 | |||
280 | This is done by resizing the filesystem to the minimal size (thereby | ||
281 | eliminating any space taken up by deleted files) and then resizing it | ||
282 | back to the supplied size. | ||
283 | |||
284 | size -- the size in, in bytes, which the filesystem image should be | ||
285 | resized to after it has been minimized; this defaults to None, | ||
286 | causing the original size specified by the kickstart file to | ||
287 | be used (or 4GiB if not specified in the kickstart). | ||
288 | """ | ||
289 | minsize = 0 | ||
290 | for item in self._instloops: | ||
291 | if item['name'] == self._img_name: | ||
292 | minsize = item['loop'].resparse(size) | ||
293 | else: | ||
294 | item['loop'].resparse(size) | ||
295 | |||
296 | return minsize | ||
297 | |||
298 | def _base_on(self, base_on=None): | ||
299 | if base_on and self._image != base_on: | ||
300 | shutil.copyfile(base_on, self._image) | ||
301 | |||
302 | def _check_imgdir(self): | ||
303 | if self.__imgdir is None: | ||
304 | self.__imgdir = self._mkdtemp() | ||
305 | |||
306 | |||
307 | # | ||
308 | # Actual implementation | ||
309 | # | ||
310 | def _mount_instroot(self, base_on=None): | ||
311 | |||
312 | if base_on and os.path.isfile(base_on): | ||
313 | self.__imgdir = os.path.dirname(base_on) | ||
314 | imgname = os.path.basename(base_on) | ||
315 | self._base_on(base_on) | ||
316 | self._set_image_size(misc.get_file_size(self._image)) | ||
317 | |||
318 | # here, self._instloops must be [] | ||
319 | self._instloops.append({ | ||
320 | "mountpoint": "/", | ||
321 | "label": self.name, | ||
322 | "name": imgname, | ||
323 | "size": self.__image_size or 4096L, | ||
324 | "fstype": self.__fstype or "ext3", | ||
325 | "extopts": None, | ||
326 | "loop": None | ||
327 | }) | ||
328 | |||
329 | self._check_imgdir() | ||
330 | |||
331 | for loop in self._instloops: | ||
332 | fstype = loop['fstype'] | ||
333 | mp = os.path.join(self._instroot, loop['mountpoint'].lstrip('/')) | ||
334 | size = loop['size'] * 1024L * 1024L | ||
335 | imgname = loop['name'] | ||
336 | |||
337 | if fstype in ("ext2", "ext3", "ext4"): | ||
338 | MyDiskMount = fs.ExtDiskMount | ||
339 | elif fstype == "btrfs": | ||
340 | MyDiskMount = fs.BtrfsDiskMount | ||
341 | elif fstype in ("vfat", "msdos"): | ||
342 | MyDiskMount = fs.VfatDiskMount | ||
343 | else: | ||
344 | msger.error('Cannot support fstype: %s' % fstype) | ||
345 | |||
346 | loop['loop'] = MyDiskMount(fs.SparseLoopbackDisk( | ||
347 | os.path.join(self.__imgdir, imgname), | ||
348 | size), | ||
349 | mp, | ||
350 | fstype, | ||
351 | self._blocksize, | ||
352 | loop['label']) | ||
353 | |||
354 | if fstype in ("ext2", "ext3", "ext4"): | ||
355 | loop['loop'].extopts = loop['extopts'] | ||
356 | |||
357 | try: | ||
358 | msger.verbose('Mounting image "%s" on "%s"' % (imgname, mp)) | ||
359 | fs.makedirs(mp) | ||
360 | loop['loop'].mount() | ||
361 | except MountError, e: | ||
362 | raise | ||
363 | |||
364 | def _unmount_instroot(self): | ||
365 | for item in reversed(self._instloops): | ||
366 | try: | ||
367 | item['loop'].cleanup() | ||
368 | except: | ||
369 | pass | ||
370 | |||
371 | def _stage_final_image(self): | ||
372 | |||
373 | if self.pack_to or self.shrink_image: | ||
374 | self._resparse(0) | ||
375 | else: | ||
376 | self._resparse() | ||
377 | |||
378 | for item in self._instloops: | ||
379 | imgfile = os.path.join(self.__imgdir, item['name']) | ||
380 | if item['fstype'] == "ext4": | ||
381 | runner.show('/sbin/tune2fs -O ^huge_file,extents,uninit_bg %s ' | ||
382 | % imgfile) | ||
383 | if self.compress_image: | ||
384 | misc.compressing(imgfile, self.compress_image) | ||
385 | |||
386 | if not self.pack_to: | ||
387 | for item in os.listdir(self.__imgdir): | ||
388 | shutil.move(os.path.join(self.__imgdir, item), | ||
389 | os.path.join(self._outdir, item)) | ||
390 | else: | ||
391 | msger.info("Pack all loop images together to %s" % self.pack_to) | ||
392 | dstfile = os.path.join(self._outdir, self.pack_to) | ||
393 | misc.packing(dstfile, self.__imgdir) | ||
394 | |||
395 | if self.pack_to: | ||
396 | mountfp_xml = os.path.splitext(self.pack_to)[0] | ||
397 | mountfp_xml = misc.strip_end(mountfp_xml, '.tar') + ".xml" | ||
398 | else: | ||
399 | mountfp_xml = self.name + ".xml" | ||
400 | # save mount points mapping file to xml | ||
401 | save_mountpoints(os.path.join(self._outdir, mountfp_xml), | ||
402 | self._instloops, | ||
403 | self.target_arch) | ||
404 | |||
405 | def copy_attachment(self): | ||
406 | if not hasattr(self, '_attachment') or not self._attachment: | ||
407 | return | ||
408 | |||
409 | self._check_imgdir() | ||
410 | |||
411 | msger.info("Copying attachment files...") | ||
412 | for item in self._attachment: | ||
413 | if not os.path.exists(item): | ||
414 | continue | ||
415 | dpath = os.path.join(self.__imgdir, os.path.basename(item)) | ||
416 | msger.verbose("Copy attachment %s to %s" % (item, dpath)) | ||
417 | shutil.copy(item, dpath) | ||
418 | |||
diff --git a/scripts/lib/mic/imager/raw.py b/scripts/lib/mic/imager/raw.py new file mode 100644 index 0000000000..838191a6f1 --- /dev/null +++ b/scripts/lib/mic/imager/raw.py | |||
@@ -0,0 +1,501 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import stat | ||
20 | import shutil | ||
21 | |||
22 | from mic import kickstart, msger | ||
23 | from mic.utils import fs_related, runner, misc | ||
24 | from mic.utils.partitionedfs import PartitionedMount | ||
25 | from mic.utils.errors import CreatorError, MountError | ||
26 | from mic.imager.baseimager import BaseImageCreator | ||
27 | |||
28 | |||
29 | class RawImageCreator(BaseImageCreator): | ||
30 | """Installs a system into a file containing a partitioned disk image. | ||
31 | |||
32 | ApplianceImageCreator is an advanced ImageCreator subclass; a sparse file | ||
33 | is formatted with a partition table, each partition loopback mounted | ||
34 | and the system installed into an virtual disk. The disk image can | ||
35 | subsequently be booted in a virtual machine or accessed with kpartx | ||
36 | """ | ||
37 | |||
38 | def __init__(self, creatoropts=None, pkgmgr=None, compress_image=None, generate_bmap=None, fstab_entry="uuid"): | ||
39 | """Initialize a ApplianceImageCreator instance. | ||
40 | |||
41 | This method takes the same arguments as ImageCreator.__init__() | ||
42 | """ | ||
43 | BaseImageCreator.__init__(self, creatoropts, pkgmgr) | ||
44 | |||
45 | self.__instloop = None | ||
46 | self.__imgdir = None | ||
47 | self.__disks = {} | ||
48 | self.__disk_format = "raw" | ||
49 | self._disk_names = [] | ||
50 | self._ptable_format = self.ks.handler.bootloader.ptable | ||
51 | self.vmem = 512 | ||
52 | self.vcpu = 1 | ||
53 | self.checksum = False | ||
54 | self.use_uuid = fstab_entry == "uuid" | ||
55 | self.appliance_version = None | ||
56 | self.appliance_release = None | ||
57 | self.compress_image = compress_image | ||
58 | self.bmap_needed = generate_bmap | ||
59 | self._need_extlinux = not kickstart.use_installerfw(self.ks, "extlinux") | ||
60 | #self.getsource = False | ||
61 | #self.listpkg = False | ||
62 | |||
63 | self._dep_checks.extend(["sync", "kpartx", "parted"]) | ||
64 | if self._need_extlinux: | ||
65 | self._dep_checks.extend(["extlinux"]) | ||
66 | |||
67 | def configure(self, repodata = None): | ||
68 | import subprocess | ||
69 | def chroot(): | ||
70 | os.chroot(self._instroot) | ||
71 | os.chdir("/") | ||
72 | |||
73 | if os.path.exists(self._instroot + "/usr/bin/Xorg"): | ||
74 | subprocess.call(["/bin/chmod", "u+s", "/usr/bin/Xorg"], | ||
75 | preexec_fn = chroot) | ||
76 | |||
77 | BaseImageCreator.configure(self, repodata) | ||
78 | |||
79 | def _get_fstab(self): | ||
80 | if kickstart.use_installerfw(self.ks, "fstab"): | ||
81 | # The fstab file will be generated by installer framework scripts | ||
82 | # instead. | ||
83 | return None | ||
84 | |||
85 | s = "" | ||
86 | for mp in self.__instloop.mountOrder: | ||
87 | p = None | ||
88 | for p1 in self.__instloop.partitions: | ||
89 | if p1['mountpoint'] == mp: | ||
90 | p = p1 | ||
91 | break | ||
92 | |||
93 | if self.use_uuid and p['uuid']: | ||
94 | device = "UUID=%s" % p['uuid'] | ||
95 | else: | ||
96 | device = "/dev/%s%-d" % (p['disk_name'], p['num']) | ||
97 | |||
98 | s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % { | ||
99 | 'device': device, | ||
100 | 'mountpoint': p['mountpoint'], | ||
101 | 'fstype': p['fstype'], | ||
102 | 'fsopts': "defaults,noatime" if not p['fsopts'] else p['fsopts']} | ||
103 | |||
104 | if p['mountpoint'] == "/": | ||
105 | for subvol in self.__instloop.subvolumes: | ||
106 | if subvol['mountpoint'] == "/": | ||
107 | continue | ||
108 | s += "%(device)s %(mountpoint)s %(fstype)s %(fsopts)s 0 0\n" % { | ||
109 | 'device': "/dev/%s%-d" % (p['disk_name'], p['num']), | ||
110 | 'mountpoint': subvol['mountpoint'], | ||
111 | 'fstype': p['fstype'], | ||
112 | 'fsopts': "defaults,noatime" if not subvol['fsopts'] else subvol['fsopts']} | ||
113 | |||
114 | s += "devpts /dev/pts devpts gid=5,mode=620 0 0\n" | ||
115 | s += "tmpfs /dev/shm tmpfs defaults 0 0\n" | ||
116 | s += "proc /proc proc defaults 0 0\n" | ||
117 | s += "sysfs /sys sysfs defaults 0 0\n" | ||
118 | return s | ||
119 | |||
120 | def _create_mkinitrd_config(self): | ||
121 | """write to tell which modules to be included in initrd""" | ||
122 | |||
123 | mkinitrd = "" | ||
124 | mkinitrd += "PROBE=\"no\"\n" | ||
125 | mkinitrd += "MODULES+=\"ext3 ata_piix sd_mod libata scsi_mod\"\n" | ||
126 | mkinitrd += "rootfs=\"ext3\"\n" | ||
127 | mkinitrd += "rootopts=\"defaults\"\n" | ||
128 | |||
129 | msger.debug("Writing mkinitrd config %s/etc/sysconfig/mkinitrd" \ | ||
130 | % self._instroot) | ||
131 | os.makedirs(self._instroot + "/etc/sysconfig/",mode=644) | ||
132 | cfg = open(self._instroot + "/etc/sysconfig/mkinitrd", "w") | ||
133 | cfg.write(mkinitrd) | ||
134 | cfg.close() | ||
135 | |||
136 | def _get_parts(self): | ||
137 | if not self.ks: | ||
138 | raise CreatorError("Failed to get partition info, " | ||
139 | "please check your kickstart setting.") | ||
140 | |||
141 | # Set a default partition if no partition is given out | ||
142 | if not self.ks.handler.partition.partitions: | ||
143 | partstr = "part / --size 1900 --ondisk sda --fstype=ext3" | ||
144 | args = partstr.split() | ||
145 | pd = self.ks.handler.partition.parse(args[1:]) | ||
146 | if pd not in self.ks.handler.partition.partitions: | ||
147 | self.ks.handler.partition.partitions.append(pd) | ||
148 | |||
149 | # partitions list from kickstart file | ||
150 | return kickstart.get_partitions(self.ks) | ||
151 | |||
152 | def get_disk_names(self): | ||
153 | """ Returns a list of physical target disk names (e.g., 'sdb') which | ||
154 | will be created. """ | ||
155 | |||
156 | if self._disk_names: | ||
157 | return self._disk_names | ||
158 | |||
159 | #get partition info from ks handler | ||
160 | parts = self._get_parts() | ||
161 | |||
162 | for i in range(len(parts)): | ||
163 | if parts[i].disk: | ||
164 | disk_name = parts[i].disk | ||
165 | else: | ||
166 | raise CreatorError("Failed to create disks, no --ondisk " | ||
167 | "specified in partition line of ks file") | ||
168 | |||
169 | if parts[i].mountpoint and not parts[i].fstype: | ||
170 | raise CreatorError("Failed to create disks, no --fstype " | ||
171 | "specified for partition with mountpoint " | ||
172 | "'%s' in the ks file") | ||
173 | |||
174 | self._disk_names.append(disk_name) | ||
175 | |||
176 | return self._disk_names | ||
177 | |||
178 | def _full_name(self, name, extention): | ||
179 | """ Construct full file name for a file we generate. """ | ||
180 | return "%s-%s.%s" % (self.name, name, extention) | ||
181 | |||
182 | def _full_path(self, path, name, extention): | ||
183 | """ Construct full file path to a file we generate. """ | ||
184 | return os.path.join(path, self._full_name(name, extention)) | ||
185 | |||
186 | # | ||
187 | # Actual implemention | ||
188 | # | ||
189 | def _mount_instroot(self, base_on = None): | ||
190 | parts = self._get_parts() | ||
191 | self.__instloop = PartitionedMount(self._instroot) | ||
192 | |||
193 | for p in parts: | ||
194 | self.__instloop.add_partition(int(p.size), | ||
195 | p.disk, | ||
196 | p.mountpoint, | ||
197 | p.fstype, | ||
198 | p.label, | ||
199 | fsopts = p.fsopts, | ||
200 | boot = p.active, | ||
201 | align = p.align, | ||
202 | part_type = p.part_type) | ||
203 | |||
204 | self.__instloop.layout_partitions(self._ptable_format) | ||
205 | |||
206 | # Create the disks | ||
207 | self.__imgdir = self._mkdtemp() | ||
208 | for disk_name, disk in self.__instloop.disks.items(): | ||
209 | full_path = self._full_path(self.__imgdir, disk_name, "raw") | ||
210 | msger.debug("Adding disk %s as %s with size %s bytes" \ | ||
211 | % (disk_name, full_path, disk['min_size'])) | ||
212 | |||
213 | disk_obj = fs_related.SparseLoopbackDisk(full_path, | ||
214 | disk['min_size']) | ||
215 | self.__disks[disk_name] = disk_obj | ||
216 | self.__instloop.add_disk(disk_name, disk_obj) | ||
217 | |||
218 | self.__instloop.mount() | ||
219 | self._create_mkinitrd_config() | ||
220 | |||
221 | def _get_required_packages(self): | ||
222 | required_packages = BaseImageCreator._get_required_packages(self) | ||
223 | if self._need_extlinux: | ||
224 | if not self.target_arch or not self.target_arch.startswith("arm"): | ||
225 | required_packages += ["syslinux", "syslinux-extlinux"] | ||
226 | return required_packages | ||
227 | |||
228 | def _get_excluded_packages(self): | ||
229 | return BaseImageCreator._get_excluded_packages(self) | ||
230 | |||
231 | def _get_syslinux_boot_config(self): | ||
232 | rootdev = None | ||
233 | root_part_uuid = None | ||
234 | for p in self.__instloop.partitions: | ||
235 | if p['mountpoint'] == "/": | ||
236 | rootdev = "/dev/%s%-d" % (p['disk_name'], p['num']) | ||
237 | root_part_uuid = p['partuuid'] | ||
238 | |||
239 | return (rootdev, root_part_uuid) | ||
240 | |||
241 | def _create_syslinux_config(self): | ||
242 | |||
243 | splash = os.path.join(self._instroot, "boot/extlinux") | ||
244 | if os.path.exists(splash): | ||
245 | splashline = "menu background splash.jpg" | ||
246 | else: | ||
247 | splashline = "" | ||
248 | |||
249 | (rootdev, root_part_uuid) = self._get_syslinux_boot_config() | ||
250 | options = self.ks.handler.bootloader.appendLine | ||
251 | |||
252 | #XXX don't hardcode default kernel - see livecd code | ||
253 | syslinux_conf = "" | ||
254 | syslinux_conf += "prompt 0\n" | ||
255 | syslinux_conf += "timeout 1\n" | ||
256 | syslinux_conf += "\n" | ||
257 | syslinux_conf += "default vesamenu.c32\n" | ||
258 | syslinux_conf += "menu autoboot Starting %s...\n" % self.distro_name | ||
259 | syslinux_conf += "menu hidden\n" | ||
260 | syslinux_conf += "\n" | ||
261 | syslinux_conf += "%s\n" % splashline | ||
262 | syslinux_conf += "menu title Welcome to %s!\n" % self.distro_name | ||
263 | syslinux_conf += "menu color border 0 #ffffffff #00000000\n" | ||
264 | syslinux_conf += "menu color sel 7 #ffffffff #ff000000\n" | ||
265 | syslinux_conf += "menu color title 0 #ffffffff #00000000\n" | ||
266 | syslinux_conf += "menu color tabmsg 0 #ffffffff #00000000\n" | ||
267 | syslinux_conf += "menu color unsel 0 #ffffffff #00000000\n" | ||
268 | syslinux_conf += "menu color hotsel 0 #ff000000 #ffffffff\n" | ||
269 | syslinux_conf += "menu color hotkey 7 #ffffffff #ff000000\n" | ||
270 | syslinux_conf += "menu color timeout_msg 0 #ffffffff #00000000\n" | ||
271 | syslinux_conf += "menu color timeout 0 #ffffffff #00000000\n" | ||
272 | syslinux_conf += "menu color cmdline 0 #ffffffff #00000000\n" | ||
273 | |||
274 | versions = [] | ||
275 | kernels = self._get_kernel_versions() | ||
276 | symkern = "%s/boot/vmlinuz" % self._instroot | ||
277 | |||
278 | if os.path.lexists(symkern): | ||
279 | v = os.path.realpath(symkern).replace('%s-' % symkern, "") | ||
280 | syslinux_conf += "label %s\n" % self.distro_name.lower() | ||
281 | syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v) | ||
282 | syslinux_conf += "\tlinux ../vmlinuz\n" | ||
283 | if self._ptable_format == 'msdos': | ||
284 | rootstr = rootdev | ||
285 | else: | ||
286 | if not root_part_uuid: | ||
287 | raise MountError("Cannot find the root GPT partition UUID") | ||
288 | rootstr = "PARTUUID=%s" % root_part_uuid | ||
289 | syslinux_conf += "\tappend ro root=%s %s\n" % (rootstr, options) | ||
290 | syslinux_conf += "\tmenu default\n" | ||
291 | else: | ||
292 | for kernel in kernels: | ||
293 | for version in kernels[kernel]: | ||
294 | versions.append(version) | ||
295 | |||
296 | footlabel = 0 | ||
297 | for v in versions: | ||
298 | syslinux_conf += "label %s%d\n" \ | ||
299 | % (self.distro_name.lower(), footlabel) | ||
300 | syslinux_conf += "\tmenu label %s (%s)\n" % (self.distro_name, v) | ||
301 | syslinux_conf += "\tlinux ../vmlinuz-%s\n" % v | ||
302 | syslinux_conf += "\tappend ro root=%s %s\n" \ | ||
303 | % (rootdev, options) | ||
304 | if footlabel == 0: | ||
305 | syslinux_conf += "\tmenu default\n" | ||
306 | footlabel += 1; | ||
307 | |||
308 | msger.debug("Writing syslinux config %s/boot/extlinux/extlinux.conf" \ | ||
309 | % self._instroot) | ||
310 | cfg = open(self._instroot + "/boot/extlinux/extlinux.conf", "w") | ||
311 | cfg.write(syslinux_conf) | ||
312 | cfg.close() | ||
313 | |||
314 | def _install_syslinux(self): | ||
315 | for name in self.__disks.keys(): | ||
316 | loopdev = self.__disks[name].device | ||
317 | |||
318 | # Set MBR | ||
319 | mbrfile = "%s/usr/share/syslinux/" % self._instroot | ||
320 | if self._ptable_format == 'gpt': | ||
321 | mbrfile += "gptmbr.bin" | ||
322 | else: | ||
323 | mbrfile += "mbr.bin" | ||
324 | |||
325 | msger.debug("Installing syslinux bootloader '%s' to %s" % \ | ||
326 | (mbrfile, loopdev)) | ||
327 | |||
328 | mbrsize = os.stat(mbrfile)[stat.ST_SIZE] | ||
329 | rc = runner.show(['dd', 'if=%s' % mbrfile, 'of=' + loopdev]) | ||
330 | if rc != 0: | ||
331 | raise MountError("Unable to set MBR to %s" % loopdev) | ||
332 | |||
333 | |||
334 | # Ensure all data is flushed to disk before doing syslinux install | ||
335 | runner.quiet('sync') | ||
336 | |||
337 | fullpathsyslinux = fs_related.find_binary_path("extlinux") | ||
338 | rc = runner.show([fullpathsyslinux, | ||
339 | "-i", | ||
340 | "%s/boot/extlinux" % self._instroot]) | ||
341 | if rc != 0: | ||
342 | raise MountError("Unable to install syslinux bootloader to %s" \ | ||
343 | % loopdev) | ||
344 | |||
345 | def _create_bootconfig(self): | ||
346 | #If syslinux is available do the required configurations. | ||
347 | if self._need_extlinux \ | ||
348 | and os.path.exists("%s/usr/share/syslinux/" % (self._instroot)) \ | ||
349 | and os.path.exists("%s/boot/extlinux/" % (self._instroot)): | ||
350 | self._create_syslinux_config() | ||
351 | self._install_syslinux() | ||
352 | |||
353 | def _unmount_instroot(self): | ||
354 | if not self.__instloop is None: | ||
355 | try: | ||
356 | self.__instloop.cleanup() | ||
357 | except MountError, err: | ||
358 | msger.warning("%s" % err) | ||
359 | |||
360 | def _resparse(self, size = None): | ||
361 | return self.__instloop.resparse(size) | ||
362 | |||
363 | def _get_post_scripts_env(self, in_chroot): | ||
364 | env = BaseImageCreator._get_post_scripts_env(self, in_chroot) | ||
365 | |||
366 | # Export the file-system UUIDs and partition UUIDs (AKA PARTUUIDs) | ||
367 | for p in self.__instloop.partitions: | ||
368 | env.update(self._set_part_env(p['ks_pnum'], "UUID", p['uuid'])) | ||
369 | env.update(self._set_part_env(p['ks_pnum'], "PARTUUID", p['partuuid'])) | ||
370 | |||
371 | return env | ||
372 | |||
373 | def _stage_final_image(self): | ||
374 | """Stage the final system image in _outdir. | ||
375 | write meta data | ||
376 | """ | ||
377 | self._resparse() | ||
378 | |||
379 | if self.compress_image: | ||
380 | for imgfile in os.listdir(self.__imgdir): | ||
381 | if imgfile.endswith('.raw') or imgfile.endswith('bin'): | ||
382 | imgpath = os.path.join(self.__imgdir, imgfile) | ||
383 | misc.compressing(imgpath, self.compress_image) | ||
384 | |||
385 | if self.pack_to: | ||
386 | dst = os.path.join(self._outdir, self.pack_to) | ||
387 | msger.info("Pack all raw images to %s" % dst) | ||
388 | misc.packing(dst, self.__imgdir) | ||
389 | else: | ||
390 | msger.debug("moving disks to stage location") | ||
391 | for imgfile in os.listdir(self.__imgdir): | ||
392 | src = os.path.join(self.__imgdir, imgfile) | ||
393 | dst = os.path.join(self._outdir, imgfile) | ||
394 | msger.debug("moving %s to %s" % (src,dst)) | ||
395 | shutil.move(src,dst) | ||
396 | self._write_image_xml() | ||
397 | |||
398 | def _write_image_xml(self): | ||
399 | imgarch = "i686" | ||
400 | if self.target_arch and self.target_arch.startswith("arm"): | ||
401 | imgarch = "arm" | ||
402 | xml = "<image>\n" | ||
403 | |||
404 | name_attributes = "" | ||
405 | if self.appliance_version: | ||
406 | name_attributes += " version='%s'" % self.appliance_version | ||
407 | if self.appliance_release: | ||
408 | name_attributes += " release='%s'" % self.appliance_release | ||
409 | xml += " <name%s>%s</name>\n" % (name_attributes, self.name) | ||
410 | xml += " <domain>\n" | ||
411 | # XXX don't hardcode - determine based on the kernel we installed for | ||
412 | # grub baremetal vs xen | ||
413 | xml += " <boot type='hvm'>\n" | ||
414 | xml += " <guest>\n" | ||
415 | xml += " <arch>%s</arch>\n" % imgarch | ||
416 | xml += " </guest>\n" | ||
417 | xml += " <os>\n" | ||
418 | xml += " <loader dev='hd'/>\n" | ||
419 | xml += " </os>\n" | ||
420 | |||
421 | i = 0 | ||
422 | for name in self.__disks.keys(): | ||
423 | full_name = self._full_name(name, self.__disk_format) | ||
424 | xml += " <drive disk='%s' target='hd%s'/>\n" \ | ||
425 | % (full_name, chr(ord('a') + i)) | ||
426 | i = i + 1 | ||
427 | |||
428 | xml += " </boot>\n" | ||
429 | xml += " <devices>\n" | ||
430 | xml += " <vcpu>%s</vcpu>\n" % self.vcpu | ||
431 | xml += " <memory>%d</memory>\n" %(self.vmem * 1024) | ||
432 | for network in self.ks.handler.network.network: | ||
433 | xml += " <interface/>\n" | ||
434 | xml += " <graphics/>\n" | ||
435 | xml += " </devices>\n" | ||
436 | xml += " </domain>\n" | ||
437 | xml += " <storage>\n" | ||
438 | |||
439 | if self.checksum is True: | ||
440 | for name in self.__disks.keys(): | ||
441 | diskpath = self._full_path(self._outdir, name, \ | ||
442 | self.__disk_format) | ||
443 | full_name = self._full_name(name, self.__disk_format) | ||
444 | |||
445 | msger.debug("Generating disk signature for %s" % full_name) | ||
446 | |||
447 | xml += " <disk file='%s' use='system' format='%s'>\n" \ | ||
448 | % (full_name, self.__disk_format) | ||
449 | |||
450 | hashes = misc.calc_hashes(diskpath, ('sha1', 'sha256')) | ||
451 | |||
452 | xml += " <checksum type='sha1'>%s</checksum>\n" \ | ||
453 | % hashes[0] | ||
454 | xml += " <checksum type='sha256'>%s</checksum>\n" \ | ||
455 | % hashes[1] | ||
456 | xml += " </disk>\n" | ||
457 | else: | ||
458 | for name in self.__disks.keys(): | ||
459 | full_name = self._full_name(name, self.__disk_format) | ||
460 | xml += " <disk file='%s' use='system' format='%s'/>\n" \ | ||
461 | % (full_name, self.__disk_format) | ||
462 | |||
463 | xml += " </storage>\n" | ||
464 | xml += "</image>\n" | ||
465 | |||
466 | msger.debug("writing image XML to %s/%s.xml" %(self._outdir, self.name)) | ||
467 | cfg = open("%s/%s.xml" % (self._outdir, self.name), "w") | ||
468 | cfg.write(xml) | ||
469 | cfg.close() | ||
470 | |||
471 | def generate_bmap(self): | ||
472 | """ Generate block map file for the image. The idea is that while disk | ||
473 | images we generate may be large (e.g., 4GiB), they may actually contain | ||
474 | only little real data, e.g., 512MiB. This data are files, directories, | ||
475 | file-system meta-data, partition table, etc. In other words, when | ||
476 | flashing the image to the target device, you do not have to copy all the | ||
477 | 4GiB of data, you can copy only 512MiB of it, which is 4 times faster. | ||
478 | |||
479 | This function generates the block map file for an arbitrary image that | ||
480 | mic has generated. The block map file is basically an XML file which | ||
481 | contains a list of blocks which have to be copied to the target device. | ||
482 | The other blocks are not used and there is no need to copy them. """ | ||
483 | |||
484 | if self.bmap_needed is None: | ||
485 | return | ||
486 | |||
487 | from mic.utils import BmapCreate | ||
488 | msger.info("Generating the map file(s)") | ||
489 | |||
490 | for name in self.__disks.keys(): | ||
491 | image = self._full_path(self.__imgdir, name, self.__disk_format) | ||
492 | bmap_file = self._full_path(self._outdir, name, "bmap") | ||
493 | |||
494 | msger.debug("Generating block map file '%s'" % bmap_file) | ||
495 | |||
496 | try: | ||
497 | creator = BmapCreate.BmapCreate(image, bmap_file) | ||
498 | creator.generate() | ||
499 | del creator | ||
500 | except BmapCreate.Error as err: | ||
501 | raise CreatorError("Failed to create bmap file: %s" % str(err)) | ||
diff --git a/scripts/lib/mic/kickstart/__init__.py b/scripts/lib/mic/kickstart/__init__.py new file mode 100644 index 0000000000..72f3ca6849 --- /dev/null +++ b/scripts/lib/mic/kickstart/__init__.py | |||
@@ -0,0 +1,892 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2007 Red Hat, Inc. | ||
4 | # Copyright (c) 2009, 2010, 2011 Intel, Inc. | ||
5 | # | ||
6 | # This program is free software; you can redistribute it and/or modify it | ||
7 | # under the terms of the GNU General Public License as published by the Free | ||
8 | # Software Foundation; version 2 of the License | ||
9 | # | ||
10 | # This program is distributed in the hope that it will be useful, but | ||
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
12 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
13 | # for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License along | ||
16 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
17 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | |||
19 | import os, sys, re | ||
20 | import shutil | ||
21 | import subprocess | ||
22 | import string | ||
23 | |||
24 | import pykickstart.sections as kssections | ||
25 | import pykickstart.commands as kscommands | ||
26 | import pykickstart.constants as ksconstants | ||
27 | import pykickstart.errors as kserrors | ||
28 | import pykickstart.parser as ksparser | ||
29 | import pykickstart.version as ksversion | ||
30 | from pykickstart.handlers.control import commandMap | ||
31 | from pykickstart.handlers.control import dataMap | ||
32 | |||
33 | from mic import msger | ||
34 | from mic.utils import errors, misc, runner, fs_related as fs | ||
35 | from custom_commands import desktop, micrepo, wicboot, partition, installerfw | ||
36 | |||
37 | |||
38 | AUTH_URL_PTN = r"(?P<scheme>.*)://(?P<username>.*)(:?P<password>.*)?@(?P<url>.*)" | ||
39 | |||
40 | |||
41 | class PrepackageSection(kssections.Section): | ||
42 | sectionOpen = "%prepackages" | ||
43 | |||
44 | def handleLine(self, line): | ||
45 | if not self.handler: | ||
46 | return | ||
47 | |||
48 | (h, s, t) = line.partition('#') | ||
49 | line = h.rstrip() | ||
50 | |||
51 | self.handler.prepackages.add([line]) | ||
52 | |||
53 | def handleHeader(self, lineno, args): | ||
54 | kssections.Section.handleHeader(self, lineno, args) | ||
55 | |||
56 | class AttachmentSection(kssections.Section): | ||
57 | sectionOpen = "%attachment" | ||
58 | |||
59 | def handleLine(self, line): | ||
60 | if not self.handler: | ||
61 | return | ||
62 | |||
63 | (h, s, t) = line.partition('#') | ||
64 | line = h.rstrip() | ||
65 | |||
66 | self.handler.attachment.add([line]) | ||
67 | |||
68 | def handleHeader(self, lineno, args): | ||
69 | kssections.Section.handleHeader(self, lineno, args) | ||
70 | |||
71 | def apply_wrapper(func): | ||
72 | def wrapper(*kargs, **kwargs): | ||
73 | try: | ||
74 | func(*kargs, **kwargs) | ||
75 | except (OSError, IOError, errors.KsError), err: | ||
76 | cfgcls = kargs[0].__class__.__name__ | ||
77 | if msger.ask("Failed to apply %s, skip and continue?" % cfgcls): | ||
78 | msger.warning("%s" % err) | ||
79 | pass | ||
80 | else: | ||
81 | # just throw out the exception | ||
82 | raise | ||
83 | return wrapper | ||
84 | |||
85 | def read_kickstart(path): | ||
86 | """Parse a kickstart file and return a KickstartParser instance. | ||
87 | |||
88 | This is a simple utility function which takes a path to a kickstart file, | ||
89 | parses it and returns a pykickstart KickstartParser instance which can | ||
90 | be then passed to an ImageCreator constructor. | ||
91 | |||
92 | If an error occurs, a CreatorError exception is thrown. | ||
93 | """ | ||
94 | |||
95 | #version = ksversion.makeVersion() | ||
96 | #ks = ksparser.KickstartParser(version) | ||
97 | |||
98 | using_version = ksversion.DEVEL | ||
99 | commandMap[using_version]["desktop"] = desktop.Mic_Desktop | ||
100 | commandMap[using_version]["repo"] = micrepo.Mic_Repo | ||
101 | commandMap[using_version]["bootloader"] = wicboot.Wic_Bootloader | ||
102 | commandMap[using_version]["part"] = partition.Wic_Partition | ||
103 | commandMap[using_version]["partition"] = partition.Wic_Partition | ||
104 | commandMap[using_version]["installerfw"] = installerfw.Mic_installerfw | ||
105 | dataMap[using_version]["RepoData"] = micrepo.Mic_RepoData | ||
106 | dataMap[using_version]["PartData"] = partition.Wic_PartData | ||
107 | superclass = ksversion.returnClassForVersion(version=using_version) | ||
108 | |||
109 | class KSHandlers(superclass): | ||
110 | def __init__(self): | ||
111 | superclass.__init__(self, mapping=commandMap[using_version]) | ||
112 | self.prepackages = ksparser.Packages() | ||
113 | self.attachment = ksparser.Packages() | ||
114 | |||
115 | ks = ksparser.KickstartParser(KSHandlers(), errorsAreFatal=False) | ||
116 | ks.registerSection(PrepackageSection(ks.handler)) | ||
117 | ks.registerSection(AttachmentSection(ks.handler)) | ||
118 | |||
119 | try: | ||
120 | ks.readKickstart(path) | ||
121 | except (kserrors.KickstartParseError, kserrors.KickstartError), err: | ||
122 | if msger.ask("Errors occured on kickstart file, skip and continue?"): | ||
123 | msger.warning("%s" % err) | ||
124 | pass | ||
125 | else: | ||
126 | raise errors.KsError("%s" % err) | ||
127 | |||
128 | return ks | ||
129 | |||
130 | class KickstartConfig(object): | ||
131 | """A base class for applying kickstart configurations to a system.""" | ||
132 | def __init__(self, instroot): | ||
133 | self.instroot = instroot | ||
134 | |||
135 | def path(self, subpath): | ||
136 | return self.instroot + subpath | ||
137 | |||
138 | def _check_sysconfig(self): | ||
139 | if not os.path.exists(self.path("/etc/sysconfig")): | ||
140 | fs.makedirs(self.path("/etc/sysconfig")) | ||
141 | |||
142 | def chroot(self): | ||
143 | os.chroot(self.instroot) | ||
144 | os.chdir("/") | ||
145 | |||
146 | def call(self, args): | ||
147 | if not os.path.exists("%s/%s" %(self.instroot, args[0])): | ||
148 | raise errors.KsError("Can't find %s in chroot" % args[0]) | ||
149 | subprocess.call(args, preexec_fn = self.chroot) | ||
150 | |||
151 | def apply(self): | ||
152 | pass | ||
153 | |||
154 | class LanguageConfig(KickstartConfig): | ||
155 | """A class to apply a kickstart language configuration to a system.""" | ||
156 | @apply_wrapper | ||
157 | def apply(self, kslang): | ||
158 | self._check_sysconfig() | ||
159 | if kslang.lang: | ||
160 | f = open(self.path("/etc/sysconfig/i18n"), "w+") | ||
161 | f.write("LANG=\"" + kslang.lang + "\"\n") | ||
162 | f.close() | ||
163 | |||
164 | class KeyboardConfig(KickstartConfig): | ||
165 | """A class to apply a kickstart keyboard configuration to a system.""" | ||
166 | @apply_wrapper | ||
167 | def apply(self, kskeyboard): | ||
168 | # | ||
169 | # FIXME: | ||
170 | # should this impact the X keyboard config too? | ||
171 | # or do we want to make X be able to do this mapping? | ||
172 | # | ||
173 | #k = rhpl.keyboard.Keyboard() | ||
174 | #if kskeyboard.keyboard: | ||
175 | # k.set(kskeyboard.keyboard) | ||
176 | #k.write(self.instroot) | ||
177 | pass | ||
178 | |||
179 | class TimezoneConfig(KickstartConfig): | ||
180 | """A class to apply a kickstart timezone configuration to a system.""" | ||
181 | @apply_wrapper | ||
182 | def apply(self, kstimezone): | ||
183 | self._check_sysconfig() | ||
184 | tz = kstimezone.timezone or "America/New_York" | ||
185 | utc = str(kstimezone.isUtc) | ||
186 | |||
187 | f = open(self.path("/etc/sysconfig/clock"), "w+") | ||
188 | f.write("ZONE=\"" + tz + "\"\n") | ||
189 | f.write("UTC=" + utc + "\n") | ||
190 | f.close() | ||
191 | tz_source = "/usr/share/zoneinfo/%s" % (tz) | ||
192 | tz_dest = "/etc/localtime" | ||
193 | try: | ||
194 | cpcmd = fs.find_binary_inchroot('cp', self.instroot) | ||
195 | if cpcmd: | ||
196 | self.call([cpcmd, "-f", tz_source, tz_dest]) | ||
197 | else: | ||
198 | cpcmd = fs.find_binary_path('cp') | ||
199 | subprocess.call([cpcmd, "-f", | ||
200 | self.path(tz_source), | ||
201 | self.path(tz_dest)]) | ||
202 | except (IOError, OSError), (errno, msg): | ||
203 | raise errors.KsError("Timezone setting error: %s" % msg) | ||
204 | |||
205 | class AuthConfig(KickstartConfig): | ||
206 | """A class to apply a kickstart authconfig configuration to a system.""" | ||
207 | @apply_wrapper | ||
208 | def apply(self, ksauthconfig): | ||
209 | auth = ksauthconfig.authconfig or "--useshadow --enablemd5" | ||
210 | args = ["/usr/share/authconfig/authconfig.py", "--update", "--nostart"] | ||
211 | self.call(args + auth.split()) | ||
212 | |||
213 | class FirewallConfig(KickstartConfig): | ||
214 | """A class to apply a kickstart firewall configuration to a system.""" | ||
215 | @apply_wrapper | ||
216 | def apply(self, ksfirewall): | ||
217 | # | ||
218 | # FIXME: should handle the rest of the options | ||
219 | # | ||
220 | if not os.path.exists(self.path("/usr/sbin/lokkit")): | ||
221 | return | ||
222 | if ksfirewall.enabled: | ||
223 | status = "--enabled" | ||
224 | else: | ||
225 | status = "--disabled" | ||
226 | |||
227 | self.call(["/usr/sbin/lokkit", | ||
228 | "-f", "--quiet", "--nostart", status]) | ||
229 | |||
230 | class RootPasswordConfig(KickstartConfig): | ||
231 | """A class to apply a kickstart root password configuration to a system.""" | ||
232 | def unset(self): | ||
233 | self.call(["/usr/bin/passwd", "-d", "root"]) | ||
234 | |||
235 | def set_encrypted(self, password): | ||
236 | self.call(["/usr/sbin/usermod", "-p", password, "root"]) | ||
237 | |||
238 | def set_unencrypted(self, password): | ||
239 | for p in ("/bin/echo", "/usr/sbin/chpasswd"): | ||
240 | if not os.path.exists("%s/%s" %(self.instroot, p)): | ||
241 | raise errors.KsError("Unable to set unencrypted password due " | ||
242 | "to lack of %s" % p) | ||
243 | |||
244 | p1 = subprocess.Popen(["/bin/echo", "root:%s" %password], | ||
245 | stdout = subprocess.PIPE, | ||
246 | preexec_fn = self.chroot) | ||
247 | p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"], | ||
248 | stdin = p1.stdout, | ||
249 | stdout = subprocess.PIPE, | ||
250 | preexec_fn = self.chroot) | ||
251 | p2.communicate() | ||
252 | |||
253 | @apply_wrapper | ||
254 | def apply(self, ksrootpw): | ||
255 | if ksrootpw.isCrypted: | ||
256 | self.set_encrypted(ksrootpw.password) | ||
257 | elif ksrootpw.password != "": | ||
258 | self.set_unencrypted(ksrootpw.password) | ||
259 | else: | ||
260 | self.unset() | ||
261 | |||
262 | class UserConfig(KickstartConfig): | ||
263 | def set_empty_passwd(self, user): | ||
264 | self.call(["/usr/bin/passwd", "-d", user]) | ||
265 | |||
266 | def set_encrypted_passwd(self, user, password): | ||
267 | self.call(["/usr/sbin/usermod", "-p", "%s" % password, user]) | ||
268 | |||
269 | def set_unencrypted_passwd(self, user, password): | ||
270 | for p in ("/bin/echo", "/usr/sbin/chpasswd"): | ||
271 | if not os.path.exists("%s/%s" %(self.instroot, p)): | ||
272 | raise errors.KsError("Unable to set unencrypted password due " | ||
273 | "to lack of %s" % p) | ||
274 | |||
275 | p1 = subprocess.Popen(["/bin/echo", "%s:%s" %(user, password)], | ||
276 | stdout = subprocess.PIPE, | ||
277 | preexec_fn = self.chroot) | ||
278 | p2 = subprocess.Popen(["/usr/sbin/chpasswd", "-m"], | ||
279 | stdin = p1.stdout, | ||
280 | stdout = subprocess.PIPE, | ||
281 | preexec_fn = self.chroot) | ||
282 | p2.communicate() | ||
283 | |||
284 | def addUser(self, userconfig): | ||
285 | args = [ "/usr/sbin/useradd" ] | ||
286 | if userconfig.groups: | ||
287 | args += [ "--groups", string.join(userconfig.groups, ",") ] | ||
288 | if userconfig.name: | ||
289 | args += [ "-m"] | ||
290 | args += [ "-d", "/home/%s" % userconfig.name ] | ||
291 | args.append(userconfig.name) | ||
292 | try: | ||
293 | dev_null = os.open("/dev/null", os.O_WRONLY) | ||
294 | msger.debug('adding user with %s' % args) | ||
295 | subprocess.call(args, | ||
296 | stdout = dev_null, | ||
297 | stderr = dev_null, | ||
298 | preexec_fn = self.chroot) | ||
299 | os.close(dev_null) | ||
300 | except: | ||
301 | msger.warning('Cannot add user using "useradd"') | ||
302 | |||
303 | if userconfig.password not in (None, ""): | ||
304 | if userconfig.isCrypted: | ||
305 | self.set_encrypted_passwd(userconfig.name, | ||
306 | userconfig.password) | ||
307 | else: | ||
308 | self.set_unencrypted_passwd(userconfig.name, | ||
309 | userconfig.password) | ||
310 | else: | ||
311 | self.set_empty_passwd(userconfig.name) | ||
312 | else: | ||
313 | raise errors.KsError("Invalid kickstart command: %s" \ | ||
314 | % userconfig.__str__()) | ||
315 | |||
316 | @apply_wrapper | ||
317 | def apply(self, user): | ||
318 | for userconfig in user.userList: | ||
319 | self.addUser(userconfig) | ||
320 | |||
321 | class ServicesConfig(KickstartConfig): | ||
322 | """A class to apply a kickstart services configuration to a system.""" | ||
323 | @apply_wrapper | ||
324 | def apply(self, ksservices): | ||
325 | if not os.path.exists(self.path("/sbin/chkconfig")): | ||
326 | return | ||
327 | for s in ksservices.enabled: | ||
328 | self.call(["/sbin/chkconfig", s, "on"]) | ||
329 | for s in ksservices.disabled: | ||
330 | self.call(["/sbin/chkconfig", s, "off"]) | ||
331 | |||
332 | class XConfig(KickstartConfig): | ||
333 | """A class to apply a kickstart X configuration to a system.""" | ||
334 | @apply_wrapper | ||
335 | def apply(self, ksxconfig): | ||
336 | if ksxconfig.startX and os.path.exists(self.path("/etc/inittab")): | ||
337 | f = open(self.path("/etc/inittab"), "rw+") | ||
338 | buf = f.read() | ||
339 | buf = buf.replace("id:3:initdefault", "id:5:initdefault") | ||
340 | f.seek(0) | ||
341 | f.write(buf) | ||
342 | f.close() | ||
343 | if ksxconfig.defaultdesktop: | ||
344 | self._check_sysconfig() | ||
345 | f = open(self.path("/etc/sysconfig/desktop"), "w") | ||
346 | f.write("DESKTOP="+ksxconfig.defaultdesktop+"\n") | ||
347 | f.close() | ||
348 | |||
349 | class DesktopConfig(KickstartConfig): | ||
350 | """A class to apply a kickstart desktop configuration to a system.""" | ||
351 | @apply_wrapper | ||
352 | def apply(self, ksdesktop): | ||
353 | if ksdesktop.defaultdesktop: | ||
354 | self._check_sysconfig() | ||
355 | f = open(self.path("/etc/sysconfig/desktop"), "w") | ||
356 | f.write("DESKTOP="+ksdesktop.defaultdesktop+"\n") | ||
357 | f.close() | ||
358 | if os.path.exists(self.path("/etc/gdm/custom.conf")): | ||
359 | f = open(self.path("/etc/skel/.dmrc"), "w") | ||
360 | f.write("[Desktop]\n") | ||
361 | f.write("Session="+ksdesktop.defaultdesktop.lower()+"\n") | ||
362 | f.close() | ||
363 | if ksdesktop.session: | ||
364 | if os.path.exists(self.path("/etc/sysconfig/uxlaunch")): | ||
365 | f = open(self.path("/etc/sysconfig/uxlaunch"), "a+") | ||
366 | f.write("session="+ksdesktop.session.lower()+"\n") | ||
367 | f.close() | ||
368 | if ksdesktop.autologinuser: | ||
369 | self._check_sysconfig() | ||
370 | f = open(self.path("/etc/sysconfig/desktop"), "a+") | ||
371 | f.write("AUTOLOGIN_USER=" + ksdesktop.autologinuser + "\n") | ||
372 | f.close() | ||
373 | if os.path.exists(self.path("/etc/gdm/custom.conf")): | ||
374 | f = open(self.path("/etc/gdm/custom.conf"), "w") | ||
375 | f.write("[daemon]\n") | ||
376 | f.write("AutomaticLoginEnable=true\n") | ||
377 | f.write("AutomaticLogin=" + ksdesktop.autologinuser + "\n") | ||
378 | f.close() | ||
379 | |||
380 | class MoblinRepoConfig(KickstartConfig): | ||
381 | """A class to apply a kickstart desktop configuration to a system.""" | ||
382 | def __create_repo_section(self, repo, type, fd): | ||
383 | baseurl = None | ||
384 | mirrorlist = None | ||
385 | reposuffix = {"base":"", "debuginfo":"-debuginfo", "source":"-source"} | ||
386 | reponame = repo.name + reposuffix[type] | ||
387 | if type == "base": | ||
388 | if repo.baseurl: | ||
389 | baseurl = repo.baseurl | ||
390 | if repo.mirrorlist: | ||
391 | mirrorlist = repo.mirrorlist | ||
392 | |||
393 | elif type == "debuginfo": | ||
394 | if repo.baseurl: | ||
395 | if repo.baseurl.endswith("/"): | ||
396 | baseurl = os.path.dirname(os.path.dirname(repo.baseurl)) | ||
397 | else: | ||
398 | baseurl = os.path.dirname(repo.baseurl) | ||
399 | baseurl += "/debug" | ||
400 | |||
401 | if repo.mirrorlist: | ||
402 | variant = repo.mirrorlist[repo.mirrorlist.find("$"):] | ||
403 | mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")] | ||
404 | mirrorlist += "debug" + "-" + variant | ||
405 | |||
406 | elif type == "source": | ||
407 | if repo.baseurl: | ||
408 | if repo.baseurl.endswith("/"): | ||
409 | baseurl = os.path.dirname( | ||
410 | os.path.dirname( | ||
411 | os.path.dirname(repo.baseurl))) | ||
412 | else: | ||
413 | baseurl = os.path.dirname(os.path.dirname(repo.baseurl)) | ||
414 | baseurl += "/source" | ||
415 | |||
416 | if repo.mirrorlist: | ||
417 | variant = repo.mirrorlist[repo.mirrorlist.find("$"):] | ||
418 | mirrorlist = repo.mirrorlist[0:repo.mirrorlist.find("$")] | ||
419 | mirrorlist += "source" + "-" + variant | ||
420 | |||
421 | fd.write("[" + reponame + "]\n") | ||
422 | fd.write("name=" + reponame + "\n") | ||
423 | fd.write("failovermethod=priority\n") | ||
424 | if baseurl: | ||
425 | auth_url = re.compile(AUTH_URL_PTN) | ||
426 | m = auth_url.match(baseurl) | ||
427 | if m: | ||
428 | baseurl = "%s://%s" % (m.group('scheme'), m.group('url')) | ||
429 | fd.write("baseurl=" + baseurl + "\n") | ||
430 | if mirrorlist: | ||
431 | fd.write("mirrorlist=" + mirrorlist + "\n") | ||
432 | """ Skip saving proxy settings """ | ||
433 | #if repo.proxy: | ||
434 | # fd.write("proxy=" + repo.proxy + "\n") | ||
435 | #if repo.proxy_username: | ||
436 | # fd.write("proxy_username=" + repo.proxy_username + "\n") | ||
437 | #if repo.proxy_password: | ||
438 | # fd.write("proxy_password=" + repo.proxy_password + "\n") | ||
439 | if repo.gpgkey: | ||
440 | fd.write("gpgkey=" + repo.gpgkey + "\n") | ||
441 | fd.write("gpgcheck=1\n") | ||
442 | else: | ||
443 | fd.write("gpgcheck=0\n") | ||
444 | if type == "source" or type == "debuginfo" or repo.disable: | ||
445 | fd.write("enabled=0\n") | ||
446 | else: | ||
447 | fd.write("enabled=1\n") | ||
448 | fd.write("\n") | ||
449 | |||
450 | def __create_repo_file(self, repo, repodir): | ||
451 | fs.makedirs(self.path(repodir)) | ||
452 | f = open(self.path(repodir + "/" + repo.name + ".repo"), "w") | ||
453 | self.__create_repo_section(repo, "base", f) | ||
454 | if repo.debuginfo: | ||
455 | self.__create_repo_section(repo, "debuginfo", f) | ||
456 | if repo.source: | ||
457 | self.__create_repo_section(repo, "source", f) | ||
458 | f.close() | ||
459 | |||
460 | @apply_wrapper | ||
461 | def apply(self, ksrepo, repodata, repourl): | ||
462 | for repo in ksrepo.repoList: | ||
463 | if repo.name in repourl: | ||
464 | repo.baseurl = repourl[repo.name] | ||
465 | if repo.save: | ||
466 | #self.__create_repo_file(repo, "/etc/yum.repos.d") | ||
467 | self.__create_repo_file(repo, "/etc/zypp/repos.d") | ||
468 | """ Import repo gpg keys """ | ||
469 | if repodata: | ||
470 | for repo in repodata: | ||
471 | if repo['repokey']: | ||
472 | runner.quiet(['rpm', | ||
473 | "--root=%s" % self.instroot, | ||
474 | "--import", | ||
475 | repo['repokey']]) | ||
476 | |||
477 | class RPMMacroConfig(KickstartConfig): | ||
478 | """A class to apply the specified rpm macros to the filesystem""" | ||
479 | @apply_wrapper | ||
480 | def apply(self, ks): | ||
481 | if not ks: | ||
482 | return | ||
483 | if not os.path.exists(self.path("/etc/rpm")): | ||
484 | os.mkdir(self.path("/etc/rpm")) | ||
485 | f = open(self.path("/etc/rpm/macros.imgcreate"), "w+") | ||
486 | if exclude_docs(ks): | ||
487 | f.write("%_excludedocs 1\n") | ||
488 | f.write("%__file_context_path %{nil}\n") | ||
489 | if inst_langs(ks) != None: | ||
490 | f.write("%_install_langs ") | ||
491 | f.write(inst_langs(ks)) | ||
492 | f.write("\n") | ||
493 | f.close() | ||
494 | |||
495 | class NetworkConfig(KickstartConfig): | ||
496 | """A class to apply a kickstart network configuration to a system.""" | ||
497 | def write_ifcfg(self, network): | ||
498 | p = self.path("/etc/sysconfig/network-scripts/ifcfg-" + network.device) | ||
499 | |||
500 | f = file(p, "w+") | ||
501 | os.chmod(p, 0644) | ||
502 | |||
503 | f.write("DEVICE=%s\n" % network.device) | ||
504 | f.write("BOOTPROTO=%s\n" % network.bootProto) | ||
505 | |||
506 | if network.bootProto.lower() == "static": | ||
507 | if network.ip: | ||
508 | f.write("IPADDR=%s\n" % network.ip) | ||
509 | if network.netmask: | ||
510 | f.write("NETMASK=%s\n" % network.netmask) | ||
511 | |||
512 | if network.onboot: | ||
513 | f.write("ONBOOT=on\n") | ||
514 | else: | ||
515 | f.write("ONBOOT=off\n") | ||
516 | |||
517 | if network.essid: | ||
518 | f.write("ESSID=%s\n" % network.essid) | ||
519 | |||
520 | if network.ethtool: | ||
521 | if network.ethtool.find("autoneg") == -1: | ||
522 | network.ethtool = "autoneg off " + network.ethtool | ||
523 | f.write("ETHTOOL_OPTS=%s\n" % network.ethtool) | ||
524 | |||
525 | if network.bootProto.lower() == "dhcp": | ||
526 | if network.hostname: | ||
527 | f.write("DHCP_HOSTNAME=%s\n" % network.hostname) | ||
528 | if network.dhcpclass: | ||
529 | f.write("DHCP_CLASSID=%s\n" % network.dhcpclass) | ||
530 | |||
531 | if network.mtu: | ||
532 | f.write("MTU=%s\n" % network.mtu) | ||
533 | |||
534 | f.close() | ||
535 | |||
536 | def write_wepkey(self, network): | ||
537 | if not network.wepkey: | ||
538 | return | ||
539 | |||
540 | p = self.path("/etc/sysconfig/network-scripts/keys-" + network.device) | ||
541 | f = file(p, "w+") | ||
542 | os.chmod(p, 0600) | ||
543 | f.write("KEY=%s\n" % network.wepkey) | ||
544 | f.close() | ||
545 | |||
546 | def write_sysconfig(self, useipv6, hostname, gateway): | ||
547 | path = self.path("/etc/sysconfig/network") | ||
548 | f = file(path, "w+") | ||
549 | os.chmod(path, 0644) | ||
550 | |||
551 | f.write("NETWORKING=yes\n") | ||
552 | |||
553 | if useipv6: | ||
554 | f.write("NETWORKING_IPV6=yes\n") | ||
555 | else: | ||
556 | f.write("NETWORKING_IPV6=no\n") | ||
557 | |||
558 | if hostname: | ||
559 | f.write("HOSTNAME=%s\n" % hostname) | ||
560 | else: | ||
561 | f.write("HOSTNAME=localhost.localdomain\n") | ||
562 | |||
563 | if gateway: | ||
564 | f.write("GATEWAY=%s\n" % gateway) | ||
565 | |||
566 | f.close() | ||
567 | |||
568 | def write_hosts(self, hostname): | ||
569 | localline = "" | ||
570 | if hostname and hostname != "localhost.localdomain": | ||
571 | localline += hostname + " " | ||
572 | l = hostname.split(".") | ||
573 | if len(l) > 1: | ||
574 | localline += l[0] + " " | ||
575 | localline += "localhost.localdomain localhost" | ||
576 | |||
577 | path = self.path("/etc/hosts") | ||
578 | f = file(path, "w+") | ||
579 | os.chmod(path, 0644) | ||
580 | f.write("127.0.0.1\t\t%s\n" % localline) | ||
581 | f.write("::1\t\tlocalhost6.localdomain6 localhost6\n") | ||
582 | f.close() | ||
583 | |||
584 | def write_resolv(self, nodns, nameservers): | ||
585 | if nodns or not nameservers: | ||
586 | return | ||
587 | |||
588 | path = self.path("/etc/resolv.conf") | ||
589 | f = file(path, "w+") | ||
590 | os.chmod(path, 0644) | ||
591 | |||
592 | for ns in (nameservers): | ||
593 | if ns: | ||
594 | f.write("nameserver %s\n" % ns) | ||
595 | |||
596 | f.close() | ||
597 | |||
598 | @apply_wrapper | ||
599 | def apply(self, ksnet): | ||
600 | fs.makedirs(self.path("/etc/sysconfig/network-scripts")) | ||
601 | |||
602 | useipv6 = False | ||
603 | nodns = False | ||
604 | hostname = None | ||
605 | gateway = None | ||
606 | nameservers = None | ||
607 | |||
608 | for network in ksnet.network: | ||
609 | if not network.device: | ||
610 | raise errors.KsError("No --device specified with " | ||
611 | "network kickstart command") | ||
612 | |||
613 | if (network.onboot and network.bootProto.lower() != "dhcp" and | ||
614 | not (network.ip and network.netmask)): | ||
615 | raise errors.KsError("No IP address and/or netmask " | ||
616 | "specified with static " | ||
617 | "configuration for '%s'" % | ||
618 | network.device) | ||
619 | |||
620 | self.write_ifcfg(network) | ||
621 | self.write_wepkey(network) | ||
622 | |||
623 | if network.ipv6: | ||
624 | useipv6 = True | ||
625 | if network.nodns: | ||
626 | nodns = True | ||
627 | |||
628 | if network.hostname: | ||
629 | hostname = network.hostname | ||
630 | if network.gateway: | ||
631 | gateway = network.gateway | ||
632 | |||
633 | if network.nameserver: | ||
634 | nameservers = network.nameserver.split(",") | ||
635 | |||
636 | self.write_sysconfig(useipv6, hostname, gateway) | ||
637 | self.write_hosts(hostname) | ||
638 | self.write_resolv(nodns, nameservers) | ||
639 | |||
640 | def use_installerfw(ks, feature): | ||
641 | """ Check if the installer framework has to be used for a feature | ||
642 | "feature". """ | ||
643 | |||
644 | features = ks.handler.installerfw.features | ||
645 | if features: | ||
646 | if feature in features or "all" in features: | ||
647 | return True | ||
648 | return False | ||
649 | |||
650 | def get_image_size(ks, default = None): | ||
651 | __size = 0 | ||
652 | for p in ks.handler.partition.partitions: | ||
653 | if p.mountpoint == "/" and p.size: | ||
654 | __size = p.size | ||
655 | if __size > 0: | ||
656 | return int(__size) * 1024L * 1024L | ||
657 | else: | ||
658 | return default | ||
659 | |||
660 | def get_image_fstype(ks, default = None): | ||
661 | for p in ks.handler.partition.partitions: | ||
662 | if p.mountpoint == "/" and p.fstype: | ||
663 | return p.fstype | ||
664 | return default | ||
665 | |||
666 | def get_image_fsopts(ks, default = None): | ||
667 | for p in ks.handler.partition.partitions: | ||
668 | if p.mountpoint == "/" and p.fsopts: | ||
669 | return p.fsopts | ||
670 | return default | ||
671 | |||
672 | def get_modules(ks): | ||
673 | devices = [] | ||
674 | if isinstance(ks.handler.device, kscommands.device.FC3_Device): | ||
675 | devices.append(ks.handler.device) | ||
676 | else: | ||
677 | devices.extend(ks.handler.device.deviceList) | ||
678 | |||
679 | modules = [] | ||
680 | for device in devices: | ||
681 | if not device.moduleName: | ||
682 | continue | ||
683 | modules.extend(device.moduleName.split(":")) | ||
684 | |||
685 | return modules | ||
686 | |||
687 | def get_timeout(ks, default = None): | ||
688 | if not hasattr(ks.handler.bootloader, "timeout"): | ||
689 | return default | ||
690 | if ks.handler.bootloader.timeout is None: | ||
691 | return default | ||
692 | return int(ks.handler.bootloader.timeout) | ||
693 | |||
694 | def get_kernel_args(ks, default = "ro rd.live.image"): | ||
695 | if not hasattr(ks.handler.bootloader, "appendLine"): | ||
696 | return default | ||
697 | if ks.handler.bootloader.appendLine is None: | ||
698 | return default | ||
699 | return "%s %s" %(default, ks.handler.bootloader.appendLine) | ||
700 | |||
701 | def get_menu_args(ks, default = ""): | ||
702 | if not hasattr(ks.handler.bootloader, "menus"): | ||
703 | return default | ||
704 | if ks.handler.bootloader.menus in (None, ""): | ||
705 | return default | ||
706 | return "%s" % ks.handler.bootloader.menus | ||
707 | |||
708 | def get_default_kernel(ks, default = None): | ||
709 | if not hasattr(ks.handler.bootloader, "default"): | ||
710 | return default | ||
711 | if not ks.handler.bootloader.default: | ||
712 | return default | ||
713 | return ks.handler.bootloader.default | ||
714 | |||
715 | def get_repos(ks, repo_urls=None): | ||
716 | repos = {} | ||
717 | for repo in ks.handler.repo.repoList: | ||
718 | inc = [] | ||
719 | if hasattr(repo, "includepkgs"): | ||
720 | inc.extend(repo.includepkgs) | ||
721 | |||
722 | exc = [] | ||
723 | if hasattr(repo, "excludepkgs"): | ||
724 | exc.extend(repo.excludepkgs) | ||
725 | |||
726 | baseurl = repo.baseurl | ||
727 | mirrorlist = repo.mirrorlist | ||
728 | |||
729 | if repo_urls and repo.name in repo_urls: | ||
730 | baseurl = repo_urls[repo.name] | ||
731 | mirrorlist = None | ||
732 | |||
733 | if repos.has_key(repo.name): | ||
734 | msger.warning("Overriding already specified repo %s" %(repo.name,)) | ||
735 | |||
736 | proxy = None | ||
737 | if hasattr(repo, "proxy"): | ||
738 | proxy = repo.proxy | ||
739 | proxy_username = None | ||
740 | if hasattr(repo, "proxy_username"): | ||
741 | proxy_username = repo.proxy_username | ||
742 | proxy_password = None | ||
743 | if hasattr(repo, "proxy_password"): | ||
744 | proxy_password = repo.proxy_password | ||
745 | if hasattr(repo, "debuginfo"): | ||
746 | debuginfo = repo.debuginfo | ||
747 | if hasattr(repo, "source"): | ||
748 | source = repo.source | ||
749 | if hasattr(repo, "gpgkey"): | ||
750 | gpgkey = repo.gpgkey | ||
751 | if hasattr(repo, "disable"): | ||
752 | disable = repo.disable | ||
753 | ssl_verify = True | ||
754 | if hasattr(repo, "ssl_verify"): | ||
755 | ssl_verify = repo.ssl_verify == "yes" | ||
756 | nocache = False | ||
757 | if hasattr(repo, "nocache"): | ||
758 | nocache = repo.nocache | ||
759 | cost = None | ||
760 | if hasattr(repo, "cost"): | ||
761 | cost = repo.cost | ||
762 | priority = None | ||
763 | if hasattr(repo, "priority"): | ||
764 | priority = repo.priority | ||
765 | |||
766 | repos[repo.name] = (repo.name, baseurl, mirrorlist, inc, exc, | ||
767 | proxy, proxy_username, proxy_password, debuginfo, | ||
768 | source, gpgkey, disable, ssl_verify, nocache, | ||
769 | cost, priority) | ||
770 | |||
771 | return repos.values() | ||
772 | |||
773 | def convert_method_to_repo(ks): | ||
774 | try: | ||
775 | ks.handler.repo.methodToRepo() | ||
776 | except (AttributeError, kserrors.KickstartError): | ||
777 | pass | ||
778 | |||
779 | def get_attachment(ks, required=()): | ||
780 | return ks.handler.attachment.packageList + list(required) | ||
781 | |||
782 | def get_pre_packages(ks, required=()): | ||
783 | return ks.handler.prepackages.packageList + list(required) | ||
784 | |||
785 | def get_packages(ks, required=()): | ||
786 | return ks.handler.packages.packageList + list(required) | ||
787 | |||
788 | def get_groups(ks, required=()): | ||
789 | return ks.handler.packages.groupList + list(required) | ||
790 | |||
791 | def get_excluded(ks, required=()): | ||
792 | return ks.handler.packages.excludedList + list(required) | ||
793 | |||
794 | def get_partitions(ks): | ||
795 | return ks.handler.partition.partitions | ||
796 | |||
797 | def ignore_missing(ks): | ||
798 | return ks.handler.packages.handleMissing == ksconstants.KS_MISSING_IGNORE | ||
799 | |||
800 | def exclude_docs(ks): | ||
801 | return ks.handler.packages.excludeDocs | ||
802 | |||
803 | def inst_langs(ks): | ||
804 | if hasattr(ks.handler.packages, "instLange"): | ||
805 | return ks.handler.packages.instLange | ||
806 | elif hasattr(ks.handler.packages, "instLangs"): | ||
807 | return ks.handler.packages.instLangs | ||
808 | return "" | ||
809 | |||
810 | def get_post_scripts(ks): | ||
811 | scripts = [] | ||
812 | for s in ks.handler.scripts: | ||
813 | if s.type != ksparser.KS_SCRIPT_POST: | ||
814 | continue | ||
815 | scripts.append(s) | ||
816 | return scripts | ||
817 | |||
818 | def add_repo(ks, repostr): | ||
819 | args = repostr.split() | ||
820 | repoobj = ks.handler.repo.parse(args[1:]) | ||
821 | if repoobj and repoobj not in ks.handler.repo.repoList: | ||
822 | ks.handler.repo.repoList.append(repoobj) | ||
823 | |||
824 | def remove_all_repos(ks): | ||
825 | while len(ks.handler.repo.repoList) != 0: | ||
826 | del ks.handler.repo.repoList[0] | ||
827 | |||
828 | def remove_duplicate_repos(ks): | ||
829 | i = 0 | ||
830 | j = i + 1 | ||
831 | while True: | ||
832 | if len(ks.handler.repo.repoList) < 2: | ||
833 | break | ||
834 | if i >= len(ks.handler.repo.repoList) - 1: | ||
835 | break | ||
836 | name = ks.handler.repo.repoList[i].name | ||
837 | baseurl = ks.handler.repo.repoList[i].baseurl | ||
838 | if j < len(ks.handler.repo.repoList): | ||
839 | if (ks.handler.repo.repoList[j].name == name or \ | ||
840 | ks.handler.repo.repoList[j].baseurl == baseurl): | ||
841 | del ks.handler.repo.repoList[j] | ||
842 | else: | ||
843 | j += 1 | ||
844 | if j >= len(ks.handler.repo.repoList): | ||
845 | i += 1 | ||
846 | j = i + 1 | ||
847 | else: | ||
848 | i += 1 | ||
849 | j = i + 1 | ||
850 | |||
851 | def resolve_groups(creatoropts, repometadata): | ||
852 | iszypp = False | ||
853 | if 'zypp' == creatoropts['pkgmgr']: | ||
854 | iszypp = True | ||
855 | ks = creatoropts['ks'] | ||
856 | |||
857 | for repo in repometadata: | ||
858 | """ Mustn't replace group with package list if repo is ready for the | ||
859 | corresponding package manager. | ||
860 | """ | ||
861 | |||
862 | if iszypp and repo["patterns"]: | ||
863 | continue | ||
864 | if not iszypp and repo["comps"]: | ||
865 | continue | ||
866 | |||
867 | # But we also must handle such cases, use zypp but repo only has comps, | ||
868 | # use yum but repo only has patterns, use zypp but use_comps is true, | ||
869 | # use yum but use_comps is false. | ||
870 | groupfile = None | ||
871 | if iszypp and repo["comps"]: | ||
872 | groupfile = repo["comps"] | ||
873 | get_pkglist_handler = misc.get_pkglist_in_comps | ||
874 | if not iszypp and repo["patterns"]: | ||
875 | groupfile = repo["patterns"] | ||
876 | get_pkglist_handler = misc.get_pkglist_in_patterns | ||
877 | |||
878 | if groupfile: | ||
879 | i = 0 | ||
880 | while True: | ||
881 | if i >= len(ks.handler.packages.groupList): | ||
882 | break | ||
883 | pkglist = get_pkglist_handler( | ||
884 | ks.handler.packages.groupList[i].name, | ||
885 | groupfile) | ||
886 | if pkglist: | ||
887 | del ks.handler.packages.groupList[i] | ||
888 | for pkg in pkglist: | ||
889 | if pkg not in ks.handler.packages.packageList: | ||
890 | ks.handler.packages.packageList.append(pkg) | ||
891 | else: | ||
892 | i = i + 1 | ||
diff --git a/scripts/lib/mic/kickstart/custom_commands/__init__.py b/scripts/lib/mic/kickstart/custom_commands/__init__.py new file mode 100644 index 0000000000..6aed0ff6fa --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/__init__.py | |||
@@ -0,0 +1,17 @@ | |||
1 | from desktop import Mic_Desktop | ||
2 | from micrepo import Mic_Repo, Mic_RepoData | ||
3 | from micpartition import Mic_Partition | ||
4 | from micpartition import Mic_PartData | ||
5 | from installerfw import Mic_installerfw | ||
6 | from partition import Wic_Partition | ||
7 | |||
8 | __all__ = ( | ||
9 | "Mic_Desktop", | ||
10 | "Mic_Repo", | ||
11 | "Mic_RepoData", | ||
12 | "Mic_Partition", | ||
13 | "Mic_PartData", | ||
14 | "Mic_installerfw", | ||
15 | "Wic_Partition", | ||
16 | "Wic_PartData", | ||
17 | ) | ||
diff --git a/scripts/lib/mic/kickstart/custom_commands/desktop.py b/scripts/lib/mic/kickstart/custom_commands/desktop.py new file mode 100644 index 0000000000..c8bd647ae3 --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/desktop.py | |||
@@ -0,0 +1,95 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2008, 2009, 2010 Intel, Inc. | ||
4 | # | ||
5 | # Yi Yang <yi.y.yang@intel.com> | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify it | ||
8 | # under the terms of the GNU General Public License as published by the Free | ||
9 | # Software Foundation; version 2 of the License | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, but | ||
12 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
13 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
14 | # for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
18 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | |||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | |||
24 | class Mic_Desktop(KickstartCommand): | ||
25 | def __init__(self, writePriority=0, | ||
26 | defaultdesktop=None, | ||
27 | defaultdm=None, | ||
28 | autologinuser=None, | ||
29 | session=None): | ||
30 | |||
31 | KickstartCommand.__init__(self, writePriority) | ||
32 | |||
33 | self.__new_version = False | ||
34 | self.op = self._getParser() | ||
35 | |||
36 | self.defaultdesktop = defaultdesktop | ||
37 | self.autologinuser = autologinuser | ||
38 | self.defaultdm = defaultdm | ||
39 | self.session = session | ||
40 | |||
41 | def __str__(self): | ||
42 | retval = "" | ||
43 | |||
44 | if self.defaultdesktop != None: | ||
45 | retval += " --defaultdesktop=%s" % self.defaultdesktop | ||
46 | if self.session != None: | ||
47 | retval += " --session=\"%s\"" % self.session | ||
48 | if self.autologinuser != None: | ||
49 | retval += " --autologinuser=%s" % self.autologinuser | ||
50 | if self.defaultdm != None: | ||
51 | retval += " --defaultdm=%s" % self.defaultdm | ||
52 | |||
53 | if retval != "": | ||
54 | retval = "# Default Desktop Settings\ndesktop %s\n" % retval | ||
55 | |||
56 | return retval | ||
57 | |||
58 | def _getParser(self): | ||
59 | try: | ||
60 | op = KSOptionParser(lineno=self.lineno) | ||
61 | except TypeError: | ||
62 | # the latest version has not lineno argument | ||
63 | op = KSOptionParser() | ||
64 | self.__new_version = True | ||
65 | |||
66 | op.add_option("--defaultdesktop", dest="defaultdesktop", | ||
67 | action="store", | ||
68 | type="string", | ||
69 | nargs=1) | ||
70 | op.add_option("--autologinuser", dest="autologinuser", | ||
71 | action="store", | ||
72 | type="string", | ||
73 | nargs=1) | ||
74 | op.add_option("--defaultdm", dest="defaultdm", | ||
75 | action="store", | ||
76 | type="string", | ||
77 | nargs=1) | ||
78 | op.add_option("--session", dest="session", | ||
79 | action="store", | ||
80 | type="string", | ||
81 | nargs=1) | ||
82 | return op | ||
83 | |||
84 | def parse(self, args): | ||
85 | if self.__new_version: | ||
86 | (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
87 | else: | ||
88 | (opts, extra) = self.op.parse_args(args=args) | ||
89 | |||
90 | if extra: | ||
91 | m = _("Unexpected arguments to %(command)s command: %(options)s") \ | ||
92 | % {"command": "desktop", "options": extra} | ||
93 | raise KickstartValueError, formatErrorMsg(self.lineno, msg=m) | ||
94 | |||
95 | self._setToSelf(self.op, opts) | ||
diff --git a/scripts/lib/mic/kickstart/custom_commands/installerfw.py b/scripts/lib/mic/kickstart/custom_commands/installerfw.py new file mode 100644 index 0000000000..2466f1dc07 --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/installerfw.py | |||
@@ -0,0 +1,63 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2013 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | from pykickstart.base import * | ||
19 | from pykickstart.options import * | ||
20 | |||
21 | class Mic_installerfw(KickstartCommand): | ||
22 | """ This class implements the "installerfw" KS option. The argument | ||
23 | of the option is a comman-separated list of MIC features which have to be | ||
24 | disabled and instead, will be done in the installer. For example, | ||
25 | "installerfw=extlinux" disables all the MIC code which installs extlinux to | ||
26 | the target images, and instead, the extlinux or whatever boot-loader will | ||
27 | be installed by the installer instead. | ||
28 | |||
29 | The installer is a tool which is external to MIC, it comes from the | ||
30 | installation repositories and can be executed by MIC in order to perform | ||
31 | various configuration actions. The main point here is to make sure MIC has | ||
32 | no hard-wired knoledge about the target OS configuration. """ | ||
33 | |||
34 | removedKeywords = KickstartCommand.removedKeywords | ||
35 | removedAttrs = KickstartCommand.removedAttrs | ||
36 | |||
37 | def __init__(self, *args, **kwargs): | ||
38 | KickstartCommand.__init__(self, *args, **kwargs) | ||
39 | self.op = self._getParser() | ||
40 | self.features = kwargs.get("installerfw", None) | ||
41 | |||
42 | def __str__(self): | ||
43 | retval = KickstartCommand.__str__(self) | ||
44 | |||
45 | if self.features: | ||
46 | retval += "# Enable installer framework features\ninstallerfw\n" | ||
47 | |||
48 | return retval | ||
49 | |||
50 | def _getParser(self): | ||
51 | op = KSOptionParser() | ||
52 | return op | ||
53 | |||
54 | def parse(self, args): | ||
55 | (_, extra) = self.op.parse_args(args=args, lineno=self.lineno) | ||
56 | |||
57 | if len(extra) != 1: | ||
58 | msg = "Kickstart command \"installerfw\" requires one " \ | ||
59 | "argumet - a list of legacy features to disable" | ||
60 | raise KickstartValueError, formatErrorMsg(self.lineno, msg = msg) | ||
61 | |||
62 | self.features = extra[0].split(",") | ||
63 | return self | ||
diff --git a/scripts/lib/mic/kickstart/custom_commands/micboot.py b/scripts/lib/mic/kickstart/custom_commands/micboot.py new file mode 100644 index 0000000000..66d1678aa7 --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/micboot.py | |||
@@ -0,0 +1,49 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2008, 2009, 2010 Intel, Inc. | ||
4 | # | ||
5 | # Anas Nashif | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify it | ||
8 | # under the terms of the GNU General Public License as published by the Free | ||
9 | # Software Foundation; version 2 of the License | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, but | ||
12 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
13 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
14 | # for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
18 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | |||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | from pykickstart.commands.bootloader import * | ||
24 | |||
25 | class Mic_Bootloader(F8_Bootloader): | ||
26 | def __init__(self, writePriority=10, appendLine="", driveorder=None, | ||
27 | forceLBA=False, location="", md5pass="", password="", | ||
28 | upgrade=False, menus=""): | ||
29 | F8_Bootloader.__init__(self, writePriority, appendLine, driveorder, | ||
30 | forceLBA, location, md5pass, password, upgrade) | ||
31 | |||
32 | self.menus = "" | ||
33 | self.ptable = "msdos" | ||
34 | |||
35 | def _getArgsAsStr(self): | ||
36 | ret = F8_Bootloader._getArgsAsStr(self) | ||
37 | |||
38 | if self.menus == "": | ||
39 | ret += " --menus=%s" %(self.menus,) | ||
40 | if self.ptable: | ||
41 | ret += " --ptable=\"%s\"" %(self.ptable,) | ||
42 | return ret | ||
43 | |||
44 | def _getParser(self): | ||
45 | op = F8_Bootloader._getParser(self) | ||
46 | op.add_option("--menus", dest="menus") | ||
47 | op.add_option("--ptable", dest="ptable", type="string") | ||
48 | return op | ||
49 | |||
diff --git a/scripts/lib/mic/kickstart/custom_commands/micpartition.py b/scripts/lib/mic/kickstart/custom_commands/micpartition.py new file mode 100644 index 0000000000..59a87fb486 --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/micpartition.py | |||
@@ -0,0 +1,57 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Marko Saukko <marko.saukko@cybercom.com> | ||
4 | # | ||
5 | # Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | ||
6 | # | ||
7 | # This copyrighted material is made available to anyone wishing to use, modify, | ||
8 | # copy, or redistribute it subject to the terms and conditions of the GNU | ||
9 | # General Public License v.2. This program is distributed in the hope that it | ||
10 | # will be useful, but WITHOUT ANY WARRANTY expressed or implied, including the | ||
11 | # implied warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
12 | # See the GNU General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along with | ||
15 | # this program; if not, write to the Free Software Foundation, Inc., 51 | ||
16 | # Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||
17 | |||
18 | from pykickstart.commands.partition import * | ||
19 | |||
20 | class Mic_PartData(FC4_PartData): | ||
21 | removedKeywords = FC4_PartData.removedKeywords | ||
22 | removedAttrs = FC4_PartData.removedAttrs | ||
23 | |||
24 | def __init__(self, *args, **kwargs): | ||
25 | FC4_PartData.__init__(self, *args, **kwargs) | ||
26 | self.deleteRemovedAttrs() | ||
27 | self.align = kwargs.get("align", None) | ||
28 | self.extopts = kwargs.get("extopts", None) | ||
29 | self.part_type = kwargs.get("part_type", None) | ||
30 | |||
31 | def _getArgsAsStr(self): | ||
32 | retval = FC4_PartData._getArgsAsStr(self) | ||
33 | |||
34 | if self.align: | ||
35 | retval += " --align" | ||
36 | if self.extopts: | ||
37 | retval += " --extoptions=%s" % self.extopts | ||
38 | if self.part_type: | ||
39 | retval += " --part-type=%s" % self.part_type | ||
40 | |||
41 | return retval | ||
42 | |||
43 | class Mic_Partition(FC4_Partition): | ||
44 | removedKeywords = FC4_Partition.removedKeywords | ||
45 | removedAttrs = FC4_Partition.removedAttrs | ||
46 | |||
47 | def _getParser(self): | ||
48 | op = FC4_Partition._getParser(self) | ||
49 | # The alignment value is given in kBytes. e.g., value 8 means that | ||
50 | # the partition is aligned to start from 8096 byte boundary. | ||
51 | op.add_option("--align", type="int", action="store", dest="align", | ||
52 | default=None) | ||
53 | op.add_option("--extoptions", type="string", action="store", dest="extopts", | ||
54 | default=None) | ||
55 | op.add_option("--part-type", type="string", action="store", dest="part_type", | ||
56 | default=None) | ||
57 | return op | ||
diff --git a/scripts/lib/mic/kickstart/custom_commands/micrepo.py b/scripts/lib/mic/kickstart/custom_commands/micrepo.py new file mode 100644 index 0000000000..b31576e400 --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/micrepo.py | |||
@@ -0,0 +1,127 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2008, 2009, 2010 Intel, Inc. | ||
4 | # | ||
5 | # Yi Yang <yi.y.yang@intel.com> | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify it | ||
8 | # under the terms of the GNU General Public License as published by the Free | ||
9 | # Software Foundation; version 2 of the License | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, but | ||
12 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
13 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
14 | # for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
18 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | |||
20 | from pykickstart.base import * | ||
21 | from pykickstart.errors import * | ||
22 | from pykickstart.options import * | ||
23 | from pykickstart.commands.repo import * | ||
24 | |||
25 | class Mic_RepoData(F8_RepoData): | ||
26 | |||
27 | def __init__(self, baseurl="", mirrorlist=None, name="", priority=None, | ||
28 | includepkgs=(), excludepkgs=(), save=False, proxy=None, | ||
29 | proxy_username=None, proxy_password=None, debuginfo=False, | ||
30 | source=False, gpgkey=None, disable=False, ssl_verify="yes", | ||
31 | nocache=False): | ||
32 | kw = {} | ||
33 | # F8_RepoData keywords | ||
34 | if includepkgs: | ||
35 | kw['includepkgs'] = includepkgs | ||
36 | if excludepkgs: | ||
37 | kw['excludepkgs'] = excludepkgs | ||
38 | |||
39 | #FC6_RepoData keywords | ||
40 | if baseurl: | ||
41 | kw['baseurl'] = baseurl | ||
42 | if mirrorlist: | ||
43 | kw['mirrorlist'] = mirrorlist | ||
44 | if name: | ||
45 | kw['name'] = name | ||
46 | |||
47 | F8_RepoData.__init__(self, **kw) | ||
48 | self.save = save | ||
49 | self.proxy = proxy | ||
50 | self.proxy_username = proxy_username | ||
51 | self.proxy_password = proxy_password | ||
52 | self.debuginfo = debuginfo | ||
53 | self.disable = disable | ||
54 | self.source = source | ||
55 | self.gpgkey = gpgkey | ||
56 | self.ssl_verify = ssl_verify.lower() | ||
57 | self.priority = priority | ||
58 | self.nocache = nocache | ||
59 | |||
60 | def _getArgsAsStr(self): | ||
61 | retval = F8_RepoData._getArgsAsStr(self) | ||
62 | |||
63 | if self.save: | ||
64 | retval += " --save" | ||
65 | if self.proxy: | ||
66 | retval += " --proxy=%s" % self.proxy | ||
67 | if self.proxy_username: | ||
68 | retval += " --proxyuser=%s" % self.proxy_username | ||
69 | if self.proxy_password: | ||
70 | retval += " --proxypasswd=%s" % self.proxy_password | ||
71 | if self.debuginfo: | ||
72 | retval += " --debuginfo" | ||
73 | if self.source: | ||
74 | retval += " --source" | ||
75 | if self.gpgkey: | ||
76 | retval += " --gpgkey=%s" % self.gpgkey | ||
77 | if self.disable: | ||
78 | retval += " --disable" | ||
79 | if self.ssl_verify: | ||
80 | retval += " --ssl_verify=%s" % self.ssl_verify | ||
81 | if self.priority: | ||
82 | retval += " --priority=%s" % self.priority | ||
83 | if self.nocache: | ||
84 | retval += " --nocache" | ||
85 | |||
86 | return retval | ||
87 | |||
88 | class Mic_Repo(F8_Repo): | ||
89 | def __init__(self, writePriority=0, repoList=None): | ||
90 | F8_Repo.__init__(self, writePriority, repoList) | ||
91 | |||
92 | def __str__(self): | ||
93 | retval = "" | ||
94 | for repo in self.repoList: | ||
95 | retval += repo.__str__() | ||
96 | |||
97 | return retval | ||
98 | |||
99 | def _getParser(self): | ||
100 | def list_cb (option, opt_str, value, parser): | ||
101 | for d in value.split(','): | ||
102 | parser.values.ensure_value(option.dest, []).append(d) | ||
103 | |||
104 | op = F8_Repo._getParser(self) | ||
105 | op.add_option("--save", action="store_true", dest="save", | ||
106 | default=False) | ||
107 | op.add_option("--proxy", type="string", action="store", dest="proxy", | ||
108 | default=None, nargs=1) | ||
109 | op.add_option("--proxyuser", type="string", action="store", | ||
110 | dest="proxy_username", default=None, nargs=1) | ||
111 | op.add_option("--proxypasswd", type="string", action="store", | ||
112 | dest="proxy_password", default=None, nargs=1) | ||
113 | op.add_option("--debuginfo", action="store_true", dest="debuginfo", | ||
114 | default=False) | ||
115 | op.add_option("--source", action="store_true", dest="source", | ||
116 | default=False) | ||
117 | op.add_option("--disable", action="store_true", dest="disable", | ||
118 | default=False) | ||
119 | op.add_option("--gpgkey", type="string", action="store", dest="gpgkey", | ||
120 | default=None, nargs=1) | ||
121 | op.add_option("--ssl_verify", type="string", action="store", | ||
122 | dest="ssl_verify", default="yes") | ||
123 | op.add_option("--priority", type="int", action="store", dest="priority", | ||
124 | default=None) | ||
125 | op.add_option("--nocache", action="store_true", dest="nocache", | ||
126 | default=False) | ||
127 | return op | ||
diff --git a/scripts/lib/mic/kickstart/custom_commands/partition.py b/scripts/lib/mic/kickstart/custom_commands/partition.py new file mode 100644 index 0000000000..450d2d492d --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/partition.py | |||
@@ -0,0 +1,482 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2013, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This module provides the OpenEmbedded partition object definitions. | ||
22 | # | ||
23 | # AUTHORS | ||
24 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
25 | # | ||
26 | |||
27 | import shutil | ||
28 | |||
29 | from pykickstart.commands.partition import * | ||
30 | from mic.utils.oe.misc import * | ||
31 | from mic.kickstart.custom_commands import * | ||
32 | from mic.plugin import pluginmgr | ||
33 | |||
34 | import os | ||
35 | from mic.utils.oe.package_manager import * | ||
36 | |||
37 | partition_methods = { | ||
38 | "do_install_pkgs":None, | ||
39 | "do_stage_partition":None, | ||
40 | "do_prepare_partition":None, | ||
41 | "do_configure_partition":None, | ||
42 | } | ||
43 | |||
44 | class Wic_PartData(Mic_PartData): | ||
45 | removedKeywords = Mic_PartData.removedKeywords | ||
46 | removedAttrs = Mic_PartData.removedAttrs | ||
47 | |||
48 | def __init__(self, *args, **kwargs): | ||
49 | Mic_PartData.__init__(self, *args, **kwargs) | ||
50 | self.deleteRemovedAttrs() | ||
51 | self.source = kwargs.get("source", None) | ||
52 | self.rootfs = kwargs.get("rootfs-dir", None) | ||
53 | self.source_file = "" | ||
54 | self.size = 0 | ||
55 | |||
56 | def _getArgsAsStr(self): | ||
57 | retval = Mic_PartData._getArgsAsStr(self) | ||
58 | |||
59 | if self.source: | ||
60 | retval += " --source=%s" % self.source | ||
61 | if self.rootfs: | ||
62 | retval += " --rootfs-dir=%s" % self.rootfs | ||
63 | |||
64 | return retval | ||
65 | |||
66 | def get_rootfs(self): | ||
67 | """ | ||
68 | Acessor for rootfs dir | ||
69 | """ | ||
70 | return self.rootfs | ||
71 | |||
72 | def set_rootfs(self, rootfs): | ||
73 | """ | ||
74 | Acessor for actual rootfs dir, which must be set by source | ||
75 | plugins. | ||
76 | """ | ||
77 | self.rootfs = rootfs | ||
78 | |||
79 | def get_size(self): | ||
80 | """ | ||
81 | Accessor for partition size, 0 or --size before set_size(). | ||
82 | """ | ||
83 | return self.size | ||
84 | |||
85 | def set_size(self, size): | ||
86 | """ | ||
87 | Accessor for actual partition size, which must be set by source | ||
88 | plugins. | ||
89 | """ | ||
90 | self.size = size | ||
91 | |||
92 | def set_source_file(self, source_file): | ||
93 | """ | ||
94 | Accessor for source_file, the location of the generated partition | ||
95 | image, which must be set by source plugins. | ||
96 | """ | ||
97 | self.source_file = source_file | ||
98 | |||
99 | def get_extra_block_count(self, current_blocks): | ||
100 | """ | ||
101 | The --size param is reflected in self.size (in MB), and we already | ||
102 | have current_blocks (1k) blocks, calculate and return the | ||
103 | number of (1k) blocks we need to add to get to --size, 0 if | ||
104 | we're already there or beyond. | ||
105 | """ | ||
106 | msger.debug("Requested partition size for %s: %d" % \ | ||
107 | (self.mountpoint, self.size)) | ||
108 | |||
109 | if not self.size: | ||
110 | return 0 | ||
111 | |||
112 | requested_blocks = self.size * 1024 | ||
113 | |||
114 | msger.debug("Requested blocks %d, current_blocks %d" % \ | ||
115 | (requested_blocks, current_blocks)) | ||
116 | |||
117 | if requested_blocks > current_blocks: | ||
118 | return requested_blocks - current_blocks | ||
119 | else: | ||
120 | return 0 | ||
121 | |||
122 | def install_pkgs(self, creator, cr_workdir, oe_builddir, rootfs_dir, | ||
123 | bootimg_dir, kernel_dir, native_sysroot): | ||
124 | """ | ||
125 | Prepare content for individual partitions, installing packages. | ||
126 | """ | ||
127 | |||
128 | if not self.source: | ||
129 | return | ||
130 | |||
131 | self._source_methods = pluginmgr.get_source_plugin_methods(self.source, partition_methods) | ||
132 | self._source_methods["do_install_pkgs"](self, creator, | ||
133 | cr_workdir, | ||
134 | oe_builddir, | ||
135 | rootfs_dir, | ||
136 | bootimg_dir, | ||
137 | kernel_dir, | ||
138 | native_sysroot) | ||
139 | |||
140 | def install_pkgs_ipk(self, cr_workdir, oe_builddir, rootfs_dir, | ||
141 | native_sysroot, packages, repourl): | ||
142 | """ | ||
143 | Install packages specified into wks file using opkg package manager. | ||
144 | This method is dependend on bb module. | ||
145 | """ | ||
146 | |||
147 | gVar = {} | ||
148 | gVar["DEPLOY_DIR_IPK"] = os.path.join(oe_builddir, "tmp/deploy/ipk") | ||
149 | |||
150 | # Run postinstall scripts even in offline mode | ||
151 | # Use the arch priority package rather than higher version one if more than one candidate is found. | ||
152 | #d.setVar("OPKG_ARGS", "--force_postinstall --prefer-arch-to-version") | ||
153 | gVar["OPKG_ARGS"] = "--force_postinstall" | ||
154 | |||
155 | # OPKG path relative to /output_path | ||
156 | gVar["OPKGLIBDIR"] = "var/lib" | ||
157 | |||
158 | source_url = repourl.split() | ||
159 | |||
160 | # Generate feed uri's names, it doesn't seem to matter what name they have | ||
161 | feed_uris = "" | ||
162 | cnt = 0 | ||
163 | archs = "" | ||
164 | for url in source_url: | ||
165 | feed_uris += "cl_def_feed%d##%s\n" % (cnt, url) | ||
166 | cnt += 1 | ||
167 | head, tail = os.path.split(url) | ||
168 | archs += " " + tail | ||
169 | |||
170 | # IPK_FEED_URIS with special formating defines the URI's used as source for packages | ||
171 | gVar['IPK_FEED_URIS'] = feed_uris | ||
172 | |||
173 | gVar['BUILD_IMAGES_FROM_FEEDS'] = "1" | ||
174 | |||
175 | # We need to provide sysroot for utilities | ||
176 | gVar['STAGING_DIR_NATIVE'] = native_sysroot | ||
177 | |||
178 | # Set WORKDIR for output | ||
179 | gVar['WORKDIR'] = cr_workdir | ||
180 | |||
181 | # Set TMPDIR for output | ||
182 | gVar['TMPDIR'] = os.path.join(cr_workdir, "tmp") | ||
183 | |||
184 | if 'ROOTFS_DIR' in rootfs_dir: | ||
185 | target_dir = rootfs_dir['ROOTFS_DIR'] | ||
186 | elif os.path.isdir(rootfs_dir): | ||
187 | target_dir = rootfs_dir | ||
188 | else: | ||
189 | msg = "Couldn't find --rootfs-dir=%s connection" | ||
190 | msg += " or it is not a valid path, exiting" | ||
191 | msger.error(msg % rootfs_dir) | ||
192 | |||
193 | # Need native sysroot /usr/bin/ for opkg-cl | ||
194 | # chnage PATH var to avoid issues with host tools | ||
195 | defpath = os.environ['PATH'] | ||
196 | os.environ['PATH'] = native_sysroot + "/usr/bin/" + ":/bin:/usr/bin:" | ||
197 | |||
198 | pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot | ||
199 | pseudo += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % target_dir | ||
200 | pseudo += "export PSEUDO_PASSWD=%s;" % target_dir | ||
201 | pseudo += "export PSEUDO_NOSYMLINKEXP=1;" | ||
202 | pseudo += "%s/usr/bin/pseudo " % native_sysroot | ||
203 | |||
204 | pm = WicOpkgPM(gVar, | ||
205 | target_dir, | ||
206 | 'opkg.conf', | ||
207 | archs, | ||
208 | pseudo, | ||
209 | native_sysroot) | ||
210 | |||
211 | pm.update() | ||
212 | |||
213 | pm.install(packages) | ||
214 | |||
215 | os.environ['PATH'] += defpath + ":" + native_sysroot + "/usr/bin/" | ||
216 | |||
217 | |||
218 | def prepare(self, cr, cr_workdir, oe_builddir, rootfs_dir, bootimg_dir, | ||
219 | kernel_dir, native_sysroot): | ||
220 | """ | ||
221 | Prepare content for individual partitions, depending on | ||
222 | partition command parameters. | ||
223 | """ | ||
224 | if not self.source: | ||
225 | if self.fstype and self.fstype == "swap": | ||
226 | self.prepare_swap_partition(cr_workdir, oe_builddir, | ||
227 | native_sysroot) | ||
228 | elif self.fstype: | ||
229 | self.prepare_empty_partition(cr_workdir, oe_builddir, | ||
230 | native_sysroot) | ||
231 | return | ||
232 | |||
233 | self._source_methods = pluginmgr.get_source_plugin_methods(self.source, partition_methods) | ||
234 | self._source_methods["do_configure_partition"](self, cr, cr_workdir, | ||
235 | oe_builddir, | ||
236 | bootimg_dir, | ||
237 | kernel_dir, | ||
238 | native_sysroot) | ||
239 | self._source_methods["do_stage_partition"](self, cr, cr_workdir, | ||
240 | oe_builddir, | ||
241 | bootimg_dir, kernel_dir, | ||
242 | native_sysroot) | ||
243 | self._source_methods["do_prepare_partition"](self, cr, cr_workdir, | ||
244 | oe_builddir, | ||
245 | bootimg_dir, kernel_dir, rootfs_dir, | ||
246 | native_sysroot) | ||
247 | |||
248 | def prepare_rootfs_from_fs_image(self, cr_workdir, oe_builddir, | ||
249 | rootfs_dir): | ||
250 | """ | ||
251 | Handle an already-created partition e.g. xxx.ext3 | ||
252 | """ | ||
253 | rootfs = oe_builddir | ||
254 | du_cmd = "du -Lbms %s" % rootfs | ||
255 | rc, out = exec_cmd(du_cmd) | ||
256 | rootfs_size = out.split()[0] | ||
257 | |||
258 | self.size = rootfs_size | ||
259 | self.source_file = rootfs | ||
260 | |||
261 | def prepare_rootfs(self, cr_workdir, oe_builddir, rootfs_dir, | ||
262 | native_sysroot): | ||
263 | """ | ||
264 | Prepare content for a rootfs partition i.e. create a partition | ||
265 | and fill it from a /rootfs dir. | ||
266 | |||
267 | Currently handles ext2/3/4 and btrfs. | ||
268 | """ | ||
269 | pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot | ||
270 | pseudo += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % rootfs_dir | ||
271 | pseudo += "export PSEUDO_PASSWD=%s;" % rootfs_dir | ||
272 | pseudo += "export PSEUDO_NOSYMLINKEXP=1;" | ||
273 | pseudo += "%s/usr/bin/pseudo " % native_sysroot | ||
274 | |||
275 | if self.fstype.startswith("ext"): | ||
276 | return self.prepare_rootfs_ext(cr_workdir, oe_builddir, | ||
277 | rootfs_dir, native_sysroot, | ||
278 | pseudo) | ||
279 | elif self.fstype.startswith("btrfs"): | ||
280 | return self.prepare_rootfs_btrfs(cr_workdir, oe_builddir, | ||
281 | rootfs_dir, native_sysroot, | ||
282 | pseudo) | ||
283 | |||
284 | def prepare_rootfs_ext(self, cr_workdir, oe_builddir, rootfs_dir, | ||
285 | native_sysroot, pseudo): | ||
286 | """ | ||
287 | Prepare content for an ext2/3/4 rootfs partition. | ||
288 | """ | ||
289 | |||
290 | image_rootfs = rootfs_dir | ||
291 | rootfs = "%s/rootfs_%s.%s" % (cr_workdir, self.label ,self.fstype) | ||
292 | |||
293 | du_cmd = "du -ks %s" % image_rootfs | ||
294 | rc, out = exec_cmd(du_cmd) | ||
295 | actual_rootfs_size = int(out.split()[0]) | ||
296 | |||
297 | extra_blocks = self.get_extra_block_count(actual_rootfs_size) | ||
298 | |||
299 | if extra_blocks < IMAGE_EXTRA_SPACE: | ||
300 | extra_blocks = IMAGE_EXTRA_SPACE | ||
301 | |||
302 | rootfs_size = actual_rootfs_size + extra_blocks | ||
303 | |||
304 | msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \ | ||
305 | (extra_blocks, self.mountpoint, rootfs_size)) | ||
306 | |||
307 | dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=0 bs=1k" % \ | ||
308 | (rootfs, rootfs_size) | ||
309 | rc, out = exec_cmd(dd_cmd) | ||
310 | |||
311 | extra_imagecmd = "-i 8192" | ||
312 | |||
313 | mkfs_cmd = "mkfs.%s -F %s %s -d %s" % \ | ||
314 | (self.fstype, extra_imagecmd, rootfs, image_rootfs) | ||
315 | rc, out = exec_native_cmd(pseudo + mkfs_cmd, native_sysroot) | ||
316 | |||
317 | |||
318 | # get the rootfs size in the right units for kickstart (Mb) | ||
319 | du_cmd = "du -Lbms %s" % rootfs | ||
320 | rc, out = exec_cmd(du_cmd) | ||
321 | rootfs_size = out.split()[0] | ||
322 | |||
323 | self.size = rootfs_size | ||
324 | self.source_file = rootfs | ||
325 | |||
326 | return 0 | ||
327 | |||
328 | def prepare_for_uboot(self, arch, cr_workdir, oe_builddir, rootfs_dir, | ||
329 | native_sysroot): | ||
330 | """ | ||
331 | Generates u-boot image from source_file( ext2/3/4 ) | ||
332 | |||
333 | """ | ||
334 | pseudo = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot | ||
335 | pseudo += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % rootfs_dir | ||
336 | pseudo += "export PSEUDO_PASSWD=%s;" % rootfs_dir | ||
337 | pseudo += "export PSEUDO_NOSYMLINKEXP=1;" | ||
338 | pseudo += "%s/usr/bin/pseudo " % native_sysroot | ||
339 | |||
340 | # 1) compress image | ||
341 | rootfs = self.source_file | ||
342 | rootfs_gzip = "%s.gz" % rootfs | ||
343 | gzip_cmd = "gzip -f -9 -c %s > %s" % (rootfs, rootfs_gzip) | ||
344 | rc, out = exec_native_cmd(pseudo + gzip_cmd, native_sysroot) | ||
345 | |||
346 | # 2) image for U-Boot | ||
347 | rootfs_uboot = "%s.u-boot" % rootfs_gzip | ||
348 | mkimage_cmd = "mkimage -A %s -O linux -T ramdisk -C gzip -n %s -d %s %s" % \ | ||
349 | (arch, self.label, rootfs_gzip, rootfs_uboot) | ||
350 | rc, out = exec_native_cmd(pseudo + mkimage_cmd, native_sysroot) | ||
351 | |||
352 | msger.info("\n\n\tThe new U-Boot ramdisk image can be found here:\n\t\t%s\n\n" % rootfs_uboot) | ||
353 | |||
354 | return 0 | ||
355 | |||
356 | def prepare_rootfs_btrfs(self, cr_workdir, oe_builddir, rootfs_dir, | ||
357 | native_sysroot, pseudo): | ||
358 | """ | ||
359 | Prepare content for a btrfs rootfs partition. | ||
360 | |||
361 | Currently handles ext2/3/4 and btrfs. | ||
362 | """ | ||
363 | image_rootfs = rootfs_dir | ||
364 | rootfs = "%s/rootfs_%s.%s" % (cr_workdir, self.label, self.fstype) | ||
365 | |||
366 | du_cmd = "du -ks %s" % image_rootfs | ||
367 | rc, out = exec_cmd(du_cmd) | ||
368 | actual_rootfs_size = int(out.split()[0]) | ||
369 | |||
370 | extra_blocks = self.get_extra_block_count(actual_rootfs_size) | ||
371 | |||
372 | if extra_blocks < IMAGE_EXTRA_SPACE: | ||
373 | extra_blocks = IMAGE_EXTRA_SPACE | ||
374 | |||
375 | rootfs_size = actual_rootfs_size + extra_blocks | ||
376 | |||
377 | msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \ | ||
378 | (extra_blocks, self.mountpoint, rootfs_size)) | ||
379 | |||
380 | dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=0 bs=1k" % \ | ||
381 | (rootfs, rootfs_size) | ||
382 | rc, out = exec_cmd(dd_cmd) | ||
383 | |||
384 | mkfs_cmd = "mkfs.%s -b %d -r %s %s" % \ | ||
385 | (self.fstype, rootfs_size * 1024, image_rootfs, rootfs) | ||
386 | rc, out = exec_native_cmd(pseudo + mkfs_cmd, native_sysroot) | ||
387 | |||
388 | # get the rootfs size in the right units for kickstart (Mb) | ||
389 | du_cmd = "du -Lbms %s" % rootfs | ||
390 | rc, out = exec_cmd(du_cmd) | ||
391 | rootfs_size = out.split()[0] | ||
392 | |||
393 | self.size = rootfs_size | ||
394 | self.source_file = rootfs | ||
395 | |||
396 | def prepare_empty_partition(self, cr_workdir, oe_builddir, native_sysroot): | ||
397 | """ | ||
398 | Prepare an empty partition. | ||
399 | """ | ||
400 | if self.fstype.startswith("ext"): | ||
401 | return self.prepare_empty_partition_ext(cr_workdir, oe_builddir, | ||
402 | native_sysroot) | ||
403 | elif self.fstype.startswith("btrfs"): | ||
404 | return self.prepare_empty_partition_btrfs(cr_workdir, oe_builddir, | ||
405 | native_sysroot) | ||
406 | |||
407 | def prepare_empty_partition_ext(self, cr_workdir, oe_builddir, | ||
408 | native_sysroot): | ||
409 | """ | ||
410 | Prepare an empty ext2/3/4 partition. | ||
411 | """ | ||
412 | fs = "%s/fs.%s" % (cr_workdir, self.fstype) | ||
413 | |||
414 | dd_cmd = "dd if=/dev/zero of=%s bs=1M seek=%d count=0" % \ | ||
415 | (fs, self.size) | ||
416 | rc, out = exec_cmd(dd_cmd) | ||
417 | |||
418 | extra_imagecmd = "-i 8192" | ||
419 | |||
420 | mkfs_cmd = "mkfs.%s -F %s %s" % (self.fstype, extra_imagecmd, fs) | ||
421 | rc, out = exec_native_cmd(mkfs_cmd, native_sysroot) | ||
422 | |||
423 | self.source_file = fs | ||
424 | |||
425 | return 0 | ||
426 | |||
427 | def prepare_empty_partition_btrfs(self, cr_workdir, oe_builddir, | ||
428 | native_sysroot): | ||
429 | """ | ||
430 | Prepare an empty btrfs partition. | ||
431 | """ | ||
432 | fs = "%s/fs.%s" % (cr_workdir, self.fstype) | ||
433 | |||
434 | dd_cmd = "dd if=/dev/zero of=%s bs=1M seek=%d count=0" % \ | ||
435 | (fs, self.size) | ||
436 | rc, out = exec_cmd(dd_cmd) | ||
437 | |||
438 | mkfs_cmd = "mkfs.%s -b %d %s" % (self.fstype, self.size * 1024, rootfs) | ||
439 | rc, out = exec_native_cmd(mkfs_cmd, native_sysroot) | ||
440 | |||
441 | mkfs_cmd = "mkfs.%s -F %s %s" % (self.fstype, extra_imagecmd, fs) | ||
442 | rc, out = exec_native_cmd(mkfs_cmd, native_sysroot) | ||
443 | |||
444 | self.source_file = fs | ||
445 | |||
446 | return 0 | ||
447 | |||
448 | def prepare_swap_partition(self, cr_workdir, oe_builddir, native_sysroot): | ||
449 | """ | ||
450 | Prepare a swap partition. | ||
451 | """ | ||
452 | fs = "%s/fs.%s" % (cr_workdir, self.fstype) | ||
453 | |||
454 | dd_cmd = "dd if=/dev/zero of=%s bs=1M seek=%d count=0" % \ | ||
455 | (fs, self.size) | ||
456 | rc, out = exec_cmd(dd_cmd) | ||
457 | |||
458 | import uuid | ||
459 | label_str = "" | ||
460 | if self.label: | ||
461 | label_str = "-L %s" % self.label | ||
462 | mkswap_cmd = "mkswap %s -U %s %s" % (label_str, str(uuid.uuid1()), fs) | ||
463 | rc, out = exec_native_cmd(mkswap_cmd, native_sysroot) | ||
464 | |||
465 | self.source_file = fs | ||
466 | |||
467 | return 0 | ||
468 | |||
469 | class Wic_Partition(Mic_Partition): | ||
470 | removedKeywords = Mic_Partition.removedKeywords | ||
471 | removedAttrs = Mic_Partition.removedAttrs | ||
472 | |||
473 | def _getParser(self): | ||
474 | op = Mic_Partition._getParser(self) | ||
475 | # use specified source file to fill the partition | ||
476 | # and calculate partition size | ||
477 | op.add_option("--source", type="string", action="store", | ||
478 | dest="source", default=None) | ||
479 | # use specified rootfs path to fill the partition | ||
480 | op.add_option("--rootfs-dir", type="string", action="store", | ||
481 | dest="rootfs", default=None) | ||
482 | return op | ||
diff --git a/scripts/lib/mic/kickstart/custom_commands/wicboot.py b/scripts/lib/mic/kickstart/custom_commands/wicboot.py new file mode 100644 index 0000000000..ab8871de4e --- /dev/null +++ b/scripts/lib/mic/kickstart/custom_commands/wicboot.py | |||
@@ -0,0 +1,57 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2014, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This module provides the OpenEmbedded bootloader object definitions. | ||
22 | # | ||
23 | # AUTHORS | ||
24 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
25 | # | ||
26 | |||
27 | from pykickstart.base import * | ||
28 | from pykickstart.errors import * | ||
29 | from pykickstart.options import * | ||
30 | from pykickstart.commands.bootloader import * | ||
31 | |||
32 | from mic.kickstart.custom_commands.micboot import * | ||
33 | |||
34 | class Wic_Bootloader(Mic_Bootloader): | ||
35 | def __init__(self, writePriority=10, appendLine="", driveorder=None, | ||
36 | forceLBA=False, location="", md5pass="", password="", | ||
37 | upgrade=False, menus=""): | ||
38 | Mic_Bootloader.__init__(self, writePriority, appendLine, driveorder, | ||
39 | forceLBA, location, md5pass, password, upgrade) | ||
40 | |||
41 | self.source = "" | ||
42 | |||
43 | def _getArgsAsStr(self): | ||
44 | retval = Mic_Bootloader._getArgsAsStr(self) | ||
45 | |||
46 | if self.source: | ||
47 | retval += " --source=%s" % self.source | ||
48 | |||
49 | return retval | ||
50 | |||
51 | def _getParser(self): | ||
52 | op = Mic_Bootloader._getParser(self) | ||
53 | # use specified source plugin to implement bootloader-specific methods | ||
54 | op.add_option("--source", type="string", action="store", | ||
55 | dest="source", default=None) | ||
56 | return op | ||
57 | |||
diff --git a/scripts/lib/mic/msger.py b/scripts/lib/mic/msger.py new file mode 100644 index 0000000000..9afc85be93 --- /dev/null +++ b/scripts/lib/mic/msger.py | |||
@@ -0,0 +1,309 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # vim: ai ts=4 sts=4 et sw=4 | ||
3 | # | ||
4 | # Copyright (c) 2009, 2010, 2011 Intel, Inc. | ||
5 | # | ||
6 | # This program is free software; you can redistribute it and/or modify it | ||
7 | # under the terms of the GNU General Public License as published by the Free | ||
8 | # Software Foundation; version 2 of the License | ||
9 | # | ||
10 | # This program is distributed in the hope that it will be useful, but | ||
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
12 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
13 | # for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License along | ||
16 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
17 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | |||
19 | import os,sys | ||
20 | import re | ||
21 | import time | ||
22 | |||
23 | __ALL__ = ['set_mode', | ||
24 | 'get_loglevel', | ||
25 | 'set_loglevel', | ||
26 | 'set_logfile', | ||
27 | 'raw', | ||
28 | 'debug', | ||
29 | 'verbose', | ||
30 | 'info', | ||
31 | 'warning', | ||
32 | 'error', | ||
33 | 'ask', | ||
34 | 'pause', | ||
35 | ] | ||
36 | |||
37 | # COLORs in ANSI | ||
38 | INFO_COLOR = 32 # green | ||
39 | WARN_COLOR = 33 # yellow | ||
40 | ERR_COLOR = 31 # red | ||
41 | ASK_COLOR = 34 # blue | ||
42 | NO_COLOR = 0 | ||
43 | |||
44 | PREFIX_RE = re.compile('^<(.*?)>\s*(.*)', re.S) | ||
45 | |||
46 | INTERACTIVE = True | ||
47 | |||
48 | LOG_LEVEL = 1 | ||
49 | LOG_LEVELS = { | ||
50 | 'quiet': 0, | ||
51 | 'normal': 1, | ||
52 | 'verbose': 2, | ||
53 | 'debug': 3, | ||
54 | 'never': 4, | ||
55 | } | ||
56 | |||
57 | LOG_FILE_FP = None | ||
58 | LOG_CONTENT = '' | ||
59 | CATCHERR_BUFFILE_FD = -1 | ||
60 | CATCHERR_BUFFILE_PATH = None | ||
61 | CATCHERR_SAVED_2 = -1 | ||
62 | |||
63 | def _general_print(head, color, msg = None, stream = None, level = 'normal'): | ||
64 | global LOG_CONTENT | ||
65 | if not stream: | ||
66 | stream = sys.stdout | ||
67 | |||
68 | if LOG_LEVELS[level] > LOG_LEVEL: | ||
69 | # skip | ||
70 | return | ||
71 | |||
72 | # encode raw 'unicode' str to utf8 encoded str | ||
73 | if msg and isinstance(msg, unicode): | ||
74 | msg = msg.encode('utf-8', 'ignore') | ||
75 | |||
76 | errormsg = '' | ||
77 | if CATCHERR_BUFFILE_FD > 0: | ||
78 | size = os.lseek(CATCHERR_BUFFILE_FD , 0, os.SEEK_END) | ||
79 | os.lseek(CATCHERR_BUFFILE_FD, 0, os.SEEK_SET) | ||
80 | errormsg = os.read(CATCHERR_BUFFILE_FD, size) | ||
81 | os.ftruncate(CATCHERR_BUFFILE_FD, 0) | ||
82 | |||
83 | # append error msg to LOG | ||
84 | if errormsg: | ||
85 | LOG_CONTENT += errormsg | ||
86 | |||
87 | # append normal msg to LOG | ||
88 | save_msg = msg.strip() if msg else None | ||
89 | if save_msg: | ||
90 | timestr = time.strftime("[%m/%d %H:%M:%S %Z] ", time.localtime()) | ||
91 | LOG_CONTENT += timestr + save_msg + '\n' | ||
92 | |||
93 | if errormsg: | ||
94 | _color_print('', NO_COLOR, errormsg, stream, level) | ||
95 | |||
96 | _color_print(head, color, msg, stream, level) | ||
97 | |||
98 | def _color_print(head, color, msg, stream, level): | ||
99 | colored = True | ||
100 | if color == NO_COLOR or \ | ||
101 | not stream.isatty() or \ | ||
102 | os.getenv('ANSI_COLORS_DISABLED') is not None: | ||
103 | colored = False | ||
104 | |||
105 | if head.startswith('\r'): | ||
106 | # need not \n at last | ||
107 | newline = False | ||
108 | else: | ||
109 | newline = True | ||
110 | |||
111 | if colored: | ||
112 | head = '\033[%dm%s:\033[0m ' %(color, head) | ||
113 | if not newline: | ||
114 | # ESC cmd to clear line | ||
115 | head = '\033[2K' + head | ||
116 | else: | ||
117 | if head: | ||
118 | head += ': ' | ||
119 | if head.startswith('\r'): | ||
120 | head = head.lstrip() | ||
121 | newline = True | ||
122 | |||
123 | if msg is not None: | ||
124 | if isinstance(msg, unicode): | ||
125 | msg = msg.encode('utf8', 'ignore') | ||
126 | |||
127 | stream.write('%s%s' % (head, msg)) | ||
128 | if newline: | ||
129 | stream.write('\n') | ||
130 | |||
131 | stream.flush() | ||
132 | |||
133 | def _color_perror(head, color, msg, level = 'normal'): | ||
134 | if CATCHERR_BUFFILE_FD > 0: | ||
135 | _general_print(head, color, msg, sys.stdout, level) | ||
136 | else: | ||
137 | _general_print(head, color, msg, sys.stderr, level) | ||
138 | |||
139 | def _split_msg(head, msg): | ||
140 | if isinstance(msg, list): | ||
141 | msg = '\n'.join(map(str, msg)) | ||
142 | |||
143 | if msg.startswith('\n'): | ||
144 | # means print \n at first | ||
145 | msg = msg.lstrip() | ||
146 | head = '\n' + head | ||
147 | |||
148 | elif msg.startswith('\r'): | ||
149 | # means print \r at first | ||
150 | msg = msg.lstrip() | ||
151 | head = '\r' + head | ||
152 | |||
153 | m = PREFIX_RE.match(msg) | ||
154 | if m: | ||
155 | head += ' <%s>' % m.group(1) | ||
156 | msg = m.group(2) | ||
157 | |||
158 | return head, msg | ||
159 | |||
160 | def get_loglevel(): | ||
161 | return (k for k,v in LOG_LEVELS.items() if v==LOG_LEVEL).next() | ||
162 | |||
163 | def set_loglevel(level): | ||
164 | global LOG_LEVEL | ||
165 | if level not in LOG_LEVELS: | ||
166 | # no effect | ||
167 | return | ||
168 | |||
169 | LOG_LEVEL = LOG_LEVELS[level] | ||
170 | |||
171 | def set_interactive(mode=True): | ||
172 | global INTERACTIVE | ||
173 | if mode: | ||
174 | INTERACTIVE = True | ||
175 | else: | ||
176 | INTERACTIVE = False | ||
177 | |||
178 | def log(msg=''): | ||
179 | # log msg to LOG_CONTENT then save to logfile | ||
180 | global LOG_CONTENT | ||
181 | if msg: | ||
182 | LOG_CONTENT += msg | ||
183 | |||
184 | def raw(msg=''): | ||
185 | _general_print('', NO_COLOR, msg) | ||
186 | |||
187 | def info(msg): | ||
188 | head, msg = _split_msg('Info', msg) | ||
189 | _general_print(head, INFO_COLOR, msg) | ||
190 | |||
191 | def verbose(msg): | ||
192 | head, msg = _split_msg('Verbose', msg) | ||
193 | _general_print(head, INFO_COLOR, msg, level = 'verbose') | ||
194 | |||
195 | def warning(msg): | ||
196 | head, msg = _split_msg('Warning', msg) | ||
197 | _color_perror(head, WARN_COLOR, msg) | ||
198 | |||
199 | def debug(msg): | ||
200 | head, msg = _split_msg('Debug', msg) | ||
201 | _color_perror(head, ERR_COLOR, msg, level = 'debug') | ||
202 | |||
203 | def error(msg): | ||
204 | head, msg = _split_msg('Error', msg) | ||
205 | _color_perror(head, ERR_COLOR, msg) | ||
206 | sys.exit(1) | ||
207 | |||
208 | def ask(msg, default=True): | ||
209 | _general_print('\rQ', ASK_COLOR, '') | ||
210 | try: | ||
211 | if default: | ||
212 | msg += '(Y/n) ' | ||
213 | else: | ||
214 | msg += '(y/N) ' | ||
215 | if INTERACTIVE: | ||
216 | while True: | ||
217 | repl = raw_input(msg) | ||
218 | if repl.lower() == 'y': | ||
219 | return True | ||
220 | elif repl.lower() == 'n': | ||
221 | return False | ||
222 | elif not repl.strip(): | ||
223 | # <Enter> | ||
224 | return default | ||
225 | |||
226 | # else loop | ||
227 | else: | ||
228 | if default: | ||
229 | msg += ' Y' | ||
230 | else: | ||
231 | msg += ' N' | ||
232 | _general_print('', NO_COLOR, msg) | ||
233 | |||
234 | return default | ||
235 | except KeyboardInterrupt: | ||
236 | sys.stdout.write('\n') | ||
237 | sys.exit(2) | ||
238 | |||
239 | def choice(msg, choices, default=0): | ||
240 | if default >= len(choices): | ||
241 | return None | ||
242 | _general_print('\rQ', ASK_COLOR, '') | ||
243 | try: | ||
244 | msg += " [%s] " % '/'.join(choices) | ||
245 | if INTERACTIVE: | ||
246 | while True: | ||
247 | repl = raw_input(msg) | ||
248 | if repl in choices: | ||
249 | return repl | ||
250 | elif not repl.strip(): | ||
251 | return choices[default] | ||
252 | else: | ||
253 | msg += choices[default] | ||
254 | _general_print('', NO_COLOR, msg) | ||
255 | |||
256 | return choices[default] | ||
257 | except KeyboardInterrupt: | ||
258 | sys.stdout.write('\n') | ||
259 | sys.exit(2) | ||
260 | |||
261 | def pause(msg=None): | ||
262 | if INTERACTIVE: | ||
263 | _general_print('\rQ', ASK_COLOR, '') | ||
264 | if msg is None: | ||
265 | msg = 'press <ENTER> to continue ...' | ||
266 | raw_input(msg) | ||
267 | |||
268 | def set_logfile(fpath): | ||
269 | global LOG_FILE_FP | ||
270 | |||
271 | def _savelogf(): | ||
272 | if LOG_FILE_FP: | ||
273 | fp = open(LOG_FILE_FP, 'w') | ||
274 | fp.write(LOG_CONTENT) | ||
275 | fp.close() | ||
276 | |||
277 | if LOG_FILE_FP is not None: | ||
278 | warning('duplicate log file configuration') | ||
279 | |||
280 | LOG_FILE_FP = fpath | ||
281 | |||
282 | import atexit | ||
283 | atexit.register(_savelogf) | ||
284 | |||
285 | def enable_logstderr(fpath): | ||
286 | global CATCHERR_BUFFILE_FD | ||
287 | global CATCHERR_BUFFILE_PATH | ||
288 | global CATCHERR_SAVED_2 | ||
289 | |||
290 | if os.path.exists(fpath): | ||
291 | os.remove(fpath) | ||
292 | CATCHERR_BUFFILE_PATH = fpath | ||
293 | CATCHERR_BUFFILE_FD = os.open(CATCHERR_BUFFILE_PATH, os.O_RDWR|os.O_CREAT) | ||
294 | CATCHERR_SAVED_2 = os.dup(2) | ||
295 | os.dup2(CATCHERR_BUFFILE_FD, 2) | ||
296 | |||
297 | def disable_logstderr(): | ||
298 | global CATCHERR_BUFFILE_FD | ||
299 | global CATCHERR_BUFFILE_PATH | ||
300 | global CATCHERR_SAVED_2 | ||
301 | |||
302 | raw(msg = None) # flush message buffer and print it. | ||
303 | os.dup2(CATCHERR_SAVED_2, 2) | ||
304 | os.close(CATCHERR_SAVED_2) | ||
305 | os.close(CATCHERR_BUFFILE_FD) | ||
306 | os.unlink(CATCHERR_BUFFILE_PATH) | ||
307 | CATCHERR_BUFFILE_FD = -1 | ||
308 | CATCHERR_BUFFILE_PATH = None | ||
309 | CATCHERR_SAVED_2 = -1 | ||
diff --git a/scripts/lib/mic/plugin.py b/scripts/lib/mic/plugin.py new file mode 100644 index 0000000000..df03c15081 --- /dev/null +++ b/scripts/lib/mic/plugin.py | |||
@@ -0,0 +1,121 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os, sys | ||
19 | |||
20 | from mic import msger | ||
21 | from mic import pluginbase | ||
22 | from mic.utils import errors | ||
23 | |||
24 | |||
25 | __ALL__ = ['PluginMgr', 'pluginmgr'] | ||
26 | |||
27 | PLUGIN_TYPES = ["imager", "source"] # TODO "hook" | ||
28 | |||
29 | |||
30 | class PluginMgr(object): | ||
31 | plugin_dirs = {} | ||
32 | |||
33 | # make the manager class as singleton | ||
34 | _instance = None | ||
35 | def __new__(cls, *args, **kwargs): | ||
36 | if not cls._instance: | ||
37 | cls._instance = super(PluginMgr, cls).__new__(cls, *args, **kwargs) | ||
38 | |||
39 | return cls._instance | ||
40 | |||
41 | def __init__(self): | ||
42 | mic_path = os.path.dirname(__file__) | ||
43 | eos = mic_path.find('scripts') + len('scripts') | ||
44 | scripts_path = mic_path[:eos] | ||
45 | |||
46 | self.plugin_dir = scripts_path + "/lib/mic/plugins" | ||
47 | |||
48 | def append_dirs(self, dirs): | ||
49 | for path in dirs: | ||
50 | self._add_plugindir(path) | ||
51 | |||
52 | # load all the plugins AGAIN | ||
53 | self._load_all() | ||
54 | |||
55 | def _add_plugindir(self, path): | ||
56 | path = os.path.abspath(os.path.expanduser(path)) | ||
57 | |||
58 | if not os.path.isdir(path): | ||
59 | msger.warning("Plugin dir is not a directory or does not exist: %s"\ | ||
60 | % path) | ||
61 | return | ||
62 | |||
63 | if path not in self.plugin_dirs: | ||
64 | self.plugin_dirs[path] = False | ||
65 | # the value True/False means "loaded" | ||
66 | |||
67 | def _load_all(self): | ||
68 | for (pdir, loaded) in self.plugin_dirs.iteritems(): | ||
69 | if loaded: continue | ||
70 | |||
71 | sys.path.insert(0, pdir) | ||
72 | for mod in [x[:-3] for x in os.listdir(pdir) if x.endswith(".py")]: | ||
73 | if mod and mod != '__init__': | ||
74 | if mod in sys.modules: | ||
75 | #self.plugin_dirs[pdir] = True | ||
76 | msger.warning("Module %s already exists, skip" % mod) | ||
77 | else: | ||
78 | try: | ||
79 | pymod = __import__(mod) | ||
80 | self.plugin_dirs[pdir] = True | ||
81 | msger.debug("Plugin module %s:%s imported"\ | ||
82 | % (mod, pymod.__file__)) | ||
83 | except ImportError, err: | ||
84 | msg = 'Failed to load plugin %s/%s: %s' \ | ||
85 | % (os.path.basename(pdir), mod, err) | ||
86 | msger.warning(msg) | ||
87 | |||
88 | del(sys.path[0]) | ||
89 | |||
90 | def get_plugins(self, ptype): | ||
91 | """ the return value is dict of name:class pairs """ | ||
92 | |||
93 | if ptype not in PLUGIN_TYPES: | ||
94 | raise errors.CreatorError('%s is not valid plugin type' % ptype) | ||
95 | |||
96 | self._add_plugindir(os.path.join(self.plugin_dir, ptype)) | ||
97 | self._load_all() | ||
98 | |||
99 | return pluginbase.get_plugins(ptype) | ||
100 | |||
101 | def get_source_plugin_methods(self, source_name, methods): | ||
102 | """ | ||
103 | The methods param is a dict with the method names to find. On | ||
104 | return, the dict values will be filled in with pointers to the | ||
105 | corresponding methods. If one or more methods are not found, | ||
106 | None is returned. | ||
107 | """ | ||
108 | return_methods = None | ||
109 | for _source_name, klass in self.get_plugins('source').iteritems(): | ||
110 | if _source_name == source_name: | ||
111 | for _method_name in methods.keys(): | ||
112 | if not hasattr(klass, _method_name): | ||
113 | msger.warning("Unimplemented %s source interface for: %s"\ | ||
114 | % (_method_name, _source_name)) | ||
115 | return None | ||
116 | func = getattr(klass, _method_name) | ||
117 | methods[_method_name] = func | ||
118 | return_methods = methods | ||
119 | return return_methods | ||
120 | |||
121 | pluginmgr = PluginMgr() | ||
diff --git a/scripts/lib/mic/pluginbase.py b/scripts/lib/mic/pluginbase.py new file mode 100644 index 0000000000..881d9969c6 --- /dev/null +++ b/scripts/lib/mic/pluginbase.py | |||
@@ -0,0 +1,158 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import shutil | ||
20 | from mic import msger | ||
21 | from mic.utils import errors | ||
22 | |||
23 | class _Plugin(object): | ||
24 | class __metaclass__(type): | ||
25 | def __init__(cls, name, bases, attrs): | ||
26 | if not hasattr(cls, 'plugins'): | ||
27 | cls.plugins = {} | ||
28 | |||
29 | elif 'mic_plugin_type' in attrs: | ||
30 | if attrs['mic_plugin_type'] not in cls.plugins: | ||
31 | cls.plugins[attrs['mic_plugin_type']] = {} | ||
32 | |||
33 | elif hasattr(cls, 'mic_plugin_type') and 'name' in attrs: | ||
34 | cls.plugins[cls.mic_plugin_type][attrs['name']] = cls | ||
35 | |||
36 | def show_plugins(cls): | ||
37 | for cls in cls.plugins[cls.mic_plugin_type]: | ||
38 | print cls | ||
39 | |||
40 | def get_plugins(cls): | ||
41 | return cls.plugins | ||
42 | |||
43 | class ImagerPlugin(_Plugin): | ||
44 | mic_plugin_type = "imager" | ||
45 | |||
46 | @classmethod | ||
47 | def check_image_exists(self, destdir, apacking=None, | ||
48 | images=(), | ||
49 | release=None): | ||
50 | |||
51 | # if it's a packing file, reset images | ||
52 | if apacking: | ||
53 | images = [apacking] | ||
54 | |||
55 | # release option will override images | ||
56 | if release is not None: | ||
57 | images = [os.path.basename(destdir.rstrip('/'))] | ||
58 | destdir = os.path.dirname(destdir.rstrip('/')) | ||
59 | |||
60 | for name in images: | ||
61 | if not name: | ||
62 | continue | ||
63 | |||
64 | image = os.path.join(destdir, name) | ||
65 | if not os.path.exists(image): | ||
66 | continue | ||
67 | |||
68 | if msger.ask("Target image/dir: %s already exists, " | ||
69 | "clean up and continue?" % image): | ||
70 | if os.path.isdir(image): | ||
71 | shutil.rmtree(image) | ||
72 | else: | ||
73 | os.unlink(image) | ||
74 | else: | ||
75 | raise errors.Abort("Cancled") | ||
76 | |||
77 | def do_create(self): | ||
78 | pass | ||
79 | |||
80 | def do_chroot(self): | ||
81 | pass | ||
82 | |||
83 | class SourcePlugin(_Plugin): | ||
84 | mic_plugin_type = "source" | ||
85 | """ | ||
86 | The methods that can be implemented by --source plugins. | ||
87 | |||
88 | Any methods not implemented in a subclass inherit these. | ||
89 | """ | ||
90 | |||
91 | @classmethod | ||
92 | def do_install_pkgs(self, part, creator, cr_workdir, oe_builddir, rootfs_dir, | ||
93 | bootimg_dir, kernel_dir, native_sysroot): | ||
94 | """ | ||
95 | Called before partitions have been prepared and assembled into a | ||
96 | disk image. Install packages into rootfs | ||
97 | """ | ||
98 | msger.debug("SourcePlugin: do_install_pkgs: part %s" % part) | ||
99 | |||
100 | @classmethod | ||
101 | def do_install_disk(self, disk, disk_name, cr, workdir, oe_builddir, | ||
102 | bootimg_dir, kernel_dir, native_sysroot): | ||
103 | """ | ||
104 | Called after all partitions have been prepared and assembled into a | ||
105 | disk image. This provides a hook to allow finalization of a | ||
106 | disk image e.g. to write an MBR to it. | ||
107 | """ | ||
108 | msger.debug("SourcePlugin: do_install_disk: disk: %s" % disk_name) | ||
109 | |||
110 | @classmethod | ||
111 | def do_stage_partition(self, part, cr, workdir, oe_builddir, bootimg_dir, | ||
112 | kernel_dir, native_sysroot): | ||
113 | """ | ||
114 | Special content staging hook called before do_prepare_partition(), | ||
115 | normally empty. | ||
116 | |||
117 | Typically, a partition will just use the passed-in parame e.g | ||
118 | straight bootimg_dir, etc, but in some cases, things need to | ||
119 | be more tailored e.g. to use a deploy dir + /boot, etc. This | ||
120 | hook allows those files to be staged in a customized fashion. | ||
121 | Not that get_bitbake_var() allows you to acces non-standard | ||
122 | variables that you might want to use for this. | ||
123 | """ | ||
124 | msger.debug("SourcePlugin: do_stage_partition: part: %s" % part) | ||
125 | |||
126 | @classmethod | ||
127 | def do_configure_partition(self, part, cr, cr_workdir, oe_builddir, | ||
128 | bootimg_dir, kernel_dir, native_sysroot): | ||
129 | """ | ||
130 | Called before do_prepare_partition(), typically used to create | ||
131 | custom configuration files for a partition, for example | ||
132 | syslinux or grub config files. | ||
133 | """ | ||
134 | msger.debug("SourcePlugin: do_configure_partition: part: %s" % part) | ||
135 | |||
136 | @classmethod | ||
137 | def do_prepare_partition(self, part, cr, cr_workdir, oe_builddir, bootimg_dir, | ||
138 | kernel_dir, rootfs_dir, native_sysroot): | ||
139 | """ | ||
140 | Called to do the actual content population for a partition i.e. it | ||
141 | 'prepares' the partition to be incorporated into the image. | ||
142 | """ | ||
143 | msger.debug("SourcePlugin: do_prepare_partition: part: %s" % part) | ||
144 | |||
145 | class BackendPlugin(_Plugin): | ||
146 | mic_plugin_type="backend" | ||
147 | |||
148 | def addRepository(self): | ||
149 | pass | ||
150 | |||
151 | def get_plugins(typen): | ||
152 | ps = ImagerPlugin.get_plugins() | ||
153 | if typen in ps: | ||
154 | return ps[typen] | ||
155 | else: | ||
156 | return None | ||
157 | |||
158 | __all__ = ['ImagerPlugin', 'BackendPlugin', 'SourcePlugin', 'get_plugins'] | ||
diff --git a/scripts/lib/mic/plugins/backend/yumpkgmgr.py b/scripts/lib/mic/plugins/backend/yumpkgmgr.py new file mode 100644 index 0000000000..955f813109 --- /dev/null +++ b/scripts/lib/mic/plugins/backend/yumpkgmgr.py | |||
@@ -0,0 +1,490 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2007 Red Hat Inc. | ||
4 | # Copyright (c) 2010, 2011 Intel, Inc. | ||
5 | # | ||
6 | # This program is free software; you can redistribute it and/or modify it | ||
7 | # under the terms of the GNU General Public License as published by the Free | ||
8 | # Software Foundation; version 2 of the License | ||
9 | # | ||
10 | # This program is distributed in the hope that it will be useful, but | ||
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
12 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
13 | # for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License along | ||
16 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
17 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | |||
19 | import os, sys | ||
20 | import re | ||
21 | import tempfile | ||
22 | import glob | ||
23 | from string import Template | ||
24 | |||
25 | import rpmUtils | ||
26 | import yum | ||
27 | |||
28 | from mic import msger | ||
29 | from mic.kickstart import ksparser | ||
30 | from mic.utils import misc, rpmmisc | ||
31 | from mic.utils.grabber import TextProgress | ||
32 | from mic.utils.proxy import get_proxy_for | ||
33 | from mic.utils.errors import CreatorError | ||
34 | from mic.imager.baseimager import BaseImageCreator | ||
35 | |||
36 | YUMCONF_TEMP = """[main] | ||
37 | installroot=$installroot | ||
38 | cachedir=/var/cache/yum | ||
39 | persistdir=/var/lib/yum | ||
40 | plugins=0 | ||
41 | reposdir= | ||
42 | failovermethod=priority | ||
43 | http_caching=packages | ||
44 | sslverify=1 | ||
45 | """ | ||
46 | |||
47 | class MyYumRepository(yum.yumRepo.YumRepository): | ||
48 | def __del__(self): | ||
49 | pass | ||
50 | |||
51 | def dirSetup(self): | ||
52 | super(MyYumRepository, self).dirSetup() | ||
53 | # relocate package dir | ||
54 | pkgdir = os.path.join(self.basecachedir, 'packages', self.id) | ||
55 | self.setAttribute('_dir_setup_pkgdir', pkgdir) | ||
56 | self._dirSetupMkdir_p(self.pkgdir) | ||
57 | |||
58 | def _getFile(self, url=None, | ||
59 | relative=None, | ||
60 | local=None, | ||
61 | start=None, | ||
62 | end=None, | ||
63 | copy_local=None, | ||
64 | checkfunc=None, | ||
65 | text=None, | ||
66 | reget='simple', | ||
67 | cache=True, | ||
68 | size=None): | ||
69 | |||
70 | m2c_connection = None | ||
71 | if not self.sslverify: | ||
72 | try: | ||
73 | import M2Crypto | ||
74 | m2c_connection = M2Crypto.SSL.Connection.clientPostConnectionCheck | ||
75 | M2Crypto.SSL.Connection.clientPostConnectionCheck = None | ||
76 | except ImportError, err: | ||
77 | raise CreatorError("%s, please try to install python-m2crypto" % str(err)) | ||
78 | |||
79 | proxy = None | ||
80 | if url: | ||
81 | proxy = get_proxy_for(url) | ||
82 | else: | ||
83 | proxy = get_proxy_for(self.urls[0]) | ||
84 | |||
85 | if proxy: | ||
86 | self.proxy = str(proxy) | ||
87 | |||
88 | size = int(size) if size else None | ||
89 | rvalue = super(MyYumRepository, self)._getFile(url, | ||
90 | relative, | ||
91 | local, | ||
92 | start, | ||
93 | end, | ||
94 | copy_local, | ||
95 | checkfunc, | ||
96 | text, | ||
97 | reget, | ||
98 | cache, | ||
99 | size) | ||
100 | |||
101 | if m2c_connection and \ | ||
102 | not M2Crypto.SSL.Connection.clientPostConnectionCheck: | ||
103 | M2Crypto.SSL.Connection.clientPostConnectionCheck = m2c_connection | ||
104 | |||
105 | return rvalue | ||
106 | |||
107 | from mic.pluginbase import BackendPlugin | ||
108 | class Yum(BackendPlugin, yum.YumBase): | ||
109 | name = 'yum' | ||
110 | |||
111 | def __init__(self, target_arch, instroot, cachedir): | ||
112 | yum.YumBase.__init__(self) | ||
113 | |||
114 | self.cachedir = cachedir | ||
115 | self.instroot = instroot | ||
116 | self.target_arch = target_arch | ||
117 | |||
118 | if self.target_arch: | ||
119 | if not rpmUtils.arch.arches.has_key(self.target_arch): | ||
120 | rpmUtils.arch.arches["armv7hl"] = "noarch" | ||
121 | rpmUtils.arch.arches["armv7tnhl"] = "armv7nhl" | ||
122 | rpmUtils.arch.arches["armv7tnhl"] = "armv7thl" | ||
123 | rpmUtils.arch.arches["armv7thl"] = "armv7hl" | ||
124 | rpmUtils.arch.arches["armv7nhl"] = "armv7hl" | ||
125 | self.arch.setup_arch(self.target_arch) | ||
126 | |||
127 | self.__pkgs_license = {} | ||
128 | self.__pkgs_content = {} | ||
129 | self.__pkgs_vcsinfo = {} | ||
130 | |||
131 | self.install_debuginfo = False | ||
132 | |||
133 | def doFileLogSetup(self, uid, logfile): | ||
134 | # don't do the file log for the livecd as it can lead to open fds | ||
135 | # being left and an inability to clean up after ourself | ||
136 | pass | ||
137 | |||
138 | def close(self): | ||
139 | try: | ||
140 | os.unlink(self.confpath) | ||
141 | os.unlink(self.conf.installroot + "/yum.conf") | ||
142 | except: | ||
143 | pass | ||
144 | |||
145 | if self.ts: | ||
146 | self.ts.close() | ||
147 | self._delRepos() | ||
148 | self._delSacks() | ||
149 | yum.YumBase.close(self) | ||
150 | self.closeRpmDB() | ||
151 | |||
152 | if not os.path.exists("/etc/fedora-release") and \ | ||
153 | not os.path.exists("/etc/meego-release"): | ||
154 | for i in range(3, os.sysconf("SC_OPEN_MAX")): | ||
155 | try: | ||
156 | os.close(i) | ||
157 | except: | ||
158 | pass | ||
159 | |||
160 | def __del__(self): | ||
161 | pass | ||
162 | |||
163 | def _writeConf(self, confpath, installroot): | ||
164 | conf = Template(YUMCONF_TEMP).safe_substitute(installroot=installroot) | ||
165 | |||
166 | f = file(confpath, "w+") | ||
167 | f.write(conf) | ||
168 | f.close() | ||
169 | |||
170 | os.chmod(confpath, 0644) | ||
171 | |||
172 | def _cleanupRpmdbLocks(self, installroot): | ||
173 | # cleans up temporary files left by bdb so that differing | ||
174 | # versions of rpm don't cause problems | ||
175 | for f in glob.glob(installroot + "/var/lib/rpm/__db*"): | ||
176 | os.unlink(f) | ||
177 | |||
178 | def setup(self): | ||
179 | # create yum.conf | ||
180 | (fn, self.confpath) = tempfile.mkstemp(dir=self.cachedir, | ||
181 | prefix='yum.conf-') | ||
182 | os.close(fn) | ||
183 | self._writeConf(self.confpath, self.instroot) | ||
184 | self._cleanupRpmdbLocks(self.instroot) | ||
185 | # do setup | ||
186 | self.doConfigSetup(fn = self.confpath, root = self.instroot) | ||
187 | self.conf.cache = 0 | ||
188 | self.doTsSetup() | ||
189 | self.doRpmDBSetup() | ||
190 | self.doRepoSetup() | ||
191 | self.doSackSetup() | ||
192 | |||
193 | def preInstall(self, pkg): | ||
194 | # FIXME: handle pre-install package | ||
195 | return None | ||
196 | |||
197 | def selectPackage(self, pkg): | ||
198 | """Select a given package. | ||
199 | Can be specified with name.arch or name* | ||
200 | """ | ||
201 | |||
202 | try: | ||
203 | self.install(pattern = pkg) | ||
204 | return None | ||
205 | except yum.Errors.InstallError: | ||
206 | return "No package(s) available to install" | ||
207 | except yum.Errors.RepoError, e: | ||
208 | raise CreatorError("Unable to download from repo : %s" % (e,)) | ||
209 | except yum.Errors.YumBaseError, e: | ||
210 | raise CreatorError("Unable to install: %s" % (e,)) | ||
211 | |||
212 | def deselectPackage(self, pkg): | ||
213 | """Deselect package. Can be specified as name.arch or name* | ||
214 | """ | ||
215 | |||
216 | sp = pkg.rsplit(".", 2) | ||
217 | txmbrs = [] | ||
218 | if len(sp) == 2: | ||
219 | txmbrs = self.tsInfo.matchNaevr(name=sp[0], arch=sp[1]) | ||
220 | |||
221 | if len(txmbrs) == 0: | ||
222 | exact, match, unmatch = yum.packages.parsePackages( | ||
223 | self.pkgSack.returnPackages(), | ||
224 | [pkg], | ||
225 | casematch=1) | ||
226 | for p in exact + match: | ||
227 | txmbrs.append(p) | ||
228 | |||
229 | if len(txmbrs) > 0: | ||
230 | for x in txmbrs: | ||
231 | self.tsInfo.remove(x.pkgtup) | ||
232 | # we also need to remove from the conditionals | ||
233 | # dict so that things don't get pulled back in as a result | ||
234 | # of them. yes, this is ugly. conditionals should die. | ||
235 | for req, pkgs in self.tsInfo.conditionals.iteritems(): | ||
236 | if x in pkgs: | ||
237 | pkgs.remove(x) | ||
238 | self.tsInfo.conditionals[req] = pkgs | ||
239 | else: | ||
240 | msger.warning("No such package %s to remove" %(pkg,)) | ||
241 | |||
242 | def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT): | ||
243 | try: | ||
244 | yum.YumBase.selectGroup(self, grp) | ||
245 | if include == ksparser.GROUP_REQUIRED: | ||
246 | for p in grp.default_packages.keys(): | ||
247 | self.deselectPackage(p) | ||
248 | |||
249 | elif include == ksparser.GROUP_ALL: | ||
250 | for p in grp.optional_packages.keys(): | ||
251 | self.selectPackage(p) | ||
252 | |||
253 | return None | ||
254 | except (yum.Errors.InstallError, yum.Errors.GroupsError), e: | ||
255 | return e | ||
256 | except yum.Errors.RepoError, e: | ||
257 | raise CreatorError("Unable to download from repo : %s" % (e,)) | ||
258 | except yum.Errors.YumBaseError, e: | ||
259 | raise CreatorError("Unable to install: %s" % (e,)) | ||
260 | |||
261 | def addRepository(self, name, url = None, mirrorlist = None, proxy = None, | ||
262 | proxy_username = None, proxy_password = None, | ||
263 | inc = None, exc = None, ssl_verify=True, nocache=False, | ||
264 | cost = None, priority=None): | ||
265 | # TODO: Handle priority attribute for repos | ||
266 | def _varSubstitute(option): | ||
267 | # takes a variable and substitutes like yum configs do | ||
268 | option = option.replace("$basearch", rpmUtils.arch.getBaseArch()) | ||
269 | option = option.replace("$arch", rpmUtils.arch.getCanonArch()) | ||
270 | return option | ||
271 | |||
272 | repo = MyYumRepository(name) | ||
273 | |||
274 | # Set proxy | ||
275 | repo.proxy = proxy | ||
276 | repo.proxy_username = proxy_username | ||
277 | repo.proxy_password = proxy_password | ||
278 | |||
279 | if url: | ||
280 | repo.baseurl.append(_varSubstitute(url)) | ||
281 | |||
282 | # check LICENSE files | ||
283 | if not rpmmisc.checkRepositoryEULA(name, repo): | ||
284 | msger.warning('skip repo:%s for failed EULA confirmation' % name) | ||
285 | return None | ||
286 | |||
287 | if mirrorlist: | ||
288 | repo.mirrorlist = _varSubstitute(mirrorlist) | ||
289 | |||
290 | conf = yum.config.RepoConf() | ||
291 | for k, v in conf.iteritems(): | ||
292 | if v or not hasattr(repo, k): | ||
293 | repo.setAttribute(k, v) | ||
294 | |||
295 | repo.sslverify = ssl_verify | ||
296 | repo.cache = not nocache | ||
297 | |||
298 | repo.basecachedir = self.cachedir | ||
299 | repo.base_persistdir = self.conf.persistdir | ||
300 | repo.failovermethod = "priority" | ||
301 | repo.metadata_expire = 0 | ||
302 | # Enable gpg check for verifying corrupt packages | ||
303 | repo.gpgcheck = 1 | ||
304 | repo.enable() | ||
305 | repo.setup(0) | ||
306 | self.repos.add(repo) | ||
307 | if cost: | ||
308 | repo.cost = cost | ||
309 | |||
310 | msger.verbose('repo: %s was added' % name) | ||
311 | return repo | ||
312 | |||
313 | def installLocal(self, pkg, po=None, updateonly=False): | ||
314 | ts = rpmUtils.transaction.initReadOnlyTransaction() | ||
315 | try: | ||
316 | hdr = rpmUtils.miscutils.hdrFromPackage(ts, pkg) | ||
317 | except rpmUtils.RpmUtilsError, e: | ||
318 | raise yum.Errors.MiscError, \ | ||
319 | 'Could not open local rpm file: %s: %s' % (pkg, e) | ||
320 | |||
321 | self.deselectPackage(hdr['name']) | ||
322 | yum.YumBase.installLocal(self, pkg, po, updateonly) | ||
323 | |||
324 | def installHasFile(self, file): | ||
325 | provides_pkg = self.whatProvides(file, None, None) | ||
326 | dlpkgs = map( | ||
327 | lambda x: x.po, | ||
328 | filter( | ||
329 | lambda txmbr: txmbr.ts_state in ("i", "u"), | ||
330 | self.tsInfo.getMembers())) | ||
331 | |||
332 | for p in dlpkgs: | ||
333 | for q in provides_pkg: | ||
334 | if (p == q): | ||
335 | return True | ||
336 | |||
337 | return False | ||
338 | |||
339 | def runInstall(self, checksize = 0): | ||
340 | os.environ["HOME"] = "/" | ||
341 | os.environ["LD_PRELOAD"] = "" | ||
342 | try: | ||
343 | (res, resmsg) = self.buildTransaction() | ||
344 | except yum.Errors.RepoError, e: | ||
345 | raise CreatorError("Unable to download from repo : %s" %(e,)) | ||
346 | |||
347 | if res != 2: | ||
348 | raise CreatorError("Failed to build transaction : %s" \ | ||
349 | % str.join("\n", resmsg)) | ||
350 | |||
351 | dlpkgs = map( | ||
352 | lambda x: x.po, | ||
353 | filter( | ||
354 | lambda txmbr: txmbr.ts_state in ("i", "u"), | ||
355 | self.tsInfo.getMembers())) | ||
356 | |||
357 | # record all pkg and the content | ||
358 | for pkg in dlpkgs: | ||
359 | pkg_long_name = misc.RPM_FMT % { | ||
360 | 'name': pkg.name, | ||
361 | 'arch': pkg.arch, | ||
362 | 'version': pkg.version, | ||
363 | 'release': pkg.release | ||
364 | } | ||
365 | self.__pkgs_content[pkg_long_name] = pkg.files | ||
366 | license = pkg.license | ||
367 | if license in self.__pkgs_license.keys(): | ||
368 | self.__pkgs_license[license].append(pkg_long_name) | ||
369 | else: | ||
370 | self.__pkgs_license[license] = [pkg_long_name] | ||
371 | |||
372 | total_count = len(dlpkgs) | ||
373 | cached_count = 0 | ||
374 | download_total_size = sum(map(lambda x: int(x.packagesize), dlpkgs)) | ||
375 | |||
376 | msger.info("\nChecking packages cached ...") | ||
377 | for po in dlpkgs: | ||
378 | local = po.localPkg() | ||
379 | repo = filter(lambda r: r.id == po.repoid, self.repos.listEnabled())[0] | ||
380 | if not repo.cache and os.path.exists(local): | ||
381 | os.unlink(local) | ||
382 | if not os.path.exists(local): | ||
383 | continue | ||
384 | if not self.verifyPkg(local, po, False): | ||
385 | msger.warning("Package %s is damaged: %s" \ | ||
386 | % (os.path.basename(local), local)) | ||
387 | else: | ||
388 | download_total_size -= int(po.packagesize) | ||
389 | cached_count +=1 | ||
390 | |||
391 | cache_avail_size = misc.get_filesystem_avail(self.cachedir) | ||
392 | if cache_avail_size < download_total_size: | ||
393 | raise CreatorError("No enough space used for downloading.") | ||
394 | |||
395 | # record the total size of installed pkgs | ||
396 | pkgs_total_size = 0L | ||
397 | for x in dlpkgs: | ||
398 | if hasattr(x, 'installedsize'): | ||
399 | pkgs_total_size += int(x.installedsize) | ||
400 | else: | ||
401 | pkgs_total_size += int(x.size) | ||
402 | |||
403 | # check needed size before actually download and install | ||
404 | if checksize and pkgs_total_size > checksize: | ||
405 | raise CreatorError("No enough space used for installing, " | ||
406 | "please resize partition size in ks file") | ||
407 | |||
408 | msger.info("Packages: %d Total, %d Cached, %d Missed" \ | ||
409 | % (total_count, cached_count, total_count - cached_count)) | ||
410 | |||
411 | try: | ||
412 | repos = self.repos.listEnabled() | ||
413 | for repo in repos: | ||
414 | repo.setCallback(TextProgress(total_count - cached_count)) | ||
415 | |||
416 | self.downloadPkgs(dlpkgs) | ||
417 | # FIXME: sigcheck? | ||
418 | |||
419 | self.initActionTs() | ||
420 | self.populateTs(keepold=0) | ||
421 | |||
422 | deps = self.ts.check() | ||
423 | if len(deps) != 0: | ||
424 | # This isn't fatal, Ubuntu has this issue but it is ok. | ||
425 | msger.debug(deps) | ||
426 | msger.warning("Dependency check failed!") | ||
427 | |||
428 | rc = self.ts.order() | ||
429 | if rc != 0: | ||
430 | raise CreatorError("ordering packages for installation failed") | ||
431 | |||
432 | # FIXME: callback should be refactored a little in yum | ||
433 | cb = rpmmisc.RPMInstallCallback(self.ts) | ||
434 | cb.tsInfo = self.tsInfo | ||
435 | cb.filelog = False | ||
436 | |||
437 | msger.warning('\nCaution, do NOT interrupt the installation, ' | ||
438 | 'else mic cannot finish the cleanup.') | ||
439 | |||
440 | installlogfile = "%s/__catched_stderr.buf" % (self.instroot) | ||
441 | msger.enable_logstderr(installlogfile) | ||
442 | self.runTransaction(cb) | ||
443 | self._cleanupRpmdbLocks(self.conf.installroot) | ||
444 | |||
445 | except rpmUtils.RpmUtilsError, e: | ||
446 | raise CreatorError("mic does NOT support delta rpm: %s" % e) | ||
447 | except yum.Errors.RepoError, e: | ||
448 | raise CreatorError("Unable to download from repo : %s" % e) | ||
449 | except yum.Errors.YumBaseError, e: | ||
450 | raise CreatorError("Unable to install: %s" % e) | ||
451 | finally: | ||
452 | msger.disable_logstderr() | ||
453 | |||
454 | def getVcsInfo(self): | ||
455 | return self.__pkgs_vcsinfo | ||
456 | |||
457 | def getAllContent(self): | ||
458 | return self.__pkgs_content | ||
459 | |||
460 | def getPkgsLicense(self): | ||
461 | return self.__pkgs_license | ||
462 | |||
463 | def getFilelist(self, pkgname): | ||
464 | if not pkgname: | ||
465 | return None | ||
466 | |||
467 | pkg = filter(lambda txmbr: txmbr.po.name == pkgname, self.tsInfo.getMembers()) | ||
468 | if not pkg: | ||
469 | return None | ||
470 | return pkg[0].po.filelist | ||
471 | |||
472 | def package_url(self, pkgname): | ||
473 | pkgs = self.pkgSack.searchNevra(name=pkgname) | ||
474 | if pkgs: | ||
475 | proxy = None | ||
476 | proxies = None | ||
477 | url = pkgs[0].remote_url | ||
478 | repoid = pkgs[0].repoid | ||
479 | repos = filter(lambda r: r.id == repoid, self.repos.listEnabled()) | ||
480 | |||
481 | if repos: | ||
482 | proxy = repos[0].proxy | ||
483 | if not proxy: | ||
484 | proxy = get_proxy_for(url) | ||
485 | if proxy: | ||
486 | proxies = {str(url.split(':')[0]): str(proxy)} | ||
487 | |||
488 | return (url, proxies) | ||
489 | |||
490 | return (None, None) | ||
diff --git a/scripts/lib/mic/plugins/backend/zypppkgmgr.py b/scripts/lib/mic/plugins/backend/zypppkgmgr.py new file mode 100755 index 0000000000..c760859832 --- /dev/null +++ b/scripts/lib/mic/plugins/backend/zypppkgmgr.py | |||
@@ -0,0 +1,973 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2010, 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import shutil | ||
20 | import urlparse | ||
21 | import rpm | ||
22 | |||
23 | import zypp | ||
24 | if not hasattr(zypp, 'PoolQuery') or \ | ||
25 | not hasattr(zypp.RepoManager, 'loadSolvFile'): | ||
26 | raise ImportError("python-zypp in host system cannot support PoolQuery or " | ||
27 | "loadSolvFile interface, please update it to enhanced " | ||
28 | "version which can be found in download.tizen.org/tools") | ||
29 | |||
30 | from mic import msger | ||
31 | from mic.kickstart import ksparser | ||
32 | from mic.utils import misc, rpmmisc, runner, fs_related | ||
33 | from mic.utils.grabber import myurlgrab, TextProgress | ||
34 | from mic.utils.proxy import get_proxy_for | ||
35 | from mic.utils.errors import CreatorError, RepoError, RpmError | ||
36 | from mic.imager.baseimager import BaseImageCreator | ||
37 | |||
38 | class RepositoryStub: | ||
39 | def __init__(self): | ||
40 | self.name = None | ||
41 | self.baseurl = [] | ||
42 | self.mirrorlist = None | ||
43 | self.proxy = None | ||
44 | self.proxy_username = None | ||
45 | self.proxy_password = None | ||
46 | self.nocache = False | ||
47 | |||
48 | self.enabled = True | ||
49 | self.autorefresh = True | ||
50 | self.keeppackages = True | ||
51 | self.priority = None | ||
52 | |||
53 | from mic.pluginbase import BackendPlugin | ||
54 | class Zypp(BackendPlugin): | ||
55 | name = 'zypp' | ||
56 | |||
57 | def __init__(self, target_arch, instroot, cachedir): | ||
58 | self.cachedir = cachedir | ||
59 | self.instroot = instroot | ||
60 | self.target_arch = target_arch | ||
61 | |||
62 | self.__pkgs_license = {} | ||
63 | self.__pkgs_content = {} | ||
64 | self.__pkgs_vcsinfo = {} | ||
65 | self.repos = [] | ||
66 | self.to_deselect = [] | ||
67 | self.localpkgs = {} | ||
68 | self.repo_manager = None | ||
69 | self.repo_manager_options = None | ||
70 | self.Z = None | ||
71 | self.ts = None | ||
72 | self.ts_pre = None | ||
73 | self.incpkgs = {} | ||
74 | self.excpkgs = {} | ||
75 | self.pre_pkgs = [] | ||
76 | self.probFilterFlags = [ rpm.RPMPROB_FILTER_OLDPACKAGE, | ||
77 | rpm.RPMPROB_FILTER_REPLACEPKG ] | ||
78 | |||
79 | self.has_prov_query = True | ||
80 | self.install_debuginfo = False | ||
81 | |||
82 | def doFileLogSetup(self, uid, logfile): | ||
83 | # don't do the file log for the livecd as it can lead to open fds | ||
84 | # being left and an inability to clean up after ourself | ||
85 | pass | ||
86 | |||
87 | def closeRpmDB(self): | ||
88 | pass | ||
89 | |||
90 | def close(self): | ||
91 | if self.ts: | ||
92 | self.ts.closeDB() | ||
93 | self.ts = None | ||
94 | |||
95 | if self.ts_pre: | ||
96 | self.ts_pre.closeDB() | ||
97 | self.ts = None | ||
98 | |||
99 | self.closeRpmDB() | ||
100 | |||
101 | if not os.path.exists("/etc/fedora-release") and \ | ||
102 | not os.path.exists("/etc/meego-release"): | ||
103 | for i in range(3, os.sysconf("SC_OPEN_MAX")): | ||
104 | try: | ||
105 | os.close(i) | ||
106 | except: | ||
107 | pass | ||
108 | |||
109 | def __del__(self): | ||
110 | self.close() | ||
111 | |||
112 | def _cleanupRpmdbLocks(self, installroot): | ||
113 | # cleans up temporary files left by bdb so that differing | ||
114 | # versions of rpm don't cause problems | ||
115 | import glob | ||
116 | for f in glob.glob(installroot + "/var/lib/rpm/__db*"): | ||
117 | os.unlink(f) | ||
118 | |||
119 | def _cleanupZyppJunk(self, installroot): | ||
120 | try: | ||
121 | shutil.rmtree(os.path.join(installroot, '.zypp')) | ||
122 | except: | ||
123 | pass | ||
124 | |||
125 | def setup(self): | ||
126 | self._cleanupRpmdbLocks(self.instroot) | ||
127 | |||
128 | def whatObsolete(self, pkg): | ||
129 | query = zypp.PoolQuery() | ||
130 | query.addKind(zypp.ResKind.package) | ||
131 | query.addAttribute(zypp.SolvAttr.obsoletes, pkg) | ||
132 | query.setMatchExact() | ||
133 | for pi in query.queryResults(self.Z.pool()): | ||
134 | return pi | ||
135 | return None | ||
136 | |||
137 | def _zyppQueryPackage(self, pkg): | ||
138 | query = zypp.PoolQuery() | ||
139 | query.addKind(zypp.ResKind.package) | ||
140 | query.addAttribute(zypp.SolvAttr.name,pkg) | ||
141 | query.setMatchExact() | ||
142 | for pi in query.queryResults(self.Z.pool()): | ||
143 | return pi | ||
144 | return None | ||
145 | |||
146 | def _splitPkgString(self, pkg): | ||
147 | sp = pkg.rsplit(".",1) | ||
148 | name = sp[0] | ||
149 | arch = None | ||
150 | if len(sp) == 2: | ||
151 | arch = sp[1] | ||
152 | sysarch = zypp.Arch(self.target_arch) | ||
153 | if not zypp.Arch(arch).compatible_with (sysarch): | ||
154 | arch = None | ||
155 | name = ".".join(sp) | ||
156 | return name, arch | ||
157 | |||
158 | def selectPackage(self, pkg): | ||
159 | """Select a given package or package pattern, can be specified | ||
160 | with name.arch or name* or *name | ||
161 | """ | ||
162 | |||
163 | if not self.Z: | ||
164 | self.__initialize_zypp() | ||
165 | |||
166 | def markPoolItem(obs, pi): | ||
167 | if obs == None: | ||
168 | pi.status().setToBeInstalled (zypp.ResStatus.USER) | ||
169 | else: | ||
170 | obs.status().setToBeInstalled (zypp.ResStatus.USER) | ||
171 | |||
172 | def cmpEVR(p1, p2): | ||
173 | # compare criterion: arch compatibility first, then repo | ||
174 | # priority, and version last | ||
175 | a1 = p1.arch() | ||
176 | a2 = p2.arch() | ||
177 | if str(a1) != str(a2): | ||
178 | if a1.compatible_with(a2): | ||
179 | return -1 | ||
180 | else: | ||
181 | return 1 | ||
182 | # Priority of a repository is an integer value between 0 (the | ||
183 | # highest priority) and 99 (the lowest priority) | ||
184 | pr1 = int(p1.repoInfo().priority()) | ||
185 | pr2 = int(p2.repoInfo().priority()) | ||
186 | if pr1 > pr2: | ||
187 | return -1 | ||
188 | elif pr1 < pr2: | ||
189 | return 1 | ||
190 | |||
191 | ed1 = p1.edition() | ||
192 | ed2 = p2.edition() | ||
193 | (e1, v1, r1) = map(str, [ed1.epoch(), ed1.version(), ed1.release()]) | ||
194 | (e2, v2, r2) = map(str, [ed2.epoch(), ed2.version(), ed2.release()]) | ||
195 | return rpm.labelCompare((e1, v1, r1), (e2, v2, r2)) | ||
196 | |||
197 | found = False | ||
198 | startx = pkg.startswith("*") | ||
199 | endx = pkg.endswith("*") | ||
200 | ispattern = startx or endx | ||
201 | name, arch = self._splitPkgString(pkg) | ||
202 | |||
203 | q = zypp.PoolQuery() | ||
204 | q.addKind(zypp.ResKind.package) | ||
205 | |||
206 | if ispattern: | ||
207 | if startx and not endx: | ||
208 | pattern = '%s$' % (pkg[1:]) | ||
209 | if endx and not startx: | ||
210 | pattern = '^%s' % (pkg[0:-1]) | ||
211 | if endx and startx: | ||
212 | pattern = '%s' % (pkg[1:-1]) | ||
213 | q.setMatchRegex() | ||
214 | q.addAttribute(zypp.SolvAttr.name,pattern) | ||
215 | |||
216 | elif arch: | ||
217 | q.setMatchExact() | ||
218 | q.addAttribute(zypp.SolvAttr.name,name) | ||
219 | |||
220 | else: | ||
221 | q.setMatchExact() | ||
222 | q.addAttribute(zypp.SolvAttr.name,pkg) | ||
223 | |||
224 | for pitem in sorted( | ||
225 | q.queryResults(self.Z.pool()), | ||
226 | cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)), | ||
227 | reverse=True): | ||
228 | item = zypp.asKindPackage(pitem) | ||
229 | if item.name() in self.excpkgs.keys() and \ | ||
230 | self.excpkgs[item.name()] == item.repoInfo().name(): | ||
231 | continue | ||
232 | if item.name() in self.incpkgs.keys() and \ | ||
233 | self.incpkgs[item.name()] != item.repoInfo().name(): | ||
234 | continue | ||
235 | |||
236 | found = True | ||
237 | obspkg = self.whatObsolete(item.name()) | ||
238 | if arch: | ||
239 | if arch == str(item.arch()): | ||
240 | item.status().setToBeInstalled (zypp.ResStatus.USER) | ||
241 | else: | ||
242 | markPoolItem(obspkg, pitem) | ||
243 | if not ispattern: | ||
244 | break | ||
245 | |||
246 | # Can't match using package name, then search from packge | ||
247 | # provides infomation | ||
248 | if found == False and not ispattern: | ||
249 | q.addAttribute(zypp.SolvAttr.provides, pkg) | ||
250 | q.addAttribute(zypp.SolvAttr.name,'') | ||
251 | |||
252 | for pitem in sorted( | ||
253 | q.queryResults(self.Z.pool()), | ||
254 | cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)), | ||
255 | reverse=True): | ||
256 | item = zypp.asKindPackage(pitem) | ||
257 | if item.name() in self.excpkgs.keys() and \ | ||
258 | self.excpkgs[item.name()] == item.repoInfo().name(): | ||
259 | continue | ||
260 | if item.name() in self.incpkgs.keys() and \ | ||
261 | self.incpkgs[item.name()] != item.repoInfo().name(): | ||
262 | continue | ||
263 | |||
264 | found = True | ||
265 | obspkg = self.whatObsolete(item.name()) | ||
266 | markPoolItem(obspkg, pitem) | ||
267 | break | ||
268 | |||
269 | if found: | ||
270 | return None | ||
271 | else: | ||
272 | raise CreatorError("Unable to find package: %s" % (pkg,)) | ||
273 | |||
274 | def inDeselectPackages(self, pitem): | ||
275 | """check if specified pacakges are in the list of inDeselectPackages | ||
276 | """ | ||
277 | item = zypp.asKindPackage(pitem) | ||
278 | name = item.name() | ||
279 | for pkg in self.to_deselect: | ||
280 | startx = pkg.startswith("*") | ||
281 | endx = pkg.endswith("*") | ||
282 | ispattern = startx or endx | ||
283 | pkgname, pkgarch = self._splitPkgString(pkg) | ||
284 | if not ispattern: | ||
285 | if pkgarch: | ||
286 | if name == pkgname and str(item.arch()) == pkgarch: | ||
287 | return True; | ||
288 | else: | ||
289 | if name == pkgname: | ||
290 | return True; | ||
291 | else: | ||
292 | if startx and name.endswith(pkg[1:]): | ||
293 | return True; | ||
294 | if endx and name.startswith(pkg[:-1]): | ||
295 | return True; | ||
296 | |||
297 | return False; | ||
298 | |||
299 | def deselectPackage(self, pkg): | ||
300 | """collect packages should not be installed""" | ||
301 | self.to_deselect.append(pkg) | ||
302 | |||
303 | def selectGroup(self, grp, include = ksparser.GROUP_DEFAULT): | ||
304 | if not self.Z: | ||
305 | self.__initialize_zypp() | ||
306 | found = False | ||
307 | q=zypp.PoolQuery() | ||
308 | q.addKind(zypp.ResKind.pattern) | ||
309 | for pitem in q.queryResults(self.Z.pool()): | ||
310 | item = zypp.asKindPattern(pitem) | ||
311 | summary = "%s" % item.summary() | ||
312 | name = "%s" % item.name() | ||
313 | if name == grp or summary == grp: | ||
314 | found = True | ||
315 | pitem.status().setToBeInstalled (zypp.ResStatus.USER) | ||
316 | break | ||
317 | |||
318 | if found: | ||
319 | if include == ksparser.GROUP_REQUIRED: | ||
320 | map( | ||
321 | lambda p: self.deselectPackage(p), | ||
322 | grp.default_packages.keys()) | ||
323 | |||
324 | return None | ||
325 | else: | ||
326 | raise CreatorError("Unable to find pattern: %s" % (grp,)) | ||
327 | |||
328 | def addRepository(self, name, | ||
329 | url = None, | ||
330 | mirrorlist = None, | ||
331 | proxy = None, | ||
332 | proxy_username = None, | ||
333 | proxy_password = None, | ||
334 | inc = None, | ||
335 | exc = None, | ||
336 | ssl_verify = True, | ||
337 | nocache = False, | ||
338 | cost=None, | ||
339 | priority=None): | ||
340 | # TODO: Handle cost attribute for repos | ||
341 | |||
342 | if not self.repo_manager: | ||
343 | self.__initialize_repo_manager() | ||
344 | |||
345 | if not proxy and url: | ||
346 | proxy = get_proxy_for(url) | ||
347 | |||
348 | repo = RepositoryStub() | ||
349 | repo.name = name | ||
350 | repo.id = name | ||
351 | repo.proxy = proxy | ||
352 | repo.proxy_username = proxy_username | ||
353 | repo.proxy_password = proxy_password | ||
354 | repo.ssl_verify = ssl_verify | ||
355 | repo.nocache = nocache | ||
356 | repo.baseurl.append(url) | ||
357 | if inc: | ||
358 | for pkg in inc: | ||
359 | self.incpkgs[pkg] = name | ||
360 | if exc: | ||
361 | for pkg in exc: | ||
362 | self.excpkgs[pkg] = name | ||
363 | |||
364 | # check LICENSE files | ||
365 | if not rpmmisc.checkRepositoryEULA(name, repo): | ||
366 | msger.warning('skip repo:%s for failed EULA confirmation' % name) | ||
367 | return None | ||
368 | |||
369 | if mirrorlist: | ||
370 | repo.mirrorlist = mirrorlist | ||
371 | |||
372 | # Enable gpg check for verifying corrupt packages | ||
373 | repo.gpgcheck = 1 | ||
374 | if priority is not None: | ||
375 | # priority 0 has issue in RepoInfo.setPriority | ||
376 | repo.priority = priority + 1 | ||
377 | |||
378 | try: | ||
379 | repo_info = zypp.RepoInfo() | ||
380 | repo_info.setAlias(repo.name) | ||
381 | repo_info.setName(repo.name) | ||
382 | repo_info.setEnabled(repo.enabled) | ||
383 | repo_info.setAutorefresh(repo.autorefresh) | ||
384 | repo_info.setKeepPackages(repo.keeppackages) | ||
385 | baseurl = zypp.Url(repo.baseurl[0]) | ||
386 | if not ssl_verify: | ||
387 | baseurl.setQueryParam("ssl_verify", "no") | ||
388 | if proxy: | ||
389 | scheme, host, path, parm, query, frag = urlparse.urlparse(proxy) | ||
390 | |||
391 | proxyinfo = host.split(":") | ||
392 | host = proxyinfo[0] | ||
393 | |||
394 | port = "80" | ||
395 | if len(proxyinfo) > 1: | ||
396 | port = proxyinfo[1] | ||
397 | |||
398 | if proxy.startswith("socks") and len(proxy.rsplit(':', 1)) == 2: | ||
399 | host = proxy.rsplit(':', 1)[0] | ||
400 | port = proxy.rsplit(':', 1)[1] | ||
401 | |||
402 | baseurl.setQueryParam ("proxy", host) | ||
403 | baseurl.setQueryParam ("proxyport", port) | ||
404 | |||
405 | repo.baseurl[0] = baseurl.asCompleteString() | ||
406 | self.repos.append(repo) | ||
407 | |||
408 | repo_info.addBaseUrl(baseurl) | ||
409 | |||
410 | if repo.priority is not None: | ||
411 | repo_info.setPriority(repo.priority) | ||
412 | |||
413 | # this hack is used to change zypp credential file location | ||
414 | # the default one is $HOME/.zypp, which cause conflicts when | ||
415 | # installing some basic packages, and the location doesn't | ||
416 | # have any interface actually, so use a tricky way anyway | ||
417 | homedir = None | ||
418 | if 'HOME' in os.environ: | ||
419 | homedir = os.environ['HOME'] | ||
420 | os.environ['HOME'] = '/' | ||
421 | else: | ||
422 | os.environ['HOME'] = '/' | ||
423 | |||
424 | self.repo_manager.addRepository(repo_info) | ||
425 | |||
426 | # save back the $HOME env | ||
427 | if homedir: | ||
428 | os.environ['HOME'] = homedir | ||
429 | else: | ||
430 | del os.environ['HOME'] | ||
431 | |||
432 | self.__build_repo_cache(name) | ||
433 | |||
434 | except RuntimeError, e: | ||
435 | raise CreatorError(str(e)) | ||
436 | |||
437 | msger.verbose('repo: %s was added' % name) | ||
438 | return repo | ||
439 | |||
440 | def installHasFile(self, file): | ||
441 | return False | ||
442 | |||
443 | def preInstall(self, pkg): | ||
444 | self.pre_pkgs.append(pkg) | ||
445 | |||
446 | def runInstall(self, checksize = 0): | ||
447 | os.environ["HOME"] = "/" | ||
448 | os.environ["LD_PRELOAD"] = "" | ||
449 | self.buildTransaction() | ||
450 | |||
451 | todo = zypp.GetResolvablesToInsDel(self.Z.pool()) | ||
452 | installed_pkgs = todo._toInstall | ||
453 | dlpkgs = [] | ||
454 | for pitem in installed_pkgs: | ||
455 | if not zypp.isKindPattern(pitem) and \ | ||
456 | not self.inDeselectPackages(pitem): | ||
457 | item = zypp.asKindPackage(pitem) | ||
458 | dlpkgs.append(item) | ||
459 | |||
460 | if not self.install_debuginfo or str(item.arch()) == "noarch": | ||
461 | continue | ||
462 | |||
463 | dipkg = self._zyppQueryPackage("%s-debuginfo" % item.name()) | ||
464 | if dipkg: | ||
465 | ditem = zypp.asKindPackage(dipkg) | ||
466 | dlpkgs.append(ditem) | ||
467 | else: | ||
468 | msger.warning("No debuginfo rpm found for: %s" \ | ||
469 | % item.name()) | ||
470 | |||
471 | # record all pkg and the content | ||
472 | localpkgs = self.localpkgs.keys() | ||
473 | for pkg in dlpkgs: | ||
474 | license = '' | ||
475 | if pkg.name() in localpkgs: | ||
476 | hdr = rpmmisc.readRpmHeader(self.ts, self.localpkgs[pkg.name()]) | ||
477 | pkg_long_name = misc.RPM_FMT % { | ||
478 | 'name': hdr['name'], | ||
479 | 'arch': hdr['arch'], | ||
480 | 'version': hdr['version'], | ||
481 | 'release': hdr['release'] | ||
482 | } | ||
483 | license = hdr['license'] | ||
484 | |||
485 | else: | ||
486 | pkg_long_name = misc.RPM_FMT % { | ||
487 | 'name': pkg.name(), | ||
488 | 'arch': pkg.arch(), | ||
489 | 'version': pkg.edition().version(), | ||
490 | 'release': pkg.edition().release() | ||
491 | } | ||
492 | |||
493 | license = pkg.license() | ||
494 | |||
495 | if license in self.__pkgs_license.keys(): | ||
496 | self.__pkgs_license[license].append(pkg_long_name) | ||
497 | else: | ||
498 | self.__pkgs_license[license] = [pkg_long_name] | ||
499 | |||
500 | total_count = len(dlpkgs) | ||
501 | cached_count = 0 | ||
502 | download_total_size = sum(map(lambda x: int(x.downloadSize()), dlpkgs)) | ||
503 | localpkgs = self.localpkgs.keys() | ||
504 | |||
505 | msger.info("Checking packages cached ...") | ||
506 | for po in dlpkgs: | ||
507 | # Check if it is cached locally | ||
508 | if po.name() in localpkgs: | ||
509 | cached_count += 1 | ||
510 | else: | ||
511 | local = self.getLocalPkgPath(po) | ||
512 | name = str(po.repoInfo().name()) | ||
513 | try: | ||
514 | repo = filter(lambda r: r.name == name, self.repos)[0] | ||
515 | except IndexError: | ||
516 | repo = None | ||
517 | nocache = repo.nocache if repo else False | ||
518 | |||
519 | if os.path.exists(local): | ||
520 | if nocache or self.checkPkg(local) !=0: | ||
521 | os.unlink(local) | ||
522 | else: | ||
523 | download_total_size -= int(po.downloadSize()) | ||
524 | cached_count += 1 | ||
525 | cache_avail_size = misc.get_filesystem_avail(self.cachedir) | ||
526 | if cache_avail_size < download_total_size: | ||
527 | raise CreatorError("No enough space used for downloading.") | ||
528 | |||
529 | # record the total size of installed pkgs | ||
530 | install_total_size = sum(map(lambda x: int(x.installSize()), dlpkgs)) | ||
531 | # check needed size before actually download and install | ||
532 | |||
533 | # FIXME: for multiple partitions for loop type, check fails | ||
534 | # skip the check temporarily | ||
535 | #if checksize and install_total_size > checksize: | ||
536 | # raise CreatorError("No enough space used for installing, " | ||
537 | # "please resize partition size in ks file") | ||
538 | |||
539 | download_count = total_count - cached_count | ||
540 | msger.info("Packages: %d Total, %d Cached, %d Missed" \ | ||
541 | % (total_count, cached_count, download_count)) | ||
542 | |||
543 | try: | ||
544 | if download_count > 0: | ||
545 | msger.info("Downloading packages ...") | ||
546 | self.downloadPkgs(dlpkgs, download_count) | ||
547 | |||
548 | self.installPkgs(dlpkgs) | ||
549 | |||
550 | except (RepoError, RpmError): | ||
551 | raise | ||
552 | except Exception, e: | ||
553 | raise CreatorError("Package installation failed: %s" % (e,)) | ||
554 | |||
555 | def getVcsInfo(self): | ||
556 | if self.__pkgs_vcsinfo: | ||
557 | return | ||
558 | |||
559 | if not self.ts: | ||
560 | self.__initialize_transaction() | ||
561 | |||
562 | mi = self.ts.dbMatch() | ||
563 | for hdr in mi: | ||
564 | lname = misc.RPM_FMT % { | ||
565 | 'name': hdr['name'], | ||
566 | 'arch': hdr['arch'], | ||
567 | 'version': hdr['version'], | ||
568 | 'release': hdr['release'] | ||
569 | } | ||
570 | self.__pkgs_vcsinfo[lname] = hdr['VCS'] | ||
571 | |||
572 | return self.__pkgs_vcsinfo | ||
573 | |||
574 | def getAllContent(self): | ||
575 | if self.__pkgs_content: | ||
576 | return self.__pkgs_content | ||
577 | |||
578 | if not self.ts: | ||
579 | self.__initialize_transaction() | ||
580 | |||
581 | mi = self.ts.dbMatch() | ||
582 | for hdr in mi: | ||
583 | lname = misc.RPM_FMT % { | ||
584 | 'name': hdr['name'], | ||
585 | 'arch': hdr['arch'], | ||
586 | 'version': hdr['version'], | ||
587 | 'release': hdr['release'] | ||
588 | } | ||
589 | self.__pkgs_content[lname] = hdr['FILENAMES'] | ||
590 | |||
591 | return self.__pkgs_content | ||
592 | |||
593 | def getPkgsLicense(self): | ||
594 | return self.__pkgs_license | ||
595 | |||
596 | def getFilelist(self, pkgname): | ||
597 | if not pkgname: | ||
598 | return None | ||
599 | |||
600 | if not self.ts: | ||
601 | self.__initialize_transaction() | ||
602 | |||
603 | mi = self.ts.dbMatch('name', pkgname) | ||
604 | for header in mi: | ||
605 | return header['FILENAMES'] | ||
606 | |||
607 | def __initialize_repo_manager(self): | ||
608 | if self.repo_manager: | ||
609 | return | ||
610 | |||
611 | # Clean up repo metadata | ||
612 | shutil.rmtree(self.cachedir + "/etc", ignore_errors = True) | ||
613 | shutil.rmtree(self.cachedir + "/solv", ignore_errors = True) | ||
614 | shutil.rmtree(self.cachedir + "/raw", ignore_errors = True) | ||
615 | |||
616 | zypp.KeyRing.setDefaultAccept( zypp.KeyRing.ACCEPT_UNSIGNED_FILE | ||
617 | | zypp.KeyRing.ACCEPT_VERIFICATION_FAILED | ||
618 | | zypp.KeyRing.ACCEPT_UNKNOWNKEY | ||
619 | | zypp.KeyRing.TRUST_KEY_TEMPORARILY | ||
620 | ) | ||
621 | |||
622 | self.repo_manager_options = \ | ||
623 | zypp.RepoManagerOptions(zypp.Pathname(self.instroot)) | ||
624 | |||
625 | self.repo_manager_options.knownReposPath = \ | ||
626 | zypp.Pathname(self.cachedir + "/etc/zypp/repos.d") | ||
627 | |||
628 | self.repo_manager_options.repoCachePath = \ | ||
629 | zypp.Pathname(self.cachedir) | ||
630 | |||
631 | self.repo_manager_options.repoRawCachePath = \ | ||
632 | zypp.Pathname(self.cachedir + "/raw") | ||
633 | |||
634 | self.repo_manager_options.repoSolvCachePath = \ | ||
635 | zypp.Pathname(self.cachedir + "/solv") | ||
636 | |||
637 | self.repo_manager_options.repoPackagesCachePath = \ | ||
638 | zypp.Pathname(self.cachedir + "/packages") | ||
639 | |||
640 | self.repo_manager = zypp.RepoManager(self.repo_manager_options) | ||
641 | |||
642 | def __build_repo_cache(self, name): | ||
643 | repo = self.repo_manager.getRepositoryInfo(name) | ||
644 | if self.repo_manager.isCached(repo) or not repo.enabled(): | ||
645 | return | ||
646 | |||
647 | msger.info('Refreshing repository: %s ...' % name) | ||
648 | self.repo_manager.buildCache(repo, zypp.RepoManager.BuildIfNeeded) | ||
649 | |||
650 | def __initialize_zypp(self): | ||
651 | if self.Z: | ||
652 | return | ||
653 | |||
654 | zconfig = zypp.ZConfig_instance() | ||
655 | |||
656 | # Set system architecture | ||
657 | if self.target_arch: | ||
658 | zconfig.setSystemArchitecture(zypp.Arch(self.target_arch)) | ||
659 | |||
660 | msger.info("zypp architecture is <%s>" % zconfig.systemArchitecture()) | ||
661 | |||
662 | # repoPackagesCachePath is corrected by this | ||
663 | self.repo_manager = zypp.RepoManager(self.repo_manager_options) | ||
664 | repos = self.repo_manager.knownRepositories() | ||
665 | for repo in repos: | ||
666 | if not repo.enabled(): | ||
667 | continue | ||
668 | self.repo_manager.loadFromCache(repo) | ||
669 | |||
670 | self.Z = zypp.ZYppFactory_instance().getZYpp() | ||
671 | self.Z.initializeTarget(zypp.Pathname(self.instroot)) | ||
672 | self.Z.target().load() | ||
673 | |||
674 | def buildTransaction(self): | ||
675 | if not self.Z.resolver().resolvePool(): | ||
676 | probs = self.Z.resolver().problems() | ||
677 | |||
678 | for problem in probs: | ||
679 | msger.warning("repo problem: %s, %s" \ | ||
680 | % (problem.description().decode("utf-8"), | ||
681 | problem.details().decode("utf-8"))) | ||
682 | |||
683 | raise RepoError("found %d resolver problem, abort!" \ | ||
684 | % len(probs)) | ||
685 | |||
686 | def getLocalPkgPath(self, po): | ||
687 | repoinfo = po.repoInfo() | ||
688 | cacheroot = repoinfo.packagesPath() | ||
689 | location= po.location() | ||
690 | rpmpath = str(location.filename()) | ||
691 | pkgpath = "%s/%s" % (cacheroot, os.path.basename(rpmpath)) | ||
692 | return pkgpath | ||
693 | |||
694 | def installLocal(self, pkg, po=None, updateonly=False): | ||
695 | if not self.ts: | ||
696 | self.__initialize_transaction() | ||
697 | |||
698 | solvfile = "%s/.solv" % (self.cachedir) | ||
699 | |||
700 | rc, out = runner.runtool([fs_related.find_binary_path("rpms2solv"), | ||
701 | pkg]) | ||
702 | if rc == 0: | ||
703 | f = open(solvfile, "w+") | ||
704 | f.write(out) | ||
705 | f.close() | ||
706 | |||
707 | warnmsg = self.repo_manager.loadSolvFile(solvfile, | ||
708 | os.path.basename(pkg)) | ||
709 | if warnmsg: | ||
710 | msger.warning(warnmsg) | ||
711 | |||
712 | os.unlink(solvfile) | ||
713 | else: | ||
714 | msger.warning('Can not get %s solv data.' % pkg) | ||
715 | |||
716 | hdr = rpmmisc.readRpmHeader(self.ts, pkg) | ||
717 | arch = zypp.Arch(hdr['arch']) | ||
718 | sysarch = zypp.Arch(self.target_arch) | ||
719 | |||
720 | if arch.compatible_with (sysarch): | ||
721 | pkgname = hdr['name'] | ||
722 | self.localpkgs[pkgname] = pkg | ||
723 | self.selectPackage(pkgname) | ||
724 | msger.info("Marking %s to be installed" % (pkg)) | ||
725 | |||
726 | else: | ||
727 | msger.warning("Cannot add package %s to transaction. " | ||
728 | "Not a compatible architecture: %s" \ | ||
729 | % (pkg, hdr['arch'])) | ||
730 | |||
731 | def downloadPkgs(self, package_objects, count): | ||
732 | localpkgs = self.localpkgs.keys() | ||
733 | progress_obj = TextProgress(count) | ||
734 | |||
735 | for po in package_objects: | ||
736 | if po.name() in localpkgs: | ||
737 | continue | ||
738 | |||
739 | filename = self.getLocalPkgPath(po) | ||
740 | if os.path.exists(filename): | ||
741 | if self.checkPkg(filename) == 0: | ||
742 | continue | ||
743 | |||
744 | dirn = os.path.dirname(filename) | ||
745 | if not os.path.exists(dirn): | ||
746 | os.makedirs(dirn) | ||
747 | |||
748 | url = self.get_url(po) | ||
749 | proxies = self.get_proxies(po) | ||
750 | |||
751 | try: | ||
752 | filename = myurlgrab(url, filename, proxies, progress_obj) | ||
753 | except CreatorError: | ||
754 | self.close() | ||
755 | raise | ||
756 | |||
757 | def preinstallPkgs(self): | ||
758 | if not self.ts_pre: | ||
759 | self.__initialize_transaction() | ||
760 | |||
761 | self.ts_pre.order() | ||
762 | cb = rpmmisc.RPMInstallCallback(self.ts_pre) | ||
763 | cb.headmsg = "Preinstall" | ||
764 | installlogfile = "%s/__catched_stderr.buf" % (self.instroot) | ||
765 | |||
766 | # start to catch stderr output from librpm | ||
767 | msger.enable_logstderr(installlogfile) | ||
768 | |||
769 | errors = self.ts_pre.run(cb.callback, '') | ||
770 | # stop catch | ||
771 | msger.disable_logstderr() | ||
772 | self.ts_pre.closeDB() | ||
773 | self.ts_pre = None | ||
774 | |||
775 | if errors is not None: | ||
776 | if len(errors) == 0: | ||
777 | msger.warning('scriptlet or other non-fatal errors occurred ' | ||
778 | 'during transaction.') | ||
779 | |||
780 | else: | ||
781 | for e in errors: | ||
782 | msger.warning(e[0]) | ||
783 | raise RepoError('Could not run transaction.') | ||
784 | |||
785 | def installPkgs(self, package_objects): | ||
786 | if not self.ts: | ||
787 | self.__initialize_transaction() | ||
788 | |||
789 | # clean rpm lock | ||
790 | self._cleanupRpmdbLocks(self.instroot) | ||
791 | self._cleanupZyppJunk(self.instroot) | ||
792 | # Set filters | ||
793 | probfilter = 0 | ||
794 | for flag in self.probFilterFlags: | ||
795 | probfilter |= flag | ||
796 | self.ts.setProbFilter(probfilter) | ||
797 | self.ts_pre.setProbFilter(probfilter) | ||
798 | |||
799 | localpkgs = self.localpkgs.keys() | ||
800 | |||
801 | for po in package_objects: | ||
802 | pkgname = po.name() | ||
803 | if pkgname in localpkgs: | ||
804 | rpmpath = self.localpkgs[pkgname] | ||
805 | else: | ||
806 | rpmpath = self.getLocalPkgPath(po) | ||
807 | |||
808 | if not os.path.exists(rpmpath): | ||
809 | # Maybe it is a local repo | ||
810 | rpmuri = self.get_url(po) | ||
811 | if rpmuri.startswith("file:/"): | ||
812 | rpmpath = rpmuri[5:] | ||
813 | |||
814 | if not os.path.exists(rpmpath): | ||
815 | raise RpmError("Error: %s doesn't exist" % rpmpath) | ||
816 | |||
817 | h = rpmmisc.readRpmHeader(self.ts, rpmpath) | ||
818 | |||
819 | if pkgname in self.pre_pkgs: | ||
820 | msger.verbose("pre-install package added: %s" % pkgname) | ||
821 | self.ts_pre.addInstall(h, rpmpath, 'u') | ||
822 | |||
823 | self.ts.addInstall(h, rpmpath, 'u') | ||
824 | |||
825 | unresolved_dependencies = self.ts.check() | ||
826 | if not unresolved_dependencies: | ||
827 | if self.pre_pkgs: | ||
828 | self.preinstallPkgs() | ||
829 | |||
830 | self.ts.order() | ||
831 | cb = rpmmisc.RPMInstallCallback(self.ts) | ||
832 | installlogfile = "%s/__catched_stderr.buf" % (self.instroot) | ||
833 | |||
834 | # start to catch stderr output from librpm | ||
835 | msger.enable_logstderr(installlogfile) | ||
836 | |||
837 | errors = self.ts.run(cb.callback, '') | ||
838 | # stop catch | ||
839 | msger.disable_logstderr() | ||
840 | self.ts.closeDB() | ||
841 | self.ts = None | ||
842 | |||
843 | if errors is not None: | ||
844 | if len(errors) == 0: | ||
845 | msger.warning('scriptlet or other non-fatal errors occurred ' | ||
846 | 'during transaction.') | ||
847 | |||
848 | else: | ||
849 | for e in errors: | ||
850 | msger.warning(e[0]) | ||
851 | raise RepoError('Could not run transaction.') | ||
852 | |||
853 | else: | ||
854 | for pkg, need, needflags, sense, key in unresolved_dependencies: | ||
855 | package = '-'.join(pkg) | ||
856 | |||
857 | if needflags == rpm.RPMSENSE_LESS: | ||
858 | deppkg = ' < '.join(need) | ||
859 | elif needflags == rpm.RPMSENSE_EQUAL: | ||
860 | deppkg = ' = '.join(need) | ||
861 | elif needflags == rpm.RPMSENSE_GREATER: | ||
862 | deppkg = ' > '.join(need) | ||
863 | else: | ||
864 | deppkg = '-'.join(need) | ||
865 | |||
866 | if sense == rpm.RPMDEP_SENSE_REQUIRES: | ||
867 | msger.warning("[%s] Requires [%s], which is not provided" \ | ||
868 | % (package, deppkg)) | ||
869 | |||
870 | elif sense == rpm.RPMDEP_SENSE_CONFLICTS: | ||
871 | msger.warning("[%s] Conflicts with [%s]" %(package,deppkg)) | ||
872 | |||
873 | raise RepoError("Unresolved dependencies, transaction failed.") | ||
874 | |||
875 | def __initialize_transaction(self): | ||
876 | if not self.ts: | ||
877 | self.ts = rpm.TransactionSet(self.instroot) | ||
878 | # Set to not verify DSA signatures. | ||
879 | self.ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS) | ||
880 | |||
881 | if not self.ts_pre: | ||
882 | self.ts_pre = rpm.TransactionSet(self.instroot) | ||
883 | # Just unpack the files, don't run scripts | ||
884 | self.ts_pre.setFlags(rpm.RPMTRANS_FLAG_ALLFILES | rpm.RPMTRANS_FLAG_NOSCRIPTS) | ||
885 | # Set to not verify DSA signatures. | ||
886 | self.ts_pre.setVSFlags(rpm._RPMVSF_NOSIGNATURES|rpm._RPMVSF_NODIGESTS) | ||
887 | |||
888 | def checkPkg(self, pkg): | ||
889 | ret = 1 | ||
890 | if not os.path.exists(pkg): | ||
891 | return ret | ||
892 | ret = rpmmisc.checkRpmIntegrity('rpm', pkg) | ||
893 | if ret != 0: | ||
894 | msger.warning("package %s is damaged: %s" \ | ||
895 | % (os.path.basename(pkg), pkg)) | ||
896 | |||
897 | return ret | ||
898 | |||
899 | def _add_prob_flags(self, *flags): | ||
900 | for flag in flags: | ||
901 | if flag not in self.probFilterFlags: | ||
902 | self.probFilterFlags.append(flag) | ||
903 | |||
904 | def get_proxies(self, pobj): | ||
905 | if not pobj: | ||
906 | return None | ||
907 | |||
908 | proxy = None | ||
909 | proxies = None | ||
910 | repoinfo = pobj.repoInfo() | ||
911 | reponame = "%s" % repoinfo.name() | ||
912 | repos = filter(lambda r: r.name == reponame, self.repos) | ||
913 | repourl = str(repoinfo.baseUrls()[0]) | ||
914 | |||
915 | if repos: | ||
916 | proxy = repos[0].proxy | ||
917 | if not proxy: | ||
918 | proxy = get_proxy_for(repourl) | ||
919 | if proxy: | ||
920 | proxies = {str(repourl.split(':')[0]): str(proxy)} | ||
921 | |||
922 | return proxies | ||
923 | |||
924 | def get_url(self, pobj): | ||
925 | if not pobj: | ||
926 | return None | ||
927 | |||
928 | name = str(pobj.repoInfo().name()) | ||
929 | try: | ||
930 | repo = filter(lambda r: r.name == name, self.repos)[0] | ||
931 | except IndexError: | ||
932 | return None | ||
933 | |||
934 | baseurl = repo.baseurl[0] | ||
935 | |||
936 | index = baseurl.find("?") | ||
937 | if index > -1: | ||
938 | baseurl = baseurl[:index] | ||
939 | |||
940 | location = pobj.location() | ||
941 | location = str(location.filename()) | ||
942 | if location.startswith("./"): | ||
943 | location = location[2:] | ||
944 | |||
945 | return os.path.join(baseurl, location) | ||
946 | |||
947 | def package_url(self, pkgname): | ||
948 | |||
949 | def cmpEVR(p1, p2): | ||
950 | ed1 = p1.edition() | ||
951 | ed2 = p2.edition() | ||
952 | (e1, v1, r1) = map(str, [ed1.epoch(), ed1.version(), ed1.release()]) | ||
953 | (e2, v2, r2) = map(str, [ed2.epoch(), ed2.version(), ed2.release()]) | ||
954 | return rpm.labelCompare((e1, v1, r1), (e2, v2, r2)) | ||
955 | |||
956 | if not self.Z: | ||
957 | self.__initialize_zypp() | ||
958 | |||
959 | q = zypp.PoolQuery() | ||
960 | q.addKind(zypp.ResKind.package) | ||
961 | q.setMatchExact() | ||
962 | q.addAttribute(zypp.SolvAttr.name, pkgname) | ||
963 | items = sorted(q.queryResults(self.Z.pool()), | ||
964 | cmp=lambda x,y: cmpEVR(zypp.asKindPackage(x), zypp.asKindPackage(y)), | ||
965 | reverse=True) | ||
966 | |||
967 | if items: | ||
968 | item = zypp.asKindPackage(items[0]) | ||
969 | url = self.get_url(item) | ||
970 | proxies = self.get_proxies(item) | ||
971 | return (url, proxies) | ||
972 | |||
973 | return (None, None) | ||
diff --git a/scripts/lib/mic/plugins/hook/.py b/scripts/lib/mic/plugins/hook/.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/mic/plugins/hook/.py | |||
diff --git a/scripts/lib/mic/plugins/hook/empty_hook.py b/scripts/lib/mic/plugins/hook/empty_hook.py new file mode 100644 index 0000000000..397585d8c1 --- /dev/null +++ b/scripts/lib/mic/plugins/hook/empty_hook.py | |||
@@ -0,0 +1,3 @@ | |||
1 | #!/usr/bin/python | ||
2 | |||
3 | # TODO: plugin base for hooks | ||
diff --git a/scripts/lib/mic/plugins/imager/direct_plugin.py b/scripts/lib/mic/plugins/imager/direct_plugin.py new file mode 100644 index 0000000000..fc7c10c3df --- /dev/null +++ b/scripts/lib/mic/plugins/imager/direct_plugin.py | |||
@@ -0,0 +1,107 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2013, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This implements the 'direct' imager plugin class for 'wic', based | ||
22 | # loosely on the raw imager plugin from 'mic' | ||
23 | # | ||
24 | # AUTHORS | ||
25 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
26 | # | ||
27 | |||
28 | import os | ||
29 | import shutil | ||
30 | import re | ||
31 | import tempfile | ||
32 | |||
33 | from mic import chroot, msger | ||
34 | from mic.utils import misc, fs_related, errors, runner, cmdln | ||
35 | from mic.conf import configmgr | ||
36 | from mic.plugin import pluginmgr | ||
37 | from mic.utils.partitionedfs import PartitionedMount | ||
38 | |||
39 | import mic.imager.direct as direct | ||
40 | from mic.pluginbase import ImagerPlugin | ||
41 | |||
42 | class DirectPlugin(ImagerPlugin): | ||
43 | name = 'direct' | ||
44 | |||
45 | @classmethod | ||
46 | def __rootfs_dir_to_dict(self, rootfs_dirs): | ||
47 | """ | ||
48 | Gets a string that contain 'connection=dir' splitted by | ||
49 | space and return a dict | ||
50 | """ | ||
51 | krootfs_dir = {} | ||
52 | for rootfs_dir in rootfs_dirs.split(' '): | ||
53 | k, v = rootfs_dir.split('=') | ||
54 | krootfs_dir[k] = v | ||
55 | |||
56 | return krootfs_dir | ||
57 | |||
58 | @classmethod | ||
59 | def do_create(self, subcmd, opts, *args): | ||
60 | """ | ||
61 | Create direct image, called from creator as 'direct' cmd | ||
62 | """ | ||
63 | if len(args) != 9: | ||
64 | raise errors.Usage("Extra arguments given") | ||
65 | |||
66 | staging_data_dir = args[0] | ||
67 | hdddir = args[1] | ||
68 | native_sysroot = args[2] | ||
69 | kernel_dir = args[3] | ||
70 | bootimg_dir = args[4] | ||
71 | rootfs_dir = args[5] | ||
72 | |||
73 | creatoropts = configmgr.create | ||
74 | ksconf = args[6] | ||
75 | |||
76 | image_output_dir = args[7] | ||
77 | oe_builddir = args[8] | ||
78 | |||
79 | krootfs_dir = self.__rootfs_dir_to_dict(rootfs_dir) | ||
80 | |||
81 | configmgr._ksconf = ksconf | ||
82 | |||
83 | creator = direct.DirectImageCreator(oe_builddir, | ||
84 | image_output_dir, | ||
85 | krootfs_dir, | ||
86 | bootimg_dir, | ||
87 | kernel_dir, | ||
88 | native_sysroot, | ||
89 | hdddir, | ||
90 | staging_data_dir, | ||
91 | creatoropts, | ||
92 | None, | ||
93 | None, | ||
94 | None) | ||
95 | |||
96 | try: | ||
97 | creator.mount(None, creatoropts["cachedir"]) | ||
98 | creator.install() | ||
99 | creator.configure(creatoropts["repomd"]) | ||
100 | creator.print_outimage_info() | ||
101 | |||
102 | except errors.CreatorError: | ||
103 | raise | ||
104 | finally: | ||
105 | creator.cleanup() | ||
106 | |||
107 | return 0 | ||
diff --git a/scripts/lib/mic/plugins/imager/fs_plugin.py b/scripts/lib/mic/plugins/imager/fs_plugin.py new file mode 100644 index 0000000000..6bcaf00729 --- /dev/null +++ b/scripts/lib/mic/plugins/imager/fs_plugin.py | |||
@@ -0,0 +1,143 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import sys | ||
20 | |||
21 | from mic import chroot, msger | ||
22 | from mic.utils import cmdln, misc, errors, fs_related | ||
23 | from mic.imager import fs | ||
24 | from mic.conf import configmgr | ||
25 | from mic.plugin import pluginmgr | ||
26 | |||
27 | from mic.pluginbase import ImagerPlugin | ||
28 | class FsPlugin(ImagerPlugin): | ||
29 | name = 'fs' | ||
30 | |||
31 | @classmethod | ||
32 | @cmdln.option("--include-src", | ||
33 | dest="include_src", | ||
34 | action="store_true", | ||
35 | default=False, | ||
36 | help="Generate a image with source rpms included") | ||
37 | def do_create(self, subcmd, opts, *args): | ||
38 | """${cmd_name}: create fs image | ||
39 | |||
40 | Usage: | ||
41 | ${name} ${cmd_name} <ksfile> [OPTS] | ||
42 | |||
43 | ${cmd_option_list} | ||
44 | """ | ||
45 | |||
46 | if len(args) != 1: | ||
47 | raise errors.Usage("Extra arguments given") | ||
48 | |||
49 | creatoropts = configmgr.create | ||
50 | ksconf = args[0] | ||
51 | |||
52 | if creatoropts['runtime'] == 'bootstrap': | ||
53 | configmgr._ksconf = ksconf | ||
54 | rt_util.bootstrap_mic() | ||
55 | |||
56 | recording_pkgs = [] | ||
57 | if len(creatoropts['record_pkgs']) > 0: | ||
58 | recording_pkgs = creatoropts['record_pkgs'] | ||
59 | |||
60 | if creatoropts['release'] is not None: | ||
61 | if 'name' not in recording_pkgs: | ||
62 | recording_pkgs.append('name') | ||
63 | if 'vcs' not in recording_pkgs: | ||
64 | recording_pkgs.append('vcs') | ||
65 | |||
66 | configmgr._ksconf = ksconf | ||
67 | |||
68 | # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there. | ||
69 | if creatoropts['release'] is not None: | ||
70 | creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name']) | ||
71 | |||
72 | # try to find the pkgmgr | ||
73 | pkgmgr = None | ||
74 | backends = pluginmgr.get_plugins('backend') | ||
75 | if 'auto' == creatoropts['pkgmgr']: | ||
76 | for key in configmgr.prefer_backends: | ||
77 | if key in backends: | ||
78 | pkgmgr = backends[key] | ||
79 | break | ||
80 | else: | ||
81 | for key in backends.keys(): | ||
82 | if key == creatoropts['pkgmgr']: | ||
83 | pkgmgr = backends[key] | ||
84 | break | ||
85 | |||
86 | if not pkgmgr: | ||
87 | raise errors.CreatorError("Can't find backend: %s, " | ||
88 | "available choices: %s" % | ||
89 | (creatoropts['pkgmgr'], | ||
90 | ','.join(backends.keys()))) | ||
91 | |||
92 | creator = fs.FsImageCreator(creatoropts, pkgmgr) | ||
93 | creator._include_src = opts.include_src | ||
94 | |||
95 | if len(recording_pkgs) > 0: | ||
96 | creator._recording_pkgs = recording_pkgs | ||
97 | |||
98 | self.check_image_exists(creator.destdir, | ||
99 | creator.pack_to, | ||
100 | [creator.name], | ||
101 | creatoropts['release']) | ||
102 | |||
103 | try: | ||
104 | creator.check_depend_tools() | ||
105 | creator.mount(None, creatoropts["cachedir"]) | ||
106 | creator.install() | ||
107 | #Download the source packages ###private options | ||
108 | if opts.include_src: | ||
109 | installed_pkgs = creator.get_installed_packages() | ||
110 | msger.info('--------------------------------------------------') | ||
111 | msger.info('Generating the image with source rpms included ...') | ||
112 | if not misc.SrcpkgsDownload(installed_pkgs, creatoropts["repomd"], creator._instroot, creatoropts["cachedir"]): | ||
113 | msger.warning("Source packages can't be downloaded") | ||
114 | |||
115 | creator.configure(creatoropts["repomd"]) | ||
116 | creator.copy_kernel() | ||
117 | creator.unmount() | ||
118 | creator.package(creatoropts["outdir"]) | ||
119 | if creatoropts['release'] is not None: | ||
120 | creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release']) | ||
121 | creator.print_outimage_info() | ||
122 | except errors.CreatorError: | ||
123 | raise | ||
124 | finally: | ||
125 | creator.cleanup() | ||
126 | |||
127 | msger.info("Finished.") | ||
128 | return 0 | ||
129 | |||
130 | @classmethod | ||
131 | def do_chroot(self, target, cmd=[]):#chroot.py parse opts&args | ||
132 | try: | ||
133 | if len(cmd) != 0: | ||
134 | cmdline = ' '.join(cmd) | ||
135 | else: | ||
136 | cmdline = "/bin/bash" | ||
137 | envcmd = fs_related.find_binary_inchroot("env", target) | ||
138 | if envcmd: | ||
139 | cmdline = "%s HOME=/root %s" % (envcmd, cmdline) | ||
140 | chroot.chroot(target, None, cmdline) | ||
141 | finally: | ||
142 | chroot.cleanup_after_chroot("dir", None, None, None) | ||
143 | return 1 | ||
diff --git a/scripts/lib/mic/plugins/imager/livecd_plugin.py b/scripts/lib/mic/plugins/imager/livecd_plugin.py new file mode 100644 index 0000000000..82cb1af7dc --- /dev/null +++ b/scripts/lib/mic/plugins/imager/livecd_plugin.py | |||
@@ -0,0 +1,255 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import shutil | ||
20 | import tempfile | ||
21 | |||
22 | from mic import chroot, msger | ||
23 | from mic.utils import misc, fs_related, errors | ||
24 | from mic.conf import configmgr | ||
25 | import mic.imager.livecd as livecd | ||
26 | from mic.plugin import pluginmgr | ||
27 | |||
28 | from mic.pluginbase import ImagerPlugin | ||
29 | class LiveCDPlugin(ImagerPlugin): | ||
30 | name = 'livecd' | ||
31 | |||
32 | @classmethod | ||
33 | def do_create(self, subcmd, opts, *args): | ||
34 | """${cmd_name}: create livecd image | ||
35 | |||
36 | Usage: | ||
37 | ${name} ${cmd_name} <ksfile> [OPTS] | ||
38 | |||
39 | ${cmd_option_list} | ||
40 | """ | ||
41 | |||
42 | if len(args) != 1: | ||
43 | raise errors.Usage("Extra arguments given") | ||
44 | |||
45 | creatoropts = configmgr.create | ||
46 | ksconf = args[0] | ||
47 | |||
48 | if creatoropts['runtime'] == 'bootstrap': | ||
49 | configmgr._ksconf = ksconf | ||
50 | rt_util.bootstrap_mic() | ||
51 | |||
52 | if creatoropts['arch'] and creatoropts['arch'].startswith('arm'): | ||
53 | msger.warning('livecd cannot support arm images, Quit') | ||
54 | return | ||
55 | |||
56 | recording_pkgs = [] | ||
57 | if len(creatoropts['record_pkgs']) > 0: | ||
58 | recording_pkgs = creatoropts['record_pkgs'] | ||
59 | |||
60 | if creatoropts['release'] is not None: | ||
61 | if 'name' not in recording_pkgs: | ||
62 | recording_pkgs.append('name') | ||
63 | if 'vcs' not in recording_pkgs: | ||
64 | recording_pkgs.append('vcs') | ||
65 | |||
66 | configmgr._ksconf = ksconf | ||
67 | |||
68 | # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there. | ||
69 | if creatoropts['release'] is not None: | ||
70 | creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name']) | ||
71 | |||
72 | # try to find the pkgmgr | ||
73 | pkgmgr = None | ||
74 | backends = pluginmgr.get_plugins('backend') | ||
75 | if 'auto' == creatoropts['pkgmgr']: | ||
76 | for key in configmgr.prefer_backends: | ||
77 | if key in backends: | ||
78 | pkgmgr = backends[key] | ||
79 | break | ||
80 | else: | ||
81 | for key in backends.keys(): | ||
82 | if key == creatoropts['pkgmgr']: | ||
83 | pkgmgr = backends[key] | ||
84 | break | ||
85 | |||
86 | if not pkgmgr: | ||
87 | raise errors.CreatorError("Can't find backend: %s, " | ||
88 | "available choices: %s" % | ||
89 | (creatoropts['pkgmgr'], | ||
90 | ','.join(backends.keys()))) | ||
91 | |||
92 | creator = livecd.LiveCDImageCreator(creatoropts, pkgmgr) | ||
93 | |||
94 | if len(recording_pkgs) > 0: | ||
95 | creator._recording_pkgs = recording_pkgs | ||
96 | |||
97 | self.check_image_exists(creator.destdir, | ||
98 | creator.pack_to, | ||
99 | [creator.name + ".iso"], | ||
100 | creatoropts['release']) | ||
101 | |||
102 | try: | ||
103 | creator.check_depend_tools() | ||
104 | creator.mount(None, creatoropts["cachedir"]) | ||
105 | creator.install() | ||
106 | creator.configure(creatoropts["repomd"]) | ||
107 | creator.copy_kernel() | ||
108 | creator.unmount() | ||
109 | creator.package(creatoropts["outdir"]) | ||
110 | if creatoropts['release'] is not None: | ||
111 | creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release']) | ||
112 | creator.print_outimage_info() | ||
113 | |||
114 | except errors.CreatorError: | ||
115 | raise | ||
116 | finally: | ||
117 | creator.cleanup() | ||
118 | |||
119 | msger.info("Finished.") | ||
120 | return 0 | ||
121 | |||
122 | @classmethod | ||
123 | def do_chroot(cls, target, cmd=[]): | ||
124 | os_image = cls.do_unpack(target) | ||
125 | os_image_dir = os.path.dirname(os_image) | ||
126 | |||
127 | # unpack image to target dir | ||
128 | imgsize = misc.get_file_size(os_image) * 1024L * 1024L | ||
129 | imgtype = misc.get_image_type(os_image) | ||
130 | if imgtype == "btrfsimg": | ||
131 | fstype = "btrfs" | ||
132 | myDiskMount = fs_related.BtrfsDiskMount | ||
133 | elif imgtype in ("ext3fsimg", "ext4fsimg"): | ||
134 | fstype = imgtype[:4] | ||
135 | myDiskMount = fs_related.ExtDiskMount | ||
136 | else: | ||
137 | raise errors.CreatorError("Unsupported filesystem type: %s" % fstype) | ||
138 | |||
139 | extmnt = misc.mkdtemp() | ||
140 | extloop = myDiskMount(fs_related.SparseLoopbackDisk(os_image, imgsize), | ||
141 | extmnt, | ||
142 | fstype, | ||
143 | 4096, | ||
144 | "%s label" % fstype) | ||
145 | try: | ||
146 | extloop.mount() | ||
147 | |||
148 | except errors.MountError: | ||
149 | extloop.cleanup() | ||
150 | shutil.rmtree(extmnt, ignore_errors = True) | ||
151 | shutil.rmtree(os_image_dir, ignore_errors = True) | ||
152 | raise | ||
153 | |||
154 | try: | ||
155 | if len(cmd) != 0: | ||
156 | cmdline = ' '.join(cmd) | ||
157 | else: | ||
158 | cmdline = "/bin/bash" | ||
159 | envcmd = fs_related.find_binary_inchroot("env", extmnt) | ||
160 | if envcmd: | ||
161 | cmdline = "%s HOME=/root %s" % (envcmd, cmdline) | ||
162 | chroot.chroot(extmnt, None, cmdline) | ||
163 | except: | ||
164 | raise errors.CreatorError("Failed to chroot to %s." %target) | ||
165 | finally: | ||
166 | chroot.cleanup_after_chroot("img", extloop, os_image_dir, extmnt) | ||
167 | |||
168 | @classmethod | ||
169 | def do_pack(cls, base_on): | ||
170 | import subprocess | ||
171 | |||
172 | def __mkinitrd(instance): | ||
173 | kernelver = instance._get_kernel_versions().values()[0][0] | ||
174 | args = [ "/usr/libexec/mkliveinitrd", "/boot/initrd-%s.img" % kernelver, "%s" % kernelver ] | ||
175 | try: | ||
176 | subprocess.call(args, preexec_fn = instance._chroot) | ||
177 | except OSError, (err, msg): | ||
178 | raise errors.CreatorError("Failed to execute /usr/libexec/mkliveinitrd: %s" % msg) | ||
179 | |||
180 | def __run_post_cleanups(instance): | ||
181 | kernelver = instance._get_kernel_versions().values()[0][0] | ||
182 | args = ["rm", "-f", "/boot/initrd-%s.img" % kernelver] | ||
183 | |||
184 | try: | ||
185 | subprocess.call(args, preexec_fn = instance._chroot) | ||
186 | except OSError, (err, msg): | ||
187 | raise errors.CreatorError("Failed to run post cleanups: %s" % msg) | ||
188 | |||
189 | convertoropts = configmgr.convert | ||
190 | convertoropts['name'] = os.path.splitext(os.path.basename(base_on))[0] | ||
191 | convertor = livecd.LiveCDImageCreator(convertoropts) | ||
192 | imgtype = misc.get_image_type(base_on) | ||
193 | if imgtype == "btrfsimg": | ||
194 | fstype = "btrfs" | ||
195 | elif imgtype in ("ext3fsimg", "ext4fsimg"): | ||
196 | fstype = imgtype[:4] | ||
197 | else: | ||
198 | raise errors.CreatorError("Unsupported filesystem type: %s" % fstype) | ||
199 | convertor._set_fstype(fstype) | ||
200 | try: | ||
201 | convertor.mount(base_on) | ||
202 | __mkinitrd(convertor) | ||
203 | convertor._create_bootconfig() | ||
204 | __run_post_cleanups(convertor) | ||
205 | convertor.launch_shell(convertoropts['shell']) | ||
206 | convertor.unmount() | ||
207 | convertor.package() | ||
208 | convertor.print_outimage_info() | ||
209 | finally: | ||
210 | shutil.rmtree(os.path.dirname(base_on), ignore_errors = True) | ||
211 | |||
212 | @classmethod | ||
213 | def do_unpack(cls, srcimg): | ||
214 | img = srcimg | ||
215 | imgmnt = misc.mkdtemp() | ||
216 | imgloop = fs_related.DiskMount(fs_related.LoopbackDisk(img, 0), imgmnt) | ||
217 | try: | ||
218 | imgloop.mount() | ||
219 | except errors.MountError: | ||
220 | imgloop.cleanup() | ||
221 | raise | ||
222 | |||
223 | # legacy LiveOS filesystem layout support, remove for F9 or F10 | ||
224 | if os.path.exists(imgmnt + "/squashfs.img"): | ||
225 | squashimg = imgmnt + "/squashfs.img" | ||
226 | else: | ||
227 | squashimg = imgmnt + "/LiveOS/squashfs.img" | ||
228 | |||
229 | tmpoutdir = misc.mkdtemp() | ||
230 | # unsquashfs requires outdir mustn't exist | ||
231 | shutil.rmtree(tmpoutdir, ignore_errors = True) | ||
232 | misc.uncompress_squashfs(squashimg, tmpoutdir) | ||
233 | |||
234 | try: | ||
235 | # legacy LiveOS filesystem layout support, remove for F9 or F10 | ||
236 | if os.path.exists(tmpoutdir + "/os.img"): | ||
237 | os_image = tmpoutdir + "/os.img" | ||
238 | else: | ||
239 | os_image = tmpoutdir + "/LiveOS/ext3fs.img" | ||
240 | |||
241 | if not os.path.exists(os_image): | ||
242 | raise errors.CreatorError("'%s' is not a valid live CD ISO : neither " | ||
243 | "LiveOS/ext3fs.img nor os.img exist" %img) | ||
244 | |||
245 | imgname = os.path.basename(srcimg) | ||
246 | imgname = os.path.splitext(imgname)[0] + ".img" | ||
247 | rtimage = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), imgname) | ||
248 | shutil.copyfile(os_image, rtimage) | ||
249 | |||
250 | finally: | ||
251 | imgloop.cleanup() | ||
252 | shutil.rmtree(tmpoutdir, ignore_errors = True) | ||
253 | shutil.rmtree(imgmnt, ignore_errors = True) | ||
254 | |||
255 | return rtimage | ||
diff --git a/scripts/lib/mic/plugins/imager/liveusb_plugin.py b/scripts/lib/mic/plugins/imager/liveusb_plugin.py new file mode 100644 index 0000000000..3d53c84410 --- /dev/null +++ b/scripts/lib/mic/plugins/imager/liveusb_plugin.py | |||
@@ -0,0 +1,260 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import shutil | ||
20 | import tempfile | ||
21 | |||
22 | from mic import chroot, msger | ||
23 | from mic.utils import misc, fs_related, errors | ||
24 | from mic.utils.partitionedfs import PartitionedMount | ||
25 | from mic.conf import configmgr | ||
26 | from mic.plugin import pluginmgr | ||
27 | |||
28 | import mic.imager.liveusb as liveusb | ||
29 | |||
30 | from mic.pluginbase import ImagerPlugin | ||
31 | class LiveUSBPlugin(ImagerPlugin): | ||
32 | name = 'liveusb' | ||
33 | |||
34 | @classmethod | ||
35 | def do_create(self, subcmd, opts, *args): | ||
36 | """${cmd_name}: create liveusb image | ||
37 | |||
38 | Usage: | ||
39 | ${name} ${cmd_name} <ksfile> [OPTS] | ||
40 | |||
41 | ${cmd_option_list} | ||
42 | """ | ||
43 | |||
44 | if len(args) != 1: | ||
45 | raise errors.Usage("Extra arguments given") | ||
46 | |||
47 | creatoropts = configmgr.create | ||
48 | ksconf = args[0] | ||
49 | |||
50 | if creatoropts['runtime'] == "bootstrap": | ||
51 | configmgr._ksconf = ksconf | ||
52 | rt_util.bootstrap_mic() | ||
53 | |||
54 | if creatoropts['arch'] and creatoropts['arch'].startswith('arm'): | ||
55 | msger.warning('liveusb cannot support arm images, Quit') | ||
56 | return | ||
57 | |||
58 | recording_pkgs = [] | ||
59 | if len(creatoropts['record_pkgs']) > 0: | ||
60 | recording_pkgs = creatoropts['record_pkgs'] | ||
61 | |||
62 | if creatoropts['release'] is not None: | ||
63 | if 'name' not in recording_pkgs: | ||
64 | recording_pkgs.append('name') | ||
65 | if 'vcs' not in recording_pkgs: | ||
66 | recording_pkgs.append('vcs') | ||
67 | |||
68 | configmgr._ksconf = ksconf | ||
69 | |||
70 | # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there. | ||
71 | if creatoropts['release'] is not None: | ||
72 | creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name']) | ||
73 | |||
74 | # try to find the pkgmgr | ||
75 | pkgmgr = None | ||
76 | backends = pluginmgr.get_plugins('backend') | ||
77 | if 'auto' == creatoropts['pkgmgr']: | ||
78 | for key in configmgr.prefer_backends: | ||
79 | if key in backends: | ||
80 | pkgmgr = backends[key] | ||
81 | break | ||
82 | else: | ||
83 | for key in backends.keys(): | ||
84 | if key == creatoropts['pkgmgr']: | ||
85 | pkgmgr = backends[key] | ||
86 | break | ||
87 | |||
88 | if not pkgmgr: | ||
89 | raise errors.CreatorError("Can't find backend: %s, " | ||
90 | "available choices: %s" % | ||
91 | (creatoropts['pkgmgr'], | ||
92 | ','.join(backends.keys()))) | ||
93 | |||
94 | creator = liveusb.LiveUSBImageCreator(creatoropts, pkgmgr) | ||
95 | |||
96 | if len(recording_pkgs) > 0: | ||
97 | creator._recording_pkgs = recording_pkgs | ||
98 | |||
99 | self.check_image_exists(creator.destdir, | ||
100 | creator.pack_to, | ||
101 | [creator.name + ".usbimg"], | ||
102 | creatoropts['release']) | ||
103 | try: | ||
104 | creator.check_depend_tools() | ||
105 | creator.mount(None, creatoropts["cachedir"]) | ||
106 | creator.install() | ||
107 | creator.configure(creatoropts["repomd"]) | ||
108 | creator.copy_kernel() | ||
109 | creator.unmount() | ||
110 | creator.package(creatoropts["outdir"]) | ||
111 | if creatoropts['release'] is not None: | ||
112 | creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release']) | ||
113 | creator.print_outimage_info() | ||
114 | |||
115 | except errors.CreatorError: | ||
116 | raise | ||
117 | finally: | ||
118 | creator.cleanup() | ||
119 | |||
120 | msger.info("Finished.") | ||
121 | return 0 | ||
122 | |||
123 | @classmethod | ||
124 | def do_chroot(cls, target, cmd=[]): | ||
125 | os_image = cls.do_unpack(target) | ||
126 | os_image_dir = os.path.dirname(os_image) | ||
127 | |||
128 | # unpack image to target dir | ||
129 | imgsize = misc.get_file_size(os_image) * 1024L * 1024L | ||
130 | imgtype = misc.get_image_type(os_image) | ||
131 | if imgtype == "btrfsimg": | ||
132 | fstype = "btrfs" | ||
133 | myDiskMount = fs_related.BtrfsDiskMount | ||
134 | elif imgtype in ("ext3fsimg", "ext4fsimg"): | ||
135 | fstype = imgtype[:4] | ||
136 | myDiskMount = fs_related.ExtDiskMount | ||
137 | else: | ||
138 | raise errors.CreatorError("Unsupported filesystem type: %s" % fstype) | ||
139 | |||
140 | extmnt = misc.mkdtemp() | ||
141 | extloop = myDiskMount(fs_related.SparseLoopbackDisk(os_image, imgsize), | ||
142 | extmnt, | ||
143 | fstype, | ||
144 | 4096, | ||
145 | "%s label" % fstype) | ||
146 | |||
147 | try: | ||
148 | extloop.mount() | ||
149 | |||
150 | except errors.MountError: | ||
151 | extloop.cleanup() | ||
152 | shutil.rmtree(extmnt, ignore_errors = True) | ||
153 | raise | ||
154 | |||
155 | try: | ||
156 | if len(cmd) != 0: | ||
157 | cmdline = ' '.join(cmd) | ||
158 | else: | ||
159 | cmdline = "/bin/bash" | ||
160 | envcmd = fs_related.find_binary_inchroot("env", extmnt) | ||
161 | if envcmd: | ||
162 | cmdline = "%s HOME=/root %s" % (envcmd, cmdline) | ||
163 | chroot.chroot(extmnt, None, cmdline) | ||
164 | except: | ||
165 | raise errors.CreatorError("Failed to chroot to %s." %target) | ||
166 | finally: | ||
167 | chroot.cleanup_after_chroot("img", extloop, os_image_dir, extmnt) | ||
168 | |||
169 | @classmethod | ||
170 | def do_pack(cls, base_on): | ||
171 | import subprocess | ||
172 | |||
173 | def __mkinitrd(instance): | ||
174 | kernelver = instance._get_kernel_versions().values()[0][0] | ||
175 | args = [ "/usr/libexec/mkliveinitrd", "/boot/initrd-%s.img" % kernelver, "%s" % kernelver ] | ||
176 | try: | ||
177 | subprocess.call(args, preexec_fn = instance._chroot) | ||
178 | |||
179 | except OSError, (err, msg): | ||
180 | raise errors.CreatorError("Failed to execute /usr/libexec/mkliveinitrd: %s" % msg) | ||
181 | |||
182 | def __run_post_cleanups(instance): | ||
183 | kernelver = instance._get_kernel_versions().values()[0][0] | ||
184 | args = ["rm", "-f", "/boot/initrd-%s.img" % kernelver] | ||
185 | |||
186 | try: | ||
187 | subprocess.call(args, preexec_fn = instance._chroot) | ||
188 | except OSError, (err, msg): | ||
189 | raise errors.CreatorError("Failed to run post cleanups: %s" % msg) | ||
190 | |||
191 | convertoropts = configmgr.convert | ||
192 | convertoropts['name'] = os.path.splitext(os.path.basename(base_on))[0] | ||
193 | convertor = liveusb.LiveUSBImageCreator(convertoropts) | ||
194 | imgtype = misc.get_image_type(base_on) | ||
195 | if imgtype == "btrfsimg": | ||
196 | fstype = "btrfs" | ||
197 | elif imgtype in ("ext3fsimg", "ext4fsimg"): | ||
198 | fstype = imgtype[:4] | ||
199 | else: | ||
200 | raise errors.CreatorError("Unsupported filesystem type: %s" % fstyp) | ||
201 | convertor._set_fstype(fstype) | ||
202 | try: | ||
203 | convertor.mount(base_on) | ||
204 | __mkinitrd(convertor) | ||
205 | convertor._create_bootconfig() | ||
206 | __run_post_cleanups(convertor) | ||
207 | convertor.launch_shell(convertoropts['shell']) | ||
208 | convertor.unmount() | ||
209 | convertor.package() | ||
210 | convertor.print_outimage_info() | ||
211 | finally: | ||
212 | shutil.rmtree(os.path.dirname(base_on), ignore_errors = True) | ||
213 | |||
214 | @classmethod | ||
215 | def do_unpack(cls, srcimg): | ||
216 | img = srcimg | ||
217 | imgsize = misc.get_file_size(img) * 1024L * 1024L | ||
218 | imgmnt = misc.mkdtemp() | ||
219 | disk = fs_related.SparseLoopbackDisk(img, imgsize) | ||
220 | imgloop = PartitionedMount(imgmnt, skipformat = True) | ||
221 | imgloop.add_disk('/dev/sdb', disk) | ||
222 | imgloop.add_partition(imgsize/1024/1024, "/dev/sdb", "/", "vfat", boot=False) | ||
223 | try: | ||
224 | imgloop.mount() | ||
225 | except errors.MountError: | ||
226 | imgloop.cleanup() | ||
227 | raise | ||
228 | |||
229 | # legacy LiveOS filesystem layout support, remove for F9 or F10 | ||
230 | if os.path.exists(imgmnt + "/squashfs.img"): | ||
231 | squashimg = imgmnt + "/squashfs.img" | ||
232 | else: | ||
233 | squashimg = imgmnt + "/LiveOS/squashfs.img" | ||
234 | |||
235 | tmpoutdir = misc.mkdtemp() | ||
236 | # unsquashfs requires outdir mustn't exist | ||
237 | shutil.rmtree(tmpoutdir, ignore_errors = True) | ||
238 | misc.uncompress_squashfs(squashimg, tmpoutdir) | ||
239 | |||
240 | try: | ||
241 | # legacy LiveOS filesystem layout support, remove for F9 or F10 | ||
242 | if os.path.exists(tmpoutdir + "/os.img"): | ||
243 | os_image = tmpoutdir + "/os.img" | ||
244 | else: | ||
245 | os_image = tmpoutdir + "/LiveOS/ext3fs.img" | ||
246 | |||
247 | if not os.path.exists(os_image): | ||
248 | raise errors.CreatorError("'%s' is not a valid live CD ISO : neither " | ||
249 | "LiveOS/ext3fs.img nor os.img exist" %img) | ||
250 | imgname = os.path.basename(srcimg) | ||
251 | imgname = os.path.splitext(imgname)[0] + ".img" | ||
252 | rtimage = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), imgname) | ||
253 | shutil.copyfile(os_image, rtimage) | ||
254 | |||
255 | finally: | ||
256 | imgloop.cleanup() | ||
257 | shutil.rmtree(tmpoutdir, ignore_errors = True) | ||
258 | shutil.rmtree(imgmnt, ignore_errors = True) | ||
259 | |||
260 | return rtimage | ||
diff --git a/scripts/lib/mic/plugins/imager/loop_plugin.py b/scripts/lib/mic/plugins/imager/loop_plugin.py new file mode 100644 index 0000000000..2a05b3c238 --- /dev/null +++ b/scripts/lib/mic/plugins/imager/loop_plugin.py | |||
@@ -0,0 +1,255 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import shutil | ||
20 | import tempfile | ||
21 | |||
22 | from mic import chroot, msger | ||
23 | from mic.utils import misc, fs_related, errors, cmdln | ||
24 | from mic.conf import configmgr | ||
25 | from mic.plugin import pluginmgr | ||
26 | from mic.imager.loop import LoopImageCreator, load_mountpoints | ||
27 | |||
28 | from mic.pluginbase import ImagerPlugin | ||
29 | class LoopPlugin(ImagerPlugin): | ||
30 | name = 'loop' | ||
31 | |||
32 | @classmethod | ||
33 | @cmdln.option("--compress-disk-image", dest="compress_image", | ||
34 | type='choice', choices=("gz", "bz2"), default=None, | ||
35 | help="Same with --compress-image") | ||
36 | # alias to compress-image for compatibility | ||
37 | @cmdln.option("--compress-image", dest="compress_image", | ||
38 | type='choice', choices=("gz", "bz2"), default=None, | ||
39 | help="Compress all loop images with 'gz' or 'bz2'") | ||
40 | @cmdln.option("--shrink", action='store_true', default=False, | ||
41 | help="Whether to shrink loop images to minimal size") | ||
42 | def do_create(self, subcmd, opts, *args): | ||
43 | """${cmd_name}: create loop image | ||
44 | |||
45 | Usage: | ||
46 | ${name} ${cmd_name} <ksfile> [OPTS] | ||
47 | |||
48 | ${cmd_option_list} | ||
49 | """ | ||
50 | |||
51 | if len(args) != 1: | ||
52 | raise errors.Usage("Extra arguments given") | ||
53 | |||
54 | creatoropts = configmgr.create | ||
55 | ksconf = args[0] | ||
56 | |||
57 | if creatoropts['runtime'] == "bootstrap": | ||
58 | configmgr._ksconf = ksconf | ||
59 | rt_util.bootstrap_mic() | ||
60 | |||
61 | recording_pkgs = [] | ||
62 | if len(creatoropts['record_pkgs']) > 0: | ||
63 | recording_pkgs = creatoropts['record_pkgs'] | ||
64 | |||
65 | if creatoropts['release'] is not None: | ||
66 | if 'name' not in recording_pkgs: | ||
67 | recording_pkgs.append('name') | ||
68 | if 'vcs' not in recording_pkgs: | ||
69 | recording_pkgs.append('vcs') | ||
70 | |||
71 | configmgr._ksconf = ksconf | ||
72 | |||
73 | # Called After setting the configmgr._ksconf | ||
74 | # as the creatoropts['name'] is reset there. | ||
75 | if creatoropts['release'] is not None: | ||
76 | creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], | ||
77 | creatoropts['release'], | ||
78 | creatoropts['name']) | ||
79 | # try to find the pkgmgr | ||
80 | pkgmgr = None | ||
81 | backends = pluginmgr.get_plugins('backend') | ||
82 | if 'auto' == creatoropts['pkgmgr']: | ||
83 | for key in configmgr.prefer_backends: | ||
84 | if key in backends: | ||
85 | pkgmgr = backends[key] | ||
86 | break | ||
87 | else: | ||
88 | for key in backends.keys(): | ||
89 | if key == creatoropts['pkgmgr']: | ||
90 | pkgmgr = backends[key] | ||
91 | break | ||
92 | |||
93 | if not pkgmgr: | ||
94 | raise errors.CreatorError("Can't find backend: %s, " | ||
95 | "available choices: %s" % | ||
96 | (creatoropts['pkgmgr'], | ||
97 | ','.join(backends.keys()))) | ||
98 | |||
99 | creator = LoopImageCreator(creatoropts, | ||
100 | pkgmgr, | ||
101 | opts.compress_image, | ||
102 | opts.shrink) | ||
103 | |||
104 | if len(recording_pkgs) > 0: | ||
105 | creator._recording_pkgs = recording_pkgs | ||
106 | |||
107 | image_names = [creator.name + ".img"] | ||
108 | image_names.extend(creator.get_image_names()) | ||
109 | self.check_image_exists(creator.destdir, | ||
110 | creator.pack_to, | ||
111 | image_names, | ||
112 | creatoropts['release']) | ||
113 | |||
114 | try: | ||
115 | creator.check_depend_tools() | ||
116 | creator.mount(None, creatoropts["cachedir"]) | ||
117 | creator.install() | ||
118 | creator.configure(creatoropts["repomd"]) | ||
119 | creator.copy_kernel() | ||
120 | creator.unmount() | ||
121 | creator.package(creatoropts["outdir"]) | ||
122 | |||
123 | if creatoropts['release'] is not None: | ||
124 | creator.release_output(ksconf, | ||
125 | creatoropts['outdir'], | ||
126 | creatoropts['release']) | ||
127 | creator.print_outimage_info() | ||
128 | |||
129 | except errors.CreatorError: | ||
130 | raise | ||
131 | finally: | ||
132 | creator.cleanup() | ||
133 | |||
134 | msger.info("Finished.") | ||
135 | return 0 | ||
136 | |||
137 | @classmethod | ||
138 | def _do_chroot_tar(cls, target, cmd=[]): | ||
139 | mountfp_xml = os.path.splitext(target)[0] + '.xml' | ||
140 | if not os.path.exists(mountfp_xml): | ||
141 | raise errors.CreatorError("No mount point file found for this tar " | ||
142 | "image, please check %s" % mountfp_xml) | ||
143 | |||
144 | import tarfile | ||
145 | tar = tarfile.open(target, 'r') | ||
146 | tmpdir = misc.mkdtemp() | ||
147 | tar.extractall(path=tmpdir) | ||
148 | tar.close() | ||
149 | |||
150 | mntdir = misc.mkdtemp() | ||
151 | |||
152 | loops = [] | ||
153 | for (mp, label, name, size, fstype) in load_mountpoints(mountfp_xml): | ||
154 | if fstype in ("ext2", "ext3", "ext4"): | ||
155 | myDiskMount = fs_related.ExtDiskMount | ||
156 | elif fstype == "btrfs": | ||
157 | myDiskMount = fs_related.BtrfsDiskMount | ||
158 | elif fstype in ("vfat", "msdos"): | ||
159 | myDiskMount = fs_related.VfatDiskMount | ||
160 | else: | ||
161 | msger.error("Cannot support fstype: %s" % fstype) | ||
162 | |||
163 | name = os.path.join(tmpdir, name) | ||
164 | size = size * 1024L * 1024L | ||
165 | loop = myDiskMount(fs_related.SparseLoopbackDisk(name, size), | ||
166 | os.path.join(mntdir, mp.lstrip('/')), | ||
167 | fstype, size, label) | ||
168 | |||
169 | try: | ||
170 | msger.verbose("Mount %s to %s" % (mp, mntdir + mp)) | ||
171 | fs_related.makedirs(os.path.join(mntdir, mp.lstrip('/'))) | ||
172 | loop.mount() | ||
173 | |||
174 | except: | ||
175 | loop.cleanup() | ||
176 | for lp in reversed(loops): | ||
177 | chroot.cleanup_after_chroot("img", lp, None, mntdir) | ||
178 | |||
179 | shutil.rmtree(tmpdir, ignore_errors=True) | ||
180 | raise | ||
181 | |||
182 | loops.append(loop) | ||
183 | |||
184 | try: | ||
185 | if len(cmd) != 0: | ||
186 | cmdline = "/usr/bin/env HOME=/root " + ' '.join(cmd) | ||
187 | else: | ||
188 | cmdline = "/usr/bin/env HOME=/root /bin/bash" | ||
189 | chroot.chroot(mntdir, None, cmdline) | ||
190 | except: | ||
191 | raise errors.CreatorError("Failed to chroot to %s." % target) | ||
192 | finally: | ||
193 | for loop in reversed(loops): | ||
194 | chroot.cleanup_after_chroot("img", loop, None, mntdir) | ||
195 | |||
196 | shutil.rmtree(tmpdir, ignore_errors=True) | ||
197 | |||
198 | @classmethod | ||
199 | def do_chroot(cls, target, cmd=[]): | ||
200 | if target.endswith('.tar'): | ||
201 | import tarfile | ||
202 | if tarfile.is_tarfile(target): | ||
203 | LoopPlugin._do_chroot_tar(target, cmd) | ||
204 | return | ||
205 | else: | ||
206 | raise errors.CreatorError("damaged tarball for loop images") | ||
207 | |||
208 | img = target | ||
209 | imgsize = misc.get_file_size(img) * 1024L * 1024L | ||
210 | imgtype = misc.get_image_type(img) | ||
211 | if imgtype == "btrfsimg": | ||
212 | fstype = "btrfs" | ||
213 | myDiskMount = fs_related.BtrfsDiskMount | ||
214 | elif imgtype in ("ext3fsimg", "ext4fsimg"): | ||
215 | fstype = imgtype[:4] | ||
216 | myDiskMount = fs_related.ExtDiskMount | ||
217 | else: | ||
218 | raise errors.CreatorError("Unsupported filesystem type: %s" \ | ||
219 | % imgtype) | ||
220 | |||
221 | extmnt = misc.mkdtemp() | ||
222 | extloop = myDiskMount(fs_related.SparseLoopbackDisk(img, imgsize), | ||
223 | extmnt, | ||
224 | fstype, | ||
225 | 4096, | ||
226 | "%s label" % fstype) | ||
227 | try: | ||
228 | extloop.mount() | ||
229 | |||
230 | except errors.MountError: | ||
231 | extloop.cleanup() | ||
232 | shutil.rmtree(extmnt, ignore_errors=True) | ||
233 | raise | ||
234 | |||
235 | try: | ||
236 | if len(cmd) != 0: | ||
237 | cmdline = ' '.join(cmd) | ||
238 | else: | ||
239 | cmdline = "/bin/bash" | ||
240 | envcmd = fs_related.find_binary_inchroot("env", extmnt) | ||
241 | if envcmd: | ||
242 | cmdline = "%s HOME=/root %s" % (envcmd, cmdline) | ||
243 | chroot.chroot(extmnt, None, cmdline) | ||
244 | except: | ||
245 | raise errors.CreatorError("Failed to chroot to %s." % img) | ||
246 | finally: | ||
247 | chroot.cleanup_after_chroot("img", extloop, None, extmnt) | ||
248 | |||
249 | @classmethod | ||
250 | def do_unpack(cls, srcimg): | ||
251 | image = os.path.join(tempfile.mkdtemp(dir="/var/tmp", prefix="tmp"), | ||
252 | "target.img") | ||
253 | msger.info("Copying file system ...") | ||
254 | shutil.copyfile(srcimg, image) | ||
255 | return image | ||
diff --git a/scripts/lib/mic/plugins/imager/raw_plugin.py b/scripts/lib/mic/plugins/imager/raw_plugin.py new file mode 100644 index 0000000000..f9625b87e8 --- /dev/null +++ b/scripts/lib/mic/plugins/imager/raw_plugin.py | |||
@@ -0,0 +1,275 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import shutil | ||
20 | import re | ||
21 | import tempfile | ||
22 | |||
23 | from mic import chroot, msger | ||
24 | from mic.utils import misc, fs_related, errors, runner, cmdln | ||
25 | from mic.conf import configmgr | ||
26 | from mic.plugin import pluginmgr | ||
27 | from mic.utils.partitionedfs import PartitionedMount | ||
28 | |||
29 | import mic.imager.raw as raw | ||
30 | |||
31 | from mic.pluginbase import ImagerPlugin | ||
32 | class RawPlugin(ImagerPlugin): | ||
33 | name = 'raw' | ||
34 | |||
35 | @classmethod | ||
36 | @cmdln.option("--compress-disk-image", dest="compress_image", type='choice', | ||
37 | choices=("gz", "bz2"), default=None, | ||
38 | help="Same with --compress-image") | ||
39 | @cmdln.option("--compress-image", dest="compress_image", type='choice', | ||
40 | choices=("gz", "bz2"), default = None, | ||
41 | help="Compress all raw images before package") | ||
42 | @cmdln.option("--generate-bmap", action="store_true", default = None, | ||
43 | help="also generate the block map file") | ||
44 | @cmdln.option("--fstab-entry", dest="fstab_entry", type='choice', | ||
45 | choices=("name", "uuid"), default="uuid", | ||
46 | help="Set fstab entry, 'name' means using device names, " | ||
47 | "'uuid' means using filesystem uuid") | ||
48 | def do_create(self, subcmd, opts, *args): | ||
49 | """${cmd_name}: create raw image | ||
50 | |||
51 | Usage: | ||
52 | ${name} ${cmd_name} <ksfile> [OPTS] | ||
53 | |||
54 | ${cmd_option_list} | ||
55 | """ | ||
56 | |||
57 | if len(args) != 1: | ||
58 | raise errors.Usage("Extra arguments given") | ||
59 | |||
60 | creatoropts = configmgr.create | ||
61 | ksconf = args[0] | ||
62 | |||
63 | if creatoropts['runtime'] == "bootstrap": | ||
64 | configmgr._ksconf = ksconf | ||
65 | rt_util.bootstrap_mic() | ||
66 | |||
67 | recording_pkgs = [] | ||
68 | if len(creatoropts['record_pkgs']) > 0: | ||
69 | recording_pkgs = creatoropts['record_pkgs'] | ||
70 | |||
71 | if creatoropts['release'] is not None: | ||
72 | if 'name' not in recording_pkgs: | ||
73 | recording_pkgs.append('name') | ||
74 | if 'vcs' not in recording_pkgs: | ||
75 | recording_pkgs.append('vcs') | ||
76 | |||
77 | configmgr._ksconf = ksconf | ||
78 | |||
79 | # Called After setting the configmgr._ksconf as the creatoropts['name'] is reset there. | ||
80 | if creatoropts['release'] is not None: | ||
81 | creatoropts['outdir'] = "%s/%s/images/%s/" % (creatoropts['outdir'], creatoropts['release'], creatoropts['name']) | ||
82 | |||
83 | # try to find the pkgmgr | ||
84 | pkgmgr = None | ||
85 | backends = pluginmgr.get_plugins('backend') | ||
86 | if 'auto' == creatoropts['pkgmgr']: | ||
87 | for key in configmgr.prefer_backends: | ||
88 | if key in backends: | ||
89 | pkgmgr = backends[key] | ||
90 | break | ||
91 | else: | ||
92 | for key in backends.keys(): | ||
93 | if key == creatoropts['pkgmgr']: | ||
94 | pkgmgr = backends[key] | ||
95 | break | ||
96 | |||
97 | if not pkgmgr: | ||
98 | raise errors.CreatorError("Can't find backend: %s, " | ||
99 | "available choices: %s" % | ||
100 | (creatoropts['pkgmgr'], | ||
101 | ','.join(backends.keys()))) | ||
102 | |||
103 | creator = raw.RawImageCreator(creatoropts, pkgmgr, opts.compress_image, | ||
104 | opts.generate_bmap, opts.fstab_entry) | ||
105 | |||
106 | if len(recording_pkgs) > 0: | ||
107 | creator._recording_pkgs = recording_pkgs | ||
108 | |||
109 | images = ["%s-%s.raw" % (creator.name, disk_name) | ||
110 | for disk_name in creator.get_disk_names()] | ||
111 | self.check_image_exists(creator.destdir, | ||
112 | creator.pack_to, | ||
113 | images, | ||
114 | creatoropts['release']) | ||
115 | |||
116 | try: | ||
117 | creator.check_depend_tools() | ||
118 | creator.mount(None, creatoropts["cachedir"]) | ||
119 | creator.install() | ||
120 | creator.configure(creatoropts["repomd"]) | ||
121 | creator.copy_kernel() | ||
122 | creator.unmount() | ||
123 | creator.generate_bmap() | ||
124 | creator.package(creatoropts["outdir"]) | ||
125 | if creatoropts['release'] is not None: | ||
126 | creator.release_output(ksconf, creatoropts['outdir'], creatoropts['release']) | ||
127 | creator.print_outimage_info() | ||
128 | |||
129 | except errors.CreatorError: | ||
130 | raise | ||
131 | finally: | ||
132 | creator.cleanup() | ||
133 | |||
134 | msger.info("Finished.") | ||
135 | return 0 | ||
136 | |||
137 | @classmethod | ||
138 | def do_chroot(cls, target, cmd=[]): | ||
139 | img = target | ||
140 | imgsize = misc.get_file_size(img) * 1024L * 1024L | ||
141 | partedcmd = fs_related.find_binary_path("parted") | ||
142 | disk = fs_related.SparseLoopbackDisk(img, imgsize) | ||
143 | imgmnt = misc.mkdtemp() | ||
144 | imgloop = PartitionedMount(imgmnt, skipformat = True) | ||
145 | imgloop.add_disk('/dev/sdb', disk) | ||
146 | img_fstype = "ext3" | ||
147 | |||
148 | msger.info("Partition Table:") | ||
149 | partnum = [] | ||
150 | for line in runner.outs([partedcmd, "-s", img, "print"]).splitlines(): | ||
151 | # no use strip to keep line output here | ||
152 | if "Number" in line: | ||
153 | msger.raw(line) | ||
154 | if line.strip() and line.strip()[0].isdigit(): | ||
155 | partnum.append(line.strip()[0]) | ||
156 | msger.raw(line) | ||
157 | |||
158 | rootpart = None | ||
159 | if len(partnum) > 1: | ||
160 | rootpart = msger.choice("please choose root partition", partnum) | ||
161 | |||
162 | # Check the partitions from raw disk. | ||
163 | # if choose root part, the mark it as mounted | ||
164 | if rootpart: | ||
165 | root_mounted = True | ||
166 | else: | ||
167 | root_mounted = False | ||
168 | partition_mounts = 0 | ||
169 | for line in runner.outs([partedcmd,"-s",img,"unit","B","print"]).splitlines(): | ||
170 | line = line.strip() | ||
171 | |||
172 | # Lines that start with number are the partitions, | ||
173 | # because parted can be translated we can't refer to any text lines. | ||
174 | if not line or not line[0].isdigit(): | ||
175 | continue | ||
176 | |||
177 | # Some vars have extra , as list seperator. | ||
178 | line = line.replace(",","") | ||
179 | |||
180 | # Example of parted output lines that are handled: | ||
181 | # Number Start End Size Type File system Flags | ||
182 | # 1 512B 3400000511B 3400000000B primary | ||
183 | # 2 3400531968B 3656384511B 255852544B primary linux-swap(v1) | ||
184 | # 3 3656384512B 3720347647B 63963136B primary fat16 boot, lba | ||
185 | |||
186 | partition_info = re.split("\s+",line) | ||
187 | |||
188 | size = partition_info[3].split("B")[0] | ||
189 | |||
190 | if len(partition_info) < 6 or partition_info[5] in ["boot"]: | ||
191 | # No filesystem can be found from partition line. Assuming | ||
192 | # btrfs, because that is the only MeeGo fs that parted does | ||
193 | # not recognize properly. | ||
194 | # TODO: Can we make better assumption? | ||
195 | fstype = "btrfs" | ||
196 | elif partition_info[5] in ["ext2","ext3","ext4","btrfs"]: | ||
197 | fstype = partition_info[5] | ||
198 | elif partition_info[5] in ["fat16","fat32"]: | ||
199 | fstype = "vfat" | ||
200 | elif "swap" in partition_info[5]: | ||
201 | fstype = "swap" | ||
202 | else: | ||
203 | raise errors.CreatorError("Could not recognize partition fs type '%s'." % partition_info[5]) | ||
204 | |||
205 | if rootpart and rootpart == line[0]: | ||
206 | mountpoint = '/' | ||
207 | elif not root_mounted and fstype in ["ext2","ext3","ext4","btrfs"]: | ||
208 | # TODO: Check that this is actually the valid root partition from /etc/fstab | ||
209 | mountpoint = "/" | ||
210 | root_mounted = True | ||
211 | elif fstype == "swap": | ||
212 | mountpoint = "swap" | ||
213 | else: | ||
214 | # TODO: Assing better mount points for the rest of the partitions. | ||
215 | partition_mounts += 1 | ||
216 | mountpoint = "/media/partition_%d" % partition_mounts | ||
217 | |||
218 | if "boot" in partition_info: | ||
219 | boot = True | ||
220 | else: | ||
221 | boot = False | ||
222 | |||
223 | msger.verbose("Size: %s Bytes, fstype: %s, mountpoint: %s, boot: %s" % (size, fstype, mountpoint, boot)) | ||
224 | # TODO: add_partition should take bytes as size parameter. | ||
225 | imgloop.add_partition((int)(size)/1024/1024, "/dev/sdb", mountpoint, fstype = fstype, boot = boot) | ||
226 | |||
227 | try: | ||
228 | imgloop.mount() | ||
229 | |||
230 | except errors.MountError: | ||
231 | imgloop.cleanup() | ||
232 | raise | ||
233 | |||
234 | try: | ||
235 | if len(cmd) != 0: | ||
236 | cmdline = ' '.join(cmd) | ||
237 | else: | ||
238 | cmdline = "/bin/bash" | ||
239 | envcmd = fs_related.find_binary_inchroot("env", imgmnt) | ||
240 | if envcmd: | ||
241 | cmdline = "%s HOME=/root %s" % (envcmd, cmdline) | ||
242 | chroot.chroot(imgmnt, None, cmdline) | ||
243 | except: | ||
244 | raise errors.CreatorError("Failed to chroot to %s." %img) | ||
245 | finally: | ||
246 | chroot.cleanup_after_chroot("img", imgloop, None, imgmnt) | ||
247 | |||
248 | @classmethod | ||
249 | def do_unpack(cls, srcimg): | ||
250 | srcimgsize = (misc.get_file_size(srcimg)) * 1024L * 1024L | ||
251 | srcmnt = misc.mkdtemp("srcmnt") | ||
252 | disk = fs_related.SparseLoopbackDisk(srcimg, srcimgsize) | ||
253 | srcloop = PartitionedMount(srcmnt, skipformat = True) | ||
254 | |||
255 | srcloop.add_disk('/dev/sdb', disk) | ||
256 | srcloop.add_partition(srcimgsize/1024/1024, "/dev/sdb", "/", "ext3", boot=False) | ||
257 | try: | ||
258 | srcloop.mount() | ||
259 | |||
260 | except errors.MountError: | ||
261 | srcloop.cleanup() | ||
262 | raise | ||
263 | |||
264 | image = os.path.join(tempfile.mkdtemp(dir = "/var/tmp", prefix = "tmp"), "target.img") | ||
265 | args = ['dd', "if=%s" % srcloop.partitions[0]['device'], "of=%s" % image] | ||
266 | |||
267 | msger.info("`dd` image ...") | ||
268 | rc = runner.show(args) | ||
269 | srcloop.cleanup() | ||
270 | shutil.rmtree(os.path.dirname(srcmnt), ignore_errors = True) | ||
271 | |||
272 | if rc != 0: | ||
273 | raise errors.CreatorError("Failed to dd") | ||
274 | else: | ||
275 | return image | ||
diff --git a/scripts/lib/mic/plugins/source/bootimg-efi.py b/scripts/lib/mic/plugins/source/bootimg-efi.py new file mode 100644 index 0000000000..2cc179a337 --- /dev/null +++ b/scripts/lib/mic/plugins/source/bootimg-efi.py | |||
@@ -0,0 +1,169 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2014, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This implements the 'bootimg-efi' source plugin class for 'wic' | ||
22 | # | ||
23 | # AUTHORS | ||
24 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
25 | # | ||
26 | |||
27 | import os | ||
28 | import shutil | ||
29 | import re | ||
30 | import tempfile | ||
31 | |||
32 | from mic import kickstart, chroot, msger | ||
33 | from mic.utils import misc, fs_related, errors, runner, cmdln | ||
34 | from mic.conf import configmgr | ||
35 | from mic.plugin import pluginmgr | ||
36 | from mic.utils.partitionedfs import PartitionedMount | ||
37 | import mic.imager.direct as direct | ||
38 | from mic.pluginbase import SourcePlugin | ||
39 | from mic.utils.oe.misc import * | ||
40 | from mic.imager.direct import DirectImageCreator | ||
41 | |||
42 | class BootimgEFIPlugin(SourcePlugin): | ||
43 | name = 'bootimg-efi' | ||
44 | |||
45 | @classmethod | ||
46 | def do_configure_partition(self, part, cr, cr_workdir, oe_builddir, | ||
47 | bootimg_dir, kernel_dir, native_sysroot): | ||
48 | """ | ||
49 | Called before do_prepare_partition(), creates grubefi config | ||
50 | """ | ||
51 | hdddir = "%s/hdd/boot" % cr_workdir | ||
52 | rm_cmd = "rm -rf %s" % cr_workdir | ||
53 | exec_cmd(rm_cmd) | ||
54 | |||
55 | install_cmd = "install -d %s/EFI/BOOT" % hdddir | ||
56 | tmp = exec_cmd(install_cmd) | ||
57 | |||
58 | splash = os.path.join(cr_workdir, "/EFI/boot/splash.jpg") | ||
59 | if os.path.exists(splash): | ||
60 | splashline = "menu background splash.jpg" | ||
61 | else: | ||
62 | splashline = "" | ||
63 | |||
64 | (rootdev, root_part_uuid) = cr._get_boot_config() | ||
65 | options = cr.ks.handler.bootloader.appendLine | ||
66 | |||
67 | grubefi_conf = "" | ||
68 | grubefi_conf += "serial --unit=0 --speed=115200 --word=8 --parity=no --stop=1\n" | ||
69 | grubefi_conf += "default=boot\n" | ||
70 | timeout = kickstart.get_timeout(cr.ks) | ||
71 | if not timeout: | ||
72 | timeout = 0 | ||
73 | grubefi_conf += "timeout=%s\n" % timeout | ||
74 | grubefi_conf += "menuentry 'boot'{\n" | ||
75 | |||
76 | kernel = "/vmlinuz" | ||
77 | |||
78 | if cr._ptable_format == 'msdos': | ||
79 | rootstr = rootdev | ||
80 | else: | ||
81 | if not root_part_uuid: | ||
82 | raise MountError("Cannot find the root GPT partition UUID") | ||
83 | rootstr = "PARTUUID=%s" % root_part_uuid | ||
84 | |||
85 | grubefi_conf += "linux %s root=%s rootwait %s\n" \ | ||
86 | % (kernel, rootstr, options) | ||
87 | grubefi_conf += "}\n" | ||
88 | if splashline: | ||
89 | syslinux_conf += "%s\n" % splashline | ||
90 | |||
91 | msger.debug("Writing grubefi config %s/hdd/boot/EFI/BOOT/grub.cfg" \ | ||
92 | % cr_workdir) | ||
93 | cfg = open("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, "w") | ||
94 | cfg.write(grubefi_conf) | ||
95 | cfg.close() | ||
96 | |||
97 | @classmethod | ||
98 | def do_prepare_partition(self, part, cr, cr_workdir, oe_builddir, bootimg_dir, | ||
99 | kernel_dir, rootfs_dir, native_sysroot): | ||
100 | """ | ||
101 | Called to do the actual content population for a partition i.e. it | ||
102 | 'prepares' the partition to be incorporated into the image. | ||
103 | In this case, prepare content for an EFI (grub) boot partition. | ||
104 | """ | ||
105 | if not bootimg_dir: | ||
106 | bootimg_dir = get_bitbake_var("HDDDIR") | ||
107 | if not bootimg_dir: | ||
108 | msger.error("Couldn't find HDDDIR, exiting\n") | ||
109 | # just so the result notes display it | ||
110 | cr.set_bootimg_dir(bootimg_dir) | ||
111 | |||
112 | staging_kernel_dir = kernel_dir | ||
113 | staging_data_dir = bootimg_dir | ||
114 | |||
115 | hdddir = "%s/hdd" % cr_workdir | ||
116 | |||
117 | install_cmd = "install -m 0644 %s/bzImage %s/bzImage" % \ | ||
118 | (staging_kernel_dir, hdddir) | ||
119 | tmp = exec_cmd(install_cmd) | ||
120 | |||
121 | shutil.copyfile("%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir, | ||
122 | "%s/grub.cfg" % cr_workdir) | ||
123 | |||
124 | cp_cmd = "cp %s/EFI/BOOT/* %s/EFI/BOOT" % (staging_data_dir, hdddir) | ||
125 | exec_cmd(cp_cmd, True) | ||
126 | |||
127 | shutil.move("%s/grub.cfg" % cr_workdir, | ||
128 | "%s/hdd/boot/EFI/BOOT/grub.cfg" % cr_workdir) | ||
129 | |||
130 | du_cmd = "du -bks %s" % hdddir | ||
131 | rc, out = exec_cmd(du_cmd) | ||
132 | blocks = int(out.split()[0]) | ||
133 | |||
134 | extra_blocks = part.get_extra_block_count(blocks) | ||
135 | |||
136 | if extra_blocks < BOOTDD_EXTRA_SPACE: | ||
137 | extra_blocks = BOOTDD_EXTRA_SPACE | ||
138 | |||
139 | blocks += extra_blocks | ||
140 | |||
141 | msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \ | ||
142 | (extra_blocks, part.mountpoint, blocks)) | ||
143 | |||
144 | # Ensure total sectors is an integral number of sectors per | ||
145 | # track or mcopy will complain. Sectors are 512 bytes, and we | ||
146 | # generate images with 32 sectors per track. This calculation is | ||
147 | # done in blocks, thus the mod by 16 instead of 32. | ||
148 | blocks += (16 - (blocks % 16)) | ||
149 | |||
150 | # dosfs image, created by mkdosfs | ||
151 | bootimg = "%s/boot.img" % cr_workdir | ||
152 | |||
153 | dosfs_cmd = "mkdosfs -n efi -C %s %d" % (bootimg, blocks) | ||
154 | exec_native_cmd(dosfs_cmd, native_sysroot) | ||
155 | |||
156 | mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir) | ||
157 | exec_native_cmd(mcopy_cmd, native_sysroot) | ||
158 | |||
159 | chmod_cmd = "chmod 644 %s" % bootimg | ||
160 | exec_cmd(chmod_cmd) | ||
161 | |||
162 | du_cmd = "du -Lbms %s" % bootimg | ||
163 | rc, out = exec_cmd(du_cmd) | ||
164 | bootimg_size = out.split()[0] | ||
165 | |||
166 | part.set_size(bootimg_size) | ||
167 | part.set_source_file(bootimg) | ||
168 | |||
169 | |||
diff --git a/scripts/lib/mic/plugins/source/bootimg-pcbios.py b/scripts/lib/mic/plugins/source/bootimg-pcbios.py new file mode 100644 index 0000000000..1211e5c93b --- /dev/null +++ b/scripts/lib/mic/plugins/source/bootimg-pcbios.py | |||
@@ -0,0 +1,195 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2014, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This implements the 'bootimg-pcbios' source plugin class for 'wic' | ||
22 | # | ||
23 | # AUTHORS | ||
24 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
25 | # | ||
26 | |||
27 | import os | ||
28 | import shutil | ||
29 | import re | ||
30 | import tempfile | ||
31 | |||
32 | from mic import kickstart, chroot, msger | ||
33 | from mic.utils import misc, fs_related, errors, runner, cmdln | ||
34 | from mic.conf import configmgr | ||
35 | from mic.plugin import pluginmgr | ||
36 | from mic.utils.partitionedfs import PartitionedMount | ||
37 | import mic.imager.direct as direct | ||
38 | from mic.pluginbase import SourcePlugin | ||
39 | from mic.utils.oe.misc import * | ||
40 | from mic.imager.direct import DirectImageCreator | ||
41 | |||
42 | class BootimgPcbiosPlugin(SourcePlugin): | ||
43 | name = 'bootimg-pcbios' | ||
44 | |||
45 | @classmethod | ||
46 | def do_install_disk(self, disk, disk_name, cr, workdir, oe_builddir, | ||
47 | bootimg_dir, kernel_dir, native_sysroot): | ||
48 | """ | ||
49 | Called after all partitions have been prepared and assembled into a | ||
50 | disk image. In this case, we install the MBR. | ||
51 | """ | ||
52 | mbrfile = "%s/syslinux/" % bootimg_dir | ||
53 | if cr._ptable_format == 'gpt': | ||
54 | mbrfile += "gptmbr.bin" | ||
55 | else: | ||
56 | mbrfile += "mbr.bin" | ||
57 | |||
58 | if not os.path.exists(mbrfile): | ||
59 | msger.error("Couldn't find %s. If using the -e option, do you have the right MACHINE set in local.conf? If not, is the bootimg_dir path correct?" % mbrfile) | ||
60 | |||
61 | full_path = cr._full_path(workdir, disk_name, "direct") | ||
62 | msger.debug("Installing MBR on disk %s as %s with size %s bytes" \ | ||
63 | % (disk_name, full_path, disk['min_size'])) | ||
64 | |||
65 | rc = runner.show(['dd', 'if=%s' % mbrfile, | ||
66 | 'of=%s' % full_path, 'conv=notrunc']) | ||
67 | if rc != 0: | ||
68 | raise MountError("Unable to set MBR to %s" % full_path) | ||
69 | |||
70 | @classmethod | ||
71 | def do_configure_partition(self, part, cr, cr_workdir, oe_builddir, | ||
72 | bootimg_dir, kernel_dir, native_sysroot): | ||
73 | """ | ||
74 | Called before do_prepare_partition(), creates syslinux config | ||
75 | """ | ||
76 | hdddir = "%s/hdd/boot" % cr_workdir | ||
77 | rm_cmd = "rm -rf " + cr_workdir | ||
78 | exec_cmd(rm_cmd) | ||
79 | |||
80 | install_cmd = "install -d %s" % hdddir | ||
81 | tmp = exec_cmd(install_cmd) | ||
82 | |||
83 | splash = os.path.join(cr_workdir, "/hdd/boot/splash.jpg") | ||
84 | if os.path.exists(splash): | ||
85 | splashline = "menu background splash.jpg" | ||
86 | else: | ||
87 | splashline = "" | ||
88 | |||
89 | (rootdev, root_part_uuid) = cr._get_boot_config() | ||
90 | options = cr.ks.handler.bootloader.appendLine | ||
91 | |||
92 | syslinux_conf = "" | ||
93 | syslinux_conf += "PROMPT 0\n" | ||
94 | timeout = kickstart.get_timeout(cr.ks) | ||
95 | if not timeout: | ||
96 | timeout = 0 | ||
97 | syslinux_conf += "TIMEOUT " + str(timeout) + "\n" | ||
98 | syslinux_conf += "\n" | ||
99 | syslinux_conf += "ALLOWOPTIONS 1\n" | ||
100 | syslinux_conf += "SERIAL 0 115200\n" | ||
101 | syslinux_conf += "\n" | ||
102 | if splashline: | ||
103 | syslinux_conf += "%s\n" % splashline | ||
104 | syslinux_conf += "DEFAULT boot\n" | ||
105 | syslinux_conf += "LABEL boot\n" | ||
106 | |||
107 | kernel = "/vmlinuz" | ||
108 | syslinux_conf += "KERNEL " + kernel + "\n" | ||
109 | |||
110 | if cr._ptable_format == 'msdos': | ||
111 | rootstr = rootdev | ||
112 | else: | ||
113 | if not root_part_uuid: | ||
114 | raise MountError("Cannot find the root GPT partition UUID") | ||
115 | rootstr = "PARTUUID=%s" % root_part_uuid | ||
116 | |||
117 | syslinux_conf += "APPEND label=boot root=%s %s\n" % (rootstr, options) | ||
118 | |||
119 | msger.debug("Writing syslinux config %s/hdd/boot/syslinux.cfg" \ | ||
120 | % cr_workdir) | ||
121 | cfg = open("%s/hdd/boot/syslinux.cfg" % cr_workdir, "w") | ||
122 | cfg.write(syslinux_conf) | ||
123 | cfg.close() | ||
124 | |||
125 | @classmethod | ||
126 | def do_prepare_partition(self, part, cr, cr_workdir, oe_builddir, bootimg_dir, | ||
127 | kernel_dir, rootfs_dir, native_sysroot): | ||
128 | """ | ||
129 | Called to do the actual content population for a partition i.e. it | ||
130 | 'prepares' the partition to be incorporated into the image. | ||
131 | In this case, prepare content for legacy bios boot partition. | ||
132 | """ | ||
133 | if not bootimg_dir: | ||
134 | bootimg_dir = get_bitbake_var("STAGING_DATADIR") | ||
135 | if not bootimg_dir: | ||
136 | msger.error("Couldn't find STAGING_DATADIR, exiting\n") | ||
137 | # just so the result notes display it | ||
138 | cr.set_bootimg_dir(bootimg_dir) | ||
139 | |||
140 | staging_kernel_dir = kernel_dir | ||
141 | staging_data_dir = bootimg_dir | ||
142 | |||
143 | hdddir = "%s/hdd/boot" % cr_workdir | ||
144 | |||
145 | install_cmd = "install -m 0644 %s/bzImage %s/vmlinuz" \ | ||
146 | % (staging_kernel_dir, hdddir) | ||
147 | tmp = exec_cmd(install_cmd) | ||
148 | |||
149 | install_cmd = "install -m 444 %s/syslinux/ldlinux.sys %s/ldlinux.sys" \ | ||
150 | % (staging_data_dir, hdddir) | ||
151 | tmp = exec_cmd(install_cmd) | ||
152 | |||
153 | du_cmd = "du -bks %s" % hdddir | ||
154 | rc, out = exec_cmd(du_cmd) | ||
155 | blocks = int(out.split()[0]) | ||
156 | |||
157 | extra_blocks = part.get_extra_block_count(blocks) | ||
158 | |||
159 | if extra_blocks < BOOTDD_EXTRA_SPACE: | ||
160 | extra_blocks = BOOTDD_EXTRA_SPACE | ||
161 | |||
162 | blocks += extra_blocks | ||
163 | |||
164 | msger.debug("Added %d extra blocks to %s to get to %d total blocks" % \ | ||
165 | (extra_blocks, part.mountpoint, blocks)) | ||
166 | |||
167 | # Ensure total sectors is an integral number of sectors per | ||
168 | # track or mcopy will complain. Sectors are 512 bytes, and we | ||
169 | # generate images with 32 sectors per track. This calculation is | ||
170 | # done in blocks, thus the mod by 16 instead of 32. | ||
171 | blocks += (16 - (blocks % 16)) | ||
172 | |||
173 | # dosfs image, created by mkdosfs | ||
174 | bootimg = "%s/boot.img" % cr_workdir | ||
175 | |||
176 | dosfs_cmd = "mkdosfs -n boot -S 512 -C %s %d" % (bootimg, blocks) | ||
177 | exec_native_cmd(dosfs_cmd, native_sysroot) | ||
178 | |||
179 | mcopy_cmd = "mcopy -i %s -s %s/* ::/" % (bootimg, hdddir) | ||
180 | exec_native_cmd(mcopy_cmd, native_sysroot) | ||
181 | |||
182 | syslinux_cmd = "syslinux %s" % bootimg | ||
183 | exec_native_cmd(syslinux_cmd, native_sysroot) | ||
184 | |||
185 | chmod_cmd = "chmod 644 %s" % bootimg | ||
186 | exec_cmd(chmod_cmd) | ||
187 | |||
188 | du_cmd = "du -Lbms %s" % bootimg | ||
189 | rc, out = exec_cmd(du_cmd) | ||
190 | bootimg_size = out.split()[0] | ||
191 | |||
192 | part.set_size(bootimg_size) | ||
193 | part.set_source_file(bootimg) | ||
194 | |||
195 | |||
diff --git a/scripts/lib/mic/plugins/source/rootfs.py b/scripts/lib/mic/plugins/source/rootfs.py new file mode 100644 index 0000000000..75999e03d2 --- /dev/null +++ b/scripts/lib/mic/plugins/source/rootfs.py | |||
@@ -0,0 +1,71 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2014, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This implements the 'rootfs' source plugin class for 'wic' | ||
22 | # | ||
23 | # AUTHORS | ||
24 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
25 | # Joao Henrique Ferreira de Freitas <joaohf (at] gmail.com> | ||
26 | # | ||
27 | |||
28 | import os | ||
29 | import shutil | ||
30 | import re | ||
31 | import tempfile | ||
32 | |||
33 | from mic import kickstart, chroot, msger | ||
34 | from mic.utils import misc, fs_related, errors, runner, cmdln | ||
35 | from mic.conf import configmgr | ||
36 | from mic.plugin import pluginmgr | ||
37 | from mic.utils.partitionedfs import PartitionedMount | ||
38 | import mic.imager.direct as direct | ||
39 | from mic.pluginbase import SourcePlugin | ||
40 | from mic.utils.oe.misc import * | ||
41 | from mic.imager.direct import DirectImageCreator | ||
42 | |||
43 | class RootfsPlugin(SourcePlugin): | ||
44 | name = 'rootfs' | ||
45 | |||
46 | @classmethod | ||
47 | def do_prepare_partition(self, part, cr, cr_workdir, oe_builddir, bootimg_dir, | ||
48 | kernel_dir, krootfs_dir, native_sysroot): | ||
49 | """ | ||
50 | Called to do the actual content population for a partition i.e. it | ||
51 | 'prepares' the partition to be incorporated into the image. | ||
52 | In this case, prepare content for legacy bios boot partition. | ||
53 | """ | ||
54 | if part.rootfs is None: | ||
55 | if not 'ROOTFS_DIR' in krootfs_dir: | ||
56 | msg = "Couldn't find --rootfs-dir, exiting" | ||
57 | msger.error(msg) | ||
58 | rootfs_dir = krootfs_dir['ROOTFS_DIR'] | ||
59 | else: | ||
60 | if part.rootfs in krootfs_dir: | ||
61 | rootfs_dir = krootfs_dir[part.rootfs] | ||
62 | elif os.path.isdir(part.rootfs): | ||
63 | rootfs_dir = part.rootfs | ||
64 | else: | ||
65 | msg = "Couldn't find --rootfs-dir=%s connection" | ||
66 | msg += " or it is not a valid path, exiting" | ||
67 | msger.error(msg % part.rootfs) | ||
68 | |||
69 | part.set_rootfs(rootfs_dir) | ||
70 | part.prepare_rootfs(cr_workdir, oe_builddir, rootfs_dir, native_sysroot) | ||
71 | |||
diff --git a/scripts/lib/mic/plugins/source/uboot.py b/scripts/lib/mic/plugins/source/uboot.py new file mode 100644 index 0000000000..57cb3cf8fe --- /dev/null +++ b/scripts/lib/mic/plugins/source/uboot.py | |||
@@ -0,0 +1,173 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2014, Enea AB. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This implements the 'uboot' source plugin class for 'wic' | ||
22 | # | ||
23 | # AUTHORS | ||
24 | # Adrian Calianu <adrian.calianu (at] enea.com> | ||
25 | # | ||
26 | |||
27 | import os | ||
28 | import shutil | ||
29 | import re | ||
30 | import tempfile | ||
31 | |||
32 | from mic import kickstart, chroot, msger | ||
33 | from mic.utils import misc, fs_related, errors, runner, cmdln | ||
34 | from mic.conf import configmgr | ||
35 | from mic.plugin import pluginmgr | ||
36 | from mic.utils.partitionedfs import PartitionedMount | ||
37 | import mic.imager.direct as direct | ||
38 | from mic.pluginbase import SourcePlugin | ||
39 | from mic.utils.oe.misc import * | ||
40 | from mic.imager.direct import DirectImageCreator | ||
41 | |||
42 | def create_local_rootfs(part, creator, cr_workdir, krootfs_dir, native_sysroot): | ||
43 | # In order to have a full control over rootfs we will make a local copy under workdir | ||
44 | # and change rootfs_dir to new location. | ||
45 | # In this way we can install more than one ROOTFS_DIRs and/or use | ||
46 | # an empty rootfs to install packages, so a rootfs could be generated only from pkgs | ||
47 | # TBD: create workdir/rootfs ; copy rootfs-> workdir/rootfs; set rootfs=workdir/rootfs | ||
48 | |||
49 | cr_workdir = os.path.abspath(cr_workdir) | ||
50 | new_rootfs_dir = "%s/rootfs_%s" % (cr_workdir, creator.name) | ||
51 | |||
52 | rootfs_exists = 1 | ||
53 | if part.rootfs is None: | ||
54 | if not 'ROOTFS_DIR' in krootfs_dir: | ||
55 | msg = "Couldn't find --rootfs-dir, exiting, " | ||
56 | msger.info(msg) | ||
57 | rootfs_exists = 0 | ||
58 | rootfs_dir = krootfs_dir['ROOTFS_DIR'] | ||
59 | creator.rootfs_dir['ROOTFS_DIR'] = new_rootfs_dir | ||
60 | else: | ||
61 | if part.rootfs in krootfs_dir: | ||
62 | rootfs_dir = krootfs_dir[part.rootfs] | ||
63 | creator.rootfs_dir[part.rootfs] = new_rootfs_dir | ||
64 | elif os.path.isdir(part.rootfs): | ||
65 | rootfs_dir = part.rootfs | ||
66 | part.rootfs = new_rootfs_dir | ||
67 | else: | ||
68 | msg = "Couldn't find --rootfs-dir=%s connection" | ||
69 | msg += " or it is not a valid path, exiting" | ||
70 | msger.info(msg % part.rootfs) | ||
71 | rootfs_exists = 0 | ||
72 | creator.rootfs_dir['ROOTFS_DIR'] = new_rootfs_dir | ||
73 | |||
74 | pseudox = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot | ||
75 | pseudox += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % new_rootfs_dir | ||
76 | pseudox += "export PSEUDO_PASSWD=%s;" % new_rootfs_dir | ||
77 | pseudox += "export PSEUDO_NOSYMLINKEXP=1;" | ||
78 | pseudox += "%s/usr/bin/pseudo " % native_sysroot | ||
79 | |||
80 | mkdir_cmd = "mkdir %s" % (new_rootfs_dir) | ||
81 | # rc, out = exec_native_cmd(pseudox + mkdir_cmd, native_sysroot) | ||
82 | rc, out = exec_cmd(mkdir_cmd, True) | ||
83 | |||
84 | if rootfs_exists == 1 and os.path.isdir(rootfs_dir): | ||
85 | defpath = os.environ['PATH'] | ||
86 | os.environ['PATH'] = native_sysroot + "/usr/bin/" + ":/bin:/usr/bin:" | ||
87 | |||
88 | rootfs_dir = os.path.abspath(rootfs_dir) | ||
89 | |||
90 | pseudoc = "export PSEUDO_PREFIX=%s/usr;" % native_sysroot | ||
91 | pseudoc += "export PSEUDO_LOCALSTATEDIR=%s/../pseudo;" % rootfs_dir | ||
92 | pseudoc += "export PSEUDO_PASSWD=%s;" % rootfs_dir | ||
93 | pseudoc += "export PSEUDO_NOSYMLINKEXP=1;" | ||
94 | pseudoc += "%s/usr/bin/pseudo " % native_sysroot | ||
95 | |||
96 | tarc_cmd = "tar cvpf %s/rootfs.tar -C %s ." % (cr_workdir, rootfs_dir) | ||
97 | rc, out = exec_native_cmd(pseudoc + tarc_cmd, native_sysroot) | ||
98 | |||
99 | tarx_cmd = "tar xpvf %s/rootfs.tar -C %s" % (cr_workdir, new_rootfs_dir) | ||
100 | rc, out = exec_native_cmd(pseudox + tarx_cmd, native_sysroot) | ||
101 | |||
102 | rm_cmd = "rm %s/rootfs.tar" % cr_workdir | ||
103 | rc, out = exec_cmd(rm_cmd, True) | ||
104 | |||
105 | os.environ['PATH'] += defpath + ":" + native_sysroot + "/usr/bin/" | ||
106 | |||
107 | return new_rootfs_dir | ||
108 | |||
109 | class UBootPlugin(SourcePlugin): | ||
110 | name = 'uboot' | ||
111 | |||
112 | @classmethod | ||
113 | def do_install_pkgs(self, part, creator, cr_workdir, oe_builddir, krootfs_dir, | ||
114 | bootimg_dir, kernel_dir, native_sysroot): | ||
115 | """ | ||
116 | Called before all partitions have been prepared and assembled into a | ||
117 | disk image. Intall packages based on wic configuration. | ||
118 | """ | ||
119 | |||
120 | # set new rootfs_dir | ||
121 | rootfs_dir = create_local_rootfs(part, creator, cr_workdir, krootfs_dir, native_sysroot) | ||
122 | |||
123 | # wks file parsing | ||
124 | packages = kickstart.get_packages(creator.ks) | ||
125 | |||
126 | # wic.conf file parsing = found under 'creator' | ||
127 | local_pkgs_path = creator._local_pkgs_path | ||
128 | repourl = creator.repourl | ||
129 | pkgmgr = creator.pkgmgr_name | ||
130 | |||
131 | # install packages | ||
132 | if packages and pkgmgr in ["opkg"]: | ||
133 | if len(repourl) > 0 : | ||
134 | part.install_pkgs_ipk(cr_workdir, oe_builddir, rootfs_dir, native_sysroot, | ||
135 | packages, repourl) | ||
136 | else: | ||
137 | msger.error("No packages repository provided in wic.conf") | ||
138 | |||
139 | @classmethod | ||
140 | def do_prepare_partition(self, part, cr, cr_workdir, oe_builddir, bootimg_dir, | ||
141 | kernel_dir, krootfs_dir, native_sysroot): | ||
142 | """ | ||
143 | Called to do the actual content population for a partition i.e. it | ||
144 | 'prepares' the partition to be incorporated into the image. | ||
145 | In this case, prepare content for legacy bios boot partition. | ||
146 | """ | ||
147 | if part.rootfs is None: | ||
148 | if not 'ROOTFS_DIR' in krootfs_dir: | ||
149 | msg = "Couldn't find --rootfs-dir, exiting" | ||
150 | msger.error(msg) | ||
151 | rootfs_dir = krootfs_dir['ROOTFS_DIR'] | ||
152 | else: | ||
153 | if part.rootfs in krootfs_dir: | ||
154 | rootfs_dir = krootfs_dir[part.rootfs] | ||
155 | elif os.path.isdir(part.rootfs): | ||
156 | rootfs_dir = part.rootfs | ||
157 | else: | ||
158 | msg = "Couldn't find --rootfs-dir=%s connection" | ||
159 | msg += " or it is not a valid path, exiting" | ||
160 | msger.error(msg % part.rootfs) | ||
161 | |||
162 | part.set_rootfs(rootfs_dir) | ||
163 | |||
164 | # change partition label wich will reflect into the final rootfs image name | ||
165 | part.label = "%s_%s" % (part.label, cr.name) | ||
166 | |||
167 | defpath = os.environ['PATH'] | ||
168 | os.environ['PATH'] = native_sysroot + "/usr/bin/" + ":/bin:/usr/bin:" | ||
169 | |||
170 | part.prepare_rootfs(cr_workdir, oe_builddir, rootfs_dir, native_sysroot) | ||
171 | part.prepare_for_uboot(cr.target_arch,cr_workdir, oe_builddir, rootfs_dir, native_sysroot) | ||
172 | |||
173 | os.environ['PATH'] += defpath + ":" + native_sysroot + "/usr/bin/" | ||
diff --git a/scripts/lib/mic/rt_util.py b/scripts/lib/mic/rt_util.py new file mode 100644 index 0000000000..2a31f4a218 --- /dev/null +++ b/scripts/lib/mic/rt_util.py | |||
@@ -0,0 +1,223 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2009, 2010, 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | from __future__ import with_statement | ||
19 | import os | ||
20 | import sys | ||
21 | import glob | ||
22 | import re | ||
23 | import shutil | ||
24 | import subprocess | ||
25 | |||
26 | from mic import bootstrap, msger | ||
27 | from mic.conf import configmgr | ||
28 | from mic.utils import errors, proxy | ||
29 | from mic.utils.fs_related import find_binary_path, makedirs | ||
30 | from mic.chroot import setup_chrootenv, cleanup_chrootenv | ||
31 | |||
32 | expath = lambda p: os.path.abspath(os.path.expanduser(p)) | ||
33 | |||
34 | def bootstrap_mic(argv=None): | ||
35 | |||
36 | |||
37 | def mychroot(): | ||
38 | os.chroot(rootdir) | ||
39 | os.chdir(cwd) | ||
40 | |||
41 | # by default, sys.argv is used to run mic in bootstrap | ||
42 | if not argv: | ||
43 | argv = sys.argv | ||
44 | if argv[0] not in ('/usr/bin/mic', 'mic'): | ||
45 | argv[0] = '/usr/bin/mic' | ||
46 | |||
47 | cropts = configmgr.create | ||
48 | bsopts = configmgr.bootstrap | ||
49 | distro = bsopts['distro_name'].lower() | ||
50 | |||
51 | rootdir = bsopts['rootdir'] | ||
52 | pkglist = bsopts['packages'] | ||
53 | cwd = os.getcwd() | ||
54 | |||
55 | # create bootstrap and run mic in bootstrap | ||
56 | bsenv = bootstrap.Bootstrap(rootdir, distro, cropts['arch']) | ||
57 | bsenv.logfile = cropts['logfile'] | ||
58 | # rootdir is regenerated as a temp dir | ||
59 | rootdir = bsenv.rootdir | ||
60 | |||
61 | if 'optional' in bsopts: | ||
62 | optlist = bsopts['optional'] | ||
63 | else: | ||
64 | optlist = [] | ||
65 | |||
66 | try: | ||
67 | msger.info("Creating %s bootstrap ..." % distro) | ||
68 | bsenv.create(cropts['repomd'], pkglist, optlist) | ||
69 | |||
70 | # bootstrap is relocated under "bootstrap" | ||
71 | if os.path.exists(os.path.join(rootdir, "bootstrap")): | ||
72 | rootdir = os.path.join(rootdir, "bootstrap") | ||
73 | |||
74 | bsenv.dirsetup(rootdir) | ||
75 | sync_mic(rootdir) | ||
76 | |||
77 | #FIXME: sync the ks file to bootstrap | ||
78 | if "/" == os.path.dirname(os.path.abspath(configmgr._ksconf)): | ||
79 | safecopy(configmgr._ksconf, rootdir) | ||
80 | |||
81 | msger.info("Start mic in bootstrap: %s\n" % rootdir) | ||
82 | bindmounts = get_bindmounts(cropts) | ||
83 | ret = bsenv.run(argv, cwd, rootdir, bindmounts) | ||
84 | |||
85 | except errors.BootstrapError, err: | ||
86 | msger.warning('\n%s' % err) | ||
87 | if msger.ask("Switch to native mode and continue?"): | ||
88 | return | ||
89 | raise | ||
90 | except RuntimeError, err: | ||
91 | #change exception type but keep the trace back | ||
92 | value, tb = sys.exc_info()[1:] | ||
93 | raise errors.BootstrapError, value, tb | ||
94 | else: | ||
95 | sys.exit(ret) | ||
96 | finally: | ||
97 | bsenv.cleanup() | ||
98 | |||
99 | def get_bindmounts(cropts): | ||
100 | binddirs = [ | ||
101 | os.getcwd(), | ||
102 | cropts['tmpdir'], | ||
103 | cropts['cachedir'], | ||
104 | cropts['outdir'], | ||
105 | cropts['local_pkgs_path'], | ||
106 | ] | ||
107 | bindfiles = [ | ||
108 | cropts['logfile'], | ||
109 | configmgr._ksconf, | ||
110 | ] | ||
111 | |||
112 | for lrepo in cropts['localrepos']: | ||
113 | binddirs.append(lrepo) | ||
114 | |||
115 | bindlist = map(expath, filter(None, binddirs)) | ||
116 | bindlist += map(os.path.dirname, map(expath, filter(None, bindfiles))) | ||
117 | bindlist = sorted(set(bindlist)) | ||
118 | bindmounts = ';'.join(bindlist) | ||
119 | return bindmounts | ||
120 | |||
121 | |||
122 | def get_mic_binpath(): | ||
123 | fp = None | ||
124 | try: | ||
125 | import pkg_resources # depends on 'setuptools' | ||
126 | except ImportError: | ||
127 | pass | ||
128 | else: | ||
129 | dist = pkg_resources.get_distribution('mic') | ||
130 | # the real script is under EGG_INFO/scripts | ||
131 | if dist.has_metadata('scripts/mic'): | ||
132 | fp = os.path.join(dist.egg_info, "scripts/mic") | ||
133 | |||
134 | if fp: | ||
135 | return fp | ||
136 | |||
137 | # not found script if 'flat' egg installed | ||
138 | try: | ||
139 | return find_binary_path('mic') | ||
140 | except errors.CreatorError: | ||
141 | raise errors.BootstrapError("Can't find mic binary in host OS") | ||
142 | |||
143 | |||
144 | def get_mic_modpath(): | ||
145 | try: | ||
146 | import mic | ||
147 | except ImportError: | ||
148 | raise errors.BootstrapError("Can't find mic module in host OS") | ||
149 | path = os.path.abspath(mic.__file__) | ||
150 | return os.path.dirname(path) | ||
151 | |||
152 | def get_mic_libpath(): | ||
153 | # TBD: so far mic lib path is hard coded | ||
154 | return "/usr/lib/mic" | ||
155 | |||
156 | # the hard code path is prepared for bootstrap | ||
157 | def sync_mic(bootstrap, binpth = '/usr/bin/mic', | ||
158 | libpth='/usr/lib', | ||
159 | pylib = '/usr/lib/python2.7/site-packages', | ||
160 | conf = '/etc/mic/mic.conf'): | ||
161 | _path = lambda p: os.path.join(bootstrap, p.lstrip('/')) | ||
162 | |||
163 | micpaths = { | ||
164 | 'binpth': get_mic_binpath(), | ||
165 | 'libpth': get_mic_libpath(), | ||
166 | 'pylib': get_mic_modpath(), | ||
167 | 'conf': '/etc/mic/mic.conf', | ||
168 | } | ||
169 | |||
170 | if not os.path.exists(_path(pylib)): | ||
171 | pyptn = '/usr/lib/python?.?/site-packages' | ||
172 | pylibs = glob.glob(_path(pyptn)) | ||
173 | if pylibs: | ||
174 | pylib = pylibs[0].replace(bootstrap, '') | ||
175 | else: | ||
176 | raise errors.BootstrapError("Can't find python site dir in: %s" % | ||
177 | bootstrap) | ||
178 | |||
179 | for key, value in micpaths.items(): | ||
180 | try: | ||
181 | safecopy(value, _path(eval(key)), False, ["*.pyc", "*.pyo"]) | ||
182 | except (OSError, IOError), err: | ||
183 | raise errors.BootstrapError(err) | ||
184 | |||
185 | # auto select backend | ||
186 | conf_str = file(_path(conf)).read() | ||
187 | conf_str = re.sub("pkgmgr\s*=\s*.*", "pkgmgr=auto", conf_str) | ||
188 | with open(_path(conf), 'w') as wf: | ||
189 | wf.write(conf_str) | ||
190 | |||
191 | # chmod +x /usr/bin/mic | ||
192 | os.chmod(_path(binpth), 0777) | ||
193 | |||
194 | # correct python interpreter | ||
195 | mic_cont = file(_path(binpth)).read() | ||
196 | mic_cont = "#!/usr/bin/python\n" + mic_cont | ||
197 | with open(_path(binpth), 'w') as wf: | ||
198 | wf.write(mic_cont) | ||
199 | |||
200 | |||
201 | def safecopy(src, dst, symlinks=False, ignore_ptns=()): | ||
202 | if os.path.isdir(src): | ||
203 | if os.path.isdir(dst): | ||
204 | dst = os.path.join(dst, os.path.basename(src)) | ||
205 | if os.path.exists(dst): | ||
206 | shutil.rmtree(dst, ignore_errors=True) | ||
207 | |||
208 | src = src.rstrip('/') | ||
209 | # check common prefix to ignore copying itself | ||
210 | if dst.startswith(src + '/'): | ||
211 | ignore_ptns = list(ignore_ptns) + [ os.path.basename(src) ] | ||
212 | |||
213 | ignores = shutil.ignore_patterns(*ignore_ptns) | ||
214 | try: | ||
215 | shutil.copytree(src, dst, symlinks, ignores) | ||
216 | except (OSError, IOError): | ||
217 | shutil.rmtree(dst, ignore_errors=True) | ||
218 | raise | ||
219 | else: | ||
220 | if not os.path.isdir(dst): | ||
221 | makedirs(os.path.dirname(dst)) | ||
222 | |||
223 | shutil.copy2(src, dst) | ||
diff --git a/scripts/lib/mic/test b/scripts/lib/mic/test new file mode 100644 index 0000000000..9daeafb986 --- /dev/null +++ b/scripts/lib/mic/test | |||
@@ -0,0 +1 @@ | |||
test | |||
diff --git a/scripts/lib/mic/utils/BmapCreate.py b/scripts/lib/mic/utils/BmapCreate.py new file mode 100644 index 0000000000..65b19a5f46 --- /dev/null +++ b/scripts/lib/mic/utils/BmapCreate.py | |||
@@ -0,0 +1,298 @@ | |||
1 | """ This module implements the block map (bmap) creation functionality and | ||
2 | provides the corresponding API in form of the 'BmapCreate' class. | ||
3 | |||
4 | The idea is that while images files may generally be very large (e.g., 4GiB), | ||
5 | they may nevertheless contain only little real data, e.g., 512MiB. This data | ||
6 | are files, directories, file-system meta-data, partition table, etc. When | ||
7 | copying the image to the target device, you do not have to copy all the 4GiB of | ||
8 | data, you can copy only 512MiB of it, which is 4 times less, so copying should | ||
9 | presumably be 4 times faster. | ||
10 | |||
11 | The block map file is an XML file which contains a list of blocks which have to | ||
12 | be copied to the target device. The other blocks are not used and there is no | ||
13 | need to copy them. The XML file also contains some additional information like | ||
14 | block size, image size, count of mapped blocks, etc. There are also many | ||
15 | commentaries, so it is human-readable. | ||
16 | |||
17 | The image has to be a sparse file. Generally, this means that when you generate | ||
18 | this image file, you should start with a huge sparse file which contains a | ||
19 | single hole spanning the entire file. Then you should partition it, write all | ||
20 | the data (probably by means of loop-back mounting the image or parts of it), | ||
21 | etc. The end result should be a sparse file where mapped areas represent useful | ||
22 | parts of the image and holes represent useless parts of the image, which do not | ||
23 | have to be copied when copying the image to the target device. | ||
24 | |||
25 | This module uses the FIBMAP ioctl to detect holes. """ | ||
26 | |||
27 | # Disable the following pylint recommendations: | ||
28 | # * Too many instance attributes - R0902 | ||
29 | # * Too few public methods - R0903 | ||
30 | # pylint: disable=R0902,R0903 | ||
31 | |||
32 | import hashlib | ||
33 | from mic.utils.misc import human_size | ||
34 | from mic.utils import Fiemap | ||
35 | |||
36 | # The bmap format version we generate | ||
37 | SUPPORTED_BMAP_VERSION = "1.3" | ||
38 | |||
39 | _BMAP_START_TEMPLATE = \ | ||
40 | """<?xml version="1.0" ?> | ||
41 | <!-- This file contains the block map for an image file, which is basically | ||
42 | a list of useful (mapped) block numbers in the image file. In other words, | ||
43 | it lists only those blocks which contain data (boot sector, partition | ||
44 | table, file-system metadata, files, directories, extents, etc). These | ||
45 | blocks have to be copied to the target device. The other blocks do not | ||
46 | contain any useful data and do not have to be copied to the target | ||
47 | device. | ||
48 | |||
49 | The block map an optimization which allows to copy or flash the image to | ||
50 | the image quicker than copying of flashing the entire image. This is | ||
51 | because with bmap less data is copied: <MappedBlocksCount> blocks instead | ||
52 | of <BlocksCount> blocks. | ||
53 | |||
54 | Besides the machine-readable data, this file contains useful commentaries | ||
55 | which contain human-readable information like image size, percentage of | ||
56 | mapped data, etc. | ||
57 | |||
58 | The 'version' attribute is the block map file format version in the | ||
59 | 'major.minor' format. The version major number is increased whenever an | ||
60 | incompatible block map format change is made. The minor number changes | ||
61 | in case of minor backward-compatible changes. --> | ||
62 | |||
63 | <bmap version="%s"> | ||
64 | <!-- Image size in bytes: %s --> | ||
65 | <ImageSize> %u </ImageSize> | ||
66 | |||
67 | <!-- Size of a block in bytes --> | ||
68 | <BlockSize> %u </BlockSize> | ||
69 | |||
70 | <!-- Count of blocks in the image file --> | ||
71 | <BlocksCount> %u </BlocksCount> | ||
72 | |||
73 | """ | ||
74 | |||
75 | class Error(Exception): | ||
76 | """ A class for exceptions generated by this module. We currently support | ||
77 | only one type of exceptions, and we basically throw human-readable problem | ||
78 | description in case of errors. """ | ||
79 | pass | ||
80 | |||
81 | class BmapCreate: | ||
82 | """ This class implements the bmap creation functionality. To generate a | ||
83 | bmap for an image (which is supposedly a sparse file), you should first | ||
84 | create an instance of 'BmapCreate' and provide: | ||
85 | |||
86 | * full path or a file-like object of the image to create bmap for | ||
87 | * full path or a file object to use for writing the results to | ||
88 | |||
89 | Then you should invoke the 'generate()' method of this class. It will use | ||
90 | the FIEMAP ioctl to generate the bmap. """ | ||
91 | |||
92 | def _open_image_file(self): | ||
93 | """ Open the image file. """ | ||
94 | |||
95 | try: | ||
96 | self._f_image = open(self._image_path, 'rb') | ||
97 | except IOError as err: | ||
98 | raise Error("cannot open image file '%s': %s" \ | ||
99 | % (self._image_path, err)) | ||
100 | |||
101 | self._f_image_needs_close = True | ||
102 | |||
103 | def _open_bmap_file(self): | ||
104 | """ Open the bmap file. """ | ||
105 | |||
106 | try: | ||
107 | self._f_bmap = open(self._bmap_path, 'w+') | ||
108 | except IOError as err: | ||
109 | raise Error("cannot open bmap file '%s': %s" \ | ||
110 | % (self._bmap_path, err)) | ||
111 | |||
112 | self._f_bmap_needs_close = True | ||
113 | |||
114 | def __init__(self, image, bmap): | ||
115 | """ Initialize a class instance: | ||
116 | * image - full path or a file-like object of the image to create bmap | ||
117 | for | ||
118 | * bmap - full path or a file object to use for writing the resulting | ||
119 | bmap to """ | ||
120 | |||
121 | self.image_size = None | ||
122 | self.image_size_human = None | ||
123 | self.block_size = None | ||
124 | self.blocks_cnt = None | ||
125 | self.mapped_cnt = None | ||
126 | self.mapped_size = None | ||
127 | self.mapped_size_human = None | ||
128 | self.mapped_percent = None | ||
129 | |||
130 | self._mapped_count_pos1 = None | ||
131 | self._mapped_count_pos2 = None | ||
132 | self._sha1_pos = None | ||
133 | |||
134 | self._f_image_needs_close = False | ||
135 | self._f_bmap_needs_close = False | ||
136 | |||
137 | if hasattr(image, "read"): | ||
138 | self._f_image = image | ||
139 | self._image_path = image.name | ||
140 | else: | ||
141 | self._image_path = image | ||
142 | self._open_image_file() | ||
143 | |||
144 | if hasattr(bmap, "read"): | ||
145 | self._f_bmap = bmap | ||
146 | self._bmap_path = bmap.name | ||
147 | else: | ||
148 | self._bmap_path = bmap | ||
149 | self._open_bmap_file() | ||
150 | |||
151 | self.fiemap = Fiemap.Fiemap(self._f_image) | ||
152 | |||
153 | self.image_size = self.fiemap.image_size | ||
154 | self.image_size_human = human_size(self.image_size) | ||
155 | if self.image_size == 0: | ||
156 | raise Error("cannot generate bmap for zero-sized image file '%s'" \ | ||
157 | % self._image_path) | ||
158 | |||
159 | self.block_size = self.fiemap.block_size | ||
160 | self.blocks_cnt = self.fiemap.blocks_cnt | ||
161 | |||
162 | def _bmap_file_start(self): | ||
163 | """ A helper function which generates the starting contents of the | ||
164 | block map file: the header comment, image size, block size, etc. """ | ||
165 | |||
166 | # We do not know the amount of mapped blocks at the moment, so just put | ||
167 | # whitespaces instead of real numbers. Assume the longest possible | ||
168 | # numbers. | ||
169 | mapped_count = ' ' * len(str(self.image_size)) | ||
170 | mapped_size_human = ' ' * len(self.image_size_human) | ||
171 | |||
172 | xml = _BMAP_START_TEMPLATE \ | ||
173 | % (SUPPORTED_BMAP_VERSION, self.image_size_human, | ||
174 | self.image_size, self.block_size, self.blocks_cnt) | ||
175 | xml += " <!-- Count of mapped blocks: " | ||
176 | |||
177 | self._f_bmap.write(xml) | ||
178 | self._mapped_count_pos1 = self._f_bmap.tell() | ||
179 | |||
180 | # Just put white-spaces instead of real information about mapped blocks | ||
181 | xml = "%s or %.1f -->\n" % (mapped_size_human, 100.0) | ||
182 | xml += " <MappedBlocksCount> " | ||
183 | |||
184 | self._f_bmap.write(xml) | ||
185 | self._mapped_count_pos2 = self._f_bmap.tell() | ||
186 | |||
187 | xml = "%s </MappedBlocksCount>\n\n" % mapped_count | ||
188 | |||
189 | # pylint: disable=C0301 | ||
190 | xml += " <!-- The checksum of this bmap file. When it is calculated, the value of\n" | ||
191 | xml += " the SHA1 checksum has be zeoro (40 ASCII \"0\" symbols). -->\n" | ||
192 | xml += " <BmapFileSHA1> " | ||
193 | |||
194 | self._f_bmap.write(xml) | ||
195 | self._sha1_pos = self._f_bmap.tell() | ||
196 | |||
197 | xml = "0" * 40 + " </BmapFileSHA1>\n\n" | ||
198 | xml += " <!-- The block map which consists of elements which may either be a\n" | ||
199 | xml += " range of blocks or a single block. The 'sha1' attribute (if present)\n" | ||
200 | xml += " is the SHA1 checksum of this blocks range. -->\n" | ||
201 | xml += " <BlockMap>\n" | ||
202 | # pylint: enable=C0301 | ||
203 | |||
204 | self._f_bmap.write(xml) | ||
205 | |||
206 | def _bmap_file_end(self): | ||
207 | """ A helper function which generates the final parts of the block map | ||
208 | file: the ending tags and the information about the amount of mapped | ||
209 | blocks. """ | ||
210 | |||
211 | xml = " </BlockMap>\n" | ||
212 | xml += "</bmap>\n" | ||
213 | |||
214 | self._f_bmap.write(xml) | ||
215 | |||
216 | self._f_bmap.seek(self._mapped_count_pos1) | ||
217 | self._f_bmap.write("%s or %.1f%%" % \ | ||
218 | (self.mapped_size_human, self.mapped_percent)) | ||
219 | |||
220 | self._f_bmap.seek(self._mapped_count_pos2) | ||
221 | self._f_bmap.write("%u" % self.mapped_cnt) | ||
222 | |||
223 | self._f_bmap.seek(0) | ||
224 | sha1 = hashlib.sha1(self._f_bmap.read()).hexdigest() | ||
225 | self._f_bmap.seek(self._sha1_pos) | ||
226 | self._f_bmap.write("%s" % sha1) | ||
227 | |||
228 | def _calculate_sha1(self, first, last): | ||
229 | """ A helper function which calculates SHA1 checksum for the range of | ||
230 | blocks of the image file: from block 'first' to block 'last'. """ | ||
231 | |||
232 | start = first * self.block_size | ||
233 | end = (last + 1) * self.block_size | ||
234 | |||
235 | self._f_image.seek(start) | ||
236 | hash_obj = hashlib.new("sha1") | ||
237 | |||
238 | chunk_size = 1024*1024 | ||
239 | to_read = end - start | ||
240 | read = 0 | ||
241 | |||
242 | while read < to_read: | ||
243 | if read + chunk_size > to_read: | ||
244 | chunk_size = to_read - read | ||
245 | chunk = self._f_image.read(chunk_size) | ||
246 | hash_obj.update(chunk) | ||
247 | read += chunk_size | ||
248 | |||
249 | return hash_obj.hexdigest() | ||
250 | |||
251 | def generate(self, include_checksums = True): | ||
252 | """ Generate bmap for the image file. If 'include_checksums' is 'True', | ||
253 | also generate SHA1 checksums for block ranges. """ | ||
254 | |||
255 | # Save image file position in order to restore it at the end | ||
256 | image_pos = self._f_image.tell() | ||
257 | |||
258 | self._bmap_file_start() | ||
259 | |||
260 | # Generate the block map and write it to the XML block map | ||
261 | # file as we go. | ||
262 | self.mapped_cnt = 0 | ||
263 | for first, last in self.fiemap.get_mapped_ranges(0, self.blocks_cnt): | ||
264 | self.mapped_cnt += last - first + 1 | ||
265 | if include_checksums: | ||
266 | sha1 = self._calculate_sha1(first, last) | ||
267 | sha1 = " sha1=\"%s\"" % sha1 | ||
268 | else: | ||
269 | sha1 = "" | ||
270 | |||
271 | if first != last: | ||
272 | self._f_bmap.write(" <Range%s> %s-%s </Range>\n" \ | ||
273 | % (sha1, first, last)) | ||
274 | else: | ||
275 | self._f_bmap.write(" <Range%s> %s </Range>\n" \ | ||
276 | % (sha1, first)) | ||
277 | |||
278 | self.mapped_size = self.mapped_cnt * self.block_size | ||
279 | self.mapped_size_human = human_size(self.mapped_size) | ||
280 | self.mapped_percent = (self.mapped_cnt * 100.0) / self.blocks_cnt | ||
281 | |||
282 | self._bmap_file_end() | ||
283 | |||
284 | try: | ||
285 | self._f_bmap.flush() | ||
286 | except IOError as err: | ||
287 | raise Error("cannot flush the bmap file '%s': %s" \ | ||
288 | % (self._bmap_path, err)) | ||
289 | |||
290 | self._f_image.seek(image_pos) | ||
291 | |||
292 | def __del__(self): | ||
293 | """ The class destructor which closes the opened files. """ | ||
294 | |||
295 | if self._f_image_needs_close: | ||
296 | self._f_image.close() | ||
297 | if self._f_bmap_needs_close: | ||
298 | self._f_bmap.close() | ||
diff --git a/scripts/lib/mic/utils/Fiemap.py b/scripts/lib/mic/utils/Fiemap.py new file mode 100644 index 0000000000..f2db6ff0b8 --- /dev/null +++ b/scripts/lib/mic/utils/Fiemap.py | |||
@@ -0,0 +1,252 @@ | |||
1 | """ This module implements python API for the FIEMAP ioctl. The FIEMAP ioctl | ||
2 | allows to find holes and mapped areas in a file. """ | ||
3 | |||
4 | # Note, a lot of code in this module is not very readable, because it deals | ||
5 | # with the rather complex FIEMAP ioctl. To understand the code, you need to | ||
6 | # know the FIEMAP interface, which is documented in the | ||
7 | # Documentation/filesystems/fiemap.txt file in the Linux kernel sources. | ||
8 | |||
9 | # Disable the following pylint recommendations: | ||
10 | # * Too many instance attributes (R0902) | ||
11 | # pylint: disable=R0902 | ||
12 | |||
13 | import os | ||
14 | import struct | ||
15 | import array | ||
16 | import fcntl | ||
17 | from mic.utils.misc import get_block_size | ||
18 | |||
19 | # Format string for 'struct fiemap' | ||
20 | _FIEMAP_FORMAT = "=QQLLLL" | ||
21 | # sizeof(struct fiemap) | ||
22 | _FIEMAP_SIZE = struct.calcsize(_FIEMAP_FORMAT) | ||
23 | # Format string for 'struct fiemap_extent' | ||
24 | _FIEMAP_EXTENT_FORMAT = "=QQQQQLLLL" | ||
25 | # sizeof(struct fiemap_extent) | ||
26 | _FIEMAP_EXTENT_SIZE = struct.calcsize(_FIEMAP_EXTENT_FORMAT) | ||
27 | # The FIEMAP ioctl number | ||
28 | _FIEMAP_IOCTL = 0xC020660B | ||
29 | |||
30 | # Minimum buffer which is required for 'class Fiemap' to operate | ||
31 | MIN_BUFFER_SIZE = _FIEMAP_SIZE + _FIEMAP_EXTENT_SIZE | ||
32 | # The default buffer size for 'class Fiemap' | ||
33 | DEFAULT_BUFFER_SIZE = 256 * 1024 | ||
34 | |||
35 | class Error(Exception): | ||
36 | """ A class for exceptions generated by this module. We currently support | ||
37 | only one type of exceptions, and we basically throw human-readable problem | ||
38 | description in case of errors. """ | ||
39 | pass | ||
40 | |||
41 | class Fiemap: | ||
42 | """ This class provides API to the FIEMAP ioctl. Namely, it allows to | ||
43 | iterate over all mapped blocks and over all holes. """ | ||
44 | |||
45 | def _open_image_file(self): | ||
46 | """ Open the image file. """ | ||
47 | |||
48 | try: | ||
49 | self._f_image = open(self._image_path, 'rb') | ||
50 | except IOError as err: | ||
51 | raise Error("cannot open image file '%s': %s" \ | ||
52 | % (self._image_path, err)) | ||
53 | |||
54 | self._f_image_needs_close = True | ||
55 | |||
56 | def __init__(self, image, buf_size = DEFAULT_BUFFER_SIZE): | ||
57 | """ Initialize a class instance. The 'image' argument is full path to | ||
58 | the file to operate on, or a file object to operate on. | ||
59 | |||
60 | The 'buf_size' argument is the size of the buffer for 'struct | ||
61 | fiemap_extent' elements which will be used when invoking the FIEMAP | ||
62 | ioctl. The larger is the buffer, the less times the FIEMAP ioctl will | ||
63 | be invoked. """ | ||
64 | |||
65 | self._f_image_needs_close = False | ||
66 | |||
67 | if hasattr(image, "fileno"): | ||
68 | self._f_image = image | ||
69 | self._image_path = image.name | ||
70 | else: | ||
71 | self._image_path = image | ||
72 | self._open_image_file() | ||
73 | |||
74 | # Validate 'buf_size' | ||
75 | if buf_size < MIN_BUFFER_SIZE: | ||
76 | raise Error("too small buffer (%d bytes), minimum is %d bytes" \ | ||
77 | % (buf_size, MIN_BUFFER_SIZE)) | ||
78 | |||
79 | # How many 'struct fiemap_extent' elements fit the buffer | ||
80 | buf_size -= _FIEMAP_SIZE | ||
81 | self._fiemap_extent_cnt = buf_size / _FIEMAP_EXTENT_SIZE | ||
82 | self._buf_size = self._fiemap_extent_cnt * _FIEMAP_EXTENT_SIZE | ||
83 | self._buf_size += _FIEMAP_SIZE | ||
84 | |||
85 | # Allocate a mutable buffer for the FIEMAP ioctl | ||
86 | self._buf = array.array('B', [0] * self._buf_size) | ||
87 | |||
88 | self.image_size = os.fstat(self._f_image.fileno()).st_size | ||
89 | |||
90 | try: | ||
91 | self.block_size = get_block_size(self._f_image) | ||
92 | except IOError as err: | ||
93 | raise Error("cannot get block size for '%s': %s" \ | ||
94 | % (self._image_path, err)) | ||
95 | |||
96 | self.blocks_cnt = self.image_size + self.block_size - 1 | ||
97 | self.blocks_cnt /= self.block_size | ||
98 | |||
99 | # Synchronize the image file to make sure FIEMAP returns correct values | ||
100 | try: | ||
101 | self._f_image.flush() | ||
102 | except IOError as err: | ||
103 | raise Error("cannot flush image file '%s': %s" \ | ||
104 | % (self._image_path, err)) | ||
105 | try: | ||
106 | os.fsync(self._f_image.fileno()), | ||
107 | except OSError as err: | ||
108 | raise Error("cannot synchronize image file '%s': %s " \ | ||
109 | % (self._image_path, err.strerror)) | ||
110 | |||
111 | # Check if the FIEMAP ioctl is supported | ||
112 | self.block_is_mapped(0) | ||
113 | |||
114 | def __del__(self): | ||
115 | """ The class destructor which closes the opened files. """ | ||
116 | |||
117 | if self._f_image_needs_close: | ||
118 | self._f_image.close() | ||
119 | |||
120 | def _invoke_fiemap(self, block, count): | ||
121 | """ Invoke the FIEMAP ioctl for 'count' blocks of the file starting from | ||
122 | block number 'block'. | ||
123 | |||
124 | The full result of the operation is stored in 'self._buf' on exit. | ||
125 | Returns the unpacked 'struct fiemap' data structure in form of a python | ||
126 | list (just like 'struct.upack()'). """ | ||
127 | |||
128 | if block < 0 or block >= self.blocks_cnt: | ||
129 | raise Error("bad block number %d, should be within [0, %d]" \ | ||
130 | % (block, self.blocks_cnt)) | ||
131 | |||
132 | # Initialize the 'struct fiemap' part of the buffer | ||
133 | struct.pack_into(_FIEMAP_FORMAT, self._buf, 0, block * self.block_size, | ||
134 | count * self.block_size, 0, 0, | ||
135 | self._fiemap_extent_cnt, 0) | ||
136 | |||
137 | try: | ||
138 | fcntl.ioctl(self._f_image, _FIEMAP_IOCTL, self._buf, 1) | ||
139 | except IOError as err: | ||
140 | error_msg = "the FIEMAP ioctl failed for '%s': %s" \ | ||
141 | % (self._image_path, err) | ||
142 | if err.errno == os.errno.EPERM or err.errno == os.errno.EACCES: | ||
143 | # The FIEMAP ioctl was added in kernel version 2.6.28 in 2008 | ||
144 | error_msg += " (looks like your kernel does not support FIEMAP)" | ||
145 | |||
146 | raise Error(error_msg) | ||
147 | |||
148 | return struct.unpack(_FIEMAP_FORMAT, self._buf[:_FIEMAP_SIZE]) | ||
149 | |||
150 | def block_is_mapped(self, block): | ||
151 | """ This function returns 'True' if block number 'block' of the image | ||
152 | file is mapped and 'False' otherwise. """ | ||
153 | |||
154 | struct_fiemap = self._invoke_fiemap(block, 1) | ||
155 | |||
156 | # The 3rd element of 'struct_fiemap' is the 'fm_mapped_extents' field. | ||
157 | # If it contains zero, the block is not mapped, otherwise it is | ||
158 | # mapped. | ||
159 | return bool(struct_fiemap[3]) | ||
160 | |||
161 | def block_is_unmapped(self, block): | ||
162 | """ This function returns 'True' if block number 'block' of the image | ||
163 | file is not mapped (hole) and 'False' otherwise. """ | ||
164 | |||
165 | return not self.block_is_mapped(block) | ||
166 | |||
167 | def _unpack_fiemap_extent(self, index): | ||
168 | """ Unpack a 'struct fiemap_extent' structure object number 'index' | ||
169 | from the internal 'self._buf' buffer. """ | ||
170 | |||
171 | offset = _FIEMAP_SIZE + _FIEMAP_EXTENT_SIZE * index | ||
172 | return struct.unpack(_FIEMAP_EXTENT_FORMAT, | ||
173 | self._buf[offset : offset + _FIEMAP_EXTENT_SIZE]) | ||
174 | |||
175 | def _do_get_mapped_ranges(self, start, count): | ||
176 | """ Implements most the functionality for the 'get_mapped_ranges()' | ||
177 | generator: invokes the FIEMAP ioctl, walks through the mapped | ||
178 | extents and yields mapped block ranges. However, the ranges may be | ||
179 | consecutive (e.g., (1, 100), (100, 200)) and 'get_mapped_ranges()' | ||
180 | simply merges them. """ | ||
181 | |||
182 | block = start | ||
183 | while block < start + count: | ||
184 | struct_fiemap = self._invoke_fiemap(block, count) | ||
185 | |||
186 | mapped_extents = struct_fiemap[3] | ||
187 | if mapped_extents == 0: | ||
188 | # No more mapped blocks | ||
189 | return | ||
190 | |||
191 | extent = 0 | ||
192 | while extent < mapped_extents: | ||
193 | fiemap_extent = self._unpack_fiemap_extent(extent) | ||
194 | |||
195 | # Start of the extent | ||
196 | extent_start = fiemap_extent[0] | ||
197 | # Starting block number of the extent | ||
198 | extent_block = extent_start / self.block_size | ||
199 | # Length of the extent | ||
200 | extent_len = fiemap_extent[2] | ||
201 | # Count of blocks in the extent | ||
202 | extent_count = extent_len / self.block_size | ||
203 | |||
204 | # Extent length and offset have to be block-aligned | ||
205 | assert extent_start % self.block_size == 0 | ||
206 | assert extent_len % self.block_size == 0 | ||
207 | |||
208 | if extent_block > start + count - 1: | ||
209 | return | ||
210 | |||
211 | first = max(extent_block, block) | ||
212 | last = min(extent_block + extent_count, start + count) - 1 | ||
213 | yield (first, last) | ||
214 | |||
215 | extent += 1 | ||
216 | |||
217 | block = extent_block + extent_count | ||
218 | |||
219 | def get_mapped_ranges(self, start, count): | ||
220 | """ A generator which yields ranges of mapped blocks in the file. The | ||
221 | ranges are tuples of 2 elements: [first, last], where 'first' is the | ||
222 | first mapped block and 'last' is the last mapped block. | ||
223 | |||
224 | The ranges are yielded for the area of the file of size 'count' blocks, | ||
225 | starting from block 'start'. """ | ||
226 | |||
227 | iterator = self._do_get_mapped_ranges(start, count) | ||
228 | |||
229 | first_prev, last_prev = iterator.next() | ||
230 | |||
231 | for first, last in iterator: | ||
232 | if last_prev == first - 1: | ||
233 | last_prev = last | ||
234 | else: | ||
235 | yield (first_prev, last_prev) | ||
236 | first_prev, last_prev = first, last | ||
237 | |||
238 | yield (first_prev, last_prev) | ||
239 | |||
240 | def get_unmapped_ranges(self, start, count): | ||
241 | """ Just like 'get_mapped_ranges()', but yields unmapped block ranges | ||
242 | instead (holes). """ | ||
243 | |||
244 | hole_first = start | ||
245 | for first, last in self._do_get_mapped_ranges(start, count): | ||
246 | if first > hole_first: | ||
247 | yield (hole_first, first - 1) | ||
248 | |||
249 | hole_first = last + 1 | ||
250 | |||
251 | if hole_first < start + count: | ||
252 | yield (hole_first, start + count - 1) | ||
diff --git a/scripts/lib/mic/utils/__init__.py b/scripts/lib/mic/utils/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/scripts/lib/mic/utils/__init__.py | |||
diff --git a/scripts/lib/mic/utils/cmdln.py b/scripts/lib/mic/utils/cmdln.py new file mode 100644 index 0000000000..b099473ee4 --- /dev/null +++ b/scripts/lib/mic/utils/cmdln.py | |||
@@ -0,0 +1,1586 @@ | |||
1 | #!/usr/bin/env python | ||
2 | # Copyright (c) 2002-2007 ActiveState Software Inc. | ||
3 | # License: MIT (see LICENSE.txt for license details) | ||
4 | # Author: Trent Mick | ||
5 | # Home: http://trentm.com/projects/cmdln/ | ||
6 | |||
7 | """An improvement on Python's standard cmd.py module. | ||
8 | |||
9 | As with cmd.py, this module provides "a simple framework for writing | ||
10 | line-oriented command intepreters." This module provides a 'RawCmdln' | ||
11 | class that fixes some design flaws in cmd.Cmd, making it more scalable | ||
12 | and nicer to use for good 'cvs'- or 'svn'-style command line interfaces | ||
13 | or simple shells. And it provides a 'Cmdln' class that add | ||
14 | optparse-based option processing. Basically you use it like this: | ||
15 | |||
16 | import cmdln | ||
17 | |||
18 | class MySVN(cmdln.Cmdln): | ||
19 | name = "svn" | ||
20 | |||
21 | @cmdln.alias('stat', 'st') | ||
22 | @cmdln.option('-v', '--verbose', action='store_true' | ||
23 | help='print verbose information') | ||
24 | def do_status(self, subcmd, opts, *paths): | ||
25 | print "handle 'svn status' command" | ||
26 | |||
27 | #... | ||
28 | |||
29 | if __name__ == "__main__": | ||
30 | shell = MySVN() | ||
31 | retval = shell.main() | ||
32 | sys.exit(retval) | ||
33 | |||
34 | See the README.txt or <http://trentm.com/projects/cmdln/> for more | ||
35 | details. | ||
36 | """ | ||
37 | |||
38 | __version_info__ = (1, 1, 2) | ||
39 | __version__ = '.'.join(map(str, __version_info__)) | ||
40 | |||
41 | import os | ||
42 | import sys | ||
43 | import re | ||
44 | import cmd | ||
45 | import optparse | ||
46 | from pprint import pprint | ||
47 | import sys | ||
48 | |||
49 | |||
50 | |||
51 | |||
52 | #---- globals | ||
53 | |||
54 | LOOP_ALWAYS, LOOP_NEVER, LOOP_IF_EMPTY = range(3) | ||
55 | |||
56 | # An unspecified optional argument when None is a meaningful value. | ||
57 | _NOT_SPECIFIED = ("Not", "Specified") | ||
58 | |||
59 | # Pattern to match a TypeError message from a call that | ||
60 | # failed because of incorrect number of arguments (see | ||
61 | # Python/getargs.c). | ||
62 | _INCORRECT_NUM_ARGS_RE = re.compile( | ||
63 | r"(takes [\w ]+ )(\d+)( arguments? \()(\d+)( given\))") | ||
64 | |||
65 | |||
66 | |||
67 | #---- exceptions | ||
68 | |||
69 | class CmdlnError(Exception): | ||
70 | """A cmdln.py usage error.""" | ||
71 | def __init__(self, msg): | ||
72 | self.msg = msg | ||
73 | def __str__(self): | ||
74 | return self.msg | ||
75 | |||
76 | class CmdlnUserError(Exception): | ||
77 | """An error by a user of a cmdln-based tool/shell.""" | ||
78 | pass | ||
79 | |||
80 | |||
81 | |||
82 | #---- public methods and classes | ||
83 | |||
84 | def alias(*aliases): | ||
85 | """Decorator to add aliases for Cmdln.do_* command handlers. | ||
86 | |||
87 | Example: | ||
88 | class MyShell(cmdln.Cmdln): | ||
89 | @cmdln.alias("!", "sh") | ||
90 | def do_shell(self, argv): | ||
91 | #...implement 'shell' command | ||
92 | """ | ||
93 | def decorate(f): | ||
94 | if not hasattr(f, "aliases"): | ||
95 | f.aliases = [] | ||
96 | f.aliases += aliases | ||
97 | return f | ||
98 | return decorate | ||
99 | |||
100 | |||
101 | class RawCmdln(cmd.Cmd): | ||
102 | """An improved (on cmd.Cmd) framework for building multi-subcommand | ||
103 | scripts (think "svn" & "cvs") and simple shells (think "pdb" and | ||
104 | "gdb"). | ||
105 | |||
106 | A simple example: | ||
107 | |||
108 | import cmdln | ||
109 | |||
110 | class MySVN(cmdln.RawCmdln): | ||
111 | name = "svn" | ||
112 | |||
113 | @cmdln.aliases('stat', 'st') | ||
114 | def do_status(self, argv): | ||
115 | print "handle 'svn status' command" | ||
116 | |||
117 | if __name__ == "__main__": | ||
118 | shell = MySVN() | ||
119 | retval = shell.main() | ||
120 | sys.exit(retval) | ||
121 | |||
122 | See <http://trentm.com/projects/cmdln> for more information. | ||
123 | """ | ||
124 | name = None # if unset, defaults basename(sys.argv[0]) | ||
125 | prompt = None # if unset, defaults to self.name+"> " | ||
126 | version = None # if set, default top-level options include --version | ||
127 | |||
128 | # Default messages for some 'help' command error cases. | ||
129 | # They are interpolated with one arg: the command. | ||
130 | nohelp = "no help on '%s'" | ||
131 | unknowncmd = "unknown command: '%s'" | ||
132 | |||
133 | helpindent = '' # string with which to indent help output | ||
134 | |||
135 | def __init__(self, completekey='tab', | ||
136 | stdin=None, stdout=None, stderr=None): | ||
137 | """Cmdln(completekey='tab', stdin=None, stdout=None, stderr=None) | ||
138 | |||
139 | The optional argument 'completekey' is the readline name of a | ||
140 | completion key; it defaults to the Tab key. If completekey is | ||
141 | not None and the readline module is available, command completion | ||
142 | is done automatically. | ||
143 | |||
144 | The optional arguments 'stdin', 'stdout' and 'stderr' specify | ||
145 | alternate input, output and error output file objects; if not | ||
146 | specified, sys.* are used. | ||
147 | |||
148 | If 'stdout' but not 'stderr' is specified, stdout is used for | ||
149 | error output. This is to provide least surprise for users used | ||
150 | to only the 'stdin' and 'stdout' options with cmd.Cmd. | ||
151 | """ | ||
152 | import sys | ||
153 | if self.name is None: | ||
154 | self.name = os.path.basename(sys.argv[0]) | ||
155 | if self.prompt is None: | ||
156 | self.prompt = self.name+"> " | ||
157 | self._name_str = self._str(self.name) | ||
158 | self._prompt_str = self._str(self.prompt) | ||
159 | if stdin is not None: | ||
160 | self.stdin = stdin | ||
161 | else: | ||
162 | self.stdin = sys.stdin | ||
163 | if stdout is not None: | ||
164 | self.stdout = stdout | ||
165 | else: | ||
166 | self.stdout = sys.stdout | ||
167 | if stderr is not None: | ||
168 | self.stderr = stderr | ||
169 | elif stdout is not None: | ||
170 | self.stderr = stdout | ||
171 | else: | ||
172 | self.stderr = sys.stderr | ||
173 | self.cmdqueue = [] | ||
174 | self.completekey = completekey | ||
175 | self.cmdlooping = False | ||
176 | |||
177 | def get_optparser(self): | ||
178 | """Hook for subclasses to set the option parser for the | ||
179 | top-level command/shell. | ||
180 | |||
181 | This option parser is used retrieved and used by `.main()' to | ||
182 | handle top-level options. | ||
183 | |||
184 | The default implements a single '-h|--help' option. Sub-classes | ||
185 | can return None to have no options at the top-level. Typically | ||
186 | an instance of CmdlnOptionParser should be returned. | ||
187 | """ | ||
188 | version = (self.version is not None | ||
189 | and "%s %s" % (self._name_str, self.version) | ||
190 | or None) | ||
191 | return CmdlnOptionParser(self, version=version) | ||
192 | |||
193 | def postoptparse(self): | ||
194 | """Hook method executed just after `.main()' parses top-level | ||
195 | options. | ||
196 | |||
197 | When called `self.options' holds the results of the option parse. | ||
198 | """ | ||
199 | pass | ||
200 | |||
201 | def main(self, argv=None, loop=LOOP_NEVER): | ||
202 | """A possible mainline handler for a script, like so: | ||
203 | |||
204 | import cmdln | ||
205 | class MyCmd(cmdln.Cmdln): | ||
206 | name = "mycmd" | ||
207 | ... | ||
208 | |||
209 | if __name__ == "__main__": | ||
210 | MyCmd().main() | ||
211 | |||
212 | By default this will use sys.argv to issue a single command to | ||
213 | 'MyCmd', then exit. The 'loop' argument can be use to control | ||
214 | interactive shell behaviour. | ||
215 | |||
216 | Arguments: | ||
217 | "argv" (optional, default sys.argv) is the command to run. | ||
218 | It must be a sequence, where the first element is the | ||
219 | command name and subsequent elements the args for that | ||
220 | command. | ||
221 | "loop" (optional, default LOOP_NEVER) is a constant | ||
222 | indicating if a command loop should be started (i.e. an | ||
223 | interactive shell). Valid values (constants on this module): | ||
224 | LOOP_ALWAYS start loop and run "argv", if any | ||
225 | LOOP_NEVER run "argv" (or .emptyline()) and exit | ||
226 | LOOP_IF_EMPTY run "argv", if given, and exit; | ||
227 | otherwise, start loop | ||
228 | """ | ||
229 | if argv is None: | ||
230 | import sys | ||
231 | argv = sys.argv | ||
232 | else: | ||
233 | argv = argv[:] # don't modify caller's list | ||
234 | |||
235 | self.optparser = self.get_optparser() | ||
236 | if self.optparser: # i.e. optparser=None means don't process for opts | ||
237 | try: | ||
238 | self.options, args = self.optparser.parse_args(argv[1:]) | ||
239 | except CmdlnUserError, ex: | ||
240 | msg = "%s: %s\nTry '%s help' for info.\n"\ | ||
241 | % (self.name, ex, self.name) | ||
242 | self.stderr.write(self._str(msg)) | ||
243 | self.stderr.flush() | ||
244 | return 1 | ||
245 | except StopOptionProcessing, ex: | ||
246 | return 0 | ||
247 | else: | ||
248 | self.options, args = None, argv[1:] | ||
249 | self.postoptparse() | ||
250 | |||
251 | if loop == LOOP_ALWAYS: | ||
252 | if args: | ||
253 | self.cmdqueue.append(args) | ||
254 | return self.cmdloop() | ||
255 | elif loop == LOOP_NEVER: | ||
256 | if args: | ||
257 | return self.cmd(args) | ||
258 | else: | ||
259 | return self.emptyline() | ||
260 | elif loop == LOOP_IF_EMPTY: | ||
261 | if args: | ||
262 | return self.cmd(args) | ||
263 | else: | ||
264 | return self.cmdloop() | ||
265 | |||
266 | def cmd(self, argv): | ||
267 | """Run one command and exit. | ||
268 | |||
269 | "argv" is the arglist for the command to run. argv[0] is the | ||
270 | command to run. If argv is an empty list then the | ||
271 | 'emptyline' handler is run. | ||
272 | |||
273 | Returns the return value from the command handler. | ||
274 | """ | ||
275 | assert isinstance(argv, (list, tuple)), \ | ||
276 | "'argv' is not a sequence: %r" % argv | ||
277 | retval = None | ||
278 | try: | ||
279 | argv = self.precmd(argv) | ||
280 | retval = self.onecmd(argv) | ||
281 | self.postcmd(argv) | ||
282 | except: | ||
283 | if not self.cmdexc(argv): | ||
284 | raise | ||
285 | retval = 1 | ||
286 | return retval | ||
287 | |||
288 | def _str(self, s): | ||
289 | """Safely convert the given str/unicode to a string for printing.""" | ||
290 | try: | ||
291 | return str(s) | ||
292 | except UnicodeError: | ||
293 | #XXX What is the proper encoding to use here? 'utf-8' seems | ||
294 | # to work better than "getdefaultencoding" (usually | ||
295 | # 'ascii'), on OS X at least. | ||
296 | #import sys | ||
297 | #return s.encode(sys.getdefaultencoding(), "replace") | ||
298 | return s.encode("utf-8", "replace") | ||
299 | |||
300 | def cmdloop(self, intro=None): | ||
301 | """Repeatedly issue a prompt, accept input, parse into an argv, and | ||
302 | dispatch (via .precmd(), .onecmd() and .postcmd()), passing them | ||
303 | the argv. In other words, start a shell. | ||
304 | |||
305 | "intro" (optional) is a introductory message to print when | ||
306 | starting the command loop. This overrides the class | ||
307 | "intro" attribute, if any. | ||
308 | """ | ||
309 | self.cmdlooping = True | ||
310 | self.preloop() | ||
311 | if self.use_rawinput and self.completekey: | ||
312 | try: | ||
313 | import readline | ||
314 | self.old_completer = readline.get_completer() | ||
315 | readline.set_completer(self.complete) | ||
316 | readline.parse_and_bind(self.completekey+": complete") | ||
317 | except ImportError: | ||
318 | pass | ||
319 | try: | ||
320 | if intro is None: | ||
321 | intro = self.intro | ||
322 | if intro: | ||
323 | intro_str = self._str(intro) | ||
324 | self.stdout.write(intro_str+'\n') | ||
325 | self.stop = False | ||
326 | retval = None | ||
327 | while not self.stop: | ||
328 | if self.cmdqueue: | ||
329 | argv = self.cmdqueue.pop(0) | ||
330 | assert isinstance(argv, (list, tuple)), \ | ||
331 | "item on 'cmdqueue' is not a sequence: %r" % argv | ||
332 | else: | ||
333 | if self.use_rawinput: | ||
334 | try: | ||
335 | line = raw_input(self._prompt_str) | ||
336 | except EOFError: | ||
337 | line = 'EOF' | ||
338 | else: | ||
339 | self.stdout.write(self._prompt_str) | ||
340 | self.stdout.flush() | ||
341 | line = self.stdin.readline() | ||
342 | if not len(line): | ||
343 | line = 'EOF' | ||
344 | else: | ||
345 | line = line[:-1] # chop '\n' | ||
346 | argv = line2argv(line) | ||
347 | try: | ||
348 | argv = self.precmd(argv) | ||
349 | retval = self.onecmd(argv) | ||
350 | self.postcmd(argv) | ||
351 | except: | ||
352 | if not self.cmdexc(argv): | ||
353 | raise | ||
354 | retval = 1 | ||
355 | self.lastretval = retval | ||
356 | self.postloop() | ||
357 | finally: | ||
358 | if self.use_rawinput and self.completekey: | ||
359 | try: | ||
360 | import readline | ||
361 | readline.set_completer(self.old_completer) | ||
362 | except ImportError: | ||
363 | pass | ||
364 | self.cmdlooping = False | ||
365 | return retval | ||
366 | |||
367 | def precmd(self, argv): | ||
368 | """Hook method executed just before the command argv is | ||
369 | interpreted, but after the input prompt is generated and issued. | ||
370 | |||
371 | "argv" is the cmd to run. | ||
372 | |||
373 | Returns an argv to run (i.e. this method can modify the command | ||
374 | to run). | ||
375 | """ | ||
376 | return argv | ||
377 | |||
378 | def postcmd(self, argv): | ||
379 | """Hook method executed just after a command dispatch is finished. | ||
380 | |||
381 | "argv" is the command that was run. | ||
382 | """ | ||
383 | pass | ||
384 | |||
385 | def cmdexc(self, argv): | ||
386 | """Called if an exception is raised in any of precmd(), onecmd(), | ||
387 | or postcmd(). If True is returned, the exception is deemed to have | ||
388 | been dealt with. Otherwise, the exception is re-raised. | ||
389 | |||
390 | The default implementation handles CmdlnUserError's, which | ||
391 | typically correspond to user error in calling commands (as | ||
392 | opposed to programmer error in the design of the script using | ||
393 | cmdln.py). | ||
394 | """ | ||
395 | import sys | ||
396 | type, exc, traceback = sys.exc_info() | ||
397 | if isinstance(exc, CmdlnUserError): | ||
398 | msg = "%s %s: %s\nTry '%s help %s' for info.\n"\ | ||
399 | % (self.name, argv[0], exc, self.name, argv[0]) | ||
400 | self.stderr.write(self._str(msg)) | ||
401 | self.stderr.flush() | ||
402 | return True | ||
403 | |||
404 | def onecmd(self, argv): | ||
405 | if not argv: | ||
406 | return self.emptyline() | ||
407 | self.lastcmd = argv | ||
408 | cmdname = self._get_canonical_cmd_name(argv[0]) | ||
409 | if cmdname: | ||
410 | handler = self._get_cmd_handler(cmdname) | ||
411 | if handler: | ||
412 | return self._dispatch_cmd(handler, argv) | ||
413 | return self.default(argv) | ||
414 | |||
415 | def _dispatch_cmd(self, handler, argv): | ||
416 | return handler(argv) | ||
417 | |||
418 | def default(self, argv): | ||
419 | """Hook called to handle a command for which there is no handler. | ||
420 | |||
421 | "argv" is the command and arguments to run. | ||
422 | |||
423 | The default implementation writes and error message to stderr | ||
424 | and returns an error exit status. | ||
425 | |||
426 | Returns a numeric command exit status. | ||
427 | """ | ||
428 | errmsg = self._str(self.unknowncmd % (argv[0],)) | ||
429 | if self.cmdlooping: | ||
430 | self.stderr.write(errmsg+"\n") | ||
431 | else: | ||
432 | self.stderr.write("%s: %s\nTry '%s help' for info.\n" | ||
433 | % (self._name_str, errmsg, self._name_str)) | ||
434 | self.stderr.flush() | ||
435 | return 1 | ||
436 | |||
437 | def parseline(self, line): | ||
438 | # This is used by Cmd.complete (readline completer function) to | ||
439 | # massage the current line buffer before completion processing. | ||
440 | # We override to drop special '!' handling. | ||
441 | line = line.strip() | ||
442 | if not line: | ||
443 | return None, None, line | ||
444 | elif line[0] == '?': | ||
445 | line = 'help ' + line[1:] | ||
446 | i, n = 0, len(line) | ||
447 | while i < n and line[i] in self.identchars: i = i+1 | ||
448 | cmd, arg = line[:i], line[i:].strip() | ||
449 | return cmd, arg, line | ||
450 | |||
451 | def helpdefault(self, cmd, known): | ||
452 | """Hook called to handle help on a command for which there is no | ||
453 | help handler. | ||
454 | |||
455 | "cmd" is the command name on which help was requested. | ||
456 | "known" is a boolean indicating if this command is known | ||
457 | (i.e. if there is a handler for it). | ||
458 | |||
459 | Returns a return code. | ||
460 | """ | ||
461 | if known: | ||
462 | msg = self._str(self.nohelp % (cmd,)) | ||
463 | if self.cmdlooping: | ||
464 | self.stderr.write(msg + '\n') | ||
465 | else: | ||
466 | self.stderr.write("%s: %s\n" % (self.name, msg)) | ||
467 | else: | ||
468 | msg = self.unknowncmd % (cmd,) | ||
469 | if self.cmdlooping: | ||
470 | self.stderr.write(msg + '\n') | ||
471 | else: | ||
472 | self.stderr.write("%s: %s\n" | ||
473 | "Try '%s help' for info.\n" | ||
474 | % (self.name, msg, self.name)) | ||
475 | self.stderr.flush() | ||
476 | return 1 | ||
477 | |||
478 | def do_help(self, argv): | ||
479 | """${cmd_name}: give detailed help on a specific sub-command | ||
480 | |||
481 | Usage: | ||
482 | ${name} help [COMMAND] | ||
483 | """ | ||
484 | if len(argv) > 1: # asking for help on a particular command | ||
485 | doc = None | ||
486 | cmdname = self._get_canonical_cmd_name(argv[1]) or argv[1] | ||
487 | if not cmdname: | ||
488 | return self.helpdefault(argv[1], False) | ||
489 | else: | ||
490 | helpfunc = getattr(self, "help_"+cmdname, None) | ||
491 | if helpfunc: | ||
492 | doc = helpfunc() | ||
493 | else: | ||
494 | handler = self._get_cmd_handler(cmdname) | ||
495 | if handler: | ||
496 | doc = handler.__doc__ | ||
497 | if doc is None: | ||
498 | return self.helpdefault(argv[1], handler != None) | ||
499 | else: # bare "help" command | ||
500 | doc = self.__class__.__doc__ # try class docstring | ||
501 | if doc is None: | ||
502 | # Try to provide some reasonable useful default help. | ||
503 | if self.cmdlooping: prefix = "" | ||
504 | else: prefix = self.name+' ' | ||
505 | doc = """Usage: | ||
506 | %sCOMMAND [ARGS...] | ||
507 | %shelp [COMMAND] | ||
508 | |||
509 | ${option_list} | ||
510 | ${command_list} | ||
511 | ${help_list} | ||
512 | """ % (prefix, prefix) | ||
513 | cmdname = None | ||
514 | |||
515 | if doc: # *do* have help content, massage and print that | ||
516 | doc = self._help_reindent(doc) | ||
517 | doc = self._help_preprocess(doc, cmdname) | ||
518 | doc = doc.rstrip() + '\n' # trim down trailing space | ||
519 | self.stdout.write(self._str(doc)) | ||
520 | self.stdout.flush() | ||
521 | do_help.aliases = ["?"] | ||
522 | |||
523 | def _help_reindent(self, help, indent=None): | ||
524 | """Hook to re-indent help strings before writing to stdout. | ||
525 | |||
526 | "help" is the help content to re-indent | ||
527 | "indent" is a string with which to indent each line of the | ||
528 | help content after normalizing. If unspecified or None | ||
529 | then the default is use: the 'self.helpindent' class | ||
530 | attribute. By default this is the empty string, i.e. | ||
531 | no indentation. | ||
532 | |||
533 | By default, all common leading whitespace is removed and then | ||
534 | the lot is indented by 'self.helpindent'. When calculating the | ||
535 | common leading whitespace the first line is ignored -- hence | ||
536 | help content for Conan can be written as follows and have the | ||
537 | expected indentation: | ||
538 | |||
539 | def do_crush(self, ...): | ||
540 | '''${cmd_name}: crush your enemies, see them driven before you... | ||
541 | |||
542 | c.f. Conan the Barbarian''' | ||
543 | """ | ||
544 | if indent is None: | ||
545 | indent = self.helpindent | ||
546 | lines = help.splitlines(0) | ||
547 | _dedentlines(lines, skip_first_line=True) | ||
548 | lines = [(indent+line).rstrip() for line in lines] | ||
549 | return '\n'.join(lines) | ||
550 | |||
551 | def _help_preprocess(self, help, cmdname): | ||
552 | """Hook to preprocess a help string before writing to stdout. | ||
553 | |||
554 | "help" is the help string to process. | ||
555 | "cmdname" is the canonical sub-command name for which help | ||
556 | is being given, or None if the help is not specific to a | ||
557 | command. | ||
558 | |||
559 | By default the following template variables are interpolated in | ||
560 | help content. (Note: these are similar to Python 2.4's | ||
561 | string.Template interpolation but not quite.) | ||
562 | |||
563 | ${name} | ||
564 | The tool's/shell's name, i.e. 'self.name'. | ||
565 | ${option_list} | ||
566 | A formatted table of options for this shell/tool. | ||
567 | ${command_list} | ||
568 | A formatted table of available sub-commands. | ||
569 | ${help_list} | ||
570 | A formatted table of additional help topics (i.e. 'help_*' | ||
571 | methods with no matching 'do_*' method). | ||
572 | ${cmd_name} | ||
573 | The name (and aliases) for this sub-command formatted as: | ||
574 | "NAME (ALIAS1, ALIAS2, ...)". | ||
575 | ${cmd_usage} | ||
576 | A formatted usage block inferred from the command function | ||
577 | signature. | ||
578 | ${cmd_option_list} | ||
579 | A formatted table of options for this sub-command. (This is | ||
580 | only available for commands using the optparse integration, | ||
581 | i.e. using @cmdln.option decorators or manually setting the | ||
582 | 'optparser' attribute on the 'do_*' method.) | ||
583 | |||
584 | Returns the processed help. | ||
585 | """ | ||
586 | preprocessors = { | ||
587 | "${name}": self._help_preprocess_name, | ||
588 | "${option_list}": self._help_preprocess_option_list, | ||
589 | "${command_list}": self._help_preprocess_command_list, | ||
590 | "${help_list}": self._help_preprocess_help_list, | ||
591 | "${cmd_name}": self._help_preprocess_cmd_name, | ||
592 | "${cmd_usage}": self._help_preprocess_cmd_usage, | ||
593 | "${cmd_option_list}": self._help_preprocess_cmd_option_list, | ||
594 | } | ||
595 | |||
596 | for marker, preprocessor in preprocessors.items(): | ||
597 | if marker in help: | ||
598 | help = preprocessor(help, cmdname) | ||
599 | return help | ||
600 | |||
601 | def _help_preprocess_name(self, help, cmdname=None): | ||
602 | return help.replace("${name}", self.name) | ||
603 | |||
604 | def _help_preprocess_option_list(self, help, cmdname=None): | ||
605 | marker = "${option_list}" | ||
606 | indent, indent_width = _get_indent(marker, help) | ||
607 | suffix = _get_trailing_whitespace(marker, help) | ||
608 | |||
609 | if self.optparser: | ||
610 | # Setup formatting options and format. | ||
611 | # - Indentation of 4 is better than optparse default of 2. | ||
612 | # C.f. Damian Conway's discussion of this in Perl Best | ||
613 | # Practices. | ||
614 | self.optparser.formatter.indent_increment = 4 | ||
615 | self.optparser.formatter.current_indent = indent_width | ||
616 | block = self.optparser.format_option_help() + '\n' | ||
617 | else: | ||
618 | block = "" | ||
619 | |||
620 | help = help.replace(indent+marker+suffix, block, 1) | ||
621 | return help | ||
622 | |||
623 | |||
624 | def _help_preprocess_command_list(self, help, cmdname=None): | ||
625 | marker = "${command_list}" | ||
626 | indent, indent_width = _get_indent(marker, help) | ||
627 | suffix = _get_trailing_whitespace(marker, help) | ||
628 | |||
629 | # Find any aliases for commands. | ||
630 | token2canonical = self._get_canonical_map() | ||
631 | aliases = {} | ||
632 | for token, cmdname in token2canonical.items(): | ||
633 | if token == cmdname: continue | ||
634 | aliases.setdefault(cmdname, []).append(token) | ||
635 | |||
636 | # Get the list of (non-hidden) commands and their | ||
637 | # documentation, if any. | ||
638 | cmdnames = {} # use a dict to strip duplicates | ||
639 | for attr in self.get_names(): | ||
640 | if attr.startswith("do_"): | ||
641 | cmdnames[attr[3:]] = True | ||
642 | cmdnames = cmdnames.keys() | ||
643 | cmdnames.sort() | ||
644 | linedata = [] | ||
645 | for cmdname in cmdnames: | ||
646 | if aliases.get(cmdname): | ||
647 | a = aliases[cmdname] | ||
648 | a.sort() | ||
649 | cmdstr = "%s (%s)" % (cmdname, ", ".join(a)) | ||
650 | else: | ||
651 | cmdstr = cmdname | ||
652 | doc = None | ||
653 | try: | ||
654 | helpfunc = getattr(self, 'help_'+cmdname) | ||
655 | except AttributeError: | ||
656 | handler = self._get_cmd_handler(cmdname) | ||
657 | if handler: | ||
658 | doc = handler.__doc__ | ||
659 | else: | ||
660 | doc = helpfunc() | ||
661 | |||
662 | # Strip "${cmd_name}: " from the start of a command's doc. Best | ||
663 | # practice dictates that command help strings begin with this, but | ||
664 | # it isn't at all wanted for the command list. | ||
665 | to_strip = "${cmd_name}:" | ||
666 | if doc and doc.startswith(to_strip): | ||
667 | #log.debug("stripping %r from start of %s's help string", | ||
668 | # to_strip, cmdname) | ||
669 | doc = doc[len(to_strip):].lstrip() | ||
670 | linedata.append( (cmdstr, doc) ) | ||
671 | |||
672 | if linedata: | ||
673 | subindent = indent + ' '*4 | ||
674 | lines = _format_linedata(linedata, subindent, indent_width+4) | ||
675 | block = indent + "Commands:\n" \ | ||
676 | + '\n'.join(lines) + "\n\n" | ||
677 | help = help.replace(indent+marker+suffix, block, 1) | ||
678 | return help | ||
679 | |||
680 | def _gen_names_and_attrs(self): | ||
681 | # Inheritance says we have to look in class and | ||
682 | # base classes; order is not important. | ||
683 | names = [] | ||
684 | classes = [self.__class__] | ||
685 | while classes: | ||
686 | aclass = classes.pop(0) | ||
687 | if aclass.__bases__: | ||
688 | classes = classes + list(aclass.__bases__) | ||
689 | for name in dir(aclass): | ||
690 | yield (name, getattr(aclass, name)) | ||
691 | |||
692 | def _help_preprocess_help_list(self, help, cmdname=None): | ||
693 | marker = "${help_list}" | ||
694 | indent, indent_width = _get_indent(marker, help) | ||
695 | suffix = _get_trailing_whitespace(marker, help) | ||
696 | |||
697 | # Determine the additional help topics, if any. | ||
698 | helpnames = {} | ||
699 | token2cmdname = self._get_canonical_map() | ||
700 | for attrname, attr in self._gen_names_and_attrs(): | ||
701 | if not attrname.startswith("help_"): continue | ||
702 | helpname = attrname[5:] | ||
703 | if helpname not in token2cmdname: | ||
704 | helpnames[helpname] = attr | ||
705 | |||
706 | if helpnames: | ||
707 | linedata = [(n, a.__doc__ or "") for n, a in helpnames.items()] | ||
708 | linedata.sort() | ||
709 | |||
710 | subindent = indent + ' '*4 | ||
711 | lines = _format_linedata(linedata, subindent, indent_width+4) | ||
712 | block = (indent | ||
713 | + "Additional help topics (run `%s help TOPIC'):\n" % self.name | ||
714 | + '\n'.join(lines) | ||
715 | + "\n\n") | ||
716 | else: | ||
717 | block = '' | ||
718 | help = help.replace(indent+marker+suffix, block, 1) | ||
719 | return help | ||
720 | |||
721 | def _help_preprocess_cmd_name(self, help, cmdname=None): | ||
722 | marker = "${cmd_name}" | ||
723 | handler = self._get_cmd_handler(cmdname) | ||
724 | if not handler: | ||
725 | raise CmdlnError("cannot preprocess '%s' into help string: " | ||
726 | "could not find command handler for %r" | ||
727 | % (marker, cmdname)) | ||
728 | s = cmdname | ||
729 | if hasattr(handler, "aliases"): | ||
730 | s += " (%s)" % (", ".join(handler.aliases)) | ||
731 | help = help.replace(marker, s) | ||
732 | return help | ||
733 | |||
734 | #TODO: this only makes sense as part of the Cmdln class. | ||
735 | # Add hooks to add help preprocessing template vars and put | ||
736 | # this one on that class. | ||
737 | def _help_preprocess_cmd_usage(self, help, cmdname=None): | ||
738 | marker = "${cmd_usage}" | ||
739 | handler = self._get_cmd_handler(cmdname) | ||
740 | if not handler: | ||
741 | raise CmdlnError("cannot preprocess '%s' into help string: " | ||
742 | "could not find command handler for %r" | ||
743 | % (marker, cmdname)) | ||
744 | indent, indent_width = _get_indent(marker, help) | ||
745 | suffix = _get_trailing_whitespace(marker, help) | ||
746 | |||
747 | # Extract the introspection bits we need. | ||
748 | func = handler.im_func | ||
749 | if func.func_defaults: | ||
750 | func_defaults = list(func.func_defaults) | ||
751 | else: | ||
752 | func_defaults = [] | ||
753 | co_argcount = func.func_code.co_argcount | ||
754 | co_varnames = func.func_code.co_varnames | ||
755 | co_flags = func.func_code.co_flags | ||
756 | CO_FLAGS_ARGS = 4 | ||
757 | CO_FLAGS_KWARGS = 8 | ||
758 | |||
759 | # Adjust argcount for possible *args and **kwargs arguments. | ||
760 | argcount = co_argcount | ||
761 | if co_flags & CO_FLAGS_ARGS: argcount += 1 | ||
762 | if co_flags & CO_FLAGS_KWARGS: argcount += 1 | ||
763 | |||
764 | # Determine the usage string. | ||
765 | usage = "%s %s" % (self.name, cmdname) | ||
766 | if argcount <= 2: # handler ::= do_FOO(self, argv) | ||
767 | usage += " [ARGS...]" | ||
768 | elif argcount >= 3: # handler ::= do_FOO(self, subcmd, opts, ...) | ||
769 | argnames = list(co_varnames[3:argcount]) | ||
770 | tail = "" | ||
771 | if co_flags & CO_FLAGS_KWARGS: | ||
772 | name = argnames.pop(-1) | ||
773 | import warnings | ||
774 | # There is no generally accepted mechanism for passing | ||
775 | # keyword arguments from the command line. Could | ||
776 | # *perhaps* consider: arg=value arg2=value2 ... | ||
777 | warnings.warn("argument '**%s' on '%s.%s' command " | ||
778 | "handler will never get values" | ||
779 | % (name, self.__class__.__name__, | ||
780 | func.func_name)) | ||
781 | if co_flags & CO_FLAGS_ARGS: | ||
782 | name = argnames.pop(-1) | ||
783 | tail = "[%s...]" % name.upper() | ||
784 | while func_defaults: | ||
785 | func_defaults.pop(-1) | ||
786 | name = argnames.pop(-1) | ||
787 | tail = "[%s%s%s]" % (name.upper(), (tail and ' ' or ''), tail) | ||
788 | while argnames: | ||
789 | name = argnames.pop(-1) | ||
790 | tail = "%s %s" % (name.upper(), tail) | ||
791 | usage += ' ' + tail | ||
792 | |||
793 | block_lines = [ | ||
794 | self.helpindent + "Usage:", | ||
795 | self.helpindent + ' '*4 + usage | ||
796 | ] | ||
797 | block = '\n'.join(block_lines) + '\n\n' | ||
798 | |||
799 | help = help.replace(indent+marker+suffix, block, 1) | ||
800 | return help | ||
801 | |||
802 | #TODO: this only makes sense as part of the Cmdln class. | ||
803 | # Add hooks to add help preprocessing template vars and put | ||
804 | # this one on that class. | ||
805 | def _help_preprocess_cmd_option_list(self, help, cmdname=None): | ||
806 | marker = "${cmd_option_list}" | ||
807 | handler = self._get_cmd_handler(cmdname) | ||
808 | if not handler: | ||
809 | raise CmdlnError("cannot preprocess '%s' into help string: " | ||
810 | "could not find command handler for %r" | ||
811 | % (marker, cmdname)) | ||
812 | indent, indent_width = _get_indent(marker, help) | ||
813 | suffix = _get_trailing_whitespace(marker, help) | ||
814 | if hasattr(handler, "optparser"): | ||
815 | # Setup formatting options and format. | ||
816 | # - Indentation of 4 is better than optparse default of 2. | ||
817 | # C.f. Damian Conway's discussion of this in Perl Best | ||
818 | # Practices. | ||
819 | handler.optparser.formatter.indent_increment = 4 | ||
820 | handler.optparser.formatter.current_indent = indent_width | ||
821 | block = handler.optparser.format_option_help() + '\n' | ||
822 | else: | ||
823 | block = "" | ||
824 | |||
825 | help = help.replace(indent+marker+suffix, block, 1) | ||
826 | return help | ||
827 | |||
828 | def _get_canonical_cmd_name(self, token): | ||
829 | map = self._get_canonical_map() | ||
830 | return map.get(token, None) | ||
831 | |||
832 | def _get_canonical_map(self): | ||
833 | """Return a mapping of available command names and aliases to | ||
834 | their canonical command name. | ||
835 | """ | ||
836 | cacheattr = "_token2canonical" | ||
837 | if not hasattr(self, cacheattr): | ||
838 | # Get the list of commands and their aliases, if any. | ||
839 | token2canonical = {} | ||
840 | cmd2funcname = {} # use a dict to strip duplicates | ||
841 | for attr in self.get_names(): | ||
842 | if attr.startswith("do_"): cmdname = attr[3:] | ||
843 | elif attr.startswith("_do_"): cmdname = attr[4:] | ||
844 | else: | ||
845 | continue | ||
846 | cmd2funcname[cmdname] = attr | ||
847 | token2canonical[cmdname] = cmdname | ||
848 | for cmdname, funcname in cmd2funcname.items(): # add aliases | ||
849 | func = getattr(self, funcname) | ||
850 | aliases = getattr(func, "aliases", []) | ||
851 | for alias in aliases: | ||
852 | if alias in cmd2funcname: | ||
853 | import warnings | ||
854 | warnings.warn("'%s' alias for '%s' command conflicts " | ||
855 | "with '%s' handler" | ||
856 | % (alias, cmdname, cmd2funcname[alias])) | ||
857 | continue | ||
858 | token2canonical[alias] = cmdname | ||
859 | setattr(self, cacheattr, token2canonical) | ||
860 | return getattr(self, cacheattr) | ||
861 | |||
862 | def _get_cmd_handler(self, cmdname): | ||
863 | handler = None | ||
864 | try: | ||
865 | handler = getattr(self, 'do_' + cmdname) | ||
866 | except AttributeError: | ||
867 | try: | ||
868 | # Private command handlers begin with "_do_". | ||
869 | handler = getattr(self, '_do_' + cmdname) | ||
870 | except AttributeError: | ||
871 | pass | ||
872 | return handler | ||
873 | |||
874 | def _do_EOF(self, argv): | ||
875 | # Default EOF handler | ||
876 | # Note: an actual EOF is redirected to this command. | ||
877 | #TODO: separate name for this. Currently it is available from | ||
878 | # command-line. Is that okay? | ||
879 | self.stdout.write('\n') | ||
880 | self.stdout.flush() | ||
881 | self.stop = True | ||
882 | |||
883 | def emptyline(self): | ||
884 | # Different from cmd.Cmd: don't repeat the last command for an | ||
885 | # emptyline. | ||
886 | if self.cmdlooping: | ||
887 | pass | ||
888 | else: | ||
889 | return self.do_help(["help"]) | ||
890 | |||
891 | |||
892 | #---- optparse.py extension to fix (IMO) some deficiencies | ||
893 | # | ||
894 | # See the class _OptionParserEx docstring for details. | ||
895 | # | ||
896 | |||
897 | class StopOptionProcessing(Exception): | ||
898 | """Indicate that option *and argument* processing should stop | ||
899 | cleanly. This is not an error condition. It is similar in spirit to | ||
900 | StopIteration. This is raised by _OptionParserEx's default "help" | ||
901 | and "version" option actions and can be raised by custom option | ||
902 | callbacks too. | ||
903 | |||
904 | Hence the typical CmdlnOptionParser (a subclass of _OptionParserEx) | ||
905 | usage is: | ||
906 | |||
907 | parser = CmdlnOptionParser(mycmd) | ||
908 | parser.add_option("-f", "--force", dest="force") | ||
909 | ... | ||
910 | try: | ||
911 | opts, args = parser.parse_args() | ||
912 | except StopOptionProcessing: | ||
913 | # normal termination, "--help" was probably given | ||
914 | sys.exit(0) | ||
915 | """ | ||
916 | |||
917 | class _OptionParserEx(optparse.OptionParser): | ||
918 | """An optparse.OptionParser that uses exceptions instead of sys.exit. | ||
919 | |||
920 | This class is an extension of optparse.OptionParser that differs | ||
921 | as follows: | ||
922 | - Correct (IMO) the default OptionParser error handling to never | ||
923 | sys.exit(). Instead OptParseError exceptions are passed through. | ||
924 | - Add the StopOptionProcessing exception (a la StopIteration) to | ||
925 | indicate normal termination of option processing. | ||
926 | See StopOptionProcessing's docstring for details. | ||
927 | |||
928 | I'd also like to see the following in the core optparse.py, perhaps | ||
929 | as a RawOptionParser which would serve as a base class for the more | ||
930 | generally used OptionParser (that works as current): | ||
931 | - Remove the implicit addition of the -h|--help and --version | ||
932 | options. They can get in the way (e.g. if want '-?' and '-V' for | ||
933 | these as well) and it is not hard to do: | ||
934 | optparser.add_option("-h", "--help", action="help") | ||
935 | optparser.add_option("--version", action="version") | ||
936 | These are good practices, just not valid defaults if they can | ||
937 | get in the way. | ||
938 | """ | ||
939 | def error(self, msg): | ||
940 | raise optparse.OptParseError(msg) | ||
941 | |||
942 | def exit(self, status=0, msg=None): | ||
943 | if status == 0: | ||
944 | raise StopOptionProcessing(msg) | ||
945 | else: | ||
946 | #TODO: don't lose status info here | ||
947 | raise optparse.OptParseError(msg) | ||
948 | |||
949 | |||
950 | |||
951 | #---- optparse.py-based option processing support | ||
952 | |||
953 | class CmdlnOptionParser(_OptionParserEx): | ||
954 | """An optparse.OptionParser class more appropriate for top-level | ||
955 | Cmdln options. For parsing of sub-command options, see | ||
956 | SubCmdOptionParser. | ||
957 | |||
958 | Changes: | ||
959 | - disable_interspersed_args() by default, because a Cmdln instance | ||
960 | has sub-commands which may themselves have options. | ||
961 | - Redirect print_help() to the Cmdln.do_help() which is better | ||
962 | equiped to handle the "help" action. | ||
963 | - error() will raise a CmdlnUserError: OptionParse.error() is meant | ||
964 | to be called for user errors. Raising a well-known error here can | ||
965 | make error handling clearer. | ||
966 | - Also see the changes in _OptionParserEx. | ||
967 | """ | ||
968 | def __init__(self, cmdln, **kwargs): | ||
969 | self.cmdln = cmdln | ||
970 | kwargs["prog"] = self.cmdln.name | ||
971 | _OptionParserEx.__init__(self, **kwargs) | ||
972 | self.disable_interspersed_args() | ||
973 | |||
974 | def print_help(self, file=None): | ||
975 | self.cmdln.onecmd(["help"]) | ||
976 | |||
977 | def error(self, msg): | ||
978 | raise CmdlnUserError(msg) | ||
979 | |||
980 | |||
981 | class SubCmdOptionParser(_OptionParserEx): | ||
982 | def set_cmdln_info(self, cmdln, subcmd): | ||
983 | """Called by Cmdln to pass relevant info about itself needed | ||
984 | for print_help(). | ||
985 | """ | ||
986 | self.cmdln = cmdln | ||
987 | self.subcmd = subcmd | ||
988 | |||
989 | def print_help(self, file=None): | ||
990 | self.cmdln.onecmd(["help", self.subcmd]) | ||
991 | |||
992 | def error(self, msg): | ||
993 | raise CmdlnUserError(msg) | ||
994 | |||
995 | |||
996 | def option(*args, **kwargs): | ||
997 | """Decorator to add an option to the optparser argument of a Cmdln | ||
998 | subcommand. | ||
999 | |||
1000 | Example: | ||
1001 | class MyShell(cmdln.Cmdln): | ||
1002 | @cmdln.option("-f", "--force", help="force removal") | ||
1003 | def do_remove(self, subcmd, opts, *args): | ||
1004 | #... | ||
1005 | """ | ||
1006 | #XXX Is there a possible optimization for many options to not have a | ||
1007 | # large stack depth here? | ||
1008 | def decorate(f): | ||
1009 | if not hasattr(f, "optparser"): | ||
1010 | f.optparser = SubCmdOptionParser() | ||
1011 | f.optparser.add_option(*args, **kwargs) | ||
1012 | return f | ||
1013 | return decorate | ||
1014 | |||
1015 | |||
1016 | class Cmdln(RawCmdln): | ||
1017 | """An improved (on cmd.Cmd) framework for building multi-subcommand | ||
1018 | scripts (think "svn" & "cvs") and simple shells (think "pdb" and | ||
1019 | "gdb"). | ||
1020 | |||
1021 | A simple example: | ||
1022 | |||
1023 | import cmdln | ||
1024 | |||
1025 | class MySVN(cmdln.Cmdln): | ||
1026 | name = "svn" | ||
1027 | |||
1028 | @cmdln.aliases('stat', 'st') | ||
1029 | @cmdln.option('-v', '--verbose', action='store_true' | ||
1030 | help='print verbose information') | ||
1031 | def do_status(self, subcmd, opts, *paths): | ||
1032 | print "handle 'svn status' command" | ||
1033 | |||
1034 | #... | ||
1035 | |||
1036 | if __name__ == "__main__": | ||
1037 | shell = MySVN() | ||
1038 | retval = shell.main() | ||
1039 | sys.exit(retval) | ||
1040 | |||
1041 | 'Cmdln' extends 'RawCmdln' by providing optparse option processing | ||
1042 | integration. See this class' _dispatch_cmd() docstring and | ||
1043 | <http://trentm.com/projects/cmdln> for more information. | ||
1044 | """ | ||
1045 | def _dispatch_cmd(self, handler, argv): | ||
1046 | """Introspect sub-command handler signature to determine how to | ||
1047 | dispatch the command. The raw handler provided by the base | ||
1048 | 'RawCmdln' class is still supported: | ||
1049 | |||
1050 | def do_foo(self, argv): | ||
1051 | # 'argv' is the vector of command line args, argv[0] is | ||
1052 | # the command name itself (i.e. "foo" or an alias) | ||
1053 | pass | ||
1054 | |||
1055 | In addition, if the handler has more than 2 arguments option | ||
1056 | processing is automatically done (using optparse): | ||
1057 | |||
1058 | @cmdln.option('-v', '--verbose', action='store_true') | ||
1059 | def do_bar(self, subcmd, opts, *args): | ||
1060 | # subcmd = <"bar" or an alias> | ||
1061 | # opts = <an optparse.Values instance> | ||
1062 | if opts.verbose: | ||
1063 | print "lots of debugging output..." | ||
1064 | # args = <tuple of arguments> | ||
1065 | for arg in args: | ||
1066 | bar(arg) | ||
1067 | |||
1068 | TODO: explain that "*args" can be other signatures as well. | ||
1069 | |||
1070 | The `cmdln.option` decorator corresponds to an `add_option()` | ||
1071 | method call on an `optparse.OptionParser` instance. | ||
1072 | |||
1073 | You can declare a specific number of arguments: | ||
1074 | |||
1075 | @cmdln.option('-v', '--verbose', action='store_true') | ||
1076 | def do_bar2(self, subcmd, opts, bar_one, bar_two): | ||
1077 | #... | ||
1078 | |||
1079 | and an appropriate error message will be raised/printed if the | ||
1080 | command is called with a different number of args. | ||
1081 | """ | ||
1082 | co_argcount = handler.im_func.func_code.co_argcount | ||
1083 | if co_argcount == 2: # handler ::= do_foo(self, argv) | ||
1084 | return handler(argv) | ||
1085 | elif co_argcount >= 3: # handler ::= do_foo(self, subcmd, opts, ...) | ||
1086 | try: | ||
1087 | optparser = handler.optparser | ||
1088 | except AttributeError: | ||
1089 | optparser = handler.im_func.optparser = SubCmdOptionParser() | ||
1090 | assert isinstance(optparser, SubCmdOptionParser) | ||
1091 | optparser.set_cmdln_info(self, argv[0]) | ||
1092 | try: | ||
1093 | opts, args = optparser.parse_args(argv[1:]) | ||
1094 | except StopOptionProcessing: | ||
1095 | #TODO: this doesn't really fly for a replacement of | ||
1096 | # optparse.py behaviour, does it? | ||
1097 | return 0 # Normal command termination | ||
1098 | |||
1099 | try: | ||
1100 | return handler(argv[0], opts, *args) | ||
1101 | except TypeError, ex: | ||
1102 | # Some TypeError's are user errors: | ||
1103 | # do_foo() takes at least 4 arguments (3 given) | ||
1104 | # do_foo() takes at most 5 arguments (6 given) | ||
1105 | # do_foo() takes exactly 5 arguments (6 given) | ||
1106 | # Raise CmdlnUserError for these with a suitably | ||
1107 | # massaged error message. | ||
1108 | import sys | ||
1109 | tb = sys.exc_info()[2] # the traceback object | ||
1110 | if tb.tb_next is not None: | ||
1111 | # If the traceback is more than one level deep, then the | ||
1112 | # TypeError do *not* happen on the "handler(...)" call | ||
1113 | # above. In that we don't want to handle it specially | ||
1114 | # here: it would falsely mask deeper code errors. | ||
1115 | raise | ||
1116 | msg = ex.args[0] | ||
1117 | match = _INCORRECT_NUM_ARGS_RE.search(msg) | ||
1118 | if match: | ||
1119 | msg = list(match.groups()) | ||
1120 | msg[1] = int(msg[1]) - 3 | ||
1121 | if msg[1] == 1: | ||
1122 | msg[2] = msg[2].replace("arguments", "argument") | ||
1123 | msg[3] = int(msg[3]) - 3 | ||
1124 | msg = ''.join(map(str, msg)) | ||
1125 | raise CmdlnUserError(msg) | ||
1126 | else: | ||
1127 | raise | ||
1128 | else: | ||
1129 | raise CmdlnError("incorrect argcount for %s(): takes %d, must " | ||
1130 | "take 2 for 'argv' signature or 3+ for 'opts' " | ||
1131 | "signature" % (handler.__name__, co_argcount)) | ||
1132 | |||
1133 | |||
1134 | |||
1135 | #---- internal support functions | ||
1136 | |||
1137 | def _format_linedata(linedata, indent, indent_width): | ||
1138 | """Format specific linedata into a pleasant layout. | ||
1139 | |||
1140 | "linedata" is a list of 2-tuples of the form: | ||
1141 | (<item-display-string>, <item-docstring>) | ||
1142 | "indent" is a string to use for one level of indentation | ||
1143 | "indent_width" is a number of columns by which the | ||
1144 | formatted data will be indented when printed. | ||
1145 | |||
1146 | The <item-display-string> column is held to 15 columns. | ||
1147 | """ | ||
1148 | lines = [] | ||
1149 | WIDTH = 78 - indent_width | ||
1150 | SPACING = 2 | ||
1151 | NAME_WIDTH_LOWER_BOUND = 13 | ||
1152 | NAME_WIDTH_UPPER_BOUND = 16 | ||
1153 | NAME_WIDTH = max([len(s) for s,d in linedata]) | ||
1154 | if NAME_WIDTH < NAME_WIDTH_LOWER_BOUND: | ||
1155 | NAME_WIDTH = NAME_WIDTH_LOWER_BOUND | ||
1156 | else: | ||
1157 | NAME_WIDTH = NAME_WIDTH_UPPER_BOUND | ||
1158 | |||
1159 | DOC_WIDTH = WIDTH - NAME_WIDTH - SPACING | ||
1160 | for namestr, doc in linedata: | ||
1161 | line = indent + namestr | ||
1162 | if len(namestr) <= NAME_WIDTH: | ||
1163 | line += ' ' * (NAME_WIDTH + SPACING - len(namestr)) | ||
1164 | else: | ||
1165 | lines.append(line) | ||
1166 | line = indent + ' ' * (NAME_WIDTH + SPACING) | ||
1167 | line += _summarize_doc(doc, DOC_WIDTH) | ||
1168 | lines.append(line.rstrip()) | ||
1169 | return lines | ||
1170 | |||
1171 | def _summarize_doc(doc, length=60): | ||
1172 | r"""Parse out a short one line summary from the given doclines. | ||
1173 | |||
1174 | "doc" is the doc string to summarize. | ||
1175 | "length" is the max length for the summary | ||
1176 | |||
1177 | >>> _summarize_doc("this function does this") | ||
1178 | 'this function does this' | ||
1179 | >>> _summarize_doc("this function does this", 10) | ||
1180 | 'this fu...' | ||
1181 | >>> _summarize_doc("this function does this\nand that") | ||
1182 | 'this function does this and that' | ||
1183 | >>> _summarize_doc("this function does this\n\nand that") | ||
1184 | 'this function does this' | ||
1185 | """ | ||
1186 | import re | ||
1187 | if doc is None: | ||
1188 | return "" | ||
1189 | assert length > 3, "length <= 3 is absurdly short for a doc summary" | ||
1190 | doclines = doc.strip().splitlines(0) | ||
1191 | if not doclines: | ||
1192 | return "" | ||
1193 | |||
1194 | summlines = [] | ||
1195 | for i, line in enumerate(doclines): | ||
1196 | stripped = line.strip() | ||
1197 | if not stripped: | ||
1198 | break | ||
1199 | summlines.append(stripped) | ||
1200 | if len(''.join(summlines)) >= length: | ||
1201 | break | ||
1202 | |||
1203 | summary = ' '.join(summlines) | ||
1204 | if len(summary) > length: | ||
1205 | summary = summary[:length-3] + "..." | ||
1206 | return summary | ||
1207 | |||
1208 | |||
1209 | def line2argv(line): | ||
1210 | r"""Parse the given line into an argument vector. | ||
1211 | |||
1212 | "line" is the line of input to parse. | ||
1213 | |||
1214 | This may get niggly when dealing with quoting and escaping. The | ||
1215 | current state of this parsing may not be completely thorough/correct | ||
1216 | in this respect. | ||
1217 | |||
1218 | >>> from cmdln import line2argv | ||
1219 | >>> line2argv("foo") | ||
1220 | ['foo'] | ||
1221 | >>> line2argv("foo bar") | ||
1222 | ['foo', 'bar'] | ||
1223 | >>> line2argv("foo bar ") | ||
1224 | ['foo', 'bar'] | ||
1225 | >>> line2argv(" foo bar") | ||
1226 | ['foo', 'bar'] | ||
1227 | |||
1228 | Quote handling: | ||
1229 | |||
1230 | >>> line2argv("'foo bar'") | ||
1231 | ['foo bar'] | ||
1232 | >>> line2argv('"foo bar"') | ||
1233 | ['foo bar'] | ||
1234 | >>> line2argv(r'"foo\"bar"') | ||
1235 | ['foo"bar'] | ||
1236 | >>> line2argv("'foo bar' spam") | ||
1237 | ['foo bar', 'spam'] | ||
1238 | >>> line2argv("'foo 'bar spam") | ||
1239 | ['foo bar', 'spam'] | ||
1240 | |||
1241 | >>> line2argv('some\tsimple\ttests') | ||
1242 | ['some', 'simple', 'tests'] | ||
1243 | >>> line2argv('a "more complex" test') | ||
1244 | ['a', 'more complex', 'test'] | ||
1245 | >>> line2argv('a more="complex test of " quotes') | ||
1246 | ['a', 'more=complex test of ', 'quotes'] | ||
1247 | >>> line2argv('a more" complex test of " quotes') | ||
1248 | ['a', 'more complex test of ', 'quotes'] | ||
1249 | >>> line2argv('an "embedded \\"quote\\""') | ||
1250 | ['an', 'embedded "quote"'] | ||
1251 | |||
1252 | # Komodo bug 48027 | ||
1253 | >>> line2argv('foo bar C:\\') | ||
1254 | ['foo', 'bar', 'C:\\'] | ||
1255 | |||
1256 | # Komodo change 127581 | ||
1257 | >>> line2argv(r'"\test\slash" "foo bar" "foo\"bar"') | ||
1258 | ['\\test\\slash', 'foo bar', 'foo"bar'] | ||
1259 | |||
1260 | # Komodo change 127629 | ||
1261 | >>> if sys.platform == "win32": | ||
1262 | ... line2argv(r'\foo\bar') == ['\\foo\\bar'] | ||
1263 | ... line2argv(r'\\foo\\bar') == ['\\\\foo\\\\bar'] | ||
1264 | ... line2argv('"foo') == ['foo'] | ||
1265 | ... else: | ||
1266 | ... line2argv(r'\foo\bar') == ['foobar'] | ||
1267 | ... line2argv(r'\\foo\\bar') == ['\\foo\\bar'] | ||
1268 | ... try: | ||
1269 | ... line2argv('"foo') | ||
1270 | ... except ValueError, ex: | ||
1271 | ... "not terminated" in str(ex) | ||
1272 | True | ||
1273 | True | ||
1274 | True | ||
1275 | """ | ||
1276 | import string | ||
1277 | line = line.strip() | ||
1278 | argv = [] | ||
1279 | state = "default" | ||
1280 | arg = None # the current argument being parsed | ||
1281 | i = -1 | ||
1282 | while 1: | ||
1283 | i += 1 | ||
1284 | if i >= len(line): break | ||
1285 | ch = line[i] | ||
1286 | |||
1287 | if ch == "\\" and i+1 < len(line): | ||
1288 | # escaped char always added to arg, regardless of state | ||
1289 | if arg is None: arg = "" | ||
1290 | if (sys.platform == "win32" | ||
1291 | or state in ("double-quoted", "single-quoted") | ||
1292 | ) and line[i+1] not in tuple('"\''): | ||
1293 | arg += ch | ||
1294 | i += 1 | ||
1295 | arg += line[i] | ||
1296 | continue | ||
1297 | |||
1298 | if state == "single-quoted": | ||
1299 | if ch == "'": | ||
1300 | state = "default" | ||
1301 | else: | ||
1302 | arg += ch | ||
1303 | elif state == "double-quoted": | ||
1304 | if ch == '"': | ||
1305 | state = "default" | ||
1306 | else: | ||
1307 | arg += ch | ||
1308 | elif state == "default": | ||
1309 | if ch == '"': | ||
1310 | if arg is None: arg = "" | ||
1311 | state = "double-quoted" | ||
1312 | elif ch == "'": | ||
1313 | if arg is None: arg = "" | ||
1314 | state = "single-quoted" | ||
1315 | elif ch in string.whitespace: | ||
1316 | if arg is not None: | ||
1317 | argv.append(arg) | ||
1318 | arg = None | ||
1319 | else: | ||
1320 | if arg is None: arg = "" | ||
1321 | arg += ch | ||
1322 | if arg is not None: | ||
1323 | argv.append(arg) | ||
1324 | if not sys.platform == "win32" and state != "default": | ||
1325 | raise ValueError("command line is not terminated: unfinished %s " | ||
1326 | "segment" % state) | ||
1327 | return argv | ||
1328 | |||
1329 | |||
1330 | def argv2line(argv): | ||
1331 | r"""Put together the given argument vector into a command line. | ||
1332 | |||
1333 | "argv" is the argument vector to process. | ||
1334 | |||
1335 | >>> from cmdln import argv2line | ||
1336 | >>> argv2line(['foo']) | ||
1337 | 'foo' | ||
1338 | >>> argv2line(['foo', 'bar']) | ||
1339 | 'foo bar' | ||
1340 | >>> argv2line(['foo', 'bar baz']) | ||
1341 | 'foo "bar baz"' | ||
1342 | >>> argv2line(['foo"bar']) | ||
1343 | 'foo"bar' | ||
1344 | >>> print argv2line(['foo" bar']) | ||
1345 | 'foo" bar' | ||
1346 | >>> print argv2line(["foo' bar"]) | ||
1347 | "foo' bar" | ||
1348 | >>> argv2line(["foo'bar"]) | ||
1349 | "foo'bar" | ||
1350 | """ | ||
1351 | escapedArgs = [] | ||
1352 | for arg in argv: | ||
1353 | if ' ' in arg and '"' not in arg: | ||
1354 | arg = '"'+arg+'"' | ||
1355 | elif ' ' in arg and "'" not in arg: | ||
1356 | arg = "'"+arg+"'" | ||
1357 | elif ' ' in arg: | ||
1358 | arg = arg.replace('"', r'\"') | ||
1359 | arg = '"'+arg+'"' | ||
1360 | escapedArgs.append(arg) | ||
1361 | return ' '.join(escapedArgs) | ||
1362 | |||
1363 | |||
1364 | # Recipe: dedent (0.1) in /Users/trentm/tm/recipes/cookbook | ||
1365 | def _dedentlines(lines, tabsize=8, skip_first_line=False): | ||
1366 | """_dedentlines(lines, tabsize=8, skip_first_line=False) -> dedented lines | ||
1367 | |||
1368 | "lines" is a list of lines to dedent. | ||
1369 | "tabsize" is the tab width to use for indent width calculations. | ||
1370 | "skip_first_line" is a boolean indicating if the first line should | ||
1371 | be skipped for calculating the indent width and for dedenting. | ||
1372 | This is sometimes useful for docstrings and similar. | ||
1373 | |||
1374 | Same as dedent() except operates on a sequence of lines. Note: the | ||
1375 | lines list is modified **in-place**. | ||
1376 | """ | ||
1377 | DEBUG = False | ||
1378 | if DEBUG: | ||
1379 | print "dedent: dedent(..., tabsize=%d, skip_first_line=%r)"\ | ||
1380 | % (tabsize, skip_first_line) | ||
1381 | indents = [] | ||
1382 | margin = None | ||
1383 | for i, line in enumerate(lines): | ||
1384 | if i == 0 and skip_first_line: continue | ||
1385 | indent = 0 | ||
1386 | for ch in line: | ||
1387 | if ch == ' ': | ||
1388 | indent += 1 | ||
1389 | elif ch == '\t': | ||
1390 | indent += tabsize - (indent % tabsize) | ||
1391 | elif ch in '\r\n': | ||
1392 | continue # skip all-whitespace lines | ||
1393 | else: | ||
1394 | break | ||
1395 | else: | ||
1396 | continue # skip all-whitespace lines | ||
1397 | if DEBUG: print "dedent: indent=%d: %r" % (indent, line) | ||
1398 | if margin is None: | ||
1399 | margin = indent | ||
1400 | else: | ||
1401 | margin = min(margin, indent) | ||
1402 | if DEBUG: print "dedent: margin=%r" % margin | ||
1403 | |||
1404 | if margin is not None and margin > 0: | ||
1405 | for i, line in enumerate(lines): | ||
1406 | if i == 0 and skip_first_line: continue | ||
1407 | removed = 0 | ||
1408 | for j, ch in enumerate(line): | ||
1409 | if ch == ' ': | ||
1410 | removed += 1 | ||
1411 | elif ch == '\t': | ||
1412 | removed += tabsize - (removed % tabsize) | ||
1413 | elif ch in '\r\n': | ||
1414 | if DEBUG: print "dedent: %r: EOL -> strip up to EOL" % line | ||
1415 | lines[i] = lines[i][j:] | ||
1416 | break | ||
1417 | else: | ||
1418 | raise ValueError("unexpected non-whitespace char %r in " | ||
1419 | "line %r while removing %d-space margin" | ||
1420 | % (ch, line, margin)) | ||
1421 | if DEBUG: | ||
1422 | print "dedent: %r: %r -> removed %d/%d"\ | ||
1423 | % (line, ch, removed, margin) | ||
1424 | if removed == margin: | ||
1425 | lines[i] = lines[i][j+1:] | ||
1426 | break | ||
1427 | elif removed > margin: | ||
1428 | lines[i] = ' '*(removed-margin) + lines[i][j+1:] | ||
1429 | break | ||
1430 | return lines | ||
1431 | |||
1432 | def _dedent(text, tabsize=8, skip_first_line=False): | ||
1433 | """_dedent(text, tabsize=8, skip_first_line=False) -> dedented text | ||
1434 | |||
1435 | "text" is the text to dedent. | ||
1436 | "tabsize" is the tab width to use for indent width calculations. | ||
1437 | "skip_first_line" is a boolean indicating if the first line should | ||
1438 | be skipped for calculating the indent width and for dedenting. | ||
1439 | This is sometimes useful for docstrings and similar. | ||
1440 | |||
1441 | textwrap.dedent(s), but don't expand tabs to spaces | ||
1442 | """ | ||
1443 | lines = text.splitlines(1) | ||
1444 | _dedentlines(lines, tabsize=tabsize, skip_first_line=skip_first_line) | ||
1445 | return ''.join(lines) | ||
1446 | |||
1447 | |||
1448 | def _get_indent(marker, s, tab_width=8): | ||
1449 | """_get_indent(marker, s, tab_width=8) -> | ||
1450 | (<indentation-of-'marker'>, <indentation-width>)""" | ||
1451 | # Figure out how much the marker is indented. | ||
1452 | INDENT_CHARS = tuple(' \t') | ||
1453 | start = s.index(marker) | ||
1454 | i = start | ||
1455 | while i > 0: | ||
1456 | if s[i-1] not in INDENT_CHARS: | ||
1457 | break | ||
1458 | i -= 1 | ||
1459 | indent = s[i:start] | ||
1460 | indent_width = 0 | ||
1461 | for ch in indent: | ||
1462 | if ch == ' ': | ||
1463 | indent_width += 1 | ||
1464 | elif ch == '\t': | ||
1465 | indent_width += tab_width - (indent_width % tab_width) | ||
1466 | return indent, indent_width | ||
1467 | |||
1468 | def _get_trailing_whitespace(marker, s): | ||
1469 | """Return the whitespace content trailing the given 'marker' in string 's', | ||
1470 | up to and including a newline. | ||
1471 | """ | ||
1472 | suffix = '' | ||
1473 | start = s.index(marker) + len(marker) | ||
1474 | i = start | ||
1475 | while i < len(s): | ||
1476 | if s[i] in ' \t': | ||
1477 | suffix += s[i] | ||
1478 | elif s[i] in '\r\n': | ||
1479 | suffix += s[i] | ||
1480 | if s[i] == '\r' and i+1 < len(s) and s[i+1] == '\n': | ||
1481 | suffix += s[i+1] | ||
1482 | break | ||
1483 | else: | ||
1484 | break | ||
1485 | i += 1 | ||
1486 | return suffix | ||
1487 | |||
1488 | |||
1489 | |||
1490 | #---- bash completion support | ||
1491 | # Note: This is still experimental. I expect to change this | ||
1492 | # significantly. | ||
1493 | # | ||
1494 | # To get Bash completion for a cmdln.Cmdln class, run the following | ||
1495 | # bash command: | ||
1496 | # $ complete -C 'python -m cmdln /path/to/script.py CmdlnClass' cmdname | ||
1497 | # For example: | ||
1498 | # $ complete -C 'python -m cmdln ~/bin/svn.py SVN' svn | ||
1499 | # | ||
1500 | #TODO: Simplify the above so don't have to given path to script (try to | ||
1501 | # find it on PATH, if possible). Could also make class name | ||
1502 | # optional if there is only one in the module (common case). | ||
1503 | |||
1504 | if __name__ == "__main__" and len(sys.argv) == 6: | ||
1505 | def _log(s): | ||
1506 | return # no-op, comment out for debugging | ||
1507 | from os.path import expanduser | ||
1508 | fout = open(expanduser("~/tmp/bashcpln.log"), 'a') | ||
1509 | fout.write(str(s) + '\n') | ||
1510 | fout.close() | ||
1511 | |||
1512 | # Recipe: module_from_path (1.0.1+) | ||
1513 | def _module_from_path(path): | ||
1514 | import imp, os, sys | ||
1515 | path = os.path.expanduser(path) | ||
1516 | dir = os.path.dirname(path) or os.curdir | ||
1517 | name = os.path.splitext(os.path.basename(path))[0] | ||
1518 | sys.path.insert(0, dir) | ||
1519 | try: | ||
1520 | iinfo = imp.find_module(name, [dir]) | ||
1521 | return imp.load_module(name, *iinfo) | ||
1522 | finally: | ||
1523 | sys.path.remove(dir) | ||
1524 | |||
1525 | def _get_bash_cplns(script_path, class_name, cmd_name, | ||
1526 | token, preceding_token): | ||
1527 | _log('--') | ||
1528 | _log('get_cplns(%r, %r, %r, %r, %r)' | ||
1529 | % (script_path, class_name, cmd_name, token, preceding_token)) | ||
1530 | comp_line = os.environ["COMP_LINE"] | ||
1531 | comp_point = int(os.environ["COMP_POINT"]) | ||
1532 | _log("COMP_LINE: %r" % comp_line) | ||
1533 | _log("COMP_POINT: %r" % comp_point) | ||
1534 | |||
1535 | try: | ||
1536 | script = _module_from_path(script_path) | ||
1537 | except ImportError, ex: | ||
1538 | _log("error importing `%s': %s" % (script_path, ex)) | ||
1539 | return [] | ||
1540 | shell = getattr(script, class_name)() | ||
1541 | cmd_map = shell._get_canonical_map() | ||
1542 | del cmd_map["EOF"] | ||
1543 | |||
1544 | # Determine if completing the sub-command name. | ||
1545 | parts = comp_line[:comp_point].split(None, 1) | ||
1546 | _log(parts) | ||
1547 | if len(parts) == 1 or not (' ' in parts[1] or '\t' in parts[1]): | ||
1548 | #TODO: if parts[1].startswith('-'): handle top-level opts | ||
1549 | _log("complete sub-command names") | ||
1550 | matches = {} | ||
1551 | for name, canon_name in cmd_map.items(): | ||
1552 | if name.startswith(token): | ||
1553 | matches[name] = canon_name | ||
1554 | if not matches: | ||
1555 | return [] | ||
1556 | elif len(matches) == 1: | ||
1557 | return matches.keys() | ||
1558 | elif len(set(matches.values())) == 1: | ||
1559 | return [matches.values()[0]] | ||
1560 | else: | ||
1561 | return matches.keys() | ||
1562 | |||
1563 | # Otherwise, complete options for the given sub-command. | ||
1564 | #TODO: refine this so it does the right thing with option args | ||
1565 | if token.startswith('-'): | ||
1566 | cmd_name = comp_line.split(None, 2)[1] | ||
1567 | try: | ||
1568 | cmd_canon_name = cmd_map[cmd_name] | ||
1569 | except KeyError: | ||
1570 | return [] | ||
1571 | handler = shell._get_cmd_handler(cmd_canon_name) | ||
1572 | optparser = getattr(handler, "optparser", None) | ||
1573 | if optparser is None: | ||
1574 | optparser = SubCmdOptionParser() | ||
1575 | opt_strs = [] | ||
1576 | for option in optparser.option_list: | ||
1577 | for opt_str in option._short_opts + option._long_opts: | ||
1578 | if opt_str.startswith(token): | ||
1579 | opt_strs.append(opt_str) | ||
1580 | return opt_strs | ||
1581 | |||
1582 | return [] | ||
1583 | |||
1584 | for cpln in _get_bash_cplns(*sys.argv[1:]): | ||
1585 | print cpln | ||
1586 | |||
diff --git a/scripts/lib/mic/utils/errors.py b/scripts/lib/mic/utils/errors.py new file mode 100644 index 0000000000..8d720f9080 --- /dev/null +++ b/scripts/lib/mic/utils/errors.py | |||
@@ -0,0 +1,71 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2007 Red Hat, Inc. | ||
4 | # Copyright (c) 2011 Intel, Inc. | ||
5 | # | ||
6 | # This program is free software; you can redistribute it and/or modify it | ||
7 | # under the terms of the GNU General Public License as published by the Free | ||
8 | # Software Foundation; version 2 of the License | ||
9 | # | ||
10 | # This program is distributed in the hope that it will be useful, but | ||
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
12 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
13 | # for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License along | ||
16 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
17 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | |||
19 | class CreatorError(Exception): | ||
20 | """An exception base class for all imgcreate errors.""" | ||
21 | keyword = '<creator>' | ||
22 | |||
23 | def __init__(self, msg): | ||
24 | self.msg = msg | ||
25 | |||
26 | def __str__(self): | ||
27 | if isinstance(self.msg, unicode): | ||
28 | self.msg = self.msg.encode('utf-8', 'ignore') | ||
29 | else: | ||
30 | self.msg = str(self.msg) | ||
31 | return self.keyword + self.msg | ||
32 | |||
33 | class Usage(CreatorError): | ||
34 | keyword = '<usage>' | ||
35 | |||
36 | def __str__(self): | ||
37 | if isinstance(self.msg, unicode): | ||
38 | self.msg = self.msg.encode('utf-8', 'ignore') | ||
39 | else: | ||
40 | self.msg = str(self.msg) | ||
41 | return self.keyword + self.msg + ', please use "--help" for more info' | ||
42 | |||
43 | class Abort(CreatorError): | ||
44 | keyword = '' | ||
45 | |||
46 | class ConfigError(CreatorError): | ||
47 | keyword = '<config>' | ||
48 | |||
49 | class KsError(CreatorError): | ||
50 | keyword = '<kickstart>' | ||
51 | |||
52 | class RepoError(CreatorError): | ||
53 | keyword = '<repo>' | ||
54 | |||
55 | class RpmError(CreatorError): | ||
56 | keyword = '<rpm>' | ||
57 | |||
58 | class MountError(CreatorError): | ||
59 | keyword = '<mount>' | ||
60 | |||
61 | class SnapshotError(CreatorError): | ||
62 | keyword = '<snapshot>' | ||
63 | |||
64 | class SquashfsError(CreatorError): | ||
65 | keyword = '<squashfs>' | ||
66 | |||
67 | class BootstrapError(CreatorError): | ||
68 | keyword = '<bootstrap>' | ||
69 | |||
70 | class RuntimeError(CreatorError): | ||
71 | keyword = '<runtime>' | ||
diff --git a/scripts/lib/mic/utils/fs_related.py b/scripts/lib/mic/utils/fs_related.py new file mode 100644 index 0000000000..dd420e88dc --- /dev/null +++ b/scripts/lib/mic/utils/fs_related.py | |||
@@ -0,0 +1,1060 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2007, Red Hat, Inc. | ||
4 | # Copyright (c) 2009, 2010, 2011 Intel, Inc. | ||
5 | # | ||
6 | # This program is free software; you can redistribute it and/or modify it | ||
7 | # under the terms of the GNU General Public License as published by the Free | ||
8 | # Software Foundation; version 2 of the License | ||
9 | # | ||
10 | # This program is distributed in the hope that it will be useful, but | ||
11 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
12 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
13 | # for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License along | ||
16 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
17 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | |||
19 | from __future__ import with_statement | ||
20 | import os | ||
21 | import sys | ||
22 | import errno | ||
23 | import stat | ||
24 | import random | ||
25 | import string | ||
26 | import time | ||
27 | import uuid | ||
28 | |||
29 | from mic import msger | ||
30 | from mic.utils import runner | ||
31 | from mic.utils.errors import * | ||
32 | from mic.utils.oe.misc import * | ||
33 | |||
34 | def find_binary_inchroot(binary, chroot): | ||
35 | paths = ["/usr/sbin", | ||
36 | "/usr/bin", | ||
37 | "/sbin", | ||
38 | "/bin" | ||
39 | ] | ||
40 | |||
41 | for path in paths: | ||
42 | bin_path = "%s/%s" % (path, binary) | ||
43 | if os.path.exists("%s/%s" % (chroot, bin_path)): | ||
44 | return bin_path | ||
45 | return None | ||
46 | |||
47 | def find_binary_path(binary): | ||
48 | if os.environ.has_key("PATH"): | ||
49 | paths = os.environ["PATH"].split(":") | ||
50 | else: | ||
51 | paths = [] | ||
52 | if os.environ.has_key("HOME"): | ||
53 | paths += [os.environ["HOME"] + "/bin"] | ||
54 | paths += ["/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin", "/sbin", "/bin"] | ||
55 | |||
56 | for path in paths: | ||
57 | bin_path = "%s/%s" % (path, binary) | ||
58 | if os.path.exists(bin_path): | ||
59 | return bin_path | ||
60 | |||
61 | print "External command '%s' not found, exiting." % binary | ||
62 | print " (Please install '%s' on your host system)" % binary | ||
63 | sys.exit(1) | ||
64 | |||
65 | def makedirs(dirname): | ||
66 | """A version of os.makedirs() that doesn't throw an | ||
67 | exception if the leaf directory already exists. | ||
68 | """ | ||
69 | try: | ||
70 | os.makedirs(dirname) | ||
71 | except OSError, err: | ||
72 | if err.errno != errno.EEXIST: | ||
73 | raise | ||
74 | |||
75 | def mksquashfs(in_img, out_img): | ||
76 | fullpathmksquashfs = find_binary_path("mksquashfs") | ||
77 | args = [fullpathmksquashfs, in_img, out_img] | ||
78 | |||
79 | if not sys.stdout.isatty(): | ||
80 | args.append("-no-progress") | ||
81 | |||
82 | ret = runner.show(args) | ||
83 | if ret != 0: | ||
84 | raise SquashfsError("'%s' exited with error (%d)" % (' '.join(args), ret)) | ||
85 | |||
86 | def resize2fs(fs, size): | ||
87 | resize2fs = find_binary_path("resize2fs") | ||
88 | if size == 0: | ||
89 | # it means to minimalize it | ||
90 | return runner.show([resize2fs, '-M', fs]) | ||
91 | else: | ||
92 | return runner.show([resize2fs, fs, "%sK" % (size / 1024,)]) | ||
93 | |||
94 | def my_fuser(fp): | ||
95 | fuser = find_binary_path("fuser") | ||
96 | if not os.path.exists(fp): | ||
97 | return False | ||
98 | |||
99 | rc = runner.quiet([fuser, "-s", fp]) | ||
100 | if rc == 0: | ||
101 | for pid in runner.outs([fuser, fp]).split(): | ||
102 | fd = open("/proc/%s/cmdline" % pid, "r") | ||
103 | cmdline = fd.read() | ||
104 | fd.close() | ||
105 | if cmdline[:-1] == "/bin/bash": | ||
106 | return True | ||
107 | |||
108 | # not found | ||
109 | return False | ||
110 | |||
111 | class BindChrootMount: | ||
112 | """Represents a bind mount of a directory into a chroot.""" | ||
113 | def __init__(self, src, chroot, dest = None, option = None): | ||
114 | self.root = os.path.abspath(os.path.expanduser(chroot)) | ||
115 | self.option = option | ||
116 | |||
117 | self.orig_src = self.src = src | ||
118 | if os.path.islink(src): | ||
119 | self.src = os.readlink(src) | ||
120 | if not self.src.startswith('/'): | ||
121 | self.src = os.path.abspath(os.path.join(os.path.dirname(src), | ||
122 | self.src)) | ||
123 | |||
124 | if not dest: | ||
125 | dest = self.src | ||
126 | self.dest = os.path.join(self.root, dest.lstrip('/')) | ||
127 | |||
128 | self.mounted = False | ||
129 | self.mountcmd = find_binary_path("mount") | ||
130 | self.umountcmd = find_binary_path("umount") | ||
131 | |||
132 | def ismounted(self): | ||
133 | with open('/proc/mounts') as f: | ||
134 | for line in f: | ||
135 | if line.split()[1] == os.path.abspath(self.dest): | ||
136 | return True | ||
137 | |||
138 | return False | ||
139 | |||
140 | def has_chroot_instance(self): | ||
141 | lock = os.path.join(self.root, ".chroot.lock") | ||
142 | return my_fuser(lock) | ||
143 | |||
144 | def mount(self): | ||
145 | if self.mounted or self.ismounted(): | ||
146 | return | ||
147 | |||
148 | makedirs(self.dest) | ||
149 | rc = runner.show([self.mountcmd, "--bind", self.src, self.dest]) | ||
150 | if rc != 0: | ||
151 | raise MountError("Bind-mounting '%s' to '%s' failed" % | ||
152 | (self.src, self.dest)) | ||
153 | if self.option: | ||
154 | rc = runner.show([self.mountcmd, "--bind", "-o", "remount,%s" % self.option, self.dest]) | ||
155 | if rc != 0: | ||
156 | raise MountError("Bind-remounting '%s' failed" % self.dest) | ||
157 | |||
158 | self.mounted = True | ||
159 | if os.path.islink(self.orig_src): | ||
160 | dest = os.path.join(self.root, self.orig_src.lstrip('/')) | ||
161 | if not os.path.exists(dest): | ||
162 | os.symlink(self.src, dest) | ||
163 | |||
164 | def unmount(self): | ||
165 | if self.has_chroot_instance(): | ||
166 | return | ||
167 | |||
168 | if self.ismounted(): | ||
169 | runner.show([self.umountcmd, "-l", self.dest]) | ||
170 | self.mounted = False | ||
171 | |||
172 | class LoopbackMount: | ||
173 | """LoopbackMount compatibility layer for old API""" | ||
174 | def __init__(self, lofile, mountdir, fstype = None): | ||
175 | self.diskmount = DiskMount(LoopbackDisk(lofile,size = 0),mountdir,fstype,rmmountdir = True) | ||
176 | self.losetup = False | ||
177 | self.losetupcmd = find_binary_path("losetup") | ||
178 | |||
179 | def cleanup(self): | ||
180 | self.diskmount.cleanup() | ||
181 | |||
182 | def unmount(self): | ||
183 | self.diskmount.unmount() | ||
184 | |||
185 | def lounsetup(self): | ||
186 | if self.losetup: | ||
187 | runner.show([self.losetupcmd, "-d", self.loopdev]) | ||
188 | self.losetup = False | ||
189 | self.loopdev = None | ||
190 | |||
191 | def loopsetup(self): | ||
192 | if self.losetup: | ||
193 | return | ||
194 | |||
195 | self.loopdev = get_loop_device(self.losetupcmd, self.lofile) | ||
196 | self.losetup = True | ||
197 | |||
198 | def mount(self): | ||
199 | self.diskmount.mount() | ||
200 | |||
201 | class SparseLoopbackMount(LoopbackMount): | ||
202 | """SparseLoopbackMount compatibility layer for old API""" | ||
203 | def __init__(self, lofile, mountdir, size, fstype = None): | ||
204 | self.diskmount = DiskMount(SparseLoopbackDisk(lofile,size),mountdir,fstype,rmmountdir = True) | ||
205 | |||
206 | def expand(self, create = False, size = None): | ||
207 | self.diskmount.disk.expand(create, size) | ||
208 | |||
209 | def truncate(self, size = None): | ||
210 | self.diskmount.disk.truncate(size) | ||
211 | |||
212 | def create(self): | ||
213 | self.diskmount.disk.create() | ||
214 | |||
215 | class SparseExtLoopbackMount(SparseLoopbackMount): | ||
216 | """SparseExtLoopbackMount compatibility layer for old API""" | ||
217 | def __init__(self, lofile, mountdir, size, fstype, blocksize, fslabel): | ||
218 | self.diskmount = ExtDiskMount(SparseLoopbackDisk(lofile,size), mountdir, fstype, blocksize, fslabel, rmmountdir = True) | ||
219 | |||
220 | |||
221 | def __format_filesystem(self): | ||
222 | self.diskmount.__format_filesystem() | ||
223 | |||
224 | def create(self): | ||
225 | self.diskmount.disk.create() | ||
226 | |||
227 | def resize(self, size = None): | ||
228 | return self.diskmount.__resize_filesystem(size) | ||
229 | |||
230 | def mount(self): | ||
231 | self.diskmount.mount() | ||
232 | |||
233 | def __fsck(self): | ||
234 | self.extdiskmount.__fsck() | ||
235 | |||
236 | def __get_size_from_filesystem(self): | ||
237 | return self.diskmount.__get_size_from_filesystem() | ||
238 | |||
239 | def __resize_to_minimal(self): | ||
240 | return self.diskmount.__resize_to_minimal() | ||
241 | |||
242 | def resparse(self, size = None): | ||
243 | return self.diskmount.resparse(size) | ||
244 | |||
245 | class Disk: | ||
246 | """Generic base object for a disk | ||
247 | |||
248 | The 'create' method must make the disk visible as a block device - eg | ||
249 | by calling losetup. For RawDisk, this is obviously a no-op. The 'cleanup' | ||
250 | method must undo the 'create' operation. | ||
251 | """ | ||
252 | def __init__(self, size, device = None): | ||
253 | self._device = device | ||
254 | self._size = size | ||
255 | |||
256 | def create(self): | ||
257 | pass | ||
258 | |||
259 | def cleanup(self): | ||
260 | pass | ||
261 | |||
262 | def get_device(self): | ||
263 | return self._device | ||
264 | def set_device(self, path): | ||
265 | self._device = path | ||
266 | device = property(get_device, set_device) | ||
267 | |||
268 | def get_size(self): | ||
269 | return self._size | ||
270 | size = property(get_size) | ||
271 | |||
272 | |||
273 | class RawDisk(Disk): | ||
274 | """A Disk backed by a block device. | ||
275 | Note that create() is a no-op. | ||
276 | """ | ||
277 | def __init__(self, size, device): | ||
278 | Disk.__init__(self, size, device) | ||
279 | |||
280 | def fixed(self): | ||
281 | return True | ||
282 | |||
283 | def exists(self): | ||
284 | return True | ||
285 | |||
286 | |||
287 | class DiskImage(Disk): | ||
288 | """ | ||
289 | A Disk backed by a file. | ||
290 | """ | ||
291 | def __init__(self, image_file, size): | ||
292 | Disk.__init__(self, size) | ||
293 | self.image_file = image_file | ||
294 | |||
295 | def exists(self): | ||
296 | return os.path.exists(self.image_file) | ||
297 | |||
298 | def create(self): | ||
299 | if self.device is not None: | ||
300 | return | ||
301 | |||
302 | blocks = self.size / 1024 | ||
303 | if self.size - blocks * 1024: | ||
304 | blocks += 1 | ||
305 | |||
306 | # create disk image | ||
307 | dd_cmd = "dd if=/dev/zero of=%s bs=1024 seek=%d count=1" % \ | ||
308 | (self.image_file, blocks) | ||
309 | rc, out = exec_cmd(dd_cmd) | ||
310 | |||
311 | self.device = self.image_file | ||
312 | |||
313 | |||
314 | class LoopbackDisk(Disk): | ||
315 | """A Disk backed by a file via the loop module.""" | ||
316 | def __init__(self, lofile, size): | ||
317 | Disk.__init__(self, size) | ||
318 | self.lofile = lofile | ||
319 | self.losetupcmd = find_binary_path("losetup") | ||
320 | |||
321 | def fixed(self): | ||
322 | return False | ||
323 | |||
324 | def exists(self): | ||
325 | return os.path.exists(self.lofile) | ||
326 | |||
327 | def create(self): | ||
328 | if self.device is not None: | ||
329 | return | ||
330 | |||
331 | self.device = get_loop_device(self.losetupcmd, self.lofile) | ||
332 | |||
333 | def cleanup(self): | ||
334 | if self.device is None: | ||
335 | return | ||
336 | msger.debug("Losetup remove %s" % self.device) | ||
337 | rc = runner.show([self.losetupcmd, "-d", self.device]) | ||
338 | self.device = None | ||
339 | |||
340 | class SparseLoopbackDisk(LoopbackDisk): | ||
341 | """A Disk backed by a sparse file via the loop module.""" | ||
342 | def __init__(self, lofile, size): | ||
343 | LoopbackDisk.__init__(self, lofile, size) | ||
344 | |||
345 | def expand(self, create = False, size = None): | ||
346 | flags = os.O_WRONLY | ||
347 | if create: | ||
348 | flags |= os.O_CREAT | ||
349 | if not os.path.exists(self.lofile): | ||
350 | makedirs(os.path.dirname(self.lofile)) | ||
351 | |||
352 | if size is None: | ||
353 | size = self.size | ||
354 | |||
355 | msger.debug("Extending sparse file %s to %d" % (self.lofile, size)) | ||
356 | if create: | ||
357 | fd = os.open(self.lofile, flags, 0644) | ||
358 | else: | ||
359 | fd = os.open(self.lofile, flags) | ||
360 | |||
361 | if size <= 0: | ||
362 | size = 1 | ||
363 | try: | ||
364 | os.ftruncate(fd, size) | ||
365 | except: | ||
366 | # may be limited by 2G in 32bit env | ||
367 | os.ftruncate(fd, 2**31L) | ||
368 | |||
369 | os.close(fd) | ||
370 | |||
371 | def truncate(self, size = None): | ||
372 | if size is None: | ||
373 | size = self.size | ||
374 | |||
375 | msger.debug("Truncating sparse file %s to %d" % (self.lofile, size)) | ||
376 | fd = os.open(self.lofile, os.O_WRONLY) | ||
377 | os.ftruncate(fd, size) | ||
378 | os.close(fd) | ||
379 | |||
380 | def create(self): | ||
381 | self.expand(create = True) | ||
382 | LoopbackDisk.create(self) | ||
383 | |||
384 | class Mount: | ||
385 | """A generic base class to deal with mounting things.""" | ||
386 | def __init__(self, mountdir): | ||
387 | self.mountdir = mountdir | ||
388 | |||
389 | def cleanup(self): | ||
390 | self.unmount() | ||
391 | |||
392 | def mount(self, options = None): | ||
393 | pass | ||
394 | |||
395 | def unmount(self): | ||
396 | pass | ||
397 | |||
398 | class DiskMount(Mount): | ||
399 | """A Mount object that handles mounting of a Disk.""" | ||
400 | def __init__(self, disk, mountdir, fstype = None, rmmountdir = True): | ||
401 | Mount.__init__(self, mountdir) | ||
402 | |||
403 | self.disk = disk | ||
404 | self.fstype = fstype | ||
405 | self.rmmountdir = rmmountdir | ||
406 | |||
407 | self.mounted = False | ||
408 | self.rmdir = False | ||
409 | if fstype: | ||
410 | self.mkfscmd = find_binary_path("mkfs." + self.fstype) | ||
411 | else: | ||
412 | self.mkfscmd = None | ||
413 | self.mountcmd = find_binary_path("mount") | ||
414 | self.umountcmd = find_binary_path("umount") | ||
415 | |||
416 | def cleanup(self): | ||
417 | Mount.cleanup(self) | ||
418 | self.disk.cleanup() | ||
419 | |||
420 | def unmount(self): | ||
421 | if self.mounted: | ||
422 | msger.debug("Unmounting directory %s" % self.mountdir) | ||
423 | runner.quiet('sync') # sync the data on this mount point | ||
424 | rc = runner.show([self.umountcmd, "-l", self.mountdir]) | ||
425 | if rc == 0: | ||
426 | self.mounted = False | ||
427 | else: | ||
428 | raise MountError("Failed to umount %s" % self.mountdir) | ||
429 | if self.rmdir and not self.mounted: | ||
430 | try: | ||
431 | os.rmdir(self.mountdir) | ||
432 | except OSError, e: | ||
433 | pass | ||
434 | self.rmdir = False | ||
435 | |||
436 | |||
437 | def __create(self): | ||
438 | self.disk.create() | ||
439 | |||
440 | |||
441 | def mount(self, options = None): | ||
442 | if self.mounted: | ||
443 | return | ||
444 | |||
445 | if not os.path.isdir(self.mountdir): | ||
446 | msger.debug("Creating mount point %s" % self.mountdir) | ||
447 | os.makedirs(self.mountdir) | ||
448 | self.rmdir = self.rmmountdir | ||
449 | |||
450 | self.__create() | ||
451 | |||
452 | msger.debug("Mounting %s at %s" % (self.disk.device, self.mountdir)) | ||
453 | if options: | ||
454 | args = [ self.mountcmd, "-o", options, self.disk.device, self.mountdir ] | ||
455 | else: | ||
456 | args = [ self.mountcmd, self.disk.device, self.mountdir ] | ||
457 | if self.fstype: | ||
458 | args.extend(["-t", self.fstype]) | ||
459 | |||
460 | rc = runner.show(args) | ||
461 | if rc != 0: | ||
462 | raise MountError("Failed to mount '%s' to '%s' with command '%s'. Retval: %s" % | ||
463 | (self.disk.device, self.mountdir, " ".join(args), rc)) | ||
464 | |||
465 | self.mounted = True | ||
466 | |||
467 | class ExtDiskMount(DiskMount): | ||
468 | """A DiskMount object that is able to format/resize ext[23] filesystems.""" | ||
469 | def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None): | ||
470 | DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir) | ||
471 | self.blocksize = blocksize | ||
472 | self.fslabel = fslabel.replace("/", "") | ||
473 | self.uuid = str(uuid.uuid4()) | ||
474 | self.skipformat = skipformat | ||
475 | self.fsopts = fsopts | ||
476 | self.extopts = None | ||
477 | self.dumpe2fs = find_binary_path("dumpe2fs") | ||
478 | self.tune2fs = find_binary_path("tune2fs") | ||
479 | |||
480 | def __parse_field(self, output, field): | ||
481 | for line in output.split("\n"): | ||
482 | if line.startswith(field + ":"): | ||
483 | return line[len(field) + 1:].strip() | ||
484 | |||
485 | raise KeyError("Failed to find field '%s' in output" % field) | ||
486 | |||
487 | def __format_filesystem(self): | ||
488 | if self.skipformat: | ||
489 | msger.debug("Skip filesystem format.") | ||
490 | return | ||
491 | |||
492 | msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device)) | ||
493 | cmdlist = [self.mkfscmd, "-F", "-L", self.fslabel, "-m", "1", "-b", | ||
494 | str(self.blocksize), "-U", self.uuid] | ||
495 | if self.extopts: | ||
496 | cmdlist.extend(self.extopts.split()) | ||
497 | cmdlist.extend([self.disk.device]) | ||
498 | |||
499 | rc, errout = runner.runtool(cmdlist, catch=2) | ||
500 | if rc != 0: | ||
501 | raise MountError("Error creating %s filesystem on disk %s:\n%s" % | ||
502 | (self.fstype, self.disk.device, errout)) | ||
503 | |||
504 | if not self.extopts: | ||
505 | msger.debug("Tuning filesystem on %s" % self.disk.device) | ||
506 | runner.show([self.tune2fs, "-c0", "-i0", "-Odir_index", "-ouser_xattr,acl", self.disk.device]) | ||
507 | |||
508 | def __resize_filesystem(self, size = None): | ||
509 | current_size = os.stat(self.disk.lofile)[stat.ST_SIZE] | ||
510 | |||
511 | if size is None: | ||
512 | size = self.disk.size | ||
513 | |||
514 | if size == current_size: | ||
515 | return | ||
516 | |||
517 | if size > current_size: | ||
518 | self.disk.expand(size) | ||
519 | |||
520 | self.__fsck() | ||
521 | |||
522 | resize2fs(self.disk.lofile, size) | ||
523 | return size | ||
524 | |||
525 | def __create(self): | ||
526 | resize = False | ||
527 | if not self.disk.fixed() and self.disk.exists(): | ||
528 | resize = True | ||
529 | |||
530 | self.disk.create() | ||
531 | |||
532 | if resize: | ||
533 | self.__resize_filesystem() | ||
534 | else: | ||
535 | self.__format_filesystem() | ||
536 | |||
537 | def mount(self, options = None): | ||
538 | self.__create() | ||
539 | DiskMount.mount(self, options) | ||
540 | |||
541 | def __fsck(self): | ||
542 | msger.info("Checking filesystem %s" % self.disk.lofile) | ||
543 | runner.quiet(["/sbin/e2fsck", "-f", "-y", self.disk.lofile]) | ||
544 | |||
545 | def __get_size_from_filesystem(self): | ||
546 | return int(self.__parse_field(runner.outs([self.dumpe2fs, '-h', self.disk.lofile]), | ||
547 | "Block count")) * self.blocksize | ||
548 | |||
549 | def __resize_to_minimal(self): | ||
550 | self.__fsck() | ||
551 | |||
552 | # | ||
553 | # Use a binary search to find the minimal size | ||
554 | # we can resize the image to | ||
555 | # | ||
556 | bot = 0 | ||
557 | top = self.__get_size_from_filesystem() | ||
558 | while top != (bot + 1): | ||
559 | t = bot + ((top - bot) / 2) | ||
560 | |||
561 | if not resize2fs(self.disk.lofile, t): | ||
562 | top = t | ||
563 | else: | ||
564 | bot = t | ||
565 | return top | ||
566 | |||
567 | def resparse(self, size = None): | ||
568 | self.cleanup() | ||
569 | if size == 0: | ||
570 | minsize = 0 | ||
571 | else: | ||
572 | minsize = self.__resize_to_minimal() | ||
573 | self.disk.truncate(minsize) | ||
574 | |||
575 | self.__resize_filesystem(size) | ||
576 | return minsize | ||
577 | |||
578 | class VfatDiskMount(DiskMount): | ||
579 | """A DiskMount object that is able to format vfat/msdos filesystems.""" | ||
580 | def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None): | ||
581 | DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir) | ||
582 | self.blocksize = blocksize | ||
583 | self.fslabel = fslabel.replace("/", "") | ||
584 | rand1 = random.randint(0, 2**16 - 1) | ||
585 | rand2 = random.randint(0, 2**16 - 1) | ||
586 | self.uuid = "%04X-%04X" % (rand1, rand2) | ||
587 | self.skipformat = skipformat | ||
588 | self.fsopts = fsopts | ||
589 | self.fsckcmd = find_binary_path("fsck." + self.fstype) | ||
590 | |||
591 | def __format_filesystem(self): | ||
592 | if self.skipformat: | ||
593 | msger.debug("Skip filesystem format.") | ||
594 | return | ||
595 | |||
596 | msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device)) | ||
597 | rc = runner.show([self.mkfscmd, "-n", self.fslabel, | ||
598 | "-i", self.uuid.replace("-", ""), self.disk.device]) | ||
599 | if rc != 0: | ||
600 | raise MountError("Error creating %s filesystem on disk %s" % (self.fstype,self.disk.device)) | ||
601 | |||
602 | msger.verbose("Tuning filesystem on %s" % self.disk.device) | ||
603 | |||
604 | def __resize_filesystem(self, size = None): | ||
605 | current_size = os.stat(self.disk.lofile)[stat.ST_SIZE] | ||
606 | |||
607 | if size is None: | ||
608 | size = self.disk.size | ||
609 | |||
610 | if size == current_size: | ||
611 | return | ||
612 | |||
613 | if size > current_size: | ||
614 | self.disk.expand(size) | ||
615 | |||
616 | self.__fsck() | ||
617 | |||
618 | #resize2fs(self.disk.lofile, size) | ||
619 | return size | ||
620 | |||
621 | def __create(self): | ||
622 | resize = False | ||
623 | if not self.disk.fixed() and self.disk.exists(): | ||
624 | resize = True | ||
625 | |||
626 | self.disk.create() | ||
627 | |||
628 | if resize: | ||
629 | self.__resize_filesystem() | ||
630 | else: | ||
631 | self.__format_filesystem() | ||
632 | |||
633 | def mount(self, options = None): | ||
634 | self.__create() | ||
635 | DiskMount.mount(self, options) | ||
636 | |||
637 | def __fsck(self): | ||
638 | msger.debug("Checking filesystem %s" % self.disk.lofile) | ||
639 | runner.show([self.fsckcmd, "-y", self.disk.lofile]) | ||
640 | |||
641 | def __get_size_from_filesystem(self): | ||
642 | return self.disk.size | ||
643 | |||
644 | def __resize_to_minimal(self): | ||
645 | self.__fsck() | ||
646 | |||
647 | # | ||
648 | # Use a binary search to find the minimal size | ||
649 | # we can resize the image to | ||
650 | # | ||
651 | bot = 0 | ||
652 | top = self.__get_size_from_filesystem() | ||
653 | return top | ||
654 | |||
655 | def resparse(self, size = None): | ||
656 | self.cleanup() | ||
657 | minsize = self.__resize_to_minimal() | ||
658 | self.disk.truncate(minsize) | ||
659 | self.__resize_filesystem(size) | ||
660 | return minsize | ||
661 | |||
662 | class BtrfsDiskMount(DiskMount): | ||
663 | """A DiskMount object that is able to format/resize btrfs filesystems.""" | ||
664 | def __init__(self, disk, mountdir, fstype, blocksize, fslabel, rmmountdir=True, skipformat = False, fsopts = None): | ||
665 | self.__check_btrfs() | ||
666 | DiskMount.__init__(self, disk, mountdir, fstype, rmmountdir) | ||
667 | self.blocksize = blocksize | ||
668 | self.fslabel = fslabel.replace("/", "") | ||
669 | self.uuid = None | ||
670 | self.skipformat = skipformat | ||
671 | self.fsopts = fsopts | ||
672 | self.blkidcmd = find_binary_path("blkid") | ||
673 | self.btrfsckcmd = find_binary_path("btrfsck") | ||
674 | |||
675 | def __check_btrfs(self): | ||
676 | found = False | ||
677 | """ Need to load btrfs module to mount it """ | ||
678 | load_module("btrfs") | ||
679 | for line in open("/proc/filesystems").xreadlines(): | ||
680 | if line.find("btrfs") > -1: | ||
681 | found = True | ||
682 | break | ||
683 | if not found: | ||
684 | raise MountError("Your system can't mount btrfs filesystem, please make sure your kernel has btrfs support and the module btrfs.ko has been loaded.") | ||
685 | |||
686 | # disable selinux, selinux will block write | ||
687 | if os.path.exists("/usr/sbin/setenforce"): | ||
688 | runner.show(["/usr/sbin/setenforce", "0"]) | ||
689 | |||
690 | def __parse_field(self, output, field): | ||
691 | for line in output.split(" "): | ||
692 | if line.startswith(field + "="): | ||
693 | return line[len(field) + 1:].strip().replace("\"", "") | ||
694 | |||
695 | raise KeyError("Failed to find field '%s' in output" % field) | ||
696 | |||
697 | def __format_filesystem(self): | ||
698 | if self.skipformat: | ||
699 | msger.debug("Skip filesystem format.") | ||
700 | return | ||
701 | |||
702 | msger.verbose("Formating %s filesystem on %s" % (self.fstype, self.disk.device)) | ||
703 | rc = runner.show([self.mkfscmd, "-L", self.fslabel, self.disk.device]) | ||
704 | if rc != 0: | ||
705 | raise MountError("Error creating %s filesystem on disk %s" % (self.fstype,self.disk.device)) | ||
706 | |||
707 | self.uuid = self.__parse_field(runner.outs([self.blkidcmd, self.disk.device]), "UUID") | ||
708 | |||
709 | def __resize_filesystem(self, size = None): | ||
710 | current_size = os.stat(self.disk.lofile)[stat.ST_SIZE] | ||
711 | |||
712 | if size is None: | ||
713 | size = self.disk.size | ||
714 | |||
715 | if size == current_size: | ||
716 | return | ||
717 | |||
718 | if size > current_size: | ||
719 | self.disk.expand(size) | ||
720 | |||
721 | self.__fsck() | ||
722 | return size | ||
723 | |||
724 | def __create(self): | ||
725 | resize = False | ||
726 | if not self.disk.fixed() and self.disk.exists(): | ||
727 | resize = True | ||
728 | |||
729 | self.disk.create() | ||
730 | |||
731 | if resize: | ||
732 | self.__resize_filesystem() | ||
733 | else: | ||
734 | self.__format_filesystem() | ||
735 | |||
736 | def mount(self, options = None): | ||
737 | self.__create() | ||
738 | DiskMount.mount(self, options) | ||
739 | |||
740 | def __fsck(self): | ||
741 | msger.debug("Checking filesystem %s" % self.disk.lofile) | ||
742 | runner.quiet([self.btrfsckcmd, self.disk.lofile]) | ||
743 | |||
744 | def __get_size_from_filesystem(self): | ||
745 | return self.disk.size | ||
746 | |||
747 | def __resize_to_minimal(self): | ||
748 | self.__fsck() | ||
749 | |||
750 | return self.__get_size_from_filesystem() | ||
751 | |||
752 | def resparse(self, size = None): | ||
753 | self.cleanup() | ||
754 | minsize = self.__resize_to_minimal() | ||
755 | self.disk.truncate(minsize) | ||
756 | self.__resize_filesystem(size) | ||
757 | return minsize | ||
758 | |||
759 | class DeviceMapperSnapshot(object): | ||
760 | def __init__(self, imgloop, cowloop): | ||
761 | self.imgloop = imgloop | ||
762 | self.cowloop = cowloop | ||
763 | |||
764 | self.__created = False | ||
765 | self.__name = None | ||
766 | self.dmsetupcmd = find_binary_path("dmsetup") | ||
767 | |||
768 | """Load dm_snapshot if it isn't loaded""" | ||
769 | load_module("dm_snapshot") | ||
770 | |||
771 | def get_path(self): | ||
772 | if self.__name is None: | ||
773 | return None | ||
774 | return os.path.join("/dev/mapper", self.__name) | ||
775 | path = property(get_path) | ||
776 | |||
777 | def create(self): | ||
778 | if self.__created: | ||
779 | return | ||
780 | |||
781 | self.imgloop.create() | ||
782 | self.cowloop.create() | ||
783 | |||
784 | self.__name = "imgcreate-%d-%d" % (os.getpid(), | ||
785 | random.randint(0, 2**16)) | ||
786 | |||
787 | size = os.stat(self.imgloop.lofile)[stat.ST_SIZE] | ||
788 | |||
789 | table = "0 %d snapshot %s %s p 8" % (size / 512, | ||
790 | self.imgloop.device, | ||
791 | self.cowloop.device) | ||
792 | |||
793 | args = [self.dmsetupcmd, "create", self.__name, "--table", table] | ||
794 | if runner.show(args) != 0: | ||
795 | self.cowloop.cleanup() | ||
796 | self.imgloop.cleanup() | ||
797 | raise SnapshotError("Could not create snapshot device using: " + ' '.join(args)) | ||
798 | |||
799 | self.__created = True | ||
800 | |||
801 | def remove(self, ignore_errors = False): | ||
802 | if not self.__created: | ||
803 | return | ||
804 | |||
805 | time.sleep(2) | ||
806 | rc = runner.show([self.dmsetupcmd, "remove", self.__name]) | ||
807 | if not ignore_errors and rc != 0: | ||
808 | raise SnapshotError("Could not remove snapshot device") | ||
809 | |||
810 | self.__name = None | ||
811 | self.__created = False | ||
812 | |||
813 | self.cowloop.cleanup() | ||
814 | self.imgloop.cleanup() | ||
815 | |||
816 | def get_cow_used(self): | ||
817 | if not self.__created: | ||
818 | return 0 | ||
819 | |||
820 | # | ||
821 | # dmsetup status on a snapshot returns e.g. | ||
822 | # "0 8388608 snapshot 416/1048576" | ||
823 | # or, more generally: | ||
824 | # "A B snapshot C/D" | ||
825 | # where C is the number of 512 byte sectors in use | ||
826 | # | ||
827 | out = runner.outs([self.dmsetupcmd, "status", self.__name]) | ||
828 | try: | ||
829 | return int((out.split()[3]).split('/')[0]) * 512 | ||
830 | except ValueError: | ||
831 | raise SnapshotError("Failed to parse dmsetup status: " + out) | ||
832 | |||
833 | def create_image_minimizer(path, image, minimal_size): | ||
834 | """ | ||
835 | Builds a copy-on-write image which can be used to | ||
836 | create a device-mapper snapshot of an image where | ||
837 | the image's filesystem is as small as possible | ||
838 | |||
839 | The steps taken are: | ||
840 | 1) Create a sparse COW | ||
841 | 2) Loopback mount the image and the COW | ||
842 | 3) Create a device-mapper snapshot of the image | ||
843 | using the COW | ||
844 | 4) Resize the filesystem to the minimal size | ||
845 | 5) Determine the amount of space used in the COW | ||
846 | 6) Restroy the device-mapper snapshot | ||
847 | 7) Truncate the COW, removing unused space | ||
848 | 8) Create a squashfs of the COW | ||
849 | """ | ||
850 | imgloop = LoopbackDisk(image, None) # Passing bogus size - doesn't matter | ||
851 | |||
852 | cowloop = SparseLoopbackDisk(os.path.join(os.path.dirname(path), "osmin"), | ||
853 | 64L * 1024L * 1024L) | ||
854 | |||
855 | snapshot = DeviceMapperSnapshot(imgloop, cowloop) | ||
856 | |||
857 | try: | ||
858 | snapshot.create() | ||
859 | |||
860 | resize2fs(snapshot.path, minimal_size) | ||
861 | |||
862 | cow_used = snapshot.get_cow_used() | ||
863 | finally: | ||
864 | snapshot.remove(ignore_errors = (not sys.exc_info()[0] is None)) | ||
865 | |||
866 | cowloop.truncate(cow_used) | ||
867 | |||
868 | mksquashfs(cowloop.lofile, path) | ||
869 | |||
870 | os.unlink(cowloop.lofile) | ||
871 | |||
872 | def load_module(module): | ||
873 | found = False | ||
874 | for line in open('/proc/modules').xreadlines(): | ||
875 | if line.startswith("%s " % module): | ||
876 | found = True | ||
877 | break | ||
878 | if not found: | ||
879 | msger.info("Loading %s..." % module) | ||
880 | runner.quiet(['modprobe', module]) | ||
881 | |||
882 | class LoopDevice(object): | ||
883 | def __init__(self, loopid=None): | ||
884 | self.device = None | ||
885 | self.loopid = loopid | ||
886 | self.created = False | ||
887 | self.kpartxcmd = find_binary_path("kpartx") | ||
888 | self.losetupcmd = find_binary_path("losetup") | ||
889 | |||
890 | def register(self, device): | ||
891 | self.device = device | ||
892 | self.loopid = None | ||
893 | self.created = True | ||
894 | |||
895 | def reg_atexit(self): | ||
896 | import atexit | ||
897 | atexit.register(self.close) | ||
898 | |||
899 | def _genloopid(self): | ||
900 | import glob | ||
901 | if not glob.glob("/dev/loop[0-9]*"): | ||
902 | return 10 | ||
903 | |||
904 | fint = lambda x: x[9:].isdigit() and int(x[9:]) or 0 | ||
905 | maxid = 1 + max(filter(lambda x: x<100, | ||
906 | map(fint, glob.glob("/dev/loop[0-9]*")))) | ||
907 | if maxid < 10: maxid = 10 | ||
908 | if maxid >= 100: raise | ||
909 | return maxid | ||
910 | |||
911 | def _kpseek(self, device): | ||
912 | rc, out = runner.runtool([self.kpartxcmd, '-l', '-v', device]) | ||
913 | if rc != 0: | ||
914 | raise MountError("Can't query dm snapshot on %s" % device) | ||
915 | for line in out.splitlines(): | ||
916 | if line and line.startswith("loop"): | ||
917 | return True | ||
918 | return False | ||
919 | |||
920 | def _loseek(self, device): | ||
921 | import re | ||
922 | rc, out = runner.runtool([self.losetupcmd, '-a']) | ||
923 | if rc != 0: | ||
924 | raise MountError("Failed to run 'losetup -a'") | ||
925 | for line in out.splitlines(): | ||
926 | m = re.match("([^:]+): .*", line) | ||
927 | if m and m.group(1) == device: | ||
928 | return True | ||
929 | return False | ||
930 | |||
931 | def create(self): | ||
932 | if not self.created: | ||
933 | if not self.loopid: | ||
934 | self.loopid = self._genloopid() | ||
935 | self.device = "/dev/loop%d" % self.loopid | ||
936 | if os.path.exists(self.device): | ||
937 | if self._loseek(self.device): | ||
938 | raise MountError("Device busy: %s" % self.device) | ||
939 | else: | ||
940 | self.created = True | ||
941 | return | ||
942 | |||
943 | mknod = find_binary_path('mknod') | ||
944 | rc = runner.show([mknod, '-m664', self.device, 'b', '7', str(self.loopid)]) | ||
945 | if rc != 0: | ||
946 | raise MountError("Failed to create device %s" % self.device) | ||
947 | else: | ||
948 | self.created = True | ||
949 | |||
950 | def close(self): | ||
951 | if self.created: | ||
952 | try: | ||
953 | self.cleanup() | ||
954 | self.device = None | ||
955 | except MountError, e: | ||
956 | msger.error("%s" % e) | ||
957 | |||
958 | def cleanup(self): | ||
959 | |||
960 | if self.device is None: | ||
961 | return | ||
962 | |||
963 | |||
964 | if self._kpseek(self.device): | ||
965 | if self.created: | ||
966 | for i in range(3, os.sysconf("SC_OPEN_MAX")): | ||
967 | try: | ||
968 | os.close(i) | ||
969 | except: | ||
970 | pass | ||
971 | runner.quiet([self.kpartxcmd, "-d", self.device]) | ||
972 | if self._loseek(self.device): | ||
973 | runner.quiet([self.losetupcmd, "-d", self.device]) | ||
974 | # FIXME: should sleep a while between two loseek | ||
975 | if self._loseek(self.device): | ||
976 | msger.warning("Can't cleanup loop device %s" % self.device) | ||
977 | elif self.loopid: | ||
978 | os.unlink(self.device) | ||
979 | |||
980 | DEVICE_PIDFILE_DIR = "/var/tmp/mic/device" | ||
981 | DEVICE_LOCKFILE = "/var/lock/__mic_loopdev.lock" | ||
982 | |||
983 | def get_loop_device(losetupcmd, lofile): | ||
984 | global DEVICE_PIDFILE_DIR | ||
985 | global DEVICE_LOCKFILE | ||
986 | |||
987 | import fcntl | ||
988 | makedirs(os.path.dirname(DEVICE_LOCKFILE)) | ||
989 | fp = open(DEVICE_LOCKFILE, 'w') | ||
990 | fcntl.flock(fp, fcntl.LOCK_EX) | ||
991 | try: | ||
992 | loopdev = None | ||
993 | devinst = LoopDevice() | ||
994 | |||
995 | # clean up left loop device first | ||
996 | clean_loop_devices() | ||
997 | |||
998 | # provide an avaible loop device | ||
999 | rc, out = runner.runtool([losetupcmd, "--find"]) | ||
1000 | if rc == 0: | ||
1001 | loopdev = out.split()[0] | ||
1002 | devinst.register(loopdev) | ||
1003 | if not loopdev or not os.path.exists(loopdev): | ||
1004 | devinst.create() | ||
1005 | loopdev = devinst.device | ||
1006 | |||
1007 | # setup a loop device for image file | ||
1008 | rc = runner.show([losetupcmd, loopdev, lofile]) | ||
1009 | if rc != 0: | ||
1010 | raise MountError("Failed to setup loop device for '%s'" % lofile) | ||
1011 | |||
1012 | devinst.reg_atexit() | ||
1013 | |||
1014 | # try to save device and pid | ||
1015 | makedirs(DEVICE_PIDFILE_DIR) | ||
1016 | pidfile = os.path.join(DEVICE_PIDFILE_DIR, os.path.basename(loopdev)) | ||
1017 | if os.path.exists(pidfile): | ||
1018 | os.unlink(pidfile) | ||
1019 | with open(pidfile, 'w') as wf: | ||
1020 | wf.write(str(os.getpid())) | ||
1021 | |||
1022 | except MountError, err: | ||
1023 | raise CreatorError("%s" % str(err)) | ||
1024 | except: | ||
1025 | raise | ||
1026 | finally: | ||
1027 | try: | ||
1028 | fcntl.flock(fp, fcntl.LOCK_UN) | ||
1029 | fp.close() | ||
1030 | os.unlink(DEVICE_LOCKFILE) | ||
1031 | except: | ||
1032 | pass | ||
1033 | |||
1034 | return loopdev | ||
1035 | |||
1036 | def clean_loop_devices(piddir=DEVICE_PIDFILE_DIR): | ||
1037 | if not os.path.exists(piddir) or not os.path.isdir(piddir): | ||
1038 | return | ||
1039 | |||
1040 | for loopdev in os.listdir(piddir): | ||
1041 | pidfile = os.path.join(piddir, loopdev) | ||
1042 | try: | ||
1043 | with open(pidfile, 'r') as rf: | ||
1044 | devpid = int(rf.read()) | ||
1045 | except: | ||
1046 | devpid = None | ||
1047 | |||
1048 | # if the process using this device is alive, skip it | ||
1049 | if not devpid or os.path.exists(os.path.join('/proc', str(devpid))): | ||
1050 | continue | ||
1051 | |||
1052 | # try to clean it up | ||
1053 | try: | ||
1054 | devinst = LoopDevice() | ||
1055 | devinst.register(os.path.join('/dev', loopdev)) | ||
1056 | devinst.cleanup() | ||
1057 | os.unlink(pidfile) | ||
1058 | except: | ||
1059 | pass | ||
1060 | |||
diff --git a/scripts/lib/mic/utils/gpt_parser.py b/scripts/lib/mic/utils/gpt_parser.py new file mode 100644 index 0000000000..5d43b70778 --- /dev/null +++ b/scripts/lib/mic/utils/gpt_parser.py | |||
@@ -0,0 +1,331 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2013 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | """ This module implements a simple GPT partitions parser which can read the | ||
19 | GPT header and the GPT partition table. """ | ||
20 | |||
21 | import struct | ||
22 | import uuid | ||
23 | import binascii | ||
24 | from mic.utils.errors import MountError | ||
25 | |||
26 | _GPT_HEADER_FORMAT = "<8s4sIIIQQQQ16sQIII" | ||
27 | _GPT_HEADER_SIZE = struct.calcsize(_GPT_HEADER_FORMAT) | ||
28 | _GPT_ENTRY_FORMAT = "<16s16sQQQ72s" | ||
29 | _GPT_ENTRY_SIZE = struct.calcsize(_GPT_ENTRY_FORMAT) | ||
30 | _SUPPORTED_GPT_REVISION = '\x00\x00\x01\x00' | ||
31 | |||
32 | def _stringify_uuid(binary_uuid): | ||
33 | """ A small helper function to transform a binary UUID into a string | ||
34 | format. """ | ||
35 | |||
36 | uuid_str = str(uuid.UUID(bytes_le = binary_uuid)) | ||
37 | |||
38 | return uuid_str.upper() | ||
39 | |||
40 | def _calc_header_crc(raw_hdr): | ||
41 | """ Calculate GPT header CRC32 checksum. The 'raw_hdr' parameter has to | ||
42 | be a list or a tuple containing all the elements of the GPT header in a | ||
43 | "raw" form, meaning that it should simply contain "unpacked" disk data. | ||
44 | """ | ||
45 | |||
46 | raw_hdr = list(raw_hdr) | ||
47 | raw_hdr[3] = 0 | ||
48 | raw_hdr = struct.pack(_GPT_HEADER_FORMAT, *raw_hdr) | ||
49 | |||
50 | return binascii.crc32(raw_hdr) & 0xFFFFFFFF | ||
51 | |||
52 | def _validate_header(raw_hdr): | ||
53 | """ Validate the GPT header. The 'raw_hdr' parameter has to be a list or a | ||
54 | tuple containing all the elements of the GPT header in a "raw" form, | ||
55 | meaning that it should simply contain "unpacked" disk data. """ | ||
56 | |||
57 | # Validate the signature | ||
58 | if raw_hdr[0] != 'EFI PART': | ||
59 | raise MountError("GPT partition table not found") | ||
60 | |||
61 | # Validate the revision | ||
62 | if raw_hdr[1] != _SUPPORTED_GPT_REVISION: | ||
63 | raise MountError("Unsupported GPT revision '%s', supported revision " \ | ||
64 | "is '%s'" % \ | ||
65 | (binascii.hexlify(raw_hdr[1]), | ||
66 | binascii.hexlify(_SUPPORTED_GPT_REVISION))) | ||
67 | |||
68 | # Validate header size | ||
69 | if raw_hdr[2] != _GPT_HEADER_SIZE: | ||
70 | raise MountError("Bad GPT header size: %d bytes, expected %d" % \ | ||
71 | (raw_hdr[2], _GPT_HEADER_SIZE)) | ||
72 | |||
73 | crc = _calc_header_crc(raw_hdr) | ||
74 | if raw_hdr[3] != crc: | ||
75 | raise MountError("GPT header crc mismatch: %#x, should be %#x" % \ | ||
76 | (crc, raw_hdr[3])) | ||
77 | |||
78 | class GptParser: | ||
79 | """ GPT partition table parser. Allows reading the GPT header and the | ||
80 | partition table, as well as modifying the partition table records. """ | ||
81 | |||
82 | def __init__(self, disk_path, sector_size = 512): | ||
83 | """ The class constructor which accepts the following parameters: | ||
84 | * disk_path - full path to the disk image or device node | ||
85 | * sector_size - size of a disk sector in bytes """ | ||
86 | |||
87 | self.sector_size = sector_size | ||
88 | self.disk_path = disk_path | ||
89 | |||
90 | try: | ||
91 | self._disk_obj = open(disk_path, 'r+b') | ||
92 | except IOError as err: | ||
93 | raise MountError("Cannot open file '%s' for reading GPT " \ | ||
94 | "partitions: %s" % (disk_path, err)) | ||
95 | |||
96 | def __del__(self): | ||
97 | """ The class destructor. """ | ||
98 | |||
99 | self._disk_obj.close() | ||
100 | |||
101 | def _read_disk(self, offset, size): | ||
102 | """ A helper function which reads 'size' bytes from offset 'offset' of | ||
103 | the disk and checks all the error conditions. """ | ||
104 | |||
105 | self._disk_obj.seek(offset) | ||
106 | try: | ||
107 | data = self._disk_obj.read(size) | ||
108 | except IOError as err: | ||
109 | raise MountError("cannot read from '%s': %s" % \ | ||
110 | (self.disk_path, err)) | ||
111 | |||
112 | if len(data) != size: | ||
113 | raise MountError("cannot read %d bytes from offset '%d' of '%s', " \ | ||
114 | "read only %d bytes" % \ | ||
115 | (size, offset, self.disk_path, len(data))) | ||
116 | |||
117 | return data | ||
118 | |||
119 | def _write_disk(self, offset, buf): | ||
120 | """ A helper function which writes buffer 'buf' to offset 'offset' of | ||
121 | the disk. This function takes care of unaligned writes and checks all | ||
122 | the error conditions. """ | ||
123 | |||
124 | # Since we may be dealing with a block device, we only can write in | ||
125 | # 'self.sector_size' chunks. Find the aligned starting and ending | ||
126 | # disk offsets to read. | ||
127 | start = (offset / self.sector_size) * self.sector_size | ||
128 | end = ((start + len(buf)) / self.sector_size + 1) * self.sector_size | ||
129 | |||
130 | data = self._read_disk(start, end - start) | ||
131 | off = offset - start | ||
132 | data = data[:off] + buf + data[off + len(buf):] | ||
133 | |||
134 | self._disk_obj.seek(start) | ||
135 | try: | ||
136 | self._disk_obj.write(data) | ||
137 | except IOError as err: | ||
138 | raise MountError("cannot write to '%s': %s" % (self.disk_path, err)) | ||
139 | |||
140 | def read_header(self, primary = True): | ||
141 | """ Read and verify the GPT header and return a dictionary containing | ||
142 | the following elements: | ||
143 | |||
144 | 'signature' : header signature | ||
145 | 'revision' : header revision | ||
146 | 'hdr_size' : header size in bytes | ||
147 | 'hdr_crc' : header CRC32 | ||
148 | 'hdr_lba' : LBA of this header | ||
149 | 'hdr_offs' : byte disk offset of this header | ||
150 | 'backup_lba' : backup header LBA | ||
151 | 'backup_offs' : byte disk offset of backup header | ||
152 | 'first_lba' : first usable LBA for partitions | ||
153 | 'first_offs' : first usable byte disk offset for partitions | ||
154 | 'last_lba' : last usable LBA for partitions | ||
155 | 'last_offs' : last usable byte disk offset for partitions | ||
156 | 'disk_uuid' : UUID of the disk | ||
157 | 'ptable_lba' : starting LBA of array of partition entries | ||
158 | 'ptable_offs' : disk byte offset of the start of the partition table | ||
159 | 'ptable_size' : partition table size in bytes | ||
160 | 'entries_cnt' : number of available partition table entries | ||
161 | 'entry_size' : size of a single partition entry | ||
162 | 'ptable_crc' : CRC32 of the partition table | ||
163 | 'primary' : a boolean, if 'True', this is the primary GPT header, | ||
164 | if 'False' - the secondary | ||
165 | 'primary_str' : contains string "primary" if this is the primary GPT | ||
166 | header, and "backup" otherwise | ||
167 | |||
168 | This dictionary corresponds to the GPT header format. Please, see the | ||
169 | UEFI standard for the description of these fields. | ||
170 | |||
171 | If the 'primary' parameter is 'True', the primary GPT header is read, | ||
172 | otherwise the backup GPT header is read instead. """ | ||
173 | |||
174 | # Read and validate the primary GPT header | ||
175 | raw_hdr = self._read_disk(self.sector_size, _GPT_HEADER_SIZE) | ||
176 | raw_hdr = struct.unpack(_GPT_HEADER_FORMAT, raw_hdr) | ||
177 | _validate_header(raw_hdr) | ||
178 | primary_str = "primary" | ||
179 | |||
180 | if not primary: | ||
181 | # Read and validate the backup GPT header | ||
182 | raw_hdr = self._read_disk(raw_hdr[6] * self.sector_size, _GPT_HEADER_SIZE) | ||
183 | raw_hdr = struct.unpack(_GPT_HEADER_FORMAT, raw_hdr) | ||
184 | _validate_header(raw_hdr) | ||
185 | primary_str = "backup" | ||
186 | |||
187 | return { 'signature' : raw_hdr[0], | ||
188 | 'revision' : raw_hdr[1], | ||
189 | 'hdr_size' : raw_hdr[2], | ||
190 | 'hdr_crc' : raw_hdr[3], | ||
191 | 'hdr_lba' : raw_hdr[5], | ||
192 | 'hdr_offs' : raw_hdr[5] * self.sector_size, | ||
193 | 'backup_lba' : raw_hdr[6], | ||
194 | 'backup_offs' : raw_hdr[6] * self.sector_size, | ||
195 | 'first_lba' : raw_hdr[7], | ||
196 | 'first_offs' : raw_hdr[7] * self.sector_size, | ||
197 | 'last_lba' : raw_hdr[8], | ||
198 | 'last_offs' : raw_hdr[8] * self.sector_size, | ||
199 | 'disk_uuid' :_stringify_uuid(raw_hdr[9]), | ||
200 | 'ptable_lba' : raw_hdr[10], | ||
201 | 'ptable_offs' : raw_hdr[10] * self.sector_size, | ||
202 | 'ptable_size' : raw_hdr[11] * raw_hdr[12], | ||
203 | 'entries_cnt' : raw_hdr[11], | ||
204 | 'entry_size' : raw_hdr[12], | ||
205 | 'ptable_crc' : raw_hdr[13], | ||
206 | 'primary' : primary, | ||
207 | 'primary_str' : primary_str } | ||
208 | |||
209 | def _read_raw_ptable(self, header): | ||
210 | """ Read and validate primary or backup partition table. The 'header' | ||
211 | argument is the GPT header. If it is the primary GPT header, then the | ||
212 | primary partition table is read and validated, otherwise - the backup | ||
213 | one. The 'header' argument is a dictionary which is returned by the | ||
214 | 'read_header()' method. """ | ||
215 | |||
216 | raw_ptable = self._read_disk(header['ptable_offs'], | ||
217 | header['ptable_size']) | ||
218 | |||
219 | crc = binascii.crc32(raw_ptable) & 0xFFFFFFFF | ||
220 | if crc != header['ptable_crc']: | ||
221 | raise MountError("Partition table at LBA %d (%s) is corrupted" % \ | ||
222 | (header['ptable_lba'], header['primary_str'])) | ||
223 | |||
224 | return raw_ptable | ||
225 | |||
226 | def get_partitions(self, primary = True): | ||
227 | """ This is a generator which parses the GPT partition table and | ||
228 | generates the following dictionary for each partition: | ||
229 | |||
230 | 'index' : the index of the partition table endry | ||
231 | 'offs' : byte disk offset of the partition table entry | ||
232 | 'type_uuid' : partition type UUID | ||
233 | 'part_uuid' : partition UUID | ||
234 | 'first_lba' : the first LBA | ||
235 | 'last_lba' : the last LBA | ||
236 | 'flags' : attribute flags | ||
237 | 'name' : partition name | ||
238 | 'primary' : a boolean, if 'True', this is the primary partition | ||
239 | table, if 'False' - the secondary | ||
240 | 'primary_str' : contains string "primary" if this is the primary GPT | ||
241 | header, and "backup" otherwise | ||
242 | |||
243 | This dictionary corresponds to the GPT header format. Please, see the | ||
244 | UEFI standard for the description of these fields. | ||
245 | |||
246 | If the 'primary' parameter is 'True', partitions from the primary GPT | ||
247 | partition table are generated, otherwise partitions from the backup GPT | ||
248 | partition table are generated. """ | ||
249 | |||
250 | if primary: | ||
251 | primary_str = "primary" | ||
252 | else: | ||
253 | primary_str = "backup" | ||
254 | |||
255 | header = self.read_header(primary) | ||
256 | raw_ptable = self._read_raw_ptable(header) | ||
257 | |||
258 | for index in xrange(0, header['entries_cnt']): | ||
259 | start = header['entry_size'] * index | ||
260 | end = start + header['entry_size'] | ||
261 | raw_entry = struct.unpack(_GPT_ENTRY_FORMAT, raw_ptable[start:end]) | ||
262 | |||
263 | if raw_entry[2] == 0 or raw_entry[3] == 0: | ||
264 | continue | ||
265 | |||
266 | part_name = str(raw_entry[5].decode('UTF-16').split('\0', 1)[0]) | ||
267 | |||
268 | yield { 'index' : index, | ||
269 | 'offs' : header['ptable_offs'] + start, | ||
270 | 'type_uuid' : _stringify_uuid(raw_entry[0]), | ||
271 | 'part_uuid' : _stringify_uuid(raw_entry[1]), | ||
272 | 'first_lba' : raw_entry[2], | ||
273 | 'last_lba' : raw_entry[3], | ||
274 | 'flags' : raw_entry[4], | ||
275 | 'name' : part_name, | ||
276 | 'primary' : primary, | ||
277 | 'primary_str' : primary_str } | ||
278 | |||
279 | def _change_partition(self, header, entry): | ||
280 | """ A helper function for 'change_partitions()' which changes a | ||
281 | a paricular instance of the partition table (primary or backup). """ | ||
282 | |||
283 | if entry['index'] >= header['entries_cnt']: | ||
284 | raise MountError("Partition table at LBA %d has only %d " \ | ||
285 | "records cannot change record number %d" % \ | ||
286 | (header['entries_cnt'], entry['index'])) | ||
287 | # Read raw GPT header | ||
288 | raw_hdr = self._read_disk(header['hdr_offs'], _GPT_HEADER_SIZE) | ||
289 | raw_hdr = list(struct.unpack(_GPT_HEADER_FORMAT, raw_hdr)) | ||
290 | _validate_header(raw_hdr) | ||
291 | |||
292 | # Prepare the new partition table entry | ||
293 | raw_entry = struct.pack(_GPT_ENTRY_FORMAT, | ||
294 | uuid.UUID(entry['type_uuid']).bytes_le, | ||
295 | uuid.UUID(entry['part_uuid']).bytes_le, | ||
296 | entry['first_lba'], | ||
297 | entry['last_lba'], | ||
298 | entry['flags'], | ||
299 | entry['name'].encode('UTF-16')) | ||
300 | |||
301 | # Write the updated entry to the disk | ||
302 | entry_offs = header['ptable_offs'] + \ | ||
303 | header['entry_size'] * entry['index'] | ||
304 | self._write_disk(entry_offs, raw_entry) | ||
305 | |||
306 | # Calculate and update partition table CRC32 | ||
307 | raw_ptable = self._read_disk(header['ptable_offs'], | ||
308 | header['ptable_size']) | ||
309 | raw_hdr[13] = binascii.crc32(raw_ptable) & 0xFFFFFFFF | ||
310 | |||
311 | # Calculate and update the GPT header CRC | ||
312 | raw_hdr[3] = _calc_header_crc(raw_hdr) | ||
313 | |||
314 | # Write the updated header to the disk | ||
315 | raw_hdr = struct.pack(_GPT_HEADER_FORMAT, *raw_hdr) | ||
316 | self._write_disk(header['hdr_offs'], raw_hdr) | ||
317 | |||
318 | def change_partition(self, entry): | ||
319 | """ Change a GPT partition. The 'entry' argument has the same format as | ||
320 | 'get_partitions()' returns. This function simply changes the partition | ||
321 | table record corresponding to 'entry' in both, the primary and the | ||
322 | backup GPT partition tables. The parition table CRC is re-calculated | ||
323 | and the GPT headers are modified accordingly. """ | ||
324 | |||
325 | # Change the primary partition table | ||
326 | header = self.read_header(True) | ||
327 | self._change_partition(header, entry) | ||
328 | |||
329 | # Change the backup partition table | ||
330 | header = self.read_header(False) | ||
331 | self._change_partition(header, entry) | ||
diff --git a/scripts/lib/mic/utils/grabber.py b/scripts/lib/mic/utils/grabber.py new file mode 100644 index 0000000000..45e30b4fb0 --- /dev/null +++ b/scripts/lib/mic/utils/grabber.py | |||
@@ -0,0 +1,97 @@ | |||
1 | #!/usr/bin/python | ||
2 | |||
3 | import os | ||
4 | import sys | ||
5 | import rpm | ||
6 | import fcntl | ||
7 | import struct | ||
8 | import termios | ||
9 | |||
10 | from mic import msger | ||
11 | from mic.utils import runner | ||
12 | from mic.utils.errors import CreatorError | ||
13 | |||
14 | from urlgrabber import grabber | ||
15 | from urlgrabber import __version__ as grabber_version | ||
16 | |||
17 | if rpm.labelCompare(grabber_version.split('.'), '3.9.0'.split('.')) == -1: | ||
18 | msger.warning("Version of python-urlgrabber is %s, lower than '3.9.0', " | ||
19 | "you may encounter some network issues" % grabber_version) | ||
20 | |||
21 | def myurlgrab(url, filename, proxies, progress_obj = None): | ||
22 | g = grabber.URLGrabber() | ||
23 | if progress_obj is None: | ||
24 | progress_obj = TextProgress() | ||
25 | |||
26 | if url.startswith("file:/"): | ||
27 | filepath = "/%s" % url.replace("file:", "").lstrip('/') | ||
28 | if not os.path.exists(filepath): | ||
29 | raise CreatorError("URLGrabber error: can't find file %s" % url) | ||
30 | if url.endswith('.rpm'): | ||
31 | return filepath | ||
32 | else: | ||
33 | # untouch repometadata in source path | ||
34 | runner.show(['cp', '-f', filepath, filename]) | ||
35 | |||
36 | else: | ||
37 | try: | ||
38 | filename = g.urlgrab(url=str(url), | ||
39 | filename=filename, | ||
40 | ssl_verify_host=False, | ||
41 | ssl_verify_peer=False, | ||
42 | proxies=proxies, | ||
43 | http_headers=(('Pragma', 'no-cache'),), | ||
44 | quote=0, | ||
45 | progress_obj=progress_obj) | ||
46 | except grabber.URLGrabError, err: | ||
47 | msg = str(err) | ||
48 | if msg.find(url) < 0: | ||
49 | msg += ' on %s' % url | ||
50 | raise CreatorError(msg) | ||
51 | |||
52 | return filename | ||
53 | |||
54 | def terminal_width(fd=1): | ||
55 | """ Get the real terminal width """ | ||
56 | try: | ||
57 | buf = 'abcdefgh' | ||
58 | buf = fcntl.ioctl(fd, termios.TIOCGWINSZ, buf) | ||
59 | return struct.unpack('hhhh', buf)[1] | ||
60 | except: # IOError | ||
61 | return 80 | ||
62 | |||
63 | def truncate_url(url, width): | ||
64 | return os.path.basename(url)[0:width] | ||
65 | |||
66 | class TextProgress(object): | ||
67 | # make the class as singleton | ||
68 | _instance = None | ||
69 | def __new__(cls, *args, **kwargs): | ||
70 | if not cls._instance: | ||
71 | cls._instance = super(TextProgress, cls).__new__(cls, *args, **kwargs) | ||
72 | |||
73 | return cls._instance | ||
74 | |||
75 | def __init__(self, totalnum = None): | ||
76 | self.total = totalnum | ||
77 | self.counter = 1 | ||
78 | |||
79 | def start(self, filename, url, *args, **kwargs): | ||
80 | self.url = url | ||
81 | self.termwidth = terminal_width() | ||
82 | msger.info("\r%-*s" % (self.termwidth, " ")) | ||
83 | if self.total is None: | ||
84 | msger.info("\rRetrieving %s ..." % truncate_url(self.url, self.termwidth - 15)) | ||
85 | else: | ||
86 | msger.info("\rRetrieving %s [%d/%d] ..." % (truncate_url(self.url, self.termwidth - 25), self.counter, self.total)) | ||
87 | |||
88 | def update(self, *args): | ||
89 | pass | ||
90 | |||
91 | def end(self, *args): | ||
92 | if self.counter == self.total: | ||
93 | msger.raw("\n") | ||
94 | |||
95 | if self.total is not None: | ||
96 | self.counter += 1 | ||
97 | |||
diff --git a/scripts/lib/mic/utils/misc.py b/scripts/lib/mic/utils/misc.py new file mode 100644 index 0000000000..95241d7f15 --- /dev/null +++ b/scripts/lib/mic/utils/misc.py | |||
@@ -0,0 +1,1065 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2010, 2011 Intel Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import sys | ||
20 | import time | ||
21 | import tempfile | ||
22 | import re | ||
23 | import shutil | ||
24 | import glob | ||
25 | import hashlib | ||
26 | import subprocess | ||
27 | import platform | ||
28 | import traceback | ||
29 | |||
30 | |||
31 | try: | ||
32 | import sqlite3 as sqlite | ||
33 | except ImportError: | ||
34 | import sqlite | ||
35 | |||
36 | try: | ||
37 | from xml.etree import cElementTree | ||
38 | except ImportError: | ||
39 | import cElementTree | ||
40 | xmlparse = cElementTree.parse | ||
41 | |||
42 | from mic import msger | ||
43 | from mic.utils.errors import CreatorError, SquashfsError | ||
44 | from mic.utils.fs_related import find_binary_path, makedirs | ||
45 | from mic.utils.proxy import get_proxy_for | ||
46 | from mic.utils import runner | ||
47 | |||
48 | |||
49 | RPM_RE = re.compile("(.*)\.(.*) (.*)-(.*)") | ||
50 | RPM_FMT = "%(name)s.%(arch)s %(version)s-%(release)s" | ||
51 | SRPM_RE = re.compile("(.*)-(\d+.*)-(\d+\.\d+).src.rpm") | ||
52 | |||
53 | |||
54 | def build_name(kscfg, release=None, prefix = None, suffix = None): | ||
55 | """Construct and return an image name string. | ||
56 | |||
57 | This is a utility function to help create sensible name and fslabel | ||
58 | strings. The name is constructed using the sans-prefix-and-extension | ||
59 | kickstart filename and the supplied prefix and suffix. | ||
60 | |||
61 | kscfg -- a path to a kickstart file | ||
62 | release -- a replacement to suffix for image release | ||
63 | prefix -- a prefix to prepend to the name; defaults to None, which causes | ||
64 | no prefix to be used | ||
65 | suffix -- a suffix to append to the name; defaults to None, which causes | ||
66 | a YYYYMMDDHHMM suffix to be used | ||
67 | |||
68 | Note, if maxlen is less then the len(suffix), you get to keep both pieces. | ||
69 | |||
70 | """ | ||
71 | name = os.path.basename(kscfg) | ||
72 | idx = name.rfind('.') | ||
73 | if idx >= 0: | ||
74 | name = name[:idx] | ||
75 | |||
76 | if release is not None: | ||
77 | suffix = "" | ||
78 | if prefix is None: | ||
79 | prefix = "" | ||
80 | if suffix is None: | ||
81 | suffix = time.strftime("%Y%m%d%H%M") | ||
82 | |||
83 | if name.startswith(prefix): | ||
84 | name = name[len(prefix):] | ||
85 | |||
86 | prefix = "%s-" % prefix if prefix else "" | ||
87 | suffix = "-%s" % suffix if suffix else "" | ||
88 | |||
89 | ret = prefix + name + suffix | ||
90 | return ret | ||
91 | |||
92 | def get_distro(): | ||
93 | """Detect linux distribution, support "meego" | ||
94 | """ | ||
95 | |||
96 | support_dists = ('SuSE', | ||
97 | 'debian', | ||
98 | 'fedora', | ||
99 | 'redhat', | ||
100 | 'centos', | ||
101 | 'meego', | ||
102 | 'moblin', | ||
103 | 'tizen') | ||
104 | try: | ||
105 | (dist, ver, id) = platform.linux_distribution( \ | ||
106 | supported_dists = support_dists) | ||
107 | except: | ||
108 | (dist, ver, id) = platform.dist( \ | ||
109 | supported_dists = support_dists) | ||
110 | |||
111 | return (dist, ver, id) | ||
112 | |||
113 | def get_distro_str(): | ||
114 | """Get composited string for current linux distribution | ||
115 | """ | ||
116 | (dist, ver, id) = get_distro() | ||
117 | |||
118 | if not dist: | ||
119 | return 'Unknown Linux Distro' | ||
120 | else: | ||
121 | distro_str = ' '.join(map(str.strip, (dist, ver, id))) | ||
122 | return distro_str.strip() | ||
123 | |||
124 | _LOOP_RULE_PTH = None | ||
125 | |||
126 | def hide_loopdev_presentation(): | ||
127 | udev_rules = "80-prevent-loop-present.rules" | ||
128 | udev_rules_dir = [ | ||
129 | '/usr/lib/udev/rules.d/', | ||
130 | '/lib/udev/rules.d/', | ||
131 | '/etc/udev/rules.d/' | ||
132 | ] | ||
133 | |||
134 | global _LOOP_RULE_PTH | ||
135 | |||
136 | for rdir in udev_rules_dir: | ||
137 | if os.path.exists(rdir): | ||
138 | _LOOP_RULE_PTH = os.path.join(rdir, udev_rules) | ||
139 | |||
140 | if not _LOOP_RULE_PTH: | ||
141 | return | ||
142 | |||
143 | try: | ||
144 | with open(_LOOP_RULE_PTH, 'w') as wf: | ||
145 | wf.write('KERNEL=="loop*", ENV{UDISKS_PRESENTATION_HIDE}="1"') | ||
146 | |||
147 | runner.quiet('udevadm trigger') | ||
148 | except: | ||
149 | pass | ||
150 | |||
151 | def unhide_loopdev_presentation(): | ||
152 | global _LOOP_RULE_PTH | ||
153 | |||
154 | if not _LOOP_RULE_PTH: | ||
155 | return | ||
156 | |||
157 | try: | ||
158 | os.unlink(_LOOP_RULE_PTH) | ||
159 | runner.quiet('udevadm trigger') | ||
160 | except: | ||
161 | pass | ||
162 | |||
163 | def extract_rpm(rpmfile, targetdir): | ||
164 | rpm2cpio = find_binary_path("rpm2cpio") | ||
165 | cpio = find_binary_path("cpio") | ||
166 | |||
167 | olddir = os.getcwd() | ||
168 | os.chdir(targetdir) | ||
169 | |||
170 | msger.verbose("Extract rpm file with cpio: %s" % rpmfile) | ||
171 | p1 = subprocess.Popen([rpm2cpio, rpmfile], stdout=subprocess.PIPE) | ||
172 | p2 = subprocess.Popen([cpio, "-idv"], stdin=p1.stdout, | ||
173 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) | ||
174 | (sout, serr) = p2.communicate() | ||
175 | msger.verbose(sout or serr) | ||
176 | |||
177 | os.chdir(olddir) | ||
178 | |||
179 | def compressing(fpath, method): | ||
180 | comp_map = { | ||
181 | "gz": "gzip", | ||
182 | "bz2": "bzip2" | ||
183 | } | ||
184 | if method not in comp_map: | ||
185 | raise CreatorError("Unsupport compress format: %s, valid values: %s" | ||
186 | % (method, ','.join(comp_map.keys()))) | ||
187 | cmd = find_binary_path(comp_map[method]) | ||
188 | rc = runner.show([cmd, "-f", fpath]) | ||
189 | if rc: | ||
190 | raise CreatorError("Failed to %s file: %s" % (comp_map[method], fpath)) | ||
191 | |||
192 | def taring(dstfile, target): | ||
193 | import tarfile | ||
194 | basen, ext = os.path.splitext(dstfile) | ||
195 | comp = {".tar": None, | ||
196 | ".gz": "gz", # for .tar.gz | ||
197 | ".bz2": "bz2", # for .tar.bz2 | ||
198 | ".tgz": "gz", | ||
199 | ".tbz": "bz2"}[ext] | ||
200 | |||
201 | # specify tarball file path | ||
202 | if not comp: | ||
203 | tarpath = dstfile | ||
204 | elif basen.endswith(".tar"): | ||
205 | tarpath = basen | ||
206 | else: | ||
207 | tarpath = basen + ".tar" | ||
208 | wf = tarfile.open(tarpath, 'w') | ||
209 | |||
210 | if os.path.isdir(target): | ||
211 | for item in os.listdir(target): | ||
212 | wf.add(os.path.join(target, item), item) | ||
213 | else: | ||
214 | wf.add(target, os.path.basename(target)) | ||
215 | wf.close() | ||
216 | |||
217 | if comp: | ||
218 | compressing(tarpath, comp) | ||
219 | # when dstfile ext is ".tgz" and ".tbz", should rename | ||
220 | if not basen.endswith(".tar"): | ||
221 | shutil.move("%s.%s" % (tarpath, comp), dstfile) | ||
222 | |||
223 | def ziping(dstfile, target): | ||
224 | import zipfile | ||
225 | wf = zipfile.ZipFile(dstfile, 'w', compression=zipfile.ZIP_DEFLATED) | ||
226 | if os.path.isdir(target): | ||
227 | for item in os.listdir(target): | ||
228 | fpath = os.path.join(target, item) | ||
229 | if not os.path.isfile(fpath): | ||
230 | continue | ||
231 | wf.write(fpath, item, zipfile.ZIP_DEFLATED) | ||
232 | else: | ||
233 | wf.write(target, os.path.basename(target), zipfile.ZIP_DEFLATED) | ||
234 | wf.close() | ||
235 | |||
236 | pack_formats = { | ||
237 | ".tar": taring, | ||
238 | ".tar.gz": taring, | ||
239 | ".tar.bz2": taring, | ||
240 | ".tgz": taring, | ||
241 | ".tbz": taring, | ||
242 | ".zip": ziping, | ||
243 | } | ||
244 | |||
245 | def packing(dstfile, target): | ||
246 | (base, ext) = os.path.splitext(dstfile) | ||
247 | if ext in (".gz", ".bz2") and base.endswith(".tar"): | ||
248 | ext = ".tar" + ext | ||
249 | if ext not in pack_formats: | ||
250 | raise CreatorError("Unsupport pack format: %s, valid values: %s" | ||
251 | % (ext, ','.join(pack_formats.keys()))) | ||
252 | func = pack_formats[ext] | ||
253 | # func should be callable | ||
254 | func(dstfile, target) | ||
255 | |||
256 | def human_size(size): | ||
257 | """Return human readable string for Bytes size | ||
258 | """ | ||
259 | |||
260 | if size <= 0: | ||
261 | return "0M" | ||
262 | import math | ||
263 | measure = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] | ||
264 | expo = int(math.log(size, 1024)) | ||
265 | mant = float(size/math.pow(1024, expo)) | ||
266 | return "{0:.1f}{1:s}".format(mant, measure[expo]) | ||
267 | |||
268 | def get_block_size(file_obj): | ||
269 | """ Returns block size for file object 'file_obj'. Errors are indicated by | ||
270 | the 'IOError' exception. """ | ||
271 | |||
272 | from fcntl import ioctl | ||
273 | import struct | ||
274 | |||
275 | # Get the block size of the host file-system for the image file by calling | ||
276 | # the FIGETBSZ ioctl (number 2). | ||
277 | binary_data = ioctl(file_obj, 2, struct.pack('I', 0)) | ||
278 | return struct.unpack('I', binary_data)[0] | ||
279 | |||
280 | def check_space_pre_cp(src, dst): | ||
281 | """Check whether disk space is enough before 'cp' like | ||
282 | operations, else exception will be raised. | ||
283 | """ | ||
284 | |||
285 | srcsize = get_file_size(src) * 1024 * 1024 | ||
286 | freesize = get_filesystem_avail(dst) | ||
287 | if srcsize > freesize: | ||
288 | raise CreatorError("space on %s(%s) is not enough for about %s files" | ||
289 | % (dst, human_size(freesize), human_size(srcsize))) | ||
290 | |||
291 | def calc_hashes(file_path, hash_names, start = 0, end = None): | ||
292 | """ Calculate hashes for a file. The 'file_path' argument is the file | ||
293 | to calculate hash functions for, 'start' and 'end' are the starting and | ||
294 | ending file offset to calculate the has functions for. The 'hash_names' | ||
295 | argument is a list of hash names to calculate. Returns the the list | ||
296 | of calculated hash values in the hexadecimal form in the same order | ||
297 | as 'hash_names'. | ||
298 | """ | ||
299 | if end == None: | ||
300 | end = os.path.getsize(file_path) | ||
301 | |||
302 | chunk_size = 65536 | ||
303 | to_read = end - start | ||
304 | read = 0 | ||
305 | |||
306 | hashes = [] | ||
307 | for hash_name in hash_names: | ||
308 | hashes.append(hashlib.new(hash_name)) | ||
309 | |||
310 | with open(file_path, "rb") as f: | ||
311 | f.seek(start) | ||
312 | |||
313 | while read < to_read: | ||
314 | if read + chunk_size > to_read: | ||
315 | chunk_size = to_read - read | ||
316 | chunk = f.read(chunk_size) | ||
317 | for hash_obj in hashes: | ||
318 | hash_obj.update(chunk) | ||
319 | read += chunk_size | ||
320 | |||
321 | result = [] | ||
322 | for hash_obj in hashes: | ||
323 | result.append(hash_obj.hexdigest()) | ||
324 | |||
325 | return result | ||
326 | |||
327 | def get_md5sum(fpath): | ||
328 | return calc_hashes(fpath, ('md5', ))[0] | ||
329 | |||
330 | |||
331 | def normalize_ksfile(ksconf, release, arch): | ||
332 | ''' | ||
333 | Return the name of a normalized ks file in which macro variables | ||
334 | @BUILD_ID@ and @ARCH@ are replace with real values. | ||
335 | |||
336 | The original ks file is returned if no special macro is used, otherwise | ||
337 | a temp file is created and returned, which will be deleted when program | ||
338 | exits normally. | ||
339 | ''' | ||
340 | |||
341 | if not release: | ||
342 | release = "latest" | ||
343 | if not arch or re.match(r'i.86', arch): | ||
344 | arch = "ia32" | ||
345 | |||
346 | with open(ksconf) as f: | ||
347 | ksc = f.read() | ||
348 | |||
349 | if "@ARCH@" not in ksc and "@BUILD_ID@" not in ksc: | ||
350 | return ksconf | ||
351 | |||
352 | msger.info("Substitute macro variable @BUILD_ID@/@ARCH@ in ks: %s" % ksconf) | ||
353 | ksc = ksc.replace("@ARCH@", arch) | ||
354 | ksc = ksc.replace("@BUILD_ID@", release) | ||
355 | |||
356 | fd, ksconf = tempfile.mkstemp(prefix=os.path.basename(ksconf)) | ||
357 | os.write(fd, ksc) | ||
358 | os.close(fd) | ||
359 | |||
360 | msger.debug('normalized ks file:%s' % ksconf) | ||
361 | |||
362 | def remove_temp_ks(): | ||
363 | try: | ||
364 | os.unlink(ksconf) | ||
365 | except OSError, err: | ||
366 | msger.warning('Failed to remove temp ks file:%s:%s' % (ksconf, err)) | ||
367 | |||
368 | import atexit | ||
369 | atexit.register(remove_temp_ks) | ||
370 | |||
371 | return ksconf | ||
372 | |||
373 | |||
374 | def _check_mic_chroot(rootdir): | ||
375 | def _path(path): | ||
376 | return rootdir.rstrip('/') + path | ||
377 | |||
378 | release_files = map(_path, [ "/etc/moblin-release", | ||
379 | "/etc/meego-release", | ||
380 | "/etc/tizen-release"]) | ||
381 | |||
382 | if not any(map(os.path.exists, release_files)): | ||
383 | msger.warning("Dir %s is not a MeeGo/Tizen chroot env" % rootdir) | ||
384 | |||
385 | if not glob.glob(rootdir + "/boot/vmlinuz-*"): | ||
386 | msger.warning("Failed to find kernel module under %s" % rootdir) | ||
387 | |||
388 | return | ||
389 | |||
390 | def selinux_check(arch, fstypes): | ||
391 | try: | ||
392 | getenforce = find_binary_path('getenforce') | ||
393 | except CreatorError: | ||
394 | return | ||
395 | |||
396 | selinux_status = runner.outs([getenforce]) | ||
397 | if arch and arch.startswith("arm") and selinux_status == "Enforcing": | ||
398 | raise CreatorError("Can't create arm image if selinux is enabled, " | ||
399 | "please run 'setenforce 0' to disable selinux") | ||
400 | |||
401 | use_btrfs = filter(lambda typ: typ == 'btrfs', fstypes) | ||
402 | if use_btrfs and selinux_status == "Enforcing": | ||
403 | raise CreatorError("Can't create btrfs image if selinux is enabled," | ||
404 | " please run 'setenforce 0' to disable selinux") | ||
405 | |||
406 | def get_image_type(path): | ||
407 | def _get_extension_name(path): | ||
408 | match = re.search("(?<=\.)\w+$", path) | ||
409 | if match: | ||
410 | return match.group(0) | ||
411 | else: | ||
412 | return None | ||
413 | |||
414 | if os.path.isdir(path): | ||
415 | _check_mic_chroot(path) | ||
416 | return "fs" | ||
417 | |||
418 | maptab = { | ||
419 | "tar": "loop", | ||
420 | "raw":"raw", | ||
421 | "vmdk":"vmdk", | ||
422 | "vdi":"vdi", | ||
423 | "iso":"livecd", | ||
424 | "usbimg":"liveusb", | ||
425 | } | ||
426 | |||
427 | extension = _get_extension_name(path) | ||
428 | if extension in maptab: | ||
429 | return maptab[extension] | ||
430 | |||
431 | fd = open(path, "rb") | ||
432 | file_header = fd.read(1024) | ||
433 | fd.close() | ||
434 | vdi_flag = "<<< Sun VirtualBox Disk Image >>>" | ||
435 | if file_header[0:len(vdi_flag)] == vdi_flag: | ||
436 | return maptab["vdi"] | ||
437 | |||
438 | output = runner.outs(['file', path]) | ||
439 | isoptn = re.compile(r".*ISO 9660 CD-ROM filesystem.*(bootable).*") | ||
440 | usbimgptn = re.compile(r".*x86 boot sector.*active.*") | ||
441 | rawptn = re.compile(r".*x86 boot sector.*") | ||
442 | vmdkptn = re.compile(r".*VMware. disk image.*") | ||
443 | ext3fsimgptn = re.compile(r".*Linux.*ext3 filesystem data.*") | ||
444 | ext4fsimgptn = re.compile(r".*Linux.*ext4 filesystem data.*") | ||
445 | btrfsimgptn = re.compile(r".*BTRFS.*") | ||
446 | if isoptn.match(output): | ||
447 | return maptab["iso"] | ||
448 | elif usbimgptn.match(output): | ||
449 | return maptab["usbimg"] | ||
450 | elif rawptn.match(output): | ||
451 | return maptab["raw"] | ||
452 | elif vmdkptn.match(output): | ||
453 | return maptab["vmdk"] | ||
454 | elif ext3fsimgptn.match(output): | ||
455 | return "ext3fsimg" | ||
456 | elif ext4fsimgptn.match(output): | ||
457 | return "ext4fsimg" | ||
458 | elif btrfsimgptn.match(output): | ||
459 | return "btrfsimg" | ||
460 | else: | ||
461 | raise CreatorError("Cannot detect the type of image: %s" % path) | ||
462 | |||
463 | |||
464 | def get_file_size(filename): | ||
465 | """ Return size in MB unit """ | ||
466 | cmd = ['du', "-s", "-b", "-B", "1M", filename] | ||
467 | rc, duOutput = runner.runtool(cmd) | ||
468 | if rc != 0: | ||
469 | raise CreatorError("Failed to run: %s" % ' '.join(cmd)) | ||
470 | size1 = int(duOutput.split()[0]) | ||
471 | |||
472 | cmd = ['du', "-s", "-B", "1M", filename] | ||
473 | rc, duOutput = runner.runtool(cmd) | ||
474 | if rc != 0: | ||
475 | raise CreatorError("Failed to run: %s" % ' '.join(cmd)) | ||
476 | |||
477 | size2 = int(duOutput.split()[0]) | ||
478 | return max(size1, size2) | ||
479 | |||
480 | |||
481 | def get_filesystem_avail(fs): | ||
482 | vfstat = os.statvfs(fs) | ||
483 | return vfstat.f_bavail * vfstat.f_bsize | ||
484 | |||
485 | def convert_image(srcimg, srcfmt, dstimg, dstfmt): | ||
486 | #convert disk format | ||
487 | if dstfmt != "raw": | ||
488 | raise CreatorError("Invalid destination image format: %s" % dstfmt) | ||
489 | msger.debug("converting %s image to %s" % (srcimg, dstimg)) | ||
490 | if srcfmt == "vmdk": | ||
491 | path = find_binary_path("qemu-img") | ||
492 | argv = [path, "convert", "-f", "vmdk", srcimg, "-O", dstfmt, dstimg] | ||
493 | elif srcfmt == "vdi": | ||
494 | path = find_binary_path("VBoxManage") | ||
495 | argv = [path, "internalcommands", "converttoraw", srcimg, dstimg] | ||
496 | else: | ||
497 | raise CreatorError("Invalid soure image format: %s" % srcfmt) | ||
498 | |||
499 | rc = runner.show(argv) | ||
500 | if rc == 0: | ||
501 | msger.debug("convert successful") | ||
502 | if rc != 0: | ||
503 | raise CreatorError("Unable to convert disk to %s" % dstfmt) | ||
504 | |||
505 | def uncompress_squashfs(squashfsimg, outdir): | ||
506 | """Uncompress file system from squshfs image""" | ||
507 | unsquashfs = find_binary_path("unsquashfs") | ||
508 | args = [ unsquashfs, "-d", outdir, squashfsimg ] | ||
509 | rc = runner.show(args) | ||
510 | if (rc != 0): | ||
511 | raise SquashfsError("Failed to uncompress %s." % squashfsimg) | ||
512 | |||
513 | def mkdtemp(dir = "/var/tmp", prefix = "wic-tmp-"): | ||
514 | """ FIXME: use the dir in wic.conf instead """ | ||
515 | |||
516 | makedirs(dir) | ||
517 | return tempfile.mkdtemp(dir = dir, prefix = prefix) | ||
518 | |||
519 | def get_repostrs_from_ks(ks): | ||
520 | def _get_temp_reponame(baseurl): | ||
521 | md5obj = hashlib.md5(baseurl) | ||
522 | tmpreponame = "%s" % md5obj.hexdigest() | ||
523 | return tmpreponame | ||
524 | |||
525 | kickstart_repos = [] | ||
526 | |||
527 | for repodata in ks.handler.repo.repoList: | ||
528 | repo = {} | ||
529 | for attr in ('name', | ||
530 | 'baseurl', | ||
531 | 'mirrorlist', | ||
532 | 'includepkgs', # val is list | ||
533 | 'excludepkgs', # val is list | ||
534 | 'cost', # int | ||
535 | 'priority',# int | ||
536 | 'save', | ||
537 | 'proxy', | ||
538 | 'proxyuser', | ||
539 | 'proxypasswd', | ||
540 | 'proxypasswd', | ||
541 | 'debuginfo', | ||
542 | 'source', | ||
543 | 'gpgkey', | ||
544 | 'ssl_verify'): | ||
545 | if hasattr(repodata, attr) and getattr(repodata, attr): | ||
546 | repo[attr] = getattr(repodata, attr) | ||
547 | |||
548 | if 'name' not in repo: | ||
549 | repo['name'] = _get_temp_reponame(repodata.baseurl) | ||
550 | |||
551 | kickstart_repos.append(repo) | ||
552 | |||
553 | return kickstart_repos | ||
554 | |||
555 | def _get_uncompressed_data_from_url(url, filename, proxies): | ||
556 | filename = myurlgrab(url, filename, proxies) | ||
557 | suffix = None | ||
558 | if filename.endswith(".gz"): | ||
559 | suffix = ".gz" | ||
560 | runner.quiet(['gunzip', "-f", filename]) | ||
561 | elif filename.endswith(".bz2"): | ||
562 | suffix = ".bz2" | ||
563 | runner.quiet(['bunzip2', "-f", filename]) | ||
564 | if suffix: | ||
565 | filename = filename.replace(suffix, "") | ||
566 | return filename | ||
567 | |||
568 | def _get_metadata_from_repo(baseurl, proxies, cachedir, reponame, filename, | ||
569 | sumtype=None, checksum=None): | ||
570 | url = os.path.join(baseurl, filename) | ||
571 | filename_tmp = str("%s/%s/%s" % (cachedir, reponame, os.path.basename(filename))) | ||
572 | if os.path.splitext(filename_tmp)[1] in (".gz", ".bz2"): | ||
573 | filename = os.path.splitext(filename_tmp)[0] | ||
574 | else: | ||
575 | filename = filename_tmp | ||
576 | if sumtype and checksum and os.path.exists(filename): | ||
577 | try: | ||
578 | sumcmd = find_binary_path("%ssum" % sumtype) | ||
579 | except: | ||
580 | file_checksum = None | ||
581 | else: | ||
582 | file_checksum = runner.outs([sumcmd, filename]).split()[0] | ||
583 | |||
584 | if file_checksum and file_checksum == checksum: | ||
585 | return filename | ||
586 | |||
587 | return _get_uncompressed_data_from_url(url,filename_tmp,proxies) | ||
588 | |||
589 | def get_metadata_from_repos(repos, cachedir): | ||
590 | my_repo_metadata = [] | ||
591 | for repo in repos: | ||
592 | reponame = repo['name'] | ||
593 | baseurl = repo['baseurl'] | ||
594 | |||
595 | |||
596 | if 'proxy' in repo: | ||
597 | proxy = repo['proxy'] | ||
598 | else: | ||
599 | proxy = get_proxy_for(baseurl) | ||
600 | |||
601 | proxies = None | ||
602 | if proxy: | ||
603 | proxies = {str(baseurl.split(":")[0]):str(proxy)} | ||
604 | |||
605 | makedirs(os.path.join(cachedir, reponame)) | ||
606 | url = os.path.join(baseurl, "repodata/repomd.xml") | ||
607 | filename = os.path.join(cachedir, reponame, 'repomd.xml') | ||
608 | repomd = myurlgrab(url, filename, proxies) | ||
609 | try: | ||
610 | root = xmlparse(repomd) | ||
611 | except SyntaxError: | ||
612 | raise CreatorError("repomd.xml syntax error.") | ||
613 | |||
614 | ns = root.getroot().tag | ||
615 | ns = ns[0:ns.rindex("}")+1] | ||
616 | |||
617 | filepaths = {} | ||
618 | checksums = {} | ||
619 | sumtypes = {} | ||
620 | |||
621 | for elm in root.getiterator("%sdata" % ns): | ||
622 | if elm.attrib["type"] == "patterns": | ||
623 | filepaths['patterns'] = elm.find("%slocation" % ns).attrib['href'] | ||
624 | checksums['patterns'] = elm.find("%sopen-checksum" % ns).text | ||
625 | sumtypes['patterns'] = elm.find("%sopen-checksum" % ns).attrib['type'] | ||
626 | break | ||
627 | |||
628 | for elm in root.getiterator("%sdata" % ns): | ||
629 | if elm.attrib["type"] in ("group_gz", "group"): | ||
630 | filepaths['comps'] = elm.find("%slocation" % ns).attrib['href'] | ||
631 | checksums['comps'] = elm.find("%sopen-checksum" % ns).text | ||
632 | sumtypes['comps'] = elm.find("%sopen-checksum" % ns).attrib['type'] | ||
633 | break | ||
634 | |||
635 | primary_type = None | ||
636 | for elm in root.getiterator("%sdata" % ns): | ||
637 | if elm.attrib["type"] in ("primary_db", "primary"): | ||
638 | primary_type = elm.attrib["type"] | ||
639 | filepaths['primary'] = elm.find("%slocation" % ns).attrib['href'] | ||
640 | checksums['primary'] = elm.find("%sopen-checksum" % ns).text | ||
641 | sumtypes['primary'] = elm.find("%sopen-checksum" % ns).attrib['type'] | ||
642 | break | ||
643 | |||
644 | if not primary_type: | ||
645 | continue | ||
646 | |||
647 | for item in ("primary", "patterns", "comps"): | ||
648 | if item not in filepaths: | ||
649 | filepaths[item] = None | ||
650 | continue | ||
651 | if not filepaths[item]: | ||
652 | continue | ||
653 | filepaths[item] = _get_metadata_from_repo(baseurl, | ||
654 | proxies, | ||
655 | cachedir, | ||
656 | reponame, | ||
657 | filepaths[item], | ||
658 | sumtypes[item], | ||
659 | checksums[item]) | ||
660 | |||
661 | """ Get repo key """ | ||
662 | try: | ||
663 | repokey = _get_metadata_from_repo(baseurl, | ||
664 | proxies, | ||
665 | cachedir, | ||
666 | reponame, | ||
667 | "repodata/repomd.xml.key") | ||
668 | except CreatorError: | ||
669 | repokey = None | ||
670 | msger.debug("\ncan't get %s/%s" % (baseurl, "repodata/repomd.xml.key")) | ||
671 | |||
672 | my_repo_metadata.append({"name":reponame, | ||
673 | "baseurl":baseurl, | ||
674 | "repomd":repomd, | ||
675 | "primary":filepaths['primary'], | ||
676 | "cachedir":cachedir, | ||
677 | "proxies":proxies, | ||
678 | "patterns":filepaths['patterns'], | ||
679 | "comps":filepaths['comps'], | ||
680 | "repokey":repokey}) | ||
681 | |||
682 | return my_repo_metadata | ||
683 | |||
684 | def get_rpmver_in_repo(repometadata): | ||
685 | for repo in repometadata: | ||
686 | if repo["primary"].endswith(".xml"): | ||
687 | root = xmlparse(repo["primary"]) | ||
688 | ns = root.getroot().tag | ||
689 | ns = ns[0:ns.rindex("}")+1] | ||
690 | |||
691 | versionlist = [] | ||
692 | for elm in root.getiterator("%spackage" % ns): | ||
693 | if elm.find("%sname" % ns).text == 'rpm': | ||
694 | for node in elm.getchildren(): | ||
695 | if node.tag == "%sversion" % ns: | ||
696 | versionlist.append(node.attrib['ver']) | ||
697 | |||
698 | if versionlist: | ||
699 | return reversed( | ||
700 | sorted( | ||
701 | versionlist, | ||
702 | key = lambda ver: map(int, ver.split('.')))).next() | ||
703 | |||
704 | elif repo["primary"].endswith(".sqlite"): | ||
705 | con = sqlite.connect(repo["primary"]) | ||
706 | for row in con.execute("select version from packages where " | ||
707 | "name=\"rpm\" ORDER by version DESC"): | ||
708 | con.close() | ||
709 | return row[0] | ||
710 | |||
711 | return None | ||
712 | |||
713 | def get_arch(repometadata): | ||
714 | archlist = [] | ||
715 | for repo in repometadata: | ||
716 | if repo["primary"].endswith(".xml"): | ||
717 | root = xmlparse(repo["primary"]) | ||
718 | ns = root.getroot().tag | ||
719 | ns = ns[0:ns.rindex("}")+1] | ||
720 | for elm in root.getiterator("%spackage" % ns): | ||
721 | if elm.find("%sarch" % ns).text not in ("noarch", "src"): | ||
722 | arch = elm.find("%sarch" % ns).text | ||
723 | if arch not in archlist: | ||
724 | archlist.append(arch) | ||
725 | elif repo["primary"].endswith(".sqlite"): | ||
726 | con = sqlite.connect(repo["primary"]) | ||
727 | for row in con.execute("select arch from packages where arch not in (\"src\", \"noarch\")"): | ||
728 | if row[0] not in archlist: | ||
729 | archlist.append(row[0]) | ||
730 | |||
731 | con.close() | ||
732 | |||
733 | uniq_arch = [] | ||
734 | for i in range(len(archlist)): | ||
735 | if archlist[i] not in rpmmisc.archPolicies.keys(): | ||
736 | continue | ||
737 | need_append = True | ||
738 | j = 0 | ||
739 | while j < len(uniq_arch): | ||
740 | if archlist[i] in rpmmisc.archPolicies[uniq_arch[j]].split(':'): | ||
741 | need_append = False | ||
742 | break | ||
743 | if uniq_arch[j] in rpmmisc.archPolicies[archlist[i]].split(':'): | ||
744 | if need_append: | ||
745 | uniq_arch[j] = archlist[i] | ||
746 | need_append = False | ||
747 | else: | ||
748 | uniq_arch.remove(uniq_arch[j]) | ||
749 | continue | ||
750 | j += 1 | ||
751 | if need_append: | ||
752 | uniq_arch.append(archlist[i]) | ||
753 | |||
754 | return uniq_arch, archlist | ||
755 | |||
756 | def get_package(pkg, repometadata, arch = None): | ||
757 | ver = "" | ||
758 | target_repo = None | ||
759 | if not arch: | ||
760 | arches = [] | ||
761 | elif arch not in rpmmisc.archPolicies: | ||
762 | arches = [arch] | ||
763 | else: | ||
764 | arches = rpmmisc.archPolicies[arch].split(':') | ||
765 | arches.append('noarch') | ||
766 | |||
767 | for repo in repometadata: | ||
768 | if repo["primary"].endswith(".xml"): | ||
769 | root = xmlparse(repo["primary"]) | ||
770 | ns = root.getroot().tag | ||
771 | ns = ns[0:ns.rindex("}")+1] | ||
772 | for elm in root.getiterator("%spackage" % ns): | ||
773 | if elm.find("%sname" % ns).text == pkg: | ||
774 | if elm.find("%sarch" % ns).text in arches: | ||
775 | version = elm.find("%sversion" % ns) | ||
776 | tmpver = "%s-%s" % (version.attrib['ver'], version.attrib['rel']) | ||
777 | if tmpver > ver: | ||
778 | ver = tmpver | ||
779 | location = elm.find("%slocation" % ns) | ||
780 | pkgpath = "%s" % location.attrib['href'] | ||
781 | target_repo = repo | ||
782 | break | ||
783 | if repo["primary"].endswith(".sqlite"): | ||
784 | con = sqlite.connect(repo["primary"]) | ||
785 | if arch: | ||
786 | sql = 'select version, release, location_href from packages ' \ | ||
787 | 'where name = "%s" and arch IN ("%s")' % \ | ||
788 | (pkg, '","'.join(arches)) | ||
789 | for row in con.execute(sql): | ||
790 | tmpver = "%s-%s" % (row[0], row[1]) | ||
791 | if tmpver > ver: | ||
792 | ver = tmpver | ||
793 | pkgpath = "%s" % row[2] | ||
794 | target_repo = repo | ||
795 | break | ||
796 | else: | ||
797 | sql = 'select version, release, location_href from packages ' \ | ||
798 | 'where name = "%s"' % pkg | ||
799 | for row in con.execute(sql): | ||
800 | tmpver = "%s-%s" % (row[0], row[1]) | ||
801 | if tmpver > ver: | ||
802 | ver = tmpver | ||
803 | pkgpath = "%s" % row[2] | ||
804 | target_repo = repo | ||
805 | break | ||
806 | con.close() | ||
807 | if target_repo: | ||
808 | makedirs("%s/packages/%s" % (target_repo["cachedir"], target_repo["name"])) | ||
809 | url = os.path.join(target_repo["baseurl"], pkgpath) | ||
810 | filename = str("%s/packages/%s/%s" % (target_repo["cachedir"], target_repo["name"], os.path.basename(pkgpath))) | ||
811 | if os.path.exists(filename): | ||
812 | ret = rpmmisc.checkRpmIntegrity('rpm', filename) | ||
813 | if ret == 0: | ||
814 | return filename | ||
815 | |||
816 | msger.warning("package %s is damaged: %s" % | ||
817 | (os.path.basename(filename), filename)) | ||
818 | os.unlink(filename) | ||
819 | |||
820 | pkg = myurlgrab(str(url), filename, target_repo["proxies"]) | ||
821 | return pkg | ||
822 | else: | ||
823 | return None | ||
824 | |||
825 | def get_source_name(pkg, repometadata): | ||
826 | |||
827 | def get_bin_name(pkg): | ||
828 | m = RPM_RE.match(pkg) | ||
829 | if m: | ||
830 | return m.group(1) | ||
831 | return None | ||
832 | |||
833 | def get_src_name(srpm): | ||
834 | m = SRPM_RE.match(srpm) | ||
835 | if m: | ||
836 | return m.group(1) | ||
837 | return None | ||
838 | |||
839 | ver = "" | ||
840 | target_repo = None | ||
841 | |||
842 | pkg_name = get_bin_name(pkg) | ||
843 | if not pkg_name: | ||
844 | return None | ||
845 | |||
846 | for repo in repometadata: | ||
847 | if repo["primary"].endswith(".xml"): | ||
848 | root = xmlparse(repo["primary"]) | ||
849 | ns = root.getroot().tag | ||
850 | ns = ns[0:ns.rindex("}")+1] | ||
851 | for elm in root.getiterator("%spackage" % ns): | ||
852 | if elm.find("%sname" % ns).text == pkg_name: | ||
853 | if elm.find("%sarch" % ns).text != "src": | ||
854 | version = elm.find("%sversion" % ns) | ||
855 | tmpver = "%s-%s" % (version.attrib['ver'], version.attrib['rel']) | ||
856 | if tmpver > ver: | ||
857 | ver = tmpver | ||
858 | fmt = elm.find("%sformat" % ns) | ||
859 | if fmt: | ||
860 | fns = fmt.getchildren()[0].tag | ||
861 | fns = fns[0:fns.rindex("}")+1] | ||
862 | pkgpath = fmt.find("%ssourcerpm" % fns).text | ||
863 | target_repo = repo | ||
864 | break | ||
865 | |||
866 | if repo["primary"].endswith(".sqlite"): | ||
867 | con = sqlite.connect(repo["primary"]) | ||
868 | for row in con.execute("select version, release, rpm_sourcerpm from packages where name = \"%s\" and arch != \"src\"" % pkg_name): | ||
869 | tmpver = "%s-%s" % (row[0], row[1]) | ||
870 | if tmpver > ver: | ||
871 | pkgpath = "%s" % row[2] | ||
872 | target_repo = repo | ||
873 | break | ||
874 | con.close() | ||
875 | if target_repo: | ||
876 | return get_src_name(pkgpath) | ||
877 | else: | ||
878 | return None | ||
879 | |||
880 | def get_pkglist_in_patterns(group, patterns): | ||
881 | found = False | ||
882 | pkglist = [] | ||
883 | try: | ||
884 | root = xmlparse(patterns) | ||
885 | except SyntaxError: | ||
886 | raise SyntaxError("%s syntax error." % patterns) | ||
887 | |||
888 | for elm in list(root.getroot()): | ||
889 | ns = elm.tag | ||
890 | ns = ns[0:ns.rindex("}")+1] | ||
891 | name = elm.find("%sname" % ns) | ||
892 | summary = elm.find("%ssummary" % ns) | ||
893 | if name.text == group or summary.text == group: | ||
894 | found = True | ||
895 | break | ||
896 | |||
897 | if not found: | ||
898 | return pkglist | ||
899 | |||
900 | found = False | ||
901 | for requires in list(elm): | ||
902 | if requires.tag.endswith("requires"): | ||
903 | found = True | ||
904 | break | ||
905 | |||
906 | if not found: | ||
907 | return pkglist | ||
908 | |||
909 | for pkg in list(requires): | ||
910 | pkgname = pkg.attrib["name"] | ||
911 | if pkgname not in pkglist: | ||
912 | pkglist.append(pkgname) | ||
913 | |||
914 | return pkglist | ||
915 | |||
916 | def get_pkglist_in_comps(group, comps): | ||
917 | found = False | ||
918 | pkglist = [] | ||
919 | try: | ||
920 | root = xmlparse(comps) | ||
921 | except SyntaxError: | ||
922 | raise SyntaxError("%s syntax error." % comps) | ||
923 | |||
924 | for elm in root.getiterator("group"): | ||
925 | id = elm.find("id") | ||
926 | name = elm.find("name") | ||
927 | if id.text == group or name.text == group: | ||
928 | packagelist = elm.find("packagelist") | ||
929 | found = True | ||
930 | break | ||
931 | |||
932 | if not found: | ||
933 | return pkglist | ||
934 | |||
935 | for require in elm.getiterator("packagereq"): | ||
936 | if require.tag.endswith("packagereq"): | ||
937 | pkgname = require.text | ||
938 | if pkgname not in pkglist: | ||
939 | pkglist.append(pkgname) | ||
940 | |||
941 | return pkglist | ||
942 | |||
943 | def is_statically_linked(binary): | ||
944 | return ", statically linked, " in runner.outs(['file', binary]) | ||
945 | |||
946 | def setup_qemu_emulator(rootdir, arch): | ||
947 | # mount binfmt_misc if it doesn't exist | ||
948 | if not os.path.exists("/proc/sys/fs/binfmt_misc"): | ||
949 | modprobecmd = find_binary_path("modprobe") | ||
950 | runner.show([modprobecmd, "binfmt_misc"]) | ||
951 | if not os.path.exists("/proc/sys/fs/binfmt_misc/register"): | ||
952 | mountcmd = find_binary_path("mount") | ||
953 | runner.show([mountcmd, "-t", "binfmt_misc", "none", "/proc/sys/fs/binfmt_misc"]) | ||
954 | |||
955 | # qemu_emulator is a special case, we can't use find_binary_path | ||
956 | # qemu emulator should be a statically-linked executable file | ||
957 | qemu_emulator = "/usr/bin/qemu-arm" | ||
958 | if not os.path.exists(qemu_emulator) or not is_statically_linked(qemu_emulator): | ||
959 | qemu_emulator = "/usr/bin/qemu-arm-static" | ||
960 | if not os.path.exists(qemu_emulator): | ||
961 | raise CreatorError("Please install a statically-linked qemu-arm") | ||
962 | |||
963 | # qemu emulator version check | ||
964 | armv7_list = [arch for arch in rpmmisc.archPolicies.keys() if arch.startswith('armv7')] | ||
965 | if arch in armv7_list: # need qemu (>=0.13.0) | ||
966 | qemuout = runner.outs([qemu_emulator, "-h"]) | ||
967 | m = re.search("version\s*([.\d]+)", qemuout) | ||
968 | if m: | ||
969 | qemu_version = m.group(1) | ||
970 | if qemu_version < "0.13": | ||
971 | raise CreatorError("Requires %s version >=0.13 for %s" % (qemu_emulator, arch)) | ||
972 | else: | ||
973 | msger.warning("Can't get version info of %s, please make sure it's higher than 0.13.0" % qemu_emulator) | ||
974 | |||
975 | if not os.path.exists(rootdir + "/usr/bin"): | ||
976 | makedirs(rootdir + "/usr/bin") | ||
977 | shutil.copy(qemu_emulator, rootdir + "/usr/bin/qemu-arm-static") | ||
978 | qemu_emulator = "/usr/bin/qemu-arm-static" | ||
979 | |||
980 | # disable selinux, selinux will block qemu emulator to run | ||
981 | if os.path.exists("/usr/sbin/setenforce"): | ||
982 | msger.info('Try to disable selinux') | ||
983 | runner.show(["/usr/sbin/setenforce", "0"]) | ||
984 | |||
985 | # unregister it if it has been registered and is a dynamically-linked executable | ||
986 | node = "/proc/sys/fs/binfmt_misc/arm" | ||
987 | if os.path.exists(node): | ||
988 | qemu_unregister_string = "-1\n" | ||
989 | fd = open("/proc/sys/fs/binfmt_misc/arm", "w") | ||
990 | fd.write(qemu_unregister_string) | ||
991 | fd.close() | ||
992 | |||
993 | # register qemu emulator for interpreting other arch executable file | ||
994 | if not os.path.exists(node): | ||
995 | qemu_arm_string = ":arm:M::\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x28\\x00:\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfa\\xff\\xff\\xff:%s:\n" % qemu_emulator | ||
996 | fd = open("/proc/sys/fs/binfmt_misc/register", "w") | ||
997 | fd.write(qemu_arm_string) | ||
998 | fd.close() | ||
999 | |||
1000 | return qemu_emulator | ||
1001 | |||
1002 | def SrcpkgsDownload(pkgs, repometadata, instroot, cachedir): | ||
1003 | def get_source_repometadata(repometadata): | ||
1004 | src_repometadata=[] | ||
1005 | for repo in repometadata: | ||
1006 | if repo["name"].endswith("-source"): | ||
1007 | src_repometadata.append(repo) | ||
1008 | if src_repometadata: | ||
1009 | return src_repometadata | ||
1010 | return None | ||
1011 | |||
1012 | def get_src_name(srpm): | ||
1013 | m = SRPM_RE.match(srpm) | ||
1014 | if m: | ||
1015 | return m.group(1) | ||
1016 | return None | ||
1017 | |||
1018 | src_repometadata = get_source_repometadata(repometadata) | ||
1019 | |||
1020 | if not src_repometadata: | ||
1021 | msger.warning("No source repo found") | ||
1022 | return None | ||
1023 | |||
1024 | src_pkgs = [] | ||
1025 | lpkgs_dict = {} | ||
1026 | lpkgs_path = [] | ||
1027 | for repo in src_repometadata: | ||
1028 | cachepath = "%s/%s/packages/*.src.rpm" %(cachedir, repo["name"]) | ||
1029 | lpkgs_path += glob.glob(cachepath) | ||
1030 | |||
1031 | for lpkg in lpkgs_path: | ||
1032 | lpkg_name = get_src_name(os.path.basename(lpkg)) | ||
1033 | lpkgs_dict[lpkg_name] = lpkg | ||
1034 | localpkgs = lpkgs_dict.keys() | ||
1035 | |||
1036 | cached_count = 0 | ||
1037 | destdir = instroot+'/usr/src/SRPMS' | ||
1038 | if not os.path.exists(destdir): | ||
1039 | os.makedirs(destdir) | ||
1040 | |||
1041 | srcpkgset = set() | ||
1042 | for _pkg in pkgs: | ||
1043 | srcpkg_name = get_source_name(_pkg, repometadata) | ||
1044 | if not srcpkg_name: | ||
1045 | continue | ||
1046 | srcpkgset.add(srcpkg_name) | ||
1047 | |||
1048 | for pkg in list(srcpkgset): | ||
1049 | if pkg in localpkgs: | ||
1050 | cached_count += 1 | ||
1051 | shutil.copy(lpkgs_dict[pkg], destdir) | ||
1052 | src_pkgs.append(os.path.basename(lpkgs_dict[pkg])) | ||
1053 | else: | ||
1054 | src_pkg = get_package(pkg, src_repometadata, 'src') | ||
1055 | if src_pkg: | ||
1056 | shutil.copy(src_pkg, destdir) | ||
1057 | src_pkgs.append(src_pkg) | ||
1058 | msger.info("%d source packages gotten from cache" % cached_count) | ||
1059 | |||
1060 | return src_pkgs | ||
1061 | |||
1062 | def strip_end(text, suffix): | ||
1063 | if not text.endswith(suffix): | ||
1064 | return text | ||
1065 | return text[:-len(suffix)] | ||
diff --git a/scripts/lib/mic/utils/oe/__init__.py b/scripts/lib/mic/utils/oe/__init__.py new file mode 100644 index 0000000000..d10e802116 --- /dev/null +++ b/scripts/lib/mic/utils/oe/__init__.py | |||
@@ -0,0 +1,22 @@ | |||
1 | # | ||
2 | # OpenEmbedded mic utils library | ||
3 | # | ||
4 | # Copyright (c) 2013, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # AUTHORS | ||
21 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
22 | # | ||
diff --git a/scripts/lib/mic/utils/oe/misc.py b/scripts/lib/mic/utils/oe/misc.py new file mode 100644 index 0000000000..7ad3aa9685 --- /dev/null +++ b/scripts/lib/mic/utils/oe/misc.py | |||
@@ -0,0 +1,144 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2013, Intel Corporation. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This module provides a place to collect various mic-related utils | ||
22 | # for the OpenEmbedded Image Tools. | ||
23 | # | ||
24 | # AUTHORS | ||
25 | # Tom Zanussi <tom.zanussi (at] linux.intel.com> | ||
26 | # | ||
27 | |||
28 | from mic import msger | ||
29 | from mic.utils import runner | ||
30 | |||
31 | def exec_cmd(cmd_and_args, as_shell = False, catch = 3): | ||
32 | """ | ||
33 | Execute command, catching stderr, stdout | ||
34 | |||
35 | Need to execute as_shell if the command uses wildcards | ||
36 | """ | ||
37 | msger.debug("exec_cmd: %s" % cmd_and_args) | ||
38 | args = cmd_and_args.split() | ||
39 | msger.debug(args) | ||
40 | |||
41 | if (as_shell): | ||
42 | rc, out = runner.runtool(cmd_and_args, catch) | ||
43 | else: | ||
44 | rc, out = runner.runtool(args, catch) | ||
45 | out = out.strip() | ||
46 | msger.debug("exec_cmd: output for %s (rc = %d): %s" % \ | ||
47 | (cmd_and_args, rc, out)) | ||
48 | |||
49 | if rc != 0: | ||
50 | # We don't throw exception when return code is not 0, because | ||
51 | # parted always fails to reload part table with loop devices. This | ||
52 | # prevents us from distinguishing real errors based on return | ||
53 | # code. | ||
54 | msger.warning("WARNING: %s returned '%s' instead of 0" % (cmd_and_args, rc)) | ||
55 | |||
56 | return (rc, out) | ||
57 | |||
58 | |||
59 | def exec_cmd_quiet(cmd_and_args, as_shell = False): | ||
60 | """ | ||
61 | Execute command, catching nothing in the output | ||
62 | |||
63 | Need to execute as_shell if the command uses wildcards | ||
64 | """ | ||
65 | return exec_cmd(cmd_and_args, as_shell, 0) | ||
66 | |||
67 | |||
68 | def exec_native_cmd(cmd_and_args, native_sysroot, catch = 3): | ||
69 | """ | ||
70 | Execute native command, catching stderr, stdout | ||
71 | |||
72 | Need to execute as_shell if the command uses wildcards | ||
73 | |||
74 | Always need to execute native commands as_shell | ||
75 | """ | ||
76 | native_paths = \ | ||
77 | "export PATH=%s/sbin:%s/usr/sbin:%s/usr/bin:$PATH" % \ | ||
78 | (native_sysroot, native_sysroot, native_sysroot) | ||
79 | native_cmd_and_args = "%s;%s" % (native_paths, cmd_and_args) | ||
80 | msger.debug("exec_native_cmd: %s" % cmd_and_args) | ||
81 | |||
82 | args = cmd_and_args.split() | ||
83 | msger.debug(args) | ||
84 | |||
85 | rc, out = exec_cmd(native_cmd_and_args, True, catch) | ||
86 | |||
87 | if rc == 127: # shell command-not-found | ||
88 | msger.error("A native (host) program required to build the image " | ||
89 | "was not found (see details above). Please make sure " | ||
90 | "it's installed and try again.") | ||
91 | |||
92 | return (rc, out) | ||
93 | |||
94 | |||
95 | def exec_native_cmd_quiet(cmd_and_args, native_sysroot): | ||
96 | """ | ||
97 | Execute native command, catching nothing in the output | ||
98 | |||
99 | Need to execute as_shell if the command uses wildcards | ||
100 | |||
101 | Always need to execute native commands as_shell | ||
102 | """ | ||
103 | return exec_native_cmd(cmd_and_args, native_sysroot, 0) | ||
104 | |||
105 | |||
106 | # kickstart doesn't support variable substution in commands, so this | ||
107 | # is our current simplistic scheme for supporting that | ||
108 | |||
109 | wks_vars = dict() | ||
110 | |||
111 | def get_wks_var(key): | ||
112 | return wks_vars[key] | ||
113 | |||
114 | def add_wks_var(key, val): | ||
115 | wks_vars[key] = val | ||
116 | |||
117 | BOOTDD_EXTRA_SPACE = 16384 | ||
118 | IMAGE_EXTRA_SPACE = 10240 | ||
119 | |||
120 | __bitbake_env_lines = "" | ||
121 | |||
122 | def set_bitbake_env_lines(bitbake_env_lines): | ||
123 | global __bitbake_env_lines | ||
124 | __bitbake_env_lines = bitbake_env_lines | ||
125 | |||
126 | def get_bitbake_env_lines(): | ||
127 | return __bitbake_env_lines | ||
128 | |||
129 | def get_line_val(line, key): | ||
130 | """ | ||
131 | Extract the value from the VAR="val" string | ||
132 | """ | ||
133 | if line.startswith(key + "="): | ||
134 | stripped_line = line.split('=')[1] | ||
135 | stripped_line = stripped_line.replace('\"', '') | ||
136 | return stripped_line | ||
137 | return None | ||
138 | |||
139 | def get_bitbake_var(key): | ||
140 | for line in __bitbake_env_lines.split('\n'): | ||
141 | if (get_line_val(line, key)): | ||
142 | val = get_line_val(line, key) | ||
143 | return val | ||
144 | return None | ||
diff --git a/scripts/lib/mic/utils/oe/package_manager.py b/scripts/lib/mic/utils/oe/package_manager.py new file mode 100644 index 0000000000..92ce98e2ce --- /dev/null +++ b/scripts/lib/mic/utils/oe/package_manager.py | |||
@@ -0,0 +1,810 @@ | |||
1 | # ex:ts=4:sw=4:sts=4:et | ||
2 | # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- | ||
3 | # | ||
4 | # Copyright (c) 2014, Enea AB. | ||
5 | # All rights reserved. | ||
6 | # | ||
7 | # This program is free software; you can redistribute it and/or modify | ||
8 | # it under the terms of the GNU General Public License version 2 as | ||
9 | # published by the Free Software Foundation. | ||
10 | # | ||
11 | # This program is distributed in the hope that it will be useful, | ||
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | # GNU General Public License for more details. | ||
15 | # | ||
16 | # You should have received a copy of the GNU General Public License along | ||
17 | # with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | # | ||
20 | # DESCRIPTION | ||
21 | # This implements the opkg package manager wrapper as a combination of | ||
22 | # meta/lib/oe/package_manager.py and bitbake/lib/bb/utils.py files and | ||
23 | # adaptation of those files to 'wic'. | ||
24 | # | ||
25 | # AUTHORS | ||
26 | # Adrian Calianu <adrian.calianu (at] enea.com> | ||
27 | # | ||
28 | # This file incorporates work covered by the following copyright and | ||
29 | # permission notice: | ||
30 | # | ||
31 | # meta/COPYING.GPLv2 (GPLv2) | ||
32 | # meta/COPYING.MIT (MIT) | ||
33 | # | ||
34 | # Copyright (C) 2004 Michael Lauer | ||
35 | # | ||
36 | # Permission to use, copy, modify, and/or distribute this software | ||
37 | # for any purpose with or without fee is hereby granted, provided | ||
38 | # that the above copyright notice and this permission notice appear | ||
39 | # in all copies. | ||
40 | # | ||
41 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL | ||
42 | # WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED | ||
43 | # WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE | ||
44 | # AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR | ||
45 | # CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS | ||
46 | # OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, | ||
47 | # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
48 | # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
49 | |||
50 | |||
51 | from abc import ABCMeta, abstractmethod | ||
52 | import os | ||
53 | import glob | ||
54 | import subprocess | ||
55 | import shutil | ||
56 | import multiprocessing | ||
57 | import re | ||
58 | import errno | ||
59 | import fcntl | ||
60 | |||
61 | from mic.utils.oe.misc import * | ||
62 | from mic import msger | ||
63 | |||
64 | def mkdirhier(directory): | ||
65 | """Create a directory like 'mkdir -p', but does not complain if | ||
66 | directory already exists like os.makedirs | ||
67 | """ | ||
68 | |||
69 | try: | ||
70 | os.makedirs(directory) | ||
71 | except OSError as e: | ||
72 | if e.errno != errno.EEXIST: | ||
73 | raise e | ||
74 | |||
75 | def remove(path, recurse=False): | ||
76 | """Equivalent to rm -f or rm -rf""" | ||
77 | if not path: | ||
78 | return | ||
79 | if recurse: | ||
80 | # shutil.rmtree(name) would be ideal but its too slow | ||
81 | subprocess.call(['rm', '-rf'] + glob.glob(path)) | ||
82 | return | ||
83 | for name in glob.glob(path): | ||
84 | try: | ||
85 | os.unlink(name) | ||
86 | except OSError as exc: | ||
87 | if exc.errno != errno.ENOENT: | ||
88 | raise | ||
89 | |||
90 | def lockfile(name, shared=False, retry=True): | ||
91 | """ | ||
92 | Use the file fn as a lock file, return when the lock has been acquired. | ||
93 | Returns a variable to pass to unlockfile(). | ||
94 | """ | ||
95 | dirname = os.path.dirname(name) | ||
96 | mkdirhier(dirname) | ||
97 | |||
98 | if not os.access(dirname, os.W_OK): | ||
99 | logger.error("Unable to acquire lock '%s', directory is not writable", | ||
100 | name) | ||
101 | sys.exit(1) | ||
102 | |||
103 | op = fcntl.LOCK_EX | ||
104 | if shared: | ||
105 | op = fcntl.LOCK_SH | ||
106 | if not retry: | ||
107 | op = op | fcntl.LOCK_NB | ||
108 | |||
109 | while True: | ||
110 | # If we leave the lockfiles lying around there is no problem | ||
111 | # but we should clean up after ourselves. This gives potential | ||
112 | # for races though. To work around this, when we acquire the lock | ||
113 | # we check the file we locked was still the lock file on disk. | ||
114 | # by comparing inode numbers. If they don't match or the lockfile | ||
115 | # no longer exists, we start again. | ||
116 | |||
117 | # This implementation is unfair since the last person to request the | ||
118 | # lock is the most likely to win it. | ||
119 | |||
120 | try: | ||
121 | lf = open(name, 'a+') | ||
122 | fileno = lf.fileno() | ||
123 | fcntl.flock(fileno, op) | ||
124 | statinfo = os.fstat(fileno) | ||
125 | if os.path.exists(lf.name): | ||
126 | statinfo2 = os.stat(lf.name) | ||
127 | if statinfo.st_ino == statinfo2.st_ino: | ||
128 | return lf | ||
129 | lf.close() | ||
130 | except Exception: | ||
131 | try: | ||
132 | lf.close() | ||
133 | except Exception: | ||
134 | pass | ||
135 | pass | ||
136 | if not retry: | ||
137 | return None | ||
138 | |||
139 | def unlockfile(lf): | ||
140 | """ | ||
141 | Unlock a file locked using lockfile() | ||
142 | """ | ||
143 | try: | ||
144 | # If we had a shared lock, we need to promote to exclusive before | ||
145 | # removing the lockfile. Attempt this, ignore failures. | ||
146 | fcntl.flock(lf.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB) | ||
147 | os.unlink(lf.name) | ||
148 | except (IOError, OSError): | ||
149 | pass | ||
150 | fcntl.flock(lf.fileno(), fcntl.LOCK_UN) | ||
151 | lf.close() | ||
152 | |||
153 | def which(path, item, direction = 0, history = False): | ||
154 | """ | ||
155 | Locate a file in a PATH | ||
156 | """ | ||
157 | |||
158 | hist = [] | ||
159 | paths = (path or "").split(':') | ||
160 | if direction != 0: | ||
161 | paths.reverse() | ||
162 | |||
163 | for p in paths: | ||
164 | next = os.path.join(p, item) | ||
165 | hist.append(next) | ||
166 | if os.path.exists(next): | ||
167 | if not os.path.isabs(next): | ||
168 | next = os.path.abspath(next) | ||
169 | if history: | ||
170 | return next, hist | ||
171 | return next | ||
172 | |||
173 | if history: | ||
174 | return "", hist | ||
175 | return "" | ||
176 | |||
177 | |||
178 | |||
179 | # this can be used by all PM backends to create the index files in parallel | ||
180 | def wic_create_index(arg): | ||
181 | index_cmd = arg | ||
182 | |||
183 | try: | ||
184 | msger.info("Executing '%s' ..." % index_cmd) | ||
185 | subprocess.check_output(index_cmd, stderr=subprocess.STDOUT, shell=True) | ||
186 | except subprocess.CalledProcessError as e: | ||
187 | return("Index creation command '%s' failed with return code %d:\n%s" % | ||
188 | (e.cmd, e.returncode, e.output)) | ||
189 | |||
190 | return None | ||
191 | |||
192 | |||
193 | class WicIndexer(object): | ||
194 | __metaclass__ = ABCMeta | ||
195 | |||
196 | def __init__(self, d, deploy_dir): | ||
197 | self.d = d | ||
198 | self.deploy_dir = deploy_dir | ||
199 | |||
200 | @abstractmethod | ||
201 | def write_index(self): | ||
202 | pass | ||
203 | |||
204 | class WicOpkgIndexer(WicIndexer): | ||
205 | def write_index(self): | ||
206 | arch_vars = ["ALL_MULTILIB_PACKAGE_ARCHS", | ||
207 | "SDK_PACKAGE_ARCHS", | ||
208 | "MULTILIB_ARCHS"] | ||
209 | |||
210 | opkg_index_cmd = which(os.getenv('PATH'), "opkg-make-index") | ||
211 | |||
212 | if not os.path.exists(os.path.join(self.deploy_dir, "Packages")): | ||
213 | open(os.path.join(self.deploy_dir, "Packages"), "w").close() | ||
214 | |||
215 | index_cmds = [] | ||
216 | for arch_var in arch_vars: | ||
217 | if self.d.has_key(arch_var): | ||
218 | archs = self.d[arch_var] | ||
219 | else: | ||
220 | archs = None | ||
221 | |||
222 | if archs is None: | ||
223 | continue | ||
224 | |||
225 | for arch in archs.split(): | ||
226 | pkgs_dir = os.path.join(self.deploy_dir, arch) | ||
227 | pkgs_file = os.path.join(pkgs_dir, "Packages") | ||
228 | |||
229 | if not os.path.isdir(pkgs_dir): | ||
230 | continue | ||
231 | |||
232 | if not os.path.exists(pkgs_file): | ||
233 | open(pkgs_file, "w").close() | ||
234 | |||
235 | index_cmds.append('%s -r %s -p %s -m %s' % | ||
236 | (opkg_index_cmd, pkgs_file, pkgs_file, pkgs_dir)) | ||
237 | |||
238 | if len(index_cmds) == 0: | ||
239 | msger.info("There are no packages in %s!" % self.deploy_dir) | ||
240 | return | ||
241 | |||
242 | nproc = multiprocessing.cpu_count() | ||
243 | pool = multiprocessing.Pool(nproc) | ||
244 | results = list(pool.imap(wic_create_index, index_cmds)) | ||
245 | pool.close() | ||
246 | pool.join() | ||
247 | |||
248 | for result in results: | ||
249 | if result is not None: | ||
250 | return(result) | ||
251 | |||
252 | class WicPkgsList(object): | ||
253 | __metaclass__ = ABCMeta | ||
254 | |||
255 | def __init__(self, d, rootfs_dir): | ||
256 | self.d = d | ||
257 | self.rootfs_dir = rootfs_dir | ||
258 | |||
259 | @abstractmethod | ||
260 | def list(self, format=None): | ||
261 | pass | ||
262 | |||
263 | |||
264 | class WicOpkgPkgsList(WicPkgsList): | ||
265 | def __init__(self, d, rootfs_dir, config_file): | ||
266 | super(WicOpkgPkgsList, self).__init__(d, rootfs_dir) | ||
267 | |||
268 | self.opkg_cmd = which(os.getenv('PATH'), "opkg-cl") | ||
269 | self.opkg_args = "-f %s -o %s " % (config_file, rootfs_dir) | ||
270 | if self.d.has_key("OPKG_ARGS"): | ||
271 | self.opkg_args += self.d["OPKG_ARGS"] | ||
272 | |||
273 | def list(self, format=None): | ||
274 | opkg_query_cmd = which(os.getenv('PATH'), "opkg-query-helper.py") | ||
275 | |||
276 | if format == "arch": | ||
277 | cmd = "%s %s status | %s -a" % \ | ||
278 | (self.opkg_cmd, self.opkg_args, opkg_query_cmd) | ||
279 | elif format == "file": | ||
280 | cmd = "%s %s status | %s -f" % \ | ||
281 | (self.opkg_cmd, self.opkg_args, opkg_query_cmd) | ||
282 | elif format == "ver": | ||
283 | cmd = "%s %s status | %s -v" % \ | ||
284 | (self.opkg_cmd, self.opkg_args, opkg_query_cmd) | ||
285 | elif format == "deps": | ||
286 | cmd = "%s %s status | %s" % \ | ||
287 | (self.opkg_cmd, self.opkg_args, opkg_query_cmd) | ||
288 | else: | ||
289 | cmd = "%s %s list_installed | cut -d' ' -f1" % \ | ||
290 | (self.opkg_cmd, self.opkg_args) | ||
291 | |||
292 | try: | ||
293 | output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True).strip() | ||
294 | except subprocess.CalledProcessError as e: | ||
295 | msger.error("Cannot get the installed packages list. Command '%s' " | ||
296 | "returned %d:\n%s" % (cmd, e.returncode, e.output)) | ||
297 | |||
298 | if output and format == "file": | ||
299 | tmp_output = "" | ||
300 | for line in output.split('\n'): | ||
301 | pkg, pkg_file, pkg_arch = line.split() | ||
302 | full_path = os.path.join(self.rootfs_dir, pkg_arch, pkg_file) | ||
303 | if os.path.exists(full_path): | ||
304 | tmp_output += "%s %s %s\n" % (pkg, full_path, pkg_arch) | ||
305 | else: | ||
306 | tmp_output += "%s %s %s\n" % (pkg, pkg_file, pkg_arch) | ||
307 | |||
308 | output = tmp_output | ||
309 | |||
310 | return output | ||
311 | |||
312 | |||
313 | class WicPackageManager(object): | ||
314 | """ | ||
315 | This is an abstract class. Do not instantiate this directly. | ||
316 | """ | ||
317 | __metaclass__ = ABCMeta | ||
318 | |||
319 | def __init__(self, d, pseudo, native_sysroot): | ||
320 | self.d = d | ||
321 | self.deploy_dir = None | ||
322 | self.deploy_lock = None | ||
323 | if self.d.has_key('PACKAGE_FEED_URIS'): | ||
324 | self.feed_uris = self.d['PACKAGE_FEED_URIS'] | ||
325 | else: | ||
326 | self.feed_uris = "" | ||
327 | self.pseudo = pseudo | ||
328 | self.native_sysroot = native_sysroot | ||
329 | |||
330 | """ | ||
331 | Update the package manager package database. | ||
332 | """ | ||
333 | @abstractmethod | ||
334 | def update(self): | ||
335 | pass | ||
336 | |||
337 | """ | ||
338 | Install a list of packages. 'pkgs' is a list object. If 'attempt_only' is | ||
339 | True, installation failures are ignored. | ||
340 | """ | ||
341 | @abstractmethod | ||
342 | def install(self, pkgs, attempt_only=False): | ||
343 | pass | ||
344 | |||
345 | """ | ||
346 | Remove a list of packages. 'pkgs' is a list object. If 'with_dependencies' | ||
347 | is False, the any dependencies are left in place. | ||
348 | """ | ||
349 | @abstractmethod | ||
350 | def remove(self, pkgs, with_dependencies=True): | ||
351 | pass | ||
352 | |||
353 | """ | ||
354 | This function creates the index files | ||
355 | """ | ||
356 | @abstractmethod | ||
357 | def write_index(self): | ||
358 | pass | ||
359 | |||
360 | @abstractmethod | ||
361 | def remove_packaging_data(self): | ||
362 | pass | ||
363 | |||
364 | @abstractmethod | ||
365 | def list_installed(self, format=None): | ||
366 | pass | ||
367 | |||
368 | @abstractmethod | ||
369 | def insert_feeds_uris(self): | ||
370 | pass | ||
371 | |||
372 | """ | ||
373 | Install complementary packages based upon the list of currently installed | ||
374 | packages e.g. locales, *-dev, *-dbg, etc. This will only attempt to install | ||
375 | these packages, if they don't exist then no error will occur. Note: every | ||
376 | backend needs to call this function explicitly after the normal package | ||
377 | installation | ||
378 | """ | ||
379 | def install_complementary(self, globs=None): | ||
380 | # we need to write the list of installed packages to a file because the | ||
381 | # oe-pkgdata-util reads it from a file | ||
382 | if self.d.has_key('WORKDIR'): | ||
383 | installed_pkgs_file = os.path.join(self.d['WORKDIR'], | ||
384 | "installed_pkgs.txt") | ||
385 | else: | ||
386 | msger.error("No WORKDIR provided!") | ||
387 | |||
388 | with open(installed_pkgs_file, "w+") as installed_pkgs: | ||
389 | installed_pkgs.write(self.list_installed("arch")) | ||
390 | |||
391 | if globs is None: | ||
392 | if self.d.has_key('IMAGE_INSTALL_COMPLEMENTARY'): | ||
393 | globs = self.d['IMAGE_INSTALL_COMPLEMENTARY'] | ||
394 | split_linguas = set() | ||
395 | |||
396 | if self.d.has_key('IMAGE_LINGUAS'): | ||
397 | for translation in self.d['IMAGE_LINGUAS'].split(): | ||
398 | split_linguas.add(translation) | ||
399 | split_linguas.add(translation.split('-')[0]) | ||
400 | |||
401 | split_linguas = sorted(split_linguas) | ||
402 | |||
403 | for lang in split_linguas: | ||
404 | globs += " *-locale-%s" % lang | ||
405 | |||
406 | if globs is None: | ||
407 | return | ||
408 | |||
409 | if not self.d.has_key('PKGDATA_DIR'): | ||
410 | msger.error("No PKGDATA_DIR provided!") | ||
411 | |||
412 | cmd = [which(os.getenv('PATH'), "oe-pkgdata-util"), | ||
413 | "glob", self.d['PKGDATA_DIR'], installed_pkgs_file, | ||
414 | globs] | ||
415 | |||
416 | rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot) | ||
417 | if rc != 0: | ||
418 | msger.error("Could not compute complementary packages list. Command " | ||
419 | "'%s' returned %d" % | ||
420 | (' '.join(cmd), rc)) | ||
421 | |||
422 | self.install(out.split(), attempt_only=True) | ||
423 | |||
424 | |||
425 | def deploy_dir_lock(self): | ||
426 | if self.deploy_dir is None: | ||
427 | raise RuntimeError("deploy_dir is not set!") | ||
428 | |||
429 | lock_file_name = os.path.join(self.deploy_dir, "deploy.lock") | ||
430 | |||
431 | self.deploy_lock = lockfile(lock_file_name) | ||
432 | |||
433 | def deploy_dir_unlock(self): | ||
434 | if self.deploy_lock is None: | ||
435 | return | ||
436 | |||
437 | unlockfile(self.deploy_lock) | ||
438 | |||
439 | self.deploy_lock = None | ||
440 | |||
441 | |||
442 | class WicOpkgPM(WicPackageManager): | ||
443 | def __init__(self, d, target_rootfs, config_file, archs, pseudo, native_sysroot, task_name='target'): | ||
444 | super(WicOpkgPM, self).__init__(d, pseudo, native_sysroot) | ||
445 | |||
446 | self.target_rootfs = target_rootfs | ||
447 | self.config_file = config_file | ||
448 | self.pkg_archs = archs | ||
449 | self.task_name = task_name | ||
450 | |||
451 | if self.d.has_key("DEPLOY_DIR_IPK"): | ||
452 | self.deploy_dir = self.d["DEPLOY_DIR_IPK"] | ||
453 | |||
454 | self.deploy_lock_file = os.path.join(self.deploy_dir, "deploy.lock") | ||
455 | self.opkg_cmd = which(os.getenv('PATH'), "opkg-cl") | ||
456 | self.opkg_args = "-f %s -o %s " % (self.config_file, target_rootfs) | ||
457 | if self.d.has_key("OPKG_ARGS"): | ||
458 | self.opkg_args += self.d["OPKG_ARGS"] | ||
459 | |||
460 | if self.d.has_key('OPKGLIBDIR'): | ||
461 | opkg_lib_dir = self.d['OPKGLIBDIR'] | ||
462 | else: | ||
463 | opkg_lib_dir = "" | ||
464 | |||
465 | if opkg_lib_dir[0] == "/": | ||
466 | opkg_lib_dir = opkg_lib_dir[1:] | ||
467 | |||
468 | self.opkg_dir = os.path.join(target_rootfs, opkg_lib_dir, "opkg") | ||
469 | |||
470 | mkdirhier(self.opkg_dir) | ||
471 | |||
472 | if self.d.has_key("TMPDIR"): | ||
473 | tmp_dir = self.d["TMPDIR"] | ||
474 | else: | ||
475 | tmp_dir = "" | ||
476 | |||
477 | self.saved_opkg_dir = '%s/saved/%s' % (tmp_dir, self.task_name) | ||
478 | if not os.path.exists('%s/saved' % tmp_dir): | ||
479 | mkdirhier('%s/saved' % tmp_dir) | ||
480 | |||
481 | if self.d.has_key('BUILD_IMAGES_FROM_FEEDS') and self.d['BUILD_IMAGES_FROM_FEEDS'] != "1": | ||
482 | self._create_config() | ||
483 | else: | ||
484 | self._create_custom_config() | ||
485 | |||
486 | self.indexer = WicOpkgIndexer(self.d, self.deploy_dir) | ||
487 | |||
488 | """ | ||
489 | This function will change a package's status in /var/lib/opkg/status file. | ||
490 | If 'packages' is None then the new_status will be applied to all | ||
491 | packages | ||
492 | """ | ||
493 | def mark_packages(self, status_tag, packages=None): | ||
494 | status_file = os.path.join(self.opkg_dir, "status") | ||
495 | |||
496 | with open(status_file, "r") as sf: | ||
497 | with open(status_file + ".tmp", "w+") as tmp_sf: | ||
498 | if packages is None: | ||
499 | tmp_sf.write(re.sub(r"Package: (.*?)\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)", | ||
500 | r"Package: \1\n\2Status: \3%s" % status_tag, | ||
501 | sf.read())) | ||
502 | else: | ||
503 | if type(packages).__name__ != "list": | ||
504 | raise TypeError("'packages' should be a list object") | ||
505 | |||
506 | status = sf.read() | ||
507 | for pkg in packages: | ||
508 | status = re.sub(r"Package: %s\n((?:[^\n]+\n)*?)Status: (.*)(?:unpacked|installed)" % pkg, | ||
509 | r"Package: %s\n\1Status: \2%s" % (pkg, status_tag), | ||
510 | status) | ||
511 | |||
512 | tmp_sf.write(status) | ||
513 | |||
514 | os.rename(status_file + ".tmp", status_file) | ||
515 | |||
516 | def _create_custom_config(self): | ||
517 | msger.info("Building from feeds activated!") | ||
518 | |||
519 | with open(self.config_file, "w+") as config_file: | ||
520 | priority = 1 | ||
521 | for arch in self.pkg_archs.split(): | ||
522 | config_file.write("arch %s %d\n" % (arch, priority)) | ||
523 | priority += 5 | ||
524 | |||
525 | if self.d.has_key('IPK_FEED_URIS'): | ||
526 | ipk_feed_uris = self.d['IPK_FEED_URIS'] | ||
527 | else: | ||
528 | ipk_feed_uris = "" | ||
529 | |||
530 | for line in ipk_feed_uris.split(): | ||
531 | feed_match = re.match("^[ \t]*(.*)##([^ \t]*)[ \t]*$", line) | ||
532 | |||
533 | if feed_match is not None: | ||
534 | feed_name = feed_match.group(1) | ||
535 | feed_uri = feed_match.group(2) | ||
536 | |||
537 | msger.info("Add %s feed with URL %s" % (feed_name, feed_uri)) | ||
538 | |||
539 | config_file.write("src/gz %s %s\n" % (feed_name, feed_uri)) | ||
540 | |||
541 | """ | ||
542 | Allow to use package deploy directory contents as quick devel-testing | ||
543 | feed. This creates individual feed configs for each arch subdir of those | ||
544 | specified as compatible for the current machine. | ||
545 | NOTE: Development-helper feature, NOT a full-fledged feed. | ||
546 | """ | ||
547 | if self.d.has_key('FEED_DEPLOYDIR_BASE_URI'): | ||
548 | feed_deploydir_base_dir = self.d['FEED_DEPLOYDIR_BASE_URI'] | ||
549 | else: | ||
550 | feed_deploydir_base_dir = "" | ||
551 | |||
552 | if feed_deploydir_base_dir != "": | ||
553 | for arch in self.pkg_archs.split(): | ||
554 | if self.d.has_key("sysconfdir"): | ||
555 | sysconfdir = self.d["sysconfdir"] | ||
556 | else: | ||
557 | sysconfdir = None | ||
558 | |||
559 | cfg_file_name = os.path.join(self.target_rootfs, | ||
560 | sysconfdir, | ||
561 | "opkg", | ||
562 | "local-%s-feed.conf" % arch) | ||
563 | |||
564 | with open(cfg_file_name, "w+") as cfg_file: | ||
565 | cfg_file.write("src/gz local-%s %s/%s" % | ||
566 | arch, | ||
567 | feed_deploydir_base_dir, | ||
568 | arch) | ||
569 | |||
570 | def _create_config(self): | ||
571 | with open(self.config_file, "w+") as config_file: | ||
572 | priority = 1 | ||
573 | for arch in self.pkg_archs.split(): | ||
574 | config_file.write("arch %s %d\n" % (arch, priority)) | ||
575 | priority += 5 | ||
576 | |||
577 | config_file.write("src oe file:%s\n" % self.deploy_dir) | ||
578 | |||
579 | for arch in self.pkg_archs.split(): | ||
580 | pkgs_dir = os.path.join(self.deploy_dir, arch) | ||
581 | if os.path.isdir(pkgs_dir): | ||
582 | config_file.write("src oe-%s file:%s\n" % | ||
583 | (arch, pkgs_dir)) | ||
584 | |||
585 | def insert_feeds_uris(self): | ||
586 | if self.feed_uris == "": | ||
587 | return | ||
588 | |||
589 | rootfs_config = os.path.join('%s/etc/opkg/base-feeds.conf' | ||
590 | % self.target_rootfs) | ||
591 | |||
592 | with open(rootfs_config, "w+") as config_file: | ||
593 | uri_iterator = 0 | ||
594 | for uri in self.feed_uris.split(): | ||
595 | config_file.write("src/gz url-%d %s/ipk\n" % | ||
596 | (uri_iterator, uri)) | ||
597 | |||
598 | for arch in self.pkg_archs.split(): | ||
599 | if not os.path.exists(os.path.join(self.deploy_dir, arch)): | ||
600 | continue | ||
601 | msger.info('Note: adding opkg channel url-%s-%d (%s)' % | ||
602 | (arch, uri_iterator, uri)) | ||
603 | |||
604 | config_file.write("src/gz uri-%s-%d %s/ipk/%s\n" % | ||
605 | (arch, uri_iterator, uri, arch)) | ||
606 | uri_iterator += 1 | ||
607 | |||
608 | def update(self): | ||
609 | self.deploy_dir_lock() | ||
610 | |||
611 | cmd = "%s %s update" % (self.opkg_cmd, self.opkg_args) | ||
612 | |||
613 | rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot) | ||
614 | if rc != 0: | ||
615 | self.deploy_dir_unlock() | ||
616 | msger.error("Unable to update the package index files. Command '%s' " | ||
617 | "returned %d" % (cmd, rc)) | ||
618 | |||
619 | self.deploy_dir_unlock() | ||
620 | |||
621 | def install(self, pkgs, attempt_only=False): | ||
622 | if attempt_only and len(pkgs) == 0: | ||
623 | return | ||
624 | |||
625 | cmd = "%s %s install %s" % (self.opkg_cmd, self.opkg_args, ' '.join(pkgs)) | ||
626 | |||
627 | os.environ['D'] = self.target_rootfs | ||
628 | os.environ['OFFLINE_ROOT'] = self.target_rootfs | ||
629 | os.environ['IPKG_OFFLINE_ROOT'] = self.target_rootfs | ||
630 | os.environ['OPKG_OFFLINE_ROOT'] = self.target_rootfs | ||
631 | if self.d.has_key('WORKDIR'): | ||
632 | os.environ['INTERCEPT_DIR'] = os.path.join(self.d['WORKDIR'], | ||
633 | "intercept_scripts") | ||
634 | else: | ||
635 | os.environ['INTERCEPT_DIR'] = "." | ||
636 | msger.warning("No WORKDIR provided!") | ||
637 | |||
638 | if self.d.has_key('STAGING_DIR_NATIVE'): | ||
639 | os.environ['NATIVE_ROOT'] = self.d['STAGING_DIR_NATIVE'] | ||
640 | else: | ||
641 | msger.error("No STAGING_DIR_NATIVE provided!") | ||
642 | |||
643 | rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot) | ||
644 | if rc != 0: | ||
645 | msger.error("Unable to install packages. " | ||
646 | "Command '%s' returned %d" % (cmd, rc)) | ||
647 | |||
648 | |||
649 | def remove(self, pkgs, with_dependencies=True): | ||
650 | if with_dependencies: | ||
651 | cmd = "%s %s --force-depends --force-remove --force-removal-of-dependent-packages remove %s" % \ | ||
652 | (self.opkg_cmd, self.opkg_args, ' '.join(pkgs)) | ||
653 | else: | ||
654 | cmd = "%s %s --force-depends remove %s" % \ | ||
655 | (self.opkg_cmd, self.opkg_args, ' '.join(pkgs)) | ||
656 | |||
657 | rc, out = exec_native_cmd(self.pseudo + cmd, self.native_sysroot) | ||
658 | if rc != 0: | ||
659 | msger.error("Unable to remove packages. Command '%s' " | ||
660 | "returned %d" % (cmd, rc)) | ||
661 | |||
662 | |||
663 | def write_index(self): | ||
664 | self.deploy_dir_lock() | ||
665 | |||
666 | result = self.indexer.write_index() | ||
667 | |||
668 | self.deploy_dir_unlock() | ||
669 | |||
670 | if result is not None: | ||
671 | msger.error(result) | ||
672 | |||
673 | def remove_packaging_data(self): | ||
674 | remove(self.opkg_dir, True) | ||
675 | # create the directory back, it's needed by PM lock | ||
676 | mkdirhier(self.opkg_dir) | ||
677 | |||
678 | def list_installed(self, format=None): | ||
679 | return WicOpkgPkgsList(self.d, self.target_rootfs, self.config_file).list(format) | ||
680 | |||
681 | def handle_bad_recommendations(self): | ||
682 | if self.d.has_key("BAD_RECOMMENDATIONS"): | ||
683 | bad_recommendations = self.d["BAD_RECOMMENDATIONS"] | ||
684 | else: | ||
685 | bad_recommendations = "" | ||
686 | |||
687 | if bad_recommendations.strip() == "": | ||
688 | return | ||
689 | |||
690 | status_file = os.path.join(self.opkg_dir, "status") | ||
691 | |||
692 | # If status file existed, it means the bad recommendations has already | ||
693 | # been handled | ||
694 | if os.path.exists(status_file): | ||
695 | return | ||
696 | |||
697 | cmd = "%s %s info " % (self.opkg_cmd, self.opkg_args) | ||
698 | |||
699 | with open(status_file, "w+") as status: | ||
700 | for pkg in bad_recommendations.split(): | ||
701 | pkg_info = cmd + pkg | ||
702 | |||
703 | try: | ||
704 | output = subprocess.check_output(pkg_info.split(), stderr=subprocess.STDOUT).strip() | ||
705 | except subprocess.CalledProcessError as e: | ||
706 | msger.error("Cannot get package info. Command '%s' " | ||
707 | "returned %d:\n%s" % (pkg_info, e.returncode, e.output)) | ||
708 | |||
709 | if output == "": | ||
710 | msger.info("Ignored bad recommendation: '%s' is " | ||
711 | "not a package" % pkg) | ||
712 | continue | ||
713 | |||
714 | for line in output.split('\n'): | ||
715 | if line.startswith("Status:"): | ||
716 | status.write("Status: deinstall hold not-installed\n") | ||
717 | else: | ||
718 | status.write(line + "\n") | ||
719 | |||
720 | ''' | ||
721 | The following function dummy installs pkgs and returns the log of output. | ||
722 | ''' | ||
723 | def dummy_install(self, pkgs): | ||
724 | if len(pkgs) == 0: | ||
725 | return | ||
726 | |||
727 | # Create an temp dir as opkg root for dummy installation | ||
728 | if self.d.has_key("TMPDIR"): | ||
729 | tmp_dir = self.d["TMPDIR"] | ||
730 | else: | ||
731 | tmp_dir = "." | ||
732 | msger.warning("No TMPDIR provided!") | ||
733 | |||
734 | temp_rootfs = '%s/opkg' % tmp_dir | ||
735 | temp_opkg_dir = os.path.join(temp_rootfs, 'var/lib/opkg') | ||
736 | mkdirhier(temp_opkg_dir) | ||
737 | |||
738 | opkg_args = "-f %s -o %s " % (self.config_file, temp_rootfs) | ||
739 | if self.d.has_key("OPKG_ARGS"): | ||
740 | opkg_args += self.d["OPKG_ARGS"] | ||
741 | |||
742 | cmd = "%s %s update" % (self.opkg_cmd, opkg_args) | ||
743 | try: | ||
744 | subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) | ||
745 | except subprocess.CalledProcessError as e: | ||
746 | msger.error("Unable to update. Command '%s' " | ||
747 | "returned %d:\n%s" % (cmd, e.returncode, e.output)) | ||
748 | |||
749 | # Dummy installation | ||
750 | cmd = "%s %s --noaction install %s " % (self.opkg_cmd, | ||
751 | opkg_args, | ||
752 | ' '.join(pkgs)) | ||
753 | try: | ||
754 | output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True) | ||
755 | except subprocess.CalledProcessError as e: | ||
756 | msger.error("Unable to dummy install packages. Command '%s' " | ||
757 | "returned %d:\n%s" % (cmd, e.returncode, e.output)) | ||
758 | |||
759 | remove(temp_rootfs, True) | ||
760 | |||
761 | return output | ||
762 | |||
763 | def backup_packaging_data(self): | ||
764 | # Save the opkglib for increment ipk image generation | ||
765 | if os.path.exists(self.saved_opkg_dir): | ||
766 | remove(self.saved_opkg_dir, True) | ||
767 | shutil.copytree(self.opkg_dir, | ||
768 | self.saved_opkg_dir, | ||
769 | symlinks=True) | ||
770 | |||
771 | def recover_packaging_data(self): | ||
772 | # Move the opkglib back | ||
773 | if os.path.exists(self.saved_opkg_dir): | ||
774 | if os.path.exists(self.opkg_dir): | ||
775 | remove(self.opkg_dir, True) | ||
776 | |||
777 | msger.info('Recover packaging data') | ||
778 | shutil.copytree(self.saved_opkg_dir, | ||
779 | self.opkg_dir, | ||
780 | symlinks=True) | ||
781 | |||
782 | |||
783 | def wic_generate_index_files(d): | ||
784 | if d.has_key('PACKAGE_CLASSES'): | ||
785 | classes = d['PACKAGE_CLASSES'].replace("package_", "").split() | ||
786 | else: | ||
787 | classes = "" | ||
788 | msger.warning("No PACKAGE_CLASSES provided!") | ||
789 | |||
790 | if d.has_key('DEPLOY_DIR_IPK'): | ||
791 | deploy_dir_ipk = d['DEPLOY_DIR_IPK'] | ||
792 | else: | ||
793 | deploy_dir_ipk = None | ||
794 | msger.warning("No DEPLOY_DIR_IPK provided!") | ||
795 | |||
796 | indexer_map = { | ||
797 | "ipk": (WicOpkgIndexer, deploy_dir_ipk) | ||
798 | } | ||
799 | |||
800 | result = None | ||
801 | |||
802 | for pkg_class in classes: | ||
803 | if not pkg_class in indexer_map: | ||
804 | continue | ||
805 | |||
806 | if os.path.exists(indexer_map[pkg_class][1]): | ||
807 | result = indexer_map[pkg_class][0](d, indexer_map[pkg_class][1]).write_index() | ||
808 | |||
809 | if result is not None: | ||
810 | msger.error(result) | ||
diff --git a/scripts/lib/mic/utils/partitionedfs.py b/scripts/lib/mic/utils/partitionedfs.py new file mode 100644 index 0000000000..6607466a83 --- /dev/null +++ b/scripts/lib/mic/utils/partitionedfs.py | |||
@@ -0,0 +1,782 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2009, 2010, 2011 Intel, Inc. | ||
4 | # Copyright (c) 2007, 2008 Red Hat, Inc. | ||
5 | # Copyright (c) 2008 Daniel P. Berrange | ||
6 | # Copyright (c) 2008 David P. Huff | ||
7 | # | ||
8 | # This program is free software; you can redistribute it and/or modify it | ||
9 | # under the terms of the GNU General Public License as published by the Free | ||
10 | # Software Foundation; version 2 of the License | ||
11 | # | ||
12 | # This program is distributed in the hope that it will be useful, but | ||
13 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
14 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
15 | # for more details. | ||
16 | # | ||
17 | # You should have received a copy of the GNU General Public License along | ||
18 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
19 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
20 | |||
21 | import os | ||
22 | |||
23 | from mic import msger | ||
24 | from mic.utils import runner | ||
25 | from mic.utils.errors import MountError | ||
26 | from mic.utils.fs_related import * | ||
27 | from mic.utils.gpt_parser import GptParser | ||
28 | from mic.utils.oe.misc import * | ||
29 | |||
30 | # Overhead of the MBR partitioning scheme (just one sector) | ||
31 | MBR_OVERHEAD = 1 | ||
32 | # Overhead of the GPT partitioning scheme | ||
33 | GPT_OVERHEAD = 34 | ||
34 | |||
35 | # Size of a sector in bytes | ||
36 | SECTOR_SIZE = 512 | ||
37 | |||
38 | class PartitionedMount(Mount): | ||
39 | def __init__(self, mountdir, skipformat = False): | ||
40 | Mount.__init__(self, mountdir) | ||
41 | self.disks = {} | ||
42 | self.partitions = [] | ||
43 | self.subvolumes = [] | ||
44 | self.mapped = False | ||
45 | self.mountOrder = [] | ||
46 | self.unmountOrder = [] | ||
47 | self.parted = find_binary_path("parted") | ||
48 | self.btrfscmd=None | ||
49 | self.skipformat = skipformat | ||
50 | self.snapshot_created = self.skipformat | ||
51 | # Size of a sector used in calculations | ||
52 | self.sector_size = SECTOR_SIZE | ||
53 | self._partitions_layed_out = False | ||
54 | |||
55 | def __add_disk(self, disk_name): | ||
56 | """ Add a disk 'disk_name' to the internal list of disks. Note, | ||
57 | 'disk_name' is the name of the disk in the target system | ||
58 | (e.g., sdb). """ | ||
59 | |||
60 | if disk_name in self.disks: | ||
61 | # We already have this disk | ||
62 | return | ||
63 | |||
64 | assert not self._partitions_layed_out | ||
65 | |||
66 | self.disks[disk_name] = \ | ||
67 | { 'disk': None, # Disk object | ||
68 | 'mapped': False, # True if kpartx mapping exists | ||
69 | 'numpart': 0, # Number of allocate partitions | ||
70 | 'partitions': [], # Indexes to self.partitions | ||
71 | 'offset': 0, # Offset of next partition (in sectors) | ||
72 | # Minimum required disk size to fit all partitions (in bytes) | ||
73 | 'min_size': 0, | ||
74 | 'ptable_format': "msdos" } # Partition table format | ||
75 | |||
76 | def add_disk(self, disk_name, disk_obj): | ||
77 | """ Add a disk object which have to be partitioned. More than one disk | ||
78 | can be added. In case of multiple disks, disk partitions have to be | ||
79 | added for each disk separately with 'add_partition()". """ | ||
80 | |||
81 | self.__add_disk(disk_name) | ||
82 | self.disks[disk_name]['disk'] = disk_obj | ||
83 | |||
84 | def __add_partition(self, part): | ||
85 | """ This is a helper function for 'add_partition()' which adds a | ||
86 | partition to the internal list of partitions. """ | ||
87 | |||
88 | assert not self._partitions_layed_out | ||
89 | |||
90 | self.partitions.append(part) | ||
91 | self.__add_disk(part['disk_name']) | ||
92 | |||
93 | def add_partition(self, size, disk_name, mountpoint, source_file = None, fstype = None, | ||
94 | label=None, fsopts = None, boot = False, align = None, | ||
95 | part_type = None): | ||
96 | """ Add the next partition. Prtitions have to be added in the | ||
97 | first-to-last order. """ | ||
98 | |||
99 | ks_pnum = len(self.partitions) | ||
100 | |||
101 | # Converting MB to sectors for parted | ||
102 | size = size * 1024 * 1024 / self.sector_size | ||
103 | |||
104 | # We need to handle subvolumes for btrfs | ||
105 | if fstype == "btrfs" and fsopts and fsopts.find("subvol=") != -1: | ||
106 | self.btrfscmd=find_binary_path("btrfs") | ||
107 | subvol = None | ||
108 | opts = fsopts.split(",") | ||
109 | for opt in opts: | ||
110 | if opt.find("subvol=") != -1: | ||
111 | subvol = opt.replace("subvol=", "").strip() | ||
112 | break | ||
113 | if not subvol: | ||
114 | raise MountError("No subvolume: %s" % fsopts) | ||
115 | self.subvolumes.append({'size': size, # In sectors | ||
116 | 'mountpoint': mountpoint, # Mount relative to chroot | ||
117 | 'fstype': fstype, # Filesystem type | ||
118 | 'fsopts': fsopts, # Filesystem mount options | ||
119 | 'disk_name': disk_name, # physical disk name holding partition | ||
120 | 'device': None, # kpartx device node for partition | ||
121 | 'mount': None, # Mount object | ||
122 | 'subvol': subvol, # Subvolume name | ||
123 | 'boot': boot, # Bootable flag | ||
124 | 'mounted': False # Mount flag | ||
125 | }) | ||
126 | |||
127 | # We still need partition for "/" or non-subvolume | ||
128 | if mountpoint == "/" or not fsopts or fsopts.find("subvol=") == -1: | ||
129 | # Don't need subvolume for "/" because it will be set as default subvolume | ||
130 | if fsopts and fsopts.find("subvol=") != -1: | ||
131 | opts = fsopts.split(",") | ||
132 | for opt in opts: | ||
133 | if opt.strip().startswith("subvol="): | ||
134 | opts.remove(opt) | ||
135 | break | ||
136 | fsopts = ",".join(opts) | ||
137 | |||
138 | part = { 'ks_pnum' : ks_pnum, # Partition number in the KS file | ||
139 | 'size': size, # In sectors | ||
140 | 'mountpoint': mountpoint, # Mount relative to chroot | ||
141 | 'source_file': source_file, # partition contents | ||
142 | 'fstype': fstype, # Filesystem type | ||
143 | 'fsopts': fsopts, # Filesystem mount options | ||
144 | 'label': label, # Partition label | ||
145 | 'disk_name': disk_name, # physical disk name holding partition | ||
146 | 'device': None, # kpartx device node for partition | ||
147 | 'mount': None, # Mount object | ||
148 | 'num': None, # Partition number | ||
149 | 'boot': boot, # Bootable flag | ||
150 | 'align': align, # Partition alignment | ||
151 | 'part_type' : part_type, # Partition type | ||
152 | 'partuuid': None } # Partition UUID (GPT-only) | ||
153 | |||
154 | self.__add_partition(part) | ||
155 | |||
156 | def layout_partitions(self, ptable_format = "msdos"): | ||
157 | """ Layout the partitions, meaning calculate the position of every | ||
158 | partition on the disk. The 'ptable_format' parameter defines the | ||
159 | partition table format, and may be either "msdos" or "gpt". """ | ||
160 | |||
161 | msger.debug("Assigning %s partitions to disks" % ptable_format) | ||
162 | |||
163 | if ptable_format not in ('msdos', 'gpt'): | ||
164 | raise MountError("Unknown partition table format '%s', supported " \ | ||
165 | "formats are: 'msdos' and 'gpt'" % ptable_format) | ||
166 | |||
167 | if self._partitions_layed_out: | ||
168 | return | ||
169 | |||
170 | self._partitions_layed_out = True | ||
171 | |||
172 | # Go through partitions in the order they are added in .ks file | ||
173 | for n in range(len(self.partitions)): | ||
174 | p = self.partitions[n] | ||
175 | |||
176 | if not self.disks.has_key(p['disk_name']): | ||
177 | raise MountError("No disk %s for partition %s" \ | ||
178 | % (p['disk_name'], p['mountpoint'])) | ||
179 | |||
180 | if p['part_type'] and ptable_format != 'gpt': | ||
181 | # The --part-type can also be implemented for MBR partitions, | ||
182 | # in which case it would map to the 1-byte "partition type" | ||
183 | # filed at offset 3 of the partition entry. | ||
184 | raise MountError("setting custom partition type is only " \ | ||
185 | "imlemented for GPT partitions") | ||
186 | |||
187 | # Get the disk where the partition is located | ||
188 | d = self.disks[p['disk_name']] | ||
189 | d['numpart'] += 1 | ||
190 | d['ptable_format'] = ptable_format | ||
191 | |||
192 | if d['numpart'] == 1: | ||
193 | if ptable_format == "msdos": | ||
194 | overhead = MBR_OVERHEAD | ||
195 | else: | ||
196 | overhead = GPT_OVERHEAD | ||
197 | |||
198 | # Skip one sector required for the partitioning scheme overhead | ||
199 | d['offset'] += overhead | ||
200 | # Steal few sectors from the first partition to offset for the | ||
201 | # partitioning overhead | ||
202 | p['size'] -= overhead | ||
203 | |||
204 | if p['align']: | ||
205 | # If not first partition and we do have alignment set we need | ||
206 | # to align the partition. | ||
207 | # FIXME: This leaves a empty spaces to the disk. To fill the | ||
208 | # gaps we could enlargea the previous partition? | ||
209 | |||
210 | # Calc how much the alignment is off. | ||
211 | align_sectors = d['offset'] % (p['align'] * 1024 / self.sector_size) | ||
212 | # We need to move forward to the next alignment point | ||
213 | align_sectors = (p['align'] * 1024 / self.sector_size) - align_sectors | ||
214 | |||
215 | msger.debug("Realignment for %s%s with %s sectors, original" | ||
216 | " offset %s, target alignment is %sK." % | ||
217 | (p['disk_name'], d['numpart'], align_sectors, | ||
218 | d['offset'], p['align'])) | ||
219 | |||
220 | # increase the offset so we actually start the partition on right alignment | ||
221 | d['offset'] += align_sectors | ||
222 | |||
223 | p['start'] = d['offset'] | ||
224 | d['offset'] += p['size'] | ||
225 | |||
226 | p['type'] = 'primary' | ||
227 | p['num'] = d['numpart'] | ||
228 | |||
229 | if d['ptable_format'] == "msdos": | ||
230 | if d['numpart'] > 2: | ||
231 | # Every logical partition requires an additional sector for | ||
232 | # the EBR, so steal the last sector from the end of each | ||
233 | # partition starting from the 3rd one for the EBR. This | ||
234 | # will make sure the logical partitions are aligned | ||
235 | # correctly. | ||
236 | p['size'] -= 1 | ||
237 | |||
238 | if d['numpart'] > 3: | ||
239 | p['type'] = 'logical' | ||
240 | p['num'] = d['numpart'] + 1 | ||
241 | |||
242 | d['partitions'].append(n) | ||
243 | msger.debug("Assigned %s to %s%d, sectors range %d-%d size %d " | ||
244 | "sectors (%d bytes)." \ | ||
245 | % (p['mountpoint'], p['disk_name'], p['num'], | ||
246 | p['start'], p['start'] + p['size'] - 1, | ||
247 | p['size'], p['size'] * self.sector_size)) | ||
248 | |||
249 | # Once all the partitions have been layed out, we can calculate the | ||
250 | # minumim disk sizes. | ||
251 | for disk_name, d in self.disks.items(): | ||
252 | d['min_size'] = d['offset'] | ||
253 | if d['ptable_format'] == 'gpt': | ||
254 | # Account for the backup partition table at the end of the disk | ||
255 | d['min_size'] += GPT_OVERHEAD | ||
256 | |||
257 | d['min_size'] *= self.sector_size | ||
258 | |||
259 | def __run_parted(self, args): | ||
260 | """ Run parted with arguments specified in the 'args' list. """ | ||
261 | |||
262 | args.insert(0, self.parted) | ||
263 | msger.debug(args) | ||
264 | |||
265 | rc, out = runner.runtool(args, catch = 3) | ||
266 | out = out.strip() | ||
267 | if out: | ||
268 | msger.debug('"parted" output: %s' % out) | ||
269 | |||
270 | if rc != 0: | ||
271 | # We don't throw exception when return code is not 0, because | ||
272 | # parted always fails to reload part table with loop devices. This | ||
273 | # prevents us from distinguishing real errors based on return | ||
274 | # code. | ||
275 | msger.debug("WARNING: parted returned '%s' instead of 0" % rc) | ||
276 | |||
277 | def __create_partition(self, device, parttype, fstype, start, size): | ||
278 | """ Create a partition on an image described by the 'device' object. """ | ||
279 | |||
280 | # Start is included to the size so we need to substract one from the end. | ||
281 | end = start + size - 1 | ||
282 | msger.debug("Added '%s' partition, sectors %d-%d, size %d sectors" % | ||
283 | (parttype, start, end, size)) | ||
284 | |||
285 | args = ["-s", device, "unit", "s", "mkpart", parttype] | ||
286 | if fstype: | ||
287 | args.extend([fstype]) | ||
288 | args.extend(["%d" % start, "%d" % end]) | ||
289 | |||
290 | return self.__run_parted(args) | ||
291 | |||
292 | def __format_disks(self): | ||
293 | self.layout_partitions() | ||
294 | |||
295 | if self.skipformat: | ||
296 | msger.debug("Skipping disk format, because skipformat flag is set.") | ||
297 | return | ||
298 | |||
299 | for dev in self.disks.keys(): | ||
300 | d = self.disks[dev] | ||
301 | msger.debug("Initializing partition table for %s" % \ | ||
302 | (d['disk'].device)) | ||
303 | self.__run_parted(["-s", d['disk'].device, "mklabel", | ||
304 | d['ptable_format']]) | ||
305 | |||
306 | msger.debug("Creating partitions") | ||
307 | |||
308 | for p in self.partitions: | ||
309 | d = self.disks[p['disk_name']] | ||
310 | if d['ptable_format'] == "msdos" and p['num'] == 5: | ||
311 | # The last sector of the 3rd partition was reserved for the EBR | ||
312 | # of the first _logical_ partition. This is why the extended | ||
313 | # partition should start one sector before the first logical | ||
314 | # partition. | ||
315 | self.__create_partition(d['disk'].device, "extended", | ||
316 | None, p['start'] - 1, | ||
317 | d['offset'] - p['start']) | ||
318 | |||
319 | if p['fstype'] == "swap": | ||
320 | parted_fs_type = "linux-swap" | ||
321 | elif p['fstype'] == "vfat": | ||
322 | parted_fs_type = "fat32" | ||
323 | elif p['fstype'] == "msdos": | ||
324 | parted_fs_type = "fat16" | ||
325 | else: | ||
326 | # Type for ext2/ext3/ext4/btrfs | ||
327 | parted_fs_type = "ext2" | ||
328 | |||
329 | # Boot ROM of OMAP boards require vfat boot partition to have an | ||
330 | # even number of sectors. | ||
331 | if p['mountpoint'] == "/boot" and p['fstype'] in ["vfat", "msdos"] \ | ||
332 | and p['size'] % 2: | ||
333 | msger.debug("Substracting one sector from '%s' partition to " \ | ||
334 | "get even number of sectors for the partition" % \ | ||
335 | p['mountpoint']) | ||
336 | p['size'] -= 1 | ||
337 | |||
338 | self.__create_partition(d['disk'].device, p['type'], | ||
339 | parted_fs_type, p['start'], p['size']) | ||
340 | |||
341 | if p['boot']: | ||
342 | if d['ptable_format'] == 'gpt': | ||
343 | flag_name = "legacy_boot" | ||
344 | else: | ||
345 | flag_name = "boot" | ||
346 | msger.debug("Set '%s' flag for partition '%s' on disk '%s'" % \ | ||
347 | (flag_name, p['num'], d['disk'].device)) | ||
348 | self.__run_parted(["-s", d['disk'].device, "set", | ||
349 | "%d" % p['num'], flag_name, "on"]) | ||
350 | |||
351 | # Parted defaults to enabling the lba flag for fat16 partitions, | ||
352 | # which causes compatibility issues with some firmware (and really | ||
353 | # isn't necessary). | ||
354 | if parted_fs_type == "fat16": | ||
355 | if d['ptable_format'] == 'msdos': | ||
356 | msger.debug("Disable 'lba' flag for partition '%s' on disk '%s'" % \ | ||
357 | (p['num'], d['disk'].device)) | ||
358 | self.__run_parted(["-s", d['disk'].device, "set", | ||
359 | "%d" % p['num'], "lba", "off"]) | ||
360 | |||
361 | # If the partition table format is "gpt", find out PARTUUIDs for all | ||
362 | # the partitions. And if users specified custom parition type UUIDs, | ||
363 | # set them. | ||
364 | for disk_name, disk in self.disks.items(): | ||
365 | if disk['ptable_format'] != 'gpt': | ||
366 | continue | ||
367 | |||
368 | pnum = 0 | ||
369 | gpt_parser = GptParser(d['disk'].device, SECTOR_SIZE) | ||
370 | # Iterate over all GPT partitions on this disk | ||
371 | for entry in gpt_parser.get_partitions(): | ||
372 | pnum += 1 | ||
373 | # Find the matching partition in the 'self.partitions' list | ||
374 | for n in d['partitions']: | ||
375 | p = self.partitions[n] | ||
376 | if p['num'] == pnum: | ||
377 | # Found, fetch PARTUUID (partition's unique ID) | ||
378 | p['partuuid'] = entry['part_uuid'] | ||
379 | msger.debug("PARTUUID for partition %d on disk '%s' " \ | ||
380 | "(mount point '%s') is '%s'" % (pnum, \ | ||
381 | disk_name, p['mountpoint'], p['partuuid'])) | ||
382 | if p['part_type']: | ||
383 | entry['type_uuid'] = p['part_type'] | ||
384 | msger.debug("Change type of partition %d on disk " \ | ||
385 | "'%s' (mount point '%s') to '%s'" % \ | ||
386 | (pnum, disk_name, p['mountpoint'], | ||
387 | p['part_type'])) | ||
388 | gpt_parser.change_partition(entry) | ||
389 | |||
390 | del gpt_parser | ||
391 | |||
392 | def __map_partitions(self): | ||
393 | """Load it if dm_snapshot isn't loaded. """ | ||
394 | load_module("dm_snapshot") | ||
395 | |||
396 | for dev in self.disks.keys(): | ||
397 | d = self.disks[dev] | ||
398 | if d['mapped']: | ||
399 | continue | ||
400 | |||
401 | msger.debug("Running kpartx on %s" % d['disk'].device ) | ||
402 | rc, kpartxOutput = runner.runtool([self.kpartx, "-l", "-v", d['disk'].device]) | ||
403 | kpartxOutput = kpartxOutput.splitlines() | ||
404 | |||
405 | if rc != 0: | ||
406 | raise MountError("Failed to query partition mapping for '%s'" % | ||
407 | d['disk'].device) | ||
408 | |||
409 | # Strip trailing blank and mask verbose output | ||
410 | i = 0 | ||
411 | while i < len(kpartxOutput) and kpartxOutput[i][0:4] != "loop": | ||
412 | i = i + 1 | ||
413 | kpartxOutput = kpartxOutput[i:] | ||
414 | |||
415 | # Make sure kpartx reported the right count of partitions | ||
416 | if len(kpartxOutput) != d['numpart']: | ||
417 | # If this disk has more than 3 partitions, then in case of MBR | ||
418 | # paritions there is an extended parition. Different versions | ||
419 | # of kpartx behave differently WRT the extended partition - | ||
420 | # some map it, some ignore it. This is why we do the below hack | ||
421 | # - if kpartx reported one more partition and the partition | ||
422 | # table type is "msdos" and the amount of partitions is more | ||
423 | # than 3, we just assume kpartx mapped the extended parition | ||
424 | # and we remove it. | ||
425 | if len(kpartxOutput) == d['numpart'] + 1 \ | ||
426 | and d['ptable_format'] == 'msdos' and len(kpartxOutput) > 3: | ||
427 | kpartxOutput.pop(3) | ||
428 | else: | ||
429 | raise MountError("Unexpected number of partitions from " \ | ||
430 | "kpartx: %d != %d" % \ | ||
431 | (len(kpartxOutput), d['numpart'])) | ||
432 | |||
433 | for i in range(len(kpartxOutput)): | ||
434 | line = kpartxOutput[i] | ||
435 | newdev = line.split()[0] | ||
436 | mapperdev = "/dev/mapper/" + newdev | ||
437 | loopdev = d['disk'].device + newdev[-1] | ||
438 | |||
439 | msger.debug("Dev %s: %s -> %s" % (newdev, loopdev, mapperdev)) | ||
440 | pnum = d['partitions'][i] | ||
441 | self.partitions[pnum]['device'] = loopdev | ||
442 | |||
443 | # grub's install wants partitions to be named | ||
444 | # to match their parent device + partition num | ||
445 | # kpartx doesn't work like this, so we add compat | ||
446 | # symlinks to point to /dev/mapper | ||
447 | if os.path.lexists(loopdev): | ||
448 | os.unlink(loopdev) | ||
449 | os.symlink(mapperdev, loopdev) | ||
450 | |||
451 | msger.debug("Adding partx mapping for %s" % d['disk'].device) | ||
452 | rc = runner.show([self.kpartx, "-v", "-a", d['disk'].device]) | ||
453 | |||
454 | if rc != 0: | ||
455 | # Make sure that the device maps are also removed on error case. | ||
456 | # The d['mapped'] isn't set to True if the kpartx fails so | ||
457 | # failed mapping will not be cleaned on cleanup either. | ||
458 | runner.quiet([self.kpartx, "-d", d['disk'].device]) | ||
459 | raise MountError("Failed to map partitions for '%s'" % | ||
460 | d['disk'].device) | ||
461 | |||
462 | # FIXME: there is a bit delay for multipath device setup, | ||
463 | # wait 10ms for the setup | ||
464 | import time | ||
465 | time.sleep(10) | ||
466 | d['mapped'] = True | ||
467 | |||
468 | def __unmap_partitions(self): | ||
469 | for dev in self.disks.keys(): | ||
470 | d = self.disks[dev] | ||
471 | if not d['mapped']: | ||
472 | continue | ||
473 | |||
474 | msger.debug("Removing compat symlinks") | ||
475 | for pnum in d['partitions']: | ||
476 | if self.partitions[pnum]['device'] != None: | ||
477 | os.unlink(self.partitions[pnum]['device']) | ||
478 | self.partitions[pnum]['device'] = None | ||
479 | |||
480 | msger.debug("Unmapping %s" % d['disk'].device) | ||
481 | rc = runner.quiet([self.kpartx, "-d", d['disk'].device]) | ||
482 | if rc != 0: | ||
483 | raise MountError("Failed to unmap partitions for '%s'" % | ||
484 | d['disk'].device) | ||
485 | |||
486 | d['mapped'] = False | ||
487 | |||
488 | def __calculate_mountorder(self): | ||
489 | msger.debug("Calculating mount order") | ||
490 | for p in self.partitions: | ||
491 | if p['mountpoint']: | ||
492 | self.mountOrder.append(p['mountpoint']) | ||
493 | self.unmountOrder.append(p['mountpoint']) | ||
494 | |||
495 | self.mountOrder.sort() | ||
496 | self.unmountOrder.sort() | ||
497 | self.unmountOrder.reverse() | ||
498 | |||
499 | def cleanup(self): | ||
500 | Mount.cleanup(self) | ||
501 | if self.disks: | ||
502 | self.__unmap_partitions() | ||
503 | for dev in self.disks.keys(): | ||
504 | d = self.disks[dev] | ||
505 | try: | ||
506 | d['disk'].cleanup() | ||
507 | except: | ||
508 | pass | ||
509 | |||
510 | def unmount(self): | ||
511 | self.__unmount_subvolumes() | ||
512 | for mp in self.unmountOrder: | ||
513 | if mp == 'swap': | ||
514 | continue | ||
515 | p = None | ||
516 | for p1 in self.partitions: | ||
517 | if p1['mountpoint'] == mp: | ||
518 | p = p1 | ||
519 | break | ||
520 | |||
521 | if p['mount'] != None: | ||
522 | try: | ||
523 | # Create subvolume snapshot here | ||
524 | if p['fstype'] == "btrfs" and p['mountpoint'] == "/" and not self.snapshot_created: | ||
525 | self.__create_subvolume_snapshots(p, p["mount"]) | ||
526 | p['mount'].cleanup() | ||
527 | except: | ||
528 | pass | ||
529 | p['mount'] = None | ||
530 | |||
531 | # Only for btrfs | ||
532 | def __get_subvolume_id(self, rootpath, subvol): | ||
533 | if not self.btrfscmd: | ||
534 | self.btrfscmd=find_binary_path("btrfs") | ||
535 | argv = [ self.btrfscmd, "subvolume", "list", rootpath ] | ||
536 | |||
537 | rc, out = runner.runtool(argv) | ||
538 | msger.debug(out) | ||
539 | |||
540 | if rc != 0: | ||
541 | raise MountError("Failed to get subvolume id from %s', return code: %d." % (rootpath, rc)) | ||
542 | |||
543 | subvolid = -1 | ||
544 | for line in out.splitlines(): | ||
545 | if line.endswith(" path %s" % subvol): | ||
546 | subvolid = line.split()[1] | ||
547 | if not subvolid.isdigit(): | ||
548 | raise MountError("Invalid subvolume id: %s" % subvolid) | ||
549 | subvolid = int(subvolid) | ||
550 | break | ||
551 | return subvolid | ||
552 | |||
553 | def __create_subvolume_metadata(self, p, pdisk): | ||
554 | if len(self.subvolumes) == 0: | ||
555 | return | ||
556 | |||
557 | argv = [ self.btrfscmd, "subvolume", "list", pdisk.mountdir ] | ||
558 | rc, out = runner.runtool(argv) | ||
559 | msger.debug(out) | ||
560 | |||
561 | if rc != 0: | ||
562 | raise MountError("Failed to get subvolume id from %s', return code: %d." % (pdisk.mountdir, rc)) | ||
563 | |||
564 | subvolid_items = out.splitlines() | ||
565 | subvolume_metadata = "" | ||
566 | for subvol in self.subvolumes: | ||
567 | for line in subvolid_items: | ||
568 | if line.endswith(" path %s" % subvol["subvol"]): | ||
569 | subvolid = line.split()[1] | ||
570 | if not subvolid.isdigit(): | ||
571 | raise MountError("Invalid subvolume id: %s" % subvolid) | ||
572 | |||
573 | subvolid = int(subvolid) | ||
574 | opts = subvol["fsopts"].split(",") | ||
575 | for opt in opts: | ||
576 | if opt.strip().startswith("subvol="): | ||
577 | opts.remove(opt) | ||
578 | break | ||
579 | fsopts = ",".join(opts) | ||
580 | subvolume_metadata += "%d\t%s\t%s\t%s\n" % (subvolid, subvol["subvol"], subvol['mountpoint'], fsopts) | ||
581 | |||
582 | if subvolume_metadata: | ||
583 | fd = open("%s/.subvolume_metadata" % pdisk.mountdir, "w") | ||
584 | fd.write(subvolume_metadata) | ||
585 | fd.close() | ||
586 | |||
587 | def __get_subvolume_metadata(self, p, pdisk): | ||
588 | subvolume_metadata_file = "%s/.subvolume_metadata" % pdisk.mountdir | ||
589 | if not os.path.exists(subvolume_metadata_file): | ||
590 | return | ||
591 | |||
592 | fd = open(subvolume_metadata_file, "r") | ||
593 | content = fd.read() | ||
594 | fd.close() | ||
595 | |||
596 | for line in content.splitlines(): | ||
597 | items = line.split("\t") | ||
598 | if items and len(items) == 4: | ||
599 | self.subvolumes.append({'size': 0, # In sectors | ||
600 | 'mountpoint': items[2], # Mount relative to chroot | ||
601 | 'fstype': "btrfs", # Filesystem type | ||
602 | 'fsopts': items[3] + ",subvol=%s" % items[1], # Filesystem mount options | ||
603 | 'disk_name': p['disk_name'], # physical disk name holding partition | ||
604 | 'device': None, # kpartx device node for partition | ||
605 | 'mount': None, # Mount object | ||
606 | 'subvol': items[1], # Subvolume name | ||
607 | 'boot': False, # Bootable flag | ||
608 | 'mounted': False # Mount flag | ||
609 | }) | ||
610 | |||
611 | def __create_subvolumes(self, p, pdisk): | ||
612 | """ Create all the subvolumes. """ | ||
613 | |||
614 | for subvol in self.subvolumes: | ||
615 | argv = [ self.btrfscmd, "subvolume", "create", pdisk.mountdir + "/" + subvol["subvol"]] | ||
616 | |||
617 | rc = runner.show(argv) | ||
618 | if rc != 0: | ||
619 | raise MountError("Failed to create subvolume '%s', return code: %d." % (subvol["subvol"], rc)) | ||
620 | |||
621 | # Set default subvolume, subvolume for "/" is default | ||
622 | subvol = None | ||
623 | for subvolume in self.subvolumes: | ||
624 | if subvolume["mountpoint"] == "/" and p['disk_name'] == subvolume['disk_name']: | ||
625 | subvol = subvolume | ||
626 | break | ||
627 | |||
628 | if subvol: | ||
629 | # Get default subvolume id | ||
630 | subvolid = self. __get_subvolume_id(pdisk.mountdir, subvol["subvol"]) | ||
631 | # Set default subvolume | ||
632 | if subvolid != -1: | ||
633 | rc = runner.show([ self.btrfscmd, "subvolume", "set-default", "%d" % subvolid, pdisk.mountdir]) | ||
634 | if rc != 0: | ||
635 | raise MountError("Failed to set default subvolume id: %d', return code: %d." % (subvolid, rc)) | ||
636 | |||
637 | self.__create_subvolume_metadata(p, pdisk) | ||
638 | |||
639 | def __mount_subvolumes(self, p, pdisk): | ||
640 | if self.skipformat: | ||
641 | # Get subvolume info | ||
642 | self.__get_subvolume_metadata(p, pdisk) | ||
643 | # Set default mount options | ||
644 | if len(self.subvolumes) != 0: | ||
645 | for subvol in self.subvolumes: | ||
646 | if subvol["mountpoint"] == p["mountpoint"] == "/": | ||
647 | opts = subvol["fsopts"].split(",") | ||
648 | for opt in opts: | ||
649 | if opt.strip().startswith("subvol="): | ||
650 | opts.remove(opt) | ||
651 | break | ||
652 | pdisk.fsopts = ",".join(opts) | ||
653 | break | ||
654 | |||
655 | if len(self.subvolumes) == 0: | ||
656 | # Return directly if no subvolumes | ||
657 | return | ||
658 | |||
659 | # Remount to make default subvolume mounted | ||
660 | rc = runner.show([self.umountcmd, pdisk.mountdir]) | ||
661 | if rc != 0: | ||
662 | raise MountError("Failed to umount %s" % pdisk.mountdir) | ||
663 | |||
664 | rc = runner.show([self.mountcmd, "-o", pdisk.fsopts, pdisk.disk.device, pdisk.mountdir]) | ||
665 | if rc != 0: | ||
666 | raise MountError("Failed to umount %s" % pdisk.mountdir) | ||
667 | |||
668 | for subvol in self.subvolumes: | ||
669 | if subvol["mountpoint"] == "/": | ||
670 | continue | ||
671 | subvolid = self. __get_subvolume_id(pdisk.mountdir, subvol["subvol"]) | ||
672 | if subvolid == -1: | ||
673 | msger.debug("WARNING: invalid subvolume %s" % subvol["subvol"]) | ||
674 | continue | ||
675 | # Replace subvolume name with subvolume ID | ||
676 | opts = subvol["fsopts"].split(",") | ||
677 | for opt in opts: | ||
678 | if opt.strip().startswith("subvol="): | ||
679 | opts.remove(opt) | ||
680 | break | ||
681 | |||
682 | opts.extend(["subvolrootid=0", "subvol=%s" % subvol["subvol"]]) | ||
683 | fsopts = ",".join(opts) | ||
684 | subvol['fsopts'] = fsopts | ||
685 | mountpoint = self.mountdir + subvol['mountpoint'] | ||
686 | makedirs(mountpoint) | ||
687 | rc = runner.show([self.mountcmd, "-o", fsopts, pdisk.disk.device, mountpoint]) | ||
688 | if rc != 0: | ||
689 | raise MountError("Failed to mount subvolume %s to %s" % (subvol["subvol"], mountpoint)) | ||
690 | subvol["mounted"] = True | ||
691 | |||
692 | def __unmount_subvolumes(self): | ||
693 | """ It may be called multiple times, so we need to chekc if it is still mounted. """ | ||
694 | for subvol in self.subvolumes: | ||
695 | if subvol["mountpoint"] == "/": | ||
696 | continue | ||
697 | if not subvol["mounted"]: | ||
698 | continue | ||
699 | mountpoint = self.mountdir + subvol['mountpoint'] | ||
700 | rc = runner.show([self.umountcmd, mountpoint]) | ||
701 | if rc != 0: | ||
702 | raise MountError("Failed to unmount subvolume %s from %s" % (subvol["subvol"], mountpoint)) | ||
703 | subvol["mounted"] = False | ||
704 | |||
705 | def __create_subvolume_snapshots(self, p, pdisk): | ||
706 | import time | ||
707 | |||
708 | if self.snapshot_created: | ||
709 | return | ||
710 | |||
711 | # Remount with subvolid=0 | ||
712 | rc = runner.show([self.umountcmd, pdisk.mountdir]) | ||
713 | if rc != 0: | ||
714 | raise MountError("Failed to umount %s" % pdisk.mountdir) | ||
715 | if pdisk.fsopts: | ||
716 | mountopts = pdisk.fsopts + ",subvolid=0" | ||
717 | else: | ||
718 | mountopts = "subvolid=0" | ||
719 | rc = runner.show([self.mountcmd, "-o", mountopts, pdisk.disk.device, pdisk.mountdir]) | ||
720 | if rc != 0: | ||
721 | raise MountError("Failed to umount %s" % pdisk.mountdir) | ||
722 | |||
723 | # Create all the subvolume snapshots | ||
724 | snapshotts = time.strftime("%Y%m%d-%H%M") | ||
725 | for subvol in self.subvolumes: | ||
726 | subvolpath = pdisk.mountdir + "/" + subvol["subvol"] | ||
727 | snapshotpath = subvolpath + "_%s-1" % snapshotts | ||
728 | rc = runner.show([ self.btrfscmd, "subvolume", "snapshot", subvolpath, snapshotpath ]) | ||
729 | if rc != 0: | ||
730 | raise MountError("Failed to create subvolume snapshot '%s' for '%s', return code: %d." % (snapshotpath, subvolpath, rc)) | ||
731 | |||
732 | self.snapshot_created = True | ||
733 | |||
734 | def __install_partition(self, num, source_file, start, size): | ||
735 | """ | ||
736 | Install source_file contents into a partition. | ||
737 | """ | ||
738 | if not source_file: # nothing to install | ||
739 | return | ||
740 | |||
741 | # Start is included in the size so need to substract one from the end. | ||
742 | end = start + size - 1 | ||
743 | msger.debug("Installed %s in partition %d, sectors %d-%d, size %d sectors" % (source_file, num, start, end, size)) | ||
744 | |||
745 | dd_cmd = "dd if=%s of=%s bs=%d seek=%d count=%d conv=notrunc" % \ | ||
746 | (source_file, self.image_file, self.sector_size, start, size) | ||
747 | rc, out = exec_cmd(dd_cmd) | ||
748 | |||
749 | |||
750 | def install(self, image_file): | ||
751 | msger.debug("Installing partitions") | ||
752 | |||
753 | self.image_file = image_file | ||
754 | |||
755 | for p in self.partitions: | ||
756 | d = self.disks[p['disk_name']] | ||
757 | if d['ptable_format'] == "msdos" and p['num'] == 5: | ||
758 | # The last sector of the 3rd partition was reserved for the EBR | ||
759 | # of the first _logical_ partition. This is why the extended | ||
760 | # partition should start one sector before the first logical | ||
761 | # partition. | ||
762 | self.__install_partition(p['num'], p['source_file'], | ||
763 | p['start'] - 1, | ||
764 | d['offset'] - p['start']) | ||
765 | |||
766 | self.__install_partition(p['num'], p['source_file'], | ||
767 | p['start'], p['size']) | ||
768 | |||
769 | def mount(self): | ||
770 | for dev in self.disks.keys(): | ||
771 | d = self.disks[dev] | ||
772 | d['disk'].create() | ||
773 | |||
774 | self.__format_disks() | ||
775 | |||
776 | self.__calculate_mountorder() | ||
777 | |||
778 | return | ||
779 | |||
780 | def resparse(self, size = None): | ||
781 | # Can't re-sparse a disk image - too hard | ||
782 | pass | ||
diff --git a/scripts/lib/mic/utils/proxy.py b/scripts/lib/mic/utils/proxy.py new file mode 100644 index 0000000000..91451a2d01 --- /dev/null +++ b/scripts/lib/mic/utils/proxy.py | |||
@@ -0,0 +1,183 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2010, 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import urlparse | ||
20 | |||
21 | _my_proxies = {} | ||
22 | _my_noproxy = None | ||
23 | _my_noproxy_list = [] | ||
24 | |||
25 | def set_proxy_environ(): | ||
26 | global _my_noproxy, _my_proxies | ||
27 | if not _my_proxies: | ||
28 | return | ||
29 | for key in _my_proxies.keys(): | ||
30 | os.environ[key + "_proxy"] = _my_proxies[key] | ||
31 | if not _my_noproxy: | ||
32 | return | ||
33 | os.environ["no_proxy"] = _my_noproxy | ||
34 | |||
35 | def unset_proxy_environ(): | ||
36 | for env in ('http_proxy', | ||
37 | 'https_proxy', | ||
38 | 'ftp_proxy', | ||
39 | 'all_proxy'): | ||
40 | if env in os.environ: | ||
41 | del os.environ[env] | ||
42 | |||
43 | ENV=env.upper() | ||
44 | if ENV in os.environ: | ||
45 | del os.environ[ENV] | ||
46 | |||
47 | def _set_proxies(proxy = None, no_proxy = None): | ||
48 | """Return a dictionary of scheme -> proxy server URL mappings. | ||
49 | """ | ||
50 | |||
51 | global _my_noproxy, _my_proxies | ||
52 | _my_proxies = {} | ||
53 | _my_noproxy = None | ||
54 | proxies = [] | ||
55 | if proxy: | ||
56 | proxies.append(("http_proxy", proxy)) | ||
57 | if no_proxy: | ||
58 | proxies.append(("no_proxy", no_proxy)) | ||
59 | |||
60 | # Get proxy settings from environment if not provided | ||
61 | if not proxy and not no_proxy: | ||
62 | proxies = os.environ.items() | ||
63 | |||
64 | # Remove proxy env variables, urllib2 can't handle them correctly | ||
65 | unset_proxy_environ() | ||
66 | |||
67 | for name, value in proxies: | ||
68 | name = name.lower() | ||
69 | if value and name[-6:] == '_proxy': | ||
70 | if name[0:2] != "no": | ||
71 | _my_proxies[name[:-6]] = value | ||
72 | else: | ||
73 | _my_noproxy = value | ||
74 | |||
75 | def _ip_to_int(ip): | ||
76 | ipint=0 | ||
77 | shift=24 | ||
78 | for dec in ip.split("."): | ||
79 | ipint |= int(dec) << shift | ||
80 | shift -= 8 | ||
81 | return ipint | ||
82 | |||
83 | def _int_to_ip(val): | ||
84 | ipaddr="" | ||
85 | shift=0 | ||
86 | for i in range(4): | ||
87 | dec = val >> shift | ||
88 | dec &= 0xff | ||
89 | ipaddr = ".%d%s" % (dec, ipaddr) | ||
90 | shift += 8 | ||
91 | return ipaddr[1:] | ||
92 | |||
93 | def _isip(host): | ||
94 | if host.replace(".", "").isdigit(): | ||
95 | return True | ||
96 | return False | ||
97 | |||
98 | def _set_noproxy_list(): | ||
99 | global _my_noproxy, _my_noproxy_list | ||
100 | _my_noproxy_list = [] | ||
101 | if not _my_noproxy: | ||
102 | return | ||
103 | for item in _my_noproxy.split(","): | ||
104 | item = item.strip() | ||
105 | if not item: | ||
106 | continue | ||
107 | |||
108 | if item[0] != '.' and item.find("/") == -1: | ||
109 | # Need to match it | ||
110 | _my_noproxy_list.append({"match":0,"needle":item}) | ||
111 | |||
112 | elif item[0] == '.': | ||
113 | # Need to match at tail | ||
114 | _my_noproxy_list.append({"match":1,"needle":item}) | ||
115 | |||
116 | elif item.find("/") > 3: | ||
117 | # IP/MASK, need to match at head | ||
118 | needle = item[0:item.find("/")].strip() | ||
119 | ip = _ip_to_int(needle) | ||
120 | netmask = 0 | ||
121 | mask = item[item.find("/")+1:].strip() | ||
122 | |||
123 | if mask.isdigit(): | ||
124 | netmask = int(mask) | ||
125 | netmask = ~((1<<(32-netmask)) - 1) | ||
126 | ip &= netmask | ||
127 | else: | ||
128 | shift=24 | ||
129 | netmask=0 | ||
130 | for dec in mask.split("."): | ||
131 | netmask |= int(dec) << shift | ||
132 | shift -= 8 | ||
133 | ip &= netmask | ||
134 | |||
135 | _my_noproxy_list.append({"match":2,"needle":ip,"netmask":netmask}) | ||
136 | |||
137 | def _isnoproxy(url): | ||
138 | (scheme, host, path, parm, query, frag) = urlparse.urlparse(url) | ||
139 | |||
140 | if '@' in host: | ||
141 | user_pass, host = host.split('@', 1) | ||
142 | |||
143 | if ':' in host: | ||
144 | host, port = host.split(':', 1) | ||
145 | |||
146 | hostisip = _isip(host) | ||
147 | for item in _my_noproxy_list: | ||
148 | if hostisip and item["match"] <= 1: | ||
149 | continue | ||
150 | |||
151 | if item["match"] == 2 and hostisip: | ||
152 | if (_ip_to_int(host) & item["netmask"]) == item["needle"]: | ||
153 | return True | ||
154 | |||
155 | if item["match"] == 0: | ||
156 | if host == item["needle"]: | ||
157 | return True | ||
158 | |||
159 | if item["match"] == 1: | ||
160 | if host.rfind(item["needle"]) > 0: | ||
161 | return True | ||
162 | |||
163 | return False | ||
164 | |||
165 | def set_proxies(proxy = None, no_proxy = None): | ||
166 | _set_proxies(proxy, no_proxy) | ||
167 | _set_noproxy_list() | ||
168 | set_proxy_environ() | ||
169 | |||
170 | def get_proxy_for(url): | ||
171 | if url.startswith('file:') or _isnoproxy(url): | ||
172 | return None | ||
173 | |||
174 | type = url[0:url.index(":")] | ||
175 | proxy = None | ||
176 | if _my_proxies.has_key(type): | ||
177 | proxy = _my_proxies[type] | ||
178 | elif _my_proxies.has_key("http"): | ||
179 | proxy = _my_proxies["http"] | ||
180 | else: | ||
181 | proxy = None | ||
182 | |||
183 | return proxy | ||
diff --git a/scripts/lib/mic/utils/rpmmisc.py b/scripts/lib/mic/utils/rpmmisc.py new file mode 100644 index 0000000000..af15763e18 --- /dev/null +++ b/scripts/lib/mic/utils/rpmmisc.py | |||
@@ -0,0 +1,600 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2008, 2009, 2010, 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import sys | ||
20 | import re | ||
21 | import rpm | ||
22 | |||
23 | from mic import msger | ||
24 | from mic.utils.errors import CreatorError | ||
25 | from mic.utils.proxy import get_proxy_for | ||
26 | from mic.utils import runner | ||
27 | |||
28 | |||
29 | class RPMInstallCallback: | ||
30 | """ Command line callback class for callbacks from the RPM library. | ||
31 | """ | ||
32 | |||
33 | def __init__(self, ts, output=1): | ||
34 | self.output = output | ||
35 | self.callbackfilehandles = {} | ||
36 | self.total_actions = 0 | ||
37 | self.total_installed = 0 | ||
38 | self.installed_pkg_names = [] | ||
39 | self.total_removed = 0 | ||
40 | self.mark = "+" | ||
41 | self.marks = 40 | ||
42 | self.lastmsg = None | ||
43 | self.tsInfo = None # this needs to be set for anything else to work | ||
44 | self.ts = ts | ||
45 | self.filelog = False | ||
46 | self.logString = [] | ||
47 | self.headmsg = "Installing" | ||
48 | |||
49 | def _dopkgtup(self, hdr): | ||
50 | tmpepoch = hdr['epoch'] | ||
51 | if tmpepoch is None: epoch = '0' | ||
52 | else: epoch = str(tmpepoch) | ||
53 | |||
54 | return (hdr['name'], hdr['arch'], epoch, hdr['version'], hdr['release']) | ||
55 | |||
56 | def _makeHandle(self, hdr): | ||
57 | handle = '%s:%s.%s-%s-%s' % (hdr['epoch'], hdr['name'], hdr['version'], | ||
58 | hdr['release'], hdr['arch']) | ||
59 | |||
60 | return handle | ||
61 | |||
62 | def _localprint(self, msg): | ||
63 | if self.output: | ||
64 | msger.info(msg) | ||
65 | |||
66 | def _makefmt(self, percent, progress = True): | ||
67 | l = len(str(self.total_actions)) | ||
68 | size = "%s.%s" % (l, l) | ||
69 | fmt_done = "[%" + size + "s/%" + size + "s]" | ||
70 | done = fmt_done % (self.total_installed + self.total_removed, | ||
71 | self.total_actions) | ||
72 | marks = self.marks - (2 * l) | ||
73 | width = "%s.%s" % (marks, marks) | ||
74 | fmt_bar = "%-" + width + "s" | ||
75 | if progress: | ||
76 | bar = fmt_bar % (self.mark * int(marks * (percent / 100.0)), ) | ||
77 | fmt = "\r %-10.10s: %-20.20s " + bar + " " + done | ||
78 | else: | ||
79 | bar = fmt_bar % (self.mark * marks, ) | ||
80 | fmt = " %-10.10s: %-20.20s " + bar + " " + done | ||
81 | return fmt | ||
82 | |||
83 | def _logPkgString(self, hdr): | ||
84 | """return nice representation of the package for the log""" | ||
85 | (n,a,e,v,r) = self._dopkgtup(hdr) | ||
86 | if e == '0': | ||
87 | pkg = '%s.%s %s-%s' % (n, a, v, r) | ||
88 | else: | ||
89 | pkg = '%s.%s %s:%s-%s' % (n, a, e, v, r) | ||
90 | |||
91 | return pkg | ||
92 | |||
93 | def callback(self, what, bytes, total, h, user): | ||
94 | if what == rpm.RPMCALLBACK_TRANS_START: | ||
95 | if bytes == 6: | ||
96 | self.total_actions = total | ||
97 | |||
98 | elif what == rpm.RPMCALLBACK_TRANS_PROGRESS: | ||
99 | pass | ||
100 | |||
101 | elif what == rpm.RPMCALLBACK_TRANS_STOP: | ||
102 | pass | ||
103 | |||
104 | elif what == rpm.RPMCALLBACK_INST_OPEN_FILE: | ||
105 | self.lastmsg = None | ||
106 | hdr = None | ||
107 | if h is not None: | ||
108 | try: | ||
109 | hdr, rpmloc = h | ||
110 | except: | ||
111 | rpmloc = h | ||
112 | hdr = readRpmHeader(self.ts, h) | ||
113 | |||
114 | handle = self._makeHandle(hdr) | ||
115 | fd = os.open(rpmloc, os.O_RDONLY) | ||
116 | self.callbackfilehandles[handle]=fd | ||
117 | if hdr['name'] not in self.installed_pkg_names: | ||
118 | self.installed_pkg_names.append(hdr['name']) | ||
119 | self.total_installed += 1 | ||
120 | return fd | ||
121 | else: | ||
122 | self._localprint("No header - huh?") | ||
123 | |||
124 | elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE: | ||
125 | hdr = None | ||
126 | if h is not None: | ||
127 | try: | ||
128 | hdr, rpmloc = h | ||
129 | except: | ||
130 | rpmloc = h | ||
131 | hdr = readRpmHeader(self.ts, h) | ||
132 | |||
133 | handle = self._makeHandle(hdr) | ||
134 | os.close(self.callbackfilehandles[handle]) | ||
135 | fd = 0 | ||
136 | |||
137 | # log stuff | ||
138 | #pkgtup = self._dopkgtup(hdr) | ||
139 | self.logString.append(self._logPkgString(hdr)) | ||
140 | |||
141 | elif what == rpm.RPMCALLBACK_INST_PROGRESS: | ||
142 | if h is not None: | ||
143 | percent = (self.total_installed*100L)/self.total_actions | ||
144 | if total > 0: | ||
145 | try: | ||
146 | hdr, rpmloc = h | ||
147 | except: | ||
148 | rpmloc = h | ||
149 | |||
150 | m = re.match("(.*)-(\d+.*)-(\d+\.\d+)\.(.+)\.rpm", os.path.basename(rpmloc)) | ||
151 | if m: | ||
152 | pkgname = m.group(1) | ||
153 | else: | ||
154 | pkgname = os.path.basename(rpmloc) | ||
155 | if self.output: | ||
156 | fmt = self._makefmt(percent) | ||
157 | msg = fmt % (self.headmsg, pkgname) | ||
158 | if msg != self.lastmsg: | ||
159 | self.lastmsg = msg | ||
160 | |||
161 | msger.info(msg) | ||
162 | |||
163 | if self.total_installed == self.total_actions: | ||
164 | msger.raw('') | ||
165 | msger.verbose('\n'.join(self.logString)) | ||
166 | |||
167 | elif what == rpm.RPMCALLBACK_UNINST_START: | ||
168 | pass | ||
169 | |||
170 | elif what == rpm.RPMCALLBACK_UNINST_PROGRESS: | ||
171 | pass | ||
172 | |||
173 | elif what == rpm.RPMCALLBACK_UNINST_STOP: | ||
174 | self.total_removed += 1 | ||
175 | |||
176 | elif what == rpm.RPMCALLBACK_REPACKAGE_START: | ||
177 | pass | ||
178 | |||
179 | elif what == rpm.RPMCALLBACK_REPACKAGE_STOP: | ||
180 | pass | ||
181 | |||
182 | elif what == rpm.RPMCALLBACK_REPACKAGE_PROGRESS: | ||
183 | pass | ||
184 | |||
185 | def readRpmHeader(ts, filename): | ||
186 | """ Read an rpm header. """ | ||
187 | |||
188 | fd = os.open(filename, os.O_RDONLY) | ||
189 | h = ts.hdrFromFdno(fd) | ||
190 | os.close(fd) | ||
191 | return h | ||
192 | |||
193 | def splitFilename(filename): | ||
194 | """ Pass in a standard style rpm fullname | ||
195 | |||
196 | Return a name, version, release, epoch, arch, e.g.:: | ||
197 | foo-1.0-1.i386.rpm returns foo, 1.0, 1, i386 | ||
198 | 1:bar-9-123a.ia64.rpm returns bar, 9, 123a, 1, ia64 | ||
199 | """ | ||
200 | |||
201 | if filename[-4:] == '.rpm': | ||
202 | filename = filename[:-4] | ||
203 | |||
204 | archIndex = filename.rfind('.') | ||
205 | arch = filename[archIndex+1:] | ||
206 | |||
207 | relIndex = filename[:archIndex].rfind('-') | ||
208 | rel = filename[relIndex+1:archIndex] | ||
209 | |||
210 | verIndex = filename[:relIndex].rfind('-') | ||
211 | ver = filename[verIndex+1:relIndex] | ||
212 | |||
213 | epochIndex = filename.find(':') | ||
214 | if epochIndex == -1: | ||
215 | epoch = '' | ||
216 | else: | ||
217 | epoch = filename[:epochIndex] | ||
218 | |||
219 | name = filename[epochIndex + 1:verIndex] | ||
220 | return name, ver, rel, epoch, arch | ||
221 | |||
222 | def getCanonX86Arch(arch): | ||
223 | # | ||
224 | if arch == "i586": | ||
225 | f = open("/proc/cpuinfo", "r") | ||
226 | lines = f.readlines() | ||
227 | f.close() | ||
228 | for line in lines: | ||
229 | if line.startswith("model name") and line.find("Geode(TM)") != -1: | ||
230 | return "geode" | ||
231 | return arch | ||
232 | # only athlon vs i686 isn't handled with uname currently | ||
233 | if arch != "i686": | ||
234 | return arch | ||
235 | |||
236 | # if we're i686 and AuthenticAMD, then we should be an athlon | ||
237 | f = open("/proc/cpuinfo", "r") | ||
238 | lines = f.readlines() | ||
239 | f.close() | ||
240 | for line in lines: | ||
241 | if line.startswith("vendor") and line.find("AuthenticAMD") != -1: | ||
242 | return "athlon" | ||
243 | # i686 doesn't guarantee cmov, but we depend on it | ||
244 | elif line.startswith("flags") and line.find("cmov") == -1: | ||
245 | return "i586" | ||
246 | |||
247 | return arch | ||
248 | |||
249 | def getCanonX86_64Arch(arch): | ||
250 | if arch != "x86_64": | ||
251 | return arch | ||
252 | |||
253 | vendor = None | ||
254 | f = open("/proc/cpuinfo", "r") | ||
255 | lines = f.readlines() | ||
256 | f.close() | ||
257 | for line in lines: | ||
258 | if line.startswith("vendor_id"): | ||
259 | vendor = line.split(':')[1] | ||
260 | break | ||
261 | if vendor is None: | ||
262 | return arch | ||
263 | |||
264 | if vendor.find("Authentic AMD") != -1 or vendor.find("AuthenticAMD") != -1: | ||
265 | return "amd64" | ||
266 | if vendor.find("GenuineIntel") != -1: | ||
267 | return "ia32e" | ||
268 | return arch | ||
269 | |||
270 | def getCanonArch(): | ||
271 | arch = os.uname()[4] | ||
272 | |||
273 | if (len(arch) == 4 and arch[0] == "i" and arch[2:4] == "86"): | ||
274 | return getCanonX86Arch(arch) | ||
275 | |||
276 | if arch == "x86_64": | ||
277 | return getCanonX86_64Arch(arch) | ||
278 | |||
279 | return arch | ||
280 | |||
281 | # Copy from libsatsolver:poolarch.c, with cleanup | ||
282 | archPolicies = { | ||
283 | "x86_64": "x86_64:i686:i586:i486:i386", | ||
284 | "i686": "i686:i586:i486:i386", | ||
285 | "i586": "i586:i486:i386", | ||
286 | "ia64": "ia64:i686:i586:i486:i386", | ||
287 | "armv7tnhl": "armv7tnhl:armv7thl:armv7nhl:armv7hl", | ||
288 | "armv7thl": "armv7thl:armv7hl", | ||
289 | "armv7nhl": "armv7nhl:armv7hl", | ||
290 | "armv7hl": "armv7hl", | ||
291 | "armv7l": "armv7l:armv6l:armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l", | ||
292 | "armv6l": "armv6l:armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l", | ||
293 | "armv5tejl": "armv5tejl:armv5tel:armv5l:armv4tl:armv4l:armv3l", | ||
294 | "armv5tel": "armv5tel:armv5l:armv4tl:armv4l:armv3l", | ||
295 | "armv5l": "armv5l:armv4tl:armv4l:armv3l", | ||
296 | } | ||
297 | |||
298 | # dict mapping arch -> ( multicompat, best personality, biarch personality ) | ||
299 | multilibArches = { | ||
300 | "x86_64": ( "athlon", "x86_64", "athlon" ), | ||
301 | } | ||
302 | |||
303 | # from yumUtils.py | ||
304 | arches = { | ||
305 | # ia32 | ||
306 | "athlon": "i686", | ||
307 | "i686": "i586", | ||
308 | "geode": "i586", | ||
309 | "i586": "i486", | ||
310 | "i486": "i386", | ||
311 | "i386": "noarch", | ||
312 | |||
313 | # amd64 | ||
314 | "x86_64": "athlon", | ||
315 | "amd64": "x86_64", | ||
316 | "ia32e": "x86_64", | ||
317 | |||
318 | # arm | ||
319 | "armv7tnhl": "armv7nhl", | ||
320 | "armv7nhl": "armv7hl", | ||
321 | "armv7hl": "noarch", | ||
322 | "armv7l": "armv6l", | ||
323 | "armv6l": "armv5tejl", | ||
324 | "armv5tejl": "armv5tel", | ||
325 | "armv5tel": "noarch", | ||
326 | |||
327 | #itanium | ||
328 | "ia64": "noarch", | ||
329 | } | ||
330 | |||
331 | def isMultiLibArch(arch=None): | ||
332 | """returns true if arch is a multilib arch, false if not""" | ||
333 | if arch is None: | ||
334 | arch = getCanonArch() | ||
335 | |||
336 | if not arches.has_key(arch): # or we could check if it is noarch | ||
337 | return False | ||
338 | |||
339 | if multilibArches.has_key(arch): | ||
340 | return True | ||
341 | |||
342 | if multilibArches.has_key(arches[arch]): | ||
343 | return True | ||
344 | |||
345 | return False | ||
346 | |||
347 | def getBaseArch(): | ||
348 | myarch = getCanonArch() | ||
349 | if not arches.has_key(myarch): | ||
350 | return myarch | ||
351 | |||
352 | if isMultiLibArch(arch=myarch): | ||
353 | if multilibArches.has_key(myarch): | ||
354 | return myarch | ||
355 | else: | ||
356 | return arches[myarch] | ||
357 | |||
358 | if arches.has_key(myarch): | ||
359 | basearch = myarch | ||
360 | value = arches[basearch] | ||
361 | while value != 'noarch': | ||
362 | basearch = value | ||
363 | value = arches[basearch] | ||
364 | |||
365 | return basearch | ||
366 | |||
367 | def checkRpmIntegrity(bin_rpm, package): | ||
368 | return runner.quiet([bin_rpm, "-K", "--nosignature", package]) | ||
369 | |||
370 | def checkSig(ts, package): | ||
371 | """ Takes a transaction set and a package, check it's sigs, | ||
372 | return 0 if they are all fine | ||
373 | return 1 if the gpg key can't be found | ||
374 | return 2 if the header is in someway damaged | ||
375 | return 3 if the key is not trusted | ||
376 | return 4 if the pkg is not gpg or pgp signed | ||
377 | """ | ||
378 | |||
379 | value = 0 | ||
380 | currentflags = ts.setVSFlags(0) | ||
381 | fdno = os.open(package, os.O_RDONLY) | ||
382 | try: | ||
383 | hdr = ts.hdrFromFdno(fdno) | ||
384 | |||
385 | except rpm.error, e: | ||
386 | if str(e) == "public key not availaiable": | ||
387 | value = 1 | ||
388 | if str(e) == "public key not available": | ||
389 | value = 1 | ||
390 | if str(e) == "public key not trusted": | ||
391 | value = 3 | ||
392 | if str(e) == "error reading package header": | ||
393 | value = 2 | ||
394 | else: | ||
395 | error, siginfo = getSigInfo(hdr) | ||
396 | if error == 101: | ||
397 | os.close(fdno) | ||
398 | del hdr | ||
399 | value = 4 | ||
400 | else: | ||
401 | del hdr | ||
402 | |||
403 | try: | ||
404 | os.close(fdno) | ||
405 | except OSError: | ||
406 | pass | ||
407 | |||
408 | ts.setVSFlags(currentflags) # put things back like they were before | ||
409 | return value | ||
410 | |||
411 | def getSigInfo(hdr): | ||
412 | """ checks signature from an hdr hand back signature information and/or | ||
413 | an error code | ||
414 | """ | ||
415 | |||
416 | import locale | ||
417 | locale.setlocale(locale.LC_ALL, 'C') | ||
418 | |||
419 | string = '%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{%|SIGGPG?{%{SIGGPG:pgpsig}}:{%|SIGPGP?{%{SIGPGP:pgpsig}}:{(none)}|}|}|}|' | ||
420 | siginfo = hdr.sprintf(string) | ||
421 | if siginfo != '(none)': | ||
422 | error = 0 | ||
423 | sigtype, sigdate, sigid = siginfo.split(',') | ||
424 | else: | ||
425 | error = 101 | ||
426 | sigtype = 'MD5' | ||
427 | sigdate = 'None' | ||
428 | sigid = 'None' | ||
429 | |||
430 | infotuple = (sigtype, sigdate, sigid) | ||
431 | return error, infotuple | ||
432 | |||
433 | def checkRepositoryEULA(name, repo): | ||
434 | """ This function is to check the EULA file if provided. | ||
435 | return True: no EULA or accepted | ||
436 | return False: user declined the EULA | ||
437 | """ | ||
438 | |||
439 | import tempfile | ||
440 | import shutil | ||
441 | import urlparse | ||
442 | import urllib2 as u2 | ||
443 | import httplib | ||
444 | from mic.utils.errors import CreatorError | ||
445 | |||
446 | def _check_and_download_url(u2opener, url, savepath): | ||
447 | try: | ||
448 | if u2opener: | ||
449 | f = u2opener.open(url) | ||
450 | else: | ||
451 | f = u2.urlopen(url) | ||
452 | except u2.HTTPError, httperror: | ||
453 | if httperror.code in (404, 503): | ||
454 | return None | ||
455 | else: | ||
456 | raise CreatorError(httperror) | ||
457 | except OSError, oserr: | ||
458 | if oserr.errno == 2: | ||
459 | return None | ||
460 | else: | ||
461 | raise CreatorError(oserr) | ||
462 | except IOError, oserr: | ||
463 | if hasattr(oserr, "reason") and oserr.reason.errno == 2: | ||
464 | return None | ||
465 | else: | ||
466 | raise CreatorError(oserr) | ||
467 | except u2.URLError, err: | ||
468 | raise CreatorError(err) | ||
469 | except httplib.HTTPException, e: | ||
470 | raise CreatorError(e) | ||
471 | |||
472 | # save to file | ||
473 | licf = open(savepath, "w") | ||
474 | licf.write(f.read()) | ||
475 | licf.close() | ||
476 | f.close() | ||
477 | |||
478 | return savepath | ||
479 | |||
480 | def _pager_file(savepath): | ||
481 | |||
482 | if os.path.splitext(savepath)[1].upper() in ('.HTM', '.HTML'): | ||
483 | pagers = ('w3m', 'links', 'lynx', 'less', 'more') | ||
484 | else: | ||
485 | pagers = ('less', 'more') | ||
486 | |||
487 | file_showed = False | ||
488 | for pager in pagers: | ||
489 | cmd = "%s %s" % (pager, savepath) | ||
490 | try: | ||
491 | os.system(cmd) | ||
492 | except OSError: | ||
493 | continue | ||
494 | else: | ||
495 | file_showed = True | ||
496 | break | ||
497 | |||
498 | if not file_showed: | ||
499 | f = open(savepath) | ||
500 | msger.raw(f.read()) | ||
501 | f.close() | ||
502 | msger.pause() | ||
503 | |||
504 | # when proxy needed, make urllib2 follow it | ||
505 | proxy = repo.proxy | ||
506 | proxy_username = repo.proxy_username | ||
507 | proxy_password = repo.proxy_password | ||
508 | |||
509 | if not proxy: | ||
510 | proxy = get_proxy_for(repo.baseurl[0]) | ||
511 | |||
512 | handlers = [] | ||
513 | auth_handler = u2.HTTPBasicAuthHandler(u2.HTTPPasswordMgrWithDefaultRealm()) | ||
514 | u2opener = None | ||
515 | if proxy: | ||
516 | if proxy_username: | ||
517 | proxy_netloc = urlparse.urlsplit(proxy).netloc | ||
518 | if proxy_password: | ||
519 | proxy_url = 'http://%s:%s@%s' % (proxy_username, proxy_password, proxy_netloc) | ||
520 | else: | ||
521 | proxy_url = 'http://%s@%s' % (proxy_username, proxy_netloc) | ||
522 | else: | ||
523 | proxy_url = proxy | ||
524 | |||
525 | proxy_support = u2.ProxyHandler({'http': proxy_url, | ||
526 | 'https': proxy_url, | ||
527 | 'ftp': proxy_url}) | ||
528 | handlers.append(proxy_support) | ||
529 | |||
530 | # download all remote files to one temp dir | ||
531 | baseurl = None | ||
532 | repo_lic_dir = tempfile.mkdtemp(prefix = 'repolic') | ||
533 | |||
534 | for url in repo.baseurl: | ||
535 | tmphandlers = handlers[:] | ||
536 | |||
537 | (scheme, host, path, parm, query, frag) = urlparse.urlparse(url.rstrip('/') + '/') | ||
538 | if scheme not in ("http", "https", "ftp", "ftps", "file"): | ||
539 | raise CreatorError("Error: invalid url %s" % url) | ||
540 | |||
541 | if '@' in host: | ||
542 | try: | ||
543 | user_pass, host = host.split('@', 1) | ||
544 | if ':' in user_pass: | ||
545 | user, password = user_pass.split(':', 1) | ||
546 | except ValueError, e: | ||
547 | raise CreatorError('Bad URL: %s' % url) | ||
548 | |||
549 | msger.verbose("adding HTTP auth: %s, XXXXXXXX" %(user)) | ||
550 | auth_handler.add_password(None, host, user, password) | ||
551 | tmphandlers.append(auth_handler) | ||
552 | url = scheme + "://" + host + path + parm + query + frag | ||
553 | |||
554 | if tmphandlers: | ||
555 | u2opener = u2.build_opener(*tmphandlers) | ||
556 | |||
557 | # try to download | ||
558 | repo_eula_url = urlparse.urljoin(url, "LICENSE.txt") | ||
559 | repo_eula_path = _check_and_download_url( | ||
560 | u2opener, | ||
561 | repo_eula_url, | ||
562 | os.path.join(repo_lic_dir, repo.id + '_LICENSE.txt')) | ||
563 | if repo_eula_path: | ||
564 | # found | ||
565 | baseurl = url | ||
566 | break | ||
567 | |||
568 | if not baseurl: | ||
569 | shutil.rmtree(repo_lic_dir) #cleanup | ||
570 | return True | ||
571 | |||
572 | # show the license file | ||
573 | msger.info('For the software packages in this yum repo:') | ||
574 | msger.info(' %s: %s' % (name, baseurl)) | ||
575 | msger.info('There is an "End User License Agreement" file that need to be checked.') | ||
576 | msger.info('Please read the terms and conditions outlined in it and answer the followed qustions.') | ||
577 | msger.pause() | ||
578 | |||
579 | _pager_file(repo_eula_path) | ||
580 | |||
581 | # Asking for the "Accept/Decline" | ||
582 | if not msger.ask('Would you agree to the terms and conditions outlined in the above End User License Agreement?'): | ||
583 | msger.warning('Will not install pkgs from this repo.') | ||
584 | shutil.rmtree(repo_lic_dir) #cleanup | ||
585 | return False | ||
586 | |||
587 | # try to find support_info.html for extra infomation | ||
588 | repo_info_url = urlparse.urljoin(baseurl, "support_info.html") | ||
589 | repo_info_path = _check_and_download_url( | ||
590 | u2opener, | ||
591 | repo_info_url, | ||
592 | os.path.join(repo_lic_dir, repo.id + '_support_info.html')) | ||
593 | if repo_info_path: | ||
594 | msger.info('There is one more file in the repo for additional support information, please read it') | ||
595 | msger.pause() | ||
596 | _pager_file(repo_info_path) | ||
597 | |||
598 | #cleanup | ||
599 | shutil.rmtree(repo_lic_dir) | ||
600 | return True | ||
diff --git a/scripts/lib/mic/utils/runner.py b/scripts/lib/mic/utils/runner.py new file mode 100644 index 0000000000..fded3c93fa --- /dev/null +++ b/scripts/lib/mic/utils/runner.py | |||
@@ -0,0 +1,109 @@ | |||
1 | #!/usr/bin/python -tt | ||
2 | # | ||
3 | # Copyright (c) 2011 Intel, Inc. | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify it | ||
6 | # under the terms of the GNU General Public License as published by the Free | ||
7 | # Software Foundation; version 2 of the License | ||
8 | # | ||
9 | # This program is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
11 | # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
12 | # for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License along | ||
15 | # with this program; if not, write to the Free Software Foundation, Inc., 59 | ||
16 | # Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
17 | |||
18 | import os | ||
19 | import subprocess | ||
20 | |||
21 | from mic import msger | ||
22 | |||
23 | def runtool(cmdln_or_args, catch=1): | ||
24 | """ wrapper for most of the subprocess calls | ||
25 | input: | ||
26 | cmdln_or_args: can be both args and cmdln str (shell=True) | ||
27 | catch: 0, quitely run | ||
28 | 1, only STDOUT | ||
29 | 2, only STDERR | ||
30 | 3, both STDOUT and STDERR | ||
31 | return: | ||
32 | (rc, output) | ||
33 | if catch==0: the output will always None | ||
34 | """ | ||
35 | |||
36 | if catch not in (0, 1, 2, 3): | ||
37 | # invalid catch selection, will cause exception, that's good | ||
38 | return None | ||
39 | |||
40 | if isinstance(cmdln_or_args, list): | ||
41 | cmd = cmdln_or_args[0] | ||
42 | shell = False | ||
43 | else: | ||
44 | import shlex | ||
45 | cmd = shlex.split(cmdln_or_args)[0] | ||
46 | shell = True | ||
47 | |||
48 | if catch != 3: | ||
49 | dev_null = os.open("/dev/null", os.O_WRONLY) | ||
50 | |||
51 | if catch == 0: | ||
52 | sout = dev_null | ||
53 | serr = dev_null | ||
54 | elif catch == 1: | ||
55 | sout = subprocess.PIPE | ||
56 | serr = dev_null | ||
57 | elif catch == 2: | ||
58 | sout = dev_null | ||
59 | serr = subprocess.PIPE | ||
60 | elif catch == 3: | ||
61 | sout = subprocess.PIPE | ||
62 | serr = subprocess.STDOUT | ||
63 | |||
64 | try: | ||
65 | p = subprocess.Popen(cmdln_or_args, stdout=sout, | ||
66 | stderr=serr, shell=shell) | ||
67 | (sout, serr) = p.communicate() | ||
68 | # combine stdout and stderr, filter None out | ||
69 | out = ''.join(filter(None, [sout, serr])) | ||
70 | except OSError, e: | ||
71 | if e.errno == 2: | ||
72 | # [Errno 2] No such file or directory | ||
73 | msger.error('Cannot run command: %s, lost dependency?' % cmd) | ||
74 | else: | ||
75 | raise # relay | ||
76 | finally: | ||
77 | if catch != 3: | ||
78 | os.close(dev_null) | ||
79 | |||
80 | return (p.returncode, out) | ||
81 | |||
82 | def show(cmdln_or_args): | ||
83 | # show all the message using msger.verbose | ||
84 | |||
85 | rc, out = runtool(cmdln_or_args, catch=3) | ||
86 | |||
87 | if isinstance(cmdln_or_args, list): | ||
88 | cmd = ' '.join(cmdln_or_args) | ||
89 | else: | ||
90 | cmd = cmdln_or_args | ||
91 | |||
92 | msg = 'running command: "%s"' % cmd | ||
93 | if out: out = out.strip() | ||
94 | if out: | ||
95 | msg += ', with output::' | ||
96 | msg += '\n +----------------' | ||
97 | for line in out.splitlines(): | ||
98 | msg += '\n | %s' % line | ||
99 | msg += '\n +----------------' | ||
100 | |||
101 | msger.verbose(msg) | ||
102 | return rc | ||
103 | |||
104 | def outs(cmdln_or_args, catch=1): | ||
105 | # get the outputs of tools | ||
106 | return runtool(cmdln_or_args, catch)[1].strip() | ||
107 | |||
108 | def quiet(cmdln_or_args): | ||
109 | return runtool(cmdln_or_args, catch=0)[0] | ||