diff --git a/config.toml.example b/config.toml.example index 58d2c5b..39cae77 100644 --- a/config.toml.example +++ b/config.toml.example @@ -1,3 +1,4 @@ +[devices] metadata_dev = '/dev/vda' data_dev = '/dev/vdb' disable_by_id_check = true @@ -8,6 +9,12 @@ disable_by_id_check = true # # cache_dev = '/dev/vdc' +[filter] +# Default tags filter +# +tags = '!experimental' + +[run] # Optional cache policy name to override the default in dm-cache tests. # If specified, all test devices use this policy instead of the default smq policy. # diff --git a/dmtest b/dmtest index b621351..a3d7189 100755 --- a/dmtest +++ b/dmtest @@ -1,4 +1,4 @@ #! /usr/bin/sh export PYTHONPATH=$PYTHONPATH:./src -python3 -m dmtest $@ +python3 -m dmtest "$@" diff --git a/src/dmtest/__main__.py b/src/dmtest/__main__.py index d02fd6d..5ca03a2 100644 --- a/src/dmtest/__main__.py +++ b/src/dmtest/__main__.py @@ -10,7 +10,9 @@ import dmtest.thin_migrate.register as thin_migrate_register import dmtest.vdo.register as vdo_register import dmtest.dependency_tracker as dep +import dmtest.config as config import dmtest.test_filter as filter +import dmtest.tag_expression as tag_expr from dmtest.utils import get_dmesg_log import io import itertools @@ -168,6 +170,10 @@ def cmd_list(tests: test_register.TestRegister, args, results: db.TestResults): for p in paths: print(f"{formatter.tree_line(p)}", end=" ") + if args.show_tags: + tags = tests.get_tags(p) + tag_str = f"[{', '.join(sorted(tags))}]" if tags else "" + print(f"{tag_str.ljust(30)}", end=" ") result = average_results(results.get_test_results(p, result_set, args.run_nr)) if result is None: print("-") @@ -430,6 +436,19 @@ def cmd_health(tests: test_register.TestRegister, args, results): print(f"{t.ljust(40, '.')} {found}") +# ----------------------------------------- +# 'list-tags' command + + +def cmd_list_tags(tests: test_register.TestRegister, args, results: db.TestResults): + tags = tests.count_tags() + if not tags: + print("No tags defined.") + return + for tag, count in sorted(tags.items()): + print(f" {tag.ljust(20)} {count} tests") + + # ----------------------------------------- # Command line parser @@ -460,6 +479,12 @@ def arg_filter(p): help="Select tests that match _all_ filters", action="store_true", ) + p.add_argument( + "--tags", + metavar="EXPRESSION", + type=str, + help="select tests matching the tag expression (e.g. '!experimental')", + ) def build_filter(args): @@ -480,6 +505,22 @@ def build_filter(args): else: top_filter.add_sub_filter(filter.StateFilter(s)) + # CLI --tags overrides config [filter].tags + tag_expression = getattr(args, "tags", None) + if tag_expression is None: + try: + cfg = config.read_config() + tag_expression = cfg.get("tags") + except (FileNotFoundError, ValueError): + pass + + if tag_expression: + matcher = tag_expr.parse_tag_expression(tag_expression) + combined = filter.AndFilter() + combined.add_sub_filter(top_filter) + combined.add_sub_filter(filter.TagFilter(matcher)) + return combined + return top_filter @@ -537,6 +578,12 @@ def command_line_parser(): arg_filter(list_p) arg_result_set(list_p) arg_run_nr(list_p) + list_p.add_argument( + "-T", + help="Show tags alongside test names", + action="store_true", + dest="show_tags", + ) log_p = subparsers.add_parser("log", help="list test logs") log_p.set_defaults(func=cmd_log) @@ -588,6 +635,9 @@ def command_line_parser(): help="only show runs whose result matches the given state", ) + list_tags_p = subparsers.add_parser("list-tags", help="list all tags with test counts") + list_tags_p.set_defaults(func=cmd_list_tags) + health_p = subparsers.add_parser( "health", help="check required tools are installed" ) diff --git a/src/dmtest/bufio/bufio_tests.py b/src/dmtest/bufio/bufio_tests.py index ed662cd..4e04564 100644 --- a/src/dmtest/bufio/bufio_tests.py +++ b/src/dmtest/bufio/bufio_tests.py @@ -292,12 +292,12 @@ def bufio_tester(data_dev, **opts): def t_create(fix): - with bufio_tester(fix.cfg["data_dev"]): + with bufio_tester(fix.cfg("data_dev")): pass def t_empty_program(fix): - with bufio_tester(fix.cfg["data_dev"]) as tester: + with bufio_tester(fix.cfg("data_dev")) as tester: with tester.program(): pass @@ -318,14 +318,14 @@ def t_new_buf(fix): nr_threads = 16 nr_gets = 1024 - with bufio_tester(fix.cfg["data_dev"]) as tester: + with bufio_tester(fix.cfg("data_dev")) as tester: for t in range(nr_threads): with tester.program() as p: do_new_buf(p, t * nr_gets) def t_stamper(fix): - with bufio_tester(fix.cfg["data_dev"]) as tester: + with bufio_tester(fix.cfg("data_dev")) as tester: with tester.program() as p: block = p.alloc_reg() buf = p.alloc_reg() @@ -386,14 +386,14 @@ def t_many_stampers(fix): nr_threads = 16 nr_gets = 1024 - with bufio_tester(fix.cfg["data_dev"]) as tester: + with bufio_tester(fix.cfg("data_dev")) as tester: for t in range(nr_threads): with tester.program() as p: do_stamper(p, t * nr_gets) def t_writeback_nothing(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") nr_blocks = units.meg(512) // units.kilo(4) with bufio_tester(data_dev) as tester: @@ -417,7 +417,7 @@ def t_writeback_nothing(fix): def do_writes_hit_disk(fix, write_method): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") nr_blocks = units.meg(128) // units.kilo(4) pattern_base = random.randint(0, 10240) @@ -477,7 +477,7 @@ def async_write(p): def t_writeback_many(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") nr_blocks = units.gig(8) // units.kilo(4) with bufio_tester(data_dev) as tester: @@ -510,7 +510,7 @@ def t_hotspots(fix): big_region_size = units.gig(1) // units.kilo(4) - with bufio_tester(fix.cfg["data_dev"]) as tester: + with bufio_tester(fix.cfg("data_dev")) as tester: # hotspot programs for b, e in regions: with tester.program() as p: @@ -545,7 +545,7 @@ def t_hotspots2(fix): big_region_size = units.gig(1) // units.kilo(4) - with bufio_tester(fix.cfg["data_dev"]) as tester: + with bufio_tester(fix.cfg("data_dev")) as tester: # hotspot programs for b, e in regions: with tester.program() as p: @@ -594,7 +594,7 @@ def volume_name(index): nr_blocks = volume_size // units.kilo(4) vm = tvm.VM() - vm.add_allocation_volume(fix.cfg["data_dev"]) + vm.add_allocation_volume(fix.cfg("data_dev")) for i in range(nr_caches): vm.add_volume(tvm.LinearVolume(volume_name(i), volume_size)) @@ -616,7 +616,7 @@ def volume_name(index): # Checks that buffers that haven't been used for a while get evicted. def t_evict_old(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") nr_blocks = units.gig(1) // units.kilo(4) data_size = utils.dev_size(data_dev) t = table.Table(targets.BufioTestTarget(data_size, data_dev)) diff --git a/src/dmtest/cache/resize_origin_tests.py b/src/dmtest/cache/resize_origin_tests.py index c3659e9..f5ec6df 100644 --- a/src/dmtest/cache/resize_origin_tests.py +++ b/src/dmtest/cache/resize_origin_tests.py @@ -77,11 +77,10 @@ def check_sized_metadata(cmeta, old_cache_dump, new_origin_size): def t_expand_origin_with_reload(fix): - cfg = fix.cfg - fast_dev = cfg["metadata_dev"] - origin_dev = cfg["data_dev"] - cache_dev = cfg.get("cache_dev", None) - policy_name = cfg.get("cache_policy", "smq") + fast_dev = fix.cfg("metadata_dev") + origin_dev = fix.cfg("data_dev") + cache_dev = fix.cfg("cache_dev") + policy_name = fix.cfg("cache_policy") block_size = units.kilo(32) cache_size = units.meg(128) @@ -120,11 +119,10 @@ def t_expand_origin_with_reload(fix): def t_shrink_origin_with_reload_drops_mappings(fix): - cfg = fix.cfg - fast_dev = cfg["metadata_dev"] - origin_dev = cfg["data_dev"] - cache_dev = cfg.get("cache_dev", None) - policy_name = cfg.get("cache_policy", "smq") + fast_dev = fix.cfg("metadata_dev") + origin_dev = fix.cfg("data_dev") + cache_dev = fix.cfg("cache_dev") + policy_name = fix.cfg("cache_policy") block_size = units.kilo(32) cache_size = units.meg(128) @@ -166,11 +164,10 @@ def t_shrink_origin_with_reload_drops_mappings(fix): # the origin, as we always have to load a new dm-cache table to change the # target length. Here we test both the approaches to ensure test coverage. def t_shrink_origin_with_teardown_drops_mappings(fix): - cfg = fix.cfg - fast_dev = cfg["metadata_dev"] - origin_dev = cfg["data_dev"] - cache_dev = cfg.get("cache_dev", None) - policy_name = cfg.get("cache_policy", "smq") + fast_dev = fix.cfg("metadata_dev") + origin_dev = fix.cfg("data_dev") + cache_dev = fix.cfg("cache_dev") + policy_name = fix.cfg("cache_policy") block_size = units.kilo(32) cache_size = units.meg(128) @@ -210,11 +207,10 @@ def t_shrink_origin_with_teardown_drops_mappings(fix): def t_shrink_origin_with_reload_should_fail_if_blocks_dirty(fix): - cfg = fix.cfg - fast_dev = cfg["metadata_dev"] - origin_dev = cfg["data_dev"] - cache_dev = cfg.get("cache_dev", None) - policy_name = cfg.get("cache_policy", "smq") + fast_dev = fix.cfg("metadata_dev") + origin_dev = fix.cfg("data_dev") + cache_dev = fix.cfg("cache_dev") + policy_name = fix.cfg("cache_policy") block_size = units.kilo(32) cache_size = units.meg(128) @@ -255,11 +251,10 @@ def t_shrink_origin_with_reload_should_fail_if_blocks_dirty(fix): def t_shrink_origin_with_teardown_should_fail_if_blocks_dirty(fix): - cfg = fix.cfg - fast_dev = cfg["metadata_dev"] - origin_dev = cfg["data_dev"] - cache_dev = cfg.get("cache_dev", None) - policy_name = cfg.get("cache_policy", "smq") + fast_dev = fix.cfg("metadata_dev") + origin_dev = fix.cfg("data_dev") + cache_dev = fix.cfg("cache_dev") + policy_name = fix.cfg("cache_policy") block_size = units.kilo(32) cache_size = units.meg(128) diff --git a/src/dmtest/cache/small_config_tests.py b/src/dmtest/cache/small_config_tests.py index ffef1f5..ecd55a4 100644 --- a/src/dmtest/cache/small_config_tests.py +++ b/src/dmtest/cache/small_config_tests.py @@ -6,11 +6,10 @@ #---------------------------------------------------------------- def t_small_config(fix): - cfg = fix.cfg - fast_dev = cfg["metadata_dev"] - origin_dev = cfg["data_dev"] - cache_dev = cfg.get("cache_dev", None) - policy_name = cfg.get("cache_policy", "smq") + fast_dev = fix.cfg("metadata_dev") + origin_dev = fix.cfg("data_dev") + cache_dev = fix.cfg("cache_dev") + policy_name = fix.cfg("cache_policy") stack = ManagedCacheStack( fast_dev, diff --git a/src/dmtest/config.py b/src/dmtest/config.py index 91a26b7..40b24ef 100644 --- a/src/dmtest/config.py +++ b/src/dmtest/config.py @@ -1,14 +1,32 @@ import toml +_CONFIG_KEYS = { + "metadata_dev": ("devices", None), + "data_dev": ("devices", None), + "cache_dev": ("devices", None), + "disable_by_id_check": ("devices", False), + "tags": ("filter", None), + "cache_policy": ("run", "smq"), +} + + +class Config: + def __init__(self, raw): + self._raw = raw + + def get(self, key): + entry = _CONFIG_KEYS.get(key) + if entry is None: + raise KeyError(f"unknown config key: {key}") + section, default = entry + return self._raw.get(section, {}).get(key, default) + + # Linux reordered my nvme drives once and I ran tests across # /boot. This check tries to avoid that. The exception is # virt devices, which don't seem to have an id. -def check_dev(cfg, name): - if cfg.get("disable_by_id_check", False): - return - - value = cfg[name] +def check_dev(value, name): if not ( value.startswith("/dev/vd") or value.startswith("/dev/mapper/") ) and not value.startswith("/dev/disk/by-id/"): @@ -16,12 +34,22 @@ def check_dev(cfg, name): def validate(cfg): - check_dev(cfg, "metadata_dev") - check_dev(cfg, "data_dev") + if "devices" not in cfg._raw: + raise ValueError( + "config.toml must have a [devices] section; " + "see config.toml.example for the expected format" + ) + for key in ("metadata_dev", "data_dev"): + if cfg.get(key) is None: + raise ValueError(f"config.toml: missing required key '{key}' in [devices]") + if not cfg.get("disable_by_id_check"): + check_dev(cfg.get("metadata_dev"), "metadata_dev") + check_dev(cfg.get("data_dev"), "data_dev") def read_config(path="config.toml"): with open(path, "r") as f: - config = toml.load(f) - validate(config) - return config + raw = toml.load(f) + cfg = Config(raw) + validate(cfg) + return cfg diff --git a/src/dmtest/fixture.py b/src/dmtest/fixture.py index 7be469f..b57661e 100644 --- a/src/dmtest/fixture.py +++ b/src/dmtest/fixture.py @@ -3,12 +3,8 @@ class Fixture: - def __str__(self): - return str(self._cfg) - def __init__(self): self._cfg = config.read_config() - @property - def cfg(self): - return self._cfg + def cfg(self, key): + return self._cfg.get(key) diff --git a/src/dmtest/tag_expression.py b/src/dmtest/tag_expression.py new file mode 100644 index 0000000..bed056c --- /dev/null +++ b/src/dmtest/tag_expression.py @@ -0,0 +1,153 @@ +class OrExpr: + def __init__(self, left, right): + self.left = left + self.right = right + + def match(self, tags): + return self.left.match(tags) or self.right.match(tags) + +class AndExpr: + def __init__(self, left, right): + self.left = left + self.right = right + + def match(self, tags): + return self.left.match(tags) and self.right.match(tags) + +class NotExpr: + def __init__(self, operand): + self.operand = operand + + def match(self, tags): + return not self.operand.match(tags) + +class TagRef: + def __init__(self, name): + self.name = name + + def match(self, tags): + return self.name in tags + +class ParseError(Exception): + pass + + +def _tokenize(expr): + tokens = [] + i = 0 + while i < len(expr): + if expr[i].isspace(): + i += 1 + continue + + if expr[i] in "()!&|": + tokens.append(expr[i]) + i += 1 + continue + + j = i + while j < len(expr) and (expr[j].isalnum() or expr[j] == '_'): + j += 1 + + if j == i: + raise ParseError(f"unexpected character '{expr[i]}' at position {i}") + if expr[i].isdigit(): + raise ParseError(f"identifier must not start with a digit: '{expr[i:j]}'") + + if expr[i:j] == "and": + tokens.append('&') + elif expr[i:j] == "or": + tokens.append('|') + elif expr[i:j] == "not": + tokens.append('!') + else: + tokens.append(expr[i:j]) + + i = j + return tokens + + +class _Parser: + """Recursive-descent parser for boolean tag expressions. + + expr := or_expr + or_expr := and_expr ('|' and_expr)* + and_expr := not_expr ('&' not_expr)* + not_expr := '!' not_expr | operand + operand := '(' expr ')' | IDENTIFIER + """ + + def __init__(self, tokens): + self.tokens = tokens + self.pos = 0 + + def peek(self): + if self.pos < len(self.tokens): + return self.tokens[self.pos] + return None + + def next(self): + token = self.peek() + self.pos += 1 + return token + + def parse_or(self): + left = self.parse_and() + while self.peek() == '|': + self.next() + right = self.parse_and() + left = OrExpr(left, right) + return left + + def parse_and(self): + left = self.parse_not() + while self.peek() == '&': + self.next() + right = self.parse_not() + left = AndExpr(left, right) + return left + + def parse_not(self): + if self.peek() == '!': + self.next() + operand = self.parse_not() + return NotExpr(operand) + return self.parse_operand() + + def parse_operand(self): + token = self.next() + if token is None: + raise ParseError("unexpected end of expression, expected an identifier or '('") + if token in ('&', '|', ')'): + raise ParseError(f"unexpected '{token}', expected an identifier or '('") + if token == '(': + inner = self.parse_or() + if self.next() != ')': + raise ParseError("missing closing ')'") + return inner + return TagRef(token) + + def parse(self): + result = self.parse_or() + if self.peek() is not None: + raise ParseError(f"unexpected trailing '{self.peek()}'") + return result + + +def parse_tag_expression(expr_str): + """Parse a boolean tag expression and return a matcher object. + + The matcher takes a frozenset of tags and returns True if the + tags satisfy the expression. + + Examples: + "smoke" -> has tag 'smoke' + "!benchmark" -> does not have tag 'benchmark' + "smoke & !experimental" -> has 'smoke', not 'experimental' + "validation | functional" -> has either + "!(benchmark | experimental)" -> has neither + """ + tokens = _tokenize(expr_str) + if not tokens: + raise ParseError("empty tag expression") + return _Parser(tokens).parse() diff --git a/src/dmtest/test_filter.py b/src/dmtest/test_filter.py index 0e6faf8..3d51964 100644 --- a/src/dmtest/test_filter.py +++ b/src/dmtest/test_filter.py @@ -4,7 +4,7 @@ class TestFilter(ABC): @abstractmethod - def matches(self, test_name, res_list): + def matches(self, path, test, res_list): pass @@ -12,23 +12,23 @@ class SubstringFilter(TestFilter): def __init__(self, substring): self.substring = substring - def matches(self, test_name, res_list): - return self.substring in test_name + def matches(self, path, test, res_list): + return self.substring in path class RegexFilter(TestFilter): def __init__(self, pattern): self.pattern = re.compile(pattern) - def matches(self, test_name, res_list): - return bool(self.pattern.search(test_name)) + def matches(self, path, test, res_list): + return bool(self.pattern.search(path)) class StateFilter(TestFilter): def __init__(self, state): self.state = state.lower() - def matches(self, test_name, res_list): + def matches(self, path, test, res_list): if res_list == []: return self.state == "-" for result in res_list: @@ -41,8 +41,8 @@ class NotFilter(TestFilter): def __init__(self, sub_filter): self.sub_filter = sub_filter - def matches(self, test_name, res_list): - return not self.sub_filter.matches(test_name, res_list) + def matches(self, path, test, res_list): + return not self.sub_filter.matches(path, test, res_list) class CompositeFilter(TestFilter): @@ -54,17 +54,25 @@ def add_sub_filter(self, sub_filter): class AndFilter(CompositeFilter): - def matches(self, test_name, res_list): + def matches(self, path, test, res_list): return all( - sub_filter.matches(test_name, res_list) for sub_filter in self.sub_filters + sub_filter.matches(path, test, res_list) for sub_filter in self.sub_filters ) class OrFilter(CompositeFilter): - def matches(self, test_name, res_list): + def matches(self, path, test, res_list): if len(self.sub_filters) == 0: return True else: return any( - sub_filter.matches(test_name, res_list) for sub_filter in self.sub_filters + sub_filter.matches(path, test, res_list) for sub_filter in self.sub_filters ) + + +class TagFilter(TestFilter): + def __init__(self, matcher): + self._matcher = matcher + + def matches(self, path, test, res_list): + return self._matcher.match(test.tags) diff --git a/src/dmtest/test_register.py b/src/dmtest/test_register.py index 8f70521..d3caac7 100644 --- a/src/dmtest/test_register.py +++ b/src/dmtest/test_register.py @@ -6,6 +6,7 @@ import dmtest.process as process import dmtest.dependency_tracker as dep +from collections import Counter from typing import NamedTuple, Callable, Optional @@ -32,37 +33,67 @@ class MissingTestDep(Exception): class Test(NamedTuple): dep_fn: Callable[[], None] test_fn: Callable[[fixture.Fixture], None] + tags: frozenset = frozenset() + + +def _parse_test_entry(entry): + if len(entry) == 2: + path, callback = entry + return path, callback, None, None + + if len(entry) == 3: + path, callback, third = entry + if callable(third): + return path, callback, third, None + if isinstance(third, (list, set, frozenset)): + return path, callback, None, third + raise TypeError( + f"test '{path}': 3rd element must be callable (dep_fn) or list (tags)" + ) + + if len(entry) == 4: + path, callback, dep_fn, tags = entry + if dep_fn is not None and not callable(dep_fn): + raise TypeError(f"test '{path}': dep_fn must be callable or None") + if tags is not None and not isinstance(tags, (list, set, frozenset)): + raise TypeError(f"test '{path}': tags must be a list") + return path, callback, dep_fn, tags + + raise ValueError(f"test entry must have 2-4 elements, got {len(entry)}") class TestRegister: def __init__(self): self._tests = {} - def register(self, path, callback, dep_fn=None): + def register(self, path, callback, dep_fn=None, tags=None): path = _normalise_path(path) - self._tests[path] = Test(dep_fn, callback) + t = frozenset(tags) if tags else frozenset() + self._tests[path] = Test(dep_fn, callback, t) - def register_batch(self, prefix, tests, batch_dep_fn=None): + def register_batch(self, prefix, tests, batch_dep_fn=None, batch_tags=None): # ensure a trailing slash prefix = str(prefix) if not prefix.endswith("/"): prefix += "/" - for test in tests: - if len(test) == 2: - path, callback = test - dep_fn = batch_dep_fn - else: - path, callback, dep_fn = test - self.register(prefix + path.lstrip("/"), callback, dep_fn) + if batch_tags is not None and not isinstance(batch_tags, (list, set, frozenset)): + raise TypeError(f"batch_tags must be a list") + + for entry in tests: + path, callback, dep_fn, tags = _parse_test_entry(entry) + dep_fn = dep_fn or batch_dep_fn + merged_tags = set(tags or []) + merged_tags.update(batch_tags or []) + self.register(prefix + path.lstrip("/"), callback, dep_fn, merged_tags) def paths(self, results, result_set, filt=None): selected = [] - for t in self._tests.keys(): - res_list = results.get_test_results(t, result_set) - if filt.matches(t, res_list): - selected.append(t) + for (path, t) in self._tests.items(): + res_list = results.get_test_results(path, result_set) + if filt.matches(path, t, res_list): + selected.append(path) return selected @@ -83,6 +114,13 @@ def run(self, path, fix): else: raise ValueError(f"can't find test {path}") + def get_tags(self, path): + t = self._tests.get(path) + return t.tags if t else frozenset() + + def count_tags(self): + return Counter(tag for t in self._tests.values() for tag in t.tags) + targets_to_kmodules = { "thin-pool": "dm_thin_pool", diff --git a/src/dmtest/thin/creation_tests.py b/src/dmtest/thin/creation_tests.py index f6c127b..cd459be 100644 --- a/src/dmtest/thin/creation_tests.py +++ b/src/dmtest/thin/creation_tests.py @@ -102,7 +102,7 @@ def t_largest_thin_id_succeeds(fix): def t_too_small_a_metadata_dev_fails(fix): vm = tvm.VM() - vm.add_allocation_volume(fix.cfg["data_dev"]) + vm.add_allocation_volume(fix.cfg("data_dev")) vm.add_volume(tvm.LinearVolume("metadata", units.kilo(16))) vm.add_volume(tvm.LinearVolume("data", units.gig(8))) diff --git a/src/dmtest/thin/discard_tests.py b/src/dmtest/thin/discard_tests.py index c4451ee..5364bc6 100644 --- a/src/dmtest/thin/discard_tests.py +++ b/src/dmtest/thin/discard_tests.py @@ -53,7 +53,7 @@ def t_blktrace(fix): trace = bt.BlkTrace([thin.path]) with trace: utils.wipe_device(thin) - tree = read_metadata(fix.cfg["metadata_dev"]) + tree = read_metadata(fix.cfg("metadata_dev")) print(f"{tree.attrib}") diff --git a/src/dmtest/thin/external_origin_tests.py b/src/dmtest/thin/external_origin_tests.py index 05756d5..026242e 100644 --- a/src/dmtest/thin/external_origin_tests.py +++ b/src/dmtest/thin/external_origin_tests.py @@ -74,9 +74,10 @@ def do_pattern_stamp_test(fix, opts=None): opts["data_size"] = opts.get("data_size", units.gig(4)) opts["origin_size"] = opts.get("origin_size", units.gig(1)) - cfg = fix.cfg + data_dev = fix.cfg("data_dev") + metadata_dev = fix.cfg("metadata_dev") - s = ExternalSnapStack(cfg["data_dev"], cfg["metadata_dev"], **opts) + s = ExternalSnapStack(data_dev, metadata_dev, **opts) with s.activate_origin() as origin: origin_stomper = stomper.PatternStomper( diff --git a/src/dmtest/thin/fs_bench.py b/src/dmtest/thin/fs_bench.py index c754924..93b0f92 100644 --- a/src/dmtest/thin/fs_bench.py +++ b/src/dmtest/thin/fs_bench.py @@ -57,7 +57,7 @@ def t_fio_thick(fix): size = units.gig(90) vm = tvm.VM() - vm.add_allocation_volume(fix.cfg['data_dev']) + vm.add_allocation_volume(fix.cfg("data_dev")) vm.add_volume(tvm.LinearVolume("thick", size)) with dmdev.dev(vm.table("thick")) as thick: diff --git a/src/dmtest/thin/utils.py b/src/dmtest/thin/utils.py index dec59b7..505778a 100644 --- a/src/dmtest/thin/utils.py +++ b/src/dmtest/thin/utils.py @@ -3,10 +3,9 @@ def standard_stack(fix, **opts): - cfg = fix.cfg if "data_size" not in opts: - opts["data_size"] = utils.dev_size(cfg["data_dev"]) - return ps.PoolStack(cfg["data_dev"], cfg["metadata_dev"], **opts) + opts["data_size"] = utils.dev_size(fix.cfg("data_dev")) + return ps.PoolStack(fix.cfg("data_dev"), fix.cfg("metadata_dev"), **opts) def standard_pool(fix, **opts): diff --git a/src/dmtest/thin_migrate/migrate_thin.py b/src/dmtest/thin_migrate/migrate_thin.py index f5c4936..15a99bd 100644 --- a/src/dmtest/thin_migrate/migrate_thin.py +++ b/src/dmtest/thin_migrate/migrate_thin.py @@ -43,7 +43,7 @@ def migrate_to_file(src_thin, dest_thin): def t_migrate_thin_to_thin(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") thin_size = min(units.gig(1), utils.dev_size(data_dev) // 2) with standard_pool(fix, block_size = 128, zero = True) as src_pool: @@ -69,7 +69,7 @@ def t_migrate_thin_to_thin(fix): def t_migrate_thin_to_file(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") thin_size = min(units.gig(1), utils.dev_size(data_dev) // 2) with standard_pool(fix, block_size = 128, zero = True) as src_pool: @@ -92,7 +92,7 @@ def t_migrate_thin_to_file(fix): def t_large_block_size(fix): thin_size = units.gig(1) - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") if utils.dev_size(data_dev) < 2 * thin_size: raise Exception("insufficient pool size for running the test") diff --git a/src/dmtest/thin_migrate/unit.py b/src/dmtest/thin_migrate/unit.py index 5e5e9c1..7cca075 100644 --- a/src/dmtest/thin_migrate/unit.py +++ b/src/dmtest/thin_migrate/unit.py @@ -9,7 +9,7 @@ #--------------------------------- def t_insufficient_buffer_size(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") thin_size = min(units.gig(1), utils.dev_size(data_dev) // 2) with standard_pool(fix, block_size = 4096, zero = True) as src_pool: @@ -31,7 +31,7 @@ def t_insufficient_buffer_size(fix): def t_input_none_thin_device(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") try: process.run(f"thin_migrate --source-dev {data_dev} --dest-file migrate_dest") except subprocess.CalledProcessError: @@ -43,7 +43,7 @@ def t_input_none_thin_device(fix): def t_device_not_present_in_metadata_snap(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") thin_size = min(units.gig(1), utils.dev_size(data_dev) // 2) with standard_pool(fix, block_size = 128, zero = True) as src_pool: @@ -64,7 +64,7 @@ def t_device_not_present_in_metadata_snap(fix): def t_output_none_block_device(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") thin_size = min(units.gig(1), utils.dev_size(data_dev) // 2) with standard_pool(fix, block_size = 128, zero = True) as src_pool: @@ -86,7 +86,7 @@ def t_output_none_block_device(fix): def t_output_unsupported_file_type(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") thin_size = min(units.gig(1), utils.dev_size(data_dev) // 2) with standard_pool(fix, block_size = 128, zero = True) as src_pool: @@ -106,7 +106,7 @@ def t_output_unsupported_file_type(fix): def t_output_device_size_differs(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") thin_size = min(units.gig(1), utils.dev_size(data_dev) // 2) with standard_pool(fix, block_size = 128, zero = True) as src_pool: @@ -127,7 +127,7 @@ def t_output_device_size_differs(fix): def t_output_device_size_differs_in_file_mode(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") thin_size = min(units.gig(1), utils.dev_size(data_dev) // 2) with standard_pool(fix, block_size = 128, zero = True) as src_pool: diff --git a/src/dmtest/vdo/full_tests.py b/src/dmtest/vdo/full_tests.py index f839799..a6057dd 100644 --- a/src/dmtest/vdo/full_tests.py +++ b/src/dmtest/vdo/full_tests.py @@ -15,7 +15,7 @@ def get_free_space(stats): return stats["physicalBlocks"] - stats["overheadBlocksUsed"] - stats["dataBlocksUsed"] def t_full(fix): - data_dev = fix.cfg["data_dev"] + data_dev = fix.cfg("data_dev") # Configure a small device so we can fill it quickly. slab_bits = 13 size_gb = 3 diff --git a/src/dmtest/vdo/load_failure_tests.py b/src/dmtest/vdo/load_failure_tests.py index 2af100d..c663390 100644 --- a/src/dmtest/vdo/load_failure_tests.py +++ b/src/dmtest/vdo/load_failure_tests.py @@ -53,7 +53,7 @@ def t_corrupt_geometry(fix): pass start_time = time.time() # Overwrite just one (4kB) block with random data - trash_device(fix.cfg["data_dev"], 8) + trash_device(fix.cfg("data_dev"), 8) stack = standard_stack(fix, format = False) started = False try: diff --git a/src/dmtest/vdo/utils.py b/src/dmtest/vdo/utils.py index bb87f6a..09608e7 100644 --- a/src/dmtest/vdo/utils.py +++ b/src/dmtest/vdo/utils.py @@ -40,8 +40,7 @@ """ def standard_stack(fix, **opts): - cfg = fix.cfg - return vs.VDOStack(cfg["data_dev"], **opts) + return vs.VDOStack(fix.cfg("data_dev"), **opts) def standard_vdo(fix, **opts): stack = standard_stack(fix, **opts)