OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 """ | 2 """ |
3 KVM configuration file utility functions. | 3 KVM configuration file utility functions. |
4 | 4 |
5 @copyright: Red Hat 2008-2010 | 5 @copyright: Red Hat 2008-2010 |
6 """ | 6 """ |
7 | 7 |
8 import logging, re, os, sys, optparse, array, traceback, cPickle | 8 import logging, re, os, sys, optparse, array, traceback, cPickle |
9 import common | 9 import common |
10 import kvm_utils | 10 import kvm_utils |
11 from autotest_lib.client.common_lib import error | 11 from autotest_lib.client.common_lib import error |
12 from autotest_lib.client.common_lib import logging_config, logging_manager | 12 from autotest_lib.client.common_lib import logging_manager |
13 | 13 |
14 | 14 |
15 class config: | 15 class config: |
16 """ | 16 """ |
17 Parse an input file or string that follows the KVM Test Config File format | 17 Parse an input file or string that follows the KVM Test Config File format |
18 and generate a list of dicts that will be later used as configuration | 18 and generate a list of dicts that will be later used as configuration |
19 parameters by the KVM tests. | 19 parameters by the KVM tests. |
20 | 20 |
21 @see: http://www.linux-kvm.org/page/KVM-Autotest/Test_Config_File | 21 @see: http://www.linux-kvm.org/page/KVM-Autotest/Test_Config_File |
22 """ | 22 """ |
23 | 23 |
24 def __init__(self, filename=None, debug=True): | 24 def __init__(self, filename=None, debug=True): |
25 """ | 25 """ |
26 Initialize the list and optionally parse a file. | 26 Initialize the list and optionally parse a file. |
27 | 27 |
28 @param filename: Path of the file that will be taken. | 28 @param filename: Path of the file that will be taken. |
29 @param debug: Whether to turn on debugging output. | 29 @param debug: Whether to turn on debugging output. |
30 """ | 30 """ |
31 self.list = [array.array("H", [4, 4, 4, 4])] | 31 self.list = [array.array("H", [4, 4, 4, 4])] |
32 self.object_cache = [] | 32 self.object_cache = [] |
33 self.object_cache_indices = {} | 33 self.object_cache_indices = {} |
34 self.regex_cache = {} | 34 self.regex_cache = {} |
35 self.filename = filename | |
36 self.debug = debug | 35 self.debug = debug |
37 if filename: | 36 if filename: |
38 self.parse_file(filename) | 37 self.parse_file(filename) |
39 | 38 |
40 | 39 |
41 def parse_file(self, filename): | 40 def parse_file(self, filename): |
42 """ | 41 """ |
43 Parse file. If it doesn't exist, raise an IOError. | 42 Parse file. If it doesn't exist, raise an IOError. |
44 | 43 |
45 @param filename: Path of the configuration file. | 44 @param filename: Path of the configuration file. |
46 """ | 45 """ |
47 if not os.path.exists(filename): | 46 if not os.path.exists(filename): |
48 raise IOError("File %s not found" % filename) | 47 raise IOError("File %s not found" % filename) |
49 self.filename = filename | |
50 str = open(filename).read() | 48 str = open(filename).read() |
51 self.list = self.parse(configreader(str), self.list) | 49 self.list = self.parse(configreader(filename, str), self.list) |
52 | 50 |
53 | 51 |
54 def parse_string(self, str): | 52 def parse_string(self, str): |
55 """ | 53 """ |
56 Parse a string. | 54 Parse a string. |
57 | 55 |
58 @param str: String to parse. | 56 @param str: String to parse. |
59 """ | 57 """ |
60 self.list = self.parse(configreader(str), self.list) | 58 self.list = self.parse(configreader('<string>', str, real_file=False), s
elf.list) |
61 | 59 |
62 | 60 |
63 def fork_and_parse(self, filename=None, str=None): | 61 def fork_and_parse(self, filename=None, str=None): |
64 """ | 62 """ |
65 Parse a file and/or a string in a separate process to save memory. | 63 Parse a file and/or a string in a separate process to save memory. |
66 | 64 |
67 Python likes to keep memory to itself even after the objects occupying | 65 Python likes to keep memory to itself even after the objects occupying |
68 it have been destroyed. If during a call to parse_file() or | 66 it have been destroyed. If during a call to parse_file() or |
69 parse_string() a lot of memory is used, it can only be freed by | 67 parse_string() a lot of memory is used, it can only be freed by |
70 terminating the process. This function works around the problem by | 68 terminating the process. This function works around the problem by |
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
290 "context, %d remain)" % | 288 "context, %d remain)" % |
291 (len_list, len(list))) | 289 (len_list, len(list))) |
292 continue | 290 continue |
293 | 291 |
294 # Parse 'variants' | 292 # Parse 'variants' |
295 if line == "variants:": | 293 if line == "variants:": |
296 # 'variants' not allowed in restricted mode | 294 # 'variants' not allowed in restricted mode |
297 # (inside an exception or inside subvariants) | 295 # (inside an exception or inside subvariants) |
298 if restricted: | 296 if restricted: |
299 e_msg = "Using variants in this context is not allowed" | 297 e_msg = "Using variants in this context is not allowed" |
300 raise error.AutotestError(e_msg) | 298 cr.raise_error(e_msg) |
301 if self.debug and not restricted: | 299 if self.debug and not restricted: |
302 _debug_print(indented_line, | 300 _debug_print(indented_line, |
303 "Entering variants block (%d dicts in " | 301 "Entering variants block (%d dicts in " |
304 "current context)" % len_list) | 302 "current context)" % len_list) |
305 list = self.parse_variants(cr, list, subvariants=False, | 303 list = self.parse_variants(cr, list, subvariants=False, |
306 prev_indent=indent) | 304 prev_indent=indent) |
307 continue | 305 continue |
308 | 306 |
309 # Parse 'subvariants' (the block is parsed for each dict | 307 # Parse 'subvariants' (the block is parsed for each dict |
310 # separately) | 308 # separately) |
(...skipping 19 matching lines...) Expand all Loading... |
330 prev_indent=indent) | 328 prev_indent=indent) |
331 list = new_list | 329 list = new_list |
332 continue | 330 continue |
333 | 331 |
334 # Parse 'include' statements | 332 # Parse 'include' statements |
335 if words[0] == "include": | 333 if words[0] == "include": |
336 if len(words) <= 1: | 334 if len(words) <= 1: |
337 continue | 335 continue |
338 if self.debug and not restricted: | 336 if self.debug and not restricted: |
339 _debug_print(indented_line, "Entering file %s" % words[1]) | 337 _debug_print(indented_line, "Entering file %s" % words[1]) |
340 if self.filename: | 338 |
341 filename = os.path.join(os.path.dirname(self.filename), | 339 cur_filename = cr.real_filename() |
342 words[1]) | 340 if cur_filename is None: |
343 if os.path.exists(filename): | 341 cr.raise_error("'include' is valid only when parsing a file"
) |
344 str = open(filename).read() | 342 |
345 list = self.parse(configreader(str), list, restricted) | 343 filename = os.path.join(os.path.dirname(cur_filename), |
346 if self.debug and not restricted: | 344 words[1]) |
347 _debug_print("", "Leaving file %s" % words[1]) | 345 if not os.path.exists(filename): |
348 else: | 346 cr.raise_error("Cannot include %s -- file not found" % (file
name)) |
349 logging.warning("Cannot include %s -- file not found", | 347 |
350 filename) | 348 str = open(filename).read() |
351 else: | 349 list = self.parse(configreader(filename, str), list, restricted) |
352 logging.warning("Cannot include %s because no file is " | 350 if self.debug and not restricted: |
353 "currently open", words[1]) | 351 _debug_print("", "Leaving file %s" % words[1]) |
| 352 |
354 continue | 353 continue |
355 | 354 |
356 # Parse multi-line exceptions | 355 # Parse multi-line exceptions |
357 # (the block is parsed for each dict separately) | 356 # (the block is parsed for each dict separately) |
358 if line.endswith(":"): | 357 if line.endswith(":"): |
359 if self.debug and not restricted: | 358 if self.debug and not restricted: |
360 _debug_print(indented_line, | 359 _debug_print(indented_line, |
361 "Entering multi-line exception block " | 360 "Entering multi-line exception block " |
362 "(%d dicts in current context outside " | 361 "(%d dicts in current context outside " |
363 "exception)" % len_list) | 362 "exception)" % len_list) |
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
532 | 531 |
533 # configreader | 532 # configreader |
534 | 533 |
535 class configreader: | 534 class configreader: |
536 """ | 535 """ |
537 Preprocess an input string and provide file-like services. | 536 Preprocess an input string and provide file-like services. |
538 This is intended as a replacement for the file and StringIO classes, | 537 This is intended as a replacement for the file and StringIO classes, |
539 whose readline() and/or seek() methods seem to be slow. | 538 whose readline() and/or seek() methods seem to be slow. |
540 """ | 539 """ |
541 | 540 |
542 def __init__(self, str): | 541 def __init__(self, filename, str, real_file=True): |
543 """ | 542 """ |
544 Initialize the reader. | 543 Initialize the reader. |
545 | 544 |
| 545 @param filename: the filename we're parsing |
546 @param str: The string to parse. | 546 @param str: The string to parse. |
| 547 @param real_file: Indicates if filename represents a real file. Defaults
to True. |
547 """ | 548 """ |
| 549 self.filename = filename |
| 550 self.is_real_file = real_file |
548 self.line_index = 0 | 551 self.line_index = 0 |
549 self.lines = [] | 552 self.lines = [] |
550 for line in str.splitlines(): | 553 self.real_number = [] |
| 554 for num, line in enumerate(str.splitlines()): |
551 line = line.rstrip().expandtabs() | 555 line = line.rstrip().expandtabs() |
552 stripped_line = line.strip() | 556 stripped_line = line.strip() |
553 indent = len(line) - len(stripped_line) | 557 indent = len(line) - len(stripped_line) |
554 if (not stripped_line | 558 if (not stripped_line |
555 or stripped_line.startswith("#") | 559 or stripped_line.startswith("#") |
556 or stripped_line.startswith("//")): | 560 or stripped_line.startswith("//")): |
557 continue | 561 continue |
558 self.lines.append((line, stripped_line, indent)) | 562 self.lines.append((line, stripped_line, indent)) |
| 563 self.real_number.append(num + 1) |
559 | 564 |
560 | 565 |
| 566 def real_filename(self): |
| 567 """Returns the filename we're reading, in case it is a real file |
| 568 |
| 569 @returns the filename we are parsing, or None in case we're not parsing
a real file |
| 570 """ |
| 571 if self.is_real_file: |
| 572 return self.filename |
| 573 |
561 def get_next_line(self): | 574 def get_next_line(self): |
562 """ | 575 """ |
563 Get the next non-empty, non-comment line in the string. | 576 Get the next non-empty, non-comment line in the string. |
564 | 577 |
565 @param file: File like object. | 578 @param file: File like object. |
566 @return: (line, stripped_line, indent), where indent is the line's | 579 @return: (line, stripped_line, indent), where indent is the line's |
567 indent level or -1 if no line is available. | 580 indent level or -1 if no line is available. |
568 """ | 581 """ |
569 try: | 582 try: |
570 if self.line_index < len(self.lines): | 583 if self.line_index < len(self.lines): |
(...skipping 10 matching lines...) Expand all Loading... |
581 """ | 594 """ |
582 return self.line_index | 595 return self.line_index |
583 | 596 |
584 | 597 |
585 def seek(self, index): | 598 def seek(self, index): |
586 """ | 599 """ |
587 Set the current line index. | 600 Set the current line index. |
588 """ | 601 """ |
589 self.line_index = index | 602 self.line_index = index |
590 | 603 |
| 604 def raise_error(self, msg): |
| 605 """Raise an error related to the last line returned by get_next_line() |
| 606 """ |
| 607 if self.line_index == 0: # nothing was read. shouldn't happen, but... |
| 608 line_id = 'BEGIN' |
| 609 elif self.line_index >= len(self.lines): # past EOF |
| 610 line_id = 'EOF' |
| 611 else: |
| 612 # line_index is the _next_ line. get the previous one |
| 613 line_id = str(self.real_number[self.line_index-1]) |
| 614 raise error.AutotestError("%s:%s: %s" % (self.filename, line_id, msg)) |
| 615 |
591 | 616 |
592 # Array structure: | 617 # Array structure: |
593 # ---------------- | 618 # ---------------- |
594 # The first 4 elements contain the indices of the 4 segments. | 619 # The first 4 elements contain the indices of the 4 segments. |
595 # a[0] -- Index of beginning of 'name' segment (always 4). | 620 # a[0] -- Index of beginning of 'name' segment (always 4). |
596 # a[1] -- Index of beginning of 'shortname' segment. | 621 # a[1] -- Index of beginning of 'shortname' segment. |
597 # a[2] -- Index of beginning of 'depend' segment. | 622 # a[2] -- Index of beginning of 'depend' segment. |
598 # a[3] -- Index of beginning of 'content' segment. | 623 # a[3] -- Index of beginning of 'content' segment. |
599 # The next elements in the array comprise the aforementioned segments: | 624 # The next elements in the array comprise the aforementioned segments: |
600 # The 'name' segment begins with a[a[0]] and ends with a[a[1]-1]. | 625 # The 'name' segment begins with a[a[0]] and ends with a[a[1]-1]. |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
675 | 700 |
676 | 701 |
677 if __name__ == "__main__": | 702 if __name__ == "__main__": |
678 parser = optparse.OptionParser("usage: %prog [options] [filename]") | 703 parser = optparse.OptionParser("usage: %prog [options] [filename]") |
679 parser.add_option('--verbose', dest="debug", action='store_true', | 704 parser.add_option('--verbose', dest="debug", action='store_true', |
680 help='include debug messages in console output') | 705 help='include debug messages in console output') |
681 | 706 |
682 options, args = parser.parse_args() | 707 options, args = parser.parse_args() |
683 debug = options.debug | 708 debug = options.debug |
684 if args: | 709 if args: |
685 filename = args[0] | 710 filenames = args |
686 else: | 711 else: |
687 filename = os.path.join(os.path.dirname(sys.argv[0]), "tests.cfg") | 712 filenames = [os.path.join(os.path.dirname(sys.argv[0]), "tests.cfg")] |
688 | 713 |
689 # Here we configure the stand alone program to use the autotest | 714 # Here we configure the stand alone program to use the autotest |
690 # logging system. | 715 # logging system. |
691 logging_manager.configure_logging(kvm_utils.KvmLoggingConfig(), | 716 logging_manager.configure_logging(kvm_utils.KvmLoggingConfig(), |
692 verbose=debug) | 717 verbose=debug) |
693 dicts = config(filename, debug=debug).get_generator() | 718 cfg = config(debug=debug) |
| 719 for fn in filenames: |
| 720 cfg.parse_file(fn) |
| 721 dicts = cfg.get_generator() |
694 for i, dict in enumerate(dicts): | 722 for i, dict in enumerate(dicts): |
695 logging.info("Dictionary #%d:", i) | 723 print "Dictionary #%d:" % (i) |
696 keys = dict.keys() | 724 keys = dict.keys() |
697 keys.sort() | 725 keys.sort() |
698 for key in keys: | 726 for key in keys: |
699 logging.info(" %s = %s", key, dict[key]) | 727 print " %s = %s" % (key, dict[key]) |
OLD | NEW |