Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(306)

Side by Side Diff: client/tests/kvm/kvm_config.py

Issue 6551020: Merge remote branch 'autotest-upstream/master' into try-box1 (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/autotest.git@master
Patch Set: patch Created 9 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « client/tests/kvm/installer.py ('k') | client/tests/kvm/kvm_utils.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 """ 2 """
3 KVM test configuration file parser 3 KVM test configuration file parser
4 4
5 @copyright: Red Hat 2008-2011 5 @copyright: Red Hat 2008-2011
6 """ 6 """
7 7
8 import re, os, sys, optparse, collections, string 8 import re, os, sys, optparse, collections
9 9
10 10
11 # Filter syntax: 11 # Filter syntax:
12 # , means OR 12 # , means OR
13 # .. means AND 13 # .. means AND
14 # . means IMMEDIATELY-FOLLOWED-BY 14 # . means IMMEDIATELY-FOLLOWED-BY
15 15
16 # Example: 16 # Example:
17 # qcow2..Fedora.14, RHEL.6..raw..boot, smp2..qcow2..migrate..ide 17 # qcow2..Fedora.14, RHEL.6..raw..boot, smp2..qcow2..migrate..ide
18 # means match all dicts whose names have: 18 # means match all dicts whose names have:
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 return False 130 return False
131 131
132 132
133 class NoOnlyFilter(Filter): 133 class NoOnlyFilter(Filter):
134 def __init__(self, line): 134 def __init__(self, line):
135 Filter.__init__(self, line.split(None, 1)[1]) 135 Filter.__init__(self, line.split(None, 1)[1])
136 self.line = line 136 self.line = line
137 137
138 138
139 class OnlyFilter(NoOnlyFilter): 139 class OnlyFilter(NoOnlyFilter):
140 def is_irrelevant(self, ctx, ctx_set, descendant_labels):
141 return self.match(ctx, ctx_set)
142
143
144 def requires_action(self, ctx, ctx_set, descendant_labels):
145 return not self.might_match(ctx, ctx_set, descendant_labels)
146
147
140 def might_pass(self, failed_ctx, failed_ctx_set, ctx, ctx_set, 148 def might_pass(self, failed_ctx, failed_ctx_set, ctx, ctx_set,
141 descendant_labels): 149 descendant_labels):
142 for word in self.filter: 150 for word in self.filter:
143 for block in word: 151 for block in word:
144 if (_match_adjacent(block, ctx, ctx_set) > 152 if (_match_adjacent(block, ctx, ctx_set) >
145 _match_adjacent(block, failed_ctx, failed_ctx_set)): 153 _match_adjacent(block, failed_ctx, failed_ctx_set)):
146 return self.might_match(ctx, ctx_set, descendant_labels) 154 return self.might_match(ctx, ctx_set, descendant_labels)
147 return False 155 return False
148 156
149 157
150 class NoFilter(NoOnlyFilter): 158 class NoFilter(NoOnlyFilter):
159 def is_irrelevant(self, ctx, ctx_set, descendant_labels):
160 return not self.might_match(ctx, ctx_set, descendant_labels)
161
162
163 def requires_action(self, ctx, ctx_set, descendant_labels):
164 return self.match(ctx, ctx_set)
165
166
151 def might_pass(self, failed_ctx, failed_ctx_set, ctx, ctx_set, 167 def might_pass(self, failed_ctx, failed_ctx_set, ctx, ctx_set,
152 descendant_labels): 168 descendant_labels):
153 for word in self.filter: 169 for word in self.filter:
154 for block in word: 170 for block in word:
155 if (_match_adjacent(block, ctx, ctx_set) < 171 if (_match_adjacent(block, ctx, ctx_set) <
156 _match_adjacent(block, failed_ctx, failed_ctx_set)): 172 _match_adjacent(block, failed_ctx, failed_ctx_set)):
157 return not self.match(ctx, ctx_set) 173 return not self.match(ctx, ctx_set)
158 return False 174 return False
159 175
160 176
161 class Condition(NoFilter): 177 class Condition(NoFilter):
162 def __init__(self, line): 178 def __init__(self, line):
163 Filter.__init__(self, line.rstrip(":")) 179 Filter.__init__(self, line.rstrip(":"))
164 self.line = line 180 self.line = line
165 self.content = [] 181 self.content = []
166 182
167 183
184 class NegativeCondition(OnlyFilter):
185 def __init__(self, line):
186 Filter.__init__(self, line.lstrip("!").rstrip(":"))
187 self.line = line
188 self.content = []
189
190
168 class Parser(object): 191 class Parser(object):
169 """ 192 """
170 Parse an input file or string that follows the KVM Test Config File format 193 Parse an input file or string that follows the KVM Test Config File format
171 and generate a list of dicts that will be later used as configuration 194 and generate a list of dicts that will be later used as configuration
172 parameters by the KVM tests. 195 parameters by the KVM tests.
173 196
174 @see: http://www.linux-kvm.org/page/KVM-Autotest/Test_Config_File 197 @see: http://www.linux-kvm.org/page/KVM-Autotest/Test_Config_File
175 """ 198 """
176 199
177 def __init__(self, filename=None, debug=False): 200 def __init__(self, filename=None, debug=False):
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
219 # new_content and unpack conditional blocks if appropriate. 242 # new_content and unpack conditional blocks if appropriate.
220 # For example, if an 'only' statement fully matches ctx, it 243 # For example, if an 'only' statement fully matches ctx, it
221 # becomes irrelevant and is not appended to new_content. 244 # becomes irrelevant and is not appended to new_content.
222 # If a conditional block fully matches, its contents are 245 # If a conditional block fully matches, its contents are
223 # unpacked into new_content. 246 # unpacked into new_content.
224 # 3. Move failed filters into failed_filters, so that next time we 247 # 3. Move failed filters into failed_filters, so that next time we
225 # reach this node or one of its ancestors, we'll check those 248 # reach this node or one of its ancestors, we'll check those
226 # filters first. 249 # filters first.
227 for t in content: 250 for t in content:
228 filename, linenum, obj = t 251 filename, linenum, obj = t
229 if type(obj) is str: 252 if type(obj) is Op:
230 new_content.append(t) 253 new_content.append(t)
231 continue 254 continue
232 elif type(obj) is OnlyFilter: 255 # obj is an OnlyFilter/NoFilter/Condition/NegativeCondition
233 if not obj.might_match(ctx, ctx_set, labels): 256 if obj.requires_action(ctx, ctx_set, labels):
257 # This filter requires action now
258 if type(obj) is OnlyFilter or type(obj) is NoFilter:
234 self._debug(" filter did not pass: %r (%s:%s)", 259 self._debug(" filter did not pass: %r (%s:%s)",
235 obj.line, filename, linenum) 260 obj.line, filename, linenum)
236 failed_filters.append(t) 261 failed_filters.append(t)
237 return False 262 return False
238 elif obj.match(ctx, ctx_set): 263 else:
239 continue
240 elif type(obj) is NoFilter:
241 if obj.match(ctx, ctx_set):
242 self._debug(" filter did not pass: %r (%s:%s)",
243 obj.line, filename, linenum)
244 failed_filters.append(t)
245 return False
246 elif not obj.might_match(ctx, ctx_set, labels):
247 continue
248 elif type(obj) is Condition:
249 if obj.match(ctx, ctx_set):
250 self._debug(" conditional block matches: %r (%s:%s)", 264 self._debug(" conditional block matches: %r (%s:%s)",
251 obj.line, filename, linenum) 265 obj.line, filename, linenum)
252 # Check and unpack the content inside this Condition 266 # Check and unpack the content inside this Condition
253 # object (note: the failed filters should go into 267 # object (note: the failed filters should go into
254 # new_internal_filters because we don't expect them to 268 # new_internal_filters because we don't expect them to
255 # come from outside this node, even if the Condition 269 # come from outside this node, even if the Condition
256 # itself was external) 270 # itself was external)
257 if not process_content(obj.content, 271 if not process_content(obj.content,
258 new_internal_filters): 272 new_internal_filters):
259 failed_filters.append(t) 273 failed_filters.append(t)
260 return False 274 return False
261 continue 275 continue
262 elif not obj.might_match(ctx, ctx_set, labels): 276 elif obj.is_irrelevant(ctx, ctx_set, labels):
263 continue 277 # This filter is no longer relevant and can be removed
264 new_content.append(t) 278 continue
279 else:
280 # Keep the filter and check it again later
281 new_content.append(t)
265 return True 282 return True
266 283
267 def might_pass(failed_ctx, 284 def might_pass(failed_ctx,
268 failed_ctx_set, 285 failed_ctx_set,
269 failed_external_filters, 286 failed_external_filters,
270 failed_internal_filters): 287 failed_internal_filters):
271 for t in failed_external_filters: 288 for t in failed_external_filters:
272 if t not in content: 289 if t not in content:
273 return True 290 return True
274 filename, linenum, filter = t 291 filename, linenum, filter = t
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
323 count = 0 340 count = 0
324 for n in node.children: 341 for n in node.children:
325 for d in self.get_dicts(n, ctx, new_content, shortname, dep): 342 for d in self.get_dicts(n, ctx, new_content, shortname, dep):
326 count += 1 343 count += 1
327 yield d 344 yield d
328 # Reached leaf? 345 # Reached leaf?
329 if not node.children: 346 if not node.children:
330 self._debug(" reached leaf, returning it") 347 self._debug(" reached leaf, returning it")
331 d = {"name": name, "dep": dep, "shortname": ".".join(shortname)} 348 d = {"name": name, "dep": dep, "shortname": ".".join(shortname)}
332 for filename, linenum, op in new_content: 349 for filename, linenum, op in new_content:
333 op.apply_to_dict(d, ctx, ctx_set) 350 op.apply_to_dict(d)
334 yield d 351 yield d
335 # If this node did not produce any dicts, remember the failed filters 352 # If this node did not produce any dicts, remember the failed filters
336 # of its descendants 353 # of its descendants
337 elif not count: 354 elif not count:
338 new_external_filters = [] 355 new_external_filters = []
339 new_internal_filters = [] 356 new_internal_filters = []
340 for n in node.children: 357 for n in node.children:
341 (failed_ctx, 358 (failed_ctx,
342 failed_ctx_set, 359 failed_ctx_set,
343 failed_external_filters, 360 failed_external_filters,
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
422 while True: 439 while True:
423 line, indent, linenum = cr.get_next_line(prev_indent) 440 line, indent, linenum = cr.get_next_line(prev_indent)
424 if not line: 441 if not line:
425 break 442 break
426 443
427 words = line.split(None, 1) 444 words = line.split(None, 1)
428 445
429 # Parse 'variants' 446 # Parse 'variants'
430 if line == "variants:": 447 if line == "variants:":
431 # 'variants' is not allowed inside a conditional block 448 # 'variants' is not allowed inside a conditional block
432 if isinstance(node, Condition): 449 if (isinstance(node, Condition) or
450 isinstance(node, NegativeCondition)):
433 raise ParserError("'variants' is not allowed inside a " 451 raise ParserError("'variants' is not allowed inside a "
434 "conditional block", 452 "conditional block",
435 None, cr.filename, linenum) 453 None, cr.filename, linenum)
436 node = self._parse_variants(cr, node, prev_indent=indent) 454 node = self._parse_variants(cr, node, prev_indent=indent)
437 continue 455 continue
438 456
439 # Parse 'include' statements 457 # Parse 'include' statements
440 if words[0] == "include": 458 if words[0] == "include":
441 if len(words) < 2: 459 if len(words) < 2:
442 raise ParserError("Syntax error: missing parameter", 460 raise ParserError("Syntax error: missing parameter",
443 line, cr.filename, linenum) 461 line, cr.filename, linenum)
444 if not isinstance(cr, FileReader): 462 filename = os.path.expanduser(words[1])
445 raise ParserError("Cannot include because no file is " 463 if isinstance(cr, FileReader) and not os.path.isabs(filename):
446 "currently open", 464 filename = os.path.join(os.path.dirname(cr.filename),
447 line, cr.filename, linenum) 465 filename)
448 filename = os.path.join(os.path.dirname(cr.filename), words[1])
449 if not os.path.isfile(filename): 466 if not os.path.isfile(filename):
450 self._warn("%r (%s:%s): file doesn't exist or is not a " 467 self._warn("%r (%s:%s): file doesn't exist or is not a "
451 "regular file", line, cr.filename, linenum) 468 "regular file", line, cr.filename, linenum)
452 continue 469 continue
453 node = self._parse(FileReader(filename), node) 470 node = self._parse(FileReader(filename), node)
454 continue 471 continue
455 472
456 # Parse 'only' and 'no' filters 473 # Parse 'only' and 'no' filters
457 if words[0] in ("only", "no"): 474 if words[0] in ("only", "no"):
458 if len(words) < 2: 475 if len(words) < 2:
459 raise ParserError("Syntax error: missing parameter", 476 raise ParserError("Syntax error: missing parameter",
460 line, cr.filename, linenum) 477 line, cr.filename, linenum)
461 try: 478 try:
462 if words[0] == "only": 479 if words[0] == "only":
463 f = OnlyFilter(line) 480 f = OnlyFilter(line)
464 elif words[0] == "no": 481 elif words[0] == "no":
465 f = NoFilter(line) 482 f = NoFilter(line)
466 except ParserError, e: 483 except ParserError, e:
467 e.line = line 484 e.line = line
468 e.filename = cr.filename 485 e.filename = cr.filename
469 e.linenum = linenum 486 e.linenum = linenum
470 raise 487 raise
471 node.content += [(cr.filename, linenum, f)] 488 node.content += [(cr.filename, linenum, f)]
472 continue 489 continue
473 490
491 # Look for operators
492 op_match = _ops_exp.search(line)
493
474 # Parse conditional blocks 494 # Parse conditional blocks
475 if line.endswith(":"): 495 if ":" in line:
476 try: 496 index = line.index(":")
477 cond = Condition(line) 497 if not op_match or index < op_match.start():
478 except ParserError, e: 498 index += 1
479 e.line = line 499 cr.set_next_line(line[index:], indent, linenum)
480 e.filename = cr.filename 500 line = line[:index]
481 e.linenum = linenum 501 try:
482 raise 502 if line.startswith("!"):
483 self._parse(cr, cond, prev_indent=indent) 503 cond = NegativeCondition(line)
484 node.content += [(cr.filename, linenum, cond)] 504 else:
485 continue 505 cond = Condition(line)
506 except ParserError, e:
507 e.line = line
508 e.filename = cr.filename
509 e.linenum = linenum
510 raise
511 self._parse(cr, cond, prev_indent=indent)
512 node.content += [(cr.filename, linenum, cond)]
513 continue
486 514
487 # Parse regular operators 515 # Parse regular operators
488 try: 516 if not op_match:
489 op = Op(line) 517 raise ParserError("Syntax error", line, cr.filename, linenum)
490 except ParserError, e: 518 node.content += [(cr.filename, linenum, Op(line, op_match))]
491 e.line = line
492 e.filename = cr.filename
493 e.linenum = linenum
494 raise
495 node.content += [(cr.filename, linenum, op)]
496 519
497 return node 520 return node
498 521
499 522
500 # Assignment operators 523 # Assignment operators
501 524
502 _reserved_keys = set(("name", "shortname", "dep")) 525 _reserved_keys = set(("name", "shortname", "dep"))
503 526
504 527
505 def _op_set(d, key, value): 528 def _op_set(d, key, value):
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
550 "<=": (r"\<\=", _op_prepend), 573 "<=": (r"\<\=", _op_prepend),
551 "?=": (r"\?\=", _op_regex_set), 574 "?=": (r"\?\=", _op_regex_set),
552 "?+=": (r"\?\+\=", _op_regex_append), 575 "?+=": (r"\?\+\=", _op_regex_append),
553 "?<=": (r"\?\<\=", _op_regex_prepend), 576 "?<=": (r"\?\<\=", _op_regex_prepend),
554 "del": (r"^del\b", _op_regex_del)} 577 "del": (r"^del\b", _op_regex_del)}
555 578
556 _ops_exp = re.compile("|".join([op[0] for op in _ops.values()])) 579 _ops_exp = re.compile("|".join([op[0] for op in _ops.values()]))
557 580
558 581
559 class Op(object): 582 class Op(object):
560 def __init__(self, line): 583 def __init__(self, line, m):
561 m = re.search(_ops_exp, line) 584 self.func = _ops[m.group()][1]
562 if not m: 585 self.key = line[:m.start()].strip()
563 raise ParserError("Syntax error: missing operator")
564 left = line[:m.start()].strip()
565 value = line[m.end():].strip() 586 value = line[m.end():].strip()
566 if value and ((value[0] == '"' and value[-1] == '"') or 587 if value and (value[0] == value[-1] == '"' or
567 (value[0] == "'" and value[-1] == "'")): 588 value[0] == value[-1] == "'"):
568 value = value[1:-1] 589 value = value[1:-1]
569 filters_and_key = map(str.strip, left.split(":"))
570 self.filters = [Filter(f) for f in filters_and_key[:-1]]
571 self.key = filters_and_key[-1]
572 self.value = value 590 self.value = value
573 self.func = _ops[m.group()][1]
574 591
575 592
576 def apply_to_dict(self, d, ctx, ctx_set): 593 def apply_to_dict(self, d):
577 for f in self.filters:
578 if not f.match(ctx, ctx_set):
579 return
580 self.func(d, self.key, self.value) 594 self.func(d, self.key, self.value)
581 595
582 596
583 # StrReader and FileReader 597 # StrReader and FileReader
584 598
585 class StrReader(object): 599 class StrReader(object):
586 """ 600 """
587 Preprocess an input string for easy reading. 601 Preprocess an input string for easy reading.
588 """ 602 """
589 def __init__(self, s): 603 def __init__(self, s):
590 """ 604 """
591 Initialize the reader. 605 Initialize the reader.
592 606
593 @param s: The string to parse. 607 @param s: The string to parse.
594 """ 608 """
595 self.filename = "<string>" 609 self.filename = "<string>"
596 self._lines = [] 610 self._lines = []
597 self._line_index = 0 611 self._line_index = 0
612 self._stored_line = None
598 for linenum, line in enumerate(s.splitlines()): 613 for linenum, line in enumerate(s.splitlines()):
599 line = line.rstrip().expandtabs() 614 line = line.rstrip().expandtabs()
600 stripped_line = line.lstrip() 615 stripped_line = line.lstrip()
601 indent = len(line) - len(stripped_line) 616 indent = len(line) - len(stripped_line)
602 if (not stripped_line 617 if (not stripped_line
603 or stripped_line.startswith("#") 618 or stripped_line.startswith("#")
604 or stripped_line.startswith("//")): 619 or stripped_line.startswith("//")):
605 continue 620 continue
606 self._lines.append((stripped_line, indent, linenum + 1)) 621 self._lines.append((stripped_line, indent, linenum + 1))
607 622
608 623
609 def get_next_line(self, prev_indent): 624 def get_next_line(self, prev_indent):
610 """ 625 """
611 Get the next non-empty, non-comment line in the string, whose 626 Get the next line in the current block.
612 indentation level is higher than prev_indent.
613 627
614 @param prev_indent: The indentation level of the previous block. 628 @param prev_indent: The indentation level of the previous block.
615 @return: (line, indent, linenum), where indent is the line's 629 @return: (line, indent, linenum), where indent is the line's
616 indentation level. If no line is available, (None, -1, -1) is 630 indentation level. If no line is available, (None, -1, -1) is
617 returned. 631 returned.
618 """ 632 """
633 if self._stored_line:
634 ret = self._stored_line
635 self._stored_line = None
636 return ret
619 if self._line_index >= len(self._lines): 637 if self._line_index >= len(self._lines):
620 return None, -1, -1 638 return None, -1, -1
621 line, indent, linenum = self._lines[self._line_index] 639 line, indent, linenum = self._lines[self._line_index]
622 if indent <= prev_indent: 640 if indent <= prev_indent:
623 return None, -1, -1 641 return None, -1, -1
624 self._line_index += 1 642 self._line_index += 1
625 return line, indent, linenum 643 return line, indent, linenum
626 644
627 645
646 def set_next_line(self, line, indent, linenum):
647 """
648 Make the next call to get_next_line() return the given line instead of
649 the real next line.
650 """
651 line = line.strip()
652 if line:
653 self._stored_line = line, indent, linenum
654
655
628 class FileReader(StrReader): 656 class FileReader(StrReader):
629 """ 657 """
630 Preprocess an input file for easy reading. 658 Preprocess an input file for easy reading.
631 """ 659 """
632 def __init__(self, filename): 660 def __init__(self, filename):
633 """ 661 """
634 Initialize the reader. 662 Initialize the reader.
635 663
636 @parse filename: The name of the input file. 664 @parse filename: The name of the input file.
637 """ 665 """
638 StrReader.__init__(self, open(filename).read()) 666 StrReader.__init__(self, open(filename).read())
639 self.filename = filename 667 self.filename = filename
640 668
641 669
642 if __name__ == "__main__": 670 if __name__ == "__main__":
643 parser = optparse.OptionParser("usage: %prog [options] <filename>") 671 parser = optparse.OptionParser('usage: %prog [options] filename '
672 '[extra code] ...\n\nExample:\n\n '
673 '%prog tests.cfg "only my_set" "no qcow2"')
644 parser.add_option("-v", "--verbose", dest="debug", action="store_true", 674 parser.add_option("-v", "--verbose", dest="debug", action="store_true",
645 help="include debug messages in console output") 675 help="include debug messages in console output")
646 parser.add_option("-f", "--fullname", dest="fullname", action="store_true", 676 parser.add_option("-f", "--fullname", dest="fullname", action="store_true",
647 help="show full dict names instead of short names") 677 help="show full dict names instead of short names")
648 parser.add_option("-c", "--contents", dest="contents", action="store_true", 678 parser.add_option("-c", "--contents", dest="contents", action="store_true",
649 help="show dict contents") 679 help="show dict contents")
650 680
651 options, args = parser.parse_args() 681 options, args = parser.parse_args()
652 if not args: 682 if not args:
653 parser.error("filename required") 683 parser.error("filename required")
654 684
655 c = Parser(args[0], debug=options.debug) 685 c = Parser(args[0], debug=options.debug)
686 for s in args[1:]:
687 c.parse_string(s)
688
656 for i, d in enumerate(c.get_dicts()): 689 for i, d in enumerate(c.get_dicts()):
657 if options.fullname: 690 if options.fullname:
658 print "dict %4d: %s" % (i + 1, d["name"]) 691 print "dict %4d: %s" % (i + 1, d["name"])
659 else: 692 else:
660 print "dict %4d: %s" % (i + 1, d["shortname"]) 693 print "dict %4d: %s" % (i + 1, d["shortname"])
661 if options.contents: 694 if options.contents:
662 keys = d.keys() 695 keys = d.keys()
663 keys.sort() 696 keys.sort()
664 for key in keys: 697 for key in keys:
665 print " %s = %s" % (key, d[key]) 698 print " %s = %s" % (key, d[key])
OLDNEW
« no previous file with comments | « client/tests/kvm/installer.py ('k') | client/tests/kvm/kvm_utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698