add mixed load test, other fixes
diff --git a/TODO b/TODO
index 042c477..56a20a3 100644
--- a/TODO
+++ b/TODO
@@ -1,51 +1,37 @@
-посмотреть настройки qemu
+2.0:
+ * Перейти на анализ логов fio
+ * Занести интервал усреднения в конфиг
+ * Делать один больщой тест на несколько минут и мерять по нему все параметры
+ * починить SW & HW info, добавить настройки qemu и все такое
+ * Все тесты - в один поток
+ * Перед началом теста проверять наличие его результатов и скипать
+ * fio --client????
+ * собрать новый fio под основные платформы и положить в git
+ * продолжение работы при большинстве ошибок
+ * Починить процессор
+ * Починить боттлнеки
+ * Юнит-тесты
+ * Сравнения билдов - пока по папкам из CLI, тектовое
+ * Make python module
+ * putget/ssbench tests
+ * rbd с нод без виртуалок
+ * отдельный тенант на все и очистка полная
+ * Per-vm stats & between vm dev
+ * Логи визуальные
+ * psql, mssql, SPC-1
+ * Тестирование кешей
-рестарт fio при ошибке
-дамп промежуточных данных и воосстановление
-печатать fio параметры
+Мелочи:
+ * печатать fio параметры в лог
+ * Зарефакторить запуск/мониторинг/оставнов процесса по SSH, запуск в фоне с чеком - в отдельную ф-цию
+ * prefill запускать в фоне и чекать периодически
+ * починить все подвисания во всех потоках - дампить стеки при подвисании и таймаут
+ * При убивании - грохать все удаленные процессы. Хранить машины и пиды в конфиге и в файле
+ * fadvise_hint=0
+ * Изменить с репорте сенсоров все на %
+ * посмотреть что с сетевыми картами
+ * Intellectual granular sensors
-Зарефакторить запуск/мониторинг/оставнов процесса по SSH
-fio --client
-собрать новый fio под основные платформы и положить в git
-
-запуск в фоне с чеком - в отдельную ф-цию
-prefill запускать в фоне и чекать периодически
-починить все подвисания во всех потоках - дампить стеки при подвисании
-
-
-
-v2 - Однопоточная версия, обработка и продолжение работы при большинстве ошибок
-
-
-
-Finding bottlenecks (алена) - починить процессор
-fadvise_hint=0
-Изменить с репорте сенсоров все на %
-посмотреть что с сетевыми картами
-Resource consumption:
- добавить процессор,
- добавить время в IO,
- генерировать репорты по всему
-
-Lab software report
-
-
-Интеграционные/функциональные тесты *
-Инфо по лабе * - заковырять в certification (Глеб)
-Сравнения билдов (пока по папкам из CLI)
-Make python module
-putget/ssbench tests (костя)
-тестирование (костя)
-отдельный тенант на все
-Per-vm stats & between vm dev
-+ собрать новый fio статиком
-
-
-Intellectual granular sensors
-
-
-Отчеты
-Добавить к отчету экстраполированные скорости
Стат-обработка:
расчет async
расчет количества измерений
diff --git a/scripts/hdd.fio b/scripts/hdd.fio
index c682380..33b05c0 100644
--- a/scripts/hdd.fio
+++ b/scripts/hdd.fio
@@ -5,13 +5,16 @@
buffered=0
iodepth=1
softrandommap=1
-filename=/var/lib/nova/instances/b9fa1a9f-7d43-4cc6-b6cc-133c2a84ab41/xxx.bin
+filename=/media/data/xxx.bin
+# filename=/tmp/xxx.bin
randrepeat=0
size=10G
ramp_time=5
-runtime=10
+runtime=15
blocksize=4k
rw=randwrite
sync=1
-numjobs=1
+direct=1
+thread=1
+numjobs=50
diff --git a/wally/pretty_yaml.py b/wally/pretty_yaml.py
index 699af7e..2cb5607 100644
--- a/wally/pretty_yaml.py
+++ b/wally/pretty_yaml.py
@@ -9,8 +9,13 @@
if isinstance(val, unicode):
val = val.encode('utf8')
- if len(bad_symbols & set(val)) != 0:
- return repr(val)
+ try:
+ float(val)
+ val = repr(val)
+ except ValueError:
+ if len(bad_symbols & set(val)) != 0:
+ val = repr(val)
+
return val
elif val is True:
return 'true'
diff --git a/wally/report.py b/wally/report.py
index 2aa5338..e893c25 100644
--- a/wally/report.py
+++ b/wally/report.py
@@ -128,14 +128,13 @@
num_res += len(result.raw_result['jobs'])
for job_info in result.raw_result['jobs']:
for k, v in job_info['latency_ms'].items():
- if isinstance(k, str):
- assert k[:2] == '>='
+ if isinstance(k, basestring) and k.startswith('>='):
lat_mks[int(k[2:]) * 1000] += v
else:
- lat_mks[k * 1000] += v
+ lat_mks[int(k) * 1000] += v
for k, v in job_info['latency_us'].items():
- lat_mks[k] += v
+ lat_mks[int(k)] += v
for k, v in lat_mks.items():
lat_mks[k] = float(v) / num_res
@@ -675,6 +674,68 @@
return render_all_html(comment, di, lab_info, images, "report_ceph.html")
+@report('mixed', 'mixed')
+def make_mixed_report(processed_results, lab_info, comment):
+ #
+ # IOPS(X% read) = 100 / ( X / IOPS_W + (100 - X) / IOPS_R )
+ #
+ is_ssd = True
+ mixed = collections.defaultdict(lambda: [])
+ for res in processed_results.values():
+ if res.name.startswith('mixed'):
+ if res.name.startswith('mixed-ssd'):
+ is_ssd = True
+ mixed[res.concurence].append((res.p.rwmixread,
+ res.lat.average / 1000.0,
+ res.lat.deviation / 1000.0,
+ res.iops.average,
+ res.iops.deviation))
+
+ if len(mixed) == 0:
+ raise ValueError("No mixed load found")
+
+ fig, p1 = plt.subplots()
+ p2 = p1.twinx()
+
+ colors = ['red', 'green', 'blue', 'orange', 'magenta', "teal"]
+ colors_it = iter(colors)
+ for conc, mix_lat_iops in sorted(mixed.items()):
+ mix_lat_iops = sorted(mix_lat_iops)
+ read_perc, lat, dev, iops, iops_dev = zip(*mix_lat_iops)
+ p1.errorbar(read_perc, iops, color=next(colors_it),
+ yerr=iops_dev, label=str(conc) + " th")
+
+ p2.errorbar(read_perc, lat, color=next(colors_it),
+ ls='--', yerr=dev, label=str(conc) + " th lat")
+
+ if is_ssd:
+ p1.set_yscale('log')
+ p2.set_yscale('log')
+
+ p1.set_xlim(-5, 105)
+
+ read_perc = set(read_perc)
+ read_perc.add(0)
+ read_perc.add(100)
+ read_perc = sorted(read_perc)
+
+ plt.xticks(read_perc, map(str, read_perc))
+
+ p1.grid(True)
+ p1.set_xlabel("% of reads")
+ p1.set_ylabel("Mixed IOPS")
+ p2.set_ylabel("Latency, ms")
+
+ handles1, labels1 = p1.get_legend_handles_labels()
+ handles2, labels2 = p2.get_legend_handles_labels()
+ plt.subplots_adjust(top=0.85)
+ plt.legend(handles1 + handles2, labels1 + labels2,
+ bbox_to_anchor=(0.5, 1.15),
+ loc='upper center',
+ prop={'size': 12}, ncol=3)
+ plt.show()
+
+
def make_load_report(idx, results_dir, fname):
dpath = os.path.join(results_dir, "io_" + str(idx))
files = sorted(os.listdir(dpath))
@@ -751,13 +812,16 @@
logger.exception("Diring {0} report generation".format(name))
continue
- try:
- with open(hpath, "w") as fd:
- fd.write(report)
- except:
- logger.exception("Diring saving {0} report".format(name))
- continue
- logger.info("Report {0} saved into {1}".format(name, hpath))
+ if report is not None:
+ try:
+ with open(hpath, "w") as fd:
+ fd.write(report)
+ except:
+ logger.exception("Diring saving {0} report".format(name))
+ continue
+ logger.info("Report {0} saved into {1}".format(name, hpath))
+ else:
+ logger.warning("No report produced by {0!r}".format(name))
if not found:
logger.warning("No report generator found for this load")
diff --git a/wally/run_test.py b/wally/run_test.py
index d4fb911..359d917 100755
--- a/wally/run_test.py
+++ b/wally/run_test.py
@@ -749,10 +749,11 @@
def get_stage_name(func):
- if func.__name__.endswith("stage"):
- return func.__name__
+ nm = get_func_name(func)
+ if nm.endswith("stage"):
+ return nm
else:
- return func.__name__ + " stage"
+ return nm + " stage"
def get_test_names(raw_res):
@@ -798,6 +799,31 @@
print(tab.draw())
+def get_func_name(obj):
+ if hasattr(obj, '__name__'):
+ return obj.__name__
+ if hasattr(obj, 'func_name'):
+ return obj.func_name
+ return obj.func.func_name
+
+
+@contextlib.contextmanager
+def log_stage(func):
+ msg_templ = "Exception during {0}: {1!s}"
+ msg_templ_no_exc = "During {0}"
+
+ logger.info("Start " + get_stage_name(func))
+
+ try:
+ yield
+ except utils.StopTestError as exc:
+ logger.error(msg_templ.format(
+ get_func_name(func), exc))
+ except Exception:
+ logger.exception(msg_templ_no_exc.format(
+ get_func_name(func)))
+
+
def main(argv):
if faulthandler is not None:
faulthandler.register(signal.SIGUSR1, all_threads=True)
@@ -876,42 +902,29 @@
if cfg_dict.get('run_web_ui', False):
start_web_ui(cfg_dict, ctx)
- msg_templ = "Exception during {0.__name__}: {1!s}"
- msg_templ_no_exc = "During {0.__name__}"
-
- try:
- for stage in stages:
+ for stage in stages:
+ ok = False
+ with log_stage(stage):
logger.info("Start " + get_stage_name(stage))
stage(cfg_dict, ctx)
- except utils.StopTestError as exc:
- logger.error(msg_templ.format(stage, exc))
- except Exception:
- logger.exception(msg_templ_no_exc.format(stage))
- finally:
- exc, cls, tb = sys.exc_info()
- for stage in ctx.clear_calls_stack[::-1]:
- try:
- logger.info("Start " + get_stage_name(stage))
- stage(cfg_dict, ctx)
- except utils.StopTestError as cleanup_exc:
- logger.error(msg_templ.format(stage, cleanup_exc))
- except Exception:
- logger.exception(msg_templ_no_exc.format(stage))
+ ok = True
+ if not ok:
+ break
- logger.debug("Start utils.cleanup")
- for clean_func, args, kwargs in utils.iter_clean_func():
- try:
- logger.info("Start " + get_stage_name(clean_func))
- clean_func(*args, **kwargs)
- except utils.StopTestError as cleanup_exc:
- logger.error(msg_templ.format(clean_func, cleanup_exc))
- except Exception:
- logger.exception(msg_templ_no_exc.format(clean_func))
+ exc, cls, tb = sys.exc_info()
+ for stage in ctx.clear_calls_stack[::-1]:
+ with log_stage(stage):
+ stage(cfg_dict, ctx)
+
+ logger.debug("Start utils.cleanup")
+ for clean_func, args, kwargs in utils.iter_clean_func():
+ with log_stage(clean_func):
+ clean_func(*args, **kwargs)
if exc is None:
for report_stage in report_stages:
- logger.info("Start " + get_stage_name(report_stage))
- report_stage(cfg_dict, ctx)
+ with log_stage(report_stage):
+ report_stage(cfg_dict, ctx)
logger.info("All info stored in {0} folder".format(cfg_dict['var_dir']))
diff --git a/wally/suits/io/defaults.cfg b/wally/suits/io/defaults.cfg
index 9aff22c..8c8644b 100644
--- a/wally/suits/io/defaults.cfg
+++ b/wally/suits/io/defaults.cfg
@@ -1,6 +1,7 @@
buffered=0
group_reporting=1
iodepth=1
+unified_rw_reporting=1
norandommap=1
diff --git a/wally/suits/io/fio_task_parser.py b/wally/suits/io/fio_task_parser.py
index aca0254..ade0028 100644
--- a/wally/suits/io/fio_task_parser.py
+++ b/wally/suits/io/fio_task_parser.py
@@ -270,7 +270,7 @@
'x': 'sync direct'
}
off_mode = {'s': 'sequential', 'r': 'random'}
- oper = {'r': 'read', 'w': 'write'}
+ oper = {'r': 'read', 'w': 'write', 'm': 'mixed'}
return smode[name[2]] + " " + \
off_mode[name[0]] + " " + oper[name[1]]
@@ -322,7 +322,10 @@
rw = {"randread": "rr",
"randwrite": "rw",
"read": "sr",
- "write": "sw"}[sec.vals["rw"]]
+ "write": "sw",
+ "randrw": "rm",
+ "rw": "sm",
+ "readwrite": "sm"}[sec.vals["rw"]]
sync_mode = get_test_sync_mode(sec)
th_count = sec.vals.get('numjobs')
diff --git a/wally/suits/io/mixed_hdd.cfg b/wally/suits/io/mixed_hdd.cfg
new file mode 100644
index 0000000..be8b1ab
--- /dev/null
+++ b/wally/suits/io/mixed_hdd.cfg
@@ -0,0 +1,12 @@
+[global]
+include defaults.cfg
+ramp_time=5
+runtime=30
+blocksize=4k
+rw=randrw
+sync=1
+direct=1
+
+[mixed-hdd-r{rwmixread}_{TEST_SUMM}]
+rwmixread={% 0,20,40,60,80,100 %}
+numjobs={% 1,8,16 %}
diff --git a/wally/suits/io/mixed_ssd.cfg b/wally/suits/io/mixed_ssd.cfg
new file mode 100644
index 0000000..2e9f04c
--- /dev/null
+++ b/wally/suits/io/mixed_ssd.cfg
@@ -0,0 +1,12 @@
+[global]
+include defaults.cfg
+ramp_time=5
+runtime=30
+blocksize=4k
+rw=randrw
+sync=1
+direct=1
+
+[mixed-ssd-r{rwmixread}_{TEST_SUMM}]
+rwmixread={% 0,20,40,60,80,85,90,95,100 %}
+numjobs={% 1,16,64,128 %}