-
Notifications
You must be signed in to change notification settings - Fork 0
CartesianConfig
This is the format used by test configuration files in the kvm test, that was turned into a common autotest library. It was designed to meet several requirements:
- Support for quick definition of multiple test scenarios (drive format, ACPI support, SMP configuration and other QEMU params) for all guests and tests
- Support for exceptions applying to specific guests/tests/scenarios
- Support for dependencies between tests
- Unification of the various config files previously used into a single (hopefully simple) format
- The parser relies on indentation.
- The parser produces a list of dictionaries (dicts). Each dictionary contains a set of key-value pairs.
- Each dict contains at least three keys:
name
,shortname
anddepend
. The values ofname
andshortname
are strings, and the value ofdepend
is a list of strings. - A list of dictionaries will henceforth be referred to as a frame.
- The initial frame contains a single dict, whose
name
andshortname
are empty strings, and whosedepend
is an empty list. -
Parsing dict contents:
- The dict parser operates on a frame, which will henceforth be referred to as the current frame.
- A statement of the form
<key> = <value>
sets the value of<key>
to<value>
in all dicts of the current frame. If a dict lacks<key>
, it will be created. - A statement of the form
<key> += <value>
appends<value>
to the value of<key>
in all dicts of the current frame. If a dict lacks<key>
, it will be created. - A statement of the form
<key> <= <value>
pre-pends<value>
to the value of<key>
in all dicts of the current frame. If a dict lacks<key>
, it will be created. - A statement of the form
<key> ?= <value>
sets the value of<key>
to<value>
, in all dicts of the current frame, but only if<key>
exists in the dict. The operators?+=
and?<=
are also supported. - A statement of the form
no <regex>
removes from the current frame all dicts whosename
field matches<regex>
. - A statement of the form
only <regex>
removes from the current frame all dicts whosename
field does not match<regex>
. -
Exceptions:
- Single line exceptions have the format
<regex>: <key> <operator> <value>
where<operator>
is any of the operators listed above (e.g.=
,+=
,?<=
). The statement following the regular expression<regex>
will apply only to the dicts in the current frame whosename
partially matches<regex>
(i.e. contains a substring that matches<regex>
). - A multi-line exception block is opened by a line of the format
<regex>:
. The text following this line should be indented. The statements in a multi-line exception block may be assignment statements (such as<key> = <value>
) orno
oronly
statements. Nested multi-line exceptions are allowed.
- Single line exceptions have the format
-
Parsing ``variants``:
- A
variants
block is opened by avariants:
statement. The contents of the block should follow thevariants:
line and should be indented. - A line in a
variants
block should be of the format- <variant_name>:
. The contents of this variant should be specified following that line, and should be indented. The contents are parsed by the dict parser described above; they may be of the format<key> <op> <value>
. They may also containvariants:
statements, or whatever the dict parser recognizes. - Each variant in a
variants
block inherits a copy of the frame in which thevariants:
statement appears. The 'current frame', which may be modified by the dict parser, becomes this copy. - The name of the variant (specified in the line
- <variant_name>:
) is pre-pended to thename
field of each dict of the variant's frame, along with a separator dot ('.'). - If the name of the variant is not preceeded by a
@
(i.e.- @<variant_name>:
), it is pre-pended to theshortname
field of each dict of the variant's frame. In other words, if a variant's name is preceeded by a@
, it is omitted from theshortname
field. - The frames of the variants defined in the
variants
block are joined into a single frame which replaces the frame in which thevariants:
statement appears.
- A
Filter syntax: , means OR .. means AND . means IMMEDIATELY-FOLLOWED-BY Example: qcow2..Fedora.14, RHEL.6..raw..boot, smp2..qcow2..migrate..ide means match all dicts whose names have: (qcow2 AND (Fedora IMMEDIATELY-FOLLOWED-BY 14)) OR ((RHEL IMMEDIATELY-FOLLOWED-BY 6) AND raw AND boot) OR (smp2 AND qcow2 AND migrate AND ide) Note: 'qcow2..Fedora.14' is equivalent to 'Fedora.14..qcow2'. 'qcow2..Fedora.14' is not equivalent to 'qcow2..14.Fedora'. 'ide, scsi' is equivalent to 'scsi, ide'. Filters can be used in 3 ways: only <filter> no <filter> <filter>: The last one starts a conditional block.
Outside of dependency re-ordering, the order of the resulting list follows in order of variant definition. To control the order, you must define a new variant block. For example, from the KVM test.cfg:
variants: - @first: only qemu_kvm_f17_quick..shutdown - @second: only qemu_kvm_f17_quick..unattended_install.cdrom.extra_cdrom_ks - @third: only qemu_kvm_f17_quick..ping - @forth: only qemu_kvm_f17_quick..shutdown only first, second, third, forth
This will result in the order specified on the last line.
The following file:
key1 = value1 key2 = value2 key3 = value3
results in the following list of dictionaries (a single dictionary):
Dictionary #0: depend = [] key1 = value1 key2 = value2 key3 = value3 name = shortname =
The following file:
key1 = value1 key2 = value2 key3 = value3 variants: - one: - two: - three:
results in the following list:
Dictionary #0: depend = [] key1 = value1 key2 = value2 key3 = value3 name = one shortname = one Dictionary #1: depend = [] key1 = value1 key2 = value2 key3 = value3 name = two shortname = two Dictionary #2: depend = [] key1 = value1 key2 = value2 key3 = value3 name = three shortname = three
The following file:
key1 = value1 key2 = value2 key3 = value3 variants: - one: key1 = Hello World key2 <= some_prefix_ - two: key2 <= another_prefix_ - three:
results in the following list:
Dictionary #0: depend = [] key1 = Hello World key2 = some_prefix_value2 key3 = value3 name = one shortname = one Dictionary #1: depend = [] key1 = value1 key2 = another_prefix_value2 key3 = value3 name = two shortname = two Dictionary #2: depend = [] key1 = value1 key2 = value2 key3 = value3 name = three shortname = three
The following file:
key1 = value1 key2 = value2 key3 = value3 variants: - one: key1 = Hello World key2 <= some_prefix_ - two: one key2 <= another_prefix_ - three: one two
results in the following list:
Dictionary #0: depend = [] key1 = Hello World key2 = some_prefix_value2 key3 = value3 name = one shortname = one Dictionary #1: depend = ['one'] key1 = value1 key2 = another_prefix_value2 key3 = value3 name = two shortname = two Dictionary #2: depend = ['one', 'two'] key1 = value1 key2 = value2 key3 = value3 name = three shortname = three
The following file:
key1 = value1 key2 = value2 key3 = value3 variants: - one: key1 = Hello World key2 <= some_prefix_ - two: one key2 <= another_prefix_ - three: one two variants: - A: - B:
results in the following list:
Dictionary #0: depend = [] key1 = Hello World key2 = some_prefix_value2 key3 = value3 name = A.one shortname = A.one Dictionary #1: depend = ['A.one'] key1 = value1 key2 = another_prefix_value2 key3 = value3 name = A.two shortname = A.two Dictionary #2: depend = ['A.one', 'A.two'] key1 = value1 key2 = value2 key3 = value3 name = A.three shortname = A.three Dictionary #3: depend = [] key1 = Hello World key2 = some_prefix_value2 key3 = value3 name = B.one shortname = B.one Dictionary #4: depend = ['B.one'] key1 = value1 key2 = another_prefix_value2 key3 = value3 name = B.two shortname = B.two Dictionary #5: depend = ['B.one', 'B.two'] key1 = value1 key2 = value2 key3 = value3 name = B.three shortname = B.three
The following file:
key1 = value1 key2 = value2 key3 = value3 variants: - one: key1 = Hello World key2 <= some_prefix_ - two: one key2 <= another_prefix_ - three: one two variants: - A: no one - B: only one,three
results in the following list:
Dictionary #0: depend = ['A.one'] key1 = value1 key2 = another_prefix_value2 key3 = value3 name = A.two shortname = A.two Dictionary #1: depend = ['A.one', 'A.two'] key1 = value1 key2 = value2 key3 = value3 name = A.three shortname = A.three Dictionary #2: depend = [] key1 = Hello World key2 = some_prefix_value2 key3 = value3 name = B.one shortname = B.one Dictionary #3: depend = ['B.one', 'B.two'] key1 = value1 key2 = value2 key3 = value3 name = B.three shortname = B.three
The following file:
key1 = value1 key2 = value2 key3 = value3 variants: - one: key1 = Hello World key2 <= some_prefix_ - two: one key2 <= another_prefix_ - three: one two variants: - @A: no one - B: only one,three
results in the following list (note the difference between the name
and shortname
fields):
Dictionary #0: depend = ['A.one'] key1 = value1 key2 = another_prefix_value2 key3 = value3 name = A.two shortname = two Dictionary #1: depend = ['A.one', 'A.two'] key1 = value1 key2 = value2 key3 = value3 name = A.three shortname = three Dictionary #2: depend = [] key1 = Hello World key2 = some_prefix_value2 key3 = value3 name = B.one shortname = B.one Dictionary #3: depend = ['B.one', 'B.two'] key1 = value1 key2 = value2 key3 = value3 name = B.three shortname = B.three
The following file:
key1 = value1 key2 = value2 key3 = value3 variants: - one: key1 = Hello World key2 <= some_prefix_ - two: one key2 <= another_prefix_ - three: one two variants: - @A: no one - B: only one,three three: key4 = some_value A: no two key5 = yet_another_value
results in the following list:
Dictionary #0: depend = ['A.one', 'A.two'] key1 = value1 key2 = value2 key3 = value3 key4 = some_value key5 = yet_another_value name = A.three shortname = three Dictionary #1: depend = [] key1 = Hello World key2 = some_prefix_value2 key3 = value3 name = B.one shortname = B.one Dictionary #2: depend = ['B.one', 'B.two'] key1 = value1 key2 = value2 key3 = value3 key4 = some_value name = B.three shortname = B.three
You may use the parser to experiment with config files in order to easily define your desired tests. The parser script may be invoked with a command line parameter, which indicates the name of the file to parse. The parser displays the resulting list of dictionaries, as well as some debugging output which may help you understand what's going on. For example, while parsing the last example file, the parser prints the following:
[foo@bar kvm]$ python ../../common_lib/cartesian_config.py -c --verbose sample.cfg DEBUG: checking out 'A' DEBUG: conditional block matches: 'A:' (sample.cfg:21) DEBUG: checking out 'A.one' DEBUG: filter did not pass: 'no one' (sample.cfg:15) DEBUG: checking out 'A.two' DEBUG: filter did not pass: 'no two' (sample.cfg:22) DEBUG: checking out 'A.three' DEBUG: conditional block matches: 'three:' (sample.cfg:19) DEBUG: reached leaf, returning it dict 1: three dep = ['A.one', 'A.two'] key1 = value1 key2 = value2 key3 = value3 key4 = some_value key5 = yet_another_value name = A.three shortname = three DEBUG: checking out 'B' DEBUG: checking out 'B.one' DEBUG: reached leaf, returning it dict 2: B.one dep = [] key1 = Hello World key2 = some_prefix_value2 key3 = value3 name = B.one shortname = B.one DEBUG: checking out 'B.two' DEBUG: filter did not pass: 'only one, three' (sample.cfg:17) DEBUG: checking out 'B.three' DEBUG: conditional block matches: 'three:' (sample.cfg:19) DEBUG: reached leaf, returning it dict 3: B.three dep = ['B.one', 'B.two'] key1 = value1 key2 = value2 key3 = value3 key4 = some_value name = B.three shortname = B.three