fix reports
diff --git a/report.html b/report.html
deleted file mode 100644
index 5e78689..0000000
--- a/report.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-    <title>Report</title>
-    <link rel="stylesheet"
-          href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
-</head>
-
-<body>
-<div class="page-header text-center">
-  <h2>Performance Report</h2>
-</div>
-<div class="container">
-    <div class="row">
-        <table style="width: auto;" class="table table-bordered table-striped">
-            <tr>
-                <td>Test</td>
-                <td>Iops</td>
-                <td>Latency</td>
-            </tr>
-            <tr>
-                <td>rand read 4k</td>
-                <td>{data[rand_read_4k][iops][0]}&#32;&plusmn;&#32;{data[rand_read_4k][iops][1]}</td>
-                <td>{data[rand_read_4k][lat]}</td>
-            </tr>
-            <tr>
-                <td>rand write 4k</td>
-                <td>{data[rand_write_4k][iops][0]}&#32;&plusmn;&#32;{data[rand_write_4k][iops][1]}</td>
-                <td>{data[rand_write_4k][lat]}</td>
-            </tr>
-            <tr></tr>
-        </table>
-    </div>
-    <div class="row">
-        <table>
-            <tr>
-                <td><img src={urls[0]}></td>
-                <td><img src={urls[1]}></td>
-            </tr>
-        </table>
-    </div>
-
-    <div class="row">
-        <table style="width: auto;" class="table table-bordered table-striped">
-            <tr>
-                <td>Disk total</td>
-                <td>{lab_info[total_disk]}</td>
-            </tr>
-            <tr>
-                <td>Memory total</td>
-                <td>{lab_info[total_memory]}</td>
-            </tr>
-            <tr>
-                <td>Nodes count</td>
-                <td>{lab_info[nodes_count]}</td>
-            </tr>
-            <tr>
-                <td>CPU count</td>
-                <td>{lab_info[processor_count]}</td>
-            </tr>
-        </table>
-    </div>
-</div>
-</body>
-
-</html>
\ No newline at end of file
diff --git a/report_templates/report.html b/report_templates/report.html
new file mode 100644
index 0000000..80cc1e2
--- /dev/null
+++ b/report_templates/report.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Report</title>
+    <link rel="stylesheet"
+          href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
+</head>
+
+<body>
+<div class="page-header text-center">
+  <h2>Performance Report</h2>
+</div>
+<div class="container">
+    <div class="row">
+        <table style="width: auto;" class="table table-bordered table-striped">
+            <tr>
+                <td>Test</td>
+                <td>Iops/BW</td>
+                <td>Test</td>
+                <td>Iops/BW</td>
+            </tr>
+            <tr>
+                <td>Rand read 4k direct max IOPS</td>
+                <td><div align="right">{direct_iops_r_max} IOPS</div></td>
+                <td>Rand write 4k sync IOPS 10ms lat</td>
+                <td><div align="right">{rws4k_10ms} IOPS</div></td>
+            </tr>
+            <tr>
+                <td>Rand write 4k direct max IOPS</td>
+                <td><div align="right">{direct_iops_w_max} IOPS</div></td>
+                <td>Rand write 4k sync IOPS 30ms lat</td>
+                <td><div align="right">{rws4k_30ms} IOPS</div></td>
+            </tr>
+            <tr>
+                <td>Direct sequantials read</td>
+                <td><div align="right">{bw_read_max} MiBps</div></td>
+                <td>Rand write 4k sync IOPS 100ms lat</td>
+                <td><div align="right">{rws4k_100ms} IOPS</div></td>
+            </tr>
+            <tr>
+                <td>Direct sequantials write</td>
+                <td><div align="right">{bw_write_max} MiBps</div></td>
+                <td></td><td></td>
+            </tr>
+        </table>
+    </div>
+    <div class="row">
+        <table>
+            <tr>
+                <td><img src="charts/rand_read_4k.png"></td>
+                <td width="10%"><H1>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</H1></td>
+                <td><img src="charts/rand_write_4k.png"></td>
+            </tr>
+        </table>
+    </div>
+
+    <!--div class="row">
+        <table style="width: auto;" class="table table-bordered table-striped">
+            <tr>
+                <td>Disk total</td>
+                <td>{lab_info[total_disk]}</td>
+            </tr>
+            <tr>
+                <td>Memory total</td>
+                <td>{lab_info[total_memory]}</td>
+            </tr>
+            <tr>
+                <td>Nodes count</td>
+                <td>{lab_info[nodes_count]}</td>
+            </tr>
+            <tr>
+                <td>CPU count</td>
+                <td>{lab_info[processor_count]}</td>
+            </tr>
+        </table>
+    </div-->
+</div>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/wally/config.py b/wally/config.py
index 7dba134..9626332 100644
--- a/wally/config.py
+++ b/wally/config.py
@@ -37,11 +37,11 @@
         else:
             run_uuid = str(uuid.uuid4())
             results_dir = os.path.join(var_dir, run_uuid)
