cfg-checker benchmark module part 2

 - fixes for fio-runner error handling
 - fixes for web-server error handling
 - proper handling of 'scheduled_to' option
 - cleanup procedure
 - kube can wait for specific phases of svc, pod, pvc, pv

Change-Id: I9b241597e6314fed1dbc3aba5e8dee1637eea1c7
diff --git a/cfg_checker/agent/fio_runner.py b/cfg_checker/agent/fio_runner.py
index 69ec661..50afeca 100644
--- a/cfg_checker/agent/fio_runner.py
+++ b/cfg_checker/agent/fio_runner.py
@@ -270,7 +270,7 @@
                 self._fio_options_common[k] = v
             else:
                 raise CheckerException(
-                    "Error running fio: '{}'".format(self._shell_output)
+                    "Unknown option: '{}': '{}'".format(k, v)
                 )
         # recalc
         self.recalc_times()
@@ -319,11 +319,12 @@
                     _line = _bb
                 if _start < 0 and _end < 0 and not _line.startswith("{"):
                     _time = get_time()
-                    self.result[_time] = {
+                    self.results[_time] = {
                         "error": _line
                     }
                     self.eta = -1
                     self.fiorun.kill_shell()
+                    self.finished = True
                     return
                 _current = _line.splitlines()
                 _raw += _current
@@ -372,6 +373,7 @@
             _ioengines = self._shell_output
             _ioengines = _ioengines.replace("\t", "")
             _ioengines = _ioengines.splitlines()[1:]
+            self._shell_output = ""
         else:
             _ioengines = []
 
@@ -488,6 +490,11 @@
         # Reset thread if it closed
         self.fio_reset()
         # Fill options
+        if "scheduled_to" in options:
+            # just ignore it
+            _k = "scheduled_to"
+            _v = options.pop(_k)
+            logger.warning("Ignoring option: '{}': '{}'".format(_k, _v))
         self.fio.update_options(options)
         # Start it
         self.fio.start()
@@ -499,7 +506,7 @@
         # Handle scheduled time
         if "scheduled_to" not in options:
             # required parameter not set
-            return False
+            raise CheckerException("Parameter missing: 'scheduled_to'")
         else:
             # set time and get rid of it from options
             _time = options.pop("scheduled_to")
diff --git a/cfg_checker/agent/webserver.py b/cfg_checker/agent/webserver.py
index 67a4567..eacce8f 100644
--- a/cfg_checker/agent/webserver.py
+++ b/cfg_checker/agent/webserver.py
@@ -8,7 +8,9 @@
 from copy import deepcopy # noqa E402
 from platform import system, release, node # noqa E402
 
+from cfg_checker.common.log import logger # noqa E402
 from cfg_checker.common.settings import pkg_dir  # noqa E402
+from cfg_checker.common.exception import CheckerException  # noqa E402
 from cfg_checker.helpers.falcon_jinja2 import FalconTemplate  # noqa E402
 from .fio_runner import FioProcessShellRun, get_time # noqa E402
 
@@ -91,19 +93,28 @@
         resp.text = json.dumps(_status)
 
     def on_post(self, req, resp):
-        def _resp(resp, code, msg):
+        def _resp(resp, code, msg, opt={}):
             resp.status = code
             resp.content_type = "application/json"
-            resp.text = json.dumps({"error": msg})
+            resp.text = json.dumps({"error": msg, "options": opt})
         # Handle actions
-        _m = req.get_media(default_when_empty={})
+        logger.info("Getting media")
+        try:
+            _m = req.get_media(default_when_empty={})
+        except falcon.MediaMalformedError:
+            _msg = "Incorrect input data"
+            logger.error(_msg)
+            _resp(resp, falcon.HTTP_400, _msg)
+            return
         if _m:
+            logger.debug("got media object:\n{}".format(json.dumps(_m)))
             # Validate action structure
             _module = _m.get('module', "")
             _action = _m.get('action', "")
             _options = _m.get('options', {})
 
             if not _module or _module not in list(_modules.keys()):
+                logger.error("invalid module '{}'".format(_module))
                 _resp(
                     resp,
                     falcon.HTTP_400,
@@ -111,6 +122,7 @@
                 )
                 return
             elif not _action or _action not in _modules[_module]['actions']:
+                logger.error("invalid action '{}'".format(_action))
                 _resp(
                     resp,
                     falcon.HTTP_400,
@@ -120,52 +132,68 @@
             else:
                 # Handle command
                 _a = _fio.actions[_action]
-                if _action == 'get_options':
-                    resp.status = falcon.HTTP_200
-                    resp.content_type = "application/json"
-                    resp.text = json.dumps({"options": _a()})
-                elif _action == 'get_resultlist':
-                    resp.status = falcon.HTTP_200
-                    resp.content_type = "application/json"
-                    resp.text = json.dumps({"resultlist": _a()})
-                elif _action == 'get_result':
-                    if 'time' not in _options:
-                        _resp(
-                            resp,
-                            falcon.HTTP_400,
-                            "No 'time' found for '{}'".format(_action)
-                        )
-                        return
-                    _time = _options['time']
-                    resp.status = falcon.HTTP_200
-                    resp.content_type = "application/json"
-                    resp.text = json.dumps({_time: _a(_time)})
-                elif _action == 'do_singlerun':
-                    _a(_options)
-                    resp.status = falcon.HTTP_200
-                    resp.content_type = "application/json"
-                    resp.text = json.dumps({"ok": True})
-                elif _action == 'do_scheduledrun':
-                    # prepare scheduled run
+                try:
+                    if _action == 'get_options':
+                        logger.info("returning options")
+                        resp.status = falcon.HTTP_200
+                        resp.content_type = "application/json"
+                        resp.text = json.dumps({"options": _a()})
+                    elif _action == 'get_resultlist':
+                        logger.info("getting results")
+                        resp.status = falcon.HTTP_200
+                        resp.content_type = "application/json"
+                        resp.text = json.dumps({"resultlist": _a()})
+                    elif _action == 'get_result':
+                        if 'time' not in _options:
+                            _msg = "No 'time' found for '{}'".format(_action)
+                            logger.error(_msg)
+                            _resp(
+                                resp,
+                                falcon.HTTP_400,
+                                _msg
+                            )
+                            return
+                        _time = _options['time']
+                        logger.info("getting results for '{}'".format(_time))
+                        resp.status = falcon.HTTP_200
+                        resp.content_type = "application/json"
+                        resp.text = json.dumps({_time: _a(_time)})
+                    elif _action == 'do_singlerun':
+                        logger.info("executing single run")
+                        _a(_options)
+                        resp.status = falcon.HTTP_200
+                        resp.content_type = "application/json"
+                        resp.text = json.dumps({"ok": True})
+                    elif _action == 'do_scheduledrun':
+                        logger.info("executing scheduled run")
+                        # prepare scheduled run
 
-                    # Run it
-                    _a(_options)
-                    resp.status = falcon.HTTP_200
-                    resp.content_type = "application/json"
-                    resp.text = json.dumps({"ok": True})
-                else:
-                    _resp(
-                        resp,
-                        falcon.HTTP_500,
-                        "Unknown error happened for '{}/{}/{}'".format(
-                            _module,
-                            _action,
-                            _options
-                        )
-                    )
+                        # Run it
+                        _a(_options)
+                        resp.status = falcon.HTTP_200
+                        resp.content_type = "application/json"
+                        resp.text = json.dumps({"ok": True})
+                    else:
+                        _msg = "Unknown error happened for '{}/{}/{}'".format(
+                                _module,
+                                _action,
+                                _options
+                            )
+                        logger.error(_msg)
+                        _resp(resp, falcon.HTTP_500, _msg)
+                except CheckerException as e:
+                    _msg = "Error for '{}/{}':\n{}".format(
+                                _module,
+                                _action,
+                                e
+                            )
+                    logger.error(_msg)
+                    _resp(resp, falcon.HTTP_500, _msg, opt=_options)
                 return
         else:
-            _resp(resp, falcon.HTTP_400, "Empty request body")
+            _msg = "Empty request body"
+            logger.error(_msg)
+            _resp(resp, falcon.HTTP_400, _msg)
 
 
 class Index: