deleted file mode 100644
@@ -1,64 +0,0 @@
-AUTHOR = """
-uril@redhat.com (Uri Lublin)
-drusso@redhat.com (Dror Russo)
-mgoldish@redhat.com (Michael Goldish)
-dhuff@redhat.com (David Huff)
-aeromenk@redhat.com (Alexey Eromenko)
-mburns@redhat.com (Mike Burns)
-"""
-TIME = 'MEDIUM'
-NAME = 'KVM test'
-TEST_TYPE = 'client'
-TEST_CLASS = 'Virtualization'
-TEST_CATEGORY = 'Functional'
-
-DOC = """
-Executes the KVM test framework on a given host. This module is separated in
-minor functions, that execute different tests for doing Quality Assurance on
-KVM (both kernelspace and userspace) code.
-
-For online docs, please refer to http://www.linux-kvm.org/page/KVM-Autotest
-"""
-
-import sys, os, logging
-# Add the KVM tests dir to the python path
-kvm_test_dir = os.path.join(os.environ['AUTODIR'],'tests/kvm')
-sys.path.append(kvm_test_dir)
-# Now we can import modules inside the KVM tests dir
-import kvm_utils, kvm_config
-
-# set English environment (command output might be localized, need to be safe)
-os.environ['LANG'] = 'en_US.UTF-8'
-
-build_cfg_path = os.path.join(kvm_test_dir, "build.cfg")
-build_cfg = kvm_config.config(build_cfg_path)
-# Make any desired changes to the build configuration here. For example:
-#build_cfg.parse_string("""
-#release_tag = 84
-#""")
-if not kvm_utils.run_tests(build_cfg.get_list(), job):
- logging.error("KVM build step failed, exiting.")
- sys.exit(1)
-
-tests_cfg_path = os.path.join(kvm_test_dir, "tests.cfg")
-tests_cfg = kvm_config.config(tests_cfg_path)
-# Make any desired changes to the test configuration here. For example:
-#tests_cfg.parse_string("""
-#display = sdl
-#install|setup: timeout_multiplier = 3
-#""")
-
-pools_cfg_path = os.path.join(kvm_test_dir, "address_pools.cfg")
-tests_cfg.parse_file(pools_cfg_path)
-hostname = os.uname()[1].split(".")[0]
-if tests_cfg.filter("^" + hostname):
- tests_cfg.parse_string("only ^%s" % hostname)
-else:
- tests_cfg.parse_string("only ^default_host")
-
-# Run the tests
-kvm_utils.run_tests(tests_cfg.get_list(), job)
-
-# Generate a nice HTML report inside the job's results dir
-kvm_utils.create_report(kvm_test_dir, job.resultdir)
-
new file mode 100755
@@ -0,0 +1,586 @@
+#!/usr/bin/python
+"""
+KVM configuration file utility functions.
+
+@copyright: Red Hat 2008-2009
+"""
+
+import logging, re, os, sys, StringIO, optparse
+import common
+import kvm_utils
+from autotest_lib.client.common_lib import error
+from autotest_lib.client.common_lib import logging_config, logging_manager
+
+
+class config:
+ """
+ Parse an input file or string that follows the KVM Test Config File format
+ and generate a list of dicts that will be later used as configuration
+ parameters by the the KVM tests.
+
+ @see: http://www.linux-kvm.org/page/KVM-Autotest/Test_Config_File
+ """
+
+ def __init__(self, filename=None, debug=False):
+ """
+ Initialize the list and optionally parse filename.
+
+ @param filename: Path of the file that will be taken.
+ @param debug: Whether to turn debugging output.
+ """
+ self.list = [{"name": "", "shortname": "", "depend": []}]
+ self.debug = debug
+ self.filename = filename
+ if filename:
+ self.parse_file(filename)
+
+
+ def parse_file(self, filename):
+ """
+ Parse filename, return the resulting list and store it in .list. If
+ filename does not exist, raise an exception.
+
+ @param filename: Path of the configuration file.
+ """
+ if not os.path.exists(filename):
+ raise Exception, "File %s not found" % filename
+ self.filename = filename
+ file = open(filename, "r")
+ self.list = self.parse(file, self.list)
+ file.close()
+ return self.list
+
+
+ def parse_string(self, str):
+ """
+ Parse a string, return the resulting list and store it in .list.
+
+ @param str: String that will be parsed.
+ """
+ file = StringIO.StringIO(str)
+ self.list = self.parse(file, self.list)
+ file.close()
+ return self.list
+
+
+ def get_list(self):
+ """
+ Return the list of dictionaries. This should probably be called after
+ parsing something.
+ """
+ return self.list
+
+
+ def match(self, filter, dict):
+ """
+ Return True if dict matches filter.
+
+ @param filter: A regular expression that defines the filter.
+ @param dict: Dictionary that will be inspected.
+ """
+ filter = re.compile(r"(\.|^)(%s)(\.|$)" % filter)
+ return bool(filter.search(dict["name"]))
+
+
+ def filter(self, filter, list=None):
+ """
+ Filter a list of dicts.
+
+ @param filter: A regular expression that will be used as a filter.
+ @param list: A list of dictionaries that will be filtered.
+ """
+ if list is None:
+ list = self.list
+ return [dict for dict in list if self.match(filter, dict)]
+
+
+ def split_and_strip(self, str, sep="="):
+ """
+ Split str and strip quotes from the resulting parts.
+
+ @param str: String that will be processed
+ @param sep: Separator that will be used to split the string
+ """
+ temp = str.split(sep, 1)
+ for i in range(len(temp)):
+ temp[i] = temp[i].strip()
+ if re.findall("^\".*\"$", temp[i]):
+ temp[i] = temp[i].strip("\"")
+ elif re.findall("^\'.*\'$", temp[i]):
+ temp[i] = temp[i].strip("\'")
+ return temp
+
+
+ def get_next_line(self, file):
+ """
+ Get the next non-empty, non-comment line in a file like object.
+
+ @param file: File like object
+ @return: If no line is available, return None.
+ """
+ while True:
+ line = file.readline()
+ if line == "": return None
+ stripped_line = line.strip()
+ if len(stripped_line) > 0 \
+ and not stripped_line.startswith('#') \
+ and not stripped_line.startswith('//'):
+ return line
+
+
+ def get_next_line_indent(self, file):
+ """
+ Return the indent level of the next non-empty, non-comment line in file.
+
+ @param file: File like object.
+ @return: If no line is available, return -1.
+ """
+ pos = file.tell()
+ line = self.get_next_line(file)
+ if not line:
+ file.seek(pos)
+ return -1
+ line = line.expandtabs()
+ indent = 0
+ while line[indent] == ' ':
+ indent += 1
+ file.seek(pos)
+ return indent
+
+
+ def add_name(self, str, name, append=False):
+ """
+ Add name to str with a separator dot and return the result.
+
+ @param str: String that will be processed
+ @param name: name that will be appended to the string.
+ @return: If append is True, append name to str.
+ Otherwise, pre-pend name to str.
+ """
+ if str == "":
+ return name
+ # Append?
+ elif append:
+ return str + "." + name
+ # Prepend?
+ else:
+ return name + "." + str
+
+
+ def parse_variants(self, file, list, subvariants=False, prev_indent=-1):
+ """
+ Read and parse lines from file like object until a line with an indent
+ level lower than or equal to prev_indent is encountered.
+
+ @brief: Parse a 'variants' or 'subvariants' block from a file-like
+ object.
+ @param file: File-like object that will be parsed
+ @param list: List of dicts to operate on
+ @param subvariants: If True, parse in 'subvariants' mode;
+ otherwise parse in 'variants' mode
+ @param prev_indent: The indent level of the "parent" block
+ @return: The resulting list of dicts.
+ """
+ new_list = []
+
+ while True:
+ indent = self.get_next_line_indent(file)
+ if indent <= prev_indent:
+ break
+ indented_line = self.get_next_line(file).rstrip()
+ line = indented_line.strip()
+
+ # Get name and dependencies
+ temp = line.strip("- ").split(":")
+ name = temp[0]
+ if len(temp) == 1:
+ dep_list = []
+ else:
+ dep_list = temp[1].split()
+
+ # See if name should be added to the 'shortname' field
+ add_to_shortname = True
+ if name.startswith("@"):
+ name = name.strip("@")
+ add_to_shortname = False
+
+ # Make a deep copy of list
+ temp_list = []
+ for dict in list:
+ new_dict = dict.copy()
+ new_dict["depend"] = dict["depend"][:]
+ temp_list.append(new_dict)
+
+ if subvariants:
+ # If we're parsing 'subvariants', first modify the list
+ self.__modify_list_subvariants(temp_list, name, dep_list,
+ add_to_shortname)
+ temp_list = self.parse(file, temp_list,
+ restricted=True, prev_indent=indent)
+ else:
+ # If we're parsing 'variants', parse before modifying the list
+ if self.debug:
+ self.__debug_print(indented_line,
+ "Entering variant '%s' "
+ "(variant inherits %d dicts)" %
+ (name, len(list)))
+ temp_list = self.parse(file, temp_list,
+ restricted=False, prev_indent=indent)
+ self.__modify_list_variants(temp_list, name, dep_list,
+ add_to_shortname)
+
+ new_list += temp_list
+
+ return new_list
+
+
+ def parse(self, file, list, restricted=False, prev_indent=-1):
+ """
+ Read and parse lines from file until a line with an indent level lower
+ than or equal to prev_indent is encountered.
+
+ @brief: Parse a file-like object.
+ @param file: A file-like object
+ @param list: A list of dicts to operate on (list is modified in
+ place and should not be used after the call)
+ @param restricted: if True, operate in restricted mode
+ (prohibit 'variants')
+ @param prev_indent: the indent level of the "parent" block
+ @return: Return the resulting list of dicts.
+ @note: List is destroyed and should not be used after the call.
+ Only the returned list should be used.
+ """
+ while True:
+ indent = self.get_next_line_indent(file)
+ if indent <= prev_indent:
+ break
+ indented_line = self.get_next_line(file).rstrip()
+ line = indented_line.strip()
+ words = line.split()
+
+ len_list = len(list)
+
+ # Look for a known operator in the line
+ operators = ["?+=", "?<=", "?=", "+=", "<=", "="]
+ op_found = None
+ op_pos = len(line)
+ for op in operators:
+ pos = line.find(op)
+ if pos >= 0 and pos < op_pos:
+ op_found = op
+ op_pos = pos
+
+ # Found an operator?
+ if op_found:
+ if self.debug and not restricted:
+ self.__debug_print(indented_line,
+ "Parsing operator (%d dicts in current "
+ "context)" % len_list)
+ (left, value) = self.split_and_strip(line, op_found)
+ filters_and_key = self.split_and_strip(left, ":")
+ filters = filters_and_key[:-1]
+ key = filters_and_key[-1]
+ filtered_list = list
+ for filter in filters:
+ filtered_list = self.filter(filter, filtered_list)
+ # Apply the operation to the filtered list
+ if op_found == "=":
+ for dict in filtered_list:
+ dict[key] = value
+ elif op_found == "+=":
+ for dict in filtered_list:
+ dict[key] = dict.get(key, "") + value
+ elif op_found == "<=":
+ for dict in filtered_list:
+ dict[key] = value + dict.get(key, "")
+ elif op_found.startswith("?"):
+ exp = re.compile("^(%s)$" % key)
+ if op_found == "?=":
+ for dict in filtered_list:
+ for key in dict.keys():
+ if exp.match(key):
+ dict[key] = value
+ elif op_found == "?+=":
+ for dict in filtered_list:
+ for key in dict.keys():
+ if exp.match(key):
+ dict[key] = dict.get(key, "") + value
+ elif op_found == "?<=":
+ for dict in filtered_list:
+ for key in dict.keys():
+ if exp.match(key):
+ dict[key] = value + dict.get(key, "")
+
+ # Parse 'no' and 'only' statements
+ elif words[0] == "no" or words[0] == "only":
+ if len(words) <= 1:
+ continue
+ filters = words[1:]
+ filtered_list = []
+ if words[0] == "no":
+ for dict in list:
+ for filter in filters:
+ if self.match(filter, dict):
+ break
+ else:
+ filtered_list.append(dict)
+ if words[0] == "only":
+ for dict in list:
+ for filter in filters:
+ if self.match(filter, dict):
+ filtered_list.append(dict)
+ break
+ list = filtered_list
+ if self.debug and not restricted:
+ self.__debug_print(indented_line,
+ "Parsing no/only (%d dicts in current "
+ "context, %d remain)" %
+ (len_list, len(list)))
+
+ # Parse 'variants'
+ elif line == "variants:":
+ # 'variants' not allowed in restricted mode
+ # (inside an exception or inside subvariants)
+ if restricted:
+ e_msg = "Using variants in this context is not allowed"
+ raise error.AutotestError(e_msg)
+ if self.debug and not restricted:
+ self.__debug_print(indented_line,
+ "Entering variants block (%d dicts in "
+ "current context)" % len_list)
+ list = self.parse_variants(file, list, subvariants=False,
+ prev_indent=indent)
+
+ # Parse 'subvariants' (the block is parsed for each dict
+ # separately)
+ elif line == "subvariants:":
+ if self.debug and not restricted:
+ self.__debug_print(indented_line,
+ "Entering subvariants block (%d dicts in "
+ "current context)" % len_list)
+ new_list = []
+ # Remember current file position
+ pos = file.tell()
+ # Read the lines in any case
+ self.parse_variants(file, [], subvariants=True,
+ prev_indent=indent)
+ # Iterate over the list...
+ for index in range(len(list)):
+ # Revert to initial file position in this 'subvariants'
+ # block
+ file.seek(pos)
+ # Everything inside 'subvariants' should be parsed in
+ # restricted mode
+ new_list += self.parse_variants(file, list[index:index+1],
+ subvariants=True,
+ prev_indent=indent)
+ list = new_list
+
+ # Parse 'include' statements
+ elif words[0] == "include":
+ if len(words) <= 1:
+ continue
+ if self.debug and not restricted:
+ self.__debug_print(indented_line,
+ "Entering file %s" % words[1])
+ if self.filename:
+ filename = os.path.join(os.path.dirname(self.filename),
+ words[1])
+ if os.path.exists(filename):
+ new_file = open(filename, "r")
+ list = self.parse(new_file, list, restricted)
+ new_file.close()
+ if self.debug and not restricted:
+ self.__debug_print("", "Leaving file %s" % words[1])
+ else:
+ logging.warning("Cannot include %s -- file not found",
+ filename)
+ else:
+ logging.warning("Cannot include %s because no file is "
+ "currently open", words[1])
+
+ # Parse multi-line exceptions
+ # (the block is parsed for each dict separately)
+ elif line.endswith(":"):
+ if self.debug and not restricted:
+ self.__debug_print(indented_line,
+ "Entering multi-line exception block "
+ "(%d dicts in current context outside "
+ "exception)" % len_list)
+ line = line.strip(":")
+ new_list = []
+ # Remember current file position
+ pos = file.tell()
+ # Read the lines in any case
+ self.parse(file, [], restricted=True, prev_indent=indent)
+ # Iterate over the list...
+ for index in range(len(list)):
+ if self.match(line, list[index]):
+ # Revert to initial file position in this
+ # exception block
+ file.seek(pos)
+ # Everything inside an exception should be parsed in
+ # restricted mode
+ new_list += self.parse(file, list[index:index+1],
+ restricted=True,
+ prev_indent=indent)
+ else:
+ new_list += list[index:index+1]
+ list = new_list
+
+ return list
+
+
+ def __debug_print(self, str1, str2=""):
+ """
+ Nicely print two strings and an arrow.
+
+ @param str1: First string
+ @param str2: Second string
+ """
+ if str2:
+ str = "%-50s ---> %s" % (str1, str2)
+ else:
+ str = str1
+ logging.debug(str)
+
+
+ def __modify_list_variants(self, list, name, dep_list, add_to_shortname):
+ """
+ Make some modifications to list, as part of parsing a 'variants' block.
+
+ @param list: List to be processed
+ @param name: Name to be prepended to the dictionary's 'name' key
+ @param dep_list: List of dependencies to be added to the dictionary's
+ 'depend' key
+ @param add_to_shortname: Boolean indicating whether name should be
+ prepended to the dictionary's 'shortname' key as well
+ """
+ for dict in list:
+ # Prepend name to the dict's 'name' field
+ dict["name"] = self.add_name(dict["name"], name)
+ # Prepend name to the dict's 'shortname' field
+ if add_to_shortname:
+ dict["shortname"] = self.add_name(dict["shortname"], name)
+ # Prepend name to each of the dict's dependencies
+ for i in range(len(dict["depend"])):
+ dict["depend"][i] = self.add_name(dict["depend"][i], name)
+ # Add new dependencies
+ dict["depend"] += dep_list
+
+
+ def __modify_list_subvariants(self, list, name, dep_list, add_to_shortname):
+ """
+ Make some modifications to list, as part of parsing a 'subvariants'
+ block.
+
+ @param list: List to be processed
+ @param name: Name to be appended to the dictionary's 'name' key
+ @param dep_list: List of dependencies to be added to the dictionary's
+ 'depend' key
+ @param add_to_shortname: Boolean indicating whether name should be
+ appended to the dictionary's 'shortname' as well
+ """
+ for dict in list:
+ # Add new dependencies
+ for dep in dep_list:
+ dep_name = self.add_name(dict["name"], dep, append=True)
+ dict["depend"].append(dep_name)
+ # Append name to the dict's 'name' field
+ dict["name"] = self.add_name(dict["name"], name, append=True)
+ # Append name to the dict's 'shortname' field
+ if add_to_shortname:
+ dict["shortname"] = self.add_name(dict["shortname"], name,
+ append=True)
+
+
+def create_control(dict_list, control_path):
+ """
+ Creates a kvm test control file from a given test list dictionary.
+
+ @param dict_list: A list with dictionaries representing kvm test parameters.
+ @param control_path: Path to the kvm control file that will be generated.
+ """
+ indent = " "
+ indent_level = 0
+ control_file = open(control_path, "w")
+ control_file.write("# Control file generated by create_control.py\n")
+ control_file.write("kvm_test_dir = os.path.join(os.environ['AUTODIR'], "
+ "'tests/kvm')\n")
+ control_file.write("sys.path.append(kvm_test_dir)\n")
+
+ while dict_list:
+ current_dict = dict_list[0]
+ test_iterations = int(current_dict.get("iterations", 1))
+ test_tag = current_dict.get("shortname")
+
+ if len(current_dict.get("depend")) == 0:
+ indent_level = 0
+
+ try:
+ future_dict = dict_list[1]
+ except IndexError:
+ control_file.write("%sjob.run_test('kvm', params=%s, tag='%s', "
+ "iterations=%s)\n" % (indent * indent_level,
+ current_dict, test_tag,
+ test_iterations))
+ break
+
+ if current_dict.get("name") in future_dict.get("depend"):
+ control_file.write("%sif job.run_test('kvm', params=%s, tag='%s', "
+ "iterations=%s):\n" % (indent * indent_level,
+ current_dict, test_tag,
+ test_iterations))
+ indent_level += 1
+ else:
+ control_file.write("%sjob.run_test('kvm', params=%s, tag='%s', "
+ "iterations=%s)\n" % (indent * indent_level,
+ current_dict, test_tag,
+ test_iterations))
+ dict_list.pop(0)
+ continue
+
+ control_file.close()
+
+
+if __name__ == "__main__":
+ parser = optparse.OptionParser()
+ parser.add_option('-f', '--file', dest="filename", action='store',
+ help='path to a config file that will be parsed. '
+ 'If not specified, will parse kvm_tests.cfg '
+ 'located inside the kvm test dir.')
+ parser.add_option('-c', '--control', dest="control_path", action='store',
+ help='path to an output control file. If not specified, '
+ 'will generate a file called "control" at the top '
+ 'of the kvm test directory.')
+ parser.add_option('--verbose', dest="debug", action='store_true',
+ help='include debug messages in console output')
+ options, args = parser.parse_args()
+ filename = options.filename
+ control_path = options.control_path
+ debug = options.debug
+
+ if not filename:
+ filename = os.path.join(os.path.dirname(sys.argv[0]), "tests.cfg")
+
+ # Here we configure the stand alone program to use the autotest
+ # logging system.
+ logging_manager.configure_logging(kvm_utils.KvmLoggingConfig(), verbose=debug)
+ list = config(filename, debug=debug).get_list()
+ i = 0
+ logging.info("List of dictionaries generated from config file %s:",
+ filename)
+ for dict in list:
+ logging.info("Dictionary #%d:", i)
+ keys = dict.keys()
+ keys.sort()
+ for key in keys:
+ logging.info(" %s = %s", key, dict[key])
+ i += 1
+
+ if not control_path:
+ control_path = os.path.join(os.path.dirname(sys.argv[0]), "control")
+
+ logging.info("Creating control file %s from config file", control_path)
+
+ create_control(list, control_path)
deleted file mode 100755
@@ -1,524 +0,0 @@
-#!/usr/bin/python
-"""
-KVM configuration file utility functions.
-
-@copyright: Red Hat 2008-2009
-"""
-
-import logging, re, os, sys, StringIO, optparse
-import common
-import kvm_utils
-from autotest_lib.client.common_lib import error
-from autotest_lib.client.common_lib import logging_config, logging_manager
-
-
-class config:
- """
- Parse an input file or string that follows the KVM Test Config File format
- and generate a list of dicts that will be later used as configuration
- parameters by the the KVM tests.
-
- @see: http://www.linux-kvm.org/page/KVM-Autotest/Test_Config_File
- """
-
- def __init__(self, filename=None, debug=False):
- """
- Initialize the list and optionally parse filename.
-
- @param filename: Path of the file that will be taken.
- @param debug: Whether to turn debugging output.
- """
- self.list = [{"name": "", "shortname": "", "depend": []}]
- self.debug = debug
- self.filename = filename
- if filename:
- self.parse_file(filename)
-
-
- def parse_file(self, filename):
- """
- Parse filename, return the resulting list and store it in .list. If
- filename does not exist, raise an exception.
-
- @param filename: Path of the configuration file.
- """
- if not os.path.exists(filename):
- raise Exception, "File %s not found" % filename
- self.filename = filename
- file = open(filename, "r")
- self.list = self.parse(file, self.list)
- file.close()
- return self.list
-
-
- def parse_string(self, str):
- """
- Parse a string, return the resulting list and store it in .list.
-
- @param str: String that will be parsed.
- """
- file = StringIO.StringIO(str)
- self.list = self.parse(file, self.list)
- file.close()
- return self.list
-
-
- def get_list(self):
- """
- Return the list of dictionaries. This should probably be called after
- parsing something.
- """
- return self.list
-
-
- def match(self, filter, dict):
- """
- Return True if dict matches filter.
-
- @param filter: A regular expression that defines the filter.
- @param dict: Dictionary that will be inspected.
- """
- filter = re.compile(r"(\.|^)(%s)(\.|$)" % filter)
- return bool(filter.search(dict["name"]))
-
-
- def filter(self, filter, list=None):
- """
- Filter a list of dicts.
-
- @param filter: A regular expression that will be used as a filter.
- @param list: A list of dictionaries that will be filtered.
- """
- if list is None:
- list = self.list
- return [dict for dict in list if self.match(filter, dict)]
-
-
- def split_and_strip(self, str, sep="="):
- """
- Split str and strip quotes from the resulting parts.
-
- @param str: String that will be processed
- @param sep: Separator that will be used to split the string
- """
- temp = str.split(sep, 1)
- for i in range(len(temp)):
- temp[i] = temp[i].strip()
- if re.findall("^\".*\"$", temp[i]):
- temp[i] = temp[i].strip("\"")
- elif re.findall("^\'.*\'$", temp[i]):
- temp[i] = temp[i].strip("\'")
- return temp
-
-
- def get_next_line(self, file):
- """
- Get the next non-empty, non-comment line in a file like object.
-
- @param file: File like object
- @return: If no line is available, return None.
- """
- while True:
- line = file.readline()
- if line == "": return None
- stripped_line = line.strip()
- if len(stripped_line) > 0 \
- and not stripped_line.startswith('#') \
- and not stripped_line.startswith('//'):
- return line
-
-
- def get_next_line_indent(self, file):
- """
- Return the indent level of the next non-empty, non-comment line in file.
-
- @param file: File like object.
- @return: If no line is available, return -1.
- """
- pos = file.tell()
- line = self.get_next_line(file)
- if not line:
- file.seek(pos)
- return -1
- line = line.expandtabs()
- indent = 0
- while line[indent] == ' ':
- indent += 1
- file.seek(pos)
- return indent
-
-
- def add_name(self, str, name, append=False):
- """
- Add name to str with a separator dot and return the result.
-
- @param str: String that will be processed
- @param name: name that will be appended to the string.
- @return: If append is True, append name to str.
- Otherwise, pre-pend name to str.
- """
- if str == "":
- return name
- # Append?
- elif append:
- return str + "." + name
- # Prepend?
- else:
- return name + "." + str
-
-
- def parse_variants(self, file, list, subvariants=False, prev_indent=-1):
- """
- Read and parse lines from file like object until a line with an indent
- level lower than or equal to prev_indent is encountered.
-
- @brief: Parse a 'variants' or 'subvariants' block from a file-like
- object.
- @param file: File-like object that will be parsed
- @param list: List of dicts to operate on
- @param subvariants: If True, parse in 'subvariants' mode;
- otherwise parse in 'variants' mode
- @param prev_indent: The indent level of the "parent" block
- @return: The resulting list of dicts.
- """
- new_list = []
-
- while True:
- indent = self.get_next_line_indent(file)
- if indent <= prev_indent:
- break
- indented_line = self.get_next_line(file).rstrip()
- line = indented_line.strip()
-
- # Get name and dependencies
- temp = line.strip("- ").split(":")
- name = temp[0]
- if len(temp) == 1:
- dep_list = []
- else:
- dep_list = temp[1].split()
-
- # See if name should be added to the 'shortname' field
- add_to_shortname = True
- if name.startswith("@"):
- name = name.strip("@")
- add_to_shortname = False
-
- # Make a deep copy of list
- temp_list = []
- for dict in list:
- new_dict = dict.copy()
- new_dict["depend"] = dict["depend"][:]
- temp_list.append(new_dict)
-
- if subvariants:
- # If we're parsing 'subvariants', first modify the list
- self.__modify_list_subvariants(temp_list, name, dep_list,
- add_to_shortname)
- temp_list = self.parse(file, temp_list,
- restricted=True, prev_indent=indent)
- else:
- # If we're parsing 'variants', parse before modifying the list
- if self.debug:
- self.__debug_print(indented_line,
- "Entering variant '%s' "
- "(variant inherits %d dicts)" %
- (name, len(list)))
- temp_list = self.parse(file, temp_list,
- restricted=False, prev_indent=indent)
- self.__modify_list_variants(temp_list, name, dep_list,
- add_to_shortname)
-
- new_list += temp_list
-
- return new_list
-
-
- def parse(self, file, list, restricted=False, prev_indent=-1):
- """
- Read and parse lines from file until a line with an indent level lower
- than or equal to prev_indent is encountered.
-
- @brief: Parse a file-like object.
- @param file: A file-like object
- @param list: A list of dicts to operate on (list is modified in
- place and should not be used after the call)
- @param restricted: if True, operate in restricted mode
- (prohibit 'variants')
- @param prev_indent: the indent level of the "parent" block
- @return: Return the resulting list of dicts.
- @note: List is destroyed and should not be used after the call.
- Only the returned list should be used.
- """
- while True:
- indent = self.get_next_line_indent(file)
- if indent <= prev_indent:
- break
- indented_line = self.get_next_line(file).rstrip()
- line = indented_line.strip()
- words = line.split()
-
- len_list = len(list)
-
- # Look for a known operator in the line
- operators = ["?+=", "?<=", "?=", "+=", "<=", "="]
- op_found = None
- op_pos = len(line)
- for op in operators:
- pos = line.find(op)
- if pos >= 0 and pos < op_pos:
- op_found = op
- op_pos = pos
-
- # Found an operator?
- if op_found:
- if self.debug and not restricted:
- self.__debug_print(indented_line,
- "Parsing operator (%d dicts in current "
- "context)" % len_list)
- (left, value) = self.split_and_strip(line, op_found)
- filters_and_key = self.split_and_strip(left, ":")
- filters = filters_and_key[:-1]
- key = filters_and_key[-1]
- filtered_list = list
- for filter in filters:
- filtered_list = self.filter(filter, filtered_list)
- # Apply the operation to the filtered list
- if op_found == "=":
- for dict in filtered_list:
- dict[key] = value
- elif op_found == "+=":
- for dict in filtered_list:
- dict[key] = dict.get(key, "") + value
- elif op_found == "<=":
- for dict in filtered_list:
- dict[key] = value + dict.get(key, "")
- elif op_found.startswith("?"):
- exp = re.compile("^(%s)$" % key)
- if op_found == "?=":
- for dict in filtered_list:
- for key in dict.keys():
- if exp.match(key):
- dict[key] = value
- elif op_found == "?+=":
- for dict in filtered_list:
- for key in dict.keys():
- if exp.match(key):
- dict[key] = dict.get(key, "") + value
- elif op_found == "?<=":
- for dict in filtered_list:
- for key in dict.keys():
- if exp.match(key):
- dict[key] = value + dict.get(key, "")
-
- # Parse 'no' and 'only' statements
- elif words[0] == "no" or words[0] == "only":
- if len(words) <= 1:
- continue
- filters = words[1:]
- filtered_list = []
- if words[0] == "no":
- for dict in list:
- for filter in filters:
- if self.match(filter, dict):
- break
- else:
- filtered_list.append(dict)
- if words[0] == "only":
- for dict in list:
- for filter in filters:
- if self.match(filter, dict):
- filtered_list.append(dict)
- break
- list = filtered_list
- if self.debug and not restricted:
- self.__debug_print(indented_line,
- "Parsing no/only (%d dicts in current "
- "context, %d remain)" %
- (len_list, len(list)))
-
- # Parse 'variants'
- elif line == "variants:":
- # 'variants' not allowed in restricted mode
- # (inside an exception or inside subvariants)
- if restricted:
- e_msg = "Using variants in this context is not allowed"
- raise error.AutotestError(e_msg)
- if self.debug and not restricted:
- self.__debug_print(indented_line,
- "Entering variants block (%d dicts in "
- "current context)" % len_list)
- list = self.parse_variants(file, list, subvariants=False,
- prev_indent=indent)
-
- # Parse 'subvariants' (the block is parsed for each dict
- # separately)
- elif line == "subvariants:":
- if self.debug and not restricted:
- self.__debug_print(indented_line,
- "Entering subvariants block (%d dicts in "
- "current context)" % len_list)
- new_list = []
- # Remember current file position
- pos = file.tell()
- # Read the lines in any case
- self.parse_variants(file, [], subvariants=True,
- prev_indent=indent)
- # Iterate over the list...
- for index in range(len(list)):
- # Revert to initial file position in this 'subvariants'
- # block
- file.seek(pos)
- # Everything inside 'subvariants' should be parsed in
- # restricted mode
- new_list += self.parse_variants(file, list[index:index+1],
- subvariants=True,
- prev_indent=indent)
- list = new_list
-
- # Parse 'include' statements
- elif words[0] == "include":
- if len(words) <= 1:
- continue
- if self.debug and not restricted:
- self.__debug_print(indented_line,
- "Entering file %s" % words[1])
- if self.filename:
- filename = os.path.join(os.path.dirname(self.filename),
- words[1])
- if os.path.exists(filename):
- new_file = open(filename, "r")
- list = self.parse(new_file, list, restricted)
- new_file.close()
- if self.debug and not restricted:
- self.__debug_print("", "Leaving file %s" % words[1])
- else:
- logging.warning("Cannot include %s -- file not found",
- filename)
- else:
- logging.warning("Cannot include %s because no file is "
- "currently open", words[1])
-
- # Parse multi-line exceptions
- # (the block is parsed for each dict separately)
- elif line.endswith(":"):
- if self.debug and not restricted:
- self.__debug_print(indented_line,
- "Entering multi-line exception block "
- "(%d dicts in current context outside "
- "exception)" % len_list)
- line = line.strip(":")
- new_list = []
- # Remember current file position
- pos = file.tell()
- # Read the lines in any case
- self.parse(file, [], restricted=True, prev_indent=indent)
- # Iterate over the list...
- for index in range(len(list)):
- if self.match(line, list[index]):
- # Revert to initial file position in this
- # exception block
- file.seek(pos)
- # Everything inside an exception should be parsed in
- # restricted mode
- new_list += self.parse(file, list[index:index+1],
- restricted=True,
- prev_indent=indent)
- else:
- new_list += list[index:index+1]
- list = new_list
-
- return list
-
-
- def __debug_print(self, str1, str2=""):
- """
- Nicely print two strings and an arrow.
-
- @param str1: First string
- @param str2: Second string
- """
- if str2:
- str = "%-50s ---> %s" % (str1, str2)
- else:
- str = str1
- logging.debug(str)
-
-
- def __modify_list_variants(self, list, name, dep_list, add_to_shortname):
- """
- Make some modifications to list, as part of parsing a 'variants' block.
-
- @param list: List to be processed
- @param name: Name to be prepended to the dictionary's 'name' key
- @param dep_list: List of dependencies to be added to the dictionary's
- 'depend' key
- @param add_to_shortname: Boolean indicating whether name should be
- prepended to the dictionary's 'shortname' key as well
- """
- for dict in list:
- # Prepend name to the dict's 'name' field
- dict["name"] = self.add_name(dict["name"], name)
- # Prepend name to the dict's 'shortname' field
- if add_to_shortname:
- dict["shortname"] = self.add_name(dict["shortname"], name)
- # Prepend name to each of the dict's dependencies
- for i in range(len(dict["depend"])):
- dict["depend"][i] = self.add_name(dict["depend"][i], name)
- # Add new dependencies
- dict["depend"] += dep_list
-
-
- def __modify_list_subvariants(self, list, name, dep_list, add_to_shortname):
- """
- Make some modifications to list, as part of parsing a 'subvariants'
- block.
-
- @param list: List to be processed
- @param name: Name to be appended to the dictionary's 'name' key
- @param dep_list: List of dependencies to be added to the dictionary's
- 'depend' key
- @param add_to_shortname: Boolean indicating whether name should be
- appended to the dictionary's 'shortname' as well
- """
- for dict in list:
- # Add new dependencies
- for dep in dep_list:
- dep_name = self.add_name(dict["name"], dep, append=True)
- dict["depend"].append(dep_name)
- # Append name to the dict's 'name' field
- dict["name"] = self.add_name(dict["name"], name, append=True)
- # Append name to the dict's 'shortname' field
- if add_to_shortname:
- dict["shortname"] = self.add_name(dict["shortname"], name,
- append=True)
-
-
-if __name__ == "__main__":
- parser = optparse.OptionParser()
- parser.add_option('-f', '--file', dest="filename", action='store',
- help='path to a config file that will be parsed. '
- 'If not specified, will parse kvm_tests.cfg '
- 'located inside the kvm test dir.')
- parser.add_option('--verbose', dest="debug", action='store_true',
- help='include debug messages in console output')
-
- options, args = parser.parse_args()
- filename = options.filename
- debug = options.debug
-
- if not filename:
- filename = os.path.join(os.path.dirname(sys.argv[0]), "tests.cfg")
-
- # Here we configure the stand alone program to use the autotest
- # logging system.
- logging_manager.configure_logging(kvm_utils.KvmLoggingConfig(), verbose=debug)
- list = config(filename, debug=debug).get_list()
- i = 0
- for dict in list:
- logging.info("Dictionary #%d:", i)
- keys = dict.keys()
- keys.sort()
- for key in keys:
- logging.info(" %s = %s", key, dict[key])
- i += 1