+        cfg_dict['run_uuid'] = run_uuid.replace('_', '-')
     else:
         results_dir = explicit_folder
 
     cfg_dict['var_dir'] = results_dir
-    cfg_dict['run_uuid'] = run_uuid.replace('_', '-')
     mkdirs_if_unxists(cfg_dict['var_dir'])
 
     in_var_dir = functools.partial(os.path.join, cfg_dict['var_dir'])
diff --git a/wally/report.py b/wally/report.py
index d191428..83cb457 100644
--- a/wally/report.py
+++ b/wally/report.py
@@ -1,17 +1,22 @@
 import os
 import sys
+import bisect
 
+import wally
 from wally import charts
-from wally.statistic import med_dev, round_3_digit, round_deviation
 from wally.utils import parse_creds
-from wally.suits.io.results_loader import filter_data
 from wally.meta_info import total_lab_info, collect_lab_data
 
+from wally.suits.io.results_loader import process_disk_info
 
-def render_html(charts_urls, dest, lab_description, info):
-    templ = open("report.html", 'r').read()
-    open(dest, 'w').write(templ.format(urls=charts_urls,
-                                       data=info, lab_info=lab_description))
+
+def render_html(dest, info, lab_description):
+    very_root_dir = os.path.dirname(os.path.dirname(wally.__file__))
+    templ_dir = os.path.join(very_root_dir, 'report_templates')
+    templ_file = os.path.join(templ_dir, "report.html")
+    templ = open(templ_file, 'r').read()
+    report = templ.format(lab_info=lab_description, **info.__dict__)
+    open(dest, 'w').write(report)
 
 
 def io_chart(title, concurence, latv, iops_or_bw, iops_or_bw_dev,
@@ -41,6 +46,107 @@
     return str(ch)
 
 
+def make_plots(processed_results, path):
+    name_filters = [
+        ('hdd_test_rrd4k', ('concurence', 'lat', 'iops'),
+         'rand_read_4k', 'Random read 4k sync'),
+        ('hdd_test_rws4k', ('concurence', 'lat', 'iops'),
+         'rand_write_4k', 'Random write 4k sync')
+    ]
+
+    for name_pref, _, fname, desc in name_filters:
+        chart_data = []
+        for res in processed_results.values():
+            if res.name.startswith(name_pref):
+                chart_data.append(res)
+
+        chart_data.sort(key=lambda x: x.raw['concurence'])
+
+        lat = [x.lat for x in chart_data]
+        concurence = [x.raw['concurence'] for x in chart_data]
+        bw = [x.bw for x in chart_data]
+        bw_dev = [x.dev * x.bw for x in chart_data]
+
+        io_chart(desc, concurence, lat, bw, bw_dev, 'bw', fname)
+
+
+class DiskInfo(object):
+    def __init__(self):
+        self.direct_iops_r_max = 0
+        self.direct_iops_w_max = 0
+        self.rws4k_10ms = 0
+        self.rws4k_30ms = 0
+        self.rws4k_100ms = 0
+        self.bw_write_max = 0
+        self.bw_read_max = 0
+
+
+def get_disk_info(processed_results):
+    di = DiskInfo()
+    rws4k_iops_lat_th = []
+
+    for res in processed_results.values():
+        if res.raw['sync_mode'] == 'd' and res.raw['blocksize'] == '4k':
+            if res.raw['rw'] == 'randwrite':
+                di.direct_iops_w_max = max(di.direct_iops_w_max, res.iops)
+            elif res.raw['rw'] == 'randread':
+                di.direct_iops_r_max = max(di.direct_iops_r_max, res.iops)
+        elif res.raw['sync_mode'] == 's' and res.raw['blocksize'] == '4k':
+            if res.raw['rw'] != 'randwrite':
+                continue
+
+            rws4k_iops_lat_th.append((res.iops, res.lat,
+                                      res.raw['concurence']))
+
+        elif res.raw['sync_mode'] == 'd' and res.raw['blocksize'] == '1m':
+
+            if res.raw['rw'] == 'write':
+                di.bw_write_max = max(di.bw_write_max, res.bw)
+            elif res.raw['rw'] == 'read':
+                di.bw_read_max = max(di.bw_read_max, res.bw)
+
+    di.bw_write_max /= 1000
+    di.bw_read_max /= 1000
+
+    rws4k_iops_lat_th.sort(key=lambda (_1, _2, conc): conc)
+
+    latv = [lat for _, lat, _ in rws4k_iops_lat_th]
+
+    for tlatv_ms in [10, 30, 100]:
+        tlat = tlatv_ms * 1000
+        pos = bisect.bisect_left(latv, tlat)
+        if 0 == pos:
+            iops3 = 0
+        elif pos == len(latv):
+            iops3 = latv[-1]
+        else:
+            lat1 = latv[pos - 1]
+            lat2 = latv[pos]
+
+            th1 = rws4k_iops_lat_th[pos - 1][2]
+            th2 = rws4k_iops_lat_th[pos][2]
+
+            iops1 = rws4k_iops_lat_th[pos - 1][0]
+            iops2 = rws4k_iops_lat_th[pos][0]
+
+            th_lat_coef = (th2 - th1) / (lat2 - lat1)
+            th3 = th_lat_coef * (tlat - lat1) + th1
+
+            th_iops_coef = (iops2 - iops1) / (th2 - th1)
+            iops3 = th_iops_coef * (th3 - th1) + iops1
+        setattr(di, 'rws4k_{}ms'.format(tlatv_ms), int(iops3))
+
+    hdi = DiskInfo()
+    hdi.direct_iops_r_max = di.direct_iops_r_max
+    hdi.direct_iops_w_max = di.direct_iops_w_max
+    hdi.rws4k_10ms = di.rws4k_10ms if 0 != di.rws4k_10ms else '-'
+    hdi.rws4k_30ms = di.rws4k_30ms if 0 != di.rws4k_30ms else '-'
+    hdi.rws4k_100ms = di.rws4k_100ms if 0 != di.rws4k_100ms else '-'
+    hdi.bw_write_max = di.bw_write_max
+    hdi.bw_read_max = di.bw_read_max
+    return hdi
+
+
 def make_io_report(results, path, lab_url=None, creds=None):
     if lab_url is not None:
         username, password, tenant_name = parse_creds(creds)
@@ -50,51 +156,17 @@
         data = collect_lab_data(lab_url, creds)
         lab_info = total_lab_info(data)
     else:
-        lab_info = ""
+        lab_info = {
+            "total_disk": "None",
+            "total_memory": "None",
+            "nodes_count": "None",
+            "processor_count": "None"
+        }
 
-    for suite_type, test_suite_data in results:
-        if suite_type != 'io' or test_suite_data is None:
-            continue
-
-        io_test_suite_res = test_suite_data['res']
-
-        charts_url = []
-        max_info = {}
-
-        name_filters = [
-            ('hdd_test_rrd4k', ('concurence', 'lat', 'iops'),
-             'rand_read_4k', 'random read 4k'),
-            # ('hdd_test_swd1m', ('concurence', 'lat', 'bw'), 'seq_write_1m'),
-            # ('hdd_test_srd1m', ('concurence', 'lat', 'bw'), 'seq_read_1m'),
-            ('hdd_test_rws4k', ('concurence', 'lat', 'iops'),
-             'rand_write_4k', 'random write 4k')
-        ]
-
-        for name_filter, fields, fname, desc in name_filters:
-            th_filter = filter_data(name_filter, fields)
-
-            data = sorted(th_filter(io_test_suite_res.values()))
-            if len(data) == 0:
-                continue
-
-            concurence, latv, iops_or_bw_v = zip(*data)
-            iops_or_bw_v, iops_or_bw_dev_v = zip(*map(med_dev, iops_or_bw_v))
-            latv, _ = zip(*map(med_dev, latv))
-
-            url = io_chart(desc, concurence, latv, iops_or_bw_v,
-                           iops_or_bw_dev_v,
-                           fields[2], fname)
-            max_lat = "%s msec" % round_3_digit(max(latv) / 1000)
-            max_iops_or_bw = max(iops_or_bw_v)
-            max_iops_or_bw_dev = iops_or_bw_dev_v[
-                iops_or_bw_v.index(max_iops_or_bw)]
-            r = round_deviation((max_iops_or_bw, max_iops_or_bw_dev))
-            max_info[fname] = {fields[2]: r,
-                                 "lat": max_lat}
-            charts_url.append(url)
-
-        if len(charts_url) != 0:
-            render_html(charts_url, path, lab_info, max_info)
+    processed_results = process_disk_info(results)
+    make_plots(processed_results, path)
+    di = get_disk_info(processed_results)
+    render_html(path, di, lab_info)
 
 
 def main(args):
diff --git a/wally/suits/io/results_loader.py b/wally/suits/io/results_loader.py
index 25721eb..9005450 100644
--- a/wally/suits/io/results_loader.py
+++ b/wally/suits/io/results_loader.py
@@ -1,10 +1,32 @@
 import re
 import json
+import collections
 
 
 from wally.utils import ssize_to_b
 from wally.statistic import med_dev
 
+PerfInfo = collections.namedtuple('PerfInfo',
+                                  ('name',
+                                   'bw', 'iops', 'dev',
+                                   'lat', 'lat_dev', 'raw'))
+
+
+def process_disk_info(test_output):
+    data = {}
+
+    for tp, pre_result in test_output:
+        if tp != 'io':
+            pass
+
+        for name, results in pre_result['res'].items():
+            bw, bw_dev = med_dev(results['bw'])
+            iops, iops_dev = med_dev(results['iops'])
+            lat, lat_dev = med_dev(results['lat'])
+            dev = bw_dev / float(bw)
+            data[name] = PerfInfo(name, bw, iops, dev, lat, lat_dev, results)
+    return data
+
 
 def parse_output(out_err):
     start_patt = r"(?ims)=+\s+RESULTS\(format=json\)\s+=+"
@@ -37,6 +59,21 @@
     return closure
 
 
+def filter_processed_data(name_prefix, fields_to_select, **filters):
+    def closure(data):
+        for name, result in data.items():
+            if name_prefix is not None:
+                if not name.startswith(name_prefix):
+                    continue
+
+            for k, v in filters.items():
+                if result.raw.get(k) != v:
+                    break
+            else:
+                yield map(result.raw.get, fields_to_select)
+    return closure
+
+
 def load_data(raw_data):
     data = list(parse_output(raw_data))[0]