diff --git a/setup.py b/setup.py index 837c6a90b..2419cc3b3 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name='yandextank', - version='1.12.7', + version='1.12.8', description='a performance measurement tool', longer_description=''' Yandex.Tank is a performance measurement and load testing automatization tool. @@ -20,13 +20,13 @@ 'psutil>=1.2.1', 'requests>=2.5.1', 'paramiko>=1.16.0', 'pandas==0.24.2', 'numpy==1.15.4', 'future>=0.16.0', 'pip>=8.1.2', - 'pyyaml>=4.2b1', 'cerberus==1.3.1', 'influxdb>=5.0.0', 'netort>=0.7.6', + 'pyyaml>=4.2b1', 'cerberus==1.3.1', 'influxdb>=5.0.0', 'netort>=0.7.7', 'retrying>=1.3.3', 'pytest-runner', 'typing' ], setup_requires=[ ], tests_require=[ - 'pytest==4.6.3', 'flake8', 'pytest-benchmark' + 'pytest==4.6.3', 'flake8', 'pytest-benchmark', 'zipp==0.5.1', 'mock' ], license='LGPLv2', classifiers=[ diff --git a/yandextank/common/const.py b/yandextank/common/const.py new file mode 100644 index 000000000..f408ca118 --- /dev/null +++ b/yandextank/common/const.py @@ -0,0 +1,5 @@ + +class RetCode(): + CONTINUE = -1 + SUCCESS = 0 + ERROR = 1 diff --git a/yandextank/core/tankcore.py b/yandextank/core/tankcore.py index 4afc691c9..c74b09483 100644 --- a/yandextank/core/tankcore.py +++ b/yandextank/core/tankcore.py @@ -19,6 +19,7 @@ import yaml from builtins import str +from yandextank.common.const import RetCode from yandextank.common.exceptions import PluginNotPrepared from yandextank.common.interfaces import GeneratorPlugin, MonitoringPlugin, MonitoringDataListener from yandextank.plugins.DataUploader.client import LPRequisites @@ -286,11 +287,19 @@ def wait_for_finish(self): aggr_retcode = self.job.aggregator.is_test_finished() if aggr_retcode >= 0: return aggr_retcode - for plugin in self.plugins.values(): + for plugin_name, plugin in self.plugins.items(): logger.debug("Polling %s", plugin) - retcode = plugin.is_test_finished() - if retcode >= 0: - return retcode + try: + retcode = plugin.is_test_finished() + if retcode >= 0: + return retcode + except Exception: + logger.warning('Plugin {} failed:'.format(plugin_name), exc_info=True) + if isinstance(plugin, GeneratorPlugin): + return RetCode.ERROR + else: + logger.warning('Disabling plugin {}'.format(plugin_name)) + plugin.is_test_finished = lambda: RetCode.CONTINUE end_time = time.time() diff = end_time - begin_time logger.debug("Polling took %s", diff) diff --git a/yandextank/plugins/NeUploader/config/schema.yaml b/yandextank/plugins/NeUploader/config/schema.yaml index 564256087..5c678059b 100644 --- a/yandextank/plugins/NeUploader/config/schema.yaml +++ b/yandextank/plugins/NeUploader/config/schema.yaml @@ -1,7 +1,7 @@ api_address: description: luna back url type: string - default: https://volta-back.yandex-team.ru/ + default: https://back.luna.yandex-team.ru/ db_name: description: luna db name type: string @@ -13,4 +13,4 @@ test_name: meta: type: dict keysrules: - forbidden: ['name', 'raw', 'aggregate', 'group', 'host', 'type'] \ No newline at end of file + forbidden: ['name', 'raw', 'aggregate', 'group', 'host', 'type'] diff --git a/yandextank/plugins/NeUploader/plugin.py b/yandextank/plugins/NeUploader/plugin.py index a8a4d1e5a..e3ed222ad 100644 --- a/yandextank/plugins/NeUploader/plugin.py +++ b/yandextank/plugins/NeUploader/plugin.py @@ -17,6 +17,7 @@ class Plugin(AbstractPlugin, MonitoringDataListener): 'proto_code', 'net_code' } + OVERALL = '__overall__' def __init__(self, core, cfg, name): super(Plugin, self).__init__(core, cfg, name) @@ -112,17 +113,26 @@ def get_metric_obj(self, col, case): case_metrics = self.metrics_objs.get(case) if case_metrics is None: - case_metrics = { - col: constructor( - name='{} {}'.format(col, case), - raw=False, - aggregate=True, - source='tank', - importance='high' if col in self.importance_high else '', - **self.cfg.get('meta', {}) - ) for col, constructor in self.col_map.items() - } - self.metrics_objs[case] = case_metrics + for col, constructor in self.col_map.items(): + # args = dict(self.cfg.get('meta', {}), + # name=col, + # case=case, + # raw=False, + # aggregate=True, + # source='tank', + # importance='high' if col in self.importance_high else '', + # ) + # if case != self.OVERALL: + # args.update(parent=self.get_metric_obj(col, self.OVERALL)) + self.metrics_objs.setdefault(case, {})[col] = constructor( + dict(self.cfg.get('meta', {}), + name=col, + source='tank', + importance='high' if col in self.importance_high else ''), + raw=False, aggregate=True, + parent=self.get_metric_obj(col, self.OVERALL) if case != self.OVERALL else None, + case=case if case != self.OVERALL else None + ) return self.metrics_objs[case][col] def upload(self, df): @@ -135,9 +145,9 @@ def upload(self, df): df_cases_set.add(tag) for column in self.col_map: - overall_metric_obj = self.get_metric_obj(column, '__overall__') + overall_metric_obj = self.get_metric_obj(column, self.OVERALL) df['value'] = df[column] - result_df = self.filter_df_by_case(df, '__overall__') + result_df = self.filter_df_by_case(df, self.OVERALL) overall_metric_obj.put(result_df) for case_name in df_cases_set: @@ -187,7 +197,8 @@ def filter_df_by_case(df, case): :param case: str with case name :return: DataFrame with columns 'ts' and 'value' """ - return df[['ts', 'value']] if case == '__overall__' else df[df.tag.str.contains(case)][['ts', 'value']] + case = case.strip() + return df[['ts', 'value']] if case == Plugin.OVERALL else df[df.tag.str.strip() == case][['ts', 'value']] def map_uploader_tags(self, uploader_tags): if not uploader_tags: diff --git a/yandextank/plugins/NeUploader/tests/test_neuploader.py b/yandextank/plugins/NeUploader/tests/test_neuploader.py index e210c5f04..bc5bc7498 100644 --- a/yandextank/plugins/NeUploader/tests/test_neuploader.py +++ b/yandextank/plugins/NeUploader/tests/test_neuploader.py @@ -1,5 +1,9 @@ +import pandas as pd import pytest import json + +from numpy.testing import assert_array_equal + from yandextank.plugins.NeUploader.plugin import Plugin @@ -24,3 +28,19 @@ def test_metrics_names(self, mon_data, names): jsondata = json.load(f) dfs = Plugin.monitoring_data_to_dfs(jsondata) assert set(dfs.keys()) == {'{}:{}'.format(panelk, name) for i in jsondata for panelk, panelv in i['data'].items() for name in panelv['metrics'].keys()} + + +DF = pd.DataFrame({'ts': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], + 'value': [43, 75, 12, 65, 24, 65, 41, 87, 15, 62], + 'tag': ['foo', 'bar', 'foo', '', '', 'null', '', 'not_null', '', 'foo']}) + + +@pytest.mark.parametrize('df, case, expected', [ + (DF, '__overall__', DF[['ts', 'value']]), + (DF, 'foo', pd.DataFrame({'ts': [0, 2, 9], + 'value': [43, 12, 62]})), + (DF, 'null', pd.DataFrame({'ts': [5], + 'value': [65]})) +]) +def test_filter_df_by_case(df, case, expected): + assert_array_equal(Plugin.filter_df_by_case(df, case), expected, )