Add stage base class, refactor discovery, etc
diff --git a/wally/suits/io/fio.py b/wally/suits/io/fio.py
index 0f4ebde..1b5f38e 100644
--- a/wally/suits/io/fio.py
+++ b/wally/suits/io/fio.py
@@ -83,7 +83,7 @@
             node.conn.rmdir(self.config.remote_dir, recursive=True, ignore_missing=True)
             node.conn.mkdir(self.config.remote_dir)
         except Exception as exc:
-            msg = "Failed to create folder {} on remote {}.".format(self.config.remote_dir, node, exc)
+            msg = "Failed to create folder {} on remote {}.".format(self.config.remote_dir, node)
             logger.exception(msg)
             raise StopTestError(msg) from exc
 
diff --git a/wally/suits/itest.py b/wally/suits/itest.py
index 6d1eeee..ef69b05 100644
--- a/wally/suits/itest.py
+++ b/wally/suits/itest.py
@@ -74,7 +74,7 @@
         pass
 
     @abc.abstractmethod
-    def format_for_console(cls, data: Any) -> str:
+    def format_for_console(self, data: Any) -> str:
         pass
 
 
@@ -122,9 +122,9 @@
                 }
 
                 assert info == expected_config, \
-                    "Test info at path {} is not equal to expected config." + \
-                    "Maybe configuration was changed before test was restarted. " + \
-                    "Current cfg is {!r}, expected cfg is {!r}".format(info_path, info, expected_config)
+                    ("Test info at path {} is not equal to expected config." +
+                     "Maybe configuration was changed before test was restarted. " +
+                     "Current cfg is {!r}, expected cfg is {!r}").format(info_path, info, expected_config)
 
                 logger.info("Test iteration {} found in storage and will be skipped".format(iter_name))
             else:
@@ -181,10 +181,10 @@
                 start_times = []  # type: List[int]
                 stop_times = []  # type: List[int]
 
+                mstorage = storage.sub_storage("result", str(run_id), "measurement")
                 for (result, (t_start, t_stop)), node in zip(results, self.config.nodes):
                     for metrics_name, data in result.items():
-                        path = "result/{}/measurement/{}/{}".format(run_id, node.info.node_id(), metrics_name)
-                        storage[path] = data  # type: ignore
+                        mstorage[node.info.node_id(), metrics_name] = data  # type: ignore
                     start_times.append(t_start)
                     stop_times.append(t_stop)
 
@@ -214,7 +214,7 @@
                     'end_time': max_stop_time
                 }
 
-                storage["result/{}/info".format(run_id)] = test_config  # type: ignore
+                storage["result", str(run_id), "info"] = test_config  # type: ignore
 
     @abc.abstractmethod
     def config_node(self, node: IRPCNode) -> None: