diff options
Diffstat (limited to 'yardstick/tests')
293 files changed, 55455 insertions, 343 deletions
diff --git a/yardstick/tests/__init__.py b/yardstick/tests/__init__.py index e69de29bb..56e3106b8 100644 --- a/yardstick/tests/__init__.py +++ b/yardstick/tests/__init__.py @@ -0,0 +1,75 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock + + +STL_MOCKS = { + 'trex_stl_lib': mock.MagicMock(), + 'trex_stl_lib.base64': mock.MagicMock(), + 'trex_stl_lib.binascii': mock.MagicMock(), + 'trex_stl_lib.collections': mock.MagicMock(), + 'trex_stl_lib.copy': mock.MagicMock(), + 'trex_stl_lib.datetime': mock.MagicMock(), + 'trex_stl_lib.functools': mock.MagicMock(), + 'trex_stl_lib.imp': mock.MagicMock(), + 'trex_stl_lib.inspect': mock.MagicMock(), + 'trex_stl_lib.json': mock.MagicMock(), + 'trex_stl_lib.linecache': mock.MagicMock(), + 'trex_stl_lib.math': mock.MagicMock(), + 'trex_stl_lib.os': mock.MagicMock(), + 'trex_stl_lib.platform': mock.MagicMock(), + 'trex_stl_lib.pprint': mock.MagicMock(), + 'trex_stl_lib.random': mock.MagicMock(), + 'trex_stl_lib.re': mock.MagicMock(), + 'trex_stl_lib.scapy': mock.MagicMock(), + 'trex_stl_lib.socket': mock.MagicMock(), + 'trex_stl_lib.string': mock.MagicMock(), + 'trex_stl_lib.struct': mock.MagicMock(), + 'trex_stl_lib.sys': mock.MagicMock(), + 'trex_stl_lib.threading': mock.MagicMock(), + 'trex_stl_lib.time': mock.MagicMock(), + 'trex_stl_lib.traceback': mock.MagicMock(), + 'trex_stl_lib.trex_stl_async_client': mock.MagicMock(), + 'trex_stl_lib.trex_stl_client': mock.MagicMock(), + 'trex_stl_lib.trex_stl_exceptions': mock.MagicMock(), + 'trex_stl_lib.trex_stl_ext': mock.MagicMock(), + 'trex_stl_lib.trex_stl_jsonrpc_client': mock.MagicMock(), + 'trex_stl_lib.trex_stl_packet_builder_interface': mock.MagicMock(), + 'trex_stl_lib.trex_stl_packet_builder_scapy': mock.MagicMock(), + 'trex_stl_lib.trex_stl_port': mock.MagicMock(), + 'trex_stl_lib.trex_stl_stats': mock.MagicMock(), + 'trex_stl_lib.trex_stl_streams': mock.MagicMock(), + 'trex_stl_lib.trex_stl_types': mock.MagicMock(), + 'trex_stl_lib.types': mock.MagicMock(), + 'trex_stl_lib.utils': mock.MagicMock(), + 'trex_stl_lib.utils.argparse': mock.MagicMock(), + 'trex_stl_lib.utils.collections': mock.MagicMock(), + 'trex_stl_lib.utils.common': mock.MagicMock(), + 'trex_stl_lib.utils.json': mock.MagicMock(), + 'trex_stl_lib.utils.os': mock.MagicMock(), + 'trex_stl_lib.utils.parsing_opts': mock.MagicMock(), + 'trex_stl_lib.utils.pwd': mock.MagicMock(), + 'trex_stl_lib.utils.random': mock.MagicMock(), + 'trex_stl_lib.utils.re': mock.MagicMock(), + 'trex_stl_lib.utils.string': mock.MagicMock(), + 'trex_stl_lib.utils.sys': mock.MagicMock(), + 'trex_stl_lib.utils.text_opts': mock.MagicMock(), + 'trex_stl_lib.utils.text_tables': mock.MagicMock(), + 'trex_stl_lib.utils.texttable': mock.MagicMock(), + 'trex_stl_lib.warnings': mock.MagicMock(), + 'trex_stl_lib.yaml': mock.MagicMock(), + 'trex_stl_lib.zlib': mock.MagicMock(), + 'trex_stl_lib.zmq': mock.MagicMock(), +} diff --git a/yardstick/tests/fixture.py b/yardstick/tests/fixture.py new file mode 100644 index 000000000..94d20eb34 --- /dev/null +++ b/yardstick/tests/fixture.py @@ -0,0 +1,47 @@ +# Copyright 2017 Intel Corporation +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import fixtures +import mock +import six + +from yardstick.common import task_template + + +class PluginParserFixture(fixtures.Fixture): + """PluginParser fixture. + + This class is intended to be used as a fixture within unit tests and + therefore consumers must register it using useFixture() within their + unit test class. + """ + + def __init__(self, rendered_plugin): + super(PluginParserFixture, self).__init__() + self._rendered_plugin = rendered_plugin + + def _setUp(self): + self.addCleanup(self._restore) + self._mock_tasktemplate_render = mock.patch.object( + task_template.TaskTemplate, 'render') + self.mock_tasktemplate_render = self._mock_tasktemplate_render.start() + self.mock_tasktemplate_render.return_value = self._rendered_plugin + self._mock_open = mock.patch.object(six.moves.builtins, 'open', create=True) + self.mock_open = self._mock_open.start() + self.mock_open.side_effect = mock.mock_open() + + def _restore(self): + self._mock_tasktemplate_render.stop() + self._mock_open.stop() diff --git a/yardstick/tests/functional/base.py b/yardstick/tests/functional/base.py new file mode 100644 index 000000000..51be013a1 --- /dev/null +++ b/yardstick/tests/functional/base.py @@ -0,0 +1,46 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import abc +import six + +from oslo_config import cfg +from oslotest import base + + +CONF = cfg.CONF + + +@six.add_metaclass(abc.ABCMeta) +class BaseFunctionalTestCase(base.BaseTestCase): + """Base class for functional tests.""" + + def setUp(self): + super(BaseFunctionalTestCase, self).setUp() + + def config(self, **kw): + """Override some configuration values. + + The keyword arguments are the names of configuration options to + override and their values. + + If a group argument is supplied, the overrides are applied to + the specified configuration option group. + + All overrides are automatically cleared at the end of the current + test by the fixtures cleanup process. + """ + group = kw.pop('group', None) + for k, v in kw.items(): + CONF.set_override(k, v, group) diff --git a/yardstick/tests/functional/benchmark/__init__.py b/yardstick/tests/functional/benchmark/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/benchmark/__init__.py diff --git a/yardstick/tests/functional/benchmark/core/__init__.py b/yardstick/tests/functional/benchmark/core/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/benchmark/core/__init__.py diff --git a/yardstick/tests/functional/benchmark/core/test_report.py b/yardstick/tests/functional/benchmark/core/test_report.py new file mode 100644 index 000000000..832d3b3e1 --- /dev/null +++ b/yardstick/tests/functional/benchmark/core/test_report.py @@ -0,0 +1,314 @@ +############################################################################## +# Copyright (c) 2018-2019 Intel Corporation. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import ast +import tempfile +import unittest + +import mock +from six.moves import configparser + +from yardstick.benchmark import core +from yardstick.benchmark.core import report +from yardstick.cmd.commands import change_osloobj_to_paras + + +GOOD_YAML_NAME = 'fake_name' +GOOD_TASK_ID = "9cbe74b6-df09-4535-8bdc-dc3a43b8a4e2" +GOOD_DB_FIELDKEYS = [ + {u'fieldKey': u'metric1', u'fieldType': u'integer'}, + {u'fieldKey': u'metric4', u'fieldType': u'integer'}, + {u'fieldKey': u'metric2', u'fieldType': u'integer'}, + {u'fieldKey': u'metric3', u'fieldType': u'integer'}, +] +GOOD_DB_METRICS = [ + {u'time': u'2018-08-20T16:49:26.372662016Z', + u'metric1': 1, u'metric2': 0, u'metric3': 8, u'metric4': 5}, + {u'time': u'2018-08-20T16:49:27.374208000Z', + u'metric1': 1, u'metric2': 1, u'metric3': 5, u'metric4': 4}, + {u'time': u'2018-08-20T16:49:28.375742976Z', + u'metric1': 2, u'metric2': 2, u'metric3': 3, u'metric4': 3}, + {u'time': u'2018-08-20T16:49:29.377299968Z', + u'metric1': 3, u'metric2': 3, u'metric3': 2, u'metric4': 2}, + {u'time': u'2018-08-20T16:49:30.378252032Z', + u'metric1': 5, u'metric2': 4, u'metric3': 1, u'metric4': 1}, + {u'time': u'2018-08-20T16:49:30.379359421Z', + u'metric1': 8, u'metric2': 5, u'metric3': 1, u'metric4': 0}, +] +GOOD_DB_BARO_METRICS = [ + {u'value': 324050, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-08-20T16:49:27.383698038Z', + u'type_instance': u'user', u'type': u'cpu'}, + { + u'value': 193798, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-12-19T16:49:27.383712594Z', + u'type_instance': u'system', u'type': u'cpu'}, + { + u'value': 324051, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-08-20T16:49:28.383696624Z', + u'type_instance': u'user', u'type': u'cpu'}, + { + u'value': 193800, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-08-20T16:49:28.383713481Z', + u'type_instance': u'system', u'type': u'cpu'}, + { + u'value': 324054, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-08-20T16:49:29.3836966789Z', + u'type_instance': u'user', u'type': u'cpu'}, + { + u'value': 193801, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-08-20T16:49:29.383716296Z', + u'type_instance': u'system', u'type': u'cpu'} +] +TIMESTAMP_START = '2018-08-20T16:49:26.372662016Z' +TIMESTAMP_END = '2018-08-20T16:49:30.379359421Z' + +yardstick_config = """ +[DEFAULT] +dispatcher = influxdb +""" + + +def my_query(query_sql, db=None): + get_fieldkeys_cmd = 'show field keys' + get_metrics_cmd = 'select * from' + get_start_time_cmd = 'ORDER ASC limit 1' + get_end_time_cmd = 'ORDER DESC limit 1' + if db: + if get_start_time_cmd in query_sql: + return TIMESTAMP_START + elif get_end_time_cmd in query_sql: + return TIMESTAMP_END + else: + return GOOD_DB_BARO_METRICS + elif get_fieldkeys_cmd in query_sql: + return GOOD_DB_FIELDKEYS + elif get_metrics_cmd in query_sql: + return GOOD_DB_METRICS + return [] + + +class ReportTestCase(unittest.TestCase): + + @mock.patch.object(report.influx, 'query', new=my_query) + @mock.patch.object(configparser.ConfigParser, + 'read', side_effect=mock.mock_open(read_data=yardstick_config)) + def test_report_generate_nsb_simple(self, *args): + tmpfile = tempfile.NamedTemporaryFile(delete=True) + + args = core.Param({"task_id": [GOOD_TASK_ID], "yaml_name": [GOOD_YAML_NAME]}) + params = change_osloobj_to_paras(args) + + with mock.patch.object(report.consts, 'DEFAULT_HTML_FILE', tmpfile.name): + report.Report().generate_nsb(params) + + data_act = None + time_act = None + keys_act = None + tree_act = None + with open(tmpfile.name) as f: + for l in f.readlines(): + if "var report_data = {" in l: + data_act = ast.literal_eval(l.strip()[18:-1]) + elif "var report_time = [" in l: + time_act = ast.literal_eval(l.strip()[18:-1]) + elif "var report_keys = [" in l: + keys_act = ast.literal_eval(l.strip()[18:-1]) + elif "var report_tree = [" in l: + tree_act = ast.literal_eval(l.strip()[18:-1]) + data_exp = { + 'metric1': [ + {'x': '16:49:26.372662', 'y': 1}, + {'x': '16:49:27.374208', 'y': 1}, + {'x': '16:49:28.375742', 'y': 2}, + {'x': '16:49:29.377299', 'y': 3}, + {'x': '16:49:30.378252', 'y': 5}, + {'x': '16:49:30.379359', 'y': 8}], + 'metric2': [ + {'x': '16:49:26.372662', 'y': 0}, + {'x': '16:49:27.374208', 'y': 1}, + {'x': '16:49:28.375742', 'y': 2}, + {'x': '16:49:29.377299', 'y': 3}, + {'x': '16:49:30.378252', 'y': 4}, + {'x': '16:49:30.379359', 'y': 5}], + 'metric3': [ + {'x': '16:49:26.372662', 'y': 8}, + {'x': '16:49:27.374208', 'y': 5}, + {'x': '16:49:28.375742', 'y': 3}, + {'x': '16:49:29.377299', 'y': 2}, + {'x': '16:49:30.378252', 'y': 1}, + {'x': '16:49:30.379359', 'y': 1}], + 'metric4': [ + {'x': '16:49:26.372662', 'y': 5}, + {'x': '16:49:27.374208', 'y': 4}, + {'x': '16:49:28.375742', 'y': 3}, + {'x': '16:49:29.377299', 'y': 2}, + {'x': '16:49:30.378252', 'y': 1}, + {'x': '16:49:30.379359', 'y': 0}], + 'myhostname.cpu_value.cpu.system.0': [ + {'x': '16:49:27.3837', 'y': 193798}, + {'x': '16:49:28.3837', 'y': 193800}, + {'x': '16:49:29.3837', 'y': 193801}], + 'myhostname.cpu_value.cpu.user.0': [ + {'x': '16:49:27.3836', 'y': 324050}, + {'x': '16:49:28.3836', 'y': 324051}, + {'x': '16:49:29.3836', 'y': 324054}], + 'myhostname.cpufreq_value.cpu.system.0': [ + {'x': '16:49:27.3837', 'y': 193798}, + {'x': '16:49:28.3837', 'y': 193800}, + {'x': '16:49:29.3837', 'y': 193801}], + 'myhostname.cpufreq_value.cpu.user.0': [ + {'x': '16:49:27.3836', 'y': 324050}, + {'x': '16:49:28.3836', 'y': 324051}, + {'x': '16:49:29.3836', 'y': 324054}], + 'myhostname.intel_pmu_value.cpu.system.0': [ + {'x': '16:49:27.3837', 'y': 193798}, + {'x': '16:49:28.3837', 'y': 193800}, + {'x': '16:49:29.3837', 'y': 193801}], + 'myhostname.intel_pmu_value.cpu.user.0': [ + {'x': '16:49:27.3836', 'y': 324050}, + {'x': '16:49:28.3836', 'y': 324051}, + {'x': '16:49:29.3836', 'y': 324054}], + 'myhostname.virt_value.cpu.system.0': [ + {'x': '16:49:27.3837', 'y': 193798}, + {'x': '16:49:28.3837', 'y': 193800}, + {'x': '16:49:29.3837', 'y': 193801}], + 'myhostname.virt_value.cpu.user.0': [ + {'x': '16:49:27.3836', 'y': 324050}, + {'x': '16:49:28.3836', 'y': 324051}, + {'x': '16:49:29.3836', 'y': 324054}], + 'myhostname.memory_value.cpu.system.0': [ + {'x': '16:49:27.3837', 'y': 193798}, + {'x': '16:49:28.3837', 'y': 193800}, + {'x': '16:49:29.3837', 'y': 193801}], + 'myhostname.memory_value.cpu.user.0': [ + {'x': '16:49:27.3836', 'y': 324050}, + {'x': '16:49:28.3836', 'y': 324051}, + {'x': '16:49:29.3836', 'y': 324054}] + } + time_exp = [ + '16:49:26.372662', '16:49:27.374208', '16:49:27.3836', + '16:49:27.3837', '16:49:28.375742', '16:49:28.3836', + '16:49:28.3837', '16:49:29.377299', '16:49:29.3836', + '16:49:29.3837', '16:49:30.378252', '16:49:30.379359', + ] + keys_exp = sorted([ + 'metric1', 'metric2', 'metric3', 'metric4', + 'myhostname.cpu_value.cpu.system.0', + 'myhostname.cpu_value.cpu.user.0', + 'myhostname.cpufreq_value.cpu.system.0', + 'myhostname.cpufreq_value.cpu.user.0', + 'myhostname.intel_pmu_value.cpu.system.0', + 'myhostname.intel_pmu_value.cpu.user.0', + 'myhostname.virt_value.cpu.system.0', + 'myhostname.virt_value.cpu.user.0', + 'myhostname.memory_value.cpu.system.0', + 'myhostname.memory_value.cpu.user.0', + ]) + tree_exp = [ + {'parent': '#', 'text': 'metric1', 'id': 'metric1'}, + {'parent': '#', 'text': 'metric2', 'id': 'metric2'}, + {'parent': '#', 'text': 'metric3', 'id': 'metric3'}, + {'parent': '#', 'text': 'metric4', 'id': 'metric4'}, + {'id': 'myhostname', 'parent': '#', 'text': 'myhostname'}, + {'id': 'myhostname.cpu_value', + 'parent': 'myhostname', + 'text': 'cpu_value'}, + {'id': 'myhostname.cpu_value.cpu', + 'parent': 'myhostname.cpu_value', + 'text': 'cpu'}, + {'id': 'myhostname.cpu_value.cpu.system', + 'parent': 'myhostname.cpu_value.cpu', + 'text': 'system'}, + {'id': 'myhostname.cpu_value.cpu.system.0', + 'parent': 'myhostname.cpu_value.cpu.system', + 'text': '0'}, + {'id': 'myhostname.cpu_value.cpu.user', + 'parent': 'myhostname.cpu_value.cpu', + 'text': 'user'}, + {'id': 'myhostname.cpu_value.cpu.user.0', + 'parent': 'myhostname.cpu_value.cpu.user', + 'text': '0'}, + {'id': 'myhostname.cpufreq_value', + 'parent': 'myhostname', + 'text': 'cpufreq_value'}, + {'id': 'myhostname.cpufreq_value.cpu', + 'parent': 'myhostname.cpufreq_value', + 'text': 'cpu'}, + {'id': 'myhostname.cpufreq_value.cpu.system', + 'parent': 'myhostname.cpufreq_value.cpu', + 'text': 'system'}, + {'id': 'myhostname.cpufreq_value.cpu.system.0', + 'parent': 'myhostname.cpufreq_value.cpu.system', + 'text': '0'}, + {'id': 'myhostname.cpufreq_value.cpu.user', + 'parent': 'myhostname.cpufreq_value.cpu', + 'text': 'user'}, + {'id': 'myhostname.cpufreq_value.cpu.user.0', + 'parent': 'myhostname.cpufreq_value.cpu.user', + 'text': '0'}, + {'id': 'myhostname.intel_pmu_value', + 'parent': 'myhostname', + 'text': 'intel_pmu_value'}, + {'id': 'myhostname.intel_pmu_value.cpu', + 'parent': 'myhostname.intel_pmu_value', + 'text': 'cpu'}, + {'id': 'myhostname.intel_pmu_value.cpu.system', + 'parent': 'myhostname.intel_pmu_value.cpu', + 'text': 'system'}, + {'id': 'myhostname.intel_pmu_value.cpu.system.0', + 'parent': 'myhostname.intel_pmu_value.cpu.system', + 'text': '0'}, + {'id': 'myhostname.intel_pmu_value.cpu.user', + 'parent': 'myhostname.intel_pmu_value.cpu', + 'text': 'user'}, + {'id': 'myhostname.intel_pmu_value.cpu.user.0', + 'parent': 'myhostname.intel_pmu_value.cpu.user', + 'text': '0'}, + {'id': 'myhostname.memory_value', + 'parent': 'myhostname', + 'text': 'memory_value'}, + {'id': 'myhostname.memory_value.cpu', + 'parent': 'myhostname.memory_value', + 'text': 'cpu'}, + {'id': 'myhostname.memory_value.cpu.system', + 'parent': 'myhostname.memory_value.cpu', + 'text': 'system'}, + {'id': 'myhostname.memory_value.cpu.system.0', + 'parent': 'myhostname.memory_value.cpu.system', + 'text': '0'}, + {'id': 'myhostname.memory_value.cpu.user', + 'parent': 'myhostname.memory_value.cpu', + 'text': 'user'}, + {'id': 'myhostname.memory_value.cpu.user.0', + 'parent': 'myhostname.memory_value.cpu.user', + 'text': '0'}, + {'id': 'myhostname.virt_value', 'parent': 'myhostname', + 'text': 'virt_value'}, + {'id': 'myhostname.virt_value.cpu', + 'parent': 'myhostname.virt_value', + 'text': 'cpu'}, + {'id': 'myhostname.virt_value.cpu.system', + 'parent': 'myhostname.virt_value.cpu', + 'text': 'system'}, + {'id': 'myhostname.virt_value.cpu.system.0', + 'parent': 'myhostname.virt_value.cpu.system', + 'text': '0'}, + {'id': 'myhostname.virt_value.cpu.user', + 'parent': 'myhostname.virt_value.cpu', + 'text': 'user'}, + {'id': 'myhostname.virt_value.cpu.user.0', + 'parent': 'myhostname.virt_value.cpu.user', + 'text': '0'} + ] + + self.assertEqual(data_exp, data_act) + self.assertEqual(time_exp, time_act) + self.assertEqual(keys_exp, keys_act) + self.assertEqual(tree_exp, tree_act) diff --git a/yardstick/tests/functional/benchmark/scenarios/__init__.py b/yardstick/tests/functional/benchmark/scenarios/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/benchmark/scenarios/__init__.py diff --git a/yardstick/tests/functional/benchmark/scenarios/networking/__init__.py b/yardstick/tests/functional/benchmark/scenarios/networking/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/benchmark/scenarios/networking/__init__.py diff --git a/yardstick/tests/functional/benchmark/scenarios/networking/test_vnf_generic.py b/yardstick/tests/functional/benchmark/scenarios/networking/test_vnf_generic.py new file mode 100644 index 000000000..38f1a978d --- /dev/null +++ b/yardstick/tests/functional/benchmark/scenarios/networking/test_vnf_generic.py @@ -0,0 +1,195 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import sys + +import mock +import unittest + +from yardstick import tests as y_tests +from yardstick.common import utils + + +with mock.patch.dict(sys.modules, y_tests.STL_MOCKS): + from yardstick.benchmark.scenarios.networking import vnf_generic + + +TRAFFIC_PROFILE_1 = """ +schema: nsb:traffic_profile:0.1 +name: rfc2544 +description: Traffic profile to run RFC2544 latency +traffic_profile: + traffic_type : RFC2544Profile + frame_rate : 100 +uplink_0: + ipv4: + id: 1 + outer_l2: + framesize: + 64B: "{{get(imix, 'imix.uplink.64B', '0') }}" + 128B: "{{get(imix, 'imix.uplink.128B', '0') }}" +""" + +TRAFFIC_PROFILE_2 = """ +{% set vports = get(extra_args, 'vports', 1) %} +traffic_profile: + traffic_type : RFC2544Profile +{% for vport in range(vports|int) %} +uplink_{{vport}}: + ipv4: 192.168.0.{{vport}} +{% endfor %} +""" + +TOPOLOGY_PROFILE = """ +{% set vports = get(extra_args, 'vports', 2) %} +nsd:nsd-catalog: + nsd: + - id: 3tg-topology + vld: +{% for vport in range(0,vports,2|int) %} + - id: uplink_{{loop.index0}} + name: tg__0 to vnf__0 link {{vport + 1}} + vnfd-connection-point-ref: + - vnfd-connection-point-ref: xe{{vport}} + - id: downlink_{{loop.index0}} + name: vnf__0 to tg__0 link {{vport + 2}} + vnfd-connection-point-ref: + - vnfd-connection-point-ref: xe{{vport+1}} +{% endfor %} +""" + +class VnfGenericTestCase(unittest.TestCase): + + def setUp(self): + scenario_cfg = {'topology': 'fake_topology', + 'task_path': 'fake_path', + 'traffic_profile': 'fake_fprofile_path'} + context_cfg = {} + topology_yaml = {'nsd:nsd-catalog': {'nsd': [mock.Mock()]}} + + with mock.patch.object(utils, 'open_relative_file') as mock_open_path: + mock_open_path.side_effect = mock.mock_open(read_data=str(topology_yaml)) + self.ns_testcase = vnf_generic.NetworkServiceTestCase(scenario_cfg, + context_cfg) + self.ns_testcase._get_traffic_profile = mock.Mock() + self.ns_testcase._get_topology = mock.Mock() + + def test__fill_traffic_profile_no_args(self): + traffic_profile = copy.deepcopy(TRAFFIC_PROFILE_1) + self.ns_testcase._get_traffic_profile.return_value = traffic_profile + + self.ns_testcase._fill_traffic_profile() + config = self.ns_testcase.traffic_profile.params + self.assertEqual('nsb:traffic_profile:0.1', config['schema']) + self.assertEqual('rfc2544', config['name']) + self.assertEqual('Traffic profile to run RFC2544 latency', + config['description']) + t_profile = {'traffic_type': 'RFC2544Profile', + 'frame_rate': 100} + self.assertEqual(t_profile, config['traffic_profile']) + uplink_0 = { + 'ipv4': {'id': 1, + 'outer_l2': {'framesize': {'128B': '0', '64B': '0'}} + } + } + self.assertEqual(uplink_0, config['uplink_0']) + + def test__fill_traffic_profile_with_args(self): + traffic_profile = copy.deepcopy(TRAFFIC_PROFILE_2) + self.ns_testcase._get_traffic_profile.return_value = traffic_profile + self.ns_testcase.scenario_cfg['extra_args'] = {'vports': 3} + + self.ns_testcase._fill_traffic_profile() + config = self.ns_testcase.traffic_profile.params + self.assertEqual({'ipv4': '192.168.0.0'}, config['uplink_0']) + self.assertEqual({'ipv4': '192.168.0.1'}, config['uplink_1']) + self.assertEqual({'ipv4': '192.168.0.2'}, config['uplink_2']) + self.assertNotIn('uplink_3', config) + + def test__fill_traffic_profile_incorrect_args(self): + traffic_profile = copy.deepcopy(TRAFFIC_PROFILE_2) + self.ns_testcase._get_traffic_profile.return_value = traffic_profile + self.ns_testcase.scenario_cfg['extra_args'] = {'incorrect_vports': 3} + + self.ns_testcase._fill_traffic_profile() + config = self.ns_testcase.traffic_profile.params + self.assertEqual({'ipv4': '192.168.0.0'}, config['uplink_0']) + self.assertNotIn('uplink_1', config) + + def test__render_topology_with_args(self): + topology_profile = copy.deepcopy(TOPOLOGY_PROFILE) + self.ns_testcase._get_topology.return_value = topology_profile + self.ns_testcase.scenario_cfg['extra_args'] = {'vports': 6} + + self.ns_testcase._render_topology() + topology = self.ns_testcase.topology + self.assertEqual("3tg-topology", topology['id']) + vld = self.ns_testcase.topology['vld'] + self.assertEqual(len(vld), 6) + for index, vport in enumerate(range(0, 6, 2)): + self.assertEqual('uplink_{}'.format(index), vld[vport]['id']) + self.assertEqual('tg__0 to vnf__0 link {}'.format(vport + 1), vld[vport]['name']) + self.assertEqual('xe{}'.format(vport), + vld[vport]['vnfd-connection-point-ref'][0] + ['vnfd-connection-point-ref']) + + self.assertEqual('downlink_{}'.format(index), vld[vport + 1]['id']) + self.assertEqual('vnf__0 to tg__0 link {}'.format(vport + 2), vld[vport + 1]['name']) + self.assertEqual('xe{}'.format(vport + 1), + vld[vport + 1]['vnfd-connection-point-ref'][0] + ['vnfd-connection-point-ref']) + + def test__render_topology_incorrect_args(self): + topology_profile = copy.deepcopy(TOPOLOGY_PROFILE) + self.ns_testcase._get_topology.return_value = topology_profile + self.ns_testcase.scenario_cfg['extra_args'] = {'fake_vports': 5} + + self.ns_testcase._render_topology() + + topology = self.ns_testcase.topology + self.assertEqual("3tg-topology", topology['id']) + vld = self.ns_testcase.topology['vld'] + self.assertEqual(len(vld), 2) + + self.assertEqual('uplink_0', vld[0]['id']) + self.assertEqual('tg__0 to vnf__0 link 1', vld[0]['name']) + self.assertEqual('xe0', + vld[0]['vnfd-connection-point-ref'][0]['vnfd-connection-point-ref']) + + self.assertEqual('downlink_0', vld[1]['id']) + self.assertEqual('vnf__0 to tg__0 link 2', vld[1]['name']) + self.assertEqual('xe1', + vld[1]['vnfd-connection-point-ref'][0]['vnfd-connection-point-ref']) + + def test__render_topology_no_args(self): + topology_profile = copy.deepcopy(TOPOLOGY_PROFILE) + self.ns_testcase._get_topology.return_value = topology_profile + + self.ns_testcase._render_topology() + + topology = self.ns_testcase.topology + self.assertEqual("3tg-topology", topology['id']) + vld = self.ns_testcase.topology['vld'] + self.assertEqual(len(vld), 2) + + self.assertEqual('uplink_0', vld[0]['id']) + self.assertEqual('tg__0 to vnf__0 link 1', vld[0]['name']) + self.assertEqual('xe0', + vld[0]['vnfd-connection-point-ref'][0]['vnfd-connection-point-ref']) + + self.assertEqual('downlink_0', vld[1]['id']) + self.assertEqual('vnf__0 to tg__0 link 2', vld[1]['name']) + self.assertEqual('xe1', + vld[1]['vnfd-connection-point-ref'][0]['vnfd-connection-point-ref']) diff --git a/yardstick/tests/functional/common/__init__.py b/yardstick/tests/functional/common/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/common/__init__.py diff --git a/yardstick/tests/functional/common/fake_directory_package/README.md b/yardstick/tests/functional/common/fake_directory_package/README.md new file mode 100644 index 000000000..689e47039 --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/README.md @@ -0,0 +1,2 @@ +# yardstick_new_plugin +Yardstick plugin diff --git a/yardstick/tests/functional/common/fake_directory_package/setup.py b/yardstick/tests/functional/common/fake_directory_package/setup.py new file mode 100644 index 000000000..cf938ef4f --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/setup.py @@ -0,0 +1,29 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from setuptools import setup, find_packages + +setup( + name='yardstick_new_plugin_2', + version='1.0.0', + packages=find_packages(), + include_package_data=True, + url='https://www.opnfv.org', + entry_points={ + 'yardstick.scenarios': [ + 'Dummy2 = yardstick_new_plugin.benchmark.scenarios.dummy2.dummy2:' + 'Dummy2', + ] + }, +) diff --git a/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/__init__.py b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/__init__.py diff --git a/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/__init__.py b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/__init__.py diff --git a/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/__init__.py b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/__init__.py diff --git a/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/__init__.py b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/__init__.py diff --git a/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/dummy2.py b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/dummy2.py new file mode 100644 index 000000000..a2211ec51 --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/dummy2.py @@ -0,0 +1,40 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from yardstick.benchmark.scenarios import base + + +LOG = logging.getLogger(__name__) + + +class Dummy2(base.Scenario): + """Execute Dummy (v2!) echo""" + __scenario_type__ = "Dummy2" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.setup_done = False + + def setup(self): + self.setup_done = True + + def run(self, result): + if not self.setup_done: + self.setup() + + result["hello"] = "yardstick" + LOG.info("Dummy (v2!) echo hello yardstick!") diff --git a/yardstick/tests/functional/common/fake_module/__init__.py b/yardstick/tests/functional/common/fake_module/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/common/fake_module/__init__.py diff --git a/yardstick/tests/functional/common/fake_module/fake_library.py b/yardstick/tests/functional/common/fake_module/fake_library.py new file mode 100644 index 000000000..28c7dc694 --- /dev/null +++ b/yardstick/tests/functional/common/fake_module/fake_library.py @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class FakeClassToBeImported(object): + pass diff --git a/yardstick/tests/functional/common/fake_pip_package/yardstick_new_plugin-1.0.0.tar.gz b/yardstick/tests/functional/common/fake_pip_package/yardstick_new_plugin-1.0.0.tar.gz Binary files differnew file mode 100644 index 000000000..e5379a78a --- /dev/null +++ b/yardstick/tests/functional/common/fake_pip_package/yardstick_new_plugin-1.0.0.tar.gz diff --git a/yardstick/tests/functional/common/messaging/__init__.py b/yardstick/tests/functional/common/messaging/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/common/messaging/__init__.py diff --git a/yardstick/tests/functional/common/messaging/test_messaging.py b/yardstick/tests/functional/common/messaging/test_messaging.py new file mode 100644 index 000000000..f3e31e718 --- /dev/null +++ b/yardstick/tests/functional/common/messaging/test_messaging.py @@ -0,0 +1,99 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import multiprocessing +import time + +from yardstick.common.messaging import consumer +from yardstick.common.messaging import payloads +from yardstick.common.messaging import producer +from yardstick.tests.functional import base + + +TOPIC = 'topic_MQ' +METHOD_INFO = 'info' + + +class DummyPayload(payloads.Payload): + REQUIRED_FIELDS = {'version', 'data'} + + +class DummyEndpoint(consumer.NotificationHandler): + + def info(self, ctxt, **kwargs): + if ctxt['id'] in self._ctx_ids: + self._queue.put('Nr {}, data: {}, id: {}'.format( + self._id, kwargs['data'], ctxt['id'])) + + +class DummyConsumer(consumer.MessagingConsumer): + + def __init__(self, _id, ctx_ids, queue): + self._id = _id + endpoints = [DummyEndpoint(_id, ctx_ids, queue)] + super(DummyConsumer, self).__init__(TOPIC, ctx_ids, endpoints) + + +class DummyProducer(producer.MessagingProducer): + pass + + +def _run_consumer(_id, ctx_ids, queue): + _consumer = DummyConsumer(_id, ctx_ids, queue) + _consumer.start_rpc_server() + _consumer.wait() + + +class MessagingTestCase(base.BaseFunctionalTestCase): + + @staticmethod + def _terminate_consumers(num_consumers, processes): + for i in range(num_consumers): + processes[i].terminate() + + def test_run_five_consumers(self): + output_queue = multiprocessing.Queue() + num_consumers = 10 + ctx_1 = 100001 + ctx_2 = 100002 + producers = [DummyProducer(TOPIC, _id=ctx_1), + DummyProducer(TOPIC, _id=ctx_2)] + + processes = [] + for i in range(num_consumers): + processes.append(multiprocessing.Process( + name='consumer_{}'.format(i), + target=_run_consumer, + args=(i, [ctx_1, ctx_2], output_queue))) + processes[i].start() + self.addCleanup(self._terminate_consumers, num_consumers, processes) + + time.sleep(2) # Let consumers to create the listeners + for producer in producers: + for message in ['message 0', 'message 1']: + producer.send_message(METHOD_INFO, + DummyPayload(version=1, data=message)) + + time.sleep(2) # Let consumers attend the calls + output = [] + while not output_queue.empty(): + output.append(output_queue.get(True, 1)) + + self.assertEqual(num_consumers * 4, len(output)) + msg_template = 'Nr {}, data: {}, id: {}' + for i in range(num_consumers): + for ctx in [ctx_1, ctx_2]: + for message in ['message 0', 'message 1']: + msg = msg_template.format(i, message, ctx) + self.assertIn(msg, output) diff --git a/yardstick/tests/functional/common/test_packages.py b/yardstick/tests/functional/common/test_packages.py new file mode 100644 index 000000000..e15f72898 --- /dev/null +++ b/yardstick/tests/functional/common/test_packages.py @@ -0,0 +1,100 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from os import path +import re +import unittest + +from yardstick.common import packages +from yardstick.common import utils +from yardstick.tests.functional import base + + +class PipPackagesTestCase(base.BaseFunctionalTestCase): + + TMP_FOLDER = '/tmp/pip_packages/' + PYTHONPATH = 'PYTHONPATH=%s' % TMP_FOLDER + + def setUp(self): + super(PipPackagesTestCase, self).setUp() + privsep_helper = os.path.join( + os.getenv('VIRTUAL_ENV'), 'bin', 'privsep-helper') + self.config( + helper_command=' '.join(['sudo', '-EH', privsep_helper]), + group='yardstick_privileged') + self.addCleanup(self._cleanup) + + def _cleanup(self): + utils.execute_command('sudo rm -rf %s' % self.TMP_FOLDER) + + def _remove_package(self, package): + os.system('%s python -m pip uninstall %s -y' % + (self.PYTHONPATH, package)) + + def _list_packages(self): + pip_list_regex = re.compile( + r"(?P<name>[\w\.-]+) \((?P<version>[\w\d_\.\-]+),*.*\)") + pip_list_regex_18 = re.compile( + r"(?P<name>[\w\.-]+)[\s]+(?P<version>[\w\d_\.\-]+),*.*") + pkg_dict = {} + pkgs = utils.execute_command('python -m pip list', + env={'PYTHONPATH': self.TMP_FOLDER}) + for line in pkgs: + match = pip_list_regex.match(line) + if not match: + match = pip_list_regex_18.match(line) + if match and match.group('name'): + pkg_dict[match.group('name')] = match.group('version') + return pkg_dict + + def test_install_from_folder(self): + dirname = path.dirname(__file__) + package_dir = dirname + '/fake_directory_package' + package_name = 'yardstick-new-plugin-2' + self.addCleanup(self._remove_package, package_name) + self._remove_package(package_name) + self.assertFalse(package_name in self._list_packages()) + + self.assertEqual(0, packages.pip_install(package_dir, self.TMP_FOLDER)) + self.assertTrue(package_name in self._list_packages()) + + @unittest.skip("see https://github.com/pypa/pip/issues/3889") + def test_install_from_pip_package(self): + dirname = path.dirname(__file__) + package_path = (dirname + + '/fake_pip_package/yardstick_new_plugin-1.0.0.tar.gz') + package_name = 'yardstick-new-plugin' + self.addCleanup(self._remove_package, package_name) + self._remove_package(package_name) + self.assertFalse(package_name in self._list_packages()) + + self.assertEqual(0, packages.pip_install(package_path, self.TMP_FOLDER)) + self.assertTrue(package_name in self._list_packages()) + + # NOTE(ralonsoh): an stable test plugin project is needed in OPNFV git + # server to execute this test. + # def test_install_from_url(self): + + def test_pip_freeze(self): + # NOTE (ralonsoh): from requirements.txt file. The best way to test + # this function is to parse requirements.txt and test-requirements.txt + # and check all packages. + pkgs_ref = {'Babel': '2.6.0', + 'SQLAlchemy': '1.2.18', + 'influxdb': '5.1.0', + 'netifaces': '0.10.9'} + pkgs = packages.pip_list() + for name, version in (pkgs_ref.items()): + self.assertEqual(version, pkgs[name]) diff --git a/yardstick/tests/functional/common/test_utils.py b/yardstick/tests/functional/common/test_utils.py new file mode 100644 index 000000000..b9f1f773a --- /dev/null +++ b/yardstick/tests/functional/common/test_utils.py @@ -0,0 +1,72 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import multiprocessing +import unittest +import socket +import sys +import time + +from yardstick.common import utils + + +class ImportModulesFromPackageTestCase(unittest.TestCase): + + def test_import_package(self): + module_name = 'yardstick.tests.functional.common.fake_module' + library_name = 'fake_library' + class_name = 'FakeClassToBeImported' + self.assertNotIn(module_name, sys.modules) + + utils.import_modules_from_package(module_name) + self.assertIn(module_name, sys.modules) + module_obj = sys.modules[module_name] + library_obj = getattr(module_obj, library_name) + class_obj = getattr(library_obj, class_name) + self.assertEqual(class_name, class_obj().__class__.__name__) + + +class SendSocketCommandTestCase(unittest.TestCase): + + @staticmethod + def _run_socket_server(port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('localhost', port)) + sock.listen(1) + conn = None + while not conn: + conn, _ = sock.accept() + sock.close() + + @staticmethod + def _terminate_server(socket_server): + # Wait until the socket server closes the open port. + time.sleep(1) + if socket_server and socket_server.is_alive(): + socket_server.terminate() + + def test_send_command(self): + port = 47001 + + socket_server = multiprocessing.Process( + name='run_socket_server', + target=SendSocketCommandTestCase._run_socket_server, + args=(port, )).start() + + self.addCleanup(self._terminate_server, socket_server) + + # Wait until the socket is open. + time.sleep(0.5) + self.assertEqual( + 0, utils.send_socket_command('localhost', port, 'test_command')) diff --git a/yardstick/tests/integration/dummy-scenario-heat-context.yaml b/yardstick/tests/integration/dummy-scenario-heat-context.yaml new file mode 100644 index 000000000..45a39951a --- /dev/null +++ b/yardstick/tests/integration/dummy-scenario-heat-context.yaml @@ -0,0 +1,38 @@ +############################################################################## +# Copyright (c) 2018 Intel +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +{% set context_name = context_name or "demo" %} +--- +# Sample Heat context config with Dummy context + +schema: "yardstick:task:0.1" + +scenarios: +- + type: Dummy + + runner: + type: Duration + duration: 5 + interval: 1 + +context: + name: {{ context_name }} + image: yardstick-image + flavor: yardstick-flavor + user: ubuntu + + servers: + athena: + name: athena + ares: + name: ares + + networks: + test: + name: test diff --git a/yardstick/tests/unit/__init__.py b/yardstick/tests/unit/__init__.py index a468b272b..c05f91c81 100644 --- a/yardstick/tests/unit/__init__.py +++ b/yardstick/tests/unit/__init__.py @@ -12,65 +12,12 @@ # See the License for the specific language governing permissions and
# limitations under the License.
-from __future__ import absolute_import
+import sys
+
import mock
+from yardstick import tests
+
-STL_MOCKS = {
- 'trex_stl_lib': mock.MagicMock(),
- 'trex_stl_lib.base64': mock.MagicMock(),
- 'trex_stl_lib.binascii': mock.MagicMock(),
- 'trex_stl_lib.collections': mock.MagicMock(),
- 'trex_stl_lib.copy': mock.MagicMock(),
- 'trex_stl_lib.datetime': mock.MagicMock(),
- 'trex_stl_lib.functools': mock.MagicMock(),
- 'trex_stl_lib.imp': mock.MagicMock(),
- 'trex_stl_lib.inspect': mock.MagicMock(),
- 'trex_stl_lib.json': mock.MagicMock(),
- 'trex_stl_lib.linecache': mock.MagicMock(),
- 'trex_stl_lib.math': mock.MagicMock(),
- 'trex_stl_lib.os': mock.MagicMock(),
- 'trex_stl_lib.platform': mock.MagicMock(),
- 'trex_stl_lib.pprint': mock.MagicMock(),
- 'trex_stl_lib.random': mock.MagicMock(),
- 'trex_stl_lib.re': mock.MagicMock(),
- 'trex_stl_lib.scapy': mock.MagicMock(),
- 'trex_stl_lib.socket': mock.MagicMock(),
- 'trex_stl_lib.string': mock.MagicMock(),
- 'trex_stl_lib.struct': mock.MagicMock(),
- 'trex_stl_lib.sys': mock.MagicMock(),
- 'trex_stl_lib.threading': mock.MagicMock(),
- 'trex_stl_lib.time': mock.MagicMock(),
- 'trex_stl_lib.traceback': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_async_client': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_client': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_exceptions': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_ext': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_jsonrpc_client': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_packet_builder_interface': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_packet_builder_scapy': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_port': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_stats': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_streams': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_types': mock.MagicMock(),
- 'trex_stl_lib.types': mock.MagicMock(),
- 'trex_stl_lib.utils': mock.MagicMock(),
- 'trex_stl_lib.utils.argparse': mock.MagicMock(),
- 'trex_stl_lib.utils.collections': mock.MagicMock(),
- 'trex_stl_lib.utils.common': mock.MagicMock(),
- 'trex_stl_lib.utils.json': mock.MagicMock(),
- 'trex_stl_lib.utils.os': mock.MagicMock(),
- 'trex_stl_lib.utils.parsing_opts': mock.MagicMock(),
- 'trex_stl_lib.utils.pwd': mock.MagicMock(),
- 'trex_stl_lib.utils.random': mock.MagicMock(),
- 'trex_stl_lib.utils.re': mock.MagicMock(),
- 'trex_stl_lib.utils.string': mock.MagicMock(),
- 'trex_stl_lib.utils.sys': mock.MagicMock(),
- 'trex_stl_lib.utils.text_opts': mock.MagicMock(),
- 'trex_stl_lib.utils.text_tables': mock.MagicMock(),
- 'trex_stl_lib.utils.texttable': mock.MagicMock(),
- 'trex_stl_lib.warnings': mock.MagicMock(),
- 'trex_stl_lib.yaml': mock.MagicMock(),
- 'trex_stl_lib.zlib': mock.MagicMock(),
- 'trex_stl_lib.zmq': mock.MagicMock(),
-}
+mock_stl = mock.patch.dict(sys.modules, tests.STL_MOCKS)
+mock_stl.start()
diff --git a/yardstick/tests/unit/apiserver/resources/test_env_action.py b/yardstick/tests/unit/apiserver/resources/test_env_action.py index b7bfe294d..657841669 100644 --- a/yardstick/tests/unit/apiserver/resources/test_env_action.py +++ b/yardstick/tests/unit/apiserver/resources/test_env_action.py @@ -32,12 +32,4 @@ class EnvTestCase(APITestCase): time.sleep(0) - self.assertTrue(u'status' in resp) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + self.assertIn(u'status', resp) diff --git a/yardstick/tests/unit/apiserver/resources/v1/__init__.py b/yardstick/tests/unit/apiserver/resources/v1/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/apiserver/resources/v1/__init__.py diff --git a/yardstick/tests/unit/apiserver/resources/v1/test_testsuites.py b/yardstick/tests/unit/apiserver/resources/v1/test_testsuites.py new file mode 100644 index 000000000..85c045f44 --- /dev/null +++ b/yardstick/tests/unit/apiserver/resources/v1/test_testsuites.py @@ -0,0 +1,35 @@ +############################################################################## +# Copyright (c) 2018 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import mock + +import unittest + +from yardstick.tests.unit.apiserver import APITestCase +from api.utils.thread import TaskThread + + +class TestsuiteTestCase(APITestCase): + + def test_run_test_suite(self): + if self.app is None: + unittest.skip('host config error') + return + + TaskThread.start = mock.MagicMock() + + url = 'yardstick/testsuites/action' + data = { + 'action': 'run_test_suite', + 'args': { + 'opts': {}, + 'testsuite': 'opnfv_smoke' + } + } + resp = self._post(url, data) + self.assertEqual(resp.get('status'), 1) diff --git a/yardstick/tests/unit/apiserver/resources/v2/__init__.py b/yardstick/tests/unit/apiserver/resources/v2/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/apiserver/resources/v2/__init__.py diff --git a/yardstick/tests/unit/apiserver/resources/v2/test_images.py b/yardstick/tests/unit/apiserver/resources/v2/test_images.py new file mode 100644 index 000000000..ab131eec5 --- /dev/null +++ b/yardstick/tests/unit/apiserver/resources/v2/test_images.py @@ -0,0 +1,46 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import mock + +import unittest + +from yardstick.tests.unit.apiserver import APITestCase +from api.resources.v2.images import format_image_info + + +class V2ImagesTestCase(APITestCase): + @mock.patch('yardstick.common.openstack_utils.list_images') + @mock.patch('yardstick.common.utils.source_env') + def test_get(self, _, mock_list_images): + if self.app is None: + unittest.skip('host config error') + return + + single_image = mock.MagicMock() + single_image.name = 'yardstick-image' + single_image.size = 16384 + single_image.status = 'active' + single_image.updated_at = '2018-04-08' + + mock_list_images.return_value = [single_image] + url = 'api/v2/yardstick/images' + resp = self._get(url) + self.assertEqual(resp.get('status'), 1) + + +class FormatImageInfoTestCase(unittest.TestCase): + def test_format_image_info(self): + image = mock.MagicMock() + image.name = 'yardstick-image' + image.size = 1048576 + image.status = 'active' + image.updated_at = '2018-04-08' + + image_dict = format_image_info(image) + self.assertEqual(image_dict.get('size'), 1) diff --git a/yardstick/tests/unit/apiserver/utils/__init__.py b/yardstick/tests/unit/apiserver/utils/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/apiserver/utils/__init__.py diff --git a/yardstick/tests/unit/apiserver/utils/test_influx.py b/yardstick/tests/unit/apiserver/utils/test_influx.py index 883608bb2..3a97ff292 100644 --- a/yardstick/tests/unit/apiserver/utils/test_influx.py +++ b/yardstick/tests/unit/apiserver/utils/test_influx.py @@ -1,33 +1,56 @@ ############################################################################## # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# Copyright (c) 2019 Intel Corporation. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -import unittest + +from influxdb import client as influxdb_client import mock +from six.moves import configparser from api.utils import influx -from six.moves import configparser as ConfigParser +from yardstick.common import constants +from yardstick.common import exceptions +from yardstick import dispatcher +from yardstick.tests.unit import base + + +class GetDataDbClientTestCase(base.BaseUnitTestCase): + @mock.patch.object(influx, '_get_influxdb_client', + return_value='fake_client') + @mock.patch.object(influx.ConfigParser, 'ConfigParser') + def test_get_data_db_client(self, mock_parser, mock_get_client): + _mock_parser = mock.Mock() + mock_parser.return_value = _mock_parser -class GetDataDbClientTestCase(unittest.TestCase): + self.assertEqual('fake_client', influx.get_data_db_client()) + _mock_parser.read.assert_called_once_with(constants.CONF_FILE) + mock_get_client.assert_called_once_with(_mock_parser, None) - @mock.patch('api.utils.influx.ConfigParser') - def test_get_data_db_client_dispatcher_not_influxdb(self, mock_parser): - mock_parser.ConfigParser().get.return_value = 'file' - # reset exception to avoid - # TypeError: catching classes that do not inherit from BaseException - mock_parser.NoOptionError = ConfigParser.NoOptionError - try: + @mock.patch.object(influx.logger, 'error') + @mock.patch.object(influx, '_get_influxdb_client', + return_value='fake_client') + @mock.patch.object(influx.ConfigParser, 'ConfigParser') + def test_get_data_db_client_parsing_error( + self, mock_parser, mock_get_client, *args): + _mock_parser = mock.Mock() + mock_parser.return_value = _mock_parser + mock_parser.NoOptionError = configparser.NoOptionError + mock_get_client.side_effect = configparser.NoOptionError('option', + 'section') + with self.assertRaises(configparser.NoOptionError): influx.get_data_db_client() - except Exception as e: # pylint: disable=broad-except - self.assertIsInstance(e, RuntimeError) + _mock_parser.read.assert_called_once_with(constants.CONF_FILE) + mock_get_client.assert_called_once_with(_mock_parser, None) -class GetIpTestCase(unittest.TestCase): + +class GetIpTestCase(base.BaseUnitTestCase): def test_get_url(self): url = 'http://localhost:8086/hello' @@ -37,24 +60,32 @@ class GetIpTestCase(unittest.TestCase): self.assertEqual(result, output) -class QueryTestCase(unittest.TestCase): - - @mock.patch('api.utils.influx.ConfigParser') - def test_query_dispatcher_not_influxdb(self, mock_parser): - mock_parser.ConfigParser().get.return_value = 'file' - # reset exception to avoid - # TypeError: catching classes that do not inherit from BaseException - mock_parser.NoOptionError = ConfigParser.NoOptionError - try: - sql = 'select * form tasklist' - influx.query(sql) - except Exception as e: # pylint: disable=broad-except - self.assertIsInstance(e, RuntimeError) +class GetInfluxdbTestCase(base.BaseUnitTestCase): + @mock.patch.object(influxdb_client, 'InfluxDBClient', + return_value='idb_client') + @mock.patch.object(influx, '_get_ip', return_value='fake_ip') + def test_get_influxdb_client(self, mock_get_ip, mock_client): + mock_parser = mock.Mock() + mock_parser.get.side_effect = [dispatcher.INFLUXDB, 'target', 'user', + 'pass', 'db_name'] -def main(): - unittest.main() + self.assertEqual('idb_client', + influx._get_influxdb_client(mock_parser)) + mock_client.assert_called_once_with('fake_ip', constants.INFLUXDB_PORT, + 'user', 'pass', 'db_name') + mock_get_ip.assert_called_once_with('target') + mock_parser.get.assert_has_calls([ + mock.call('DEFAULT', 'dispatcher'), + mock.call('dispatcher_influxdb', 'target'), + mock.call('dispatcher_influxdb', 'username'), + mock.call('dispatcher_influxdb', 'password'), + mock.call('dispatcher_influxdb', 'db_name')]) + def test_get_influxdb_client_no_influxdb_client(self): + mock_parser = mock.Mock() + mock_parser.get.return_value = dispatcher.FILE -if __name__ == '__main__': - main() + with self.assertRaises(exceptions.InfluxDBConfigurationMissing): + influx._get_influxdb_client(mock_parser) + mock_parser.get.assert_called_once_with('DEFAULT', 'dispatcher') diff --git a/yardstick/tests/unit/base.py b/yardstick/tests/unit/base.py new file mode 100644 index 000000000..b943efc2b --- /dev/null +++ b/yardstick/tests/unit/base.py @@ -0,0 +1,23 @@ +# Copyright 2018 Intel Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import abc + +import six +import unittest + + +@six.add_metaclass(abc.ABCMeta) +class BaseUnitTestCase(unittest.TestCase): + """Base class for unit tests""" diff --git a/yardstick/tests/unit/benchmark/__init__.py b/yardstick/tests/unit/benchmark/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/__init__.py diff --git a/yardstick/tests/unit/benchmark/contexts/__init__.py b/yardstick/tests/unit/benchmark/contexts/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/__init__.py diff --git a/yardstick/tests/unit/benchmark/contexts/nodes_duplicate_sample.yaml b/yardstick/tests/unit/benchmark/contexts/nodes_duplicate_sample.yaml new file mode 100644 index 000000000..dbdd3700d --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/nodes_duplicate_sample.yaml @@ -0,0 +1,21 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +nodes: +- + name: node1 + role: Controller + ip: 10.229.47.137 + user: root + key_filename: /root/.yardstick_key +- + name: node1 + role: Controller + ip: 10.229.47.138 + user: root + key_filename: /root/.yardstick_key diff --git a/yardstick/tests/unit/benchmark/contexts/nodes_duplicate_sample_new.yaml b/yardstick/tests/unit/benchmark/contexts/nodes_duplicate_sample_new.yaml new file mode 100644 index 000000000..306915ca1 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/nodes_duplicate_sample_new.yaml @@ -0,0 +1,32 @@ +nodes: +- + name: sriov + role: Sriov1 + ip: 10.123.123.122 + user: root + auth_type: password + password: password + vf_macs: + - "00:00:00:00:00:00" + - "00:00:00:00:00:00" + phy_ports: # Physical ports to configure sriov + - "0000:06:00.0" + - "0000:06:00.1" + phy_driver: i40e # kernel driver + images: "/var/lib/libvirt/images/ubuntu1.img" +- + name: sriov + role: Sriov1 + ip: 10.123.123.111 + user: root + auth_type: password + password: password + vf_macs: + - "00:00:00:00:00:00" + - "00:00:00:00:00:00" + phy_ports: # Physical ports to configure sriov + - "0000:06:00.0" + - "0000:06:00.1" + phy_driver: i40e # kernel driver + images: "/var/lib/libvirt/images/ubuntu1.img" + diff --git a/yardstick/tests/unit/benchmark/contexts/nodes_duplicate_sample_ovs.yaml b/yardstick/tests/unit/benchmark/contexts/nodes_duplicate_sample_ovs.yaml new file mode 100644 index 000000000..65449c91c --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/nodes_duplicate_sample_ovs.yaml @@ -0,0 +1,63 @@ +# Copyright (c) 2016 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +nodes: +- + name: ovs + role: test + ip: 10.223.197.222 + user: root + auth_type: password + password: intel123 + vpath: "/usr/local/" + vports: + - dpdkvhostuser0 + - dpdkvhostuser1 + vports_mac: + - "00:00:00:00:00:03" + - "00:00:00:00:00:04" + phy_ports: # Physical ports to configure ovs + - "0000:06:00.0" + - "0000:06:00.1" + flow: + - ovs-ofctl add-flow br0 in_port=1,action=output:3 + - ovs-ofctl add-flow br0 in_port=3,action=output:1 + - ovs-ofctl add-flow br0 in_port=4,action=output:2 + - ovs-ofctl add-flow br0 in_port=2,action=output:4 + phy_driver: i40e # kernel driver + images: "/var/lib/libvirt/images/ubuntu1.img" +- + name: ovs + role: test + ip: 10.223.197.112 + user: root + auth_type: password + password: intel123 + vpath: "/usr/local/" + vports: + - dpdkvhostuser0 + - dpdkvhostuser1 + vports_mac: + - "00:00:00:00:00:03" + - "00:00:00:00:00:04" + phy_ports: # Physical ports to configure ovs + - "0000:06:00.0" + - "0000:06:00.1" + flow: + - ovs-ofctl add-flow br0 in_port=1,action=output:3 + - ovs-ofctl add-flow br0 in_port=3,action=output:1 + - ovs-ofctl add-flow br0 in_port=4,action=output:2 + - ovs-ofctl add-flow br0 in_port=2,action=output:4 + phy_driver: i40e # kernel driver + images: "/var/lib/libvirt/images/ubuntu1.img" + diff --git a/yardstick/tests/unit/benchmark/contexts/nodes_sample.yaml b/yardstick/tests/unit/benchmark/contexts/nodes_sample.yaml new file mode 100644 index 000000000..8d50c3aea --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/nodes_sample.yaml @@ -0,0 +1,33 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +nodes: +- + name: node1 + role: Controller + ip: 10.229.47.137 + user: root + key_filename: /root/.yardstick_key +- + name: node2 + role: Controller + ip: 10.229.47.138 + user: root + key_filename: /root/.yardstick_key +- + name: node3 + role: Compute + ip: 10.229.47.139 + user: root + key_filename: /root/.yardstick_key +- + name: node4 + role: Baremetal + ip: 10.229.47.140 + user: root + key_filename: /root/.yardstick_key diff --git a/yardstick/tests/unit/benchmark/contexts/nodes_sample_new.yaml b/yardstick/tests/unit/benchmark/contexts/nodes_sample_new.yaml new file mode 100644 index 000000000..a400bec03 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/nodes_sample_new.yaml @@ -0,0 +1,96 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +nodes: +- + name: trafficgen_1 + role: TrafficGen + ip: 10.123.123.123 + user: root + auth_type: password + password: password + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:03:00.0" + driver: ixgbe + dpdk_port_num: 0 + local_ip: "152.16.100.20" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:00" + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:03:00.1" + driver: ixgbe + dpdk_port_num: 1 + local_ip: "152.16.100.21" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:00" +- + name: sriov + role: Sriov + ip: 10.123.123.122 + user: root + auth_type: password + password: password + vf_macs: + - "00:00:00:00:00:00" + - "00:00:00:00:00:00" + phy_ports: # Physical ports to configure sriov + - "0000:06:00.0" + - "0000:06:00.1" + phy_driver: i40e # kernel driver + images: "/var/lib/libvirt/images/ubuntu1.img" + +- + name: vnf + role: vnf + ip: 10.123.123.121 + user: root + auth_type: password + password: password + host: 10.123.123.121 #BM host == ip, SRIOV & ovs-dpdk host == compute node. + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:06:00.0" + driver: i40e + dpdk_port_num: 0 + local_ip: "152.16.100.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:00" + + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:06:00.1" + driver: i40e + dpdk_port_num: 1 + local_ip: "152.16.40.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:00" + routing_table: + - network: "152.16.100.20" + netmask: "255.255.255.0" + gateway: "152.16.100.20" + if: "xe0" + - network: "152.16.40.20" + netmask: "255.255.255.0" + gateway: "152.16.40.20" + if: "xe1" + nd_route_tbl: + - network: "0064:ff9b:0:0:0:0:9810:6414" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:6414" + if: "xe0" + - network: "0064:ff9b:0:0:0:0:9810:2814" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:2814" + if: "xe1" + diff --git a/yardstick/tests/unit/benchmark/contexts/nodes_sample_new_sriov.yaml b/yardstick/tests/unit/benchmark/contexts/nodes_sample_new_sriov.yaml new file mode 100644 index 000000000..55ff2e778 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/nodes_sample_new_sriov.yaml @@ -0,0 +1,82 @@ +nodes: +- + name: trafficgen_1 + role: TrafficGen + ip: 10.123.123.123 + user: root + auth_type: password + password: password + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:03:00.0" + driver: ixgbe + dpdk_port_num: 0 + local_ip: "152.16.100.20" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:00" + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:03:00.1" + driver: ixgbe + dpdk_port_num: 1 + local_ip: "152.16.100.21" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:00" +- + name: sriov + role: Sriov1 + ip: 10.123.123.122 + user: root + auth_type: password + password: password + vf_macs: + - "00:00:00:00:00:00" + - "00:00:00:00:00:00" + phy_ports: # Physical ports to configure sriov + - "0000:06:00.0" + - "0000:06:00.1" + phy_driver: i40e # kernel driver + images: "/var/lib/libvirt/images/ubuntu1.img" + +- + name: vnf + role: vnf + ip: 10.123.123.121 + user: root + auth_type: password + password: password + host: 10.123.123.121 #BM host == ip, SRIOV & ovs-dpdk host == compute node. + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:06:00.0" + driver: i40e + dpdk_port_num: 0 + local_ip: "152.16.100.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:00" + + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:06:00.1" + driver: i40e + dpdk_port_num: 1 + local_ip: "152.16.40.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:00" + routing_table: + - network: "152.16.100.20" + netmask: "255.255.255.0" + gateway: "152.16.100.20" + if: "xe0" + - network: "152.16.40.20" + netmask: "255.255.255.0" + gateway: "152.16.40.20" + if: "xe1" + nd_route_tbl: + - network: "0064:ff9b:0:0:0:0:9810:6414" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:6414" + if: "xe0" + - network: "0064:ff9b:0:0:0:0:9810:2814" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:2814" + if: "xe1" + diff --git a/yardstick/tests/unit/benchmark/contexts/nodes_sample_ovs.yaml b/yardstick/tests/unit/benchmark/contexts/nodes_sample_ovs.yaml new file mode 100644 index 000000000..b1da1ea9f --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/nodes_sample_ovs.yaml @@ -0,0 +1,104 @@ +# Copyright (c) 2016 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +nodes: +- + name: trafficgen_1 + role: TrafficGen + ip: 10.223.197.182 + user: root + auth_type: password + password: intel123 + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:03:00.0" + driver: ixgbe + dpdk_port_num: 0 + local_ip: "152.16.100.20" + netmask: "255.255.255.0" + local_mac: "90:e2:ba:77:ce:68" + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:03:00.1" + driver: ixgbe + dpdk_port_num: 1 + local_ip: "152.16.100.21" + netmask: "255.255.255.0" + local_mac: "90:e2:ba:77:ce:69" +- + name: ovs + role: Ovsdpdk + ip: 10.223.197.222 + user: root + auth_type: password + password: intel123 + vpath: "/usr/local/" + vports: + - dpdkvhostuser0 + - dpdkvhostuser1 + vports_mac: + - "00:00:00:00:00:03" + - "00:00:00:00:00:04" + phy_ports: # Physical ports to configure ovs + - "0000:06:00.0" + - "0000:06:00.1" + flow: + - ovs-ofctl add-flow br0 in_port=1,action=output:3 + - ovs-ofctl add-flow br0 in_port=3,action=output:1 + - ovs-ofctl add-flow br0 in_port=4,action=output:2 + - ovs-ofctl add-flow br0 in_port=2,action=output:4 + phy_driver: i40e # kernel driver + images: "/var/lib/libvirt/images/ubuntu1.img" + +- + name: vnf + role: vnf + ip: 10.223.197.155 + user: root + auth_type: password + password: intel123 + host: 10.223.197.140 + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:00:04.0" + driver: virtio-pci + dpdk_port_num: 0 + local_ip: "152.16.100.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:03" + + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:00:05.0" + driver: virtio-pci + dpdk_port_num: 1 + local_ip: "152.16.40.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:04" + routing_table: + - network: "152.16.100.20" + netmask: "255.255.255.0" + gateway: "152.16.100.20" + if: "xe0" + - network: "152.16.40.20" + netmask: "255.255.255.0" + gateway: "152.16.40.20" + if: "xe1" + nd_route_tbl: + - network: "0064:ff9b:0:0:0:0:9810:6414" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:6414" + if: "xe0" + - network: "0064:ff9b:0:0:0:0:9810:2814" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:2814" + if: "xe1" diff --git a/yardstick/tests/unit/benchmark/contexts/nodes_sample_ovsdpdk.yaml b/yardstick/tests/unit/benchmark/contexts/nodes_sample_ovsdpdk.yaml new file mode 100644 index 000000000..c02849a05 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/nodes_sample_ovsdpdk.yaml @@ -0,0 +1,104 @@ +# Copyright (c) 2016 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +nodes: +- + name: trafficgen_1 + role: TrafficGen + ip: 10.223.197.182 + user: root + auth_type: password + password: intel123 + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:03:00.0" + driver: ixgbe + dpdk_port_num: 0 + local_ip: "152.16.100.20" + netmask: "255.255.255.0" + local_mac: "90:e2:ba:77:ce:68" + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:03:00.1" + driver: ixgbe + dpdk_port_num: 1 + local_ip: "152.16.100.21" + netmask: "255.255.255.0" + local_mac: "90:e2:ba:77:ce:69" +- + name: ovs + role: Ovsdpdk1 + ip: 10.223.197.222 + user: root + auth_type: password + password: intel123 + vpath: "/usr/local/" + vports: + - dpdkvhostuser0 + - dpdkvhostuser1 + vports_mac: + - "00:00:00:00:00:03" + - "00:00:00:00:00:04" + phy_ports: # Physical ports to configure ovs + - "0000:06:00.0" + - "0000:06:00.1" + flow: + - ovs-ofctl add-flow br0 in_port=1,action=output:3 + - ovs-ofctl add-flow br0 in_port=3,action=output:1 + - ovs-ofctl add-flow br0 in_port=4,action=output:2 + - ovs-ofctl add-flow br0 in_port=2,action=output:4 + phy_driver: i40e # kernel driver + images: "/var/lib/libvirt/images/ubuntu1.img" + +- + name: vnf + role: vnf + ip: 10.223.197.155 + user: root + auth_type: password + password: intel123 + host: 10.223.197.140 + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:00:04.0" + driver: virtio-pci + dpdk_port_num: 0 + local_ip: "152.16.100.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:03" + + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:00:05.0" + driver: virtio-pci + dpdk_port_num: 1 + local_ip: "152.16.40.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:04" + routing_table: + - network: "152.16.100.20" + netmask: "255.255.255.0" + gateway: "152.16.100.20" + if: "xe0" + - network: "152.16.40.20" + netmask: "255.255.255.0" + gateway: "152.16.40.20" + if: "xe1" + nd_route_tbl: + - network: "0064:ff9b:0:0:0:0:9810:6414" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:6414" + if: "xe0" + - network: "0064:ff9b:0:0:0:0:9810:2814" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:2814" + if: "xe1" diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/__init__.py b/yardstick/tests/unit/benchmark/contexts/standalone/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/standalone/__init__.py diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/nodes_duplicate_sample.yaml b/yardstick/tests/unit/benchmark/contexts/standalone/nodes_duplicate_sample.yaml new file mode 100644 index 000000000..2e501a6af --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/standalone/nodes_duplicate_sample.yaml @@ -0,0 +1,37 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +nodes: +- + name: node1 + role: Controller + ip: 10.229.47.137 + user: root + key_filename: /root/.yardstick_key +- + name: node1 + role: Controller + ip: 10.229.47.138 + user: root + key_filename: /root/.yardstick_key + +- + name: node5 + role: Sriov + ip: 10.229.47.140 + user: root + password: password + key_filename: /root/.yardstick_key + +- + name: node5 + role: OvsDpdk + ip: 10.229.47.140 + user: root + password: password + key_filename: /root/.yardstick_key diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/nodes_ovs_dpdk_sample.yaml b/yardstick/tests/unit/benchmark/contexts/standalone/nodes_ovs_dpdk_sample.yaml new file mode 100644 index 000000000..0f51dbe63 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/standalone/nodes_ovs_dpdk_sample.yaml @@ -0,0 +1,40 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +nodes: +- + name: node1 + role: Controller + ip: 10.229.47.137 + user: root + key_filename: /root/.yardstick_key +- + name: node2 + role: Controller + ip: 10.229.47.138 + user: root + key_filename: /root/.yardstick_key +- + name: node3 + role: Compute + ip: 10.229.47.139 + user: root + key_filename: /root/.yardstick_key +- + name: node4 + role: Baremetal + ip: 10.229.47.140 + user: root + key_filename: /root/.yardstick_key +- + name: node5 + role: OvsDpdk + ip: 10.229.47.140 + user: root + password: password + key_filename: /root/.yardstick_key diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/nodes_sample.yaml b/yardstick/tests/unit/benchmark/contexts/standalone/nodes_sample.yaml new file mode 100644 index 000000000..8d50c3aea --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/standalone/nodes_sample.yaml @@ -0,0 +1,33 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +nodes: +- + name: node1 + role: Controller + ip: 10.229.47.137 + user: root + key_filename: /root/.yardstick_key +- + name: node2 + role: Controller + ip: 10.229.47.138 + user: root + key_filename: /root/.yardstick_key +- + name: node3 + role: Compute + ip: 10.229.47.139 + user: root + key_filename: /root/.yardstick_key +- + name: node4 + role: Baremetal + ip: 10.229.47.140 + user: root + key_filename: /root/.yardstick_key diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/nodes_sriov_sample.yaml b/yardstick/tests/unit/benchmark/contexts/standalone/nodes_sriov_sample.yaml new file mode 100644 index 000000000..1c43b8725 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/standalone/nodes_sriov_sample.yaml @@ -0,0 +1,40 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +nodes: +- + name: node1 + role: Controller + ip: 10.229.47.137 + user: root + key_filename: /root/.yardstick_key +- + name: node2 + role: Controller + ip: 10.229.47.138 + user: root + key_filename: /root/.yardstick_key +- + name: node3 + role: Compute + ip: 10.229.47.139 + user: root + key_filename: /root/.yardstick_key +- + name: node4 + role: Baremetal + ip: 10.229.47.140 + user: root + key_filename: /root/.yardstick_key +- + name: node5 + role: Sriov + ip: 10.229.47.140 + user: root + password: password + key_filename: /root/.yardstick_key diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py b/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py new file mode 100644 index 000000000..e76a3ca27 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py @@ -0,0 +1,640 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import os +import uuid + +import mock +import netaddr +import unittest +from xml.etree import ElementTree + +from yardstick import ssh +from yardstick.benchmark.contexts.standalone import model +from yardstick.common import exceptions +from yardstick import constants +from yardstick.network_services import utils + + +XML_SAMPLE = """<?xml version="1.0"?> +<domain type="kvm"> + <devices> + </devices> +</domain> +""" + +XML_SAMPLE_INTERFACE = """<?xml version="1.0"?> +<domain type="kvm"> + <devices> + <interface> + </interface> + </devices> +</domain> +""" + + +class ModelLibvirtTestCase(unittest.TestCase): + + XML_STR = model.VM_TEMPLATE.format( + vm_name="vm_name", + random_uuid=uuid.uuid4(), + mac_addr="00:01:02:03:04:05", + memory=2048, vcpu=2, cpu=2, + numa_cpus=0 - 10, + socket=1, threads=1, + vm_image="/var/lib/libvirt/images/yardstick-nsb-image.img", + cpuset=2 - 10, cputune='', machine='pc') + + def setUp(self): + self.pci_address_str = '0001:04:03.2' + self.pci_address = utils.PciAddress(self.pci_address_str) + self.mac = '00:00:00:00:00:01' + self._mock_ssh = mock.Mock() + self.mock_ssh = self._mock_ssh.start() + self.addCleanup(self._cleanup) + + def _cleanup(self): + self._mock_ssh.stop() + + # TODO: Remove mocking of yardstick.ssh.SSH (here and elsewhere) + # In this case, we are mocking a param to be passed into other methods + # It can be a generic Mock() with return values set for the right methods + def test_check_if_vm_exists_and_delete(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = mock.Mock(return_value=(0, "a", "")) + ssh.return_value = ssh_mock + # NOTE(ralonsoh): this test doesn't cover function execution. + model.Libvirt.check_if_vm_exists_and_delete('vm-0', ssh_mock) + + def test_virsh_create_vm(self): + self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0)) + model.Libvirt.virsh_create_vm(self.mock_ssh, 'vm-0') + self.mock_ssh.execute.assert_called_once_with('virsh create vm-0') + + def test_virsh_create_vm_error(self): + self.mock_ssh.execute = mock.Mock(return_value=(1, 0, 'error_create')) + with self.assertRaises(exceptions.LibvirtCreateError) as exc: + model.Libvirt.virsh_create_vm(self.mock_ssh, 'vm-0') + self.assertEqual('Error creating the virtual machine. Error: ' + 'error_create.', str(exc.exception)) + self.mock_ssh.execute.assert_called_once_with('virsh create vm-0') + + def test_virsh_destroy_vm(self): + self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0)) + model.Libvirt.virsh_destroy_vm('vm-0', self.mock_ssh) + self.mock_ssh.execute.assert_called_once_with('virsh destroy vm-0') + + @mock.patch.object(model, 'LOG') + def test_virsh_destroy_vm_error(self, mock_logger): + self.mock_ssh.execute = mock.Mock(return_value=(1, 0, 'error_destroy')) + mock_logger.warning = mock.Mock() + model.Libvirt.virsh_destroy_vm('vm-0', self.mock_ssh) + mock_logger.warning.assert_called_once_with( + 'Error destroying VM %s. Error: %s', 'vm-0', 'error_destroy') + self.mock_ssh.execute.assert_called_once_with('virsh destroy vm-0') + + def test_add_interface_address(self): + xml = ElementTree.ElementTree( + element=ElementTree.fromstring(XML_SAMPLE_INTERFACE)) + interface = xml.find('devices').find('interface') + result = model.Libvirt._add_interface_address(interface, self.pci_address) + self.assertEqual('pci', result.get('type')) + self.assertEqual('0x{}'.format(self.pci_address.domain), + result.get('domain')) + self.assertEqual('0x{}'.format(self.pci_address.bus), + result.get('bus')) + self.assertEqual('0x{}'.format(self.pci_address.slot), + result.get('slot')) + self.assertEqual('0x{}'.format(self.pci_address.function), + result.get('function')) + + def test_add_ovs_interfaces(self): + xml_input = copy.deepcopy(XML_SAMPLE) + xml_output = model.Libvirt.add_ovs_interface( + '/usr/local', 0, self.pci_address_str, self.mac, xml_input, 4) + + root = ElementTree.fromstring(xml_output) + et_out = ElementTree.ElementTree(element=root) + interface = et_out.find('devices').find('interface') + self.assertEqual('vhostuser', interface.get('type')) + mac = interface.find('mac') + self.assertEqual(self.mac, mac.get('address')) + source = interface.find('source') + self.assertEqual('unix', source.get('type')) + self.assertEqual('/usr/local/var/run/openvswitch/dpdkvhostuser0', + source.get('path')) + self.assertEqual('client', source.get('mode')) + _model = interface.find('model') + self.assertEqual('virtio', _model.get('type')) + driver = interface.find('driver') + self.assertEqual('4', driver.get('queues')) + host = driver.find('host') + self.assertEqual('off', host.get('mrg_rxbuf')) + self.assertIsNotNone(interface.find('address')) + + def test_add_sriov_interfaces(self): + xml_input = copy.deepcopy(XML_SAMPLE) + vm_pci = '0001:05:04.2' + xml_output = model.Libvirt.add_sriov_interfaces( + vm_pci, self.pci_address_str, self.mac, xml_input) + root = ElementTree.fromstring(xml_output) + et_out = ElementTree.ElementTree(element=root) + interface = et_out.find('devices').find('interface') + self.assertEqual('yes', interface.get('managed')) + self.assertEqual('hostdev', interface.get('type')) + mac = interface.find('mac') + self.assertEqual(self.mac, mac.get('address')) + source = interface.find('source') + source_address = source.find('address') + self.assertIsNotNone(source.find('address')) + + self.assertEqual('pci', source_address.get('type')) + self.assertEqual('0x' + self.pci_address_str.split(':')[0], + source_address.get('domain')) + self.assertEqual('0x' + self.pci_address_str.split(':')[1], + source_address.get('bus')) + self.assertEqual('0x' + self.pci_address_str.split(':')[2].split('.')[0], + source_address.get('slot')) + self.assertEqual('0x' + self.pci_address_str.split(':')[2].split('.')[1], + source_address.get('function')) + + interface_address = interface.find('address') + self.assertEqual('pci', interface_address.get('type')) + self.assertEqual('0x' + vm_pci.split(':')[0], + interface_address.get('domain')) + self.assertEqual('0x' + vm_pci.split(':')[1], + interface_address.get('bus')) + self.assertEqual('0x' + vm_pci.split(':')[2].split('.')[0], + interface_address.get('slot')) + self.assertEqual('0x' + vm_pci.split(':')[2].split('.')[1], + interface_address.get('function')) + + def test_add_cdrom(self): + xml_input = copy.deepcopy(XML_SAMPLE) + xml_output = model.Libvirt.add_cdrom('/var/lib/libvirt/images/data.img', xml_input) + + root = ElementTree.fromstring(xml_output) + et_out = ElementTree.ElementTree(element=root) + disk = et_out.find('devices').find('disk') + self.assertEqual('file', disk.get('type')) + self.assertEqual('cdrom', disk.get('device')) + driver = disk.find('driver') + self.assertEqual('qemu', driver.get('name')) + self.assertEqual('raw', driver.get('type')) + source = disk.find('source') + self.assertEqual('/var/lib/libvirt/images/data.img', source.get('file')) + target = disk.find('target') + self.assertEqual('hdb', target.get('dev')) + self.assertIsNotNone(disk.find('readonly')) + + def test_gen_cdrom_image(self): + self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0)) + root = ElementTree.fromstring(self.XML_STR) + hostname = root.find('name').text + meta_data = "/tmp/meta-data" + user_data = "/tmp/user-data" + network_data = "/tmp/network-config" + file_path = "/tmp/cdrom-0.img" + key_filename = "id_rsa" + pub_key_str = "KEY" + user = 'root' + mac = "00:11:22:33:44:55" + ip = "1.1.1.7/24" + user_config = [" - name: {user_name}", + " ssh_authorized_keys:", + " - {pub_key_str}"] + + user_conf = os.linesep.join(user_config).format(pub_key_str=pub_key_str, user_name=user) + with mock.patch('six.moves.builtins.open', mock.mock_open(read_data=pub_key_str), + create=True) as mock_file: + with open(key_filename, "r") as h: + result = h.read() + model.Libvirt.gen_cdrom_image(self.mock_ssh, file_path, hostname, user, key_filename, + mac, ip) + mock_file.assert_called_with(".".join([key_filename, "pub"]), "r") + self.assertEqual(result, pub_key_str) + + self.mock_ssh.execute.assert_has_calls([ + mock.call("touch %s" % meta_data), + mock.call(model.USER_DATA_TEMPLATE.format(user_file=user_data, host=hostname, + user_config=user_conf)), + mock.call(model.NETWORK_DATA_TEMPLATE.format(network_file=network_data, + mac_address=mac, ip_address=ip)), + mock.call("genisoimage -output {0} -volid cidata" + " -joliet -r {1} {2} {3}".format(file_path, meta_data, user_data, + network_data)), + mock.call("rm {0} {1} {2}".format(meta_data, user_data, network_data)) + ]) + + def test_create_snapshot_qemu(self): + self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0)) + index = 1 + vm_image = '/var/lib/libvirt/images/%s.qcow2' % index + base_image = '/tmp/base_image' + + model.Libvirt.create_snapshot_qemu(self.mock_ssh, index, base_image) + self.mock_ssh.execute.assert_has_calls([ + mock.call('rm -- "%s"' % vm_image), + mock.call('test -r %s' % base_image), + mock.call('qemu-img create -f qcow2 -o backing_file=%s %s' % + (base_image, vm_image)) + ]) + + @mock.patch.object(os.path, 'basename', return_value='base_image') + @mock.patch.object(os.path, 'normpath') + @mock.patch.object(os, 'access', return_value=True) + def test_create_snapshot_qemu_no_image_remote(self, + mock_os_access, mock_normpath, mock_basename): + self.mock_ssh.execute = mock.Mock( + side_effect=[(0, 0, 0), (1, 0, 0), (0, 0, 0), (0, 0, 0)]) + index = 1 + vm_image = '/var/lib/libvirt/images/%s.qcow2' % index + base_image = '/tmp/base_image' + mock_normpath.return_value = base_image + + model.Libvirt.create_snapshot_qemu(self.mock_ssh, index, base_image) + self.mock_ssh.execute.assert_has_calls([ + mock.call('rm -- "%s"' % vm_image), + mock.call('test -r %s' % base_image), + mock.call('mv -- "/tmp/%s" "%s"' % ('base_image', base_image)), + mock.call('qemu-img create -f qcow2 -o backing_file=%s %s' % + (base_image, vm_image)) + ]) + mock_os_access.assert_called_once_with(base_image, os.R_OK) + mock_normpath.assert_called_once_with(base_image) + mock_basename.assert_has_calls([mock.call(base_image)]) + self.mock_ssh.put_file.assert_called_once_with(base_image, + '/tmp/base_image') + + @mock.patch.object(model.Libvirt, 'gen_cdrom_image') + def test_check_update_key(self, mock_gen_cdrom_image): + node = { + 'user': 'defuser', + 'key_filename': '/home/ubuntu/id_rsa', + 'ip': '1.1.1.7', + 'netmask': '255.255.255.0'} + cdrom_img = "/var/lib/libvirt/images/data.img" + id_name = 'fake_name' + key_filename = node.get('key_filename') + root = ElementTree.fromstring(self.XML_STR) + hostname = root.find('name').text + mac = "00:11:22:33:44:55" + ip = "{0}/{1}".format(node.get('ip'), node.get('netmask')) + ip = "{0}/{1}".format(node.get('ip'), netaddr.IPNetwork(ip).prefixlen) + model.StandaloneContextHelper.check_update_key(self.mock_ssh, node, hostname, id_name, + cdrom_img, mac) + mock_gen_cdrom_image.assert_called_once_with(self.mock_ssh, cdrom_img, hostname, + node.get('user'), key_filename, mac, ip) + + @mock.patch.object(os, 'access', return_value=False) + def test_create_snapshot_qemu_no_image_local(self, mock_os_access): + self.mock_ssh.execute = mock.Mock(side_effect=[(0, 0, 0), (1, 0, 0)]) + base_image = '/tmp/base_image' + + with self.assertRaises(exceptions.LibvirtQemuImageBaseImageNotPresent): + model.Libvirt.create_snapshot_qemu(self.mock_ssh, 3, base_image) + mock_os_access.assert_called_once_with(base_image, os.R_OK) + + def test_create_snapshot_qemu_error_qemuimg_command(self): + self.mock_ssh.execute = mock.Mock( + side_effect=[(0, 0, 0), (0, 0, 0), (1, 0, 0)]) + index = 1 + vm_image = '/var/lib/libvirt/images/%s.qcow2' % index + base_image = '/tmp/base_image' + + with self.assertRaises(exceptions.LibvirtQemuImageCreateError): + model.Libvirt.create_snapshot_qemu(self.mock_ssh, index, + base_image) + self.mock_ssh.execute.assert_has_calls([ + mock.call('rm -- "%s"' % vm_image), + mock.call('test -r %s' % base_image), + mock.call('qemu-img create -f qcow2 -o backing_file=%s %s' % + (base_image, vm_image)) + ]) + + @mock.patch.object(model.Libvirt, 'pin_vcpu_for_perf', return_value='4,5') + @mock.patch.object(model.Libvirt, 'create_snapshot_qemu', + return_value='qemu_image') + def test_build_vm_xml(self, mock_create_snapshot_qemu, + mock_pin_vcpu_for_perf): + extra_specs = {'hw:cpu_cores': '4', + 'hw:cpu_sockets': '3', + 'hw:cpu_threads': '2', + 'cputune': 'cool'} + flavor = {'ram': '1024', + 'extra_specs': extra_specs, + 'hw_socket': '1', + 'images': 'images'} + mac = model.StandaloneContextHelper.get_mac_address(0x00) + _uuid = uuid.uuid4() + connection = mock.Mock() + cdrom_img = '/tmp/cdrom-0.img' + with mock.patch.object(model.StandaloneContextHelper, + 'get_mac_address', return_value=mac) as \ + mock_get_mac_address, \ + mock.patch.object(uuid, 'uuid4', return_value=_uuid): + xml_out, mac = model.Libvirt.build_vm_xml( + connection, flavor, 'vm_name', 100, cdrom_img) + + xml_ref = model.VM_TEMPLATE.format(vm_name='vm_name', + random_uuid=_uuid, mac_addr=mac, memory='1024', vcpu='8', cpu='4', + numa_cpus='0-7', socket='3', threads='2', + vm_image='qemu_image', cpuset='4,5', cputune='cool', + machine='pc-i440fx-xenial') + xml_ref = model.Libvirt.add_cdrom(cdrom_img, xml_ref) + self.assertEqual(xml_out, xml_ref) + mock_get_mac_address.assert_called_once_with(0x00) + mock_create_snapshot_qemu.assert_called_once_with( + connection, 100, 'images') + mock_pin_vcpu_for_perf.assert_called_once_with(connection, '1') + + # TODO: Edit this test to test state instead of output + # update_interrupts_hugepages_perf does not return anything + def test_update_interrupts_hugepages_perf(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "a", "")) + ssh.return_value = ssh_mock + # NOTE(ralonsoh): 'update_interrupts_hugepages_perf' always return + # None, this check is trivial. + #status = Libvirt.update_interrupts_hugepages_perf(ssh_mock) + #self.assertIsNone(status) + model.Libvirt.update_interrupts_hugepages_perf(ssh_mock) + + @mock.patch.object(model, 'CpuSysCores') + @mock.patch.object(model.Libvirt, 'update_interrupts_hugepages_perf') + def test_pin_vcpu_for_perf(self, *args): + # NOTE(ralonsoh): test mocked methods/variables. + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "a", "")) + ssh.return_value = ssh_mock + status = model.Libvirt.pin_vcpu_for_perf(ssh_mock, 4) + self.assertIsNotNone(status) + + +class StandaloneContextHelperTestCase(unittest.TestCase): + + NODE_SAMPLE = "nodes_sample.yaml" + NODE_SRIOV_SAMPLE = "nodes_sriov_sample.yaml" + + NETWORKS = { + 'mgmt': {'cidr': '152.16.100.10/24'}, + 'private_0': { + 'phy_port': "0000:05:00.0", + 'vpci': "0000:00:07.0", + 'cidr': '152.16.100.10/24', + 'gateway_ip': '152.16.100.20'}, + 'public_0': { + 'phy_port': "0000:05:00.1", + 'vpci': "0000:00:08.0", + 'cidr': '152.16.40.10/24', + 'gateway_ip': '152.16.100.20'} + } + + def setUp(self): + self.helper = model.StandaloneContextHelper() + + def test___init__(self): + self.assertIsNone(self.helper.file_path) + + def test_install_req_libs(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(1, "a", "")) + ssh.return_value = ssh_mock + # NOTE(ralonsoh): this test doesn't cover function execution. This test + # should also check mocked function calls. + model.StandaloneContextHelper.install_req_libs(ssh_mock) + + def test_get_kernel_module(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(1, "i40e", "")) + ssh.return_value = ssh_mock + # NOTE(ralonsoh): this test doesn't cover function execution. This test + # should also check mocked function calls. + model.StandaloneContextHelper.get_kernel_module( + ssh_mock, "05:00.0", None) + + @mock.patch.object(model.StandaloneContextHelper, 'get_kernel_module') + def test_get_nic_details(self, mock_get_kernel_module): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = mock.Mock(return_value=(1, "i40e ixgbe", "")) + ssh.return_value = ssh_mock + mock_get_kernel_module.return_value = "i40e" + # NOTE(ralonsoh): this test doesn't cover function execution. This test + # should also check mocked function calls. + model.StandaloneContextHelper.get_nic_details( + ssh_mock, self.NETWORKS, 'dpdk-devbind.py') + + def test_get_virtual_devices(self): + pattern = "PCI_SLOT_NAME=0000:05:00.0" + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(1, pattern, "")) + ssh.return_value = ssh_mock + # NOTE(ralonsoh): this test doesn't cover function execution. This test + # should also check mocked function calls. + model.StandaloneContextHelper.get_virtual_devices( + ssh_mock, '0000:00:05.0') + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + def test_parse_pod_file(self): + self.helper.file_path = self._get_file_abspath("dummy") + self.assertRaises(IOError, self.helper.parse_pod_file, + self.helper.file_path) + + self.helper.file_path = self._get_file_abspath(self.NODE_SAMPLE) + self.assertRaises(TypeError, self.helper.parse_pod_file, + self.helper.file_path) + + self.helper.file_path = self._get_file_abspath(self.NODE_SRIOV_SAMPLE) + self.assertIsNotNone(self.helper.parse_pod_file(self.helper.file_path)) + + def test_get_mac_address(self): + status = model.StandaloneContextHelper.get_mac_address() + self.assertIsNotNone(status) + + @mock.patch('yardstick.ssh.SSH') + def test_get_mgmt_ip(self, *args): + # NOTE(ralonsoh): test mocked methods/variables. + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = mock.Mock( + return_value=(1, "1.2.3.4 00:00:00:00:00:01", "")) + ssh.return_value = ssh_mock + # NOTE(ralonsoh): this test doesn't cover function execution. This test + # should also check mocked function calls. + status = model.StandaloneContextHelper.get_mgmt_ip( + ssh_mock, "00:00:00:00:00:01", "1.1.1.1/24", {}) + self.assertIsNotNone(status) + + @mock.patch('yardstick.ssh.SSH') + def test_get_mgmt_ip_no(self, *args): + # NOTE(ralonsoh): test mocked methods/variables. + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(1, "", "")) + ssh.return_value = ssh_mock + # NOTE(ralonsoh): this test doesn't cover function execution. This test + # should also check mocked function calls. + model.WAIT_FOR_BOOT = 0 + status = model.StandaloneContextHelper.get_mgmt_ip( + ssh_mock, "99", "1.1.1.1/24", {}) + self.assertIsNone(status) + + +class ServerTestCase(unittest.TestCase): + + NETWORKS = { + 'mgmt': {'cidr': '152.16.100.10/24'}, + 'private_0': { + 'phy_port': "0000:05:00.0", + 'vpci': "0000:00:07.0", + 'driver': 'i40e', + 'mac': '', + 'cidr': '152.16.100.10/24', + 'gateway_ip': '152.16.100.20'}, + 'public_0': { + 'phy_port': "0000:05:00.1", + 'vpci': "0000:00:08.0", + 'driver': 'i40e', + 'mac': '', + 'cidr': '152.16.40.10/24', + 'gateway_ip': '152.16.100.20'} + } + + def setUp(self): + self.server = model.Server() + + def test___init__(self): + self.assertIsNotNone(self.server) + + def test_build_vnf_interfaces(self): + vnf = { + "network_ports": { + 'mgmt': {'cidr': '152.16.100.10/24'}, + 'xe0': ['private_0'], + 'xe1': ['public_0'], + } + } + status = model.Server.build_vnf_interfaces(vnf, self.NETWORKS) + self.assertIsNotNone(status) + + def test_generate_vnf_instance(self): + vnf = { + "network_ports": { + 'mgmt': {'cidr': '152.16.100.10/24'}, + 'xe0': ['private_0'], + 'xe1': ['public_0'], + } + } + status = self.server.generate_vnf_instance( + {}, self.NETWORKS, '1.1.1.1/24', 'vm-0', vnf, '00:00:00:00:00:01') + self.assertIsNotNone(status) + + +class OvsDeployTestCase(unittest.TestCase): + + OVS_DETAILS = {'version': {'ovs': 'ovs_version', 'dpdk': 'dpdk_version'}} + + def setUp(self): + self._mock_ssh = mock.patch.object(ssh, 'SSH') + self.mock_ssh = self._mock_ssh.start() + self.ovs_deploy = model.OvsDeploy(self.mock_ssh, + '/tmp/dpdk-devbind.py', + self.OVS_DETAILS) + self._mock_path_isfile = mock.patch.object(os.path, 'isfile') + self._mock_path_join = mock.patch.object(os.path, 'join') + self.mock_path_isfile = self._mock_path_isfile.start() + self.mock_path_join = self._mock_path_join.start() + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_ssh.stop() + self._mock_path_isfile.stop() + self._mock_path_join.stop() + + @mock.patch.object(model.StandaloneContextHelper, 'install_req_libs') + def test_prerequisite(self, mock_install_req_libs): + pkgs = ["git", "build-essential", "pkg-config", "automake", + "autotools-dev", "libltdl-dev", "cmake", "libnuma-dev", + "libpcap-dev"] + self.ovs_deploy.prerequisite() + mock_install_req_libs.assert_called_once_with( + self.ovs_deploy.connection, pkgs) + + def test_ovs_deploy_no_file(self): + self.mock_path_isfile.return_value = False + mock_file = mock.Mock() + self.mock_path_join.return_value = mock_file + + self.ovs_deploy.ovs_deploy() + self.mock_path_isfile.assert_called_once_with(mock_file) + self.mock_path_join.assert_called_once_with( + constants.YARDSTICK_ROOT_PATH, + 'yardstick/resources/scripts/install/', + self.ovs_deploy.OVS_DEPLOY_SCRIPT) + + @mock.patch.object(os.environ, 'get', return_value='test_proxy') + def test_ovs_deploy(self, mock_env_get): + self.mock_path_isfile.return_value = True + mock_deploy_file = mock.Mock() + mock_remove_ovs_deploy = mock.Mock() + self.mock_path_join.side_effect = [mock_deploy_file, + mock_remove_ovs_deploy] + dpdk_version = self.OVS_DETAILS['version']['dpdk'] + ovs_version = self.OVS_DETAILS['version']['ovs'] + + with mock.patch.object(self.ovs_deploy.connection, 'put') as \ + mock_put, \ + mock.patch.object(self.ovs_deploy.connection, 'execute') as \ + mock_execute, \ + mock.patch.object(self.ovs_deploy, 'prerequisite'): + mock_execute.return_value = (0, 0, 0) + self.ovs_deploy.ovs_deploy() + + self.mock_path_isfile.assert_called_once_with(mock_deploy_file) + self.mock_path_join.assert_has_calls([ + mock.call(constants.YARDSTICK_ROOT_PATH, + 'yardstick/resources/scripts/install/', + self.ovs_deploy.OVS_DEPLOY_SCRIPT), + mock.call(self.ovs_deploy.bin_path, + self.ovs_deploy.OVS_DEPLOY_SCRIPT) + ]) + mock_put.assert_called_once_with(mock_deploy_file, + mock_remove_ovs_deploy) + cmd = ("sudo -E %(remote_ovs_deploy)s --ovs='%(ovs_version)s' " + "--dpdk='%(dpdk_version)s' -p='%(proxy)s'" % + {'remote_ovs_deploy': mock_remove_ovs_deploy, + 'ovs_version': ovs_version, + 'dpdk_version': dpdk_version, + 'proxy': 'test_proxy'}) + mock_execute.assert_called_once_with(cmd) + mock_env_get.assert_has_calls([mock.call('http_proxy', '')]) diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py b/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py new file mode 100644 index 000000000..413bb68b7 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py @@ -0,0 +1,500 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import io +import os + +import mock +import six +import unittest + +from yardstick.benchmark import contexts +from yardstick.benchmark.contexts import base +from yardstick.benchmark.contexts.standalone import model +from yardstick.benchmark.contexts.standalone import ovs_dpdk +from yardstick.common import exceptions +from yardstick.common import utils as common_utils +from yardstick.network_services import utils + + +class OvsDpdkContextTestCase(unittest.TestCase): + + NODES_SAMPLE = "nodes_sample.yaml" + NODES_ovs_dpdk_SAMPLE = "nodes_ovs_dpdk_sample.yaml" + NODES_DUPLICATE_SAMPLE = "nodes_duplicate_sample.yaml" + + NETWORKS = { + 'private_0': { + 'phy_port': "0000:05:00.0", + 'vpci': "0000:00:07.0", + 'cidr': '152.16.100.10/24', + 'interface': 'if0', + 'mac': "00:00:00:00:00:01", + 'vf_pci': {'vf_pci': 0}, + 'gateway_ip': '152.16.100.20'}, + 'public_0': { + 'phy_port': "0000:05:00.1", + 'vpci': "0000:00:08.0", + 'cidr': '152.16.40.10/24', + 'interface': 'if0', + 'vf_pci': {'vf_pci': 0}, + 'mac': "00:00:00:00:00:01", + 'gateway_ip': '152.16.100.20'}, + } + + def setUp(self): + self.attrs = { + 'name': 'foo', + 'task_id': '1234567890', + 'file': self._get_file_abspath(self.NODES_ovs_dpdk_SAMPLE) + } + self.ovs_dpdk = ovs_dpdk.OvsDpdkContext() + self._mock_log = mock.patch.object(ovs_dpdk, 'LOG') + self.mock_log = self._mock_log.start() + self.addCleanup(self._remove_contexts) + self.addCleanup(self._stop_mocks) + + @staticmethod + def _remove_contexts(): + for context in base.Context.list: + context._delete_context() + base.Context.list = [] + + def _stop_mocks(self): + self._mock_log.stop() + + @mock.patch('yardstick.benchmark.contexts.standalone.model.Server') + @mock.patch('yardstick.benchmark.contexts.standalone.model.StandaloneContextHelper') + def test___init__(self, mock_helper, mock_server): + self.ovs_dpdk.helper = mock_helper + self.ovs_dpdk.vnf_node = mock_server + self.assertIsNone(self.ovs_dpdk.file_path) + self.assertTrue(self.ovs_dpdk.first_run) + + def test_init(self): + ATTRS = { + 'name': contexts.CONTEXT_STANDALONEOVSDPDK, + 'task_id': '1234567890', + 'file': 'pod', + 'flavor': {}, + 'servers': {}, + 'networks': {}, + } + + self.ovs_dpdk.helper.parse_pod_file = mock.Mock( + return_value=[{}, {}, {}]) + self.assertIsNone(self.ovs_dpdk.init(ATTRS)) + + def test_setup_ovs(self): + fake_path = '/fake_path' + fake_dpdk_nic_bind = 'dpdk_tool.py' + self.ovs_dpdk.ovs_properties = {'vpath': fake_path} + self.ovs_dpdk.dpdk_devbind = fake_dpdk_nic_bind + self.ovs_dpdk.networks = self.NETWORKS + self.ovs_dpdk.connection = mock.Mock() + self.ovs_dpdk.connection.execute = mock.Mock(return_value=(0, 0, 0)) + create_from = fake_path + '/etc/openvswitch/conf.db' + create_to = fake_path + '/share/openvswitch/vswitch.ovsschema' + cmd_list = [ + 'killall -r "ovs.*" -q | true', + 'mkdir -p {0}/etc/openvswitch'.format(fake_path), + 'mkdir -p {0}/var/run/openvswitch'.format(fake_path), + 'rm {0}/etc/openvswitch/conf.db | true'.format(fake_path), + 'ovsdb-tool create {0} {1}'.format(create_from, create_to), + 'modprobe vfio-pci', + 'chmod a+x /dev/vfio', + 'chmod 0666 /dev/vfio/*', + '{0} --force -b vfio-pci {1}'.format(fake_dpdk_nic_bind, + self.ovs_dpdk.networks['private_0']['phy_port']), + '{0} --force -b vfio-pci {1}'.format(fake_dpdk_nic_bind, + self.ovs_dpdk.networks['public_0']['phy_port']) + ] + calls = [mock.call(cmd, timeout=self.ovs_dpdk.CMD_TIMEOUT) + for cmd in cmd_list] + + self.ovs_dpdk.setup_ovs() + self.ovs_dpdk.connection.execute.assert_has_calls(calls, + any_order=True) + + def test_setup_ovs_exception(self): + self.ovs_dpdk.networks = self.NETWORKS + self.ovs_dpdk.connection = mock.Mock() + self.ovs_dpdk.connection.execute = mock.Mock(return_value=(1, 0, 0)) + + with self.assertRaises(exceptions.OVSSetupError): + self.ovs_dpdk.setup_ovs() + + def test_start_ovs_serverswitch(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "a", "")) + ssh.return_value = ssh_mock + self.ovs_dpdk.connection = ssh_mock + self.ovs_dpdk.networks = self.NETWORKS + self.ovs_dpdk.ovs_properties = {} + self.ovs_dpdk.wait_for_vswitchd = 0 + self.assertIsNone(self.ovs_dpdk.start_ovs_serverswitch()) + + def test_setup_ovs_bridge_add_flows(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "a", "")) + ssh.return_value = ssh_mock + self.ovs_dpdk.connection = ssh_mock + self.ovs_dpdk.networks = self.NETWORKS + self.ovs_dpdk.ovs_properties = { + 'version': {'ovs': '2.7.0'} + } + self.ovs_dpdk.wait_for_vswitchd = 0 + self.assertIsNone(self.ovs_dpdk.setup_ovs_bridge_add_flows()) + self.ovs_dpdk.ovs_properties.update( + {'dpdk_pmd-rxq-affinity': {'0': "0:1"}}) + self.ovs_dpdk.ovs_properties.update( + {'vhost_pmd-rxq-affinity': {'0': "0:1"}}) + self.NETWORKS['private_0'].update({'port_num': '0'}) + self.NETWORKS['public_0'].update({'port_num': '1'}) + self.ovs_dpdk.setup_ovs_bridge_add_flows() + + @mock.patch("yardstick.ssh.SSH") + def test_cleanup_ovs_dpdk_env(self, mock_ssh): + mock_ssh.execute.return_value = 0, "a", "" + self.ovs_dpdk.connection = mock_ssh + self.ovs_dpdk.networks = self.NETWORKS + self.ovs_dpdk.ovs_properties = { + 'version': {'ovs': '2.7.0'} + } + self.ovs_dpdk.wait_for_vswitchd = 0 + self.assertIsNone(self.ovs_dpdk.cleanup_ovs_dpdk_env()) + + @mock.patch.object(utils, 'get_nsb_option') + @mock.patch.object(model.OvsDeploy, 'ovs_deploy') + def test_check_ovs_dpdk_env(self, mock_ovs_deploy, mock_get_nsb_option): + self.ovs_dpdk.connection = mock.Mock() + self.ovs_dpdk.connection.execute = mock.Mock( + return_value=(1, 0, 0)) + self.ovs_dpdk.networks = self.NETWORKS + self.ovs_dpdk.ovs_properties = { + 'version': {'ovs': '2.7.0', 'dpdk': '16.11.1'} + } + self.ovs_dpdk.wait_for_vswitchd = 0 + self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock() + mock_get_nsb_option.return_value = 'fake_path' + + self.ovs_dpdk.check_ovs_dpdk_env() + mock_ovs_deploy.assert_called_once() + mock_get_nsb_option.assert_called_once_with('bin_path') + + def test_check_ovs_dpdk_env_wrong_version(self): + self.ovs_dpdk.connection = mock.Mock() + self.ovs_dpdk.connection.execute = mock.Mock( + return_value=(1, 0, 0)) + self.ovs_dpdk.networks = self.NETWORKS + self.ovs_dpdk.ovs_properties = { + 'version': {'ovs': '0.0.1', 'dpdk': '9.8.7'} + } + self.ovs_dpdk.wait_for_vswitchd = 0 + self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock() + + with self.assertRaises(exceptions.OVSUnsupportedVersion): + self.ovs_dpdk.check_ovs_dpdk_env() + + @mock.patch('yardstick.ssh.SSH') + def test_deploy(self, *args): + self.ovs_dpdk.vm_deploy = False + self.assertIsNone(self.ovs_dpdk.deploy()) + + self.ovs_dpdk.vm_deploy = True + self.ovs_dpdk.host_mgmt = {} + self.ovs_dpdk.install_req_libs = mock.Mock() + self.ovs_dpdk.helper.get_nic_details = mock.Mock(return_value={}) + self.ovs_dpdk.check_ovs_dpdk_env = mock.Mock(return_value={}) + self.ovs_dpdk.setup_ovs = mock.Mock(return_value={}) + self.ovs_dpdk.start_ovs_serverswitch = mock.Mock(return_value={}) + self.ovs_dpdk.setup_ovs_bridge_add_flows = mock.Mock(return_value={}) + self.ovs_dpdk.setup_ovs_dpdk_context = mock.Mock(return_value={}) + self.ovs_dpdk.wait_for_vnfs_to_start = mock.Mock(return_value={}) + # TODO(elfoley): This test should check states/sideeffects instead of + # output. + self.assertIsNone(self.ovs_dpdk.deploy()) + + @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete') + def test_undeploy(self, mock_libvirt): + self.ovs_dpdk.vm_deploy = True + self.ovs_dpdk.connection = mock.Mock() + self.ovs_dpdk.vm_names = ['vm-0', 'vm-1'] + self.ovs_dpdk.drivers = ['vm-0', 'vm-1'] + self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock() + self.ovs_dpdk.networks = self.NETWORKS + self.ovs_dpdk.undeploy() + mock_libvirt.assert_has_calls([ + mock.call(self.ovs_dpdk.vm_names[0], self.ovs_dpdk.connection), + mock.call(self.ovs_dpdk.vm_names[1], self.ovs_dpdk.connection) + ]) + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + def test__get_server_with_dic_attr_name(self): + + self.ovs_dpdk.init(self.attrs) + + attr_name = {'name': 'foo.bar'} + result = self.ovs_dpdk._get_server(attr_name) + + self.assertEqual(result, None) + + def test__get_server_not_found(self): + + self.ovs_dpdk.helper.parse_pod_file = mock.Mock( + return_value=[{}, {}, {}]) + self.ovs_dpdk.init(self.attrs) + + attr_name = 'bar.foo' + result = self.ovs_dpdk._get_server(attr_name) + + self.assertEqual(result, None) + + def test__get_server_mismatch(self): + + self.ovs_dpdk.init(self.attrs) + + attr_name = 'bar.foo1' + result = self.ovs_dpdk._get_server(attr_name) + + self.assertEqual(result, None) + + def test__get_server_duplicate(self): + + self.attrs['file'] = self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE) + + self.ovs_dpdk.init(self.attrs) + + attr_name = 'node1.foo-12345678' + with self.assertRaises(ValueError): + self.ovs_dpdk._get_server(attr_name) + + def test__get_server_found(self): + + self.ovs_dpdk.init(self.attrs) + + attr_name = 'node1.foo-12345678' + result = self.ovs_dpdk._get_server(attr_name) + + self.assertEqual(result['ip'], '10.229.47.137') + self.assertEqual(result['name'], 'node1.foo-12345678') + self.assertEqual(result['user'], 'root') + self.assertEqual(result['key_filename'], '/root/.yardstick_key') + + def test__get_physical_node_for_server(self): + attrs = self.attrs + attrs.update({'servers': {'server1': {}}}) + self.ovs_dpdk.init(attrs) + + # When server is not from this context + result = self.ovs_dpdk._get_physical_node_for_server('server1.another-context') + self.assertIsNone(result) + + # When node_name is not from this context + result = self.ovs_dpdk._get_physical_node_for_server('fake.foo-12345678') + self.assertIsNone(result) + + result = self.ovs_dpdk._get_physical_node_for_server('server1.foo-12345678') + self.assertEqual(result, 'node5.foo') + + # TODO(elfoley): Split this test for networks that exist and networks that + # don't + def test__get_network(self): + network1 = { + 'name': 'net_1', + 'vld_id': 'vld111', + 'segmentation_id': 'seg54', + 'network_type': 'type_a', + 'physical_network': 'phys', + } + network2 = { + 'name': 'net_2', + 'vld_id': 'vld999', + } + self.ovs_dpdk.networks = { + 'a': network1, + 'b': network2, + } + + # Tests for networks that do not exist + attr_name = {} + self.assertIsNone(self.ovs_dpdk._get_network(attr_name)) + + attr_name = {'vld_id': 'vld777'} + self.assertIsNone(self.ovs_dpdk._get_network(attr_name)) + + self.assertIsNone(self.ovs_dpdk._get_network(None)) + + # TODO(elfoley): Split this test + attr_name = 'vld777' + self.assertIsNone(self.ovs_dpdk._get_network(attr_name)) + + # Tests for networks that exist + attr_name = {'vld_id': 'vld999'} + expected = { + "name": 'net_2', + "vld_id": 'vld999', + "segmentation_id": None, + "network_type": None, + "physical_network": None, + } + result = self.ovs_dpdk._get_network(attr_name) + self.assertDictEqual(result, expected) + + attr_name = 'a' + expected = network1 + result = self.ovs_dpdk._get_network(attr_name) + self.assertDictEqual(result, expected) + + def test_configure_nics_for_ovs_dpdk(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "a", "")) + ssh.return_value = ssh_mock + self.ovs_dpdk.vm_deploy = True + self.ovs_dpdk.connection = ssh_mock + self.ovs_dpdk.vm_names = ['vm-0', 'vm-1'] + self.ovs_dpdk.drivers = [] + self.ovs_dpdk.networks = self.NETWORKS + self.ovs_dpdk.helper.get_mac_address = mock.Mock(return_value="") + self.ovs_dpdk.get_vf_datas = mock.Mock(return_value="") + self.assertIsNone(self.ovs_dpdk.configure_nics_for_ovs_dpdk()) + + @mock.patch.object(model.Libvirt, 'add_ovs_interface') + def test__enable_interfaces(self, mock_add_ovs_interface): + self.ovs_dpdk.vm_deploy = True + self.ovs_dpdk.connection = mock.Mock() + self.ovs_dpdk.vm_names = ['vm-0', 'vm-1'] + self.ovs_dpdk.drivers = [] + self.ovs_dpdk.networks = self.NETWORKS + self.ovs_dpdk.ovs_properties = {'vpath': 'fake_path'} + self.ovs_dpdk.get_vf_datas = mock.Mock(return_value="") + self.ovs_dpdk._enable_interfaces(0, ["private_0"], 'test') + mock_add_ovs_interface.assert_called_once_with( + 'fake_path', 0, self.NETWORKS['private_0']['vpci'], + self.NETWORKS['private_0']['mac'], 'test', 1) + + @mock.patch.object(ovs_dpdk.OvsDpdkContext, '_check_hugepages') + @mock.patch.object(common_utils, 'setup_hugepages') + @mock.patch.object(model.StandaloneContextHelper, 'check_update_key') + @mock.patch.object(model.Libvirt, 'write_file') + @mock.patch.object(model.Libvirt, 'build_vm_xml') + @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete') + @mock.patch.object(model.Libvirt, 'virsh_create_vm') + def test_setup_ovs_dpdk_context(self, mock_create_vm, mock_check_if_exists, + mock_build_xml, mock_write_file, + mock_check_update_key, + mock_setup_hugepages, + mock__check_hugepages): + self.ovs_dpdk.vm_deploy = True + self.ovs_dpdk.connection = mock.Mock() + self.ovs_dpdk.vm_names = ['vm-0', 'vm-1'] + self.ovs_dpdk.drivers = [] + self.ovs_dpdk.servers = { + 'vnf_0': { + 'network_ports': { + 'mgmt': {'cidr': '152.16.100.10/24'}, + 'xe0': ['private_0'], + 'xe1': ['public_0'] + } + } + } + self.ovs_dpdk.networks = self.NETWORKS + self.ovs_dpdk.host_mgmt = {} + self.ovs_dpdk.vm_flavor = {'ram': '1024'} + self.ovs_dpdk.file_path = '/var/lib/libvirt/images/cdrom-0.img' + self.ovs_dpdk.configure_nics_for_ovs_dpdk = mock.Mock(return_value="") + self.ovs_dpdk._name_task_id = 'fake_name' + xml_str = 'vm-0' + self.ovs_dpdk.mac = '00:00:00:00:00:01' + mock_build_xml.return_value = (xml_str, self.ovs_dpdk.mac) + self.ovs_dpdk._enable_interfaces = mock.Mock(return_value=xml_str) + vnf_instance = mock.Mock() + vnf_instance_2 = mock.Mock() + mock_check_update_key.return_value = vnf_instance_2 + self.ovs_dpdk.vnf_node.generate_vnf_instance = mock.Mock( + return_value=vnf_instance) + + self.assertEqual([vnf_instance_2], + self.ovs_dpdk.setup_ovs_dpdk_context()) + mock_setup_hugepages.assert_called_once_with(self.ovs_dpdk.connection, + (1024 + 4096) * 1024) # ram + dpdk_socket0_mem + dpdk_socket1_mem + mock__check_hugepages.assert_called_once() + mock_create_vm.assert_called_once_with( + self.ovs_dpdk.connection, '/tmp/vm_ovs_0.xml') + mock_check_if_exists.assert_called_once_with( + 'vm-0', self.ovs_dpdk.connection) + mock_build_xml.assert_called_once_with( + self.ovs_dpdk.connection, self.ovs_dpdk.vm_flavor, 'vm-0', 0, self.ovs_dpdk.file_path) + mock_write_file.assert_called_once_with('/tmp/vm_ovs_0.xml', xml_str) + mock_check_update_key.assert_called_once_with(self.ovs_dpdk.connection, + vnf_instance, + xml_str, + self.ovs_dpdk._name_task_id, + self.ovs_dpdk.file_path, + self.ovs_dpdk.mac) + + @mock.patch.object(io, 'BytesIO') + def test__check_hugepages(self, mock_bytesio): + data = six.BytesIO('HugePages_Total: 20\n' + 'HugePages_Free: 20\n' + 'HugePages_Rsvd: 0\n' + 'HugePages_Surp: 0\n' + 'Hugepagesize: 1048576 kB'.encode()) + mock_bytesio.return_value = data + self.ovs_dpdk.connection = mock.Mock() + self.ovs_dpdk._check_hugepages() + + @mock.patch.object(io, 'BytesIO') + def test__check_hugepages_no_info(self, mock_bytesio): + data = six.BytesIO(''.encode()) + mock_bytesio.return_value = data + self.ovs_dpdk.connection = mock.Mock() + with self.assertRaises(exceptions.OVSHugepagesInfoError): + self.ovs_dpdk._check_hugepages() + + @mock.patch.object(io, 'BytesIO') + def test__check_hugepages_no_total_hp(self, mock_bytesio): + data = six.BytesIO('HugePages_Total: 0\n' + 'HugePages_Free: 0\n' + 'HugePages_Rsvd: 0\n' + 'HugePages_Surp: 0\n' + 'Hugepagesize: 1048576 kB'.encode()) + mock_bytesio.return_value = data + self.ovs_dpdk.connection = mock.Mock() + with self.assertRaises(exceptions.OVSHugepagesNotConfigured): + self.ovs_dpdk._check_hugepages() + + @mock.patch.object(io, 'BytesIO') + def test__check_hugepages_no_free_hp(self, mock_bytesio): + data = six.BytesIO('HugePages_Total: 20\n' + 'HugePages_Free: 0\n' + 'HugePages_Rsvd: 0\n' + 'HugePages_Surp: 0\n' + 'Hugepagesize: 1048576 kB'.encode()) + mock_bytesio.return_value = data + self.ovs_dpdk.connection = mock.Mock() + with self.assertRaises(exceptions.OVSHugepagesZeroFree) as exc: + self.ovs_dpdk._check_hugepages() + self.assertEqual('There are no HugePages free in this system. Total ' + 'HugePages configured: 20', exc.exception.msg) diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py b/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py new file mode 100644 index 000000000..0809a983a --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py @@ -0,0 +1,362 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import mock +import unittest + +from yardstick import ssh +from yardstick.benchmark import contexts +from yardstick.benchmark.contexts import base +from yardstick.benchmark.contexts.standalone import model +from yardstick.benchmark.contexts.standalone import sriov +from yardstick.common import utils + + +class SriovContextTestCase(unittest.TestCase): + + NODES_SAMPLE = "nodes_sample.yaml" + NODES_SRIOV_SAMPLE = "nodes_sriov_sample.yaml" + NODES_DUPLICATE_SAMPLE = "nodes_duplicate_sample.yaml" + + ATTRS = { + 'name': contexts.CONTEXT_STANDALONESRIOV, + 'task_id': '1234567890', + 'file': 'pod', + 'flavor': {}, + 'servers': {}, + 'networks': {}, + } + + NETWORKS = { + 'mgmt': {'cidr': '152.16.100.10/24'}, + 'private_0': { + 'phy_port': "0000:05:00.0", + 'vpci': "0000:00:07.0", + 'cidr': '152.16.100.10/24', + 'interface': 'if0', + 'mac': "00:00:00:00:00:01", + 'vf_pci': {'vf_pci': 0}, + 'gateway_ip': '152.16.100.20'}, + 'public_0': { + 'phy_port': "0000:05:00.1", + 'vpci': "0000:00:08.0", + 'cidr': '152.16.40.10/24', + 'interface': 'if0', + 'vf_pci': {'vf_pci': 0}, + 'mac': "00:00:00:00:00:01", + 'gateway_ip': '152.16.100.20'}, + } + + def setUp(self): + self.attrs = { + 'name': 'foo', + 'task_id': '1234567890', + 'file': self._get_file_abspath(self.NODES_SRIOV_SAMPLE), + } + self.sriov = sriov.SriovContext() + self.addCleanup(self._remove_contexts) + + @staticmethod + def _remove_contexts(): + for context in base.Context.list: + context._delete_context() + base.Context.list = [] + + @mock.patch.object(model, 'StandaloneContextHelper') + @mock.patch.object(model, 'Libvirt') + @mock.patch.object(model, 'Server') + def test___init__(self, mock_helper, mock_libvirt, mock_server): + # pylint: disable=unused-argument + # NOTE(ralonsoh): this test doesn't cover function execution. + self.sriov.helper = mock_helper + self.sriov.vnf_node = mock_server + self.assertIsNone(self.sriov.file_path) + self.assertTrue(self.sriov.first_run) + + def test_init(self): + self.sriov.helper.parse_pod_file = mock.Mock(return_value=[{}, {}, {}]) + self.assertIsNone(self.sriov.init(self.ATTRS)) + + @mock.patch.object(ssh, 'SSH', return_value=(0, "a", "")) + def test_deploy(self, *args): + # NOTE(ralonsoh): this test doesn't cover function execution. + self.sriov.vm_deploy = False + self.assertIsNone(self.sriov.deploy()) + + self.sriov.vm_deploy = True + self.sriov.host_mgmt = {} + self.sriov.install_req_libs = mock.Mock() + self.sriov.get_nic_details = mock.Mock(return_value={}) + self.sriov.setup_sriov_context = mock.Mock(return_value={}) + self.sriov.wait_for_vnfs_to_start = mock.Mock(return_value={}) + self.assertIsNone(self.sriov.deploy()) + + @mock.patch.object(ssh, 'SSH', return_value=(0, "a", "")) + @mock.patch.object(model, 'Libvirt') + def test_undeploy(self, mock_libvirt, mock_ssh): + # pylint: disable=unused-argument + # NOTE(ralonsoh): the pylint exception should be removed. + self.sriov.vm_deploy = False + self.assertIsNone(self.sriov.undeploy()) + + self.sriov.vm_deploy = True + self.sriov.connection = mock_ssh + self.sriov.vm_names = ['vm-0', 'vm-1'] + self.sriov.drivers = ['vm-0', 'vm-1'] + self.assertIsNone(self.sriov.undeploy()) + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + def test__get_server_with_dic_attr_name(self): + + self.sriov.init(self.attrs) + + attr_name = {'name': 'foo.bar'} + result = self.sriov._get_server(attr_name) + + self.assertEqual(result, None) + + def test__get_server_not_found(self): + + self.sriov.helper.parse_pod_file = mock.Mock(return_value=[{}, {}, {}]) + self.sriov.init(self.attrs) + + attr_name = 'bar.foo' + result = self.sriov._get_server(attr_name) + + self.assertEqual(result, None) + + def test__get_server_mismatch(self): + + self.sriov.init(self.attrs) + + attr_name = 'bar.foo1' + result = self.sriov._get_server(attr_name) + + self.assertEqual(result, None) + + def test__get_server_duplicate(self): + + self.attrs['file'] = self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE) + + self.sriov.init(self.attrs) + + attr_name = 'node1.foo-12345678' + with self.assertRaises(ValueError): + self.sriov._get_server(attr_name) + + def test__get_server_found(self): + + self.sriov.init(self.attrs) + + attr_name = 'node1.foo-12345678' + result = self.sriov._get_server(attr_name) + + self.assertEqual(result['ip'], '10.229.47.137') + self.assertEqual(result['name'], 'node1.foo-12345678') + self.assertEqual(result['user'], 'root') + self.assertEqual(result['key_filename'], '/root/.yardstick_key') + + def test__get_physical_node_for_server(self): + attrs = self.attrs + attrs.update({'servers': {'server1': {}}}) + self.sriov.init(attrs) + + # When server is not from this context + result = self.sriov._get_physical_node_for_server('server1.another-context') + self.assertIsNone(result) + + # When node_name is not from this context + result = self.sriov._get_physical_node_for_server('fake.foo-12345678') + self.assertIsNone(result) + + result = self.sriov._get_physical_node_for_server('server1.foo-12345678') + self.assertEqual(result, 'node5.foo') + + def test__get_server_no_task_id(self): + self.attrs['flags'] = {'no_setup': True} + self.sriov.init(self.attrs) + + attr_name = 'node1.foo' + result = self.sriov._get_server(attr_name) + + self.assertEqual(result['ip'], '10.229.47.137') + self.assertEqual(result['name'], 'node1.foo') + self.assertEqual(result['user'], 'root') + self.assertEqual(result['key_filename'], '/root/.yardstick_key') + + # TODO(elfoley): Split this test + # There are at least two sets of inputs/outputs + def test__get_network(self): + network1 = { + 'name': 'net_1', + 'vld_id': 'vld111', + 'segmentation_id': 'seg54', + 'network_type': 'type_a', + 'physical_network': 'phys', + } + network2 = { + 'name': 'net_2', + 'vld_id': 'vld999', + } + self.sriov.networks = { + 'a': network1, + 'b': network2, + } + + attr_name = {} + self.assertIsNone(self.sriov._get_network(attr_name)) + + attr_name = {'vld_id': 'vld777'} + self.assertIsNone(self.sriov._get_network(attr_name)) + + self.assertIsNone(self.sriov._get_network(None)) + + attr_name = 'vld777' + self.assertIsNone(self.sriov._get_network(attr_name)) + + attr_name = {'vld_id': 'vld999'} + expected = { + "name": 'net_2', + "vld_id": 'vld999', + "segmentation_id": None, + "network_type": None, + "physical_network": None, + } + result = self.sriov._get_network(attr_name) + self.assertDictEqual(result, expected) + + attr_name = 'a' + expected = network1 + result = self.sriov._get_network(attr_name) + self.assertDictEqual(result, expected) + + def test_configure_nics_for_sriov(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "a", "")) + ssh.return_value = ssh_mock + self.sriov.vm_deploy = True + self.sriov.connection = ssh_mock + self.sriov.vm_names = ['vm-0', 'vm-1'] + self.sriov.drivers = [] + self.sriov.networks = self.NETWORKS + self.sriov.helper.get_mac_address = mock.Mock(return_value="") + self.sriov._get_vf_data = mock.Mock(return_value="") + self.assertIsNone(self.sriov.configure_nics_for_sriov()) + + @mock.patch.object(ssh, 'SSH', return_value=(0, "a", "")) + @mock.patch.object(model.Libvirt, 'add_sriov_interfaces', + return_value='out_xml') + def test__enable_interfaces(self, mock_add_sriov, mock_ssh): + self.sriov.vm_deploy = True + self.sriov.connection = mock_ssh + self.sriov.vm_names = ['vm-0', 'vm-1'] + self.sriov.drivers = [] + self.sriov.networks = self.NETWORKS + self.assertEqual( + 'out_xml', + self.sriov._enable_interfaces(0, 0, ['private_0'], 'test')) + mock_add_sriov.assert_called_once_with( + '0000:00:0a.0', 0, self.NETWORKS['private_0']['mac'], 'test') + + @mock.patch.object(utils, 'setup_hugepages') + @mock.patch.object(model.StandaloneContextHelper, 'check_update_key') + @mock.patch.object(model.Libvirt, 'build_vm_xml') + @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete') + @mock.patch.object(model.Libvirt, 'write_file') + @mock.patch.object(model.Libvirt, 'virsh_create_vm') + def test_setup_sriov_context(self, mock_create_vm, mock_write_file, + mock_check, mock_build_vm_xml, + mock_check_update_key, mock_setup_hugepages): + self.sriov.servers = { + 'vnf_0': { + 'network_ports': { + 'mgmt': {'cidr': '152.16.100.10/24'}, + 'xe0': ['private_0'], + 'xe1': ['public_0'] + } + } + } + connection = mock.Mock() + self.sriov.connection = connection + self.sriov.host_mgmt = {'ip': '1.2.3.4'} + self.sriov.vm_flavor = {'ram': '1024'} + self.sriov.networks = 'networks' + self.sriov.configure_nics_for_sriov = mock.Mock() + self.sriov._name_task_id = 'fake_name' + cfg = '/tmp/vm_sriov_0.xml' + vm_name = 'vm-0' + mac = '00:00:00:00:00:01' + xml_out = mock.Mock() + mock_build_vm_xml.return_value = (xml_out, mac) + mock_check_update_key.return_value = 'node_2' + cdrom_img = '/var/lib/libvirt/images/cdrom-0.img' + + with mock.patch.object(self.sriov, 'vnf_node') as mock_vnf_node, \ + mock.patch.object(self.sriov, '_enable_interfaces') as \ + mock_enable_interfaces: + mock_enable_interfaces.return_value = 'out_xml' + mock_vnf_node.generate_vnf_instance = mock.Mock( + return_value='node_1') + nodes_out = self.sriov.setup_sriov_context() + mock_setup_hugepages.assert_called_once_with(connection, 1024*1024) + mock_check_update_key.assert_called_once_with(connection, 'node_1', vm_name, + self.sriov._name_task_id, cdrom_img, + mac) + self.assertEqual(['node_2'], nodes_out) + mock_vnf_node.generate_vnf_instance.assert_called_once_with( + self.sriov.vm_flavor, 'networks', '1.2.3.4', 'vnf_0', + self.sriov.servers['vnf_0'], '00:00:00:00:00:01') + mock_build_vm_xml.assert_called_once_with( + connection, self.sriov.vm_flavor, vm_name, 0, cdrom_img) + mock_create_vm.assert_called_once_with(connection, cfg) + mock_check.assert_called_once_with(vm_name, connection) + mock_write_file.assert_called_once_with(cfg, 'out_xml') + mock_enable_interfaces.assert_has_calls([ + mock.call(0, mock.ANY, ['private_0'], mock.ANY), + mock.call(0, mock.ANY, ['public_0'], mock.ANY)], any_order=True) + + def test__get_vf_data(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "a", "")) + ssh_mock.put = \ + mock.Mock(return_value=(0, "a", "")) + ssh.return_value = ssh_mock + self.sriov.vm_deploy = True + self.sriov.connection = ssh_mock + self.sriov.vm_names = ['vm-0', 'vm-1'] + self.sriov.drivers = [] + self.sriov.servers = { + 'vnf_0': { + 'network_ports': { + 'mgmt': {'cidr': '152.16.100.10/24'}, + 'xe0': ['private_0'], + 'xe1': ['public_0'] + } + } + } + self.sriov.networks = self.NETWORKS + self.sriov.helper.get_virtual_devices = mock.Mock( + return_value={'0000:00:01.0': ''}) + self.assertIsNotNone(self.sriov._get_vf_data( + '0000:00:01.0', '00:00:00:00:00:01', 'if0')) diff --git a/yardstick/tests/unit/benchmark/contexts/standalone_duplicate_sample.yaml b/yardstick/tests/unit/benchmark/contexts/standalone_duplicate_sample.yaml new file mode 100644 index 000000000..e468d0465 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/standalone_duplicate_sample.yaml @@ -0,0 +1,135 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +--- +# Sample config file about the POD information, including the +# name/IP/user/ssh key of Bare Metal and Controllers/Computes +# +# The options of this config file include: +# name: the name of this node +# role: node's role, support role: Master/Controller/Comupte/BareMetal +# ip: the node's IP address +# user: the username for login +# key_filename:the path of the private key file for login + +nodes: +- + name: node1 + role: TrafficGen + ip: 1.1.1.1 + user: root + password: r00t + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:05:00.0" + driver: i40e + dpdk_port_num: 0 + local_ip: "152.16.100.20" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:01" + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:05:00.1" + driver: i40e + dpdk_port_num: 1 + local_ip: "152.16.100.21" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:02" +- + name: node2 + role: nfvi_node + class: OvsDpdk + ip: 1.1.1.2 + user: root + password: r00t + vports: + - dpdkvhostuser0 + - dpdkvhostuser1 + vports_mac: + - "00:00:00:00:00:03" + - "00:00:00:00:00:04" + phy_ports: # Physical ports to configure sriov + - "0000:05:00.0" + - "0000:05:00.1" + flow: + - ovs-ofctl add-flow br0 in_port=1,action=output:3 + - ovs-ofctl add-flow br0 in_port=3,action=output:1 + - ovs-ofctl add-flow br0 in_port=4,action=output:2 + - ovs-ofctl add-flow br0 in_port=2,action=output:4 + phy_driver: i40e # kernel driver + images: "/var/lib/libvirt/images/ubuntu.qcow2" +- + name: node2 + role: nfvi_node + class: OvsDpdk + ip: 1.1.1.5 + user: root + password: r00t + vports: + - dpdkvhostuser0 + - dpdkvhostuser1 + vports_mac: + - "00:00:00:00:00:03" + - "00:00:00:00:00:04" + phy_ports: # Physical ports to configure sriov + - "0000:05:00.0" + - "0000:05:00.1" + flow: + - ovs-ofctl add-flow br0 in_port=1,action=output:3 + - ovs-ofctl add-flow br0 in_port=3,action=output:1 + - ovs-ofctl add-flow br0 in_port=4,action=output:2 + - ovs-ofctl add-flow br0 in_port=2,action=output:4 + phy_driver: i40e # kernel driver + images: "/var/lib/libvirt/images/ubuntu.qcow2" + +- + name: node3 + role: vnf + ip: 1.1.1.3 + user: root + password: r00t + host: 1.1.1.1 + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:00:04.0" + driver: virtio-pci + dpdk_port_num: 0 + local_ip: "152.16.100.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:05" + + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:00:05.0" + driver: virtio-pci + dpdk_port_num: 1 + local_ip: "152.16.40.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:06" + routing_table: + - network: "152.16.100.20" + netmask: "255.255.255.0" + gateway: "152.16.100.20" + if: "xe0" + - network: "152.16.40.20" + netmask: "255.255.255.0" + gateway: "152.16.40.20" + if: "xe1" + nd_route_tbl: + - network: "0064:ff9b:0:0:0:0:9810:6414" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:6414" + if: "xe0" + - network: "0064:ff9b:0:0:0:0:9810:2814" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:2814" + if: "xe1" diff --git a/yardstick/tests/unit/benchmark/contexts/standalone_sample.yaml b/yardstick/tests/unit/benchmark/contexts/standalone_sample.yaml new file mode 100644 index 000000000..95e12d62f --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/standalone_sample.yaml @@ -0,0 +1,112 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +--- +# Sample config file about the POD information, including the +# name/IP/user/ssh key of Bare Metal and Controllers/Computes +# +# The options of this config file include: +# name: the name of this node +# role: node's role, support role: Master/Controller/Comupte/BareMetal +# ip: the node's IP address +# user: the username for login +# key_filename:the path of the private key file for login + +nodes: +- + name: node1 + role: TrafficGen + ip: 1.1.1.1 + user: root + password: r00t + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:05:00.0" + driver: i40e + dpdk_port_num: 0 + local_ip: "152.16.100.20" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:01" + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:05:00.1" + driver: i40e + dpdk_port_num: 1 + local_ip: "152.16.100.21" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:02" +- + name: node2 + role: nfvi_node + class: OvsDpdk + ip: 1.1.1.2 + user: root + password: r00t + vports: + - dpdkvhostuser0 + - dpdkvhostuser1 + vports_mac: + - "00:00:00:00:00:03" + - "00:00:00:00:00:04" + phy_ports: # Physical ports to configure sriov + - "0000:05:00.0" + - "0000:05:00.1" + flow: + - ovs-ofctl add-flow br0 in_port=1,action=output:3 + - ovs-ofctl add-flow br0 in_port=3,action=output:1 + - ovs-ofctl add-flow br0 in_port=4,action=output:2 + - ovs-ofctl add-flow br0 in_port=2,action=output:4 + phy_driver: i40e # kernel driver + images: "/var/lib/libvirt/images/ubuntu.qcow2" + +- + name: node3 + role: vnf + ip: 1.1.1.3 + user: root + password: r00t + host: 1.1.1.1 + interfaces: + xe0: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:00:04.0" + driver: virtio-pci + dpdk_port_num: 0 + local_ip: "152.16.100.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:05" + + xe1: # logical name from topology.yaml and vnfd.yaml + vpci: "0000:00:05.0" + driver: virtio-pci + dpdk_port_num: 1 + local_ip: "152.16.40.19" + netmask: "255.255.255.0" + local_mac: "00:00:00:00:00:06" + routing_table: + - network: "152.16.100.20" + netmask: "255.255.255.0" + gateway: "152.16.100.20" + if: "xe0" + - network: "152.16.40.20" + netmask: "255.255.255.0" + gateway: "152.16.40.20" + if: "xe1" + nd_route_tbl: + - network: "0064:ff9b:0:0:0:0:9810:6414" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:6414" + if: "xe0" + - network: "0064:ff9b:0:0:0:0:9810:2814" + netmask: "112" + gateway: "0064:ff9b:0:0:0:0:9810:2814" + if: "xe1" diff --git a/yardstick/tests/unit/benchmark/contexts/test_base.py b/yardstick/tests/unit/benchmark/contexts/test_base.py new file mode 100644 index 000000000..5fd7352f5 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/test_base.py @@ -0,0 +1,174 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import errno + +import mock + +from yardstick.benchmark.contexts import base +from yardstick.benchmark.contexts.base import Context +from yardstick.common import yaml_loader +from yardstick.tests.unit import base as ut_base +from yardstick.common.constants import YARDSTICK_ROOT_PATH + + +class DummyContextClass(Context): + + __context_type__ = "Dummy" + + def __init__(self, host_name_separator='.'): + super(DummyContextClass, self).__init__\ + (host_name_separator=host_name_separator) + self.nodes = [] + self.controllers = [] + self.computes = [] + self.baremetals = [] + + def _get_network(self, *args): + pass + + def _get_server(self, *args): + pass + + def deploy(self): + pass + + def undeploy(self): + pass + + def _get_physical_nodes(self): + pass + + def _get_physical_node_for_server(self, server_name): + pass + + +class FlagsTestCase(ut_base.BaseUnitTestCase): + + def setUp(self): + self.flags = base.Flags() + + def test___init__(self): + self.assertFalse(self.flags.no_setup) + self.assertFalse(self.flags.no_teardown) + self.assertEqual({'verify': False}, self.flags.os_cloud_config) + + def test___init__with_flags(self): + flags = base.Flags(no_setup=True) + self.assertTrue(flags.no_setup) + self.assertFalse(flags.no_teardown) + + def test_parse(self): + self.flags.parse(no_setup=True, no_teardown='False', + os_cloud_config={'verify': True}) + + self.assertTrue(self.flags.no_setup) + self.assertEqual('False', self.flags.no_teardown) + self.assertEqual({'verify': True}, self.flags.os_cloud_config) + + def test_parse_forbidden_flags(self): + self.flags.parse(foo=42) + with self.assertRaises(AttributeError): + _ = self.flags.foo + + +class ContextTestCase(ut_base.BaseUnitTestCase): + + @staticmethod + def _remove_ctx(ctx_obj): + if ctx_obj in base.Context.list: + base.Context.list.remove(ctx_obj) + + def test_split_host_name(self): + ctx_obj = DummyContextClass() + self.addCleanup(self._remove_ctx, ctx_obj) + config_name = 'host_name.ctx_name' + self.assertEqual(('host_name', 'ctx_name'), + ctx_obj.split_host_name(config_name)) + + def test_split_host_name_wrong_separator(self): + ctx_obj = DummyContextClass() + self.addCleanup(self._remove_ctx, ctx_obj) + config_name = 'host_name-ctx_name' + self.assertEqual((None, None), + ctx_obj.split_host_name(config_name)) + + def test_split_host_name_other_separator(self): + ctx_obj = DummyContextClass(host_name_separator='-') + self.addCleanup(self._remove_ctx, ctx_obj) + config_name = 'host_name-ctx_name' + self.assertEqual(('host_name', 'ctx_name'), + ctx_obj.split_host_name(config_name)) + + def test_get_physical_nodes(self): + ctx_obj = DummyContextClass() + self.addCleanup(self._remove_ctx, ctx_obj) + + result = Context.get_physical_nodes() + + self.assertEqual(result, {None: None}) + + @mock.patch.object(Context, 'get_context_from_server') + def test_get_physical_node_from_server(self, mock_get_ctx): + ctx_obj = DummyContextClass() + self.addCleanup(self._remove_ctx, ctx_obj) + + mock_get_ctx.return_value = ctx_obj + + result = Context.get_physical_node_from_server("mock_server") + + mock_get_ctx.assert_called_once() + self.assertIsNone(result) + + @mock.patch.object(yaml_loader, 'read_yaml_file') + def test_read_pod_file(self, mock_read_yaml_file): + attrs = {'name': 'foo', + 'task_id': '12345678', + 'file': 'pod.yaml' + } + + ctx_obj = DummyContextClass() + cfg = {"nodes": [ + { + "name": "node1", + "role": "Controller", + "ip": "10.229.47.137", + "user": "root", + "key_filename": "/root/.yardstick_key" + }, + { + "name": "node2", + "role": "Compute", + "ip": "10.229.47.139", + "user": "root", + "key_filename": "/root/.yardstick_key" + } + ] + } + + mock_read_yaml_file.return_value = cfg + result = ctx_obj.read_pod_file(attrs) + self.assertEqual(result, cfg) + + mock_read_yaml_file.side_effect = IOError(errno.EPERM, '') + with self.assertRaises(IOError): + ctx_obj.read_pod_file(attrs) + + mock_read_yaml_file.side_effect = IOError(errno.ENOENT, '') + with self.assertRaises(IOError): + ctx_obj.read_pod_file(attrs) + + file_path = os.path.join(YARDSTICK_ROOT_PATH, 'pod.yaml') + self.assertEqual(ctx_obj.file_path, file_path) diff --git a/yardstick/tests/unit/benchmark/contexts/test_dummy.py b/yardstick/tests/unit/benchmark/contexts/test_dummy.py new file mode 100644 index 000000000..33832375f --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/test_dummy.py @@ -0,0 +1,86 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import unittest + +from yardstick.benchmark.contexts import base +from yardstick.benchmark.contexts import dummy + + +class DummyContextTestCase(unittest.TestCase): + + def setUp(self): + self.attrs = { + 'name': 'foo', + 'task_id': '1234567890', + } + self.test_context = dummy.DummyContext() + self.addCleanup(self._delete_contexts) + + @staticmethod + def _delete_contexts(): + for context in base.Context.list: + context._delete_context() + + def test___init__(self): + self.assertFalse(self.test_context._flags.no_setup) + self.assertFalse(self.test_context._flags.no_teardown) + self.assertIsNone(self.test_context._name) + self.assertIsNone(self.test_context._task_id) + + def test_init(self): + self.test_context.init(self.attrs) + self.assertEqual(self.test_context._name, 'foo') + self.assertEqual(self.test_context._task_id, '1234567890') + self.assertFalse(self.test_context._flags.no_setup) + self.assertFalse(self.test_context._flags.no_teardown) + + self.assertEqual(self.test_context.name, 'foo-12345678') + self.assertEqual(self.test_context.assigned_name, 'foo') + + def test_init_flags_no_setup(self): + self.attrs['flags'] = {'no_setup': True, 'no_teardown': False} + + self.test_context.init(self.attrs) + + self.assertEqual(self.test_context._name, 'foo') + self.assertEqual(self.test_context._task_id, '1234567890') + self.assertTrue(self.test_context._flags.no_setup) + self.assertFalse(self.test_context._flags.no_teardown) + + self.assertEqual(self.test_context.name, 'foo') + self.assertEqual(self.test_context.assigned_name, 'foo') + + def test_init_flags_no_teardown(self): + self.attrs['flags'] = {'no_setup': False, 'no_teardown': True} + + self.test_context.init(self.attrs) + + self.assertFalse(self.test_context._flags.no_setup) + self.assertTrue(self.test_context._flags.no_teardown) + + self.assertEqual(self.test_context.name, 'foo') + self.assertEqual(self.test_context.assigned_name, 'foo') + + def test__get_server(self): + self.test_context.init(self.attrs) + self.test_context.deploy() + + result = self.test_context._get_server(None) + self.assertEqual(result, None) + + self.test_context.undeploy() + + def test__get_physical_nodes(self): + result = self.test_context._get_physical_nodes() + self.assertIsNone(result) + + def test__get_physical_node_for_server(self): + result = self.test_context._get_physical_node_for_server("fake") + self.assertIsNone(result) diff --git a/yardstick/tests/unit/benchmark/contexts/test_heat.py b/yardstick/tests/unit/benchmark/contexts/test_heat.py new file mode 100644 index 000000000..96946cded --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/test_heat.py @@ -0,0 +1,893 @@ +############################################################################## +# Copyright (c) 2015 Ericsson AB and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from collections import OrderedDict +import logging +import os + +import mock +import unittest +import collections + +from yardstick.benchmark.contexts import base +from yardstick.benchmark.contexts import heat +from yardstick.benchmark.contexts import model +from yardstick.common import constants as consts +from yardstick.common import exceptions as y_exc +from yardstick.common import openstack_utils +from yardstick.common import yaml_loader +from yardstick import ssh + + +LOG = logging.getLogger(__name__) + + +class HeatContextTestCase(unittest.TestCase): + + HEAT_POD_SAMPLE = { + "nodes": [ + { + "name": "node1", + "role": "Controller", + "ip": "10.229.47.137", + "user": "root", + "key_filename": "/root/.yardstick_key" + }, + { + "name": "node2", + "role": "Compute", + "ip": "10.229.47.139", + "user": "root", + "key_filename": "/root/.yardstick_key" + } + ] + } + + def __init__(self, *args, **kwargs): + + super(HeatContextTestCase, self).__init__(*args, **kwargs) + + def setUp(self): + self.test_context = heat.HeatContext() + self.addCleanup(self._remove_contexts) + self.mock_context = mock.Mock(spec=heat.HeatContext()) + + def _remove_contexts(self): + if self.test_context in self.test_context.list: + self.test_context._delete_context() + + def test___init__(self): + self.assertIsNone(self.test_context._name) + self.assertIsNone(self.test_context._task_id) + self.assertFalse(self.test_context._flags.no_setup) + self.assertFalse(self.test_context._flags.no_teardown) + self.assertIsNone(self.test_context.stack) + self.assertEqual(self.test_context.networks, OrderedDict()) + self.assertEqual(self.test_context.servers, []) + self.assertEqual(self.test_context.placement_groups, []) + self.assertEqual(self.test_context.server_groups, []) + self.assertIsNone(self.test_context.keypair_name) + self.assertIsNone(self.test_context.secgroup_name) + self.assertIsNone(self.test_context.security_group) + self.assertEqual(self.test_context._server_map, {}) + self.assertIsNone(self.test_context._image) + self.assertIsNone(self.test_context._flavor) + self.assertIsNone(self.test_context._user) + self.assertIsNone(self.test_context.template_file) + self.assertIsNone(self.test_context.heat_parameters) + self.assertIsNone(self.test_context.key_filename) + self.assertTrue(self.test_context.yardstick_gen_key_file) + + @mock.patch.object(yaml_loader, 'read_yaml_file') + @mock.patch('yardstick.benchmark.contexts.heat.PlacementGroup') + @mock.patch('yardstick.benchmark.contexts.heat.ServerGroup') + @mock.patch('yardstick.benchmark.contexts.heat.Network') + @mock.patch('yardstick.benchmark.contexts.heat.Server') + def test_init(self, mock_server, mock_network, mock_sg, mock_pg, + mock_read_yaml): + + mock_read_yaml.return_value = self.HEAT_POD_SAMPLE + pgs = {'pgrp1': {'policy': 'availability'}} + sgs = {'servergroup1': {'policy': 'affinity'}} + networks = {'bar': {'cidr': '10.0.1.0/24'}} + servers = {'baz': {'floating_ip': True, 'placement': 'pgrp1'}} + attrs = {'name': 'foo', + 'file': 'pod.yaml', + 'task_id': '1234567890', + 'placement_groups': pgs, + 'server_groups': sgs, + 'networks': networks, + 'servers': servers} + + with mock.patch.object(openstack_utils, 'get_shade_client'), \ + mock.patch.object(openstack_utils, 'get_shade_operator_client'): + self.test_context.init(attrs) + + self.assertFalse(self.test_context._flags.no_setup) + self.assertFalse(self.test_context._flags.no_teardown) + self.assertEqual(self.test_context._name, "foo") + self.assertEqual(self.test_context._task_id, '1234567890') + self.assertEqual(self.test_context.name, "foo-12345678") + self.assertEqual(self.test_context.keypair_name, "foo-12345678-key") + self.assertEqual(self.test_context.secgroup_name, "foo-12345678-secgroup") + + mock_pg.assert_called_with('pgrp1', self.test_context, + pgs['pgrp1']['policy']) + mock_sg.assert_called_with('servergroup1', self.test_context, + sgs['servergroup1']['policy']) + self.assertEqual(len(self.test_context.placement_groups), 1) + self.assertEqual(len(self.test_context.server_groups), 1) + + mock_network.assert_called_with( + 'bar', self.test_context, networks['bar']) + self.assertEqual(len(self.test_context.networks), 1) + + mock_server.assert_called_with('baz', self.test_context, + servers['baz']) + self.assertEqual(len(self.test_context.servers), 1) + + def test_init_no_name_or_task_id(self): + attrs = {} + self.assertRaises(KeyError, self.test_context.init, attrs) + + def test_name(self): + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.assertEqual(self.test_context.name, 'foo-12345678') + self.assertEqual(self.test_context.assigned_name, 'foo') + + def test_name_flags(self): + self.test_context._flags = base.Flags( + **{"no_setup": True, "no_teardown": True}) + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + + self.assertEqual(self.test_context.name, 'foo') + self.assertEqual(self.test_context.assigned_name, 'foo') + + def test_init_no_setup_no_teardown(self): + + attrs = {'name': 'foo', + 'task_id': '1234567890', + 'placement_groups': {}, + 'server_groups': {}, + 'networks': {}, + 'servers': {}, + 'file': "pod.yaml", + 'flags': { + 'no_setup': True, + 'no_teardown': True, + }, + } + + with mock.patch.object(openstack_utils, 'get_shade_client'), \ + mock.patch.object(openstack_utils, 'get_shade_operator_client'): + self.test_context.init(attrs) + + self.assertTrue(self.test_context._flags.no_setup) + self.assertTrue(self.test_context._flags.no_teardown) + + def test_init_key_filename(self): + attrs = {'name': 'foo', + 'file': 'pod.yaml', + 'task_id': '1234567890', + 'server_groups': {}, + 'networks': {}, + 'servers': {}, + 'heat_template': "/root/clearwater.yaml", + 'key_filename': '/etc/yardstick/yardstick.pem'} + + with mock.patch.object(openstack_utils, 'get_shade_client'), \ + mock.patch.object(openstack_utils, 'get_shade_operator_client'): + self.test_context.init(attrs) + + self.assertIsNotNone(self.test_context.key_filename) + self.assertFalse(self.test_context.yardstick_gen_key_file) + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test__add_resources_to_template_no_servers(self, mock_template): + self.test_context._name = 'ctx' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.test_context.keypair_name = "ctx-key" + self.test_context.secgroup_name = "ctx-secgroup" + self.test_context.key_uuid = "2f2e4997-0a8e-4eb7-9fa4-f3f8fbbc393b" + netattrs = {'cidr': '10.0.0.0/24', 'provider': None, + 'external_network': 'ext_net'} + + self.test_context.networks = OrderedDict( + {"mynet": model.Network("mynet", self.test_context, + netattrs)}) + + self.test_context._add_resources_to_template(mock_template) + mock_template.add_keypair.assert_called_with( + "ctx-key", + "ctx-12345678") + mock_template.add_security_group.assert_called_with("ctx-secgroup", None) + mock_template.add_network.assert_called_with( + "ctx-12345678-mynet", 'physnet1', None, None, None, None) + mock_template.add_router.assert_called_with( + "ctx-12345678-mynet-router", + netattrs["external_network"], + "ctx-12345678-mynet-subnet") + mock_template.add_router_interface.assert_called_with( + "ctx-12345678-mynet-router-if0", + "ctx-12345678-mynet-router", + "ctx-12345678-mynet-subnet") + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test_attrs_get(self, *args): + image, flavor, user = expected_tuple = 'foo1', 'foo2', 'foo3' + self.assertNotEqual(self.test_context.image, image) + self.assertNotEqual(self.test_context.flavor, flavor) + self.assertNotEqual(self.test_context.user, user) + self.test_context._image = image + self.test_context._flavor = flavor + self.test_context._user = user + attr_tuple = self.test_context.image, self.test_context.flavor, self.test_context.user + self.assertEqual(attr_tuple, expected_tuple) + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test_attrs_set_negative(self, *args): + with self.assertRaises(AttributeError): + self.test_context.image = 'foo' + + with self.assertRaises(AttributeError): + self.test_context.flavor = 'foo' + + with self.assertRaises(AttributeError): + self.test_context.user = 'foo' + + def test__create_new_stack(self): + template = mock.Mock() + self.test_context._create_new_stack(template) + template.create.assert_called_once() + + def test__create_new_stack_stack_create_failed(self): + template = mock.Mock() + template.create.side_effect = y_exc.HeatTemplateError + + self.assertRaises(y_exc.HeatTemplateError, + self.test_context._create_new_stack, + template) + + def test__create_new_stack_keyboard_interrupt(self): + template = mock.Mock() + template.create.side_effect = KeyboardInterrupt + self.assertRaises(y_exc.StackCreationInterrupt, + self.test_context._create_new_stack, + template) + + @mock.patch.object(os.path, 'exists', return_value=True) + @mock.patch.object(heat.HeatContext, '_add_resources_to_template') + @mock.patch.object(heat.HeatContext, '_create_new_stack') + def test_deploy_stack_creation_failed(self, mock_create, + mock_resources_template, mock_path_exists): + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = 'foo-12345678' + mock_create.side_effect = y_exc.HeatTemplateError + self.assertRaises(y_exc.HeatTemplateError, + self.test_context.deploy) + + mock_path_exists.assert_called() + mock_resources_template.assert_called_once() + + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(ssh.SSH, 'gen_keys') + @mock.patch.object(heat, 'HeatTemplate') + def test_deploy(self, mock_template, mock_genkeys, mock_path_exists): + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.test_context.template_file = '/bar/baz/some-heat-file' + self.test_context.heat_parameters = {'image': 'cirros'} + self.test_context.get_neutron_info = mock.MagicMock() + self.test_context.deploy() + + mock_template.assert_called_with( + 'foo-12345678', template_file='/bar/baz/some-heat-file', + heat_parameters={'image': 'cirros'}, + os_cloud_config=self.test_context._flags.os_cloud_config) + self.assertIsNotNone(self.test_context.stack) + key_filename = ''.join( + [consts.YARDSTICK_ROOT_PATH, + 'yardstick/resources/files/yardstick_key-', + self.test_context._name_task_id]) + mock_genkeys.assert_called_once_with(key_filename) + mock_path_exists.assert_any_call(key_filename) + + @mock.patch.object(heat, 'HeatTemplate') + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(ssh.SSH, 'gen_keys') + @mock.patch.object(heat.HeatContext, '_retrieve_existing_stack') + @mock.patch.object(heat.HeatContext, '_create_new_stack') + def test_deploy_no_setup(self, mock_create_new_stack, + mock_retrieve_existing_stack, mock_genkeys, mock_path_exists, + *args): + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context.template_file = '/bar/baz/some-heat-file' + self.test_context.heat_parameters = {'image': 'cirros'} + self.test_context.get_neutron_info = mock.MagicMock() + self.test_context._flags.no_setup = True + self.test_context.deploy() + + mock_create_new_stack.assert_not_called() + mock_retrieve_existing_stack.assert_called_with(self.test_context.name) + self.assertIsNotNone(self.test_context.stack) + key_filename = ''.join( + [consts.YARDSTICK_ROOT_PATH, + 'yardstick/resources/files/yardstick_key-', + self.test_context._name]) + mock_genkeys.assert_called_once_with(key_filename) + mock_path_exists.assert_any_call(key_filename) + + @mock.patch.object(heat, 'HeatTemplate') + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(ssh.SSH, 'gen_keys') + @mock.patch.object(heat.HeatContext, '_create_new_stack') + @mock.patch.object(heat.HeatContext, '_retrieve_existing_stack', + return_value=None) + def test_deploy_try_retrieve_context_does_not_exist(self, + mock_retrieve_stack, mock_create_new_stack, mock_genkeys, + mock_path_exists, *args): + self.test_context._name = 'demo' + self.test_context._task_id = '1234567890' + self.test_context._flags.no_setup = True + self.test_context.template_file = '/bar/baz/some-heat-file' + self.test_context.get_neutron_info = mock.MagicMock() + self.test_context.deploy() + + mock_retrieve_stack.assert_called_once_with(self.test_context._name) + mock_create_new_stack.assert_called() + key_filename = ''.join( + [consts.YARDSTICK_ROOT_PATH, + 'yardstick/resources/files/yardstick_key-', + self.test_context._name]) + mock_genkeys.assert_called_once_with(key_filename) + mock_path_exists.assert_any_call(key_filename) + + @mock.patch.object(heat, 'HeatTemplate', return_value='heat_template') + @mock.patch.object(heat.HeatContext, '_add_resources_to_template') + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(ssh.SSH, 'gen_keys') + def test_deploy_ssh_key_before_adding_resources(self, mock_genkeys, + mock_path_exists, mock_add_resources, *args): + mock_manager = mock.Mock() + mock_manager.attach_mock(mock_add_resources, + '_add_resources_to_template') + mock_manager.attach_mock(mock_genkeys, 'gen_keys') + mock_manager.reset_mock() + self.test_context._name_task_id = 'demo-12345678' + self.test_context.get_neutron_info = mock.Mock() + with mock.patch.object(self.test_context, '_create_new_stack') as \ + mock_create_stack, \ + mock.patch.object(self.test_context, 'get_neutron_info') as \ + mock_neutron_info: + self.test_context.deploy() + + mock_neutron_info.assert_called_once() + mock_create_stack.assert_called_once() + key_filename = ''.join( + [consts.YARDSTICK_ROOT_PATH, + 'yardstick/resources/files/yardstick_key-', + self.test_context._name_task_id]) + mock_genkeys.assert_called_once_with(key_filename) + mock_path_exists.assert_any_call(key_filename) + + mock_call_gen_keys = mock.call.gen_keys(key_filename) + mock_call_add_resources = ( + mock.call._add_resources_to_template('heat_template')) + self.assertTrue(mock_manager.mock_calls.index(mock_call_gen_keys) < + mock_manager.mock_calls.index(mock_call_add_resources)) + + @mock.patch.object(heat, 'HeatTemplate') + @mock.patch.object(ssh.SSH, 'gen_keys') + @mock.patch.object(heat.HeatContext, '_create_new_stack') + def test_deploy_with_key_filename_provided(self, mock_create_new_stack, + mock_gen_keys, *args): + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.test_context.template_file = '/bar/baz/some-heat-file' + self.test_context.heat_parameters = {'image': 'cirros'} + self.test_context.yardstick_gen_key_file = False + self.test_context.key_filename = '/etc/yardstick/yardstick.pem' + self.test_context.get_neutron_info = mock.MagicMock() + self.test_context.deploy() + + mock_create_new_stack.assert_called() + mock_gen_keys.assert_not_called() + + def test_check_for_context(self): + pass + # check that the context exists + + def test_add_server_port(self): + network1 = mock.MagicMock() + network2 = mock.MagicMock() + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.test_context.stack = mock.MagicMock() + self.test_context.networks = { + 'a': network1, + 'c': network2, + } + self.test_context.stack.outputs = { + u'b': u'10.20.30.45', + u'b-subnet_id': 1, + u'foo-12345678-a-subnet-cidr': u'10.20.0.0/15', + u'foo-12345678-a-subnet-gateway_ip': u'10.20.30.1', + u'b-mac_address': u'00:01', + u'b-device_id': u'dev21', + u'b-network_id': u'net789', + u'd': u'40.30.20.15', + u'd-subnet_id': 2, + u'foo-12345678-c-subnet-cidr': u'40.30.0.0/18', + u'foo-12345678-c-subnet-gateway_ip': u'40.30.20.254', + u'd-mac_address': u'00:10', + u'd-device_id': u'dev43', + u'd-network_id': u'net987', + u'e': u'40.30.20.15', + u'e-subnet_id': 2, + u'e-mac_address': u'00:10', + u'e-device_id': u'dev43', + u'e-network_id': u'net987', + } + server = mock.MagicMock() + server.private_ip = None + server.ports = OrderedDict([ + ('a', [{'stack_name': 'b', 'port': 'port_a'}]), + ('c', [{'stack_name': 'd', 'port': 'port_c'}, + {'stack_name': 'e', 'port': 'port_f'}]), + ]) + + expected = { + "private_ip": '10.20.30.45', + "subnet_id": 1, + "subnet_cidr": '10.20.0.0/15', + "network": '10.20.0.0', + "netmask": '255.254.0.0', + "name": "port_a", + "gateway_ip": '10.20.30.1', + "mac_address": '00:01', + "device_id": 'dev21', + "network_id": 'net789', + "network_name": 'a', + "local_mac": '00:01', + "local_ip": '10.20.30.45', + } + self.test_context.add_server_port(server) + self.assertEqual(server.private_ip, '10.20.30.45') + self.assertEqual(len(server.interfaces), 3) + self.assertDictEqual(server.interfaces['port_a'], expected) + + @mock.patch('yardstick.benchmark.contexts.heat.os') + @mock.patch.object(heat.HeatContext, '_delete_key_file') + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test_undeploy(self, mock_template, mock_delete_key, *args): + self.test_context.stack = mock_template + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + # mock_os.path.exists.return_value = True + self.test_context.key_filename = 'foo/bar/foobar' + self.test_context.undeploy() + mock_delete_key.assert_called() + mock_template.delete.assert_called_once() + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test_undeploy_no_teardown(self, mock_template): + self.test_context.stack = mock_template + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._flags.no_teardown = True + self.test_context.undeploy() + + mock_template.delete.assert_not_called() + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + @mock.patch('yardstick.benchmark.contexts.heat.os') + def test_undeploy_key_filename(self, mock_os, mock_template): + self.test_context.stack = mock_template + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id) + mock_os.path.exists.return_value = True + self.test_context.key_filename = 'foo/bar/foobar' + self.assertIsNone(self.test_context.undeploy()) + + @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources") + def test__get_server_found_dict(self, *args): + """ + Use HeatContext._get_server to get a server that matches + based on a dictionary input. + """ + foo2_server = mock.Mock() + foo2_server.key_filename = None + foo2_server.private_ip = '10.0.0.2' + foo2_server.public_ip = '127.0.0.2' + foo2_server.context.user = 'oof' + + baz3_server = mock.Mock() + baz3_server.key_filename = None + baz3_server.private_ip = '10.0.0.3' + baz3_server.public_ip = '127.0.0.3' + baz3_server.context.user = 'zab' + + self.test_context._name = 'bar' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.test_context._user = 'bot' + self.test_context.stack = mock.Mock() + self.test_context.stack.outputs = { + 'private_ip': '10.0.0.1', + 'public_ip': '127.0.0.1', + } + self.test_context._server_map = { + 'baz3': baz3_server, + 'foo2': foo2_server, + } + + attr_name = { + 'name': 'foo.bar-12345678', + 'private_ip_attr': 'private_ip', + 'public_ip_attr': 'public_ip', + } + self.test_context.key_uuid = 'foo-42' + result = self.test_context._get_server(attr_name) + self.assertEqual(result['user'], 'bot') + self.assertEqual(result['ip'], '127.0.0.1') + self.assertEqual(result['private_ip'], '10.0.0.1') + + @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources") + def test__get_server_found_dict_no_attrs(self, *args): + """ + Use HeatContext._get_server to get a server that matches + based on a dictionary input. + """ + foo2_server = mock.Mock() + foo2_server.private_ip = '10.0.0.2' + foo2_server.public_ip = '127.0.0.2' + foo2_server.context.user = 'oof' + + baz3_server = mock.Mock() + baz3_server.private_ip = '10.0.0.3' + baz3_server.public_ip = '127.0.0.3' + baz3_server.context.user = 'zab' + + self.test_context._name = 'bar' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.test_context._user = 'bot' + self.test_context.stack = mock.Mock() + self.test_context.stack.outputs = { + 'private_ip': '10.0.0.1', + 'public_ip': '127.0.0.1', + } + self.test_context._server_map = { + 'baz3': baz3_server, + 'foo2': foo2_server, + } + + attr_name = { + 'name': 'foo.bar-12345678', + } + + self.test_context.key_uuid = 'foo-42' + result = self.test_context._get_server(attr_name) + self.assertEqual(result['user'], 'bot') + # no private ip attr mapping in the map results in None value in the result + self.assertIsNone(result['private_ip']) + # no public ip attr mapping in the map results in no value in the result + self.assertNotIn('ip', result) + + @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources") + def test__get_server_found_not_dict(self, *args): + """ + Use HeatContext._get_server to get a server that matches + based on a non-dictionary input + """ + foo2_server = mock.Mock() + foo2_server.private_ip = '10.0.0.2' + foo2_server.public_ip = '127.0.0.2' + foo2_server.context.user = 'oof' + + baz3_server = mock.Mock() + baz3_server.private_ip = '10.0.0.3' + baz3_server.public_ip = None + baz3_server.context.user = 'zab' + + self.test_context._name = 'bar1' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = 'bar1-12345678' + self.test_context.stack = mock.Mock() + self.test_context.stack.outputs = { + 'private_ip': '10.0.0.1', + 'public_ip': '127.0.0.1', + } + self.test_context.generate_routing_table = mock.MagicMock(return_value=[]) + + self.test_context._server_map = { + 'baz3': baz3_server, + 'foo2': foo2_server, + } + + attr_name = 'baz3' + result = self.test_context._get_server(attr_name) + self.assertEqual(result['user'], 'zab') + self.assertEqual(result['private_ip'], '10.0.0.3') + # no public_ip on the server results in no value in the result + self.assertNotIn('public_ip', result) + + @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources") + def test__get_server_none_found_not_dict(self, *args): + """ + Use HeatContext._get_server to not get a server due to + None value associated with the match to a non-dictionary + input + """ + foo2_server = mock.Mock() + foo2_server.private_ip = '10.0.0.2' + foo2_server.public_ip = '127.0.0.2' + foo2_server.context.user = 'oof' + + baz3_server = mock.Mock() + baz3_server.private_ip = '10.0.0.3' + baz3_server.public_ip = None + baz3_server.context.user = 'zab' + + self.test_context._name = 'bar1' + self.test_context.stack = mock.Mock() + self.test_context.stack.outputs = { + 'private_ip': '10.0.0.1', + 'public_ip': '127.0.0.1', + } + self.test_context._server_map = { + 'baz3': baz3_server, + 'foo2': foo2_server, + 'wow4': None, + } + + self.test_context.key_uuid = 'foo-42' + attr_name = 'wow4' + result = self.test_context._get_server(attr_name) + self.assertIsNone(result) + + @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources") + def test__get_server_not_found_dict(self, *args): + """ + Use HeatContext._get_server to not get a server for lack + of a match to a dictionary input + """ + foo2_server = mock.Mock() + foo2_server.private_ip = '10.0.0.2' + foo2_server.public_ip = '127.0.0.2' + foo2_server.context.user = 'oof' + + baz3_server = mock.Mock() + baz3_server.private_ip = '10.0.0.3' + baz3_server.public_ip = None + baz3_server.context.user = 'zab' + + self.test_context._name = 'bar1' + self.test_context._task_id = '1235467890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.test_context.stack = mock.Mock() + self.test_context.stack.outputs = { + 'private_ip': '10.0.0.1', + 'public_ip': '127.0.0.1', + } + self.test_context._server_map = { + 'baz3': baz3_server, + 'foo2': foo2_server, + } + + self.test_context.key_uuid = 'foo-42' + attr_name = { + 'name': 'foo.wow4', + 'private_ip_attr': 'private_ip', + 'public_ip_attr': 'public_ip', + } + result = self.test_context._get_server(attr_name) + self.assertIsNone(result) + + @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources") + def test__get_server_not_found_not_dict(self, *args): + """ + Use HeatContext._get_server to not get a server for lack + of a match to a non-dictionary input + """ + foo2_server = mock.Mock() + foo2_server.private_ip = '10.0.0.2' + foo2_server.public_ip = '127.0.0.2' + foo2_server.context.user = 'oof' + + baz3_server = mock.Mock() + baz3_server.private_ip = '10.0.0.3' + baz3_server.public_ip = None + baz3_server.context.user = 'zab' + + self.mock_context._name = 'bar1' + self.test_context.stack = mock.Mock() + self.mock_context.stack.outputs = { + 'private_ip': '10.0.0.1', + 'public_ip': '127.0.0.1', + } + self.mock_context._server_map = { + 'baz3': baz3_server, + 'foo2': foo2_server, + } + + self.test_context.key_uuid = 'foo-42' + attr_name = 'foo.wow4' + result = self.test_context._get_server(attr_name) + self.assertIsNone(result) + + @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources") + def test__get_server_found_dict_found_interfaces_dict(self, *args): + """ + Use HeatContext._get_server to get a server that matches + based on a dictionary input. + """ + self.test_context._name = 'bar' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.test_context._user = 'bot' + self.test_context.stack = mock.Mock() + self.test_context.stack.outputs = { + 'private_ip': '10.0.0.1', + 'public_ip': '127.0.0.1', + 'local_mac_addr': '64:00:6a:18:0f:d6', + 'private_netmask': '255.255.255.0', + 'private_net_name': 'private_network', + 'private_net_gateway': '127.0.0.254' + } + + attr_name = { + 'name': 'foo.bar-12345678', + 'private_ip_attr': 'private_ip', + 'public_ip_attr': 'public_ip', + 'interfaces': { + 'data_net': { + 'local_ip': 'private_ip', + 'local_mac': 'local_mac_addr', + 'netmask': 'private_netmask', + 'network': 'private_net_name', + 'gateway_ip': 'private_net_gateway' + } + } + } + self.test_context.key_uuid = 'foo-42' + result = self.test_context._get_server(attr_name) + self.assertIsInstance(result['interfaces'], collections.Mapping) + for key in attr_name.get("interfaces").keys(): + self.assertEqual(result['interfaces'][key]['local_ip'], '10.0.0.1') + self.assertEqual(result['interfaces'][key]['local_mac'], '64:00:6a:18:0f:d6') + self.assertEqual(result['interfaces'][key]['netmask'], '255.255.255.0') + self.assertEqual(result['interfaces'][key]['gateway_ip'], '127.0.0.254') + + # TODO: Split this into more granular tests + def test__get_network(self): + network1 = mock.MagicMock() + network1.name = 'net_1' + network1.vld_id = 'vld111' + network1.segmentation_id = 'seg54' + network1.network_type = 'type_a' + network1.physical_network = 'phys' + + network2 = mock.MagicMock() + network2.name = 'net_2' + network2.segmentation_id = 'seg45' + network2.network_type = 'type_b' + network2.physical_network = 'virt' + + self.test_context.networks = { + 'a': network1, + 'b': network2, + } + + attr_name = None + self.assertIsNone(self.test_context._get_network(attr_name)) + + attr_name = {} + self.assertIsNone(self.test_context._get_network(attr_name)) + + attr_name = {'network_type': 'nosuch'} + self.assertIsNone(self.test_context._get_network(attr_name)) + + attr_name = 'vld777' + self.assertIsNone(self.test_context._get_network(attr_name)) + + attr_name = {'segmentation_id': 'seg45'} + expected = { + "name": 'net_2', + "segmentation_id": 'seg45', + "network_type": 'type_b', + "physical_network": 'virt', + } + result = self.test_context._get_network(attr_name) + self.assertDictEqual(result, expected) + + attr_name = 'a' + expected = { + "name": 'net_1', + "segmentation_id": 'seg54', + "network_type": 'type_a', + "physical_network": 'phys', + } + result = self.test_context._get_network(attr_name) + self.assertDictEqual(result, expected) + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + def test__get_physical_nodes(self): + self.test_context.nodes = {} + nodes = self.test_context._get_physical_nodes() + self.assertEquals(nodes, {}) + + @mock.patch.object(yaml_loader, 'read_yaml_file') + def test__get_physical_node_for_server(self, mock_read_yaml): + attrs = {'name': 'foo', + 'task_id': '12345678', + 'file': "pod.yaml", + 'servers': {'vnf': {}}, + 'networks': {'mgmt': {'cidr': '10.0.1.0/24'}} + } + + with mock.patch.object(openstack_utils, 'get_shade_client'), \ + mock.patch.object(openstack_utils, 'get_shade_operator_client'): + mock_read_yaml.return_value = self.HEAT_POD_SAMPLE + self.test_context.init(attrs) + + with mock.patch('yardstick.common.openstack_utils.get_server') as mock_get_server: + mock_get_server.return_value = {'vnf': {}} + + # When server is not from this context + result = self.test_context._get_physical_node_for_server('node1.foo-context') + self.assertIsNone(result) + + # When node_name is not from this context + result = self.test_context._get_physical_node_for_server('fake.foo-12345678') + self.assertIsNone(result) + + mock_munch = mock.Mock() + mock_munch.toDict = mock.Mock(return_value={ + 'OS-EXT-SRV-ATTR:hypervisor_hostname': 'hypervisor_hostname' + }) + mock_get_server.return_value = mock_munch + + hypervisor = mock.Mock() + hypervisor.hypervisor_hostname = 'hypervisor_hostname' + hypervisor.host_ip = '10.229.47.137' + + self.test_context.operator_client.list_hypervisors = mock.Mock( + return_value=[hypervisor]) + + mock_get_server.return_value = mock_munch + + result = self.test_context._get_physical_node_for_server('vnf.foo-12345678') + self.assertEqual(result, 'node1.foo') diff --git a/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py b/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py new file mode 100644 index 000000000..b526e7cc7 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py @@ -0,0 +1,273 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import collections +import time + +import mock +import unittest + +from yardstick.benchmark import contexts +from yardstick.benchmark.contexts import base +from yardstick.benchmark.contexts import kubernetes +from yardstick.common import constants +from yardstick.common import exceptions +from yardstick.common import kubernetes_utils as k8s_utils +from yardstick.orchestrator import kubernetes as orchestrator_kubernetes + + +CONTEXT_CFG = { + 'type': contexts.CONTEXT_KUBERNETES, + 'name': 'k8s', + 'task_id': '1234567890', + 'servers': { + 'host': { + 'image': 'openretriever/yardstick', + 'command': '/bin/bash', + 'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; ' + 'service ssh restart;while true ; do sleep 10000; done'] + }, + 'target': { + 'image': 'openretriever/yardstick', + 'command': '/bin/bash', + 'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; ' + 'service ssh restart;while true ; do sleep 10000; done'] + } + }, + 'networks': { + 'flannel': { + 'args': 'flannel_args', + 'plugin': 'flannel' + }, + 'sriov01': { + 'args': 'sriov_args', + 'plugin': 'sriov' + }, + } +} + + +class NodePort(object): + def __init__(self): + self.node_port = 30000 + self.port = constants.SSH_PORT + self.name = 'port_name' + self.protocol = 'TCP' + self.target_port = constants.SSH_PORT + + +class Service(object): + def __init__(self): + self.ports = [NodePort()] + + +class Status(object): + def __init__(self): + self.pod_ip = '172.16.10.131' + + +class Pod(object): + def __init__(self): + self.status = Status() + + +class KubernetesTestCase(unittest.TestCase): + + def setUp(self): + self.k8s_context = kubernetes.KubernetesContext() + self.addCleanup(self._remove_contexts) + self.k8s_context.init(CONTEXT_CFG) + + @staticmethod + def _remove_contexts(): + for context in base.Context.list: + context._delete_context() + base.Context.list = [] + + @mock.patch.object(kubernetes.KubernetesContext, '_delete_services') + @mock.patch.object(kubernetes.KubernetesContext, '_delete_ssh_key') + @mock.patch.object(kubernetes.KubernetesContext, '_delete_rcs') + @mock.patch.object(kubernetes.KubernetesContext, '_delete_pods') + @mock.patch.object(kubernetes.KubernetesContext, '_delete_networks') + @mock.patch.object(kubernetes.KubernetesContext, '_delete_crd') + def test_undeploy(self, mock_delete_pods, mock_delete_rcs, + mock_delete_ssh, mock_delete_services, + mock_delete_networks, mock_delete_crd): + + self.k8s_context.undeploy() + mock_delete_ssh.assert_called_once() + mock_delete_rcs.assert_called_once() + mock_delete_pods.assert_called_once() + mock_delete_services.assert_called_once() + mock_delete_networks.assert_called_once() + mock_delete_crd.assert_called_once() + + @mock.patch.object(kubernetes.KubernetesContext, '_create_services') + @mock.patch.object(kubernetes.KubernetesContext, '_wait_until_running') + @mock.patch.object(orchestrator_kubernetes.KubernetesTemplate, + 'get_rc_pods') + @mock.patch.object(kubernetes.KubernetesContext, '_create_rcs') + @mock.patch.object(kubernetes.KubernetesContext, '_set_ssh_key') + @mock.patch.object(kubernetes.KubernetesContext, '_create_networks') + @mock.patch.object(kubernetes.KubernetesContext, '_create_crd') + def test_deploy(self, mock_set_ssh_key, mock_create_rcs, mock_get_rc_pods, + mock_wait_until_running, mock_create_services, + mock_create_networks, mock_create_crd): + + with mock.patch.object(time, 'sleep'): + self.k8s_context.deploy() + mock_set_ssh_key.assert_called_once() + mock_create_rcs.assert_called_once() + mock_create_services.assert_called_once() + mock_get_rc_pods.assert_called_once() + mock_wait_until_running.assert_called_once() + mock_create_networks.assert_called_once() + mock_create_crd.assert_called_once() + + @mock.patch.object(kubernetes, 'paramiko', **{"resource_filename.return_value": ""}) + @mock.patch.object(kubernetes, 'pkg_resources', **{"resource_filename.return_value": ""}) + @mock.patch.object(kubernetes, 'utils') + @mock.patch.object(kubernetes, 'open', create=True) + @mock.patch.object(k8s_utils, 'delete_config_map') + @mock.patch.object(k8s_utils, 'create_config_map') + def test_ssh_key(self, mock_create, mock_delete, *args): + self.k8s_context._set_ssh_key() + self.k8s_context._delete_ssh_key() + + mock_create.assert_called_once() + mock_delete.assert_called_once() + + @mock.patch.object(k8s_utils, 'read_pod_status') + def test_wait_until_running(self, mock_read_pod_status): + + self.k8s_context.template.pods = ['server'] + mock_read_pod_status.return_value = 'Running' + self.k8s_context._wait_until_running() + + @mock.patch.object(k8s_utils, 'get_pod_by_name') + @mock.patch.object(kubernetes.KubernetesContext, '_get_node_ip') + def test_get_server(self, mock_get_node_ip, mock_get_pod_by_name): + mock_get_pod_by_name.return_value = Pod() + mock_get_node_ip.return_value = '172.16.10.131' + with mock.patch.object(self.k8s_context, '_get_service_ports') as \ + mock_get_sports: + mock_get_sports.return_value = [ + {'port': constants.SSH_PORT, 'node_port': 30000}] + server = self.k8s_context._get_server('server_name') + self.assertEqual('server_name', server['name']) + self.assertEqual(30000, server['ssh_port']) + + @mock.patch.object(kubernetes.KubernetesContext, '_create_rc') + def test_create_rcs(self, mock_create_rc): + self.k8s_context._create_rcs() + mock_create_rc.assert_called() + + @mock.patch.object(k8s_utils, 'create_replication_controller') + def test_create_rc(self, mock_create_replication_controller): + self.k8s_context._create_rc({}) + mock_create_replication_controller.assert_called_once() + + @mock.patch.object(kubernetes.KubernetesContext, '_delete_rc') + def test_delete_rcs(self, mock_delete_rc): + self.k8s_context._delete_rcs() + mock_delete_rc.assert_called() + + @mock.patch.object(k8s_utils, 'delete_replication_controller') + def test_delete_rc(self, mock_delete_replication_controller): + self.k8s_context._delete_rc({}) + mock_delete_replication_controller.assert_called_once() + + @mock.patch.object(k8s_utils, 'get_node_list') + def test_get_node_ip(self, mock_get_node_list): + self.k8s_context._get_node_ip() + mock_get_node_list.assert_called_once() + + @mock.patch.object(orchestrator_kubernetes.ServiceNodePortObject, 'create') + def test_create_services(self, mock_create): + self.k8s_context._create_services() + mock_create.assert_called() + + @mock.patch.object(orchestrator_kubernetes.ServiceNodePortObject, 'delete') + def test_delete_services(self, mock_delete): + self.k8s_context._delete_services() + mock_delete.assert_called() + + def test_init(self): + self.k8s_context._delete_context() + with mock.patch.object(orchestrator_kubernetes, 'KubernetesTemplate', + return_value='fake_template') as mock_k8stemplate: + self.k8s_context = kubernetes.KubernetesContext() + self.k8s_context.init(CONTEXT_CFG) + mock_k8stemplate.assert_called_once_with(self.k8s_context.name, + CONTEXT_CFG) + self.assertEqual('fake_template', self.k8s_context.template) + self.assertEqual(2, len(self.k8s_context._networks)) + self.assertIn('flannel', self.k8s_context._networks.keys()) + self.assertIn('sriov01', self.k8s_context._networks.keys()) + + def test__get_physical_nodes(self): + result = self.k8s_context._get_physical_nodes() + self.assertIsNone(result) + + def test__get_physical_node_for_server(self): + result = self.k8s_context._get_physical_node_for_server("fake") + self.assertIsNone(result) + + def test__get_network(self): + networks = collections.OrderedDict([('n1', 'data1'), ('n2', 'data2')]) + self.k8s_context._networks = networks + self.assertEqual({'name': 'n1'}, self.k8s_context._get_network('n1')) + self.assertEqual({'name': 'n2'}, self.k8s_context._get_network('n2')) + self.assertIsNone(self.k8s_context._get_network('n3')) + + @mock.patch.object(orchestrator_kubernetes.KubernetesTemplate, + 'get_rc_by_name') + def test__get_interfaces(self, mock_get_rc): + rc = orchestrator_kubernetes.ReplicationControllerObject('rc_name') + rc._networks = ['net1', 'net2'] + mock_get_rc.return_value = rc + expected = {'net1': {'network_name': 'net1', + 'local_mac': None, + 'local_ip': None}, + 'net2': {'network_name': 'net2', + 'local_mac': None, + 'local_ip': None}} + self.assertEqual(expected, self.k8s_context._get_interfaces('rc_name')) + + @mock.patch.object(orchestrator_kubernetes.KubernetesTemplate, + 'get_rc_by_name') + def test__get_interfaces_no_networks(self, mock_get_rc): + rc = orchestrator_kubernetes.ReplicationControllerObject('rc_name') + mock_get_rc.return_value = rc + self.assertEqual({}, self.k8s_context._get_interfaces('rc_name')) + + @mock.patch.object(orchestrator_kubernetes.KubernetesTemplate, + 'get_rc_by_name', return_value=None) + def test__get_interfaces_no_rc(self, *args): + self.assertEqual({}, self.k8s_context._get_interfaces('rc_name')) + + @mock.patch.object(k8s_utils, 'get_service_by_name', + return_value=Service()) + def test__get_service_ports(self, mock_get_service_by_name): + name = 'rc_name' + service_ports = self.k8s_context._get_service_ports(name) + mock_get_service_by_name.assert_called_once_with(name + '-service') + expected = {'node_port': 30000, + 'port': constants.SSH_PORT, + 'name': 'port_name', + 'protocol': 'TCP', + 'target_port': constants.SSH_PORT} + self.assertEqual(expected, service_ports[0]) + + @mock.patch.object(k8s_utils, 'get_service_by_name', + return_value=None) + def test__get_service_ports_exception(self, *args): + name = 'rc_name' + with self.assertRaises(exceptions.KubernetesServiceObjectNotDefined): + self.k8s_context._get_service_ports(name) diff --git a/yardstick/tests/unit/benchmark/contexts/test_model.py b/yardstick/tests/unit/benchmark/contexts/test_model.py new file mode 100644 index 000000000..20cc00b4e --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/test_model.py @@ -0,0 +1,586 @@ +############################################################################## +# Copyright (c) 2015 Ericsson AB and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.contexts.model + +from __future__ import absolute_import +import unittest +import mock + +from yardstick.benchmark.contexts import model + + +class ObjectTestCase(unittest.TestCase): + + def setUp(self): + self.mock_context = mock.Mock() + + def test_construct(self): + + test_object = model.Object('foo', self.mock_context) + + self.assertEqual(test_object.name, 'foo') + self.assertEqual(test_object._context, self.mock_context) + self.assertIsNone(test_object.stack_name) + self.assertIsNone(test_object.stack_id) + + def test_dn(self): + + self.mock_context.name = 'bar' + test_object = model.Object('foo', self.mock_context) + + self.assertEqual('foo.bar', test_object.dn) + + +class PlacementGroupTestCase(unittest.TestCase): + + def setUp(self): + self.mock_context = mock.Mock() + self.mock_context.name = 'bar' + + def tearDown(self): + model.PlacementGroup.map = {} + + def test_sucessful_construct(self): + + test_pg = model.PlacementGroup('foo', self.mock_context, 'affinity') + + self.assertEqual(test_pg.name, 'foo') + self.assertEqual(test_pg.members, set()) + self.assertEqual(test_pg.stack_name, 'bar-foo') + self.assertEqual(test_pg.policy, 'affinity') + + test_map = {'foo': test_pg} + self.assertEqual(model.PlacementGroup.map, test_map) + + def test_wrong_policy_in_construct(self): + + self.assertRaises(ValueError, model.PlacementGroup, 'foo', + self.mock_context, 'baz') + + def test_add_member(self): + + test_pg = model.PlacementGroup('foo', self.mock_context, 'affinity') + test_pg.add_member('foo') + + self.assertEqual(test_pg.members, set(['foo'])) + + def test_get_name_successful(self): + + model.PlacementGroup.map = {'foo': True} + self.assertTrue(model.PlacementGroup.get('foo')) + + def test_get_name_unsuccessful(self): + + self.assertIsNone(model.PlacementGroup.get('foo')) + + +class RouterTestCase(unittest.TestCase): + + def test_construct(self): + + mock_context = mock.Mock() + mock_context.name = 'baz' + test_router = model.Router('foo', 'bar', mock_context, 'qux') + + self.assertEqual(test_router.stack_name, 'baz-bar-foo') + self.assertEqual(test_router.stack_if_name, 'baz-bar-foo-if0') + self.assertEqual(test_router.external_gateway_info, 'qux') + + +class NetworkTestCase(unittest.TestCase): + + def setUp(self): + self.mock_context = mock.Mock() + self.mock_context.name = 'bar' + + def tearDown(self): + model.Network.list = [] + + def test_construct_no_external_network(self): + + attrs = {'cidr': '10.0.0.0/24'} + test_network = model.Network('foo', self.mock_context, attrs) + + self.assertEqual(test_network.stack_name, 'bar-foo') + self.assertEqual(test_network.subnet_stack_name, 'bar-foo-subnet') + self.assertEqual(test_network.subnet_cidr, attrs['cidr']) + self.assertIsNone(test_network.router) + self.assertIn(test_network, model.Network.list) + + def test_construct_has_external_network(self): + + attrs = {'external_network': 'ext_net'} + test_network = model.Network('foo', self.mock_context, attrs) + exp_router = model.Router('router', 'foo', self.mock_context, + 'ext_net') + + self.assertEqual(test_network.router.stack_name, exp_router.stack_name) + self.assertEqual(test_network.router.stack_if_name, + exp_router.stack_if_name) + self.assertEqual(test_network.router.external_gateway_info, + exp_router.external_gateway_info) + + def test_has_route_to(self): + + attrs = {'external_network': 'ext_net'} + test_network = model.Network('foo', self.mock_context, attrs) + + self.assertTrue(test_network.has_route_to('ext_net')) + + def test_has_no_route_to(self): + + attrs = {} + test_network = model.Network('foo', self.mock_context, attrs) + + self.assertFalse(test_network.has_route_to('ext_net')) + + @mock.patch('yardstick.benchmark.contexts.model.Network.has_route_to') + def test_find_by_route_to(self, mock_has_route_to): + + mock_network = mock.Mock() + model.Network.list = [mock_network] + mock_has_route_to.return_value = True + + self.assertIs(mock_network, model.Network.find_by_route_to('foo')) + + def test_find_external_network(self): + + mock_network = mock.Mock() + mock_network.router = mock.Mock() #pylint ignore assignment-from-none + mock_network.router.external_gateway_info = 'ext_net' + model.Network.list = [mock_network] + + self.assertEqual(model.Network.find_external_network(), 'ext_net') + + def test_construct_gateway_ip_is_null(self): + + attrs = {'gateway_ip': 'null'} + test_network = model.Network('foo', self.mock_context, attrs) + self.assertEqual(test_network.gateway_ip, 'null') + + def test_construct_gateway_ip_is_none(self): + + attrs = {'gateway_ip': None} + test_network = model.Network('foo', self.mock_context, attrs) + self.assertEqual(test_network.gateway_ip, 'null') + + def test_construct_gateway_ip_is_absent(self): + + attrs = {} + test_network = model.Network('foo', self.mock_context, attrs) + self.assertIsNone(test_network.gateway_ip) + + +class ServerTestCase(unittest.TestCase): + + def setUp(self): + self.mock_context = mock.Mock() + self.mock_context.name = 'bar' + self.mock_context.keypair_name = 'some-keys' + self.mock_context.secgroup_name = 'some-secgroup' + self.mock_context.user = "some-user" + netattrs = {'cidr': '10.0.0.0/24', 'provider': None, 'external_network': 'ext_net'} + self.mock_context.networks = [model.Network("some-network", self.mock_context, netattrs)] + + def test_construct_defaults(self): + + attrs = None + test_server = model.Server('foo', self.mock_context, attrs) + + self.assertEqual(test_server.stack_name, 'foo.bar') + self.assertEqual(test_server.keypair_name, 'some-keys') + self.assertEqual(test_server.secgroup_name, 'some-secgroup') + self.assertEqual(test_server.placement_groups, []) + self.assertIsNone(test_server.server_group) + self.assertEqual(test_server.instances, 1) + self.assertIsNone(test_server.floating_ip) + self.assertIsNone(test_server._image) + self.assertIsNone(test_server._flavor) + self.assertIn(test_server, model.Server.list) + + @mock.patch('yardstick.benchmark.contexts.model.PlacementGroup') + def test_construct_get_wrong_placement_group(self, mock_pg): + + attrs = {'placement': 'baz'} + mock_pg.get.return_value = None + + self.assertRaises(ValueError, model.Server, 'foo', + self.mock_context, attrs) + + @mock.patch('yardstick.benchmark.contexts.model.PlacementGroup') + def test_construct_get_wrong_server_group(self, mock_sg): + + attrs = {'server_group': 'baz'} + mock_sg.get.return_value = None + + self.assertRaises(ValueError, model.Server, 'foo', + self.mock_context, attrs) + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test__add_instance(self, mock_template): + attrs = {'image': 'some-image', 'flavor': 'some-flavor', 'floating_ip': '192.168.1.10', + 'floating_ip_assoc': 'some-vm', + 'availability_zone': 'zone'} + test_server = model.Server('foo', self.mock_context, attrs) + + self.mock_context.flavors = ['flavor1', 'flavor2', 'some-flavor'] + + mock_network = mock.Mock() + mock_network.name = 'some-network' + mock_network.stack_name = 'some-network-stack' + mock_network.allowed_address_pairs = ["1", "2"] + mock_network.vnic_type = 'normal' + mock_network.subnet_stack_name = 'some-network-stack-subnet' + mock_network.provider = 'sriov' + mock_network.net_flags = {} + mock_network.external_network = 'ext_net' + mock_network.router = model.Router('some-router', 'some-network', self.mock_context, + 'ext_net') + + test_server._add_instance(mock_template, 'some-server', + [mock_network], 'hints') + + mock_template.add_port.assert_called_with( + 'some-server-some-network-port', + mock_network, + sec_group_id=self.mock_context.secgroup_name, + provider=mock_network.provider, + allowed_address_pairs=mock_network.allowed_address_pairs) + + mock_template.add_floating_ip.assert_called_with( + 'some-server-fip', + mock_network.external_network, + 'some-server-some-network-port', + 'bar-some-network-some-router-if0', + 'some-secgroup' + ) + + mock_template.add_floating_ip_association.assert_called_with( + 'some-server-fip-assoc', + 'some-server-fip', + 'some-server-some-network-port' + ) + + mock_template.add_server.assert_called_with( + 'some-server', 'some-image', + flavor='some-flavor', + flavors=['flavor1', 'flavor2', 'some-flavor'], + ports=['some-server-some-network-port'], + user=self.mock_context.user, + key_name=self.mock_context.keypair_name, + user_data='', + scheduler_hints='hints', + availability_zone='zone') + + def test_override_ip(self): + network_ports = { + 'mgmt': ['mgmt'], + 'uplink_0': [ + {'xe0': {'local_ip': '10.44.0.20', 'netmask': '255.255.255.0'}}, + ], + 'downlink_0': [ + {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}}, + ], + } + attrs = { + 'image': 'some-image', 'flavor': 'some-flavor', + } + test_server = model.Server('foo', self.mock_context, attrs) + test_server.interfaces = { + "xe0": { + "local_ip": "1.2.3.4", + "netmask": "255.255.255.0", + }, + "xe1": { + "local_ip": "1.2.3.5", + "netmask": "255.255.255.0" + } + } + test_server.network_ports = network_ports + + test_server.override_ip("uplink_0", {"port": "xe0"}) + self.assertEqual(test_server.interfaces["xe0"], network_ports["uplink_0"][0]["xe0"]) + + def test_override_ip_multiple(self): + network_ports = { + 'mgmt': ['mgmt'], + 'uplink_0': [ + {'xe0': {'local_ip': '10.44.0.20', 'netmask': '255.255.255.0'}}, + {'xe0': {'local_ip': '10.44.0.21', 'netmask': '255.255.255.0'}}, + ], + 'downlink_0': [ + {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}}, + ], + } + attrs = { + 'image': 'some-image', 'flavor': 'some-flavor', + } + test_server = model.Server('foo', self.mock_context, attrs) + test_server.interfaces = { + "xe0": { + "local_ip": "1.2.3.4", + "netmask": "255.255.255.0", + }, + "xe1": { + "local_ip": "1.2.3.5", + "netmask": "255.255.255.0" + } + } + test_server.network_ports = network_ports + test_server.override_ip("uplink_0", {"port": "xe0"}) + self.assertEqual(test_server.interfaces["xe0"], network_ports["uplink_0"][0]["xe0"]) + + def test_override_ip_mixed(self): + network_ports = { + 'mgmt': ['mgmt'], + 'uplink_0': [ + 'xe0', + {'xe0': {'local_ip': '10.44.0.21', 'netmask': '255.255.255.0'}}, + ], + 'downlink_0': [ + {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}}, + ], + } + attrs = { + 'image': 'some-image', 'flavor': 'some-flavor', + } + test_server = model.Server('foo', self.mock_context, attrs) + test_server.interfaces = { + "xe0": { + "local_ip": "1.2.3.4", + "netmask": "255.255.255.0", + }, + "xe1": { + "local_ip": "1.2.3.5", + "netmask": "255.255.255.0" + } + } + test_server.network_ports = network_ports + test_server.override_ip("uplink_0", {"port": "xe0"}) + self.assertEqual(test_server.interfaces["xe0"], network_ports["uplink_0"][1]["xe0"]) + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test__add_instance_with_ip_override_invalid_syntax(self, mock_template): + network_ports = { + 'mgmt': ['mgmt'], + 'uplink_0': 'xe0', + 'downlink_0': [ + {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}}, + ], + } + attrs = { + 'image': 'some-image', 'flavor': 'some-flavor', + } + test_server = model.Server('foo', self.mock_context, attrs) + test_server.network_ports = network_ports + context = type("Context", (object,), {}) + # can't use Mock because Mock.name is reserved + context.name = "context" + networks = [model.Network(n, context, {}) for n in network_ports] + + with self.assertRaises(SyntaxError): + test_server._add_instance(mock_template, 'some-server', + networks, 'hints') + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test__add_instance_with_ip_override(self, mock_template): + network_ports = { + 'mgmt': ['mgmt'], + 'uplink_0': [ + {'xe0': {'local_ip': '10.44.0.20', 'netmask': '255.255.255.0'}}, + ], + 'downlink_0': [ + {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}}, + ], + } + attrs = { + 'image': 'some-image', 'flavor': 'some-flavor', + } + test_server = model.Server('foo', self.mock_context, attrs) + test_server.network_ports = network_ports + context = type("Context", (object,), {}) + # can't use Mock because Mock.name is reserved + context.name = "context" + networks = [model.Network(n, context, {}) for n in network_ports] + + test_server._add_instance(mock_template, 'some-server', + networks, 'hints') + self.assertEqual(test_server.ports, { + 'downlink_0': [{'port': 'xe1', 'stack_name': 'some-server-xe1-port'}], + 'mgmt': [{'port': 'mgmt', 'stack_name': 'some-server-mgmt-port'}], + 'uplink_0': [{'port': 'xe0', 'stack_name': 'some-server-xe0-port'}] + }) + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test__add_instance_with_multiple_ip_override(self, mock_template): + network_ports = { + 'mgmt': ['mgmt'], + 'uplink_0': [ + {'xe0': {'local_ip': '10.44.0.20', 'netmask': '255.255.255.0'}}, + {'xe0': {'local_ip': '10.44.0.21', 'netmask': '255.255.255.0'}}, + ], + 'downlink_0': [ + {'xe1': {'local_ip': '10.44.0.30', 'netmask': '255.255.255.0'}}, + ], + } + attrs = { + 'image': 'some-image', 'flavor': 'some-flavor', + } + test_server = model.Server('foo', self.mock_context, attrs) + test_server.network_ports = network_ports + context = type("Context", (object,), {}) + # can't use Mock because Mock.name is reserved + context.name = "context" + networks = [model.Network(n, context, {}) for n in network_ports] + + test_server._add_instance(mock_template, 'some-server', + networks, 'hints') + self.assertEqual(test_server.ports, { + 'downlink_0': [{'port': 'xe1', 'stack_name': 'some-server-xe1-port'}], + 'mgmt': [{'port': 'mgmt', 'stack_name': 'some-server-mgmt-port'}], + 'uplink_0': [{'port': 'xe0', 'stack_name': 'some-server-xe0-port'}, + # this is not an error, we can produce this, it is left to Heat + # to detect duplicate ports and error + {'port': 'xe0', 'stack_name': 'some-server-xe0-port'}] + }) + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test__add_instance_with_user_data(self, mock_template): + user_data = "USER_DATA" + attrs = { + 'image': 'some-image', 'flavor': 'some-flavor', + 'user_data': user_data, + } + test_server = model.Server('foo', self.mock_context, attrs) + + test_server._add_instance(mock_template, 'some-server', + [], 'hints') + + mock_template.add_server.assert_called_with( + 'some-server', 'some-image', + flavor='some-flavor', + flavors=self.mock_context.flavors, + ports=[], + user=self.mock_context.user, + key_name=self.mock_context.keypair_name, + user_data=user_data, + scheduler_hints='hints', + availability_zone=None) + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test__add_instance_with_availablity_zone(self, mock_template): + attrs = { + 'image': 'some-image', 'flavor': 'some-flavor', + 'availability_zone': 'zone', + } + test_server = model.Server('foo', self.mock_context, attrs) + + test_server._add_instance(mock_template, 'some-server', + [], 'hints') + + mock_template.add_server.assert_called_with( + 'some-server', 'some-image', + flavor='some-flavor', + flavors=self.mock_context.flavors, + ports=[], + user=self.mock_context.user, + key_name=self.mock_context.keypair_name, + user_data='', + scheduler_hints='hints', + availability_zone='zone') + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test__add_instance_plus_flavor(self, mock_template): + + user_data = '' + attrs = { + 'image': 'some-image', 'flavor': 'flavor1', + 'flavors': ['flavor2'], 'user_data': user_data + } + test_server = model.Server('ServerFlavor-2', self.mock_context, attrs) + + self.mock_context.flavors = ['flavor2'] + mock_network = mock.Mock() + mock_network.allowed_address_pairs = ["1", "2"] + mock_network.vnic_type = 'normal' + mock_network.net_flags = {} + mock_network.configure_mock(name='some-network', stack_name='some-network-stack', + subnet_stack_name='some-network-stack-subnet', + provider='some-provider') + + test_server._add_instance(mock_template, 'ServerFlavor-2', + [mock_network], 'hints') + + mock_template.add_port.assert_called_with( + 'ServerFlavor-2-some-network-port', + mock_network, + provider=mock_network.provider, + sec_group_id=self.mock_context.secgroup_name, + allowed_address_pairs=mock_network.allowed_address_pairs) + + mock_template.add_server.assert_called_with( + 'ServerFlavor-2', 'some-image', + flavor='flavor1', + flavors=['flavor2'], + ports=['ServerFlavor-2-some-network-port'], + user=self.mock_context.user, + key_name=self.mock_context.keypair_name, + user_data=user_data, + scheduler_hints='hints', + availability_zone=None) + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test__add_instance_misc(self, mock_template): + + user_data = '' + attrs = { + 'image': 'some-image', 'flavor': 'flavor1', + 'flavors': ['flavor2'], 'user_data': user_data + } + test_server = model.Server('ServerFlavor-3', self.mock_context, attrs) + + self.mock_context.flavors = ['flavor2'] + self.mock_context.flavor = {'vcpus': 4} + mock_network = mock.Mock() + mock_network.name = 'some-network' + mock_network.stack_name = 'some-network-stack' + mock_network.subnet_stack_name = 'some-network-stack-subnet' + mock_network.net_flags = {} + + test_server._add_instance(mock_template, 'ServerFlavor-3', + [mock_network], 'hints') + + mock_template.add_port( + 'ServerFlavor-3-some-network-port', + mock_network.stack_name, + mock_network.subnet_stack_name, + sec_group_id=self.mock_context.secgroup_name) + + mock_template.add_flavor( + vcpus=4, + ram=2048, + disk=1) + + mock_template.add_flavor( + vcpus=4, + ram=2048, + disk=1, + extra_specs={'cat': 1, 'dog': 2, 'dragon': 1000}) + + mock_template.add_server.assert_called_with( + 'ServerFlavor-3', 'some-image', + flavor='flavor1', + flavors=['flavor2'], + ports=['ServerFlavor-3-some-network-port'], + user=self.mock_context.user, + key_name=self.mock_context.keypair_name, + user_data=user_data, + scheduler_hints='hints', + availability_zone=None) diff --git a/yardstick/tests/unit/benchmark/contexts/test_node.py b/yardstick/tests/unit/benchmark/contexts/test_node.py new file mode 100644 index 000000000..da16074d9 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/test_node.py @@ -0,0 +1,392 @@ +############################################################################## +# Copyright (c) 2015-2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import os +import errno + +import mock +import unittest + +from yardstick.benchmark.contexts import base +from yardstick.benchmark.contexts import node +from yardstick.common import constants as consts +from yardstick.common import exceptions +from yardstick.common import yaml_loader + + +class NodeContextTestCase(unittest.TestCase): + + PREFIX = 'yardstick.benchmark.contexts.node' + + NODES_SAMPLE = "nodes_sample.yaml" + NODES_DUPLICATE_SAMPLE = "nodes_duplicate_sample.yaml" + + def setUp(self): + self.test_context = node.NodeContext() + self.addCleanup(self._remove_contexts) + self.os_path_join = os.path.join + self.attrs = { + 'name': 'foo', + 'task_id': '1234567890', + 'file': self._get_file_abspath(self.NODES_SAMPLE) + } + + @staticmethod + def _remove_contexts(): + for context in base.Context.list: + context._delete_context() + base.Context.list = [] + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = self.os_path_join(curr_path, filename) + return file_path + + def test___init__(self): + self.assertIsNone(self.test_context._name) + self.assertIsNone(self.test_context.file_path) + self.assertEqual(self.test_context.nodes, []) + self.assertEqual(self.test_context.controllers, []) + self.assertEqual(self.test_context.computes, []) + self.assertEqual(self.test_context.baremetals, []) + self.assertEqual(self.test_context.env, {}) + self.assertEqual(self.test_context.attrs, {}) + + @mock.patch.object(yaml_loader, 'read_yaml_file') + @mock.patch('{}.os.path.join'.format(PREFIX)) + def test_init_negative(self, mock_path_join, read_mock): + special_path = '/foo/bar/error_file' + error_path = self._get_file_abspath("error_file") + + def path_join(*args): + if args == (consts.YARDSTICK_ROOT_PATH, error_path): + return special_path + return self.os_path_join(*args) + + # we can't count mock_path_join calls because + # it can catch join calls for .pyc files. + mock_path_join.side_effect = path_join + read_calls = 0 + + with self.assertRaises(KeyError): + self.test_context.init({}) + + self.assertEqual(read_mock.call_count, read_calls) + + attrs = { + 'name': 'foo', + 'task_id': '1234567890', + 'file': error_path, + } + read_mock.side_effect = IOError(errno.EBUSY, 'busy') + with self.assertRaises(IOError) as raised: + self.test_context.init(attrs) + + read_calls += 1 + self.assertEqual(read_mock.call_count, read_calls) + self.assertIn(attrs['file'], self.test_context.file_path) + self.assertEqual(raised.exception.errno, errno.EBUSY) + self.assertEqual(str(raised.exception), str(read_mock.side_effect)) + + read_mock.side_effect = IOError(errno.ENOENT, 'not found') + with self.assertRaises(IOError) as raised: + self.test_context.init(attrs) + + read_calls += 2 + self.assertEqual(read_mock.call_count, read_calls) + self.assertEqual(self.test_context.file_path, special_path) + self.assertEqual(raised.exception.errno, errno.ENOENT) + self.assertEqual(str(raised.exception), str(read_mock.side_effect)) + + def test__dispatch_script(self): + self.test_context.init(self.attrs) + + self.test_context.env = {'bash': [{'script': 'dummy'}]} + self.test_context._execute_script = mock.Mock() + self.assertEqual(self.test_context._dispatch_script('bash'), None) + + def test__dispatch_ansible(self): + self.test_context.init(self.attrs) + + self.test_context.env = {'ansible': [{'script': 'dummy'}]} + self.test_context._do_ansible_job = mock.Mock() + self.assertEqual(self.test_context._dispatch_ansible('ansible'), None) + self.test_context.env = {} + self.assertEqual(self.test_context._dispatch_ansible('ansible'), None) + + @mock.patch("{}.AnsibleCommon".format(PREFIX)) + def test__do_ansible_job(self, *args): + self.assertIsNone(self.test_context._do_ansible_job('dummy')) + + def test_init(self): + self.test_context.init(self.attrs) + + self.assertEqual(self.test_context.name, "foo-12345678") + self.assertEqual(len(self.test_context.nodes), 4) + self.assertEqual(len(self.test_context.controllers), 2) + self.assertEqual(len(self.test_context.computes), 1) + self.assertEqual(self.test_context.computes[0]["name"], "node3") + self.assertEqual(len(self.test_context.baremetals), 1) + self.assertEqual(self.test_context.baremetals[0]["name"], "node4") + + def test__get_server_with_dict_attr_name(self): + self.test_context.init(self.attrs) + result = self.test_context._get_server({'name': 'node1.foo-12345678'}) + + self.assertIsNone(result, None) + + def test__get_server_not_found(self): + self.test_context.init(self.attrs) + + self.assertIsNone(self.test_context._get_server('bar.foo-12345678')) + + def test__get_server_mismatch(self): + self.test_context.init(self.attrs) + + self.assertIsNone(self.test_context._get_server('bar.foo1')) + + def test__get_server_duplicate(self): + self.attrs['file'] = self._get_file_abspath( + self.NODES_DUPLICATE_SAMPLE) + self.test_context.init(self.attrs) + + with self.assertRaises(ValueError): + self.test_context._get_server('node1.foo-12345678') + + def test__get_server_found(self): + self.test_context.init(self.attrs) + + result = self.test_context._get_server('node1.foo-12345678') + + self.assertEqual(result['ip'], '10.229.47.137') + self.assertEqual(result['name'], 'node1.foo-12345678') + self.assertEqual(result['user'], 'root') + self.assertEqual(result['key_filename'], '/root/.yardstick_key') + + def test__get_physical_nodes(self): + self.test_context.init(self.attrs) + nodes = self.test_context._get_physical_nodes() + self.assertEqual(nodes, self.test_context.nodes) + + def test__get_physical_node_for_server(self): + self.test_context.init(self.attrs) + + # When server is not from this context + result = self.test_context._get_physical_node_for_server('node1.another-context') + self.assertIsNone(result) + + # When node_name is not from this context + result = self.test_context._get_physical_node_for_server('fake.foo-12345678') + self.assertIsNone(result) + + result = self.test_context._get_physical_node_for_server('node1.foo-12345678') + self.assertEqual(result, 'node1.foo') + + def test_update_collectd_options_for_node(self): + self.test_context.init(self.attrs) + options = {'collectd': {'interval': 5}} + + with self.assertRaises(exceptions.ContextUpdateCollectdForNodeError): + self.test_context.update_collectd_options_for_node(options, 'fake.foo-12345678') + + self.test_context.update_collectd_options_for_node(options, 'node1.foo-12345678') + + node_collectd_options = [node for node in self.test_context.nodes + if node['name'] == 'node1'][0]['collectd'] + + self.assertEqual(node_collectd_options, options) + + @mock.patch('{}.NodeContext._dispatch_script'.format(PREFIX)) + def test_deploy(self, dispatch_script_mock): + obj = node.NodeContext() + self.addCleanup(obj._delete_context) + obj.env = { + 'type': 'script' + } + obj.deploy() + dispatch_script_mock.assert_called_once() + + @mock.patch('{}.NodeContext._dispatch_ansible'.format(PREFIX)) + def test_deploy_anisible(self, dispatch_ansible_mock): + obj = node.NodeContext() + self.addCleanup(obj._delete_context) + obj.env = { + 'type': 'ansible' + } + obj.deploy() + dispatch_ansible_mock.assert_called_once() + + @mock.patch('{}.NodeContext._dispatch_script'.format(PREFIX)) + def test_undeploy(self, dispatch_script_mock): + obj = node.NodeContext() + obj.env = { + 'type': 'script' + } + obj.undeploy() + dispatch_script_mock.assert_called_once() + + @mock.patch('{}.NodeContext._dispatch_ansible'.format(PREFIX)) + def test_undeploy_anisble(self, dispatch_ansible_mock): + obj = node.NodeContext() + obj.env = { + 'type': 'ansible' + } + obj.undeploy() + dispatch_ansible_mock.assert_called_once() + + @mock.patch('{}.ssh.SSH._put_file_shell'.format(PREFIX)) + @mock.patch('{}.ssh.SSH.execute'.format(PREFIX)) + def test_execute_remote_script(self, execute_mock, put_file_mock): + obj = node.NodeContext() + self.addCleanup(obj._delete_context) + obj.env = {'prefix': 'yardstick.benchmark.scenarios.compute'} + node_name_args = 'node5' + obj.nodes = [{ + 'name': node_name_args, + 'user': 'ubuntu', + 'ip': '10.10.10.10', + 'pwd': 'ubuntu', + }] + + info = {'script': 'computecapacity.bash'} + execute_mock.return_value = (0, '', '') + obj._execute_remote_script('node5', info) + + put_file_mock.assert_called_once() + execute_mock.assert_called() + + @mock.patch('{}.NodeContext._execute_local_script'.format(PREFIX)) + def test_execute_script_local(self, local_execute_mock): + node_name = 'local' + info = {} + obj = node.NodeContext() + self.addCleanup(obj._delete_context) + obj._execute_script(node_name, info) + local_execute_mock.assert_called_once() + + @mock.patch('{}.NodeContext._execute_remote_script'.format(PREFIX)) + def test_execute_script_remote(self, remote_execute_mock): + node_name = 'node5' + info = {} + obj = node.NodeContext() + self.addCleanup(obj._delete_context) + obj._execute_script(node_name, info) + remote_execute_mock.assert_called_once() + + def test_get_script(self): + script_args = 'hello.bash' + info_args = { + 'script': script_args + } + obj = node.NodeContext() + self.addCleanup(obj._delete_context) + script, options = obj._get_script(info_args) + self.assertEqual(script_args, script) + self.assertEqual('', options) + + def test_node_info(self): + node_name_args = 'node5' + obj = node.NodeContext() + self.addCleanup(obj._delete_context) + obj.nodes = [{'name': node_name_args, 'check': node_name_args}] + node_info = obj._get_node_info(node_name_args) + self.assertEqual(node_info.get('check'), node_name_args) + + @mock.patch('{}.ssh.SSH.wait'.format(PREFIX)) + def test_get_client(self, wait_mock): + node_name_args = 'node5' + obj = node.NodeContext() + self.addCleanup(obj._delete_context) + obj.nodes = [{ + 'name': node_name_args, + 'user': 'ubuntu', + 'ip': '10.10.10.10', + 'pwd': 'ubuntu', + }] + obj._get_client(node_name_args) + wait_mock.assert_called_once() + + def test_get_server(self): + self.test_context.init(self.attrs) + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.assertEqual('foo-12345678', self.test_context.name) + self.assertIsNotNone(self.test_context._task_id) + + result = self.test_context.get_server('node1.foo-12345678') + + self.assertEqual(result['ip'], '10.229.47.137') + self.assertEqual(result['name'], 'node1.foo-12345678') + self.assertEqual(result['user'], 'root') + self.assertEqual(result['key_filename'], '/root/.yardstick_key') + + def test_get_server_server_not_in_context(self): + self.test_context.init(self.attrs) + + with self.assertRaises(ValueError): + self.test_context.get_server('my2.foo-12345678') + + def test_get_context_from_server(self): + self.test_context._name = 'vnf1' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.test_context.nodes = [{'name': 'my', 'value': 100}] + self.test_context.attrs = {'attr1': 200} + + self.assertIs( + self.test_context.get_context_from_server('my.vnf1-12345678'), + self.test_context) + + # TODO: Split this into more granular tests + def test__get_network(self): + network1 = { + 'name': 'net_1', + 'vld_id': 'vld111', + 'segmentation_id': 'seg54', + 'network_type': 'type_a', + 'physical_network': 'phys', + } + network2 = { + 'name': 'net_2', + 'vld_id': 'vld999', + } + self.test_context.networks = { + 'a': network1, + 'b': network2, + } + + attr_name = {} + self.assertIsNone(self.test_context._get_network(attr_name)) + + attr_name = {'vld_id': 'vld777'} + self.assertIsNone(self.test_context._get_network(attr_name)) + + self.assertIsNone(self.test_context._get_network(None)) + + attr_name = 'vld777' + self.assertIsNone(self.test_context._get_network(attr_name)) + + attr_name = {'vld_id': 'vld999'} + expected = { + "name": 'net_2', + "vld_id": 'vld999', + "segmentation_id": None, + "network_type": None, + "physical_network": None, + } + result = self.test_context._get_network(attr_name) + self.assertDictEqual(result, expected) + + attr_name = 'a' + expected = network1 + result = self.test_context._get_network(attr_name) + self.assertDictEqual(result, expected) diff --git a/yardstick/tests/unit/benchmark/core/__init__.py b/yardstick/tests/unit/benchmark/core/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/core/__init__.py diff --git a/yardstick/tests/unit/benchmark/core/no_constraint_no_args_scenario_sample.yaml b/yardstick/tests/unit/benchmark/core/no_constraint_no_args_scenario_sample.yaml new file mode 100644 index 000000000..44c4a31ff --- /dev/null +++ b/yardstick/tests/unit/benchmark/core/no_constraint_no_args_scenario_sample.yaml @@ -0,0 +1,21 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +--- +# Huawei US bare daily task suite + +schema: "yardstick:suite:0.1" + +name: "os-nosdn-nofeature-ha" +test_cases_dir: "tests/opnfv/test_cases/" +test_cases: +- + file_name: opnfv_yardstick_tc037.yaml +- + file_name: opnfv_yardstick_tc043.yaml + diff --git a/yardstick/tests/unit/benchmark/core/no_constraint_with_args_scenario_sample.yaml b/yardstick/tests/unit/benchmark/core/no_constraint_with_args_scenario_sample.yaml new file mode 100644 index 000000000..ced13f19e --- /dev/null +++ b/yardstick/tests/unit/benchmark/core/no_constraint_with_args_scenario_sample.yaml @@ -0,0 +1,23 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +--- +# Huawei US bare daily task suite + +schema: "yardstick:suite:0.1" + +name: "os-nosdn-nofeature-ha" +test_cases_dir: "tests/opnfv/test_cases/" +test_cases: +- + file_name: opnfv_yardstick_tc037.yaml +- + file_name: opnfv_yardstick_tc043.yaml + task_args: + huawei-pod1: '{"host": "node1.LF","target": "node2.LF"}' + diff --git a/yardstick/tests/unit/benchmark/core/test_plugin.py b/yardstick/tests/unit/benchmark/core/test_plugin.py new file mode 100644 index 000000000..53621316b --- /dev/null +++ b/yardstick/tests/unit/benchmark/core/test_plugin.py @@ -0,0 +1,148 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import copy +import os +import pkg_resources + +import mock +import six +import testtools + +from yardstick import ssh +from yardstick.benchmark.core import plugin +from yardstick.tests import fixture + + +class PluginTestCase(testtools.TestCase): + + FILE = """ +schema: "yardstick:plugin:0.1" + +plugins: + name: sample + +deployment: + ip: 10.1.0.50 + user: root + password: root +""" + + NAME = 'sample' + DEPLOYMENT = {'ip': '10.1.0.50', 'user': 'root', 'password': 'root'} + + def setUp(self): + super(PluginTestCase, self).setUp() + self.plugin_parser = plugin.PluginParser(mock.Mock()) + self.plugin = plugin.Plugin() + self.useFixture(fixture.PluginParserFixture(PluginTestCase.FILE)) + + self._mock_ssh_from_node = mock.patch.object(ssh.SSH, 'from_node') + self.mock_ssh_from_node = self._mock_ssh_from_node.start() + self.mock_ssh_obj = mock.Mock() + self.mock_ssh_from_node.return_value = self.mock_ssh_obj + self.mock_ssh_obj.wait = mock.Mock() + self.mock_ssh_obj._put_file_shell = mock.Mock() + self._mock_log_info = mock.patch.object(plugin.LOG, 'info') + self.mock_log_info = self._mock_log_info.start() + + self.addCleanup(self._cleanup) + + def _cleanup(self): + self._mock_ssh_from_node.stop() + self._mock_log_info.stop() + + @mock.patch.object(six.moves.builtins, 'print') + def test_install(self, *args): + args = mock.Mock() + args.input_file = [mock.Mock()] + with mock.patch.object(self.plugin, '_install_setup') as \ + mock_install, \ + mock.patch.object(self.plugin, '_run') as mock_run: + self.plugin.install(args) + mock_install.assert_called_once_with(PluginTestCase.NAME, + PluginTestCase.DEPLOYMENT) + mock_run.assert_called_once_with(PluginTestCase.NAME) + + @mock.patch.object(six.moves.builtins, 'print') + def test_remove(self, *args): + args = mock.Mock() + args.input_file = [mock.Mock()] + with mock.patch.object(self.plugin, '_remove_setup') as \ + mock_remove, \ + mock.patch.object(self.plugin, '_run') as mock_run: + self.plugin.remove(args) + mock_remove.assert_called_once_with(PluginTestCase.NAME, + PluginTestCase.DEPLOYMENT) + mock_run.assert_called_once_with(PluginTestCase.NAME) + + @mock.patch.object(pkg_resources, 'resource_filename', + return_value='script') + def test__install_setup(self, mock_resource_filename): + plugin_name = 'plugin_name' + self.plugin._install_setup(plugin_name, PluginTestCase.DEPLOYMENT) + mock_resource_filename.assert_called_once_with( + 'yardstick.resources', 'scripts/install/' + plugin_name + '.bash') + self.mock_ssh_from_node.assert_called_once_with( + PluginTestCase.DEPLOYMENT) + self.mock_ssh_obj.wait.assert_called_once_with(timeout=600) + self.mock_ssh_obj._put_file_shell.assert_called_once_with( + 'script', '~/{0}.sh'.format(plugin_name)) + + @mock.patch.object(pkg_resources, 'resource_filename', + return_value='script') + @mock.patch.object(os, 'environ', return_value='1.2.3.4') + def test__install_setup_with_ip_local(self, mock_os_environ, + mock_resource_filename): + plugin_name = 'plugin_name' + deployment = copy.deepcopy(PluginTestCase.DEPLOYMENT) + deployment['ip'] = 'local' + self.plugin._install_setup(plugin_name, deployment) + mock_os_environ.__getitem__.assert_called_once_with('JUMP_HOST_IP') + mock_resource_filename.assert_called_once_with( + 'yardstick.resources', + 'scripts/install/' + plugin_name + '.bash') + self.mock_ssh_from_node.assert_called_once_with( + deployment, overrides={'ip': os.environ["JUMP_HOST_IP"]}) + self.mock_ssh_obj.wait.assert_called_once_with(timeout=600) + self.mock_ssh_obj._put_file_shell.assert_called_once_with( + 'script', '~/{0}.sh'.format(plugin_name)) + + @mock.patch.object(pkg_resources, 'resource_filename', + return_value='script') + def test__remove_setup(self, mock_resource_filename): + plugin_name = 'plugin_name' + self.plugin._remove_setup(plugin_name, PluginTestCase.DEPLOYMENT) + mock_resource_filename.assert_called_once_with( + 'yardstick.resources', + 'scripts/remove/' + plugin_name + '.bash') + self.mock_ssh_from_node.assert_called_once_with( + PluginTestCase.DEPLOYMENT) + self.mock_ssh_obj.wait.assert_called_once_with(timeout=600) + self.mock_ssh_obj._put_file_shell.assert_called_once_with( + 'script', '~/{0}.sh'.format(plugin_name)) + + @mock.patch.object(pkg_resources, 'resource_filename', + return_value='script') + @mock.patch.object(os, 'environ', return_value='1.2.3.4') + def test__remove_setup_with_ip_local(self, mock_os_environ, + mock_resource_filename): + plugin_name = 'plugin_name' + deployment = copy.deepcopy(PluginTestCase.DEPLOYMENT) + deployment['ip'] = 'local' + self.plugin._remove_setup(plugin_name, deployment) + mock_os_environ.__getitem__.assert_called_once_with('JUMP_HOST_IP') + mock_resource_filename.assert_called_once_with( + 'yardstick.resources', + 'scripts/remove/' + plugin_name + '.bash') + self.mock_ssh_from_node.assert_called_once_with( + deployment, overrides={'ip': os.environ["JUMP_HOST_IP"]}) + self.mock_ssh_obj.wait.assert_called_once_with(timeout=600) + self.mock_ssh_obj._put_file_shell.mock_os_environ( + 'script', '~/{0}.sh'.format(plugin_name)) diff --git a/yardstick/tests/unit/benchmark/core/test_report.py b/yardstick/tests/unit/benchmark/core/test_report.py new file mode 100644 index 000000000..89fb1e90a --- /dev/null +++ b/yardstick/tests/unit/benchmark/core/test_report.py @@ -0,0 +1,584 @@ +############################################################################## +# Copyright (c) 2017 Rajesh Kudaka. +# Copyright (c) 2018-2019 Intel Corporation. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import six +import unittest +import uuid + +from api.utils import influx +from yardstick.benchmark.core import report +from yardstick.cmd.commands import change_osloobj_to_paras + +GOOD_YAML_NAME = 'fake_name' +GOOD_TASK_ID = str(uuid.uuid4()) +GOOD_DB_FIELDKEYS = [{'fieldKey': 'fake_key'}] +GOOD_DB_METRICS = [{ + 'fake_key': 1.234, + 'time': '0000-00-00T12:34:56.789012Z', + }] +GOOD_TIMESTAMP = ['12:34:56.789012'] +BAD_YAML_NAME = 'F@KE_NAME' +BAD_TASK_ID = 'aaaaaa-aaaaaaaa-aaaaaaaaaa-aaaaaa' +MORE_DB_FIELDKEYS = [ + {'fieldKey': 'fake_key'}, + {'fieldKey': 'str_str'}, + {'fieldKey': u'str_unicode'}, + {u'fieldKey': 'unicode_str'}, + {u'fieldKey': u'unicode_unicode'}, + ] +MORE_DB_METRICS = [{ + 'fake_key': None, + 'time': '0000-00-00T00:00:00.000000Z', + }, { + 'fake_key': 123, + 'time': '0000-00-00T00:00:01.000000Z', + }, { + 'fake_key': 4.56, + 'time': '0000-00-00T00:00:02.000000Z', + }, { + 'fake_key': 9876543210987654321, + 'time': '0000-00-00T00:00:03.000000Z', + }, { + 'fake_key': 'str_str value', + 'time': '0000-00-00T00:00:04.000000Z', + }, { + 'fake_key': u'str_unicode value', + 'time': '0000-00-00T00:00:05.000000Z', + }, { + u'fake_key': 'unicode_str value', + 'time': '0000-00-00T00:00:06.000000Z', + }, { + u'fake_key': u'unicode_unicode value', + 'time': '0000-00-00T00:00:07.000000Z', + }, { + 'fake_key': '7.89', + 'time': '0000-00-00T00:00:08.000000Z', + }, { + 'fake_key': '1011', + 'time': '0000-00-00T00:00:09.000000Z', + }, { + 'fake_key': '9876543210123456789', + 'time': '0000-00-00T00:00:10.000000Z', + }] +MORE_TIMESTAMP = ['00:00:%02d.000000' % n for n in range(len(MORE_DB_METRICS))] +MORE_EMPTY_DATA = [None] * len(MORE_DB_METRICS) +MORE_EXPECTED_TABLE_VALS = { + 'Timestamp': MORE_TIMESTAMP, + 'fake_key': [ + None, + 123, + 4.56, + 9876543210987654321 if six.PY3 else 9.876543210987655e+18, + None, + None, + None, + None, + 7.89, + 1011, + 9876543210123456789 if six.PY3 else 9.876543210123457e+18, + ], + 'str_str': MORE_EMPTY_DATA, + 'str_unicode': MORE_EMPTY_DATA, + 'unicode_str': MORE_EMPTY_DATA, + 'unicode_unicode': MORE_EMPTY_DATA, + } +MORE_EXPECTED_DATASETS = [{ + 'label': key, + 'data': MORE_EXPECTED_TABLE_VALS[key], + } + for key in map(str, [field['fieldKey'] for field in MORE_DB_FIELDKEYS]) + ] + + +class JSTreeTestCase(unittest.TestCase): + + def setUp(self): + self.jstree = report.JSTree() + + def test__create_node(self): + _id = "tg__0.DropPackets" + + expected_data = [ + {"id": "tg__0", "text": "tg__0", "parent": "#"}, + {"id": "tg__0.DropPackets", "text": "DropPackets", "parent": "tg__0"} + ] + self.jstree._create_node(_id) + + self.assertEqual(self.jstree._created_nodes, ['#', 'tg__0', 'tg__0.DropPackets']) + self.assertEqual(self.jstree.jstree_data, expected_data) + + def test_format_for_jstree(self): + data = [ + 'tg__0.DropPackets', + 'tg__0.LatencyAvg.5', 'tg__0.LatencyAvg.6', + 'tg__0.LatencyMax.5', 'tg__0.LatencyMax.6', + 'tg__0.RxThroughput', 'tg__0.TxThroughput', + 'tg__1.DropPackets', + 'tg__1.LatencyAvg.5', 'tg__1.LatencyAvg.6', + 'tg__1.LatencyMax.5', 'tg__1.LatencyMax.6', + 'tg__1.RxThroughput', 'tg__1.TxThroughput', + 'vnf__0.curr_packets_in', 'vnf__0.packets_dropped', 'vnf__0.packets_fwd', + ] + + expected_output = [ + {"id": "tg__0", "text": "tg__0", "parent": "#"}, + {"id": "tg__0.DropPackets", "text": "DropPackets", "parent": "tg__0"}, + {"id": "tg__0.LatencyAvg", "text": "LatencyAvg", "parent": "tg__0"}, + {"id": "tg__0.LatencyAvg.5", "text": "5", "parent": "tg__0.LatencyAvg"}, + {"id": "tg__0.LatencyAvg.6", "text": "6", "parent": "tg__0.LatencyAvg"}, + {"id": "tg__0.LatencyMax", "text": "LatencyMax", "parent": "tg__0"}, + {"id": "tg__0.LatencyMax.5", "text": "5", "parent": "tg__0.LatencyMax"}, + {"id": "tg__0.LatencyMax.6", "text": "6", "parent": "tg__0.LatencyMax"}, + {"id": "tg__0.RxThroughput", "text": "RxThroughput", "parent": "tg__0"}, + {"id": "tg__0.TxThroughput", "text": "TxThroughput", "parent": "tg__0"}, + {"id": "tg__1", "text": "tg__1", "parent": "#"}, + {"id": "tg__1.DropPackets", "text": "DropPackets", "parent": "tg__1"}, + {"id": "tg__1.LatencyAvg", "text": "LatencyAvg", "parent": "tg__1"}, + {"id": "tg__1.LatencyAvg.5", "text": "5", "parent": "tg__1.LatencyAvg"}, + {"id": "tg__1.LatencyAvg.6", "text": "6", "parent": "tg__1.LatencyAvg"}, + {"id": "tg__1.LatencyMax", "text": "LatencyMax", "parent": "tg__1"}, + {"id": "tg__1.LatencyMax.5", "text": "5", "parent": "tg__1.LatencyMax"}, + {"id": "tg__1.LatencyMax.6", "text": "6", "parent": "tg__1.LatencyMax"}, + {"id": "tg__1.RxThroughput", "text": "RxThroughput", "parent": "tg__1"}, + {"id": "tg__1.TxThroughput", "text": "TxThroughput", "parent": "tg__1"}, + {"id": "vnf__0", "text": "vnf__0", "parent": "#"}, + {"id": "vnf__0.curr_packets_in", "text": "curr_packets_in", "parent": "vnf__0"}, + {"id": "vnf__0.packets_dropped", "text": "packets_dropped", "parent": "vnf__0"}, + {"id": "vnf__0.packets_fwd", "text": "packets_fwd", "parent": "vnf__0"}, + ] + + result = self.jstree.format_for_jstree(data) + self.assertEqual(expected_output, result) + + +class ReportTestCase(unittest.TestCase): + + def setUp(self): + super(ReportTestCase, self).setUp() + self.param = change_osloobj_to_paras({}) + self.param.yaml_name = [GOOD_YAML_NAME] + self.param.task_id = [GOOD_TASK_ID] + self.rep = report.Report() + + def test___init__(self): + self.assertEqual([], self.rep.Timestamp) + self.assertEqual("", self.rep.yaml_name) + self.assertEqual("", self.rep.task_id) + + def test__validate(self): + self.rep._validate(GOOD_YAML_NAME, GOOD_TASK_ID) + self.assertEqual(GOOD_YAML_NAME, self.rep.yaml_name) + self.assertEqual(GOOD_TASK_ID, str(self.rep.task_id)) + + def test__validate_invalid_yaml_name(self): + with six.assertRaisesRegex(self, ValueError, "yaml*"): + self.rep._validate(BAD_YAML_NAME, GOOD_TASK_ID) + + def test__validate_invalid_task_id(self): + with six.assertRaisesRegex(self, ValueError, "task*"): + self.rep._validate(GOOD_YAML_NAME, BAD_TASK_ID) + + @mock.patch.object(influx, 'query') + def test__get_fieldkeys(self, mock_query): + mock_query.return_value = GOOD_DB_FIELDKEYS + self.rep.yaml_name = GOOD_YAML_NAME + self.rep.task_id = GOOD_TASK_ID + self.assertEqual(GOOD_DB_FIELDKEYS, self.rep._get_fieldkeys()) + + @mock.patch.object(influx, 'query') + def test__get_fieldkeys_nodbclient(self, mock_query): + mock_query.side_effect = RuntimeError + self.assertRaises(RuntimeError, self.rep._get_fieldkeys) + + @mock.patch.object(influx, 'query') + def test__get_fieldkeys_testcase_not_found(self, mock_query): + mock_query.return_value = [] + self.rep.yaml_name = GOOD_YAML_NAME + self.rep.task_id = GOOD_TASK_ID + six.assertRaisesRegex(self, KeyError, "Test case", self.rep._get_fieldkeys) + + @mock.patch.object(influx, 'query') + def test__get_metrics(self, mock_query): + mock_query.return_value = GOOD_DB_METRICS + self.rep.yaml_name = GOOD_YAML_NAME + self.rep.task_id = GOOD_TASK_ID + self.assertEqual(GOOD_DB_METRICS, self.rep._get_metrics()) + + @mock.patch.object(influx, 'query') + def test__get_metrics_task_not_found(self, mock_query): + mock_query.return_value = [] + self.rep.yaml_name = GOOD_YAML_NAME + self.rep.task_id = GOOD_TASK_ID + six.assertRaisesRegex(self, KeyError, "Task ID", self.rep._get_metrics) + + @mock.patch.object(influx, 'query') + def test__get_task_start_time(self, mock_query): + self.rep.yaml_name = GOOD_YAML_NAME + self.rep.task_id = GOOD_TASK_ID + mock_query.return_value = [{ + u'free.memory0.used': u'9789088', + u'free.memory0.available': u'22192984', + u'free.memory0.shared': u'219152', + u'time': u'2019-01-22T16:20:14.568075776Z', + }] + expected = "2019-01-22T16:20:14.568075776Z" + + self.assertEqual( + expected, + self.rep._get_task_start_time() + ) + + def test__get_task_start_time_task_not_found(self): + pass + + @mock.patch.object(influx, 'query') + def test__get_task_end_time(self, mock_query): + self.rep.yaml_name = GOOD_YAML_NAME + self.rep.task_id = GOOD_TASK_ID + # TODO(elfoley): write this test! + mock_query.return_value = [{ + + }] + + @mock.patch.object(influx, 'query') + def test__get_baro_metrics(self, mock_query): + self.rep.yaml_name = GOOD_YAML_NAME + self.rep.task_id = GOOD_TASK_ID + self.rep._get_task_start_time = mock.Mock(return_value=0) + self.rep._get_task_end_time = mock.Mock(return_value=0) + + influx_return_values = ([{ + u'value': 324050, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-12-19T14:11:25.383698038Z', + u'type_instance': u'user', u'type': u'cpu', + }, { + u'value': 193798, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-12-19T14:11:25.383712594Z', + u'type_instance': u'system', u'type': u'cpu', + }, { + u'value': 324051, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-12-19T14:11:35.383696624Z', + u'type_instance': u'user', u'type': u'cpu', + }, { + u'value': 193800, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-12-19T14:11:35.383713481Z', + u'type_instance': u'system', u'type': u'cpu', + }, { + u'value': 324054, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-12-19T14:11:45.3836966789Z', + u'type_instance': u'user', u'type': u'cpu', + }, { + u'value': 193801, u'instance': u'0', u'host': u'myhostname', + u'time': u'2018-12-19T14:11:45.383716296Z', + u'type_instance': u'system', u'type': u'cpu', + }], + [{ + u'value': 3598453000, u'host': u'myhostname', + u'time': u'2018-12-19T14:11:25.383698038Z', + u'type_instance': u'0', u'type': u'cpufreq', + }, { + u'value': 3530250000, u'type_instance': u'0', u'host': u'myhostname', + u'time': u'2018-12-19T14:11:35.383712594Z', u'type': u'cpufreq', + }, { + u'value': 3600281000, u'type_instance': u'0', u'host': u'myhostname', + u'time': u'2018-12-19T14:11:45.383696624Z', u'type': u'cpufreq', + }], + ) + + def ret_vals(vals): + for x in vals: + yield x + while True: + yield [] + + mock_query.side_effect = ret_vals(influx_return_values) + + BARO_EXPECTED_METRICS = { + 'Timestamp': [ + '14:11:25.3836', '14:11:25.3837', + '14:11:35.3836', '14:11:35.3837', + '14:11:45.3836', '14:11:45.3837'], + 'myhostname.cpu_value.cpu.user.0': { + '14:11:25.3836': 324050, + '14:11:35.3836': 324051, + '14:11:45.3836': 324054, + }, + 'myhostname.cpu_value.cpu.system.0': { + '14:11:25.3837': 193798, + '14:11:35.3837': 193800, + '14:11:45.3837': 193801, + }, + 'myhostname.cpufreq_value.cpufreq.0': { + '14:11:25.3836': 3598453000, + '14:11:35.3837': 3530250000, + '14:11:45.3836': 3600281000, + } + } + self.assertEqual( + BARO_EXPECTED_METRICS, + self.rep._get_baro_metrics() + ) + + def test__get_timestamps(self): + + metrics = MORE_DB_METRICS + self.assertEqual( + MORE_TIMESTAMP, + self.rep._get_timestamps(metrics) + ) + + def test__format_datasets(self): + metric_name = "free.memory0.used" + metrics = [{ + u'free.memory1.free': u'1958664', + u'free.memory0.used': u'9789560', + }, { + u'free.memory1.free': u'1958228', + u'free.memory0.used': u'9789790', + }, { + u'free.memory1.free': u'1956156', + u'free.memory0.used': u'9791092', + }, { + u'free.memory1.free': u'1956280', + u'free.memory0.used': u'9790796', + }] + self.assertEqual( + [9789560, 9789790, 9791092, 9790796,], + self.rep._format_datasets(metric_name, metrics) + ) + + def test__format_datasets_val_none(self): + metric_name = "free.memory0.used" + metrics = [{ + u'free.memory1.free': u'1958664', + u'free.memory0.used': 9876543109876543210, + }, { + u'free.memory1.free': u'1958228', + }, { + u'free.memory1.free': u'1956156', + u'free.memory0.used': u'9791092', + }, { + u'free.memory1.free': u'1956280', + u'free.memory0.used': u'9790796', + }] + + exp0 = 9876543109876543210 if six.PY3 else 9.876543109876543e+18 + self.assertEqual( + [exp0, None, 9791092, 9790796], + self.rep._format_datasets(metric_name, metrics) + ) + + def test__format_datasets_val_incompatible(self): + metric_name = "free.memory0.used" + metrics = [{ + u'free.memory0.used': "some incompatible value", + }, { + }] + self.assertEqual( + [None, None], + self.rep._format_datasets(metric_name, metrics) + ) + + def test__combine_times(self): + yard_times = [ + '00:00:00.000000', + '00:00:01.000000', + '00:00:02.000000', + '00:00:06.000000', + '00:00:08.000000', + '00:00:09.000000', + ] + baro_times = [ + '00:00:01.000000', + '00:00:03.000000', + '00:00:04.000000', + '00:00:05.000000', + '00:00:07.000000', + '00:00:10.000000', + ] + expected_combo = [ + '00:00:00.000000', + '00:00:01.000000', + '00:00:02.000000', + '00:00:03.000000', + '00:00:04.000000', + '00:00:05.000000', + '00:00:06.000000', + '00:00:07.000000', + '00:00:08.000000', + '00:00:09.000000', + '00:00:10.000000', + ] + + actual_combo = self.rep._combine_times(yard_times, baro_times) + self.assertEqual(len(expected_combo), len(actual_combo)) + + self.assertEqual( + expected_combo, + actual_combo, + ) + + def test__combine_times_2(self): + time1 = ['14:11:25.383698', '14:11:25.383712', '14:11:35.383696',] + time2 = [ + '16:20:14.568075', '16:20:24.575083', + '16:20:34.580989', '16:20:44.586801', ] + time_exp = [ + '14:11:25.383698', '14:11:25.383712', '14:11:35.383696', + '16:20:14.568075', '16:20:24.575083', '16:20:34.580989', + '16:20:44.586801', + ] + self.assertEqual(time_exp, self.rep._combine_times(time1, time2)) + + def test__combine_metrics(self): + BARO_METRICS = { + 'myhostname.cpu_value.cpu.user.0': { + '14:11:25.3836': 324050, '14:11:35.3836': 324051, + '14:11:45.3836': 324054, + }, + 'myhostname.cpu_value.cpu.system.0': { + '14:11:25.3837': 193798, '14:11:35.3837': 193800, + '14:11:45.3837': 193801, + } + } + BARO_TIMES = [ + '14:11:25.3836', '14:11:25.3837', '14:11:35.3836', + '14:11:35.3837', '14:11:45.3836', '14:11:45.3837', + ] + YARD_METRICS = { + 'free.memory9.free': { + '16:20:14.5680': 1958244, '16:20:24.5750': 1955964, + '16:20:34.5809': 1956040, '16:20:44.5868': 1956428, + }, + 'free.memory7.used': { + '16:20:14.5680': 9789068, '16:20:24.5750': 9791284, + '16:20:34.5809': 9791228, '16:20:44.5868': 9790692, + }, + 'free.memory2.total':{ + '16:20:14.5680': 32671288, '16:20:24.5750': 32671288, + '16:20:34.5809': 32671288, '16:20:44.5868': 32671288, + }, + 'free.memory7.free': { + '16:20:14.5680': 1958368, '16:20:24.5750': 1956104, + '16:20:34.5809': 1956040, '16:20:44.5868': 1956552, + }, + 'free.memory1.used': { + '16:20:14.5680': 9788872, '16:20:24.5750': 9789212, + '16:20:34.5809': 9791168, '16:20:44.5868': 9790996, + }, + } + YARD_TIMES = [ + '16:20:14.5680', '16:20:24.5750', + '16:20:34.5809', '16:20:44.5868', + ] + + expected_output = { + 'myhostname.cpu_value.cpu.user.0': [{ + 'x': '14:11:25.3836', 'y': 324050, }, { + 'x': '14:11:35.3836', 'y': 324051, }, { + 'x': '14:11:45.3836', 'y': 324054, }], + 'myhostname.cpu_value.cpu.system.0' : [{ + 'x': '14:11:25.3837', 'y': 193798, }, { + 'x': '14:11:35.3837', 'y': 193800, }, { + 'x': '14:11:45.3837', 'y': 193801, }], + 'free.memory9.free': [{ + 'x': '16:20:14.5680', 'y': 1958244, }, { + 'x': '16:20:24.5750', 'y': 1955964, }, { + 'x': '16:20:34.5809', 'y': 1956040, }, { + 'x': '16:20:44.5868', 'y': 1956428, }], + 'free.memory7.used': [{ + 'x': '16:20:14.5680', 'y': 9789068, }, { + 'x': '16:20:24.5750', 'y': 9791284, }, { + 'x': '16:20:34.5809', 'y': 9791228, }, { + 'x': '16:20:44.5868', 'y': 9790692, }], + 'free.memory2.total': [{ + 'x': '16:20:14.5680', 'y': 32671288, }, { + 'x': '16:20:24.5750', 'y': 32671288, }, { + 'x': '16:20:34.5809', 'y': 32671288, }, { + 'x': '16:20:44.5868', 'y': 32671288, }], + 'free.memory7.free': [{ + 'x': '16:20:14.5680', 'y': 1958368, }, { + 'x': '16:20:24.5750', 'y': 1956104, }, { + 'x': '16:20:34.5809', 'y': 1956040, }, { + 'x': '16:20:44.5868', 'y': 1956552, }], + 'free.memory1.used': [{ + 'x': '16:20:14.5680', 'y': 9788872, }, { + 'x': '16:20:24.5750', 'y': 9789212, }, { + 'x': '16:20:34.5809', 'y': 9791168, }, { + 'x': '16:20:44.5868', 'y': 9790996, }], + } + + actual_output, _, _ = self.rep._combine_metrics( + BARO_METRICS, BARO_TIMES, YARD_METRICS, YARD_TIMES + ) + self.assertEquals( + sorted(expected_output.keys()), + sorted(actual_output.keys()) + ) + + self.assertEquals( + expected_output, + actual_output, + ) + + @mock.patch.object(report.Report, '_get_metrics') + @mock.patch.object(report.Report, '_get_fieldkeys') + def test__generate_common(self, mock_keys, mock_metrics): + mock_metrics.return_value = MORE_DB_METRICS + mock_keys.return_value = MORE_DB_FIELDKEYS + datasets, table_vals = self.rep._generate_common(self.param) + self.assertEqual(MORE_EXPECTED_DATASETS, datasets) + self.assertEqual(MORE_EXPECTED_TABLE_VALS, table_vals) + + @mock.patch.object(report.Report, '_get_metrics') + @mock.patch.object(report.Report, '_get_fieldkeys') + @mock.patch.object(report.Report, '_validate') + def test_generate(self, mock_valid, mock_keys, mock_metrics): + mock_metrics.return_value = GOOD_DB_METRICS + mock_keys.return_value = GOOD_DB_FIELDKEYS + self.rep.generate(self.param) + mock_valid.assert_called_once_with(GOOD_YAML_NAME, GOOD_TASK_ID) + mock_metrics.assert_called_once_with() + mock_keys.assert_called_once_with() + self.assertEqual(GOOD_TIMESTAMP, self.rep.Timestamp) + + @mock.patch.object(report.Report, '_get_baro_metrics') + @mock.patch.object(report.Report, '_get_metrics') + @mock.patch.object(report.Report, '_get_fieldkeys') + @mock.patch.object(report.Report, '_validate') + def test_generate_nsb( + self, mock_valid, mock_keys, mock_metrics, mock_baro_metrics): + + mock_metrics.return_value = GOOD_DB_METRICS + mock_keys.return_value = GOOD_DB_FIELDKEYS + BARO_METRICS = { + # TODO: is timestamp needed here? + 'Timestamp': [ + '14:11:25.383698', '14:11:25.383712', '14:11:35.383696', + '14:11:35.383713', '14:11:45.383700', '14:11:45.383716'], + 'myhostname.cpu_value.cpu.user.0': { + '14:11:25.383698': 324050, + '14:11:35.383696': 324051, + '14:11:45.383700': 324054, + }, + 'myhostname.cpu_value.cpu.system.0': { + '14:11:25.383712': 193798, + '14:11:35.383713': 193800, + '14:11:45.383716': 193801, + } + } + mock_baro_metrics.return_value = BARO_METRICS + + self.rep.generate_nsb(self.param) + mock_valid.assert_called_once_with(GOOD_YAML_NAME, GOOD_TASK_ID) + mock_metrics.assert_called_once_with() + mock_keys.assert_called_once_with() + self.assertEqual(GOOD_TIMESTAMP, self.rep.Timestamp) diff --git a/yardstick/tests/unit/benchmark/core/test_task.py b/yardstick/tests/unit/benchmark/core/test_task.py new file mode 100644 index 000000000..0f09b3e59 --- /dev/null +++ b/yardstick/tests/unit/benchmark/core/test_task.py @@ -0,0 +1,644 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import copy +import io +import logging +import os +import sys + +import mock +import six +from six.moves import builtins +import unittest +import uuid +import collections + +from yardstick.benchmark.contexts import base +from yardstick.benchmark.contexts import dummy +from yardstick.benchmark.core import task +from yardstick.common import constants as consts +from yardstick.common import exceptions +from yardstick.common import task_template +from yardstick.common import utils + + +class TaskTestCase(unittest.TestCase): + + def setUp(self): + self._mock_log = mock.patch.object(task, 'LOG') + self.mock_log = self._mock_log.start() + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_log.stop() + + @mock.patch.object(base, 'Context') + def test_parse_nodes_with_context_same_context(self, mock_context): + scenario_cfg = { + "nodes": { + "host": "node1.LF", + "target": "node2.LF" + } + } + server_info = { + "ip": "10.20.0.3", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + mock_context.get_server.return_value = server_info + + context_cfg = task.parse_nodes_with_context(scenario_cfg) + + self.assertEqual(context_cfg["host"], server_info) + self.assertEqual(context_cfg["target"], server_info) + + def test_set_dispatchers(self): + t = task.Task() + output_config = {"DEFAULT": {"dispatcher": "file, http"}} + t._set_dispatchers(output_config) + self.assertEqual(output_config, output_config) + + @mock.patch.object(task, 'DispatcherBase') + def test__do_output(self, mock_dispatcher): + t = task.Task() + output_config = {"DEFAULT": {"dispatcher": "file, http"}} + + dispatcher1 = mock.MagicMock() + dispatcher1.__dispatcher_type__ = 'file' + + dispatcher2 = mock.MagicMock() + dispatcher2.__dispatcher_type__ = 'http' + + mock_dispatcher.get = mock.MagicMock(return_value=[dispatcher1, + dispatcher2]) + self.assertIsNone(t._do_output(output_config, {})) + + @mock.patch.object(base, 'Context') + def test_parse_networks_from_nodes(self, mock_context): + nodes = { + 'node1': { + 'interfaces': { + 'mgmt': { + 'network_name': 'mgmt', + }, + 'xe0': { + 'network_name': 'uplink_0', + }, + 'xe1': { + 'network_name': 'downlink_0', + }, + }, + }, + 'node2': { + 'interfaces': { + 'mgmt': { + 'network_name': 'mgmt', + }, + 'uplink_0': { + 'network_name': 'uplink_0', + }, + 'downlink_0': { + 'network_name': 'downlink_0', + }, + }, + }, + } + + mock_context.get_network.side_effect = iter([ + None, + { + 'name': 'mgmt', + 'network_type': 'flat', + }, + {}, + { + 'name': 'uplink_0', + 'subnet_cidr': '10.20.0.0/16', + }, + { + 'name': 'downlink_0', + 'segmentation_id': '1001', + }, + { + 'name': 'uplink_1', + }, + ]) + + # one for each interface + expected_get_network_calls = 6 + expected = { + 'mgmt': {'name': 'mgmt', 'network_type': 'flat'}, + 'uplink_0': {'name': 'uplink_0', 'subnet_cidr': '10.20.0.0/16'}, + 'uplink_1': {'name': 'uplink_1'}, + 'downlink_0': {'name': 'downlink_0', 'segmentation_id': '1001'}, + } + + networks = task.get_networks_from_nodes(nodes) + self.assertEqual(mock_context.get_network.call_count, expected_get_network_calls) + self.assertDictEqual(networks, expected) + + @mock.patch.object(base, 'Context') + @mock.patch.object(task, 'base_runner') + def test_run(self, mock_base_runner, *args): + scenario = { + 'host': 'athena.demo', + 'target': 'ares.demo', + 'runner': { + 'duration': 60, + 'interval': 1, + 'type': 'Duration' + }, + 'type': 'Ping' + } + + t = task.Task() + runner = mock.Mock() + runner.join.return_value = 0 + runner.get_output.return_value = {} + runner.get_result.return_value = [] + mock_base_runner.Runner.get.return_value = runner + t._run([scenario], False, "yardstick.out") + runner.run.assert_called_once() + + @mock.patch.object(base, 'Context') + @mock.patch.object(task, 'base_runner') + def test_run_ProxDuration(self, mock_base_runner, *args): + scenario = { + 'host': 'athena.demo', + 'target': 'ares.demo', + 'runner': { + 'duration': 60, + 'interval': 1, + 'sampled': 'yes', + 'confirmation': 1, + 'type': 'ProxDuration' + }, + 'type': 'Ping' + } + + t = task.Task() + runner = mock.Mock() + runner.join.return_value = 0 + runner.get_output.return_value = {} + runner.get_result.return_value = [] + mock_base_runner.Runner.get.return_value = runner + t._run([scenario], False, "yardstick.out") + runner.run.assert_called_once() + + @mock.patch.object(os, 'environ') + def test_check_precondition(self, mock_os_environ): + cfg = { + 'precondition': { + 'installer_type': 'compass', + 'deploy_scenarios': 'os-nosdn', + 'pod_name': 'huawei-pod1' + } + } + + t = task.TaskParser('/opt') + mock_os_environ.get.side_effect = ['compass', + 'os-nosdn', + 'huawei-pod1'] + result = t._check_precondition(cfg) + self.assertTrue(result) + + def test_parse_suite_no_constraint_no_args(self): + SAMPLE_SCENARIO_PATH = "no_constraint_no_args_scenario_sample.yaml" + t = task.TaskParser(self._get_file_abspath(SAMPLE_SCENARIO_PATH)) + with mock.patch.object(os, 'environ', + new={'NODE_NAME': 'huawei-pod1', 'INSTALLER_TYPE': 'compass'}): + task_files, task_args, task_args_fnames = t.parse_suite() + + self.assertEqual(task_files[0], self.change_to_abspath( + 'tests/opnfv/test_cases/opnfv_yardstick_tc037.yaml')) + self.assertEqual(task_files[1], self.change_to_abspath( + 'tests/opnfv/test_cases/opnfv_yardstick_tc043.yaml')) + self.assertIsNone(task_args[0]) + self.assertIsNone(task_args[1]) + self.assertIsNone(task_args_fnames[0]) + self.assertIsNone(task_args_fnames[1]) + + def test_parse_suite_no_constraint_with_args(self): + SAMPLE_SCENARIO_PATH = "no_constraint_with_args_scenario_sample.yaml" + t = task.TaskParser(self._get_file_abspath(SAMPLE_SCENARIO_PATH)) + with mock.patch.object(os, 'environ', + new={'NODE_NAME': 'huawei-pod1', 'INSTALLER_TYPE': 'compass'}): + task_files, task_args, task_args_fnames = t.parse_suite() + + self.assertEqual(task_files[0], self.change_to_abspath( + 'tests/opnfv/test_cases/opnfv_yardstick_tc037.yaml')) + self.assertEqual(task_files[1], self.change_to_abspath( + 'tests/opnfv/test_cases/opnfv_yardstick_tc043.yaml')) + self.assertIsNone(task_args[0]) + self.assertEqual(task_args[1], + '{"host": "node1.LF","target": "node2.LF"}') + self.assertIsNone(task_args_fnames[0]) + self.assertIsNone(task_args_fnames[1]) + + def test_parse_suite_with_constraint_no_args(self): + SAMPLE_SCENARIO_PATH = "with_constraint_no_args_scenario_sample.yaml" + t = task.TaskParser(self._get_file_abspath(SAMPLE_SCENARIO_PATH)) + with mock.patch.object(os, 'environ', + new={'NODE_NAME': 'huawei-pod1', 'INSTALLER_TYPE': 'compass'}): + task_files, task_args, task_args_fnames = t.parse_suite() + self.assertEqual(task_files[0], self.change_to_abspath( + 'tests/opnfv/test_cases/opnfv_yardstick_tc037.yaml')) + self.assertEqual(task_files[1], self.change_to_abspath( + 'tests/opnfv/test_cases/opnfv_yardstick_tc043.yaml')) + self.assertIsNone(task_args[0]) + self.assertIsNone(task_args[1]) + self.assertIsNone(task_args_fnames[0]) + self.assertIsNone(task_args_fnames[1]) + + def test_parse_suite_with_constraint_with_args(self): + SAMPLE_SCENARIO_PATH = "with_constraint_with_args_scenario_sample.yaml" + t = task.TaskParser(self._get_file_abspath(SAMPLE_SCENARIO_PATH)) + with mock.patch('os.environ', + new={'NODE_NAME': 'huawei-pod1', 'INSTALLER_TYPE': 'compass'}): + task_files, task_args, task_args_fnames = t.parse_suite() + + self.assertEqual(task_files[0], self.change_to_abspath( + 'tests/opnfv/test_cases/opnfv_yardstick_tc037.yaml')) + self.assertEqual(task_files[1], self.change_to_abspath( + 'tests/opnfv/test_cases/opnfv_yardstick_tc043.yaml')) + self.assertIsNone(task_args[0]) + self.assertEqual(task_args[1], + '{"host": "node1.LF","target": "node2.LF"}') + self.assertIsNone(task_args_fnames[0]) + self.assertIsNone(task_args_fnames[1]) + + def test_parse_options(self): + options = { + 'openstack': { + 'EXTERNAL_NETWORK': '$network' + }, + 'nodes': ['node1', '$node'], + 'host': '$host' + } + + t = task.Task() + t.outputs = { + 'network': 'ext-net', + 'node': 'node2', + 'host': 'server.yardstick' + } + + expected_result = { + 'openstack': { + 'EXTERNAL_NETWORK': 'ext-net' + }, + 'nodes': ['node1', 'node2'], + 'host': 'server.yardstick' + } + + actual_result = t._parse_options(options) + self.assertEqual(expected_result, actual_result) + + def test_parse_options_no_teardown(self): + options = { + 'openstack': { + 'EXTERNAL_NETWORK': '$network' + }, + 'nodes': ['node1', '$node'], + 'host': '$host', + 'contexts' : {'name': "my-context", + 'no_teardown': True} + } + + t = task.Task() + t.outputs = { + 'network': 'ext-net', + 'node': 'node2', + 'host': 'server.yardstick' + } + + expected_result = { + 'openstack': { + 'EXTERNAL_NETWORK': 'ext-net' + }, + 'nodes': ['node1', 'node2'], + 'host': 'server.yardstick', + 'contexts': {'name': 'my-context', + 'no_teardown': True, + } + } + + actual_result = t._parse_options(options) + self.assertEqual(expected_result, actual_result) + + @mock.patch.object(builtins, 'open', side_effect=mock.mock_open()) + @mock.patch.object(task, 'utils') + @mock.patch.object(logging, 'root') + def test_set_log(self, mock_logging_root, *args): + task_obj = task.Task() + task_obj.task_id = 'task_id' + task_obj._set_log() + mock_logging_root.addHandler.assert_called() + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + def change_to_abspath(self, filepath): + return os.path.join(consts.YARDSTICK_ROOT_PATH, filepath) + + +class TaskParserTestCase(unittest.TestCase): + + TASK = """ +{% set value1 = value1 or 'var1' %} +{% set value2 = value2 or 'var2' %} +key1: {{ value1 }} +key2: + - {{ value2 }}""" + + TASK_RENDERED_1 = u""" + + +key1: var1 +key2: + - var2""" + + TASK_RENDERED_2 = u""" + + +key1: var3 +key2: + - var4""" + + def setUp(self): + self.parser = task.TaskParser('fake/path') + self.scenario = { + 'host': 'athena.demo', + 'target': 'kratos.demo', + 'targets': [ + 'ares.demo', 'mars.demo' + ], + 'options': { + 'server_name': { + 'host': 'jupiter.demo', + 'target': 'saturn.demo', + }, + }, + 'nodes': { + 'tg__0': 'tg_0.demo', + 'vnf__0': 'vnf_0.demo', + } + } + + @staticmethod + def _remove_contexts(): + for context in base.Context.list: + context._delete_context() + base.Context.list = [] + + def test__change_node_names(self): + + ctx_attrs = { + 'name': 'demo', + 'task_id': '1234567890', + 'servers': [ + 'athena', 'kratos', + 'ares', 'mars', + 'jupiter', 'saturn', + 'tg_0', 'vnf_0' + ] + } + + my_context = dummy.DummyContext() + self.addCleanup(self._remove_contexts) + my_context.init(ctx_attrs) + + expected_scenario = { + 'host': 'athena.demo-12345678', + 'target': 'kratos.demo-12345678', + 'targets': [ + 'ares.demo-12345678', 'mars.demo-12345678' + ], + 'options': { + 'server_name': { + 'host': 'jupiter.demo-12345678', + 'target': 'saturn.demo-12345678', + }, + }, + 'nodes': { + 'tg__0': 'tg_0.demo-12345678', + 'vnf__0': 'vnf_0.demo-12345678', + } + } + + scenario = copy.deepcopy(self.scenario) + + self.parser._change_node_names(scenario, [my_context]) + self.assertEqual(scenario, expected_scenario) + + def test__change_node_names_context_not_found(self): + scenario = copy.deepcopy(self.scenario) + self.assertRaises(exceptions.ScenarioConfigContextNameNotFound, + self.parser._change_node_names, + scenario, []) + + def test__change_node_names_context_name_unchanged(self): + ctx_attrs = { + 'name': 'demo', + 'task_id': '1234567890', + 'flags': { + 'no_setup': True, + 'no_teardown': True + } + } + + my_context = dummy.DummyContext() + self.addCleanup(self._remove_contexts) + my_context.init(ctx_attrs) + + scenario = copy.deepcopy(self.scenario) + expected_scenario = copy.deepcopy(self.scenario) + + self.parser._change_node_names(scenario, [my_context]) + self.assertEqual(scenario, expected_scenario) + + def test__change_node_names_options_empty(self): + ctx_attrs = { + 'name': 'demo', + 'task_id': '1234567890' + } + + my_context = dummy.DummyContext() + self.addCleanup(self._remove_contexts) + my_context.init(ctx_attrs) + scenario = copy.deepcopy(self.scenario) + scenario['options'] = None + + self.parser._change_node_names(scenario, [my_context]) + self.assertIsNone(scenario['options']) + + def test__change_node_names_options_server_name_empty(self): + ctx_attrs = { + 'name': 'demo', + 'task_id': '1234567890' + } + + my_context = dummy.DummyContext() + self.addCleanup(self._remove_contexts) + my_context.init(ctx_attrs) + scenario = copy.deepcopy(self.scenario) + scenario['options']['server_name'] = None + + self.parser._change_node_names(scenario, [my_context]) + self.assertIsNone(scenario['options']['server_name']) + + def test__change_node_names_target_map(self): + ctx_attrs = { + 'name': 'demo', + 'task_id': '1234567890' + } + my_context = dummy.DummyContext() + self.addCleanup(self._remove_contexts) + my_context.init(ctx_attrs) + scenario = copy.deepcopy(self.scenario) + scenario['nodes'] = { + 'tg__0': { + 'name': 'tg__0.demo', + 'public_ip_attr': "1.1.1.1", + }, + 'vnf__0': { + 'name': 'vnf__0.demo', + 'public_ip_attr': "2.2.2.2", + } + } + self.parser._change_node_names(scenario, [my_context]) + for target in scenario['nodes'].values(): + self.assertIsInstance(target, collections.Mapping) + + def test__change_node_names_not_target_map(self): + ctx_attrs = { + 'name': 'demo', + 'task_id': '1234567890' + } + my_context = dummy.DummyContext() + self.addCleanup(self._remove_contexts) + my_context.init(ctx_attrs) + scenario = copy.deepcopy(self.scenario) + self.parser._change_node_names(scenario, [my_context]) + for target in scenario['nodes'].values(): + self.assertNotIsInstance(target, collections.Mapping) + + def test__parse_tasks(self): + task_obj = task.Task() + _uuid = uuid.uuid4() + task_obj.task_id = _uuid + task_files = ['/directory/task_file_name.yml'] + mock_parser = mock.Mock() + mock_parser.parse_task.return_value = {'rendered': 'File content'} + mock_args = mock.Mock() + mock_args.render_only = False + + tasks = task_obj._parse_tasks(mock_parser, task_files, mock_args, + ['arg1'], ['file_arg1']) + self.assertEqual( + [{'rendered': 'File content', 'case_name': 'task_file_name'}], + tasks) + mock_parser.parse_task.assert_called_once_with( + _uuid, 'arg1', 'file_arg1') + + @mock.patch.object(sys, 'exit') + @mock.patch.object(utils, 'write_file') + @mock.patch.object(utils, 'makedirs') + def test__parse_tasks_render_only(self, mock_makedirs, mock_write_file, + mock_exit): + task_obj = task.Task() + _uuid = uuid.uuid4() + task_obj.task_id = _uuid + task_files = ['/directory/task_file_name.yml'] + mock_parser = mock.Mock() + mock_parser.parse_task.return_value = {'rendered': 'File content'} + mock_args = mock.Mock() + mock_args.render_only = '/output_directory' + + task_obj._parse_tasks(mock_parser, task_files, mock_args, + ['arg1'], ['file_arg1']) + mock_makedirs.assert_called_once_with('/output_directory') + mock_write_file.assert_called_once_with( + '/output_directory/000-task_file_name.yml', 'File content') + mock_exit.assert_called_once_with(0) + + def test__render_task_no_args(self): + task_parser = task.TaskParser('task_file') + task_str = io.StringIO(six.text_type(self.TASK)) + with mock.patch.object(six.moves.builtins, 'open', + return_value=task_str) as mock_open: + parsed, rendered = task_parser._render_task(None, None) + + self.assertEqual(self.TASK_RENDERED_1, rendered) + self.assertEqual({'key1': 'var1', 'key2': ['var2']}, parsed) + mock_open.assert_called_once_with('task_file') + + def test__render_task_arguments(self): + task_parser = task.TaskParser('task_file') + task_str = io.StringIO(six.text_type(self.TASK)) + with mock.patch.object(six.moves.builtins, 'open', + return_value=task_str) as mock_open: + parsed, rendered = task_parser._render_task('value1: "var1"', None) + + self.assertEqual(self.TASK_RENDERED_1, rendered) + self.assertEqual({'key1': 'var1', 'key2': ['var2']}, parsed) + mock_open.assert_called_once_with('task_file') + + def test__render_task_file_arguments(self): + task_parser = task.TaskParser('task_file') + with mock.patch.object(six.moves.builtins, 'open') as mock_open: + mock_open.side_effect = ( + io.StringIO(six.text_type('value2: var4')), + io.StringIO(six.text_type(self.TASK)) + ) + parsed, rendered = task_parser._render_task('value1: "var3"', + 'args_file') + + self.assertEqual(self.TASK_RENDERED_2, rendered) + self.assertEqual({'key1': 'var3', 'key2': ['var4']}, parsed) + mock_open.assert_has_calls([mock.call('args_file'), + mock.call('task_file')]) + + @mock.patch.object(builtins, 'print') + def test__render_task_error_arguments(self, *args): + with self.assertRaises(exceptions.TaskRenderArgumentError): + task.TaskParser('task_file')._render_task('value1="var3"', None) + + def test__render_task_error_task_file(self): + task_parser = task.TaskParser('task_file') + with mock.patch.object(six.moves.builtins, 'open') as mock_open: + mock_open.side_effect = ( + io.StringIO(six.text_type('value2: var4')), + IOError() + ) + with self.assertRaises(exceptions.TaskReadError): + task_parser._render_task('value1: "var3"', 'args_file') + + mock_open.assert_has_calls([mock.call('args_file'), + mock.call('task_file')]) + + def test__render_task_render_error(self): + task_parser = task.TaskParser('task_file') + with mock.patch.object(six.moves.builtins, 'open') as mock_open, \ + mock.patch.object(task_template.TaskTemplate, 'render', + side_effect=TypeError) as mock_render: + mock_open.side_effect = ( + io.StringIO(six.text_type('value2: var4')), + io.StringIO(six.text_type(self.TASK)) + ) + with self.assertRaises(exceptions.TaskRenderError): + task_parser._render_task('value1: "var3"', 'args_file') + + mock_open.assert_has_calls([mock.call('args_file'), + mock.call('task_file')]) + mock_render.assert_has_calls( + [mock.call(self.TASK, value1='var3', value2='var4')]) diff --git a/yardstick/tests/unit/benchmark/core/test_testcase.py b/yardstick/tests/unit/benchmark/core/test_testcase.py new file mode 100644 index 000000000..077848d77 --- /dev/null +++ b/yardstick/tests/unit/benchmark/core/test_testcase.py @@ -0,0 +1,35 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +from six.moves import builtins + +from yardstick.benchmark.core import testcase +from yardstick.tests.unit import base as ut_base + + +class Arg(object): + + def __init__(self): + self.casename = ('opnfv_yardstick_tc001', ) + + +class TestcaseTestCase(ut_base.BaseUnitTestCase): + + def test_list_all(self): + t = testcase.Testcase() + result = t.list_all("") + self.assertIsInstance(result, list) + + @mock.patch.object(builtins, 'print') + def test_show(self, *args): + t = testcase.Testcase() + casename = Arg() + result = t.show(casename) + self.assertTrue(result) diff --git a/yardstick/tests/unit/benchmark/core/with_constraint_no_args_scenario_sample.yaml b/yardstick/tests/unit/benchmark/core/with_constraint_no_args_scenario_sample.yaml new file mode 100644 index 000000000..168d4b01a --- /dev/null +++ b/yardstick/tests/unit/benchmark/core/with_constraint_no_args_scenario_sample.yaml @@ -0,0 +1,24 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +--- +# Huawei US bare daily task suite + +schema: "yardstick:suite:0.1" + +name: "os-nosdn-nofeature-ha" +test_cases_dir: "tests/opnfv/test_cases/" +test_cases: +- + file_name: opnfv_yardstick_tc037.yaml +- + file_name: opnfv_yardstick_tc043.yaml + constraint: + installer: compass + pod: huawei-pod1 + diff --git a/yardstick/tests/unit/benchmark/core/with_constraint_with_args_scenario_sample.yaml b/yardstick/tests/unit/benchmark/core/with_constraint_with_args_scenario_sample.yaml new file mode 100644 index 000000000..299e5de56 --- /dev/null +++ b/yardstick/tests/unit/benchmark/core/with_constraint_with_args_scenario_sample.yaml @@ -0,0 +1,26 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +--- +# Huawei US bare daily task suite + +schema: "yardstick:suite:0.1" + +name: "os-nosdn-nofeature-ha" +test_cases_dir: "tests/opnfv/test_cases/" +test_cases: +- + file_name: opnfv_yardstick_tc037.yaml +- + file_name: opnfv_yardstick_tc043.yaml + constraint: + installer: compass + pod: huawei-pod1 + task_args: + huawei-pod1: '{"host": "node1.LF","target": "node2.LF"}' + diff --git a/yardstick/tests/unit/benchmark/runner/__init__.py b/yardstick/tests/unit/benchmark/runner/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/runner/__init__.py diff --git a/yardstick/tests/unit/benchmark/runner/test_arithmetic.py b/yardstick/tests/unit/benchmark/runner/test_arithmetic.py new file mode 100644 index 000000000..35d935cd5 --- /dev/null +++ b/yardstick/tests/unit/benchmark/runner/test_arithmetic.py @@ -0,0 +1,446 @@ +############################################################################## +# Copyright (c) 2018 Nokia and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest +import multiprocessing +import os +import time + +from yardstick.benchmark.runners import arithmetic +from yardstick.common import exceptions as y_exc + + +class ArithmeticRunnerTest(unittest.TestCase): + class MyMethod(object): + SLA_VALIDATION_ERROR_SIDE_EFFECT = 1 + BROAD_EXCEPTION_SIDE_EFFECT = 2 + + def __init__(self, side_effect=0): + self.count = 101 + self.side_effect = side_effect + + def __call__(self, data): + self.count += 1 + data['my_key'] = self.count + if self.side_effect == self.SLA_VALIDATION_ERROR_SIDE_EFFECT: + raise y_exc.SLAValidationError(case_name='My Case', + error_msg='my error message') + elif self.side_effect == self.BROAD_EXCEPTION_SIDE_EFFECT: + raise y_exc.YardstickException + return self.count + + def setUp(self): + self.scenario_cfg = { + 'runner': { + 'interval': 0, + 'iter_type': 'nested_for_loops', + 'iterators': [ + { + 'name': 'stride', + 'start': 64, + 'stop': 128, + 'step': 64 + }, + { + 'name': 'size', + 'start': 500, + 'stop': 2000, + 'step': 500 + } + ] + }, + 'type': 'some_type' + } + + self.benchmark = mock.Mock() + self.benchmark_cls = mock.Mock(return_value=self.benchmark) + + def _assert_defaults__worker_process_run_setup_and_teardown(self): + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + self.benchmark.teardown.assert_called_once() + + @mock.patch.object(os, 'getpid') + @mock.patch.object(multiprocessing, 'Process') + def test__run_benchmark_called_with(self, mock_multiprocessing_process, + mock_os_getpid): + mock_os_getpid.return_value = 101 + + runner = arithmetic.ArithmeticRunner({}) + benchmark_cls = mock.Mock() + runner._run_benchmark(benchmark_cls, 'my_method', self.scenario_cfg, + {}) + mock_multiprocessing_process.assert_called_once_with( + name='Arithmetic-some_type-101', + target=arithmetic._worker_process, + args=(runner.result_queue, benchmark_cls, 'my_method', + self.scenario_cfg, {}, runner.aborted, runner.output_queue)) + + @mock.patch.object(os, 'getpid') + def test__worker_process_runner_id(self, mock_os_getpid): + mock_os_getpid.return_value = 101 + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self.assertEqual(self.scenario_cfg['runner']['runner_id'], 101) + + @mock.patch.object(time, 'sleep') + def test__worker_process_calls_nested_for_loops(self, mock_time_sleep): + self.scenario_cfg['runner']['interval'] = 99 + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.benchmark.my_method.assert_has_calls([mock.call({})] * 8) + self.assertEqual(self.benchmark.my_method.call_count, 8) + mock_time_sleep.assert_has_calls([mock.call(99)] * 8) + self.assertEqual(mock_time_sleep.call_count, 8) + + @mock.patch.object(time, 'sleep') + def test__worker_process_calls_tuple_loops(self, mock_time_sleep): + self.scenario_cfg['runner']['interval'] = 99 + self.scenario_cfg['runner']['iter_type'] = 'tuple_loops' + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.benchmark.my_method.assert_has_calls([mock.call({})] * 2) + self.assertEqual(self.benchmark.my_method.call_count, 2) + mock_time_sleep.assert_has_calls([mock.call(99)] * 2) + self.assertEqual(mock_time_sleep.call_count, 2) + + def test__worker_process_stored_options_nested_for_loops(self): + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + + def test__worker_process_stored_options_tuple_loops(self): + self.scenario_cfg['runner']['iter_type'] = 'tuple_loops' + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 1000}) + + def test__worker_process_aborted_set_early(self): + aborted = multiprocessing.Event() + aborted.set() + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + aborted, mock.Mock()) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.scenario_cfg['options'], {}) + self.benchmark.my_method.assert_not_called() + + def test__worker_process_output_queue_nested_for_loops(self): + self.benchmark.my_method = self.MyMethod() + + output_queue = multiprocessing.Queue() + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 109) + result = [] + while not output_queue.empty(): + result.append(output_queue.get()) + self.assertListEqual(result, [102, 103, 104, 105, 106, 107, 108, 109]) + + def test__worker_process_output_queue_tuple_loops(self): + self.scenario_cfg['runner']['iter_type'] = 'tuple_loops' + self.benchmark.my_method = self.MyMethod() + + output_queue = multiprocessing.Queue() + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 103) + result = [] + while not output_queue.empty(): + result.append(output_queue.get()) + self.assertListEqual(result, [102, 103]) + + def test__worker_process_queue_nested_for_loops(self): + self.benchmark.my_method = self.MyMethod() + + queue = multiprocessing.Queue() + timestamp = time.time() + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 109) + count = 0 + while not queue.empty(): + count += 1 + result = queue.get() + self.assertEqual(result['errors'], '') + self.assertEqual(result['data'], {'my_key': count + 101}) + self.assertEqual(result['sequence'], count) + self.assertGreater(result['timestamp'], timestamp) + timestamp = result['timestamp'] + + def test__worker_process_queue_tuple_loops(self): + self.scenario_cfg['runner']['iter_type'] = 'tuple_loops' + self.benchmark.my_method = self.MyMethod() + + queue = multiprocessing.Queue() + timestamp = time.time() + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 103) + count = 0 + while not queue.empty(): + count += 1 + result = queue.get() + self.assertEqual(result['errors'], '') + self.assertEqual(result['data'], {'my_key': count + 101}) + self.assertEqual(result['sequence'], count) + self.assertGreater(result['timestamp'], timestamp) + timestamp = result['timestamp'] + + def test__worker_process_except_sla_validation_error_no_sla_cfg(self): + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.call_count, 8) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + + def test__worker_process_output_on_sla_validation_error_no_sla_cfg(self): + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.SLA_VALIDATION_ERROR_SIDE_EFFECT) + + queue = multiprocessing.Queue() + output_queue = multiprocessing.Queue() + timestamp = time.time() + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 109) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + count = 0 + while not queue.empty(): + count += 1 + result = queue.get() + self.assertEqual(result['errors'], '') + self.assertEqual(result['data'], {'my_key': count + 101}) + self.assertEqual(result['sequence'], count) + self.assertGreater(result['timestamp'], timestamp) + timestamp = result['timestamp'] + self.assertEqual(count, 8) + self.assertTrue(output_queue.empty()) + + def test__worker_process_except_sla_validation_error_sla_cfg_monitor(self): + self.scenario_cfg['sla'] = {'action': 'monitor'} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.call_count, 8) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + + def test__worker_process_output_sla_validation_error_sla_cfg_monitor(self): + self.scenario_cfg['sla'] = {'action': 'monitor'} + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.SLA_VALIDATION_ERROR_SIDE_EFFECT) + + queue = multiprocessing.Queue() + output_queue = multiprocessing.Queue() + timestamp = time.time() + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 109) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + count = 0 + while not queue.empty(): + count += 1 + result = queue.get() + self.assertEqual(result['errors'], + ('My Case SLA validation failed. ' + 'Error: my error message',)) + self.assertEqual(result['data'], {'my_key': count + 101}) + self.assertEqual(result['sequence'], count) + self.assertGreater(result['timestamp'], timestamp) + timestamp = result['timestamp'] + self.assertEqual(count, 8) + self.assertTrue(output_queue.empty()) + + def test__worker_process_raise_sla_validation_error_sla_cfg_assert(self): + self.scenario_cfg['sla'] = {'action': 'assert'} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + + with self.assertRaises(y_exc.SLAValidationError): + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.my_method.assert_called_once() + self.benchmark.setup.assert_called_once() + self.benchmark.teardown.assert_not_called() + + def test__worker_process_output_sla_validation_error_sla_cfg_assert(self): + self.scenario_cfg['sla'] = {'action': 'assert'} + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.SLA_VALIDATION_ERROR_SIDE_EFFECT) + + queue = multiprocessing.Queue() + output_queue = multiprocessing.Queue() + with self.assertRaisesRegexp( + y_exc.SLAValidationError, + 'My Case SLA validation failed. Error: my error message'): + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + self.assertEqual(self.benchmark.my_method.count, 102) + self.benchmark.teardown.assert_not_called() + self.assertTrue(queue.empty()) + self.assertTrue(output_queue.empty()) + + def test__worker_process_broad_exception_no_sla_cfg_early_exit(self): + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.YardstickException) + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.benchmark.my_method.assert_called_once() + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 64, 'size': 500}) + + def test__worker_process_output_on_broad_exception_no_sla_cfg(self): + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.BROAD_EXCEPTION_SIDE_EFFECT) + + queue = multiprocessing.Queue() + output_queue = multiprocessing.Queue() + timestamp = time.time() + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 102) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 64, 'size': 500}) + self.assertEqual(queue.qsize(), 1) + result = queue.get() + self.assertGreater(result['timestamp'], timestamp) + self.assertEqual(result['data'], {'my_key': 102}) + self.assertRegexpMatches( + result['errors'], + 'YardstickException: An unknown exception occurred.') + self.assertEqual(result['sequence'], 1) + self.assertTrue(output_queue.empty()) + + def test__worker_process_broad_exception_sla_cfg_not_none(self): + self.scenario_cfg['sla'] = {'action': 'some action'} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.YardstickException) + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.call_count, 8) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + + def test__worker_process_output_on_broad_exception_sla_cfg_not_none(self): + self.scenario_cfg['sla'] = {'action': 'some action'} + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.BROAD_EXCEPTION_SIDE_EFFECT) + + queue = multiprocessing.Queue() + output_queue = multiprocessing.Queue() + timestamp = time.time() + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 109) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + self.assertTrue(output_queue.empty()) + count = 0 + while not queue.empty(): + count += 1 + result = queue.get() + self.assertGreater(result['timestamp'], timestamp) + self.assertEqual(result['data'], {'my_key': count + 101}) + self.assertRegexpMatches( + result['errors'], + 'YardstickException: An unknown exception occurred.') + self.assertEqual(result['sequence'], count) + + def test__worker_process_benchmark_teardown_on_broad_exception(self): + self.benchmark.teardown = mock.Mock( + side_effect=y_exc.YardstickException) + + with self.assertRaises(SystemExit) as raised: + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + self.assertEqual(raised.exception.code, 1) + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.call_count, 8) diff --git a/yardstick/tests/unit/benchmark/runner/test_base.py b/yardstick/tests/unit/benchmark/runner/test_base.py new file mode 100644 index 000000000..07d6f1843 --- /dev/null +++ b/yardstick/tests/unit/benchmark/runner/test_base.py @@ -0,0 +1,119 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import time + +import mock +import subprocess + +from yardstick.benchmark.runners import base as runner_base +from yardstick.benchmark.runners import iteration +from yardstick.tests.unit import base as ut_base + + +class ActionTestCase(ut_base.BaseUnitTestCase): + + def setUp(self): + self._mock_log = mock.patch.object(runner_base.log, 'error') + self.mock_log = self._mock_log.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_log.stop() + + @mock.patch.object(subprocess, 'check_output') + def test__execute_shell_command(self, mock_subprocess): + mock_subprocess.side_effect = subprocess.CalledProcessError(-1, '') + self.assertEqual(runner_base._execute_shell_command("")[0], -1) + + @mock.patch.object(subprocess, 'check_output') + def test__single_action(self, mock_subprocess): + mock_subprocess.side_effect = subprocess.CalledProcessError(-1, '') + runner_base._single_action(0, 'echo', mock.Mock()) + + @mock.patch.object(subprocess, 'check_output') + def test__periodic_action(self, mock_subprocess): + mock_subprocess.side_effect = subprocess.CalledProcessError(-1, '') + runner_base._periodic_action(0, 'echo', mock.Mock()) + + +class ScenarioOutputTestCase(ut_base.BaseUnitTestCase): + + def setUp(self): + self.output_queue = mock.Mock() + self.scenario_output = runner_base.ScenarioOutput(self.output_queue, + sequence=1) + + @mock.patch.object(time, 'time') + def test_push(self, mock_time): + mock_time.return_value = 2 + data = {"value1": 1} + self.scenario_output.push(data) + self.output_queue.put.assert_called_once_with({'timestamp': 2, + 'sequence': 1, + 'data': data}, True, 10) + + def test_push_no_timestamp(self): + self.scenario_output["value1"] = 1 + self.scenario_output.push(None, False) + self.output_queue.put.assert_called_once_with({'sequence': 1, + 'value1': 1}, True, 10) + + +class RunnerTestCase(ut_base.BaseUnitTestCase): + + def setUp(self): + config = { + 'output_config': { + 'DEFAULT': { + 'dispatcher': 'file' + } + } + } + self.runner = iteration.IterationRunner(config) + + @mock.patch("yardstick.benchmark.runners.iteration.multiprocessing") + def test_get_output(self, *args): + self.runner.output_queue.put({'case': 'opnfv_yardstick_tc002'}) + self.runner.output_queue.put({'criteria': 'PASS'}) + + idle_result = { + 'case': 'opnfv_yardstick_tc002', + 'criteria': 'PASS' + } + + for _ in range(1000): + time.sleep(0.01) + if not self.runner.output_queue.empty(): + break + actual_result = self.runner.get_output() + self.assertEqual(idle_result, actual_result) + + @mock.patch("yardstick.benchmark.runners.iteration.multiprocessing") + def test_get_result(self, *args): + self.runner.result_queue.put({'case': 'opnfv_yardstick_tc002'}) + self.runner.result_queue.put({'criteria': 'PASS'}) + + idle_result = [ + {'case': 'opnfv_yardstick_tc002'}, + {'criteria': 'PASS'} + ] + + for _ in range(1000): + time.sleep(0.01) + if not self.runner.result_queue.empty(): + break + actual_result = self.runner.get_result() + self.assertEqual(idle_result, actual_result) + + def test__run_benchmark(self): + runner = runner_base.Runner(mock.Mock()) + + with self.assertRaises(NotImplementedError): + runner._run_benchmark(mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock()) diff --git a/yardstick/tests/unit/benchmark/runner/test_duration.py b/yardstick/tests/unit/benchmark/runner/test_duration.py new file mode 100644 index 000000000..fa47e96bf --- /dev/null +++ b/yardstick/tests/unit/benchmark/runner/test_duration.py @@ -0,0 +1,315 @@ +############################################################################## +# Copyright (c) 2018 Nokia and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest +import multiprocessing +import os +import time + +from yardstick.benchmark.runners import duration +from yardstick.common import exceptions as y_exc + + +class DurationRunnerTest(unittest.TestCase): + class MyMethod(object): + SLA_VALIDATION_ERROR_SIDE_EFFECT = 1 + BROAD_EXCEPTION_SIDE_EFFECT = 2 + + def __init__(self, side_effect=0): + self.count = 101 + self.side_effect = side_effect + + def __call__(self, data): + self.count += 1 + data['my_key'] = self.count + if self.side_effect == self.SLA_VALIDATION_ERROR_SIDE_EFFECT: + raise y_exc.SLAValidationError(case_name='My Case', + error_msg='my error message') + elif self.side_effect == self.BROAD_EXCEPTION_SIDE_EFFECT: + raise y_exc.YardstickException + return self.count + + def setUp(self): + self.scenario_cfg = { + 'runner': {'interval': 0, "duration": 0}, + 'type': 'some_type' + } + + self.benchmark = mock.Mock() + self.benchmark_cls = mock.Mock(return_value=self.benchmark) + + def _assert_defaults__worker_run_setup_and_teardown(self): + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + self.benchmark.teardown.assert_called_once() + + def _assert_defaults__worker_run_one_iteration(self): + self.benchmark.pre_run_wait_time.assert_called_once_with(0) + self.benchmark.my_method.assert_called_once_with({}) + self.benchmark.post_run_wait_time.assert_called_once_with(0) + + @mock.patch.object(os, 'getpid') + @mock.patch.object(multiprocessing, 'Process') + def test__run_benchmark_called_with(self, mock_multiprocessing_process, + mock_os_getpid): + mock_os_getpid.return_value = 101 + + runner = duration.DurationRunner({}) + benchmark_cls = mock.Mock() + runner._run_benchmark(benchmark_cls, 'my_method', self.scenario_cfg, + {}) + mock_multiprocessing_process.assert_called_once_with( + name='Duration-some_type-101', + target=duration._worker_process, + args=(runner.result_queue, benchmark_cls, 'my_method', + self.scenario_cfg, {}, runner.aborted, runner.output_queue)) + + @mock.patch.object(os, 'getpid') + def test__worker_process_runner_id(self, mock_os_getpid): + mock_os_getpid.return_value = 101 + + duration._worker_process(mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self.assertEqual(self.scenario_cfg['runner']['runner_id'], 101) + + def test__worker_process_called_with_cfg(self): + duration._worker_process(mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + self._assert_defaults__worker_run_one_iteration() + + def test__worker_process_called_with_cfg_loop(self): + self.scenario_cfg['runner']['duration'] = 0.01 + + duration._worker_process(mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + self.assertGreater(self.benchmark.pre_run_wait_time.call_count, 0) + self.assertGreater(self.benchmark.my_method.call_count, 0) + self.assertGreater(self.benchmark.post_run_wait_time.call_count, 0) + + def test__worker_process_called_without_cfg(self): + scenario_cfg = {'runner': {}} + aborted = multiprocessing.Event() + aborted.set() + + duration._worker_process(mock.Mock(), self.benchmark_cls, 'my_method', + scenario_cfg, {}, aborted, mock.Mock()) + + self.benchmark_cls.assert_called_once_with(scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + self.benchmark.pre_run_wait_time.assert_called_once_with(1) + self.benchmark.my_method.assert_called_once_with({}) + self.benchmark.post_run_wait_time.assert_called_once_with(1) + self.benchmark.teardown.assert_called_once() + + def test__worker_process_output_queue(self): + self.benchmark.my_method = mock.Mock(return_value='my_result') + + output_queue = multiprocessing.Queue() + duration._worker_process(mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.1) + + self._assert_defaults__worker_run_setup_and_teardown() + self._assert_defaults__worker_run_one_iteration() + self.assertEquals(output_queue.get(), 'my_result') + + def test__worker_process_output_queue_multiple_iterations(self): + self.scenario_cfg['runner']['duration'] = 0.01 + self.benchmark.my_method = self.MyMethod() + + output_queue = multiprocessing.Queue() + duration._worker_process(mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.1) + + self._assert_defaults__worker_run_setup_and_teardown() + self.assertGreater(self.benchmark.pre_run_wait_time.call_count, 0) + self.assertGreater(self.benchmark.my_method.count, 1) + self.assertGreater(self.benchmark.post_run_wait_time.call_count, 0) + + count = 101 + while not output_queue.empty(): + count += 1 + self.assertEquals(output_queue.get(), count) + + def test__worker_process_queue(self): + self.benchmark.my_method = self.MyMethod() + + queue = multiprocessing.Queue() + timestamp = time.time() + duration._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + time.sleep(0.1) + + self._assert_defaults__worker_run_setup_and_teardown() + self.benchmark.pre_run_wait_time.assert_called_once_with(0) + self.benchmark.post_run_wait_time.assert_called_once_with(0) + + result = queue.get() + self.assertGreater(result['timestamp'], timestamp) + self.assertEqual(result['errors'], '') + self.assertEqual(result['data'], {'my_key': 102}) + self.assertEqual(result['sequence'], 1) + + def test__worker_process_queue_multiple_iterations(self): + self.scenario_cfg['runner']['duration'] = 0.5 + self.benchmark.my_method = self.MyMethod() + + queue = multiprocessing.Queue() + timestamp = time.time() + duration._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + time.sleep(0.1) + + self._assert_defaults__worker_run_setup_and_teardown() + self.assertGreater(self.benchmark.pre_run_wait_time.call_count, 0) + self.assertGreater(self.benchmark.my_method.count, 1) + self.assertGreater(self.benchmark.post_run_wait_time.call_count, 0) + + count = 0 + while not queue.empty(): + count += 1 + result = queue.get() + self.assertGreater(result['timestamp'], timestamp) + self.assertEqual(result['errors'], '') + self.assertEqual(result['data'], {'my_key': count + 101}) + self.assertEqual(result['sequence'], count) + + def test__worker_process_except_sla_validation_error_no_sla_cfg(self): + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + + duration._worker_process(mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + self._assert_defaults__worker_run_one_iteration() + + def test__worker_process_except_sla_validation_error_sla_cfg_monitor(self): + self.scenario_cfg['sla'] = {'action': 'monitor'} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + + duration._worker_process(mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + self._assert_defaults__worker_run_one_iteration() + + def test__worker_process_raise_sla_validation_error_sla_cfg_default(self): + self.scenario_cfg['sla'] = {} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + + with self.assertRaises(y_exc.SLAValidationError): + duration._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + self.benchmark.pre_run_wait_time.assert_called_once_with(0) + self.benchmark.my_method.assert_called_once_with({}) + + def test__worker_process_raise_sla_validation_error_sla_cfg_assert(self): + self.scenario_cfg['sla'] = {'action': 'assert'} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + + with self.assertRaises(y_exc.SLAValidationError): + duration._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + self.benchmark.pre_run_wait_time.assert_called_once_with(0) + self.benchmark.my_method.assert_called_once_with({}) + + def test__worker_process_queue_on_sla_validation_error_monitor(self): + self.scenario_cfg['sla'] = {'action': 'monitor'} + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.SLA_VALIDATION_ERROR_SIDE_EFFECT) + + queue = multiprocessing.Queue() + timestamp = time.time() + duration._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + time.sleep(0.1) + + self._assert_defaults__worker_run_setup_and_teardown() + self.benchmark.pre_run_wait_time.assert_called_once_with(0) + self.benchmark.post_run_wait_time.assert_called_once_with(0) + + result = queue.get() + self.assertGreater(result['timestamp'], timestamp) + self.assertEqual(result['errors'], ('My Case SLA validation failed. ' + 'Error: my error message',)) + self.assertEqual(result['data'], {'my_key': 102}) + self.assertEqual(result['sequence'], 1) + + def test__worker_process_broad_exception(self): + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.YardstickException) + + duration._worker_process(mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + self._assert_defaults__worker_run_one_iteration() + + def test__worker_process_queue_on_broad_exception(self): + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.BROAD_EXCEPTION_SIDE_EFFECT) + + queue = multiprocessing.Queue() + timestamp = time.time() + duration._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + time.sleep(0.1) + + self._assert_defaults__worker_run_setup_and_teardown() + self.benchmark.pre_run_wait_time.assert_called_once_with(0) + self.benchmark.post_run_wait_time.assert_called_once_with(0) + + result = queue.get() + self.assertGreater(result['timestamp'], timestamp) + self.assertNotEqual(result['errors'], '') + self.assertEqual(result['data'], {'my_key': 102}) + self.assertEqual(result['sequence'], 1) + + def test__worker_process_benchmark_teardown_on_broad_exception(self): + self.benchmark.teardown = mock.Mock( + side_effect=y_exc.YardstickException) + + with self.assertRaises(SystemExit) as raised: + duration._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + self.assertEqual(raised.exception.code, 1) + self._assert_defaults__worker_run_setup_and_teardown() + self._assert_defaults__worker_run_one_iteration() diff --git a/yardstick/tests/unit/benchmark/runner/test_iteration.py b/yardstick/tests/unit/benchmark/runner/test_iteration.py new file mode 100644 index 000000000..783b236f5 --- /dev/null +++ b/yardstick/tests/unit/benchmark/runner/test_iteration.py @@ -0,0 +1,45 @@ +############################################################################## +# Copyright (c) 2018 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest +import multiprocessing +from yardstick.benchmark.runners import iteration +from yardstick.common import exceptions as y_exc + + +class IterationRunnerTest(unittest.TestCase): + def setUp(self): + self.scenario_cfg = { + 'runner': {'interval': 0, "duration": 0}, + 'type': 'some_type' + } + + self.benchmark = mock.Mock() + self.benchmark_cls = mock.Mock(return_value=self.benchmark) + + def _assert_defaults__worker_run_setup_and_teardown(self): + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + + def _assert_defaults__worker_run_one_iteration(self): + self.benchmark.pre_run_wait_time.assert_called_once_with(0) + self.benchmark.my_method.assert_called_once_with({}) + + def test__worker_process_broad_exception(self): + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.YardstickException) + + with self.assertRaises(Exception): + iteration._worker_process(mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_one_iteration() + self._assert_defaults__worker_run_setup_and_teardown() diff --git a/yardstick/tests/unit/benchmark/runner/test_proxduration.py b/yardstick/tests/unit/benchmark/runner/test_proxduration.py new file mode 100644 index 000000000..056195fd3 --- /dev/null +++ b/yardstick/tests/unit/benchmark/runner/test_proxduration.py @@ -0,0 +1,286 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +import unittest +import multiprocessing +import os + +from yardstick.benchmark.runners import proxduration +from yardstick.common import constants +from yardstick.common import exceptions as y_exc + + +class ProxDurationRunnerTest(unittest.TestCase): + + class MyMethod(object): + SLA_VALIDATION_ERROR_SIDE_EFFECT = 1 + BROAD_EXCEPTION_SIDE_EFFECT = 2 + + def __init__(self, side_effect=0): + self.count = 101 + self.side_effect = side_effect + + def __call__(self, data): + self.count += 1 + data['my_key'] = self.count + if self.side_effect == self.SLA_VALIDATION_ERROR_SIDE_EFFECT: + raise y_exc.SLAValidationError(case_name='My Case', + error_msg='my error message') + elif self.side_effect == self.BROAD_EXCEPTION_SIDE_EFFECT: + raise y_exc.YardstickException + return self.count + + def setUp(self): + self.scenario_cfg = { + 'runner': {'interval': 0, "duration": 0}, + 'type': 'some_type' + } + + self.benchmark = mock.Mock() + self.benchmark_cls = mock.Mock(return_value=self.benchmark) + + def _assert_defaults__worker_run_setup_and_teardown(self): + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + self.benchmark.teardown.assert_called_once() + + @mock.patch.object(os, 'getpid') + @mock.patch.object(multiprocessing, 'Process') + def test__run_benchmark_called_with(self, mock_multiprocessing_process, + mock_os_getpid): + mock_os_getpid.return_value = 101 + + runner = proxduration.ProxDurationRunner({}) + benchmark_cls = mock.Mock() + runner._run_benchmark(benchmark_cls, 'my_method', self.scenario_cfg, + {}) + mock_multiprocessing_process.assert_called_once_with( + name='ProxDuration-some_type-101', + target=proxduration._worker_process, + args=(runner.result_queue, benchmark_cls, 'my_method', + self.scenario_cfg, {}, runner.aborted, runner.output_queue)) + + @mock.patch.object(os, 'getpid') + def test__worker_process_runner_id(self, mock_os_getpid): + mock_os_getpid.return_value = 101 + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', self.scenario_cfg, + {}, multiprocessing.Event(), mock.Mock()) + + self.assertEqual(101, self.scenario_cfg['runner']['runner_id']) + + def test__worker_process_called_with_cfg(self): + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', self.scenario_cfg, + {}, multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + + def test__worker_process_called_with_cfg_loop(self): + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', self.scenario_cfg, + {}, multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + self.assertGreater(self.benchmark.my_method.call_count, 0) + + def test__worker_process_called_without_cfg(self): + scenario_cfg = {'runner': {}} + aborted = multiprocessing.Event() + aborted.set() + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', scenario_cfg, {}, + aborted, mock.Mock()) + + self.benchmark_cls.assert_called_once_with(scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + self.benchmark.teardown.assert_called_once() + + def test__worker_process_output_queue(self): + self.benchmark.my_method = mock.Mock(return_value='my_result') + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + output_queue = mock.Mock() + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', self.scenario_cfg, + {}, multiprocessing.Event(), output_queue) + + self._assert_defaults__worker_run_setup_and_teardown() + output_queue.put.assert_has_calls( + [mock.call('my_result', True, constants.QUEUE_PUT_TIMEOUT)]) + + def test__worker_process_output_queue_multiple_iterations(self): + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + self.benchmark.my_method = self.MyMethod() + output_queue = mock.Mock() + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', self.scenario_cfg, + {}, multiprocessing.Event(), output_queue) + + self._assert_defaults__worker_run_setup_and_teardown() + for idx in range(102, 101 + len(output_queue.method_calls)): + output_queue.put.assert_has_calls( + [mock.call(idx, True, constants.QUEUE_PUT_TIMEOUT)]) + + def test__worker_process_queue(self): + self.benchmark.my_method = self.MyMethod() + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + queue = mock.Mock() + proxduration._worker_process( + queue, self.benchmark_cls, 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + benchmark_output = {'timestamp': mock.ANY, + 'sequence': 1, + 'data': {'my_key': 102}, + 'errors': ''} + queue.put.assert_has_calls( + [mock.call(benchmark_output, True, constants.QUEUE_PUT_TIMEOUT)]) + + def test__worker_process_queue_multiple_iterations(self): + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + self.benchmark.my_method = self.MyMethod() + queue = mock.Mock() + proxduration._worker_process( + queue, self.benchmark_cls, 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + for idx in range(102, 101 + len(queue.method_calls)): + benchmark_output = {'timestamp': mock.ANY, + 'sequence': idx - 101, + 'data': {'my_key': idx}, + 'errors': ''} + queue.put.assert_has_calls( + [mock.call(benchmark_output, True, + constants.QUEUE_PUT_TIMEOUT)]) + + def test__worker_process_except_sla_validation_error_no_sla_cfg(self): + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', self.scenario_cfg, + {}, multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + + @mock.patch.object(proxduration.LOG, 'warning') + def test__worker_process_except_sla_validation_error_sla_cfg_monitor( + self, *args): + self.scenario_cfg['sla'] = {'action': 'monitor'} + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', self.scenario_cfg, + {}, multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + + def test__worker_process_raise_sla_validation_error_sla_cfg_default(self): + self.scenario_cfg['sla'] = {} + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + with self.assertRaises(y_exc.SLAValidationError): + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, multiprocessing.Event(), mock.Mock()) + + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + self.benchmark.my_method.assert_called_once_with({}) + + def test__worker_process_raise_sla_validation_error_sla_cfg_assert(self): + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + self.scenario_cfg['sla'] = {'action': 'assert'} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + + with self.assertRaises(y_exc.SLAValidationError): + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, multiprocessing.Event(), mock.Mock()) + + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + self.benchmark.my_method.assert_called_once_with({}) + + @mock.patch.object(proxduration.LOG, 'warning') + def test__worker_process_queue_on_sla_validation_error_monitor( + self, *args): + self.scenario_cfg['sla'] = {'action': 'monitor'} + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.SLA_VALIDATION_ERROR_SIDE_EFFECT) + queue = mock.Mock() + proxduration._worker_process( + queue, self.benchmark_cls, 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + benchmark_output = {'timestamp': mock.ANY, + 'sequence': 1, + 'data': {'my_key': 102}, + 'errors': ('My Case SLA validation failed. ' + 'Error: my error message', )} + queue.put.assert_has_calls( + [mock.call(benchmark_output, True, constants.QUEUE_PUT_TIMEOUT)]) + + @mock.patch.object(proxduration.LOG, 'exception') + def test__worker_process_broad_exception(self, *args): + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.YardstickException) + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_run_setup_and_teardown() + + @mock.patch.object(proxduration.LOG, 'exception') + def test__worker_process_queue_on_broad_exception(self, *args): + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.BROAD_EXCEPTION_SIDE_EFFECT) + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + queue = mock.Mock() + proxduration._worker_process( + queue, self.benchmark_cls, 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + benchmark_output = {'timestamp': mock.ANY, + 'sequence': 1, + 'data': {'my_key': 102}, + 'errors': mock.ANY} + queue.put.assert_has_calls( + [mock.call(benchmark_output, True, constants.QUEUE_PUT_TIMEOUT)]) + + @mock.patch.object(proxduration.LOG, 'exception') + def test__worker_process_benchmark_teardown_on_broad_exception( + self, *args): + self.benchmark.teardown = mock.Mock( + side_effect=y_exc.YardstickException) + self.scenario_cfg["runner"] = {"sampled": True, "duration": 0.1} + + with self.assertRaises(SystemExit) as raised: + proxduration._worker_process( + mock.Mock(), self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, multiprocessing.Event(), mock.Mock()) + self.assertEqual(1, raised.exception.code) + self._assert_defaults__worker_run_setup_and_teardown() diff --git a/yardstick/tests/unit/benchmark/runner/test_search.py b/yardstick/tests/unit/benchmark/runner/test_search.py new file mode 100644 index 000000000..d5d1b8ded --- /dev/null +++ b/yardstick/tests/unit/benchmark/runner/test_search.py @@ -0,0 +1,192 @@ +# Copyright (c) 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import time + +import mock +import unittest + +from yardstick.benchmark.runners.search import SearchRunner +from yardstick.benchmark.runners.search import SearchRunnerHelper +from yardstick.common import exceptions as y_exc + + +class TestSearchRunnerHelper(unittest.TestCase): + + def test___call__(self): + scenario_cfg = { + 'runner': {}, + } + + benchmark = mock.Mock() + method = getattr(benchmark(), 'my_method') + helper = SearchRunnerHelper( + benchmark, 'my_method', scenario_cfg, {}, mock.Mock()) + + with helper.get_benchmark_instance(): + helper() + + method.assert_called_once() + + def test___call___error(self): + scenario_cfg = { + 'runner': {}, + } + + helper = SearchRunnerHelper( + mock.Mock(), 'my_method', scenario_cfg, {}, mock.Mock()) + + with self.assertRaises(RuntimeError): + helper() + + @mock.patch.object(time, 'sleep') + @mock.patch.object(time, 'time') + def test_is_not_done(self, mock_time, *args): + scenario_cfg = { + 'runner': {}, + } + + mock_time.side_effect = range(1000) + + helper = SearchRunnerHelper( + mock.Mock(), 'my_method', scenario_cfg, {}, mock.Mock()) + + index = -1 + for index in helper.is_not_done(): + if index >= 10: + break + + self.assertGreaterEqual(index, 10) + + @mock.patch.object(time, 'sleep') + def test_is_not_done_immediate_stop(self, *args): + scenario_cfg = { + 'runner': { + 'run_step': '', + }, + } + + helper = SearchRunnerHelper( + mock.Mock(), 'my_method', scenario_cfg, {}, mock.Mock()) + + index = -1 + for index in helper.is_not_done(): + if index >= 10: + break + + self.assertEqual(index, -1) + + +class TestSearchRunner(unittest.TestCase): + + def test__worker_run_once(self): + def update(*args): + args[-1].update(data) + + data = { + 'key1': { + 'inner1': 'value1', + 'done': 0, + }, + 'key2': { + 'done': None, + }, + } + + runner = SearchRunner({}) + runner.worker_helper = mock.Mock(side_effect=update) + + self.assertFalse(runner._worker_run_once('sequence 1')) + + def test__worker_run_once_done(self): + def update(*args): + args[-1].update(data) + + data = { + 'key1': { + 'inner1': 'value1', + 'done': 0, + }, + 'key2': { + 'done': None, + }, + 'key3': { + 'done': True, + }, + 'key4': [], + 'key5': 'value5', + } + + runner = SearchRunner({}) + runner.worker_helper = mock.Mock(side_effect=update) + + self.assertTrue(runner._worker_run_once('sequence 1')) + + def test__worker_run_once_assertion_error_assert(self): + runner = SearchRunner({}) + runner.sla_action = 'assert' + runner.worker_helper = mock.Mock(side_effect=y_exc.SLAValidationError) + + with self.assertRaises(y_exc.SLAValidationError): + runner._worker_run_once('sequence 1') + + def test__worker_run_once_assertion_error_monitor(self): + runner = SearchRunner({}) + runner.sla_action = 'monitor' + runner.worker_helper = mock.Mock(side_effect=y_exc.SLAValidationError) + + self.assertFalse(runner._worker_run_once('sequence 1')) + + def test__worker_run_once_non_assertion_error_none(self): + runner = SearchRunner({}) + runner.worker_helper = mock.Mock(side_effect=RuntimeError) + + self.assertTrue(runner._worker_run_once('sequence 1')) + + def test__worker_run_once_non_assertion_error(self): + runner = SearchRunner({}) + runner.sla_action = 'monitor' + runner.worker_helper = mock.Mock(side_effect=RuntimeError) + + self.assertFalse(runner._worker_run_once('sequence 1')) + + def test__worker_run(self): + scenario_cfg = { + 'runner': {'interval': 0, 'timeout': 1}, + } + + runner = SearchRunner({}) + runner._worker_run_once = mock.Mock(side_effect=[0, 0, 1]) + + runner._worker_run(mock.Mock(), 'my_method', scenario_cfg, {}) + + def test__worker_run_immediate_stop(self): + scenario_cfg = { + 'runner': { + 'run_step': '', + }, + } + + runner = SearchRunner({}) + runner._worker_run(mock.Mock(), 'my_method', scenario_cfg, {}) + + @mock.patch('yardstick.benchmark.runners.search.multiprocessing') + def test__run_benchmark(self, mock_multi_process): + scenario_cfg = { + 'runner': {}, + } + + runner = SearchRunner({}) + runner._run_benchmark(mock.Mock(), 'my_method', scenario_cfg, {}) + mock_multi_process.Process.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/__init__.py b/yardstick/tests/unit/benchmark/scenarios/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/__init__.py diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/__init__.py b/yardstick/tests/unit/benchmark/scenarios/availability/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/__init__.py diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_baremetal.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_baremetal.py new file mode 100644 index 000000000..35455a49c --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_baremetal.py @@ -0,0 +1,93 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest + +from yardstick.benchmark.scenarios.availability.attacker import \ + attacker_baremetal + + +class ExecuteShellTestCase(unittest.TestCase): + + def setUp(self): + self._mock_subprocess = mock.patch.object(attacker_baremetal, + 'subprocess') + self.mock_subprocess = self._mock_subprocess.start() + + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_subprocess.stop() + + def test__execute_shell_command_successful(self): + self.mock_subprocess.check_output.return_value = (0, 'unittest') + exitcode, _ = attacker_baremetal._execute_shell_command("env") + self.assertEqual(exitcode, 0) + + @mock.patch.object(attacker_baremetal, 'LOG') + def test__execute_shell_command_fail_cmd_exception(self, mock_log): + self.mock_subprocess.check_output.side_effect = RuntimeError + exitcode, _ = attacker_baremetal._execute_shell_command("env") + self.assertEqual(exitcode, -1) + mock_log.error.assert_called_once() + + +class AttackerBaremetalTestCase(unittest.TestCase): + + def setUp(self): + self._mock_ssh = mock.patch.object(attacker_baremetal, 'ssh') + self.mock_ssh = self._mock_ssh.start() + self._mock_subprocess = mock.patch.object(attacker_baremetal, + 'subprocess') + self.mock_subprocess = self._mock_subprocess.start() + self.addCleanup(self._stop_mocks) + + self.mock_ssh.SSH.from_node().execute.return_value = ( + 0, "running", '') + + host = { + "ipmi_ip": "10.20.0.5", + "ipmi_user": "root", + "ipmi_password": "123456", + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.context = {"node1": host} + self.attacker_cfg = { + 'fault_type': 'bear-metal-down', + 'host': 'node1', + } + + self.ins = attacker_baremetal.BaremetalAttacker(self.attacker_cfg, + self.context) + + def _stop_mocks(self): + self._mock_ssh.stop() + self._mock_subprocess.stop() + + def test__attacker_baremetal_all_successful(self): + self.ins.setup() + self.ins.inject_fault() + self.ins.recover() + + def test__attacker_baremetal_check_failure(self): + self.mock_ssh.SSH.from_node().execute.return_value = ( + 0, "error check", '') + self.ins.setup() + + def test__attacker_baremetal_recover_successful(self): + self.attacker_cfg["jump_host"] = 'node1' + self.context["node1"]["password"] = "123456" + ins = attacker_baremetal.BaremetalAttacker(self.attacker_cfg, + self.context) + + ins.setup() + ins.recover() diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_general.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_general.py new file mode 100644 index 000000000..c1b3c0d72 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_general.py @@ -0,0 +1,56 @@ +############################################################################## +# Copyright (c) 2016 Juan Qiu and others +# juan_ qiu@tongji.edu.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.availability.attacker +# .attacker_general + +from __future__ import absolute_import +import mock +import unittest + +from yardstick.benchmark.scenarios.availability.attacker import baseattacker + + +@mock.patch('yardstick.benchmark.scenarios.availability.attacker.' + 'attacker_general.ssh') +class GeneralAttackerServiceTestCase(unittest.TestCase): + + def setUp(self): + host = { + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.context = {"node1": host} + self.attacker_cfg = { + 'fault_type': 'general-attacker', + 'action_parameter': {'process_name': 'nova_api'}, + 'rollback_parameter': {'process_name': 'nova_api'}, + 'key': 'stop-service', + 'attack_key': 'stop-service', + 'host': 'node1', + } + + def test__attacker_service_all_successful(self, mock_ssh): + + cls = baseattacker.BaseAttacker.get_attacker_cls(self.attacker_cfg) + ins = cls(self.attacker_cfg, self.context) + + mock_ssh.SSH.from_node().execute.return_value = (0, "running", '') + ins.setup() + ins.inject_fault() + ins.recover() + + def test__attacker_service_check_failuer(self, mock_ssh): + + cls = baseattacker.BaseAttacker.get_attacker_cls(self.attacker_cfg) + ins = cls(self.attacker_cfg, self.context) + + mock_ssh.SSH.from_node().execute.return_value = (0, "error check", '') + ins.setup() diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_process.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_process.py new file mode 100644 index 000000000..2e9f1c6bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_process.py @@ -0,0 +1,53 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for +# yardstick.benchmark.scenarios.availability.attacker.attacker_process + +from __future__ import absolute_import +import mock +import unittest + +from yardstick.benchmark.scenarios.availability.attacker import baseattacker + + +@mock.patch( + 'yardstick.benchmark.scenarios.availability.attacker.attacker_process.ssh') +class AttackerServiceTestCase(unittest.TestCase): + + def setUp(self): + host = { + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.context = {"node1": host} + self.attacker_cfg = { + 'fault_type': 'kill-process', + 'process_name': 'nova-api', + 'host': 'node1', + } + + def test__attacker_service_all_successful(self, mock_ssh): + + cls = baseattacker.BaseAttacker.get_attacker_cls(self.attacker_cfg) + ins = cls(self.attacker_cfg, self.context) + + mock_ssh.SSH.from_node().execute.return_value = (0, "10", '') + ins.setup() + ins.inject_fault() + ins.recover() + + def test__attacker_service_check_failuer(self, mock_ssh): + + cls = baseattacker.BaseAttacker.get_attacker_cls(self.attacker_cfg) + ins = cls(self.attacker_cfg, self.context) + + mock_ssh.SSH.from_node().execute.return_value = (0, None, '') + ins.setup() diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_baseattacker.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_baseattacker.py new file mode 100644 index 000000000..74f86983b --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_baseattacker.py @@ -0,0 +1,36 @@ +############################################################################## +# Copyright (c) 2018 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import unittest + +from yardstick.benchmark.scenarios.availability.attacker import baseattacker + + +class BaseAttackerTestCase(unittest.TestCase): + + def setUp(self): + self.attacker_cfg = { + 'fault_type': 'test-attacker', + 'action_parameter': {'process_name': 'nova_api'}, + 'rollback_parameter': {'process_name': 'nova_api'}, + 'key': 'stop-service', + 'attack_key': 'stop-service', + 'host': 'node1', + } + self.base_attacker = baseattacker.BaseAttacker({}, {}) + + def test__init__(self): + self.assertEqual(self.base_attacker.data, {}) + self.assertFalse(self.base_attacker.mandatory) + self.assertEqual(self.base_attacker.intermediate_variables, {}) + self.assertFalse(self.base_attacker.mandatory) + + def test_get_attacker_cls(self): + with self.assertRaises(RuntimeError): + baseattacker.BaseAttacker.get_attacker_cls(self.attacker_cfg) diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_basemonitor.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_basemonitor.py new file mode 100644 index 000000000..8d042c406 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_basemonitor.py @@ -0,0 +1,119 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import time + +import mock +import unittest + +from yardstick.benchmark.scenarios.availability.monitor import basemonitor + + +class MonitorMgrTestCase(unittest.TestCase): + + def setUp(self): + self.monitor_configs = [ + { + "monitor_type": "openstack-cmd", + "command_name": "openstack router list", + "monitor_time": 10, + "monitor_number": 3, + "sla": { + "max_outage_time": 5 + } + }, + { + "monitor_type": "process", + "process_name": "neutron-server", + "host": "node1", + "monitor_time": 20, + "monitor_number": 3, + "sla": { + "max_recover_time": 20 + } + } + ] + self.MonitorMgr = basemonitor.MonitorMgr([]) + self.MonitorMgr.init_monitors(self.monitor_configs, None) + self.monitor_list = self.MonitorMgr._monitor_list + for mo in self.monitor_list: + mo._result = {"outage_time": 10} + + @mock.patch.object(basemonitor, 'BaseMonitor') + def test__MonitorMgr_setup_successful(self, *args): + instance = basemonitor.MonitorMgr({"nova-api": 10}) + instance.init_monitors(self.monitor_configs, None) + instance.start_monitors() + instance.wait_monitors() + + # TODO(elfoley): Check the return value + ret = instance.verify_SLA() # pylint: disable=unused-variable + + @mock.patch.object(basemonitor, 'BaseMonitor') + def test_MonitorMgr_getitem(self, *args): + monitorMgr = basemonitor.MonitorMgr({"nova-api": 10}) + monitorMgr.init_monitors(self.monitor_configs, None) + + @mock.patch.object(basemonitor, 'BaseMonitor') + def test_store_result(self, *args): + expect = {'process_neutron-server_outage_time': 10, + 'openstack-router-list_outage_time': 10} + result = {} + self.MonitorMgr.store_result(result) + self.assertDictEqual(result, expect) + + +class BaseMonitorTestCase(unittest.TestCase): + + class MonitorSimple(basemonitor.BaseMonitor): + __monitor_type__ = "MonitorForTest" + + def setup(self): + self.monitor_result = False + + def monitor_func(self): + return self.monitor_result + + def setUp(self): + self.monitor_cfg = { + 'monitor_type': 'MonitorForTest', + 'command_name': 'nova image-list', + 'monitor_time': 0.01, + 'sla': {'max_outage_time': 5} + } + + def _close_queue(self, instace): + time.sleep(0.1) + instace._queue.close() + + def test__basemonitor_start_wait_successful(self): + ins = basemonitor.BaseMonitor(self.monitor_cfg, None, {"nova-api": 10}) + self.addCleanup(self._close_queue, ins) + ins.start_monitor() + ins.wait_monitor() + + def test__basemonitor_all_successful(self): + ins = self.MonitorSimple(self.monitor_cfg, None, {"nova-api": 10}) + self.addCleanup(self._close_queue, ins) + ins.setup() + ins.run() + ins.verify_SLA() + + @mock.patch.object(basemonitor, 'multiprocessing') + def test__basemonitor_func_false(self, mock_multiprocess): + ins = self.MonitorSimple(self.monitor_cfg, None, {"nova-api": 10}) + self.addCleanup(self._close_queue, ins) + ins.setup() + mock_multiprocess.Event().is_set.return_value = False + ins.run() + ins.verify_SLA() + + def test__basemonitor_getmonitorcls_successfule(self): + with self.assertRaises(RuntimeError): + basemonitor.BaseMonitor.get_monitor_cls(self.monitor_cfg) diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_baseoperation.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_baseoperation.py new file mode 100644 index 000000000..b7c9f62ff --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_baseoperation.py @@ -0,0 +1,79 @@ +############################################################################## +# Copyright (c) 2016 Huan Li and others +# lihuansse@tongji.edu.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest + +from yardstick.benchmark.scenarios.availability.operation import baseoperation + + +class OperationMgrTestCase(unittest.TestCase): + + def setUp(self): + config = { + 'operation_type': 'general-operation', + 'key': 'service-status' + } + + self.operation_configs = [] + self.operation_configs.append(config) + + @mock.patch.object(baseoperation, 'BaseOperation') + def test_all_successful(self, *args): + mgr_ins = baseoperation.OperationMgr() + mgr_ins.init_operations(self.operation_configs, None) + _ = mgr_ins["service-status"] + mgr_ins.rollback() + + @mock.patch.object(baseoperation, 'BaseOperation') + def test_getitem_fail(self, *args): + mgr_ins = baseoperation.OperationMgr() + mgr_ins.init_operations(self.operation_configs, None) + with self.assertRaises(KeyError): + _ = mgr_ins["operation-not-exist"] + + +class TestOperation(baseoperation.BaseOperation): + __operation__type__ = "test-operation" + + def setup(self): + pass + + def run(self): + pass + + def rollback(self): + pass + + +class BaseOperationTestCase(unittest.TestCase): + + def setUp(self): + self.config = { + 'operation_type': 'general-operation', + 'key': 'service-status' + } + self.base_ins = baseoperation.BaseOperation(self.config, None) + + def test_all_successful(self): + self.base_ins.setup() + self.base_ins.run() + self.base_ins.rollback() + + def test_get_script_fullpath(self): + self.base_ins.get_script_fullpath("ha_tools/test.bash") + + # TODO(elfoley): Fix test to check on expected outputs + # pylint: disable=unused-variable + def test_get_operation_cls_successful(self): + operation_ins = self.base_ins.get_operation_cls("test-operation") + + def test_get_operation_cls_fail(self): + with self.assertRaises(RuntimeError): + self.base_ins.get_operation_cls("operation-not-exist") diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_baseresultchecker.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_baseresultchecker.py new file mode 100644 index 000000000..d4df02819 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_baseresultchecker.py @@ -0,0 +1,89 @@ +############################################################################## +# Copyright (c) 2016 Huan Li and others +# lihuansse@tongji.edu.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest + +from yardstick.benchmark.scenarios.availability.result_checker import \ + baseresultchecker + + +class ResultCheckerMgrTestCase(unittest.TestCase): + + def setUp(self): + config = { + 'checker_type': 'general-result-checker', + 'key': 'process-checker' + } + + self.checker_configs = [] + self.checker_configs.append(config) + + self.mgr_ins = baseresultchecker.ResultCheckerMgr() + + self._mock_basechecker = mock.patch.object(baseresultchecker, + 'BaseResultChecker') + self.mock_basechecker = self._mock_basechecker.start() + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_basechecker.stop() + + def test_ResultCheckerMgr_setup_successful(self): + self.mgr_ins.verify() + + def test_getitem_succeessful(self): + self.mgr_ins.init_ResultChecker(self.checker_configs, None) + _ = self.mgr_ins["process-checker"] + + def test_getitem_fail(self): + self.mgr_ins.init_ResultChecker(self.checker_configs, None) + with self.assertRaises(KeyError): + _ = self.mgr_ins["checker-not-exist"] + + +class BaseResultCheckerTestCase(unittest.TestCase): + + class ResultCheckeSimple(baseresultchecker.BaseResultChecker): + __result_checker__type__ = "ResultCheckeForTest" + + def setup(self): + self.success = False + + def verify(self): + return self.success + + def setUp(self): + self.checker_cfg = { + 'checker_type': 'general-result-checker', + 'key': 'process-checker' + } + self.ins = baseresultchecker.BaseResultChecker(self.checker_cfg, None) + + def test_baseresultchecker_setup_verify_successful(self): + self.ins.setup() + self.ins.verify() + + def test_baseresultchecker_verfiy_pass(self): + self.ins.setup() + self.ins.actualResult = True + self.ins.expectedResult = True + self.ins.verify() + + def test_get_script_fullpath(self): + self.ins.get_script_fullpath("test.bash") + + def test_get_resultchecker_cls_successful(self): + baseresultchecker.BaseResultChecker.get_resultchecker_cls( + "ResultCheckeForTest") + + def test_get_resultchecker_cls_fail(self): + with self.assertRaises(RuntimeError): + baseresultchecker.BaseResultChecker.get_resultchecker_cls( + "ResultCheckeNotExist") diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_director.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_director.py new file mode 100644 index 000000000..e49544e1c --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_director.py @@ -0,0 +1,106 @@ +############################################################################## +# Copyright (c) 2016 Huan Li and others +# lihuansse@tongji.edu.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.availability.director + +from __future__ import absolute_import +import mock +import unittest + +from yardstick.benchmark.scenarios.availability.director import Director + + +# pylint: disable=unused-argument +# disable this for now because I keep forgetting mock patch arg ordering + + +@mock.patch('yardstick.benchmark.scenarios.availability.director.basemonitor') +@mock.patch('yardstick.benchmark.scenarios.availability.director.baseattacker') +@mock.patch( + 'yardstick.benchmark.scenarios.availability.director.baseoperation') +@mock.patch( + 'yardstick.benchmark.scenarios.availability.director.baseresultchecker') +class DirectorTestCase(unittest.TestCase): + + def setUp(self): + self.scenario_cfg = { + 'type': "general_scenario", + 'options': { + 'attackers': [{ + 'fault_type': "general-attacker", + 'key': "kill-process"}], + 'monitors': [{ + 'monitor_type': "general-monitor", + 'key': "service-status"}], + 'operations': [{ + 'operation_type': 'general-operation', + 'key': 'service-status'}], + 'resultCheckers': [{ + 'checker_type': 'general-result-checker', + 'key': 'process-checker', }], + 'steps': [ + { + 'actionKey': "service-status", + 'actionType': "operation", + 'index': 1}, + { + 'actionKey': "kill-process", + 'actionType': "attacker", + 'index': 2}, + { + 'actionKey': "process-checker", + 'actionType': "resultchecker", + 'index': 3}, + { + 'actionKey': "service-status", + 'actionType': "monitor", + 'index': 4}, + ] + } + } + host = { + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.ctx = {"nodes": {"node1": host}} + + def test_director_all_successful(self, mock_checer, mock_opertion, + mock_attacker, mock_monitor): + ins = Director(self.scenario_cfg, self.ctx) + opertion_action = ins.createActionPlayer("operation", "service-status") + attacker_action = ins.createActionPlayer("attacker", "kill-process") + checker_action = ins.createActionPlayer("resultchecker", + "process-checker") + monitor_action = ins.createActionPlayer("monitor", "service-status") + + opertion_rollback = ins.createActionRollbacker("operation", + "service-status") + attacker_rollback = ins.createActionRollbacker("attacker", + "kill-process") + ins.executionSteps.append(opertion_rollback) + ins.executionSteps.append(attacker_rollback) + + opertion_action.action() + attacker_action.action() + checker_action.action() + monitor_action.action() + + attacker_rollback.rollback() + opertion_rollback.rollback() + + ins.stopMonitors() + ins.verify() + ins.knockoff() + + def test_director_get_wrong_item(self, mock_checer, mock_opertion, + mock_attacker, mock_monitor): + ins = Director(self.scenario_cfg, self.ctx) + ins.createActionPlayer("wrong_type", "wrong_key") + ins.createActionRollbacker("wrong_type", "wrong_key") diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_command.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_command.py new file mode 100644 index 000000000..1aebcc85b --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_command.py @@ -0,0 +1,95 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest + +from yardstick.benchmark.scenarios.availability.monitor import monitor_command + + +class ExecuteShellTestCase(unittest.TestCase): + + def setUp(self): + self._mock_subprocess = mock.patch.object(monitor_command, 'subprocess') + self.mock_subprocess = self._mock_subprocess.start() + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_subprocess.stop() + + def test__fun_execute_shell_command_successful(self): + cmd = "env" + self.mock_subprocess.check_output.return_value = (0, 'unittest') + exitcode, _t = monitor_command._execute_shell_command(cmd) + self.assertEqual(exitcode, 0) + + @mock.patch.object(monitor_command, 'LOG') + def test__fun_execute_shell_command_fail_cmd_exception(self, mock_log): + cmd = "env" + self.mock_subprocess.check_output.side_effect = RuntimeError + exitcode, _ = monitor_command._execute_shell_command(cmd) + self.assertEqual(exitcode, -1) + mock_log.error.assert_called_once() + + +class MonitorOpenstackCmdTestCase(unittest.TestCase): + + def setUp(self): + host = { + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.context = {"node1": host} + self.config = { + 'monitor_type': 'openstack-api', + 'command_name': 'nova image-list', + 'monitor_time': 1, + 'sla': {'max_outage_time': 5} + } + self._mock_subprocess = mock.patch.object(monitor_command, 'subprocess') + self.mock_subprocess = self._mock_subprocess.start() + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_subprocess.stop() + + def test__monitor_command_monitor_func_successful(self): + + instance = monitor_command.MonitorOpenstackCmd(self.config, None, {"nova-api": 10}) + instance.setup() + self.mock_subprocess.check_output.return_value = (0, 'unittest') + ret = instance.monitor_func() + self.assertTrue(ret) + instance._result = {"outage_time": 0} + instance.verify_SLA() + + @mock.patch.object(monitor_command, 'LOG') + def test__monitor_command_monitor_func_failure(self, mock_log): + self.mock_subprocess.check_output.return_value = (1, 'unittest') + instance = monitor_command.MonitorOpenstackCmd(self.config, None, {"nova-api": 10}) + instance.setup() + self.mock_subprocess.check_output.side_effect = RuntimeError + ret = instance.monitor_func() + self.assertFalse(ret) + mock_log.error.assert_called_once() + instance._result = {"outage_time": 10} + instance.verify_SLA() + + @mock.patch.object(monitor_command, 'ssh') + def test__monitor_command_ssh_monitor_successful(self, mock_ssh): + + self.mock_subprocess.check_output.return_value = (0, 'unittest') + self.config["host"] = "node1" + instance = monitor_command.MonitorOpenstackCmd( + self.config, self.context, {"nova-api": 10}) + instance.setup() + mock_ssh.SSH.from_node().execute.return_value = (0, "0", '') + ret = instance.monitor_func() + self.assertTrue(ret) diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_general.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_general.py new file mode 100644 index 000000000..5907c8b6a --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_general.py @@ -0,0 +1,82 @@ +############################################################################## +# Copyright (c) 2016 Huan Li and others +# lihuansse@tongji.edu.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.availability.monitor +# .monitor_general + +from __future__ import absolute_import +import mock +import unittest +from yardstick.benchmark.scenarios.availability.monitor import monitor_general + + +# pylint: disable=unused-argument +# disable this for now because I keep forgetting mock patch arg ordering + + +@mock.patch('yardstick.benchmark.scenarios.availability.monitor.' + 'monitor_general.ssh') +@mock.patch('yardstick.benchmark.scenarios.availability.monitor.' + 'monitor_general.open') +class GeneralMonitorServiceTestCase(unittest.TestCase): + + def setUp(self): + host = { + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.context = {"node1": host} + self.monitor_cfg = { + 'monitor_type': 'general-monitor', + 'key': 'service-status', + 'monitor_key': 'service-status', + 'host': 'node1', + 'monitor_time': 3, + 'parameter': {'serviceName': 'haproxy'}, + 'sla': {'max_outage_time': 1} + } + self.monitor_cfg_noparam = { + 'monitor_type': 'general-monitor', + 'key': 'service-status', + 'monitor_key': 'service-status', + 'host': 'node1', + 'monitor_time': 3, + 'sla': {'max_outage_time': 1} + } + + def test__monitor_general_all_successful(self, mock_open, mock_ssh): + ins = monitor_general.GeneralMonitor(self.monitor_cfg, self.context, {"nova-api": 10}) + + ins.setup() + mock_ssh.SSH.from_node().execute.return_value = (0, "running", '') + ins.monitor_func() + ins._result = {'outage_time': 0} + ins.verify_SLA() + + def test__monitor_general_all_successful_noparam(self, mock_open, + mock_ssh): + ins = monitor_general.GeneralMonitor( + self.monitor_cfg_noparam, self.context, {"nova-api": 10}) + + ins.setup() + mock_ssh.SSH.from_node().execute.return_value = (0, "running", '') + ins.monitor_func() + ins._result = {'outage_time': 0} + ins.verify_SLA() + + def test__monitor_general_failure(self, mock_open, mock_ssh): + ins = monitor_general.GeneralMonitor( + self.monitor_cfg_noparam, self.context, {"nova-api": 10}) + + ins.setup() + mock_ssh.SSH.from_node().execute.return_value = (1, "error", 'error') + ins.monitor_func() + ins._result = {'outage_time': 2} + ins.verify_SLA() diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_multi.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_multi.py new file mode 100644 index 000000000..dc3a4b99a --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_multi.py @@ -0,0 +1,82 @@ +############################################################################## +# Copyright (c) 2016 Huan Li and others +# lihuansse@tongji.edu.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.availability.monitor +# .monitor_multi + +from __future__ import absolute_import +import mock +import unittest +from yardstick.benchmark.scenarios.availability.monitor import monitor_multi + + +# pylint: disable=unused-argument +# disable this for now because I keep forgetting mock patch arg ordering + + +@mock.patch('yardstick.benchmark.scenarios.availability.monitor.' + 'monitor_general.ssh') +@mock.patch('yardstick.benchmark.scenarios.availability.monitor.' + 'monitor_general.open') +class MultiMonitorServiceTestCase(unittest.TestCase): + + def setUp(self): + host = { + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.context = {"node1": host} + self.monitor_cfg = { + 'monitor_type': 'general-monitor', + 'monitor_number': 3, + 'key': 'service-status', + 'monitor_key': 'service-status', + 'host': 'node1', + 'monitor_time': 0.1, + 'parameter': {'serviceName': 'haproxy'}, + 'sla': {'max_outage_time': 1} + } + + def test__monitor_multi_all_successful(self, mock_open, mock_ssh): + ins = monitor_multi.MultiMonitor( + self.monitor_cfg, self.context, {"nova-api": 10}) + + mock_ssh.SSH.from_node().execute.return_value = (0, "running", '') + + ins.start_monitor() + ins.wait_monitor() + ins.verify_SLA() + + def test__monitor_multi_all_fail(self, mock_open, mock_ssh): + ins = monitor_multi.MultiMonitor( + self.monitor_cfg, self.context, {"nova-api": 10}) + + mock_ssh.SSH.from_node().execute.return_value = (0, "running", '') + + ins.start_monitor() + ins.wait_monitor() + ins.verify_SLA() + + def test__monitor_multi_no_sla(self, mock_open, mock_ssh): + monitor_cfg = { + 'monitor_type': 'general-monitor', + 'monitor_number': 3, + 'key': 'service-status', + 'monitor_key': 'service-status', + 'host': 'node1', + 'monitor_time': 0.1, + 'parameter': {'serviceName': 'haproxy'} + } + ins = monitor_multi.MultiMonitor( + monitor_cfg, self.context, {"nova-api": 10}) + mock_ssh.SSH.from_node().execute.return_value = (0, "running", '') + ins.start_monitor() + ins.wait_monitor() + self.assertTrue(ins.verify_SLA()) diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_process.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_process.py new file mode 100644 index 000000000..8c73bf221 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_process.py @@ -0,0 +1,73 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for +# yardstick.benchmark.scenarios.availability.monitor.monitor_process + +from __future__ import absolute_import +import mock +import unittest + +from yardstick.benchmark.scenarios.availability.monitor import monitor_process + + +@mock.patch( + 'yardstick.benchmark.scenarios.availability.monitor.monitor_process.ssh') +class MonitorProcessTestCase(unittest.TestCase): + + def setUp(self): + host = { + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.context = {"node1": host} + self.monitor_cfg = { + 'monitor_type': 'process', + 'process_name': 'nova-api', + 'host': "node1", + 'monitor_time': 1, + 'sla': {'max_recover_time': 5} + } + + def test__monitor_process_all_successful(self, mock_ssh): + + ins = monitor_process.MonitorProcess(self.monitor_cfg, self.context, {"nova-api": 10}) + + mock_ssh.SSH.from_node().execute.return_value = (0, "1", '') + ins.setup() + ins.monitor_func() + ins._result = {"outage_time": 0} + ins.verify_SLA() + + def test__monitor_process_down_failuer(self, mock_ssh): + + ins = monitor_process.MonitorProcess(self.monitor_cfg, self.context, {"nova-api": 10}) + + mock_ssh.SSH.from_node().execute.return_value = (0, "0", '') + ins.setup() + ins.monitor_func() + ins._result = {"outage_time": 10} + ins.verify_SLA() + + def test__monitor_process_no_sla(self, mock_ssh): + + monitor_cfg = { + 'monitor_type': 'process', + 'process_name': 'nova-api', + 'host': "node1", + 'monitor_time': 1, + } + ins = monitor_process.MonitorProcess(monitor_cfg, self.context, {"nova-api": 10}) + + mock_ssh.SSH.from_node().execute.return_value = (0, "0", '') + ins.setup() + ins.monitor_func() + ins._result = {"outage_time": 10} + self.assertTrue(ins.verify_SLA()) diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_operation_general.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_operation_general.py new file mode 100644 index 000000000..2b09c0385 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_operation_general.py @@ -0,0 +1,74 @@ +############################################################################## +# Copyright (c) 2016 Huan Li and others +# lihuansse@tongji.edu.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.availability.operation +# .operation_general + +from __future__ import absolute_import +import mock +import unittest +from yardstick.benchmark.scenarios.availability.operation import \ + operation_general + + +# pylint: disable=unused-argument +# disable this for now because I keep forgetting mock patch arg ordering + + +@mock.patch('yardstick.benchmark.scenarios.availability.operation.' + 'operation_general.ssh') +@mock.patch('yardstick.benchmark.scenarios.availability.operation.' + 'operation_general.open') +class GeneralOperaionTestCase(unittest.TestCase): + + def setUp(self): + host = { + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.context = {"node1": host} + self.operation_cfg = { + 'operation_type': 'general-operation', + 'action_parameter': {'ins_cup': 2}, + 'rollback_parameter': {'ins_id': 'id123456'}, + 'key': 'nova-create-instance', + 'operation_key': 'nova-create-instance', + 'host': 'node1', + } + self.operation_cfg_noparam = { + 'operation_type': 'general-operation', + 'key': 'nova-create-instance', + 'operation_key': 'nova-create-instance', + 'host': 'node1', + } + + def test__operation_successful(self, mock_open, mock_ssh): + ins = operation_general.GeneralOperaion(self.operation_cfg, + self.context) + mock_ssh.SSH.from_node().execute.return_value = (0, "success", '') + ins.setup() + ins.run() + ins.rollback() + + def test__operation_successful_noparam(self, mock_open, mock_ssh): + ins = operation_general.GeneralOperaion(self.operation_cfg_noparam, + self.context) + mock_ssh.SSH.from_node().execute.return_value = (0, "success", '') + ins.setup() + ins.run() + ins.rollback() + + def test__operation_fail(self, mock_open, mock_ssh): + ins = operation_general.GeneralOperaion(self.operation_cfg, + self.context) + mock_ssh.SSH.from_node().execute.return_value = (1, "failed", '') + ins.setup() + ins.run() + ins.rollback() diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_result_checker_general.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_result_checker_general.py new file mode 100644 index 000000000..324a5bda2 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_result_checker_general.py @@ -0,0 +1,118 @@ +############################################################################## +# Copyright (c) 2016 Huan Li and others +# lihuansse@tongji.edu.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.availability.result_checker +# .result_checker_general + +from __future__ import absolute_import +import mock +import unittest +import copy + +from yardstick.benchmark.scenarios.availability.result_checker import \ + result_checker_general + + +# pylint: disable=unused-argument +# disable this for now because I keep forgetting mock patch arg ordering + + +@mock.patch('yardstick.benchmark.scenarios.availability.result_checker.' + 'result_checker_general.ssh') +@mock.patch('yardstick.benchmark.scenarios.availability.result_checker.' + 'result_checker_general.open') +class GeneralResultCheckerTestCase(unittest.TestCase): + + def setUp(self): + host = { + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.context = {"node1": host} + self.checker_cfg = { + 'parameter': {'processname': 'process'}, + 'checker_type': 'general-result-checker', + 'condition': 'eq', + 'expectedValue': 1, + 'key': 'process-checker', + 'checker_key': 'process-checker', + 'host': 'node1' + } + + def test__result_checker_eq(self, mock_open, mock_ssh): + ins = result_checker_general.GeneralResultChecker(self.checker_cfg, + self.context) + mock_ssh.SSH.from_node().execute.return_value = (0, "1", '') + ins.setup() + self.assertTrue(ins.verify()) + + def test__result_checker_gt(self, mock_open, mock_ssh): + config = copy.deepcopy(self.checker_cfg) + config['condition'] = 'gt' + ins = result_checker_general.GeneralResultChecker(config, + self.context) + mock_ssh.SSH.from_node().execute.return_value = (0, "2", '') + ins.setup() + self.assertTrue(ins.verify()) + + def test__result_checker_gt_eq(self, mock_open, mock_ssh): + config = copy.deepcopy(self.checker_cfg) + config['condition'] = 'gt_eq' + ins = result_checker_general.GeneralResultChecker(config, + self.context) + mock_ssh.SSH.from_node().execute.return_value = (0, "1", '') + ins.setup() + self.assertTrue(ins.verify()) + + def test__result_checker_lt(self, mock_open, mock_ssh): + config = copy.deepcopy(self.checker_cfg) + config['condition'] = 'lt' + ins = result_checker_general.GeneralResultChecker(config, + self.context) + mock_ssh.SSH.from_node().execute.return_value = (0, "0", '') + ins.setup() + self.assertTrue(ins.verify()) + + def test__result_checker_lt_eq(self, mock_open, mock_ssh): + config = copy.deepcopy(self.checker_cfg) + config['condition'] = 'lt_eq' + ins = result_checker_general.GeneralResultChecker(config, + self.context) + mock_ssh.SSH.from_node().execute.return_value = (0, "1", '') + ins.setup() + self.assertTrue(ins.verify()) + + def test__result_checker_in(self, mock_open, mock_ssh): + config = copy.deepcopy(self.checker_cfg) + config['condition'] = 'in' + config['expectedValue'] = "value" + ins = result_checker_general.GeneralResultChecker(config, + self.context) + mock_ssh.SSH.from_node().execute.return_value = (0, "value return", '') + ins.setup() + self.assertTrue(ins.verify()) + + def test__result_checker_wrong(self, mock_open, mock_ssh): + config = copy.deepcopy(self.checker_cfg) + config['condition'] = 'wrong' + ins = result_checker_general.GeneralResultChecker(config, + self.context) + mock_ssh.SSH.from_node().execute.return_value = (0, "1", '') + ins.setup() + self.assertFalse(ins.verify()) + + def test__result_checker_fail(self, mock_open, mock_ssh): + config = copy.deepcopy(self.checker_cfg) + config.pop('parameter') + ins = result_checker_general.GeneralResultChecker(config, + self.context) + mock_ssh.SSH.from_node().execute.return_value = (1, "fail", '') + ins.setup() + ins.verify() diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_scenario_general.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_scenario_general.py new file mode 100644 index 000000000..dbf3d83b2 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_scenario_general.py @@ -0,0 +1,76 @@ +############################################################################## +# Copyright (c) 2016 Huan Li and others +# lihuansse@tongji.edu.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest + +from yardstick.benchmark.scenarios.availability import scenario_general +from yardstick.common import exceptions as y_exc + + +class ScenarioGeneralTestCase(unittest.TestCase): + + @mock.patch.object(scenario_general, 'Director') + def setUp(self, *args): + self.scenario_cfg = { + 'type': "general_scenario", + 'options': { + 'attackers': [{ + 'fault_type': "general-attacker", + 'key': "kill-process"}], + 'monitors': [{ + 'monitor_type': "general-monitor", + 'key': "service-status"}], + 'steps': [ + { + 'actionKey': "kill-process", + 'actionType': "attacker", + 'index': 1}, + { + 'actionKey': "service-status", + 'actionType': "monitor", + 'index': 2}] + } + } + self.instance = scenario_general.ScenarioGeneral(self.scenario_cfg, + None) + self.instance.setup() + self.instance.director.verify.return_value = True + + def test_scenario_general_all_successful(self): + ret = {} + self.instance.run(ret) + self.instance.teardown() + self.assertEqual(ret['sla_pass'], 1) + + @mock.patch.object(scenario_general.LOG, 'exception') + def test_scenario_general_exception(self, *args): + self.instance.director.createActionPlayer.side_effect = ( + KeyError('Wrong')) + self.instance.director.data = {} + ret = {} + self.instance.run(ret) + self.instance.teardown() + self.assertEqual(ret['sla_pass'], 1) + + def test_scenario_general_case_fail(self): + self.instance.director.verify.return_value = False + self.instance.director.data = {} + ret = {} + self.assertRaises(y_exc.SLAValidationError, self.instance.run, ret) + self.instance.teardown() + self.assertEqual(ret['sla_pass'], 0) + + def test_scenario_general_case_service_not_found_fail(self): + self.instance.director.verify.return_value = True + self.instance.director.data = {"general-attacker": 0} + ret = {} + self.assertRaises(y_exc.SLAValidationError, self.instance.run, ret) + self.instance.teardown() + self.assertEqual(ret['sla_pass'], 0) diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_serviceha.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_serviceha.py new file mode 100644 index 000000000..d61fa67c7 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_serviceha.py @@ -0,0 +1,131 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest + +from yardstick.benchmark.scenarios.availability import serviceha +from yardstick.common import exceptions as y_exc + + +class ServicehaTestCase(unittest.TestCase): + + def setUp(self): + host = { + "ip": "10.20.0.5", + "user": "root", + "key_filename": "/root/.ssh/id_rsa" + } + self.ctx = {"nodes": {"node1": host}} + attacker_cfg = { + "fault_type": "kill-process", + "process_name": "nova-api", + "host": "node1" + } + attacker_cfgs = [] + attacker_cfgs.append(attacker_cfg) + monitor_cfg = { + "monitor_cmd": "nova image-list", + "monitor_time": 0.1 + } + monitor_cfgs = [] + monitor_cfgs.append(monitor_cfg) + + options = { + "attackers": attacker_cfgs, + "monitors": monitor_cfgs + } + sla = {"outage_time": 5} + self.args = {"options": options, "sla": sla} + self.test__serviceha = serviceha.ServiceHA(self.args, self.ctx) + + def test___init__(self): + + self.assertEqual(self.test__serviceha.data, {}) + self.assertFalse(self.test__serviceha.setup_done) + self.assertFalse(self.test__serviceha.sla_pass) + + # NOTE(elfoley): This should be split into test_setup and test_run + # NOTE(elfoley): This should explicitly test outcomes and states + @mock.patch.object(serviceha, 'baseattacker') + @mock.patch.object(serviceha, 'basemonitor') + def test__serviceha_setup_run_successful(self, mock_monitor, *args): + p = serviceha.ServiceHA(self.args, self.ctx) + + p.setup() + self.assertTrue(p.setup_done) + mock_monitor.MonitorMgr().verify_SLA.return_value = True + ret = {} + p.run(ret) + p.teardown() + + p.setup() + self.assertTrue(p.setup_done) + + @mock.patch.object(serviceha, 'baseattacker') + @mock.patch.object(serviceha, 'basemonitor') + def test__serviceha_run_sla_error(self, mock_monitor, *args): + p = serviceha.ServiceHA(self.args, self.ctx) + + p.setup() + self.assertEqual(p.setup_done, True) + + mock_monitor.MonitorMgr().verify_SLA.return_value = False + + ret = {} + self.assertRaises(y_exc.SLAValidationError, p.run, ret) + self.assertEqual(ret['sla_pass'], 0) + + @mock.patch.object(serviceha, 'baseattacker') + @mock.patch.object(serviceha, 'basemonitor') + def test__serviceha_run_service_not_found_sla_error(self, mock_monitor, + *args): + p = serviceha.ServiceHA(self.args, self.ctx) + + p.setup() + self.assertTrue(p.setup_done) + p.data["kill-process"] = 0 + + mock_monitor.MonitorMgr().verify_SLA.return_value = True + + ret = {} + self.assertRaises(y_exc.SLAValidationError, p.run, ret) + self.assertEqual(ret['sla_pass'], 0) + + @mock.patch.object(serviceha, 'baseattacker') + @mock.patch.object(serviceha, 'basemonitor') + def test__serviceha_no_teardown_when_sla_pass(self, mock_monitor, + *args): + p = serviceha.ServiceHA(self.args, self.ctx) + p.setup() + self.assertTrue(p.setup_done) + mock_monitor.MonitorMgr().verify_SLA.return_value = True + ret = {} + p.run(ret) + attacker = mock.Mock() + attacker.mandatory = False + p.attackers = [attacker] + p.teardown() + attacker.recover.assert_not_called() + + @mock.patch.object(serviceha, 'baseattacker') + @mock.patch.object(serviceha, 'basemonitor') + def test__serviceha_teardown_when_mandatory(self, mock_monitor, + *args): + p = serviceha.ServiceHA(self.args, self.ctx) + p.setup() + self.assertTrue(p.setup_done) + mock_monitor.MonitorMgr().verify_SLA.return_value = True + ret = {} + p.run(ret) + attacker = mock.Mock() + attacker.mandatory = True + p.attackers = [attacker] + p.teardown() + attacker.recover.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_util.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_util.py new file mode 100644 index 000000000..4d97585d4 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_util.py @@ -0,0 +1,56 @@ +############################################################################## +# Copyright (c) 2016 Kanglin Yin and others +# 14_ykl@tongji.edu.cn +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.availability.utils + +import mock +import unittest + +from yardstick.benchmark.scenarios.availability import util + + +class ExecuteShellTestCase(unittest.TestCase): + + def setUp(self): + self.param_config = {'serviceName': '@serviceName', 'value': 1} + self.intermediate_variables = {'@serviceName': 'nova-api'} + self.std_output = '| id | 1 |' + self.cmd_config = {'cmd': 'ls', 'param': '-a'} + + self._mock_subprocess = mock.patch.object(util, 'subprocess') + self.mock_subprocess = self._mock_subprocess.start() + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_subprocess.stop() + + def test_util_build_command_shell(self): + result = util.build_shell_command(self.param_config, True, + self.intermediate_variables) + self.assertIn("nova-api", result) + + def test_read_stdout_item(self): + result = util.read_stdout_item(self.std_output, 'id') + self.assertEqual('1', result) + + def test_buildshellparams(self): + result = util.buildshellparams(self.cmd_config, True) + self.assertEqual('/bin/bash -s {0} {1}', result) + + def test__fun_execute_shell_command_successful(self): + cmd = "env" + self.mock_subprocess.check_output.return_value = (0, 'unittest') + exitcode, _ = util.execute_shell_command(cmd) + self.assertEqual(exitcode, 0) + + def test__fun_execute_shell_command_fail_cmd_exception(self): + cmd = "env" + self.mock_subprocess.check_output.side_effect = RuntimeError + exitcode, _ = util.execute_shell_command(cmd) + self.assertEqual(exitcode, -1) diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/__init__.py b/yardstick/tests/unit/benchmark/scenarios/compute/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/__init__.py diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/cachestat_sample_output.txt b/yardstick/tests/unit/benchmark/scenarios/compute/cachestat_sample_output.txt new file mode 100644 index 000000000..e2c79a9b1 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/cachestat_sample_output.txt @@ -0,0 +1,5 @@ +Counting cache functions... Output every 1 seconds. + HITS MISSES DIRTIES RATIO BUFFERS_MB CACHE_MB + 6462 0 29 100.0% 1157 66782 + +Ending tracing... diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/cpuload_sample_output1.txt b/yardstick/tests/unit/benchmark/scenarios/compute/cpuload_sample_output1.txt new file mode 100644 index 000000000..723e64bcb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/cpuload_sample_output1.txt @@ -0,0 +1,9 @@ +Linux 3.13.0-68-generic (elxg482ls42) 11/30/2015 _x86_64_ (1 CPU) + +04:34:26 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle +04:34:26 PM all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 +04:34:26 PM 0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 + +Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle +Average: all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 +Average: 0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/cpuload_sample_output2.txt b/yardstick/tests/unit/benchmark/scenarios/compute/cpuload_sample_output2.txt new file mode 100644 index 000000000..c66520a27 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/cpuload_sample_output2.txt @@ -0,0 +1,2 @@ +cpu 245813227 366650 17338727 1195600354 2652765 178 177114 0 80439531 0 +cpu0 32334587 35782 1659040 87008833 401178 60 73571 0 8030817 0 diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/memload_sample_output.txt b/yardstick/tests/unit/benchmark/scenarios/compute/memload_sample_output.txt new file mode 100644 index 000000000..1793e2f10 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/memload_sample_output.txt @@ -0,0 +1,3 @@ + total used free shared buff/cache available +Mem: 263753976 76737332 187016644 2844 853528 67252400 +Swap: 268029948 0 268029948 diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_cachestat.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_cachestat.py new file mode 100644 index 000000000..6f66c30f9 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_cachestat.py @@ -0,0 +1,95 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.cachestat.CACHEstat + +from __future__ import absolute_import +import mock +import unittest +import os + +from yardstick.benchmark.scenarios.compute import cachestat + + +@mock.patch('yardstick.benchmark.scenarios.compute.cachestat.ssh') +class CACHEstatTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'root', + 'key_filename': "mykey.key" + } + } + + self.result = {} + + def test_cachestat_successful_setup(self, mock_ssh): + c = cachestat.CACHEstat({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + c.setup() + self.assertIsNotNone(c.client) + self.assertTrue(c.setup_done) + + def test_execute_command_success(self, mock_ssh): + c = cachestat.CACHEstat({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + c.setup() + + expected_result = 'abcdefg' + mock_ssh.SSH.from_node().execute.return_value = (0, expected_result, '') + result = c._execute_command("foo") + self.assertEqual(result, expected_result) + + def test_execute_command_failed(self, mock_ssh): + c = cachestat.CACHEstat({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + c.setup() + + mock_ssh.SSH.from_node().execute.return_value = (127, '', 'Failed executing \ + command') + self.assertRaises(RuntimeError, c._execute_command, + "cat /proc/meminfo") + + def test_get_cache_usage_successful(self, mock_ssh): + options = { + "interval": 1, + } + args = {"options": options} + c = cachestat.CACHEstat(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + c.setup() + + output = self._read_file("cachestat_sample_output.txt") + mock_ssh.SSH.from_node().execute.return_value = (0, output, '') + result = c._get_cache_usage() + expected_result = {"cachestat": {"cache0": {"HITS": "6462", + "DIRTIES": "29", + "RATIO": "100.0%", + "MISSES": "0", + "BUFFERS_MB": "1157", + "CACHE_MB": "66782"}}, + "average": {"HITS": 6462, "DIRTIES": 29, + "RATIO": "100.0%", + "MISSES": 0, "BUFFERS_MB": 1157, + "CACHE_MB": 66782}, + "max": {"HITS": 6462, + "DIRTIES": 29, "RATIO": 100.0, "MISSES": 0, + "BUFFERS_MB": 1157, "CACHE_MB": 66782}} + + self.assertEqual(result, expected_result) + + def _read_file(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + output = os.path.join(curr_path, filename) + with open(output) as f: + sample_output = f.read() + return sample_output diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_computecapacity.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_computecapacity.py new file mode 100644 index 000000000..4bef589f4 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_computecapacity.py @@ -0,0 +1,64 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for +# yardstick.benchmark.scenarios.compute.computecapacity.ComputeCapacity + +from __future__ import absolute_import + +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.compute import computecapacity + +SAMPLE_OUTPUT = '{"Cpu_number": "2", "Core_number": "24",\ + "Memory_size": "263753976 kB", "Thread_number": "48",\ + "Cache_size": "30720 KB", "HT_Open": "0"}' + + +@mock.patch('yardstick.benchmark.scenarios.compute.computecapacity.ssh') +class ComputeCapacityTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'nodes': { + 'host': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'key_filename': "mykey.key", + 'password': "root" + }, + } + } + + self.result = {} + + def test_capacity_successful_setup(self, mock_ssh): + c = computecapacity.ComputeCapacity({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + c.setup() + self.assertIsNotNone(c.client) + self.assertTrue(c.setup_done) + + def test_capacity_successful(self, mock_ssh): + c = computecapacity.ComputeCapacity({}, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, SAMPLE_OUTPUT, '') + c.run(self.result) + expected_result = jsonutils.loads(SAMPLE_OUTPUT) + self.assertEqual(self.result, expected_result) + + def test_capacity_unsuccessful_script_error(self, mock_ssh): + c = computecapacity.ComputeCapacity({}, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, c.run, self.result) diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_cpuload.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_cpuload.py new file mode 100644 index 000000000..da6e6a22e --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_cpuload.py @@ -0,0 +1,262 @@ +############################################################################## +# Copyright (c) 2015 Ericsson AB and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.lmbench.Lmbench + +from __future__ import absolute_import +import mock +import unittest +import os + +from yardstick.benchmark.scenarios.compute import cpuload + + +@mock.patch('yardstick.benchmark.scenarios.compute.cpuload.ssh') +class CPULoadTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'key_filename': "mykey.key" + } + } + + self.result = {} + + def test_setup_mpstat_installed(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + + args = {'options': options} + + l = cpuload.CPULoad(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + l.setup() + self.assertIsNotNone(l.client) + self.assertTrue(l.setup_done) + self.assertTrue(l.has_mpstat) + + def test_setup_mpstat_not_installed(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + + args = {'options': options} + + l = cpuload.CPULoad(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (127, '', '') + + l.setup() + self.assertIsNotNone(l.client) + self.assertTrue(l.setup_done) + self.assertFalse(l.has_mpstat) + + def test_execute_command_success(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + + args = {'options': options} + + l = cpuload.CPULoad(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + l.setup() + + expected_result = 'abcdefg' + mock_ssh.SSH.from_node().execute.return_value = (0, expected_result, '') + result = l._execute_command("foo") + self.assertEqual(result, expected_result) + + def test_execute_command_failed(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + + args = {'options': options} + + l = cpuload.CPULoad(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + l.setup() + + mock_ssh.SSH.from_node().execute.return_value = (127, '', 'abcdefg') + self.assertRaises(RuntimeError, l._execute_command, + "cat /proc/loadavg") + + def test_get_loadavg(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + + args = {'options': options} + + l = cpuload.CPULoad(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + l.setup() + + mock_ssh.SSH.from_node().execute.return_value = \ + (0, '1.50 1.45 1.51 3/813 14322', '') + result = l._get_loadavg() + expected_result = \ + {'loadavg': ['1.50', '1.45', '1.51', '3/813', '14322']} + self.assertEqual(result, expected_result) + + def test_get_cpu_usage_mpstat(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + + args = {'options': options} + + l = cpuload.CPULoad(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + l.setup() + + l.interval = 1 + l.count = 1 + mpstat_output = self._read_file("cpuload_sample_output1.txt") + mock_ssh.SSH.from_node().execute.return_value = (0, mpstat_output, '') + result = l._get_cpu_usage_mpstat() + + expected_result = \ + {"mpstat_minimum": + {"cpu": {"%steal": "0.00", "%usr": "0.00", "%gnice": "0.00", + "%idle": "100.00", "%guest": "0.00", + "%iowait": "0.00", "%sys": "0.00", "%soft": "0.00", + "%irq": "0.00", "%nice": "0.00"}, + "cpu0": {"%steal": "0.00", "%usr": "0.00", "%gnice": "0.00", + "%idle": "100.00", "%guest": "0.00", + "%iowait": "0.00", "%sys": "0.00", "%soft": "0.00", + "%irq": "0.00", "%nice": "0.00"}}, + "mpstat_average": + {"cpu": {"%steal": "0.00", "%usr": "0.00", "%gnice": "0.00", + "%idle": "100.00", "%guest": "0.00", + "%iowait": "0.00", "%sys": "0.00", "%soft": "0.00", + "%irq": "0.00", "%nice": "0.00"}, + "cpu0": {"%steal": "0.00", "%usr": "0.00", "%gnice": "0.00", + "%idle": "100.00", "%guest": "0.00", + "%iowait": "0.00", "%sys": "0.00", "%soft": "0.00", + "%irq": "0.00", "%nice": "0.00"}}, + "mpstat_maximun": + {"cpu": {"%steal": "0.00", "%usr": "0.00", "%gnice": "0.00", + "%idle": "100.00", "%guest": "0.00", + "%iowait": "0.00", "%sys": "0.00", "%soft": "0.00", + "%irq": "0.00", "%nice": "0.00"}, + "cpu0": {"%steal": "0.00", "%usr": "0.00", "%gnice": "0.00", + "%idle": "100.00", "%guest": "0.00", + "%iowait": "0.00", "%sys": "0.00", "%soft": "0.00", + "%irq": "0.00", "%nice": "0.00"}}} + + self.assertDictEqual(result, expected_result) + + def test_get_cpu_usage(self, mock_ssh): + options = { + "interval": 0, + "count": 1 + } + + args = {'options': options} + + l = cpuload.CPULoad(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + l.setup() + + l.interval = 0 + output = self._read_file("cpuload_sample_output2.txt") + mock_ssh.SSH.from_node().execute.return_value = (0, output, '') + result = l._get_cpu_usage() + + expected_result = \ + {'mpstat': + {'cpu': + {'%steal': '0.00', + '%usr': '11.31', + '%gnice': '0.00', + '%idle': '81.78', + '%iowait': '0.18', + '%guest': '5.50', + '%sys': '1.19', + '%soft': '0.01', + '%irq': '0.00', + '%nice': '0.03'}, + 'cpu0': + {'%steal': '0.00', + '%usr': '20.00', + '%gnice': '0.00', + '%idle': '71.60', + '%iowait': '0.33', + '%guest': '6.61', + '%sys': '1.37', + '%soft': '0.06', + '%irq': '0.00', + '%nice': '0.03'}}} + + self.assertDictEqual(result, expected_result) + + def test_run_proc_stat(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + + args = {'options': options} + + l = cpuload.CPULoad(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + l.setup() + + l.interval = 0 + stat_output = self._read_file("cpuload_sample_output2.txt") + mock_ssh.SSH.from_node().execute.side_effect = \ + [(0, '1.50 1.45 1.51 3/813 14322', ''), (0, stat_output, '')] + + l.run(self.result) + expected_result = { + 'loadavg': ['1.50', '1.45', '1.51', '3/813', '14322'], + 'mpstat': + {'cpu': + {'%steal': '0.00', + '%usr': '11.31', + '%gnice': '0.00', + '%idle': '81.78', + '%iowait': '0.18', + '%guest': '5.50', + '%sys': '1.19', + '%soft': '0.01', + '%irq': '0.00', + '%nice': '0.03'}, + 'cpu0': + {'%steal': '0.00', + '%usr': '20.00', + '%gnice': '0.00', + '%idle': '71.60', + '%iowait': '0.33', + '%guest': '6.61', + '%sys': '1.37', + '%soft': '0.06', + '%irq': '0.00', + '%nice': '0.03'}}} + + self.assertDictEqual(self.result, expected_result) + + def _read_file(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + output = os.path.join(curr_path, filename) + with open(output) as f: + sample_output = f.read() + return sample_output diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_cyclictest.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_cyclictest.py new file mode 100644 index 000000000..4fadde4dc --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_cyclictest.py @@ -0,0 +1,167 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and other. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.cyclictest.Cyclictest + +from __future__ import absolute_import + +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.compute import cyclictest +from yardstick.common import exceptions as y_exc + + +@mock.patch('yardstick.benchmark.scenarios.compute.cyclictest.ssh') +class CyclictestTestCase(unittest.TestCase): + + def setUp(self): + self.scenario_cfg = { + "host": "kvm.LF", + "setup_options": { + "rpm_dir": "/opt/rpm", + "host_setup_seqs": [ + "host-setup0.sh", + "host-setup1.sh", + "host-run-qemu.sh" + ], + "script_dir": "/opt/scripts", + "image_dir": "/opt/image", + "guest_setup_seqs": [ + "guest-setup0.sh", + "guest-setup1.sh" + ] + }, + "sla": { + "action": "monitor", + "max_min_latency": 50, + "max_avg_latency": 100, + "max_max_latency": 1000 + }, + "options": { + "priority": 99, + "threads": 1, + "loops": 1000, + "affinity": 1, + "interval": 1000, + "histogram": 90 + } + } + self.context_cfg = { + "host": { + "ip": "10.229.43.154", + "key_filename": "/yardstick/resources/files/yardstick_key", + "role": "BareMetal", + "name": "kvm.LF", + "user": "root" + } + } + + def test_cyclictest_successful_setup(self, mock_ssh): + + c = cyclictest.Cyclictest(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + c.setup() + self.assertIsNotNone(c.guest) + self.assertIsNotNone(c.host) + self.assertTrue(c.setup_done) + + def test_cyclictest_successful_no_sla(self, mock_ssh): + result = {} + self.scenario_cfg.pop("sla", None) + c = cyclictest.Cyclictest(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + c.setup() + + c.guest = mock_ssh.SSH.from_node() + sample_output = '{"min": 100, "avg": 500, "max": 1000}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + c.run(result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(result, expected_result) + + def test_cyclictest_successful_sla(self, mock_ssh): + result = {} + self.scenario_cfg.update({"sla": { + "action": "monitor", + "max_min_latency": 100, + "max_avg_latency": 500, + "max_max_latency": 1000 + } + }) + c = cyclictest.Cyclictest(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + c.setup() + + c.guest = mock_ssh.SSH.from_node() + sample_output = '{"min": 100, "avg": 500, "max": 1000}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + c.run(result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(result, expected_result) + + def test_cyclictest_unsuccessful_sla_min_latency(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_min_latency": 10}}) + c = cyclictest.Cyclictest(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + c.setup() + + c.guest = mock_ssh.SSH.from_node() + sample_output = '{"min": 100, "avg": 500, "max": 1000}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, c.run, result) + + def test_cyclictest_unsuccessful_sla_avg_latency(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_avg_latency": 10}}) + c = cyclictest.Cyclictest(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + c.setup() + + c.guest = mock_ssh.SSH.from_node() + sample_output = '{"min": 100, "avg": 500, "max": 1000}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, c.run, result) + + def test_cyclictest_unsuccessful_sla_max_latency(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_max_latency": 10}}) + c = cyclictest.Cyclictest(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + c.setup() + + c.guest = mock_ssh.SSH.from_node() + sample_output = '{"min": 100, "avg": 500, "max": 1000}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, c.run, result) + + def test_cyclictest_unsuccessful_script_error(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_max_latency": 10}}) + c = cyclictest.Cyclictest(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + c.setup() + + c.guest = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, c.run, result) diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_lmbench.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_lmbench.py new file mode 100644 index 000000000..ba63e5f9e --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_lmbench.py @@ -0,0 +1,192 @@ +############################################################################## +# Copyright (c) 2015 Ericsson AB and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.compute import lmbench +from yardstick.common import exceptions as y_exc +from yardstick import ssh + + +class LmbenchTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'key_filename': "mykey.key" + } + } + + self.result = {} + + self._mock_ssh = mock.patch.object(ssh, 'SSH') + self.mock_ssh = self._mock_ssh.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_ssh.stop() + + def test_successful_setup(self): + + l = lmbench.Lmbench({}, self.ctx) + self.mock_ssh.from_node().execute.return_value = (0, '', '') + + l.setup() + self.assertIsNotNone(l.client) + self.assertTrue(l.setup_done) + + def test_unsuccessful_unknown_type_run(self): + + options = { + "test_type": "foo" + } + args = {'options': options} + + l = lmbench.Lmbench(args, self.ctx) + + self.assertRaises(RuntimeError, l.run, self.result) + + def test_successful_latency_run_no_sla(self): + + options = { + "test_type": "latency", + "stride": 64, + "stop_size": 16 + } + args = {'options': options} + l = lmbench.Lmbench(args, self.ctx) + + sample_output = '[{"latency": 4.944, "size": 0.00049}]' + self.mock_ssh.from_node().execute.return_value = (0, sample_output, '') + l.run(self.result) + expected_result = {"latencies0.latency": 4.944, "latencies0.size": 0.00049} + self.assertEqual(self.result, expected_result) + + def test_successful_bandwidth_run_no_sla(self): + + options = { + "test_type": "bandwidth", + "size": 500, + "benchmark": "rd", + "warmup": 0 + } + args = {"options": options} + l = lmbench.Lmbench(args, self.ctx) + + sample_output = '{"size(MB)": 0.262144, "bandwidth(MBps)": 11025.5}' + self.mock_ssh.from_node().execute.return_value = (0, sample_output, '') + l.run(self.result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(self.result, expected_result) + + def test_successful_latency_run_sla(self): + + options = { + "test_type": "latency", + "stride": 64, + "stop_size": 16 + } + args = { + "options": options, + "sla": {"max_latency": 35} + } + l = lmbench.Lmbench(args, self.ctx) + + sample_output = '[{"latency": 4.944, "size": 0.00049}]' + self.mock_ssh.from_node().execute.return_value = (0, sample_output, '') + l.run(self.result) + expected_result = {"latencies0.latency": 4.944, "latencies0.size": 0.00049} + self.assertEqual(self.result, expected_result) + + def test_successful_bandwidth_run_sla(self): + + options = { + "test_type": "bandwidth", + "size": 500, + "benchmark": "rd", + "warmup": 0 + } + args = { + "options": options, + "sla": {"min_bandwidth": 10000} + } + l = lmbench.Lmbench(args, self.ctx) + + sample_output = '{"size(MB)": 0.262144, "bandwidth(MBps)": 11025.5}' + self.mock_ssh.from_node().execute.return_value = (0, sample_output, '') + l.run(self.result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(self.result, expected_result) + + def test_unsuccessful_latency_run_sla(self): + + options = { + "test_type": "latency", + "stride": 64, + "stop_size": 16 + } + args = { + "options": options, + "sla": {"max_latency": 35} + } + l = lmbench.Lmbench(args, self.ctx) + + sample_output = '[{"latency": 37.5, "size": 0.00049}]' + self.mock_ssh.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, l.run, self.result) + + def test_unsuccessful_bandwidth_run_sla(self): + + options = { + "test_type": "bandwidth", + "size": 500, + "benchmark": "rd", + "warmup": 0 + } + args = { + "options": options, + "sla": {"min_bandwidth": 10000} + } + l = lmbench.Lmbench(args, self.ctx) + + sample_output = '{"size(MB)": 0.262144, "bandwidth(MBps)": 9925.5}' + self.mock_ssh.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, l.run, self.result) + + def test_successful_latency_for_cache_run_sla(self): + + options = { + "test_type": "latency_for_cache", + "repetition": 1, + "warmup": 0 + } + args = { + "options": options, + "sla": {"max_latency": 35} + } + l = lmbench.Lmbench(args, self.ctx) + + sample_output = "{\"L1cache\": 1.6}" + self.mock_ssh.from_node().execute.return_value = (0, sample_output, '') + l.run(self.result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(self.result, expected_result) + + def test_unsuccessful_script_error(self): + + options = {"test_type": "bandwidth"} + args = {"options": options} + l = lmbench.Lmbench(args, self.ctx) + + self.mock_ssh.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, l.run, self.result) diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_memload.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_memload.py new file mode 100644 index 000000000..8213d4490 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_memload.py @@ -0,0 +1,109 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.memload.MEMLoad + +from __future__ import absolute_import +import mock +import unittest +import os + +from yardstick.benchmark.scenarios.compute import memload + + +@mock.patch('yardstick.benchmark.scenarios.compute.memload.ssh') +class MEMLoadTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'root', + 'key_filename': "mykey.key" + } + } + + self.result = {} + + def test_memload_successful_setup(self, mock_ssh): + m = memload.MEMLoad({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + m.setup() + self.assertIsNotNone(m.client) + self.assertTrue(m.setup_done) + + def test_execute_command_success(self, mock_ssh): + m = memload.MEMLoad({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + m.setup() + + expected_result = 'abcdefg' + mock_ssh.SSH.from_node().execute.return_value = (0, expected_result, '') + result = m._execute_command("foo") + self.assertEqual(result, expected_result) + + def test_execute_command_failed(self, mock_ssh): + m = memload.MEMLoad({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + m.setup() + + mock_ssh.SSH.from_node().execute.return_value = (127, '', 'Failed executing \ + command') + self.assertRaises(RuntimeError, m._execute_command, + "cat /proc/meminfo") + + def test_get_mem_usage_successful(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + args = {"options": options} + m = memload.MEMLoad(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + m.setup() + + output = self._read_file("memload_sample_output.txt") + mock_ssh.SSH.from_node().execute.return_value = (0, output, '') + result = m._get_mem_usage() + expected_result = { + "max": { + 'shared': 2844, + 'buff/cache': 853528, + 'total': 263753976, + 'free': 187016644, + 'used': 76737332 + }, + "average": { + 'shared': 2844, + 'buff/cache': 853528, + 'total': 263753976, + 'free': 187016644, + 'used': 76737332 + }, + "free": { + "memory0": { + "used": "76737332", + "buff/cache": "853528", + "free": "187016644", + "shared": "2844", + "total": "263753976", + "available": "67252400" + } + } + } + + self.assertEqual(result, expected_result) + + def _read_file(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + output = os.path.join(curr_path, filename) + with open(output) as f: + sample_output = f.read() + return sample_output diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_plugintest.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_plugintest.py new file mode 100644 index 000000000..875301729 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_plugintest.py @@ -0,0 +1,60 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.plugintest.PluginTest + +from __future__ import absolute_import + +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.compute import plugintest + + +@mock.patch('yardstick.benchmark.scenarios.compute.plugintest.ssh') +class PluginTestTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'nodes': { + 'host1': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'key_filename': "mykey.key", + 'password': "root" + }, + } + } + + self.result = {} + + def test_sample_successful_setup(self, mock_ssh): + s = plugintest.PluginTest({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + s.setup() + self.assertIsNotNone(s.client) + self.assertTrue(s.setup_done) + + def test_sample_successful(self, mock_ssh): + s = plugintest.PluginTest({}, self.ctx) + + sample_output = '{"Test Output": "Hello world!"}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + s.run(self.result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(self.result, expected_result) + + def test_sample_unsuccessful_script_error(self, mock_ssh): + s = plugintest.PluginTest({}, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, s.run, self.result) diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py new file mode 100644 index 000000000..02040ca01 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py @@ -0,0 +1,158 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and other. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.qemu_migrate.QemuMigrate + +from __future__ import absolute_import + +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.compute import qemu_migrate +from yardstick.common import exceptions as y_exc + + +@mock.patch('yardstick.benchmark.scenarios.compute.qemu_migrate.ssh') +class QemuMigrateTestCase(unittest.TestCase): + + def setUp(self): + self.scenario_cfg = { + "host": "kvm.LF", + "setup_options": { + "rpm_dir": "/opt/rpm", + "script_dir": "/opt/scripts", + "image_dir": "/opt/image", + "host_setup_seqs": [ + "host-setup0.sh", + "host-setup1.sh", + "setup-ovsdpdk.sh", + "host-install-qemu.sh", + "host-run-qemu4lm.sh" + ] + }, + "sla": { + "action": "monitor", + "max_totaltime": 10, + "max_downtime": 0.10, + "max_setuptime": 0.50 + }, + "options": { + "smp": 99, + "migrate_to_port": 4444, + "incoming_ip": 0, + "qmp_src_path": "/tmp/qmp-sock-src", + "qmp_dst_path": "/tmp/qmp-sock-dst", + "max_down_time": "0.10" + } + } + self.context_cfg = { + "host": { + "ip": "10.229.43.154", + "key_filename": "/yardstick/resources/files/yardstick_key", + "role": "BareMetal", + "name": "kvm.LF", + "user": "root" + } + } + + def test_qemu_migrate_successful_setup(self, mock_ssh): + + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + q.setup() + self.assertIsNotNone(q.host) + self.assertTrue(q.setup_done) + + def test_qemu_migrate_successful_no_sla(self, mock_ssh): + result = {} + self.scenario_cfg.pop("sla", None) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + q.run(result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(result, expected_result) + + def test_qemu_migrate_successful_sla(self, mock_ssh): + result = {} + self.scenario_cfg.update({"sla": { + "action": "monitor", + "max_totaltime": 15, + "max_downtime": 2, + "max_setuptime": 1 + } + }) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + q.run(result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(result, expected_result) + + def test_qemu_migrate_unsuccessful_sla_totaltime(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_totaltime": 10}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, q.run, result) + + def test_qemu_migrate_unsuccessful_sla_downtime(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_downtime": 0.10}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, q.run, result) + + def test_qemu_migrate_unsuccessful_sla_setuptime(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_setuptime": 0.50}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + sample_output = '{"totaltime": 15, "downtime": 2, "setuptime": 1}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, q.run, result) + + def test_qemu_migrate_unsuccessful_script_error(self, mock_ssh): + + result = {} + self.scenario_cfg.update({"sla": {"max_totaltime": 10}}) + q = qemu_migrate.QemuMigrate(self.scenario_cfg, self.context_cfg) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + q.setup() + + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, q.run, result) diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_ramspeed.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_ramspeed.py new file mode 100644 index 000000000..9e055befe --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_ramspeed.py @@ -0,0 +1,236 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.ramspeed.Ramspeed + +from __future__ import absolute_import + +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.common import utils +from yardstick.benchmark.scenarios.compute import ramspeed +from yardstick.common import exceptions as y_exc + + +@mock.patch('yardstick.benchmark.scenarios.compute.ramspeed.ssh') +class RamspeedTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'root', + 'key_filename': "mykey.key" + } + } + + self.result = {} + + def test_ramspeed_successful_setup(self, mock_ssh): + + r = ramspeed.Ramspeed({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + r.setup() + self.assertIsNotNone(r.client) + self.assertTrue(r.setup_done, True) + + def test_ramspeed_successful__run_no_sla(self, mock_ssh): + + options = { + "test_id": 1, + "load": 16, + "block_size": 32 + } + args = {"options": options} + r = ramspeed.Ramspeed(args, self.ctx) + + sample_output = '{"Result": [{"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 1, "Bandwidth(MBps)": 19909.18}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 2, "Bandwidth(MBps)": 19873.89},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 4, "Bandwidth(MBps)":\ + 19907.56}, {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 8,\ + "Bandwidth(MBps)": 19906.94}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 16, "Bandwidth(MBps)": 19881.74}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 32, "Bandwidth(MBps)": 19395.65},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 64, "Bandwidth(MBps)":\ + 17623.14}, {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 128,\ + "Bandwidth(MBps)": 17677.36}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 256, "Bandwidth(MBps)": 16113.49}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 512, "Bandwidth(MBps)": 14659.19},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 1024, "Bandwidth(MBps)":\ + 14680.75}, {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 2048,\ + "Bandwidth(MBps)": 14756.45}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 4096, "Bandwidth(MBps)": 14604.44}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 8192, "Bandwidth(MBps)": 14159.86},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 16384,\ + "Bandwidth(MBps)": 14128.94}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 32768, "Bandwidth(MBps)": 8340.85}]}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + r.run(self.result) + expected_result = utils.flatten_dict_key(jsonutils.loads(sample_output)) + self.assertEqual(self.result, expected_result) + + def test_ramspeed_successful_run_sla(self, mock_ssh): + + options = { + "test_id": 1, + "load": 16, + "block_size": 32 + } + args = {"options": options, "sla": {"min_bandwidth": 6000}} + r = ramspeed.Ramspeed(args, self.ctx) + + sample_output = '{"Result": [{"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 1, "Bandwidth(MBps)": 19909.18}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 2, "Bandwidth(MBps)": 19873.89},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 4, "Bandwidth(MBps)":\ + 19907.56}, {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 8,\ + "Bandwidth(MBps)": 19906.94}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 16, "Bandwidth(MBps)": 19881.74}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 32, "Bandwidth(MBps)": 19395.65},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 64, "Bandwidth(MBps)":\ + 17623.14}, {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 128,\ + "Bandwidth(MBps)": 17677.36}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 256, "Bandwidth(MBps)": 16113.49}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 512, "Bandwidth(MBps)": 14659.19},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 1024, "Bandwidth(MBps)":\ + 14680.75}, {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 2048,\ + "Bandwidth(MBps)": 14756.45}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 4096, "Bandwidth(MBps)": 14604.44}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 8192, "Bandwidth(MBps)": 14159.86},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 16384,\ + "Bandwidth(MBps)": 14128.94}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 32768, "Bandwidth(MBps)": 8340.85}]}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + r.run(self.result) + expected_result = utils.flatten_dict_key(jsonutils.loads(sample_output)) + self.assertEqual(self.result, expected_result) + + def test_ramspeed_unsuccessful_run_sla(self, mock_ssh): + options = { + "test_id": 1, + "load": 8, + "block_size": 64 + } + args = {"options": options, "sla": {"min_bandwidth": 100000}} + r = ramspeed.Ramspeed(args, self.ctx) + + sample_output = '{"Result": [{"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 1, "Bandwidth(MBps)": 5000.18}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 2, "Bandwidth(MBps)": 5000.89},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 4,\ + "Bandwidth(MBps)": 5000.56}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 8, "Bandwidth(MBps)": 19906.94}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 16, "Bandwidth(MBps)": 19881.74},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 32,\ + "Bandwidth(MBps)": 19395.65}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 64, "Bandwidth(MBps)": 17623.14}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 128, "Bandwidth(MBps)": 17677.36},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 256, "Bandwidth(MBps)":\ + 16113.49}, {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 512,\ + "Bandwidth(MBps)": 14659.19}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 1024, "Bandwidth(MBps)": 14680.75}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 2048, "Bandwidth(MBps)": 14756.45},\ + {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 4096, "Bandwidth(MBps)":\ + 14604.44}, {"Test_type": "INTEGER & WRITING", "Block_size(kb)": 8192,\ + "Bandwidth(MBps)": 14159.86}, {"Test_type": "INTEGER & WRITING",\ + "Block_size(kb)": 16384, "Bandwidth(MBps)": 14128.94}, {"Test_type":\ + "INTEGER & WRITING", "Block_size(kb)": 32768, "Bandwidth(MBps)": 8340.85}]}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, r.run, self.result) + + def test_ramspeed_unsuccessful_script_error(self, mock_ssh): + options = { + "test_id": 1, + "load": 16, + "block_size": 32 + } + args = {"options": options} + r = ramspeed.Ramspeed(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, r.run, self.result) + + def test_ramspeed_mem_successful_run_no_sla(self, mock_ssh): + options = { + "test_id": 3, + "load": 16, + "block_size": 32, + "iteration": 1 + } + args = {"options": options} + r = ramspeed.Ramspeed(args, self.ctx) + + sample_output = '{"Result": [{"Test_type": "INTEGER Copy:",\ + "Bandwidth(MBps)": 8353.97}, {"Test_type": "INTEGER Scale:",\ + "Bandwidth(MBps)": 9078.59}, {"Test_type": "INTEGER Add:",\ + "Bandwidth(MBps)": 10057.48}, {"Test_type": "INTEGER Triad:",\ + "Bandwidth(MBps)": 10116.27}, {"Test_type": "INTEGER AVERAGE:",\ + "Bandwidth(MBps)": 9401.58}]}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + r.run(self.result) + expected_result = utils.flatten_dict_key(jsonutils.loads(sample_output)) + self.assertEqual(self.result, expected_result) + + def test_ramspeed_mem_successful_run_sla(self, mock_ssh): + options = { + "test_id": 3, + "load": 16, + "block_size": 32, + "iteration": 1 + } + args = {"options": options, "sla": {"min_bandwidth": 6000}} + r = ramspeed.Ramspeed(args, self.ctx) + + sample_output = '{"Result": [{"Test_type": "INTEGER Copy:",\ + "Bandwidth(MBps)": 8353.97}, {"Test_type": "INTEGER Scale:",\ + "Bandwidth(MBps)": 9078.59}, {"Test_type": "INTEGER Add:",\ + "Bandwidth(MBps)": 10057.48}, {"Test_type": "INTEGER Triad:",\ + "Bandwidth(MBps)": 10116.27}, {"Test_type": "INTEGER AVERAGE:",\ + "Bandwidth(MBps)": 9401.58}]}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + r.run(self.result) + expected_result = utils.flatten_dict_key(jsonutils.loads(sample_output)) + self.assertEqual(self.result, expected_result) + + def test_ramspeed_mem_unsuccessful_run_sla(self, mock_ssh): + options = { + "test_id": 3, + "load": 16, + "block_size": 32, + "iteration": 1 + } + args = {"options": options, "sla": {"min_bandwidth": 86000}} + r = ramspeed.Ramspeed(args, self.ctx) + + sample_output = '{"Result": [{"Test_type": "INTEGER Copy:",\ + "Bandwidth(MBps)": 4000.97}, {"Test_type": "INTEGER Scale:",\ + "Bandwidth(MBps)": 4400.59}, {"Test_type": "INTEGER Add:",\ + "Bandwidth(MBps)": 4300.48}, {"Test_type": "INTEGER Triad:",\ + "Bandwidth(MBps)": 1300.27}, {"Test_type": "INTEGER AVERAGE:",\ + "Bandwidth(MBps)": 2401.58}]}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, r.run, self.result) + + def test_ramspeed_unsuccessful_unknown_type_run(self, mock_ssh): + options = { + "test_id": 30, + "load": 16, + "block_size": 32 + } + args = {'options': options} + r = ramspeed.Ramspeed(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'No such type_id: 30 for \ + Ramspeed scenario') + self.assertRaises(RuntimeError, r.run, self.result) diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu.py new file mode 100644 index 000000000..643e1eae2 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu.py @@ -0,0 +1,75 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.spec_cpu.SpecCPU + +from __future__ import absolute_import + +import unittest + +import mock + +from yardstick.benchmark.scenarios.compute import spec_cpu + + +@mock.patch('yardstick.benchmark.scenarios.compute.spec_cpu.ssh') +class SpecCPUTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'root', + 'key_filename': "mykey.key" + } + } + + self.result = {} + + def test_spec_cpu_successful_setup(self, mock_ssh): + + options = { + "SPECint_benchmark": "perlbench", + "output_format": "all", + "runspec_iterations": "1", + "runspec_tune": "base", + "runspec_size": "test" + } + args = {"options": options} + s = spec_cpu.SpecCPU(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + s.setup() + self.assertIsNotNone(s.client) + self.assertTrue(s.setup_done, True) + + def test_spec_cpu_successful__run_no_sla(self, mock_ssh): + + options = { + "SPECint_benchmark": "perlbench", + "runspec_tune": "all", + "output_format": "all" + } + args = {"options": options} + s = spec_cpu.SpecCPU(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + s.run(self.result) + expected_result = {} + self.assertEqual(self.result, expected_result) + + def test_ramspeed_unsuccessful_script_error(self, mock_ssh): + options = { + "benchmark_subset": "int" + } + args = {"options": options} + s = spec_cpu.SpecCPU(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, s.run, self.result) diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu_for_vm.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu_for_vm.py new file mode 100644 index 000000000..74ef576b6 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu_for_vm.py @@ -0,0 +1,76 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.spec_cpu_for_vm.SpecCPUforVM + +from __future__ import absolute_import + +import unittest + +import mock + +from yardstick.benchmark.scenarios.compute import spec_cpu_for_vm + + +@mock.patch('yardstick.benchmark.scenarios.compute.spec_cpu_for_vm.ssh') +class SpecCPUforVMTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'root', + 'key_filename': "mykey.key" + } + } + + self.result = {} + + def test_spec_cpu_successful_setup(self, mock_ssh): + + options = { + "SPECint_benchmark": "perlbench", + "runspec_tune": "all", + "output_format": "all", + "runspec_iterations": "1", + "runspec_size": "test" + } + args = {"options": options} + s = spec_cpu_for_vm.SpecCPUforVM(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + s.setup() + self.assertIsNotNone(s.client) + self.assertTrue(s.setup_done, True) + + def test_spec_cpu_successful__run_no_sla(self, mock_ssh): + + options = { + "SPECint_benchmark": "perlbench", + "runspec_tune": "all", + "output_format": "all" + } + args = {"options": options} + s = spec_cpu_for_vm.SpecCPUforVM(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + mock_ssh.SSH.from_node().get.return_value = (0, '', '') + s.run(self.result) + expected_result = {'SPEC_CPU_result': ''} + self.assertEqual(self.result, expected_result) + + def test_spec_cpu_unsuccessful_script_error(self, mock_ssh): + options = { + "benchmark_subset": "int" + } + args = {"options": options} + s = spec_cpu_for_vm.SpecCPUforVM(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, s.run, self.result) diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_unixbench.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_unixbench.py new file mode 100644 index 000000000..e4a8d6e26 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_unixbench.py @@ -0,0 +1,163 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and other. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.compute.unixbench.Unixbench + +from __future__ import absolute_import + +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.compute import unixbench +from yardstick.common import exceptions as y_exc + + +@mock.patch('yardstick.benchmark.scenarios.compute.unixbench.ssh') +class UnixbenchTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + "host": { + "ip": "192.168.50.28", + "user": "root", + "key_filename": "mykey.key" + } + } + + def test_unixbench_successful_setup(self, mock_ssh): + + u = unixbench.Unixbench({}, self.ctx) + u.setup() + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + self.assertIsNotNone(u.client) + self.assertTrue(u.setup_done) + + def test_unixbench_successful_no_sla(self, mock_ssh): + + options = { + "test_type": 'dhry2reg', + "run_mode": 'verbose' + } + args = { + "options": options, + } + u = unixbench.Unixbench(args, self.ctx) + result = {} + + u.server = mock_ssh.SSH.from_node() + + sample_output = '{"Score":"4425.4"}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + u.run(result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(result, expected_result) + + def test_unixbench_successful_in_quiet_mode(self, mock_ssh): + + options = { + "test_type": 'dhry2reg', + "run_mode": 'quiet', + "copies": 1 + } + args = { + "options": options, + } + u = unixbench.Unixbench(args, self.ctx) + result = {} + + u.server = mock_ssh.SSH.from_node() + + sample_output = '{"Score":"4425.4"}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + u.run(result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(result, expected_result) + + def test_unixbench_successful_sla(self, mock_ssh): + + options = { + "test_type": 'dhry2reg', + "run_mode": 'verbose' + } + sla = { + "single_score": '100', + "parallel_score": '500' + } + args = { + "options": options, + "sla": sla + } + u = unixbench.Unixbench(args, self.ctx) + result = {} + + u.server = mock_ssh.SSH.from_node() + + sample_output = '{"signle_score":"2251.7","parallel_score":"4395.9"}' + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + u.run(result) + expected_result = jsonutils.loads(sample_output) + self.assertEqual(result, expected_result) + + def test_unixbench_unsuccessful_sla_single_score(self, mock_ssh): + + args = { + "options": {}, + "sla": {"single_score": "500"} + } + u = unixbench.Unixbench(args, self.ctx) + result = {} + + u.server = mock_ssh.SSH.from_node() + sample_output = '{"single_score":"200.7","parallel_score":"4395.9"}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, u.run, result) + + def test_unixbench_unsuccessful_sla_parallel_score(self, mock_ssh): + + args = { + "options": {}, + "sla": {"parallel_score": "4000"} + } + u = unixbench.Unixbench(args, self.ctx) + result = {} + + u.server = mock_ssh.SSH.from_node() + sample_output = '{"signle_score":"2251.7","parallel_score":"3395.9"}' + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, u.run, result) + + def test_unixbench_unsuccessful_script_error(self, mock_ssh): + + options = { + "test_type": 'dhry2reg', + "run_mode": 'verbose' + } + sla = { + "single_score": '100', + "parallel_score": '500' + } + args = { + "options": options, + "sla": sla + } + u = unixbench.Unixbench(args, self.ctx) + result = {} + + u.server = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, u.run, result) diff --git a/yardstick/tests/unit/benchmark/scenarios/dummy/__init__.py b/yardstick/tests/unit/benchmark/scenarios/dummy/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/dummy/__init__.py diff --git a/yardstick/tests/unit/benchmark/scenarios/dummy/test_dummy.py b/yardstick/tests/unit/benchmark/scenarios/dummy/test_dummy.py new file mode 100644 index 000000000..875302da8 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/dummy/test_dummy.py @@ -0,0 +1,32 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.dummy.dummy + +from __future__ import absolute_import +import unittest + +from yardstick.benchmark.scenarios.dummy import dummy + + +class DummyTestCase(unittest.TestCase): + + def setUp(self): + self.test_context = dummy.Dummy(None, None) + + self.assertIsNone(self.test_context.scenario_cfg) + self.assertIsNone(self.test_context.context_cfg) + self.assertFalse(self.test_context.setup_done) + + def test_run(self): + result = {} + self.test_context.run(result) + + self.assertEqual(result["hello"], "yardstick") + self.assertTrue(self.test_context.setup_done) diff --git a/yardstick/tests/unit/benchmark/scenarios/energy/__init__.py b/yardstick/tests/unit/benchmark/scenarios/energy/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/energy/__init__.py diff --git a/yardstick/tests/unit/benchmark/scenarios/energy/energy_sample_chassis_output.txt b/yardstick/tests/unit/benchmark/scenarios/energy/energy_sample_chassis_output.txt new file mode 100644 index 000000000..9b3afd1fb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/energy/energy_sample_chassis_output.txt @@ -0,0 +1,14 @@ +{ + "@odata.id": "/redfish/v1/Chassis", + "Name": "ChassisCollection", + "@odata.context": "/redfish/v1/$metadata#ChassisCollection.ChassisCollection", + "Members": [ + { + "@odata.id": "/redfish/v1/Chassis/1" + } + ], + "@odata.type": "#ChassisCollection.ChassisCollection", + "@odata.etag": "\"af5a94479815eb5f87fe91ea08fde0ac\"", + "Members@odata.count": 1, + "Description": "A collection of Chassis resource instances." +} diff --git a/yardstick/tests/unit/benchmark/scenarios/energy/energy_sample_power_metrics.txt b/yardstick/tests/unit/benchmark/scenarios/energy/energy_sample_power_metrics.txt new file mode 100644 index 000000000..343ed3667 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/energy/energy_sample_power_metrics.txt @@ -0,0 +1,300 @@ +{ + "PowerControl@odata.count": 1, + "@odata.id": "/redfish/v1/Chassis/1/Power", + "Redundancy@odata.count": 1, + "@odata.context": "/redfish/v1/$metadata#Power.Power", + "Voltages": [ + { + "MaxReadingRange": 14.28, + "RelatedItem": [ + { + "@odata.id": "/redfish/v1/Systems/1" + }, + { + "@odata.id": "/redfish/v1/Chassis/1" + } + ], + "@odata.id": "/redfish/v1/Chassis/1/Power#/Voltages/0", + "Status": { + "State": "Enabled" + }, + "SensorNumber": 140, + "Name": "SysBrd 12V", + "PhysicalContext": "VoltageRegulator", + "LowerThresholdCritical": 10.81, + "RelatedItem@odata.count": 2, + "MemberId": "0", + "MinReadingRange": null, + "ReadingVolts": 12.15, + "UpperThresholdCritical": 13.22 + }, + { + "MaxReadingRange": 3.95, + "RelatedItem": [ + { + "@odata.id": "/redfish/v1/Systems/1" + }, + { + "@odata.id": "/redfish/v1/Chassis/1" + } + ], + "@odata.id": "/redfish/v1/Chassis/1/Power#/Voltages/1", + "Status": { + "State": "Enabled" + }, + "SensorNumber": 141, + "Name": "SysBrd 3.3V", + "PhysicalContext": "VoltageRegulator", + "LowerThresholdCritical": 2.98, + "RelatedItem@odata.count": 2, + "MemberId": "1", + "MinReadingRange": null, + "UpperThresholdCritical": 3.63, + "ReadingVolts": 3.36 + }, + { + "MaxReadingRange": 5.97, + "RelatedItem": [ + { + "@odata.id": "/redfish/v1/Systems/1" + }, + { + "@odata.id": "/redfish/v1/Chassis/1" + } + ], + "@odata.id": "/redfish/v1/Chassis/1/Power#/Voltages/2", + "Status": { + "State": "Enabled" + }, + "SensorNumber": 142, + "Name": "SysBrd 5V", + "PhysicalContext": "VoltageRegulator", + "LowerThresholdCritical": 4.49, + "RelatedItem@odata.count": 2, + "MemberId": "2", + "MinReadingRange": null, + "UpperThresholdCritical": 5.5, + "ReadingVolts": 5.03 + }, + { + "MaxReadingRange": 3.32, + "RelatedItem": [ + { + "@odata.id": "/redfish/v1/Systems/1" + }, + { + "@odata.id": "/redfish/v1/Chassis/1" + } + ], + "@odata.id": "/redfish/v1/Chassis/1/Power#/Voltages/3", + "Status": { + "State": "Enabled" + }, + "SensorNumber": 3, + "Name": "CMOS Battery", + "PhysicalContext": "VoltageRegulator", + "LowerThresholdCritical": 2.25, + "RelatedItem@odata.count": 2, + "MemberId": "3", + "MinReadingRange": null, + "LowerThresholdNonCritical": 2.39, + "ReadingVolts": 3.12 + } + ], + "Voltages@odata.count": 4, + "Redundancy": [ + { + "@odata.id": "/redfish/v1/Chassis/1/Power#/Redundancy/0", + "Status": { + "State": "Enabled", + "Health": "OK" + }, + "Name": "PSU Redundancy", + "MinNumNeeded": 2, + "Oem": { + "Lenovo": { + "NonRedundantAvailablePower": 1100, + "@odata.type": "#LenovoRedundancy.v1_0_0.LenovoRedundancyProperties", + "PowerRedundancySettings": { + "EstimatedUsage": "58.55%", + "MaxPowerLimitWatts": 1100, + "PowerFailureLimit": 0, + "PowerRedundancyPolicy": "RedundantWithThrottling" + } + } + }, + "RedundancyEnabled": true, + "RedundancySet": [ + { + "@odata.id": "/redfish/v1/Chassis/1/Power#/PowerSupplies/0" + }, + { + "@odata.id": "/redfish/v1/Chassis/1/Power#/PowerSupplies/1" + } + ], + "RedundancySet@odata.count": 2, + "MaxNumSupported": 2, + "Mode": "N+m", + "MemberId": "0" + } + ], + "Description": "Power Consumption and Power Limiting", + "Name": "Power", + "PowerSupplies@odata.count": 2, + "Oem": { + "Lenovo": { + "@odata.type": "#LenovoPower.v1_0_0.Capabilities", + "LocalPowerControlEnabled": true, + "PowerOnPermissionEnabled": true, + "PowerRestorePolicy": "Restore", + "WakeOnLANEnabled": true + } + }, + "@odata.type": "#Power.v1_5_1.Power", + "Id": "Power", + "@odata.etag": "\"ad85a1403e07a433386e9907d00565cc\"", + "PowerControl": [ + { + "PowerAllocatedWatts": 1100, + "RelatedItem": [ + { + "@odata.id": "/redfish/v1/Chassis/1" + } + ], + "@odata.id": "/redfish/v1/Chassis/1/Power#/PowerControl/0", + "Status": { + "HealthRollup": "Warning", + "State": "Enabled" + }, + "PowerLimit": { + "LimitException": "NoAction", + "LimitInWatts": null + }, + "Name": "Server Power Control", + "Oem": { + "Lenovo": { + "PowerUtilization": { + "MaxLimitInWatts": 1100, + "EnablePowerCapping": false, + "LimitMode": "AC", + "EnablePowerCapping@Redfish.Deprecated": "The property is deprecated. Please use LimitInWatts instead.", + "CapacityMinAC": 617, + "MinLimitInWatts": 0, + "GuaranteedInWatts": 617, + "CapacityMinDC": 578, + "CapacityMaxDC": 749, + "CapacityMaxAC": 802 + }, + "HistoryPowerMetric": { + "@odata.id": "/redfish/v1/Chassis/1/Power/PowerControl/0/Oem/Lenovo/HistoryPowerMetric" + }, + "@odata.type": "#LenovoPower.v1_0_0.PowerControl" + } + }, + "PowerAvailableWatts": 0, + "PowerMetrics": { + "IntervalInMin": 60, + "AverageConsumedWatts": 314.716675, + "MinConsumedWatts": 311, + "MaxConsumedWatts": 318 + }, + "RelatedItem@odata.count": 1, + "MemberId": "0", + "PowerRequestedWatts": 802, + "PowerConsumedWatts": 344, + "PowerCapacityWatts": 1100 + } + ], + "PowerSupplies": [ + { + "SerialNumber": "A4DB8BP11WJ", + "InputRanges": [ + { + "InputType": null, + "OutputWattage": null, + "MinimumVoltage": null, + "MaximumVoltage": null + } + ], + "@odata.id": "/redfish/v1/Chassis/1/Power#/PowerSupplies/0", + "RelatedItem@odata.count": 1, + "MemberId": "0", + "PartNumber": "SP57A02023", + "FirmwareVersion": "4.52", + "Status": { + "State": "Enabled", + "Health": "Warning" + }, + "LineInputVoltage": null, + "Name": "PSU1", + "PowerSupplyType": "Unknown", + "LastPowerOutputWatts": 316, + "Oem": { + "Lenovo": { + "Location": { + "InfoFormat": "Slot X", + "Info": "Slot 1" + }, + "HistoryPowerSupplyMetric": { + "@odata.id": "/redfish/v1/Chassis/1/Power/PowerSupplies/0/Oem/Lenovo/HistoryPowerSupplyMetric" + }, + "@odata.type": "#LenovoPower.v1_0_0.PowerSupply" + } + }, + "PowerCapacityWatts": null, + "Manufacturer": "ACBE", + "LineInputVoltageType": "Unknown", + "Model": "LENOVO-SP57A02023", + "RelatedItem": [ + { + "@odata.id": "/redfish/v1/Chassis/1" + } + ] + }, + { + "SerialNumber": "A4DB8BP12J7", + "InputRanges": [ + { + "InputType": "AC", + "OutputWattage": 1100, + "MinimumVoltage": 200, + "MaximumVoltage": 240 + } + ], + "@odata.id": "/redfish/v1/Chassis/1/Power#/PowerSupplies/1", + "RelatedItem@odata.count": 1, + "MemberId": "1", + "PartNumber": "SP57A02023", + "FirmwareVersion": "4.52", + "Status": { + "State": "Enabled", + "Health": "OK" + }, + "LineInputVoltage": 220, + "Name": "PSU2", + "PowerSupplyType": "AC", + "LastPowerOutputWatts": 316, + "Oem": { + "Lenovo": { + "Location": { + "InfoFormat": "Slot X", + "Info": "Slot 2" + }, + "HistoryPowerSupplyMetric": { + "@odata.id": "/redfish/v1/Chassis/1/Power/PowerSupplies/1/Oem/Lenovo/HistoryPowerSupplyMetric" + }, + "@odata.type": "#LenovoPower.v1_0_0.PowerSupply" + } + }, + "PowerCapacityWatts": 1100, + "Manufacturer": "ACBE", + "LineInputVoltageType": "ACMidLine", + "Model": "LENOVO-SP57A02023", + "RelatedItem": [ + { + "@odata.id": "/redfish/v1/Chassis/1" + } + ] + } + ] +} diff --git a/yardstick/tests/unit/benchmark/scenarios/energy/test_energy.py b/yardstick/tests/unit/benchmark/scenarios/energy/test_energy.py new file mode 100644 index 000000000..98daefeb7 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/energy/test_energy.py @@ -0,0 +1,182 @@ +############################################################################## +# Copyright (c) 2019 Lenovo Group Limited Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.energy.energy.Energy + +from __future__ import absolute_import +import unittest +import mock +import os +from yardstick.benchmark.scenarios.energy import energy + + +class EnergyTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'target': { + 'ip': '172.16.0.137', + 'user': 'root', + 'password': 'passw0rd', + 'redfish_ip': '10.229.17.105', + 'redfish_user': 'USERID', + 'redfish_pwd': "PASSW0RD", + } + } + self.result = {} + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_setup_response_success(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + mock_send_request.return_value.status_code = 200 + p.setup() + self.assertTrue(p.get_response) + self.assertTrue(p.setup_done) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_setup_response_failed(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + mock_send_request.return_value.status_code = 404 + p.setup() + self.assertFalse(p.get_response) + self.assertTrue(p.setup_done) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_load_chassis_list_success(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + expect_result = self._read_file("energy_sample_chassis_output.txt") + expect_result = str(expect_result) + expect_result = expect_result.replace("'", '"') + mock_send_request.return_value.status_code = 200 + mock_send_request.return_value.text = expect_result + self.result = p.load_chassis_list() + self.assertEqual(self.result, ["/redfish/v1/Chassis/1"]) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_load_chassis_response_fail(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + mock_send_request.return_value.status_code = 404 + self.result = p.load_chassis_list() + self.assertEqual(self.result, []) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_load_chassis_wrongtype_response(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + mock_send_request.return_value.status_code = 200 + expect_result = {} + mock_send_request.return_value.text = expect_result + self.result = p.load_chassis_list() + self.assertEqual(self.result, []) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_load_chassis_inproper_key(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + mock_send_request.return_value.status_code = 200 + expect_result = '{"some_key": "some_value"}' + mock_send_request.return_value.text = expect_result + self.result = p.load_chassis_list() + self.assertEqual(self.result, []) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_energy_getpower_success(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + expect_result = self._read_file("energy_sample_power_metrics.txt") + expect_result = str(expect_result) + expect_result = expect_result.replace("'", '"') + mock_send_request.return_value.status_code = 200 + mock_send_request.return_value.text = expect_result + self.result = p.get_power("/redfish/v1/Chassis/1") + self.assertEqual(self.result, 344) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_energy_getpower_response_fail(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + mock_send_request.return_value.status_code = 404 + self.result = p.get_power("/redfish/v1/Chassis/1") + self.assertEqual(self.result, -1) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_energy_getpower_wrongtype_response(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + mock_send_request.return_value.status_code = 200 + expect_result = {} + mock_send_request.return_value.text = expect_result + self.result = p.get_power("/redfish/v1/Chassis/1") + self.assertEqual(self.result, -1) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_energy_getpower_inproper_key(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + mock_send_request.return_value.status_code = 200 + expect_result = '{"some_key": "some_value"}' + mock_send_request.return_value.text = expect_result + self.result = p.get_power("/redfish/v1/Chassis/1") + self.assertEqual(self.result, -1) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_run_success(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + mock_send_request.return_value.status_code = 200 + chassis_list = mock.Mock(return_value=["/redfish/v1/Chassis/1"]) + p.load_chassis_list = chassis_list + power = mock.Mock(return_value=344) + p.get_power = power + p.run(self.result) + self.assertEqual(self.result, {"power": 344}) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_run_no_response(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + mock_send_request.return_value.status_code = 404 + chassis_list = mock.Mock(return_value=["/redfish/v1/Chassis/1"]) + p.load_chassis_list = chassis_list + p.run(self.result) + self.assertEqual(self.result, {"power": -1}) + + @mock.patch('yardstick.benchmark.scenarios.' + 'energy.energy.Energy._send_request') + def test_run_wrong_chassis(self, mock_send_request): + args = {} + p = energy.Energy(args, self.ctx) + mock_send_request.return_value.status_code = 200 + chassis_list = mock.Mock(return_value=[]) + p.load_chassis_list = chassis_list + p.run(self.result) + self.assertEqual(self.result, {"power": -1}) + + def _read_file(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + output = os.path.join(curr_path, filename) + with open(output) as f: + sample_output = f.read() + return sample_output diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/__init__.py b/yardstick/tests/unit/benchmark/scenarios/lib/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/__init__.py diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_add_memory_load.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_add_memory_load.py new file mode 100644 index 000000000..af4f0c8ab --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_add_memory_load.py @@ -0,0 +1,57 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.add_memory_load import AddMemoryLoad + + +class AddMemoryLoadTestCase(unittest.TestCase): + + @mock.patch('yardstick.ssh.SSH.from_node') + def test_add_memory_load_with_load(self, mock_from_node): + scenario_cfg = { + 'options': { + 'memory_load': 0.5 + } + } + context_cfg = { + 'host': {} + } + mock_from_node().execute.return_value = (0, '0 2048 512', '') + obj = AddMemoryLoad(scenario_cfg, context_cfg) + obj.run({}) + mock_from_node.assert_called() + + @mock.patch('yardstick.ssh.SSH.from_node') + def test_add_memory_load_without_load(self, mock_from_node): + scenario_cfg = { + 'options': { + 'memory_load': 0 + } + } + context_cfg = { + 'host': {} + } + obj = AddMemoryLoad(scenario_cfg, context_cfg) + obj.run({}) + mock_from_node.assert_called_once() + + @mock.patch('yardstick.ssh.SSH.from_node') + def test_add_memory_load_without_args(self, mock_from_node): + scenario_cfg = { + 'options': { + } + } + context_cfg = { + 'host': {} + } + obj = AddMemoryLoad(scenario_cfg, context_cfg) + obj.run({}) + mock_from_node.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_attach_volume.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_attach_volume.py new file mode 100644 index 000000000..bb7fa4536 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_attach_volume.py @@ -0,0 +1,56 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import attach_volume + + +class AttachVolumeTestCase(unittest.TestCase): + + def setUp(self): + + self._mock_attach_volume_to_server = mock.patch.object( + openstack_utils, 'attach_volume_to_server') + self.mock_attach_volume_to_server = ( + self._mock_attach_volume_to_server.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(attach_volume, 'LOG') + self.mock_log = self._mock_log.start() + _uuid = uuidutils.generate_uuid() + self.args = {'options': {'server_name_or_id': _uuid, + 'volume_name_or_id': _uuid}} + self.result = {} + self.addCleanup(self._stop_mock) + self.attachvol_obj = attach_volume.AttachVolume(self.args, mock.ANY) + + def _stop_mock(self): + self._mock_attach_volume_to_server.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_attach_volume_to_server.return_value = True + self.assertIsNone(self.attachvol_obj.run(self.result)) + self.assertEqual({'attach_volume': 1}, self.result) + self.mock_log.info.asset_called_once_with( + 'Attach volume to server successful!') + + def test_run_fail(self): + self.mock_attach_volume_to_server.return_value = False + with self.assertRaises(exceptions.ScenarioAttachVolumeError): + self.attachvol_obj.run(self.result) + self.assertEqual({'attach_volume': 0}, self.result) + self.mock_log.error.assert_called_once_with( + 'Attach volume to server failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_check_connectivity.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_connectivity.py new file mode 100644 index 000000000..a48353a4f --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_connectivity.py @@ -0,0 +1,78 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.lib.check_connectivity.CheckConnectivity + +from __future__ import absolute_import + +import mock +import unittest + +from yardstick.benchmark.scenarios.lib import check_connectivity + + +class CheckConnectivityTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'root', + 'key_filename': 'mykey.key', + 'ssh_port': '22' + }, + 'target': { + 'ipaddr': '172.16.0.138' + } + } + + @mock.patch('yardstick.benchmark.scenarios.lib.check_connectivity.ssh') + def test_check_connectivity(self, mock_ssh): + + args = { + 'options': {'src_ip_addr': '192.168.23.2', + 'dest_ip_addr': '192.168.23.10', + 'ssh_user': 'root', + 'ssh_passwd': 'root', + 'ssh_port': '22', + 'ssh_timeout': 600, + 'ping_parameter': "-s 2048" + }, + 'sla': {'status': 'True', + 'action': 'assert'} + } + + # TODO(elfoley): Properly check the outputs + result = {} # pylint: disable=unused-variable + + obj = check_connectivity.CheckConnectivity(args, {}) + obj.setup() + mock_ssh.SSH.execute.return_value = (0, '100', '') + + @mock.patch('yardstick.benchmark.scenarios.lib.check_connectivity.ssh') + def test_check_connectivity_key(self, mock_ssh): + + args = { + 'options': {'ssh_user': 'root', + 'ssh_key': '/root/.ssh/id_rsa', + 'ssh_port': '22', + 'ssh_timeout': 600, + 'ping_parameter': "-s 2048" + }, + 'sla': {'status': 'True', + 'action': 'assert'} + } + + # TODO(elfoley): Properly check the outputs + result = {} # pylint: disable=unused-variable + + obj = check_connectivity.CheckConnectivity(args, self.ctx) + obj.setup() + + mock_ssh.SSH.execute.return_value = (0, '100', '') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_check_numa_info.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_numa_info.py new file mode 100644 index 000000000..270c9d3c9 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_numa_info.py @@ -0,0 +1,76 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import mock +import unittest + +from yardstick.benchmark.scenarios.lib.check_numa_info import CheckNumaInfo + + +class CheckNumaInfoTestCase(unittest.TestCase): + + @mock.patch.object(CheckNumaInfo, '_check_vm2_status') + def test_run(self, mock_check_vm2): + scenario_cfg = {'info1': {}, 'info2': {}} + obj = CheckNumaInfo(scenario_cfg, {}) + obj.run({}) + mock_check_vm2.assert_called_once() + + def test_check_vm2_status_length_eq_1(self): + info1 = { + 'pinning': [0], + 'vcpupin': [{ + 'cpuset': '1,2' + }] + } + info2 = { + 'pinning': [0], + 'vcpupin': [{ + 'cpuset': '1,2' + }] + } + scenario_cfg = {'info1': info1, 'info2': info2} + obj = CheckNumaInfo(scenario_cfg, {}) + status = obj._check_vm2_status(info1, info2) + self.assertTrue(status) + + def test_check_vm2_status_length_gt_1(self): + info1 = { + 'pinning': [0, 1], + 'vcpupin': [{ + 'cpuset': '1,2' + }] + } + info2 = { + 'pinning': [0, 1], + 'vcpupin': [{ + 'cpuset': '1,2' + }] + } + scenario_cfg = {'info1': info1, 'info2': info2} + obj = CheckNumaInfo(scenario_cfg, {}) + status = obj._check_vm2_status(info1, info2) + self.assertFalse(status) + + def test_check_vm2_status_length_not_in_set(self): + info1 = { + 'pinning': [0], + 'vcpupin': [{ + 'cpuset': '1,7' + }] + } + info2 = { + 'pinning': [0], + 'vcpupin': [{ + 'cpuset': '1,7' + }] + } + scenario_cfg = {'info1': info1, 'info2': info2} + obj = CheckNumaInfo(scenario_cfg, {}) + status = obj._check_vm2_status(info1, info2) + self.assertFalse(status) diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_check_value.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_value.py new file mode 100644 index 000000000..b0488bacd --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_value.py @@ -0,0 +1,63 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest + +from yardstick.benchmark.scenarios.lib import check_value +from yardstick.common import exceptions as y_exc + + +class CheckValueTestCase(unittest.TestCase): + + def test_eq_pass(self): + scenario_cfg = {'options': {'operator': 'eq', + 'value1': 1, + 'value2': 1}} + obj = check_value.CheckValue(scenario_cfg, {}) + result = obj.run({}) + + self.assertEqual({}, result) + + def test_ne_pass(self): + scenario_cfg = {'options': {'operator': 'ne', + 'value1': 1, + 'value2': 2}} + obj = check_value.CheckValue(scenario_cfg, {}) + result = obj.run({}) + + self.assertEqual({}, result) + + def test_result(self): + scenario_cfg = {'options': {'operator': 'eq', + 'value1': 1, + 'value2': 1}, + 'output': 'foo'} + obj = check_value.CheckValue(scenario_cfg, {}) + result = obj.run({}) + + self.assertDictEqual(result, {'foo': 'PASS'}) + + def test_eq(self): + scenario_cfg = {'options': {'operator': 'eq', + 'value1': 1, + 'value2': 2}} + obj = check_value.CheckValue(scenario_cfg, {}) + + with self.assertRaises(y_exc.ValueCheckError): + result = obj.run({}) + self.assertEqual({}, result) + + def test_ne(self): + scenario_cfg = {'options': {'operator': 'ne', + 'value1': 1, + 'value2': 1}} + obj = check_value.CheckValue(scenario_cfg, {}) + + with self.assertRaises(y_exc.ValueCheckError): + result = obj.run({}) + self.assertEqual({}, result) diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_flavor.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_flavor.py new file mode 100644 index 000000000..0b175fae8 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_flavor.py @@ -0,0 +1,29 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.create_flavor import CreateFlavor + + +class CreateFlavorTestCase(unittest.TestCase): + + @mock.patch('yardstick.common.openstack_utils.create_flavor') + def test_create_flavor(self, mock_create_flavor): + options = { + 'flavor_name': 'yardstick_test_flavor', + 'vcpus': '2', + 'ram': '1024', + 'disk': '100', + 'is_public': 'True' + } + args = {"options": options} + obj = CreateFlavor(args, {}) + obj.run({}) + mock_create_flavor.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_floating_ip.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_floating_ip.py new file mode 100644 index 000000000..894cc1c2a --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_floating_ip.py @@ -0,0 +1,57 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import unittest +import mock + +from yardstick.benchmark.scenarios.lib import create_floating_ip +from yardstick.common import openstack_utils +from yardstick.common import exceptions + + +class CreateFloatingIpTestCase(unittest.TestCase): + + def setUp(self): + self._mock_create_floating_ip = mock.patch.object( + openstack_utils, 'create_floating_ip') + self.mock_create_floating_ip = self._mock_create_floating_ip.start() + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_floating_ip, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'network_name_or_id': 'yardstick_net'}} + self.result = {} + + self.fip_obj = create_floating_ip.CreateFloatingIp(self.args, mock.ANY) + self.fip_obj.scenario_cfg = {'output': 'key1\nkey2'} + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_create_floating_ip.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_create_floating_ip.return_value = {'fip_id': 'value1', + 'fip_addr': 'value2'} + output = self.fip_obj.run(self.result) + self.assertEqual({'floating_ip_create': 1}, self.result) + self.assertEqual({'key1': 'value1', 'key2': 'value2'}, output) + self.mock_log.info.asset_called_once_with( + 'Creating floating ip successful!') + + def test_run_no_fip(self): + self.mock_create_floating_ip.return_value = None + with self.assertRaises(exceptions.ScenarioCreateFloatingIPError): + self.fip_obj.run(self.result) + self.assertEqual({'floating_ip_create': 0}, self.result) + self.mock_log.error.assert_called_once_with( + 'Creating floating ip failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_image.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_image.py new file mode 100644 index 000000000..aebd1dfe8 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_image.py @@ -0,0 +1,55 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +from oslo_utils import uuidutils +import unittest + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import create_image + + +class CreateImageTestCase(unittest.TestCase): + + def setUp(self): + self._mock_create_image = mock.patch.object( + openstack_utils, 'create_image') + self.mock_create_image = ( + self._mock_create_image.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_image, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'image_name': 'yardstick_image'}} + self.result = {} + self.cimage_obj = create_image.CreateImage(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_create_image.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self.cimage_obj.scenario_cfg = {'output': 'id'} + self.mock_create_image.return_value = _uuid + output = self.cimage_obj.run(self.result) + self.assertEqual({'image_create': 1}, self.result) + self.assertEqual({'id': _uuid}, output) + self.mock_log.info.asset_called_once_with('Create image successful!') + + def test_run_fail(self): + self.mock_create_image.return_value = None + with self.assertRaises(exceptions.ScenarioCreateImageError): + self.cimage_obj.run(self.result) + self.assertEqual({'image_create': 0}, self.result) + self.mock_log.error.assert_called_once_with('Create image failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_keypair.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_keypair.py new file mode 100644 index 000000000..a7b683f47 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_keypair.py @@ -0,0 +1,57 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import create_keypair + + +class CreateKeypairTestCase(unittest.TestCase): + + def setUp(self): + + self._mock_create_keypair = mock.patch.object( + openstack_utils, 'create_keypair') + self.mock_create_keypair = ( + self._mock_create_keypair.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_keypair, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'key_name': 'yardstick_key'}} + self.result = {} + + self.ckeypair_obj = create_keypair.CreateKeypair(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_create_keypair.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self.ckeypair_obj.scenario_cfg = {'output': 'id'} + self.mock_create_keypair.return_value = { + 'name': 'key-name', 'type': 'ssh', 'id': _uuid} + output = self.ckeypair_obj.run(self.result) + self.assertDictEqual({'keypair_create': 1}, self.result) + self.assertDictEqual({'id': _uuid}, output) + self.mock_log.info.asset_called_once_with('Create keypair successful!') + + def test_run_fail(self): + self.mock_create_keypair.return_value = None + with self.assertRaises(exceptions.ScenarioCreateKeypairError): + self.ckeypair_obj.run(self.result) + self.assertDictEqual({'keypair_create': 0}, self.result) + self.mock_log.error.assert_called_once_with('Create keypair failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_network.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_network.py new file mode 100644 index 000000000..17a4ef2e1 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_network.py @@ -0,0 +1,57 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import create_network + + +class CreateNetworkTestCase(unittest.TestCase): + + def setUp(self): + + self._mock_create_neutron_net = mock.patch.object( + openstack_utils, 'create_neutron_net') + self.mock_create_neutron_net = self._mock_create_neutron_net.start() + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_network, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'network_name': 'yardstick_net'}} + self.result = {} + + self._cnet_obj = create_network.CreateNetwork(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_create_neutron_net.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self._cnet_obj.scenario_cfg = {'output': 'id'} + self.mock_create_neutron_net.return_value = _uuid + output = self._cnet_obj.run(self.result) + self.assertEqual({"network_create": 1}, self.result) + self.assertEqual({'id': _uuid}, output) + self.mock_log.info.asset_called_once_with('Create network successful!') + + def test_run_fail_exception(self): + self.mock_create_neutron_net.return_value = None + with self.assertRaises(exceptions.ScenarioCreateNetworkError): + self._cnet_obj.run(self.result) + self.assertEqual({"network_create": 0}, self.result) + self.mock_log.error.assert_called_once_with( + 'Create network failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_port.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_port.py new file mode 100644 index 000000000..bea02a630 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_port.py @@ -0,0 +1,27 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.create_port import CreatePort + + +class CreatePortTestCase(unittest.TestCase): + + @mock.patch('yardstick.common.openstack_utils.get_neutron_client') + def test_create_port(self, mock_get_neutron_client): + options = { + 'openstack_paras': { + 'name': 'yardstick_port' + } + } + args = {"options": options} + obj = CreatePort(args, {}) + obj.run({}) + mock_get_neutron_client.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_router.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_router.py new file mode 100644 index 000000000..8d6f119ab --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_router.py @@ -0,0 +1,57 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import create_router + + +class CreateRouterTestCase(unittest.TestCase): + + def setUp(self): + + self._mock_create_neutron_router = mock.patch.object( + openstack_utils, 'create_neutron_router') + self.mock_create_neutron_router = ( + self._mock_create_neutron_router.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_router, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'name': 'yardstick_net'}} + self.result = {} + + self.crouter_obj = create_router.CreateRouter(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_create_neutron_router.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self.crouter_obj.scenario_cfg = {'output': 'id'} + self.mock_create_neutron_router.return_value = _uuid + output = self.crouter_obj.run(self.result) + self.assertEqual({"router_create": 1}, self.result) + self.assertEqual({'id': _uuid}, output) + self.mock_log.info.asset_called_once_with('Create router successful!') + + def test_run_fail(self): + self.mock_create_neutron_router.return_value = None + with self.assertRaises(exceptions.ScenarioCreateRouterError): + self.crouter_obj.run(self.result) + self.assertEqual({"router_create": 0}, self.result) + self.mock_log.error.assert_called_once_with('Create router failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_sec_group.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_sec_group.py new file mode 100644 index 000000000..0477a49d4 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_sec_group.py @@ -0,0 +1,59 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import create_sec_group + + +class CreateSecurityGroupTestCase(unittest.TestCase): + + def setUp(self): + + self._mock_create_security_group_full = mock.patch.object( + openstack_utils, 'create_security_group_full') + self.mock_create_security_group_full = ( + self._mock_create_security_group_full.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_sec_group, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'sg_name': 'yardstick_sg'}} + self.result = {} + + self.csecgp_obj = create_sec_group.CreateSecgroup(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_create_security_group_full.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self.csecgp_obj.scenario_cfg = {'output': 'id'} + self.mock_create_security_group_full.return_value = _uuid + output = self.csecgp_obj.run(self.result) + self.assertEqual({'sg_create': 1}, self.result) + self.assertEqual({'id': _uuid}, output) + self.mock_log.info.asset_called_once_with( + 'Create security group successful!') + + def test_run_fail(self): + self.mock_create_security_group_full.return_value = None + with self.assertRaises(exceptions.ScenarioCreateSecurityGroupError): + self.csecgp_obj.run(self.result) + self.assertEqual({'sg_create': 0}, self.result) + self.mock_log.error.assert_called_once_with( + 'Create security group failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_server.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_server.py new file mode 100644 index 000000000..b58785112 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_server.py @@ -0,0 +1,59 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import create_server + + +class CreateServerTestCase(unittest.TestCase): + + def setUp(self): + + self._mock_create_instance_and_wait_for_active = mock.patch.object( + openstack_utils, 'create_instance_and_wait_for_active') + self.mock_create_instance_and_wait_for_active = ( + self._mock_create_instance_and_wait_for_active.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_server, 'LOG') + self.mock_log = self._mock_log.start() + self.args = { + 'options': {'name': 'server-name', 'image': 'image-name', + 'flavor': 'flavor-name'}} + self.result = {} + + self.addCleanup(self._stop_mock) + self.cserver_obj = create_server.CreateServer(self.args, mock.ANY) + + def _stop_mock(self): + self._mock_create_instance_and_wait_for_active.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self.cserver_obj.scenario_cfg = {'output': 'id'} + self.mock_create_instance_and_wait_for_active.return_value = ( + {'name': 'server-name', 'flavor': 'flavor-name', 'id': _uuid}) + output = self.cserver_obj.run(self.result) + self.assertEqual({'instance_create': 1}, self.result) + self.assertEqual({'id': _uuid}, output) + self.mock_log.info.asset_called_once_with('Create server successful!') + + def test_run_fail(self): + self.mock_create_instance_and_wait_for_active.return_value = None + with self.assertRaises(exceptions.ScenarioCreateServerError): + self.cserver_obj.run(self.result) + self.assertEqual({'instance_create': 0}, self.result) + self.mock_log.error.assert_called_once_with('Create server failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_subnet.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_subnet.py new file mode 100644 index 000000000..856e985c4 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_subnet.py @@ -0,0 +1,58 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import create_subnet + + +class CreateSubnetTestCase(unittest.TestCase): + + def setUp(self): + + self._mock_create_neutron_subnet = mock.patch.object( + openstack_utils, 'create_neutron_subnet') + self.mock_create_neutron_subnet = ( + self._mock_create_neutron_subnet.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_subnet, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'network_name_or_id': 'yardstick_net'}} + self.result = {"subnet_create": 0} + + self._csubnet_obj = create_subnet.CreateSubnet(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_create_neutron_subnet.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self._csubnet_obj.scenario_cfg = {'output': 'id'} + self.mock_create_neutron_subnet.return_value = _uuid + output = self._csubnet_obj.run(self.result) + self.assertDictEqual({"subnet_create": 1}, self.result) + self.assertDictEqual({'id': _uuid}, output) + self.mock_log.info.asset_called_once_with('Create subnet successful!') + + def test_run_fail(self): + self._csubnet_obj.scenario_cfg = {'output': 'id'} + self.mock_create_neutron_subnet.return_value = None + with self.assertRaises(exceptions.ScenarioCreateSubnetError): + self._csubnet_obj.run(self.result) + self.assertDictEqual({"subnet_create": 0}, self.result) + self.mock_log.error.assert_called_once_with('Create subnet failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_volume.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_volume.py new file mode 100644 index 000000000..f91d2c3f4 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_volume.py @@ -0,0 +1,58 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import create_volume + + +class CreateVolumeTestCase(unittest.TestCase): + + def setUp(self): + + self._mock_create_volume = mock.patch.object( + openstack_utils, 'create_volume') + self.mock_create_volume = ( + self._mock_create_volume.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_volume, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'size_gb': 1}} + self.result = {} + + self.cvolume_obj = create_volume.CreateVolume(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_create_volume.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self.cvolume_obj.scenario_cfg = {'output': 'id'} + self.mock_create_volume.return_value = {'name': 'yardstick_volume', + 'id': _uuid, + 'status': 'available'} + output = self.cvolume_obj.run(self.result) + self.assertDictEqual({'volume_create': 1}, self.result) + self.assertDictEqual({'id': _uuid}, output) + self.mock_log.info.asset_called_once_with('Create volume successful!') + + def test_run_fail(self): + self.mock_create_volume.return_value = None + with self.assertRaises(exceptions.ScenarioCreateVolumeError): + self.cvolume_obj.run(self.result) + self.assertDictEqual({'volume_create': 0}, self.result) + self.mock_log.error.assert_called_once_with('Create volume failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_flavor.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_flavor.py new file mode 100644 index 000000000..24dbf8a16 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_flavor.py @@ -0,0 +1,27 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.delete_flavor import DeleteFlavor + + +class DeleteFlavorTestCase(unittest.TestCase): + + @mock.patch('yardstick.common.openstack_utils.delete_flavor') + @mock.patch('yardstick.common.openstack_utils.get_nova_client') + def test_delete_flavor(self, mock_get_nova_client, mock_delete_flavor): + options = { + 'flavor_name': 'yardstick_test_flavor' + } + args = {"options": options} + obj = DeleteFlavor(args, {}) + obj.run({}) + mock_get_nova_client.assert_called_once() + mock_delete_flavor.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_floating_ip.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_floating_ip.py new file mode 100644 index 000000000..45a39eba2 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_floating_ip.py @@ -0,0 +1,55 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import delete_floating_ip + + +class DeleteFloatingIpTestCase(unittest.TestCase): + + def setUp(self): + self._mock_delete_floating_ip = mock.patch.object( + openstack_utils, 'delete_floating_ip') + self.mock_delete_floating_ip = self._mock_delete_floating_ip.start() + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(delete_floating_ip, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'floating_ip_id': uuidutils.generate_uuid()}} + self.result = {} + + self.del_obj = delete_floating_ip.DeleteFloatingIp( + self.args, mock.ANY) + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_delete_floating_ip.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_delete_floating_ip.return_value = True + self.assertIsNone(self.del_obj.run(self.result)) + self.assertEqual({"delete_floating_ip": 1}, self.result) + self.mock_log.info.assert_called_once_with( + "Delete floating ip successful!") + + def test_run_fail(self): + self.mock_delete_floating_ip.return_value = False + with self.assertRaises(exceptions.ScenarioDeleteFloatingIPError): + self.del_obj.run(self.result) + self.assertEqual({"delete_floating_ip": 0}, self.result) + self.mock_log.error.assert_called_once_with( + "Delete floating ip failed!") diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_image.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_image.py new file mode 100644 index 000000000..8a1d6d695 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_image.py @@ -0,0 +1,52 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import delete_image + + +class DeleteImageTestCase(unittest.TestCase): + + def setUp(self): + self._mock_delete_image = mock.patch.object( + openstack_utils, 'delete_image') + self.mock_delete_image = ( + self._mock_delete_image.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(delete_image, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'name_or_id': 'yardstick_image'}} + self.result = {} + + self.delimg_obj = delete_image.DeleteImage(self.args, mock.ANY) + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_delete_image.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_delete_image.return_value = True + self.assertIsNone(self.delimg_obj.run(self.result)) + self.assertEqual({'delete_image': 1}, self.result) + self.mock_log.info.assert_called_once_with('Delete image successful!') + + def test_run_fail(self): + self.mock_delete_image.return_value = False + with self.assertRaises(exceptions.ScenarioDeleteImageError): + self.delimg_obj.run(self.result) + self.assertEqual({'delete_image': 0}, self.result) + self.mock_log.error.assert_called_once_with('Delete image failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_keypair.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_keypair.py new file mode 100644 index 000000000..c7940251e --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_keypair.py @@ -0,0 +1,51 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import delete_keypair + + +class DeleteKeypairTestCase(unittest.TestCase): + + def setUp(self): + self._mock_delete_keypair = mock.patch.object( + openstack_utils, 'delete_keypair') + self.mock_delete_keypair = self._mock_delete_keypair.start() + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(delete_keypair, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'key_name': 'yardstick_key'}} + self.result = {} + self.delkey_obj = delete_keypair.DeleteKeypair(self.args, mock.ANY) + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_delete_keypair.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_delete_keypair.return_value = True + self.assertIsNone(self.delkey_obj.run(self.result)) + self.assertEqual({'delete_keypair': 1}, self.result) + self.mock_log.info.assert_called_once_with( + 'Delete keypair successful!') + + def test_run_fail(self): + self.mock_delete_keypair.return_value = False + with self.assertRaises(exceptions.ScenarioDeleteKeypairError): + self.delkey_obj.run(self.result) + self.assertEqual({'delete_keypair': 0}, self.result) + self.mock_log.error.assert_called_once_with("Delete keypair failed!") diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_network.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_network.py new file mode 100644 index 000000000..b6dbf4791 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_network.py @@ -0,0 +1,54 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import delete_network + + +class DeleteNetworkTestCase(unittest.TestCase): + + def setUp(self): + self._mock_delete_neutron_net = mock.patch.object( + openstack_utils, "delete_neutron_net") + self.mock_delete_neutron_net = self._mock_delete_neutron_net.start() + self._mock_get_shade_client = mock.patch.object( + openstack_utils, "get_shade_client") + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(delete_network, "LOG") + self.mock_log = self._mock_log.start() + self.args = {"options": {"network_name_or_id": ( + uuidutils.generate_uuid())}} + self.result = {} + self.del_obj = delete_network.DeleteNetwork(self.args, mock.ANY) + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_delete_neutron_net.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_delete_neutron_net.return_value = True + self.assertIsNone(self.del_obj.run(self.result)) + self.assertEqual({"delete_network": 1}, self.result) + self.mock_log.info.assert_called_once_with( + "Delete network successful!") + + def test_run_fail(self): + self.mock_delete_neutron_net.return_value = False + with self.assertRaises(exceptions.ScenarioDeleteNetworkError): + self.del_obj.run(self.result) + self.assertEqual({"delete_network": 0}, self.result) + self.mock_log.error.assert_called_once_with("Delete network failed!") diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_port.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_port.py new file mode 100644 index 000000000..9fd318580 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_port.py @@ -0,0 +1,25 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.delete_port import DeletePort + + +class DeletePortTestCase(unittest.TestCase): + + @mock.patch('yardstick.common.openstack_utils.get_neutron_client') + def test_delete_port(self, mock_get_neutron_client): + options = { + 'port_id': '123-123-123' + } + args = {"options": options} + obj = DeletePort(args, {}) + obj.run({}) + mock_get_neutron_client.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router.py new file mode 100644 index 000000000..b76100f19 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router.py @@ -0,0 +1,54 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import delete_router + + +class DeleteRouterTestCase(unittest.TestCase): + + def setUp(self): + self._mock_delete_neutron_router = mock.patch.object( + openstack_utils, 'delete_neutron_router') + self.mock_delete_neutron_router = ( + self._mock_delete_neutron_router.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(delete_router, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'router_id': uuidutils.generate_uuid()}} + self.result = {"delete_router": 0} + + self._del_obj = delete_router.DeleteRouter(self.args, mock.ANY) + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_delete_neutron_router.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_delete_neutron_router.return_value = True + self.assertIsNone(self._del_obj.run(self.result)) + self.assertEqual({"delete_router": 1}, self.result) + self.mock_log.info.assert_called_once_with("Delete router successful!") + + def test_run_fail(self): + self.mock_delete_neutron_router.return_value = False + with self.assertRaises(exceptions.ScenarioDeleteRouterError): + self._del_obj.run(self.result) + self.assertEqual({"delete_router": 0}, self.result) + self.mock_log.error.assert_called_once_with("Delete router failed!") diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_gateway.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_gateway.py new file mode 100644 index 000000000..0c9cf7c17 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_gateway.py @@ -0,0 +1,27 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.delete_router_gateway import DeleteRouterGateway + + +class DeleteRouterGatewayTestCase(unittest.TestCase): + + @mock.patch('yardstick.common.openstack_utils.get_neutron_client') + @mock.patch('yardstick.common.openstack_utils.remove_gateway_router') + def test_delete_router_gateway(self, mock_get_neutron_client, mock_remove_gateway_router): + options = { + 'router_id': '123-123-123' + } + args = {"options": options} + obj = DeleteRouterGateway(args, {}) + obj.run({}) + mock_get_neutron_client.assert_called_once() + mock_remove_gateway_router.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_interface.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_interface.py new file mode 100644 index 000000000..823cb951a --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_interface.py @@ -0,0 +1,56 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.benchmark.scenarios.lib import delete_router_interface +from yardstick.common import openstack_utils +from yardstick.common import exceptions + + +class DeleteRouterInterfaceTestCase(unittest.TestCase): + + def setUp(self): + self._mock_remove_router_interface = mock.patch.object( + openstack_utils, 'remove_router_interface') + self.mock_remove_router_interface = ( + self._mock_remove_router_interface.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(delete_router_interface, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'router': uuidutils.generate_uuid()}} + self.result = {} + self.delrout_obj = delete_router_interface.DeleteRouterInterface( + self.args, mock.ANY) + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_remove_router_interface.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_remove_router_interface.return_value = True + self.assertIsNone(self.delrout_obj.run(self.result)) + self.assertEqual({"delete_router_interface": 1}, self.result) + self.mock_log.info.assert_called_once_with( + "Delete router interface successful!") + + def test_run_fail(self): + self.mock_remove_router_interface.return_value = False + with self.assertRaises(exceptions.ScenarioRemoveRouterIntError): + self.delrout_obj.run(self.result) + self.assertEqual({"delete_router_interface": 0}, self.result) + self.mock_log.error.assert_called_once_with( + "Delete router interface failed!") diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_server.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_server.py new file mode 100644 index 000000000..55fe53df8 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_server.py @@ -0,0 +1,54 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import delete_server + + +class DeleteServerTestCase(unittest.TestCase): + + def setUp(self): + self._mock_delete_instance = mock.patch.object( + openstack_utils, 'delete_instance') + self.mock_delete_instance = ( + self._mock_delete_instance.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(delete_server, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'name_or_id': uuidutils.generate_uuid() + }} + self.result = {} + + self.delserver_obj = delete_server.DeleteServer(self.args, mock.ANY) + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_delete_instance.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_delete_instance.return_value = True + self.assertIsNone(self.delserver_obj.run(self.result)) + self.assertEqual({'delete_server': 1}, self.result) + self.mock_log.info.assert_called_once_with('Delete server successful!') + + def test_run_fail(self): + self.mock_delete_instance.return_value = False + with self.assertRaises(exceptions.ScenarioDeleteServerError): + self.delserver_obj.run(self.result) + self.assertEqual({'delete_server': 0}, self.result) + self.mock_log.error.assert_called_once_with('Delete server failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_volume.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_volume.py new file mode 100644 index 000000000..0db16f396 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_volume.py @@ -0,0 +1,52 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import delete_volume + + +class DeleteVolumeTestCase(unittest.TestCase): + + def setUp(self): + self._mock_delete_volume = mock.patch.object( + openstack_utils, 'delete_volume') + self.mock_delete_volume = ( + self._mock_delete_volume.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(delete_volume, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'name_or_id': 'yardstick_volume'}} + self.result = {} + + self.delvol_obj = delete_volume.DeleteVolume(self.args, mock.ANY) + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_delete_volume.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_delete_volume.return_value = True + self.assertIsNone(self.delvol_obj.run(self.result)) + self.assertEqual({'delete_volume': 1}, self.result) + self.mock_log.info.assert_called_once_with('Delete volume successful!') + + def test_run_fail(self): + self.mock_delete_volume.return_value = False + with self.assertRaises(exceptions.ScenarioDeleteVolumeError): + self.delvol_obj.run(self.result) + self.assertEqual({'delete_volume': 0}, self.result) + self.mock_log.error.assert_called_once_with('Delete volume failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_detach_volume.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_detach_volume.py new file mode 100644 index 000000000..2bc57f495 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_detach_volume.py @@ -0,0 +1,57 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import detach_volume + + +class DetachVolumeTestCase(unittest.TestCase): + + def setUp(self): + self._mock_detach_volume = mock.patch.object( + openstack_utils, 'detach_volume') + self.mock_detach_volume = ( + self._mock_detach_volume.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(detach_volume, 'LOG') + self.mock_log = self._mock_log.start() + _uuid = uuidutils.generate_uuid() + self.args = {'options': {'server_name_or_id': _uuid, + 'volume_name_or_id': _uuid}} + self.result = {} + + self.detachvol_obj = detach_volume.DetachVolume(self.args, mock.ANY) + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_detach_volume.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_detach_volume.return_value = True + self.assertIsNone(self.detachvol_obj.run(self.result)) + self.assertEqual({'detach_volume': 1}, self.result) + self.mock_log.info.assert_called_once_with( + 'Detach volume from server successful!') + + def test_run_fail(self): + self.mock_detach_volume.return_value = False + with self.assertRaises(exceptions.ScenarioDetachVolumeError): + self.detachvol_obj.run(self.result) + self.assertEqual({'detach_volume': 0}, self.result) + self.mock_log.error.assert_called_once_with( + 'Detach volume from server failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_flavor.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_flavor.py new file mode 100644 index 000000000..1c1364348 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_flavor.py @@ -0,0 +1,57 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import get_flavor + + +class GetFlavorTestCase(unittest.TestCase): + + def setUp(self): + + self._mock_get_flavor = mock.patch.object( + openstack_utils, 'get_flavor') + self.mock_get_flavor = self._mock_get_flavor.start() + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(get_flavor, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'name_or_id': 'yardstick_flavor'}} + self.result = {} + + self.getflavor_obj = get_flavor.GetFlavor(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_get_flavor.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self.getflavor_obj.scenario_cfg = {'output': 'flavor'} + self.mock_get_flavor.return_value = ( + {'name': 'flavor-name', 'id': _uuid}) + output = self.getflavor_obj.run(self.result) + self.assertDictEqual({'get_flavor': 1}, self.result) + self.assertDictEqual({'flavor': {'name': 'flavor-name', 'id': _uuid}}, + output) + self.mock_log.info.asset_called_once_with('Get flavor successful!') + + def test_run_fail(self): + self.mock_get_flavor.return_value = None + with self.assertRaises(exceptions.ScenarioGetFlavorError): + self.getflavor_obj.run(self.result) + self.assertDictEqual({'get_flavor': 0}, self.result) + self.mock_log.error.assert_called_once_with('Get flavor failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_migrate_target_host.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_migrate_target_host.py new file mode 100644 index 000000000..879b2b988 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_migrate_target_host.py @@ -0,0 +1,43 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.get_migrate_target_host import GetMigrateTargetHost + +BASE = 'yardstick.benchmark.scenarios.lib.get_migrate_target_host' + + +class GetMigrateTargetHostTestCase(unittest.TestCase): + + @mock.patch('{}.openstack_utils.get_nova_client'.format(BASE)) + @mock.patch('{}.GetMigrateTargetHost._get_migrate_host'.format(BASE)) + @mock.patch('{}.GetMigrateTargetHost._get_current_host_name'.format(BASE)) + def test_get_migrate_target_host(self, + mock_get_current_host_name, + mock_get_migrate_host, + mock_get_nova_client): + obj = GetMigrateTargetHost({}, {}) + obj.run({}) + mock_get_nova_client.assert_called_once() + mock_get_current_host_name.assert_called_once() + mock_get_migrate_host.assert_called_once() + + @mock.patch('{}.openstack_utils.get_nova_client'.format(BASE)) + def test_get_migrate_host(self, mock_get_nova_client): + class A(object): + def __init__(self, service): + self.service = service + self.host = 'host4' + + mock_get_nova_client().hosts.list_all.return_value = [A('compute')] + obj = GetMigrateTargetHost({}, {}) + host = obj._get_migrate_host('host5') + mock_get_nova_client.assert_called() + self.assertEqual(host, 'host4') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_numa_info.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_numa_info.py new file mode 100644 index 000000000..bea978b8a --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_numa_info.py @@ -0,0 +1,103 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +import mock + +from yardstick.benchmark.scenarios.lib.get_numa_info import GetNumaInfo + + +# pylint: disable=unused-argument +# disable this for now because I keep forgetting mock patch arg ordering + + +BASE = 'yardstick.benchmark.scenarios.lib.get_numa_info' + + +class GetNumaInfoTestCase(unittest.TestCase): + + @mock.patch('{}.GetNumaInfo._check_numa_node'.format(BASE)) + @mock.patch('{}.GetNumaInfo._get_current_host_name'.format(BASE)) + @mock.patch('yardstick.benchmark.scenarios.lib.get_numa_info.yaml_load') + @mock.patch('yardstick.common.task_template.TaskTemplate.render') + def test_get_numa_info(self, + mock_render, + mock_safe_load, + mock_get_current_host_name, + mock_check_numa_node): + scenario_cfg = { + 'options': { + 'server': { + 'id': '1' + }, + 'file': 'yardstick/ssh.py' + }, + 'output': 'numa_info' + } + mock_safe_load.return_value = { + 'nodes': [] + } + obj = GetNumaInfo(scenario_cfg, {}) + obj.run({}) + mock_get_current_host_name.assert_called_once() + mock_check_numa_node.assert_called_once() + + @mock.patch('yardstick.ssh.SSH.from_node') + @mock.patch('{}.GetNumaInfo._get_current_host_name'.format(BASE)) + @mock.patch('yardstick.benchmark.scenarios.lib.get_numa_info.yaml_load') + @mock.patch('yardstick.common.task_template.TaskTemplate.render') + def test_check_numa_node(self, + mock_render, + mock_safe_load, + mock_get_current_host_name, + mock_from_node): + scenario_cfg = { + 'options': { + 'server': { + 'id': '1' + }, + 'file': 'yardstick/ssh.py' + }, + 'output': 'numa_info' + } + mock_safe_load.return_value = { + 'nodes': [] + } + data = """ + <data> + </data> + """ + mock_from_node().execute.return_value = (0, data, '') + obj = GetNumaInfo(scenario_cfg, {}) + result = obj._check_numa_node('1', 'host4') + self.assertEqual(result, {'pinning': [], 'vcpupin': []}) + + @mock.patch('{}.change_obj_to_dict'.format(BASE)) + @mock.patch('{}.get_nova_client'.format(BASE)) + @mock.patch('yardstick.benchmark.scenarios.lib.get_numa_info.yaml_load') + @mock.patch('yardstick.common.task_template.TaskTemplate.render') + def test_get_current_host_name(self, + mock_render, + mock_safe_load, + mock_get_nova_client, + mock_change_obj_to_dict): + scenario_cfg = { + 'options': { + 'server': { + 'id': '1' + }, + 'file': 'yardstick/ssh.py' + }, + 'output': 'numa_info' + } + mock_get_nova_client().servers.get.return_value = '' + mock_change_obj_to_dict.return_value = {'OS-EXT-SRV-ATTR:host': 'host5'} + + obj = GetNumaInfo(scenario_cfg, {}) + result = obj._get_current_host_name('1') + self.assertEqual(result, 'host5') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server.py new file mode 100644 index 000000000..5b5329cb0 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server.py @@ -0,0 +1,57 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +from oslo_utils import uuidutils +import unittest +import mock + +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import get_server + + +class GetServerTestCase(unittest.TestCase): + + def setUp(self): + + self._mock_get_server = mock.patch.object( + openstack_utils, 'get_server') + self.mock_get_server = self._mock_get_server.start() + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(get_server, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'name_or_id': 'yardstick_key'}} + self.result = {} + + self.getserver_obj = get_server.GetServer(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_get_server.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self.getserver_obj.scenario_cfg = {'output': 'server'} + self.mock_get_server.return_value = ( + {'name': 'server-name', 'id': _uuid}) + output = self.getserver_obj.run(self.result) + self.assertDictEqual({'get_server': 1}, self.result) + self.assertDictEqual({'server': {'name': 'server-name', 'id': _uuid}}, + output) + self.mock_log.info.asset_called_once_with('Get Server successful!') + + def test_run_fail(self): + self.mock_get_server.return_value = None + with self.assertRaises(exceptions.ScenarioGetServerError): + self.getserver_obj.run(self.result) + self.assertDictEqual({'get_server': 0}, self.result) + self.mock_log.error.assert_called_once_with('Get Server failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server_ip.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server_ip.py new file mode 100644 index 000000000..04fca16aa --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server_ip.py @@ -0,0 +1,33 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest + +from yardstick.benchmark.scenarios.lib.get_server_ip import GetServerIp + + +class GetServerIpTestCase(unittest.TestCase): + def test_get_server_ip(self): + scenario_cfg = { + 'options': { + 'server': { + 'addresses': { + 'net1': [ + { + 'OS-EXT-IPS:type': 'floating', + 'addr': '127.0.0.1' + } + ] + } + } + }, + 'output': 'ip' + } + obj = GetServerIp(scenario_cfg, {}) + result = obj.run({}) + self.assertEqual(result, {'ip': '127.0.0.1'}) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/__init__.py b/yardstick/tests/unit/benchmark/scenarios/networking/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/__init__.py diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/imix_voice.yaml b/yardstick/tests/unit/benchmark/scenarios/networking/imix_voice.yaml new file mode 100644 index 000000000..b8f8e5358 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/imix_voice.yaml @@ -0,0 +1,41 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#imix definition for voice traffic +# +# it is a typical case for testing the synthetic VNF performance. +# +#percentage of the packets can be less than 100% +#the traffic in downstream and upstream direction could be different + +schema: "nsb:imix:0.1" + +imix: + private: + imix_small: 100 #ipv4 case - 72B should be 0 ipv6 case - 84B + imix_128B: 0 + imix_256B: 0 + imix_373B: 0 + imix_570B: 0 + imix_1400B: 0 + imix_1500B: 0 + + public: + imix_small: 100 #ipv4 case - 72B ipv6 - 84B + imix_128B: 0 + imix_256B: 0 + imix_373B: 0 + imix_570B: 0 + imix_1400B: 0 + imix_1500B: 0 diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/iperf3_sample_output.json b/yardstick/tests/unit/benchmark/scenarios/networking/iperf3_sample_output.json new file mode 100644 index 000000000..b56009ba1 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/iperf3_sample_output.json @@ -0,0 +1 @@ +{"start": {"connecting_to": {"host": "172.16.0.252", "port": 5201}, "timestamp": {"timesecs": 1436254758, "time": "Tue, 07 Jul 2015 07:39:18 GMT"}, "test_start": {"protocol": "TCP", "num_streams": 1, "omit": 0, "bytes": 0, "blksize": 131072, "duration": 10, "blocks": 0, "reverse": 0}, "system_info": "Linux client 3.13.0-55-generic #94-Ubuntu SMP Thu Jun 18 00:27:10 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux\n", "version": "iperf 3.0.7", "connected": [{"local_host": "10.0.1.2", "local_port": 37633, "remote_host": "172.16.0.252", "socket": 4, "remote_port": 5201}], "cookie": "client.1436254758.606879.1fb328dc230", "tcp_mss_default": 1448}, "intervals": [{"sum": {"end": 1.00068, "seconds": 1.00068, "bytes": 16996624, "bits_per_second": 135881000.0, "start": 0, "retransmits": 0, "omitted": false}, "streams": [{"end": 1.00068, "socket": 4, "seconds": 1.00068, "bytes": 16996624, "bits_per_second": 135881000.0, "start": 0, "retransmits": 0, "omitted": false, "snd_cwnd": 451776}]}, {"sum": {"end": 2.00048, "seconds": 0.999804, "bytes": 20010192, "bits_per_second": 160113000.0, "start": 1.00068, "retransmits": 0, "omitted": false}, "streams": [{"end": 2.00048, "socket": 4, "seconds": 0.999804, "bytes": 20010192, "bits_per_second": 160113000.0, "start": 1.00068, "retransmits": 0, "omitted": false, "snd_cwnd": 713864}]}, {"sum": {"end": 3.00083, "seconds": 1.00035, "bytes": 18330464, "bits_per_second": 146592000.0, "start": 2.00048, "retransmits": 0, "omitted": false}, "streams": [{"end": 3.00083, "socket": 4, "seconds": 1.00035, "bytes": 18330464, "bits_per_second": 146592000.0, "start": 2.00048, "retransmits": 0, "omitted": false, "snd_cwnd": 768888}]}, {"sum": {"end": 4.00707, "seconds": 1.00624, "bytes": 19658376, "bits_per_second": 156292000.0, "start": 3.00083, "retransmits": 0, "omitted": false}, "streams": [{"end": 4.00707, "socket": 4, "seconds": 1.00624, "bytes": 19658376, "bits_per_second": 156292000.0, "start": 3.00083, "retransmits": 0, "omitted": false, "snd_cwnd": 812328}]}, {"sum": {"end": 5.00104, "seconds": 0.993972, "bytes": 15709072, "bits_per_second": 126435000.0, "start": 4.00707, "retransmits": 0, "omitted": false}, "streams": [{"end": 5.00104, "socket": 4, "seconds": 0.993972, "bytes": 15709072, "bits_per_second": 126435000.0, "start": 4.00707, "retransmits": 0, "omitted": false, "snd_cwnd": 849976}]}, {"sum": {"end": 6.00049, "seconds": 0.999443, "bytes": 19616288, "bits_per_second": 157018000.0, "start": 5.00104, "retransmits": 53, "omitted": false}, "streams": [{"end": 6.00049, "socket": 4, "seconds": 0.999443, "bytes": 19616288, "bits_per_second": 157018000.0, "start": 5.00104, "retransmits": 53, "omitted": false, "snd_cwnd": 641464}]}, {"sum": {"end": 7.00085, "seconds": 1.00036, "bytes": 22250480, "bits_per_second": 177939000.0, "start": 6.00049, "retransmits": 0, "omitted": false}, "streams": [{"end": 7.00085, "socket": 4, "seconds": 1.00036, "bytes": 22250480, "bits_per_second": 177939000.0, "start": 6.00049, "retransmits": 0, "omitted": false, "snd_cwnd": 706624}]}, {"sum": {"end": 8.00476, "seconds": 1.00391, "bytes": 22282240, "bits_per_second": 177564000.0, "start": 7.00085, "retransmits": 0, "omitted": false}, "streams": [{"end": 8.00476, "socket": 4, "seconds": 1.00391, "bytes": 22282240, "bits_per_second": 177564000.0, "start": 7.00085, "retransmits": 0, "omitted": false, "snd_cwnd": 761648}]}, {"sum": {"end": 9.0016, "seconds": 0.996847, "bytes": 19657680, "bits_per_second": 157759000.0, "start": 8.00476, "retransmits": 28, "omitted": false}, "streams": [{"end": 9.0016, "socket": 4, "seconds": 0.996847, "bytes": 19657680, "bits_per_second": 157759000.0, "start": 8.00476, "retransmits": 28, "omitted": false, "snd_cwnd": 570512}]}, {"sum": {"end": 10.0112, "seconds": 1.00955, "bytes": 20932520, "bits_per_second": 165876000.0, "start": 9.0016, "retransmits": 0, "omitted": false}, "streams": [{"end": 10.0112, "socket": 4, "seconds": 1.00955, "bytes": 20932520, "bits_per_second": 165876000.0, "start": 9.0016, "retransmits": 0, "omitted": false, "snd_cwnd": 615400}]}], "end": {"sum_received": {"seconds": 10.0112, "start": 0, "end": 10.0112, "bytes": 193366712, "bits_per_second": 154521000.0}, "streams": [{"sender": {"end": 10.0112, "socket": 4, "seconds": 10.0112, "bytes": 195443936, "bits_per_second": 156181000.0, "start": 0, "retransmits": 81}, "receiver": {"end": 10.0112, "socket": 4, "seconds": 10.0112, "bytes": 193366712, "bits_per_second": 154521000.0, "start": 0}}], "sum_sent": {"end": 10.0112, "seconds": 10.0112, "bytes": 195443936, "bits_per_second": 156181000.0, "start": 0, "retransmits": 81}, "cpu_utilization_percent": {"remote_user": 1.10295, "remote_system": 40.0403, "host_user": 2.41785, "remote_total": 41.1438, "host_system": 5.09548, "host_total": 7.51411}}}
\ No newline at end of file diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/iperf3_sample_output_udp.json b/yardstick/tests/unit/benchmark/scenarios/networking/iperf3_sample_output_udp.json new file mode 100644 index 000000000..8173c8f64 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/iperf3_sample_output_udp.json @@ -0,0 +1 @@ +{"start":{"connected":[{"socket":4, "local_host":"10.0.1.2", "local_port":46384, "remote_host":"172.16.9.195", "remote_port":5201}], "version":"iperf 3.0.7", "system_info":"Linux zeus 3.13.0-61-generic #100-Ubuntu SMP Wed Jul 29 11:21:34 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux\n", "timestamp":{"time":"Tue, 29 Sep 2015 01:48:23 GMT", "timesecs":1443491303}, "connecting_to":{"host":"172.16.9.195", "port":5201}, "cookie":"zeus.1443491303.539703.3479129b58a5b", "test_start":{"protocol":"UDP", "num_streams":1, "blksize":8192, "omit":0, "duration":10, "bytes":0, "blocks":0, "reverse":0}}, "intervals":[{"streams":[{"socket":4, "start":0, "end":1.00022, "seconds":1.00022, "bytes":2252800, "bits_per_second":1.80184e+07, "packets":275, "omitted":false}], "sum":{"start":0, "end":1.00022, "seconds":1.00022, "bytes":2252800, "bits_per_second":1.80184e+07, "packets":275, "omitted":false}}, {"streams":[{"socket":4, "start":1.00022, "end":2.00022, "seconds":0.999993, "bytes":2498560, "bits_per_second":1.99886e+07, "packets":305, "omitted":false}], "sum":{"start":1.00022, "end":2.00022, "seconds":0.999993, "bytes":2498560, "bits_per_second":1.99886e+07, "packets":305, "omitted":false}}, {"streams":[{"socket":4, "start":2.00022, "end":3.00022, "seconds":1, "bytes":2506752, "bits_per_second":2.0054e+07, "packets":306, "omitted":false}], "sum":{"start":2.00022, "end":3.00022, "seconds":1, "bytes":2506752, "bits_per_second":2.0054e+07, "packets":306, "omitted":false}}, {"streams":[{"socket":4, "start":3.00022, "end":4.00022, "seconds":1, "bytes":2498560, "bits_per_second":19988480, "packets":305, "omitted":false}], "sum":{"start":3.00022, "end":4.00022, "seconds":1, "bytes":2498560, "bits_per_second":19988480, "packets":305, "omitted":false}}, {"streams":[{"socket":4, "start":4.00022, "end":5.0002, "seconds":0.999977, "bytes":2498560, "bits_per_second":1.99889e+07, "packets":305, "omitted":false}], "sum":{"start":4.00022, "end":5.0002, "seconds":0.999977, "bytes":2498560, "bits_per_second":1.99889e+07, "packets":305, "omitted":false}}, {"streams":[{"socket":4, "start":5.0002, "end":6.00024, "seconds":1.00004, "bytes":2498560, "bits_per_second":1.99877e+07, "packets":305, "omitted":false}], "sum":{"start":5.0002, "end":6.00024, "seconds":1.00004, "bytes":2498560, "bits_per_second":1.99877e+07, "packets":305, "omitted":false}}, {"streams":[{"socket":4, "start":6.00024, "end":7.00023, "seconds":0.999998, "bytes":2498560, "bits_per_second":1.99885e+07, "packets":305, "omitted":false}], "sum":{"start":6.00024, "end":7.00023, "seconds":0.999998, "bytes":2498560, "bits_per_second":1.99885e+07, "packets":305, "omitted":false}}, {"streams":[{"socket":4, "start":7.00023, "end":8.00023, "seconds":0.999999, "bytes":2506752, "bits_per_second":2.0054e+07, "packets":306, "omitted":false}], "sum":{"start":7.00023, "end":8.00023, "seconds":0.999999, "bytes":2506752, "bits_per_second":2.0054e+07, "packets":306, "omitted":false}}, {"streams":[{"socket":4, "start":8.00023, "end":9.00018, "seconds":0.999945, "bytes":2498560, "bits_per_second":1.99896e+07, "packets":305, "omitted":false}], "sum":{"start":8.00023, "end":9.00018, "seconds":0.999945, "bytes":2498560, "bits_per_second":1.99896e+07, "packets":305, "omitted":false}}, {"streams":[{"socket":4, "start":9.00018, "end":10.0002, "seconds":1.00004, "bytes":2498560, "bits_per_second":1.99876e+07, "packets":305, "omitted":false}], "sum":{"start":9.00018, "end":10.0002, "seconds":1.00004, "bytes":2498560, "bits_per_second":1.99876e+07, "packets":305, "omitted":false}}], "end":{"streams":[{"udp":{"socket":4, "start":0, "end":10.0002, "seconds":10.0002, "bytes":24756224, "bits_per_second":1.98045e+07, "jitter_ms":0.0113579, "lost_packets":0, "packets":3022, "lost_percent":0}}], "sum":{"start":0, "end":10.0002, "seconds":10.0002, "bytes":24756224, "bits_per_second":1.98045e+07, "jitter_ms":0.0113579, "lost_packets":0, "packets":3022, "lost_percent":0}, "cpu_utilization_percent":{"host_total":0.647561, "host_user":0.146468, "host_system":0.501083, "remote_total":0.31751, "remote_user":0, "remote_system":0.31751}}} diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/ipv4_1flow_Packets_vpe.yaml b/yardstick/tests/unit/benchmark/scenarios/networking/ipv4_1flow_Packets_vpe.yaml new file mode 100644 index 000000000..f3046f463 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/ipv4_1flow_Packets_vpe.yaml @@ -0,0 +1,18 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +flow: + srcip4_range: '152.16.0.20' + dstip4_range: '152.40.0.20' + count: 1 diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/ipv4_throughput_vpe.yaml b/yardstick/tests/unit/benchmark/scenarios/networking/ipv4_throughput_vpe.yaml new file mode 100644 index 000000000..2123e4705 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/ipv4_throughput_vpe.yaml @@ -0,0 +1,101 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# flow definition for ACL tests - 1K flows - ipv4 only +# +# the number of flows defines the widest range of parameters +# for example if srcip_range=1.0.0.1-1.0.0.255 and dst_ip_range=10.0.0.1-10.0.1.255 +# and it should define only 16 flows +# +#there is assumption that packets generated will have a random sequences of following addresses pairs +# in the packets +# 1. src=1.x.x.x(x.x.x =random from 1..255) dst=10.x.x.x (random from 1..512) +# 2. src=1.x.x.x(x.x.x =random from 1..255) dst=10.x.x.x (random from 1..512) +# ... +# 512. src=1.x.x.x(x.x.x =random from 1..255) dst=10.x.x.x (random from 1..512) +# +# not all combination should be filled +# Any other field with random range will be added to flow definition +# +# the example.yaml provides all possibilities for traffic generation +# +# the profile defines a public and private side to make limited traffic correlation +# between private and public side same way as it is made by IXIA solution. +# +schema: "isb:traffic_profile:0.1" + +# This file is a template, it will be filled with values from tc.yaml before passing to the traffic generator + +name: rfc2544 +description: Traffic profile to run RFC2544 latency +traffic_profile: + traffic_type : RFC2544Profile # defines traffic behavior - constant or look for highest possible throughput + frame_rate : 100 # pc of linerate + # that specifies a range (e.g. ipv4 address, port) + + +private: + ipv4: + outer_l2: + framesize: + 64B: "{{ get(imix, 'imix.uplink.imix_small', '0') }}" + 128B: "{{ get(imix, 'imix.uplink.imix_128B', '0') }}" + 256B: "{{ get(imix, 'imix.uplink.imix_256B', '0') }}" + 373b: "{{ get(imix, 'imix.uplink.imix_373B', '0') }}" + 570B: "{{get(imix, 'imix.uplink.imix_570B', '0') }}" + 1400B: "{{get(imix, 'imix.uplink.imix_1400B', '0') }}" + 1518B: "{{get(imix, 'imix.uplink.imix_1500B', '0') }}" + + QinQ: + S-VLAN: + id: 128 + priority: 0 + cfi: 0 + C-VLAN: + id: 512 + priority: 0 + cfi: 0 + + outer_l3v4: + proto: "tcp" + srcip4: "{{get(flow, 'flow.srcip4_range', '192.168.0.0-192.168.255.255') }}" + dstip4: "{{get(flow, 'flow.dstip4_range', '192.16.0.0-192.16.0.31') }}" + ttl: 32 + dscp: 32 + + outer_l4: + srcport: "{{get(flow, 'flow.srcport_range', '0') }}" + dstport: "{{get(flow, 'flow.dstport_range', '0') }}" +public: + ipv4: + outer_l2: + framesize: + 64B: "{{ get(imix, 'imix.uplink.imix_small', '0') }}" + 128B: "{{ get(imix, 'imix.uplink.imix_128B', '0') }}" + 256B: "{{ get(imix, 'imix.uplink.imix_256B', '0') }}" + 373b: "{{ get(imix, 'imix.uplink.imix_373B', '0') }}" + 570B: "{{get(imix, 'imix.uplink.imix_570B', '0') }}" + 1400B: "{{get(imix, 'imix.uplink.imix_1400B', '0') }}" + 1518B: "{{get(imix, 'imix.uplink.imix_1500B', '0') }}" + + outer_l3v4: + proto: "tcp" + srcip4: "{{get(flow, 'flow.dstip4_range', '192.16.0.0-192.16.0.31') }}" + dstip4: "{{get(flow, 'flow.srcip4_range', '192.168.0.0-192.168.255.255') }}" + ttl: 32 + dscp: 32 + + outer_l4: + srcport: "{{get(flow, 'flow.dstport_range', '0') }}" + dstport: "{{get(flow, 'flow.srcport_range', '0') }}" diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/netperf_sample_output.json b/yardstick/tests/unit/benchmark/scenarios/networking/netperf_sample_output.json new file mode 100755 index 000000000..bba76cfa5 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/netperf_sample_output.json @@ -0,0 +1 @@ +{"mean_latency":"9.49","troughput":"823.77","troughput_unit":"10^6bits/s"}
\ No newline at end of file diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/netutilization_sample_output1.txt b/yardstick/tests/unit/benchmark/scenarios/networking/netutilization_sample_output1.txt new file mode 100644 index 000000000..f90457cb3 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/netutilization_sample_output1.txt @@ -0,0 +1,9 @@ +Linux 3.19.0-25-generic (huawei-pod4) 07/19/2016 _x86_64_ (1 CPU) + +02:01:50 PM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil +02:01:51 PM eth0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 +02:01:51 PM lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + +Average: IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil +Average: eth0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 +Average: lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/netutilization_sample_output2.txt b/yardstick/tests/unit/benchmark/scenarios/networking/netutilization_sample_output2.txt new file mode 100644 index 000000000..417613ec1 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/netutilization_sample_output2.txt @@ -0,0 +1,13 @@ +Linux 3.19.0-25-generic (huawei-pod4) 07/19/2016 _x86_64_ (1 CPU) + +02:01:50 PM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil +02:01:51 PM eth0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 +02:01:51 PM lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + +02:01:52 PM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil +02:01:53 PM eth0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 +02:01:53 PM lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 + +Average: IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil +Average: eth0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 +Average: lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_iperf3.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_iperf3.py new file mode 100644 index 000000000..5f342df7d --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_iperf3.py @@ -0,0 +1,190 @@ +############################################################################## +# Copyright (c) 2015 Ericsson AB and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import os +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.common import utils +from yardstick.benchmark.scenarios.networking import iperf3 +from yardstick.common import exceptions as y_exc + + +@mock.patch.object(iperf3, 'ssh') +class IperfTestCase(unittest.TestCase): + output_name_tcp = 'iperf3_sample_output.json' + output_name_udp = 'iperf3_sample_output_udp.json' + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'root', + 'key_filename': 'mykey.key' + }, + 'target': { + 'ip': '172.16.0.138', + 'user': 'root', + 'key_filename': 'mykey.key', + 'ipaddr': '172.16.0.138', + } + } + self._mock_log_info = mock.patch.object(iperf3.LOG, 'info') + self.mock_log_info = self._mock_log_info.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_log_info.stop() + + def test_iperf_successful_setup(self, mock_ssh): + p = iperf3.Iperf({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + p.setup() + self.assertIsNotNone(p.target) + self.assertIsNotNone(p.host) + mock_ssh.SSH.from_node().execute.assert_called_with("iperf3 -s -D") + + def test_iperf_unsuccessful_setup(self, mock_ssh): + p = iperf3.Iperf({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, p.setup) + + def test_iperf_successful_teardown(self, mock_ssh): + p = iperf3.Iperf({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + p.target = mock_ssh.SSH.from_node() + + p.teardown() + mock_ssh.SSH.from_node().close.assert_called() + mock_ssh.SSH.from_node().execute.assert_called_with("pkill iperf3") + + def test_iperf_successful_no_sla(self, mock_ssh): + options = {} + args = {'options': options} + result = {} + + p = iperf3.Iperf(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.output_name_tcp) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + expected_result = utils.flatten_dict_key(jsonutils.loads(sample_output)) + p.run(result) + self.assertEqual(result, expected_result) + + def test_iperf_successful_sla(self, mock_ssh): + options = {} + args = { + 'options': options, + 'sla': {'bytes_per_second': 15000000} + } + result = {} + + p = iperf3.Iperf(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.output_name_tcp) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + expected_result = utils.flatten_dict_key(jsonutils.loads(sample_output)) + p.run(result) + self.assertEqual(result, expected_result) + + def test_iperf_unsuccessful_sla(self, mock_ssh): + options = {} + args = { + 'options': options, + 'sla': {'bytes_per_second': 25000000} + } + result = {} + + p = iperf3.Iperf(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.output_name_tcp) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, p.run, result) + + def test_iperf_successful_sla_jitter(self, mock_ssh): + options = {"protocol": "udp", "bandwidth": "20m"} + args = { + 'options': options, + 'sla': {'jitter': 10} + } + result = {} + + p = iperf3.Iperf(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.output_name_udp) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + expected_result = utils.flatten_dict_key(jsonutils.loads(sample_output)) + p.run(result) + self.assertEqual(result, expected_result) + + def test_iperf_unsuccessful_sla_jitter(self, mock_ssh): + options = {"protocol": "udp", "bandwidth": "20m"} + args = { + 'options': options, + 'sla': {'jitter': 0.0001} + } + result = {} + + p = iperf3.Iperf(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.output_name_udp) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, p.run, result) + + def test_iperf_successful_tcp_protocal(self, mock_ssh): + options = {"protocol": "tcp", "nodelay": "yes"} + args = { + 'options': options, + 'sla': {'bytes_per_second': 15000000} + } + result = {} + + p = iperf3.Iperf(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.output_name_tcp) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + expected_result = utils.flatten_dict_key(jsonutils.loads(sample_output)) + p.run(result) + self.assertEqual(result, expected_result) + + def test_iperf_unsuccessful_script_error(self, mock_ssh): + options = {} + args = {'options': options} + result = {} + + p = iperf3.Iperf(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, p.run, result) + + @staticmethod + def _read_sample_output(filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + output = os.path.join(curr_path, filename) + with open(output) as f: + sample_output = f.read() + return sample_output diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_moongen_testpmd.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_moongen_testpmd.py new file mode 100644 index 000000000..620155c7e --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_moongen_testpmd.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python + +# Copyright 2017 Nokia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Unittest for yardstick.benchmark.scenarios.networking.MoongenTestPMD + +from __future__ import absolute_import +try: + from unittest import mock +except ImportError: + import mock +import unittest + +from yardstick.benchmark.scenarios.networking import moongen_testpmd + + +@mock.patch('yardstick.benchmark.scenarios.networking.moongen_testpmd.subprocess') +class MoongenTestPMDTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + "host": { + "ip": "10.229.47.137", + "user": "ubuntu", + "password": "ubuntu", + }, + } + self.TestPMDargs = { + 'task_id': "1234-5678", + 'options': { + 'multistream': 1, + 'frame_size': 1024, + 'testpmd_queue': 2, + 'trafficgen_port1': 'ens5', + 'trafficgen_port2': 'ens6', + 'moongen_host_user': 'root', + 'moongen_host_passwd': 'root', + 'moongen_host_ip': '10.5.201.151', + 'moongen_dir': '/home/lua-trafficgen', + 'moongen_runBidirec': 'true', + 'Package_Loss': 0, + 'SearchRuntime': 60, + 'moongen_port1_mac': '88:cf:98:2f:4d:ed', + 'moongen_port2_mac': '88:cf:98:2f:4d:ee', + 'forward_type': 'testpmd', + }, + 'sla': { + 'metrics': 'throughput_rx_mpps', + 'throughput_rx_mpps': 0.5, + 'action': 'monitor', + } + } + self.L2fwdargs = { + 'task_id': "1234-5678", + 'options': { + 'multistream': 1, + 'frame_size': 1024, + 'testpmd_queue': 2, + 'trafficgen_port1': 'ens5', + 'trafficgen_port2': 'ens6', + 'moongen_host_user': 'root', + 'moongen_host_passwd': 'root', + 'moongen_host_ip': '10.5.201.151', + 'moongen_dir': '/home/lua-trafficgen', + 'moongen_runBidirec': 'true', + 'Package_Loss': 0, + 'SearchRuntime': 60, + 'moongen_port1_mac': '88:cf:98:2f:4d:ed', + 'moongen_port2_mac': '88:cf:98:2f:4d:ee', + 'forward_type': 'l2fwd', + }, + 'sla': { + 'metrics': 'throughput_rx_mpps', + 'throughput_rx_mpps': 0.5, + 'action': 'monitor', + } + } + + self._mock_ssh = mock.patch( + 'yardstick.benchmark.scenarios.networking.moongen_testpmd.ssh') + self.mock_ssh = self._mock_ssh.start() + + self.addCleanup(self._cleanup) + + def _cleanup(self): + self._mock_ssh.stop() + + def test_MoongenTestPMD_setup(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + def test_MoongenTestPMD_teardown(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + p.teardown() + self.assertFalse(p.setup_done) + + def test_MoongenTestPMD_l2fwd_is_forward_setup_no(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.L2fwdargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + result = p._is_forward_setup() + self.assertFalse(result) + + def test_MoongenTestPMD_l2fwd_is_forward_setup_yes(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.L2fwdargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + + result = p._is_forward_setup() + self.assertTrue(result) + + def test_MoongenTestPMD_testpmd_is_forward_setup_no(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + + result = p._is_forward_setup() + self.assertFalse(result) + + def test_MoongenTestPMD_testpmd_is_forward_setup_yes(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + result = p._is_forward_setup() + self.assertTrue(result) + + @mock.patch('time.sleep') + def test_MoongenTestPMD_testpmd_forward_setup_first(self, _, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + + p.forward_setup() + self.assertFalse(p._is_forward_setup()) + self.assertTrue(p.forward_setup_done) + + @mock.patch('time.sleep') + def test_MoongenTestPMD_testpmd_dpdk_setup_next(self, _, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + p.forward_setup() + self.assertTrue(p._is_forward_setup()) + self.assertTrue(p.forward_setup_done) + + @mock.patch('time.sleep') + def test_MoongenTestPMD_l2fwd_forward_setup_first(self, _, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.L2fwdargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + p.forward_setup() + self.assertFalse(p._is_forward_setup()) + self.assertTrue(p.forward_setup_done) + + @mock.patch('time.sleep') + def test_MoongenTestPMD_l2fwd_dpdk_setup_next(self, _, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.L2fwdargs, self.ctx) + + # setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + p.forward_setup() + self.assertTrue(p._is_forward_setup()) + self.assertTrue(p.forward_setup_done) + + def test_moongen_testpmd_generate_config_file(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.generate_config_file(frame_size=1, multistream=1, + runBidirec="True", tg_port1_vlan=1, + tg_port2_vlan=2, SearchRuntime=1, + Package_Loss=0) + self.assertTrue(p.CONFIG_FILE) + + def test_moongen_testpmd_result_to_data_match(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + mock_subprocess.call().execute.return_value = None + result = ("[REPORT]Device 1->0: Tx frames: 420161490 Rx Frames: 420161490" + " frame loss: 0, 0.000000% Rx Mpps: 7.002708\n[REPORT] " + "total: Tx frames: 840321216 Rx Frames: 840321216 frame loss: " + "0, 0.000000% Tx Mpps: 14.005388 Rx Mpps: 14.005388\n'") + p.result_to_data(result=result) + self.assertTrue(p.TO_DATA) + + def test_moongen_testpmd_result_to_data_not_match(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + mock_subprocess.call().execute.return_value = None + result = ("") + p.result_to_data(result=result) + self.assertTrue(p.TO_DATA) + + @mock.patch('time.sleep') + def test_moongen_testpmd_run_ok(self, _, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + p.setup_done = True + p.forward_setup_done = True + p.setup() + + # run() specific mocks + p.server = self.mock_ssh.SSH.from_node() + mock_subprocess.call().execute.return_value = None + mock_subprocess.call().execute.return_value = None + result = ("[REPORT]Device 1->0: Tx frames: 420161490 Rx Frames: 420161490" + " frame loss: 0, 0.000000% Rx Mpps: 7.002708\n[REPORT] " + "total: Tx frames: 840321216 Rx Frames: 840321216 frame loss: " + "0, 0.000000% Tx Mpps: 14.005388 Rx Mpps: 14.005388\n'") + self.mock_ssh.SSH.from_node().execute.return_value = ( + 0, result, '') + + test_result = {} + p.run(test_result) + + self.assertEqual(test_result['rx_mpps'], 14.005388) + + def test_moongen_testpmd_run_falied_vsperf_execution(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # run() specific mocks + mock_subprocess.call().execute.return_value = None + mock_subprocess.call().execute.return_value = None + self.mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + result = {} + self.assertRaises(RuntimeError, p.run, result) + + def test_moongen_testpmd_run_falied_csv_report(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # run() specific mocks + mock_subprocess.call().execute.return_value = None + mock_subprocess.call().execute.return_value = None + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + self.mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + result = {} + self.assertRaises(RuntimeError, p.run, result) + +def main(): + unittest.main() + + +if __name__ == '__main__': + main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf.py new file mode 100755 index 000000000..a7abcd98a --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf.py @@ -0,0 +1,122 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.networking.netperf.Netperf + +from __future__ import absolute_import + +import os +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.networking import netperf +from yardstick.common import exceptions as y_exc + + +@mock.patch('yardstick.benchmark.scenarios.networking.netperf.ssh') +class NetperfTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'key_filename': 'mykey.key' + }, + 'target': { + 'ip': '172.16.0.138', + 'user': 'cirros', + 'key_filename': 'mykey.key', + 'ipaddr': '172.16.0.138' + } + } + + def test_netperf_successful_setup(self, mock_ssh): + + p = netperf.Netperf({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + p.setup() + self.assertIsNotNone(p.server) + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + def test_netperf_successful_no_sla(self, mock_ssh): + + options = {} + args = {'options': options} + result = {} + + p = netperf.Netperf(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output() + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + expected_result = jsonutils.loads(sample_output) + p.run(result) + self.assertEqual(result, expected_result) + + def test_netperf_successful_sla(self, mock_ssh): + + options = {} + args = { + 'options': options, + 'sla': {'mean_latency': 100} + } + result = {} + + p = netperf.Netperf(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output() + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + expected_result = jsonutils.loads(sample_output) + p.run(result) + self.assertEqual(result, expected_result) + + def test_netperf_unsuccessful_sla(self, mock_ssh): + + options = {} + args = { + 'options': options, + 'sla': {'mean_latency': 5} + } + result = {} + + p = netperf.Netperf(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output() + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, p.run, result) + + def test_netperf_unsuccessful_script_error(self, mock_ssh): + + options = {} + args = {'options': options} + result = {} + + p = netperf.Netperf(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, p.run, result) + + def _read_sample_output(self): + curr_path = os.path.dirname(os.path.abspath(__file__)) + output = os.path.join(curr_path, 'netperf_sample_output.json') + with open(output) as f: + sample_output = f.read() + return sample_output diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf_node.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf_node.py new file mode 100755 index 000000000..a577dba59 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf_node.py @@ -0,0 +1,122 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for +# yardstick.benchmark.scenarios.networking.netperf_node.NetperfNode + +from __future__ import absolute_import + +import os +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.networking import netperf_node +from yardstick.common import exceptions as y_exc + + +@mock.patch('yardstick.benchmark.scenarios.networking.netperf_node.ssh') +class NetperfNodeTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '192.168.10.10', + 'user': 'root', + 'password': 'root' + }, + 'target': { + 'ip': '192.168.10.11', + 'user': 'root', + 'password': 'root' + } + } + + def test_netperf_node_successful_setup(self, mock_ssh): + + p = netperf_node.NetperfNode({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + p.setup() + self.assertIsNotNone(p.server) + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + def test_netperf_node_successful_no_sla(self, mock_ssh): + + options = {} + args = {'options': options} + result = {} + + p = netperf_node.NetperfNode(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output() + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + expected_result = jsonutils.loads(sample_output) + p.run(result) + self.assertEqual(result, expected_result) + + def test_netperf_node_successful_sla(self, mock_ssh): + + options = {} + args = { + 'options': options, + 'sla': {'mean_latency': 100} + } + result = {} + + p = netperf_node.NetperfNode(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output() + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + expected_result = jsonutils.loads(sample_output) + p.run(result) + self.assertEqual(result, expected_result) + + def test_netperf_node_unsuccessful_sla(self, mock_ssh): + + options = {} + args = { + 'options': options, + 'sla': {'mean_latency': 5} + } + result = {} + + p = netperf_node.NetperfNode(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output() + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, p.run, result) + + def test_netperf_node_unsuccessful_script_error(self, mock_ssh): + + options = {} + args = {'options': options} + result = {} + + p = netperf_node.NetperfNode(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.host = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, p.run, result) + + def _read_sample_output(self): + curr_path = os.path.dirname(os.path.abspath(__file__)) + output = os.path.join(curr_path, 'netperf_sample_output.json') + with open(output) as f: + sample_output = f.read() + return sample_output diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_netutilization.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_netutilization.py new file mode 100644 index 000000000..4cdfde6b1 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_netutilization.py @@ -0,0 +1,225 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and other. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for +# yardstick.benchmark.scenarios.networking.netutilization.NetUtilization + +from __future__ import absolute_import +import mock +import unittest +import os + +from yardstick.benchmark.scenarios.networking import netutilization + + +@mock.patch('yardstick.benchmark.scenarios.networking.netutilization.ssh') +class NetUtilizationTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'key_filename': "mykey.key" + } + } + + self.result = {} + + def test_setup_success(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + args = {'options': options} + + n = netutilization.NetUtilization(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + n.setup() + self.assertIsNotNone(n.client) + self.assertTrue(n.setup_done) + + def test_execute_command_success(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + args = {'options': options} + + n = netutilization.NetUtilization(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + n.setup() + + expected_result = 'abcdefg' + mock_ssh.SSH.from_node().execute.return_value = (0, expected_result, '') + result = n._execute_command("foo") + self.assertEqual(result, expected_result) + + def test_execute_command_failed(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + args = {'options': options} + + n = netutilization.NetUtilization(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + n.setup() + + mock_ssh.SSH.from_node().execute.return_value = (127, '', 'abcdefg') + self.assertRaises(RuntimeError, n._execute_command, + "failed") + + def test_get_network_utilization_success(self, mock_ssh): + options = { + "interval": 1, + "count": 1 + } + args = {'options': options} + + n = netutilization.NetUtilization(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + n.setup() + + mpstat_output = self._read_file("netutilization_sample_output1.txt") + mock_ssh.SSH.from_node().execute.return_value = (0, mpstat_output, '') + result = n._get_network_utilization() + + expected_result = \ + {"network_utilization_maximun": { + "lo": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}, + "eth0": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}}, + "network_utilization_average": { + "lo": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}, + "eth0": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}}, + "network_utilization_minimum": { + "lo": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}, + "eth0": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}}} + + self.assertDictEqual(result, expected_result) + + def test_get_network_utilization_2_success(self, mock_ssh): + options = { + "interval": 1, + "count": 2 + } + args = {'options': options} + + n = netutilization.NetUtilization(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + n.setup() + + mpstat_output = self._read_file("netutilization_sample_output2.txt") + mock_ssh.SSH.from_node().execute.return_value = (0, mpstat_output, '') + result = n._get_network_utilization() + + expected_result = \ + {"network_utilization_maximun": { + "lo": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}, + "eth0": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}}, + "network_utilization_average": { + "lo": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}, + "eth0": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}}, + "network_utilization_minimum": { + "lo": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}, + "eth0": {"rxcmp/s": "0.00", + "%ifutil": "0.00", + "txcmp/s": "0.00", + "txkB/s": "0.00", + "rxkB/s": "0.00", + "rxpck/s": "0.00", + "txpck/s": "0.00", + "rxmcst/s": "0.00"}}} + + self.assertDictEqual(result, expected_result) + + def _read_file(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + output = os.path.join(curr_path, filename) + with open(output) as f: + sample_output = f.read() + return sample_output diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_networkcapacity.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_networkcapacity.py new file mode 100644 index 000000000..36e8c8a77 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_networkcapacity.py @@ -0,0 +1,59 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for +# yardstick.benchmark.scenarios.networking.networkcapacity.NetworkCapacity + +from __future__ import absolute_import + +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.networking import networkcapacity + +SAMPLE_OUTPUT = \ + '{"Number of connections":"308","Number of frames received": "166503"}' + + +@mock.patch('yardstick.benchmark.scenarios.networking.networkcapacity.ssh') +class NetworkCapacityTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'password': "root" + }, + } + + self.result = {} + + def test_capacity_successful_setup(self, mock_ssh): + c = networkcapacity.NetworkCapacity({}, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + c.setup() + self.assertIsNotNone(c.client) + self.assertTrue(c.setup_done) + + def test_capacity_successful(self, mock_ssh): + c = networkcapacity.NetworkCapacity({}, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, SAMPLE_OUTPUT, '') + c.run(self.result) + expected_result = jsonutils.loads(SAMPLE_OUTPUT) + self.assertEqual(self.result, expected_result) + + def test_capacity_unsuccessful_script_error(self, mock_ssh): + c = networkcapacity.NetworkCapacity({}, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, c.run, self.result) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_nstat.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_nstat.py new file mode 100644 index 000000000..b02d58437 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_nstat.py @@ -0,0 +1,105 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest + +from yardstick.benchmark.scenarios.networking import nstat + + +@mock.patch('yardstick.benchmark.scenarios.networking.nstat.ssh') +class NstatTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + "host": { + "ip": "192.168.50.28", + "user": "root", + "key_filename": "mykey.key" + } + } + + def test_nstat_successful_setup(self, mock_ssh): + + n = nstat.Nstat({}, self.ctx) + n.setup() + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + self.assertIsNotNone(n.client) + self.assertTrue(n.setup_done) + + def test_nstat_successful_no_sla(self, mock_ssh): + + options = { + "duration": 0 + } + args = { + "options": options, + } + n = nstat.Nstat(args, self.ctx) + result = {} + + sample_output = '#kernel\nIpInReceives 1837 0.0\nIpInHdrErrors 0 0.0\nIpInAddrErrors 2 0.0\nIcmpInMsgs 319 0.0\nIcmpInErrors 0 0.0\nTcpInSegs 36 0.0\nTcpInErrs 0 0.0\nUdpInDatagrams 1318 0.0\nUdpInErrors 0 0.0\n' # pylint: disable=line-too-long + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + n.run(result) + expected_result = {"TcpInErrs": 0, "UdpInDatagrams": 1318, + "Tcp_segment_error_rate": 0.0, "IpInAddrErrors": 2, + "IpInHdrErrors": 0, "IcmpInErrors": 0, "IpErrors": 2, + "TcpInSegs": 36, "IpInReceives": 1837, "IcmpInMsgs": 319, + "IP_datagram_error_rate": 0.001, "Udp_datagram_error_rate": 0.0, + "Icmp_message_error_rate": 0.0, "UdpInErrors": 0} + self.assertEqual(result, expected_result) + + def test_nstat_successful_sla(self, mock_ssh): + + options = { + "duration": 0 + } + sla = { + "IP_datagram_error_rate": 0.1 + } + args = { + "options": options, + "sla": sla + } + n = nstat.Nstat(args, self.ctx) + result = {} + + sample_output = '#kernel\nIpInReceives 1837 0.0\nIpInHdrErrors 0 0.0\nIpInAddrErrors 2 0.0\nIcmpInMsgs 319 0.0\nIcmpInErrors 0 0.0\nTcpInSegs 36 0.0\nTcpInErrs 0 0.0\nUdpInDatagrams 1318 0.0\nUdpInErrors 0 0.0\n' # pylint: disable=line-too-long + + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + n.run(result) + expected_result = {"TcpInErrs": 0, "UdpInDatagrams": 1318, + "Tcp_segment_error_rate": 0.0, "IpInAddrErrors": 2, + "IpInHdrErrors": 0, "IcmpInErrors": 0, "IpErrors": 2, + "TcpInSegs": 36, "IpInReceives": 1837, "IcmpInMsgs": 319, + "IP_datagram_error_rate": 0.001, "Udp_datagram_error_rate": 0.0, + "Icmp_message_error_rate": 0.0, "UdpInErrors": 0} + self.assertEqual(result, expected_result) + + def test_nstat_unsuccessful_cmd_error(self, mock_ssh): + + options = { + "duration": 0 + } + sla = { + "IP_datagram_error_rate": 0.1 + } + args = { + "options": options, + "sla": sla + } + n = nstat.Nstat(args, self.ctx) + result = {} + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, n.run, result) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_ping.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_ping.py new file mode 100644 index 000000000..944202658 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_ping.py @@ -0,0 +1,107 @@ +############################################################################## +# Copyright (c) 2015 Ericsson AB and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.networking.ping.Ping + +from __future__ import absolute_import +import mock +import unittest + +from yardstick.benchmark.scenarios.networking import ping +from yardstick.common import exceptions as y_exc + + +class PingTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'key_filename': "mykey.key" + }, + "target": { + "ipaddr": "10.229.17.105", + } + } + + @mock.patch('yardstick.benchmark.scenarios.networking.ping.ssh') + def test_ping_successful_no_sla(self, mock_ssh): + + args = { + 'options': {'packetsize': 200}, + 'target': 'ares.demo' + } + result = {} + + p = ping.Ping(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, '100', '') + p.run(result) + self.assertEqual(result, {'rtt.ares': 100.0}) + + @mock.patch('yardstick.benchmark.scenarios.networking.ping.ssh') + def test_ping_successful_sla(self, mock_ssh): + + args = { + 'options': {'packetsize': 200}, + 'sla': {'max_rtt': 150}, + 'target': 'ares.demo' + } + result = {} + + p = ping.Ping(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, '100', '') + p.run(result) + self.assertEqual(result, {'rtt.ares': 100.0}) + + @mock.patch('yardstick.benchmark.scenarios.networking.ping.ssh') + def test_ping_unsuccessful_sla(self, mock_ssh): + + args = { + 'options': {'packetsize': 200}, + 'sla': {'max_rtt': 50}, + 'target': 'ares.demo' + } + result = {} + + p = ping.Ping(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, '100', '') + self.assertRaises(y_exc.SLAValidationError, p.run, result) + + @mock.patch('yardstick.benchmark.scenarios.networking.ping.ssh') + def test_ping_unsuccessful_script_error(self, mock_ssh): + + args = { + 'options': {'packetsize': 200}, + 'sla': {'max_rtt': 50}, + 'target': 'ares.demo' + } + result = {} + + p = ping.Ping(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, p.run, result) + + @mock.patch('yardstick.benchmark.scenarios.networking.ping.ssh') + def test_ping_unsuccessful_no_sla(self, mock_ssh): + + args = { + 'options': {'packetsize': 200}, + 'target': 'ares.demo' + } + result = {} + + p = ping.Ping(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + self.assertRaises(y_exc.SLAValidationError, p.run, result) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_ping6.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_ping6.py new file mode 100644 index 000000000..ad5217a14 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_ping6.py @@ -0,0 +1,117 @@ +############################################################################## +# Copyright (c) 2015 Ericsson AB and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.networking.ping.Ping + +from __future__ import absolute_import +import mock +import unittest + +from yardstick.benchmark.scenarios.networking import ping6 +from yardstick.common import exceptions as y_exc + + +class PingTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'nodes': { + 'host1': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'role': "Controller", + 'key_filename': "mykey.key", + 'password': "root" + }, + 'host2': { + "ip": "172.16.0.138", + "key_filename": "/root/.ssh/id_rsa", + "role": "Compute", + "name": "node3.IPV6", + "user": "root" + }, + } + } + + def test_get_controller_node(self): + args = { + 'options': {'host': 'host1', 'packetsize': 200, 'ping_count': 5}, + 'sla': {'max_rtt': 50} + } + p = ping6.Ping6(args, self.ctx) + controller_node = p._get_controller_node(['host1', 'host2']) + self.assertEqual(controller_node, 'host1') + + @mock.patch('yardstick.benchmark.scenarios.networking.ping6.ssh') + def test_ping_successful_setup(self, mock_ssh): + args = { + 'options': {'host': 'host1', 'packetsize': 200, 'ping_count': 5}, + 'sla': {'max_rtt': 50} + } + p = ping6.Ping6(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '0', '') + p.setup() + + self.assertTrue(p.setup_done) + + @mock.patch('yardstick.benchmark.scenarios.networking.ping6.ssh') + def test_ping_successful_no_sla(self, mock_ssh): + args = { + 'options': {'host': 'host1', 'packetsize': 200, 'ping_count': 5}, + + } + result = {} + + p = ping6.Ping6(args, self.ctx) + p.client = mock_ssh.SSH.from_node() + mock_ssh.SSH.from_node().execute.side_effect = [(0, 'host1', ''), (0, 100, '')] + p.run(result) + self.assertEqual(result, {'rtt': 100.0}) + + @mock.patch('yardstick.benchmark.scenarios.networking.ping6.ssh') + def test_ping_successful_sla(self, mock_ssh): + args = { + 'options': {'host': 'host1', 'packetsize': 200, 'ping_count': 5}, + 'sla': {'max_rtt': 150} + } + result = {} + + p = ping6.Ping6(args, self.ctx) + p.client = mock_ssh.SSH.from_node() + mock_ssh.SSH.from_node().execute.side_effect = [(0, 'host1', ''), (0, 100, '')] + p.run(result) + self.assertEqual(result, {'rtt': 100.0}) + + @mock.patch('yardstick.benchmark.scenarios.networking.ping6.ssh') + def test_ping_unsuccessful_sla(self, mock_ssh): + args = { + 'options': {'host': 'host1', 'packetsize': 200, 'ping_count': 5}, + 'sla': {'max_rtt': 50} + } + result = {} + + p = ping6.Ping6(args, self.ctx) + p.client = mock_ssh.SSH.from_node() + mock_ssh.SSH.from_node().execute.side_effect = [(0, 'host1', ''), (0, 100, '')] + self.assertRaises(y_exc.SLAValidationError, p.run, result) + + @mock.patch('yardstick.benchmark.scenarios.networking.ping6.ssh') + def test_ping_unsuccessful_script_error(self, mock_ssh): + + args = { + 'options': {'host': 'host1', 'packetsize': 200, 'ping_count': 5}, + 'sla': {'max_rtt': 150} + } + result = {} + + p = ping6.Ping6(args, self.ctx) + p.client = mock_ssh.SSH.from_node() + mock_ssh.SSH.from_node().execute.side_effect = [ + (0, 'host1', ''), (1, '', 'FOOBAR')] + self.assertRaises(RuntimeError, p.run, result) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen.py new file mode 100644 index 000000000..5761e2403 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen.py @@ -0,0 +1,453 @@ +############################################################################## +# Copyright (c) 2015 Ericsson AB and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest +import logging + +from oslo_serialization import jsonutils + +from yardstick import ssh +from yardstick.benchmark.scenarios.networking import pktgen +from yardstick.common import exceptions as y_exc + + +logging.disable(logging.CRITICAL) + + +class PktgenTestCase(unittest.TestCase): + + def setUp(self): + self.context_cfg = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'root', + 'key_filename': 'mykey.key' + }, + 'target': { + 'ip': '172.16.0.138', + 'user': 'root', + 'key_filename': 'mykey.key', + 'ipaddr': '172.16.0.138' + } + } + self.scenario_cfg = { + 'options': {'packetsize': 60} + } + + self._mock_SSH = mock.patch.object(ssh, 'SSH') + self.mock_SSH = self._mock_SSH.start() + + self.mock_SSH.from_node().execute.return_value = (0, '', '') + self.mock_SSH.from_node().run.return_value = 0 + + self.addCleanup(self._stop_mock) + + self.scenario = pktgen.Pktgen(self.scenario_cfg, self.context_cfg) + self.scenario.setup() + + def _stop_mock(self): + self._mock_SSH.stop() + + def test_setup_successful(self): + self.assertIsNotNone(self.scenario.server) + self.assertIsNotNone(self.scenario.client) + self.assertTrue(self.scenario.setup_done) + + def test_iptables_setup_successful(self): + self.scenario.number_of_ports = 10 + self.scenario._iptables_setup() + + self.mock_SSH.from_node().run.assert_called_with( + "sudo iptables -F; " + "sudo iptables -A INPUT -p udp --dport 1000:%s -j DROP" + % 1010, timeout=60) + + def test_iptables_setup_unsuccessful(self): + self.scenario.number_of_ports = 10 + self.mock_SSH.from_node().run.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._iptables_setup() + + def test_iptables_get_result_successful(self): + self.scenario.number_of_ports = 10 + self.mock_SSH.from_node().execute.return_value = (0, '150000', '') + + result = self.scenario._iptables_get_result() + + self.assertEqual(result, 150000) + self.mock_SSH.from_node().execute.assert_called_with( + "sudo iptables -L INPUT -vnx |" + "awk '/dpts:1000:%s/ {{printf \"%%s\", $1}}'" + % 1010, raise_on_error=True) + + def test_iptables_get_result_unsuccessful(self): + self.scenario.number_of_ports = 10 + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._iptables_get_result() + + def test_run_successful_no_sla(self): + self.scenario._iptables_get_result = mock.Mock(return_value=149300) + sample_output = jsonutils.dumps({"packets_per_second": 9753, + "errors": 0, + "packets_sent": 149776, + "packetsize": 60, + "flows": 110, + "ppm": 3179}) + self.mock_SSH.from_node().execute.return_value = (0, sample_output, '') + + result = {} + self.scenario.run(result) + + expected_result = jsonutils.loads(sample_output) + expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 + self.assertEqual(result, expected_result) + + def test_run_successful_sla(self): + self.scenario_cfg['sla'] = {'max_ppm': 10000} + scenario = pktgen.Pktgen(self.scenario_cfg, self.context_cfg) + scenario.setup() + scenario._iptables_get_result = mock.Mock(return_value=149300) + sample_output = jsonutils.dumps({"packets_per_second": 9753, + "errors": 0, + "packets_sent": 149776, + "packetsize": 60, + "flows": 110, + "ppm": 3179}) + self.mock_SSH.from_node().execute.return_value = (0, sample_output, '') + + result = {} + scenario.run(result) + + expected_result = jsonutils.loads(sample_output) + expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 + self.assertEqual(result, expected_result) + + def test_run_unsuccessful_sla(self): + self.scenario_cfg['sla'] = {'max_ppm': 1000} + scenario = pktgen.Pktgen(self.scenario_cfg, self.context_cfg) + scenario.setup() + scenario._iptables_get_result = mock.Mock(return_value=149300) + sample_output = jsonutils.dumps({"packets_per_second": 9753, + "errors": 0, + "packets_sent": 149776, + "packetsize": 60, + "flows": 110}) + self.mock_SSH.from_node().execute.return_value = (0, sample_output, '') + + with self.assertRaises(y_exc.SLAValidationError): + scenario.run({}) + + def test_run_ssh_error_not_caught(self): + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario.run({}) + + def test_get_vnic_driver_name(self): + self.mock_SSH.from_node().execute.return_value = (0, 'ixgbevf', '') + vnic_driver_name = self.scenario._get_vnic_driver_name() + + self.assertEqual(vnic_driver_name, 'ixgbevf') + + def test_get_vnic_driver_name_unsuccessful(self): + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._get_vnic_driver_name() + + def test_get_sriov_queue_number(self): + self.mock_SSH.from_node().execute.return_value = (0, '2', '') + + self.scenario.queue_number = self.scenario._get_sriov_queue_number() + self.assertEqual(self.scenario.queue_number, 2) + + def test_get_sriov_queue_number_unsuccessful(self): + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._get_sriov_queue_number() + + def test_get_available_queue_number(self): + self.mock_SSH.from_node().execute.return_value = (0, '4', '') + + self.assertEqual(self.scenario._get_available_queue_number(), 4) + self.mock_SSH.from_node().execute.assert_called_with( + "sudo ethtool -l eth0 | grep Combined | head -1 |" + "awk '{printf $2}'", raise_on_error=True) + + def test_get_available_queue_number_unsuccessful(self): + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._get_available_queue_number() + + def test_get_usable_queue_number(self): + self.mock_SSH.from_node().execute.return_value = (0, '1', '') + + self.assertEqual(self.scenario._get_usable_queue_number(), 1) + self.mock_SSH.from_node().execute.assert_called_with( + "sudo ethtool -l eth0 | grep Combined | tail -1 |" + "awk '{printf $2}'", raise_on_error=True) + + def test_get_usable_queue_number_unsuccessful(self): + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._get_usable_queue_number() + + def test_enable_ovs_multiqueue(self): + self.scenario._get_usable_queue_number = mock.Mock(return_value=1) + self.scenario._get_available_queue_number = mock.Mock(return_value=4) + self.scenario.queue_number = self.scenario._enable_ovs_multiqueue() + + self.assertEqual(self.scenario.queue_number, 4) + self.mock_SSH.from_node().run.assert_has_calls( + (mock.call("sudo ethtool -L eth0 combined 4"), + mock.call("sudo ethtool -L eth0 combined 4"))) + + def test_enable_ovs_multiqueue_1q(self): + self.scenario._get_usable_queue_number = mock.Mock(return_value=1) + self.scenario._get_available_queue_number = mock.Mock(return_value=1) + self.scenario.queue_number = self.scenario._enable_ovs_multiqueue() + + self.assertEqual(self.scenario.queue_number, 1) + self.mock_SSH.from_node().run.assert_not_called() + + def test_enable_ovs_multiqueue_unsuccessful(self): + self.mock_SSH.from_node().run.side_effect = y_exc.SSHError + self.scenario._get_usable_queue_number = mock.Mock(return_value=1) + self.scenario._get_available_queue_number = mock.Mock(return_value=4) + + with self.assertRaises(y_exc.SSHError): + self.scenario._enable_ovs_multiqueue() + + def test_setup_irqmapping_ovs(self): + self.mock_SSH.from_node().execute.return_value = (0, '10', '') + self.scenario._setup_irqmapping_ovs(4) + + self.mock_SSH.from_node().run.assert_called_with( + "echo 8 | sudo tee /proc/irq/10/smp_affinity") + + def test_setup_irqmapping_ovs_1q(self): + self.mock_SSH.from_node().execute.return_value = (0, '10', '') + self.scenario._setup_irqmapping_ovs(1) + + self.mock_SSH.from_node().run.assert_called_with( + "echo 1 | sudo tee /proc/irq/10/smp_affinity") + + def test_setup_irqmapping_ovs_unsuccessful(self): + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._setup_irqmapping_ovs(4) + + def test_setup_irqmapping_ovs_1q_unsuccessful(self): + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._setup_irqmapping_ovs(1) + + def test_setup_irqmapping_sriov(self): + self.mock_SSH.from_node().execute.return_value = (0, '10', '') + self.scenario._setup_irqmapping_sriov(2) + + self.mock_SSH.from_node().run.assert_called_with( + "echo 2 | sudo tee /proc/irq/10/smp_affinity") + + def test_setup_irqmapping_sriov_1q(self): + self.mock_SSH.from_node().execute.return_value = (0, '10', '') + self.scenario._setup_irqmapping_sriov(1) + + self.mock_SSH.from_node().run.assert_called_with( + "echo 1 | sudo tee /proc/irq/10/smp_affinity") + + def test_setup_irqmapping_sriov_unsuccessful(self): + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._setup_irqmapping_sriov(2) + + def test_setup_irqmapping_sriov_1q_unsuccessful(self): + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._setup_irqmapping_sriov(1) + + def test_is_irqbalance_disabled(self): + self.mock_SSH.from_node().execute.return_value = (0, '', '') + + self.assertFalse(self.scenario._is_irqbalance_disabled()) + self.mock_SSH.from_node().execute.assert_called_with( + "grep ENABLED /etc/default/irqbalance", raise_on_error=True) + + def test_is_irqbalance_disabled_unsuccessful(self): + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._is_irqbalance_disabled() + + def test_disable_irqbalance(self): + self.scenario._disable_irqbalance() + + self.mock_SSH.from_node().run.assert_called_with( + "sudo service irqbalance disable") + + def test_disable_irqbalance_unsuccessful(self): + self.mock_SSH.from_node().run.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario._disable_irqbalance() + + def test_multiqueue_setup_ovs(self): + self.mock_SSH.from_node().execute.return_value = (0, '4', '') + self.scenario._is_irqbalance_disabled = mock.Mock(return_value=False) + self.scenario._get_vnic_driver_name = mock.Mock( + return_value="virtio_net") + self.scenario._get_usable_queue_number = mock.Mock(return_value=1) + self.scenario._get_available_queue_number = mock.Mock(return_value=4) + + self.scenario.multiqueue_setup() + + self.assertEqual(self.scenario.queue_number, 4) + self.assertTrue(self.scenario.multiqueue_setup_done) + + def test_multiqueue_setup_ovs_1q(self): + self.mock_SSH.from_node().execute.return_value = (0, '1', '') + self.scenario._is_irqbalance_disabled = mock.Mock(return_value=False) + self.scenario._get_vnic_driver_name = mock.Mock( + return_value="virtio_net") + self.scenario._get_usable_queue_number = mock.Mock(return_value=1) + self.scenario._get_available_queue_number = mock.Mock(return_value=1) + + self.scenario.multiqueue_setup() + + self.assertEqual(self.scenario.queue_number, 1) + self.assertTrue(self.scenario.multiqueue_setup_done) + + def test_multiqueue_setup_sriov(self): + self.mock_SSH.from_node().execute.return_value = (0, '2', '') + self.scenario._is_irqbalance_disabled = mock.Mock(return_value=False) + self.scenario._get_vnic_driver_name = mock.Mock(return_value="ixgbevf") + + self.scenario.multiqueue_setup() + + self.assertEqual(self.scenario.queue_number, 2) + self.assertTrue(self.scenario.multiqueue_setup_done) + + def test_multiqueue_setup_sriov_1q(self): + self.mock_SSH.from_node().execute.return_value = (0, '1', '') + self.scenario._is_irqbalance_disabled = mock.Mock(return_value=False) + self.scenario._get_vnic_driver_name = mock.Mock(return_value="ixgbevf") + + self.scenario.multiqueue_setup() + + self.assertEqual(self.scenario.queue_number, 1) + self.assertTrue(self.scenario.multiqueue_setup_done) + + def test_run_with_setup_done(self): + scenario_cfg = { + 'options': { + 'packetsize': 60, + 'number_of_ports': 10, + 'duration': 20, + 'multiqueue': True}, + 'sla': { + 'max_ppm': 1} + } + scenario = pktgen.Pktgen(scenario_cfg, self.context_cfg) + scenario.server = self.mock_SSH.from_node() + scenario.client = self.mock_SSH.from_node() + scenario.setup_done = True + scenario.multiqueue_setup_done = True + scenario._iptables_get_result = mock.Mock(return_value=149300) + + sample_output = jsonutils.dumps({"packets_per_second": 9753, + "errors": 0, + "packets_sent": 149300, + "flows": 110, + "ppm": 0}) + self.mock_SSH.from_node().execute.return_value = (0, sample_output, '') + + result = {} + scenario.run(result) + + expected_result = jsonutils.loads(sample_output) + expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 + self.assertEqual(result, expected_result) + + def test_run_with_ovs_multiqueque(self): + scenario_cfg = { + 'options': { + 'packetsize': 60, + 'number_of_ports': 10, + 'duration': 20, + 'multiqueue': True}, + 'sla': {'max_ppm': 1} + } + scenario = pktgen.Pktgen(scenario_cfg, self.context_cfg) + scenario.setup() + scenario._get_vnic_driver_name = mock.Mock(return_value="virtio_net") + scenario._get_usable_queue_number = mock.Mock(return_value=1) + scenario._get_available_queue_number = mock.Mock(return_value=4) + scenario._enable_ovs_multiqueue = mock.Mock(return_value=4) + scenario._setup_irqmapping_ovs = mock.Mock() + scenario._iptables_get_result = mock.Mock(return_value=149300) + + sample_output = jsonutils.dumps({"packets_per_second": 9753, + "errors": 0, + "packets_sent": 149300, + "flows": 110, + "ppm": 0}) + self.mock_SSH.from_node().execute.return_value = (0, sample_output, '') + + result = {} + scenario.run(result) + + expected_result = jsonutils.loads(sample_output) + expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 + self.assertEqual(result, expected_result) + + def test_run_with_sriov_multiqueque(self): + scenario_cfg = { + 'options': { + 'packetsize': 60, + 'number_of_ports': 10, + 'duration': 20, + 'multiqueue': True}, + 'sla': {'max_ppm': 1} + } + scenario = pktgen.Pktgen(scenario_cfg, self.context_cfg) + scenario.setup() + scenario._get_vnic_driver_name = mock.Mock(return_value="ixgbevf") + scenario._get_sriov_queue_number = mock.Mock(return_value=2) + scenario._setup_irqmapping_sriov = mock.Mock() + scenario._iptables_get_result = mock.Mock(return_value=149300) + + sample_output = jsonutils.dumps({"packets_per_second": 9753, + "errors": 0, + "packets_sent": 149300, + "flows": 110, + "ppm": 0}) + self.mock_SSH.from_node().execute.return_value = (0, sample_output, '') + + result = {} + scenario.run(result) + + expected_result = jsonutils.loads(sample_output) + expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 + self.assertEqual(result, expected_result) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk.py new file mode 100644 index 000000000..70cd8ad40 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk.py @@ -0,0 +1,136 @@ +############################################################################## +# Copyright (c) 2015 ZTE and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock +import unittest +import time +import logging + +import yardstick.common.utils as utils +from yardstick import ssh +from yardstick.benchmark.scenarios.networking import pktgen_dpdk +from yardstick.common import exceptions as y_exc + + +logging.disable(logging.CRITICAL) + + +class PktgenDPDKLatencyTestCase(unittest.TestCase): + + def setUp(self): + self.context_cfg = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'root', + 'key_filename': 'mykey.key' + }, + 'target': { + 'ip': '172.16.0.138', + 'user': 'root', + 'key_filename': 'mykey.key', + 'ipaddr': '172.16.0.138' + } + } + self.scenario_cfg = { + 'options': {'packetsize': 60} + } + + self._mock_SSH = mock.patch.object(ssh, 'SSH') + self.mock_SSH = self._mock_SSH.start() + + self._mock_time_sleep = mock.patch.object(time, 'sleep') + self.mock_time_sleep = self._mock_time_sleep.start() + + self._mock_utils_get_port_ip = mock.patch.object(utils, 'get_port_ip') + self.mock_utils_get_port_ip = self._mock_utils_get_port_ip.start() + + self._mock_utils_get_port_mac = mock.patch.object(utils, + 'get_port_mac') + self.mock_utils_get_port_mac = self._mock_utils_get_port_mac.start() + + self.mock_SSH.from_node().execute.return_value = (0, '', '') + + self.addCleanup(self._stop_mock) + + self.scenario = pktgen_dpdk.PktgenDPDKLatency(self.scenario_cfg, + self.context_cfg) + self.scenario.server = self.mock_SSH.from_node() + self.scenario.client = self.mock_SSH.from_node() + + def _stop_mock(self): + self._mock_SSH.stop() + self._mock_time_sleep.stop() + self._mock_utils_get_port_ip.stop() + self._mock_utils_get_port_mac.stop() + + def test_setup(self): + scenario = pktgen_dpdk.PktgenDPDKLatency(self.scenario_cfg, + self.context_cfg) + scenario.setup() + + self.assertIsNotNone(scenario.server) + self.assertIsNotNone(scenario.client) + self.assertTrue(scenario.setup_done) + + def test_run_get_port_ip_command(self): + self.scenario.run({}) + + self.mock_utils_get_port_ip.assert_has_calls( + [mock.call(self.scenario.server, 'ens4'), + mock.call(self.scenario.server, 'ens5')]) + + def test_get_port_mac_command(self): + self.scenario.run({}) + + self.mock_utils_get_port_mac.assert_has_calls( + [mock.call(self.scenario.server, 'ens5'), + mock.call(self.scenario.server, 'ens4'), + mock.call(self.scenario.server, 'ens5')]) + + def test_run_no_sla(self): + sample_output = '100\n110\n112\n130\n149\n150\n90\n150\n200\n162\n' + self.mock_SSH.from_node().execute.return_value = (0, sample_output, '') + + result = {} + self.scenario.run(result) + # with python 3 we get float, might be due python division changes + # AssertionError: {'avg_latency': 132.33333333333334} != { + # 'avg_latency': 132} + delta = result['avg_latency'] - 132 + self.assertLessEqual(delta, 1) + + def test_run_sla(self): + self.scenario_cfg['sla'] = {'max_latency': 100} + scenario = pktgen_dpdk.PktgenDPDKLatency(self.scenario_cfg, + self.context_cfg) + + sample_output = '100\n100\n100\n100\n100\n100\n100\n100\n100\n100\n' + self.mock_SSH.from_node().execute.return_value = (0, sample_output, '') + + result = {} + scenario.run(result) + + self.assertEqual(result, {"avg_latency": 100}) + + def test_run_sla_error(self): + self.scenario_cfg['sla'] = {'max_latency': 100} + scenario = pktgen_dpdk.PktgenDPDKLatency(self.scenario_cfg, + self.context_cfg) + + sample_output = '100\n110\n112\n130\n149\n150\n90\n150\n200\n162\n' + self.mock_SSH.from_node().execute.return_value = (0, sample_output, '') + + with self.assertRaises(y_exc.SLAValidationError): + scenario.run({}) + + def test_run_last_command_raise_on_error(self): + self.mock_SSH.from_node().execute.side_effect = y_exc.SSHError + + with self.assertRaises(y_exc.SSHError): + self.scenario.run({}) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk_throughput.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk_throughput.py new file mode 100644 index 000000000..39392e4bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk_throughput.py @@ -0,0 +1,194 @@ +############################################################################## +# Copyright (c) 2017 Nokia and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.networking.pktgen.PktgenDPDK + +from __future__ import absolute_import +import unittest + +from oslo_serialization import jsonutils +import mock + +from yardstick.benchmark.scenarios.networking import pktgen_dpdk_throughput +from yardstick.common import exceptions as y_exc + + +# pylint: disable=unused-argument +# disable this for now because I keep forgetting mock patch arg ordering + + +@mock.patch('yardstick.benchmark.scenarios.networking.pktgen_dpdk_throughput.ssh') +class PktgenDPDKTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'root', + 'key_filename': 'mykey.key' + }, + 'target': { + 'ip': '172.16.0.138', + 'user': 'root', + 'key_filename': 'mykey.key', + } + } + + self._mock_time = mock.patch( + 'yardstick.benchmark.scenarios.networking.pktgen_dpdk_throughput.time') + self.mock_time = self._mock_time.start() + + self.addCleanup(self._cleanup) + + def _cleanup(self): + self._mock_time.stop() + + def test_pktgen_dpdk_throughput_successful_setup(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen_dpdk_throughput.PktgenDPDK(args, self.ctx) + p.setup() + + mock_ssh.SSH().execute.return_value = (0, '', '') + self.assertIsNotNone(p.server) + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + def test_pktgen_dpdk_throughput_successful_no_sla(self, mock_ssh): + args = { + 'options': {'packetsize': 60, 'number_of_ports': 10}, + } + + result = {} + + p = pktgen_dpdk_throughput.PktgenDPDK(args, self.ctx) + + p.server = mock_ssh.SSH() + p.client = mock_ssh.SSH() + + mock_dpdk_result = mock.Mock() + mock_dpdk_result.return_value = 149300 + p._dpdk_get_result = mock_dpdk_result + + sample_output = '{"packets_per_second": 9753, "errors": 0, \ + "packets_sent": 149776, "flows": 110}' + mock_ssh.SSH().execute.return_value = (0, sample_output, '') + + p.run(result) + expected_result = jsonutils.loads(sample_output) + expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 + self.assertEqual(result, expected_result) + + def test_pktgen_dpdk_throughput_successful_sla(self, mock_ssh): + args = { + 'options': {'packetsize': 60, 'number_of_ports': 10}, + 'sla': {'max_ppm': 10000} + } + result = {} + + p = pktgen_dpdk_throughput.PktgenDPDK(args, self.ctx) + + p.server = mock_ssh.SSH() + p.client = mock_ssh.SSH() + + mock_dpdk_result = mock.Mock() + mock_dpdk_result.return_value = 149300 + p._dpdk_get_result = mock_dpdk_result + + sample_output = '{"packets_per_second": 9753, "errors": 0, \ + "packets_sent": 149776, "flows": 110}' + mock_ssh.SSH().execute.return_value = (0, sample_output, '') + + p.run(result) + expected_result = jsonutils.loads(sample_output) + expected_result["packets_received"] = 149300 + expected_result["packetsize"] = 60 + self.assertEqual(result, expected_result) + + def test_pktgen_dpdk_throughput_unsuccessful_sla(self, mock_ssh): + args = { + 'options': {'packetsize': 60, 'number_of_ports': 10}, + 'sla': {'max_ppm': 1000} + } + result = {} + + p = pktgen_dpdk_throughput.PktgenDPDK(args, self.ctx) + + p.server = mock_ssh.SSH() + p.client = mock_ssh.SSH() + + mock_dpdk_result = mock.Mock() + mock_dpdk_result.return_value = 149300 + p._dpdk_get_result = mock_dpdk_result + + sample_output = '{"packets_per_second": 9753, "errors": 0, \ + "packets_sent": 149776, "flows": 110}' + mock_ssh.SSH().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, p.run, result) + + def test_pktgen_dpdk_throughput_unsuccessful_script_error( + self, mock_ssh): + args = { + 'options': {'packetsize': 60, 'number_of_ports': 10}, + 'sla': {'max_ppm': 1000} + } + result = {} + + p = pktgen_dpdk_throughput.PktgenDPDK(args, self.ctx) + + p.server = mock_ssh.SSH() + p.client = mock_ssh.SSH() + + mock_ssh.SSH().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, p.run, result) + + def test_pktgen_dpdk_throughput_is_dpdk_setup(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen_dpdk_throughput.PktgenDPDK(args, self.ctx) + p.server = mock_ssh.SSH() + + mock_ssh.SSH().execute.return_value = (0, '', '') + + p._is_dpdk_setup("server") + + mock_ssh.SSH().execute.assert_called_with( + "ip a | grep eth1 2>/dev/null") + + def test_pktgen_dpdk_throughput_dpdk_setup(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen_dpdk_throughput.PktgenDPDK(args, self.ctx) + p.server = mock_ssh.SSH() + p.client = mock_ssh.SSH() + + mock_ssh.SSH().execute.return_value = (0, '', '') + + p.dpdk_setup() + + self.assertTrue(p.dpdk_setup_done) + + def test_pktgen_dpdk_throughput_dpdk_get_result(self, mock_ssh): + args = { + 'options': {'packetsize': 60}, + } + p = pktgen_dpdk_throughput.PktgenDPDK(args, self.ctx) + p.server = mock_ssh.SSH() + p.client = mock_ssh.SSH() + + mock_ssh.SSH().execute.return_value = (0, '10000', '') + + p._dpdk_get_result() + + mock_ssh.SSH().execute.assert_called_with( + "sudo /dpdk/destdir/bin/dpdk-procinfo -- --stats-reset > /dev/null 2>&1") diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_sfc.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_sfc.py new file mode 100644 index 000000000..a5e5e39dc --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_sfc.py @@ -0,0 +1,68 @@ +############################################################################## +# Copyright (c) 2015 Ericsson AB and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.networking.sfc + +from __future__ import absolute_import +import mock +import unittest + +from yardstick.benchmark.scenarios.networking import sfc + + +class SfcTestCase(unittest.TestCase): + + def setUp(self): + scenario_cfg = {} + context_cfg = { + # Used in Sfc.setup() + 'target': { + 'user': 'root', + 'password': 'opnfv', + 'ip': '127.0.0.1', + }, + + # Used in Sfc.run() + 'host': { + 'user': 'root', + 'password': 'opnfv', + 'ip': None, + } + } + + self.sfc = sfc.Sfc(scenario_cfg=scenario_cfg, context_cfg=context_cfg) + + @mock.patch('yardstick.benchmark.scenarios.networking.sfc.ssh') + @mock.patch('yardstick.benchmark.scenarios.networking.sfc.sfc_openstack') + @mock.patch('yardstick.benchmark.scenarios.networking.sfc.subprocess') + def test_run_for_success(self, mock_subprocess, mock_openstack, mock_ssh): + # Mock a successfull SSH in Sfc.setup() and Sfc.run() + mock_ssh.SSH.from_node().execute.return_value = (0, '100', '') + mock_openstack.get_an_IP.return_value = "127.0.0.1" + mock_subprocess.call.return_value = 'mocked!' + + result = {} + self.sfc.setup() + self.sfc.run(result) + self.sfc.teardown() + + @mock.patch('yardstick.benchmark.scenarios.networking.sfc.ssh') + @mock.patch('yardstick.benchmark.scenarios.networking.sfc.sfc_openstack') + @mock.patch('yardstick.benchmark.scenarios.networking.sfc.subprocess') + def test2_run_for_success(self, mock_subprocess, mock_openstack, mock_ssh): + # Mock a successfull SSH in Sfc.setup() and Sfc.run() + mock_ssh.SSH.from_node().execute.return_value = ( + 0, 'vxlan_tool.py', 'succeeded timed out') + mock_openstack.get_an_IP.return_value = "127.0.0.1" + mock_subprocess.call.return_value = 'mocked!' + + result = {} + self.sfc.setup() + self.sfc.run(result) + self.sfc.teardown() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py new file mode 100644 index 000000000..cf9a26a76 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py @@ -0,0 +1,873 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from copy import deepcopy +import os +import sys + +import mock +import unittest + +from yardstick import tests +from yardstick.common import exceptions +from yardstick.common import utils +from yardstick.network_services.collector.subscriber import Collector +from yardstick.network_services.traffic_profile import base +from yardstick.network_services.vnf_generic import vnfdgen +from yardstick.network_services.vnf_generic.vnf.base import GenericTrafficGen +from yardstick.network_services.vnf_generic.vnf.base import GenericVNF + + +stl_patch = mock.patch.dict(sys.modules, tests.STL_MOCKS) +stl_patch.start() + +if stl_patch: + from yardstick.benchmark.scenarios.networking import vnf_generic + +# pylint: disable=unused-argument +# disable this for now because I keep forgetting mock patch arg ordering + + +COMPLETE_TREX_VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + { + 'benchmark': { + 'kpi': [ + 'rx_throughput_fps', + 'tx_throughput_fps', + 'tx_throughput_mbps', + 'rx_throughput_mbps', + 'tx_throughput_pc_linerate', + 'rx_throughput_pc_linerate', + 'min_latency', + 'max_latency', + 'avg_latency', + ], + }, + 'connection-point': [ + { + 'name': 'xe0', + 'type': 'VPORT', + }, + { + 'name': 'xe1', + 'type': 'VPORT', + }, + ], + 'description': 'TRex stateless traffic generator for RFC2544', + 'id': 'TrexTrafficGen', + 'mgmt-interface': { + 'ip': '1.1.1.1', + 'password': 'berta', + 'user': 'berta', + 'vdu-id': 'trexgen-baremetal', + }, + 'name': 'trexgen', + 'short-name': 'trexgen', + 'class-name': 'TrexTrafficGen', + 'vdu': [ + { + 'description': 'TRex stateless traffic generator for RFC2544', + 'external-interface': [ + { + 'name': 'xe0', + 'virtual-interface': { + 'bandwidth': '10 Gbps', + 'dst_ip': '1.1.1.1', + 'dst_mac': '00:01:02:03:04:05', + 'local_ip': '1.1.1.2', + 'local_mac': '00:01:02:03:05:05', + 'type': 'PCI-PASSTHROUGH', + 'netmask': "255.255.255.0", + 'driver': 'i40', + 'vpci': '0000:00:10.2', + }, + 'vnfd-connection-point-ref': 'xe0', + }, + { + 'name': 'xe1', + 'virtual-interface': { + 'bandwidth': '10 Gbps', + 'dst_ip': '2.1.1.1', + 'dst_mac': '00:01:02:03:04:06', + 'local_ip': '2.1.1.2', + 'local_mac': '00:01:02:03:05:06', + 'type': 'PCI-PASSTHROUGH', + 'netmask': "255.255.255.0", + 'driver': 'i40', + 'vpci': '0000:00:10.1', + }, + 'vnfd-connection-point-ref': 'xe1', + }, + ], + 'id': 'trexgen-baremetal', + 'name': 'trexgen-baremetal', + }, + ], + }, + ], + }, +} + +IP_ADDR_SHOW = """ +28: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP \ +group default qlen 1000 + link/ether 90:e2:ba:a7:6a:c8 brd ff:ff:ff:ff:ff:ff + inet 1.1.1.1/8 brd 1.255.255.255 scope global eth1 + inet6 fe80::92e2:baff:fea7:6ac8/64 scope link + valid_lft forever preferred_lft forever +29: eth5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP \ +group default qlen 1000 + link/ether 90:e2:ba:a7:6a:c9 brd ff:ff:ff:ff:ff:ff + inet 2.1.1.1/8 brd 2.255.255.255 scope global eth5 + inet6 fe80::92e2:baff:fea7:6ac9/64 scope link tentative + valid_lft forever preferred_lft forever +""" + +SYS_CLASS_NET = """ +lrwxrwxrwx 1 root root 0 sie 10 14:16 eth1 -> \ +../../devices/pci0000:80/0000:80:02.2/0000:84:00.1/net/eth1 +lrwxrwxrwx 1 root root 0 sie 3 10:37 eth2 -> \ +../../devices/pci0000:00/0000:00:01.1/0000:84:00.2/net/eth5 +""" + +TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64, + }, +} + + +class TestNetworkServiceTestCase(unittest.TestCase): + + def setUp(self): + self.tg__0 = { + 'name': 'trafficgen_1.yardstick', + 'ip': '10.10.10.11', + 'role': 'TrafficGen', + 'user': 'root', + 'password': 'r00t', + 'interfaces': { + 'xe0': { + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.20', + 'local_mac': '00:00:00:00:00:01', + 'driver': 'i40e', + 'vpci': '0000:07:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.20', + 'local_mac': '00:00:00:00:00:02', + 'driver': 'i40e', + 'vpci': '0000:07:00.1', + 'dpdk_port_num': 1, + }, + }, + } + + self.vnf__0 = { + 'name': 'vnf.yardstick', + 'ip': '10.10.10.12', + 'host': '10.223.197.164', + 'role': 'vnf', + 'user': 'root', + 'password': 'r00t', + 'interfaces': { + 'xe0': { + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.19', + 'local_mac': '00:00:00:00:00:03', + 'driver': 'i40e', + 'vpci': '0000:07:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.19', + 'local_mac': '00:00:00:00:00:04', + 'driver': 'i40e', + 'vpci': '0000:07:00.1', + 'dpdk_port_num': 1, + }, + }, + 'routing_table': [ + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'network': '152.16.100.20', + 'if': 'xe0', + }, + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'network': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'nd_route_tbl': [ + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + } + + self.context_cfg = { + 'nodes': { + 'tg__0': self.tg__0, + 'vnf__0': self.vnf__0, + }, + 'networks': { + GenericVNF.UPLINK: { + 'vld_id': GenericVNF.UPLINK, + }, + GenericVNF.DOWNLINK: { + 'vld_id': GenericVNF.DOWNLINK, + }, + }, + } + + self.vld0 = { + 'vnfd-connection-point-ref': [ + { + 'vnfd-connection-point-ref': 'xe0', + 'member-vnf-index-ref': '1', + 'vnfd-id-ref': 'trexgen' + }, + { + 'vnfd-connection-point-ref': 'xe0', + 'member-vnf-index-ref': '2', + 'vnfd-id-ref': 'trexgen' + } + ], + 'type': 'ELAN', + 'id': GenericVNF.UPLINK, + 'name': 'tg__0 to vnf__0 link 1' + } + + self.vld1 = { + 'vnfd-connection-point-ref': [ + { + 'vnfd-connection-point-ref': 'xe1', + 'member-vnf-index-ref': '1', + 'vnfd-id-ref': 'trexgen' + }, + { + 'vnfd-connection-point-ref': 'xe1', + 'member-vnf-index-ref': '2', + 'vnfd-id-ref': 'trexgen' + } + ], + 'type': 'ELAN', + 'id': GenericVNF.DOWNLINK, + 'name': 'vnf__0 to tg__0 link 2' + } + + self.topology = { + 'id': 'trex-tg-topology', + 'short-name': 'trex-tg-topology', + 'name': 'trex-tg-topology', + 'description': 'trex-tg-topology', + 'constituent-vnfd': [ + { + 'member-vnf-index': '1', + 'VNF model': 'tg_trex_tpl.yaml', + 'vnfd-id-ref': 'tg__0', + }, + { + 'member-vnf-index': '2', + 'VNF model': 'tg_trex_tpl.yaml', + 'vnfd-id-ref': 'vnf__0', + }, + ], + 'vld': [self.vld0, self.vld1], + } + + self.scenario_cfg = { + 'task_path': "", + "topology": self._get_file_abspath("vpe_vnf_topology.yaml"), + 'task_id': 'a70bdf4a-8e67-47a3-9dc1-273c14506eb7', + 'tc': 'tc_ipv4_1Mflow_64B_packetsize', + 'traffic_profile': 'ipv4_throughput_vpe.yaml', + 'extra_args': {'arg1': 'value1', 'arg2': 'value2'}, + 'type': 'ISB', + 'tc_options': { + 'rfc2544': { + 'allowed_drop_rate': '0.8 - 1', + }, + }, + 'options': { + 'simulated_users': {'uplink': [1, 2]}, + 'page_object': {'uplink': [1, 2]}, + 'framesize': {'64B': 100} + }, + 'runner': { + 'object': 'NetworkServiceTestCase', + 'interval': 35, + 'output_filename': 'yardstick.out', + 'runner_id': 74476, + 'duration': 400, + 'type': 'Duration', + }, + 'traffic_options': { + 'flow': 'ipv4_1flow_Packets_vpe.yaml', + 'imix': 'imix_voice.yaml' + }, + 'nodes': { + 'tg__2': 'trafficgen_2.yardstick', + 'tg__0': 'trafficgen_1.yardstick', + 'vnf__0': 'vnf.yardstick', + }, + } + + self.s = vnf_generic.NetworkServiceTestCase(self.scenario_cfg, + self.context_cfg) + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + def test___init__(self): + self.assertIsNotNone(self.topology) + + def test__get_ip_flow_range_string(self): + result = '152.16.100.2-152.16.100.254' + self.assertEqual(result, self.s._get_ip_flow_range( + '152.16.100.2-152.16.100.254')) + + def test__get_ip_flow_range_no_nodes(self): + self.assertEqual('0.0.0.0', self.s._get_ip_flow_range({})) + + def test__get_ip_flow_range_no_node_data(self): + node_data = {'tg__0': 'xe0'} + self.s.context_cfg['nodes']['tg__0'] = {} + result = self.s._get_ip_flow_range(node_data) + self.assertEqual('0.0.0.2-0.0.0.254', result) + + def test__et_ip_flow_range_ipv4(self): + node_data = {'tg__0': 'xe0'} + self.s.context_cfg['nodes']['tg__0'] = { + 'interfaces': { + 'xe0': {'local_ip': '192.168.1.15', + 'netmask': '255.255.255.128'} + } + } + result = self.s._get_ip_flow_range(node_data) + self.assertEqual('192.168.1.2-192.168.1.126', result) + + def test__get_ip_flow_range_ipv4_mask_30(self): + node_data = {'tg__0': 'xe0'} + self.s.context_cfg['nodes']['tg__0'] = { + 'interfaces': { + 'xe0': {'local_ip': '192.168.1.15', 'netmask': 30} + } + } + result = self.s._get_ip_flow_range(node_data) + self.assertEqual('192.168.1.15', result) + + def test__get_ip_flow_range_ipv6(self): + node_data = {'tg__0': 'xe0'} + self.s.context_cfg['nodes']['tg__0'] = { + 'interfaces': { + 'xe0': {'local_ip': '2001::11', 'netmask': 64} + } + } + result = self.s._get_ip_flow_range(node_data) + self.assertEqual('2001::2-2001::ffff:ffff:ffff:fffe', result) + + def test___get_traffic_flow(self): + self.scenario_cfg["traffic_options"]["flow"] = \ + self._get_file_abspath("ipv4_1flow_Packets_vpe.yaml") + self.scenario_cfg['options'] = { + 'flow': { + 'src_ip': [ + { + 'tg__0': 'xe0', + }, + ], + 'dst_ip': [ + { + 'tg__0': 'xe1', + }, + ], + 'public_ip': ['1.1.1.1'], + }, + } + expected_flow = {'flow': {'dst_ip_0': '152.16.40.2-152.16.40.254', + 'public_ip_0': '1.1.1.1', + 'src_ip_0': '152.16.100.2-152.16.100.254'}} + self.assertEqual(expected_flow, self.s._get_traffic_flow()) + + def test___get_traffic_flow_error(self): + self.scenario_cfg["traffic_options"]["flow"] = \ + "ipv4_1flow_Packets_vpe.yaml1" + self.assertEqual({'flow': {}}, self.s._get_traffic_flow()) + + def test_get_vnf_imp(self): + vnfd = COMPLETE_TREX_VNFD['vnfd:vnfd-catalog']['vnfd'][0]['class-name'] + with mock.patch.dict(sys.modules, tests.STL_MOCKS): + self.assertIsNotNone(self.s.get_vnf_impl(vnfd)) + + with self.assertRaises(exceptions.IncorrectConfig) as raised: + self.s.get_vnf_impl('NonExistentClass') + + exc_str = str(raised.exception) + print(exc_str) + self.assertIn('No implementation', exc_str) + self.assertIn('found in', exc_str) + + def test_load_vnf_models_invalid(self): + self.context_cfg["nodes"]['tg__0']['VNF model'] = \ + self._get_file_abspath("tg_trex_tpl.yaml") + self.context_cfg["nodes"]['vnf__0']['VNF model'] = \ + self._get_file_abspath("tg_trex_tpl.yaml") + + vnf = mock.Mock(autospec=GenericVNF) + self.s.get_vnf_impl = mock.Mock(return_value=vnf) + + self.assertIsNotNone( + self.s.load_vnf_models(self.scenario_cfg, self.context_cfg)) + + def test_load_vnf_models_no_model(self): + vnf = mock.Mock(autospec=GenericVNF) + self.s.get_vnf_impl = mock.Mock(return_value=vnf) + + self.assertIsNotNone( + self.s.load_vnf_models(self.scenario_cfg, self.context_cfg)) + + def test_map_topology_to_infrastructure(self): + self.s.map_topology_to_infrastructure() + + nodes = self.context_cfg["nodes"] + self.assertEqual('../../vnf_descriptors/tg_rfc2544_tpl.yaml', + nodes['tg__0']['VNF model']) + self.assertEqual('../../vnf_descriptors/vpe_vnf.yaml', + nodes['vnf__0']['VNF model']) + + def test_map_topology_to_infrastructure_insufficient_nodes(self): + cfg = deepcopy(self.context_cfg) + del cfg['nodes']['vnf__0'] + + cfg_patch = mock.patch.object(self.s, 'context_cfg', cfg) + with cfg_patch: + with self.assertRaises(exceptions.IncorrectConfig): + self.s.map_topology_to_infrastructure() + + def test_map_topology_to_infrastructure_config_invalid(self): + ssh_mock = mock.Mock() + ssh_mock.execute.return_value = 0, SYS_CLASS_NET + IP_ADDR_SHOW, "" + + cfg = deepcopy(self.s.context_cfg) + + # delete all, we don't know which will come first + del cfg['nodes']['vnf__0']['interfaces']['xe0']['local_mac'] + del cfg['nodes']['vnf__0']['interfaces']['xe1']['local_mac'] + del cfg['nodes']['tg__0']['interfaces']['xe0']['local_mac'] + del cfg['nodes']['tg__0']['interfaces']['xe1']['local_mac'] + + config_patch = mock.patch.object(self.s, 'context_cfg', cfg) + with config_patch: + with self.assertRaises(exceptions.IncorrectConfig): + self.s.map_topology_to_infrastructure() + + def test__resolve_topology_invalid_config(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, "")) + ssh.from_node.return_value = ssh_mock + + # purge an important key from the data structure + for interface in self.tg__0['interfaces'].values(): + del interface['local_mac'] + + with self.assertRaises(exceptions.IncorrectConfig) as raised: + self.s._resolve_topology() + + self.assertIn('not found', str(raised.exception)) + + # restore local_mac + for index, interface in enumerate(self.tg__0['interfaces'].values()): + interface['local_mac'] = '00:00:00:00:00:{:2x}'.format(index) + + # make a connection point ref with 3 points + self.s.topology["vld"][0]['vnfd-connection-point-ref'].append( + self.s.topology["vld"][0]['vnfd-connection-point-ref'][0]) + + with self.assertRaises(exceptions.IncorrectConfig) as raised: + self.s._resolve_topology() + + self.assertIn('wrong endpoint count', str(raised.exception)) + + # make a connection point ref with 1 point + self.s.topology["vld"][0]['vnfd-connection-point-ref'] = \ + self.s.topology["vld"][0]['vnfd-connection-point-ref'][:1] + + with self.assertRaises(exceptions.IncorrectConfig) as raised: + self.s._resolve_topology() + + self.assertIn('wrong endpoint count', str(raised.exception)) + + def test_run(self): + tgen = mock.Mock(autospec=GenericTrafficGen) + tgen.traffic_finished = True + verified_dict = {"verified": True} + tgen.verify_traffic = lambda x: verified_dict + tgen.name = "tgen__1" + vnf = mock.Mock(autospec=GenericVNF) + vnf.runs_traffic = False + self.s.vnfs = [tgen, vnf] + self.s.traffic_profile = mock.Mock() + self.s.collector = mock.Mock(autospec=Collector) + self.s.collector.get_kpi = \ + mock.Mock(return_value={tgen.name: verified_dict}) + result = {} + self.s.run(result) + self.assertDictEqual(result, {tgen.name: verified_dict}) + + def test_setup(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, "")) + ssh.from_node.return_value = ssh_mock + + tgen = mock.Mock(autospec=GenericTrafficGen) + tgen.traffic_finished = True + verified_dict = {"verified": True} + tgen.verify_traffic = lambda x: verified_dict + tgen.terminate = mock.Mock(return_value=True) + tgen.name = "tgen__1" + vnf = mock.Mock(autospec=GenericVNF) + vnf.runs_traffic = False + vnf.terminate = mock.Mock(return_value=True) + self.s.vnfs = [tgen, vnf] + self.s.traffic_profile = mock.Mock() + self.s.collector = mock.Mock(autospec=Collector) + self.s.collector.get_kpi = \ + mock.Mock(return_value={tgen.name: verified_dict}) + self.s.map_topology_to_infrastructure = mock.Mock(return_value=0) + self.s.load_vnf_models = mock.Mock(return_value=self.s.vnfs) + self.s._fill_traffic_profile = \ + mock.Mock(return_value=TRAFFIC_PROFILE) + self.assertIsNone(self.s.setup()) + + def test_setup_exception(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, "")) + ssh.from_node.return_value = ssh_mock + + tgen = mock.Mock(autospec=GenericTrafficGen) + tgen.traffic_finished = True + verified_dict = {"verified": True} + tgen.verify_traffic = lambda x: verified_dict + tgen.terminate = mock.Mock(return_value=True) + tgen.name = "tgen__1" + vnf = mock.Mock(autospec=GenericVNF) + vnf.runs_traffic = False + vnf.instantiate.side_effect = RuntimeError( + "error during instantiate") + vnf.terminate = mock.Mock(return_value=True) + self.s.vnfs = [tgen, vnf] + self.s.traffic_profile = mock.Mock() + self.s.collector = mock.Mock(autospec=Collector) + self.s.collector.get_kpi = \ + mock.Mock(return_value={tgen.name: verified_dict}) + self.s.map_topology_to_infrastructure = mock.Mock(return_value=0) + self.s.load_vnf_models = mock.Mock(return_value=self.s.vnfs) + self.s._fill_traffic_profile = \ + mock.Mock(return_value=TRAFFIC_PROFILE) + with self.assertRaises(RuntimeError): + self.s.setup() + + def test__get_traffic_profile(self): + self.scenario_cfg["traffic_profile"] = \ + self._get_file_abspath("ipv4_throughput_vpe.yaml") + self.assertIsNotNone(self.s._get_traffic_profile()) + + def test__get_traffic_profile_exception(self): + with mock.patch.dict(self.scenario_cfg, {'traffic_profile': ''}): + with self.assertRaises(IOError): + self.s._get_traffic_profile() + + def test__key_list_to_dict(self): + result = self.s._key_list_to_dict("uplink", {"uplink": [1, 2]}) + self.assertEqual({"uplink_0": 1, "uplink_1": 2}, result) + + def test__get_simulated_users(self): + result = self.s._get_simulated_users() + self.assertEqual({'simulated_users': {'uplink_0': 1, 'uplink_1': 2}}, + result) + + def test__get_page_object(self): + result = self.s._get_page_object() + self.assertEqual({'page_object': {'uplink_0': 1, 'uplink_1': 2}}, + result) + + def test___get_traffic_imix_exception(self): + with mock.patch.dict(self.scenario_cfg["traffic_options"], {'imix': ''}): + self.assertEqual({'imix': {'64B': 100}}, + self.s._get_traffic_imix()) + + def test__get_ip_priority(self): + with mock.patch.dict(self.scenario_cfg["options"], + {'priority': {'raw': '0x01'}}): + self.assertEqual({'raw': '0x01'}, self.s._get_ip_priority()) + + def test__get_ip_priority_exception(self): + self.assertEqual({}, self.s._get_ip_priority()) + + @mock.patch.object(base.TrafficProfile, 'get') + @mock.patch.object(vnfdgen, 'generate_vnfd') + def test__fill_traffic_profile(self, mock_generate, mock_tprofile_get): + fake_tprofile = mock.Mock() + fake_vnfd = mock.MagicMock() + with mock.patch.object(self.s, '_get_traffic_profile', + return_value=fake_tprofile) as mock_get_tp: + mock_generate.return_value = fake_vnfd + self.s._fill_traffic_profile() + mock_get_tp.assert_called_once() + mock_generate.assert_called_once_with( + fake_tprofile, + {'downlink': {}, + 'extra_args': {'arg1': 'value1', 'arg2': 'value2'}, + 'flow': {'flow': {}}, + 'imix': {'imix': {'64B': 100}}, + 'priority': {}, + 'uplink': {}, + 'duration': 30, + 'simulated_users': { + 'simulated_users': {'uplink_0': 1, 'uplink_1': 2}}, + 'page_object': { + 'page_object': {'uplink_0': 1, 'uplink_1': 2}},} + ) + mock_tprofile_get.assert_called_once_with(fake_vnfd) + + @mock.patch.object(base.TrafficProfile, 'get') + @mock.patch.object(vnfdgen, 'generate_vnfd') + def test__fill_traffic_profile2(self, mock_generate, mock_tprofile_get): + fake_tprofile = mock.Mock() + fake_vnfd = {} + with mock.patch.object(self.s, '_get_traffic_profile', + return_value=fake_tprofile) as mock_get_tp: + mock_generate.return_value = fake_vnfd + + self.s.scenario_cfg["options"] = {"traffic_config": {"duration": 99899}} + self.s._fill_traffic_profile() + mock_get_tp.assert_called_once() + self.assertIn("traffic_profile", fake_vnfd) + self.assertIn("duration", fake_vnfd["traffic_profile"]) + self.assertEqual(99899, fake_vnfd["traffic_profile"]["duration"]) + + @mock.patch.object(utils, 'open_relative_file') + def test__get_topology(self, mock_open_path): + self.s.scenario_cfg['topology'] = 'fake_topology' + self.s.scenario_cfg['task_path'] = 'fake_path' + mock_open_path.side_effect = mock.mock_open(read_data='fake_data') + self.assertEqual('fake_data', self.s._get_topology()) + mock_open_path.assert_called_once_with('fake_topology', 'fake_path') + + @mock.patch.object(vnfdgen, 'generate_vnfd') + def test__render_topology(self, mock_generate): + fake_topology = 'fake_topology' + mock_generate.return_value = {'nsd:nsd-catalog': {'nsd': ['fake_nsd']}} + with mock.patch.object(self.s, '_get_topology', + return_value=fake_topology) as mock_get_topology: + self.s._render_topology() + mock_get_topology.assert_called_once() + + mock_generate.assert_called_once_with( + fake_topology, + {'extra_args': {'arg1': 'value1', 'arg2': 'value2'}} + ) + self.assertEqual(self.s.topology, 'fake_nsd') + + def test_teardown(self): + vnf = mock.Mock(autospec=GenericVNF) + vnf.terminate = mock.Mock(return_value=True) + vnf.name = str(vnf) + self.s.vnfs = [vnf] + self.s.traffic_profile = mock.Mock() + self.s.collector = mock.Mock(autospec=Collector) + self.s.collector.stop = \ + mock.Mock(return_value=True) + self.assertIsNone(self.s.teardown()) + + def test_teardown_exception(self): + vnf = mock.Mock(autospec=GenericVNF) + vnf.terminate = mock.Mock( + side_effect=RuntimeError("error duing terminate")) + vnf.name = str(vnf) + self.s.vnfs = [vnf] + self.s.traffic_profile = mock.Mock() + self.s.collector = mock.Mock(autospec=Collector) + self.s.collector.stop = \ + mock.Mock(return_value=True) + with self.assertRaises(RuntimeError): + self.s.teardown() + + +class TestNetworkServiceRFC2544TestCase(TestNetworkServiceTestCase): + + def setUp(self): + super(TestNetworkServiceRFC2544TestCase, self).setUp() + self.s = vnf_generic.NetworkServiceRFC2544(self.scenario_cfg, + self.context_cfg) + + def test_run(self): + tgen = mock.Mock(autospec=GenericTrafficGen) + tgen.traffic_finished = True + verified_dict = {"verified": True} + tgen.verify_traffic = lambda x: verified_dict + tgen.name = "tgen__1" + tgen.wait_on_trafic.return_value = 'COMPLETE' + vnf = mock.Mock(autospec=GenericVNF) + vnf.runs_traffic = False + self.s.vnfs = [tgen, vnf] + self.s.traffic_profile = mock.Mock() + self.s._fill_traffic_profile = mock.Mock() + self.s.collector = mock.Mock(autospec=Collector) + self.s.collector.get_kpi = mock.Mock( + return_value={tgen.name: verified_dict}) + result = mock.Mock() + self.s.run(result) + self.s._fill_traffic_profile.assert_called_once() + result.push.assert_called_once() + + def test_setup(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, "")) + ssh.from_node.return_value = ssh_mock + + tgen = mock.Mock(autospec=GenericTrafficGen) + tgen.traffic_finished = True + verified_dict = {"verified": True} + tgen.verify_traffic = lambda x: verified_dict + tgen.terminate = mock.Mock(return_value=True) + tgen.name = "tgen__1" + tgen.run_traffic.return_value = 'tg_id' + vnf = mock.Mock(autospec=GenericVNF) + vnf.runs_traffic = False + vnf.terminate = mock.Mock(return_value=True) + self.s.vnfs = [tgen, vnf] + self.s.traffic_profile = mock.Mock() + self.s.collector = mock.Mock(autospec=Collector) + self.s.collector.get_kpi = \ + mock.Mock(return_value={tgen.name: verified_dict}) + self.s.map_topology_to_infrastructure = mock.Mock(return_value=0) + self.s.load_vnf_models = mock.Mock(return_value=self.s.vnfs) + self.s.setup() + + def test_setup_exception(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, "")) + ssh.from_node.return_value = ssh_mock + + tgen = mock.Mock(autospec=GenericTrafficGen) + tgen.traffic_finished = True + verified_dict = {"verified": True} + tgen.verify_traffic = lambda x: verified_dict + tgen.terminate = mock.Mock(return_value=True) + tgen.name = "tgen__1" + vnf = mock.Mock(autospec=GenericVNF) + vnf.runs_traffic = False + vnf.instantiate.side_effect = RuntimeError( + "error during instantiate") + vnf.terminate = mock.Mock(return_value=True) + self.s.vnfs = [tgen, vnf] + self.s.traffic_profile = mock.Mock() + self.s.collector = mock.Mock(autospec=Collector) + self.s.collector.get_kpi = \ + mock.Mock(return_value={tgen.name: verified_dict}) + self.s.map_topology_to_infrastructure = mock.Mock(return_value=0) + self.s.load_vnf_models = mock.Mock(return_value=self.s.vnfs) + self.s._fill_traffic_profile = \ + mock.Mock(return_value=TRAFFIC_PROFILE) + with self.assertRaises(RuntimeError): + self.s.setup() + +class TestNetworkServiceRFC3511TestCase(TestNetworkServiceTestCase): + + def setUp(self): + super(TestNetworkServiceRFC3511TestCase, self).setUp() + self.s = vnf_generic.NetworkServiceRFC3511(self.scenario_cfg, + self.context_cfg) + + def test_run(self): + tgen = mock.Mock(autospec=GenericTrafficGen) + tgen.traffic_finished = True + verified_dict = {"verified": True} + tgen.verify_traffic = lambda x: verified_dict + tgen.name = "tgen__1" + vnf = mock.Mock(autospec=GenericVNF) + vnf.runs_traffic = False + self.s.vnfs = [tgen, vnf] + self.s.traffic_profile = mock.Mock() + self.s._fill_traffic_profile = mock.Mock() + self.s.collector = mock.Mock(autospec=Collector) + self.s.collector.get_kpi = mock.Mock() + result = mock.Mock() + self.s.run(result) + self.s._fill_traffic_profile.assert_called_once() + result.push.assert_called_once() + + def test_setup(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, "")) + ssh.from_node.return_value = ssh_mock + + tgen = mock.Mock(autospec=GenericTrafficGen) + tgen.traffic_finished = True + verified_dict = {"verified": True} + tgen.verify_traffic = lambda x: verified_dict + tgen.terminate = mock.Mock(return_value=True) + tgen.name = "tgen__1" + tgen.run_traffic.return_value = 'tg_id' + vnf = mock.Mock(autospec=GenericVNF) + vnf.runs_traffic = False + vnf.terminate = mock.Mock(return_value=True) + self.s.vnfs = [tgen, vnf] + self.s.traffic_profile = mock.Mock() + self.s.collector = mock.Mock(autospec=Collector) + self.s.collector.get_kpi = \ + mock.Mock(return_value={tgen.name: verified_dict}) + self.s.map_topology_to_infrastructure = mock.Mock(return_value=0) + self.s.load_vnf_models = mock.Mock(return_value=self.s.vnfs) + self.s.setup() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf.py new file mode 100644 index 000000000..a1c27f5fb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf.py @@ -0,0 +1,196 @@ +# Copyright 2016 Intel Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +import unittest +import subprocess +import yardstick.ssh as ssh + +from yardstick.benchmark.scenarios.networking import vsperf +from yardstick import exceptions as y_exc + + +class VsperfTestCase(unittest.TestCase): + + def setUp(self): + self.context_cfg = { + "host": { + "ip": "10.229.47.137", + "user": "ubuntu", + "password": "ubuntu", + }, + } + self.scenario_cfg = { + 'options': { + 'testname': 'p2p_rfc2544_continuous', + 'traffic_type': 'continuous', + 'frame_size': '64', + 'bidirectional': 'True', + 'iload': 100, + 'trafficgen_port1': 'eth1', + 'trafficgen_port2': 'eth3', + 'external_bridge': 'br-ex', + 'conf_file': 'vsperf-yardstick.conf', + 'setup_script': 'setup_yardstick.sh', + 'test_params': 'TRAFFICGEN_DURATION=30;', + }, + 'sla': { + 'metrics': 'throughput_rx_fps', + 'throughput_rx_fps': 500000, + 'action': 'monitor', + } + } + + self._mock_SSH = mock.patch.object(ssh, 'SSH') + self.mock_SSH = self._mock_SSH.start() + self.mock_SSH.from_node().execute.return_value = ( + 0, 'throughput_rx_fps\r\n14797660.000\r\n', '') + + self._mock_subprocess_call = mock.patch.object(subprocess, 'call') + self.mock_subprocess_call = self._mock_subprocess_call.start() + self.mock_subprocess_call.return_value = None + + self.addCleanup(self._stop_mock) + + self.scenario = vsperf.Vsperf(self.scenario_cfg, self.context_cfg) + + def _stop_mock(self): + self._mock_SSH.stop() + self._mock_subprocess_call.stop() + + def test_setup(self): + self.scenario.setup() + self.assertIsNotNone(self.scenario.client) + self.assertTrue(self.scenario.setup_done) + + def test_setup_tg_port_not_set(self): + del self.scenario_cfg['options']['trafficgen_port1'] + del self.scenario_cfg['options']['trafficgen_port2'] + scenario = vsperf.Vsperf(self.scenario_cfg, self.context_cfg) + scenario.setup() + + self.mock_subprocess_call.assert_called_once_with( + 'setup_yardstick.sh setup', shell=True) + self.assertIsNone(scenario.tg_port1) + self.assertIsNone(scenario.tg_port2) + self.assertIsNotNone(scenario.client) + self.assertTrue(scenario.setup_done) + + def test_setup_no_setup_script(self): + del self.scenario_cfg['options']['setup_script'] + scenario = vsperf.Vsperf(self.scenario_cfg, self.context_cfg) + scenario.setup() + + self.mock_subprocess_call.assert_has_calls( + (mock.call('sudo bash -c "ovs-vsctl add-port br-ex eth1"', + shell=True), + mock.call('sudo bash -c "ovs-vsctl add-port br-ex eth3"', + shell=True))) + self.assertEqual(2, self.mock_subprocess_call.call_count) + self.assertIsNone(scenario.setup_script) + self.assertIsNotNone(scenario.client) + self.assertTrue(scenario.setup_done) + + def test_run_ok(self): + self.scenario.setup() + + result = {} + self.scenario.run(result) + + self.assertEqual(result['throughput_rx_fps'], '14797660.000') + + def test_run_ok_setup_not_done(self): + result = {} + self.scenario.run(result) + + self.assertTrue(self.scenario.setup_done) + self.assertEqual(result['throughput_rx_fps'], '14797660.000') + + def test_run_ssh_command_call_counts(self): + self.scenario.run({}) + + self.assertEqual(self.mock_SSH.from_node().execute.call_count, 2) + self.mock_SSH.from_node().run.assert_called_once() + + def test_run_sla_fail(self): + self.mock_SSH.from_node().execute.return_value = ( + 0, 'throughput_rx_fps\r\n123456.000\r\n', '') + + with self.assertRaises(y_exc.SLAValidationError) as raised: + self.scenario.run({}) + + self.assertTrue('VSPERF_throughput_rx_fps(123456.000000) < ' + 'SLA_throughput_rx_fps(500000.000000)' + in str(raised.exception)) + + def test_run_sla_fail_metric_not_collected(self): + self.mock_SSH.from_node().execute.return_value = ( + 0, 'nonexisting_metric\r\n14797660.000\r\n', '') + + with self.assertRaises(y_exc.SLAValidationError) as raised: + self.scenario.run({}) + + self.assertTrue('throughput_rx_fps was not collected by VSPERF' + in str(raised.exception)) + + def test_run_faulty_result_csv(self): + self.mock_SSH.from_node().execute.return_value = ( + 0, 'faulty output not csv', '') + + with self.assertRaises(y_exc.SLAValidationError) as raised: + self.scenario.run({}) + + self.assertTrue('throughput_rx_fps was not collected by VSPERF' + in str(raised.exception)) + + def test_run_sla_fail_metric_not_defined_in_sla(self): + del self.scenario_cfg['sla']['throughput_rx_fps'] + scenario = vsperf.Vsperf(self.scenario_cfg, self.context_cfg) + scenario.setup() + + with self.assertRaises(y_exc.SLAValidationError) as raised: + scenario.run({}) + self.assertTrue('throughput_rx_fps is not defined in SLA' + in str(raised.exception)) + + def test_teardown(self): + self.scenario.setup() + self.assertIsNotNone(self.scenario.client) + self.assertTrue(self.scenario.setup_done) + + self.scenario.teardown() + self.assertFalse(self.scenario.setup_done) + + def test_teardown_tg_port_not_set(self): + del self.scenario_cfg['options']['trafficgen_port1'] + del self.scenario_cfg['options']['trafficgen_port2'] + scenario = vsperf.Vsperf(self.scenario_cfg, self.context_cfg) + scenario.teardown() + + self.mock_subprocess_call.assert_called_once_with( + 'setup_yardstick.sh teardown', shell=True) + self.assertFalse(scenario.setup_done) + + def test_teardown_no_setup_script(self): + del self.scenario_cfg['options']['setup_script'] + scenario = vsperf.Vsperf(self.scenario_cfg, self.context_cfg) + scenario.teardown() + + self.mock_subprocess_call.assert_has_calls( + (mock.call('sudo bash -c "ovs-vsctl del-port br-ex eth1"', + shell=True), + mock.call('sudo bash -c "ovs-vsctl del-port br-ex eth3"', + shell=True))) + self.assertEqual(2, self.mock_subprocess_call.call_count) + self.assertFalse(scenario.setup_done) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py new file mode 100644 index 000000000..8bbe6911e --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py @@ -0,0 +1,181 @@ +# Copyright 2017 Nokia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import subprocess +import time + +import mock +import unittest + +from yardstick import exceptions as y_exc +from yardstick.benchmark.scenarios.networking import vsperf_dpdk +from yardstick.common import exceptions as y_exc +from yardstick import ssh + + +class VsperfDPDKTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + "host": { + "ip": "10.229.47.137", + "user": "ubuntu", + "password": "ubuntu", + }, + } + self.args = { + 'task_id': "1234-5678", + 'options': { + 'testname': 'pvp_tput', + 'traffic_type': 'rfc2544_throughput', + 'frame_size': '64', + 'test_params': 'TRAFFICGEN_DURATION=30;', + 'trafficgen_port1': 'ens4', + 'trafficgen_port2': 'ens5', + 'conf_file': 'vsperf-yardstick.conf', + 'setup_script': 'setup_yardstick.sh', + 'moongen_helper_file': '~/moongen.py', + 'moongen_host_ip': '10.5.201.151', + 'moongen_port1_mac': '8c:dc:d4:ae:7c:5c', + 'moongen_port2_mac': '8c:dc:d4:ae:7c:5d', + 'trafficgen_port1_nw': 'test2', + 'trafficgen_port2_nw': 'test3', + }, + 'sla': { + 'metrics': 'throughput_rx_fps', + 'throughput_rx_fps': 500000, + 'action': 'monitor', + } + } + self._mock_ssh = mock.patch.object(ssh, 'SSH') + self.mock_ssh = self._mock_ssh.start() + self._mock_subprocess_call = mock.patch.object(subprocess, 'call') + self.mock_subprocess_call = self._mock_subprocess_call.start() + mock_call_obj = mock.Mock() + mock_call_obj.execute.return_value = None + self.mock_subprocess_call.return_value = mock_call_obj + + self._mock_log_info = mock.patch.object(vsperf_dpdk.LOG, 'info') + self.mock_log_info = self._mock_log_info.start() + + self.addCleanup(self._cleanup) + + self.scenario = vsperf_dpdk.VsperfDPDK(self.args, self.ctx) + self.scenario.setup() + + def _cleanup(self): + self._mock_ssh.stop() + self._mock_subprocess_call.stop() + self._mock_log_info.stop() + + def test_setup(self): + self.assertIsNotNone(self.scenario.client) + self.assertTrue(self.scenario.setup_done) + + def test_teardown(self): + self.scenario.teardown() + self.assertFalse(self.scenario.setup_done) + + def test_is_dpdk_setup_no(self): + # is_dpdk_setup() specific mocks + self.mock_ssh.from_node().execute.return_value = (0, 'dummy', '') + + self.assertFalse(self.scenario._is_dpdk_setup()) + + def test_is_dpdk_setup_yes(self): + # is_dpdk_setup() specific mocks + self.mock_ssh.from_node().execute.return_value = (0, '', '') + + self.assertTrue(self.scenario._is_dpdk_setup()) + + @mock.patch.object(time, 'sleep') + def test_dpdk_setup_first(self, *args): + # is_dpdk_setup() specific mocks + self.mock_ssh.from_node().execute.return_value = (0, 'dummy', '') + + self.scenario.dpdk_setup() + self.assertFalse(self.scenario._is_dpdk_setup()) + self.assertTrue(self.scenario.dpdk_setup_done) + + @mock.patch.object(time, 'sleep') + def test_dpdk_setup_next(self, *args): + self.mock_ssh.from_node().execute.return_value = (0, '', '') + + self.scenario.dpdk_setup() + self.assertTrue(self.scenario._is_dpdk_setup()) + self.assertTrue(self.scenario.dpdk_setup_done) + + @mock.patch.object(subprocess, 'check_output') + def test_run_ok(self, *args): + # run() specific mocks + self.mock_ssh.from_node().execute.return_value = ( + 0, 'throughput_rx_fps\r\n14797660.000\r\n', '') + + result = {} + self.scenario.run(result) + self.assertEqual(result['throughput_rx_fps'], '14797660.000') + + @mock.patch.object(time, 'sleep') + @mock.patch.object(subprocess, 'check_output') + def test_vsperf_run_sla_fail(self, *args): + self.mock_ssh.from_node().execute.return_value = ( + 0, 'throughput_rx_fps\r\n123456.000\r\n', '') + + with self.assertRaises(y_exc.SLAValidationError) as raised: + self.scenario.run({}) + + self.assertIn('VSPERF_throughput_rx_fps(123456.000000) < ' + 'SLA_throughput_rx_fps(500000.000000)', + str(raised.exception)) + + @mock.patch.object(time, 'sleep') + @mock.patch.object(subprocess, 'check_output') + def test_vsperf_run_sla_fail_metric_not_collected(self, *args): + self.mock_ssh.from_node().execute.return_value = ( + 0, 'nonexisting_metric\r\n123456.000\r\n', '') + + with self.assertRaises(y_exc.SLAValidationError) as raised: + self.scenario.run({}) + + self.assertIn('throughput_rx_fps was not collected by VSPERF', + str(raised.exception)) + + @mock.patch.object(time, 'sleep') + @mock.patch.object(subprocess, 'check_output') + def test_vsperf_run_sla_fail_metric_not_collected_faulty_csv(self, *args): + self.scenario.setup() + + self.mock_ssh.from_node().execute.return_value = ( + 0, 'faulty output not csv', '') + + with self.assertRaises(y_exc.SLAValidationError) as raised: + self.scenario.run({}) + + self.assertIn('throughput_rx_fps was not collected by VSPERF', + str(raised.exception)) + + @mock.patch.object(time, 'sleep') + @mock.patch.object(subprocess, 'check_output') + def test_vsperf_run_sla_fail_sla_not_defined(self, *args): + del self.scenario.scenario_cfg['sla']['throughput_rx_fps'] + self.scenario.setup() + + self.mock_ssh.from_node().execute.return_value = ( + 0, 'throughput_rx_fps\r\n14797660.000\r\n', '') + + with self.assertRaises(y_exc.SLAValidationError) as raised: + self.scenario.run({}) + + self.assertIn('throughput_rx_fps is not defined in SLA', + str(raised.exception)) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/tg_trex_tpl.yaml b/yardstick/tests/unit/benchmark/scenarios/networking/tg_trex_tpl.yaml new file mode 100644 index 000000000..b1641836b --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/tg_trex_tpl.yaml @@ -0,0 +1,75 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +vnfd:vnfd-catalog: + vnfd: + - id: TrexTrafficGen # NSPerf class mapping + name: trexgen + short-name: trexgen + description: TRex stateless traffic generator for tests + vm-flavor: + vcpu-count: '4' + memory-mb: '4096' + mgmt-interface: + vdu-id: trexgen-baremetal + user: '{{user}}' # Value filled by vnfdgen + password: '{{password}}' # Value filled by vnfdgen + ip: '{{ip}}' # Value filled by vnfdgen + connection-point: + - name: xe0 + type: VPORT + - name: xe1 + type: VPORT + vdu: + - id: trexgen-baremetal + name: trexgen-baremetal + description: TRex stateless traffic generator for tests + external-interface: + - name: xe0 + virtual-interface: + type: PCI-PASSTHROUGH + # Substitution variables MUST be quoted. Otherwise Python can misinterpet them. + vpci: '{{ interfaces.xe0.vpci }}' # Value filled by vnfdgen + local_ip: '{{ interfaces.xe0.local_ip }}' # Value filled by vnfdgen + driver: '{{ interfaces.xe0.driver}}' # Value filled by vnfdgen + dst_ip: '{{ interfaces.xe0.dst_ip }}' # Value filled by vnfdgen + local_mac: '{{ interfaces.xe0.local_mac }}' # Value filled by vnfdgen + dst_mac: '{{ interfaces.xe0.dst_mac }}' # Value filled by vnfdgen + vld_id: '{{ interfaces.xe0.vld_id }}' # Value filled by vnfdgen + bandwidth: 10 Gbps + vnfd-connection-point-ref: xe0 + - name: xe1 + virtual-interface: + type: PCI-PASSTHROUGH + vpci: '{{ interfaces.xe1.vpci }}' # Value filled by vnfdgen + local_ip: '{{ interfaces.xe1.local_ip }}' # Value filled by vnfdgen + driver: '{{ interfaces.xe1.driver}}' # Value filled by vnfdgen + dst_ip: '{{ interfaces.xe1.dst_ip }}' # Value filled by vnfdgen + local_mac: '{{ interfaces.xe1.local_mac }}' # Value filled by vnfdgen + dst_mac: '{{ interfaces.xe1.dst_mac }}' # Value filled by vnfdgen + vld_id: '{{ interfaces.xe1.vld_id }}' # Value filled by vnfdgen + bandwidth: 10 Gbps + vnfd-connection-point-ref: xe1 + + benchmark: + kpi: + - rx_throughput_fps + - tx_throughput_fps + - tx_throughput_mbps + - rx_throughput_mbps + - tx_throughput_pc_linerate + - rx_throughput_pc_linerate + - min_latency + - max_latency + - avg_latency diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/vpe_vnf_topology.yaml b/yardstick/tests/unit/benchmark/scenarios/networking/vpe_vnf_topology.yaml new file mode 100644 index 000000000..aaf84bb5e --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/vpe_vnf_topology.yaml @@ -0,0 +1,50 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +nsd:nsd-catalog: + nsd: + - id: VPE + name: VPE + short-name: VPE + description: scenario with VPE,L3fwd and VNF + constituent-vnfd: + - member-vnf-index: '1' + vnfd-id-ref: tg__0 + VNF model: ../../vnf_descriptors/tg_rfc2544_tpl.yaml #tg_trex_tpl.yaml #TREX + - member-vnf-index: '2' + vnfd-id-ref: vnf__0 + VNF model: ../../vnf_descriptors/vpe_vnf.yaml #VPE VNF + + vld: + - id: uplink + name: tg__0 to vnf__0 link 1 + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: tg__0 + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: vnf__0 + + - id: downlink + name: vnf__0 to tg__0 link 2 + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: vnf__0 + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: tg__0 diff --git a/yardstick/tests/unit/benchmark/scenarios/parser/__init__.py b/yardstick/tests/unit/benchmark/scenarios/parser/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/parser/__init__.py diff --git a/yardstick/tests/unit/benchmark/scenarios/parser/test_parser.py b/yardstick/tests/unit/benchmark/scenarios/parser/test_parser.py new file mode 100644 index 000000000..9fd5cce38 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/parser/test_parser.py @@ -0,0 +1,70 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and other. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import subprocess + +import unittest +import mock + +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.parser import parser + + +class ParserTestCase(unittest.TestCase): + + def setUp(self): + args = { + 'options': {'yangfile': '/root/yardstick/samples/yang.yaml', + 'toscafile': '/root/yardstick/samples/tosca.yaml'}, + } + self.scenario = parser.Parser(scenario_cfg=args, context_cfg={}) + + self._mock_popen = mock.patch.object(subprocess, 'Popen') + self.mock_popen = self._mock_popen.start() + self._mock_call = mock.patch.object(subprocess, 'call') + self.mock_call = self._mock_call.start() + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_popen.stop() + self._mock_call.stop() + + def test_setup_successful(self): + + self.mock_call.return_value = 0 + self.scenario.setup() + self.assertTrue(self.scenario.setup_done) + + def test_run_successful(self): + + result = {} + + self.mock_popen().returncode = 0 + + expected_result = jsonutils.loads('{"yangtotosca": "success"}') + + self.scenario.run(result) + self.assertEqual(result, expected_result) + + def test_run_fail(self): + result = {} + + self.mock_popen().returncode = 1 + expected_result = jsonutils.loads('{"yangtotosca": "fail"}') + + self.scenario.run(result) + self.assertEqual(result, expected_result) + + def test_teardown_successful(self): + + self.mock_call.return_value = 0 + self.scenario.teardown() + self.assertTrue(self.scenario.teardown_done) diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/__init__.py b/yardstick/tests/unit/benchmark/scenarios/storage/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/storage/__init__.py diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/fio_read_sample_output.json b/yardstick/tests/unit/benchmark/scenarios/storage/fio_read_sample_output.json new file mode 100644 index 000000000..e9f642aba --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/storage/fio_read_sample_output.json @@ -0,0 +1 @@ +{"fioversion": "fio-2.1.3","jobs": [{"jobname": "yardstick-fio","groupid": 0,"error": 0,"read": {"io_bytes": 2166860,"bw": 36113,"iops": 9028,"runtime": 60001,"slat": {"min": 7,"max": 1807,"mean": 10.49,"stddev": 3.00},"clat": {"min": 1,"max": 16902,"mean": 97.84,"stddev": 78.16,"percentile": {"1.000000": 84,"5.000000": 86,"10.000000": 87,"20.000000": 88,"30.000000": 89,"40.000000": 90,"50.000000": 91,"60.000000": 93,"70.000000": 98,"80.000000": 103,"90.000000": 111,"95.000000": 127,"99.000000": 161,"99.500000": 177,"99.900000": 215,"99.950000": 266,"99.990000": 4128,"0.00": 0,"0.00": 0,"0.00": 0}},"lat": {"min": 86,"max": 16912,"mean": 108.70,"stddev": 78.29},"bw_min": 0,"bw_max": 38128,"bw_agg": 35816.54,"bw_mean": 35816.54,"bw_dev": 3579.16},"write": {"io_bytes": 0,"bw": 0,"iops": 0,"runtime": 0,"slat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00},"clat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00,"percentile": {"1.000000": 0,"5.000000": 0,"10.000000": 0,"20.000000": 0,"30.000000": 0,"40.000000": 0,"50.000000": 0,"60.000000": 0,"70.000000": 0,"80.000000": 0,"90.000000": 0,"95.000000": 0,"99.000000": 0,"99.500000": 0,"99.900000": 0,"99.950000": 0,"99.990000": 0,"0.00": 0,"0.00": 0,"0.00": 0}},"lat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00},"bw_min": 0,"bw_max": 0,"bw_agg": 0.00,"bw_mean": 0.00,"bw_dev": 0.00},"trim": {"io_bytes": 0,"bw": 0,"iops": 0,"runtime": 0,"slat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00},"clat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00,"percentile": {"1.000000": 0,"5.000000": 0,"10.000000": 0,"20.000000": 0,"30.000000": 0,"40.000000": 0,"50.000000": 0,"60.000000": 0,"70.000000": 0,"80.000000": 0,"90.000000": 0,"95.000000": 0,"99.000000": 0,"99.500000": 0,"99.900000": 0,"99.950000": 0,"99.990000": 0,"0.00": 0,"0.00": 0,"0.00": 0}},"lat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00},"bw_min": 0,"bw_max": 0,"bw_agg": 0.00,"bw_mean": 0.00,"bw_dev": 0.00},"usr_cpu": 4.86,"sys_cpu": 19.38,"ctx": 632024,"majf": 0,"minf": 30,"iodepth_level": {"1": 116.58,"2": 0.00,"4": 0.00,"8": 0.00,"16": 0.00,"32": 0.00,">=64": 0.00},"latency_us": {"2": 0.01,"4": 0.01,"10": 0.00,"20": 0.00,"50": 0.01,"100": 72.60,"250": 27.34,"500": 0.04,"750": 0.01,"1000": 0.01},"latency_ms": {"2": 0.01,"4": 0.01,"10": 0.01,"20": 0.01,"50": 0.00,"100": 0.00,"250": 0.00,"500": 0.00,"750": 0.00,"1000": 0.00,"2000": 0.00,">=2000": 0.00}}],"disk_util": [{"name": "vda","read_ios": 631084,"write_ios": 212,"read_merges": 0,"write_merges": 232,"read_ticks": 57300,"write_ticks": 324,"in_queue": 57400,"util": 81.55}]} diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/fio_rw_sample_output.json b/yardstick/tests/unit/benchmark/scenarios/storage/fio_rw_sample_output.json new file mode 100644 index 000000000..4c7501818 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/storage/fio_rw_sample_output.json @@ -0,0 +1 @@ +{"jobs": [{"trim": {"io_bytes": 0, "slat": {"max": 0, "mean": 0.0, "stddev": 0.0, "min": 0}, "bw_max": 0, "bw_mean": 0.0, "iops": 0, "bw": 0, "lat": {"max": 0, "mean": 0.0, "stddev": 0.0, "min": 0}, "bw_agg": 0.0, "clat": {"max": 0, "mean": 0.0, "percentile": {"70.000000": 0, "5.000000": 0, "50.000000": 0, "99.990000": 0, "30.000000": 0, "10.000000": 0, "99.000000": 0, "0.00": 0, "90.000000": 0, "95.000000": 0, "60.000000": 0, "40.000000": 0, "20.000000": 0, "99.900000": 0, "99.950000": 0, "1.000000": 0, "99.500000": 0, "80.000000": 0}, "stddev": 0.0, "min": 0}, "runtime": 0, "bw_min": 0, "bw_dev": 0.0}, "latency_us": {"10": 0.01, "750": 0.03, "20": 0.0, "50": 0.02, "2": 0.01, "4": 0.01, "100": 0.75, "250": 88.37, "500": 10.72, "1000": 0.01}, "latency_ms": {"10": 0.01, "750": 0.0, "20": 0.01, ">=2000": 0.0, "50": 0.01, "2000": 0.0, "2": 0.07, "4": 0.01, "100": 0.0, "250": 0.01, "500": 0.0, "1000": 0.01}, "read": {"io_bytes": 839056, "slat": {"max": 1990, "mean": 18.14, "stddev": 15.4, "min": 0}, "bw_max": 10328, "bw_mean": 8721.27, "iops": 20972, "bw": 83888, "lat": {"max": 776676, "mean": 236.8, "stddev": 4668.12, "min": 45}, "bw_agg": 8721.27, "clat": {"max": 776663, "mean": 217.79, "percentile": {"70.000000": 199, "5.000000": 119, "50.000000": 175, "99.990000": 15168, "30.000000": 155, "10.000000": 131, "99.000000": 342, "0.00": 0, "90.000000": 247, "95.000000": 278, "60.000000": 185, "40.000000": 165, "20.000000": 145, "99.900000": 820, "99.950000": 1272, "1.000000": 96, "99.500000": 370, "80.000000": 217}, "stddev": 4667.79, "min": 0}, "runtime": 10002, "bw_min": 4, "bw_dev": 2178.08}, "majf": 0, "ctx": 490590, "minf": 87, "jobname": "yardstick-fio", "write": {"io_bytes": 841992, "slat": {"max": 2594, "mean": 19.78, "stddev": 16.25, "min": 0}, "bw_max": 10472, "bw_mean": 8464.0, "iops": 21045, "bw": 84182, "lat": {"max": 776709, "mean": 233.55, "stddev": 3115.46, "min": 64}, "bw_agg": 8464.0, "clat": {"max": 776685, "mean": 212.87, "percentile": {"70.000000": 211, "5.000000": 135, "50.000000": 187, "99.990000": 3536, "30.000000": 169, "10.000000": 145, "99.000000": 358, "0.00": 0, "90.000000": 258, "95.000000": 290, "60.000000": 197, "40.000000": 177, "20.000000": 159, "99.900000": 756, "99.950000": 1288, "1.000000": 114, "99.500000": 382, "80.000000": 229}, "stddev": 3115.23, "min": 0}, "runtime": 10002, "bw_min": 4, "bw_dev": 2584.23}, "iodepth_level": {"16": 0.0, "32": 0.0, "1": 111.92, "2": 0.0, "4": 0.0, ">=64": 0.0, "8": 0.0}, "usr_cp": 2.87, "error": 0, "sys_cp": 12.37, "groupid": 0}], "fio version": "fio-2.1.3", "disk_util": [{"aggr_write_ticks": 42020, "read_merges": 0, "name": "dm-0", "write_ios": 233547, "aggr_write_ios": 235129, "aggr_read_ticks": 42576, "read_ios": 233492, "util": 97.22, "read_ticks": 42096, "aggr_write_merge": 0, "write_merges": 0, "aggr_in_queue": 84524, "aggr_read_ios": 235224, "aggr_util": 96.96, "aggr_read_merges": 0, "in_queue": 83732, "write_ticks": 41468}, {"read_merges": 0, "name": "vda", "write_ios": 235129, "read_ios": 235224, "util": 96.96, "read_ticks": 42576, "write_merges": 0, "in_queue": 84524, "write_ticks": 42020}]} diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/fio_write_sample_output.json b/yardstick/tests/unit/benchmark/scenarios/storage/fio_write_sample_output.json new file mode 100644 index 000000000..7c760e8bc --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/storage/fio_write_sample_output.json @@ -0,0 +1 @@ +{"fioversion": "fio-2.1.3","jobs": [{"jobname": "yardstick-fio","groupid": 0,"error": 0,"read": {"io_bytes": 0,"bw": 0,"iops": 0,"runtime": 0,"slat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00},"clat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00,"percentile": {"1.000000": 0,"5.000000": 0,"10.000000": 0,"20.000000": 0,"30.000000": 0,"40.000000": 0,"50.000000": 0,"60.000000": 0,"70.000000": 0,"80.000000": 0,"90.000000": 0,"95.000000": 0,"99.000000": 0,"99.500000": 0,"99.900000": 0,"99.950000": 0,"99.990000": 0,"0.00": 0,"0.00": 0,"0.00": 0}},"lat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00},"bw_min": 0,"bw_max": 0,"bw_agg": 0.00,"bw_mean": 0.00,"bw_dev": 0.00},"write": {"io_bytes": 2106508,"bw": 35107,"iops": 8776,"runtime": 60001,"slat": {"min": 8,"max": 5166,"mean": 11.83,"stddev": 7.05},"clat": {"min": 1,"max": 23472,"mean": 99.54,"stddev": 44.23,"percentile": {"1.000000": 85,"5.000000": 87,"10.000000": 88,"20.000000": 89,"30.000000": 90,"40.000000": 91,"50.000000": 93,"60.000000": 99,"70.000000": 104,"80.000000": 107,"90.000000": 113,"95.000000": 127,"99.000000": 161,"99.500000": 179,"99.900000": 231,"99.950000": 286,"99.990000": 628,"0.00": 0,"0.00": 0,"0.00": 0}},"lat": {"min": 87,"max": 23486,"mean": 111.74,"stddev": 45.61},"bw_min": 0,"bw_max": 37288,"bw_agg": 34839.53,"bw_mean": 34839.53,"bw_dev": 3387.37},"trim": {"io_bytes": 0,"bw": 0,"iops": 0,"runtime": 0,"slat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00},"clat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00,"percentile": {"1.000000": 0,"5.000000": 0,"10.000000": 0,"20.000000": 0,"30.000000": 0,"40.000000": 0,"50.000000": 0,"60.000000": 0,"70.000000": 0,"80.000000": 0,"90.000000": 0,"95.000000": 0,"99.000000": 0,"99.500000": 0,"99.900000": 0,"99.950000": 0,"99.990000": 0,"0.00": 0,"0.00": 0,"0.00": 0}},"lat": {"min": 0,"max": 0,"mean": 0.00,"stddev": 0.00},"bw_min": 0,"bw_max": 0,"bw_agg": 0.00,"bw_mean": 0.00,"bw_dev": 0.00},"usr_cpu": 5.25,"sys_cpu": 19.72,"ctx": 616160,"majf": 0,"minf": 27,"iodepth_level": {"1": 116.90,"2": 0.00,"4": 0.00,"8": 0.00,"16": 0.00,"32": 0.00,">=64": 0.00},"latency_us": {"2": 0.01,"4": 0.01,"10": 0.00,"20": 0.00,"50": 0.01,"100": 60.74,"250": 39.18,"500": 0.06,"750": 0.01,"1000": 0.01},"latency_ms": {"2": 0.01,"4": 0.01,"10": 0.01,"20": 0.00,"50": 0.01,"100": 0.00,"250": 0.00,"500": 0.00,"750": 0.00,"1000": 0.00,"2000": 0.00,">=2000": 0.00}}],"disk_util": [{"name": "vda","read_ios": 0,"write_ios": 615418,"read_merges": 0,"write_merges": 231,"read_ticks": 0,"write_ticks": 58284,"in_queue": 58024,"util": 82.45}]} diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/test_bonnie.py b/yardstick/tests/unit/benchmark/scenarios/storage/test_bonnie.py new file mode 100644 index 000000000..d78506584 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/storage/test_bonnie.py @@ -0,0 +1,63 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +from __future__ import absolute_import + +import unittest + +import mock + +from yardstick.benchmark.scenarios.storage import bonnie + + +class BonnieTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'root', + 'key_filename': "mykey.key" + } + } + + self.result = {} + + @mock.patch('yardstick.benchmark.scenarios.storage.bonnie.ssh') + def test_bonnie_successful_setup(self, mock_ssh): + + options = { + "file_size": "1024", + "ram_size": "512", + "test_dir": "/tmp", + "concurrency": "1", + "test_user": "root" + } + args = {"options": options} + b = bonnie.Bonnie(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + b.setup() + self.assertIsNotNone(b.client) + self.assertTrue(b.setup_done, True) + + @mock.patch('yardstick.benchmark.scenarios.storage.bonnie.ssh') + def test_bonnie_unsuccessful_script_error(self, mock_ssh): + options = { + "file_size": "1024", + "ram_size": "512", + "test_dir": "/tmp", + "concurrency": "1", + "test_user": "root" + } + args = {"options": options} + b = bonnie.Bonnie(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, b.run, self.result) diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/test_fio.py b/yardstick/tests/unit/benchmark/scenarios/storage/test_fio.py new file mode 100644 index 000000000..6e69ddc6d --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/storage/test_fio.py @@ -0,0 +1,280 @@ +############################################################################## +# Copyright (c) 2015 Ericsson AB and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.storage.fio.Fio + +from __future__ import absolute_import + +import os +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.storage import fio +from yardstick.common import exceptions as y_exc + + +@mock.patch('yardstick.benchmark.scenarios.storage.fio.ssh') +class FioTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'key_filename': 'mykey.key' + } + } + self.sample_output = { + 'read': 'fio_read_sample_output.json', + 'write': 'fio_write_sample_output.json', + 'rw': 'fio_rw_sample_output.json' + } + + def test_fio_successful_setup(self, mock_ssh): + + options = { + 'filename': '/home/ubuntu/data.raw', + 'bs': '4k', + 'rw': 'rw', + 'ramp_time': 10 + } + args = {'options': options} + p = fio.Fio(args, self.ctx) + p.setup() + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + def test_fio_job_file_successful_setup(self, mock_ssh): + + options = { + 'job_file': 'job_file.ini', + 'directory': '/FIO_Test' + } + args = {'options': options} + p = fio.Fio(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '/dev/vdb', '') + p.setup() + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + def test_fio_job_file_no_disk__setup(self, mock_ssh): + + options = { + 'job_file': 'job_file.ini', + 'directory': '/FIO_Test' + } + args = {'options': options} + p = fio.Fio(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + p.setup() + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + def test_fio_successful_no_sla(self, mock_ssh): + + options = { + 'filename': '/home/ubuntu/data.raw', + 'bs': '4k', + 'rw': 'rw', + 'ramp_time': 10 + } + args = {'options': options} + p = fio.Fio(args, self.ctx) + result = {} + + p.client = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.sample_output['rw']) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + p.run(result) + + expected_result = '{"read_bw": 83888, "read_iops": 20972,' \ + '"read_lat": 236.8, "write_bw": 84182, "write_iops": 21045,'\ + '"write_lat": 233.55}' + expected_result = jsonutils.loads(expected_result) + self.assertEqual(result, expected_result) + + def test_fio_successful_read_no_sla(self, mock_ssh): + + options = { + 'filename': '/home/ubuntu/data.raw', + 'bs': '4k', + 'rw': "read", + 'ramp_time': 10 + } + args = {'options': options} + p = fio.Fio(args, self.ctx) + result = {} + + p.client = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.sample_output['read']) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + p.run(result) + + expected_result = '{"read_bw": 36113, "read_iops": 9028,' \ + '"read_lat": 108.7}' + expected_result = jsonutils.loads(expected_result) + self.assertEqual(result, expected_result) + + def test_fio_successful_write_no_sla(self, mock_ssh): + + options = { + 'filename': '/home/ubuntu/data.raw', + 'bs': '4k', + 'rw': 'write', + 'ramp_time': 10 + } + args = {'options': options} + p = fio.Fio(args, self.ctx) + result = {} + + p.client = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.sample_output['write']) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + p.run(result) + + expected_result = '{"write_bw": 35107, "write_iops": 8776,'\ + '"write_lat": 111.74}' + expected_result = jsonutils.loads(expected_result) + self.assertEqual(result, expected_result) + + def test_fio_successful_lat_sla(self, mock_ssh): + + options = { + 'filename': '/home/ubuntu/data.raw', + 'bs': '4k', + 'rw': 'rw', + 'ramp_time': 10 + } + args = { + 'options': options, + 'sla': {'write_lat': 300.1} + } + p = fio.Fio(args, self.ctx) + result = {} + + p.client = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.sample_output['rw']) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + p.run(result) + + expected_result = '{"read_bw": 83888, "read_iops": 20972,' \ + '"read_lat": 236.8, "write_bw": 84182, "write_iops": 21045,'\ + '"write_lat": 233.55}' + expected_result = jsonutils.loads(expected_result) + self.assertEqual(result, expected_result) + + def test_fio_unsuccessful_lat_sla(self, mock_ssh): + + options = { + 'filename': '/home/ubuntu/data.raw', + 'bs': '4k', + 'rw': 'rw', + 'ramp_time': 10 + } + args = { + 'options': options, + 'sla': {'write_lat': 200.1} + } + p = fio.Fio(args, self.ctx) + result = {} + + p.client = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.sample_output['rw']) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, p.run, result) + + def test_fio_successful_bw_iops_sla(self, mock_ssh): + + options = { + 'filename': '/home/ubuntu/data.raw', + 'bs': '4k', + 'rw': 'rw', + 'ramp_time': 10 + } + args = { + 'options': options, + 'sla': {'read_iops': 20000} + } + p = fio.Fio(args, self.ctx) + result = {} + + p.client = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.sample_output['rw']) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + + p.run(result) + + expected_result = '{"read_bw": 83888, "read_iops": 20972,' \ + '"read_lat": 236.8, "write_bw": 84182, "write_iops": 21045,'\ + '"write_lat": 233.55}' + expected_result = jsonutils.loads(expected_result) + self.assertEqual(result, expected_result) + + def test_fio_unsuccessful_bw_iops_sla(self, mock_ssh): + + options = { + 'filename': '/home/ubuntu/data.raw', + 'bs': '4k', + 'rw': 'rw', + 'ramp_time': 10 + } + args = { + 'options': options, + 'sla': {'read_iops': 30000} + } + p = fio.Fio(args, self.ctx) + result = {} + + p.client = mock_ssh.SSH.from_node() + + sample_output = self._read_sample_output(self.sample_output['rw']) + mock_ssh.SSH.from_node().execute.return_value = (0, sample_output, '') + self.assertRaises(y_exc.SLAValidationError, p.run, result) + + def test_fio_unsuccessful_script_error(self, mock_ssh): + + options = { + 'filename': '/home/ubuntu/data.raw', + 'bs': '4k', + 'rw': 'rw', + 'ramp_time': 10 + } + args = {'options': options} + p = fio.Fio(args, self.ctx) + result = {} + + p.client = mock_ssh.SSH.from_node() + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, p.run, result) + + def _read_sample_output(self, file_name): + curr_path = os.path.dirname(os.path.abspath(__file__)) + output = os.path.join(curr_path, file_name) + with open(output) as f: + sample_output = f.read() + return sample_output diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/test_storagecapacity.py b/yardstick/tests/unit/benchmark/scenarios/storage/test_storagecapacity.py new file mode 100644 index 000000000..c1c731b0a --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/storage/test_storagecapacity.py @@ -0,0 +1,99 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for +# yardstick.benchmark.scenarios.storage.storagecapacity.StorageCapacity + +from __future__ import absolute_import + +import unittest + +import mock +from oslo_serialization import jsonutils + +from yardstick.benchmark.scenarios.storage import storagecapacity + +DISK_SIZE_SAMPLE_OUTPUT = \ + '{"Numberf of devides": "2", "Total disk size in bytes": "1024000000"}' +BLOCK_SIZE_SAMPLE_OUTPUT = '{"/dev/sda": 1024, "/dev/sdb": 4096}' +DISK_UTIL_RAW_OUTPUT = "vda 10.00\nvda 0.00" +DISK_UTIL_SAMPLE_OUTPUT = \ + '{"vda": {"avg_util": 5.0, "max_util": 10.0, "min_util": 0.0}}' + + +@mock.patch('yardstick.benchmark.scenarios.storage.storagecapacity.ssh') +class StorageCapacityTestCase(unittest.TestCase): + + def setUp(self): + self.scn = { + "options": { + 'test_type': 'disk_size' + } + } + self.ctx = { + "host": { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'password': "root" + } + } + self.result = {} + + def test_capacity_successful_setup(self, mock_ssh): + c = storagecapacity.StorageCapacity(self.scn, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + c.setup() + self.assertIsNotNone(c.client) + self.assertTrue(c.setup_done) + + def test_capacity_disk_size_successful(self, mock_ssh): + c = storagecapacity.StorageCapacity(self.scn, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, DISK_SIZE_SAMPLE_OUTPUT, '') + c.run(self.result) + expected_result = jsonutils.loads( + DISK_SIZE_SAMPLE_OUTPUT) + self.assertEqual(self.result, expected_result) + + def test_capacity_block_size_successful(self, mock_ssh): + args = { + "options": { + 'test_type': 'block_size' + } + } + c = storagecapacity.StorageCapacity(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, BLOCK_SIZE_SAMPLE_OUTPUT, '') + c.run(self.result) + expected_result = jsonutils.loads( + BLOCK_SIZE_SAMPLE_OUTPUT) + self.assertEqual(self.result, expected_result) + + def test_capacity_disk_utilization_successful(self, mock_ssh): + args = { + "options": { + 'test_type': 'disk_utilization', + 'interval': 1, + 'count': 2 + } + } + c = storagecapacity.StorageCapacity(args, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (0, DISK_UTIL_RAW_OUTPUT, '') + c.run(self.result) + expected_result = jsonutils.loads( + DISK_UTIL_SAMPLE_OUTPUT) + self.assertEqual(self.result, expected_result) + + def test_capacity_unsuccessful_script_error(self, mock_ssh): + c = storagecapacity.StorageCapacity(self.scn, self.ctx) + + mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') + self.assertRaises(RuntimeError, c.run, self.result) diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/test_storperf.py b/yardstick/tests/unit/benchmark/scenarios/storage/test_storperf.py new file mode 100644 index 000000000..2ba53cb93 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/storage/test_storperf.py @@ -0,0 +1,513 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.benchmark.scenarios.storage.storperf.StorPerf + +from __future__ import absolute_import + +import json +import unittest + +import mock +from oslo_serialization import jsonutils +import requests + +from yardstick.benchmark.scenarios.storage import storperf + + +# pylint: disable=unused-argument +# disable this for now because I keep forgetting mock patch arg ordering +def mocked_requests_config_post(*args, **kwargs): + class MockResponseConfigPost(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseConfigPost( + '{"stack_id": "dac27db1-3502-4300-b301-91c64e6a1622",' + '"stack_created": false}', + 200) + + +def mocked_requests_config_post_fail(*args, **kwargs): + class MockResponseConfigPost(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseConfigPost( + '{"message": "ERROR: Parameter \'public_network\' is invalid: ' + + 'Error validating value \'foo\': Unable to find network with ' + + 'name or id \'foo\'"}', + 400) + + +def mocked_requests_config_get(*args, **kwargs): + class MockResponseConfigGet(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseConfigGet( + '{"stack_id": "dac27db1-3502-4300-b301-91c64e6a1622",' + '"stack_created": true}', + 200) + + +def mocked_requests_config_get_not_created(*args, **kwargs): + class MockResponseConfigGet(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseConfigGet( + '{"stack_id": "",' + '"stack_created": false}', + 200) + + +def mocked_requests_config_get_no_payload(*args, **kwargs): + class MockResponseConfigGet(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseConfigGet( + '{}', + 200) + + +def mocked_requests_initialize_post_fail(*args, **kwargs): + class MockResponseJobPost(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseJobPost( + '{"message": "ERROR: Stack StorPerfAgentGroup does not exist"}', + 400) + + +def mocked_requests_job_get(*args, **kwargs): + class MockResponseJobGet(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseJobGet( + '{"Status": "Completed",\ + "_ssd_preconditioning.queue-depth.8.block-size.16384.duration": 6}', + 200) + + +def mocked_requests_job_post(*args, **kwargs): + class MockResponseJobPost(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseJobPost('{"job_id": \ + "d46bfb8c-36f4-4a40-813b-c4b4a437f728"}', 200) + + +def mocked_requests_job_post_fail(*args, **kwargs): + class MockResponseJobPost(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseJobPost( + '{"message": "ERROR: Stack StorPerfAgentGroup does not exist"}', + 400) + + +def mocked_requests_job_delete(*args, **kwargs): + class MockResponseJobDelete(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseJobDelete('{}', 200) + + +def mocked_requests_delete(*args, **kwargs): + class MockResponseDelete(object): + + def __init__(self, json_data, status_code): + self.json_data = json_data + self.status_code = status_code + + return MockResponseDelete('{}', 200) + + +def mocked_requests_delete_failed(*args, **kwargs): + class MockResponseDeleteFailed(object): + + def __init__(self, json_data, status_code): + self.json_data = json_data + self.status_code = status_code + + return MockResponseDeleteFailed('{"message": "Teardown failed"}', 400) + + +class StorPerfTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + 'host': { + 'ip': '172.16.0.137', + 'user': 'cirros', + 'key_filename': "mykey.key" + } + } + + self.result = {} + + @mock.patch.object(requests, 'post') + @mock.patch.object(requests, 'get') + def test_setup(self, mock_get, mock_post): + mock_post.side_effect = [mocked_requests_config_post(), + mocked_requests_job_post()] + mock_get.side_effect = [mocked_requests_config_get(), + mocked_requests_job_get()] + + options = { + "agent_count": 8, + "public_network": 'ext-net', + "volume_size": 10, + "block_sizes": 4096, + "queue_depths": 4, + "workload": "rs", + "StorPerf_ip": "192.168.23.2", + "query_interval": 0, + "timeout": 60 + } + + args = { + "options": options + } + + s = storperf.StorPerf(args, self.ctx) + + s.setup() + + self.assertTrue(s.setup_done) + + @mock.patch.object(requests, 'get') + def test_query_setup_state_unsuccessful(self, mock_get): + mock_get.side_effect = mocked_requests_config_get_not_created + args = { + "options": {} + } + s = storperf.StorPerf(args, self.ctx) + result = s._query_setup_state() + self.assertFalse(result) + + @mock.patch.object(requests, 'get') + def test_query_setup_state_no_payload(self, mock_get): + mock_get.side_effect = mocked_requests_config_get_no_payload + args = { + "options": {} + } + s = storperf.StorPerf(args, self.ctx) + result = s._query_setup_state() + self.assertFalse(result) + + @mock.patch.object(requests, 'post') + @mock.patch.object(requests, 'get') + def test_setup_config_post_failed(self, mock_get, mock_post): + mock_post.side_effect = mocked_requests_config_post_fail + + args = { + "options": { + "public_network": "foo" + } + } + + s = storperf.StorPerf(args, self.ctx) + + self.assertRaises(RuntimeError, s.setup) + + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + def test_run_v1_successful(self, mock_post, mock_get): + mock_post.side_effect = mocked_requests_job_post + mock_get.side_effect = mocked_requests_job_get + + options = { + "agent_count": 8, + "public_network": 'ext-net', + "volume_size": 10, + "block_sizes": 4096, + "queue_depths": 4, + "workload": "rs", + "StorPerf_ip": "192.168.23.2", + "query_interval": 0, + "timeout": 60 + } + expected_post = { + 'metadata': { + 'build_tag': 'latest', + 'test_case': 'opnfv_yardstick_tc074' + }, + 'deadline': 60, + 'block_sizes': 4096, + 'queue_depths': 4, + "workload": "rs", + 'agent_count': 8 + } + + args = { + "options": options + } + + s = storperf.StorPerf(args, self.ctx) + s.setup_done = True + + sample_output = '{"Status": "Completed",\ + "_ssd_preconditioning.queue-depth.8.block-size.16384.duration": 6}' + + expected_result = jsonutils.loads(sample_output) + + s.run(self.result) + + mock_post.assert_called_once_with( + 'http://192.168.23.2:5000/api/v1.0/jobs', + json=jsonutils.loads(json.dumps(expected_post))) + + self.assertEqual(self.result, expected_result) + + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + def test_run_v2_successful(self, mock_post, mock_get): + mock_post.side_effect = mocked_requests_job_post + mock_get.side_effect = mocked_requests_job_get + + options = { + "agent_count": 8, + "public_network": 'ext-net', + "volume_size": 10, + "block_sizes": 4096, + "queue_depths": 4, + "workloads": { + "read_sequential": { + "rw": "rs" + } + }, + "StorPerf_ip": "192.168.23.2", + "query_interval": 0, + "timeout": 60 + } + expected_post = { + 'metadata': { + 'build_tag': 'latest', + 'test_case': 'opnfv_yardstick_tc074' + }, + 'deadline': 60, + 'block_sizes': 4096, + 'queue_depths': 4, + 'workloads': { + 'read_sequential': { + 'rw': 'rs' + } + }, + 'agent_count': 8 + } + + args = { + "options": options + } + + s = storperf.StorPerf(args, self.ctx) + s.setup_done = True + + sample_output = '{"Status": "Completed",\ + "_ssd_preconditioning.queue-depth.8.block-size.16384.duration": 6}' + + expected_result = jsonutils.loads(sample_output) + + s.run(self.result) + mock_post.assert_called_once_with( + 'http://192.168.23.2:5000/api/v2.0/jobs', + json=expected_post) + + self.assertEqual(self.result, expected_result) + + @mock.patch('time.sleep') + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + def test_run_failed(self, mock_post, mock_get, _): + mock_post.side_effect = mocked_requests_job_post_fail + mock_get.side_effect = mocked_requests_job_get + + options = { + "agent_count": 8, + "public_network": 'ext-net', + "volume_size": 10, + "block_sizes": 4096, + "queue_depths": 4, + "workloads": { + "read_sequential": { + "rw": "rs" + } + }, + "StorPerf_ip": "192.168.23.2", + "query_interval": 0, + "timeout": 60 + } + expected_post = { + 'metadata': { + 'build_tag': 'latest', + 'test_case': 'opnfv_yardstick_tc074' + }, + 'deadline': 60, + 'block_sizes': 4096, + 'queue_depths': 4, + 'workloads': { + 'read_sequential': { + 'rw': 'rs' + } + }, + 'agent_count': 8 + } + + args = { + "options": options + } + + s = storperf.StorPerf(args, self.ctx) + s.setup_done = True + + self.assertRaises(RuntimeError, s.run, self.ctx) + mock_post.assert_called_once_with( + 'http://192.168.23.2:5000/api/v2.0/jobs', + json=expected_post) + + @mock.patch('time.sleep') + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + @mock.patch.object(storperf.StorPerf, 'setup') + def test_run_calls_setup(self, mock_setup, mock_post, mock_get, _): + mock_post.side_effect = mocked_requests_job_post + mock_get.side_effect = mocked_requests_job_get + + args = { + "options": { + 'timeout': 60, + } + } + + s = storperf.StorPerf(args, self.ctx) + + s.run(self.result) + + mock_setup.assert_called_once() + + @mock.patch('time.sleep') + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + def test_initialize_disks(self, mock_post, mock_get, _): + mock_post.side_effect = mocked_requests_job_post + mock_get.side_effect = mocked_requests_job_get + + args = { + "options": { + "StorPerf_ip": "192.168.23.2" + } + } + + s = storperf.StorPerf(args, self.ctx) + + s.initialize_disks() + + mock_post.assert_called_once_with( + 'http://192.168.23.2:5000/api/v1.0/initializations', + json={}) + + @mock.patch('time.sleep') + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + def test_initialize_disks_post_failed(self, mock_post, mock_get, _): + mock_post.side_effect = mocked_requests_initialize_post_fail + mock_get.side_effect = mocked_requests_job_get + + args = { + "options": { + "StorPerf_ip": "192.168.23.2" + } + } + + s = storperf.StorPerf(args, self.ctx) + + self.assertRaises(RuntimeError, s.initialize_disks) + mock_post.assert_called_once_with( + 'http://192.168.23.2:5000/api/v1.0/initializations', + json={}) + + @mock.patch.object(requests, 'delete') + def test_teardown(self, mock_delete): + mock_delete.side_effect = mocked_requests_job_delete + options = { + "agent_count": 8, + "public_network": 'ext-net', + "volume_size": 10, + "block_sizes": 4096, + "queue_depths": 4, + "workload": "rs", + "StorPerf_ip": "192.168.23.2", + "query_interval": 10, + "timeout": 60 + } + + args = { + "options": options + } + + s = storperf.StorPerf(args, self.ctx) + + s.teardown() + + self.assertFalse(s.setup_done) + mock_delete.assert_called_once_with( + 'http://192.168.23.2:5000/api/v1.0/configurations') + + @mock.patch.object(requests, 'delete') + def test_teardown_request_delete_failed(self, mock_delete): + mock_delete.side_effect = mocked_requests_delete_failed + options = { + "agent_count": 8, + "public_network": 'ext-net', + "volume_size": 10, + "block_sizes": 4096, + "queue_depths": 4, + "workload": "rs", + "StorPerf_ip": "192.168.23.2", + "query_interval": 10, + "timeout": 60 + } + + args = { + "options": options + } + + s = storperf.StorPerf(args, self.ctx) + + self.assertRaises(RuntimeError, s.teardown) + mock_delete.assert_called_once_with( + 'http://192.168.23.2:5000/api/v1.0/configurations') diff --git a/yardstick/tests/unit/benchmark/scenarios/test_base.py b/yardstick/tests/unit/benchmark/scenarios/test_base.py new file mode 100644 index 000000000..284a71cc8 --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/test_base.py @@ -0,0 +1,135 @@ +# Copyright 2017: Intel Ltd. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import time + +import mock + +from yardstick.benchmark.scenarios import base +from yardstick.tests.unit import base as ut_base + + +class _TestScenario(base.Scenario): + __scenario_type__ = 'Test Scenario' + + def run(self): + pass + + +class ScenarioTestCase(ut_base.BaseUnitTestCase): + + def test_get_scenario_type(self): + scenario_type = 'dummy scenario' + + class DummyScenario(base.Scenario): + __scenario_type__ = scenario_type + + self.assertEqual(scenario_type, DummyScenario.get_scenario_type()) + + def test_get_scenario_type_not_defined(self): + class DummyScenario(base.Scenario): + pass + + self.assertEqual(str(None), DummyScenario.get_scenario_type()) + + def test_get_description(self): + docstring = """First line + Second line + Third line + """ + + class DummyScenario(base.Scenario): + __doc__ = docstring + + self.assertEqual(docstring.splitlines()[0], + DummyScenario.get_description()) + + def test_get_description_empty(self): + class DummyScenario(base.Scenario): + pass + + self.assertEqual(str(None), DummyScenario.get_description()) + + def test_get_types(self): + scenario_names = set( + scenario.__scenario_type__ for scenario in + base.Scenario.get_types() if hasattr(scenario, + '__scenario_type__')) + existing_scenario_class_names = { + 'Iperf3', 'CACHEstat', 'SpecCPU2006', 'Dummy', 'NSPerf', 'Parser'} + self.assertTrue(existing_scenario_class_names.issubset(scenario_names)) + + def test_get_cls_existing_scenario(self): + scenario_name = 'NSPerf' + scenario = base.Scenario.get_cls(scenario_name) + self.assertEqual(scenario_name, scenario.__scenario_type__) + + def test_get_cls_non_existing_scenario(self): + wrong_scenario_name = 'Non-existing-scenario' + with self.assertRaises(RuntimeError) as exc: + base.Scenario.get_cls(wrong_scenario_name) + self.assertEqual('No such scenario type %s' % wrong_scenario_name, + str(exc.exception)) + + def test_get_existing_scenario(self): + scenario_name = 'NSPerf' + scenario_module = ('yardstick.benchmark.scenarios.networking.' + 'vnf_generic.NetworkServiceTestCase') + self.assertEqual(scenario_module, base.Scenario.get(scenario_name)) + + def test_get_non_existing_scenario(self): + wrong_scenario_name = 'Non-existing-scenario' + with self.assertRaises(RuntimeError) as exc: + base.Scenario.get(wrong_scenario_name) + self.assertEqual('No such scenario type %s' % wrong_scenario_name, + str(exc.exception)) + + def test_scenario_abstract_class(self): + # pylint: disable=abstract-class-instantiated + with self.assertRaises(TypeError): + base.Scenario() + + @mock.patch.object(time, 'sleep') + def test_pre_run_wait_time(self, mock_sleep): + """Ensure default behaviour (backwards compatibility): no wait time""" + test_scenario = _TestScenario() + test_scenario.pre_run_wait_time(mock.ANY) + mock_sleep.assert_not_called() + + @mock.patch.object(time, 'sleep') + def test_post_run_wait_time(self, mock_sleep): + """Ensure default behaviour (backwards compatibility): wait time""" + test_scenario = _TestScenario() + test_scenario.post_run_wait_time(100) + mock_sleep.assert_called_once_with(100) + + +class IterScenarioClassesTestCase(ut_base.BaseUnitTestCase): + + def test_no_scenario_type_defined(self): + some_existing_scenario_class_names = [ + 'Iperf3', 'CACHEstat', 'SpecCPU2006', 'Dummy', 'NSPerf', 'Parser'] + scenario_types = [scenario.__scenario_type__ for scenario + in base._iter_scenario_classes()] + for class_name in some_existing_scenario_class_names: + self.assertIn(class_name, scenario_types) + + def test_scenario_type_defined(self): + some_existing_scenario_class_names = [ + 'Iperf3', 'CACHEstat', 'SpecCPU2006', 'Dummy', 'NSPerf', 'Parser'] + for class_name in some_existing_scenario_class_names: + scenario_class = next(base._iter_scenario_classes( + scenario_type=class_name)) + self.assertEqual(class_name, scenario_class.__scenario_type__) diff --git a/yardstick/tests/unit/common/banned_modules/__init__.py b/yardstick/tests/unit/common/banned_modules/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/common/banned_modules/__init__.py diff --git a/yardstick/tests/unit/common/banned_modules/banned_module.py b/yardstick/tests/unit/common/banned_modules/banned_module.py new file mode 100644 index 000000000..f57f939d2 --- /dev/null +++ b/yardstick/tests/unit/common/banned_modules/banned_module.py @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class BannedClass(object): + pass diff --git a/yardstick/tests/unit/common/banned_modules/importing_module.py b/yardstick/tests/unit/common/banned_modules/importing_module.py new file mode 100644 index 000000000..fc2542f0c --- /dev/null +++ b/yardstick/tests/unit/common/banned_modules/importing_module.py @@ -0,0 +1,21 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from yardstick.common import import_tools +from yardstick.tests.unit.common.banned_modules import banned_module + + +@import_tools.decorator_banned_modules +class ImportingClass(banned_module.BannedClass): + pass diff --git a/yardstick/tests/unit/common/messaging/__init__.py b/yardstick/tests/unit/common/messaging/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/common/messaging/__init__.py diff --git a/yardstick/tests/unit/common/messaging/test_consumer.py b/yardstick/tests/unit/common/messaging/test_consumer.py new file mode 100644 index 000000000..612dcaecd --- /dev/null +++ b/yardstick/tests/unit/common/messaging/test_consumer.py @@ -0,0 +1,54 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +from oslo_config import cfg +import oslo_messaging + +from yardstick.common import messaging +from yardstick.common.messaging import consumer +from yardstick.tests.unit import base as ut_base + + +class TestEndPoint(object): + def action_1(self): + pass + + +class _MessagingConsumer(consumer.MessagingConsumer): + pass + + +class MessagingConsumerTestCase(ut_base.BaseUnitTestCase): + + def test__init(self): + with mock.patch.object(oslo_messaging, 'get_rpc_server') as \ + mock_get_rpc_server, \ + mock.patch.object(oslo_messaging, 'get_rpc_transport') as \ + mock_get_rpc_transport, \ + mock.patch.object(oslo_messaging, 'Target') as \ + mock_Target: + mock_get_rpc_transport.return_value = 'test_rpc_transport' + mock_Target.return_value = 'test_Target' + + _MessagingConsumer('test_topic', 'test_pid', [TestEndPoint], + fanout=True) + mock_get_rpc_transport.assert_called_once_with( + cfg.CONF, url=messaging.TRANSPORT_URL) + mock_Target.assert_called_once_with( + topic='test_topic', fanout=True, server=messaging.SERVER) + mock_get_rpc_server.assert_called_once_with( + 'test_rpc_transport', 'test_Target', [TestEndPoint], + executor=messaging.RPC_SERVER_EXECUTOR, + access_policy=oslo_messaging.DefaultRPCAccessPolicy) diff --git a/yardstick/tests/unit/common/messaging/test_payloads.py b/yardstick/tests/unit/common/messaging/test_payloads.py new file mode 100644 index 000000000..37b1f1926 --- /dev/null +++ b/yardstick/tests/unit/common/messaging/test_payloads.py @@ -0,0 +1,82 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from yardstick.common import exceptions +from yardstick.common.messaging import payloads +from yardstick.tests.unit import base as ut_base + + +class _DummyPayload(payloads.Payload): + REQUIRED_FIELDS = {'version', 'key1', 'key2'} + + +class PayloadTestCase(ut_base.BaseUnitTestCase): + + def test__init(self): + payload = _DummyPayload(version=1, key1='value1', key2='value2') + self.assertEqual(1, payload.version) + self.assertEqual('value1', payload.key1) + self.assertEqual('value2', payload.key2) + self.assertEqual(3, len(payload._fields)) + + def test__init_missing_required_fields(self): + with self.assertRaises(exceptions.PayloadMissingAttributes): + _DummyPayload(key1='value1', key2='value2') + + def test_obj_to_dict(self): + payload = _DummyPayload(version=1, key1='value1', key2='value2') + payload_dict = payload.obj_to_dict() + self.assertEqual({'version': 1, 'key1': 'value1', 'key2': 'value2'}, + payload_dict) + + def test_dict_to_obj(self): + _dict = {'version': 2, 'key1': 'value100', 'key2': 'value200'} + payload = _DummyPayload.dict_to_obj(_dict) + self.assertEqual(set(_dict.keys()), payload._fields) + + +class TrafficGeneratorPayloadTestCase(ut_base.BaseUnitTestCase): + + def test_init(self): + tg_payload = payloads.TrafficGeneratorPayload( + version=1, iteration=10, kpi={'key1': 'value1'}) + self.assertEqual(1, tg_payload.version) + self.assertEqual(10, tg_payload.iteration) + self.assertEqual({'key1': 'value1'}, tg_payload.kpi) + self.assertEqual(3, len(tg_payload._fields)) + + def test__init_missing_required_fields(self): + with self.assertRaises(exceptions.PayloadMissingAttributes): + payloads.TrafficGeneratorPayload(version=1, iteration=10) + with self.assertRaises(exceptions.PayloadMissingAttributes): + payloads.TrafficGeneratorPayload(iteration=10, kpi={}) + with self.assertRaises(exceptions.PayloadMissingAttributes): + payloads.TrafficGeneratorPayload(iteration=10) + + +class RunnerPayloadTestCase(ut_base.BaseUnitTestCase): + + def test_init(self): + runner_payload = payloads.RunnerPayload(version=5, + data={'key1': 'value1'}) + self.assertEqual(5, runner_payload.version) + self.assertEqual({'key1': 'value1'}, runner_payload.data) + + def test__init_missing_required_fields(self): + with self.assertRaises(exceptions.PayloadMissingAttributes): + payloads.RunnerPayload(version=1) + with self.assertRaises(exceptions.PayloadMissingAttributes): + payloads.RunnerPayload(data=None) + with self.assertRaises(exceptions.PayloadMissingAttributes): + payloads.RunnerPayload() diff --git a/yardstick/tests/unit/common/messaging/test_producer.py b/yardstick/tests/unit/common/messaging/test_producer.py new file mode 100644 index 000000000..22286e5c3 --- /dev/null +++ b/yardstick/tests/unit/common/messaging/test_producer.py @@ -0,0 +1,53 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +from oslo_config import cfg +import oslo_messaging + +from yardstick.common import messaging +from yardstick.common.messaging import producer +from yardstick.tests.unit import base as ut_base + + +class _MessagingProducer(producer.MessagingProducer): + pass + + +class MessagingProducerTestCase(ut_base.BaseUnitTestCase): + + def test__init(self): + with mock.patch.object(oslo_messaging, 'RPCClient') as \ + mock_RPCClient, \ + mock.patch.object(oslo_messaging, 'get_rpc_transport') as \ + mock_get_rpc_transport, \ + mock.patch.object(oslo_messaging, 'Target') as \ + mock_Target: + mock_get_rpc_transport.return_value = 'test_rpc_transport' + mock_Target.return_value = 'test_Target' + + _MessagingProducer('test_topic', 'test_pid', fanout=True) + mock_get_rpc_transport.assert_called_once_with( + cfg.CONF, url=messaging.TRANSPORT_URL) + mock_Target.assert_called_once_with( + topic='test_topic', fanout=True, server=messaging.SERVER) + mock_RPCClient.assert_called_once_with('test_rpc_transport', + 'test_Target') + + def test_id(self): + with mock.patch.object(oslo_messaging, 'RPCClient'), \ + mock.patch.object(oslo_messaging, 'get_rpc_transport'), \ + mock.patch.object(oslo_messaging, 'Target'): + msg_producer = _MessagingProducer('topic', 'id_to_check') + self.assertEqual('id_to_check', msg_producer.id) diff --git a/yardstick/tests/unit/common/test_ansible_common.py b/yardstick/tests/unit/common/test_ansible_common.py index 89ea128af..bf82f6288 100644 --- a/yardstick/tests/unit/common/test_ansible_common.py +++ b/yardstick/tests/unit/common/test_ansible_common.py @@ -12,27 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. - -from __future__ import absolute_import - -import os +import collections +import shutil +import subprocess import tempfile -from collections import defaultdict import mock -import unittest - -from six.moves.configparser import ConfigParser -from six.moves import StringIO +from six import moves +from six.moves import configparser from yardstick.common import ansible_common +from yardstick.tests.unit import base as ut_base -PREFIX = 'yardstick.common.ansible_common' +class OverwriteDictTestCase(ut_base.BaseUnitTestCase): -class OverwriteDictTestCase(unittest.TestCase): def test_overwrite_dict_cfg(self): - c = ConfigParser(allow_no_value=True) + c = configparser.ConfigParser(allow_no_value=True) d = { "section_a": "empty_value", "section_b": {"key_c": "Val_d", "key_d": "VAL_D"}, @@ -42,86 +38,78 @@ class OverwriteDictTestCase(unittest.TestCase): # Python3 and Python2 convert empty values into None or '' # we don't really care but we need to compare correctly for unittest self.assertTrue(c.has_option("section_a", "empty_value")) - self.assertEqual(sorted(c.items("section_b")), [('key_c', 'Val_d'), ('key_d', 'VAL_D')]) + self.assertEqual(sorted(c.items("section_b")), + [('key_c', 'Val_d'), ('key_d', 'VAL_D')]) self.assertTrue(c.has_option("section_c", "key_c")) self.assertTrue(c.has_option("section_c", "key_d")) -class FilenameGeneratorTestCase(unittest.TestCase): - @mock.patch('{}.NamedTemporaryFile'.format(PREFIX)) +class FilenameGeneratorTestCase(ut_base.BaseUnitTestCase): + + @mock.patch.object(tempfile, 'NamedTemporaryFile') def test__handle_existing_file(self, _): - ansible_common.FileNameGenerator._handle_existing_file("/dev/null") + ansible_common.FileNameGenerator._handle_existing_file('/dev/null') def test_get_generator_from_file(self): - ansible_common.FileNameGenerator.get_generator_from_filename("/dev/null", "", "", "") + ansible_common.FileNameGenerator.get_generator_from_filename( + '/dev/null', '', '', '') def test_get_generator_from_file_middle(self): - ansible_common.FileNameGenerator.get_generator_from_filename("/dev/null", "", "", - "null") + ansible_common.FileNameGenerator.get_generator_from_filename( + '/dev/null', '', '', 'null') def test_get_generator_from_file_prefix(self): - ansible_common.FileNameGenerator.get_generator_from_filename("/dev/null", "", "null", - "middle") + ansible_common.FileNameGenerator.get_generator_from_filename( + '/dev/null', '', 'null', 'middle') -class AnsibleNodeTestCase(unittest.TestCase): - def test_ansible_node(self): - ansible_common.AnsibleNode() +class AnsibleNodeTestCase(ut_base.BaseUnitTestCase): def test_ansible_node_len(self): - a = ansible_common.AnsibleNode() - len(a) + self.assertEqual(0, len(ansible_common.AnsibleNode())) def test_ansible_node_repr(self): - a = ansible_common.AnsibleNode() - repr(a) + self.assertEqual('AnsibleNode<{}>', repr(ansible_common.AnsibleNode())) def test_ansible_node_iter(self): - a = ansible_common.AnsibleNode() - for _ in a: - pass + node = ansible_common.AnsibleNode(data={'a': 1, 'b': 2, 'c': 3}) + for key in node: + self.assertIn(key, ('a', 'b', 'c')) def test_is_role(self): - a = ansible_common.AnsibleNode() - self.assertFalse(a.is_role("", default="foo")) + node = ansible_common.AnsibleNode() + self.assertFalse(node.is_role('', default='foo')) def test_ansible_node_get_tuple(self): - a = ansible_common.AnsibleNode({"name": "name"}) - self.assertEqual(a.get_tuple(), ('name', a)) + node = ansible_common.AnsibleNode({'name': 'name'}) + self.assertEqual(node.get_tuple(), ('name', node)) def test_gen_inventory_line(self): - a = ansible_common.AnsibleNode(defaultdict(str)) + a = ansible_common.AnsibleNode(collections.defaultdict(str)) self.assertEqual(a.gen_inventory_line(), "") def test_ansible_node_delitem(self): - a = ansible_common.AnsibleNode({"name": "name"}) - del a['name'] + node = ansible_common.AnsibleNode({'name': 'name'}) + self.assertEqual(1, len(node)) + del node['name'] + self.assertEqual(0, len(node)) def test_ansible_node_getattr(self): - a = ansible_common.AnsibleNode({"name": "name"}) - self.assertEqual(getattr(a, "nosuch", None), None) + node = ansible_common.AnsibleNode({'name': 'name'}) + self.assertIsNone(getattr(node, 'nosuch', None)) -class AnsibleNodeDictTestCase(unittest.TestCase): - def test_ansible_node_dict(self): - n = ansible_common.AnsibleNode - ansible_common.AnsibleNodeDict(n, {}) +class AnsibleNodeDictTestCase(ut_base.BaseUnitTestCase): def test_ansible_node_dict_len(self): n = ansible_common.AnsibleNode a = ansible_common.AnsibleNodeDict(n, {}) - len(a) + self.assertEqual(0, len(a)) def test_ansible_node_dict_repr(self): n = ansible_common.AnsibleNode a = ansible_common.AnsibleNodeDict(n, {}) - repr(a) - - def test_ansible_node_dict_iter(self): - n = ansible_common.AnsibleNode - a = ansible_common.AnsibleNodeDict(n, {}) - for _ in a: - pass + self.assertEqual('{}', repr(a)) def test_ansible_node_dict_get(self): n = ansible_common.AnsibleNode @@ -143,12 +131,15 @@ class AnsibleNodeDictTestCase(unittest.TestCase): ["name ansible_ssh_pass=PASS ansible_user=user"]) -class AnsibleCommonTestCase(unittest.TestCase): - def test_get_timeouts(self): - self.assertAlmostEquals(ansible_common.AnsibleCommon.get_timeout(-100), 1200.0) +class AnsibleCommonTestCase(ut_base.BaseUnitTestCase): + + @staticmethod + def _delete_tmpdir(dir): + shutil.rmtree(dir) - def test__init__(self): - ansible_common.AnsibleCommon({}) + def test_get_timeouts(self): + self.assertAlmostEqual( + ansible_common.AnsibleCommon.get_timeout(-100), 1200.0) def test_reset(self): a = ansible_common.AnsibleCommon({}) @@ -183,66 +174,68 @@ class AnsibleCommonTestCase(unittest.TestCase): a.deploy_dir = "d" self.assertEqual(a.deploy_dir, "d") - @mock.patch('{}.open'.format(PREFIX)) - def test__gen_ansible_playbook_file_list(self, _): + @mock.patch.object(moves.builtins, 'open') + def test__gen_ansible_playbook_file_list(self, *args): d = tempfile.mkdtemp() - try: - a = ansible_common.AnsibleCommon({}) - a._gen_ansible_playbook_file(["a"], d) - finally: - os.rmdir(d) - - @mock.patch('{}.NamedTemporaryFile'.format(PREFIX)) - @mock.patch('{}.open'.format(PREFIX)) - def test__gen_ansible_inventory_file(self, _, __): + self.addCleanup(self._delete_tmpdir, d) + a = ansible_common.AnsibleCommon({}) + a._gen_ansible_playbook_file(["a"], d) + + @mock.patch.object(tempfile, 'NamedTemporaryFile') + @mock.patch.object(moves.builtins, 'open') + def test__gen_ansible_inventory_file(self, *args): nodes = [{ "name": "name", "user": "user", "password": "PASS", "role": "role", }] d = tempfile.mkdtemp() - try: - a = ansible_common.AnsibleCommon(nodes) - a.gen_inventory_ini_dict() - inv_context = a._gen_ansible_inventory_file(d) - with inv_context: - c = StringIO() - inv_context.write_func(c) - self.assertIn("ansible_ssh_pass=PASS", c.getvalue()) - finally: - os.rmdir(d) - - @mock.patch('{}.NamedTemporaryFile'.format(PREFIX)) - @mock.patch('{}.open'.format(PREFIX)) - def test__gen_ansible_playbook_file_list_multiple(self, _, __): + self.addCleanup(self._delete_tmpdir, d) + a = ansible_common.AnsibleCommon(nodes) + a.gen_inventory_ini_dict() + inv_context = a._gen_ansible_inventory_file(d) + with inv_context: + c = moves.StringIO() + inv_context.write_func(c) + self.assertIn("ansible_ssh_pass=PASS", c.getvalue()) + + @mock.patch.object(tempfile, 'NamedTemporaryFile') + @mock.patch.object(moves.builtins, 'open') + def test__gen_ansible_playbook_file_list_multiple(self, *args): d = tempfile.mkdtemp() - try: - a = ansible_common.AnsibleCommon({}) - a._gen_ansible_playbook_file(["a", "b"], d) - finally: - os.rmdir(d) - - @mock.patch('{}.NamedTemporaryFile'.format(PREFIX)) - @mock.patch('{}.Popen'.format(PREFIX)) - @mock.patch('{}.open'.format(PREFIX)) - def test_do_install_tmp_dir(self, _, mock_popen, __): + self.addCleanup(self._delete_tmpdir, d) + a = ansible_common.AnsibleCommon({}) + a._gen_ansible_playbook_file(["a", "b"], d) + + @mock.patch.object(tempfile, 'NamedTemporaryFile') + @mock.patch.object(subprocess, 'Popen') + @mock.patch.object(moves.builtins, 'open') + def test_do_install_tmp_dir(self, _, mock_popen, *args): mock_popen.return_value.communicate.return_value = "", "" mock_popen.return_value.wait.return_value = 0 d = tempfile.mkdtemp() - try: - a = ansible_common.AnsibleCommon({}) - a.do_install('', d) - finally: - os.rmdir(d) - - @mock.patch('{}.NamedTemporaryFile'.format(PREFIX)) - @mock.patch('{}.Popen'.format(PREFIX)) - @mock.patch('{}.open'.format(PREFIX)) - def test_execute_ansible_check(self, _, mock_popen, __): + self.addCleanup(self._delete_tmpdir, d) + a = ansible_common.AnsibleCommon({}) + a.do_install('', d) + + @mock.patch.object(tempfile, 'NamedTemporaryFile') + @mock.patch.object(moves.builtins, 'open') + @mock.patch.object(subprocess, 'Popen') + def test_execute_ansible_check(self, mock_popen, *args): mock_popen.return_value.communicate.return_value = "", "" mock_popen.return_value.wait.return_value = 0 d = tempfile.mkdtemp() - try: - a = ansible_common.AnsibleCommon({}) - a.execute_ansible('', d, ansible_check=True, verbose=True) - finally: - os.rmdir(d) + self.addCleanup(self._delete_tmpdir, d) + a = ansible_common.AnsibleCommon({}) + a.execute_ansible('', d, ansible_check=True, verbose=True) + + def test_get_sut_info(self): + d = tempfile.mkdtemp() + a = ansible_common.AnsibleCommon({}) + self.addCleanup(self._delete_tmpdir, d) + with mock.patch.object(a, '_exec_get_sut_info_cmd'): + a.get_sut_info(d) + + def test_get_sut_info_not_exist(self): + a = ansible_common.AnsibleCommon({}) + with self.assertRaises(OSError): + a.get_sut_info('/hello/world') diff --git a/yardstick/tests/unit/common/test_exceptions.py b/yardstick/tests/unit/common/test_exceptions.py new file mode 100644 index 000000000..884015536 --- /dev/null +++ b/yardstick/tests/unit/common/test_exceptions.py @@ -0,0 +1,28 @@ +# Copyright 2018 Intel Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from yardstick.common import exceptions +from yardstick.tests.unit import base as ut_base + + +class ErrorClassTestCase(ut_base.BaseUnitTestCase): + + def test_init(self): + with self.assertRaises(RuntimeError): + exceptions.ErrorClass() + + def test_getattr(self): + error_instance = exceptions.ErrorClass(test='') + with self.assertRaises(AttributeError): + error_instance.get_name() diff --git a/yardstick/tests/unit/common/test_httpClient.py b/yardstick/tests/unit/common/test_httpClient.py index eb09d1a52..12a8be3a0 100644 --- a/yardstick/tests/unit/common/test_httpClient.py +++ b/yardstick/tests/unit/common/test_httpClient.py @@ -33,11 +33,3 @@ class HttpClientTestCase(unittest.TestCase): url = 'http://localhost:5000/hello' httpClient.HttpClient().get(url) mock_requests.get.assert_called_with(url) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/common/test_import_tools.py b/yardstick/tests/unit/common/test_import_tools.py new file mode 100644 index 000000000..c8fe5fde0 --- /dev/null +++ b/yardstick/tests/unit/common/test_import_tools.py @@ -0,0 +1,46 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from yardstick.common import exceptions as y_exc +from yardstick.common import import_tools + + +@import_tools.decorator_banned_modules +class DummyClass(object): + pass + + +class DecoratorBannedModule(unittest.TestCase): + + MODULE = 'yardstick.tests.unit.common.banned_modules.banned_module' + + def test_passt(self): + self.assertIsNotNone(DummyClass()) + + def test_banned_module(self): + import_tools.BANNED_MODULES[self.MODULE] = 'Banned module!!' + from yardstick.tests.unit.common.banned_modules import importing_module + self.addCleanup(self._remove_module) + + with self.assertRaises(y_exc.YardstickBannedModuleImported) as exc: + importing_module.ImportingClass() + + msg = ('Module "%s" cannnot be imported. Reason: "Banned module!!"' + % self.MODULE) + self.assertEqual(msg, str(exc.exception)) + + def _remove_module(self): + del import_tools.BANNED_MODULES[self.MODULE] diff --git a/yardstick/tests/unit/common/test_kubernetes_utils.py b/yardstick/tests/unit/common/test_kubernetes_utils.py new file mode 100644 index 000000000..ba6b5f388 --- /dev/null +++ b/yardstick/tests/unit/common/test_kubernetes_utils.py @@ -0,0 +1,447 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +from kubernetes import client +from kubernetes.client import rest +from kubernetes import config + +from yardstick.common import constants +from yardstick.common import exceptions +from yardstick.common import kubernetes_utils +from yardstick.tests.unit import base + + +class GetExtensionsV1betaApiTestCase(base.BaseUnitTestCase): + + @mock.patch.object(client, 'ApiextensionsV1beta1Api', return_value='api') + @mock.patch.object(config, 'load_kube_config') + def test_execute_correct(self, mock_load_kube_config, mock_api): + self.assertEqual('api', kubernetes_utils.get_extensions_v1beta_api()) + mock_load_kube_config.assert_called_once_with( + config_file=constants.K8S_CONF_FILE) + mock_api.assert_called_once() + + @mock.patch.object(config, 'load_kube_config') + def test_execute_exception(self, mock_load_kube_config): + mock_load_kube_config.side_effect = IOError + with self.assertRaises(exceptions.KubernetesConfigFileNotFound): + kubernetes_utils.get_extensions_v1beta_api() + + +class GetCustomObjectsApiTestCase(base.BaseUnitTestCase): + + @mock.patch.object(client, 'CustomObjectsApi', return_value='api') + @mock.patch.object(config, 'load_kube_config') + def test_execute_correct(self, mock_load_kube_config, mock_api): + self.assertEqual('api', kubernetes_utils.get_custom_objects_api()) + mock_load_kube_config.assert_called_once_with( + config_file=constants.K8S_CONF_FILE) + mock_api.assert_called_once() + + @mock.patch.object(config, 'load_kube_config') + def test_execute_exception(self, mock_load_kube_config): + mock_load_kube_config.side_effect = IOError + with self.assertRaises(exceptions.KubernetesConfigFileNotFound): + kubernetes_utils.get_custom_objects_api() + + +class CreateCustomResourceDefinitionTestCase(base.BaseUnitTestCase): + + @mock.patch.object(client, 'V1beta1CustomResourceDefinition', + return_value='crd_obj') + @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api') + def test_execute_correct(self, mock_get_api, mock_crd): + mock_create_crd = mock.Mock() + mock_get_api.return_value = mock_create_crd + body = {'spec': 'fake_spec', 'metadata': 'fake_metadata'} + + kubernetes_utils.create_custom_resource_definition(body) + mock_get_api.assert_called_once() + mock_crd.assert_called_once_with(spec='fake_spec', + metadata='fake_metadata') + mock_create_crd.create_custom_resource_definition.\ + assert_called_once_with('crd_obj') + + @mock.patch.object(client, 'V1beta1CustomResourceDefinition', + return_value='crd_obj') + @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api') + def test_execute_exception(self, mock_get_api, mock_crd): + mock_create_crd = mock.Mock() + mock_create_crd.create_custom_resource_definition.\ + side_effect = rest.ApiException + mock_get_api.return_value = mock_create_crd + body = {'spec': 'fake_spec', 'metadata': 'fake_metadata'} + + with self.assertRaises(exceptions.KubernetesApiException): + kubernetes_utils.create_custom_resource_definition(body) + mock_get_api.assert_called_once() + mock_crd.assert_called_once_with(spec='fake_spec', + metadata='fake_metadata') + mock_create_crd.create_custom_resource_definition.\ + assert_called_once_with('crd_obj') + + +class DeleteCustomResourceDefinitionTestCase(base.BaseUnitTestCase): + + @mock.patch.object(client, 'V1DeleteOptions', return_value='del_obj') + @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api') + def test_execute_correct(self, mock_get_api, mock_delobj): + mock_delete_crd = mock.Mock() + mock_get_api.return_value = mock_delete_crd + + kubernetes_utils.delete_custom_resource_definition('name') + mock_get_api.assert_called_once() + mock_delobj.assert_called_once() + mock_delete_crd.delete_custom_resource_definition.\ + assert_called_once_with('name', 'del_obj') + + @mock.patch.object(client, 'V1DeleteOptions', return_value='del_obj') + @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api') + def test_execute_exception(self, mock_get_api, mock_delobj): + mock_delete_crd = mock.Mock() + mock_delete_crd.delete_custom_resource_definition.\ + side_effect = rest.ApiException + mock_get_api.return_value = mock_delete_crd + + with self.assertRaises(exceptions.KubernetesApiException): + kubernetes_utils.delete_custom_resource_definition('name') + mock_delobj.assert_called_once() + mock_delete_crd.delete_custom_resource_definition.\ + assert_called_once_with('name', 'del_obj') + + @mock.patch.object(client, 'V1DeleteOptions', return_value='del_obj') + @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api') + @mock.patch.object(kubernetes_utils, 'LOG') + def test_execute_skip_exception(self, mock_log, mock_get_api, mock_delobj): + mock_delete_crd = mock.Mock() + mock_delete_crd.delete_custom_resource_definition.side_effect = rest.ApiException( + status=404) + + mock_get_api.return_value = mock_delete_crd + kubernetes_utils.delete_custom_resource_definition('name', skip_codes=[404]) + + mock_delobj.assert_called_once() + mock_delete_crd.delete_custom_resource_definition.assert_called_once_with( + 'name', 'del_obj') + + mock_log.info.assert_called_once() + + +class GetCustomResourceDefinitionTestCase(base.BaseUnitTestCase): + + @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api') + def test_execute_value(self, mock_get_api): + crd_obj = mock.Mock() + crd_obj.spec.names.kind = 'some_kind' + crd_list = mock.Mock() + crd_list.items = [crd_obj] + mock_api = mock.Mock() + mock_api.list_custom_resource_definition.return_value = crd_list + mock_get_api.return_value = mock_api + self.assertEqual( + crd_obj, + kubernetes_utils.get_custom_resource_definition('some_kind')) + + @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api') + def test_execute_none(self, mock_get_api): + crd_obj = mock.Mock() + crd_obj.spec.names.kind = 'some_kind' + crd_list = mock.Mock() + crd_list.items = [crd_obj] + mock_api = mock.Mock() + mock_api.list_custom_resource_definition.return_value = crd_list + mock_get_api.return_value = mock_api + self.assertIsNone( + kubernetes_utils.get_custom_resource_definition('other_kind')) + + @mock.patch.object(kubernetes_utils, 'get_extensions_v1beta_api') + def test_execute_exception(self, mock_get_api): + mock_api = mock.Mock() + mock_api.list_custom_resource_definition.\ + side_effect = rest.ApiException + mock_get_api.return_value = mock_api + with self.assertRaises(exceptions.KubernetesApiException): + kubernetes_utils.get_custom_resource_definition('kind') + + +class GetNetworkTestCase(base.BaseUnitTestCase): + @mock.patch.object(kubernetes_utils, 'get_custom_objects_api') + def test_execute_correct(self, mock_get_api): + mock_api = mock.Mock() + mock_get_api.return_value = mock_api + group = 'group.com' + version = mock.Mock() + plural = 'networks' + name = 'net_one' + + kubernetes_utils.get_network( + constants.SCOPE_CLUSTER, group, version, plural, name) + mock_api.get_cluster_custom_object.assert_called_once_with( + group, version, plural, name) + + mock_api.reset_mock() + kubernetes_utils.get_network( + constants.SCOPE_NAMESPACED, group, version, plural, name) + mock_api.get_namespaced_custom_object.assert_called_once_with( + group, version, 'default', plural, name) + + @mock.patch.object(kubernetes_utils, 'get_custom_objects_api') + def test_execute_exception(self, mock_get_api): + mock_api = mock.Mock() + mock_api.get_cluster_custom_object.side_effect = rest.ApiException(404) + mock_api.get_namespaced_custom_object.side_effect = rest.ApiException(404) + mock_get_api.return_value = mock_api + group = 'group.com' + version = mock.Mock() + plural = 'networks' + name = 'net_one' + + network_obj = kubernetes_utils.get_network( + constants.SCOPE_CLUSTER, group, version, plural, name) + self.assertIsNone(network_obj) + + mock_api.reset_mock() + network_obj = kubernetes_utils.get_network( + constants.SCOPE_NAMESPACED, group, version, plural, name) + self.assertIsNone(network_obj) + + +class CreateNetworkTestCase(base.BaseUnitTestCase): + @mock.patch.object(kubernetes_utils, 'get_custom_objects_api') + @mock.patch.object(kubernetes_utils, 'get_network') + def test_execute_correct(self, mock_get_net, mock_get_api): + mock_get_net.return_value = None + mock_api = mock.Mock() + mock_get_api.return_value = mock_api + group = 'group.com' + version = mock.Mock() + plural = 'networks' + body = mock.Mock() + name = 'net_one' + + kubernetes_utils.create_network( + constants.SCOPE_CLUSTER, group, version, plural, body, name) + mock_api.create_cluster_custom_object.assert_called_once_with( + group, version, plural, body) + + mock_api.reset_mock() + kubernetes_utils.create_network( + constants.SCOPE_NAMESPACED, group, version, plural, body, name) + mock_api.create_namespaced_custom_object.assert_called_once_with( + group, version, 'default', plural, body) + + @mock.patch.object(kubernetes_utils, 'get_custom_objects_api') + @mock.patch.object(kubernetes_utils, 'get_network') + def test_network_already_created(self, mock_get_net, mock_get_api): + mock_get_net.return_value = mock.Mock + mock_api = mock.Mock() + mock_get_api.return_value = mock_api + group = 'group.com' + version = mock.Mock() + plural = 'networks' + body = mock.Mock() + name = 'net_one' + + mock_api.reset_mock() + kubernetes_utils.create_network( + constants.SCOPE_CLUSTER, group, version, plural, body, name) + mock_api.create_cluster_custom_object.assert_not_called() + + mock_api.reset_mock() + kubernetes_utils.create_network( + constants.SCOPE_NAMESPACED, group, version, plural, body, name) + mock_api.create_namespaced_custom_object.assert_not_called() + + @mock.patch.object(kubernetes_utils, 'get_custom_objects_api') + @mock.patch.object(kubernetes_utils, 'get_network') + def test_execute_exception(self, mock_get_net, mock_get_api): + mock_get_net.return_value = None + mock_api = mock.Mock() + mock_api.create_cluster_custom_object.side_effect = rest.ApiException + mock_get_api.return_value = mock_api + with self.assertRaises(exceptions.KubernetesApiException): + kubernetes_utils.create_network( + constants.SCOPE_CLUSTER, mock.ANY, mock.ANY, mock.ANY, + mock.ANY, mock.ANY) + + +class DeleteNetworkTestCase(base.BaseUnitTestCase): + @mock.patch.object(kubernetes_utils, 'get_custom_objects_api') + def test_execute_correct(self, mock_get_api): + mock_api = mock.Mock() + mock_get_api.return_value = mock_api + group = 'group.com' + version = mock.Mock() + plural = 'networks' + name = 'network' + + kubernetes_utils.delete_network( + constants.SCOPE_CLUSTER, group, version, plural, name) + mock_api.delete_cluster_custom_object.assert_called_once_with( + group, version, plural, name, {}) + + mock_api.reset_mock() + kubernetes_utils.delete_network( + constants.SCOPE_NAMESPACED, group, version, plural, name) + mock_api.delete_namespaced_custom_object.assert_called_once_with( + group, version, 'default', plural, name, {}) + + @mock.patch.object(kubernetes_utils, 'get_custom_objects_api') + def test_execute_exception(self, mock_get_api): + mock_api = mock.Mock() + mock_api.delete_cluster_custom_object.side_effect = rest.ApiException + mock_get_api.return_value = mock_api + with self.assertRaises(exceptions.KubernetesApiException): + kubernetes_utils.delete_network( + constants.SCOPE_CLUSTER, mock.ANY, mock.ANY, mock.ANY, + mock.ANY) + + @mock.patch.object(kubernetes_utils, 'get_custom_objects_api') + @mock.patch.object(kubernetes_utils, 'LOG') + def test_execute_skip_exception(self, mock_log, mock_get_api): + mock_api = mock.Mock() + mock_api.delete_cluster_custom_object.side_effect = rest.ApiException(status=404) + + mock_get_api.return_value = mock_api + kubernetes_utils.delete_network( + constants.SCOPE_CLUSTER, mock.ANY, mock.ANY, mock.ANY, + mock.ANY, skip_codes=[404]) + + mock_log.info.assert_called_once() + + +class DeletePodTestCase(base.BaseUnitTestCase): + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_correct(self, mock_get_api): + mock_api = mock.Mock() + mock_get_api.return_value = mock_api + + kubernetes_utils.delete_pod("name", body=None) + mock_api.delete_namespaced_pod.assert_called_once_with( + "name", 'default', None) + + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_exception(self, mock_get_api): + mock_api = mock.Mock() + mock_api.delete_namespaced_pod.side_effect = rest.ApiException(status=200) + + mock_get_api.return_value = mock_api + with self.assertRaises(exceptions.KubernetesApiException): + kubernetes_utils.delete_pod(mock.ANY, skip_codes=[404]) + + @mock.patch.object(kubernetes_utils, 'LOG') + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_skip_exception(self, mock_get_api, *args): + mock_api = mock.Mock() + mock_api.delete_namespaced_pod.side_effect = rest.ApiException(status=404) + + mock_get_api.return_value = mock_api + kubernetes_utils.delete_pod(mock.ANY, skip_codes=[404]) + + +class DeleteServiceTestCase(base.BaseUnitTestCase): + @mock.patch.object(client, "V1DeleteOptions") + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_correct(self, mock_get_api, mock_options): + mock_api = mock.Mock() + mock_get_api.return_value = mock_api + mock_options.return_value = None + kubernetes_utils.delete_service("name", "default", None) + mock_api.delete_namespaced_service.assert_called_once_with( + "name", 'default', None) + + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_exception(self, mock_get_api): + mock_api = mock.Mock() + mock_api.delete_namespaced_service.side_effect = rest.ApiException(status=200) + + mock_get_api.return_value = mock_api + with self.assertRaises(exceptions.KubernetesApiException): + kubernetes_utils.delete_service(mock.ANY, skip_codes=[404]) + + @mock.patch.object(kubernetes_utils, 'LOG') + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_skip_exception(self, mock_get_api, *args): + mock_api = mock.Mock() + mock_api.delete_namespaced_service.side_effect = rest.ApiException(status=404) + + mock_get_api.return_value = mock_api + kubernetes_utils.delete_service(mock.ANY, skip_codes=[404]) + + +class DeleteReplicationControllerTestCase(base.BaseUnitTestCase): + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_correct(self, mock_get_api): + mock_api = mock.Mock() + mock_get_api.return_value = mock_api + kubernetes_utils.delete_replication_controller( + "name", "default", body=None) + + mock_api.delete_namespaced_replication_controller.assert_called_once_with( + "name", "default", None) + + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_exception(self, mock_get_api): + mock_api = mock.Mock() + mock_api.delete_namespaced_replication_controller.side_effect = ( + rest.ApiException(status=200) + ) + + mock_get_api.return_value = mock_api + with self.assertRaises(exceptions.KubernetesApiException): + kubernetes_utils.delete_replication_controller(mock.ANY, skip_codes=[404]) + + @mock.patch.object(kubernetes_utils, 'get_core_api') + @mock.patch.object(kubernetes_utils, 'LOG') + def test_execute_skip_exception(self, mock_log, mock_get_api): + mock_api = mock.Mock() + mock_api.delete_namespaced_replication_controller.side_effect = ( + rest.ApiException(status=404) + ) + + mock_get_api.return_value = mock_api + kubernetes_utils.delete_replication_controller(mock.ANY, skip_codes=[404]) + + mock_log.info.assert_called_once() + + +class DeleteConfigMapTestCase(base.BaseUnitTestCase): + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_correct(self, mock_get_api): + mock_api = mock.Mock() + mock_get_api.return_value = mock_api + kubernetes_utils.delete_config_map("name", body=None) + mock_api.delete_namespaced_config_map.assert_called_once_with( + "name", "default", None + ) + + @mock.patch.object(kubernetes_utils, 'get_core_api') + def test_execute_exception(self, mock_get_api): + mock_api = mock.Mock() + mock_api.delete_namespaced_config_map.side_effect = rest.ApiException(status=200) + + mock_get_api.return_value = mock_api + with self.assertRaises(exceptions.KubernetesApiException): + kubernetes_utils.delete_config_map(mock.ANY, skip_codes=[404]) + + @mock.patch.object(kubernetes_utils, 'get_core_api') + @mock.patch.object(kubernetes_utils, 'LOG') + def test_execute_skip_exception(self, mock_log, mock_get_api): + mock_api = mock.Mock() + mock_api.delete_namespaced_config_map.side_effect = rest.ApiException(status=404) + + mock_get_api.return_value = mock_api + kubernetes_utils.delete_config_map(mock.ANY, skip_codes=[404]) + mock_log.info.assert_called_once() diff --git a/yardstick/tests/unit/common/test_openstack_utils.py b/yardstick/tests/unit/common/test_openstack_utils.py index bf468489e..f6a0bdcc1 100644 --- a/yardstick/tests/unit/common/test_openstack_utils.py +++ b/yardstick/tests/unit/common/test_openstack_utils.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. # @@ -9,12 +7,15 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -# Unittest for yardstick.common.openstack_utils +import os -from __future__ import absolute_import -import unittest import mock +from oslo_utils import uuidutils +import shade +from shade import exc +import unittest +from yardstick.common import constants from yardstick.common import openstack_utils @@ -29,18 +30,701 @@ class GetCredentialsTestCase(unittest.TestCase): class GetHeatApiVersionTestCase(unittest.TestCase): - def test_get_heat_api_version_check_result(self): + @mock.patch.object(openstack_utils, 'log') + def test_get_heat_api_version_check_result(self, *args): API = 'HEAT_API_VERSION' expected_result = '2' - with mock.patch.dict('os.environ', {API: '2'}, clear=True): + with mock.patch.dict(os.environ, {API: '2'}, clear=True): api_version = openstack_utils.get_heat_api_version() self.assertEqual(api_version, expected_result) -def main(): - unittest.main() +class GetShadeClientTestCase(unittest.TestCase): + + @mock.patch.object(shade, 'openstack_cloud', return_value='os_client') + def test_get_shade_client(self, mock_openstack_cloud): + os_cloud_config = {'param1': True, 'param2': 'value2'} + self.assertEqual('os_client', + openstack_utils.get_shade_client(**os_cloud_config)) + os_cloud_config.update(constants.OS_CLOUD_DEFAULT_CONFIG) + mock_openstack_cloud.assert_called_once_with(**os_cloud_config) + + mock_openstack_cloud.reset_mock() + os_cloud_config = {'verify': True, 'param2': 'value2'} + self.assertEqual('os_client', + openstack_utils.get_shade_client(**os_cloud_config)) + mock_openstack_cloud.assert_called_once_with(**os_cloud_config) + + @mock.patch.object(shade, 'openstack_cloud', return_value='os_client') + def test_get_shade_client_no_parameters(self, mock_openstack_cloud): + self.assertEqual('os_client', openstack_utils.get_shade_client()) + mock_openstack_cloud.assert_called_once_with( + **constants.OS_CLOUD_DEFAULT_CONFIG) + + @mock.patch.object(shade, 'operator_cloud', return_value='os_client') + def test_get_shade_operator_client(self, mock_operator_cloud): + self.assertEqual('os_client', openstack_utils.get_shade_operator_client()) + mock_operator_cloud.assert_called_once_with( + **constants.OS_CLOUD_DEFAULT_CONFIG) + + +class DeleteNeutronNetTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + + def test_delete_neutron_net(self): + self.mock_shade_client.delete_network.return_value = True + output = openstack_utils.delete_neutron_net(self.mock_shade_client, + 'network_name_or_id') + self.assertTrue(output) + + def test_delete_neutron_net_fail(self): + self.mock_shade_client.delete_network.return_value = False + output = openstack_utils.delete_neutron_net(self.mock_shade_client, + 'network_name_or_id') + self.assertFalse(output) + + @mock.patch.object(openstack_utils, 'log') + def test_delete_neutron_net_exception(self, mock_logger): + self.mock_shade_client.delete_network.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.delete_neutron_net(self.mock_shade_client, + 'network_name_or_id') + self.assertFalse(output) + mock_logger.error.assert_called_once() + + +class CreateNeutronNetTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.network_name = 'name' + self.mock_shade_client.create_network = mock.Mock() + + def test_create_neutron_net(self): + _uuid = uuidutils.generate_uuid() + self.mock_shade_client.create_network.return_value = {'id': _uuid} + output = openstack_utils.create_neutron_net(self.mock_shade_client, + self.network_name) + self.assertEqual(_uuid, output) + + @mock.patch.object(openstack_utils, 'log') + def test_create_neutron_net_exception(self, mock_logger): + self.mock_shade_client.create_network.side_effect = ( + exc.OpenStackCloudException('error message')) + + output = openstack_utils.create_neutron_net(self.mock_shade_client, + self.network_name) + mock_logger.error.assert_called_once() + self.assertIsNone(output) + + +class CreateNeutronSubnetTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.network_name_or_id = 'name_or_id' + self.mock_shade_client.create_subnet = mock.Mock() + + def test_create_neutron_subnet(self): + _uuid = uuidutils.generate_uuid() + self.mock_shade_client.create_subnet.return_value = {'id': _uuid} + output = openstack_utils.create_neutron_subnet( + self.mock_shade_client, self.network_name_or_id) + self.assertEqual(_uuid, output) + + @mock.patch.object(openstack_utils, 'log') + def test_create_neutron_subnet_exception(self, mock_logger): + self.mock_shade_client.create_subnet.side_effect = ( + exc.OpenStackCloudException('error message')) + + output = openstack_utils.create_neutron_subnet( + self.mock_shade_client, self.network_name_or_id) + mock_logger.error.assert_called_once() + self.assertIsNone(output) + + +class DeleteNeutronRouterTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.delete_router = mock.Mock() + + def test_delete_neutron_router(self): + self.mock_shade_client.delete_router.return_value = True + output = openstack_utils.delete_neutron_router(self.mock_shade_client, + 'router_id') + self.assertTrue(output) + + def test_delete_neutron_router_fail(self): + self.mock_shade_client.delete_router.return_value = False + output = openstack_utils.delete_neutron_router(self.mock_shade_client, + 'router_id') + self.assertFalse(output) + + @mock.patch.object(openstack_utils, 'log') + def test_delete_neutron_router_exception(self, mock_logger): + self.mock_shade_client.delete_router.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.delete_neutron_router(self.mock_shade_client, + 'router_id') + mock_logger.error.assert_called_once() + self.assertFalse(output) + + +class CreateNeutronRouterTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.create_subnet = mock.Mock() + + def test_create_neutron_router(self): + _uuid = uuidutils.generate_uuid() + self.mock_shade_client.create_router.return_value = {'id': _uuid} + output = openstack_utils.create_neutron_router( + self.mock_shade_client) + self.assertEqual(_uuid, output) + + @mock.patch.object(openstack_utils, 'log') + def test_create_neutron_subnet_exception(self, mock_logger): + self.mock_shade_client.create_router.side_effect = ( + exc.OpenStackCloudException('error message')) + + output = openstack_utils.create_neutron_router( + self.mock_shade_client) + mock_logger.error.assert_called_once() + self.assertIsNone(output) + + +class RemoveRouterInterfaceTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.router = 'router' + self.mock_shade_client.remove_router_interface = mock.Mock() + + def test_remove_router_interface(self): + self.mock_shade_client.remove_router_interface.return_value = True + output = openstack_utils.remove_router_interface( + self.mock_shade_client, self.router) + self.assertTrue(output) + + @mock.patch.object(openstack_utils, 'log') + def test_remove_router_interface_exception(self, mock_logger): + self.mock_shade_client.remove_router_interface.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.remove_router_interface( + self.mock_shade_client, self.router) + mock_logger.error.assert_called_once() + self.assertFalse(output) + + +class CreateFloatingIpTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.network_name_or_id = 'name' + self.mock_shade_client.create_floating_ip = mock.Mock() + + def test_create_floating_ip(self): + self.mock_shade_client.create_floating_ip.return_value = \ + {'floating_ip_address': 'value1', 'id': 'value2'} + output = openstack_utils.create_floating_ip(self.mock_shade_client, + self.network_name_or_id) + self.assertEqual({'fip_addr': 'value1', 'fip_id': 'value2'}, output) + + @mock.patch.object(openstack_utils, 'log') + def test_create_floating_ip_exception(self, mock_logger): + self.mock_shade_client.create_floating_ip.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.create_floating_ip( + self.mock_shade_client, self.network_name_or_id) + mock_logger.error.assert_called_once() + self.assertIsNone(output) + + +class DeleteFloatingIpTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.floating_ip_id = 'floating_ip_id' + self.mock_shade_client.delete_floating_ip = mock.Mock() + + def test_delete_floating_ip(self): + self.mock_shade_client.delete_floating_ip.return_value = True + output = openstack_utils.delete_floating_ip(self.mock_shade_client, + 'floating_ip_id') + self.assertTrue(output) + + def test_delete_floating_ip_fail(self): + self.mock_shade_client.delete_floating_ip.return_value = False + output = openstack_utils.delete_floating_ip(self.mock_shade_client, + 'floating_ip_id') + self.assertFalse(output) + + @mock.patch.object(openstack_utils, 'log') + def test_delete_floating_ip_exception(self, mock_logger): + self.mock_shade_client.delete_floating_ip.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.delete_floating_ip(self.mock_shade_client, + 'floating_ip_id') + mock_logger.error.assert_called_once() + self.assertFalse(output) + + +class CreateSecurityGroupRuleTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.secgroup_name_or_id = 'sg_name_id' + self.mock_shade_client.create_security_group_rule = mock.Mock() + self._mock_log = mock.patch.object(openstack_utils, 'log') + self.mock_log = self._mock_log.start() + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_log.stop() + + def test_create_security_group_rule(self): + self.mock_shade_client.create_security_group_rule.return_value = ( + {'security_group_rule'}) + output = openstack_utils.create_security_group_rule( + self.mock_shade_client, self.secgroup_name_or_id) + self.assertTrue(output) + + def test_create_security_group_rule_exception(self): + self.mock_shade_client.create_security_group_rule.side_effect = ( + exc.OpenStackCloudException('error message')) + + output = openstack_utils.create_security_group_rule( + self.mock_shade_client, self.secgroup_name_or_id) + self.mock_log.error.assert_called_once() + self.assertFalse(output) + + +class ListImageTestCase(unittest.TestCase): + + def test_list_images(self): + mock_shade_client = mock.MagicMock() + mock_shade_client.list_images.return_value = [] + openstack_utils.list_images(mock_shade_client) + + @mock.patch.object(openstack_utils, 'log') + def test_list_images_exception(self, mock_logger): + mock_shade_client = mock.MagicMock() + mock_shade_client.list_images = mock.MagicMock() + mock_shade_client.list_images.side_effect = ( + exc.OpenStackCloudException('error message')) + images = openstack_utils.list_images(mock_shade_client) + mock_logger.error.assert_called_once() + self.assertFalse(images) + + +class SecurityGroupTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.sg_name = 'sg_name' + self.sg_description = 'sg_description' + self._uuid = uuidutils.generate_uuid() + self._mock_log = mock.patch.object(openstack_utils, 'log') + self.mock_log = self._mock_log.start() + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_log.stop() + + def test_create_security_group_full_existing_security_group(self): + self.mock_shade_client.get_security_group.return_value = ( + {'name': 'name', 'id': self._uuid}) + output = openstack_utils.create_security_group_full( + self.mock_shade_client, self.sg_name, self.sg_description) + self.mock_shade_client.get_security_group.assert_called_once() + self.assertEqual(self._uuid, output) + + def test_create_security_group_full_non_existing_security_group(self): + self.mock_shade_client.get_security_group.return_value = None + self.mock_shade_client.create_security_group.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.create_security_group_full( + self.mock_shade_client, self.sg_name, self.sg_description) + self.mock_log.error.assert_called_once() + self.assertIsNone(output) + + @mock.patch.object(openstack_utils, 'create_security_group_rule') + def test_create_security_group_full_create_rule_fail( + self, mock_create_security_group_rule): + self.mock_shade_client.get_security_group.return_value = None + self.mock_shade_client.create_security_group.return_value = ( + {'name': 'name', 'id': self._uuid}) + mock_create_security_group_rule.return_value = False + output = openstack_utils.create_security_group_full( + self.mock_shade_client, self.sg_name, self.sg_description) + mock_create_security_group_rule.assert_called() + self.mock_shade_client.delete_security_group(self.sg_name) + self.mock_log.error.assert_called_once() + self.assertIsNone(output) + + @mock.patch.object(openstack_utils, 'create_security_group_rule') + def test_create_security_group_full( + self, mock_create_security_group_rule): + self.mock_shade_client.get_security_group.return_value = None + self.mock_shade_client.create_security_group.return_value = ( + {'name': 'name', 'id': self._uuid}) + mock_create_security_group_rule.return_value = True + output = openstack_utils.create_security_group_full( + self.mock_shade_client, self.sg_name, self.sg_description) + mock_create_security_group_rule.assert_called() + self.mock_shade_client.delete_security_group(self.sg_name) + self.assertEqual(self._uuid, output) + + +class CreateInstanceTestCase(unittest.TestCase): + + def test_create_instance_and_wait_for_active(self): + self.mock_shade_client = mock.Mock() + name = 'server_name' + image = 'image_name' + flavor = 'flavor_name' + self.mock_shade_client.create_server.return_value = ( + {'name': name, 'image': image, 'flavor': flavor}) + output = openstack_utils.create_instance_and_wait_for_active( + self.mock_shade_client, name, image, flavor) + self.assertEqual( + {'name': name, 'image': image, 'flavor': flavor}, output) + + @mock.patch.object(openstack_utils, 'log') + def test_create_instance_and_wait_for_active_fail(self, mock_logger): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.create_server.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.create_instance_and_wait_for_active( + self.mock_shade_client, 'server_name', 'image_name', 'flavor_name') + mock_logger.error.assert_called_once() + self.assertIsNone(output) + + +class DeleteInstanceTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + + def test_delete_instance(self): + self.mock_shade_client.delete_server.return_value = True + output = openstack_utils.delete_instance(self.mock_shade_client, + 'instance_name_id') + self.assertTrue(output) + + def test_delete_instance_fail(self): + self.mock_shade_client.delete_server.return_value = False + output = openstack_utils.delete_instance(self.mock_shade_client, + 'instance_name_id') + self.assertFalse(output) + + @mock.patch.object(openstack_utils, 'log') + def test_delete_instance_exception(self, mock_logger): + self.mock_shade_client.delete_server.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.delete_instance(self.mock_shade_client, + 'instance_name_id') + mock_logger.error.assert_called_once() + self.assertFalse(output) + + +class CreateKeypairTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.name = 'key_name' + + def test_create_keypair(self): + self.mock_shade_client.create_keypair.return_value = ( + {'name': 'key-name', 'type': 'ssh'}) + output = openstack_utils.create_keypair( + self.mock_shade_client, self.name) + self.assertEqual( + {'name': 'key-name', 'type': 'ssh'}, + output) + + @mock.patch.object(openstack_utils, 'log') + def test_create_keypair_fail(self, mock_logger): + self.mock_shade_client.create_keypair.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.create_keypair( + self.mock_shade_client, self.name) + mock_logger.error.assert_called_once() + self.assertIsNone(output) + + +class DeleteKeypairTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + + def test_delete_keypair(self): + self.mock_shade_client.delete_keypair.return_value = True + output = openstack_utils.delete_keypair(self.mock_shade_client, + 'key_name') + self.assertTrue(output) + + def test_delete_keypair_fail(self): + self.mock_shade_client.delete_keypair.return_value = False + output = openstack_utils.delete_keypair(self.mock_shade_client, + 'key_name') + self.assertFalse(output) + + @mock.patch.object(openstack_utils, 'log') + def test_delete_keypair_exception(self, mock_logger): + self.mock_shade_client.delete_keypair.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.delete_keypair(self.mock_shade_client, + 'key_name') + mock_logger.error.assert_called_once() + self.assertFalse(output) + + +class AttachVolumeToServerTestCase(unittest.TestCase): + + def test_attach_volume_to_server(self): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.get_server.return_value = {'server_dict'} + self.mock_shade_client.get_volume.return_value = {'volume_dict'} + self.mock_shade_client.attach_volume.return_value = True + output = openstack_utils.attach_volume_to_server( + self.mock_shade_client, 'server_name_or_id', 'volume_name_or_id') + self.assertTrue(output) + + @mock.patch.object(openstack_utils, 'log') + def test_attach_volume_to_server_fail(self, mock_logger): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.attach_volume.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.attach_volume_to_server( + self.mock_shade_client, 'server_name_or_id', 'volume_name_or_id') + mock_logger.error.assert_called_once() + self.assertFalse(output) + + +class GetServerTestCase(unittest.TestCase): + + def test_get_server(self): + self.mock_shade_client = mock.Mock() + _uuid = uuidutils.generate_uuid() + self.mock_shade_client.get_server.return_value = { + 'name': 'server_name', 'id': _uuid} + output = openstack_utils.get_server(self.mock_shade_client, + 'server_name_or_id') + self.assertEqual({'name': 'server_name', 'id': _uuid}, output) + + @mock.patch.object(openstack_utils, 'log') + def test_get_server_exception(self, mock_logger): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.get_server.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.get_server(self.mock_shade_client, + 'server_name_or_id') + mock_logger.error.assert_called_once() + self.assertIsNone(output) + + +class GetFlavorTestCase(unittest.TestCase): + + def test_get_flavor(self): + self.mock_shade_client = mock.Mock() + _uuid = uuidutils.generate_uuid() + self.mock_shade_client.get_flavor.return_value = { + 'name': 'flavor_name', 'id': _uuid} + output = openstack_utils.get_flavor(self.mock_shade_client, + 'flavor_name_or_id') + self.assertEqual({'name': 'flavor_name', 'id': _uuid}, output) + + @mock.patch.object(openstack_utils, 'log') + def test_get_flavor_exception(self, mock_logger): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.get_flavor.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.get_flavor(self.mock_shade_client, + 'flavor_name_or_id') + mock_logger.error.assert_called_once() + self.assertIsNone(output) + + +class GetVolumeIDTestCase(unittest.TestCase): + + def test_get_volume_id(self): + self.mock_shade_client = mock.Mock() + _uuid = uuidutils.generate_uuid() + self.mock_shade_client.get_volume_id.return_value = _uuid + output = openstack_utils.get_volume_id(self.mock_shade_client, + 'volume_name') + self.assertEqual(_uuid, output) + + def test_get_volume_id_None(self): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.get_volume_id.return_value = None + output = openstack_utils.get_volume_id(self.mock_shade_client, + 'volume_name') + self.assertIsNone(output) + + +class GetVolumeTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.get_volume = mock.Mock() + + def test_get_volume(self): + self.mock_shade_client.get_volume.return_value = {'volume'} + output = openstack_utils.get_volume(self.mock_shade_client, + 'volume_name_or_id') + self.assertEqual({'volume'}, output) + + def test_get_volume_None(self): + self.mock_shade_client.get_volume.return_value = None + output = openstack_utils.get_volume(self.mock_shade_client, + 'volume_name_or_id') + self.assertIsNone(output) + + +class CreateVolumeTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.size = 1 + + def test_create_volume(self): + self.mock_shade_client.create_volume.return_value = ( + {'name': 'volume-name', 'size': self.size}) + output = openstack_utils.create_volume( + self.mock_shade_client, self.size) + self.assertEqual( + {'name': 'volume-name', 'size': self.size}, + output) + + @mock.patch.object(openstack_utils, 'log') + def test_create_volume_fail(self, mock_logger): + self.mock_shade_client.create_volume.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.create_volume(self.mock_shade_client, + self.size) + mock_logger.error.assert_called_once() + self.assertIsNone(output) + + +class DeleteVolumeTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + + def test_delete_volume(self): + self.mock_shade_client.delete_volume.return_value = True + output = openstack_utils.delete_volume(self.mock_shade_client, + 'volume_name_or_id') + self.assertTrue(output) + + def test_delete_volume_fail(self): + self.mock_shade_client.delete_volume.return_value = False + output = openstack_utils.delete_volume(self.mock_shade_client, + 'volume_name_or_id') + self.assertFalse(output) + + @mock.patch.object(openstack_utils, 'log') + def test_delete_volume_exception(self, mock_logger): + self.mock_shade_client.delete_volume.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.delete_volume(self.mock_shade_client, + 'volume_name_or_id') + mock_logger.error.assert_called_once() + self.assertFalse(output) + + +class DetachVolumeTestCase(unittest.TestCase): + + @mock.patch.object(openstack_utils, 'get_server') + def test_detach_volume(self, mock_get_server): + self.mock_shade_client = mock.Mock() + mock_get_server.return_value = {'server_dict'} + self.mock_shade_client.get_volume.return_value = {'volume_dict'} + output = openstack_utils.detach_volume(self.mock_shade_client, + 'server_name_or_id', + 'volume_name_or_id') + self.assertTrue(output) + + @mock.patch.object(openstack_utils, 'get_server') + @mock.patch.object(openstack_utils, 'log') + def test_detach_volume_exception(self, mock_logger, mock_get_server): + self.mock_shade_client = mock.Mock() + mock_get_server.return_value = {'server_dict'} + self.mock_shade_client.get_volume.return_value = {'volume_dict'} + self.mock_shade_client.detach_volume.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.detach_volume(self.mock_shade_client, + 'server_name_or_id', + 'volume_name_or_id') + mock_logger.error.assert_called_once() + self.assertFalse(output) + + +class CreateImageTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self._uuid = uuidutils.generate_uuid() + self.name = 'image_name' + self._mock_log = mock.patch.object(openstack_utils, 'log') + self.mock_log = self._mock_log.start() + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_log.stop() + + def test_create_image_already_exit(self): + self.mock_shade_client.get_image_id.return_value = self._uuid + output = openstack_utils.create_image(self.mock_shade_client, self.name) + self.mock_log.info.assert_called_once() + self.assertEqual(self._uuid, output) + + def test_create_image(self): + self.mock_shade_client.get_image_id.return_value = None + self.mock_shade_client.create_image.return_value = {'id': self._uuid} + output = openstack_utils.create_image(self.mock_shade_client, self.name) + self.assertEqual(self._uuid, output) + + def test_create_image_exception(self): + self.mock_shade_client.get_image_id.return_value = None + self.mock_shade_client.create_image.side_effect = ( + exc.OpenStackCloudException('error message')) + + output = openstack_utils.create_image(self.mock_shade_client, + self.name) + self.mock_log.error.assert_called_once() + self.assertIsNone(output) + + +class DeleteImageTestCase(unittest.TestCase): + + def test_delete_image(self): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.delete_image.return_value = True + output = openstack_utils.delete_image(self.mock_shade_client, + 'image_name_or_id') + self.assertTrue(output) + def test_delete_image_fail(self): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.delete_image.return_value = False + output = openstack_utils.delete_image(self.mock_shade_client, + 'image_name_or_id') + self.assertFalse(output) -if __name__ == '__main__': - main() + @mock.patch.object(openstack_utils, 'log') + def test_delete_image_exception(self, mock_logger): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.delete_image.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.delete_image(self.mock_shade_client, + 'image_name_or_id') + mock_logger.error.assert_called_once() + self.assertFalse(output) diff --git a/yardstick/tests/unit/common/test_packages.py b/yardstick/tests/unit/common/test_packages.py new file mode 100644 index 000000000..09d76fe44 --- /dev/null +++ b/yardstick/tests/unit/common/test_packages.py @@ -0,0 +1,88 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +from pip._internal import exceptions as pip_exceptions +from pip._internal.operations import freeze +import unittest + +from yardstick.common import packages + + +class PipExecuteActionTestCase(unittest.TestCase): + + def setUp(self): + self._mock_pip_main = mock.patch.object(packages, '_pip_main') + self.mock_pip_main = self._mock_pip_main.start() + self.mock_pip_main.return_value = 0 + self._mock_freeze = mock.patch.object(freeze, 'freeze') + self.mock_freeze = self._mock_freeze.start() + self.addCleanup(self._cleanup) + + def _cleanup(self): + self._mock_pip_main.stop() + self._mock_freeze.stop() + + def test_pip_execute_action(self): + self.assertEqual(0, packages._pip_execute_action('test_package')) + + def test_remove(self): + self.assertEqual(0, packages._pip_execute_action('test_package', + action='uninstall')) + + def test_install(self): + self.assertEqual(0, packages._pip_execute_action( + 'test_package', action='install', target='temp_dir')) + + def test_pip_execute_action_error(self): + self.mock_pip_main.return_value = 1 + self.assertEqual(1, packages._pip_execute_action('test_package')) + + def test_pip_execute_action_exception(self): + self.mock_pip_main.side_effect = pip_exceptions.PipError + self.assertEqual(1, packages._pip_execute_action('test_package')) + + def test_pip_list(self): + pkg_input = [ + 'XStatic-Rickshaw==1.5.0.0', + 'xvfbwrapper==0.2.9', + '-e git+https://git.opnfv.org/yardstick@50773a24afc02c9652b662ecca' + '2fc5621ea6097a#egg=yardstick', + 'zope.interface==4.4.3' + ] + pkg_dict = { + 'XStatic-Rickshaw': '1.5.0.0', + 'xvfbwrapper': '0.2.9', + 'yardstick': '50773a24afc02c9652b662ecca2fc5621ea6097a', + 'zope.interface': '4.4.3' + } + self.mock_freeze.return_value = pkg_input + + pkg_output = packages.pip_list() + for pkg_name, pkg_version in pkg_output.items(): + self.assertEqual(pkg_dict.get(pkg_name), pkg_version) + + def test_pip_list_single_package(self): + pkg_input = [ + 'XStatic-Rickshaw==1.5.0.0', + 'xvfbwrapper==0.2.9', + '-e git+https://git.opnfv.org/yardstick@50773a24afc02c9652b662ecca' + '2fc5621ea6097a#egg=yardstick', + 'zope.interface==4.4.3' + ] + self.mock_freeze.return_value = pkg_input + + pkg_output = packages.pip_list(pkg_name='xvfbwrapper') + self.assertEqual(1, len(pkg_output)) + self.assertEqual(pkg_output.get('xvfbwrapper'), '0.2.9') diff --git a/yardstick/tests/unit/common/test_process.py b/yardstick/tests/unit/common/test_process.py index 1c6dfec27..e0933c6ac 100644 --- a/yardstick/tests/unit/common/test_process.py +++ b/yardstick/tests/unit/common/test_process.py @@ -90,10 +90,11 @@ class ExecuteTestCase(unittest.TestCase): additional_env=self.additional_env) self.assertEqual(self.stdout, out) - def test_execute_exception(self): + @mock.patch.object(process, 'LOG') + def test_execute_exception(self, *args): self.obj.returncode = self.RET_CODE_WRONG - self.assertRaises(exceptions.ProcessExecutionError, process.execute, - self.input_cmd, additional_env=self.additional_env) + with self.assertRaises(exceptions.ProcessExecutionError): + process.execute(self.input_cmd, additional_env=self.additional_env) self.obj.communicate.assert_called_once_with(None) def test_execute_with_extra_code(self): @@ -107,7 +108,8 @@ class ExecuteTestCase(unittest.TestCase): additional_env=self.additional_env) self.assertEqual(self.stdout, out) - def test_execute_exception_no_check(self): + @mock.patch.object(process, 'LOG') + def test_execute_exception_no_check(self, *args): self.obj.returncode = self.RET_CODE_WRONG out = process.execute(self.input_cmd, additional_env=self.additional_env, diff --git a/yardstick/tests/unit/common/test_template_format.py b/yardstick/tests/unit/common/test_template_format.py index 44aa80333..6e4827e16 100644 --- a/yardstick/tests/unit/common/test_template_format.py +++ b/yardstick/tests/unit/common/test_template_format.py @@ -22,16 +22,21 @@ from yardstick.common import template_format class TemplateFormatTestCase(unittest.TestCase): - def test_parse_to_value_exception(self): + def test_parse_scanner(self): - # TODO(elfoley): Don't hide the error that occurs in - # template_format.parse - # TODO(elfoley): Separate these tests; one per error type with mock.patch.object(yaml, 'load') as yaml_loader: yaml_loader.side_effect = yaml.scanner.ScannerError() self.assertRaises(ValueError, template_format.parse, 'FOOBAR') + + def test_parse_parser(self): + + with mock.patch.object(yaml, 'load') as yaml_loader: yaml_loader.side_effect = yaml.parser.ParserError() self.assertRaises(ValueError, template_format.parse, 'FOOBAR') + + def test_parse_reader(self): + + with mock.patch.object(yaml, 'load') as yaml_loader: yaml_loader.side_effect = \ yaml.reader.ReaderError('', '', '', '', '') self.assertRaises(ValueError, template_format.parse, 'FOOBAR') @@ -45,10 +50,3 @@ class TemplateFormatTestCase(unittest.TestCase): "Resources: {}\n" \ "Outputs: {}" self.assertRaises(ValueError, template_format.parse, yaml2) - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/common/test_utils.py b/yardstick/tests/unit/common/test_utils.py index 452b93a56..8fed5ecf1 100644 --- a/yardstick/tests/unit/common/test_utils.py +++ b/yardstick/tests/unit/common/test_utils.py @@ -7,26 +7,30 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -# Unittest for yardstick.common.utils - -from __future__ import absolute_import - from copy import deepcopy import errno +import importlib import ipaddress from itertools import product, chain -import mock import os +import socket +import time +import threading + +import mock import six from six.moves import configparser import unittest import yardstick -from yardstick.common import utils +from yardstick import ssh from yardstick.common import constants +from yardstick.common import utils +from yardstick.common import exceptions +from yardstick.tests.unit import base as ut_base -class IterSubclassesTestCase(unittest.TestCase): +class IterSubclassesTestCase(ut_base.BaseUnitTestCase): # Disclaimer: this class is a modified copy from # rally/tests/unit/common/plugin/test_discover.py # Copyright 2015: Mirantis Inc. @@ -47,7 +51,7 @@ class IterSubclassesTestCase(unittest.TestCase): self.assertEqual([B, C, D], list(utils.itersubclasses(A))) -class ImportModulesFromPackageTestCase(unittest.TestCase): +class ImportModulesFromPackageTestCase(ut_base.BaseUnitTestCase): @mock.patch('yardstick.common.utils.os.walk') def test_import_modules_from_package_no_mod(self, mock_walk): @@ -60,8 +64,8 @@ class ImportModulesFromPackageTestCase(unittest.TestCase): utils.import_modules_from_package('foo.bar') @mock.patch('yardstick.common.utils.os.walk') - @mock.patch('yardstick.common.utils.importutils') - def test_import_modules_from_package(self, mock_importutils, mock_walk): + @mock.patch.object(importlib, 'import_module') + def test_import_modules_from_package(self, mock_import_module, mock_walk): yardstick_root = os.path.dirname(os.path.dirname(yardstick.__file__)) mock_walk.return_value = ([ @@ -69,10 +73,10 @@ class ImportModulesFromPackageTestCase(unittest.TestCase): ]) utils.import_modules_from_package('foo.bar') - mock_importutils.import_module.assert_called_with('bar.baz') + mock_import_module.assert_called_once_with('bar.baz') -class GetParaFromYaml(unittest.TestCase): +class GetParaFromYaml(ut_base.BaseUnitTestCase): @mock.patch('yardstick.common.utils.os.environ.get') def test_get_param_para_not_found(self, get_env): @@ -96,7 +100,7 @@ class GetParaFromYaml(unittest.TestCase): return file_path -class CommonUtilTestCase(unittest.TestCase): +class CommonUtilTestCase(ut_base.BaseUnitTestCase): def setUp(self): self.data = { @@ -128,15 +132,80 @@ class CommonUtilTestCase(unittest.TestCase): ("=".join(item) for item in sorted(flattened_data.items()))) self.assertEqual(result, line) + def test_get_key_with_default_negative(self): + with self.assertRaises(KeyError): + utils.get_key_with_default({}, 'key1') + + @mock.patch('yardstick.common.utils.open', create=True) + def test_(self, mock_open): + mock_open.side_effect = IOError + + with self.assertRaises(IOError): + utils.find_relative_file('my/path', 'task/path') + + self.assertEqual(mock_open.call_count, 2) + + @mock.patch('yardstick.common.utils.open', create=True) + def test_open_relative_path(self, mock_open): + mock_open_result = mock_open() + mock_open_call_count = 1 # initial call to get result + + self.assertEqual(utils.open_relative_file('foo', 'bar'), mock_open_result) -class TestMacAddressToHex(unittest.TestCase): + mock_open_call_count += 1 # one more call expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + self.assertIn('foo', mock_open.call_args_list[-1][0][0]) + self.assertNotIn('bar', mock_open.call_args_list[-1][0][0]) + + def open_effect(*args, **kwargs): + if kwargs.get('name', args[0]) == os.path.join('bar', 'foo'): + return mock_open_result + raise IOError(errno.ENOENT, 'not found') + + mock_open.side_effect = open_effect + self.assertEqual(utils.open_relative_file('foo', 'bar'), mock_open_result) + + mock_open_call_count += 2 # two more calls expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + self.assertIn('foo', mock_open.call_args_list[-1][0][0]) + self.assertIn('bar', mock_open.call_args_list[-1][0][0]) + + # test an IOError of type ENOENT + mock_open.side_effect = IOError(errno.ENOENT, 'not found') + with self.assertRaises(IOError): + # the second call still raises + utils.open_relative_file('foo', 'bar') + + mock_open_call_count += 2 # two more calls expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + self.assertIn('foo', mock_open.call_args_list[-1][0][0]) + self.assertIn('bar', mock_open.call_args_list[-1][0][0]) + + # test an IOError other than ENOENT + mock_open.side_effect = IOError(errno.EBUSY, 'busy') + with self.assertRaises(IOError): + utils.open_relative_file('foo', 'bar') + + mock_open_call_count += 1 # one more call expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + + +class TestMacAddressToHex(ut_base.BaseUnitTestCase): def test_mac_address_to_hex_list(self): self.assertEqual(utils.mac_address_to_hex_list("ea:3e:e1:9a:99:e8"), ['0xea', '0x3e', '0xe1', '0x9a', '0x99', '0xe8']) + def test_mac_address_to_hex_list_too_short_mac(self): + with self.assertRaises(exceptions.InvalidMacAddress): + utils.mac_address_to_hex_list("ea:3e:e1:9a") + + def test_mac_address_to_hex_list_no_int_mac(self): + with self.assertRaises(exceptions.InvalidMacAddress): + utils.mac_address_to_hex_list("invalid_mac") + -class TranslateToStrTestCase(unittest.TestCase): +class TranslateToStrTestCase(ut_base.BaseUnitTestCase): def test_translate_to_str_unicode(self): input_str = u'hello' @@ -162,7 +231,7 @@ class TranslateToStrTestCase(unittest.TestCase): self.assertIs(input_value, result) -class TestParseCpuInfo(unittest.TestCase): +class TestParseCpuInfo(ut_base.BaseUnitTestCase): def test_single_socket_no_hyperthread(self): cpuinfo = """\ @@ -249,8 +318,8 @@ power management: """ socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) - assert sorted(socket_map.keys()) == [0] - assert sorted(socket_map[0].keys()) == [2, 3, 4] + self.assertEqual(sorted(socket_map.keys()), [0]) + self.assertEqual(sorted(socket_map[0].keys()), [2, 3, 4]) def test_single_socket_hyperthread(self): cpuinfo = """\ @@ -337,11 +406,11 @@ power management: """ socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) - assert sorted(socket_map.keys()) == [0] - assert sorted(socket_map[0].keys()) == [1, 2, 3] - assert sorted(socket_map[0][1]) == [5] - assert sorted(socket_map[0][2]) == [6] - assert sorted(socket_map[0][3]) == [7] + self.assertEqual(sorted(socket_map.keys()), [0]) + self.assertEqual(sorted(socket_map[0].keys()), [1, 2, 3]) + self.assertEqual(sorted(socket_map[0][1]), [5]) + self.assertEqual(sorted(socket_map[0][2]), [6]) + self.assertEqual(sorted(socket_map[0][3]), [7]) def test_dual_socket_hyperthread(self): cpuinfo = """\ @@ -536,15 +605,15 @@ power management: """ socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) - assert sorted(socket_map.keys()) == [0, 1] - assert sorted(socket_map[0].keys()) == [0, 1, 2] - assert sorted(socket_map[1].keys()) == [26, 27, 28] - assert sorted(socket_map[0][0]) == [44] - assert sorted(socket_map[0][1]) == [1] - assert sorted(socket_map[0][2]) == [2] - assert sorted(socket_map[1][26]) == [85] - assert sorted(socket_map[1][27]) == [86] - assert sorted(socket_map[1][28]) == [43, 87] + self.assertEqual(sorted(socket_map.keys()), [0, 1]) + self.assertEqual(sorted(socket_map[0].keys()), [0, 1, 2]) + self.assertEqual(sorted(socket_map[1].keys()), [26, 27, 28]) + self.assertEqual(sorted(socket_map[0][0]), [44]) + self.assertEqual(sorted(socket_map[0][1]), [1]) + self.assertEqual(sorted(socket_map[0][2]), [2]) + self.assertEqual(sorted(socket_map[1][26]), [85]) + self.assertEqual(sorted(socket_map[1][27]), [86]) + self.assertEqual(sorted(socket_map[1][28]), [43, 87]) def test_dual_socket_no_hyperthread(self): cpuinfo = """\ @@ -740,14 +809,14 @@ power management: """ socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) processors = socket_map.processors() - assert processors == [1, 2, 43, 44, 85, 86, 87] + self.assertEqual(processors, [1, 2, 43, 44, 85, 86, 87]) cores = socket_map.cores() - assert cores == [0, 1, 2, 26, 27, 28] + self.assertEqual(cores, [0, 1, 2, 26, 27, 28]) sockets = socket_map.sockets() - assert sockets == [0, 1] + self.assertEqual(sockets, [0, 1]) -class ChangeObjToDictTestCase(unittest.TestCase): +class ChangeObjToDictTestCase(ut_base.BaseUnitTestCase): def test_change_obj_to_dict(self): class A(object): @@ -760,7 +829,7 @@ class ChangeObjToDictTestCase(unittest.TestCase): self.assertEqual(obj_r, obj_s) -class SetDictValueTestCase(unittest.TestCase): +class SetDictValueTestCase(ut_base.BaseUnitTestCase): def test_set_dict_value(self): input_dic = { @@ -770,7 +839,7 @@ class SetDictValueTestCase(unittest.TestCase): self.assertEqual(output_dic.get('welcome', {}).get('to'), 'yardstick') -class RemoveFileTestCase(unittest.TestCase): +class RemoveFileTestCase(ut_base.BaseUnitTestCase): def test_remove_file(self): try: @@ -780,7 +849,83 @@ class RemoveFileTestCase(unittest.TestCase): self.assertTrue(isinstance(e, OSError)) -class TestUtils(unittest.TestCase): +class ParseIniFileTestCase(ut_base.BaseUnitTestCase): + + def setUp(self): + self._mock_config_parser_type = mock.patch.object(configparser, + 'ConfigParser') + self.mock_config_parser_type = self._mock_config_parser_type.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_config_parser_type.stop() + + def test_parse_ini_file(self): + defaults = {'default1': 'value1', + 'default2': 'value2'} + s1 = {'key1': 'value11', + 'key2': 'value22'} + s2 = {'key1': 'value123', + 'key2': 'value234'} + + mock_config_parser = mock.Mock() + self.mock_config_parser_type.return_value = mock_config_parser + mock_config_parser.read.return_value = True + mock_config_parser.sections.return_value = ['s1', 's2'] + mock_config_parser.items.side_effect = iter([ + defaults.items(), + s1.items(), + s2.items(), + ]) + + expected = {'DEFAULT': defaults, + 's1': s1, + 's2': s2} + result = utils.parse_ini_file('my_path') + self.assertDictEqual(expected, result) + + @mock.patch.object(utils, 'logger') + def test_parse_ini_file_missing_section_header(self, *args): + mock_config_parser = mock.Mock() + self.mock_config_parser_type.return_value = mock_config_parser + mock_config_parser.read.side_effect = ( + configparser.MissingSectionHeaderError( + mock.Mock(), 321, mock.Mock())) + + with self.assertRaises(configparser.MissingSectionHeaderError): + utils.parse_ini_file('my_path') + + def test_parse_ini_file_no_file(self): + mock_config_parser = mock.Mock() + self.mock_config_parser_type.return_value = mock_config_parser + mock_config_parser.read.return_value = False + with self.assertRaises(RuntimeError): + utils.parse_ini_file('my_path') + + def test_parse_ini_file_no_default_section_header(self): + s1 = {'key1': 'value11', + 'key2': 'value22'} + s2 = {'key1': 'value123', + 'key2': 'value234'} + + mock_config_parser = mock.Mock() + self.mock_config_parser_type.return_value = mock_config_parser + mock_config_parser.read.return_value = True + mock_config_parser.sections.return_value = ['s1', 's2'] + mock_config_parser.items.side_effect = iter([ + configparser.NoSectionError(mock.Mock()), + s1.items(), + s2.items(), + ]) + + expected = {'DEFAULT': {}, + 's1': s1, + 's2': s2} + result = utils.parse_ini_file('my_path') + self.assertDictEqual(expected, result) + + +class TestUtils(ut_base.BaseUnitTestCase): @mock.patch('yardstick.common.utils.os.makedirs') def test_makedirs(self, *_): @@ -837,7 +982,7 @@ class TestUtils(unittest.TestCase): os.environ.clear() os.environ.update(base_env) - @mock.patch('yardstick.common.utils.configparser.ConfigParser') + @mock.patch.object(configparser, 'ConfigParser') def test_parse_ini_file(self, mock_config_parser_type): defaults = { 'default1': 'value1', @@ -869,23 +1014,26 @@ class TestUtils(unittest.TestCase): result = utils.parse_ini_file('my_path') self.assertDictEqual(result, expected) - @mock.patch('yardstick.common.utils.configparser.ConfigParser') - def test_parse_ini_file_missing_section_header(self, mock_config_parser_type): + @mock.patch.object(utils, 'logger') + @mock.patch.object(configparser, 'ConfigParser') + def test_parse_ini_file_missing_section_header( + self, mock_config_parser_type, *args): mock_config_parser = mock_config_parser_type() - mock_config_parser.read.side_effect = \ - configparser.MissingSectionHeaderError(mock.Mock(), 321, mock.Mock()) + mock_config_parser.read.side_effect = ( + configparser.MissingSectionHeaderError(mock.Mock(), 321, + mock.Mock())) with self.assertRaises(configparser.MissingSectionHeaderError): utils.parse_ini_file('my_path') - @mock.patch('yardstick.common.utils.configparser.ConfigParser') + @mock.patch.object(configparser, 'ConfigParser') def test_parse_ini_file_no_file(self, mock_config_parser_type): mock_config_parser = mock_config_parser_type() mock_config_parser.read.return_value = False with self.assertRaises(RuntimeError): utils.parse_ini_file('my_path') - @mock.patch('yardstick.common.utils.configparser.ConfigParser') + @mock.patch.object(configparser, 'ConfigParser') def test_parse_ini_file_no_default_section_header(self, mock_config_parser_type): s1 = { 'key1': 'value11', @@ -931,16 +1079,8 @@ class TestUtils(unittest.TestCase): with self.assertRaises(RuntimeError): utils.validate_non_string_sequence(1, raise_exc=RuntimeError) - def test_error_class(self): - with self.assertRaises(RuntimeError): - utils.ErrorClass() - - error_instance = utils.ErrorClass(test='') - with self.assertRaises(AttributeError): - error_instance.get_name() - -class TestUtilsIpAddrMethods(unittest.TestCase): +class TestUtilsIpAddrMethods(ut_base.BaseUnitTestCase): GOOD_IP_V4_ADDRESS_STR_LIST = [ u'0.0.0.0', @@ -983,6 +1123,28 @@ class TestUtilsIpAddrMethods(unittest.TestCase): u'123:4567:89ab:cdef:123:4567:89ab:cdef/129', ] + def test_make_ipv4_address(self): + for addr in self.GOOD_IP_V4_ADDRESS_STR_LIST: + # test with no mask + expected = ipaddress.IPv4Address(addr) + self.assertEqual(utils.make_ipv4_address(addr), expected, addr) + + def test_make_ipv4_address_error(self): + addr_list = self.INVALID_IP_ADDRESS_STR_LIST +\ + self.GOOD_IP_V6_ADDRESS_STR_LIST + for addr in addr_list: + self.assertRaises(Exception, utils.make_ipv4_address, addr) + + def test_get_ip_range_count(self): + iprange = "192.168.0.1-192.168.0.25" + count = utils.get_ip_range_count(iprange) + self.assertEqual(count, 24) + + def test_get_ip_range_start(self): + iprange = "192.168.0.1-192.168.0.25" + start = utils.get_ip_range_start(iprange) + self.assertEqual(start, "192.168.0.1") + def test_safe_ip_address(self): addr_list = self.GOOD_IP_V4_ADDRESS_STR_LIST for addr in addr_list: @@ -1066,8 +1228,22 @@ class TestUtilsIpAddrMethods(unittest.TestCase): for value in chain(value_iter, self.INVALID_IP_ADDRESS_STR_LIST): self.assertEqual(utils.ip_to_hex(value), value) + def test_get_mask_from_ip_range_ipv4(self): + ip_str = '1.1.1.1' + for mask in range(8, 30): + ip = ipaddress.ip_network(ip_str + '/' + str(mask), strict=False) + result = utils.get_mask_from_ip_range(ip[2], ip[-2]) + self.assertEqual(mask, result) + + def test_get_mask_from_ip_range_ipv6(self): + ip_str = '2001::1' + for mask in range(8, 120): + ip = ipaddress.ip_network(ip_str + '/' + str(mask), strict=False) + result = utils.get_mask_from_ip_range(ip[2], ip[-2]) + self.assertEqual(mask, result) + -class SafeDecodeUtf8TestCase(unittest.TestCase): +class SafeDecodeUtf8TestCase(ut_base.BaseUnitTestCase): @unittest.skipIf(six.PY2, 'This test should only be launched with Python 3.x') @@ -1078,8 +1254,210 @@ class SafeDecodeUtf8TestCase(unittest.TestCase): self.assertEqual('this is a byte array', out) -def main(): - unittest.main() +class ReadMeminfoTestCase(ut_base.BaseUnitTestCase): + + MEMINFO = (b'MemTotal: 65860500 kB\n' + b'MemFree: 28690900 kB\n' + b'MemAvailable: 52873764 kB\n' + b'Active(anon): 3015676 kB\n' + b'HugePages_Total: 8\n' + b'Hugepagesize: 1048576 kB') + MEMINFO_DICT = {'MemTotal': '65860500', + 'MemFree': '28690900', + 'MemAvailable': '52873764', + 'Active(anon)': '3015676', + 'HugePages_Total': '8', + 'Hugepagesize': '1048576'} + + def test_read_meminfo(self): + ssh_client = ssh.SSH('user', 'host') + with mock.patch.object(ssh_client, 'get_file_obj') as \ + mock_get_client, \ + mock.patch.object(six, 'BytesIO', + return_value=six.BytesIO(self.MEMINFO)): + output = utils.read_meminfo(ssh_client) + mock_get_client.assert_called_once_with('/proc/meminfo', mock.ANY) + self.assertEqual(self.MEMINFO_DICT, output) + + +class TimerTestCase(ut_base.BaseUnitTestCase): + + def test__getattr(self): + with utils.Timer() as timer: + time.sleep(1) + self.assertEqual(1, round(timer.total_seconds(), 0)) + self.assertEqual(1, timer.delta.seconds) + + def test__enter_with_timeout(self): + with utils.Timer(timeout=10) as timer: + time.sleep(1) + self.assertEqual(1, round(timer.total_seconds(), 0)) + + def test__enter_with_timeout_exception(self): + with self.assertRaises(exceptions.TimerTimeout): + with utils.Timer(timeout=1): + time.sleep(2) + + def test__enter_with_timeout_no_exception(self): + with utils.Timer(timeout=1, raise_exception=False): + time.sleep(2) + + def test__iter(self): + iterations = [] + for i in utils.Timer(timeout=2): + iterations.append(i) + time.sleep(1.1) + self.assertEqual(2, len(iterations)) + + def test_delta_time_sec(self): + with utils.Timer() as timer: + self.assertIsInstance(timer.delta_time_sec(), float) + + +class WaitUntilTrueTestCase(ut_base.BaseUnitTestCase): + + def test_no_timeout(self): + self.assertIsNone(utils.wait_until_true(lambda: True, + timeout=1, sleep=1)) + + def test_timeout_generic_exception(self): + with self.assertRaises(exceptions.WaitTimeout): + self.assertIsNone(utils.wait_until_true(lambda: False, + timeout=1, sleep=1)) + + def test_timeout_given_exception(self): + class MyTimeoutException(exceptions.YardstickException): + message = 'My timeout exception' + + with self.assertRaises(MyTimeoutException): + self.assertIsNone( + utils.wait_until_true(lambda: False, timeout=1, sleep=1, + exception=MyTimeoutException)) + + def _run_thread(self): + with self.assertRaises(exceptions.WaitTimeout): + utils.wait_until_true(lambda: False, timeout=1, sleep=1) + + def test_timeout_no_main_thread(self): + new_thread = threading.Thread(target=self._run_thread) + new_thread.start() + new_thread.join(timeout=3) + + +class SendSocketCommandTestCase(unittest.TestCase): + + @mock.patch.object(socket, 'socket') + def test_execute_correct(self, mock_socket): + mock_socket_obj = mock.Mock() + mock_socket_obj.connect_ex.return_value = 0 + mock_socket.return_value = mock_socket_obj + self.assertEqual(0, utils.send_socket_command('host', 22, 'command')) + mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM) + mock_socket_obj.connect_ex.assert_called_once_with(('host', 22)) + mock_socket_obj.sendall.assert_called_once_with(six.b('command')) + mock_socket_obj.close.assert_called_once() + + @mock.patch.object(socket, 'socket') + def test_execute_exception(self, mock_socket): + mock_socket_obj = mock.Mock() + mock_socket_obj.connect_ex.return_value = 0 + mock_socket.return_value = mock_socket_obj + mock_socket_obj.sendall.side_effect = socket.error + self.assertEqual(1, utils.send_socket_command('host', 22, 'command')) + mock_socket.assert_called_once_with(socket.AF_INET, socket.SOCK_STREAM) + mock_socket_obj.connect_ex.assert_called_once_with(('host', 22)) + mock_socket_obj.sendall.assert_called_once_with(six.b('command')) + mock_socket_obj.close.assert_called_once() + + +class GetPortMacTestCase(unittest.TestCase): + + def setUp(self): + self.ssh_client = mock.Mock() + self.ssh_client.execute.return_value = (0, 'foo ', '') -if __name__ == '__main__': - main() + def test_ssh_client_execute_called(self): + utils.get_port_mac(self.ssh_client, 99) + self.ssh_client.execute.assert_called_once_with( + "ifconfig |grep HWaddr |grep 99 |awk '{print $5}' ", + raise_on_error=True) + + def test_return_value(self): + self.assertEqual('foo', utils.get_port_mac(self.ssh_client, 99)) + + +class GetPortIPTestCase(unittest.TestCase): + + def setUp(self): + self.ssh_client = mock.Mock() + self.ssh_client.execute.return_value = (0, 'foo ', '') + + def test_ssh_client_execute_called(self): + utils.get_port_ip(self.ssh_client, 99) + self.ssh_client.execute.assert_called_once_with( + "ifconfig 99 |grep 'inet addr' |awk '{print $2}' |cut -d ':' -f2 ", + raise_on_error=True) + + def test_return_value(self): + self.assertEqual('foo', utils.get_port_ip(self.ssh_client, 99)) + + +class SafeCaseTestCase(unittest.TestCase): + + def test_correct_type_int(self): + self.assertEqual(35, utils.safe_cast('35', int, 0)) + + def test_correct_int_as_string(self): + self.assertEqual(25, utils.safe_cast('25', 'int', 0)) + + def test_incorrect_type_as_string(self): + with self.assertRaises(exceptions.InvalidType): + utils.safe_cast('100', 'intt', 0) + + def test_default_value(self): + self.assertEqual(0, utils.safe_cast('', 'int', 0)) + + +class SetupHugepagesTestCase(unittest.TestCase): + + @mock.patch.object(six, 'BytesIO', return_value=six.BytesIO(b'5\n')) + @mock.patch.object(utils, 'read_meminfo', + return_value={'Hugepagesize': '1024'}) + def test_setup_hugepages(self, mock_meminfo, *args): + ssh = mock.Mock() + ssh.execute = mock.Mock() + hp_size_kb, hp_number, hp_number_set = utils.setup_hugepages(ssh, 10 * 1024) + mock_meminfo.assert_called_once_with(ssh) + ssh.execute.assert_called_once_with( + 'echo 10 | sudo tee /proc/sys/vm/nr_hugepages') + self.assertEqual(hp_size_kb, 1024) + self.assertEqual(hp_number, 10) + self.assertEqual(hp_number_set, 5) + + +class GetOSSampleInfoTestCase(unittest.TestCase): + + def test_get_os_version(self, *args): + ssh = mock.Mock() + ssh.execute.return_value = (0, "18.04", "") + utils.get_os_version(ssh) + ssh.execute.assert_called_once_with("cat /etc/lsb-release") + + def test_get_kernel_version(self, *args): + ssh = mock.Mock() + ssh.execute.return_value = (0, "Linux", "") + utils.get_kernel_version(ssh) + ssh.execute.assert_called_once_with("uname -a") + + def test_get_sample_vnf_info(self, *args): + json_out = """ + {"UDP_Replay": { + "branch_commit": "47123bfc1b3c0d0b01884aebbce1a3e09ad7ddb0", + "md5": "4577702f6d6848380bd912232a1b9ca5", + "path_vnf": "/opt/nsb_bin/UDP_Replay" + } + }""" + json_file = '/opt/nsb_bin/yardstick_sample_vnf.json' + ssh = mock.Mock() + ssh.execute.return_value = (0, json_out, "") + utils.get_sample_vnf_info(ssh, json_file) diff --git a/yardstick/tests/unit/common/test_yaml_loader.py b/yardstick/tests/unit/common/test_yaml_loader.py index 90cbb8157..e621dcbc5 100644 --- a/yardstick/tests/unit/common/test_yaml_loader.py +++ b/yardstick/tests/unit/common/test_yaml_loader.py @@ -12,7 +12,6 @@ # yardstick: this file is copied from python-heatclient and slightly modified -from __future__ import absolute_import import unittest from yardstick.common import yaml_loader @@ -22,11 +21,4 @@ class TemplateFormatTestCase(unittest.TestCase): def test_parse_to_value_exception(self): - self.assertEquals(yaml_loader.yaml_load("string"), u"string") - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() + self.assertEqual(yaml_loader.yaml_load("string"), u"string") diff --git a/yardstick/tests/unit/dispatcher/__init__.py b/yardstick/tests/unit/dispatcher/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/dispatcher/__init__.py diff --git a/yardstick/tests/unit/dispatcher/test_influxdb.py b/yardstick/tests/unit/dispatcher/test_influxdb.py new file mode 100644 index 000000000..c844d4bbb --- /dev/null +++ b/yardstick/tests/unit/dispatcher/test_influxdb.py @@ -0,0 +1,104 @@ +############################################################################## +# Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +# Unittest for yardstick.dispatcher.influxdb + +import mock +import unittest + +from yardstick.dispatcher.influxdb import InfluxdbDispatcher +from yardstick import _init_logging + + +_init_logging() + + +class InfluxdbDispatcherTestCase(unittest.TestCase): + + def setUp(self): + self.data1 = { + "runner_id": 8921, + "context_cfg": { + "host": { + "ip": "10.229.43.154", + "key_filename": + "/root/yardstick/yardstick/resources/files" + "/yardstick_key", + "name": "kvm.LF", + "user": "root" + }, + "target": { + "ipaddr": "10.229.44.134" + } + }, + "scenario_cfg": { + "runner": { + "interval": 1, + "object": "yardstick.benchmark.scenarios.networking.ping" + ".Ping", + "output_filename": "/tmp/yardstick.out", + "runner_id": 8921, + "duration": 10, + "type": "Duration" + }, + "host": "kvm.LF", + "type": "Ping", + "target": "10.229.44.134", + "sla": { + "action": "monitor", + "max_rtt": 10 + }, + "tc": "ping", + "task_id": "ea958583-c91e-461a-af14-2a7f9d7f79e7" + } + } + self.data2 = { + "benchmark": { + "timestamp": "1451478117.883505", + "errors": "", + "data": { + "rtt": 0.613 + }, + "sequence": 1 + }, + "runner_id": 8921 + } + + self.yardstick_conf = {'dispatcher_influxdb': {}} + + @mock.patch('yardstick.dispatcher.influxdb.requests') + def test_record_result_data(self, mock_requests): + type(mock_requests.post.return_value).status_code = 204 + influxdb = InfluxdbDispatcher(self.yardstick_conf) + data = { + 'status': 1, + 'result': { + 'criteria': 'PASS', + 'info': { + }, + 'task_id': 'b9e2bbc2-dfd8-410d-8c24-07771e9f7979', + 'testcases': { + } + } + } + self.assertEqual(influxdb.flush_result_data(data), 0) + + def test__get_nano_timestamp(self): + influxdb = InfluxdbDispatcher(self.yardstick_conf) + results = {'timestamp': '1451461248.925574'} + self.assertEqual(influxdb._get_nano_timestamp(results), + '1451461248925574144') + + @mock.patch('yardstick.dispatcher.influxdb.time') + def test__get_nano_timestamp_except(self, mock_time): + results = {} + influxdb = InfluxdbDispatcher(self.yardstick_conf) + mock_time.time.return_value = 1451461248.925574 + self.assertEqual(influxdb._get_nano_timestamp(results), + '1451461248925574144') diff --git a/yardstick/tests/unit/dispatcher/test_influxdb_line_protocol.py b/yardstick/tests/unit/dispatcher/test_influxdb_line_protocol.py new file mode 100644 index 000000000..641e97fae --- /dev/null +++ b/yardstick/tests/unit/dispatcher/test_influxdb_line_protocol.py @@ -0,0 +1,63 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +# Unittest for yardstick.dispatcher.influxdb_line_protocol + +# yardstick comment: this file is a modified copy of +# influxdb-python/influxdb/tests/test_line_protocol.py + +import unittest +from third_party.influxdb.influxdb_line_protocol import make_lines + + +class TestLineProtocol(unittest.TestCase): + + def test_make_lines(self): + data = { + "tags": { + "empty_tag": "", + "none_tag": None, + "integer_tag": 2, + "string_tag": "hello" + }, + "points": [ + { + "measurement": "test", + "fields": { + "string_val": "hello!", + "int_val": 1, + "float_val": 1.1, + "none_field": None, + "bool_val": True, + } + } + ] + } + + self.assertEqual( + make_lines(data), + 'test,integer_tag=2,string_tag=hello ' + 'bool_val=True,float_val=1.1,int_val=1i,string_val="hello!"\n' + ) + + def test_string_val_newline(self): + data = { + "points": [ + { + "measurement": "m1", + "fields": { + "multi_line": "line1\nline1\nline3" + } + } + ] + } + + self.assertEqual( + make_lines(data), + 'm1 multi_line="line1\\nline1\\nline3"\n' + ) diff --git a/yardstick/tests/unit/network_services/__init__.py b/yardstick/tests/unit/network_services/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/network_services/__init__.py diff --git a/yardstick/tests/unit/network_services/collector/__init__.py b/yardstick/tests/unit/network_services/collector/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/network_services/collector/__init__.py diff --git a/yardstick/tests/unit/network_services/collector/test_publisher.py b/yardstick/tests/unit/network_services/collector/test_publisher.py new file mode 100644 index 000000000..145441ddd --- /dev/null +++ b/yardstick/tests/unit/network_services/collector/test_publisher.py @@ -0,0 +1,36 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest + +from yardstick.network_services.collector import publisher + + +class PublisherTestCase(unittest.TestCase): + + def setUp(self): + self.test_publisher = publisher.Publisher() + + def test_successful_init(self): + pass + + def test_unsuccessful_init(self): + pass + + def test_start(self): + self.assertIsNone(self.test_publisher.start()) + + def test_stop(self): + self.assertIsNone(self.test_publisher.stop()) diff --git a/yardstick/tests/unit/network_services/collector/test_subscriber.py b/yardstick/tests/unit/network_services/collector/test_subscriber.py new file mode 100644 index 000000000..cffa4d492 --- /dev/null +++ b/yardstick/tests/unit/network_services/collector/test_subscriber.py @@ -0,0 +1,120 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import mock +import unittest + +from yardstick.network_services.collector import subscriber +from yardstick import ssh + + +class MockVnfAprrox(object): + + def __init__(self): + self.result = {} + self.name = "vnf__1" + + def collect_kpi(self): + self.result = { + 'pkt_in_up_stream': 100, + 'pkt_drop_up_stream': 5, + 'pkt_in_down_stream': 50, + 'pkt_drop_down_stream': 40 + } + return self.result + + +class CollectorTestCase(unittest.TestCase): + + NODES = { + 'context1': [{'name': 'node1', + 'ip': '1.2.3.4', + 'collectd': { + 'plugins': {'abc': 12, 'def': 34}, + 'interval': 987} + } + ] + } + + def setUp(self): + vnf = MockVnfAprrox() + vnf.start_collect = mock.Mock() + vnf.stop_collect = mock.Mock() + self.ssh_patch = mock.patch.object(ssh, 'AutoConnectSSH') + mock_ssh = self.ssh_patch.start() + mock_instance = mock.Mock() + mock_instance.execute.return_value = 0, '', '' + mock_ssh.from_node.return_value = mock_instance + self.collector = subscriber.Collector([vnf], self.NODES) + + def tearDown(self): + self.ssh_patch.stop() + + def test___init__(self, *args): + vnf = MockVnfAprrox() + collector = subscriber.Collector([vnf], self.NODES) + self.assertEqual(len(collector.vnfs), 1) + self.assertEqual(len(collector.nodes), 1) + + def test___init__no_node_information(self, *args): + vnf = MockVnfAprrox() + nodes = copy.deepcopy(self.NODES) + nodes['context1'].append(None) + collector = subscriber.Collector([vnf], nodes) + self.assertEqual(len(collector.vnfs), 1) + self.assertEqual(len(collector.nodes), 1) + + def test___init__no_node_information_in_context(self, *args): + vnf = MockVnfAprrox() + nodes = copy.deepcopy(self.NODES) + nodes['context1'] = None + collector = subscriber.Collector([vnf], nodes) + self.assertEqual(len(collector.vnfs), 1) + self.assertEqual(len(collector.nodes), 1) + + def test_start(self, *args): + resource_profile = mock.MagicMock() + self.collector.resource_profiles = {'key': resource_profile} + self.collector.bin_path = 'path' + + self.assertIsNone(self.collector.start()) + for vnf in self.collector.vnfs: + vnf.start_collect.assert_called_once() + + for resource_profile in self.collector.resource_profiles.values(): + resource_profile.initiate_systemagent.assert_called_once_with('path') + resource_profile.start.assert_called_once() + resource_profile.amqp_process_for_nfvi_kpi.assert_called_once() + + def test_stop(self, *_): + resource_profile = mock.MagicMock() + self.collector.resource_profiles = {'key': resource_profile} + + self.assertIsNone(self.collector.stop()) + for vnf in self.collector.vnfs: + vnf.stop_collect.assert_called_once() + + for resource in self.collector.resource_profiles.values(): + resource.stop.assert_called_once() + + def test_get_kpi(self, *args): + result = self.collector.get_kpi() + + self.assertEqual(2, len(result)) + self.assertEqual(4, len(result["vnf__1"])) + self.assertEqual(result["vnf__1"]["pkt_in_up_stream"], 100) + self.assertEqual(result["vnf__1"]["pkt_drop_up_stream"], 5) + self.assertEqual(result["vnf__1"]["pkt_in_down_stream"], 50) + self.assertEqual(result["vnf__1"]["pkt_drop_down_stream"], 40) diff --git a/yardstick/tests/unit/network_services/helpers/__init__.py b/yardstick/tests/unit/network_services/helpers/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/__init__.py diff --git a/yardstick/tests/unit/network_services/helpers/acl_vnf_topology_ixia.yaml b/yardstick/tests/unit/network_services/helpers/acl_vnf_topology_ixia.yaml new file mode 100644 index 000000000..f60834fbd --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/acl_vnf_topology_ixia.yaml @@ -0,0 +1,50 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +nsd:nsd-catalog: + nsd: + - id: VACL + name: VACL + short-name: VACL + description: scenario with VACL,L3fwd and VNF + constituent-vnfd: + - member-vnf-index: '1' + vnfd-id-ref: tg__1 + VNF model: ../../vnf_descriptors/ixia_rfc2544_tpl.yaml + - member-vnf-index: '2' + vnfd-id-ref: vnf__1 + VNF model: ../../vnf_descriptors/acl_vnf.yaml + + vld: + - id: uplink_1 + name: tg__1 to vnf__1 link 1 + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: tg__1 #TREX + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: xe0 + vnfd-id-ref: vnf__1 #VNF + + - id: downlink_1 + name: vnf__1 to tg__1 link 2 + type: ELAN + vnfd-connection-point-ref: + - member-vnf-index-ref: '2' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: vnf__1 #L3fwd + - member-vnf-index-ref: '1' + vnfd-connection-point-ref: xe1 + vnfd-id-ref: tg__1 #VACL VNF diff --git a/yardstick/tests/unit/network_services/helpers/test_cpu.py b/yardstick/tests/unit/network_services/helpers/test_cpu.py new file mode 100644 index 000000000..a1c0826fb --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/test_cpu.py @@ -0,0 +1,215 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from __future__ import division +import unittest +import mock +import subprocess + +from yardstick.network_services.helpers.cpu import \ + CpuSysCores + + +class TestCpuSysCores(unittest.TestCase): + + def setUp(self): + self._mock_ssh = mock.patch("yardstick.ssh.SSH") + self.mock_ssh = self._mock_ssh.start() + + self.addCleanup(self._cleanup) + + def _cleanup(self): + self._mock_ssh.stop() + + def test___init__(self): + self.mock_ssh.execute.return_value = 1, "", "" + self.mock_ssh.put.return_value = 1, "", "" + cpu_topo = CpuSysCores(self.mock_ssh) + self.assertIsNotNone(cpu_topo.connection) + + def test__get_core_details(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(1, "", "")) + ssh_mock.put = \ + mock.Mock(return_value=(1, "", "")) + cpu_topo = CpuSysCores(ssh_mock) + subprocess.check_output = mock.Mock(return_value=0) + lines = ["cpu:1", "topo:2", ""] + self.assertEqual([{'topo': '2', 'cpu': '1'}], + cpu_topo._get_core_details(lines)) + + def test_get_core_socket(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(1, "cpu:1\ntest:2\n \n", "")) + ssh_mock.put = \ + mock.Mock(return_value=(1, "", "")) + cpu_topo = CpuSysCores(ssh_mock) + subprocess.check_output = mock.Mock(return_value=0) + cpu_topo._get_core_details = \ + mock.Mock(side_effect=[[{'Core(s) per socket': '2', 'Thread(s) per core': '1'}], + [{'physical id': '2', 'processor': '1'}]]) + self.assertEqual({'thread_per_core': '1', '2': ['1'], + 'cores_per_socket': '2'}, + cpu_topo.get_core_socket()) + + def test_validate_cpu_cfg(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(1, "cpu:1\ntest:2\n \n", "")) + ssh_mock.put = \ + mock.Mock(return_value=(1, "", "")) + cpu_topo = CpuSysCores(ssh_mock) + subprocess.check_output = mock.Mock(return_value=0) + cpu_topo._get_core_details = \ + mock.Mock(side_effect=[[{'Core(s) per socket': '2', 'Thread(s) per core': '1'}], + [{'physical id': '2', 'processor': '1'}]]) + cpu_topo.core_map = \ + {'thread_per_core': '1', '2': ['1'], 'cores_per_socket': '2'} + self.assertEqual(-1, cpu_topo.validate_cpu_cfg()) + + def test_validate_cpu_cfg_2t(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(1, "cpu:1\ntest:2\n \n", "")) + ssh_mock.put = \ + mock.Mock(return_value=(1, "", "")) + cpu_topo = CpuSysCores(ssh_mock) + subprocess.check_output = mock.Mock(return_value=0) + cpu_topo._get_core_details = \ + mock.Mock(side_effect=[[{'Core(s) per socket': '2', 'Thread(s) per core': '1'}], + [{'physical id': '2', 'processor': '1'}]]) + cpu_topo.core_map = \ + {'thread_per_core': 1, '2': ['1'], 'cores_per_socket': '2'} + vnf_cfg = {'lb_config': 'SW', 'lb_count': 1, 'worker_config': + '1C/2T', 'worker_threads': 1} + self.assertEqual(-1, cpu_topo.validate_cpu_cfg(vnf_cfg)) + + def test_validate_cpu_cfg_fail(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(1, "cpu:1\ntest:2\n \n", "")) + ssh_mock.put = \ + mock.Mock(return_value=(1, "", "")) + cpu_topo = CpuSysCores(ssh_mock) + subprocess.check_output = mock.Mock(return_value=0) + cpu_topo._get_core_details = \ + mock.Mock(side_effect=[[{'Core(s) per socket': '2', 'Thread(s) per core': '1'}], + [{'physical id': '2', 'processor': '1'}]]) + cpu_topo.core_map = \ + {'thread_per_core': 1, '2': [1], 'cores_per_socket': 2} + vnf_cfg = {'lb_config': 'SW', 'lb_count': 1, 'worker_config': + '1C/1T', 'worker_threads': 1} + self.assertEqual(-1, cpu_topo.validate_cpu_cfg(vnf_cfg)) + + def test_get_cpu_layout(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock( + return_value=(1, "# CPU,Core,Socket,Node,,L1d,L1i,L2,L3\n'" + "0,0,0,0,,0,0,0,0\n" + "1,1,0,0,,1,1,1,0\n", "")) + ssh_mock.put = \ + mock.Mock(return_value=(1, "", "")) + cpu_topo = CpuSysCores(ssh_mock) + subprocess.check_output = mock.Mock(return_value=0) + self.assertEqual({'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]}, + cpu_topo.get_cpu_layout()) + + def test__str2int(self): + self.assertEqual(1, CpuSysCores._str2int("1")) + + def test__str2int_error(self): + self.assertEqual(0, CpuSysCores._str2int("err")) + + def test_smt_enabled(self): + self.assertEqual(False, CpuSysCores.smt_enabled( + {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]})) + + def test_is_smt_enabled(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + self.assertEqual(False, cpu_topo.is_smt_enabled()) + + def test_cpu_list_per_node(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + self.assertEqual([0, 1], cpu_topo.cpu_list_per_node(0, False)) + + def test_cpu_list_per_node_error(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'err': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + with self.assertRaises(RuntimeError) as raised: + cpu_topo.cpu_list_per_node(0, False) + self.assertIn('Node cpuinfo not available.', str(raised.exception)) + + def test_cpu_list_per_node_smt_error(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + with self.assertRaises(RuntimeError) as raised: + cpu_topo.cpu_list_per_node(0, True) + self.assertIn('SMT is not enabled.', str(raised.exception)) + + def test_cpu_slice_of_list_per_node(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + self.assertEqual([1], + cpu_topo.cpu_slice_of_list_per_node(0, 1, 0, + False)) + + def test_cpu_slice_of_list_per_node_error(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + with self.assertRaises(RuntimeError) as raised: + cpu_topo.cpu_slice_of_list_per_node(1, 1, 1, False) + self.assertIn('cpu_cnt + skip_cnt > length(cpu list).', + str(raised.exception)) + + def test_cpu_list_per_node_str(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + cpu_topo = CpuSysCores(ssh_mock) + cpu_topo.cpuinfo = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0, 0], + [1, 1, 0, 0, 0, 1, 1, 1, 0]]} + self.assertEqual("1", + cpu_topo.cpu_list_per_node_str(0, 1, 1, ',', + False)) diff --git a/yardstick/tests/unit/network_services/helpers/test_dpdkbindnic_helper.py b/yardstick/tests/unit/network_services/helpers/test_dpdkbindnic_helper.py new file mode 100644 index 000000000..e19311613 --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/test_dpdkbindnic_helper.py @@ -0,0 +1,632 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +import unittest + +import os + +from yardstick.common import exceptions +from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkInterface +from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkNode +from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkBindHelper +from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkBindHelperException +from yardstick.network_services.helpers.dpdkbindnic_helper import NETWORK_KERNEL +from yardstick.network_services.helpers.dpdkbindnic_helper import NETWORK_DPDK +from yardstick.network_services.helpers.dpdkbindnic_helper import CRYPTO_KERNEL +from yardstick.network_services.helpers.dpdkbindnic_helper import CRYPTO_DPDK +from yardstick.network_services.helpers.dpdkbindnic_helper import NETWORK_OTHER +from yardstick.network_services.helpers.dpdkbindnic_helper import CRYPTO_OTHER + + +NAME = "tg_0" + + +class TestDpdkInterface(unittest.TestCase): + + SAMPLE_NETDEVS = { + 'enp11s0': { + 'address': '0a:de:ad:be:ef:f5', + 'device': '0x1533', + 'driver': 'igb', + 'ifindex': '2', + 'interface_name': 'enp11s0', + 'operstate': 'down', + 'pci_bus_id': '0000:0b:00.0', + 'subsystem_device': '0x1533', + 'subsystem_vendor': '0x15d9', + 'vendor': '0x8086' + }, + 'lan': { + 'address': '0a:de:ad:be:ef:f4', + 'device': '0x153a', + 'driver': 'e1000e', + 'ifindex': '3', + 'interface_name': 'lan', + 'operstate': 'up', + 'pci_bus_id': '0000:00:19.0', + 'subsystem_device': '0x153a', + 'subsystem_vendor': '0x15d9', + 'vendor': '0x8086' + } + } + + SAMPLE_VM_NETDEVS = { + 'eth1': { + 'address': 'fa:de:ad:be:ef:5b', + 'device': '0x0001', + 'driver': 'virtio_net', + 'ifindex': '3', + 'interface_name': 'eth1', + 'operstate': 'down', + 'pci_bus_id': '0000:00:04.0', + 'vendor': '0x1af4' + } + } + + def test_parse_netdev_info(self): + output = """\ +/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/ifindex:2 +/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/address:0a:de:ad:be:ef:f5 +/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/operstate:down +/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/vendor:0x8086 +/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/device:0x1533 +/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/subsystem_vendor:0x15d9 +/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/subsystem_device:0x1533 +/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/driver:igb +/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/pci_bus_id:0000:0b:00.0 +/sys/devices/pci0000:00/0000:00:19.0/net/lan/ifindex:3 +/sys/devices/pci0000:00/0000:00:19.0/net/lan/address:0a:de:ad:be:ef:f4 +/sys/devices/pci0000:00/0000:00:19.0/net/lan/operstate:up +/sys/devices/pci0000:00/0000:00:19.0/net/lan/device/vendor:0x8086 +/sys/devices/pci0000:00/0000:00:19.0/net/lan/device/device:0x153a +/sys/devices/pci0000:00/0000:00:19.0/net/lan/device/subsystem_vendor:0x15d9 +/sys/devices/pci0000:00/0000:00:19.0/net/lan/device/subsystem_device:0x153a +/sys/devices/pci0000:00/0000:00:19.0/net/lan/driver:e1000e +/sys/devices/pci0000:00/0000:00:19.0/net/lan/pci_bus_id:0000:00:19.0 +""" + res = DpdkBindHelper.parse_netdev_info(output) + self.assertDictEqual(res, self.SAMPLE_NETDEVS) + + def test_parse_netdev_info_virtio(self): + output = """\ +/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/ifindex:3 +/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/address:fa:de:ad:be:ef:5b +/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/operstate:down +/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/device/vendor:0x1af4 +/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/device/device:0x0001 +/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/driver:virtio_net +""" + res = DpdkBindHelper.parse_netdev_info(output) + self.assertDictEqual(res, self.SAMPLE_VM_NETDEVS) + + def test_probe_missing_values(self): + mock_dpdk_node = mock.Mock() + mock_dpdk_node.netdevs = self.SAMPLE_NETDEVS.copy() + + interface = {'local_mac': '0a:de:ad:be:ef:f5'} + dpdk_intf = DpdkInterface(mock_dpdk_node, interface) + + dpdk_intf.probe_missing_values() + self.assertEqual(interface['vpci'], '0000:0b:00.0') + + interface['local_mac'] = '0a:de:ad:be:ef:f4' + dpdk_intf.probe_missing_values() + self.assertEqual(interface['vpci'], '0000:00:19.0') + + def test_probe_missing_values_no_update(self): + mock_dpdk_node = mock.Mock() + mock_dpdk_node.netdevs = self.SAMPLE_NETDEVS.copy() + del mock_dpdk_node.netdevs['enp11s0']['driver'] + del mock_dpdk_node.netdevs['lan']['driver'] + + interface = {'local_mac': '0a:de:ad:be:ef:f5'} + dpdk_intf = DpdkInterface(mock_dpdk_node, interface) + + dpdk_intf.probe_missing_values() + self.assertNotIn('vpci', interface) + self.assertNotIn('driver', interface) + + def test_probe_missing_values_negative(self): + mock_dpdk_node = mock.Mock() + mock_dpdk_node.netdevs.values.side_effect = ( + exceptions.IncorrectNodeSetup(error_msg='')) + + interface = {'local_mac': '0a:de:ad:be:ef:f5'} + dpdk_intf = DpdkInterface(mock_dpdk_node, interface) + + with self.assertRaises(exceptions.IncorrectConfig): + dpdk_intf.probe_missing_values() + + +class TestDpdkNode(unittest.TestCase): + + INTERFACES = [ + {'name': 'name1', + 'virtual-interface': { + 'local_mac': 404, + 'vpci': 'pci10', + }}, + {'name': 'name2', + 'virtual-interface': { + 'local_mac': 404, + 'vpci': 'pci2', + }}, + {'name': 'name3', + 'virtual-interface': { + 'local_mac': 404, + 'vpci': 'some-pci1', + }}, + ] + + def test_probe_dpdk_drivers(self): + mock_ssh_helper = mock.Mock() + mock_ssh_helper.execute.return_value = 0, '', '' + + interfaces = [ + {'name': 'name1', + 'virtual-interface': { + 'local_mac': 404, + 'vpci': 'pci10', + }}, + {'name': 'name2', + 'virtual-interface': { + 'local_mac': 404, + 'vpci': 'pci2', + }}, + {'name': 'name3', + 'virtual-interface': { + 'local_mac': 404, + 'vpci': 'some-pci1', + }}, + ] + + dpdk_node = DpdkNode(NAME, interfaces, mock_ssh_helper) + dpdk_helper = dpdk_node.dpdk_helper + + dpdk_helper.probe_real_kernel_drivers = mock.Mock() + dpdk_helper.real_kernel_interface_driver_map = { + 'pci1': 'driver1', + 'pci2': 'driver2', + 'pci3': 'driver3', + 'pci4': 'driver1', + 'pci6': 'driver3', + } + + dpdk_node._probe_dpdk_drivers() + self.assertNotIn('driver', interfaces[0]['virtual-interface']) + self.assertEqual(interfaces[1]['virtual-interface']['driver'], 'driver2') + self.assertEqual(interfaces[2]['virtual-interface']['driver'], 'driver1') + + def test_check(self): + def update(): + if not mock_force_rebind.called: + raise exceptions.IncorrectConfig(error_msg='') + + interfaces[0]['virtual-interface'].update({ + 'vpci': '0000:01:02.1', + 'local_ip': '10.20.30.40', + 'netmask': '255.255.0.0', + 'driver': 'ixgbe', + }) + + mock_ssh_helper = mock.Mock() + mock_ssh_helper.execute.return_value = 0, '', '' + + interfaces = [ + {'name': 'name1', + 'virtual-interface': { + 'local_mac': 404, + }}, + ] + + dpdk_node = DpdkNode(NAME, interfaces, mock_ssh_helper) + dpdk_node._probe_missing_values = mock_probe_missing = mock.Mock(side_effect=update) + dpdk_node._force_rebind = mock_force_rebind = mock.Mock() + + self.assertIsNone(dpdk_node.check()) + self.assertEqual(mock_probe_missing.call_count, 2) + + @mock.patch('yardstick.network_services.helpers.dpdkbindnic_helper.DpdkInterface') + def test_check_negative(self, mock_intf_type): + mock_ssh_helper = mock.Mock() + mock_ssh_helper.execute.return_value = 0, '', '' + + mock_intf_type().check.side_effect = exceptions.SSHError + + dpdk_node = DpdkNode(NAME, self.INTERFACES, mock_ssh_helper) + + with self.assertRaises(exceptions.IncorrectSetup): + dpdk_node.check() + + def test_probe_netdevs(self): + mock_ssh_helper = mock.Mock() + mock_ssh_helper.execute.return_value = 0, '', '' + + expected = {'key1': 500, 'key2': 'hello world'} + update = {'key1': 1000, 'key3': []} + + dpdk_node = DpdkNode(NAME, self.INTERFACES, mock_ssh_helper) + dpdk_helper = dpdk_node.dpdk_helper + dpdk_helper.find_net_devices = mock.Mock(side_effect=[expected, update]) + + self.assertDictEqual(dpdk_node.netdevs, {}) + dpdk_node._probe_netdevs() + self.assertDictEqual(dpdk_node.netdevs, expected) + + expected = {'key1': 1000, 'key2': 'hello world', 'key3': []} + dpdk_node._probe_netdevs() + self.assertDictEqual(dpdk_node.netdevs, expected) + + def test_probe_netdevs_setup_negative(self): + mock_ssh_helper = mock.Mock() + mock_ssh_helper.execute.return_value = 0, '', '' + + dpdk_node = DpdkNode(NAME, self.INTERFACES, mock_ssh_helper) + dpdk_helper = dpdk_node.dpdk_helper + dpdk_helper.find_net_devices = mock.Mock(side_effect=DpdkBindHelperException) + + with self.assertRaises(DpdkBindHelperException): + dpdk_node._probe_netdevs() + + def test_force_rebind(self): + mock_ssh_helper = mock.Mock() + mock_ssh_helper.execute.return_value = 0, '', '' + + dpdk_node = DpdkNode(NAME, self.INTERFACES, mock_ssh_helper) + dpdk_helper = dpdk_node.dpdk_helper + dpdk_helper.force_dpdk_rebind = mock_helper_func = mock.Mock() + + dpdk_node._force_rebind() + mock_helper_func.assert_called_once() + + +class TestDpdkBindHelper(unittest.TestCase): + bin_path = "/opt/nsb_bin" + EXAMPLE_OUTPUT = """ + +Network devices using DPDK-compatible driver +============================================ +0000:00:04.0 'Virtio network device' drv=igb_uio unused= +0000:00:05.0 'Virtio network device' drv=igb_uio unused= + +Network devices using kernel driver +=================================== +0000:00:03.0 'Virtio network device' if=ens3 drv=virtio-pci unused=igb_uio *Active* + +Other network devices +===================== +<none> + +Crypto devices using DPDK-compatible driver +=========================================== +<none> + +Crypto devices using kernel driver +================================== +<none> + +Other crypto devices +==================== +<none> +""" + + PARSED_EXAMPLE = { + NETWORK_DPDK: [ + {'active': False, + 'dev_type': 'Virtio network device', + 'driver': 'igb_uio', + 'iface': None, + 'unused': '', + 'vpci': '0000:00:04.0', + }, + {'active': False, + 'dev_type': 'Virtio network device', + 'driver': 'igb_uio', + 'iface': None, + 'unused': '', + 'vpci': '0000:00:05.0', + } + ], + NETWORK_KERNEL: [ + {'active': True, + 'dev_type': 'Virtio network device', + 'driver': 'virtio-pci', + 'iface': 'ens3', + 'unused': 'igb_uio', + 'vpci': '0000:00:03.0', + } + ], + CRYPTO_KERNEL: [], + CRYPTO_DPDK: [], + NETWORK_OTHER: [], + CRYPTO_OTHER: [], + } + + CLEAN_STATUS = { + NETWORK_KERNEL: [], + NETWORK_DPDK: [], + CRYPTO_KERNEL: [], + CRYPTO_DPDK: [], + NETWORK_OTHER: [], + CRYPTO_OTHER: [], + } + + ONE_INPUT_LINE = ("0000:00:03.0 'Virtio network device' if=ens3 " + "drv=virtio-pci unused=igb_uio *Active*") + + ONE_INPUT_LINE_PARSED = [{ + 'vpci': '0000:00:03.0', + 'dev_type': 'Virtio network device', + 'iface': 'ens3', + 'driver': 'virtio-pci', + 'unused': 'igb_uio', + 'active': True, + }] + + def test___init__(self): + conn = mock.Mock() + conn.provision_tool = mock.Mock(return_value='path_to_tool') + conn.join_bin_path.return_value = os.path.join(self.bin_path, DpdkBindHelper.DPDK_DEVBIND) + + dpdk_bind_helper = DpdkBindHelper(conn) + + self.assertEqual(conn, dpdk_bind_helper.ssh_helper) + self.assertEqual(self.CLEAN_STATUS, dpdk_bind_helper.dpdk_status) + self.assertIsNone(dpdk_bind_helper.status_nic_row_re) + self.assertEqual(dpdk_bind_helper.dpdk_devbind, + os.path.join(self.bin_path, dpdk_bind_helper.DPDK_DEVBIND)) + self.assertIsNone(dpdk_bind_helper._status_cmd_attr) + + def test__dpdk_execute(self): + conn = mock.Mock() + conn.execute = mock.Mock(return_value=(0, 'output', 'error')) + conn.provision_tool = mock.Mock(return_value='tool_path') + dpdk_bind_helper = DpdkBindHelper(conn) + self.assertEqual((0, 'output', 'error'), dpdk_bind_helper._dpdk_execute('command')) + + def test__dpdk_execute_failure(self): + conn = mock.Mock() + conn.execute = mock.Mock(return_value=(1, 'output', 'error')) + conn.provision_tool = mock.Mock(return_value='tool_path') + dpdk_bind_helper = DpdkBindHelper(conn) + with self.assertRaises(DpdkBindHelperException): + dpdk_bind_helper._dpdk_execute('command') + + def test__addline(self): + conn = mock.Mock() + + dpdk_bind_helper = DpdkBindHelper(conn) + + dpdk_bind_helper._add_line(NETWORK_KERNEL, self.ONE_INPUT_LINE) + + self.assertIsNotNone(dpdk_bind_helper.dpdk_status) + self.assertEqual(self.ONE_INPUT_LINE_PARSED, dpdk_bind_helper.dpdk_status[NETWORK_KERNEL]) + + def test__switch_active_dict_by_header(self): + line = "Crypto devices using DPDK-compatible driver" + olddict = 'olddict' + self.assertEqual(CRYPTO_DPDK, DpdkBindHelper._switch_active_dict(line, olddict)) + + def test__switch_active_dict_by_header_empty(self): + line = "<none>" + olddict = 'olddict' + self.assertEqual(olddict, DpdkBindHelper._switch_active_dict(line, olddict)) + + def test_parse_dpdk_status_output(self): + conn = mock.Mock() + + dpdk_bind_helper = DpdkBindHelper(conn) + + dpdk_bind_helper._parse_dpdk_status_output(self.EXAMPLE_OUTPUT) + + self.maxDiff = None + self.assertEqual(self.PARSED_EXAMPLE, dpdk_bind_helper.dpdk_status) + + def test_kernel_bound_pci_addresses(self): + mock_ssh_helper = mock.Mock() + mock_ssh_helper.execute.return_value = 0, '', '' + + expected = ['a', 'b', 3] + + dpdk_helper = DpdkBindHelper(mock_ssh_helper) + dpdk_helper.dpdk_status = { + NETWORK_DPDK: [{'vpci': 4}, {'vpci': 5}, {'vpci': 'g'}], + NETWORK_KERNEL: [{'vpci': 'a'}, {'vpci': 'b'}, {'vpci': 3}], + CRYPTO_DPDK: [], + } + + result = dpdk_helper.kernel_bound_pci_addresses + self.assertEqual(result, expected) + + def test_find_net_devices_negative(self): + mock_ssh_helper = mock.Mock() + mock_ssh_helper.execute.return_value = 1, 'error', 'debug' + + dpdk_helper = DpdkBindHelper(mock_ssh_helper) + + self.assertDictEqual(dpdk_helper.find_net_devices(), {}) + + def test_read_status(self): + conn = mock.Mock() + conn.execute = mock.Mock(return_value=(0, self.EXAMPLE_OUTPUT, '')) + conn.provision_tool = mock.Mock(return_value='path_to_tool') + + dpdk_bind_helper = DpdkBindHelper(conn) + + self.assertEqual(self.PARSED_EXAMPLE, dpdk_bind_helper.read_status()) + + def test__get_bound_pci_addresses(self): + conn = mock.Mock() + + dpdk_bind_helper = DpdkBindHelper(conn) + + dpdk_bind_helper._parse_dpdk_status_output(self.EXAMPLE_OUTPUT) + + self.assertEqual(['0000:00:04.0', '0000:00:05.0'], + dpdk_bind_helper._get_bound_pci_addresses(NETWORK_DPDK)) + self.assertEqual(['0000:00:03.0'], + dpdk_bind_helper._get_bound_pci_addresses(NETWORK_KERNEL)) + + def test_interface_driver_map(self): + conn = mock.Mock() + + dpdk_bind_helper = DpdkBindHelper(conn) + + dpdk_bind_helper._parse_dpdk_status_output(self.EXAMPLE_OUTPUT) + + self.assertEqual({'0000:00:04.0': 'igb_uio', + '0000:00:03.0': 'virtio-pci', + '0000:00:05.0': 'igb_uio', + }, + dpdk_bind_helper.interface_driver_map) + + def test_bind(self): + conn = mock.Mock() + conn.execute = mock.Mock(return_value=(0, '', '')) + conn.join_bin_path.return_value = os.path.join(self.bin_path, DpdkBindHelper.DPDK_DEVBIND) + + dpdk_bind_helper = DpdkBindHelper(conn) + dpdk_bind_helper.read_status = mock.Mock() + + dpdk_bind_helper.bind(['0000:00:03.0', '0000:00:04.0'], 'my_driver') + + conn.execute.assert_called_with('sudo /opt/nsb_bin/dpdk-devbind.py --force ' + '-b my_driver 0000:00:03.0 0000:00:04.0') + dpdk_bind_helper.read_status.assert_called_once() + + def test_bind_single_pci(self): + conn = mock.Mock() + conn.execute = mock.Mock(return_value=(0, '', '')) + conn.join_bin_path.return_value = os.path.join(self.bin_path, DpdkBindHelper.DPDK_DEVBIND) + + dpdk_bind_helper = DpdkBindHelper(conn) + dpdk_bind_helper.read_status = mock.Mock() + + dpdk_bind_helper.bind('0000:00:03.0', 'my_driver') + + conn.execute.assert_called_with('sudo /opt/nsb_bin/dpdk-devbind.py --force ' + '-b my_driver 0000:00:03.0') + dpdk_bind_helper.read_status.assert_called_once() + + def test_rebind_drivers(self): + conn = mock.Mock() + + dpdk_bind_helper = DpdkBindHelper(conn) + + dpdk_bind_helper.bind = mock.Mock() + dpdk_bind_helper.used_drivers = { + 'd1': ['0000:05:00.0'], + 'd3': ['0000:05:01.0', '0000:05:02.0'], + } + + dpdk_bind_helper.rebind_drivers() + + dpdk_bind_helper.bind.assert_any_call(['0000:05:00.0'], 'd1', True) + dpdk_bind_helper.bind.assert_any_call(['0000:05:01.0', '0000:05:02.0'], 'd3', True) + + def test_save_used_drivers(self): + conn = mock.Mock() + dpdk_bind_helper = DpdkBindHelper(conn) + dpdk_bind_helper.dpdk_status = self.PARSED_EXAMPLE + + dpdk_bind_helper.save_used_drivers() + + expected = { + 'igb_uio': ['0000:00:04.0', '0000:00:05.0'], + 'virtio-pci': ['0000:00:03.0'], + } + + self.assertDictEqual(expected, dpdk_bind_helper.used_drivers) + + def test_force_dpdk_rebind(self): + mock_ssh_helper = mock.Mock() + mock_ssh_helper.execute.return_value = 0, '', '' + + dpdk_helper = DpdkBindHelper(mock_ssh_helper, 'driver2') + dpdk_helper.dpdk_status = { + NETWORK_DPDK: [ + { + 'vpci': 'pci1', + }, + { + 'vpci': 'pci3', + }, + { + 'vpci': 'pci6', + }, + { + 'vpci': 'pci3', + }, + ] + } + dpdk_helper.real_kernel_interface_driver_map = { + 'pci1': 'real_driver1', + 'pci2': 'real_driver2', + 'pci3': 'real_driver1', + 'pci4': 'real_driver4', + 'pci6': 'real_driver6', + } + dpdk_helper.load_dpdk_driver = mock.Mock() + dpdk_helper.read_status = mock.Mock() + dpdk_helper.save_real_kernel_interface_driver_map = mock.Mock() + dpdk_helper.save_used_drivers = mock.Mock() + dpdk_helper.bind = mock_bind = mock.Mock() + + dpdk_helper.force_dpdk_rebind() + self.assertEqual(mock_bind.call_count, 2) + + def test_save_real_kernel_drivers(self): + mock_ssh_helper = mock.Mock() + mock_ssh_helper.execute.return_value = 0, '', '' + + dpdk_helper = DpdkBindHelper(mock_ssh_helper) + dpdk_helper.real_kernel_drivers = { + 'abc': '123', + } + dpdk_helper.real_kernel_interface_driver_map = { + 'abc': 'AAA', + 'def': 'DDD', + 'abs': 'AAA', + 'ghi': 'GGG', + } + + # save_used_drivers must be called before save_real_kernel_drivers can be + with self.assertRaises(AttributeError): + dpdk_helper.save_real_kernel_drivers() + + dpdk_helper.save_used_drivers() + + expected_used_drivers = { + 'AAA': ['abc', 'abs'], + 'DDD': ['def'], + 'GGG': ['ghi'], + } + dpdk_helper.save_real_kernel_drivers() + self.assertDictEqual(dpdk_helper.used_drivers, expected_used_drivers) + self.assertDictEqual(dpdk_helper.real_kernel_drivers, {}) + + def test_get_real_kernel_driver(self): + mock_ssh_helper = mock.Mock() + mock_ssh_helper.execute.side_effect = [ + (0, 'non-matching text', ''), + (0, 'pre Kernel modules: real_driver1', ''), + (0, 'before Ethernet middle Virtio network device after', ''), + ] + + dpdk_helper = DpdkBindHelper(mock_ssh_helper) + + self.assertIsNone(dpdk_helper.get_real_kernel_driver('abc')) + self.assertEqual(dpdk_helper.get_real_kernel_driver('abc'), 'real_driver1') + self.assertEqual(dpdk_helper.get_real_kernel_driver('abc'), DpdkBindHelper.VIRTIO_DRIVER) diff --git a/yardstick/tests/unit/network_services/helpers/test_iniparser.py b/yardstick/tests/unit/network_services/helpers/test_iniparser.py new file mode 100644 index 000000000..1a09f0761 --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/test_iniparser.py @@ -0,0 +1,223 @@ +# Copyright (c) 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest +from contextlib import contextmanager +import mock + +from yardstick.tests import STL_MOCKS + + +STLClient = mock.MagicMock() +stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) +stl_patch.start() + +if stl_patch: + from yardstick.network_services.helpers.iniparser import ParseError + from yardstick.network_services.helpers.iniparser import LineParser + from yardstick.network_services.helpers.iniparser import BaseParser + from yardstick.network_services.helpers.iniparser import ConfigParser + +PARSE_TEXT_1 = """\ + +[section1] +key1=value1 +list1: value2 + value3 + value4 +key3='single quote value' ; comment here +key4= + +[section2] ; comment with #2 other symbol +# here is a comment line +list2: value5 +key with no value # mixed comment ; symbols +; another comment line +key5= + +[section1] ; reopen a section! +key2="double quote value" +""" + +PARSE_TEXT_2 = """\ +[section1] +list1 = item1 + item2 + ended by eof""" + +PARSE_TEXT_BAD_1 = """\ +key1=value1 +""" + +PARSE_TEXT_BAD_2 = """\ +[section1 +""" + +PARSE_TEXT_BAD_3 = """\ +[] +""" + +PARSE_TEXT_BAD_4 = """\ +[section1] + bad continuation +""" + +PARSE_TEXT_BAD_5 = """\ +[section1] +=value with no key +""" + + +class TestParseError(unittest.TestCase): + + def test___str__(self): + error = ParseError('a', 2, 'c') + self.assertEqual(str(error), "at line 2, a: 'c'") + + +class TestLineParser(unittest.TestCase): + + def test___repr__(self): + line_parser = LineParser('', 101) + self.assertIsNotNone(repr(line_parser)) + + def test_error_invalid_assignment(self): + line_parser = LineParser('', 101) + self.assertIsNotNone(line_parser.error_invalid_assignment()) + + +class TestBaseParser(unittest.TestCase): + + @staticmethod + def make_open(text_blob): + @contextmanager + def internal_open(*args): + yield text_blob.split('\n') + + return internal_open + + def test_parse(self): + parser = BaseParser() + parser.parse() + + def test_parse_empty_string(self): + parser = BaseParser() + self.assertIsNone(parser.parse('')) + + def test_not_implemented_methods(self): + parser = BaseParser() + + with self.assertRaises(NotImplementedError): + parser.assignment('key', 'value', LineParser('', 100)) + + with self.assertRaises(NotImplementedError): + parser.new_section('section') + + with self.assertRaises(NotImplementedError): + parser.comment('comment') + + +class TestConfigParser(unittest.TestCase): + + @staticmethod + def make_open(text_blob): + @contextmanager + def internal_open(*args): + yield text_blob.split('\n') + + return internal_open + + @mock.patch('yardstick.network_services.helpers.iniparser.open') + def test_parse(self, mock_open): + mock_open.side_effect = self.make_open(PARSE_TEXT_1) + + existing_data = [['section0', [['key0', 'value0']]]] + config_parser = ConfigParser('my_file', existing_data) + config_parser.parse() + + expected = [ + [ + 'section0', + [ + ['key0', 'value0'], + ], + ], + [ + 'section1', + [ + ['key1', 'value1'], + ['list1', 'value2\nvalue3\nvalue4'], + ['key3', 'single quote value'], + ['key4', ''], + ['key2', 'double quote value'], + ], + ], + [ + 'section2', + [ + ['list2', 'value5'], + ['key with no value', '@'], + ['key5', ''], + ], + ], + ] + + self.assertEqual(config_parser.sections, expected) + self.assertIsNotNone(config_parser.find_section('section1')) + self.assertIsNone(config_parser.find_section('section3')) + self.assertEqual(config_parser.find_section_index('section1'), 1) + self.assertEqual(config_parser.find_section_index('section3'), -1) + + @mock.patch('yardstick.network_services.helpers.iniparser.open') + def test_parse_2(self, mock_open): + mock_open.side_effect = self.make_open(PARSE_TEXT_2) + + config_parser = ConfigParser('my_file') + config_parser.parse() + + expected = [ + [ + 'section1', + [ + ['list1', 'item1\nitem2\nended by eof'], + ], + ], + ] + + self.assertEqual(config_parser.sections, expected) + + @mock.patch('yardstick.network_services.helpers.iniparser.open') + def test_parse_negative(self, mock_open): + bad_text_dict = { + 'no section': PARSE_TEXT_BAD_1, + 'incomplete section': PARSE_TEXT_BAD_2, + 'empty section name': PARSE_TEXT_BAD_3, + 'bad_continuation': PARSE_TEXT_BAD_4, + 'value with no key': PARSE_TEXT_BAD_5, + } + + for bad_reason, bad_text in bad_text_dict.items(): + mock_open.side_effect = self.make_open(bad_text) + + config_parser = ConfigParser('my_file', []) + + try: + # TODO: replace with assertRaises, when the UT framework supports + # advanced messages when exceptions fail to occur + config_parser.parse() + except ParseError: + pass + else: + self.fail('\n'.join([bad_reason, bad_text, str(config_parser.sections)])) diff --git a/yardstick/tests/unit/network_services/helpers/test_samplevnf_helper.py b/yardstick/tests/unit/network_services/helpers/test_samplevnf_helper.py new file mode 100644 index 000000000..e66e7fbb8 --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/test_samplevnf_helper.py @@ -0,0 +1,1044 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import mock +import os +import six +import unittest + +from yardstick.network_services.helpers import samplevnf_helper +from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper + + +class TestPortPairs(unittest.TestCase): + def test_port_pairs_list(self): + vnfd = TestMultiPortConfig.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + interfaces = vnfd['vdu'][0]['external-interface'] + port_pairs = samplevnf_helper.PortPairs(interfaces) + self.assertEqual(port_pairs.port_pair_list, [("xe0", "xe1")]) + + def test_valid_networks(self): + vnfd = TestMultiPortConfig.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + interfaces = vnfd['vdu'][0]['external-interface'] + port_pairs = samplevnf_helper.PortPairs(interfaces) + self.assertEqual(port_pairs.valid_networks, [ + ("uplink_0", "downlink_0")]) + + def test_all_ports(self): + vnfd = TestMultiPortConfig.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + interfaces = vnfd['vdu'][0]['external-interface'] + port_pairs = samplevnf_helper.PortPairs(interfaces) + self.assertEqual(set(port_pairs.all_ports), {"xe0", "xe1"}) + + def test_uplink_ports(self): + vnfd = TestMultiPortConfig.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + interfaces = vnfd['vdu'][0]['external-interface'] + port_pairs = samplevnf_helper.PortPairs(interfaces) + self.assertEqual(port_pairs.uplink_ports, ["xe0"]) + + def test_downlink_ports(self): + vnfd = TestMultiPortConfig.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + interfaces = vnfd['vdu'][0]['external-interface'] + port_pairs = samplevnf_helper.PortPairs(interfaces) + self.assertEqual(port_pairs.downlink_ports, ["xe1"]) + + +class TestMultiPortConfig(unittest.TestCase): + + VNFD_0 = {'short-name': 'VpeVnf', + 'vdu': + [{'routing_table': + [{'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0'}, + {'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1'}], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': + [{'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'id': 'vpevnf-baremetal', + 'external-interface': + [ + {'virtual-interface': + { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'ifname': 'xe0', + 'local_iface_name': 'eth0', + 'local_mac': '00:00:00:00:00:02', + 'vld_id': 'uplink_0', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'ifname': 'xe1', + 'local_iface_name': 'eth1', + 'local_mac': '00:00:00:00:00:01', + 'vld_id': 'downlink_0', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'} + ]}], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': + {'vdu-id': 'vpevnf-baremetal', + 'host': '1.2.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.2.1.1'}, + 'benchmark': + {'kpi': ['packets_in', 'packets_fwd', 'packets_dropped']}, + 'connection-point': [{'type': 'VPORT', 'name': 'xe0'}, + {'type': 'VPORT', 'name': 'xe1'}], + 'id': 'AclApproxVnf', 'name': 'VPEVnfSsh'} + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ] + } + } + + def setUp(self): + self._mock_open = mock.patch.object(six.moves.builtins, 'open') + self.mock_open = self._mock_open.start() + self._mock_config_parser = mock.patch.object( + samplevnf_helper, 'ConfigParser') + self.mock_config_parser = self._mock_config_parser.start() + + self.addCleanup(self._cleanup) + + def _cleanup(self): + self._mock_open.stop() + self._mock_config_parser.stop() + + def test_validate_ip_and_prefixlen(self): + ip_addr, prefix_len = ( + samplevnf_helper.MultiPortConfig.validate_ip_and_prefixlen( + '10.20.30.40', '16')) + self.assertEqual(ip_addr, '10.20.30.40') + self.assertEqual(prefix_len, 16) + + ip_addr, prefix_len = ( + samplevnf_helper.MultiPortConfig.validate_ip_and_prefixlen( + '::1', '40')) + self.assertEqual(ip_addr, '0000:0000:0000:0000:0000:0000:0000:0001') + self.assertEqual(prefix_len, 40) + + def test_validate_ip_and_prefixlen_negative(self): + with self.assertRaises(AttributeError): + samplevnf_helper.MultiPortConfig.validate_ip_and_prefixlen('', '') + + with self.assertRaises(AttributeError): + samplevnf_helper.MultiPortConfig.validate_ip_and_prefixlen( + '10.20.30.400', '16') + + with self.assertRaises(AttributeError): + samplevnf_helper.MultiPortConfig.validate_ip_and_prefixlen( + '10.20.30.40', '33') + + with self.assertRaises(AttributeError): + samplevnf_helper.MultiPortConfig.validate_ip_and_prefixlen( + '::1', '129') + + @mock.patch.object(os.path, 'isfile', return_value=False) + def test___init__(self, *args): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + self.assertEqual(0, opnfv_vnf.swq) + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + self.assertEqual(0, opnfv_vnf.swq) + + def test_update_timer(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.get_config_tpl_data = mock.MagicMock() + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.update_write_parser = mock.MagicMock() + self.assertIsNone(opnfv_vnf.update_timer()) + + def test_generate_script(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = VnfdHelper(self.VNFD_0) + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.get_config_tpl_data = mock.MagicMock() + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.update_write_parser = mock.MagicMock() + opnfv_vnf.generate_script_data = \ + mock.Mock(return_value={'link_config': 0, 'arp_config': '', + 'arp_config6': '', 'actions': '', + 'arp_route_tbl': '', 'arp_route_tbl6': '', + 'flows': ''}) + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + self.assertIsNotNone(opnfv_vnf.generate_script(self.VNFD)) + opnfv_vnf.lb_config = 'HW' + self.assertIsNotNone(opnfv_vnf.generate_script(self.VNFD)) + + def test_generate_script_data(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.get_config_tpl_data = mock.MagicMock() + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.update_write_parser = mock.MagicMock() + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.vnf_type = 'ACL' + opnfv_vnf.generate_link_config = mock.Mock() + opnfv_vnf.generate_arp_config = mock.Mock() + opnfv_vnf.generate_arp_config6 = mock.Mock() + opnfv_vnf.generate_action_config = mock.Mock() + opnfv_vnf.generate_rule_config = mock.Mock() + self.assertIsNotNone(opnfv_vnf.generate_script_data()) + + def test_generate_arp_config6(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.get_config_tpl_data = mock.MagicMock() + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.update_write_parser = mock.MagicMock() + opnfv_vnf.generate_script_data = \ + mock.Mock(return_value={'link_config': 0, 'arp_config': '', + 'arp_config6': '', 'actions': '', + 'rules': ''}) + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.get_port_pairs = mock.Mock() + opnfv_vnf.vnf_type = 'VFW' + opnfv_vnf.get_ports_gateway = mock.Mock(return_value=u'1.1.1.1') + opnfv_vnf.get_netmask_gateway = mock.Mock( + return_value=u'255.255.255.0') + opnfv_vnf.get_ports_gateway6 = mock.Mock(return_value=u'1.1.1.1') + opnfv_vnf.get_netmask_gateway6 = mock.Mock( + return_value=u'255.255.255.0') + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.interfaces = mock.MagicMock() + opnfv_vnf.get_ports_gateway6 = mock.Mock() + self.assertIsNotNone(opnfv_vnf.generate_arp_config6()) + + def test_generate_arp_config(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.get_config_tpl_data = mock.MagicMock() + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.update_write_parser = mock.MagicMock() + opnfv_vnf.generate_script_data = \ + mock.Mock(return_value={'link_config': 0, 'arp_config': '', + 'arp_config6': '', 'actions': '', + 'rules': ''}) + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.get_port_pairs = mock.Mock() + opnfv_vnf.vnf_type = 'VFW' + opnfv_vnf.get_ports_gateway = mock.Mock(return_value=u'1.1.1.1') + opnfv_vnf.get_netmask_gateway = mock.Mock( + return_value=u'255.255.255.0') + opnfv_vnf.get_ports_gateway6 = mock.Mock(return_value=u'1.1.1.1') + opnfv_vnf.get_netmask_gateway6 = mock.Mock( + return_value=u'255.255.255.0') + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.interfaces = mock.MagicMock() + opnfv_vnf.get_ports_gateway6 = mock.Mock() + self.assertIsNotNone(opnfv_vnf.generate_arp_config()) + + def test_get_ports_gateway(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.get_config_tpl_data = mock.MagicMock() + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.update_write_parser = mock.MagicMock() + opnfv_vnf.generate_script_data = \ + mock.Mock(return_value={'link_config': 0, 'arp_config': '', + 'arp_config6': '', 'actions': '', + 'rules': ''}) + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.get_port_pairs = mock.Mock() + opnfv_vnf.vnf_type = 'VFW' + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.interfaces = mock.MagicMock() + opnfv_vnf.get_ports_gateway6 = mock.Mock() + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.assertIsNotNone(opnfv_vnf.get_ports_gateway('xe0')) + + def test_get_ports_gateway6(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.get_config_tpl_data = mock.MagicMock() + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.update_write_parser = mock.MagicMock() + opnfv_vnf.generate_script_data = \ + mock.Mock(return_value={'link_config': 0, 'arp_config': '', + 'arp_config6': '', 'actions': '', + 'rules': ''}) + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.get_port_pairs = mock.Mock() + opnfv_vnf.vnf_type = 'VFW' + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.interfaces = mock.MagicMock() + opnfv_vnf.get_ports_gateway6 = mock.Mock() + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.assertIsNotNone(opnfv_vnf.get_ports_gateway6('xe0')) + + def test_get_netmask_gateway(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.get_config_tpl_data = mock.MagicMock() + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.update_write_parser = mock.MagicMock() + opnfv_vnf.generate_script_data = \ + mock.Mock(return_value={'link_config': 0, 'arp_config': '', + 'arp_config6': '', 'actions': '', + 'rules': ''}) + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.get_port_pairs = mock.Mock() + opnfv_vnf.vnf_type = 'VFW' + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.interfaces = mock.MagicMock() + opnfv_vnf.get_ports_gateway6 = mock.Mock() + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.assertIsNotNone(opnfv_vnf.get_netmask_gateway('xe0')) + + def test_get_netmask_gateway6(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.get_config_tpl_data = mock.MagicMock() + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.update_write_parser = mock.MagicMock() + opnfv_vnf.generate_script_data = \ + mock.Mock(return_value={'link_config': 0, 'arp_config': '', + 'arp_config6': '', 'actions': '', + 'rules': ''}) + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.get_port_pairs = mock.Mock() + opnfv_vnf.vnf_type = 'VFW' + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.interfaces = mock.MagicMock() + opnfv_vnf.get_ports_gateway6 = mock.Mock() + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.assertIsNotNone(opnfv_vnf.get_netmask_gateway6('xe0')) + + def test_generate_link_config(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.get_config_tpl_data = mock.MagicMock() + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.update_write_parser = mock.MagicMock() + opnfv_vnf.generate_script_data = \ + mock.Mock(return_value={'link_config': 0, 'arp_config': '', + 'arp_config6': '', 'actions': '', + 'rules': ''}) + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.get_port_pairs = mock.Mock() + opnfv_vnf.vnf_type = 'VFW' + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.get_ports_gateway6 = mock.Mock() + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + opnfv_vnf.interfaces = opnfv_vnf.vnfd['vdu'][0]['external-interface'] + opnfv_vnf.all_ports = ['32', '1', '987'] + opnfv_vnf.validate_ip_and_prefixlen = mock.Mock( + return_value=('10.20.30.40', 16)) + + result = opnfv_vnf.generate_link_config() + self.assertEqual(len(result.splitlines()), 9) + + def test_generate_config(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.get_config_tpl_data = mock.MagicMock() + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.update_write_parser = mock.MagicMock() + opnfv_vnf.generate_script_data = \ + mock.Mock(return_value={'link_config': 0, 'arp_config': '', + 'arp_config6': '', 'actions': '', + 'rules': ''}) + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.get_ports_gateway6 = mock.Mock() + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + opnfv_vnf.interfaces = opnfv_vnf.vnfd['vdu'][0]['external-interface'] + opnfv_vnf.generate_lb_to_port_pair_mapping = mock.Mock() + opnfv_vnf.generate_config_data = mock.Mock() + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.is_openstack = True + self.assertIsNone(opnfv_vnf.generate_config()) + opnfv_vnf.is_openstack = False + self.assertIsNone(opnfv_vnf.generate_config()) + + def test_get_config_tpl_data(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=True) + opnfv_vnf.read_parser.get = mock.Mock(return_value='filename') + + self.assertIsNotNone(opnfv_vnf.get_config_tpl_data('filename')) + + def test_get_txrx_tpl_data(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=True) + opnfv_vnf.read_parser.get = mock.Mock(return_value='filename') + + self.assertIsNotNone(opnfv_vnf.get_txrx_tpl_data('filename')) + + def test_init_write_parser_template(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=True) + opnfv_vnf.read_parser.get = mock.Mock(return_value='filename') + + self.assertIsNone(opnfv_vnf.init_write_parser_template('filename')) + opnfv_vnf.write_parser.add_section = mock.MagicMock() + opnfv_vnf.read_parser.item = mock.Mock(return_value=[1, 2, 3]) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=False) + opnfv_vnf.write_parser.set = mock.Mock() + self.assertIsNone(opnfv_vnf.init_write_parser_template('filename')) + + def test_init_write_parser_template_2(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + self.assertIsNone(opnfv_vnf.init_write_parser_template('filename')) + + def test_update_write_parser(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + self.assertIsNone(opnfv_vnf.update_write_parser({'filename': 1})) + + def test_get_worker_threads(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + opnfv_vnf.worker_config = '1t' + result = opnfv_vnf.get_worker_threads(1) + self.assertEqual(1, result) + opnfv_vnf.worker_config = '2t' + result = opnfv_vnf.get_worker_threads(2) + self.assertEqual(2, result) + opnfv_vnf.worker_config = '2t' + result = opnfv_vnf.get_worker_threads(3) + self.assertEqual(2, result) + + # TODO(elfoley): Split this test into smaller tests + def test_generate_next_core_id(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + opnfv_vnf.worker_config = '1t' + opnfv_vnf.start_core = 0 + result = opnfv_vnf.generate_next_core_id() + self.assertIsNone(result) + opnfv_vnf.worker_config = '2t' + opnfv_vnf.start_core = 'a' + self.assertRaises(ValueError, opnfv_vnf.generate_next_core_id) + opnfv_vnf.worker_config = '2t' + opnfv_vnf.start_core = 1 + result = opnfv_vnf.generate_next_core_id() + self.assertIsNone(result) + + def test_generate_lb_to_port_pair_mapping(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = VnfdHelper(self.VNFD_0) + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + opnfv_vnf.worker_config = '1t' + opnfv_vnf.start_core = 0 + opnfv_vnf.lb_count = 1 + opnfv_vnf._port_pairs = samplevnf_helper.PortPairs(vnfd_mock.interfaces) + opnfv_vnf.port_pair_list = opnfv_vnf._port_pairs.port_pair_list + result = opnfv_vnf.generate_lb_to_port_pair_mapping() + self.assertIsNone(result) + result = opnfv_vnf.set_priv_to_pub_mapping() + self.assertEqual('(0,1)', result) + + def test_set_priv_que_handler(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = VnfdHelper(self.VNFD_0) + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.port_pairs = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + opnfv_vnf.worker_config = '1t' + opnfv_vnf.start_core = 0 + opnfv_vnf.lb_count = 1 + result = opnfv_vnf.set_priv_que_handler() + self.assertIsNone(result) + + def test_generate_arp_route_tbl(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = "" + vnfd_mock = mock.MagicMock() + vnfd_mock.port_num.side_effect = ['32', '1', '987'] + vnfd_mock.find_interface.side_effect = [ + { + 'virtual-interface': { + 'dst_ip': '10.20.30.40', + 'netmask': '20', + }, + }, + { + 'virtual-interface': { + 'dst_ip': '10.200.30.40', + 'netmask': '24', + }, + }, + { + 'virtual-interface': { + 'dst_ip': '10.20.3.40', + 'netmask': '8', + }, + }, + ] + + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.all_ports = [3, 2, 5] + + expected = 'routeadd net 32 10.20.30.40 0xfffff000\n' \ + 'routeadd net 1 10.200.30.40 0xffffff00\n' \ + 'routeadd net 987 10.20.3.40 0xff000000' + result = opnfv_vnf.generate_arp_route_tbl() + self.assertEqual(result, expected) + + def test_generate_arpicmp_data(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.port_pairs = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + opnfv_vnf.worker_config = '1t' + opnfv_vnf.start_core = 0 + opnfv_vnf.lb_count = 1 + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + opnfv_vnf.interfaces = opnfv_vnf.vnfd['vdu'][0]['external-interface'] + result = opnfv_vnf.generate_arpicmp_data() + self.assertIsNotNone(result) + opnfv_vnf.nfv_type = 'ovs' + opnfv_vnf.lb_to_port_pair_mapping = [0, 1] + result = opnfv_vnf.generate_arpicmp_data() + self.assertIsNotNone(result) + opnfv_vnf.nfv_type = 'openstack' + opnfv_vnf.lb_to_port_pair_mapping = [0, 1] + result = opnfv_vnf.generate_arpicmp_data() + self.assertIsNotNone(result) + opnfv_vnf.lb_config = 'HW' + opnfv_vnf.lb_to_port_pair_mapping = [0, 1] + result = opnfv_vnf.generate_arpicmp_data() + self.assertIsNotNone(result) + + def test_generate_final_txrx_data(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.port_pairs = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + opnfv_vnf.worker_config = '1t' + opnfv_vnf.start_core = 0 + opnfv_vnf.lb_count = 1 + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + opnfv_vnf.interfaces = opnfv_vnf.vnfd['vdu'][0]['external-interface'] + opnfv_vnf.lb_to_port_pair_mapping = [0, 1] + opnfv_vnf.ports_len = 2 + opnfv_vnf.lb_index = 1 + opnfv_vnf.pktq_out_os = [1, 2] + result = opnfv_vnf.generate_final_txrx_data() + self.assertIsNotNone(result) + opnfv_vnf.nfv_type = 'openstack' + opnfv_vnf.pktq_out_os = [1, 2] + opnfv_vnf.lb_index = 1 + result = opnfv_vnf.generate_final_txrx_data() + self.assertIsNotNone(result) + + def test_generate_initial_txrx_data(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.port_pairs = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + opnfv_vnf.worker_config = '1t' + opnfv_vnf.start_core = 0 + opnfv_vnf.lb_count = 1 + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + opnfv_vnf.interfaces = opnfv_vnf.vnfd['vdu'][0]['external-interface'] + opnfv_vnf.lb_to_port_pair_mapping = [0, 1] + opnfv_vnf.lb_index = 1 + opnfv_vnf.ports_len = 2 + result = opnfv_vnf.generate_initial_txrx_data() + self.assertIsNotNone(result) + opnfv_vnf.nfv_type = 'openstack' + opnfv_vnf.pktq_out_os = [1, 2] + result = opnfv_vnf.generate_initial_txrx_data() + self.assertIsNotNone(result) + opnfv_vnf.nfv_type = 'ovs' + opnfv_vnf.init_ovs = False + opnfv_vnf.ovs_pktq_out = '' + opnfv_vnf.pktq_out_os = [1, 2] + opnfv_vnf.lb_index = 1 + result = opnfv_vnf.generate_initial_txrx_data() + self.assertIsNotNone(result) + opnfv_vnf.nfv_type = 'ovs' + opnfv_vnf.init_ovs = True + opnfv_vnf.pktq_out_os = [1, 2] + opnfv_vnf.ovs_pktq_out = '' + opnfv_vnf.lb_index = 1 + result = opnfv_vnf.generate_initial_txrx_data() + self.assertIsNotNone(result) + + def test_generate_lb_data(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.port_pairs = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + opnfv_vnf.worker_config = '1t' + opnfv_vnf.start_core = 0 + opnfv_vnf.lb_count = 1 + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + opnfv_vnf.interfaces = opnfv_vnf.vnfd['vdu'][0]['external-interface'] + opnfv_vnf.lb_to_port_pair_mapping = [0, 1] + opnfv_vnf.lb_index = 1 + opnfv_vnf.ports_len = 2 + opnfv_vnf.prv_que_handler = 0 + result = opnfv_vnf.generate_lb_data() + self.assertIsNotNone(result) + + def test_generate_vnf_data(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.port_pairs = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + opnfv_vnf.worker_config = '1t' + opnfv_vnf.start_core = 0 + opnfv_vnf.lb_count = 1 + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + opnfv_vnf.interfaces = opnfv_vnf.vnfd['vdu'][0]['external-interface'] + opnfv_vnf.lb_to_port_pair_mapping = [0, 1] + opnfv_vnf.lb_index = 1 + opnfv_vnf.ports_len = 1 + opnfv_vnf.pktq_out = ['1', '2'] + opnfv_vnf.vnf_tpl = {'public_ip_port_range': '98164810', + 'vnf_set': '(2,4,5)'} + opnfv_vnf.prv_que_handler = 0 + result = opnfv_vnf.generate_vnf_data() + self.assertIsNotNone(result) + opnfv_vnf.lb_config = 'HW' + opnfv_vnf.mul = 0.1 + result = opnfv_vnf.generate_vnf_data() + self.assertIsNotNone(result) + opnfv_vnf.lb_config = 'HW' + opnfv_vnf.mul = 0.1 + opnfv_vnf.vnf_type = 'ACL' + result = opnfv_vnf.generate_vnf_data() + self.assertIsNotNone(result) + + def test_generate_config_data(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = VnfdHelper(self.VNFD_0) + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.port_pairs = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + opnfv_vnf.worker_config = '1t' + opnfv_vnf.start_core = 0 + opnfv_vnf.lb_count = 1 + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + opnfv_vnf.interfaces = opnfv_vnf.vnfd['vdu'][0]['external-interface'] + opnfv_vnf.lb_to_port_pair_mapping = [0, 1] + opnfv_vnf.lb_index = 1 + opnfv_vnf.ports_len = 1 + opnfv_vnf.pktq_out = ['1', '2'] + opnfv_vnf.prv_que_handler = 0 + opnfv_vnf.init_write_parser_template = mock.Mock() + opnfv_vnf.arpicmp_tpl = mock.MagicMock() + opnfv_vnf.txrx_tpl = mock.MagicMock() + opnfv_vnf.loadb_tpl = mock.MagicMock() + opnfv_vnf.vnf_tpl = {'public_ip_port_range': '98164810 (1,65535)', + 'vnf_set': "(2,4,5)"} + opnfv_vnf.generate_vnf_data = mock.Mock(return_value={}) + opnfv_vnf.update_write_parser = mock.Mock() + result = opnfv_vnf.generate_config_data() + self.assertIsNone(result) + opnfv_vnf.generate_final_txrx_data = mock.Mock() + opnfv_vnf.update_write_parser = mock.Mock() + result = opnfv_vnf.generate_config_data() + self.assertIsNone(result) + opnfv_vnf.lb_to_port_pair_mapping = [0, 1] + opnfv_vnf.lb_index = 1 + opnfv_vnf.ports_len = 1 + opnfv_vnf.pktq_out = ['1', '2'] + opnfv_vnf.prv_que_handler = 0 + opnfv_vnf.init_write_parser_template = mock.Mock() + opnfv_vnf.arpicmp_tpl = mock.MagicMock() + opnfv_vnf.txrx_tpl = mock.MagicMock() + opnfv_vnf.loadb_tpl = mock.MagicMock() + opnfv_vnf.vnf_type = 'CGNAPT' + opnfv_vnf.update_timer = mock.Mock() + opnfv_vnf.port_pair_list = [("xe0", "xe1"), ("xe0", "xe2")] + opnfv_vnf.lb_to_port_pair_mapping = [0, 1] + opnfv_vnf.generate_arpicmp_data = mock.Mock() + result = opnfv_vnf.generate_config_data() + self.assertIsNone(result) + + def test_init_eal(self): + topology_file = mock.Mock() + config_tpl = mock.Mock() + tmp_file = mock.Mock() + vnfd_mock = mock.MagicMock() + opnfv_vnf = samplevnf_helper.MultiPortConfig( + topology_file, config_tpl, tmp_file, vnfd_mock) + opnfv_vnf.socket = 0 + opnfv_vnf.start_core = 0 + opnfv_vnf.port_pair_list = [("xe0", "xe1")] + opnfv_vnf.port_pairs = [("xe0", "xe1")] + opnfv_vnf.txrx_pipeline = '' + opnfv_vnf.rules = '' + opnfv_vnf.write_parser = mock.MagicMock() + opnfv_vnf.read_parser = mock.MagicMock() + opnfv_vnf.read_parser.sections = mock.Mock(return_value=['MASTER']) + opnfv_vnf.read_parser.has_option = mock.Mock(return_value=[]) + opnfv_vnf.write_parser.set = mock.Mock() + opnfv_vnf.write_parser.add_section = mock.Mock() + opnfv_vnf.read_parser.items = mock.MagicMock() + opnfv_vnf.pipeline_counter = 0 + opnfv_vnf.worker_config = '1t' + opnfv_vnf.start_core = 0 + opnfv_vnf.lb_count = 1 + opnfv_vnf.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + opnfv_vnf.interfaces = opnfv_vnf.vnfd['vdu'][0]['external-interface'] + opnfv_vnf.lb_to_port_pair_mapping = [0, 1] + opnfv_vnf.lb_index = 1 + opnfv_vnf.ports_len = 1 + opnfv_vnf.pktq_out = ['1', '2'] + opnfv_vnf.prv_que_handler = 0 + opnfv_vnf.init_write_parser_template = mock.Mock() + opnfv_vnf.arpicmp_tpl = mock.MagicMock() + opnfv_vnf.txrx_tpl = mock.MagicMock() + opnfv_vnf.loadb_tpl = mock.MagicMock() + opnfv_vnf.vnf_tpl = {'public_ip_port_range': '98164810 (1,65535)'} + opnfv_vnf.generate_vnf_data = mock.Mock(return_value={}) + opnfv_vnf.update_write_parser = mock.Mock() + opnfv_vnf.tmp_file = "/tmp/config" + result = opnfv_vnf.init_eal() + self.assertIsNone(result) diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/__init__.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/vpp_helpers/__init__.py diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_multiple_loss_ratio_search.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_multiple_loss_ratio_search.py new file mode 100644 index 000000000..d3145546a --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_multiple_loss_ratio_search.py @@ -0,0 +1,2164 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import mock + +from yardstick.network_services.helpers.vpp_helpers.multiple_loss_ratio_search import \ + MultipleLossRatioSearch +from yardstick.network_services.helpers.vpp_helpers.ndr_pdr_result import \ + NdrPdrResult +from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \ + ReceiveRateInterval +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement +from yardstick.network_services.traffic_profile.rfc2544 import PortPgIDMap + + +class TestMultipleLossRatioSearch(unittest.TestCase): + + def test___init__(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(True, algorithm.latency) + self.assertEqual(64, algorithm.pkt_size) + self.assertEqual(30, algorithm.final_trial_duration) + self.assertEqual(0.005, algorithm.final_relative_width) + self.assertEqual(2, algorithm.number_of_intermediate_phases) + self.assertEqual(1, algorithm.initial_trial_duration) + self.assertEqual(720, algorithm.timeout) + self.assertEqual(1, algorithm.doublings) + + def test_double_relative_width(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(0.00997, algorithm.double_relative_width(0.005)) + + def test_double_step_down(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(99003.0, algorithm.double_step_down(0.005, 100000)) + + def test_expand_down(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(99003.0, algorithm.expand_down(0.005, 1, 100000)) + + def test_double_step_up(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(101007.0401907013, + algorithm.double_step_up(0.005, 100000)) + + def test_expand_up(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(101007.0401907013, + algorithm.expand_up(0.005, 1, 100000)) + + def test_half_relative_width(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(0.0025031328369998773, + algorithm.half_relative_width(0.005)) + + def test_half_step_up(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + self.assertEqual(100250.94142341711, + algorithm.half_step_up(0.005, 100000)) + + def test_init_generator(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), + mock.Mock(), mock.Mock())) + self.assertEqual(ports, algorithm.ports) + self.assertEqual(port_pg_id, algorithm.port_pg_id) + + def test_collect_kpi(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + algorithm.init_generator(ports, port_pg_id, mock.Mock, mock.Mock, + mock.Mock()) + self.assertIsNone(algorithm.collect_kpi({}, 100000)) + + def test_narrow_down_ndr_and_pdr(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, 'ndrpdr') as \ + mock_ndrpdr: + ndr_measured_low = ReceiveRateMeasurement(10, 13880000, 13879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(10, 14880000, 14879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(10, 11880000, 11879927, + 0) + pdr_measured_high = ReceiveRateMeasurement(10, 12880000, 12879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock_ndrpdr.return_value = MultipleLossRatioSearch.ProgressState( + starting_result, 2, 30, 0.005, 0.0, + 4857361, 4977343) + self.assertEqual( + {'Result_NDR_LOWER': {'bandwidth_total_Gbps': 0.9327310944, + 'rate_total_pps': 1387992.7}, + 'Result_NDR_UPPER': { + 'bandwidth_total_Gbps': 0.9999310943999999, + 'rate_total_pps': 1487992.7}, + 'Result_NDR_packets_lost': {'packet_loss_ratio': 0.0, + 'packets_lost': 0.0}, + 'Result_PDR_LOWER': { + 'bandwidth_total_Gbps': 0.7983310943999999, + 'rate_total_pps': 1187992.7}, + 'Result_PDR_UPPER': {'bandwidth_total_Gbps': 0.8655310944, + 'rate_total_pps': 1287992.7}, + 'Result_PDR_packets_lost': {'packet_loss_ratio': 0.0, + 'packets_lost': 0.0}, + 'Result_stream0_NDR_LOWER': {'avg_latency': 3081.0, + 'max_latency': 3962.0, + 'min_latency': 1000.0}, + 'Result_stream0_PDR_LOWER': {'avg_latency': 3081.0, + 'max_latency': 3962.0, + 'min_latency': 1000.0}, + 'Result_stream1_NDR_LOWER': {'avg_latency': 3149.0, + 'max_latency': 3730.0, + 'min_latency': 500.0}, + 'Result_stream1_PDR_LOWER': {'avg_latency': 3149.0, + 'max_latency': 3730.0, + 'min_latency': 500.0}}, + algorithm.narrow_down_ndr_and_pdr(12880000, 15880000, 0.0)) + + def test__measure_and_update_state(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + starting_result = NdrPdrResult(starting_interval, starting_interval) + previous_state = MultipleLossRatioSearch.ProgressState(starting_result, + 2, 30, 0.005, + 0.0, 4857361, + 4977343) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure: + mock_measure.return_value = ReceiveRateMeasurement(1, + 4626121.09635, + 4626100, 13074) + state = algorithm._measure_and_update_state(previous_state, + 4626121.09635) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(1, state.result.ndr_interval.measured_low.duration) + self.assertEqual(4626121.09635, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(4626100, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(13074, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(4613026, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(4626100, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(13074.0, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(4613026.0, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.00283, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(1, state.result.ndr_interval.measured_high.duration) + self.assertEqual(4857361, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(4857339, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(84965, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(4772374, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(4857339, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(84965.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(4772374.0, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.01749, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(1, state.result.pdr_interval.measured_low.duration) + self.assertEqual(4626121.09635, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(4626100, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(13074, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(4613026, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(4626100, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(13074.0, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(4613026.0, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.00283, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(1, state.result.pdr_interval.measured_high.duration) + self.assertEqual(4857361, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(4857339, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(84965, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(4772374, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(4857339, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(84965.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(4772374.0, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.01749, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(2, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(4857361, state.minimum_transmit_rate) + self.assertEqual(4977343, state.maximum_transmit_rate) + + def test_new_interval(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 3972540.4108, 21758482, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.0) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(3972540.4108, result.measured_low.target_tr) + self.assertEqual(21758482, result.measured_low.transmit_count) + self.assertEqual(0, result.measured_low.loss_count) + self.assertEqual(21758482, result.measured_low.receive_count) + self.assertEqual(21758482, result.measured_low.transmit_rate) + self.assertEqual(0.0, result.measured_low.loss_rate) + self.assertEqual(21758482.0, result.measured_low.receive_rate) + self.assertEqual(0.0, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4857361, result.measured_high.target_tr) + self.assertEqual(4857339, result.measured_high.transmit_count) + self.assertEqual(84965, result.measured_high.loss_count) + self.assertEqual(4772374, result.measured_high.receive_count) + self.assertEqual(4857339, result.measured_high.transmit_rate) + self.assertEqual(84965.0, result.measured_high.loss_rate) + self.assertEqual(4772374.0, result.measured_high.receive_rate) + self.assertEqual(0.01749, result.measured_high.loss_fraction) + + def test_new_interval_zero(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 4977343, 21758482, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.0) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4857361.0, result.measured_low.target_tr) + self.assertEqual(4857339, result.measured_low.transmit_count) + self.assertEqual(84965, result.measured_low.loss_count) + self.assertEqual(4772374, result.measured_low.receive_count) + self.assertEqual(4857339.0, result.measured_low.transmit_rate) + self.assertEqual(84965.0, result.measured_low.loss_rate) + self.assertEqual(4772374.0, result.measured_low.receive_rate) + self.assertEqual(0.01749, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4977343.0, result.measured_high.target_tr) + self.assertEqual(21758482, result.measured_high.transmit_count) + self.assertEqual(0, result.measured_high.loss_count) + self.assertEqual(21758482, result.measured_high.receive_count) + self.assertEqual(21758482.0, result.measured_high.transmit_rate) + self.assertEqual(0.0, result.measured_high.loss_rate) + self.assertEqual(21758482.0, result.measured_high.receive_rate) + self.assertEqual(0.0, result.measured_high.loss_fraction) + + def test_new_interval_one(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 5000000, 2175848, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.0) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4857361.0, result.measured_low.target_tr) + self.assertEqual(4857339, result.measured_low.transmit_count) + self.assertEqual(84965, result.measured_low.loss_count) + self.assertEqual(4772374, result.measured_low.receive_count) + self.assertEqual(4857339.0, result.measured_low.transmit_rate) + self.assertEqual(84965.0, result.measured_low.loss_rate) + self.assertEqual(4772374.0, result.measured_low.receive_rate) + self.assertEqual(0.01749, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4977343.0, result.measured_high.target_tr) + self.assertEqual(4977320, result.measured_high.transmit_count) + self.assertEqual(119959, result.measured_high.loss_count) + self.assertEqual(4857361, result.measured_high.receive_count) + self.assertEqual(4977320.0, result.measured_high.transmit_rate) + self.assertEqual(119959.0, result.measured_high.loss_rate) + self.assertEqual(4857361.0, result.measured_high.receive_rate) + self.assertEqual(0.0241, result.measured_high.loss_fraction) + + def test_new_interval_valid_1st(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 4000000, 2175848, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.5) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4857361.0, result.measured_low.target_tr) + self.assertEqual(4857339, result.measured_low.transmit_count) + self.assertEqual(84965, result.measured_low.loss_count) + self.assertEqual(4772374, result.measured_low.receive_count) + self.assertEqual(4857339.0, result.measured_low.transmit_rate) + self.assertEqual(84965.0, result.measured_low.loss_rate) + self.assertEqual(4772374.0, result.measured_low.receive_rate) + self.assertEqual(0.01749, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4977343.0, result.measured_high.target_tr) + self.assertEqual(4977320, result.measured_high.transmit_count) + self.assertEqual(119959, result.measured_high.loss_count) + self.assertEqual(4857361, result.measured_high.receive_count) + self.assertEqual(4977320.0, result.measured_high.transmit_rate) + self.assertEqual(119959.0, result.measured_high.loss_rate) + self.assertEqual(4857361.0, result.measured_high.receive_rate) + self.assertEqual(0.0241, result.measured_high.loss_fraction) + + def test_new_interval_valid_1st_loss(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 4000000, 2175848, 1000000) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.02) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4000000.0, result.measured_low.target_tr) + self.assertEqual(2175848, result.measured_low.transmit_count) + self.assertEqual(1000000, result.measured_low.loss_count) + self.assertEqual(1175848, result.measured_low.receive_count) + self.assertEqual(2175848.0, result.measured_low.transmit_rate) + self.assertEqual(1000000.0, result.measured_low.loss_rate) + self.assertEqual(1175848.0, result.measured_low.receive_rate) + self.assertEqual(0.45959, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4977343.0, result.measured_high.target_tr) + self.assertEqual(4977320, result.measured_high.transmit_count) + self.assertEqual(119959, result.measured_high.loss_count) + self.assertEqual(4857361, result.measured_high.receive_count) + self.assertEqual(4977320.0, result.measured_high.transmit_rate) + self.assertEqual(119959.0, result.measured_high.loss_rate) + self.assertEqual(4857361.0, result.measured_high.receive_rate) + self.assertEqual(0.0241, result.measured_high.loss_fraction) + + def test_new_interval_valid_2nd(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 5000000, 2175848, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.5) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4977343.0, result.measured_low.target_tr) + self.assertEqual(4977320, result.measured_low.transmit_count) + self.assertEqual(119959, result.measured_low.loss_count) + self.assertEqual(4857361, result.measured_low.receive_count) + self.assertEqual(4977320.0, result.measured_low.transmit_rate) + self.assertEqual(119959.0, result.measured_low.loss_rate) + self.assertEqual(4857361.0, result.measured_low.receive_rate) + self.assertEqual(0.0241, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(5000000.0, result.measured_high.target_tr) + self.assertEqual(2175848, result.measured_high.transmit_count) + self.assertEqual(0, result.measured_high.loss_count) + self.assertEqual(2175848, result.measured_high.receive_count) + self.assertEqual(2175848.0, result.measured_high.transmit_rate) + self.assertEqual(0.0, result.measured_high.loss_rate) + self.assertEqual(2175848.0, result.measured_high.receive_rate) + self.assertEqual(0.0, result.measured_high.loss_fraction) + + def test_new_interval_valid_3rd(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 4867361, 2175848, 0) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.5) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4867361.0, result.measured_low.target_tr) + self.assertEqual(2175848, result.measured_low.transmit_count) + self.assertEqual(0, result.measured_low.loss_count) + self.assertEqual(2175848, result.measured_low.receive_count) + self.assertEqual(2175848.0, result.measured_low.transmit_rate) + self.assertEqual(0.0, result.measured_low.loss_rate) + self.assertEqual(2175848.0, result.measured_low.receive_rate) + self.assertEqual(0.0, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4977343.0, result.measured_high.target_tr) + self.assertEqual(4977320, result.measured_high.transmit_count) + self.assertEqual(119959, result.measured_high.loss_count) + self.assertEqual(4857361, result.measured_high.receive_count) + self.assertEqual(4977320.0, result.measured_high.transmit_rate) + self.assertEqual(119959.0, result.measured_high.loss_rate) + self.assertEqual(4857361.0, result.measured_high.receive_rate) + self.assertEqual(0.0241, result.measured_high.loss_fraction) + + def test_new_interval_valid_3rd_loss(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + measured = ReceiveRateMeasurement(1, 4867361, 2175848, 1000000) + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + result = algorithm._new_interval(receive_rate_interval, measured, 0.2) + self.assertIsInstance(result, ReceiveRateInterval) + self.assertEqual(1, result.measured_low.duration) + self.assertEqual(4857361.0, result.measured_low.target_tr) + self.assertEqual(4857339, result.measured_low.transmit_count) + self.assertEqual(84965, result.measured_low.loss_count) + self.assertEqual(4772374, result.measured_low.receive_count) + self.assertEqual(4857339.0, result.measured_low.transmit_rate) + self.assertEqual(84965.0, result.measured_low.loss_rate) + self.assertEqual(4772374.0, result.measured_low.receive_rate) + self.assertEqual(0.01749, result.measured_low.loss_fraction) + self.assertEqual(1, result.measured_high.duration) + self.assertEqual(4867361.0, result.measured_high.target_tr) + self.assertEqual(2175848, result.measured_high.transmit_count) + self.assertEqual(1000000, result.measured_high.loss_count) + self.assertEqual(1175848, result.measured_high.receive_count) + self.assertEqual(2175848.0, result.measured_high.transmit_rate) + self.assertEqual(1000000.0, result.measured_high.loss_rate) + self.assertEqual(1175848.0, result.measured_high.receive_rate) + self.assertEqual(0.45959, result.measured_high.loss_fraction) + + def test_ndrpdr(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure: + measured_low = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_ndr_rel_width(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + measured_low = ReceiveRateMeasurement(30, 880000, 879927, 0) + measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + ending_interval = ReceiveRateInterval(measured_high, measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + ending_result = NdrPdrResult(ending_interval, ending_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.005, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_pdr_rel_width(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 14880000, 14879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 880000, 879927, 0) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(ndr_interval, ndr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.005, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_ndr_lo_duration(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + measured_low = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 100) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(starting_result, -1, 30, + 0.005, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 50, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879827, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495994.23333, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(1e-05, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879827, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495994.23333, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(1e-05, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_ndr_hi_duration(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + measured_low = ReceiveRateMeasurement(60, 14880000, 14879927, 0) + measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 100) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(starting_result, -1, 30, + 0.005, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 50, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(60.0, state.result.ndr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(247998.78333, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(247998.78333, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879827, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495994.23333, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(1e-05, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(60.0, state.result.pdr_interval.measured_low.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(247998.78333, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(247998.78333, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879827, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495994.23333, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(1e-05, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.005, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_error(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=0) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure: + measured_low = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, 0) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 14880000, + 14880000) + with self.assertRaises(RuntimeError) as raised: + algorithm.ndrpdr(previous_state) + + self.assertIn('Optimized search takes too long.', + str(raised.exception)) + + def test_ndrpdr_update_state_ndr_hi(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(pdr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_ndr_hi_duration(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(pdr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 50, 0.005, 0.0, 4880000, + 10880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(0, state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_ndr_lo(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 100000) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 100000) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(pdr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 100000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_pdr_lo(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 100000) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(pdr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 100000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_pdr_lo_duration(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + pdr_measured_high = ReceiveRateMeasurement(30, 14880000, 14879927, + 100000) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(pdr_interval, pdr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 50, 0.005, 0.0, 14880000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(14880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(12880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(12779927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(425997.56667, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.00776, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(14880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(14879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(14779927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(495997.56667, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(492664.23333, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.00672, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_pdr_hi(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + pdr_measured_high = ReceiveRateMeasurement(30, 13880000, 14879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(ndr_interval, ndr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 30, 0.005, 0.0, 100000, + 14880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(10880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(10879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(10879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(362664.23333, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(362664.23333, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(12779927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(425997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.00776, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(10880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(10879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(10879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(362664.23333, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(362664.23333, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(12880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(12779927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(425997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.00776, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_ndrpdr_update_state_pdr_hi_duration(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock(), mock.Mock, + mock.Mock())) + with mock.patch.object(algorithm, 'measure') as \ + mock_measure, \ + mock.patch.object(algorithm, '_measure_and_update_state') as \ + mock__measure_and_update_state: + ndr_measured_low = ReceiveRateMeasurement(30, 10880000, 10879927, + 0) + ndr_measured_high = ReceiveRateMeasurement(30, 12880000, 12879927, + 100000) + ndr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_low = ReceiveRateMeasurement(30, 12880000, 12879927, + 0) + pdr_measured_high = ReceiveRateMeasurement(30, 13880000, 14879927, + 0) + pdr_measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + pdr_measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + ndr_interval = ReceiveRateInterval(ndr_measured_low, + ndr_measured_high) + pdr_interval = ReceiveRateInterval(pdr_measured_low, + pdr_measured_high) + starting_result = NdrPdrResult(ndr_interval, pdr_interval) + ending_result = NdrPdrResult(ndr_interval, ndr_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock__measure_and_update_state.return_value = \ + MultipleLossRatioSearch.ProgressState(ending_result, -1, 30, + 0.2, 0.0, 14880000, + 14880000) + previous_state = MultipleLossRatioSearch.ProgressState( + starting_result, -1, 50, 0.005, 0.0, 100000, + 10880000) + state = algorithm.ndrpdr(previous_state) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertIsInstance(state, MultipleLossRatioSearch.ProgressState) + self.assertEqual(30, state.result.ndr_interval.measured_low.duration) + self.assertEqual(10880000.0, + state.result.ndr_interval.measured_low.target_tr) + self.assertEqual(10879927, + state.result.ndr_interval.measured_low.transmit_count) + self.assertEqual(0, + state.result.ndr_interval.measured_low.loss_count) + self.assertEqual(10879927, + state.result.ndr_interval.measured_low.receive_count) + self.assertEqual(362664.23333, + state.result.ndr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_rate) + self.assertEqual(362664.23333, + state.result.ndr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.ndr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.ndr_interval.measured_high.duration) + self.assertEqual(12880000.0, + state.result.ndr_interval.measured_high.target_tr) + self.assertEqual(12879927, + state.result.ndr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.ndr_interval.measured_high.loss_count) + self.assertEqual(12779927, + state.result.ndr_interval.measured_high.receive_count) + self.assertEqual(429330.9, + state.result.ndr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.ndr_interval.measured_high.loss_rate) + self.assertEqual(425997.56667, + state.result.ndr_interval.measured_high.receive_rate) + self.assertEqual(0.00776, + state.result.ndr_interval.measured_high.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_low.duration) + self.assertEqual(10880000.0, + state.result.pdr_interval.measured_low.target_tr) + self.assertEqual(10879927, + state.result.pdr_interval.measured_low.transmit_count) + self.assertEqual(0, + state.result.pdr_interval.measured_low.loss_count) + self.assertEqual(10879927, + state.result.pdr_interval.measured_low.receive_count) + self.assertEqual(362664.23333, + state.result.pdr_interval.measured_low.transmit_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_rate) + self.assertEqual(362664.23333, + state.result.pdr_interval.measured_low.receive_rate) + self.assertEqual(0.0, + state.result.pdr_interval.measured_low.loss_fraction) + self.assertEqual(30, state.result.pdr_interval.measured_high.duration) + self.assertEqual(12880000, + state.result.pdr_interval.measured_high.target_tr) + self.assertEqual(12879927, + state.result.pdr_interval.measured_high.transmit_count) + self.assertEqual(100000, + state.result.pdr_interval.measured_high.loss_count) + self.assertEqual(12779927, + state.result.pdr_interval.measured_high.receive_count) + self.assertEqual(429330.9, + state.result.pdr_interval.measured_high.transmit_rate) + self.assertEqual(3333.33333, + state.result.pdr_interval.measured_high.loss_rate) + self.assertEqual(425997.56667, + state.result.pdr_interval.measured_high.receive_rate) + self.assertEqual(0.00776, + state.result.pdr_interval.measured_high.loss_fraction) + self.assertEqual(-1, state.phases) + self.assertEqual(30, state.duration) + self.assertEqual(0.2, state.width_goal) + self.assertEqual(0.0, state.packet_loss_ratio) + self.assertEqual(14880000, state.minimum_transmit_rate) + self.assertEqual(14880000, state.maximum_transmit_rate) + + def test_measure(self): + measurer = mock.MagicMock() + measurer.sent = 102563094 + measurer.loss = 30502 + algorithm = MultipleLossRatioSearch(measurer=measurer, latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.MagicMock(), + mock.Mock, mock.Mock())) + measurement = algorithm.measure(30, 3418770.3425, True) + self.assertIsInstance(measurement, ReceiveRateMeasurement) + self.assertEqual(30, measurement.duration) + self.assertEqual(3418770.3425, measurement.target_tr) + self.assertEqual(102563094, measurement.transmit_count) + self.assertEqual(30502, measurement.loss_count) + self.assertEqual(102532592, measurement.receive_count) + self.assertEqual(3418769.8, measurement.transmit_rate) + self.assertEqual(1016.73333, measurement.loss_rate) + self.assertEqual(3417753.06667, measurement.receive_rate) + self.assertEqual(0.0003, measurement.loss_fraction) + + def test_perform_additional_measurements_based_on_ndrpdr_result(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + ports = [0, 1] + port_pg_id = PortPgIDMap() + port_pg_id.add_port(0) + port_pg_id.add_port(1) + self.assertIsNone( + algorithm.init_generator(ports, port_pg_id, mock.Mock, mock.Mock, + mock.Mock())) + result = mock.MagicMock() + result.ndr_interval.measured_low.target_tr.return_result = 100000 + self.assertIsNone( + algorithm.perform_additional_measurements_based_on_ndrpdr_result( + result)) + + def test_display_single_bound(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + result_samples = {} + self.assertIsNone( + algorithm.display_single_bound(result_samples, 'NDR_LOWER', + 4857361, 64, + ['20/849/1069', '40/69/183'])) + self.assertEqual( + {'Result_NDR_LOWER': {'bandwidth_total_Gbps': 3.264146592, + 'rate_total_pps': 4857361.0}, + 'Result_stream0_NDR_LOWER': {'avg_latency': 849.0, + 'max_latency': 1069.0, + 'min_latency': 20.0}, + 'Result_stream1_NDR_LOWER': {'avg_latency': 69.0, + 'max_latency': 183.0, + 'min_latency': 40.0}}, + result_samples) + + def test_check_ndrpdr_interval_validity(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + result_samples = {} + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 0) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 0) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertEqual('Minimal rate loss fraction 0.0 reach target 0.0', + algorithm.check_ndrpdr_interval_validity( + result_samples, 'NDR_LOWER', + receive_rate_interval)) + self.assertEqual( + {'Result_NDR_LOWER_packets_lost': {'packet_loss_ratio': 0.0, + 'packets_lost': 0.0}}, + result_samples) + + def test_check_ndrpdr_interval_validity_fail(self): + algorithm = MultipleLossRatioSearch(measurer=mock.Mock(), latency=True, + pkt_size=64, + final_trial_duration=30, + final_relative_width=0.005, + number_of_intermediate_phases=2, + initial_trial_duration=1, + timeout=720) + result_samples = {} + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertEqual( + 'Minimal rate loss fraction 0.01749 does not reach target 0.005\n84965 packets lost.', + algorithm.check_ndrpdr_interval_validity(result_samples, + 'NDR_LOWER', + receive_rate_interval, + 0.005)) + self.assertEqual({'Result_NDR_LOWER_packets_lost': { + 'packet_loss_ratio': 0.01749, + 'packets_lost': 84965.0}}, result_samples) diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_ndr_pdr_result.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_ndr_pdr_result.py new file mode 100644 index 000000000..ea9c39a03 --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_ndr_pdr_result.py @@ -0,0 +1,91 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import mock + +from yardstick.network_services.helpers.vpp_helpers.ndr_pdr_result import \ + NdrPdrResult +from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \ + ReceiveRateInterval +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement + + +class TestNdrPdrResult(unittest.TestCase): + + def test___init__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + ndrpdr_result = NdrPdrResult(starting_interval, starting_interval) + self.assertIsInstance(ndrpdr_result.ndr_interval, ReceiveRateInterval) + self.assertIsInstance(ndrpdr_result.pdr_interval, ReceiveRateInterval) + + def test___init__ndr_error(self): + starting_interval = mock.MagicMock() + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + end_interval = ReceiveRateInterval(measured_low, measured_high) + with self.assertRaises(TypeError) as raised: + NdrPdrResult(starting_interval, end_interval) + self.assertIn('ndr_interval, is not a ReceiveRateInterval: ', + str(raised.exception)) + + def test___init__pdr_error(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + end_interval = mock.MagicMock() + with self.assertRaises(TypeError) as raised: + NdrPdrResult(starting_interval, end_interval) + self.assertIn('pdr_interval, is not a ReceiveRateInterval: ', + str(raised.exception)) + + def test_width_in_goals(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + ndrpdr_result = NdrPdrResult(starting_interval, starting_interval) + self.assertEqual('ndr 4.86887; pdr 4.86887', + ndrpdr_result.width_in_goals(0.005)) + + def test___str__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + ndrpdr_result = NdrPdrResult(starting_interval, starting_interval) + self.assertEqual( + 'NDR=[d=1.0,Tr=4857361.0,Df=0.01749;d=1.0,Tr=4977343.0,Df=0.0241);' + 'PDR=[d=1.0,Tr=4857361.0,Df=0.01749;d=1.0,Tr=4977343.0,Df=0.0241)', + ndrpdr_result.__str__()) + + def test___repr__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + starting_interval = ReceiveRateInterval(measured_low, measured_high) + ndrpdr_result = NdrPdrResult(starting_interval, starting_interval) + self.assertEqual( + 'NdrPdrResult(ndr_interval=ReceiveRateInterval(measured_low=' \ + 'ReceiveRateMeasurement(duration=1.0,target_tr=4857361.0,' \ + 'transmit_count=4857339,loss_count=84965),measured_high=' \ + 'ReceiveRateMeasurement(duration=1.0,target_tr=4977343.0,' \ + 'transmit_count=4977320,loss_count=119959)),pdr_interval=' \ + 'ReceiveRateInterval(measured_low=ReceiveRateMeasurement' \ + '(duration=1.0,target_tr=4857361.0,transmit_count=4857339,' \ + 'loss_count=84965),measured_high=ReceiveRateMeasurement' \ + '(duration=1.0,target_tr=4977343.0,transmit_count=4977320,' \ + 'loss_count=119959)))', + ndrpdr_result.__repr__()) diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_interval.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_interval.py new file mode 100644 index 000000000..bbf241613 --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_interval.py @@ -0,0 +1,100 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +import mock + +from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \ + ReceiveRateInterval +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement + + +class TestReceiveRateInterval(unittest.TestCase): + + def test__init__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertIsInstance(receive_rate_interval.measured_low, + ReceiveRateMeasurement) + self.assertIsInstance(receive_rate_interval.measured_high, + ReceiveRateMeasurement) + + def test__init__measured_low_error(self): + measured_low = mock.MagicMock() + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + with self.assertRaises(TypeError) as raised: + ReceiveRateInterval(measured_low, measured_high) + self.assertIn('measured_low is not a ReceiveRateMeasurement: ', + str(raised.exception)) + + def test__init__measured_high_error(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = mock.MagicMock() + with self.assertRaises(TypeError) as raised: + ReceiveRateInterval(measured_low, measured_high) + self.assertIn('measured_high is not a ReceiveRateMeasurement: ', + str(raised.exception)) + + def test_sort(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertIsNone(receive_rate_interval.sort()) + self.assertEqual(119982.0, receive_rate_interval.abs_tr_width) + self.assertEqual(0.02411, + receive_rate_interval.rel_tr_width) + + def test_sort_swap(self): + measured_low = ReceiveRateMeasurement(1, 14857361, 14857339, 184965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertIsNone(receive_rate_interval.sort()) + self.assertEqual(9880018.0, receive_rate_interval.abs_tr_width) + self.assertEqual(0.66499, + receive_rate_interval.rel_tr_width) + + def test_width_in_goals(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertEqual(4.86887, + receive_rate_interval.width_in_goals(0.005)) + + def test___str__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertEqual( + '[d=1.0,Tr=4857361.0,Df=0.01749;d=1.0,Tr=4977343.0,Df=0.0241)', + receive_rate_interval.__str__()) + + def test___repr__(self): + measured_low = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + measured_high = ReceiveRateMeasurement(1, 4977343, 4977320, 119959) + receive_rate_interval = ReceiveRateInterval(measured_low, + measured_high) + self.assertEqual('ReceiveRateInterval(measured_low=' \ + 'ReceiveRateMeasurement(duration=1.0,target_tr=4857361.0,' \ + 'transmit_count=4857339,loss_count=84965),measured_high=' \ + 'ReceiveRateMeasurement(duration=1.0,target_tr=4977343.0,' \ + 'transmit_count=4977320,loss_count=119959))', + receive_rate_interval.__repr__()) diff --git a/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_measurement.py b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_measurement.py new file mode 100644 index 000000000..d4e2d7920 --- /dev/null +++ b/yardstick/tests/unit/network_services/helpers/vpp_helpers/test_receive_rate_measurement.py @@ -0,0 +1,44 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement + + +class TestReceiveRateMeasurement(unittest.TestCase): + + def test__init__(self): + measured = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + self.assertEqual(1, measured.duration) + self.assertEqual(4857361, measured.target_tr) + self.assertEqual(4857339, measured.transmit_count) + self.assertEqual(84965, measured.loss_count) + self.assertEqual(4772374, measured.receive_count) + self.assertEqual(4857339, measured.transmit_rate) + self.assertEqual(84965.0, measured.loss_rate) + self.assertEqual(4772374.0, measured.receive_rate) + self.assertEqual(0.01749, measured.loss_fraction) + + def test___str__(self): + measured = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + self.assertEqual('d=1.0,Tr=4857361.0,Df=0.01749', + measured.__str__()) + + def test___repr__(self): + measured = ReceiveRateMeasurement(1, 4857361, 4857339, 84965) + self.assertEqual('ReceiveRateMeasurement(duration=1.0,' \ + 'target_tr=4857361.0,transmit_count=4857339,loss_count=84965)', + measured.__repr__()) diff --git a/yardstick/tests/unit/network_services/libs/__init__.py b/yardstick/tests/unit/network_services/libs/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/network_services/libs/__init__.py diff --git a/yardstick/tests/unit/network_services/libs/ixia_libs/__init__.py b/yardstick/tests/unit/network_services/libs/ixia_libs/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/network_services/libs/ixia_libs/__init__.py diff --git a/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py b/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py new file mode 100644 index 000000000..a20592dc7 --- /dev/null +++ b/yardstick/tests/unit/network_services/libs/ixia_libs/test_ixnet_api.py @@ -0,0 +1,1057 @@ +# Copyright (c) 2018-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +import IxNetwork +import unittest +import re + +from copy import deepcopy +from collections import OrderedDict + +from yardstick.common import exceptions +from yardstick.network_services.libs.ixia_libs.ixnet import ixnet_api +from yardstick.network_services.traffic_profile import ixia_rfc2544 + + +UPLINK = 'uplink' +DOWNLINK = 'downlink' + +TRAFFIC_PROFILE = { + 'uplink_0': { + 'ipv4': { + 'outer_l2': { + 'framesize': { + '128B': '0', + '1518B': '0', + '64B': '0', + '373b': '0', + '256B': '0', + '1400B': '0', + '570B': '0'}}, + 'id': 1}}, + 'description': 'Traffic profile to run RFC2544 latency', + 'name': 'rfc2544', + 'schema': 'isb:traffic_profile:0.1', + 'traffic_profile': { + 'injection_time': None, + 'enable_latency': True, + 'frame_rate': '100%', + 'traffic_type': 'IXIARFC2544Profile'}, + 'downlink_0': { + 'ipv4': { + 'outer_l2': { + 'framesize': { + '128B': '0', + '1518B': '0', + '64B': '0', + '373b': '0', + '256B': '0', + '1400B': '0', + '570B': '0'}}, + 'id': 2}}} + + +TRAFFIC_PARAMETERS = { + UPLINK: { + 'id': 1, + 'bidir': 'False', + 'duration': 60, + 'rate': 10000.5, + 'rate_unit': 'fps', + 'outer_l2': { + 'framesize': {'64B': '25', '256B': '75'}, + 'QinQ': None + }, + 'outer_l3': { + 'count': 512, + 'srcseed': 10, + 'dstseed': 20, + 'dscp': 0, + 'proto': 'udp', + 'ttl': 32, + 'dstip': '152.16.40.20', + 'srcip': '152.16.100.20', + 'dstmask': 24, + 'srcmask': 24, + 'priority': {'raw': '0x01'} + }, + 'outer_l4': { + 'seed': 1, + 'count': 1, + 'dstport': 2001, + 'srcport': 1234, + 'srcportmask': 0, + 'dstportmask': 0 + }, + 'traffic_type': 'continuous' + }, + DOWNLINK: { + 'id': 2, + 'bidir': 'False', + 'duration': 60, + 'rate': 75.2, + 'rate_unit': '%', + 'outer_l2': { + 'framesize': {'128B': '35', '1024B': '65'}, + 'QinQ': None + }, + 'outer_l3': { + 'count': 1024, + 'srcseed': 30, + 'dstseed': 40, + 'dscp': 0, + 'proto': 'udp', + 'ttl': 32, + 'dstip': '2001::10', + 'srcip': '2021::10', + 'dstmask': 64, + 'srcmask': 64, + 'priority': {'raw': '0x01'} + }, + 'outer_l4': { + 'seed': 1, + 'count': 1, + 'dstport': 1234, + 'srcport': 2001, + 'srcportmask': 0, + 'dstportmask': 0 + }, + 'traffic_type': 'continuous' + } +} + + +class TestIxNextgen(unittest.TestCase): + + def setUp(self): + self.ixnet = mock.Mock() + self.ixnet.execute = mock.Mock() + self.ixnet.getRoot.return_value = 'my_root' + self.ixnet_gen = ixnet_api.IxNextgen() + self.ixnet_gen._ixnet = self.ixnet + self._mock_log = mock.patch.object(ixnet_api.log, 'info') + self.mock_log = self._mock_log.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self.mock_log.stop() + + def test_get_config(self): + tg_cfg = { + 'vdu': [ + { + 'external-interface': [ + {'virtual-interface': {'vpci': '0000:07:00.1'}}, + {'virtual-interface': {'vpci': '0001:08:01.2'}} + ] + }, + ], + 'mgmt-interface': { + 'ip': 'test1', + 'tg-config': { + 'dut_result_dir': 'test2', + 'version': 'test3', + 'ixchassis': 'test4', + 'tcl_port': 'test5', + }, + } + } + + expected = { + 'machine': 'test1', + 'port': 'test5', + 'chassis': 'test4', + 'cards': ['0000', '0001'], + 'ports': ['07', '08'], + 'output_dir': 'test2', + 'version': 'test3', + 'bidir': True, + } + + result = ixnet_api.IxNextgen.get_config(tg_cfg) + self.assertEqual(result, expected) + + def test__get_config_element_by_flow_group_name(self): + self.ixnet_gen._ixnet.getList.side_effect = [['traffic_item'], + ['fg_01']] + self.ixnet_gen._ixnet.getAttribute.return_value = 'flow_group_01' + output = self.ixnet_gen._get_config_element_by_flow_group_name( + 'flow_group_01') + self.assertEqual('traffic_item/configElement:flow_group_01', output) + + def test__get_config_element_by_flow_group_name_no_match(self): + self.ixnet_gen._ixnet.getList.side_effect = [['traffic_item'], + ['fg_01']] + self.ixnet_gen._ixnet.getAttribute.return_value = 'flow_group_02' + output = self.ixnet_gen._get_config_element_by_flow_group_name( + 'flow_group_01') + self.assertIsNone(output) + + def test__get_stack_item(self): + self.ixnet_gen._ixnet.getList.return_value = ['tcp1', 'tcp2', 'udp'] + with mock.patch.object( + self.ixnet_gen, '_get_config_element_by_flow_group_name') as \ + mock_get_cfg_element: + mock_get_cfg_element.return_value = 'cfg_element' + output = self.ixnet_gen._get_stack_item(mock.ANY, ixnet_api.PROTO_TCP) + self.assertEqual(['tcp1', 'tcp2'], output) + + def test__get_stack_item_no_config_element(self): + with mock.patch.object( + self.ixnet_gen, '_get_config_element_by_flow_group_name', + return_value=None): + with self.assertRaises(exceptions.IxNetworkFlowNotPresent): + self.ixnet_gen._get_stack_item(mock.ANY, mock.ANY) + + def test__get_field_in_stack_item(self): + self.ixnet_gen._ixnet.getList.return_value = ['field1', 'field2'] + output = self.ixnet_gen._get_field_in_stack_item(mock.ANY, 'field2') + self.assertEqual('field2', output) + + def test__get_field_in_stack_item_no_field_present(self): + self.ixnet_gen._ixnet.getList.return_value = ['field1', 'field2'] + with self.assertRaises(exceptions.IxNetworkFieldNotPresentInStackItem): + self.ixnet_gen._get_field_in_stack_item(mock.ANY, 'field3') + + def test__parse_framesize(self): + framesize = {'64B': '75', '512b': '25'} + output = self.ixnet_gen._parse_framesize(framesize) + self.assertEqual(2, len(output)) + self.assertIn([64, 64, 75], output) + self.assertIn([512, 512, 25], output) + + def test_add_topology(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.add_topology('topology 1', 'vports') + self.ixnet_gen.ixnet.add.assert_called_once_with('my_root', 'topology') + self.ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'obj', '-name', 'topology 1', '-vports', 'vports') + self.ixnet_gen.ixnet.commit.assert_called_once() + + def test_add_device_group(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.add_device_group('topology', 'device group 1', '1') + self.ixnet_gen.ixnet.add.assert_called_once_with('topology', + 'deviceGroup') + self.ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'obj', '-name', 'device group 1', '-multiplier', '1') + self.ixnet_gen.ixnet.commit.assert_called_once() + + def test_add_ethernet(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.add_ethernet('device_group', 'ethernet 1') + self.ixnet_gen.ixnet.add.assert_called_once_with('device_group', + 'ethernet') + self.ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'obj', '-name', 'ethernet 1') + self.ixnet_gen.ixnet.commit.assert_called_once() + + def test_add_vlans_single(self): + obj = 'ethernet' + self.ixnet_gen.ixnet.getAttribute.return_value = 'attr' + self.ixnet_gen.ixnet.getList.return_value = ['vlan1', 'vlan2'] + vlan1 = ixnet_api.Vlan(vlan_id=100, tp_id='ethertype88a8', prio=2) + vlan2 = ixnet_api.Vlan(vlan_id=101, tp_id='ethertype88a8', prio=3) + self.ixnet_gen.add_vlans(obj, [vlan1, vlan2]) + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call('ethernet', + '-vlanCount', 2) + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call('attr/singleValue', + '-value', 100) + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call('attr/singleValue', + '-value', 101) + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call('attr/singleValue', + '-value', 2) + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call('attr/singleValue', + '-value', 3) + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', 'ethertype88a8') + self.assertEqual(self.ixnet.commit.call_count, 2) + + def test_add_vlans_increment(self): + obj = 'ethernet' + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.ixnet.getAttribute.return_value = 'attr' + self.ixnet_gen.ixnet.getList.return_value = ['vlan1'] + vlan = ixnet_api.Vlan(vlan_id=100, vlan_id_step=1, prio=3, prio_step=2) + self.ixnet_gen.add_vlans(obj, [vlan]) + self.ixnet.setMultiAttribute.assert_any_call('obj', '-start', 100, + '-step', 1, + '-direction', 'increment') + self.ixnet.setMultiAttribute.assert_any_call('obj', '-start', 3, + '-step', 2, + '-direction', 'increment') + + self.assertEqual(self.ixnet.commit.call_count, 2) + + def test_add_vlans_invalid(self): + vlans = [] + self.assertRaises(RuntimeError, self.ixnet_gen.add_vlans, 'obj', vlans) + + def test_add_ipv4(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.add_ipv4('ethernet 1', name='ipv4 1') + self.ixnet_gen.ixnet.add.assert_called_once_with('ethernet 1', 'ipv4') + self.ixnet_gen.ixnet.setAttribute.assert_called_once_with('obj', + '-name', + 'ipv4 1') + self.assertEqual(self.ixnet.commit.call_count, 2) + + def test_add_ipv4_single(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.ixnet.getAttribute.return_value = 'attr' + self.ixnet_gen.add_ipv4('ethernet 1', name='ipv4 1', addr='100.1.1.100', + prefix='24', gateway='100.1.1.200') + self.ixnet_gen.ixnet.add.assert_called_once_with('ethernet 1', 'ipv4') + self.ixnet_gen.ixnet.setAttribute.assert_called_once_with('obj', + '-name', + 'ipv4 1') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', '100.1.1.100') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', '24') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', '100.1.1.200') + + self.assertEqual(self.ixnet.commit.call_count, 2) + + def test_add_ipv4_counter(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.ixnet.getAttribute.return_value = 'attr' + self.ixnet_gen.add_ipv4('ethernet 1', name='ipv4 1', + addr='100.1.1.100', + addr_step='1', + addr_direction='increment', + prefix='24', + gateway='100.1.1.200', + gw_step='1', + gw_direction='increment') + self.ixnet_gen.ixnet.add.assert_any_call('ethernet 1', 'ipv4') + self.ixnet_gen.ixnet.setAttribute.assert_called_once_with('obj', + '-name', + 'ipv4 1') + self.ixnet_gen.ixnet.add.assert_any_call('attr', 'counter') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call('obj', '-start', + '100.1.1.100', + '-step', '1', + '-direction', + 'increment') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', '24') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call('obj', '-start', + '100.1.1.200', + '-step', '1', + '-direction', + 'increment') + self.assertEqual(self.ixnet.commit.call_count, 2) + + def test_add_pppox_client(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.ixnet.getAttribute.return_value = 'attr' + self.ixnet_gen.add_pppox_client('ethernet 1', 'pap', 'user', 'pwd') + self.ixnet_gen.ixnet.add.assert_called_once_with('ethernet 1', + 'pppoxclient') + + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', 'pap') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', 'user') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', 'pwd') + + self.assertEqual(self.ixnet.commit.call_count, 2) + + def test_add_pppox_client_invalid_auth(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.ixnet.getAttribute.return_value = 'attr' + self.assertRaises(NotImplementedError, self.ixnet_gen.add_pppox_client, + 'ethernet 1', 'invalid_auth', 'user', 'pwd') + + self.ixnet_gen.ixnet.setMultiAttribute.assert_not_called() + + def test_add_bgp(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.ixnet.getAttribute.return_value = 'attr' + self.ixnet_gen.add_bgp(ipv4='ipv4 1', + dut_ip='10.0.0.1', + local_as=65000, + bgp_type='external') + self.ixnet_gen.ixnet.add.assert_called_once_with('ipv4 1', 'bgpIpv4Peer') + self.ixnet_gen.ixnet.setAttribute.assert_any_call( + 'attr/singleValue', '-value', '10.0.0.1') + self.ixnet_gen.ixnet.setAttribute.assert_any_call( + 'attr/singleValue', '-value', 65000) + self.ixnet_gen.ixnet.setAttribute.assert_any_call( + 'attr/singleValue', '-value', 'external') + + def test_add_interface(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.add_interface(vport='vport', + ip='10.0.0.2', + mac='00:00:00:00:00:00', + gateway='10.0.0.1') + self.ixnet_gen.ixnet.add.assert_any_call('vport', 'interface') + self.ixnet_gen.ixnet.add.assert_any_call('obj', 'ipv4') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'obj/ethernet', '-macAddress', '00:00:00:00:00:00') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'obj', '-ip', '10.0.0.2') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'obj', '-gateway', '10.0.0.1') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'obj', '-enabled', 'true') + + def test_add_static_ipv4(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.add_static_ipv4(iface='iface', + vport='vport', + start_ip='10.0.0.0', + count='100', + mask='32') + self.ixnet_gen.ixnet.add.assert_called_once_with( + 'vport/protocols/static', 'ip') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'obj', '-protocolInterface', 'iface', + '-ipStart', '10.0.0.0', + '-count', '100', + '-mask', '32', + '-enabled', 'true') + + @mock.patch.object(IxNetwork, 'IxNet') + def test_connect(self, mock_ixnet): + mock_ixnet.return_value = self.ixnet + with mock.patch.object(self.ixnet_gen, 'get_config') as mock_config: + mock_config.return_value = {'machine': 'machine_fake', + 'port': 'port_fake', + 'version': 12345} + self.ixnet_gen.connect(mock.ANY) + + self.ixnet.connect.assert_called_once_with( + 'machine_fake', '-port', 'port_fake', '-version', '12345') + mock_config.assert_called_once() + + def test_connect_invalid_config_no_machine(self): + self.ixnet_gen.get_config = mock.Mock(return_value={ + 'port': 'port_fake', + 'version': '12345'}) + self.assertRaises(KeyError, self.ixnet_gen.connect, mock.ANY) + self.ixnet.connect.assert_not_called() + + def test_connect_invalid_config_no_port(self): + self.ixnet_gen.get_config = mock.Mock(return_value={ + 'machine': 'machine_fake', + 'version': '12345'}) + self.assertRaises(KeyError, self.ixnet_gen.connect, mock.ANY) + self.ixnet.connect.assert_not_called() + + def test_connect_invalid_config_no_version(self): + self.ixnet_gen.get_config = mock.Mock(return_value={ + 'machine': 'machine_fake', + 'port': 'port_fake'}) + self.assertRaises(KeyError, self.ixnet_gen.connect, mock.ANY) + self.ixnet.connect.assert_not_called() + + def test_connect_no_config(self): + self.ixnet_gen.get_config = mock.Mock(return_value={}) + self.assertRaises(KeyError, self.ixnet_gen.connect, mock.ANY) + self.ixnet.connect.assert_not_called() + + def test_clear_config(self): + self.ixnet_gen.clear_config() + self.ixnet.execute.assert_called_once_with('newConfig') + + @mock.patch.object(ixnet_api, 'log') + def test_assign_ports_2_ports(self, *args): + self.ixnet.getAttribute.side_effect = ['up', 'down'] + config = { + 'chassis': '1.1.1.1', + 'cards': ['1', '2'], + 'ports': ['2', '2']} + self.ixnet_gen._cfg = config + + self.assertIsNone(self.ixnet_gen.assign_ports()) + self.assertEqual(self.ixnet.execute.call_count, 1) + self.assertEqual(self.ixnet.commit.call_count, 3) + self.assertEqual(self.ixnet.getAttribute.call_count, 2) + + @mock.patch.object(ixnet_api, 'log') + def test_assign_ports_port_down(self, mock_log): + self.ixnet.getAttribute.return_value = 'down' + config = { + 'chassis': '1.1.1.1', + 'cards': ['1', '2'], + 'ports': ['3', '4']} + self.ixnet_gen._cfg = config + self.ixnet_gen.assign_ports() + mock_log.warning.assert_called() + + def test_assign_ports_no_config(self): + self.ixnet_gen._cfg = {} + self.assertRaises(KeyError, self.ixnet_gen.assign_ports) + + def test__create_traffic_item(self): + self.ixnet.add.return_value = 'my_new_traffic_item' + self.ixnet.remapIds.return_value = ['my_traffic_item_id'] + + self.ixnet_gen._create_traffic_item() + self.ixnet.add.assert_called_once_with( + 'my_root/traffic', 'trafficItem') + self.ixnet.setMultiAttribute.assert_called_once_with( + 'my_new_traffic_item', '-name', 'RFC2544', '-trafficType', 'raw') + self.assertEqual(2, self.ixnet.commit.call_count) + self.ixnet.remapIds.assert_called_once_with('my_new_traffic_item') + self.ixnet.setAttribute('my_traffic_item_id/tracking', + '-trackBy', 'trafficGroupId0') + + def test__create_flow_groups(self): + uplink_endpoints = ['up_endp1', 'up_endp2'] + downlink_endpoints = ['down_endp1', 'down_endp2'] + self.ixnet_gen.ixnet.getList.side_effect = [['traffic_item'], ['1', '2']] + self.ixnet_gen.ixnet.add.side_effect = ['endp1', 'endp2', 'endp3', + 'endp4'] + self.ixnet_gen._create_flow_groups(uplink_endpoints, downlink_endpoints) + self.ixnet_gen.ixnet.add.assert_has_calls([ + mock.call('traffic_item', 'endpointSet'), + mock.call('traffic_item', 'endpointSet')]) + self.ixnet_gen.ixnet.setMultiAttribute.assert_has_calls([ + mock.call('endp1', '-name', '1', '-sources', ['up_endp1'], + '-destinations', ['down_endp1']), + mock.call('endp2', '-name', '2', '-sources', ['down_endp1'], + '-destinations', ['up_endp1']), + mock.call('endp3', '-name', '3', '-sources', ['up_endp2'], + '-destinations', ['down_endp2']), + mock.call('endp4', '-name', '4', '-sources', ['down_endp2'], + '-destinations', ['up_endp2'])]) + + def test__append_protocol_to_stack(self): + + self.ixnet_gen._append_procotol_to_stack('my_protocol', 'prev_element') + self.ixnet.execute.assert_called_with( + 'append', 'prev_element', + 'my_root/traffic/protocolTemplate:"my_protocol"') + + def test__setup_config_elements(self): + # the config parsed from some_file + yaml_data = {'traffic_profile': {} + } + traffic_profile = ixia_rfc2544.IXIARFC2544Profile(yaml_data) + traffic_profile.params = TRAFFIC_PROFILE + self.ixnet_gen.ixnet.getList.side_effect = [['traffic_item'], + ['cfg_element']] + with mock.patch.object(self.ixnet_gen, '_append_procotol_to_stack') as \ + mock_append_proto: + self.ixnet_gen._setup_config_elements(traffic_profile=traffic_profile) + mock_append_proto.assert_has_calls([ + mock.call(ixnet_api.PROTO_UDP, 'cfg_element/stack:"ethernet-1"'), + mock.call(ixnet_api.PROTO_IPV4, 'cfg_element/stack:"ethernet-1"')]) + self.ixnet_gen.ixnet.setAttribute.assert_has_calls([ + mock.call('cfg_element/frameRateDistribution', '-portDistribution', + 'splitRateEvenly'), + mock.call('cfg_element/frameRateDistribution', + '-streamDistribution', 'splitRateEvenly')]) + + @mock.patch.object(ixnet_api.IxNextgen, '_create_traffic_item') + @mock.patch.object(ixnet_api.IxNextgen, '_create_flow_groups') + @mock.patch.object(ixnet_api.IxNextgen, '_setup_config_elements') + def test_create_traffic_model(self, mock__setup_config_elements, + mock__create_flow_groups, + mock__create_traffic_item): + # the config parsed from some_file + yaml_data = {'traffic_profile': {}} + traffic_profile = ixia_rfc2544.IXIARFC2544Profile(yaml_data) + uplink_ports = ['port1', 'port3'] + downlink_ports = ['port2', 'port4'] + uplink_endpoints = ['port1/protocols', 'port3/protocols'] + downlink_endpoints = ['port2/protocols', 'port4/protocols'] + self.ixnet_gen.create_traffic_model(uplink_ports, downlink_ports, + traffic_profile=traffic_profile) + mock__create_traffic_item.assert_called_once_with('raw') + mock__create_flow_groups.assert_called_once_with(uplink_endpoints, + downlink_endpoints) + mock__setup_config_elements.assert_called_once() + + @mock.patch.object(ixnet_api.IxNextgen, '_create_traffic_item') + @mock.patch.object(ixnet_api.IxNextgen, '_create_flow_groups') + @mock.patch.object(ixnet_api.IxNextgen, '_setup_config_elements') + def test_create_ipv4_traffic_model(self, mock__setup_config_elements, + mock__create_flow_groups, + mock__create_traffic_item): + uplink_topologies = ['up1', 'up3'] + downlink_topologies = ['down2', 'down4'] + traffic_profile = 'fake_profile' + self.ixnet_gen.create_ipv4_traffic_model(uplink_topologies, + downlink_topologies, + traffic_profile) + mock__create_traffic_item.assert_called_once_with('ipv4') + mock__create_flow_groups.assert_called_once_with(uplink_topologies, + downlink_topologies) + mock__setup_config_elements.assert_called_once_with( + traffic_profile='fake_profile', add_default_proto=False) + + def test_flows_settings(self): + cfg = {'uplink_0': { + 'ipv4': { + 'outer_l2': { + 'framesize': { + '128B': '0', + '1518B': '0', + '64B': '0', + '373b': '0', + '256B': '0', + '1400B': '0', + '570B': '0'}}, + 'id': 1}}} + + expected = [ + {'ipv4': { + 'id': 1, + 'outer_l2': { + 'framesize': { + '1518B': '0', + '1400B': '0', + '128B': '0', + '64B': '0', + '256B': '0', + '373b': '0', + '570B': '0'}}}}] + + self.assertEqual(expected, self.ixnet_gen._flows_settings(cfg=cfg)) + + def test_is_qinq(self): + flow_data = {'ipv4': { + 'outer_l2': {}, + 'id': 1}} + self.assertEqual(False, self.ixnet_gen.is_qinq(flow_data=flow_data)) + + flow_data = {'ipv4': { + 'outer_l2': { + 'QinQ': { + 'C-VLAN': { + 'priority': 0, + 'cfi': 0, + 'id': 512}, + 'S-VLAN': { + 'priority': 0, + 'cfi': 0, + 'id': 128}}, + }, + 'id': 1}} + self.assertEqual(True, self.ixnet_gen.is_qinq(flow_data=flow_data)) + + def test__update_frame_mac(self): + with mock.patch.object(self.ixnet_gen, '_get_field_in_stack_item') as \ + mock_get_field: + mock_get_field.return_value = 'field_descriptor' + self.ixnet_gen._update_frame_mac('ethernet_descriptor', 'field', 'mac') + mock_get_field.assert_called_once_with('ethernet_descriptor', 'field') + self.ixnet_gen.ixnet.setMultiAttribute( + 'field_descriptor', '-singleValue', 'mac', '-fieldValue', 'mac', + '-valueType', 'singleValue') + self.ixnet_gen.ixnet.commit.assert_called_once() + + def test_update_frame(self): + with mock.patch.object( + self.ixnet_gen, '_get_config_element_by_flow_group_name', + return_value='cfg_element'), \ + mock.patch.object(self.ixnet_gen, '_update_frame_mac') as \ + mock_update_frame, \ + mock.patch.object(self.ixnet_gen, '_get_stack_item') as \ + mock_get_stack_item: + mock_get_stack_item.side_effect = [['item1'], ['item2'], + ['item3'], ['item4']] + self.ixnet_gen.update_frame(TRAFFIC_PARAMETERS, 50) + + self.assertEqual(6, len(self.ixnet_gen.ixnet.setMultiAttribute.mock_calls)) + self.assertEqual(4, len(mock_update_frame.mock_calls)) + + self.ixnet_gen.ixnet.setMultiAttribute.assert_has_calls([ + mock.call('cfg_element/transmissionControl', + '-type', 'continuous', '-duration', 50) + ]) + + def test_update_frame_qinq(self): + with mock.patch.object(self.ixnet_gen, + '_get_config_element_by_flow_group_name', + return_value='cfg_element'), \ + mock.patch.object(self.ixnet_gen, '_update_frame_mac'),\ + mock.patch.object(self.ixnet_gen, '_get_stack_item', + return_value='item'), \ + mock.patch.object(self.ixnet_gen, '_get_field_in_stack_item', + return_value='field'): + + traffic_parameters = deepcopy(TRAFFIC_PARAMETERS) + traffic_parameters[UPLINK]['outer_l2']['QinQ'] = { + 'S-VLAN': {'id': 128, + 'priority': 1, + 'cfi': 0}, + 'C-VLAN': {'id': 512, + 'priority': 0, + 'cfi': 2} + } + + self.ixnet_gen.update_frame(traffic_parameters, 50) + + self.ixnet_gen.ixnet.setMultiAttribute.assert_has_calls([ + mock.call('field', '-auto', 'false', '-singleValue', '0x88a8', + '-fieldValue', '0x88a8', '-valueType', 'singleValue'), + mock.call('field', '-auto', 'false', '-singleValue', 1, + '-fieldValue', 1, '-valueType', 'singleValue'), + mock.call('field', '-auto', 'false', '-singleValue', 128, + '-fieldValue', 128, '-valueType', 'singleValue'), + mock.call('field', '-auto', 'false', '-singleValue', 512, + '-fieldValue', 512, '-valueType', 'singleValue'), + mock.call('field', '-auto', 'false', '-singleValue', 2, + '-fieldValue', 2, '-valueType', 'singleValue') + ], any_order=True) + + def test_update_frame_flow_not_present(self): + with mock.patch.object( + self.ixnet_gen, '_get_config_element_by_flow_group_name', + return_value=None): + with self.assertRaises(exceptions.IxNetworkFlowNotPresent): + self.ixnet_gen.update_frame(TRAFFIC_PARAMETERS, 40) + + def test_get_statistics(self): + with mock.patch.object(self.ixnet_gen, '_build_stats_map') as \ + mock_build_stats: + self.ixnet_gen.get_statistics() + + mock_build_stats.assert_has_calls([ + mock.call(self.ixnet_gen.PORT_STATISTICS, + self.ixnet_gen.PORT_STATS_NAME_MAP), + mock.call(self.ixnet_gen.FLOW_STATISTICS, + self.ixnet_gen.LATENCY_NAME_MAP)]) + + def test_set_flow_tracking(self): + self.ixnet_gen._ixnet.getList.return_value = ['traffic_item'] + self.ixnet_gen.set_flow_tracking(track_by=['vlanVlanId0']) + self.ixnet_gen.ixnet.setAttribute.assert_called_once_with( + 'traffic_item/tracking', '-trackBy', ['vlanVlanId0']) + self.assertEqual(self.ixnet.commit.call_count, 1) + + def test__set_egress_flow_tracking(self): + self.ixnet_gen._ixnet.getList.side_effect = [['traffic_item'], + ['encapsulation']] + self.ixnet_gen._set_egress_flow_tracking(encapsulation='Ethernet', + offset='IPv4 TOS Precedence') + self.ixnet_gen.ixnet.setAttribute.assert_any_call( + 'traffic_item', '-egressEnabled', True) + self.ixnet_gen.ixnet.setAttribute.assert_any_call( + 'encapsulation', '-encapsulation', 'Ethernet') + self.ixnet_gen.ixnet.setAttribute.assert_any_call( + 'encapsulation', '-offset', 'IPv4 TOS Precedence') + self.assertEqual(self.ixnet.commit.call_count, 2) + + def test__get_view_page_stats(self): + expected_stats = [ + {'header1': 'row1_1', 'header2': 'row1_2'}, + {'header1': 'row2_1', 'header2': 'row2_2'} + ] + self.ixnet_gen._ixnet.getAttribute.side_effect = [ + ['header1', 'header2'], + [ + [['row1_1', 'row1_2']], + [['row2_1', 'row2_2']] + ] + ] + stats = self.ixnet_gen._get_view_page_stats('view_obj') + self.assertListEqual(stats, expected_stats) + + @mock.patch.object(ixnet_api.IxNextgen, '_get_view_page_stats') + def test_get_pppoe_scenario_statistics(self, mock_get_view): + + pattern = re.compile('Flow 2') + + expected_stats = { + 'port_statistics': [{ + 'port_1': 'port_stat1', + 'port_2': 'port_stat2' + }], + 'flow_statistic': [{ + 'flow_1': 'flow_stat1', + 'flow_2': 'flow_stat2' + }], + 'pppox_client_per_port': [{ + 'sub_1': 'sub_stat1', + 'sub_2': 'sub_stat2' + }] + } + + pppoe_scenario_stats = OrderedDict([ + ('port_statistics', 'view_obj'), + ('flow_statistic', 'view_obj'), + ('pppox_client_per_port', 'view_obj') + ]) + + pppoe_scenario_stats_map = { + 'port_statistics': {'port_1': 'Port 1', + 'port_2': 'Port 2'}, + 'flow_statistic': {'flow_1': 'Flow 1', + 'flow_2': pattern}, + 'pppox_client_per_port': {'sub_1': 'Sub 1', + 'sub_2': 'Sub 2'} + } + + # All stats keys + port_stats = [{'Port 1': 'port_stat1', + 'Port 2': 'port_stat2', + 'Port 3': 'port_stat3'}] + flows_stats = [{'Flow 1': 'flow_stat1', + 'Flow 2': 'flow_stat2', + 'Flow 3': 'flow_stat3'}] + pppoe_sub_stats = [{'Sub 1': 'sub_stat1', + 'Sub 2': 'sub_stat2', + 'Sub 3': 'sub_stat3'}] + + mock_get_view.side_effect = [port_stats, flows_stats, pppoe_sub_stats] + self.ixnet_gen._ixnet.getAttribute.return_value = '1' + + with mock.patch.multiple(ixnet_api.IxNextgen, + PPPOE_SCENARIO_STATS=pppoe_scenario_stats, + PPPOE_SCENARIO_STATS_MAP=pppoe_scenario_stats_map): + stats = self.ixnet_gen.get_pppoe_scenario_statistics() + self.assertDictEqual(stats, expected_stats) + self.assertEqual(self.ixnet_gen.ixnet.getAttribute.call_count, 6) + self.ixnet_gen.ixnet.setAttribute.assert_not_called() + + def test__update_ipv4_address(self): + with mock.patch.object(self.ixnet_gen, '_get_field_in_stack_item', + return_value='field_desc'): + self.ixnet_gen._update_ipv4_address(mock.ANY, mock.ANY, '192.168.1.1', + 100, 26, 25) + self.ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'field_desc', '-seed', 100, '-fixedBits', '192.168.1.1', + '-randomMask', '0.0.0.63', '-valueType', 'random', + '-countValue', 25) + + def test__update_ipv4_priority_raw(self): + priority = {'raw': '0x01'} + self.ixnet_gen._set_priority_field = mock.Mock() + with mock.patch.object(self.ixnet_gen, '_get_field_in_stack_item', + return_value='field_desc'): + self.ixnet_gen._update_ipv4_priority('field_desc', priority) + + self.ixnet_gen._set_priority_field.assert_called_once_with( + 'field_desc', priority['raw']) + + def test__update_ipv4_priority_dscp(self): + priority = {'dscp': {'defaultPHB': [0, 1, 2, 3]}} + self.ixnet_gen._set_priority_field = mock.Mock() + with mock.patch.object(self.ixnet_gen, '_get_field_in_stack_item', + return_value='field_desc'): + self.ixnet_gen._update_ipv4_priority('field_desc', priority) + + self.ixnet_gen._set_priority_field.assert_called_once_with( + 'field_desc', priority['dscp']['defaultPHB']) + + def test__update_ipv4_priority_tos(self): + priority = {'tos': {'precedence': [0, 4, 7]}} + self.ixnet_gen._set_priority_field = mock.Mock() + with mock.patch.object(self.ixnet_gen, '_get_field_in_stack_item', + return_value='field_desc'): + self.ixnet_gen._update_ipv4_priority('field_desc', priority) + + self.ixnet_gen._set_priority_field.assert_called_once_with( + 'field_desc', priority['tos']['precedence']) + + def test__update_ipv4_priority_wrong_priority_type(self): + priority = {'test': [0, 4, 7]} + self.ixnet_gen._set_priority_field = mock.Mock() + with mock.patch.object(self.ixnet_gen, '_get_field_in_stack_item', + return_value='field_desc'): + self.ixnet_gen._update_ipv4_priority('field_desc', priority) + + self.ixnet_gen._set_priority_field.assert_not_called() + + def test__update_ipv4_priority_not_supported_dscp_class(self): + priority = {'dscp': {'testPHB': [0, 4, 7]}} + self.ixnet_gen._set_priority_field = mock.Mock() + self.ixnet_gen._get_field_in_stack_item = mock.Mock() + self.ixnet_gen._update_ipv4_priority('field_desc', priority) + self.ixnet_gen._set_priority_field.assert_not_called() + self.ixnet_gen._get_field_in_stack_item.assert_not_called() + + def test__update_ipv4_priority_not_supported_tos_field(self): + priority = {'tos': {'test': [0, 4, 7]}} + self.ixnet_gen._set_priority_field = mock.Mock() + self.ixnet_gen._get_field_in_stack_item = mock.Mock() + self.ixnet_gen._update_ipv4_priority('field_desc', priority) + self.ixnet_gen._set_priority_field.assert_not_called() + self.ixnet_gen._get_field_in_stack_item.assert_not_called() + + def test__set_priority_field_list_value(self): + value = [1, 4, 7] + self.ixnet_gen._set_priority_field('field_desc', value) + self.ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'field_desc', + '-valueList', [1, 4, 7], + '-activeFieldChoice', 'true', + '-valueType', 'valueList') + + def test__set_priority_field_single_value(self): + value = 7 + self.ixnet_gen._set_priority_field('field_desc', value) + self.ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'field_desc', + '-activeFieldChoice', 'true', + '-singleValue', '7') + + def test__update_udp_port(self): + with mock.patch.object(self.ixnet_gen, '_get_field_in_stack_item', + return_value='field_desc'): + self.ixnet_gen._update_udp_port(mock.ANY, mock.ANY, 1234, + 2, 0, 2) + + self.ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'field_desc', + '-auto', 'false', + '-seed', 1, + '-fixedBits', 1234, + '-randomMask', 0, + '-valueType', 'random', + '-countValue', 1) + + def test_update_ip_packet(self): + with mock.patch.object(self.ixnet_gen, '_update_ipv4_address') as \ + mock_update_add, \ + mock.patch.object(self.ixnet_gen, '_get_stack_item'), \ + mock.patch.object(self.ixnet_gen, + '_get_config_element_by_flow_group_name', return_value='celm'), \ + mock.patch.object(self.ixnet_gen, '_update_ipv4_priority') as \ + mock_update_priority: + self.ixnet_gen.update_ip_packet(TRAFFIC_PARAMETERS) + + self.assertEqual(4, len(mock_update_add.mock_calls)) + self.assertEqual(2, len(mock_update_priority.mock_calls)) + + def test_update_ip_packet_exception_no_config_element(self): + with mock.patch.object(self.ixnet_gen, + '_get_config_element_by_flow_group_name', + return_value=None): + with self.assertRaises(exceptions.IxNetworkFlowNotPresent): + self.ixnet_gen.update_ip_packet(TRAFFIC_PARAMETERS) + + def test_update_l4(self): + with mock.patch.object(self.ixnet_gen, '_update_udp_port') as \ + mock_update_udp, \ + mock.patch.object(self.ixnet_gen, '_get_stack_item'), \ + mock.patch.object(self.ixnet_gen, + '_get_config_element_by_flow_group_name', return_value='celm'): + self.ixnet_gen.update_l4(TRAFFIC_PARAMETERS) + + self.assertEqual(4, len(mock_update_udp.mock_calls)) + + def test_update_l4_exception_no_config_element(self): + with mock.patch.object(self.ixnet_gen, + '_get_config_element_by_flow_group_name', + return_value=None): + with self.assertRaises(exceptions.IxNetworkFlowNotPresent): + self.ixnet_gen.update_l4(TRAFFIC_PARAMETERS) + + def test_update_l4_exception_no_supported_proto(self): + traffic_parameters = { + UPLINK: { + 'id': 1, + 'outer_l3': { + 'proto': 'unsupported', + }, + 'outer_l4': { + 'seed': 1 + } + }, + } + with mock.patch.object(self.ixnet_gen, + '_get_config_element_by_flow_group_name', + return_value='celm'): + with self.assertRaises(exceptions.IXIAUnsupportedProtocol): + self.ixnet_gen.update_l4(traffic_parameters) + + @mock.patch.object(ixnet_api.IxNextgen, '_get_traffic_state') + def test_start_traffic(self, mock_ixnextgen_get_traffic_state): + self.ixnet_gen._ixnet.getList.return_value = [0] + + mock_ixnextgen_get_traffic_state.side_effect = [ + 'stopped', 'started', 'started', 'started'] + + result = self.ixnet_gen.start_traffic() + self.assertIsNone(result) + self.ixnet.getList.assert_called_once() + self.assertEqual(3, self.ixnet_gen._ixnet.execute.call_count) + + @mock.patch.object(ixnet_api.IxNextgen, '_get_traffic_state') + def test_start_traffic_traffic_running( + self, mock_ixnextgen_get_traffic_state): + self.ixnet_gen._ixnet.getList.return_value = [0] + mock_ixnextgen_get_traffic_state.side_effect = [ + 'started', 'stopped', 'started'] + + result = self.ixnet_gen.start_traffic() + self.assertIsNone(result) + self.ixnet.getList.assert_called_once() + self.assertEqual(4, self.ixnet_gen._ixnet.execute.call_count) + + @mock.patch.object(ixnet_api.IxNextgen, '_get_traffic_state') + def test_start_traffic_wait_for_traffic_to_stop( + self, mock_ixnextgen_get_traffic_state): + self.ixnet_gen._ixnet.getList.return_value = [0] + mock_ixnextgen_get_traffic_state.side_effect = [ + 'started', 'started', 'started', 'stopped', 'started'] + + result = self.ixnet_gen.start_traffic() + self.assertIsNone(result) + self.ixnet.getList.assert_called_once() + self.assertEqual(4, self.ixnet_gen._ixnet.execute.call_count) + + @mock.patch.object(ixnet_api.IxNextgen, '_get_traffic_state') + def test_start_traffic_wait_for_traffic_start( + self, mock_ixnextgen_get_traffic_state): + self.ixnet_gen._ixnet.getList.return_value = [0] + mock_ixnextgen_get_traffic_state.side_effect = [ + 'stopped', 'stopped', 'stopped', 'started'] + + result = self.ixnet_gen.start_traffic() + self.assertIsNone(result) + self.ixnet.getList.assert_called_once() + self.assertEqual(3, self.ixnet_gen._ixnet.execute.call_count) + + def test__get_protocol_status(self): + self.ixnet.getAttribute.return_value = ['up'] + self.ixnet_gen._get_protocol_status('ipv4') + self.ixnet.getAttribute.assert_called_once_with('ipv4', + '-sessionStatus') + + @mock.patch.object(ixnet_api.IxNextgen, '_get_protocol_status') + def test_is_protocols_running(self, mock_ixnextgen_get_protocol_status): + mock_ixnextgen_get_protocol_status.return_value = ['up', 'up'] + result = self.ixnet_gen.is_protocols_running(['ethernet', 'ipv4']) + self.assertTrue(result) + + @mock.patch.object(ixnet_api.IxNextgen, '_get_protocol_status') + def test_is_protocols_stopped(self, mock_ixnextgen_get_protocol_status): + mock_ixnextgen_get_protocol_status.return_value = ['down', 'down'] + result = self.ixnet_gen.is_protocols_running(['ethernet', 'ipv4']) + self.assertFalse(result) + + def test_start_protocols(self): + self.ixnet_gen.start_protocols() + self.ixnet.execute.assert_called_once_with('startAllProtocols') + + def test_stop_protocols(self): + self.ixnet_gen.stop_protocols() + self.ixnet.execute.assert_called_once_with('stopAllProtocols') + + def test_get_vports(self): + self.ixnet_gen._ixnet.getRoot.return_value = 'root' + self.ixnet_gen.get_vports() + self.ixnet.getList.assert_called_once_with('root', 'vport') diff --git a/yardstick/tests/unit/network_services/nfvi/__init__.py b/yardstick/tests/unit/network_services/nfvi/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/network_services/nfvi/__init__.py diff --git a/yardstick/tests/unit/network_services/nfvi/test_collectd.py b/yardstick/tests/unit/network_services/nfvi/test_collectd.py new file mode 100644 index 000000000..fe59aecfb --- /dev/null +++ b/yardstick/tests/unit/network_services/nfvi/test_collectd.py @@ -0,0 +1,150 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import multiprocessing +import mock + +from yardstick.network_services.nfvi.collectd import AmqpConsumer + + +class TestAmqpConsumer(unittest.TestCase): + def setUp(self): + self.queue = multiprocessing.Queue() + self.url = 'amqp://admin:admin@127.0.0.1:5672/%2F' + self.amqp_consumer = AmqpConsumer(self.url, self.queue) + + def test___init__(self): + self.assertEqual(self.url, self.amqp_consumer._url) + + def test_on_connection_open(self): + self.amqp_consumer._connection = mock.Mock(autospec=AmqpConsumer) + self.amqp_consumer._connection.add_on_close_callback = \ + mock.Mock(return_value=0) + self.amqp_consumer._connection.channel = mock.Mock(return_value=0) + self.assertIsNone(self.amqp_consumer.on_connection_open(10)) + + def test_on_connection_closed(self): + self.amqp_consumer._connection = mock.Mock(autospec=AmqpConsumer) + self.amqp_consumer._connection.ioloop = mock.Mock() + self.amqp_consumer._connection.ioloop.stop = mock.Mock(return_value=0) + self.amqp_consumer._connection.add_timeout = mock.Mock(return_value=0) + self.amqp_consumer._closing = True + self.assertIsNone( + self.amqp_consumer.on_connection_closed("", 404, "Not Found")) + self.amqp_consumer._closing = False + self.assertIsNone( + self.amqp_consumer.on_connection_closed("", 404, "Not Found")) + + def test_reconnect(self): + self.amqp_consumer._connection = mock.Mock(autospec=AmqpConsumer) + self.amqp_consumer._connection.ioloop = mock.Mock() + self.amqp_consumer._connection.ioloop.stop = mock.Mock(return_value=0) + self.amqp_consumer.connect = mock.Mock(return_value=0) + self.amqp_consumer._closing = True + self.assertIsNone(self.amqp_consumer.reconnect()) + + def test_on_channel_open(self): + self.amqp_consumer._connection = mock.Mock(autospec=AmqpConsumer) + self.amqp_consumer._connection.add_on_close_callback = \ + mock.Mock(return_value=0) + self.amqp_consumer._channel = mock.Mock() + self.amqp_consumer.add_on_channel_close_callback = mock.Mock() + self.amqp_consumer._channel.exchange_declare = \ + mock.Mock(return_value=0) + self.assertIsNone( + self.amqp_consumer.on_channel_open(self.amqp_consumer._channel)) + + def test_add_on_channel_close_callback(self): + self.amqp_consumer._connection = mock.Mock(autospec=AmqpConsumer) + self.amqp_consumer._connection.add_on_close_callback = \ + mock.Mock(return_value=0) + self.amqp_consumer._channel = mock.Mock() + self.amqp_consumer._channel.add_on_close_callback = mock.Mock() + self.assertIsNone(self.amqp_consumer.add_on_channel_close_callback()) + + def test_on_channel_closed(self): + self.amqp_consumer._connection = mock.Mock(autospec=AmqpConsumer) + self.amqp_consumer._connection.close = mock.Mock(return_value=0) + _channel = mock.Mock() + self.assertIsNone( + self.amqp_consumer.on_channel_closed(_channel, "", "")) + + def test_ion_exchange_declareok(self): + self.amqp_consumer.setup_queue = mock.Mock(return_value=0) + self.assertIsNone(self.amqp_consumer.on_exchange_declareok(10)) + + def test_setup_queue(self): + self.amqp_consumer._channel = mock.Mock() + self.amqp_consumer._channel.add_on_close_callback = mock.Mock() + self.assertIsNone(self.amqp_consumer.setup_queue("collectd")) + + def test_on_queue_declareok(self): + self.amqp_consumer._channel = mock.Mock() + self.amqp_consumer._channel.queue_bind = mock.Mock() + self.assertIsNone(self.amqp_consumer.on_queue_declareok(10)) + + def test__on_bindok(self): + self.amqp_consumer._channel = mock.Mock() + self.amqp_consumer._channel.basic_consume = mock.Mock() + self.amqp_consumer.add_on_cancel_callback = mock.Mock() + self.assertIsNone(self.amqp_consumer._on_bindok(10)) + + def test_add_on_cancel_callback(self): + self.amqp_consumer._channel = mock.Mock() + self.amqp_consumer._channel.add_on_cancel_callback = mock.Mock() + self.assertIsNone(self.amqp_consumer.add_on_cancel_callback()) + + def test_on_consumer_cancelled(self): + self.amqp_consumer._channel = mock.Mock() + self.amqp_consumer._channel.close = mock.Mock() + self.assertIsNone(self.amqp_consumer.on_consumer_cancelled(10)) + + def test_on_message(self): + body = "msg {} cpu/cpu-0/ipc 101010:10" + properties = "" + basic_deliver = mock.Mock() + basic_deliver.delivery_tag = mock.Mock(return_value=0) + self.amqp_consumer.ack_message = mock.Mock() + self.assertIsNone( + self.amqp_consumer.on_message(10, basic_deliver, properties, body)) + + def test_ack_message(self): + self.amqp_consumer._channel = mock.Mock() + self.amqp_consumer._channel.basic_ack = mock.Mock() + self.assertIsNone(self.amqp_consumer.ack_message(10)) + + def test_on_cancelok(self): + self.amqp_consumer._channel = mock.Mock() + self.amqp_consumer._channel.close = mock.Mock() + self.assertIsNone(self.amqp_consumer.on_cancelok(10)) + + def test_run(self): + self.amqp_consumer._connection = mock.Mock(autospec=AmqpConsumer) + self.amqp_consumer.connect = mock.Mock() + self.amqp_consumer._connection.ioloop.start = mock.Mock() + self.assertIsNone(self.amqp_consumer.run()) + + def test_stop(self): + self.amqp_consumer._connection = mock.Mock(autospec=AmqpConsumer) + self.amqp_consumer.connect = mock.Mock() + self.amqp_consumer._connection.ioloop.start = mock.Mock() + self.amqp_consumer._channel = mock.Mock() + self.amqp_consumer._channel.basic_cancel = mock.Mock() + self.assertIsNone(self.amqp_consumer.stop()) + + def test_close_connection(self): + self.amqp_consumer._connection = mock.Mock(autospec=AmqpConsumer) + self.amqp_consumer._connection.close = mock.Mock() + self.assertIsNone(self.amqp_consumer.close_connection()) diff --git a/yardstick/tests/unit/network_services/nfvi/test_resource.py b/yardstick/tests/unit/network_services/nfvi/test_resource.py new file mode 100644 index 000000000..c06658218 --- /dev/null +++ b/yardstick/tests/unit/network_services/nfvi/test_resource.py @@ -0,0 +1,319 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import errno + +import mock +import unittest + +from yardstick.common import exceptions +from yardstick.common.exceptions import ResourceCommandError +from yardstick.network_services.nfvi.resource import ResourceProfile +from yardstick.network_services.nfvi import resource, collectd + + +class TestResourceProfile(unittest.TestCase): + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{'short-name': 'VpeVnf', + 'vdu': + [{'routing_table': + [{'network': '172.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '172.16.100.20', + 'if': 'xe0'}, + {'network': '172.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '172.16.40.20', + 'if': 'xe1'}], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': + [{'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'id': 'vpevnf-baremetal', + 'external-interface': + [{'virtual-interface': + {'dst_mac': '3c:fd:fe:9e:64:38', + 'vpci': '0000:05:00.0', + 'local_ip': '172.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'dst_ip': '172.16.100.20', + 'local_mac': '3c:fd:fe:a1:2b:80'}, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + {'dst_mac': '00:1e:67:d0:60:5c', + 'vpci': '0000:05:00.1', + 'local_ip': '172.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '172.16.40.20', + 'local_mac': '3c:fd:fe:a1:2b:81'}, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'}]}], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': + {'vdu-id': 'vpevnf-baremetal', + 'host': '127.0.0.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '127.0.0.1'}, + 'benchmark': + {'kpi': ['packets_in', 'packets_fwd', 'packets_dropped']}, + 'connection-point': [{'type': 'VPORT', 'name': 'xe0'}, + {'type': 'VPORT', 'name': 'xe1'}], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh'}]}} + + def setUp(self): + with mock.patch("yardstick.ssh.AutoConnectSSH") as ssh: + self.ssh_mock = mock.Mock(autospec=ssh.SSH) + self.ssh_mock.execute = \ + mock.Mock(return_value=(0, "", "")) + ssh.from_node.return_value = self.ssh_mock + + mgmt = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]['mgmt-interface'] + # interfaces = \ + # self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]['vdu'][0]['external-interface'] + port_names = \ + self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]['vdu'][0]['external-interface'] + self.resource_profile = \ + ResourceProfile(mgmt, port_names) + self.resource_profile.connection = self.ssh_mock + + def test___init__(self): + self.assertTrue(self.resource_profile.enable) + + def test_check_if_system_agent_running(self): + self.assertEqual(self.resource_profile.check_if_system_agent_running("collectd"), + (0, "")) + + def test_check_if_system_agent_running_excetion(self): + with mock.patch.object(self.resource_profile.connection, "execute") as mock_execute: + mock_execute.side_effect = OSError(errno.ECONNRESET, "error") + self.assertEqual( + self.resource_profile.check_if_system_agent_running("collectd"), + (1, None)) + + def test_get_cpu_data(self): + reskey = ["", "cpufreq", "cpufreq-0"] + value = "metric:10" + val = self.resource_profile.get_cpu_data(reskey[1], reskey[2], value) + self.assertIsNotNone(val) + + def test_get_cpu_data_error(self): + reskey = ["", "", ""] + value = "metric:10" + val = self.resource_profile.get_cpu_data(reskey[0], reskey[1], value) + self.assertEqual(val, ('error', 'Invalid', '', '')) + + def test__start_collectd(self): + ssh_mock = mock.Mock() + ssh_mock.execute = mock.Mock(return_value=(0, "", "")) + self.assertIsNone(self.resource_profile._start_collectd(ssh_mock, + "/opt/nsb_bin")) + + ssh_mock.execute = mock.Mock(side_effect=exceptions.SSHError) + with self.assertRaises(exceptions.SSHError): + self.resource_profile._start_collectd(ssh_mock, "/opt/nsb_bin") + + ssh_mock.execute = mock.Mock(return_value=(1, "", "")) + with self.assertRaises(ResourceCommandError): + self.resource_profile._start_collectd(ssh_mock, "/opt/nsb_bin") + + def test__reset_rabbitmq(self): + ssh_mock = mock.Mock() + ssh_mock.execute = mock.Mock(return_value=(1, "", "")) + with self.assertRaises(exceptions.ResourceCommandError): + self.resource_profile._reset_rabbitmq(ssh_mock) + + def test__check_rabbitmq_user(self): + ssh_mock = mock.Mock() + ssh_mock.execute = mock.Mock(return_value=(0, "title\nadmin\t[]", "")) + self.assertTrue(self.resource_profile._check_rabbitmq_user(ssh_mock)) + + def test__set_rabbitmq_admin_user(self): + ssh_mock = mock.Mock() + ssh_mock.execute = mock.Mock(return_value=(1, "", "")) + with self.assertRaises(exceptions.ResourceCommandError): + self.resource_profile._set_rabbitmq_admin_user(ssh_mock) + + def test__start_rabbitmq(self): + ssh_mock = mock.Mock() + self.resource_profile._reset_rabbitmq = mock.Mock() + self.resource_profile._set_rabbitmq_admin_user = mock.Mock() + + self.resource_profile._reset_mq_flag = True + ssh_mock.execute = mock.Mock(return_value=(1, "", "")) + with self.assertRaises(exceptions.ResourceCommandError): + self.resource_profile._start_rabbitmq(ssh_mock) + + self.resource_profile._reset_mq_flag = False + self.resource_profile._check_rabbitmq_user = mock.Mock(return_value=False) + ssh_mock.execute = mock.Mock(return_value=(1, "", "")) + with self.assertRaises(exceptions.ResourceCommandError): + self.resource_profile._start_rabbitmq(ssh_mock) + + def test__prepare_collectd_conf(self): + self.assertIsNone( + self.resource_profile._prepare_collectd_conf("/opt/nsb_bin")) + + def test__setup_ovs_stats(self): + # TODO(elfoley): This method doesn't actually return anything, the side + # effects should be checked + self.assertIsNone( + self.resource_profile._setup_ovs_stats(self.ssh_mock)) + + def test__provide_config_file(self,): + loadplugin = range(5) + port_names = range(5) + kwargs = { + "interval": '25', + "loadplugin": loadplugin, + "port_names": port_names, + } + self.resource_profile._provide_config_file("/opt/nsb_bin", "collectd.conf", kwargs) + self.ssh_mock.execute.assert_called_once() + + def test_initiate_systemagent(self): + self.resource_profile._start_collectd = mock.Mock() + self.resource_profile._start_rabbitmq = mock.Mock() + self.assertIsNone( + self.resource_profile.initiate_systemagent("/opt/nsb_bin")) + + def test_initiate_systemagent_raise(self): + self.resource_profile._start_rabbitmq = mock.Mock(side_effect=RuntimeError) + with self.assertRaises(RuntimeError): + self.resource_profile.initiate_systemagent("/opt/nsb_bin") + + def test__parse_hugepages(self): + reskey = ["cpu", "cpuFreq"] + value = "timestamp:12345" + res = self.resource_profile.parse_hugepages(reskey, value) + self.assertEqual({'cpu/cpuFreq': '12345'}, res) + + def test__parse_dpdkstat(self): + reskey = ["dpdk0", "0"] + value = "tx:12345" + res = self.resource_profile.parse_dpdkstat(reskey, value) + self.assertEqual({'dpdk0/0': '12345'}, res) + + def test__parse_virt(self): + reskey = ["vm0", "cpu"] + value = "load:45" + res = self.resource_profile.parse_virt(reskey, value) + self.assertEqual({'vm0/cpu': '45'}, res) + + def test__parse_ovs_stats(self): + reskey = ["ovs", "stats"] + value = "tx:45" + res = self.resource_profile.parse_ovs_stats(reskey, value) + self.assertEqual({'ovs/stats': '45'}, res) + + def test_parse_collectd_result(self): + res = self.resource_profile.parse_collectd_result({}) + expected_result = {'cpu': {}, 'dpdkstat': {}, 'hugepages': {}, + 'memory': {}, 'ovs_stats': {}, 'timestamp': '', + 'virt': {}} + self.assertDictEqual(res, expected_result) + + def test_parse_collectd_result_cpu(self): + metric = {"nsb_stats/cpu/0/ipc": "101"} + self.resource_profile.get_cpu_data = mock.Mock(return_value=[1, + "ipc", + "1234", + ""]) + res = self.resource_profile.parse_collectd_result(metric) + expected_result = {'cpu': {1: {'ipc': '1234'}}, 'dpdkstat': {}, 'hugepages': {}, + 'memory': {}, 'ovs_stats': {}, 'timestamp': '', + 'virt': {}} + self.assertDictEqual(res, expected_result) + + def test_parse_collectd_result_memory(self): + metric = {"nsb_stats/memory/bw": "101"} + res = self.resource_profile.parse_collectd_result(metric) + expected_result = {'cpu': {}, 'dpdkstat': {}, 'hugepages': {}, + 'memory': {'bw': '101'}, 'ovs_stats': {}, 'timestamp': '', + 'virt': {}} + self.assertDictEqual(res, expected_result) + + def test_parse_collectd_result_hugepage(self): + # amqp returns bytes + metric = {b"nsb_stats/hugepages/free": b"101"} + self.resource_profile.parse_hugepages = mock.Mock(return_value={"free": "101"}) + res = self.resource_profile.parse_collectd_result(metric) + expected_result = {'cpu': {}, 'dpdkstat': {}, 'hugepages': {'free': '101'}, + 'memory': {}, 'ovs_stats': {}, 'timestamp': '', + 'virt': {}} + self.assertDictEqual(res, expected_result) + + def test_parse_collectd_result_dpdk_virt_ovs(self): + metric = {b"nsb_stats/dpdkstat/tx": b"101", + b"nsb_stats/ovs_stats/tx": b"101", + b"nsb_stats/virt/virt/memory": b"101"} + self.resource_profile.parse_dpdkstat = \ + mock.Mock(return_value={"tx": "101"}) + self.resource_profile.parse_virt = \ + mock.Mock(return_value={"memory": "101"}) + self.resource_profile.parse_ovs_stats = \ + mock.Mock(return_value={"tx": "101"}) + res = self.resource_profile.parse_collectd_result(metric) + expected_result = {'cpu': {}, 'dpdkstat': {'tx': '101'}, 'hugepages': {}, + 'memory': {}, 'ovs_stats': {'tx': '101'}, 'timestamp': '', + 'virt': {'memory': '101'}} + self.assertDictEqual(res, expected_result) + + def test_amqp_process_for_nfvi_kpi(self): + self.resource_profile.amqp_client = \ + mock.MagicMock(side_effect=[None, mock.MagicMock()]) + self.resource_profile.run_collectd_amqp = \ + mock.Mock(return_value=0) + res = self.resource_profile.amqp_process_for_nfvi_kpi() + self.assertIsNone(res) + + def test_amqp_collect_nfvi_kpi(self): + self.resource_profile.amqp_client = \ + mock.MagicMock(side_effect=[None, mock.MagicMock()]) + self.resource_profile.run_collectd_amqp = \ + mock.Mock(return_value=0) + self.resource_profile.parse_collectd_result = mock.Mock() + res = self.resource_profile.amqp_collect_nfvi_kpi() + self.assertIsNotNone(res) + + def test_run_collectd_amqp(self): + resource.AmqpConsumer = mock.Mock(autospec=collectd) + self.assertIsNone(self.resource_profile.run_collectd_amqp()) + + def test_start(self): + self.assertIsNone(self.resource_profile.start()) + + def test_stop(self): + self.assertIsNone(self.resource_profile.stop()) + + def test_stop_amqp_not_running(self): + self.resource_profile.amqp_client = mock.MagicMock() + # TODO(efoley): Fix this incorrect test. + # Should check that we don't try to stop amqp when it's not running + self.assertIsNone(self.resource_profile.stop()) diff --git a/yardstick/tests/unit/network_services/test_utils.py b/yardstick/tests/unit/network_services/test_utils.py new file mode 100644 index 000000000..2b2eb7109 --- /dev/null +++ b/yardstick/tests/unit/network_services/test_utils.py @@ -0,0 +1,141 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import os +import unittest +import mock + +from yardstick.network_services import utils + + +class UtilsTestCase(unittest.TestCase): + """Test all VNF helper methods.""" + + DPDK_PATH = os.path.join(utils.NSB_ROOT, "dpdk-devbind.py") + + def setUp(self): + super(UtilsTestCase, self).setUp() + + def test_get_nsb_options(self): + result = utils.get_nsb_option("bin_path", None) + self.assertEqual(result, utils.NSB_ROOT) + + def test_get_nsb_option_is_invalid_key(self): + result = utils.get_nsb_option("bin", None) + self.assertEqual(result, None) + + def test_get_nsb_option_default(self): + default = object() + result = utils.get_nsb_option("nosuch", default) + self.assertIs(result, default) + + def test_provision_tool(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, self.DPDK_PATH, "")) + ssh.return_value = ssh_mock + tool_path = utils.provision_tool(ssh_mock, self.DPDK_PATH) + self.assertEqual(tool_path, self.DPDK_PATH) + + def test_provision_tool_no_path(self): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(1, self.DPDK_PATH, "")) + ssh.return_value = ssh_mock + tool_path = utils.provision_tool(ssh_mock, self.DPDK_PATH) + self.assertEqual(tool_path, self.DPDK_PATH) + + +class PciAddressTestCase(unittest.TestCase): + + PCI_ADDRESS_DBSF = '000A:07:03.2' + PCI_ADDRESS_BSF = '06:02.1' + PCI_ADDRESS_DBSF_MULTILINE_1 = '0001:08:04.3\nother text\n' + PCI_ADDRESS_DBSF_MULTILINE_2 = 'first line\n 0001:08:04.3 \nother text\n' + # Will match and return the first address found. + PCI_ADDRESS_DBSF_MULTILINE_3 = ' 0001:08:04.1 \n 05:03.1 \nother\n' + PCI_ADDRESS_BSF_MULTILINE_1 = 'first line\n 08:04.3 \n 0002:05:03.1\n' + BAD_INPUT_1 = 'no address found' + BAD_INPUT_2 = '001:08:04.1' + BAD_INPUT_3 = '08:4.1' + + def test_pciaddress_dbsf(self): + pci_address = utils.PciAddress(PciAddressTestCase.PCI_ADDRESS_DBSF) + self.assertEqual('000a', pci_address.domain) + self.assertEqual('07', pci_address.bus) + self.assertEqual('03', pci_address.slot) + self.assertEqual('2', pci_address.function) + + def test_pciaddress_bsf(self): + pci_address = utils.PciAddress(PciAddressTestCase.PCI_ADDRESS_BSF) + self.assertEqual('0000', pci_address.domain) + self.assertEqual('06', pci_address.bus) + self.assertEqual('02', pci_address.slot) + self.assertEqual('1', pci_address.function) + + def test_pciaddress_dbsf_multiline_1(self): + pci_address = utils.PciAddress( + PciAddressTestCase.PCI_ADDRESS_DBSF_MULTILINE_1) + self.assertEqual('0001', pci_address.domain) + self.assertEqual('08', pci_address.bus) + self.assertEqual('04', pci_address.slot) + self.assertEqual('3', pci_address.function) + + def test_pciaddress_dbsf_multiline_2(self): + pci_address = utils.PciAddress( + PciAddressTestCase.PCI_ADDRESS_DBSF_MULTILINE_2) + self.assertEqual('0001', pci_address.domain) + self.assertEqual('08', pci_address.bus) + self.assertEqual('04', pci_address.slot) + self.assertEqual('3', pci_address.function) + + def test_pciaddress_dbsf_multiline_3(self): + pci_address = utils.PciAddress( + PciAddressTestCase.PCI_ADDRESS_DBSF_MULTILINE_3) + self.assertEqual('0001', pci_address.domain) + self.assertEqual('08', pci_address.bus) + self.assertEqual('04', pci_address.slot) + self.assertEqual('1', pci_address.function) + + def test_pciaddress_bsf_multiline_1(self): + pci_address = utils.PciAddress( + PciAddressTestCase.PCI_ADDRESS_BSF_MULTILINE_1) + self.assertEqual('0000', pci_address.domain) + self.assertEqual('08', pci_address.bus) + self.assertEqual('04', pci_address.slot) + self.assertEqual('3', pci_address.function) + + def test_pciaddress_bad_input_no_address(self): + with self.assertRaises(ValueError) as exception: + utils.PciAddress(PciAddressTestCase.BAD_INPUT_1) + self.assertEqual('Invalid PCI address: {}'.format( + PciAddressTestCase.BAD_INPUT_1), str(exception.exception)) + + def test_pciaddress_bad_input_dbsf_bad_formatted(self): + # In this test case, the domain has only 3 characters instead of 4. + pci_address = utils.PciAddress( + PciAddressTestCase.BAD_INPUT_2) + self.assertEqual('0000', pci_address.domain) + self.assertEqual('08', pci_address.bus) + self.assertEqual('04', pci_address.slot) + self.assertEqual('1', pci_address.function) + + def test_pciaddress_bad_input_bsf_bad_formatted(self): + with self.assertRaises(ValueError) as exception: + utils.PciAddress(PciAddressTestCase.BAD_INPUT_3) + self.assertEqual('Invalid PCI address: {}'.format( + PciAddressTestCase.BAD_INPUT_3), str(exception.exception)) diff --git a/yardstick/tests/unit/network_services/traffic_profile/__init__.py b/yardstick/tests/unit/network_services/traffic_profile/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/__init__.py diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_base.py b/yardstick/tests/unit/network_services/traffic_profile/test_base.py new file mode 100644 index 000000000..d9244e31b --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_base.py @@ -0,0 +1,112 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +import mock +import unittest + +from yardstick.common import exceptions +from yardstick.network_services import traffic_profile as tprofile_package +from yardstick.network_services.traffic_profile import base +from yardstick import tests as y_tests + + +class TestTrafficProfile(unittest.TestCase): + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64}} + + def _get_res_mock(self, **kw): + _mock = mock.MagicMock() + for k, v in kw.items(): + setattr(_mock, k, v) + return _mock + + def test___init__(self): + traffic_profile = base.TrafficProfile(self.TRAFFIC_PROFILE) + self.assertEqual(self.TRAFFIC_PROFILE, traffic_profile.params) + + def test_execute_traffic(self): + traffic_profile = base.TrafficProfile(self.TRAFFIC_PROFILE) + self.assertRaises(NotImplementedError, + traffic_profile.execute_traffic, {}) + + def test_get_existing_traffic_profile(self): + traffic_profile_list = [ + 'RFC2544Profile', 'FixedProfile', 'TrafficProfileGenericHTTP', + 'IXIARFC2544Profile', 'ProxACLProfile', 'ProxBinSearchProfile', + 'ProxProfile', 'ProxRampProfile'] + with mock.patch.dict(sys.modules, y_tests.STL_MOCKS): + tprofile_package.register_modules() + + for tp in traffic_profile_list: + traffic_profile = base.TrafficProfile.get( + {'traffic_profile': {'traffic_type': tp}}) + self.assertEqual(tp, traffic_profile.__class__.__name__) + + def test_get_non_existing_traffic_profile(self): + self.assertRaises(exceptions.TrafficProfileNotImplemented, + base.TrafficProfile.get, self.TRAFFIC_PROFILE) + + +class TestDummyProfile(unittest.TestCase): + def test_execute(self): + tp_config = {'traffic_profile': {'duration': 15}} + dummy_profile = base.DummyProfile(tp_config) + self.assertIsNone(dummy_profile.execute({})) + + +class TrafficProfileConfigTestCase(unittest.TestCase): + + def test__init(self): + tp_config = {'traffic_profile': {'packet_sizes': {'64B': 100}}} + tp_config_obj = base.TrafficProfileConfig(tp_config) + self.assertEqual({'64B': 100}, tp_config_obj.packet_sizes) + self.assertEqual(base.TrafficProfileConfig.DEFAULT_DURATION, + tp_config_obj.duration) + + def test__init_set_duration(self): + tp_config = {'traffic_profile': {'duration': 15}} + tp_config_obj = base.TrafficProfileConfig(tp_config) + self.assertEqual(base.TrafficProfileConfig.DEFAULT_SCHEMA, + tp_config_obj.schema) + self.assertEqual(float(base.TrafficProfileConfig.DEFAULT_FRAME_RATE), + tp_config_obj.frame_rate) + self.assertEqual(15, tp_config_obj.duration) + + def test__parse_rate(self): + tp_config = {'traffic_profile': {'packet_sizes': {'64B': 100}}} + tp_config_obj = base.TrafficProfileConfig(tp_config) + self.assertEqual((100.0, 'fps'), tp_config_obj.parse_rate('100 ')) + self.assertEqual((200.5, 'fps'), tp_config_obj.parse_rate('200.5')) + self.assertEqual((300.8, 'fps'), tp_config_obj.parse_rate('300.8fps')) + self.assertEqual((400.2, 'fps'), + tp_config_obj.parse_rate('400.2 fps')) + self.assertEqual((500.3, '%'), tp_config_obj.parse_rate('500.3%')) + self.assertEqual((600.1, '%'), tp_config_obj.parse_rate('600.1 %')) + + def test__parse_rate_exception(self): + tp_config = {'traffic_profile': {'packet_sizes': {'64B': 100}}} + tp_config_obj = base.TrafficProfileConfig(tp_config) + with self.assertRaises(exceptions.TrafficProfileRate): + tp_config_obj.parse_rate('100Fps') + with self.assertRaises(exceptions.TrafficProfileRate): + tp_config_obj.parse_rate('100 kbps') diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_fixed.py b/yardstick/tests/unit/network_services/traffic_profile/test_fixed.py new file mode 100644 index 000000000..2f6713760 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_fixed.py @@ -0,0 +1,117 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import mock +import unittest + +from yardstick.tests import STL_MOCKS + +STLClient = mock.MagicMock() +stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) +stl_patch.start() + +if stl_patch: + from yardstick.network_services.traffic_profile.base import TrafficProfile + from yardstick.network_services.traffic_profile.fixed import FixedProfile + + +class TestFixedProfile(unittest.TestCase): + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64}} + + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{'short-name': 'VpeVnf', + 'vdu': + [{'routing_table': + [{'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0'}, + {'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1'}], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': + [{'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'id': 'vpevnf-baremetal', + 'external-interface': + [{'virtual-interface': + {'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.100.20', + 'local_mac': '00:00:00:00:00:01'}, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_mac': '00:00:00:00:00:02'}, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'}]}], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': + {'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1'}, + 'benchmark': + {'kpi': ['packets_in', 'packets_fwd', 'packets_dropped']}, + 'connection-point': [{'type': 'VPORT', 'name': 'xe0'}, + {'type': 'VPORT', 'name': 'xe1'}], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh'}]}} + + def test___init__(self): + fixed_profile = FixedProfile(self.TRAFFIC_PROFILE) + self.assertIsNotNone(fixed_profile) + + def test_execute(self): + traffic_generator = mock.Mock(autospec=TrafficProfile) + traffic_generator.my_ports = [0, 1] + traffic_generator.vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + traffic_generator.client = \ + mock.Mock(return_value=True) + fixed_profile = FixedProfile(self.TRAFFIC_PROFILE) + fixed_profile.params = self.TRAFFIC_PROFILE + fixed_profile.first_run = True + self.assertIsNone(fixed_profile.execute(traffic_generator)) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_http.py b/yardstick/tests/unit/network_services/traffic_profile/test_http.py new file mode 100644 index 000000000..874ec37d4 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_http.py @@ -0,0 +1,47 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from yardstick.network_services.traffic_profile import http + + +class TestTrafficProfileGenericHTTP(unittest.TestCase): + + TP_CONFIG = {'traffic_profile': {'duration': 10}, + "uplink_0": {}, "downlink_0": {}} + + def test___init__(self): + tp_generic_http = http.TrafficProfileGenericHTTP( + self.TP_CONFIG) + self.assertIsNotNone(tp_generic_http) + + def test_get_links_param(self): + tp_generic_http = http.TrafficProfileGenericHTTP( + self.TP_CONFIG) + + links = tp_generic_http.get_links_param() + self.assertEqual({"uplink_0": {}, "downlink_0": {}}, links) + + def test_execute(self): + tp_generic_http = http.TrafficProfileGenericHTTP( + self.TP_CONFIG) + traffic_generator = {} + self.assertIsNone(tp_generic_http.execute(traffic_generator)) + + def test__send_http_request(self): + tp_generic_http = http.TrafficProfileGenericHTTP( + self.TP_CONFIG) + self.assertIsNone(tp_generic_http._send_http_request( + '10.1.1.1', '250', '/req')) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_http_ixload.py b/yardstick/tests/unit/network_services/traffic_profile/test_http_ixload.py new file mode 100644 index 000000000..996360c2e --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_http_ixload.py @@ -0,0 +1,452 @@ +# Copyright (c) 2017-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import mock + +from oslo_serialization import jsonutils + +from yardstick.network_services.traffic_profile import http_ixload +from yardstick.network_services.traffic_profile.http_ixload import \ + join_non_strings, validate_non_string_sequence + + +class TestJoinNonStrings(unittest.TestCase): + + def test_validate_non_string_sequence(self): + self.assertEqual(validate_non_string_sequence([1, 2, 3]), [1, 2, 3]) + self.assertIsNone(validate_non_string_sequence('123')) + self.assertIsNone(validate_non_string_sequence(1)) + + self.assertEqual(validate_non_string_sequence(1, 2), 2) + self.assertEqual(validate_non_string_sequence(1, default=2), 2) + + with self.assertRaises(RuntimeError): + validate_non_string_sequence(1, raise_exc=RuntimeError) + + def test_join_non_strings(self): + self.assertEqual(join_non_strings(':'), '') + self.assertEqual(join_non_strings(':', 'a'), 'a') + self.assertEqual(join_non_strings(':', 'a', 2, 'c'), 'a:2:c') + self.assertEqual(join_non_strings(':', ['a', 2, 'c']), 'a:2:c') + self.assertEqual(join_non_strings(':', 'abc'), 'abc') + + +class TestIxLoadTrafficGen(unittest.TestCase): + + def setUp(self): + ports = [1, 2, 3] + self.test_input = { + "remote_server": "REMOTE_SERVER", + "ixload_cfg": "IXLOAD_CFG", + "result_dir": "RESULT_DIR", + "ixia_chassis": "IXIA_CHASSIS", + "IXIA": { + "card": "CARD", + "ports": ports, + }, + 'links_param': { + "uplink_0": { + "ip": {"address": "address", + "gateway": "gateway", + "subnet_prefix": "subnet_prefix", + "mac": "mac" + }}} + } + + def test_parse_run_test(self): + ports = [1, 2, 3] + test_input = { + "remote_server": "REMOTE_SERVER", + "ixload_cfg": "IXLOAD_CFG", + "result_dir": "RESULT_DIR", + "ixia_chassis": "IXIA_CHASSIS", + "IXIA": { + "card": "CARD", + "ports": ports, + }, + 'links_param': {} + } + j = jsonutils.dump_as_bytes(test_input) + ixload = http_ixload.IXLOADHttpTest(j) + self.assertDictEqual(ixload.test_input, test_input) + self.assertIsNone(ixload.parse_run_test()) + self.assertEqual(ixload.ports_to_reassign, [ + ["IXIA_CHASSIS", "CARD", 1], + ["IXIA_CHASSIS", "CARD", 2], + ["IXIA_CHASSIS", "CARD", 3], + ]) + self.assertEqual({}, ixload.links_param) + + def test_format_ports_for_reassignment(self): + ports = [ + ["IXIA_CHASSIS", "CARD", 1], + ["IXIA_CHASSIS", "CARD", 2], + ["IXIA_CHASSIS", "CARD", 3], + ] + formatted = http_ixload.IXLOADHttpTest.format_ports_for_reassignment(ports) + self.assertEqual(formatted, [ + "IXIA_CHASSIS;CARD;1", + "IXIA_CHASSIS;CARD;2", + "IXIA_CHASSIS;CARD;3", + ]) + + def test_reassign_ports(self): + ports = [1, 2, 3] + test_input = { + "remote_server": "REMOTE_SERVER", + "ixload_cfg": "IXLOAD_CFG", + "result_dir": "RESULT_DIR", + "ixia_chassis": "IXIA_CHASSIS", + "IXIA": { + "card": "CARD", + "ports": ports, + }, + 'links_param': {} + } + j = jsonutils.dump_as_bytes(test_input) + ixload = http_ixload.IXLOADHttpTest(j) + repository = mock.Mock() + test = mock.MagicMock() + test.setPorts = mock.Mock() + ports_to_reassign = [(1, 2, 3), (1, 2, 4)] + ixload.format_ports_for_reassignment = mock.Mock(return_value=["1;2;3"]) + self.assertIsNone(ixload.reassign_ports(test, repository, ports_to_reassign)) + + def test_reassign_ports_error(self): + ports = [1, 2, 3] + test_input = { + "remote_server": "REMOTE_SERVER", + "ixload_cfg": "IXLOAD_CFG", + "result_dir": "RESULT_DIR", + "ixia_chassis": "IXIA_CHASSIS", + "IXIA": { + "card": "CARD", + "ports": ports, + }, + 'links_param': {} + } + j = jsonutils.dump_as_bytes(test_input) + ixload = http_ixload.IXLOADHttpTest(j) + repository = mock.Mock() + test = "test" + ports_to_reassign = [(1, 2, 3), (1, 2, 4)] + ixload.format_ports_for_reassignment = mock.Mock(return_value=["1;2;3"]) + ixload.ix_load = mock.MagicMock() + ixload.ix_load.delete = mock.Mock() + ixload.ix_load.disconnect = mock.Mock() + with self.assertRaises(Exception): + ixload.reassign_ports(test, repository, ports_to_reassign) + + def test_stat_collector(self): + args = [0, 1] + self.assertIsNone( + http_ixload.IXLOADHttpTest.stat_collector(*args)) + + def test_IxL_StatCollectorCommand(self): + args = [[0, 1, 2, 3], [0, 1, 2, 3]] + self.assertIsNone( + http_ixload.IXLOADHttpTest.IxL_StatCollectorCommand(*args)) + + def test_set_results_dir(self): + test_stat_collector = mock.MagicMock() + test_stat_collector.setResultDir = mock.Mock() + results_on_windows = "c:/Results" + self.assertIsNone( + http_ixload.IXLOADHttpTest.set_results_dir(test_stat_collector, + results_on_windows)) + + def test_set_results_dir_error(self): + test_stat_collector = "" + results_on_windows = "c:/Results" + with self.assertRaises(Exception): + http_ixload.IXLOADHttpTest.set_results_dir(test_stat_collector, results_on_windows) + + def test_load_config_file(self): + ports = [1, 2, 3] + test_input = { + "remote_server": "REMOTE_SERVER", + "ixload_cfg": "IXLOAD_CFG", + "result_dir": "RESULT_DIR", + "ixia_chassis": "IXIA_CHASSIS", + "IXIA": { + "card": "CARD", + "ports": ports, + }, + 'links_param': {} + } + j = jsonutils.dump_as_bytes(test_input) + ixload = http_ixload.IXLOADHttpTest(j) + ixload.ix_load = mock.MagicMock() + ixload.ix_load.new = mock.Mock(return_value="") + self.assertIsNotNone(ixload.load_config_file("ixload.cfg")) + + def test_load_config_file_error(self): + ports = [1, 2, 3] + test_input = { + "remote_server": "REMOTE_SERVER", + "ixload_cfg": "IXLOAD_CFG", + "result_dir": "RESULT_DIR", + "ixia_chassis": "IXIA_CHASSIS", + "IXIA": { + "card": "CARD", + "ports": ports, + }, + 'links_param': {} + } + j = jsonutils.dump_as_bytes(test_input) + ixload = http_ixload.IXLOADHttpTest(j) + ixload.ix_load = "test" + with self.assertRaises(Exception): + ixload.load_config_file("ixload.cfg") + + @mock.patch('yardstick.network_services.traffic_profile.http_ixload.StatCollectorUtils') + @mock.patch('yardstick.network_services.traffic_profile.http_ixload.IxLoad') + def test_start_http_test_connect_error(self, mock_ixload_type, *args): + ports = [1, 2, 3] + test_input = { + "remote_server": "REMOTE_SERVER", + "ixload_cfg": "IXLOAD_CFG", + "result_dir": "RESULT_DIR", + "ixia_chassis": "IXIA_CHASSIS", + "IXIA": { + "card": "CARD", + "ports": ports, + }, + 'links_param': {} + } + + j = jsonutils.dump_as_bytes(test_input) + + mock_ixload_type.return_value.connect.side_effect = RuntimeError + + ixload = http_ixload.IXLOADHttpTest(j) + ixload.results_on_windows = 'windows_result_dir' + ixload.result_dir = 'my_result_dir' + + with self.assertRaises(RuntimeError): + ixload.start_http_test() + + def test_update_config(self): + net_taraffic_0 = mock.Mock() + net_taraffic_0.name = "HTTP client@uplink_0" + net_taraffic_1 = mock.Mock() + net_taraffic_1.name = "HTTP client@uplink_1" + + community_list = [net_taraffic_0, net_taraffic_1, Exception] + ixload = http_ixload.IXLOADHttpTest( + jsonutils.dump_as_bytes(self.test_input)) + + ixload.links_param = {"uplink_0": {"ip": {}, + "http_client": {}}} + + ixload.test = mock.Mock() + ixload.test.communityList = community_list + + ixload.update_network_param = mock.Mock() + ixload.update_http_client_param = mock.Mock() + + ixload.update_config() + + ixload.update_network_param.assert_called_once_with(net_taraffic_0, {}) + ixload.update_http_client_param.assert_called_once_with(net_taraffic_0, + {}) + + def test_update_network_mac_address(self): + ethernet = mock.MagicMock() + net_traffic = mock.Mock() + net_traffic.network.getL1Plugin.return_value = ethernet + + ixload = http_ixload.IXLOADHttpTest( + jsonutils.dump_as_bytes(self.test_input)) + + ix_net_l2_ethernet_plugin = ethernet.childrenList[0] + ix_net_ip_v4_v6_plugin = ix_net_l2_ethernet_plugin.childrenList[0] + ix_net_ip_v4_v6_range = ix_net_ip_v4_v6_plugin.rangeList[0] + + ixload.update_network_mac_address(net_traffic, "auto") + ix_net_ip_v4_v6_range.config.assert_called_once_with( + autoMacGeneration=True) + + ixload.update_network_mac_address(net_traffic, "00:00:00:00:00:01") + ix_net_ip_v4_v6_range.config.assert_called_with( + autoMacGeneration=False) + mac_range = ix_net_ip_v4_v6_range.getLowerRelatedRange("MacRange") + mac_range.config.assert_called_once_with(mac="00:00:00:00:00:01") + + net_traffic.network.getL1Plugin.return_value = Exception + + with self.assertRaises(http_ixload.InvalidRxfFile): + ixload.update_network_mac_address(net_traffic, "auto") + + def test_update_network_address(self): + ethernet = mock.MagicMock() + net_traffic = mock.Mock() + net_traffic.network.getL1Plugin.return_value = ethernet + + ixload = http_ixload.IXLOADHttpTest( + jsonutils.dump_as_bytes(self.test_input)) + + ix_net_l2_ethernet_plugin = ethernet.childrenList[0] + ix_net_ip_v4_v6_plugin = ix_net_l2_ethernet_plugin.childrenList[0] + ix_net_ip_v4_v6_range = ix_net_ip_v4_v6_plugin.rangeList[0] + + ixload.update_network_address(net_traffic, "address", "gateway", + "prefix") + ix_net_ip_v4_v6_range.config.assert_called_once_with( + prefix="prefix", + ipAddress="address", + gatewayAddress="gateway") + + net_traffic.network.getL1Plugin.return_value = Exception + + with self.assertRaises(http_ixload.InvalidRxfFile): + ixload.update_network_address(net_traffic, "address", "gateway", + "prefix") + + def test_update_network_param(self): + net_traffic = mock.Mock() + + ixload = http_ixload.IXLOADHttpTest( + jsonutils.dump_as_bytes(self.test_input)) + + ixload.update_network_address = mock.Mock() + ixload.update_network_mac_address = mock.Mock() + + param = {"address": "address", + "gateway": "gateway", + "subnet_prefix": "subnet_prefix", + "mac": "mac" + } + + ixload.update_network_param(net_traffic, param) + + ixload.update_network_address.assert_called_once_with(net_traffic, + "address", + "gateway", + "subnet_prefix") + + ixload.update_network_mac_address.assert_called_once_with( + net_traffic, + "mac") + + def test_update_http_client_param(self): + net_traffic = mock.Mock() + + ixload = http_ixload.IXLOADHttpTest( + jsonutils.dump_as_bytes(self.test_input)) + + ixload.update_page_size = mock.Mock() + ixload.update_user_count = mock.Mock() + + param = {"page_object": "page_object", + "simulated_users": "simulated_users"} + + ixload.update_http_client_param(net_traffic, param) + + ixload.update_page_size.assert_called_once_with(net_traffic, + "page_object") + ixload.update_user_count.assert_called_once_with(net_traffic, + "simulated_users") + + def test_update_page_size(self): + activity = mock.MagicMock() + net_traffic = mock.Mock() + + ixload = http_ixload.IXLOADHttpTest( + jsonutils.dump_as_bytes(self.test_input)) + + net_traffic.activityList = [activity] + ix_http_command = activity.agent.actionList[0] + ixload.update_page_size(net_traffic, "page_object") + ix_http_command.config.assert_called_once_with( + pageObject="page_object") + + net_traffic.activityList = [] + with self.assertRaises(http_ixload.InvalidRxfFile): + ixload.update_page_size(net_traffic, "page_object") + + def test_update_user_count(self): + activity = mock.MagicMock() + net_traffic = mock.Mock() + + ixload = http_ixload.IXLOADHttpTest( + jsonutils.dump_as_bytes(self.test_input)) + + net_traffic.activityList = [activity] + ixload.update_user_count(net_traffic, 123) + activity.config.assert_called_once_with(userObjectiveValue=123) + + net_traffic.activityList = [] + with self.assertRaises(http_ixload.InvalidRxfFile): + ixload.update_user_count(net_traffic, 123) + + @mock.patch('yardstick.network_services.traffic_profile.http_ixload.IxLoad') + @mock.patch('yardstick.network_services.traffic_profile.http_ixload.StatCollectorUtils') + def test_start_http_test(self, *args): + ports = [1, 2, 3] + test_input = { + "remote_server": "REMOTE_SERVER", + "ixload_cfg": "IXLOAD_CFG", + "result_dir": "RESULT_DIR", + "ixia_chassis": "IXIA_CHASSIS", + "IXIA": { + "card": "CARD", + "ports": ports, + }, + 'links_param': {} + } + + j = jsonutils.dump_as_bytes(test_input) + + ixload = http_ixload.IXLOADHttpTest(j) + ixload.results_on_windows = 'windows_result_dir' + ixload.result_dir = 'my_result_dir' + ixload.load_config_file = mock.MagicMock() + + self.assertIsNone(ixload.start_http_test()) + + @mock.patch('yardstick.network_services.traffic_profile.http_ixload.IxLoad') + @mock.patch('yardstick.network_services.traffic_profile.http_ixload.StatCollectorUtils') + def test_start_http_test_reassign_error(self, *args): + ports = [1, 2, 3] + test_input = { + "remote_server": "REMOTE_SERVER", + "ixload_cfg": "IXLOAD_CFG", + "result_dir": "RESULT_DIR", + "ixia_chassis": "IXIA_CHASSIS", + "IXIA": { + "card": "CARD", + "ports": ports, + }, + 'links_param': {} + } + + j = jsonutils.dump_as_bytes(test_input) + + ixload = http_ixload.IXLOADHttpTest(j) + ixload.load_config_file = mock.MagicMock() + + reassign_ports = mock.Mock(side_effect=RuntimeError) + ixload.reassign_ports = reassign_ports + ixload.results_on_windows = 'windows_result_dir' + ixload.result_dir = 'my_result_dir' + + ixload.start_http_test() + reassign_ports.assert_called_once() + + @mock.patch("yardstick.network_services.traffic_profile.http_ixload.IXLOADHttpTest") + def test_main(self, *args): + args = ["1", "2", "3"] + http_ixload.main(args) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py b/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py new file mode 100644 index 000000000..ddd1828ae --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py @@ -0,0 +1,1024 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy + +import mock +import unittest +import collections + +from yardstick.network_services.traffic_profile import ixia_rfc2544 +from yardstick.network_services.traffic_profile import trex_traffic_profile + + +class TestIXIARFC2544Profile(unittest.TestCase): + + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64, + }, + } + + PROFILE = { + 'description': 'Traffic profile to run RFC2544 latency', + 'name': 'rfc2544', + 'traffic_profile': { + 'traffic_type': 'IXIARFC2544Profile', + 'frame_rate': 100}, + ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: { + 'ipv4': { + 'outer_l2': { + 'framesize': { + '64B': '100', + '1518B': '0', + '128B': '0', + '1400B': '0', + '256B': '0', + '373b': '0', + '570B': '0'}}, + 'outer_l3v4': { + 'dstip4': '1.1.1.1-1.15.255.255', + 'proto': 'udp', + 'count': '1', + 'srcip4': '90.90.1.1-90.105.255.255', + 'dscp': 0, + 'ttl': 32}, + 'outer_l4': { + 'srcport': '2001', + 'dsrport': '1234'}}}, + ixia_rfc2544.IXIARFC2544Profile.UPLINK: { + 'ipv4': { + 'outer_l2': { + 'framesize': { + '64B': '100', + '1518B': '0', + '128B': '0', + '1400B': '0', + '256B': '0', + '373b': '0', + '570B': '0'}}, + 'outer_l3v4': { + 'dstip4': '9.9.1.1-90.105.255.255', + 'proto': 'udp', + 'count': '1', + 'srcip4': '1.1.1.1-1.15.255.255', + 'dscp': 0, + 'ttl': 32}, + 'outer_l4': { + 'dstport': '2001', + 'srcport': '1234'}}}, + 'schema': 'isb:traffic_profile:0.1'} + + def test_get_ixia_traffic_profile_error(self): + traffic_generator = mock.Mock( + autospec=trex_traffic_profile.TrexProfile) + traffic_generator.my_ports = [0, 1] + traffic_generator.uplink_ports = [-1] + traffic_generator.downlink_ports = [1] + traffic_generator.client = \ + mock.Mock(return_value=True) + STATIC_TRAFFIC = { + ixia_rfc2544.IXIARFC2544Profile.UPLINK: { + "id": 1, + "bidir": "False", + "duration": 60, + "iload": "100", + "outer_l2": { + "dstmac": "00:00:00:00:00:03", + "framesPerSecond": True, + "framesize": 64, + "srcmac": "00:00:00:00:00:01" + }, + "outer_l3": { + "dscp": 0, + "dstip4": "152.16.40.20", + "proto": "udp", + "srcip4": "152.16.100.20", + "ttl": 32 + }, + "outer_l3v4": { + "dscp": 0, + "dstip4": "152.16.40.20", + "proto": "udp", + "srcip4": "152.16.100.20", + "ttl": 32 + }, + "outer_l3v6": { + "count": 1024, + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32 + }, + "outer_l4": { + "dstport": "2001", + "srcport": "1234" + }, + "traffic_type": "continuous" + }, + ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: { + "id": 2, + "bidir": "False", + "duration": 60, + "iload": "100", + "outer_l2": { + "dstmac": "00:00:00:00:00:04", + "framesPerSecond": True, + "framesize": 64, + "srcmac": "00:00:00:00:00:01" + }, + "outer_l3": { + "count": 1024, + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32 + }, + "outer_l3v4": { + "count": 1024, + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32 + }, + "outer_l3v6": { + "count": 1024, + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32 + }, + "outer_l4": { + "dstport": "1234", + "srcport": "2001" + }, + "traffic_type": "continuous" + } + } + ixia_rfc2544.STATIC_TRAFFIC = STATIC_TRAFFIC + + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) + r_f_c2544_profile.rate = 100 + mac = {"src_mac_0": "00:00:00:00:00:01", + "src_mac_1": "00:00:00:00:00:02", + "src_mac_2": "00:00:00:00:00:02", + "dst_mac_0": "00:00:00:00:00:03", + "dst_mac_1": "00:00:00:00:00:04", + "dst_mac_2": "00:00:00:00:00:04"} + result = r_f_c2544_profile._get_ixia_traffic_profile(self.PROFILE, mac) + self.assertIsNotNone(result) + + def test_get_ixia_traffic_profile(self): + traffic_generator = mock.Mock( + autospec=trex_traffic_profile.TrexProfile) + traffic_generator.my_ports = [0, 1] + traffic_generator.uplink_ports = [-1] + traffic_generator.downlink_ports = [1] + traffic_generator.client = \ + mock.Mock(return_value=True) + STATIC_TRAFFIC = { + ixia_rfc2544.IXIARFC2544Profile.UPLINK: { + "id": 1, + "bidir": "False", + "duration": 60, + "iload": "100", + "outer_l2": { + "dstmac": "00:00:00:00:00:03", + "framesPerSecond": True, + "framesize": 64, + "srcmac": "00:00:00:00:00:01" + }, + "outer_l3": { + "dscp": 0, + "dstip4": "152.16.40.20", + "proto": "udp", + "srcip4": "152.16.100.20", + "ttl": 32 + }, + "outer_l3v4": { + "dscp": 0, + "dstip4": "152.16.40.20", + "proto": "udp", + "srcip4": "152.16.100.20", + "ttl": 32, + "count": "1" + }, + "outer_l3v6": { + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32, + }, + "outer_l4": { + "dstport": "2001", + "srcport": "1234", + "count": "1" + }, + "traffic_type": "continuous" + }, + ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: { + "id": 2, + "bidir": "False", + "duration": 60, + "iload": "100", + "outer_l2": { + "dstmac": "00:00:00:00:00:04", + "framesPerSecond": True, + "framesize": 64, + "srcmac": "00:00:00:00:00:01" + }, + "outer_l3": { + "count": 1024, + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32 + }, + "outer_l3v4": { + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32, + }, + "outer_l3v6": { + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32, + }, + "outer_l4": { + "dstport": "1234", + "srcport": "2001", + "count": "1" + }, + "traffic_type": "continuous" + } + } + ixia_rfc2544.STATIC_TRAFFIC = STATIC_TRAFFIC + + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) + r_f_c2544_profile.rate = 100 + mac = {"src_mac_0": "00:00:00:00:00:01", + "src_mac_1": "00:00:00:00:00:02", + "src_mac_2": "00:00:00:00:00:02", + "dst_mac_0": "00:00:00:00:00:03", + "dst_mac_1": "00:00:00:00:00:04", + "dst_mac_2": "00:00:00:00:00:04"} + result = r_f_c2544_profile._get_ixia_traffic_profile(self.PROFILE, mac) + self.assertIsNotNone(result) + + @mock.patch("yardstick.network_services.traffic_profile.ixia_rfc2544.open") + def test_get_ixia_traffic_profile_v6(self, *args): + traffic_generator = mock.Mock( + autospec=trex_traffic_profile.TrexProfile) + traffic_generator.my_ports = [0, 1] + traffic_generator.uplink_ports = [-1] + traffic_generator.downlink_ports = [1] + traffic_generator.client = \ + mock.Mock(return_value=True) + STATIC_TRAFFIC = { + ixia_rfc2544.IXIARFC2544Profile.UPLINK: { + "id": 1, + "bidir": "False", + "duration": 60, + "iload": "100", + "outer_l2": { + "dstmac": "00:00:00:00:00:03", + "framesPerSecond": True, + "framesize": 64, + "srcmac": "00:00:00:00:00:01" + }, + "outer_l3": { + "dscp": 0, + "dstip4": "152.16.40.20", + "proto": "udp", + "srcip4": "152.16.100.20", + "ttl": 32 + }, + "outer_l3v4": { + "dscp": 0, + "dstip4": "152.16.40.20", + "proto": "udp", + "srcip4": "152.16.100.20", + "ttl": 32 + }, + "outer_l3v6": { + "count": 1024, + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32 + }, + "outer_l4": { + "dstport": "2001", + "srcport": "1234" + }, + "traffic_type": "continuous" + }, + ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: { + "id": 2, + "bidir": "False", + "duration": 60, + "iload": "100", + "outer_l2": { + "dstmac": "00:00:00:00:00:04", + "framesPerSecond": True, + "framesize": 64, + "srcmac": "00:00:00:00:00:01" + }, + "outer_l3": { + "count": 1024, + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32 + }, + "outer_l3v4": { + "count": 1024, + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32 + }, + "outer_l3v6": { + "count": 1024, + "dscp": 0, + "dstip4": "152.16.100.20", + "proto": "udp", + "srcip4": "152.16.40.20", + "ttl": 32 + }, + "outer_l4": { + "dstport": "1234", + "srcport": "2001" + }, + "traffic_type": "continuous" + } + } + ixia_rfc2544.STATIC_TRAFFIC = STATIC_TRAFFIC + + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) + r_f_c2544_profile.rate = 100 + mac = {"src_mac_0": "00:00:00:00:00:01", + "src_mac_1": "00:00:00:00:00:02", + "src_mac_2": "00:00:00:00:00:02", + "dst_mac_0": "00:00:00:00:00:03", + "dst_mac_1": "00:00:00:00:00:04", + "dst_mac_2": "00:00:00:00:00:04"} + profile_data = {'description': 'Traffic profile to run RFC2544', + 'name': 'rfc2544', + 'traffic_profile': + {'traffic_type': 'IXIARFC2544Profile', + 'frame_rate': 100}, + ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: + {'ipv4': + {'outer_l2': {'framesize': + {'64B': '100', '1518B': '0', + '128B': '0', '1400B': '0', + '256B': '0', '373b': '0', + '570B': '0'}}, + 'outer_l3v4': {'dstip4': '1.1.1.1-1.15.255.255', + 'proto': 'udp', 'count': '1', + 'srcip4': '90.90.1.1-90.105.255.255', + 'dscp': 0, 'ttl': 32}, + 'outer_l3v6': {'dstip6': '1.1.1.1-1.15.255.255', + 'proto': 'udp', 'count': '1', + 'srcip6': '90.90.1.1-90.105.255.255', + 'dscp': 0, 'ttl': 32}, + 'outer_l4': {'srcport': '2001', + 'dsrport': '1234'}}}, + ixia_rfc2544.IXIARFC2544Profile.UPLINK: {'ipv4': + {'outer_l2': {'framesize': + {'64B': '100', '1518B': '0', + '128B': '0', '1400B': '0', + '256B': '0', '373b': '0', + '570B': '0'}}, + 'outer_l3v4': + {'dstip4': '9.9.1.1-90.105.255.255', + 'proto': 'udp', 'count': '1', + 'srcip4': '1.1.1.1-1.15.255.255', + 'dscp': 0, 'ttl': 32}, + 'outer_l3v6': + {'dstip6': '9.9.1.1-90.105.255.255', + 'proto': 'udp', 'count': '1', + 'srcip6': '1.1.1.1-1.15.255.255', + 'dscp': 0, 'ttl': 32}, + + 'outer_l4': {'dstport': '2001', + 'srcport': '1234'}}}, + 'schema': 'isb:traffic_profile:0.1'} + result = r_f_c2544_profile._get_ixia_traffic_profile(profile_data, mac) + self.assertIsNotNone(result) + + def test__init__(self): + t_profile_data = copy.deepcopy(self.TRAFFIC_PROFILE) + t_profile_data['traffic_profile']['frame_rate'] = 12345678 + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile(t_profile_data) + self.assertEqual(12345678, r_f_c2544_profile.rate) + + def test__get_ip_and_mask_range(self): + ip_range = '1.2.0.2-1.2.255.254' + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) + ip, mask = r_f_c2544_profile._get_ip_and_mask(ip_range) + self.assertEqual('1.2.0.2', ip) + self.assertEqual(16, mask) + + def test__get_ip_and_mask_single(self): + ip_range = '192.168.1.10' + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) + ip, mask = r_f_c2544_profile._get_ip_and_mask(ip_range) + self.assertEqual('192.168.1.10', ip) + self.assertIsNone(mask) + + def test__get_fixed_and_mask_range(self): + fixed_mask = '8-48' + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) + fixed, mask = r_f_c2544_profile._get_fixed_and_mask(fixed_mask) + self.assertEqual(8, fixed) + self.assertEqual(48, mask) + + def test__get_fixed_and_mask_single(self): + fixed_mask = 1234 + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) + fixed, mask = r_f_c2544_profile._get_fixed_and_mask(fixed_mask) + self.assertEqual(1234, fixed) + self.assertEqual(0, mask) + + def test__get_ixia_traffic_profile_default_args(self): + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) + + expected = {} + result = r_f_c2544_profile._get_ixia_traffic_profile({}) + self.assertDictEqual(result, expected) + + @mock.patch.object(ixia_rfc2544.IXIARFC2544Profile, + '_update_traffic_tracking_options') + def test__ixia_traffic_generate(self, mock_upd_tracking_opts): + traffic_generator = mock.Mock( + autospec=trex_traffic_profile.TrexProfile) + traffic_generator.networks = { + "uplink_0": ["xe0"], + "downlink_0": ["xe1"], + } + traffic_generator.client = \ + mock.Mock(return_value=True) + traffic = {ixia_rfc2544.IXIARFC2544Profile.DOWNLINK: {'iload': 10}, + ixia_rfc2544.IXIARFC2544Profile.UPLINK: {'iload': 10}} + ixia_obj = mock.MagicMock() + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile( + self.TRAFFIC_PROFILE) + r_f_c2544_profile.rate = 100 + result = r_f_c2544_profile._ixia_traffic_generate(traffic, ixia_obj, + traffic_generator) + self.assertIsNone(result) + mock_upd_tracking_opts.assert_called_once_with(traffic_generator) + + def test__update_traffic_tracking_options(self): + mock_traffic_gen = mock.Mock() + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile._update_traffic_tracking_options(mock_traffic_gen) + mock_traffic_gen.update_tracking_options.assert_called_once() + + def test__get_framesize(self): + traffic_profile = { + 'uplink_0': {'ipv4': {'outer_l2': {'framesize': {'64B': 100}}}}, + 'downlink_0': {'ipv4': {'outer_l2': {'framesize': {'64B': 100}}}}, + 'uplink_1': {'ipv4': {'outer_l2': {'framesize': {'64B': 100}}}}, + 'downlink_1': {'ipv4': {'outer_l2': {'framesize': {'64B': 100}}}} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.params = traffic_profile + result = rfc2544_profile._get_framesize() + self.assertEqual(result, '64B') + + def test__get_framesize_IMIX_traffic(self): + traffic_profile = { + 'uplink_0': {'ipv4': {'outer_l2': {'framesize': {'64B': 50, + '128B': 50}}}}, + 'downlink_0': {'ipv4': {'outer_l2': {'framesize': {'64B': 50, + '128B': 50}}}}, + 'uplink_1': {'ipv4': {'outer_l2': {'framesize': {'64B': 50, + '128B': 50}}}}, + 'downlink_1': {'ipv4': {'outer_l2': {'framesize': {'64B': 50, + '128B': 50}}}} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.params = traffic_profile + result = rfc2544_profile._get_framesize() + self.assertEqual(result, 'IMIX') + + def test__get_framesize_zero_pkt_size_weight(self): + traffic_profile = { + 'uplink_0': {'ipv4': {'outer_l2': {'framesize': {'64B': 0}}}}, + 'downlink_0': {'ipv4': {'outer_l2': {'framesize': {'64B': 0}}}}, + 'uplink_1': {'ipv4': {'outer_l2': {'framesize': {'64B': 0}}}}, + 'downlink_1': {'ipv4': {'outer_l2': {'framesize': {'64B': 0}}}} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.params = traffic_profile + result = rfc2544_profile._get_framesize() + self.assertEqual(result, '') + + def test_execute_traffic_first_run(self): + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.first_run = True + rfc2544_profile.rate = 50 + traffic_gen = mock.Mock() + traffic_gen.rfc_helper.iteration.value = 0 + with mock.patch.object(rfc2544_profile, '_get_ixia_traffic_profile') \ + as mock_get_tp, \ + mock.patch.object(rfc2544_profile, '_ixia_traffic_generate') \ + as mock_tgenerate: + mock_get_tp.return_value = 'fake_tprofile' + output = rfc2544_profile.execute_traffic(traffic_gen, + ixia_obj=mock.ANY) + + self.assertTrue(output) + self.assertFalse(rfc2544_profile.first_run) + self.assertEqual(50, rfc2544_profile.max_rate) + self.assertEqual(0, rfc2544_profile.min_rate) + mock_get_tp.assert_called_once() + mock_tgenerate.assert_called_once() + + def test_execute_traffic_not_first_run(self): + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.first_run = False + rfc2544_profile.max_rate = 70 + rfc2544_profile.min_rate = 0 + traffic_gen = mock.Mock() + traffic_gen.rfc_helper.iteration.value = 0 + with mock.patch.object(rfc2544_profile, '_get_ixia_traffic_profile') \ + as mock_get_tp, \ + mock.patch.object(rfc2544_profile, '_ixia_traffic_generate') \ + as mock_tgenerate: + mock_get_tp.return_value = 'fake_tprofile' + rfc2544_profile.full_profile = mock.ANY + output = rfc2544_profile.execute_traffic(traffic_gen, + ixia_obj=mock.ANY) + + self.assertFalse(output) + self.assertEqual(35.0, rfc2544_profile.rate) + mock_get_tp.assert_called_once() + mock_tgenerate.assert_called_once() + + def test_update_traffic_profile(self): + traffic_generator = mock.Mock( + autospec=trex_traffic_profile.TrexProfile) + traffic_generator.networks = { + "uplink_0": ["xe0"], # private, one value for intfs + "downlink_0": ["xe1", "xe2"], # public, two values for intfs + "downlink_1": ["xe3"], # not in TRAFFIC PROFILE + "tenant_0": ["xe4"], # not public or private + } + + ports_expected = [8, 3, 5] + traffic_generator.vnfd_helper.port_num.side_effect = ports_expected + traffic_generator.client.return_value = True + + traffic_profile = copy.deepcopy(self.TRAFFIC_PROFILE) + traffic_profile.update({ + "uplink_0": ["xe0"], + "downlink_0": ["xe1", "xe2"], + }) + + r_f_c2544_profile = ixia_rfc2544.IXIARFC2544Profile(traffic_profile) + r_f_c2544_profile.full_profile = {} + r_f_c2544_profile.get_streams = mock.Mock() + + self.assertIsNone( + r_f_c2544_profile.update_traffic_profile(traffic_generator)) + self.assertEqual(r_f_c2544_profile.ports, ports_expected) + + def test_get_drop_percentage_completed(self): + samples = {'iface_name_1': + {'InPackets': 1000, 'OutPackets': 1000, + 'InBytes': 64000, 'OutBytes': 64000, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25}, + 'iface_name_2': + {'InPackets': 1005, 'OutPackets': 1007, + 'InBytes': 64320, 'OutBytes': 64448, + 'LatencyAvg': 23, + 'LatencyMin': 13, + 'LatencyMax': 28} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.rate = 100.0 + rfc2544_profile._get_next_rate = mock.Mock(return_value=100.0) + rfc2544_profile._get_framesize = mock.Mock(return_value='64B') + completed, samples = rfc2544_profile.get_drop_percentage( + samples, 0, 1, 4, 0.1) + self.assertTrue(completed) + self.assertEqual(66.9, samples['TxThroughput']) + self.assertEqual(66.833, samples['RxThroughput']) + self.assertEqual(0.099651, samples['DropPercentage']) + self.assertEqual(21.5, samples['LatencyAvg']) + self.assertEqual(13.0, samples['LatencyMin']) + self.assertEqual(28.0, samples['LatencyMax']) + self.assertEqual(100.0, samples['Rate']) + self.assertEqual('64B', samples['PktSize']) + + def test_get_drop_percentage_over_drop_percentage(self): + samples = {'iface_name_1': + {'InPackets': 1000, 'OutPackets': 1000, + 'InBytes': 64000, 'OutBytes': 64000, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25}, + 'iface_name_2': + {'InPackets': 1005, 'OutPackets': 1007, + 'InBytes': 64320, 'OutBytes': 64448, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.rate = 1000 + rfc2544_profile._get_next_rate = mock.Mock(return_value=50.0) + completed, samples = rfc2544_profile.get_drop_percentage( + samples, 0, 0.05, 4, 0.1) + self.assertFalse(completed) + self.assertEqual(66.9, samples['TxThroughput']) + self.assertEqual(66.833, samples['RxThroughput']) + self.assertEqual(0.099651, samples['DropPercentage']) + self.assertEqual(rfc2544_profile.rate, rfc2544_profile.max_rate) + + def test_get_drop_percentage_under_drop_percentage(self): + samples = {'iface_name_1': + {'InPackets': 1000, 'OutPackets': 1000, + 'InBytes': 64000, 'OutBytes': 64000, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25}, + 'iface_name_2': + {'InPackets': 1005, 'OutPackets': 1007, + 'InBytes': 64320, 'OutBytes': 64448, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.rate = 1000 + rfc2544_profile._get_next_rate = mock.Mock(return_value=50.0) + completed, samples = rfc2544_profile.get_drop_percentage( + samples, 0.2, 1, 4, 0.1) + self.assertFalse(completed) + self.assertEqual(66.9, samples['TxThroughput']) + self.assertEqual(66.833, samples['RxThroughput']) + self.assertEqual(0.099651, samples['DropPercentage']) + self.assertEqual(rfc2544_profile.rate, rfc2544_profile.min_rate) + + @mock.patch.object(ixia_rfc2544.LOG, 'info') + def test_get_drop_percentage_not_flow(self, *args): + samples = {'iface_name_1': + {'InPackets': 1000, 'OutPackets': 0, + 'InBytes': 64000, 'OutBytes': 0, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25}, + 'iface_name_2': + {'InPackets': 1005, 'OutPackets': 0, + 'InBytes': 64320, 'OutBytes': 0, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.rate = 1000 + rfc2544_profile._get_next_rate = mock.Mock(return_value=50.0) + completed, samples = rfc2544_profile.get_drop_percentage( + samples, 0.2, 1, 4, 0.1) + self.assertFalse(completed) + self.assertEqual(0.0, samples['TxThroughput']) + self.assertEqual(66.833, samples['RxThroughput']) + self.assertEqual(100, samples['DropPercentage']) + self.assertEqual(rfc2544_profile.rate, rfc2544_profile.max_rate) + + def test_get_drop_percentage_first_run(self): + samples = {'iface_name_1': + {'InPackets': 1000, 'OutPackets': 1000, + 'InBytes': 64000, 'OutBytes': 64000, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25}, + 'iface_name_2': + {'InPackets': 1005, 'OutPackets': 1007, + 'InBytes': 64320, 'OutBytes': 64448, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25} + } + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile._get_next_rate = mock.Mock(return_value=50.0) + completed, samples = rfc2544_profile.get_drop_percentage( + samples, 0, 1, 4, 0.1, first_run=True) + self.assertTrue(completed) + self.assertEqual(66.9, samples['TxThroughput']) + self.assertEqual(66.833, samples['RxThroughput']) + self.assertEqual(0.099651, samples['DropPercentage']) + self.assertEqual(33.45, rfc2544_profile.rate) + + def test_get_drop_percentage_resolution(self): + rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile._get_next_rate = mock.Mock(return_value=0.1) + samples = {'iface_name_1': + {'InPackets': 1000, 'OutPackets': 1000, + 'InBytes': 64000, 'OutBytes': 64000, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25}, + 'iface_name_2': + {'InPackets': 1005, 'OutPackets': 1007, + 'InBytes': 64320, 'OutBytes': 64448, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25} + } + rfc2544_profile.rate = 0.19 + completed, _ = rfc2544_profile.get_drop_percentage( + samples, 0, 0.05, 4, 0.1) + self.assertTrue(completed) + + samples = {'iface_name_1': + {'InPackets': 1000, 'OutPackets': 1000, + 'InBytes': 64000, 'OutBytes': 64000, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25}, + 'iface_name_2': + {'InPackets': 1005, 'OutPackets': 1007, + 'InBytes': 64320, 'OutBytes': 64448, + 'LatencyAvg': 20, + 'LatencyMin': 15, + 'LatencyMax': 25} + } + rfc2544_profile.rate = 0.5 + completed, _ = rfc2544_profile.get_drop_percentage( + samples, 0, 0.05, 4, 0.1) + self.assertFalse(completed) + + +class TestIXIARFC2544PppoeScenarioProfile(unittest.TestCase): + + TRAFFIC_PROFILE = { + "schema": "nsb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100}, + 'uplink_0': {'ipv4': {'port': 'xe0', 'id': 1}}, + 'downlink_0': {'ipv4': {'port': 'xe2', 'id': 2}}, + 'uplink_1': {'ipv4': {'port': 'xe1', 'id': 3}}, + 'downlink_1': {'ipv4': {'port': 'xe2', 'id': 4}} + } + + def setUp(self): + self.ixia_tp = ixia_rfc2544.IXIARFC2544PppoeScenarioProfile( + self.TRAFFIC_PROFILE) + self.ixia_tp.rate = 100.0 + self.ixia_tp._get_next_rate = mock.Mock(return_value=50.0) + self.ixia_tp._get_framesize = mock.Mock(return_value='64B') + + def test___init__(self): + self.assertIsInstance(self.ixia_tp.full_profile, + collections.OrderedDict) + + def test__get_flow_groups_params(self): + expected_tp = collections.OrderedDict([ + ('uplink_0', {'ipv4': {'id': 1, 'port': 'xe0'}}), + ('downlink_0', {'ipv4': {'id': 2, 'port': 'xe2'}}), + ('uplink_1', {'ipv4': {'id': 3, 'port': 'xe1'}}), + ('downlink_1', {'ipv4': {'id': 4, 'port': 'xe2'}})]) + + self.ixia_tp._get_flow_groups_params() + self.assertDictEqual(self.ixia_tp.full_profile, expected_tp) + + @mock.patch.object(ixia_rfc2544.IXIARFC2544PppoeScenarioProfile, + '_get_flow_groups_params') + def test_update_traffic_profile(self, mock_get_flow_groups_params): + networks = { + 'uplink_0': 'data1', + 'downlink_0': 'data2', + 'uplink_1': 'data3', + 'downlink_1': 'data4' + } + ports = ['xe0', 'xe1', 'xe2', 'xe3'] + mock_traffic_gen = mock.Mock() + mock_traffic_gen.networks = networks + mock_traffic_gen.vnfd_helper.port_num.side_effect = ports + self.ixia_tp.update_traffic_profile(mock_traffic_gen) + mock_get_flow_groups_params.assert_called_once() + self.assertEqual(self.ixia_tp.ports, ports) + + def test__get_prio_flows_drop_percentage(self): + + input_stats = { + '0': { + 'InPackets': 50, + 'OutPackets': 100, + 'Store-Forward_Avg_latency_ns': 10, + 'Store-Forward_Min_latency_ns': 10, + 'Store-Forward_Max_latency_ns': 10}} + + result = self.ixia_tp._get_prio_flows_drop_percentage(input_stats) + self.assertIsNotNone(result['0'].get('DropPercentage')) + self.assertEqual(result['0'].get('DropPercentage'), 50.0) + + def test__get_prio_flows_drop_percentage_traffic_not_flowing(self): + input_stats = { + '0': { + 'InPackets': 0, + 'OutPackets': 0, + 'Store-Forward_Avg_latency_ns': 0, + 'Store-Forward_Min_latency_ns': 0, + 'Store-Forward_Max_latency_ns': 0}} + + result = self.ixia_tp._get_prio_flows_drop_percentage(input_stats) + self.assertIsNotNone(result['0'].get('DropPercentage')) + self.assertEqual(result['0'].get('DropPercentage'), 100) + + def test__get_summary_pppoe_subs_counters(self): + input_stats = { + 'xe0': { + 'OutPackets': 100, + 'SessionsUp': 4, + 'SessionsDown': 0, + 'SessionsNotStarted': 0, + 'SessionsTotal': 4}, + 'xe1': { + 'OutPackets': 100, + 'SessionsUp': 4, + 'SessionsDown': 0, + 'SessionsNotStarted': 0, + 'SessionsTotal': 4} + } + + expected_stats = { + 'SessionsUp': 8, + 'SessionsDown': 0, + 'SessionsNotStarted': 0, + 'SessionsTotal': 8 + } + + res = self.ixia_tp._get_summary_pppoe_subs_counters(input_stats) + self.assertDictEqual(res, expected_stats) + + @mock.patch.object(ixia_rfc2544.IXIARFC2544PppoeScenarioProfile, + '_get_prio_flows_drop_percentage') + @mock.patch.object(ixia_rfc2544.IXIARFC2544PppoeScenarioProfile, + '_get_summary_pppoe_subs_counters') + def test_get_drop_percentage(self, mock_get_pppoe_subs, + mock_sum_prio_drop_rate): + samples = { + 'priority_stats': { + '0': { + 'InPackets': 100, + 'OutPackets': 100, + 'InBytes': 6400, + 'OutBytes': 6400, + 'LatencyAvg': 10, + 'LatencyMin': 10, + 'LatencyMax': 10}}, + 'xe0': { + 'InPackets': 100, + 'OutPackets': 100, + 'InBytes': 6400, + 'OutBytes': 6400, + 'LatencyAvg': 10, + 'LatencyMin': 10, + 'LatencyMax': 10}} + + mock_get_pppoe_subs.return_value = {'SessionsUp': 1} + mock_sum_prio_drop_rate.return_value = {'0': {'DropPercentage': 0.0}} + + self.ixia_tp._get_framesize = mock.Mock(return_value='64B') + status, res = self.ixia_tp.get_drop_percentage( + samples, tol_min=0.0, tolerance=0.0001, precision=0, + resolution=0.1, first_run=True) + self.assertIsNotNone(res.get('DropPercentage')) + self.assertIsNotNone(res.get('Priority')) + self.assertIsNotNone(res.get('SessionsUp')) + self.assertEqual(res['DropPercentage'], 0.0) + self.assertEqual(res['Rate'], 100.0) + self.assertEqual(res['PktSize'], '64B') + self.assertTrue(status) + mock_sum_prio_drop_rate.assert_called_once() + mock_get_pppoe_subs.assert_called_once() + + @mock.patch.object(ixia_rfc2544.IXIARFC2544PppoeScenarioProfile, + '_get_prio_flows_drop_percentage') + @mock.patch.object(ixia_rfc2544.IXIARFC2544PppoeScenarioProfile, + '_get_summary_pppoe_subs_counters') + def test_get_drop_percentage_failed_status(self, mock_get_pppoe_subs, + mock_sum_prio_drop_rate): + samples = { + 'priority_stats': { + '0': { + 'InPackets': 90, + 'OutPackets': 100, + 'InBytes': 5760, + 'OutBytes': 6400, + 'LatencyAvg': 10, + 'LatencyMin': 10, + 'LatencyMax': 10}}, + 'xe0': { + 'InPackets': 90, + 'OutPackets': 100, + 'InBytes': 5760, + 'OutBytes': 6400, + 'LatencyAvg': 10, + 'LatencyMin': 10, + 'LatencyMax': 10}} + + mock_get_pppoe_subs.return_value = {'SessionsUp': 1} + mock_sum_prio_drop_rate.return_value = {'0': {'DropPercentage': 0.0}} + + status, res = self.ixia_tp.get_drop_percentage( + samples, tol_min=0.0, tolerance=0.0001, precision=0, + resolution=0.1, first_run=True) + self.assertIsNotNone(res.get('DropPercentage')) + self.assertIsNotNone(res.get('Priority')) + self.assertIsNotNone(res.get('SessionsUp')) + self.assertEqual(res['DropPercentage'], 10.0) + self.assertFalse(status) + mock_sum_prio_drop_rate.assert_called_once() + mock_get_pppoe_subs.assert_called_once() + + @mock.patch.object(ixia_rfc2544.IXIARFC2544PppoeScenarioProfile, + '_get_prio_flows_drop_percentage') + @mock.patch.object(ixia_rfc2544.IXIARFC2544PppoeScenarioProfile, + '_get_summary_pppoe_subs_counters') + def test_get_drop_percentage_priority_flow_check(self, mock_get_pppoe_subs, + mock_sum_prio_drop_rate): + samples = { + 'priority_stats': { + '0': { + 'InPackets': 100, + 'OutPackets': 100, + 'InBytes': 6400, + 'OutBytes': 6400, + 'LatencyAvg': 10, + 'LatencyMin': 10, + 'LatencyMax': 10}}, + 'xe0': { + 'InPackets': 90, + 'OutPackets': 100, + 'InBytes': 5760, + 'OutBytes': 6400, + 'LatencyAvg': 10, + 'LatencyMin': 10, + 'LatencyMax': 10 + }} + + mock_get_pppoe_subs.return_value = {'SessionsUp': 1} + mock_sum_prio_drop_rate.return_value = {'0': {'DropPercentage': 0.0}} + + tc_rfc2544_opts = {'priority': '0', + 'allowed_drop_rate': '0.0001 - 0.0001'} + status, res = self.ixia_tp.get_drop_percentage( + samples, tol_min=15.0000, tolerance=15.0001, precision=0, + resolution=0.1, first_run=True, tc_rfc2544_opts=tc_rfc2544_opts) + self.assertIsNotNone(res.get('DropPercentage')) + self.assertIsNotNone(res.get('Priority')) + self.assertIsNotNone(res.get('SessionsUp')) + self.assertTrue(status) + mock_sum_prio_drop_rate.assert_called_once() + mock_get_pppoe_subs.assert_called_once() diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_landslide_profile.py b/yardstick/tests/unit/network_services/traffic_profile/test_landslide_profile.py new file mode 100644 index 000000000..afd550029 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_landslide_profile.py @@ -0,0 +1,136 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import unittest + +from yardstick.network_services.traffic_profile import landslide_profile + +TP_CONFIG = { + 'schema': "nsb:traffic_profile:0.1", + 'name': 'LandslideProfile', + 'description': 'Spirent Landslide traffic profile (Data Message Flow)', + 'traffic_profile': { + 'traffic_type': 'LandslideProfile' + }, + 'dmf_config': { + 'dmf': { + 'library': 'test', + 'name': 'Fireball UDP', + 'description': "Basic data flow using UDP/IP (Fireball DMF)", + 'keywords': 'UDP ', + 'dataProtocol': 'fb_udp', + 'burstCount': 1, + 'clientPort': { + 'clientPort': 2002, + 'isClientPortRange': 'false' + }, + 'serverPort': 2003, + 'connection': { + 'initiatingSide': 'Client', + 'disconnectSide': 'Client', + 'underlyingProtocol': 'none', + 'persistentConnection': 'false' + }, + 'protocolId': 0, + 'persistentConnection': 'false', + 'transactionRate': 8.0, + 'transactions': { + 'totalTransactions': 0, + 'retries': 0, + 'dataResponseTime': 60000, + 'packetSize': 64 + }, + 'segment': { + 'segmentSize': 64000, + 'maxSegmentSize': 0 + }, + 'size': { + 'sizeDistribution': 'Fixed', + 'sizeDeviation': 10 + }, + 'interval': { + 'intervalDistribution': 'Fixed', + 'intervalDeviation': 10 + }, + 'ipHeader': { + 'typeOfService': 0, + 'timeToLive': 64 + }, + 'tcpConnection': { + 'force3Way': 'false', + 'fixedRetryTime': 0, + 'maxPacketsToForceAck': 0 + }, + 'tcp': { + 'windowSize': 32768, + 'windowScaling': -1, + 'disableFinAckWait': 'false' + }, + 'disconnectType': 'FIN', + 'slowStart': 'false', + 'connectOnly': 'false', + 'vtag': { + 'VTagMask': '0x0', + 'VTagValue': '0x0' + }, + 'sctpPayloadProtocolId': 0, + 'billingIncludeSyn': 'true', + 'billingIncludeSubflow': 'true', + 'billingRecordPerTransaction': 'false', + 'tcpPush': 'false', + 'hostDataExpansionRatio': 1 + } + } +} +DMF_OPTIONS = { + 'dmf': { + 'transactionRate': 5, + 'packetSize': 512, + 'burstCount': 1 + } +} + + +class TestLandslideProfile(unittest.TestCase): + + def test___init__(self): + ls_traffic_profile = landslide_profile.LandslideProfile(TP_CONFIG) + self.assertListEqual([TP_CONFIG["dmf_config"]], + ls_traffic_profile.dmf_config) + + def test___init__config_not_a_dict(self): + _tp_config = copy.deepcopy(TP_CONFIG) + _tp_config['dmf_config'] = [_tp_config['dmf_config']] + ls_traffic_profile = landslide_profile.LandslideProfile(_tp_config) + self.assertListEqual(_tp_config['dmf_config'], + ls_traffic_profile.dmf_config) + + def test_execute(self): + ls_traffic_profile = landslide_profile.LandslideProfile(TP_CONFIG) + self.assertIsNone(ls_traffic_profile.execute(None)) + + def test_update_dmf_options_dict(self): + ls_traffic_profile = landslide_profile.LandslideProfile(TP_CONFIG) + ls_traffic_profile.update_dmf(DMF_OPTIONS) + self.assertDictContainsSubset(DMF_OPTIONS['dmf'], + ls_traffic_profile.dmf_config[0]) + + def test_update_dmf_options_list(self): + ls_traffic_profile = landslide_profile.LandslideProfile(TP_CONFIG) + _dmf_options = copy.deepcopy(DMF_OPTIONS) + _dmf_options['dmf'] = [_dmf_options['dmf']] + ls_traffic_profile.update_dmf(_dmf_options) + self.assertTrue(all([x in ls_traffic_profile.dmf_config[0] + for x in DMF_OPTIONS['dmf']])) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_pktgen.py b/yardstick/tests/unit/network_services/traffic_profile/test_pktgen.py new file mode 100644 index 000000000..08542b4f1 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_pktgen.py @@ -0,0 +1,63 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock + +from yardstick.common import utils +from yardstick.network_services.traffic_profile import pktgen +from yardstick.tests.unit import base as ut_base + + +class TestIXIARFC2544Profile(ut_base.BaseUnitTestCase): + + def setUp(self): + self._tp_config = {'traffic_profile': {}} + self._host = 'localhost' + self._port = '12345' + self.tp = pktgen.PktgenTrafficProfile(self._tp_config) + self.tp.init(self._host, self._port) + self._mock_send_socket_command = mock.patch.object( + utils, 'send_socket_command', return_value=0) + self.mock_send_socket_command = self._mock_send_socket_command.start() + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_send_socket_command.stop() + + def test_start(self): + self.tp.start() + self.mock_send_socket_command.assert_called_once_with( + self._host, self._port, 'pktgen.start("0")') + + def test_stop(self): + self.tp.stop() + self.mock_send_socket_command.assert_called_once_with( + self._host, self._port, 'pktgen.stop("0")') + + def test_rate(self): + rate = 75 + self.tp.rate(rate) + command = 'pktgen.set("0", "rate", 75)' + self.mock_send_socket_command.assert_called_once_with( + self._host, self._port, command) + + def test_clear_all_stats(self): + self.tp.clear_all_stats() + self.mock_send_socket_command.assert_called_once_with( + self._host, self._port, 'clr') + + def test_help(self): + self.tp.help() + self.mock_send_socket_command.assert_called_once_with( + self._host, self._port, 'help') diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_prox_acl.py b/yardstick/tests/unit/network_services/traffic_profile/test_prox_acl.py new file mode 100644 index 000000000..48c449b20 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_prox_acl.py @@ -0,0 +1,74 @@ +# Copyright (c) 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest +import mock + +from yardstick.tests import STL_MOCKS + +STLClient = mock.MagicMock() +stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) +stl_patch.start() + +if stl_patch: + from yardstick.network_services.traffic_profile.prox_ACL import ProxACLProfile + from yardstick.network_services.vnf_generic.vnf.prox_helpers import ProxTestDataTuple + + +class TestProxACLProfile(unittest.TestCase): + + def test_run_test_with_pkt_size(self): + def target(*args): + runs.append(args[2]) + if args[2] < 0 or args[2] > 100: + raise RuntimeError(' '.join([str(args), str(runs)])) + if args[2] > 75.0: + return fail_tuple, {} + return success_tuple, {} + + tp_config = { + 'traffic_profile': { + 'upper_bound': 100.0, + 'lower_bound': 0.0, + 'tolerated_loss': 50.0, + 'attempts': 20 + }, + } + + runs = [] + success_tuple = ProxTestDataTuple( + 10.0, 1, 2, 3, 4, [5.1, 5.2, 5.3], 995, 1000, 123.4) + fail_tuple = ProxTestDataTuple( + 10.0, 1, 2, 3, 4, [5.6, 5.7, 5.8], 850, 1000, 123.4) + + traffic_gen = mock.MagicMock() + + profile_helper = mock.MagicMock() + profile_helper.run_test = target + + profile = ProxACLProfile(tp_config) + profile.init(mock.MagicMock()) + + profile.prox_config["attempts"] = 20 + profile.queue = mock.MagicMock() + profile.tolerated_loss = 50.0 + profile.pkt_size = 128 + profile.duration = 30 + profile.test_value = 100.0 + profile.tolerated_loss = 100.0 + profile._profile_helper = profile_helper + + profile.run_test_with_pkt_size( + traffic_gen, profile.pkt_size, profile.duration) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_prox_binsearch.py b/yardstick/tests/unit/network_services/traffic_profile/test_prox_binsearch.py new file mode 100644 index 000000000..f17656328 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_prox_binsearch.py @@ -0,0 +1,302 @@ +# Copyright (c) 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import mock + +from yardstick.network_services.vnf_generic.vnf.prox_helpers import ProxTestDataTuple +from yardstick.network_services.traffic_profile import prox_binsearch + + +class TestProxBinSearchProfile(unittest.TestCase): + + THEOR_MAX_THROUGHPUT = 0.00012340000000000002 + + def setUp(self): + self._mock_log_info = mock.patch.object(prox_binsearch.LOG, 'info') + self.mock_log_info = self._mock_log_info.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_log_info.stop() + + def test_execute_1(self): + def target(*args, **_): + runs.append(args[2]) + if args[2] < 0 or args[2] > 100: + raise RuntimeError(' '.join([str(args), str(runs)])) + if args[2] > 75.0: + return fail_tuple, {} + return success_tuple, {} + + def side_effect_func(arg1, arg2): + if arg1 == "confirmation": + return arg2 + else: + return {} + + tp_config = { + 'traffic_profile': { + 'packet_sizes': [200], + 'test_precision': 2.0, + 'tolerated_loss': 0.001, + }, + } + + runs = [] + success_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.1, 5.2, 5.3], 995, 1000, 123.4) + fail_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.6, 5.7, 5.8], 850, 1000, 123.4) + + traffic_generator = mock.MagicMock() + attrs1 = {'get.return_value': 10} + traffic_generator.scenario_helper.all_options.configure_mock(**attrs1) + + attrs2 = {'__getitem__.return_value': 10, 'get.return_value': 10} + attrs3 = {'get.side_effect': side_effect_func} + traffic_generator.scenario_helper.scenario_cfg["runner"].configure_mock(**attrs2) + traffic_generator.scenario_helper.scenario_cfg["options"].configure_mock(**attrs3) + + profile_helper = mock.MagicMock() + profile_helper.run_test = target + + profile = prox_binsearch.ProxBinSearchProfile(tp_config) + profile.init(mock.MagicMock()) + profile._profile_helper = profile_helper + + profile.execute_traffic(traffic_generator) + + self.assertEqual(round(profile.current_lower, 2), 74.69) + self.assertEqual(round(profile.current_upper, 2), 76.09) + self.assertEqual(len(runs), 7) + + # Result Samples inc theor_max + result_tuple = {'Actual_throughput': 5e-07, + 'theor_max_throughput': self.THEOR_MAX_THROUGHPUT, + 'PktSize': 200, + 'Status': 'Result'} + + test_results = profile.queue.put.call_args[0] + for k in result_tuple: + self.assertEqual(result_tuple[k], test_results[0][k]) + + success_result_tuple = {"CurrentDropPackets": 0.5, + "DropPackets": 0.5, + "LatencyAvg": 5.3, + "LatencyMax": 5.2, + "LatencyMin": 5.1, + "PktSize": 200, + "RxThroughput": 7.5e-07, + "Throughput": 7.5e-07, + "TxThroughput": self.THEOR_MAX_THROUGHPUT, + "Status": 'Success'} + + calls = profile.queue.put(success_result_tuple) + profile.queue.put.assert_has_calls(calls) + + success_result_tuple2 = {"CurrentDropPackets": 0.5, + "DropPackets": 0.5, + "LatencyAvg": 5.3, + "LatencyMax": 5.2, + "LatencyMin": 5.1, + "PktSize": 200, + "RxThroughput": 7.5e-07, + "Throughput": 7.5e-07, + "TxThroughput": 123.4, + "can_be_lost": 409600, + "drop_total": 20480, + "rx_total": 4075520, + "tx_total": 4096000, + "Status": 'Success'} + + calls = profile.queue.put(success_result_tuple2) + profile.queue.put.assert_has_calls(calls) + + def test_execute_2(self): + def target(*args, **_): + runs.append(args[2]) + if args[2] < 0 or args[2] > 100: + raise RuntimeError(' '.join([str(args), str(runs)])) + if args[2] > 25.0: + return fail_tuple, {} + return success_tuple, {} + + def side_effect_func(arg1, _): + if arg1 == "confirmation": + return 2 + else: + return {} + + tp_config = { + 'traffic_profile': { + 'packet_sizes': [200], + 'test_precision': 2.0, + 'tolerated_loss': 0.001, + }, + } + + runs = [] + success_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.1, 5.2, 5.3], 995, 1000, 123.4) + fail_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.6, 5.7, 5.8], 850, 1000, 123.4) + + traffic_generator = mock.MagicMock() + attrs1 = {'get.return_value': 10} + traffic_generator.scenario_helper.all_options.configure_mock(**attrs1) + + attrs2 = {'__getitem__.return_value': 0, 'get.return_value': 0} + attrs3 = {'get.side_effect': side_effect_func} + + traffic_generator.scenario_helper.scenario_cfg["runner"].configure_mock(**attrs2) + traffic_generator.scenario_helper.scenario_cfg["options"].configure_mock(**attrs3) + + profile_helper = mock.MagicMock() + profile_helper.run_test = target + + profile = prox_binsearch.ProxBinSearchProfile(tp_config) + profile.init(mock.MagicMock()) + profile._profile_helper = profile_helper + + profile.execute_traffic(traffic_generator) + self.assertEqual(round(profile.current_lower, 2), 24.06) + self.assertEqual(round(profile.current_upper, 2), 25.47) + self.assertEqual(len(runs), 21) + + def test_execute_3(self): + def target(*args, **_): + runs.append(args[2]) + if args[2] < 0 or args[2] > 100: + raise RuntimeError(' '.join([str(args), str(runs)])) + if args[2] > 75.0: + return fail_tuple, {} + return success_tuple, {} + + tp_config = { + 'traffic_profile': { + 'packet_sizes': [200], + 'test_precision': 2.0, + 'tolerated_loss': 0.001, + }, + } + + runs = [] + success_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.1, 5.2, 5.3], 995, 1000, 123.4) + fail_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.6, 5.7, 5.8], 850, 1000, 123.4) + + traffic_generator = mock.MagicMock() + + profile_helper = mock.MagicMock() + profile_helper.run_test = target + + profile = prox_binsearch.ProxBinSearchProfile(tp_config) + profile.init(mock.MagicMock()) + profile._profile_helper = profile_helper + + profile.upper_bound = 100.0 + profile.lower_bound = 99.0 + profile.execute_traffic(traffic_generator) + + result_tuple = {'Actual_throughput': 0, 'theor_max_throughput': 0, + "Status": 'Result', "Next_Step": ''} + profile.queue.put.assert_called_with(result_tuple) + + # Check for success_ tuple (None expected) + calls = profile.queue.put.mock_calls + for call in calls: + for call_detail in call[1]: + if call_detail["Status"] == 'Success': + self.assertRaises(AttributeError) + + def test_execute_4(self): + + def target(*args, **_): + runs.append(args[2]) + if args[2] < 0 or args[2] > 100: + raise RuntimeError(' '.join([str(args), str(runs)])) + if args[2] > 75.0: + return fail_tuple, {} + + return success_tuple, {} + + tp_config = { + 'traffic_profile': { + 'packet_sizes': [200], + 'test_precision': 2.0, + 'tolerated_loss': 0.001, + }, + } + + runs = [] + success_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.1, 5.2, 5.3], 995, 1000, 123.4) + fail_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.6, 5.7, 5.8], 850, 1000, 123.4) + + traffic_generator = mock.MagicMock() + attrs1 = {'get.return_value': 100000} + traffic_generator.scenario_helper.all_options.configure_mock(**attrs1) + + attrs2 = {'__getitem__.return_value': 0, 'get.return_value': 0} + + traffic_generator.scenario_helper.scenario_cfg["runner"].configure_mock(**attrs2) + + profile_helper = mock.MagicMock() + profile_helper.run_test = target + + profile = prox_binsearch.ProxBinSearchProfile(tp_config) + profile.init(mock.MagicMock()) + profile._profile_helper = profile_helper + + profile.execute_traffic(traffic_generator) + self.assertEqual(round(profile.current_lower, 2), 74.69) + self.assertEqual(round(profile.current_upper, 2), 76.09) + self.assertEqual(len(runs), 7) + + # Result Samples inc theor_max + result_tuple = {'Actual_throughput': 5e-07, + 'theor_max_throughput': self.THEOR_MAX_THROUGHPUT, + 'PktSize': 200, + "Status": 'Result'} + + test_results = profile.queue.put.call_args[0] + for k in result_tuple: + self.assertEqual(result_tuple[k], test_results[0][k]) + + success_result_tuple = {"CurrentDropPackets": 0.5, + "DropPackets": 0.5, + "LatencyAvg": 5.3, + "LatencyMax": 5.2, + "LatencyMin": 5.1, + "PktSize": 200, + "RxThroughput": 7.5e-07, + "Throughput": 7.5e-07, + "TxThroughput": self.THEOR_MAX_THROUGHPUT, + "Status": 'Success'} + + calls = profile.queue.put(success_result_tuple) + profile.queue.put.assert_has_calls(calls) + + success_result_tuple2 = {"CurrentDropPackets": 0.5, + "DropPackets": 0.5, + "LatencyAvg": 5.3, + "LatencyMax": 5.2, + "LatencyMin": 5.1, + "PktSize": 200, + "RxThroughput": 7.5e-07, + "Throughput": 7.5e-07, + "TxThroughput": 123.4, + "can_be_lost": 409600, + "drop_total": 20480, + "rx_total": 4075520, + "tx_total": 4096000, + "Status": 'Success'} + + calls = profile.queue.put(success_result_tuple2) + profile.queue.put.assert_has_calls(calls) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_prox_irq.py b/yardstick/tests/unit/network_services/traffic_profile/test_prox_irq.py new file mode 100644 index 000000000..1d9eb0887 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_prox_irq.py @@ -0,0 +1,57 @@ +# Copyright (c) 2018-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import time + +import unittest +import mock + +from yardstick.network_services.traffic_profile import prox_irq + + +class TestProxIrqProfile(unittest.TestCase): + + def setUp(self): + self._mock_log_info = mock.patch.object(prox_irq.LOG, 'info') + self.mock_log_info = self._mock_log_info.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_log_info.stop() + + @mock.patch.object(time, 'sleep') + def test_execute_1(self, *args): + tp_config = { + 'traffic_profile': { + }, + } + + traffic_generator = mock.MagicMock() + attrs1 = {'get.return_value' : 10} + traffic_generator.scenario_helper.all_options.configure_mock(**attrs1) + + attrs2 = {'__getitem__.return_value' : 10, 'get.return_value': 10} + traffic_generator.scenario_helper.scenario_cfg["runner"].configure_mock(**attrs2) + + profile_helper = mock.MagicMock() + + profile = prox_irq.ProxIrqProfile(tp_config) + profile.init(mock.MagicMock()) + profile._profile_helper = profile_helper + + profile.execute_traffic(traffic_generator) + profile.run_test() + is_ended_flag = profile.is_ended() + + self.assertFalse(is_ended_flag) + self.assertEqual(profile.lower_bound, 10.0) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_prox_profile.py b/yardstick/tests/unit/network_services/traffic_profile/test_prox_profile.py new file mode 100644 index 000000000..1593a0835 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_prox_profile.py @@ -0,0 +1,130 @@ +# Copyright (c) 2017-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import time + +import unittest +import mock + +from yardstick.tests import STL_MOCKS + +STLClient = mock.MagicMock() +stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) +stl_patch.start() + +if stl_patch: + from yardstick.network_services.traffic_profile.prox_profile import ProxProfile + from yardstick.network_services.vnf_generic.vnf.prox_helpers import ProxResourceHelper + + +class TestProxProfile(unittest.TestCase): + + def test_sort_vpci(self): + traffic_generator = mock.Mock() + interface_1 = {'virtual-interface': {'vpci': 'id1'}, 'name': 'name1'} + interface_2 = {'virtual-interface': {'vpci': 'id2'}, 'name': 'name2'} + interface_3 = {'virtual-interface': {'vpci': 'id3'}, 'name': 'name3'} + interfaces = [interface_2, interface_3, interface_1] + traffic_generator.vnfd_helper = { + 'vdu': [{'external-interface': interfaces}]} + output = ProxProfile.sort_vpci(traffic_generator) + self.assertEqual([interface_1, interface_2, interface_3], output) + + def test_fill_samples(self): + samples = {} + + traffic_generator = mock.MagicMock() + interfaces = [ + ['id1', 'name1'], + ['id2', 'name2'] + ] + traffic_generator.resource_helper.sut.port_stats.side_effect = [ + list(range(12)), + list(range(10, 22)), + ] + + expected = { + 'name1': { + 'in_packets': 6, + 'out_packets': 7, + }, + 'name2': { + 'in_packets': 16, + 'out_packets': 17, + }, + } + with mock.patch.object(ProxProfile, 'sort_vpci', return_value=interfaces): + ProxProfile.fill_samples(samples, traffic_generator) + + self.assertDictEqual(samples, expected) + + def test_init(self): + tp_config = { + 'traffic_profile': {}, + } + + profile = ProxProfile(tp_config) + queue = mock.Mock() + profile.init(queue) + self.assertIs(profile.queue, queue) + + @mock.patch.object(time, 'sleep') + def test_execute_traffic(self, *args): + packet_sizes = [ + 10, + 100, + 1000, + ] + tp_config = { + 'traffic_profile': { + 'packet_sizes': packet_sizes, + }, + } + + traffic_generator = mock.MagicMock() + + setup_helper = traffic_generator.setup_helper + setup_helper.find_in_section.return_value = None + + prox_resource_helper = ProxResourceHelper(setup_helper) + traffic_generator.resource_helper = prox_resource_helper + + profile = ProxProfile(tp_config) + + self.assertFalse(profile.done.is_set()) + for _ in packet_sizes: + with self.assertRaises(NotImplementedError): + profile.execute_traffic(traffic_generator) + + self.assertIsNone(profile.execute_traffic(traffic_generator)) + self.assertTrue(profile.done.is_set()) + + def test_bounds_iterator(self): + tp_config = { + 'traffic_profile': {}, + } + + profile = ProxProfile(tp_config) + value = 0.0 + for value in profile.bounds_iterator(): + pass + + self.assertEqual(value, 100.0) + + mock_logger = mock.MagicMock() + for _ in profile.bounds_iterator(mock_logger): + pass + + mock_logger.debug.assert_called_once() + self.assertEqual(mock_logger.info.call_count, 10) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_prox_ramp.py b/yardstick/tests/unit/network_services/traffic_profile/test_prox_ramp.py new file mode 100644 index 000000000..7a77e3295 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_prox_ramp.py @@ -0,0 +1,95 @@ +# Copyright (c) 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest +import mock + +from yardstick.tests import STL_MOCKS + +STLClient = mock.MagicMock() +stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) +stl_patch.start() + +if stl_patch: + from yardstick.network_services.traffic_profile.prox_ramp import ProxRampProfile + from yardstick.network_services.vnf_generic.vnf.prox_helpers import ProxProfileHelper + from yardstick.network_services.vnf_generic.vnf.prox_helpers import ProxTestDataTuple + + +class TestProxRampProfile(unittest.TestCase): + + def test_run_test_with_pkt_size(self): + tp_config = { + 'traffic_profile': { + 'lower_bound': 10.0, + 'upper_bound': 100.0, + 'step_value': 10.0, + }, + } + + success_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.1, 5.2, 5.3], 995, 1000, 123.4) + + traffic_gen = mock.MagicMock() + traffic_gen._test_type = 'Generic' + + profile_helper = ProxProfileHelper(traffic_gen.resource_helper) + profile_helper.run_test = run_test = mock.MagicMock(return_value=success_tuple) + + profile = ProxRampProfile(tp_config) + profile.fill_samples = fill_samples = mock.MagicMock() + profile.queue = mock.MagicMock() + profile._profile_helper = profile_helper + + profile.run_test_with_pkt_size(traffic_gen, 128, 30) + self.assertEqual(run_test.call_count, 10) + self.assertEqual(fill_samples.call_count, 10) + + def test_run_test_with_pkt_size_with_fail(self): + tp_config = { + 'traffic_profile': { + 'lower_bound': 10.0, + 'upper_bound': 100.0, + 'step_value': 10.0, + }, + } + + success_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.1, 5.2, 5.3], 995, 1000, 123.4) + fail_tuple = ProxTestDataTuple(10.0, 1, 2, 3, 4, [5.6, 5.7, 5.8], 850, 1000, 123.4) + + result_list = [ + success_tuple, + success_tuple, + success_tuple, + fail_tuple, + success_tuple, + fail_tuple, + fail_tuple, + fail_tuple, + ] + + traffic_gen = mock.MagicMock() + traffic_gen._test_type = 'Generic' + + profile_helper = ProxProfileHelper(traffic_gen.resource_helper) + profile_helper.run_test = run_test = mock.MagicMock(side_effect=result_list) + + profile = ProxRampProfile(tp_config) + profile.fill_samples = fill_samples = mock.MagicMock() + profile.queue = mock.MagicMock() + profile._profile_helper = profile_helper + + profile.run_test_with_pkt_size(traffic_gen, 128, 30) + self.assertEqual(run_test.call_count, 4) + self.assertEqual(fill_samples.call_count, 3) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_rfc2544.py b/yardstick/tests/unit/network_services/traffic_profile/test_rfc2544.py new file mode 100644 index 000000000..febcfe5da --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_rfc2544.py @@ -0,0 +1,341 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime + +import mock +from trex_stl_lib import api as Pkt +from trex_stl_lib import trex_stl_client +from trex_stl_lib import trex_stl_packet_builder_scapy +from trex_stl_lib import trex_stl_streams + +from yardstick.common import constants +from yardstick.network_services.traffic_profile import rfc2544 +from yardstick.tests.unit import base + + +class TestRFC2544Profile(base.BaseUnitTestCase): + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, + "flow_number": 10, + "frame_size": 64}} + + PROFILE = {'description': 'Traffic profile to run RFC2544 latency', + 'name': 'rfc2544', + 'traffic_profile': {'traffic_type': 'RFC2544Profile', + 'frame_rate': 100}, + 'downlink_0': + {'ipv4': + {'outer_l2': + {'framesize': + {'64B': '100', '1518B': '0', + '128B': '0', '1400B': '0', + '256B': '0', '373b': '0', + '570B': '0'}}, + 'outer_l3v4': + {'dstip4': '1.1.1.1-1.15.255.255', + 'proto': 'udp', + 'srcip4': '90.90.1.1-90.105.255.255', + 'dscp': 0, 'ttl': 32, 'count': 1}, + 'outer_l4': + {'srcport': '2001', + 'dsrport': '1234', 'count': 1}}}, + 'uplink_0': + {'ipv4': + {'outer_l2': + {'framesize': + {'64B': '100', '1518B': '0', + '128B': '0', '1400B': '0', + '256B': '0', '373b': '0', + '570B': '0'}}, + 'outer_l3v4': + {'dstip4': '9.9.1.1-90.105.255.255', + 'proto': 'udp', + 'srcip4': '1.1.1.1-1.15.255.255', + 'dscp': 0, 'ttl': 32, 'count': 1}, + 'outer_l4': + {'dstport': '2001', + 'srcport': '1234', 'count': 1}}}, + 'schema': 'isb:traffic_profile:0.1'} + + def test___init__(self): + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + self.assertEqual(rfc2544_profile.max_rate, rfc2544_profile.rate) + self.assertEqual(0, rfc2544_profile.min_rate) + + def test_stop_traffic(self): + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + mock_generator = mock.Mock() + rfc2544_profile.stop_traffic(traffic_generator=mock_generator) + mock_generator.client.stop.assert_called_once() + mock_generator.client.reset.assert_called_once() + mock_generator.client.remove_all_streams.assert_called_once() + + def test_execute_traffic(self): + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + mock_generator = mock.Mock() + mock_generator.networks = { + 'downlink_0': ['xe0', 'xe1'], + 'uplink_0': ['xe2', 'xe3'], + 'downlink_1': []} + mock_generator.port_num.side_effect = [10, 20, 30, 40] + mock_generator.rfc2544_helper.correlated_traffic = False + rfc2544_profile.params = { + 'downlink_0': 'profile1', + 'uplink_0': 'profile2'} + + with mock.patch.object(rfc2544_profile, '_create_profile') as \ + mock_create_profile: + rfc2544_profile.execute_traffic(traffic_generator=mock_generator) + mock_create_profile.assert_has_calls([ + mock.call('profile1', rfc2544_profile.rate, mock.ANY, False), + mock.call('profile1', rfc2544_profile.rate, mock.ANY, False), + mock.call('profile2', rfc2544_profile.rate, mock.ANY, False), + mock.call('profile2', rfc2544_profile.rate, mock.ANY, False)]) + mock_generator.client.add_streams.assert_has_calls([ + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[30]), + mock.call(mock.ANY, ports=[40])]) + mock_generator.client.start(ports=[10, 20, 30, 40], + duration=rfc2544_profile.config.duration, + force=True) + + @mock.patch.object(trex_stl_streams, 'STLProfile') + def test__create_profile(self, mock_stl_profile): + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + port_pg_id = mock.ANY + profile_data = {'packetid_1': {'outer_l2': {'framesize': 'imix_info'}}} + rate = 100 + with mock.patch.object(rfc2544_profile, '_create_imix_data') as \ + mock_create_imix, \ + mock.patch.object(rfc2544_profile, '_create_vm') as \ + mock_create_vm, \ + mock.patch.object(rfc2544_profile, '_create_streams') as \ + mock_create_streams: + mock_create_imix.return_value = 'imix_data' + mock_create_streams.return_value = ['stream1'] + rfc2544_profile._create_profile(profile_data, rate, port_pg_id, + True) + + mock_create_imix.assert_called_once_with('imix_info') + mock_create_vm.assert_called_once_with( + {'outer_l2': {'framesize': 'imix_info'}}) + mock_create_streams.assert_called_once_with('imix_data', 100, + port_pg_id, True) + mock_stl_profile.assert_called_once_with(['stream1']) + + def test__create_imix_data_mode_DIP(self): + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + data = {'64B': 50, '128B': 50} + self.assertEqual( + {'64': 50.0, '128': 50.0}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_PACKETS)) + data = {'64B': 1, '128b': 3} + self.assertEqual( + {'64': 25.0, '128': 75.0}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_PACKETS)) + data = {} + self.assertEqual( + {}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_PACKETS)) + + def test__create_imix_data_mode_DIB(self): + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + data = {'64B': 25, '128B': 25, '512B': 25, '1518B': 25} + byte_total = 64 * 25 + 128 * 25 + 512 * 25 + 1518 * 25 + self.assertEqual( + {'64': 64 * 25.0 * 100 / byte_total, + '128': 128 * 25.0 * 100 / byte_total, + '512': 512 * 25.0 * 100 / byte_total, + '1518': 1518 * 25.0 * 100/ byte_total}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_BYTES)) + data = {} + self.assertEqual( + {}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_BYTES)) + data = {'64B': 100} + self.assertEqual( + {'64': 100.0}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_BYTES)) + + def test__create_vm(self): + packet = {'outer_l2': 'l2_definition'} + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + with mock.patch.object(rfc2544_profile, '_set_outer_l2_fields') as \ + mock_l2_fileds: + rfc2544_profile._create_vm(packet) + mock_l2_fileds.assert_called_once_with('l2_definition') + + @mock.patch.object(trex_stl_packet_builder_scapy, 'STLPktBuilder', + return_value='packet') + def test__create_single_packet(self, mock_pktbuilder): + size = 128 + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.ether_packet = Pkt.Eth() + rfc2544_profile.ip_packet = Pkt.IP() + rfc2544_profile.udp_packet = Pkt.UDP() + rfc2544_profile.trex_vm = 'trex_vm' + base_pkt = (rfc2544_profile.ether_packet / rfc2544_profile.ip_packet / + rfc2544_profile.udp_packet) + pad = (size - len(base_pkt)) * 'x' + output = rfc2544_profile._create_single_packet(size=size) + mock_pktbuilder.assert_called_once_with(pkt=base_pkt / pad, + vm='trex_vm') + self.assertEqual(output, 'packet') + + @mock.patch.object(trex_stl_packet_builder_scapy, 'STLPktBuilder', + return_value='packet') + def test__create_single_packet_qinq(self, mock_pktbuilder): + size = 128 + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.ether_packet = Pkt.Eth() + rfc2544_profile.ip_packet = Pkt.IP() + rfc2544_profile.udp_packet = Pkt.UDP() + rfc2544_profile.trex_vm = 'trex_vm' + rfc2544_profile.qinq = True + rfc2544_profile.qinq_packet = Pkt.Dot1Q(vlan=1) / Pkt.Dot1Q(vlan=2) + base_pkt = (rfc2544_profile.ether_packet / + rfc2544_profile.qinq_packet / rfc2544_profile.ip_packet / + rfc2544_profile.udp_packet) + pad = (size - len(base_pkt)) * 'x' + output = rfc2544_profile._create_single_packet(size=size) + mock_pktbuilder.assert_called_once_with(pkt=base_pkt / pad, + vm='trex_vm') + self.assertEqual(output, 'packet') + + @mock.patch.object(trex_stl_streams, 'STLFlowLatencyStats') + @mock.patch.object(trex_stl_streams, 'STLTXCont') + @mock.patch.object(trex_stl_client, 'STLStream') + def test__create_streams(self, mock_stream, mock_txcont, mock_latency): + imix_data = {'64': 25, '512': 75} + rate = 35 + port_pg_id = rfc2544.PortPgIDMap() + port_pg_id.add_port(10) + mock_stream.side_effect = ['stream1', 'stream2'] + mock_txcont.side_effect = ['txcont1', 'txcont2'] + mock_latency.side_effect = ['latency1', 'latency2'] + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + with mock.patch.object(rfc2544_profile, '_create_single_packet'): + output = rfc2544_profile._create_streams(imix_data, rate, + port_pg_id, True) + self.assertEqual(['stream1', 'stream2'], output) + mock_latency.assert_has_calls([ + mock.call(pg_id=1), mock.call(pg_id=2)]) + mock_txcont.assert_has_calls([ + mock.call(percentage=float(25 * 35) / 100), + mock.call(percentage=float(75 * 35) / 100)], any_order=True) + + @mock.patch.object(rfc2544.RFC2544Profile, '_get_framesize') + def test_get_drop_percentage(self, mock_get_framesize): + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + rfc2544_profile.iteration = 1 + mock_get_framesize.return_value = '64B' + + samples = [ + {'xe1': {'out_packets': 2100, + 'in_packets': 2010, + 'out_bytes': 134400, + 'in_bytes': 128640, + 'timestamp': datetime.datetime(2000, 1, 1, 1, 1, 1, 1)}, + 'xe2': {'out_packets': 4100, + 'in_packets': 4010, + 'out_bytes': 262400, + 'in_bytes': 256640, + 'timestamp': datetime.datetime(2000, 1, 1, 1, 1, 1, 1)}}, + {'xe1': {'out_packets': 2110, + 'in_packets': 2040, + 'out_bytes': 135040, + 'in_bytes': 130560, + 'latency': 'Latency1', + 'timestamp': datetime.datetime(2000, 1, 1, 1, 1, 1, 31)}, + 'xe2': {'out_packets': 4150, + 'in_packets': 4010, + 'out_bytes': 265600, + 'in_bytes': 256640, + 'latency': 'Latency2', + 'timestamp': datetime.datetime(2000, 1, 1, 1, 1, 1, 31)}} + ] + completed, output = rfc2544_profile.get_drop_percentage( + samples, 0, 0, False, 0.1) + expected = {'xe1': {'OutPackets': 10, + 'InPackets': 30, + 'OutBytes': 640, + 'InBytes': 1920}, + 'xe2': {'OutPackets': 50, + 'InPackets': 0, + 'OutBytes': 3200, + 'InBytes': 0}, + 'DropPercentage': 50.0, + 'RxThroughput': 1000000.0, + 'TxThroughput': 2000000.0, + 'RxThroughputBps': 64000000.0, + 'TxThroughputBps': 128000000.0, + 'Rate': 100.0, + 'Iteration': 1, + 'PktSize': '64B', + 'Status': 'Failure'} + self.assertEqual(expected, output) + self.assertFalse(completed) + + +class PortPgIDMapTestCase(base.BaseUnitTestCase): + + def test_add_port(self): + port_pg_id_map = rfc2544.PortPgIDMap() + port_pg_id_map.add_port(10) + self.assertEqual(10, port_pg_id_map._last_port) + self.assertEqual([], port_pg_id_map._port_pg_id_map[10]) + + def test_get_pg_ids(self): + port_pg_id_map = rfc2544.PortPgIDMap() + port_pg_id_map.add_port(10) + port_pg_id_map.increase_pg_id() + port_pg_id_map.increase_pg_id() + port_pg_id_map.add_port(20) + port_pg_id_map.increase_pg_id() + self.assertEqual([1, 2], port_pg_id_map.get_pg_ids(10)) + self.assertEqual([3], port_pg_id_map.get_pg_ids(20)) + self.assertEqual([], port_pg_id_map.get_pg_ids(30)) + + def test_increase_pg_id_no_port(self): + port_pg_id_map = rfc2544.PortPgIDMap() + self.assertIsNone(port_pg_id_map.increase_pg_id()) + + def test_increase_pg_id_last_port(self): + port_pg_id_map = rfc2544.PortPgIDMap() + port_pg_id_map.add_port(10) + self.assertEqual(1, port_pg_id_map.increase_pg_id()) + self.assertEqual([1], port_pg_id_map.get_pg_ids(10)) + self.assertEqual(10, port_pg_id_map._last_port) + + def test_increase_pg_id(self): + port_pg_id_map = rfc2544.PortPgIDMap() + port_pg_id_map.add_port(10) + port_pg_id_map.increase_pg_id() + self.assertEqual(2, port_pg_id_map.increase_pg_id(port=20)) + self.assertEqual([1], port_pg_id_map.get_pg_ids(10)) + self.assertEqual([2], port_pg_id_map.get_pg_ids(20)) + self.assertEqual(20, port_pg_id_map._last_port) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_sip.py b/yardstick/tests/unit/network_services/traffic_profile/test_sip.py new file mode 100644 index 000000000..bf26ee44d --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_sip.py @@ -0,0 +1,51 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import mock + +from yardstick.network_services.traffic_profile import sip + + +class TestSipProfile(unittest.TestCase): + + TRAFFIC_PROFILE = { + "schema": "nsb:traffic_profile:0.1", + "name": "sip", + "description": "Traffic profile to run sip", + "traffic_profile": { + "traffic_type": "SipProfile", + "frame_rate": 100, # pps + "duration": 10, + "enable_latency": False}} + + def setUp(self): + self.sip_profile = sip.SipProfile(self.TRAFFIC_PROFILE) + + def test___init__(self): + self.assertIsNone(self.sip_profile.generator) + + def test_execute_traffic(self): + self.sip_profile.generator = None + mock_traffic_generator = mock.Mock() + self.sip_profile.execute_traffic(mock_traffic_generator) + self.assertIsNotNone(self.sip_profile.generator) + + def test_is_ended_true(self): + self.sip_profile.generator = mock.Mock(return_value=True) + self.assertTrue(self.sip_profile.is_ended()) + + def test_is_ended_false(self): + self.sip_profile.generator = None + self.assertFalse(self.sip_profile.is_ended()) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_trex_traffic_profile.py b/yardstick/tests/unit/network_services/traffic_profile/test_trex_traffic_profile.py new file mode 100644 index 000000000..628e85459 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_trex_traffic_profile.py @@ -0,0 +1,277 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import ipaddress + +import six +import unittest + +from yardstick.common import exceptions as y_exc +from yardstick.network_services.traffic_profile import base as tp_base +from yardstick.network_services.traffic_profile import trex_traffic_profile + + +class TestTrexProfile(unittest.TestCase): + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64}} + + EXAMPLE_ETHERNET_ADDR = "00:00:00:00:00:01" + EXAMPLE_IP_ADDR = "10.0.0.1" + EXAMPLE_IPv6_ADDR = "0064:ff9b:0:0:0:0:9810:6414" + + PROFILE = { + 'description': 'Traffic profile to run RFC2544 latency', + 'name': 'rfc2544', + 'traffic_profile': {'traffic_type': 'RFC2544Profile', + 'frame_rate': 100}, + tp_base.TrafficProfile.DOWNLINK: { + 'ipv4': {'outer_l2': {'framesize': {'64B': '100', + '1518B': '0', + '128B': '0', + '1400B': '0', + '256B': '0', + '373b': '0', + '570B': '0'}, + "srcmac": "00:00:00:00:00:02", + "dstmac": "00:00:00:00:00:01"}, + 'outer_l3v4': {'dstip4': '1.1.1.1-1.1.2.2', + 'proto': 'udp', + 'srcip4': '9.9.1.1-90.1.2.2', + 'dscp': 0, 'ttl': 32, + 'count': 1}, + 'outer_l4': {'srcport': '2001', + 'dsrport': '1234', + 'count': 1}}}, + tp_base.TrafficProfile.UPLINK: { + 'ipv4': + {'outer_l2': {'framesize': + {'64B': '100', '1518B': '0', + '128B': '0', '1400B': '0', + '256B': '0', '373b': '0', + '570B': '0'}, + "srcmac": "00:00:00:00:00:01", + "dstmac": "00:00:00:00:00:02"}, + 'outer_l3v4': {'dstip4': '9.9.1.1-90.105.255.255', + 'proto': 'udp', + 'srcip4': '1.1.1.1-1.15.255.255', + 'dscp': 0, 'ttl': 32, 'count': 1}, + 'outer_l4': {'dstport': '2001', + 'srcport': '1234', + 'count': 1}}}, + 'schema': 'isb:traffic_profile:0.1'} + PROFILE_v6 = { + 'description': 'Traffic profile to run RFC2544 latency', + 'name': 'rfc2544', + 'traffic_profile': {'traffic_type': 'RFC2544Profile', + 'frame_rate': 100}, + tp_base.TrafficProfile.DOWNLINK: { + 'ipv6': {'outer_l2': {'framesize': + {'64B': '100', '1518B': '0', + '128B': '0', '1400B': '0', + '256B': '0', '373b': '0', + '570B': '0'}, + "srcmac": "00:00:00:00:00:02", + "dstmac": "00:00:00:00:00:01"}, + 'outer_l3v4': { + 'dstip6': + '0064:ff9b:0:0:0:0:9810:6414-0064:ff9b:0:0:0:0:9810:6420', + 'proto': 'udp', + 'srcip6': + '0064:ff9b:0:0:0:0:9810:2814-0064:ff9b:0:0:0:0:9810:2820', + 'dscp': 0, 'ttl': 32, + 'count': 1}, + 'outer_l4': {'srcport': '2001', + 'dsrport': '1234', + 'count': 1}}}, + tp_base.TrafficProfile.UPLINK: { + 'ipv6': {'outer_l2': {'framesize': + {'64B': '100', '1518B': '0', + '128B': '0', '1400B': '0', + '256B': '0', '373b': '0', + '570B': '0'}, + "srcmac": "00:00:00:00:00:01", + "dstmac": "00:00:00:00:00:02"}, + 'outer_l3v4': { + 'dstip6': + '0064:ff9b:0:0:0:0:9810:2814-0064:ff9b:0:0:0:0:9810:2820', + 'proto': 'udp', + 'srcip6': + '0064:ff9b:0:0:0:0:9810:6414-0064:ff9b:0:0:0:0:9810:6420', + 'dscp': 0, 'ttl': 32, + 'count': 1}, + 'outer_l4': {'dstport': '2001', + 'srcport': '1234', + 'count': 1}}}, + 'schema': 'isb:traffic_profile:0.1'} + + def test___init__(self): + trex_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + self.assertEqual(trex_profile.pps, 100) + + def test_qinq(self): + trex_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + qinq = {"S-VLAN": {"id": 128, "priority": 0, "cfi": 0}, + "C-VLAN": {"id": 512, "priority": 0, "cfi": 0}} + + trex_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + self.assertIsNone(trex_profile.set_qinq(qinq)) + + qinq = {"S-VLAN": {"id": "128-130", "priority": 0, "cfi": 0}, + "C-VLAN": {"id": "512-515", "priority": 0, "cfi": 0}} + self.assertIsNone(trex_profile.set_qinq(qinq)) + + def test__set_outer_l2_fields(self): + trex_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + qinq = {"S-VLAN": {"id": 128, "priority": 0, "cfi": 0}, + "C-VLAN": {"id": 512, "priority": 0, "cfi": 0}} + outer_l2 = self.PROFILE[ + tp_base.TrafficProfile.UPLINK]['ipv4']['outer_l2'] + outer_l2['QinQ'] = qinq + self.assertIsNone(trex_profile._set_outer_l2_fields(outer_l2)) + + def test__set_outer_l3v4_fields(self): + trex_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + outer_l3v4 = self.PROFILE[ + tp_base.TrafficProfile.UPLINK]['ipv4']['outer_l3v4'] + outer_l3v4['proto'] = 'tcp' + self.assertIsNone(trex_profile._set_outer_l3v4_fields(outer_l3v4)) + + def test__set_outer_l3v6_fields(self): + trex_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + outer_l3v6 = self.PROFILE_v6[ + tp_base.TrafficProfile.UPLINK]['ipv6']['outer_l3v4'] + outer_l3v6['proto'] = 'tcp' + outer_l3v6['tc'] = 1 + outer_l3v6['hlim'] = 10 + self.assertIsNone(trex_profile._set_outer_l3v6_fields(outer_l3v6)) + + def test__set_outer_l4_fields(self): + trex_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + outer_l4 = self.PROFILE[ + tp_base.TrafficProfile.UPLINK]['ipv4']['outer_l4'] + self.assertIsNone(trex_profile._set_outer_l4_fields(outer_l4)) + + def test__count_ip_ipv4(self): + start, end, count = trex_traffic_profile.TrexProfile._count_ip( + '1.1.1.1', '1.2.3.4') + self.assertEqual('1.1.1.1', str(start)) + self.assertEqual('1.2.3.4', str(end)) + diff = (int(ipaddress.IPv4Address(six.u('1.2.3.4'))) - + int(ipaddress.IPv4Address(six.u('1.1.1.1')))) + self.assertEqual(diff, count) + + def test__count_ip_ipv6(self): + start_ip = '0064:ff9b:0:0:0:0:9810:6414' + end_ip = '0064:ff9b:0:0:0:0:9810:6420' + start, end, count = trex_traffic_profile.TrexProfile._count_ip( + start_ip, end_ip) + self.assertEqual(0x98106414, start) + self.assertEqual(0x98106420, end) + self.assertEqual(0x98106420 - 0x98106414, count) + + def test__count_ip_ipv6_exception(self): + start_ip = '0064:ff9b:0:0:0:0:9810:6420' + end_ip = '0064:ff9b:0:0:0:0:9810:6414' + with self.assertRaises(y_exc.IPv6RangeError): + trex_traffic_profile.TrexProfile._count_ip(start_ip, end_ip) + + def test__dscp_range_action_partial_actual_count_zero(self): + traffic_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + dscp_partial = traffic_profile._dscp_range_action_partial() + + flow_vars_initial_length = len(traffic_profile.vm_flow_vars) + dscp_partial('1', '1', 'unneeded') + self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) + + def test__dscp_range_action_partial_count_greater_than_actual(self): + traffic_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + dscp_partial = traffic_profile._dscp_range_action_partial() + + flow_vars_initial_length = len(traffic_profile.vm_flow_vars) + dscp_partial('1', '10', '100') + self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) + + def test__udp_range_action_partial_actual_count_zero(self): + traffic_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + traffic_profile.udp['field1'] = 'value1' + udp_partial = traffic_profile._udp_range_action_partial('field1') + + flow_vars_initial_length = len(traffic_profile.vm_flow_vars) + udp_partial('1', '1', 'unneeded') + self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) + + def test__udp_range_action_partial_count_greater_than_actual(self): + traffic_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + traffic_profile.udp['field1'] = 'value1' + udp_partial = traffic_profile._udp_range_action_partial( + 'field1', 'not_used_count') + flow_vars_initial_length = len(traffic_profile.vm_flow_vars) + udp_partial('1', '10', '100') + self.assertEqual(len(traffic_profile.vm_flow_vars), flow_vars_initial_length + 2) + + def test__general_single_action_partial(self): + trex_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + trex_profile._general_single_action_partial( + trex_traffic_profile.ETHERNET)(trex_traffic_profile.SRC)( + self.EXAMPLE_ETHERNET_ADDR) + self.assertEqual(self.EXAMPLE_ETHERNET_ADDR, + trex_profile.ether_packet.src) + + trex_profile._general_single_action_partial(trex_traffic_profile.IP)( + trex_traffic_profile.DST)(self.EXAMPLE_IP_ADDR) + self.assertEqual(self.EXAMPLE_IP_ADDR, trex_profile.ip_packet.dst) + + trex_profile._general_single_action_partial(trex_traffic_profile.IPv6)( + trex_traffic_profile.DST)(self.EXAMPLE_IPv6_ADDR) + self.assertEqual(self.EXAMPLE_IPv6_ADDR, trex_profile.ip6_packet.dst) + + trex_profile._general_single_action_partial(trex_traffic_profile.UDP)( + trex_traffic_profile.SRC_PORT)(5060) + self.assertEqual(5060, trex_profile.udp_packet.sport) + + trex_profile._general_single_action_partial(trex_traffic_profile.IP)( + trex_traffic_profile.TYPE_OF_SERVICE)(0) + self.assertEqual(0, trex_profile.ip_packet.tos) + + def test__set_proto_addr(self): + trex_profile = trex_traffic_profile.TrexProfile(self.PROFILE) + + ether_range = "00:00:00:00:00:01-00:00:00:00:00:02" + ip_range = "1.1.1.2-1.1.1.10" + ipv6_range = '0064:ff9b:0:0:0:0:9810:6414-0064:ff9b:0:0:0:0:9810:6420' + + trex_profile._set_proto_addr(trex_traffic_profile.ETHERNET, + trex_traffic_profile.SRC, ether_range) + trex_profile._set_proto_addr(trex_traffic_profile.ETHERNET, + trex_traffic_profile.DST, ether_range) + trex_profile._set_proto_addr(trex_traffic_profile.IP, + trex_traffic_profile.SRC, ip_range) + trex_profile._set_proto_addr(trex_traffic_profile.IP, + trex_traffic_profile.DST, ip_range) + trex_profile._set_proto_addr(trex_traffic_profile.IPv6, + trex_traffic_profile.SRC, ipv6_range) + trex_profile._set_proto_addr(trex_traffic_profile.IPv6, + trex_traffic_profile.DST, ipv6_range) + trex_profile._set_proto_addr(trex_traffic_profile.UDP, + trex_traffic_profile.SRC_PORT, + '5060-5090') + trex_profile._set_proto_addr(trex_traffic_profile.UDP, + trex_traffic_profile.DST_PORT, '5060') diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py b/yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py new file mode 100644 index 000000000..8ad17b547 --- /dev/null +++ b/yardstick/tests/unit/network_services/traffic_profile/test_vpp_rfc2544.py @@ -0,0 +1,890 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +from trex_stl_lib import trex_stl_client +from trex_stl_lib import trex_stl_packet_builder_scapy +from trex_stl_lib import trex_stl_streams + +from yardstick.common import constants +from yardstick.network_services.helpers.vpp_helpers.multiple_loss_ratio_search import \ + MultipleLossRatioSearch +from yardstick.network_services.helpers.vpp_helpers.ndr_pdr_result import \ + NdrPdrResult +from yardstick.network_services.helpers.vpp_helpers.receive_rate_interval import \ + ReceiveRateInterval +from yardstick.network_services.helpers.vpp_helpers.receive_rate_measurement import \ + ReceiveRateMeasurement +from yardstick.network_services.traffic_profile import base as tp_base +from yardstick.network_services.traffic_profile import rfc2544, vpp_rfc2544 +from yardstick.network_services.traffic_profile.rfc2544 import PortPgIDMap +from yardstick.tests.unit import base + + +class TestVppRFC2544Profile(base.BaseUnitTestCase): + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "duration": 30, + "enable_latency": True, + "frame_rate": 100, + "intermediate_phases": 2, + "lower_bound": 1.0, + "step_interval": 0.5, + "test_precision": 0.1, + "upper_bound": 100.0}} + + TRAFFIC_PROFILE_MAX_RATE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "duration": 30, + "enable_latency": True, + "frame_rate": 10000, + "intermediate_phases": 2, + "lower_bound": 1.0, + "step_interval": 0.5, + "test_precision": 0.1, + "upper_bound": 100.0}} + + PROFILE = { + "description": "Traffic profile to run RFC2544 latency", + "downlink_0": { + "ipv4": { + "id": 2, + "outer_l2": { + "framesize": { + "1024B": "0", + "1280B": "0", + "128B": "0", + "1400B": "0", + "1500B": "0", + "1518B": "0", + "256B": "0", + "373b": "0", + "512B": "0", + "570B": "0", + "64B": "100" + } + }, + "outer_l3v4": { + "count": "1", + "dstip4": "10.0.0.0-10.0.0.100", + "proto": 61, + "srcip4": "20.0.0.0-20.0.0.100" + } + } + }, + "name": "rfc2544", + "schema": "nsb:traffic_profile:0.1", + "traffic_profile": { + "duration": 30, + "enable_latency": True, + "frame_rate": 100, + "intermediate_phases": 2, + "lower_bound": 1.0, + "step_interval": 0.5, + "test_precision": 0.1, + "traffic_type": "VppRFC2544Profile", + "upper_bound": 100.0 + }, + "uplink": { + "ipv4": { + "id": 1, + "outer_l2": { + "framesize": { + "1024B": "0", + "1280B": "0", + "128B": "0", + "1400B": "0", + "1500B": "0", + "1518B": "0", + "256B": "0", + "373B": "0", + "512B": "0", + "570B": "0", + "64B": "100" + } + }, + "outer_l3v4": { + "count": "10", + "dstip4": "20.0.0.0-20.0.0.100", + "proto": 61, + "srcip4": "10.0.0.0-10.0.0.100" + } + } + } + } + + def test___init__(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + self.assertEqual(vpp_rfc2544_profile.max_rate, + vpp_rfc2544_profile.rate) + self.assertEqual(0, vpp_rfc2544_profile.min_rate) + self.assertEqual(2, vpp_rfc2544_profile.number_of_intermediate_phases) + self.assertEqual(30, vpp_rfc2544_profile.duration) + self.assertEqual(0.1, vpp_rfc2544_profile.precision) + self.assertEqual(1.0, vpp_rfc2544_profile.lower_bound) + self.assertEqual(100.0, vpp_rfc2544_profile.upper_bound) + self.assertEqual(0.5, vpp_rfc2544_profile.step_interval) + self.assertEqual(True, vpp_rfc2544_profile.enable_latency) + + def test_init_traffic_params(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + mock_generator = mock.MagicMock() + mock_generator.rfc2544_helper.latency = True + mock_generator.rfc2544_helper.tolerance_low = 0.0 + mock_generator.rfc2544_helper.tolerance_high = 0.005 + mock_generator.scenario_helper.all_options = { + "vpp_config": { + "max_rate": 14880000 + } + } + vpp_rfc2544_profile.init_traffic_params(mock_generator) + self.assertEqual(0.0, vpp_rfc2544_profile.tolerance_low) + self.assertEqual(0.005, vpp_rfc2544_profile.tolerance_high) + self.assertEqual(14880000, vpp_rfc2544_profile.max_rate) + self.assertEqual(True, vpp_rfc2544_profile.enable_latency) + + def test_calculate_frame_size(self): + imix = {'40B': 7, '576B': 4, '1500B': 1} + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + self.assertEqual((4084 / 12, 12), + vpp_rfc2544_profile.calculate_frame_size(imix)) + + def test_calculate_frame_size_empty(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + self.assertEqual((64, 100), + vpp_rfc2544_profile.calculate_frame_size(None)) + + def test_calculate_frame_size_error(self): + imix = {'40B': -7, '576B': 4, '1500B': 1} + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + self.assertEqual((64, 100), + vpp_rfc2544_profile.calculate_frame_size(imix)) + + def test__gen_payload(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + self.assertIsNotNone(vpp_rfc2544_profile._gen_payload(4)) + + def test_register_generator(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + mock_generator = mock.MagicMock() + mock_generator.rfc2544_helper.latency = True + mock_generator.rfc2544_helper.tolerance_low = 0.0 + mock_generator.rfc2544_helper.tolerance_high = 0.005 + mock_generator.scenario_helper.all_options = { + "vpp_config": { + "max_rate": 14880000 + } + } + vpp_rfc2544_profile.register_generator(mock_generator) + self.assertEqual(0.0, vpp_rfc2544_profile.tolerance_low) + self.assertEqual(0.005, vpp_rfc2544_profile.tolerance_high) + self.assertEqual(14880000, vpp_rfc2544_profile.max_rate) + self.assertEqual(True, vpp_rfc2544_profile.enable_latency) + + def test_stop_traffic(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + mock_generator = mock.Mock() + vpp_rfc2544_profile.stop_traffic(traffic_generator=mock_generator) + mock_generator.client.stop.assert_called_once() + mock_generator.client.reset.assert_called_once() + mock_generator.client.remove_all_streams.assert_called_once() + + def test_execute_traffic(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + vpp_rfc2544_profile.init_queue(mock.MagicMock()) + vpp_rfc2544_profile.params = { + 'downlink_0': 'profile1', + 'uplink_0': 'profile2'} + mock_generator = mock.MagicMock() + mock_generator.networks = { + 'downlink_0': ['xe0', 'xe1'], + 'uplink_0': ['xe2', 'xe3'], + 'uplink_1': ['xe2', 'xe3']} + mock_generator.port_num.side_effect = [10, 20, 30, 40] + mock_generator.rfc2544_helper.correlated_traffic = False + + with mock.patch.object(vpp_rfc2544_profile, 'create_profile') as \ + mock_create_profile: + vpp_rfc2544_profile.execute_traffic( + traffic_generator=mock_generator) + mock_create_profile.assert_has_calls([ + mock.call('profile1', 10), + mock.call('profile1', 20), + mock.call('profile2', 30), + mock.call('profile2', 40)]) + mock_generator.client.add_streams.assert_has_calls([ + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[30]), + mock.call(mock.ANY, ports=[40])]) + + def test_execute_traffic_correlated_traffic(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + vpp_rfc2544_profile.init_queue(mock.MagicMock()) + vpp_rfc2544_profile.params = { + 'downlink_0': 'profile1', + 'uplink_0': 'profile2'} + mock_generator = mock.MagicMock() + mock_generator.networks = { + 'downlink_0': ['xe0', 'xe1'], + 'uplink_0': ['xe2', 'xe3']} + mock_generator.port_num.side_effect = [10, 20, 30, 40] + mock_generator.rfc2544_helper.correlated_traffic = True + + with mock.patch.object(vpp_rfc2544_profile, 'create_profile') as \ + mock_create_profile: + vpp_rfc2544_profile.execute_traffic( + traffic_generator=mock_generator) + mock_create_profile.assert_has_calls([ + mock.call('profile2', 10), + mock.call('profile2', 20)]) + mock_generator.client.add_streams.assert_has_calls([ + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20])]) + + def test_execute_traffic_max_rate(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE_MAX_RATE) + vpp_rfc2544_profile.init_queue(mock.MagicMock()) + vpp_rfc2544_profile.pkt_size = 64 + vpp_rfc2544_profile.params = { + 'downlink_0': 'profile1', + 'uplink_0': 'profile2'} + mock_generator = mock.MagicMock() + mock_generator.networks = { + 'downlink_0': ['xe0', 'xe1'], + 'uplink_0': ['xe2', 'xe3']} + mock_generator.port_num.side_effect = [10, 20, 30, 40] + mock_generator.rfc2544_helper.correlated_traffic = False + + with mock.patch.object(vpp_rfc2544_profile, 'create_profile') as \ + mock_create_profile: + vpp_rfc2544_profile.execute_traffic( + traffic_generator=mock_generator) + mock_create_profile.assert_has_calls([ + mock.call('profile1', 10), + mock.call('profile1', 20), + mock.call('profile2', 30), + mock.call('profile2', 40)]) + mock_generator.client.add_streams.assert_has_calls([ + mock.call(mock.ANY, ports=[10]), + mock.call(mock.ANY, ports=[20]), + mock.call(mock.ANY, ports=[30]), + mock.call(mock.ANY, ports=[40])]) + + @mock.patch.object(trex_stl_streams, 'STLProfile') + def test_create_profile(self, mock_stl_profile): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + port = mock.ANY + profile_data = {'packetid_1': {'outer_l2': {'framesize': 'imix_info'}}} + with mock.patch.object(vpp_rfc2544_profile, 'calculate_frame_size') as \ + mock_calculate_frame_size, \ + mock.patch.object(vpp_rfc2544_profile, '_create_imix_data') as \ + mock_create_imix, \ + mock.patch.object(vpp_rfc2544_profile, '_create_vm') as \ + mock_create_vm, \ + mock.patch.object(vpp_rfc2544_profile, + '_create_single_stream') as \ + mock_create_single_stream: + mock_calculate_frame_size.return_value = 64, 100 + mock_create_imix.return_value = 'imix_data' + mock_create_single_stream.return_value = ['stream1'] + vpp_rfc2544_profile.create_profile(profile_data, port) + + mock_create_imix.assert_called_once_with('imix_info') + mock_create_vm.assert_called_once_with( + {'outer_l2': {'framesize': 'imix_info'}}) + mock_create_single_stream.assert_called_once_with(port, 'imix_data', + 100) + mock_stl_profile.assert_called_once_with(['stream1']) + + @mock.patch.object(trex_stl_streams, 'STLProfile') + def test_create_profile_max_rate(self, mock_stl_profile): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE_MAX_RATE) + port = mock.ANY + profile_data = {'packetid_1': {'outer_l2': {'framesize': 'imix_info'}}} + with mock.patch.object(vpp_rfc2544_profile, 'calculate_frame_size') as \ + mock_calculate_frame_size, \ + mock.patch.object(vpp_rfc2544_profile, '_create_imix_data') as \ + mock_create_imix, \ + mock.patch.object(vpp_rfc2544_profile, '_create_vm') as \ + mock_create_vm, \ + mock.patch.object(vpp_rfc2544_profile, + '_create_single_stream') as \ + mock_create_single_stream: + mock_calculate_frame_size.return_value = 64, 100 + mock_create_imix.return_value = 'imix_data' + mock_create_single_stream.return_value = ['stream1'] + vpp_rfc2544_profile.create_profile(profile_data, port) + + mock_create_imix.assert_called_once_with('imix_info', 'mode_DIP') + mock_create_vm.assert_called_once_with( + {'outer_l2': {'framesize': 'imix_info'}}) + mock_create_single_stream.assert_called_once_with(port, 'imix_data', + 100) + mock_stl_profile.assert_called_once_with(['stream1']) + + def test__create_imix_data_mode_DIP(self): + rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(self.TRAFFIC_PROFILE) + data = {'64B': 50, '128B': 50} + self.assertEqual( + {'64': 50.0, '128': 50.0}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_PACKETS)) + data = {'64B': 1, '128b': 3} + self.assertEqual( + {'64': 25.0, '128': 75.0}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_PACKETS)) + data = {} + self.assertEqual( + {}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_PACKETS)) + + def test__create_imix_data_mode_DIB(self): + rfc2544_profile = vpp_rfc2544.VppRFC2544Profile(self.TRAFFIC_PROFILE) + data = {'64B': 25, '128B': 25, '512B': 25, '1518B': 25} + byte_total = 64 * 25 + 128 * 25 + 512 * 25 + 1518 * 25 + self.assertEqual( + {'64': 64 * 25.0 * 100 / byte_total, + '128': 128 * 25.0 * 100 / byte_total, + '512': 512 * 25.0 * 100 / byte_total, + '1518': 1518 * 25.0 * 100 / byte_total}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_BYTES)) + data = {} + self.assertEqual( + {}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_BYTES)) + data = {'64B': 100} + self.assertEqual( + {'64': 100.0}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_BYTES)) + + def test__create_vm(self): + packet = {'outer_l2': 'l2_definition'} + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + with mock.patch.object(vpp_rfc2544_profile, '_set_outer_l2_fields') as \ + mock_l2_fileds: + vpp_rfc2544_profile._create_vm(packet) + mock_l2_fileds.assert_called_once_with('l2_definition') + + @mock.patch.object(trex_stl_packet_builder_scapy, 'STLPktBuilder', + return_value='packet') + def test__create_single_packet(self, mock_pktbuilder): + size = 128 + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + vpp_rfc2544_profile.ether_packet = mock.MagicMock() + vpp_rfc2544_profile.ip_packet = mock.MagicMock() + vpp_rfc2544_profile.udp_packet = mock.MagicMock() + vpp_rfc2544_profile.trex_vm = 'trex_vm' + # base_pkt = ( + # vpp_rfc2544_profile.ether_packet / vpp_rfc2544_profile.ip_packet / + # vpp_rfc2544_profile.udp_packet) + # pad = (size - len(base_pkt)) * 'x' + output = vpp_rfc2544_profile._create_single_packet(size=size) + self.assertEqual(mock_pktbuilder.call_count, 2) + # mock_pktbuilder.assert_called_once_with(pkt=base_pkt / pad, + # vm='trex_vm') + self.assertEqual(output, ('packet', 'packet')) + + def test__set_outer_l3v4_fields(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + outer_l3v4 = self.PROFILE[ + tp_base.TrafficProfile.UPLINK]['ipv4']['outer_l3v4'] + outer_l3v4['proto'] = 'tcp' + self.assertIsNone( + vpp_rfc2544_profile._set_outer_l3v4_fields(outer_l3v4)) + + @mock.patch.object(trex_stl_streams, 'STLFlowLatencyStats') + @mock.patch.object(trex_stl_streams, 'STLTXCont') + @mock.patch.object(trex_stl_client, 'STLStream') + def test__create_single_stream(self, mock_stream, mock_txcont, + mock_latency): + imix_data = {'64': 25, '512': 75} + mock_stream.side_effect = ['stream1', 'stream2', 'stream3', 'stream4'] + mock_txcont.side_effect = ['txcont1', 'txcont2', 'txcont3', 'txcont4'] + mock_latency.side_effect = ['latency1', 'latency2'] + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + vpp_rfc2544_profile.port_pg_id = rfc2544.PortPgIDMap() + vpp_rfc2544_profile.port_pg_id.add_port(10) + with mock.patch.object(vpp_rfc2544_profile, '_create_single_packet') as \ + mock_create_single_packet: + mock_create_single_packet.return_value = 64, 100 + output = vpp_rfc2544_profile._create_single_stream(10, imix_data, + 100, 0.0) + self.assertEqual(['stream1', 'stream2', 'stream3', 'stream4'], output) + mock_latency.assert_has_calls([ + mock.call(pg_id=1), mock.call(pg_id=2)]) + mock_txcont.assert_has_calls([ + mock.call(percentage=25 * 100 / 100), + mock.call(percentage=75 * 100 / 100)], any_order=True) + + @mock.patch.object(trex_stl_streams, 'STLFlowLatencyStats') + @mock.patch.object(trex_stl_streams, 'STLTXCont') + @mock.patch.object(trex_stl_client, 'STLStream') + def test__create_single_stream_max_rate(self, mock_stream, mock_txcont, + mock_latency): + imix_data = {'64': 25, '512': 75} + mock_stream.side_effect = ['stream1', 'stream2', 'stream3', 'stream4'] + mock_txcont.side_effect = ['txcont1', 'txcont2', 'txcont3', 'txcont4'] + mock_latency.side_effect = ['latency1', 'latency2'] + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE_MAX_RATE) + vpp_rfc2544_profile.pkt_size = 64 + vpp_rfc2544_profile.port_pg_id = rfc2544.PortPgIDMap() + vpp_rfc2544_profile.port_pg_id.add_port(1) + with mock.patch.object(vpp_rfc2544_profile, '_create_single_packet') as \ + mock_create_single_packet: + mock_create_single_packet.return_value = 64, 100 + output = vpp_rfc2544_profile._create_single_stream(1, imix_data, + 100, 0.0) + self.assertEqual(['stream1', 'stream2', 'stream3', 'stream4'], output) + mock_latency.assert_has_calls([ + mock.call(pg_id=1), mock.call(pg_id=2)]) + mock_txcont.assert_has_calls([ + mock.call(pps=int(25 * 100 / 100)), + mock.call(pps=int(75 * 100 / 100))], any_order=True) + + @mock.patch.object(trex_stl_streams, 'STLFlowLatencyStats') + @mock.patch.object(trex_stl_streams, 'STLTXCont') + @mock.patch.object(trex_stl_client, 'STLStream') + def test__create_single_stream_mlr_search(self, mock_stream, mock_txcont, + mock_latency): + imix_data = {'64': 25, '512': 75} + mock_stream.side_effect = ['stream1', 'stream2', 'stream3', 'stream4'] + mock_txcont.side_effect = ['txcont1', 'txcont2', 'txcont3', 'txcont4'] + mock_latency.side_effect = ['latency1', 'latency2'] + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + vpp_rfc2544_profile.max_rate = 14880000 + vpp_rfc2544_profile.port_pg_id = rfc2544.PortPgIDMap() + vpp_rfc2544_profile.port_pg_id.add_port(10) + with mock.patch.object(vpp_rfc2544_profile, '_create_single_packet') as \ + mock_create_single_packet: + mock_create_single_packet.return_value = 64, 100 + output = vpp_rfc2544_profile._create_single_stream(10, imix_data, + 100, 0.0) + self.assertEqual(['stream1', 'stream2', 'stream3', 'stream4'], output) + mock_latency.assert_has_calls([ + mock.call(pg_id=1), mock.call(pg_id=2)]) + mock_txcont.assert_has_calls([ + mock.call(pps=25 * 100 / 100), + mock.call(pps=75 * 100 / 100)], any_order=True) + + def test_binary_search_with_optimized(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + vpp_rfc2544_profile.pkt_size = 64 + vpp_rfc2544_profile.init_queue(mock.MagicMock()) + mock_generator = mock.MagicMock() + mock_generator.vnfd_helper.interfaces = [ + {"name": "xe0"}, {"name": "xe0"} + ] + + vpp_rfc2544_profile.ports = [0, 1] + vpp_rfc2544_profile.port_pg_id = PortPgIDMap() + vpp_rfc2544_profile.port_pg_id.add_port(0) + vpp_rfc2544_profile.port_pg_id.add_port(1) + vpp_rfc2544_profile.profiles = mock.MagicMock() + vpp_rfc2544_profile.test_data = mock.MagicMock() + vpp_rfc2544_profile.queue = mock.MagicMock() + + with mock.patch.object(MultipleLossRatioSearch, 'measure') as \ + mock_measure, \ + mock.patch.object(MultipleLossRatioSearch, 'ndrpdr') as \ + mock_ndrpdr: + measured_low = ReceiveRateMeasurement(1, 14880000, 14879927, 0) + measured_high = ReceiveRateMeasurement(1, 14880000, 14879927, 0) + measured_low.latency = ['1000/3081/3962', '500/3149/3730'] + measured_high.latency = ['1000/3081/3962', '500/3149/3730'] + starting_interval = ReceiveRateInterval(measured_low, + measured_high) + starting_result = NdrPdrResult(starting_interval, + starting_interval) + mock_measure.return_value = ReceiveRateMeasurement(1, 14880000, + 14879927, 0) + mock_ndrpdr.return_value = MultipleLossRatioSearch.ProgressState( + starting_result, 2, 30, 0.005, 0.0, + 4857361, 4977343) + + result_samples = vpp_rfc2544_profile.binary_search_with_optimized( + traffic_generator=mock_generator, duration=30, + timeout=720, + test_data={}) + + expected = {'Result_NDR_LOWER': {'bandwidth_total_Gbps': 9.999310944, + 'rate_total_pps': 14879927.0}, + 'Result_NDR_UPPER': {'bandwidth_total_Gbps': 9.999310944, + 'rate_total_pps': 14879927.0}, + 'Result_NDR_packets_lost': {'packet_loss_ratio': 0.0, + 'packets_lost': 0.0}, + 'Result_PDR_LOWER': {'bandwidth_total_Gbps': 9.999310944, + 'rate_total_pps': 14879927.0}, + 'Result_PDR_UPPER': {'bandwidth_total_Gbps': 9.999310944, + 'rate_total_pps': 14879927.0}, + 'Result_PDR_packets_lost': {'packet_loss_ratio': 0.0, + 'packets_lost': 0.0}, + 'Result_stream0_NDR_LOWER': {'avg_latency': 3081.0, + 'max_latency': 3962.0, + 'min_latency': 1000.0}, + 'Result_stream0_PDR_LOWER': {'avg_latency': 3081.0, + 'max_latency': 3962.0, + 'min_latency': 1000.0}, + 'Result_stream1_NDR_LOWER': {'avg_latency': 3149.0, + 'max_latency': 3730.0, + 'min_latency': 500.0}, + 'Result_stream1_PDR_LOWER': {'avg_latency': 3149.0, + 'max_latency': 3730.0, + 'min_latency': 500.0}} + self.assertEqual(expected, result_samples) + + def test_binary_search(self): + vpp_rfc2544_profile = vpp_rfc2544.VppRFC2544Profile( + self.TRAFFIC_PROFILE) + vpp_rfc2544_profile.pkt_size = 64 + vpp_rfc2544_profile.init_queue(mock.MagicMock()) + mock_generator = mock.MagicMock() + mock_generator.vnfd_helper.interfaces = [ + {"name": "xe0"}, {"name": "xe1"} + ] + stats = { + "0": { + "ibytes": 55549120, + "ierrors": 0, + "ipackets": 867955, + "obytes": 55549696, + "oerrors": 0, + "opackets": 867964, + "rx_bps": 104339032.0, + "rx_bps_L1": 136944984.0, + "rx_pps": 203787.2, + "rx_util": 1.36944984, + "tx_bps": 134126008.0, + "tx_bps_L1": 176040392.0, + "tx_pps": 261964.9, + "tx_util": 1.7604039200000001 + }, + "1": { + "ibytes": 55549696, + "ierrors": 0, + "ipackets": 867964, + "obytes": 55549120, + "oerrors": 0, + "opackets": 867955, + "rx_bps": 134119648.0, + "rx_bps_L1": 176032032.0, + "rx_pps": 261952.4, + "rx_util": 1.76032032, + "tx_bps": 104338192.0, + "tx_bps_L1": 136943872.0, + "tx_pps": 203785.5, + "tx_util": 1.36943872 + }, + "flow_stats": { + "1": { + "rx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "rx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "rx_bytes": { + "0": 6400, + "1": 0, + "total": 6400 + }, + "rx_pkts": { + "0": 100, + "1": 0, + "total": 100 + }, + "rx_pps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "tx_bytes": { + "0": 0, + "1": 6400, + "total": 6400 + }, + "tx_pkts": { + "0": 0, + "1": 100, + "total": 100 + }, + "tx_pps": { + "0": 0, + "1": 0, + "total": 0 + } + }, + "2": { + "rx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "rx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "rx_bytes": { + "0": 0, + "1": 6464, + "total": 6464 + }, + "rx_pkts": { + "0": 0, + "1": 101, + "total": 101 + }, + "rx_pps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "tx_bytes": { + "0": 6464, + "1": 0, + "total": 6464 + }, + "tx_pkts": { + "0": 101, + "1": 0, + "total": 101 + }, + "tx_pps": { + "0": 0, + "1": 0, + "total": 0 + } + }, + "global": { + "rx_err": { + "0": 0, + "1": 0 + }, + "tx_err": { + "0": 0, + "1": 0 + } + } + }, + "global": { + "bw_per_core": 45.6, + "cpu_util": 0.1494, + "queue_full": 0, + "rx_bps": 238458672.0, + "rx_cpu_util": 4.751e-05, + "rx_drop_bps": 0.0, + "rx_pps": 465739.6, + "tx_bps": 238464208.0, + "tx_pps": 465750.4 + }, + "latency": { + "1": { + "err_cntrs": { + "dropped": 0, + "dup": 0, + "out_of_order": 0, + "seq_too_high": 0, + "seq_too_low": 0 + }, + "latency": { + "average": 63.375, + "histogram": { + "20": 1, + "30": 18, + "40": 12, + "50": 10, + "60": 12, + "70": 11, + "80": 6, + "90": 10, + "100": 20 + }, + "jitter": 23, + "last_max": 122, + "total_max": 123, + "total_min": 20 + } + }, + "2": { + "err_cntrs": { + "dropped": 0, + "dup": 0, + "out_of_order": 0, + "seq_too_high": 0, + "seq_too_low": 0 + }, + "latency": { + "average": 74, + "histogram": { + "60": 20, + "70": 10, + "80": 3, + "90": 4, + "100": 64 + }, + "jitter": 6, + "last_max": 83, + "total_max": 135, + "total_min": 60 + } + }, + "global": { + "bad_hdr": 0, + "old_flow": 0 + } + }, + "total": { + "ibytes": 111098816, + "ierrors": 0, + "ipackets": 1735919, + "obytes": 111098816, + "oerrors": 0, + "opackets": 1735919, + "rx_bps": 238458680.0, + "rx_bps_L1": 312977016.0, + "rx_pps": 465739.6, + "rx_util": 3.1297701599999996, + "tx_bps": 238464200.0, + "tx_bps_L1": 312984264.0, + "tx_pps": 465750.4, + "tx_util": 3.12984264 + } + } + samples = { + "xe0": { + "in_packets": 867955, + "latency": { + "2": { + "avg_latency": 74.0, + "max_latency": 135.0, + "min_latency": 60.0 + } + }, + "out_packets": 867964, + "rx_throughput_bps": 104339032.0, + "rx_throughput_fps": 203787.2, + "tx_throughput_bps": 134126008.0, + "tx_throughput_fps": 261964.9 + }, + "xe1": { + "in_packets": 867964, + "latency": { + "1": { + "avg_latency": 63.375, + "max_latency": 123.0, + "min_latency": 20.0 + } + }, + "out_packets": 867955, + "rx_throughput_bps": 134119648.0, + "rx_throughput_fps": 261952.4, + "tx_throughput_bps": 104338192.0, + "tx_throughput_fps": 203785.5 + } + } + + mock_generator.loss = 0 + mock_generator.sent = 2169700 + mock_generator.send_traffic_on_tg = mock.Mock(return_value=stats) + mock_generator.generate_samples = mock.Mock(return_value=samples) + + result_samples = vpp_rfc2544_profile.binary_search( + traffic_generator=mock_generator, duration=30, + tolerance_value=0.005, + test_data={}) + + expected = {'Result_theor_max_throughput': 134126008.0, + 'xe0': {'Result_Actual_throughput': 104339032.0}, + 'xe1': {'Result_Actual_throughput': 134119648.0}} + self.assertEqual(expected, result_samples) diff --git a/yardstick/tests/unit/network_services/vnf_generic/__init__.py b/yardstick/tests/unit/network_services/vnf_generic/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/__init__.py diff --git a/yardstick/tests/unit/network_services/vnf_generic/test_vnfdgen.py b/yardstick/tests/unit/network_services/vnf_generic/test_vnfdgen.py new file mode 100644 index 000000000..55b1955bc --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/test_vnfdgen.py @@ -0,0 +1,277 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from six.moves import range +import unittest + +from yardstick.common.yaml_loader import yaml_load +from yardstick.network_services.vnf_generic import vnfdgen + + +UPLINK = "uplink" +DOWNLINK = "downlink" + +TREX_VNFD_TEMPLATE = """ +vnfd:vnfd-catalog: + vnfd: + - id: TrexTrafficGen # ISB class mapping + name: trexgen + short-name: trexgen + description: TRex stateless traffic generator for RFC2544 + mgmt-interface: + vdu-id: trexgen-baremetal + user: {{user}} # Value filled by vnfdgen + password: {{password}} # Value filled by vnfdgen + ip: {{ip}} # Value filled by vnfdgen + connection-point: + - name: xe0 + type: VPORT + - name: xe1 + type: VPORT + vdu: + - id: trexgen-baremetal + name: trexgen-baremetal + description: TRex stateless traffic generator for RFC2544 + external-interface: + - name: xe0 + virtual-interface: + type: PCI-PASSTHROUGH + vpci: '{{ interfaces.xe0.vpci}}' + local_ip: '{{ interfaces.xe0.local_ip }}' + dst_ip: '{{ interfaces.xe0.dst_ip }}' + local_mac: '{{ interfaces.xe0.local_mac }}' + dst_mac: '{{ interfaces.xe0.dst_mac }}' + bandwidth: 10 Gbps + vnfd-connection-point-ref: xe0 + - name: xe1 + virtual-interface: + type: PCI-PASSTHROUGH + vpci: '{{ interfaces.xe1.vpci }}' + local_ip: '{{ interfaces.xe1.local_ip }}' + dst_ip: '{{ interfaces.xe1.dst_ip }}' + local_mac: '{{ interfaces.xe1.local_mac }}' + dst_mac: '{{ interfaces.xe1.dst_mac }}' + bandwidth: 10 Gbps + vnfd-connection-point-ref: xe1 + routing_table: {{ routing_table }} + nd_route_tbl: {{ nd_route_tbl }} + + benchmark: + kpi: + - rx_throughput_fps + - tx_throughput_fps + - tx_throughput_mbps + - rx_throughput_mbps + - tx_throughput_pc_linerate + - rx_throughput_pc_linerate + - min_latency + - max_latency + - avg_latency +""" + +COMPLETE_TREX_VNFD = \ + {'vnfd:vnfd-catalog': + {'vnfd': + [{'benchmark': + {'kpi': + ['rx_throughput_fps', + 'tx_throughput_fps', + 'tx_throughput_mbps', + 'rx_throughput_mbps', + 'tx_throughput_pc_linerate', + 'rx_throughput_pc_linerate', + 'min_latency', + 'max_latency', + 'avg_latency']}, + 'connection-point': [{'name': 'xe0', + 'type': 'VPORT'}, + {'name': 'xe1', + 'type': 'VPORT'}], + 'description': 'TRex stateless traffic generator for RFC2544', + 'id': 'TrexTrafficGen', + 'mgmt-interface': {'ip': '1.1.1.1', + 'password': 'berta', + 'user': 'berta', + 'vdu-id': 'trexgen-baremetal'}, + 'name': 'trexgen', + 'short-name': 'trexgen', + 'vdu': [{'description': 'TRex stateless traffic generator for RFC2544', + 'external-interface': + [{'name': 'xe0', + 'virtual-interface': {'bandwidth': '10 Gbps', + 'dst_ip': '1.1.1.1', + 'dst_mac': '00:01:02:03:04:05', + 'local_ip': '1.1.1.2', + 'local_mac': '00:01:02:03:05:05', + 'type': 'PCI-PASSTHROUGH', + 'vpci': '0000:00:10.2'}, + 'vnfd-connection-point-ref': 'xe0'}, + {'name': 'xe1', + 'virtual-interface': {'bandwidth': '10 Gbps', + 'dst_ip': '2.1.1.1', + 'dst_mac': '00:01:02:03:04:06', + 'local_ip': '2.1.1.2', + 'local_mac': '00:01:02:03:05:06', + 'type': 'PCI-PASSTHROUGH', + 'vpci': '0000:00:10.1'}, + 'vnfd-connection-point-ref': 'xe1'}], + 'id': 'trexgen-baremetal', + 'nd_route_tbl': [{'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + 'netmask': '112', + 'network': '0064:ff9b:0:0:0:0:9810:6414'}, + {'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + 'netmask': '112', + 'network': '0064:ff9b:0:0:0:0:9810:2814'}], + 'routing_table': [{'gateway': '152.16.100.20', + 'if': 'xe0', + 'netmask': '255.255.255.0', + 'network': '152.16.100.20'}, + {'gateway': '152.16.40.20', + 'if': 'xe1', + 'netmask': '255.255.255.0', + 'network': '152.16.40.20'}], + 'name': 'trexgen-baremetal'}]}]}} + +NODE_CFG = {'ip': '1.1.1.1', + 'name': 'demeter', + 'password': 'berta', + 'role': 'TrafficGen', + 'user': 'berta', + 'interfaces': {'xe0': {'dpdk_port_num': 1, + 'dst_ip': '1.1.1.1', + 'dst_mac': '00:01:02:03:04:05', + 'local_ip': '1.1.1.2', + 'local_mac': '00:01:02:03:05:05', + 'vpci': '0000:00:10.2'}, + 'xe1': {'dpdk_port_num': 0, + 'dst_ip': '2.1.1.1', + 'dst_mac': '00:01:02:03:04:06', + 'local_ip': '2.1.1.2', + 'local_mac': '00:01:02:03:05:06', + 'vpci': '0000:00:10.1'}}, + 'nd_route_tbl': [{u'gateway': u'0064:ff9b:0:0:0:0:9810:6414', + u'if': u'xe0', + u'netmask': u'112', + u'network': u'0064:ff9b:0:0:0:0:9810:6414'}, + {u'gateway': u'0064:ff9b:0:0:0:0:9810:2814', + u'if': u'xe1', + u'netmask': u'112', + u'network': u'0064:ff9b:0:0:0:0:9810:2814'}], + 'routing_table': [{u'gateway': u'152.16.100.20', + u'if': u'xe0', + u'netmask': u'255.255.255.0', + u'network': u'152.16.100.20'}, + {u'gateway': u'152.16.40.20', + u'if': u'xe1', + u'netmask': u'255.255.255.0', + u'network': u'152.16.40.20'}], + } + + +# need to template, but can't use {} so use %s +TRAFFIC_PROFILE_TPL = """ +%(0)s: + - ipv4: + outer_l2: + framesize: + 64B: "{{ get(imix, '%(0)s.imix_small', 10) }}" + 128B: "{{ get(imix, '%(0)s.imix_128B', 10) }}" + 256B: "{{ get(imix, '%(0)s.imix_256B', 10) }}" + 373B: "{{ get(imix, '%(0)s.imix_373B', 10) }}" + 570B: "{{get(imix, '%(0)s.imix_570B', 10) }}" + 1400B: "{{get(imix, '%(0)s.imix_1400B', 10) }}" + 1518B: "{{get(imix, '%(0)s.imix_1500B', 40) }}" +""" % {"0": UPLINK} + +TRAFFIC_PROFILE = { + UPLINK: [{"ipv4": {"outer_l2": + {"framesize": {"64B": '10', "128B": '10', + "256B": '10', "373B": '10', + "570B": '10', "1400B": '10', + "1518B": '40'}}}}]} + + +class TestRender(unittest.TestCase): + + def test_render_none(self): + + tmpl = "{{ routing_table }}" + self.assertEqual(vnfdgen.render(tmpl, routing_table=None), u'~') + self.assertIsNone( + yaml_load(vnfdgen.render(tmpl, routing_table=None))) + + def test_render_unicode_dict(self): + + tmpl = "{{ routing_table }}" + self.assertEqual(yaml_load(vnfdgen.render( + tmpl, **NODE_CFG)), NODE_CFG["routing_table"]) + + +class TestVnfdGen(unittest.TestCase): + """ Class to verify VNFS testcases """ + + def test_generate_vnfd(self): + """ Function to verify vnfd generation based on template """ + self.maxDiff = None + generated_vnfd = vnfdgen.generate_vnfd(TREX_VNFD_TEMPLATE, NODE_CFG) + self.assertDictEqual(COMPLETE_TREX_VNFD, generated_vnfd) + + def test_generate_tp_no_vars(self): + """ Function to verify traffic profile generation without imix """ + + self.maxDiff = None + generated_tp = vnfdgen.generate_vnfd(TRAFFIC_PROFILE_TPL, {"imix": {}}) + self.assertDictEqual(TRAFFIC_PROFILE, generated_tp) + + def test_deepgetitem(self): + d = {'a': 1, 'b': 2} + self.assertEqual(vnfdgen.deepgetitem(d, "a"), 1) + + def test_dict_flatten_int(self): + d = {'a': 1, 'b': 2} + self.assertEqual(vnfdgen.deepgetitem(d, "a"), 1) + + def test_dict_flatten_str_int_key_first(self): + d = {'0': 1, 0: 24, 'b': 2} + self.assertEqual(vnfdgen.deepgetitem(d, "0"), 1) + + def test_dict_flatten_int_key_fallback(self): + d = {0: 1, 'b': 2} + self.assertEqual(vnfdgen.deepgetitem(d, "0"), 1) + + def test_dict_flatten_list(self): + d = {'a': 1, 'b': list(range(2))} + self.assertEqual(vnfdgen.deepgetitem(d, "b.0"), 0) + + def test_dict_flatten_dict(self): + d = {'a': 1, 'b': {x: x for x in list(range(2))}} + self.assertEqual(vnfdgen.deepgetitem(d, "b.0"), 0) + + def test_dict_flatten_only_str_key(self): + d = {'0': 1, 0: 24, 'b': 2} + self.assertRaises(AttributeError, vnfdgen.deepgetitem, d, 0) + + def test_generate_tp_single_var(self): + """ Function to verify traffic profile generation with imix """ + + generated_tp = \ + vnfdgen.generate_vnfd(TRAFFIC_PROFILE_TPL, + {"imix": {UPLINK: {"imix_small": '20'}}}) + self.maxDiff = None + tp2 = dict(TRAFFIC_PROFILE) + tp2[UPLINK][0]["ipv4"]["outer_l2"]["framesize"]["64B"] = '20' + self.assertDictEqual(tp2, generated_tp) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/__init__.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/__init__.py diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/tc_baremetal_rfc2544_ipv4_1flow_64B.yaml b/yardstick/tests/unit/network_services/vnf_generic/vnf/tc_baremetal_rfc2544_ipv4_1flow_64B.yaml new file mode 100644 index 000000000..09c22ad9e --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/tc_baremetal_rfc2544_ipv4_1flow_64B.yaml @@ -0,0 +1,41 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +--- +schema: yardstick:task:0.1 +scenarios: +- type: NSPerf + traffic_profile: "../../traffic_profiles/ipv4_throughput_vpe.yaml" + topology: vpe_vnf_topology.yaml + nodes: + tg__0: trafficgen_0.yardstick + vnf__0: vnf_0.yardstick + tc_options: + rfc2544: + allowed_drop_rate: 0.8 - 1 + vnf_options: + vpe: + cfg: vpe_config + runner: + type: Duration + duration: 400 + interval: 35 + traffic_options: + flow: "../../traffic_profiles/ipv4_1flow_Packets_vpe.yaml" + imix: "../../traffic_profiles/imix_voice.yaml" +context: + type: Node + name: yardstick + nfvi_type: baremetal + file: /etc/yardstick/nodes/pod.yaml diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_acl_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_acl_vnf.py new file mode 100644 index 000000000..12bb42f20 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_acl_vnf.py @@ -0,0 +1,518 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import mock +import os +import re +import copy + +from yardstick.common import utils +from yardstick.common import exceptions +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.vnf_generic.vnf import acl_vnf +from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper +from yardstick.network_services.nfvi.resource import ResourceProfile +from yardstick.network_services.vnf_generic.vnf.acl_vnf import AclApproxSetupEnvSetupEnvHelper +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh + + +TEST_FILE_YAML = 'nsb_test_case.yaml' +SSH_HELPER = 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper' + + +name = 'vnf__1' + + +@mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.Process") +class TestAclApproxVnf(unittest.TestCase): + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{'short-name': 'VpeVnf', + 'vdu': + [{'routing_table': + [{'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0'}, + {'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1'}], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': + [{'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'id': 'vpevnf-baremetal', + 'external-interface': + [{'virtual-interface': + {'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02'}, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01'}, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'}]}], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': + {'vdu-id': 'vpevnf-baremetal', + 'host': '1.2.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.2.1.1'}, + 'benchmark': + {'kpi': ['packets_in', 'packets_fwd', 'packets_dropped']}, + 'connection-point': [{'type': 'VPORT', 'name': 'xe0'}, + {'type': 'VPORT', 'name': 'xe1'}], + 'id': 'AclApproxVnf', 'name': 'VPEVnfSsh'}]}} + + scenario_cfg = {'options': {'packetsize': 64, 'traffic_type': 4, + 'rfc2544': {'allowed_drop_rate': '0.8 - 1'}, + 'vnf__1': {'rules': 'acl_1rule.yaml', + 'vnf_config': {'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': + '1C/1T', + 'worker_threads': 1}} + }, + 'task_id': 'a70bdf4a-8e67-47a3-9dc1-273c14506eb7', + 'task_path': '/tmp', + 'tc': 'tc_ipv4_1Mflow_64B_packetsize', + 'runner': {'object': 'NetworkServiceTestCase', + 'interval': 35, + 'output_filename': '/tmp/yardstick.out', + 'runner_id': 74476, 'duration': 400, + 'type': 'Duration'}, + 'traffic_profile': 'ipv4_throughput_acl.yaml', + 'traffic_options': {'flow': 'ipv4_Packets_acl.yaml', + 'imix': 'imix_voice.yaml'}, + 'type': 'ISB', + 'nodes': {'tg__2': 'trafficgen_2.yardstick', + 'tg__1': 'trafficgen_1.yardstick', + 'vnf__1': 'vnf.yardstick'}, + 'topology': 'vpe-tg-topology-baremetal.yaml'} + + context_cfg = {'nodes': {'tg__2': + {'member-vnf-index': '3', + 'role': 'TrafficGen', + 'name': 'trafficgen_2.yardstick', + 'vnfd-id-ref': 'tg__2', + 'ip': '1.2.1.1', + 'interfaces': + {'xe0': {'local_iface_name': 'ens513f0', + 'vld_id': acl_vnf.AclApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.20', + 'dst_mac': '00:00:00:00:00:01', + 'local_mac': '00:00:00:00:00:03', + 'dst_ip': '152.16.40.19', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.0', + 'dpdk_port_num': 0}, + 'xe1': {'local_iface_name': 'ens513f1', + 'netmask': '255.255.255.0', + 'network': '202.16.100.0', + 'local_ip': '202.16.100.20', + 'local_mac': '00:1e:67:d0:60:5d', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.1', + 'dpdk_port_num': 1}}, + 'password': 'r00t', + 'VNF model': 'l3fwd_vnf.yaml', + 'user': 'root'}, + 'tg__1': + {'member-vnf-index': '1', + 'role': 'TrafficGen', + 'name': 'trafficgen_1.yardstick', + 'vnfd-id-ref': 'tg__1', + 'ip': '1.2.1.1', + 'interfaces': + {'xe0': {'local_iface_name': 'ens785f0', + 'vld_id': acl_vnf.AclApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.20', + 'dst_mac': '00:00:00:00:00:02', + 'local_mac': '00:00:00:00:00:04', + 'dst_ip': '152.16.100.19', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0}, + 'xe1': {'local_iface_name': 'ens785f1', + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.21', + 'local_mac': '00:00:00:00:00:01', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1}}, + 'password': 'r00t', + 'VNF model': 'tg_rfc2544_tpl.yaml', + 'user': 'root'}, + 'vnf__1': + {'name': 'vnf.yardstick', + 'vnfd-id-ref': 'vnf__1', + 'ip': '1.2.1.1', + 'interfaces': + {'xe0': {'local_iface_name': 'ens786f0', + 'vld_id': acl_vnf.AclApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.19', + 'dst_mac': '00:00:00:00:00:04', + 'local_mac': '00:00:00:00:00:02', + 'dst_ip': '152.16.100.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0}, + 'xe1': {'local_iface_name': 'ens786f1', + 'vld_id': acl_vnf.AclApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.19', + 'dst_mac': '00:00:00:00:00:03', + 'local_mac': '00:00:00:00:00:01', + 'dst_ip': '152.16.40.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1}}, + 'routing_table': + [{'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'network': '152.16.100.20', + 'if': 'xe0'}, + {'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'network': '152.16.40.20', + 'if': 'xe1'}], + 'member-vnf-index': '2', + 'host': '1.2.1.1', + 'role': 'vnf', + 'user': 'root', + 'nd_route_tbl': + [{'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'password': 'r00t', + 'VNF model': 'acl_vnf.yaml'}}} + + def test___init__(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + acl_approx_vnf = acl_vnf.AclApproxVnf(name, vnfd) + self.assertIsNone(acl_approx_vnf._vnf_process) + + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.time") + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch(SSH_HELPER) + def test_collect_kpi(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + acl_approx_vnf = acl_vnf.AclApproxVnf(name, vnfd) + acl_approx_vnf.scenario_helper.scenario_cfg = { + 'nodes': {acl_approx_vnf.name: "mock"} + } + acl_approx_vnf.q_in = mock.MagicMock() + acl_approx_vnf.q_out = mock.MagicMock() + acl_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + acl_approx_vnf.resource = mock.Mock(autospec=ResourceProfile) + acl_approx_vnf.vnf_execute = mock.Mock(return_value="") + result = { + 'physical_node': 'mock_node', + 'packets_dropped': 0, + 'packets_fwd': 0, + 'packets_in': 0 + } + self.assertEqual(result, acl_approx_vnf.collect_kpi()) + + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.time") + @mock.patch(SSH_HELPER) + def test_vnf_execute_command(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + acl_approx_vnf = acl_vnf.AclApproxVnf(name, vnfd) + acl_approx_vnf.q_in = mock.MagicMock() + acl_approx_vnf.q_out = mock.MagicMock() + acl_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + cmd = "quit" + self.assertEqual("", acl_approx_vnf.vnf_execute(cmd)) + + @mock.patch(SSH_HELPER) + def test_get_stats(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + acl_approx_vnf = acl_vnf.AclApproxVnf(name, vnfd) + acl_approx_vnf.q_in = mock.MagicMock() + acl_approx_vnf.q_out = mock.MagicMock() + acl_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + result = "ACL TOTAL: pkts_processed: 100, pkts_drop: 0, spkts_received: 100" + acl_approx_vnf.vnf_execute = mock.Mock(return_value=result) + self.assertEqual(result, acl_approx_vnf.get_stats()) + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + @mock.patch("yardstick.network_services.vnf_generic.vnf.acl_vnf.hex") + @mock.patch("yardstick.network_services.vnf_generic.vnf.acl_vnf.eval") + @mock.patch('yardstick.network_services.vnf_generic.vnf.acl_vnf.open') + @mock.patch(SSH_HELPER) + def test_run_acl(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + acl_approx_vnf = acl_vnf.AclApproxVnf(name, vnfd) + acl_approx_vnf._build_config = mock.MagicMock() + acl_approx_vnf.queue_wrapper = mock.MagicMock() + acl_approx_vnf.scenario_helper.scenario_cfg = self.scenario_cfg + acl_approx_vnf.vnf_cfg = {'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1} + acl_approx_vnf.all_options = {'traffic_type': '4', + 'topology': 'nsb_test_case.yaml'} + acl_approx_vnf._run() + acl_approx_vnf.ssh_helper.run.assert_called_once() + + @mock.patch.object(utils, 'find_relative_file') + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.Context") + @mock.patch(SSH_HELPER) + def test_instantiate(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + acl_approx_vnf = acl_vnf.AclApproxVnf(name, vnfd) + acl_approx_vnf.deploy_helper = mock.MagicMock() + acl_approx_vnf.resource_helper = mock.MagicMock() + acl_approx_vnf._build_config = mock.MagicMock() + self.scenario_cfg['vnf_options'] = {'acl': {'cfg': "", + 'rules': ""}} + acl_approx_vnf.q_out.put("pipeline>") + acl_approx_vnf.WAIT_TIME = 0 + self.scenario_cfg.update({"nodes": {"vnf__1": ""}}) + self.assertIsNone(acl_approx_vnf.instantiate(self.scenario_cfg, + self.context_cfg)) + + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.time") + @mock.patch(SSH_HELPER) + def test_terminate(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + acl_approx_vnf = acl_vnf.AclApproxVnf(name, vnfd) + acl_approx_vnf._vnf_process = mock.MagicMock() + acl_approx_vnf._vnf_process.terminate = mock.Mock() + acl_approx_vnf.used_drivers = {"01:01.0": "i40e", + "01:01.1": "i40e"} + acl_approx_vnf.vnf_execute = mock.MagicMock() + acl_approx_vnf.dpdk_devbind = "dpdk-devbind.py" + acl_approx_vnf._resource_collect_stop = mock.Mock() + self.assertIsNone(acl_approx_vnf.terminate()) + + +class TestAclApproxSetupEnvSetupEnvHelper(unittest.TestCase): + + ACL_CONFIG = {"access-list-entries": [{ + "actions": [ + "count", + {"fwd": { + "port": 0 + } + } + ], + "matches": { + "destination-ipv4-network": "152.16.0.0/24", + "destination-port-range": { + "lower-port": 0, + "upper-port": 65535 + }, + "source-ipv4-network": "0.0.0.0/0", + "source-port-range": { + "lower-port": 0, + "upper-port": 65535 + }, + "protocol-mask": 255, + "protocol": 127, + "priority": 1 + }, + "rule-name": "rule1588" + } + ]} + + def test_get_default_flows(self): + """Check if default ACL SampleVNF CLI commands are + generated correctly""" + ssh_helper = mock.Mock() + vnfd_helper = VnfdHelper({'vdu': [ + {'external-interface': [ + { + 'virtual-interface': { + 'local_ip': '152.16.100.19', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'dst_ip': '152.16.100.20', + 'vld_id': 'uplink_0', + 'ifname': 'xe0', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0' + }, + { + 'virtual-interface': { + 'local_ip': '152.16.40.19', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'dst_ip': '152.16.40.20', + 'vld_id': 'downlink_0', + 'ifname': 'xe1', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1' + } + ]} + ]}) + setup_helper = AclApproxSetupEnvSetupEnvHelper(vnfd_helper, ssh_helper, None) + self.check_acl_commands(setup_helper.get_flows_config(), [ + # format: (<cli pattern>, <number of expected matches>) + ("^p action add [0-9]+ accept$", 2), + ("^p action add [0-9]+ count$", 2), + ("^p action add [0-9]+ fwd 1$", 1), + ("^p action add [0-9]+ fwd 0$", 1), + ("^p acl add 1 152.16.100.0 24 152.16.40.0 24 0 65535 0 65535 0 0 [0-9]+$", 1), + ("^p acl add 1 152.16.40.0 24 152.16.100.0 24 0 65535 0 65535 0 0 [0-9]+$", 1), + ("^p acl applyruleset$", 1) + ]) + + @mock.patch.object(AclApproxSetupEnvSetupEnvHelper, 'get_default_flows') + def test_get_flows_config(self, get_default_flows): + """Check if provided ACL config can be converted to + ACL SampleVNF CLI commands correctly""" + ssh_helper = mock.Mock() + setup_helper = AclApproxSetupEnvSetupEnvHelper(None, ssh_helper, None) + get_default_flows.return_value = ({}, []) + self.check_acl_commands(setup_helper.get_flows_config(self.ACL_CONFIG), [ + # format: (<cli pattern>, <number of expected matches>) + ("^p action add [0-9]+ count$", 1), + ("^p action add [0-9]+ fwd 0$", 1), + ("^p acl add 1 0.0.0.0 0 152.16.0.0 24 0 65535 0 65535 127 0 [0-9]+$", 1), + ("^p acl applyruleset$", 1) + ]) + + @mock.patch.object(AclApproxSetupEnvSetupEnvHelper, 'get_default_flows') + def test_get_flows_config_invalid_action(self, get_default_flows): + """Check if incorrect ACL config fails to convert + to ACL SampleVNF CLI commands""" + ssh_helper = mock.Mock() + setup_helper = AclApproxSetupEnvSetupEnvHelper(None, ssh_helper, None) + get_default_flows.return_value = ({}, []) + # duplicate config and add invald action + acl_config = copy.deepcopy(self.ACL_CONFIG) + acl_config['access-list-entries'][0]["actions"].append({"xnat": {}}) + self.assertRaises(exceptions.AclUnknownActionTemplate, + setup_helper.get_flows_config, acl_config) + + @mock.patch.object(AclApproxSetupEnvSetupEnvHelper, 'get_default_flows') + def test_get_flows_config_invalid_action_param(self, get_default_flows): + """Check if ACL config with invalid action parameter fails to convert + to ACL SampleVNF CLI commands""" + ssh_helper = mock.Mock() + setup_helper = AclApproxSetupEnvSetupEnvHelper(None, ssh_helper, None) + get_default_flows.return_value = ({}, []) + # duplicate config and add action with invalid parameter + acl_config = copy.deepcopy(self.ACL_CONFIG) + acl_config['access-list-entries'][0]["actions"].append( + {"nat": {"xport": 0}}) + self.assertRaises(exceptions.AclMissingActionArguments, + setup_helper.get_flows_config, acl_config) + + def check_acl_commands(self, config, expected_cli_patterns): + """Check if expected ACL CLI commands (given as a list of patterns, + `expected_cli_patterns` parameter) present in SampleVNF ACL + configuration (given as a multiline string, `config` parameter)""" + # Example of expected config: + # --------------------------- + # p action add 1 accept + # p action add 1 fwd 1 + # p action add 2 accept + # p action add 2 count + # p action add 2 fwd 0 + # p acl add 1 152.16.100.0 24 152.16.40.0 24 0 65535 0 65535 0 0 1 + # p acl add 1 152.16.40.0 24 152.16.100.0 24 0 65535 0 65535 0 0 2 + # p acl applyruleset + # --------------------------- + # NOTE: The config above consists of actions ids, which are actually + # unknown (generated at runtime), thus it's incorrect just to compare + # the example ACL config above with the configuration returned by + # get_flows_config() function. It's more correct to use CLI patterns + # (RE) to find the required SampleVNF CLI commands in the multiline + # string (SampleVNF ACL configuration). + for pattern, num_of_match in expected_cli_patterns: + # format: (<cli pattern>, <number of expected matches>) + result = re.findall(pattern, config, re.MULTILINE) + self.assertEqual(len(result), num_of_match) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.open') + @mock.patch.object(utils, 'find_relative_file') + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.MultiPortConfig') + @mock.patch.object(utils, 'open_relative_file') + def test_build_config(self, *args): + vnfd_helper = mock.Mock() + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.vnf_cfg = {'lb_config': 'HW'} + scenario_helper.options = {} + scenario_helper.all_options = {} + + acl_approx_setup_helper = AclApproxSetupEnvSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + acl_approx_setup_helper.get_flows_config = mock.Mock() + acl_approx_setup_helper.ssh_helper.provision_tool = mock.Mock(return_value='tool_path') + acl_approx_setup_helper.ssh_helper.all_ports = mock.Mock() + acl_approx_setup_helper.vnfd_helper.port_nums = mock.Mock(return_value=[0, 1]) + expected = 'sudo tool_path -p 0x3 -f /tmp/acl_config -s /tmp/acl_script --hwlb 3' + self.assertEqual(acl_approx_setup_helper.build_config(), expected) + acl_approx_setup_helper.get_flows_config.assert_called_once() diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_agnostic_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_agnostic_vnf.py new file mode 100644 index 000000000..7c7fe5955 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_agnostic_vnf.py @@ -0,0 +1,68 @@ +# Copyright (c) 2018-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from yardstick.network_services.vnf_generic.vnf import agnostic_vnf + +NAME = 'vnf__0' + +VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [{ + 'id': 'AgnosticVnf', # NSB python class mapping + 'name': 'AgnosticVnf', + 'short-name': 'AgnosticVnf', + 'description': 'AgnosticVnf', + 'mgmt-interface': { + 'vdu-id': 'vepcvnf-baremetal', + 'user': 'user', + 'password': 'password', + 'ip': 'ip' + }, + 'vdu': [{ + 'id': 'vepcvnf-baremetal', + 'name': 'vepc-vnf-baremetal', + 'description': 'vAgnosticVnf workload', + 'external-interface': []}], + 'benchmark': { + 'kpi': []}}]}} + + +class TestAgnosticVnf(unittest.TestCase): + + def setUp(self): + self.vnfd = VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.agnostic_vnf = agnostic_vnf.AgnosticVnf(NAME, self.vnfd) + + def test_instantiate(self): + self.assertIsNone(self.agnostic_vnf.instantiate({}, {})) + + def test_wait_for_instantiate(self): + self.assertIsNone(self.agnostic_vnf.wait_for_instantiate()) + + def test_terminate(self): + self.assertIsNone(self.agnostic_vnf.terminate()) + + def test_scale(self): + self.assertIsNone(self.agnostic_vnf.scale()) + + def test_collect_kpi(self): + self.assertIsNone(self.agnostic_vnf.collect_kpi()) + + def test_start_collect(self): + self.assertIsNone(self.agnostic_vnf.start_collect()) + + def test_stop_collect(self): + self.assertIsNone(self.agnostic_vnf.stop_collect()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_base.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_base.py new file mode 100644 index 000000000..1a72e042b --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_base.py @@ -0,0 +1,236 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import multiprocessing +import os + +import mock +import unittest + +from yardstick.network_services.vnf_generic.vnf import base +from yardstick.ssh import SSH +from yardstick.tests.unit import base as ut_base + + +IP_PIPELINE_CFG_FILE_TPL = ("arp_route_tbl = ({port0_local_ip_hex}," + "{port0_netmask_hex},1,{port1_local_ip_hex}) " + "({port1_local_ip_hex},{port1_netmask_hex},0," + "{port0_local_ip_hex})") + +IP_PIPELINE_ND_CFG_FILE_TPL = """ +nd_route_tbl = ({port1_dst_ip_hex6},""" +"""{port1_dst_netmask_hex6},1,{port1_dst_ip_hex6})""" + +_LOCAL_OBJECT = object() + +VNFD_0 = { + 'short-name': 'VpeVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0' + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1' + }, + ], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0' + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1' + }, + ], + 'id': 'vpevnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.100.20', + 'local_mac': '00:00:00:00:00:01' + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0' + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_mac': '00:00:00:00:00:02' + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1' + }, + ], + }, + ], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1' + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh' +} + +VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ] + } +} + + +class FileAbsPath(object): + def __init__(self, module_file): + super(FileAbsPath, self).__init__() + self.module_path = os.path.dirname(os.path.abspath(module_file)) + + def get_path(self, filename): + file_path = os.path.join(self.module_path, filename) + return file_path + + +def mock_ssh(mock_ssh_type, spec=None, exec_result=_LOCAL_OBJECT, run_result=_LOCAL_OBJECT): + if spec is None: + spec = SSH + + if exec_result is _LOCAL_OBJECT: + exec_result = 0, "", "" + + if run_result is _LOCAL_OBJECT: + run_result = 0, "", "" + + mock_ssh_instance = mock.Mock(autospec=spec) + mock_ssh_instance._get_client.return_value = mock.Mock() + mock_ssh_instance.execute.return_value = exec_result + mock_ssh_instance.run.return_value = run_result + mock_ssh_type.from_node.return_value = mock_ssh_instance + return mock_ssh_instance + + +class TestQueueFileWrapper(unittest.TestCase): + def setUp(self): + self.prompt = "pipeline>" + self.q_in = multiprocessing.Queue() + self.q_out = multiprocessing.Queue() + + def test___init__(self): + queue_file_wrapper = \ + base.QueueFileWrapper(self.q_in, self.q_out, self.prompt) + self.assertEqual(queue_file_wrapper.prompt, self.prompt) + + def test_clear(self): + queue_file_wrapper = \ + base.QueueFileWrapper(self.q_in, self.q_out, self.prompt) + queue_file_wrapper.bufsize = 5 + queue_file_wrapper.write("pipeline>") + queue_file_wrapper.close() + self.assertIsNone(queue_file_wrapper.clear()) + self.assertIsNotNone(queue_file_wrapper.q_out.empty()) + + def test_close(self): + queue_file_wrapper = \ + base.QueueFileWrapper(self.q_in, self.q_out, self.prompt) + self.assertIsNone(queue_file_wrapper.close()) + + def test_read(self): + queue_file_wrapper = \ + base.QueueFileWrapper(self.q_in, self.q_out, self.prompt) + queue_file_wrapper.q_in.put("pipeline>") + self.assertEqual("pipeline>", queue_file_wrapper.read(20)) + + def test_write(self): + queue_file_wrapper = \ + base.QueueFileWrapper(self.q_in, self.q_out, self.prompt) + queue_file_wrapper.write("pipeline>") + self.assertIsNotNone(queue_file_wrapper.q_out.empty()) + + +class TestGenericVNF(ut_base.BaseUnitTestCase): + + def test_definition(self): + """Make sure that the abstract class cannot be instantiated""" + with self.assertRaises(TypeError) as exc: + # pylint: disable=abstract-class-instantiated + base.GenericVNF('vnf1', VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + + msg = ("Can't instantiate abstract class GenericVNF with abstract " + "methods collect_kpi, instantiate, scale, start_collect, " + "stop_collect, terminate, wait_for_instantiate") + + self.assertEqual(msg, str(exc.exception)) + + +class GenericTrafficGenTestCase(ut_base.BaseUnitTestCase): + + def test_definition(self): + """Make sure that the abstract class cannot be instantiated""" + vnfd = VNFD['vnfd:vnfd-catalog']['vnfd'][0] + name = 'vnf1' + with self.assertRaises(TypeError) as exc: + # pylint: disable=abstract-class-instantiated + base.GenericTrafficGen(name, vnfd) + msg = ("Can't instantiate abstract class GenericTrafficGen with " + "abstract methods collect_kpi, instantiate, run_traffic, " + "scale, terminate") + self.assertEqual(msg, str(exc.exception)) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_cgnapt_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_cgnapt_vnf.py new file mode 100644 index 000000000..d0672dcfd --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_cgnapt_vnf.py @@ -0,0 +1,412 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from copy import deepcopy +import time + +import mock +import unittest + +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.common import utils +from yardstick.common import process +from yardstick.network_services.vnf_generic.vnf import cgnapt_vnf +from yardstick.network_services.vnf_generic.vnf import sample_vnf +from yardstick.network_services.nfvi import resource + + +TEST_FILE_YAML = 'nsb_test_case.yaml' +name = 'vnf__0' + + +class TestCgnaptApproxSetupEnvHelper(unittest.TestCase): + + def test__generate_ip_from_pool(self): + + _ip = '1.2.3.4' + ip = cgnapt_vnf.CgnaptApproxSetupEnvHelper._generate_ip_from_pool(_ip) + self.assertEqual(next(ip), _ip) + self.assertEqual(next(ip), '1.2.4.4') + self.assertEqual(next(ip), '1.2.5.4') + + def test__update_cgnat_script_file(self): + + sample = """\ +# See the License for the specific language governing permissions and +# limitations under the License. + +link 0 down +link 0 config {port0_local_ip} {port0_prefixlen} +link 0 up +link 1 down +link 1 config {port1_local_ip} {port1_prefixlen} +link 1 up +""" + header = "This is a header" + + out = cgnapt_vnf.CgnaptApproxSetupEnvHelper._update_cgnat_script_file( + header, sample.splitlines()) + self.assertNotIn("This is a header", out) + + def test__get_cgnapt_config(self): + vnfd_helper = mock.MagicMock() + vnfd_helper.port_pairs.uplink_ports = [{"name": 'a'}, {"name": "b"}, {"name": "c"}] + + helper = cgnapt_vnf.CgnaptApproxSetupEnvHelper( + vnfd_helper, mock.Mock(), mock.Mock()) + result = helper._get_cgnapt_config() + self.assertIsNotNone(result) + + def test_scale(self): + helper = cgnapt_vnf.CgnaptApproxSetupEnvHelper( + mock.Mock(), mock.Mock(), mock.Mock()) + with self.assertRaises(NotImplementedError): + helper.scale() + + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.open') + @mock.patch.object(utils, 'find_relative_file') + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.MultiPortConfig') + @mock.patch.object(utils, 'open_relative_file') + def test_build_config(self, *args): + vnfd_helper = mock.Mock() + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.vnf_cfg = {'lb_config': 'HW'} + scenario_helper.options = {} + scenario_helper.all_options = {} + + cgnat_approx_setup_helper = cgnapt_vnf.CgnaptApproxSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + + cgnat_approx_setup_helper.ssh_helper.provision_tool = mock.Mock(return_value='tool_path') + cgnat_approx_setup_helper.ssh_helper.all_ports = mock.Mock() + cgnat_approx_setup_helper.vnfd_helper.port_nums = mock.Mock(return_value=[0, 1]) + expected = 'sudo tool_path -p 0x3 -f /tmp/cgnapt_config -s /tmp/cgnapt_script --hwlb 3' + self.assertEqual(cgnat_approx_setup_helper.build_config(), expected) + + +@mock.patch.object(sample_vnf, 'Process') +class TestCgnaptApproxVnf(unittest.TestCase): + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{'short-name': 'VpeVnf', + 'vdu': + [{'routing_table': + [{'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0'}, + {'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1'}], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': + [{'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'id': 'vpevnf-baremetal', + 'external-interface': + [{'virtual-interface': + {'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02'}, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01'}, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'}]}], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': + {'vdu-id': 'vpevnf-baremetal', + 'host': '1.2.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.2.1.1'}, + 'benchmark': + {'kpi': ['packets_in', 'packets_fwd', 'packets_dropped']}, + 'connection-point': [{'type': 'VPORT', 'name': 'xe0'}, + {'type': 'VPORT', 'name': 'xe1'}], + 'id': 'CgnaptApproxVnf', 'name': 'VPEVnfSsh'}]}} + + SCENARIO_CFG = { + 'options': { + 'packetsize': 64, + 'traffic_type': 4, + 'rfc2544': { + 'allowed_drop_rate': '0.8 - 1', + }, + 'vnf__0': { + 'napt': 'dynamic', + 'vnf_config': { + 'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': + '1C/1T', + 'worker_threads': 1, + }, + }, + 'flow': {'count': 1, + 'dst_ip': [{'tg__1': 'xe0'}], + 'public_ip': [''], + 'src_ip': [{'tg__0': 'xe0'}]}, + }, + 'task_id': 'a70bdf4a-8e67-47a3-9dc1-273c14506eb7', + 'task_path': '/tmp', + 'tc': 'tc_ipv4_1Mflow_64B_packetsize', + 'runner': { + 'object': 'NetworkServiceTestCase', + 'interval': 35, + 'output_filename': '/tmp/yardstick.out', + 'runner_id': 74476, + 'duration': 400, + 'type': 'Duration', + }, + 'traffic_profile': 'ipv4_throughput_acl.yaml', + 'type': 'NSPerf', + 'nodes': { + 'tg__1': 'trafficgen_1.yardstick', + 'tg__0': 'trafficgen_0.yardstick', + 'vnf__0': 'vnf.yardstick', + }, + 'topology': 'vpe-tg-topology-baremetal.yaml', + } + + context_cfg = {'nodes': {'tg__2': + {'member-vnf-index': '3', + 'role': 'TrafficGen', + 'name': 'trafficgen_2.yardstick', + 'vnfd-id-ref': 'tg__2', + 'ip': '1.2.1.1', + 'interfaces': + {'xe0': {'local_iface_name': 'ens513f0', + 'vld_id': cgnapt_vnf.CgnaptApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.20', + 'dst_mac': '00:00:00:00:00:01', + 'local_mac': '00:00:00:00:00:03', + 'dst_ip': '152.16.40.19', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.0', + 'dpdk_port_num': 0}, + 'xe1': {'local_iface_name': 'ens513f1', + 'netmask': '255.255.255.0', + 'network': '202.16.100.0', + 'local_ip': '202.16.100.20', + 'local_mac': '00:1e:67:d0:60:5d', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.1', + 'dpdk_port_num': 1}}, + 'password': 'r00t', + 'VNF model': 'l3fwd_vnf.yaml', + 'user': 'root'}, + 'tg__1': + {'member-vnf-index': '1', + 'role': 'TrafficGen', + 'name': 'trafficgen_1.yardstick', + 'vnfd-id-ref': 'tg__1', + 'ip': '1.2.1.1', + 'interfaces': + {'xe0': {'local_iface_name': 'ens785f0', + 'vld_id': cgnapt_vnf.CgnaptApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.20', + 'dst_mac': '00:00:00:00:00:02', + 'local_mac': '00:00:00:00:00:04', + 'dst_ip': '152.16.100.19', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0}, + 'xe1': {'local_iface_name': 'ens785f1', + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.21', + 'local_mac': '00:00:00:00:00:01', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1}}, + 'password': 'r00t', + 'VNF model': 'tg_rfc2544_tpl.yaml', + 'user': 'root'}, + 'vnf__0': + {'name': 'vnf.yardstick', + 'vnfd-id-ref': 'vnf__0', + 'ip': '1.2.1.1', + 'interfaces': + {'xe0': {'local_iface_name': 'ens786f0', + 'vld_id': cgnapt_vnf.CgnaptApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.19', + 'dst_mac': '00:00:00:00:00:04', + 'local_mac': '00:00:00:00:00:02', + 'dst_ip': '152.16.100.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0}, + 'xe1': {'local_iface_name': 'ens786f1', + 'vld_id': cgnapt_vnf.CgnaptApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.19', + 'dst_mac': '00:00:00:00:00:03', + 'local_mac': '00:00:00:00:00:01', + 'dst_ip': '152.16.40.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1}}, + 'routing_table': + [{'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'network': '152.16.100.20', + 'if': 'xe0'}, + {'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'network': '152.16.40.20', + 'if': 'xe1'}], + 'member-vnf-index': '2', + 'host': '1.2.1.1', + 'role': 'vnf', + 'user': 'root', + 'nd_route_tbl': + [{'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'password': 'r00t', + 'VNF model': 'cgnapt_vnf.yaml'}}} + + def setUp(self): + self.scenario_cfg = deepcopy(self.SCENARIO_CFG) + + def test___init__(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + cgnapt_approx_vnf = cgnapt_vnf.CgnaptApproxVnf(name, vnfd) + self.assertIsNone(cgnapt_approx_vnf._vnf_process) + + @mock.patch.object(process, 'check_if_process_failed') + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + def test_collect_kpi(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + cgnapt_approx_vnf = cgnapt_vnf.CgnaptApproxVnf(name, vnfd) + cgnapt_approx_vnf.scenario_helper.scenario_cfg = { + 'nodes': {cgnapt_approx_vnf.name: "mock"} + } + cgnapt_approx_vnf._vnf_process = mock.MagicMock( + **{"is_alive.return_value": True, "exitcode": None}) + cgnapt_approx_vnf.q_in = mock.MagicMock() + cgnapt_approx_vnf.q_out = mock.MagicMock() + cgnapt_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + cgnapt_approx_vnf.resource = mock.Mock( + autospec=resource.ResourceProfile) + result = { + 'physical_node': 'mock_node', + 'packets_dropped': 0, + 'packets_fwd': 0, + 'packets_in': 0 + } + with mock.patch.object(cgnapt_approx_vnf, 'get_stats', + return_value=''): + self.assertEqual(result, cgnapt_approx_vnf.collect_kpi()) + + @mock.patch.object(time, 'sleep') + def test_vnf_execute_command(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + cgnapt_approx_vnf = cgnapt_vnf.CgnaptApproxVnf(name, vnfd) + cgnapt_approx_vnf.q_in = mock.Mock() + cgnapt_approx_vnf.q_out = mock.Mock() + cgnapt_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + self.assertEqual("", cgnapt_approx_vnf.vnf_execute('quit')) + + def test_get_stats(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + cgnapt_approx_vnf = cgnapt_vnf.CgnaptApproxVnf(name, vnfd) + with mock.patch.object(cgnapt_approx_vnf, 'vnf_execute') as mock_exec: + mock_exec.return_value = 'output' + self.assertEqual('output', cgnapt_approx_vnf.get_stats()) + + mock_exec.assert_called_once_with('p cgnapt stats') + + def test_run_vcgnapt(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + cgnapt_approx_vnf = cgnapt_vnf.CgnaptApproxVnf(name, vnfd) + cgnapt_approx_vnf.ssh_helper = mock.Mock() + cgnapt_approx_vnf.setup_helper = mock.Mock() + with mock.patch.object(cgnapt_approx_vnf, '_build_config'), \ + mock.patch.object(cgnapt_approx_vnf, '_build_run_kwargs'): + cgnapt_approx_vnf._run() + + cgnapt_approx_vnf.ssh_helper.run.assert_called_once() + cgnapt_approx_vnf.setup_helper.kill_vnf.assert_called_once() + + @mock.patch.object(ctx_base.Context, 'get_context_from_server') + def test_instantiate(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + cgnapt_approx_vnf = cgnapt_vnf.CgnaptApproxVnf(name, vnfd) + cgnapt_approx_vnf.deploy_helper = mock.MagicMock() + cgnapt_approx_vnf.resource_helper = mock.MagicMock() + cgnapt_approx_vnf._build_config = mock.MagicMock() + self.scenario_cfg['vnf_options'] = {'acl': {'cfg': "", + 'rules': ""}} + cgnapt_approx_vnf.q_out.put("pipeline>") + cgnapt_vnf.WAIT_TIME = 3 + self.scenario_cfg.update({"nodes": {"vnf__0": ""}}) + with mock.patch.object(cgnapt_approx_vnf, '_start_vnf'): + self.assertIsNone(cgnapt_approx_vnf.instantiate( + self.scenario_cfg, self.context_cfg)) + + @mock.patch.object(time, 'sleep') + def test__vnf_up_post(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.scenario_cfg['options'][name]['napt'] = 'static' + cgnapt_approx_vnf = cgnapt_vnf.CgnaptApproxVnf(name, vnfd) + cgnapt_approx_vnf.vnf_execute = mock.Mock() + cgnapt_approx_vnf.scenario_helper.scenario_cfg = self.scenario_cfg + with mock.patch.object(cgnapt_approx_vnf, 'setup_helper') as \ + mock_setup_helper: + mock_setup_helper._generate_ip_from_pool.return_value = ['ip1'] + mock_setup_helper._get_cgnapt_config.return_value = ['gw_ip1'] + cgnapt_approx_vnf._vnf_up_post() + + def test__vnf_up_post_short(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + cgnapt_approx_vnf = cgnapt_vnf.CgnaptApproxVnf(name, vnfd) + cgnapt_approx_vnf.scenario_helper.scenario_cfg = self.scenario_cfg + cgnapt_approx_vnf._vnf_up_post() diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_epc_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_epc_vnf.py new file mode 100644 index 000000000..b1bef2e39 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_epc_vnf.py @@ -0,0 +1,92 @@ +# Copyright (c) 2018-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import unittest + +from yardstick.network_services.vnf_generic.vnf import epc_vnf + +NAME = 'vnf__0' + +VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [{ + 'id': 'EPCVnf', # NSB python class mapping + 'name': 'EPCVnf', + 'short-name': 'EPCVnf', + 'description': 'EPCVnf', + 'mgmt-interface': { + 'vdu-id': 'vepcvnf-baremetal', + 'user': 'user', # Value filled by vnfdgen + 'password': 'password', # Value filled by vnfdgen + 'ip': 'ip' # Value filled by vnfdgen + }, + 'vdu': [{ + 'id': 'vepcvnf-baremetal', + 'name': 'vepc-vnf-baremetal', + 'description': 'vEPCVnf workload', + 'external-interface': []}], + 'benchmark': { + 'kpi': []}}]}} + + +class TestEPCVnf(unittest.TestCase): + + def setUp(self): + self.vnfd = VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.epc_vnf = epc_vnf.EPCVnf(NAME, self.vnfd) + + def test___init__(self, *args): + _epc_vnf = epc_vnf.EPCVnf(NAME, self.vnfd) + for x in {'user', 'password', 'ip'}: + self.assertEqual(self.vnfd['mgmt-interface'][x], + _epc_vnf.vnfd_helper.mgmt_interface[x]) + self.assertEqual(NAME, _epc_vnf.name) + self.assertEqual([], _epc_vnf.kpi) + self.assertEqual({}, _epc_vnf.config) + self.assertFalse(_epc_vnf.runs_traffic) + + def test___init__missing_ip(self, *args): + _vnfd = copy.deepcopy(self.vnfd) + _vnfd['mgmt-interface'].pop('ip') + _epc_vnf = epc_vnf.EPCVnf(NAME, _vnfd) + for x in {'user', 'password'}: + self.assertEqual(_vnfd['mgmt-interface'][x], + _epc_vnf.vnfd_helper.mgmt_interface[x]) + self.assertNotIn('ip', _epc_vnf.vnfd_helper.mgmt_interface) + self.assertEqual(NAME, _epc_vnf.name) + self.assertEqual([], _epc_vnf.kpi) + self.assertEqual({}, _epc_vnf.config) + self.assertFalse(_epc_vnf.runs_traffic) + + def test_instantiate(self): + self.assertIsNone(self.epc_vnf.instantiate({}, {})) + + def test_wait_for_instantiate(self): + self.assertIsNone(self.epc_vnf.wait_for_instantiate()) + + def test_terminate(self): + self.assertIsNone(self.epc_vnf.terminate()) + + def test_scale(self): + self.assertIsNone(self.epc_vnf.scale()) + + def test_collect_kpi(self): + self.assertIsNone(self.epc_vnf.collect_kpi()) + + def test_start_collect(self): + self.assertIsNone(self.epc_vnf.start_collect()) + + def test_stop_collect(self): + self.assertIsNone(self.epc_vnf.stop_collect()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_ipsec_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_ipsec_vnf.py new file mode 100644 index 000000000..00dc4a5d1 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_ipsec_vnf.py @@ -0,0 +1,2151 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from multiprocessing import Process + +import mock + +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.common import utils +from yardstick.network_services.helpers import cpu +from yardstick.network_services.nfvi.resource import ResourceProfile +from yardstick.network_services.vnf_generic.vnf import ipsec_vnf, vpp_helpers +from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper +from yardstick.network_services.vnf_generic.vnf.ipsec_vnf import CryptoAlg, \ + IntegAlg, VipsecApproxSetupEnvHelper +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import \ + mock_ssh + +SSH_HELPER = 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper' + +NAME = 'vnf__1' + + +class TestCryptoAlg(unittest.TestCase): + + def test__init__(self): + encr_alg = CryptoAlg.AES_GCM_128 + self.assertEqual('aes-gcm-128', encr_alg.alg_name) + self.assertEqual('AES-GCM', encr_alg.scapy_name) + self.assertEqual(20, encr_alg.key_len) + + +class TestIntegAlg(unittest.TestCase): + + def test__init__(self): + auth_alg = IntegAlg.AES_GCM_128 + self.assertEqual('aes-gcm-128', auth_alg.alg_name) + self.assertEqual('AES-GCM', auth_alg.scapy_name) + self.assertEqual(20, auth_alg.key_len) + + +@mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.Process") +class TestVipsecApproxVnf(unittest.TestCase): + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{ + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "VPP IPsec", + "id": "VipsecApproxVnf", + "mgmt-interface": { + "ip": "10.10.10.101", + "password": "r00t", + "user": "root", + "vdu-id": "ipsecvnf-baremetal" + }, + "name": "IpsecVnf", + "short-name": "IpsecVnf", + "vdu": [ + { + "description": "VPP Ipsec", + "external-interface": [ + { + "name": "xe0", + "virtual-interface": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:ff:06.0" + }, + "vnfd-connection-point-ref": "xe0" + }, + { + "name": "xe1", + "virtual-interface": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:ff:07.0" + }, + "vnfd-connection-point-ref": "xe1" + } + ], + "id": "ipsecvnf-baremetal", + "name": "ipsecvnf-baremetal", + "routing_table": [] + } + ] + } + ]}} + + VNFD_ERROR = {'vnfd:vnfd-catalog': + {'vnfd': + [{ + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "VPP IPsec", + "id": "VipsecApproxVnf", + "mgmt-interface": { + "ip": "10.10.10.101", + "password": "r00t", + "user": "root", + "vdu-id": "ipsecvnf-baremetal" + }, + "name": "IpsecVnf", + "short-name": "IpsecVnf", + "vdu": [ + { + "description": "VPP Ipsec", + "external-interface": [ + { + "name": "xe0", + "virtual-interface": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_1", + "vpci": "0000:ff:06.0" + }, + "vnfd-connection-point-ref": "xe0" + }, + { + "name": "xe1", + "virtual-interface": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:ff:07.0" + }, + "vnfd-connection-point-ref": "xe1" + } + ], + "id": "ipsecvnf-baremetal", + "name": "ipsecvnf-baremetal", + "routing_table": [] + } + ] + } + ]}} + + scenario_cfg = { + "nodes": { + "tg__0": "trafficgen.yardstick-5486cc2f", + "vnf__0": "vnf0.yardstick-5486cc2f", + "vnf__1": "vnf1.yardstick-5486cc2f" + }, + "options": { + "flow": { + "count": 1, + "dst_ip": [ + "20.0.0.0-20.0.0.100" + ], + "src_ip": [ + "10.0.0.0-10.0.0.100" + ] + }, + "framesize": { + "downlink": { + "64B": 100 + }, + "uplink": { + "64B": 100 + } + }, + "rfc2544": { + "allowed_drop_rate": "0.0 - 0.005" + }, + "tg__0": { + "collectd": { + "interval": 1 + }, + "queues_per_port": 7 + }, + "traffic_type": 4, + "vnf__0": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vnf__1": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vpp_config": { + "crypto_algorithms": "aes-gcm", + "tunnel": 1 + } + }, + "runner": { + "duration": 500, + "interval": 10, + "object": + "yardstick.benchmark.scenarios.networking.vnf_generic.NetworkServiceTestCase", + "output_config": { + "DEFAULT": { + "debug": "False", + "dispatcher": [ + "influxdb" + ] + }, + "dispatcher_file": { + "debug": "False", + "dispatcher": "influxdb", + "file_path": "/tmp/yardstick.out" + }, + "dispatcher_http": { + "debug": "False", + "dispatcher": "influxdb", + "target": "http://127.0.0.1:8000/results", + "timeout": "20" + }, + "dispatcher_influxdb": { + "db_name": "yardstick", + "debug": "False", + "dispatcher": "influxdb", + "password": "r00t", + "target": "http://192.168.100.3:8086", + "timeout": "20", + "username": "root" + }, + "nsb": { + "bin_path": "/opt/nsb_bin", + "debug": "False", + "dispatcher": "influxdb", + "trex_client_lib": "/opt/nsb_bin/trex_client/stl", + "trex_path": "/opt/nsb_bin/trex/scripts" + } + }, + "runner_id": 1105, + "type": "Duration" + }, + "task_id": "5486cc2f-d4d3-4feb-b0df-5e0bcd584c9e", + "task_path": "samples/vnf_samples/nsut/ipsec", + "tc": "tc_baremetal_rfc2544_ipv4_1flow_sw_aesgcm_4cores_64B_trex", + "topology": "vpp-tg-topology-2.yaml", + "traffic_profile": "../../traffic_profiles/ipv4_throughput_latency_vpp.yaml", + "type": "NSPerf" + } + + context_cfg = { + "networks": {}, + "nodes": { + "tg__0": { + "VNF model": "../../vnf_descriptors/tg_vpp_tpl.yaml", + "ctx_type": "Node", + "interfaces": { + "xe0": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:00:06.0" + }, + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "xe1": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "192.168.101.2", + "dst_mac": "90:e2:ba:7c:41:a9", + "ifname": "xe1", + "local_ip": "192.168.101.1", + "local_mac": "90:e2:ba:7c:30:e9", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "192.168.101.1", + "dst_mac": "90:e2:ba:7c:30:e9", + "ifname": "xe0", + "local_ip": "192.168.101.2", + "local_mac": "90:e2:ba:7c:41:a9", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "tg__0", + "vld_id": "downlink_0", + "vpci": "0000:00:06.0" + }, + "peer_name": "vnf__1", + "vld_id": "downlink_0", + "vpci": "0000:81:00.1" + } + }, + "ip": "10.10.10.10", + "member-vnf-index": "1", + "name": "trafficgen.yardstick-5486cc2f", + "password": "r00t", + "port": 22, + "role": "TrafficGen", + "user": "root", + "username": "root", + "vnfd-id-ref": "tg__0" + }, + "vnf__0": { + "VNF model": "../../vnf_descriptors/vpp_vnfd.yaml", + "ctx_type": "Node", + "interfaces": { + "xe0": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:00:06.0" + }, + "xe1": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_intf": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + } + }, + "ip": "10.10.10.101", + "member-vnf-index": "2", + "name": "vnf0.yardstick-5486cc2f", + "password": "r00t", + "port": 22, + "role": "VirtualNetworkFunction", + "user": "root", + "username": "root", + "vnfd-id-ref": "vnf__0" + }, + "vnf__1": { + "VNF model": "../../vnf_descriptors/vpp_vnfd.yaml", + "ctx_type": "Node", + "interfaces": { + "xe0": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.101.1", + "dst_mac": "90:e2:ba:7c:30:e9", + "ifname": "xe0", + "local_ip": "192.168.101.2", + "local_mac": "90:e2:ba:7c:41:a9", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_intf": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "192.168.101.2", + "dst_mac": "90:e2:ba:7c:41:a9", + "ifname": "xe1", + "local_ip": "192.168.101.1", + "local_mac": "90:e2:ba:7c:30:e9", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__1", + "vld_id": "downlink_0", + "vpci": "0000:81:00.1" + }, + "peer_name": "tg__0", + "vld_id": "downlink_0", + "vpci": "0000:00:06.0" + }, + "xe1": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_intf": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + } + }, + "ip": "10.10.10.102", + "member-vnf-index": "3", + "name": "vnf1.yardstick-5486cc2f", + "password": "r00t", + "port": 22, + "role": "VirtualNetworkFunction", + "user": "root", + "username": "root", + "vnfd-id-ref": "vnf__1" + } + } + } + + def test___init__(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + self.assertIsNone(vipsec_vnf._vnf_process) + + @mock.patch(SSH_HELPER) + def test__run(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf._build_config = mock.MagicMock() + vipsec_vnf.setup_helper.kill_vnf = mock.MagicMock() + vipsec_vnf.setup_helper.create_ipsec_tunnels = mock.MagicMock() + vipsec_vnf.queue_wrapper = mock.MagicMock() + vipsec_vnf.scenario_helper.scenario_cfg = self.scenario_cfg + vipsec_vnf.vnf_cfg = {'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1} + vipsec_vnf.all_options = {'traffic_type': '4', + 'topology': 'nsb_test_case.yaml'} + vipsec_vnf._run() + # vipsec_vnf.setup_helper.ssh_helper.execute.assert_called_once() + + @mock.patch(SSH_HELPER) + def test_wait_for_instantiate(self, ssh, *args): + mock_ssh(ssh) + + mock_process = mock.Mock(autospec=Process) + mock_process.is_alive.return_value = True + mock_process.exitcode = 432 + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf.resource_helper.resource = mock.MagicMock() + vipsec_vnf.setup_helper = mock.MagicMock() + vipsec_vnf.setup_helper.check_status.return_value = True + vipsec_vnf._vnf_process = mock_process + vipsec_vnf.WAIT_TIME = 0 + self.assertEqual(vipsec_vnf.wait_for_instantiate(), 432) + + @mock.patch(SSH_HELPER) + def test_wait_for_instantiate_crash(self, ssh, *args): + mock_ssh(ssh) + + mock_process = mock.Mock(autospec=Process) + mock_process.is_alive.return_value = False + mock_process.exitcode = 432 + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf.resource_helper.resource = mock.MagicMock() + vipsec_vnf.setup_helper = mock.MagicMock() + vipsec_vnf.setup_helper.check_status.return_value = False + vipsec_vnf._vnf_process = mock_process + vipsec_vnf.WAIT_TIME = 0 + vipsec_vnf.WAIT_TIME_FOR_SCRIPT = 0 + + with self.assertRaises(RuntimeError) as raised: + vipsec_vnf.wait_for_instantiate() + + self.assertIn('VNF process died', str(raised.exception)) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', + return_value='mock_node') + @mock.patch.object(ipsec_vnf.VipsecApproxSetupEnvHelper, + 'get_vpp_statistics', + return_value={'packets_in': 0, 'packets_fwd': 0, + 'packets_dropped': 0}) + @mock.patch(SSH_HELPER) + def test_collect_kpi(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf.scenario_helper.scenario_cfg = { + 'nodes': {vipsec_vnf.name: "mock"} + } + result = { + 'collect_stats': {'packets_in': 0, 'packets_fwd': 0, + 'packets_dropped': 0}, + 'physical_node': 'mock_node' + } + self.assertEqual(result, vipsec_vnf.collect_kpi()) + + @mock.patch.object(utils, 'find_relative_file') + @mock.patch( + "yardstick.network_services.vnf_generic.vnf.sample_vnf.Context") + @mock.patch(SSH_HELPER) + def test_instantiate(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf.deploy_helper = mock.MagicMock() + vipsec_vnf.resource_helper = mock.MagicMock() + vipsec_vnf._build_config = mock.MagicMock() + vipsec_vnf.WAIT_TIME = 0 + self.scenario_cfg.update({"nodes": {"vnf__1": ""}}) + self.assertIsNone(vipsec_vnf.instantiate(self.scenario_cfg, + self.context_cfg)) + + @mock.patch.object(ipsec_vnf.VipsecApproxSetupEnvHelper, 'kill_vnf', + return_value='') + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.time") + @mock.patch(SSH_HELPER) + def test_terminate(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vipsec_vnf = ipsec_vnf.VipsecApproxVnf(NAME, vnfd) + vipsec_vnf._vnf_process = mock.MagicMock() + vipsec_vnf._vnf_process.terminate = mock.Mock() + self.assertIsNone(vipsec_vnf.terminate()) + + +class TestVipsecApproxSetupEnvHelper(unittest.TestCase): + ALL_OPTIONS = { + "flow": { + "count": 1, + "dst_ip": [ + "20.0.0.0-20.0.0.100" + ], + "src_ip": [ + "10.0.0.0-10.0.0.100" + ] + }, + "framesize": { + "downlink": { + "64B": 100 + }, + "uplink": { + "64B": 100 + } + }, + "rfc2544": { + "allowed_drop_rate": "0.0 - 0.005" + }, + "tg__0": { + "collectd": { + "interval": 1 + }, + "queues_per_port": 7 + }, + "traffic_type": 4, + "vnf__0": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vnf__1": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vpp_config": { + "crypto_algorithms": "aes-gcm", + "tunnel": 1 + } + } + + ALL_OPTIONS_CBC_ALGORITHMS = { + "flow": { + "count": 1, + "dst_ip": [ + "20.0.0.0-20.0.0.100" + ], + "src_ip": [ + "10.0.0.0-10.0.0.100" + ] + }, + "framesize": { + "downlink": { + "64B": 100 + }, + "uplink": { + "64B": 100 + } + }, + "rfc2544": { + "allowed_drop_rate": "0.0 - 0.005" + }, + "tg__0": { + "collectd": { + "interval": 1 + }, + "queues_per_port": 7 + }, + "traffic_type": 4, + "vnf__0": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vnf__1": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vpp_config": { + "crypto_algorithms": "cbc-sha1", + "tunnel": 1 + } + } + + ALL_OPTIONS_ERROR = { + "flow_error": { + "count": 1, + "dst_ip": [ + "20.0.0.0-20.0.0.100" + ], + "src_ip": [ + "10.0.0.0-10.0.0.100" + ] + }, + "framesize": { + "downlink": { + "64B": 100 + }, + "uplink": { + "64B": 100 + } + }, + "rfc2544": { + "allowed_drop_rate": "0.0 - 0.005" + }, + "tg__0": { + "collectd": { + "interval": 1 + }, + "queues_per_port": 7 + }, + "traffic_type": 4, + "vnf__0": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vnf__1": { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + }, + "vpp_config": { + "crypto_algorithms": "aes-gcm", + "tunnel": 1 + } + } + + OPTIONS = { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "SW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + } + + OPTIONS_HW = { + "collectd": { + "interval": 1 + }, + "vnf_config": { + "crypto_type": "HW_cryptodev", + "rxq": 1, + "worker_config": "1C/1T", + "worker_threads": 4 + } + } + + CPU_LAYOUT = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 1, 1, 0], + [2, 1, 0, 0, 0, 2, 2, 1], + [3, 1, 0, 0, 0, 3, 3, 1], + [4, 2, 0, 0, 0, 4, 4, 2], + [5, 2, 0, 0, 0, 5, 5, 2], + [6, 3, 0, 0, 0, 6, 6, 3], + [7, 3, 0, 0, 0, 7, 7, 3], + [8, 4, 0, 0, 0, 8, 8, 4], + [9, 5, 0, 1, 0, 9, 9, 4], + [10, 6, 0, 1, 0, 10, 10, 5], + [11, 6, 0, 1, 0, 11, 11, 5], + [12, 7, 0, 1, 0, 12, 12, 6], + [13, 7, 0, 1, 0, 13, 13, 6], + [14, 8, 0, 1, 0, 14, 14, 7], + [15, 8, 0, 1, 0, 15, 15, 7], + [16, 9, 0, 1, 0, 16, 16, 8], + [17, 9, 0, 1, 0, 17, 17, 8]]} + CPU_SMT = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 1, 1, 0], + [2, 1, 0, 0, 0, 2, 2, 1], + [3, 1, 0, 0, 0, 3, 3, 1], + [4, 2, 0, 0, 0, 4, 4, 2], + [5, 2, 0, 0, 0, 5, 5, 2], + [6, 3, 0, 0, 0, 6, 6, 3], + [7, 3, 0, 0, 0, 7, 7, 3], + [8, 4, 0, 0, 0, 8, 8, 4], + [9, 5, 0, 1, 0, 0, 0, 0], + [10, 6, 0, 1, 0, 1, 1, 0], + [11, 6, 0, 1, 0, 2, 2, 1], + [12, 7, 0, 1, 0, 3, 3, 1], + [13, 7, 0, 1, 0, 4, 4, 2], + [14, 8, 0, 1, 0, 5, 5, 2], + [15, 8, 0, 1, 0, 6, 6, 3], + [16, 9, 0, 1, 0, 7, 7, 3], + [17, 9, 0, 1, 0, 8, 8, 4]]} + + VPP_INTERFACES_DUMP = [ + { + "sw_if_index": 0, + "sup_sw_if_index": 0, + "l2_address_length": 0, + "l2_address": [0, 0, 0, 0, 0, 0, 0, 0], + "interface_name": "local0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 0, + "link_speed": 0, + "mtu": 0, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length": 6, + "l2_address": [144, 226, 186, 124, 65, 168, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 2, + "sup_sw_if_index": 2, + "l2_address_length": 6, + "l2_address": [78, 144, 133, 211, 197, 19, 0, 0], + "interface_name": "VirtualFunctionEthernetff/7/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9206, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + } + ] + + VPP_INTERFACES_STATUS = \ + ' Name Idx State MTU (L3/IP4/IP6/MPLS)' \ + 'Counter Count \n' \ + 'TenGigabitEthernetff/6/0 1 up 9000/0/0/0 \n' \ + 'VirtualFunctionEthernetff/7/0 2 up 9000/0/0/0 \n' \ + 'ipsec0 2 up 9000/0/0/0 \n' \ + 'local0 0 down 0/0/0/0 ' + + VPP_INTERFACES_STATUS_FALSE = \ + ' Name Idx State MTU (L3/IP4/IP6/MPLS)' \ + 'Counter Count \n' \ + 'TenGigabitEthernetff/6/0 1 down 9000/0/0/0 \n' \ + 'VirtualFunctionEthernetff/7/0 2 down 9000/0/0/0 \n' \ + 'ipsec0 2 down 9000/0/0/0 \n' \ + 'local0 0 down 0/0/0/0 ' + + def test__get_crypto_type(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('SW_cryptodev', + ipsec_approx_setup_helper._get_crypto_type()) + + def test__get_crypto_algorithms(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('aes-gcm', + ipsec_approx_setup_helper._get_crypto_algorithms()) + + def test__get_n_tunnels(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(1, ipsec_approx_setup_helper._get_n_tunnels()) + + def test__get_n_connections(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(1, ipsec_approx_setup_helper._get_n_connections()) + + def test__get_n_connections_error(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS_ERROR + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with self.assertRaises(KeyError) as raised: + ipsec_approx_setup_helper._get_n_connections() + self.assertIn( + 'Missing flow definition in scenario section of the task definition file', + str(raised.exception)) + + def test__get_flow_src_start_ip(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('10.0.0.0', + ipsec_approx_setup_helper._get_flow_src_start_ip()) + + def test__get_flow_src_start_ip_vnf1(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD_ERROR['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('20.0.0.0', + ipsec_approx_setup_helper._get_flow_src_start_ip()) + + def test__get_flow_src_start_ip_error(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS_ERROR + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with self.assertRaises(KeyError) as raised: + ipsec_approx_setup_helper._get_flow_src_start_ip() + self.assertIn( + 'Missing flow definition in scenario section of the task definition file', + str(raised.exception)) + + def test__get_flow_dst_start_ip(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('20.0.0.0', + ipsec_approx_setup_helper._get_flow_dst_start_ip()) + + def test__get_flow_dst_start_ip_vnf1(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD_ERROR['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual('10.0.0.0', + ipsec_approx_setup_helper._get_flow_dst_start_ip()) + + def test__get_flow_dst_start_ip_error(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.all_options = self.ALL_OPTIONS_ERROR + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with self.assertRaises(KeyError) as raised: + ipsec_approx_setup_helper._get_flow_dst_start_ip() + self.assertIn( + 'Missing flow definition in scenario section of the task definition file', + str(raised.exception)) + + def test_build_config(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.build_config()) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual('TenGigabitEthernetff/6/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_name')) + self.assertEqual(1, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_sw_index')) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + self.assertEqual('VirtualFunctionEthernetff/7/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_name')) + self.assertEqual(2, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_sw_index')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 4) + + def test_build_config_cbc_algorithms(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS_CBC_ALGORITHMS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.build_config()) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual('TenGigabitEthernetff/6/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_name')) + self.assertEqual(1, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_sw_index')) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + self.assertEqual('VirtualFunctionEthernetff/7/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_name')) + self.assertEqual(2, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_sw_index')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 4) + + @mock.patch.object(utils, 'setup_hugepages') + def test_setup_vnf_environment(self, *args): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.nodes = [None, None] + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + self.assertIsInstance( + ipsec_approx_setup_helper.setup_vnf_environment(), + ResourceProfile) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual('TenGigabitEthernetff/6/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_name')) + self.assertEqual(1, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_sw_index')) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + self.assertEqual('VirtualFunctionEthernetff/7/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_name')) + self.assertEqual(2, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_sw_index')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 4) + + @mock.patch.object(utils, 'setup_hugepages') + def test_setup_vnf_environment_hw(self, *args): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.nodes = [None, None] + scenario_helper.options = self.OPTIONS_HW + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + self.assertIsInstance( + ipsec_approx_setup_helper.setup_vnf_environment(), + ResourceProfile) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual('TenGigabitEthernetff/6/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_name')) + self.assertEqual(1, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe0', 'vpp_sw_index')) + self.assertEqual(0, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + self.assertEqual('VirtualFunctionEthernetff/7/0', + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_name')) + self.assertEqual(2, + ipsec_approx_setup_helper.get_value_by_interface_key( + 'xe1', 'vpp_sw_index')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 4) + + def test_calculate_frame_size(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(16984 / 48, + ipsec_approx_setup_helper.calculate_frame_size( + {'64B': 28, '570B': 16, '1518B': 4})) + + def test_calculate_frame_size_64(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(64, + ipsec_approx_setup_helper.calculate_frame_size({})) + + def test_calculate_frame_size_64_error(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(64, + ipsec_approx_setup_helper.calculate_frame_size( + {'64B': -28, '570B': 16, '1518B': 4})) + + def test_check_status(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, self.VPP_INTERFACES_STATUS, '' + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertTrue(ipsec_approx_setup_helper.check_status()) + + def test_check_status_false(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, self.VPP_INTERFACES_STATUS_FALSE, '' + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertFalse(ipsec_approx_setup_helper.check_status()) + + def test_get_vpp_statistics(self): + def execute(cmd): + if 'TenGigabitEthernetff/6/0' in cmd: + return 0, output_xe0, '' + elif 'VirtualFunctionEthernetff/7/0' in cmd: + return 0, output_xe1, '' + return 0, '0', '' + + output_xe0 = \ + ' Name Idx State MTU (L3/IP4/IP6/MPLS)' \ + ' Counter Count \n' \ + 'TenGigabitEthernetff/6/0 1 up 9200/0/0/0 ' \ + 'rx packets 23373568\n' \ + ' ' \ + 'rx bytes 1402414080\n' \ + ' ' \ + 'tx packets 20476416\n' \ + ' ' \ + 'tx bytes 1228584960\n' \ + ' ' \ + 'ip4 23373568\n' \ + ' ' \ + 'rx-miss 27789925' + output_xe1 = \ + ' Name Idx State MTU (L3/IP4/IP6/MPLS)' \ + ' Counter Count \n' \ + 'VirtualFunctionEthernetff/7/0 2 up 9200/0/0/0 ' \ + 'rx packets 23373568\n' \ + ' ' \ + 'rx bytes 1402414080\n' \ + ' ' \ + 'tx packets 20476416\n' \ + ' ' \ + 'tx bytes 1228584960\n' \ + ' ' \ + 'ip4 23373568\n' \ + ' ' \ + 'rx-miss 27789925' + + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute = execute + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertEqual({'xe0': {'packets_dropped': 27789925, + 'packets_fwd': 20476416, + 'packets_in': 23373568}, + 'xe1': {'packets_dropped': 27789925, + 'packets_fwd': 20476416, + 'packets_in': 23373568}}, + ipsec_approx_setup_helper.get_vpp_statistics()) + + def test_parser_vpp_stats(self): + output = \ + ' Name Idx State MTU (L3/IP4/IP6/MPLS)' \ + 'Counter Count \n' \ + 'TenGigabitEthernetff/6/0 1 up 9200/0/0/0 ' \ + 'rx packets 23373568\n' \ + ' ' \ + 'rx bytes 1402414080\n' \ + ' ' \ + 'tx packets 20476416\n' \ + ' ' \ + 'tx bytes 1228584960\n' \ + ' ' \ + 'ip4 23373568\n' \ + ' ' \ + 'rx-miss 27789925' + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual({'xe0': {'packets_dropped': 27789925, + 'packets_fwd': 20476416, + 'packets_in': 23373568}}, + ipsec_approx_setup_helper.parser_vpp_stats('xe0', + 'TenGigabitEthernetff/6/0', + output)) + + def test_parser_vpp_stats_no_miss(self): + output = \ + ' Name Idx State ' \ + 'Counter Count \n' \ + 'TenGigabitEthernetff/6/0 1 up ' \ + 'rx packets 23373568\n' \ + ' ' \ + 'rx bytes 1402414080\n' \ + ' ' \ + 'tx packets 20476416\n' \ + ' ' \ + 'tx bytes 1228584960\n' \ + ' ' \ + 'ip4 23373568' + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual({'xe0': {'packets_dropped': 2897152, + 'packets_fwd': 20476416, + 'packets_in': 23373568}}, + ipsec_approx_setup_helper.parser_vpp_stats('xe0', + 'TenGigabitEthernetff/6/0', + output)) + + def test_create_ipsec_tunnels(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out, \ + mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template, \ + mock.patch.object(ipsec_approx_setup_helper, + 'vpp_get_interface_data') as \ + mock_ipsec_approx_setup_helper: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + mock_vat_terminal_exec_cmd_from_template.return_value = self.VPP_INTERFACES_DUMP + mock_ipsec_approx_setup_helper.return_value = self.VPP_INTERFACES_DUMP + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.create_ipsec_tunnels()) + self.assertGreaterEqual( + mock_vat_terminal_exec_cmd_from_template.call_count, 9) + + def test_create_ipsec_tunnels_cbc_algorithms(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS_CBC_ALGORITHMS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out, \ + mock.patch.object(ipsec_approx_setup_helper, + 'find_encrypted_data_interface') as \ + mock_find_encrypted_data_interface, \ + mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template, \ + mock.patch.object(ipsec_approx_setup_helper, + 'vpp_get_interface_data') as \ + mock_ipsec_approx_setup_helper: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + mock_find_encrypted_data_interface.return_value = { + 'dpdk_port_num': 0, + 'driver': 'igb_uio', + 'dst_ip': '192.168.100.1', + 'dst_mac': '90:e2:ba:7c:30:e8', + 'ifname': 'xe0', + 'local_ip': '192.168.100.2', + 'local_mac': '90:e2:ba:7c:41:a8', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'vnf__1', + 'numa_node': 0, + 'peer_ifname': 'xe0', + 'peer_intf': {'dpdk_port_num': 0, + 'driver': 'igb_uio', + 'dst_ip': '192.168.100.2', + 'dst_mac': '90:e2:ba:7c:41:a8', + 'ifname': 'xe0', + 'local_ip': '192.168.100.1', + 'local_mac': '90:e2:ba:7c:30:e8', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'tg__0', + 'peer_ifname': 'xe0', + 'peer_name': 'vnf__0', + 'vld_id': 'uplink_0', + 'vpci': '0000:81:00.0'}, + 'peer_name': 'tg__0', + 'vld_id': 'uplink_0', + 'vpci': '0000:ff:06.0', + 'vpp_name': u'TenGigabitEthernetff/6/0', + 'vpp_sw_index': 1} + mock_vat_terminal_exec_cmd_from_template.return_value = self.VPP_INTERFACES_DUMP + mock_ipsec_approx_setup_helper.return_value = self.VPP_INTERFACES_DUMP + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.create_ipsec_tunnels()) + self.assertGreaterEqual( + mock_vat_terminal_exec_cmd_from_template.call_count, 9) + + def test_find_raw_data_interface(self): + expected = {'dpdk_port_num': 0, + 'driver': 'igb_uio', + 'dst_ip': '192.168.100.1', + 'dst_mac': '90:e2:ba:7c:30:e8', + 'ifname': 'xe0', + 'local_ip': '192.168.100.2', + 'local_mac': '90:e2:ba:7c:41:a8', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'vnf__0', + 'numa_node': 0, + 'peer_ifname': 'xe0', + 'peer_intf': {'dpdk_port_num': 0, + 'driver': 'igb_uio', + 'dst_ip': '192.168.100.2', + 'dst_mac': '90:e2:ba:7c:41:a8', + 'ifname': 'xe0', + 'local_ip': '192.168.100.1', + 'local_mac': '90:e2:ba:7c:30:e8', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'tg__0', + 'peer_ifname': 'xe0', + 'peer_name': 'vnf__0', + 'vld_id': 'uplink_0', + 'vpci': '0000:81:00.0'}, + 'peer_name': 'tg__0', + 'vld_id': 'uplink_0', + 'vpci': '0000:ff:06.0', + 'vpp_name': u'TenGigabitEthernetff/6/0', + 'vpp_sw_index': 1} + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(expected, + ipsec_approx_setup_helper.find_raw_data_interface()) + + def test_find_raw_data_interface_error(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD_ERROR['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + with self.assertRaises(KeyError): + ipsec_approx_setup_helper.find_raw_data_interface() + + def test_find_encrypted_data_interface(self): + expected = {'dpdk_port_num': 1, + 'driver': 'igb_uio', + 'dst_ip': '1.1.1.2', + 'dst_mac': '0a:b1:ec:fd:a2:66', + 'ifname': 'xe1', + 'local_ip': '1.1.1.1', + 'local_mac': '4e:90:85:d3:c5:13', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'vnf__0', + 'numa_node': 0, + 'peer_ifname': 'xe1', + 'peer_intf': {'driver': 'igb_uio', + 'dst_ip': '1.1.1.1', + 'dst_mac': '4e:90:85:d3:c5:13', + 'ifname': 'xe1', + 'local_ip': '1.1.1.2', + 'local_mac': '0a:b1:ec:fd:a2:66', + 'netmask': '255.255.255.0', + 'network': {}, + 'node_name': 'vnf__1', + 'peer_ifname': 'xe1', + 'peer_name': 'vnf__0', + 'vld_id': 'ciphertext', + 'vpci': '0000:00:07.0'}, + 'peer_name': 'vnf__1', + 'vld_id': 'ciphertext', + 'vpci': '0000:ff:07.0', + 'vpp_name': u'VirtualFunctionEthernetff/7/0', + 'vpp_sw_index': 2} + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + self.assertEqual(expected, + ipsec_approx_setup_helper.find_encrypted_data_interface()) + + def test_create_startup_configuration_of_vpp(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsInstance( + ipsec_approx_setup_helper.create_startup_configuration_of_vpp(), + vpp_helpers.VppConfigGenerator) + + def test_add_worker_threads_and_rxqueues(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone( + ipsec_approx_setup_helper.add_worker_threads_and_rxqueues( + vpp_config_generator, 1, 1)) + self.assertEqual( + 'cpu\n{\n corelist-workers 2\n main-core 1\n}\ndpdk\n{\n ' \ + 'dev default\n {\n num-rx-queues 1\n }\n num-mbufs 32768\n}\n', + vpp_config_generator.dump_config()) + + def test_add_worker_threads_and_rxqueues_smt(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_SMT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_SMT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone( + ipsec_approx_setup_helper.add_worker_threads_and_rxqueues( + vpp_config_generator, 1)) + self.assertEqual( + 'cpu\n{\n corelist-workers 2,6\n main-core 1\n}\ndpdk\n{\n ' \ + 'dev default\n {\n num-rx-queues 1\n }\n num-mbufs 32768\n}\n', + vpp_config_generator.dump_config()) + + def test_add_worker_threads_and_rxqueues_with_numa(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone( + ipsec_approx_setup_helper.add_worker_threads_and_rxqueues( + vpp_config_generator, 1, 1)) + self.assertEqual( + 'cpu\n{\n corelist-workers 2\n main-core 1\n}\ndpdk\n{\n ' \ + 'dev default\n {\n num-rx-queues 1\n }\n num-mbufs 32768\n}\n', + vpp_config_generator.dump_config()) + + def test_add_pci_devices(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.add_pci_devices( + vpp_config_generator)) + self.assertEqual( + 'dpdk\n{\n dev 0000:ff:06.0 \n dev 0000:ff:07.0 \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_cryptodev(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.add_dpdk_cryptodev( + vpp_config_generator, 'aesni_gcm', 1)) + self.assertEqual( + 'dpdk\n{\n vdev cryptodev_aesni_gcm_pmd,socket_id=0 \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_cryptodev_hw(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS_HW + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.add_dpdk_cryptodev( + vpp_config_generator, 'aesni_gcm', 1)) + self.assertEqual( + 'dpdk\n{\n dev 0000:ff:01.0 \n uio-driver igb_uio\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_cryptodev_smt_used(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out: + mock_get_cpu_layout.return_value = self.CPU_SMT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_LAYOUT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.add_dpdk_cryptodev( + vpp_config_generator, 'aesni_gcm', 1)) + self.assertEqual( + 'dpdk\n{\n vdev cryptodev_aesni_gcm_pmd,socket_id=0 \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_cryptodev_smt_used_hw(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS_HW + scenario_helper.all_options = self.ALL_OPTIONS + vpp_config_generator = vpp_helpers.VppConfigGenerator() + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout: + mock_get_cpu_layout.return_value = self.CPU_SMT + ipsec_approx_setup_helper.sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper.sys_cores.cpuinfo = self.CPU_SMT + ipsec_approx_setup_helper._update_vnfd_helper( + ipsec_approx_setup_helper.sys_cores.get_cpu_layout()) + self.assertIsNone(ipsec_approx_setup_helper.add_dpdk_cryptodev( + vpp_config_generator, 'aesni_gcm', 1)) + self.assertEqual( + 'dpdk\n{\n dev 0000:ff:01.0 \n dev 0000:ff:01.1 \n uio-driver igb_uio\n}\n', + vpp_config_generator.dump_config()) + + def test_initialize_ipsec(self): + vnfd_helper = VnfdHelper( + TestVipsecApproxVnf.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + scenario_helper.all_options = self.ALL_OPTIONS + + ipsec_approx_setup_helper = VipsecApproxSetupEnvHelper(vnfd_helper, + ssh_helper, + scenario_helper) + + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout, \ + mock.patch.object(ipsec_approx_setup_helper, + 'execute_script_json_out') as \ + mock_execute_script_json_out, \ + mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template, \ + mock.patch.object(ipsec_approx_setup_helper, + 'vpp_get_interface_data') as \ + mock_ipsec_approx_setup_helper: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + mock_execute_script_json_out.return_value = str( + self.VPP_INTERFACES_DUMP).replace("\'", "\"") + mock_vat_terminal_exec_cmd_from_template.return_value = '' + mock_ipsec_approx_setup_helper.return_value = self.VPP_INTERFACES_DUMP + sys_cores = cpu.CpuSysCores(ssh_helper) + ipsec_approx_setup_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + ipsec_approx_setup_helper.update_vpp_interface_data() + ipsec_approx_setup_helper.iface_update_numa() + self.assertIsNone(ipsec_approx_setup_helper.initialize_ipsec()) + self.assertGreaterEqual( + mock_vat_terminal_exec_cmd_from_template.call_count, 9) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_prox_helpers.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_prox_helpers.py new file mode 100644 index 000000000..32f384027 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_prox_helpers.py @@ -0,0 +1,2825 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from itertools import repeat, chain +import os +import socket +import time + +import mock +import unittest + +from yardstick.common import utils +from yardstick.network_services import constants +from yardstick.network_services.vnf_generic.vnf import base as vnf_base +from yardstick.network_services.vnf_generic.vnf import prox_helpers +from yardstick.network_services.vnf_generic.vnf import sample_vnf + + +class TestCoreTuple(unittest.TestCase): + def test___init__(self): + core_tuple = prox_helpers.CoreSocketTuple('core 5s6') + self.assertEqual(core_tuple.core_id, 5) + self.assertEqual(core_tuple.socket_id, 6) + self.assertFalse(core_tuple.is_hyperthread()) + + core_tuple = prox_helpers.CoreSocketTuple('core 5s6h') + self.assertEqual(core_tuple.core_id, 5) + self.assertEqual(core_tuple.socket_id, 6) + self.assertTrue(core_tuple.is_hyperthread()) + + def test___init__negative(self): + bad_inputs = [ + '', + '5', + '5s', + '6h', + '5s6', + 'core', + 'core h', + 'core 5s', + 'core 5 6', + 'core 5 6h', + 'core 5d6', + 'core 5d6h', + 1, + 2.3, + [], + {}, + object(), + ] + + for bad_input in bad_inputs: + with self.assertRaises(ValueError): + prox_helpers.CoreSocketTuple(bad_input) + + def test_find_in_topology(self): + topology_in = { + 6: { + 5: { + 'key1': ['a', 'b'], + 'key2': ['c', 'd'], + }, + }, + } + + core_tuple = prox_helpers.CoreSocketTuple('core 5s6') + + expected = 'a' + result = core_tuple.find_in_topology(topology_in) + self.assertEqual(result, expected) + + core_tuple = prox_helpers.CoreSocketTuple('core 5s6h') + + expected = 'c' + result = core_tuple.find_in_topology(topology_in) + self.assertEqual(result, expected) + + def test_find_in_topology_negative(self): + core_tuple = prox_helpers.CoreSocketTuple('core 6s5') + with self.assertRaises(ValueError): + # no socket key + core_tuple.find_in_topology({}) + + with self.assertRaises(ValueError): + # no core key + core_tuple.find_in_topology({5: {}}) + + with self.assertRaises(ValueError): + # no first value (as needed by non-hyperthread core) + core_tuple.find_in_topology({5: {6: {'key1': []}}}) + + core_tuple = prox_helpers.CoreSocketTuple('core 6s5h') + with self.assertRaises(ValueError): + # no second value (as needed by hyperthread core) + core_tuple.find_in_topology({5: {6: {'key1': ['e']}}}) + + +class TestTotStatsTuple(unittest.TestCase): + def test___new___negative(self): + with self.assertRaises(TypeError): + # no values + prox_helpers.TotStatsTuple() + + with self.assertRaises(TypeError): + # one, non-integer value + prox_helpers.TotStatsTuple('a') + + with self.assertRaises(TypeError): + # too many values + prox_helpers.TotStatsTuple(3, 4, 5, 6, 7) + + +class TestProxTestDataTuple(unittest.TestCase): + def test___init__(self): + prox_test_data = prox_helpers.ProxTestDataTuple( + 1, 2, 3, 4, 5, 6, 7, 8, 9) + self.assertEqual(prox_test_data.tolerated, 1) + self.assertEqual(prox_test_data.tsc_hz, 2) + self.assertEqual(prox_test_data.delta_rx, 3) + self.assertEqual(prox_test_data.delta_tx, 4) + self.assertEqual(prox_test_data.delta_tsc, 5) + self.assertEqual(prox_test_data.latency, 6) + self.assertEqual(prox_test_data.rx_total, 7) + self.assertEqual(prox_test_data.tx_total, 8) + self.assertEqual(prox_test_data.requested_pps, 9) + + def test_properties(self): + prox_test_data = prox_helpers.ProxTestDataTuple( + 1, 2, 3, 4, 5, 6, 7, 8, 9) + self.assertEqual(prox_test_data.pkt_loss, 12.5) + self.assertEqual(prox_test_data.tx_mpps, 1.6 / 1e6) + self.assertEqual(prox_test_data.can_be_lost, 0) + self.assertEqual(prox_test_data.drop_total, 1) + self.assertFalse(prox_test_data.success) + + prox_test_data = prox_helpers.ProxTestDataTuple( + 10, 2, 3, 4, 5, 6, 997, 998, 9) + self.assertTrue(prox_test_data.success) + + def test_pkt_loss_zero_division(self): + prox_test_data = prox_helpers.ProxTestDataTuple( + 1, 2, 3, 4, 5, 6, 7, 0, 9) + self.assertEqual(prox_test_data.pkt_loss, 100.0) + + def test_get_samples(self): + prox_test_data = prox_helpers.ProxTestDataTuple( + 1, 2, 3, 4, 5, [6.1, 6.9, 6.4], 7, 8, 9) + + expected = { + "Throughput": 1.2 / 1e6, + "DropPackets": 12.5, + "CurrentDropPackets": 12.5, + "RequestedTxThroughput": 9 / 1e6, + "TxThroughput": 1.6 / 1e6, + "RxThroughput": 1.2 / 1e6, + "PktSize": 64, + "PortSample": 1, + "LatencyMin": 6.1, + "LatencyMax": 6.9, + "LatencyAvg": 6.4, + } + result = prox_test_data.get_samples(64, port_samples={"PortSample": 1}) + self.assertDictEqual(result, expected) + + expected = { + "Throughput": 1.2 / 1e6, + "DropPackets": 0.123, + "CurrentDropPackets": 0.123, + "RequestedTxThroughput": 9 / 1e6, + "TxThroughput": 1.6 / 1e6, + "RxThroughput": 1.2 / 1e6, + "PktSize": 64, + "LatencyMin": 6.1, + "LatencyMax": 6.9, + "LatencyAvg": 6.4, + } + result = prox_test_data.get_samples(64, 0.123) + self.assertDictEqual(result, expected) + + @mock.patch('yardstick.LOG_RESULT', create=True) + def test_log_data(self, mock_logger): + my_mock_logger = mock.MagicMock() + prox_test_data = prox_helpers.ProxTestDataTuple( + 1, 2, 3, 4, 5, [6.1, 6.9, 6.4], 7, 8, 9) + prox_test_data.log_data() + + my_mock_logger.debug.assert_not_called() + mock_logger.debug.assert_not_called() + + mock_logger.debug.reset_mock() + prox_test_data.log_data(my_mock_logger) + my_mock_logger.assert_not_called() + mock_logger.debug.assert_not_called() + + +class TestPacketDump(unittest.TestCase): + PAYLOAD = "payload" + + def test__init__(self): + prox_helpers.PacketDump("port_id", len(self.PAYLOAD), self.PAYLOAD) + + def test___str__(self): + expected = '<PacketDump port: port_id payload: {}>'.format(self.PAYLOAD) + dump1 = prox_helpers.PacketDump( + "port_id", len(self.PAYLOAD), self.PAYLOAD) + self.assertEqual(str(dump1), expected) + + def test_port_id(self): + p = prox_helpers.PacketDump("port_id", len(self.PAYLOAD), self.PAYLOAD) + self.assertEqual(p.port_id, "port_id") + + def test_data_len(self): + p = prox_helpers.PacketDump("port_id", len(self.PAYLOAD), self.PAYLOAD) + self.assertEqual(p.data_len, len(self.PAYLOAD)) + + def test_payload(self): + p = prox_helpers.PacketDump("port_id", len(self.PAYLOAD), self.PAYLOAD) + self.assertEqual(p.payload(), self.PAYLOAD) + + self.assertEqual(p.payload(3), self.PAYLOAD[3:]) + + self.assertEqual(p.payload(end=3), self.PAYLOAD[:4]) + + self.assertEqual(p.payload(2, 4), self.PAYLOAD[2:5]) + + +PACKET_DUMP_1 = """\ +pktdump,3,11 +hello world +""" + +PACKET_DUMP_2 = """\ +pktdump,3,11 +hello world +pktdump,2,9 +brown fox jumped over +pktdump,4,8 +lazy +dog +""" + +PACKET_DUMP_NON_1 = """\ +not_a_dump,1,2 +other data +""" + +PACKET_DUMP_MIXED_1 = """\ +pktdump,3,11 +hello world +not_a_dump,1,2 +other data +""" + +PACKET_DUMP_BAD_1 = """\ +pktdump,one,12 +bad port id +""" + +PACKET_DUMP_BAD_2 = """\ +pktdump,3,twelve +bad data length +""" + +PACKET_DUMP_BAD_3 = """\ +pktdump,3 +no data length value +""" + + +class TestProxSocketHelper(unittest.TestCase): + + def setUp(self): + self._mock_time_sleep = mock.patch.object(time, 'sleep') + self.mock_time_sleep = self._mock_time_sleep.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_time_sleep.stop() + + @mock.patch.object(prox_helpers, 'socket') + def test___init__(self, mock_socket): + expected = mock_socket.socket() + prox = prox_helpers.ProxSocketHelper() + result = prox._sock + self.assertEqual(result, expected) + + def test_connect(self): + mock_sock = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_sock) + prox.connect('10.20.30.40', 23456) + mock_sock.connect.assert_called_once() + + def test_get_sock(self): + mock_sock = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_sock) + result = prox.get_socket() + self.assertIs(result, mock_sock) + + # TODO(elfoley): Split this into three tests + @mock.patch.object(prox_helpers, 'select') + def test_get_data(self, mock_select): + mock_select.select.side_effect = [[1], [0]] + mock_socket = mock.MagicMock() + mock_recv = mock_socket.recv() + mock_recv.decode.return_value = "" + prox = prox_helpers.ProxSocketHelper(mock_socket) + ret = prox.get_data() + self.assertEqual(ret, "") + self.assertEqual(len(prox._pkt_dumps), 0) + + mock_select.select.reset_mock() + mock_select.select.side_effect = chain([['a'], ['']], + repeat([1], 3)) + mock_recv.decode.return_value = PACKET_DUMP_1 + ret = prox.get_data() + self.assertEqual(mock_select.select.call_count, 2) + self.assertEqual(ret, 'pktdump,3,11') + self.assertEqual(len(prox._pkt_dumps), 1) + + mock_select.select.reset_mock() + mock_select.select.side_effect = chain([[object()], [None]], + repeat([1], 3)) + mock_recv.decode.return_value = PACKET_DUMP_2 + ret = prox.get_data() + self.assertEqual(mock_select.select.call_count, 1) + self.assertEqual(ret, 'jumped over') + self.assertEqual(len(prox._pkt_dumps), 3) + + @mock.patch.object(prox_helpers, 'select') + def test_get_string(self, mock_select): + mock_select.select.side_effect = [[1], [0]] + mock_socket = mock.MagicMock() + mock_recv = mock_socket.recv() + mock_recv.decode.return_value = "" + prox = prox_helpers.ProxSocketHelper(mock_socket) + status, ret = prox.get_string() + self.assertEqual(ret, "") + self.assertTrue(status) + self.assertEqual(len(prox._pkt_dumps), 0) + + @mock.patch.object(prox_helpers, 'select') + def test_get_string2(self, mock_select): + mock_select.select.side_effect = chain([['a'], ['']], + repeat([1], 3)) + mock_socket = mock.MagicMock() + mock_recv = mock_socket.recv() + mock_recv.decode.return_value = PACKET_DUMP_1 + prox = prox_helpers.ProxSocketHelper(mock_socket) + status, ret = prox.get_string() + self.assertEqual(mock_select.select.call_count, 2) + self.assertEqual(ret, 'pktdump,3,11') + self.assertTrue(status) + self.assertEqual(len(prox._pkt_dumps), 1) + + @mock.patch.object(prox_helpers, 'select') + def test_get_string3(self, mock_select): + mock_select.select.side_effect = chain([[object()], [None]], + repeat([1], 3)) + mock_socket = mock.MagicMock() + mock_recv = mock_socket.recv() + mock_recv.decode.return_value = PACKET_DUMP_2 + prox = prox_helpers.ProxSocketHelper(mock_socket) + status, ret = prox.get_string() + self.assertTrue(status) + self.assertTrue(mock_select.select.assert_called_once) + self.assertEqual(ret, 'jumped over') + self.assertEqual(len(prox._pkt_dumps), 2) + + def test__parse_socket_data_mixed_data(self): + prox = prox_helpers.ProxSocketHelper(mock.MagicMock()) + ret, _ = prox._parse_socket_data(PACKET_DUMP_NON_1, False) + self.assertEqual(ret, 'not_a_dump,1,2') + self.assertEqual(len(prox._pkt_dumps), 0) + + ret, _ = prox._parse_socket_data(PACKET_DUMP_MIXED_1, False) + self.assertEqual(ret, 'not_a_dump,1,2') + self.assertEqual(len(prox._pkt_dumps), 1) + + def test__parse_socket_data_bad_data(self): + prox = prox_helpers.ProxSocketHelper(mock.MagicMock()) + with self.assertRaises(ValueError): + prox._parse_socket_data(PACKET_DUMP_BAD_1, False) + + with self.assertRaises(ValueError): + prox._parse_socket_data(PACKET_DUMP_BAD_2, False) + + ret, _ = prox._parse_socket_data(PACKET_DUMP_BAD_3, False) + self.assertEqual(ret, 'pktdump,3') + + def test__parse_socket_data_pkt_dump_only(self): + prox = prox_helpers.ProxSocketHelper(mock.MagicMock()) + ret, _ = prox._parse_socket_data('', True) + self.assertFalse(ret) + + ret, _ = prox._parse_socket_data(PACKET_DUMP_1, True) + self.assertTrue(ret) + + ret, _ = prox._parse_socket_data(PACKET_DUMP_2, True) + self.assertTrue(ret) + + def test_put_command(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.put_command("data") + mock_socket.sendall.assert_called_once() + + def test_put_command_socket_error(self): + mock_socket = mock.MagicMock() + mock_socket.sendall.side_effect = OSError + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.put_command("data") + mock_socket.sendall.assert_called_once() + + def test_get_packet_dump(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox._pkt_dumps = [] + self.assertIsNone(prox.get_packet_dump()) + + prox._pkt_dumps = [234] + self.assertEqual(prox.get_packet_dump(), 234) + self.assertEqual(prox._pkt_dumps, []) + + def test_stop_all_reset(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.stop_all_reset() + mock_socket.sendall.assert_called() + + def test_stop_all(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.stop_all() + mock_socket.sendall.assert_called() + + def test_stop(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.stop([3, 4, 5], 16) + mock_socket.sendall.assert_called() + + def test_start_all(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.start_all() + mock_socket.sendall.assert_called() + + def test_start(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.start([3, 4, 5]) + mock_socket.sendall.assert_called() + + def test_reset_stats(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.reset_stats() + mock_socket.sendall.assert_called() + + def test_set_pkt_size(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.set_pkt_size([3, 4, 5], 1024) + self.assertEqual(mock_socket.sendall.call_count, 3) + + def test_set_value(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.set_value([3, 4, 5], 10, 20, 30) + self.assertEqual(mock_socket.sendall.call_count, 3) + + def test_reset_values(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.reset_values([3, 4, 5]) + self.assertEqual(mock_socket.sendall.call_count, 3) + + def test_set_speed(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.set_speed([3, 4, 5], 1000) + self.assertEqual(mock_socket.sendall.call_count, 3) + + def test_slope_speed(self): + core_data = [ + { + 'cores': [3, 4, 5], + 'speed': 1000, + }, + { + 'cores': [9, 10, 11], + 'speed': '500.5', + }, + ] + + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.set_speed = set_speed = mock.MagicMock() + prox.slope_speed(core_data, 5) + self.assertEqual(set_speed.call_count, 20) + + set_speed.reset_mock() + prox.slope_speed(core_data, 5, 5) + self.assertEqual(set_speed.call_count, 10) + + def test_set_pps(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.set_pps([3, 4, 5], 1000, 512) + self.assertEqual(mock_socket.sendall.call_count, 3) + + def test_lat_stats(self): + latency_output = [ + '1, 2 , 3', # has white space + '4,5', # too short + '7,8,9,10.5,11', # too long with float, but float is in unused portion + 'twelve,13,14', # value as English word + '15,16.2,17', # float in used portion + ] + + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_data = mock.MagicMock(side_effect=latency_output) + + expected = ( + { + 3: 1, + 5: 7, + }, + { + 3: 2, + 5: 8, + }, + { + 3: 3, + 5: 9, + }, + ) + result = prox.lat_stats([3, 4, 5, 6, 7], 16) + self.assertEqual(mock_socket.sendall.call_count, 5) + self.assertEqual(result, expected) + + def test_get_all_tot_stats_error(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_data = mock.MagicMock(return_value='3,4,5') + expected = [0, 0, 0, 0] + result = prox.get_all_tot_stats() + self.assertEqual(result, expected) + + def test_get_all_tot_stats(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_data = mock.MagicMock(return_value='3,4,5,6') + expected = 3, 4, 5, 6 + result = prox.get_all_tot_stats() + self.assertEqual(result, expected) + + def test_hz(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_data = mock.MagicMock(return_value='3,4,5,6') + expected = 6 + result = prox.hz() + self.assertEqual(result, expected) + + def test_core_stats(self): + core_stats = [ + '3,4,5,6', + '7,8,9,10,NaN', + '11,12,13,14,15', + ] + + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_data = mock.MagicMock(side_effect=core_stats) + expected = 21, 24, 27, 14 + result = prox.core_stats([3, 4, 5], 16) + self.assertEqual(result, expected) + + @mock.patch.object(prox_helpers.LOG, 'error') + def test_irq_core_stats(self, *args): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_data = mock.MagicMock(return_value=('0,1,2,3,4,5,0,1,2,3,4,5,0,1,2,3')) + + data_0 = {"cpu": 0, 'bucket_0': 1, 'bucket_1': 2, 'bucket_2': 3, 'bucket_3': 4, + 'bucket_4': 5, 'bucket_5': 0, 'bucket_6': 1, 'bucket_7': 2, 'bucket_8': 3, + 'bucket_9': 4, 'bucket_10': 5, 'bucket_11': 0, 'bucket_12': 1, + "max_irq": 0, "overflow": 10} + + data_1 = {"cpu": 1, 'bucket_0': 1, 'bucket_1': 2, 'bucket_2': 3, 'bucket_3': 4, + 'bucket_4': 5, 'bucket_5': 0, 'bucket_6': 1, 'bucket_7': 2, 'bucket_8': 3, + 'bucket_9': 4, 'bucket_10': 5, 'bucket_11': 0, 'bucket_12': 1, + "max_irq": 0, "overflow": 10} + + expected = {"core_0": data_0, "core_1": data_1} + + result = prox.irq_core_stats([[0, 1], [1, 0]]) + self.assertDictEqual(result, expected) + + @mock.patch.object(prox_helpers.LOG, 'error') + def test_multi_port_stats(self, *args): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_string = mock.MagicMock(return_value=(True, '0,1,2,3,4,5;1,1,2,3,4,5')) + expected = [[0, 1, 2, 3, 4, 5], [1, 1, 2, 3, 4, 5]] + status, result = prox.multi_port_stats([0, 1]) + self.assertEqual(result, expected) + self.assertEqual(status, True) + + prox.get_string = mock.MagicMock( + return_value=(True, '0,1,2,3,4,5;1,1,2,3,4,5')) + status, result = prox.multi_port_stats([0]) + self.assertEqual(status, False) + + prox.get_string = mock.MagicMock( + return_value=(True, '0,1,2,3,4,5;1,1,2,3,4,5')) + status, result = prox.multi_port_stats([0, 1, 2]) + self.assertEqual(status, False) + + prox.get_string = mock.MagicMock( + return_value=(True, '0,1,2,3;1,1,2,3,4,5')) + status, result = prox.multi_port_stats([0, 1]) + self.assertEqual(status, False) + + prox.get_string = mock.MagicMock( + return_value=(True, '99,1,2,3,4,5;1,1,2,3,4,5')) + status, result = prox.multi_port_stats([0, 1]) + self.assertEqual(status, False) + + prox.get_string = mock.MagicMock( + return_value=(True, '99,1,2,3,4,5;1,1,2,3,4,5')) + status, result = prox.multi_port_stats([99, 1]) + expected = [[99, 1, 2, 3, 4, 5], [1, 1, 2, 3, 4, 5]] + self.assertEqual(status, True) + self.assertEqual(result, expected) + + prox.get_string = mock.MagicMock( + return_value=(True, + '2,21,22,23,24,25;1,11,12,13,14,15;0,1,2,3,4,5')) + + sample1 = [0, 1, 2, 3, 4, 5] + sample2 = [1, 11, 12, 13, 14, 15] + sample3 = [2, 21, 22, 23, 24, 25] + expected = [sample3, sample2, sample1] + status, result = prox.multi_port_stats([1, 2, 0]) + self.assertTrue(status) + self.assertListEqual(result, expected) + + prox.get_string = mock.MagicMock( + return_value=(True, '6,21,22,23,24,25;1,11,12,13,14,15;0,1,2,3,4,5')) + ok, result = prox.multi_port_stats([1, 6, 0]) + sample1 = [6, 21, 22, 23, 24, 25] + sample2 = [1, 11, 12, 13, 14, 15] + sample3 = [0, 1, 2, 3, 4, 5] + expected = [sample1, sample2, sample3] + self.assertListEqual(result, expected) + self.assertTrue(ok) + + @mock.patch.object(prox_helpers.LOG, 'error') + def test_multi_port_stats_diff(self, *args): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_string = mock.MagicMock(return_value=(True, '0,1,2,3,4,5;1,1,2,3,4,5')) + _, t1 = prox.multi_port_stats([0, 1]) + + prox.get_string = mock.MagicMock(return_value=(True, '0,2,4,6,8,6;1,4,8,16,32,6')) + _, t2 = prox.multi_port_stats([0, 1]) + + prox.get_string = mock.MagicMock(return_value=(True, '0,1,1,1,1,1;1,1,1,1,1,1')) + _, t3 = prox.multi_port_stats([0, 1]) + + prox.get_string = mock.MagicMock(return_value=(True, '0,2,2,2,2,2;1,2,2,2,2,2')) + _, t4 = prox.multi_port_stats([0, 1]) + + expected = [[0, 1.0, 2.0, 0, 0, 1], [1, 3.0, 6.0, 0, 0, 1]] + result = prox.multi_port_stats_diff(t1, t2, 1) + + self.assertListEqual(result, expected) + + result = prox.multi_port_stats_diff(t4, t3, 1) + expected = [[0, 1.0, 1.0, 0, 0, 1], [1, 1.0, 1.0, 0, 0, 1]] + + self.assertListEqual(result, expected) + + prox.get_string = mock.MagicMock(return_value=(True, '0,2,4,6,8,10')) + ok, t5 = prox.multi_port_stats([0, 1]) + self.assertFalse(ok) + self.assertListEqual(t5, []) + + result = prox.multi_port_stats_diff(t5, t4, 1) + expected = [[0, 0.0, 0.0, 0, 0, 0], [1, 0.0, 0.0, 0, 0, 0]] + self.assertListEqual(result, expected) + + prox.get_string = mock.MagicMock(return_value=(True, '0,10,10,20,30,0;1,30,40,50,60,0')) + _, t6 = prox.multi_port_stats([0, 1]) + + prox.get_string = \ + mock.MagicMock(return_value=(True, '0,100,100,100,100,0;1,100,100,100,100,0')) + _, t7 = prox.multi_port_stats([0, 1]) + + result = prox.multi_port_stats_diff(t6, t7, 1) + expected = [[0, 0.0, 0.0, 0, 0, 0], [1, 0.0, 0.0, 0, 0, 0]] + self.assertListEqual(result, expected) + + result = prox.multi_port_stats_diff(t1, t2, 0) + expected = [[0, 0.0, 0.0, 0, 0, 1], [1, 0.0, 0.0, 0, 0, 1]] + self.assertListEqual(result, expected) + + @mock.patch.object(prox_helpers.LOG, 'error') + def test_multi_port_stats_tuple(self, *args): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_string = mock.MagicMock(return_value=(True, '0,1,2,3,4,5;1,1,2,3,4,5')) + _, result1 = prox.multi_port_stats([0, 1]) + prox.get_string = mock.MagicMock(return_value=(True, '0,2,4,6,8,6;1,4,8,16,32,6')) + _, result2 = prox.multi_port_stats([0, 1]) + + result = prox.multi_port_stats_diff(result1, result2, 1) + + vnfd_helper = mock.MagicMock() + vnfd_helper.ports_iter.return_value = [('xe0', 0), ('xe1', 1)] + + expected = {'xe0': {'in_packets': 1.0, 'out_packets': 2.0}, + 'xe1': {'in_packets': 3.0, 'out_packets': 6.0}} + live_stats = prox.multi_port_stats_tuple(result, vnfd_helper.ports_iter()) + self.assertDictEqual(live_stats, expected) + + live_stats = prox.multi_port_stats_tuple(result, None) + expected = {} + self.assertDictEqual(live_stats, expected) + + live_stats = prox.multi_port_stats_tuple(None, vnfd_helper.ports_iter()) + self.assertDictEqual(live_stats, expected) + + def test_port_stats(self): + port_stats = [ + ','.join(str(n) for n in range(3, 15)), + ','.join(str(n) for n in range(8, 32, 2)), + ','.join(str(n) for n in range(5, 89, 7)), + ] + + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_data = mock.MagicMock(side_effect=port_stats) + expected = [16, 26, 36, 46, 56, 66, 76, 86, 96, 106, 116, 126] + result = prox.port_stats([3, 4, 5]) + self.assertEqual(result, expected) + + def test_measure_tot_stats(self): + start_tot = 3, 4, 5, 6 + end_tot = 7, 9, 11, 13 + delta_tot = 4, 5, 6, 7 + + get_data_output = [ + ','.join(str(n) for n in start_tot), + ','.join(str(n) for n in end_tot), + ] + + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_data = mock.MagicMock(side_effect=get_data_output) + expected = { + 'start_tot': start_tot, + 'end_tot': end_tot, + 'delta': delta_tot, + } + with prox.measure_tot_stats() as result: + pass + self.assertEqual(result, expected) + + def test_tot_stats(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_data = mock.MagicMock(return_value='3,4,5,6') + expected = 3, 4, 5 + result = prox.tot_stats() + self.assertEqual(result, expected) + + def test_tot_ierrors(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.get_data = mock.MagicMock(return_value='3,4,5,6') + expected = 3, 3 + result = prox.tot_ierrors() + self.assertEqual(result, expected) + + def test_set_count(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.set_count(432, [3, 4, 5]) + self.assertEqual(mock_socket.sendall.call_count, 3) + + def test_dump_rx(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.dump_rx(3, 5, 8) + mock_socket.sendall.assert_called_once() + + def test_quit(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.quit() + mock_socket.sendall.assert_called() + + def test_force_quit(self): + mock_socket = mock.MagicMock() + prox = prox_helpers.ProxSocketHelper(mock_socket) + prox.force_quit() + mock_socket.sendall.assert_called() + + +class TestProxDpdkVnfSetupEnvHelper(unittest.TestCase): + + VNFD0 = { + 'short-name': 'ProxVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0', + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'description': 'PROX approximation using DPDK', + 'name': 'proxvnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'id': 'proxvnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'vld_id': 'uplink_0', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.19', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02', + 'ifname': 'xe0', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0', + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'vld_id': 'downlink_0', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01', + 'ifname': 'xe1', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1', + }, + ], + }, + ], + 'description': 'PROX approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'proxvnf-baremetal', + 'host': '1.2.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.2.1.1', + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'id': 'ProxApproxVnf', + 'name': 'ProxVnf', + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD0, + ], + }, + } + + def test_global_section(self): + setup_helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + mock.MagicMock(), mock.MagicMock(), mock.MagicMock()) + + setup_helper._prox_config_data = [('a', [])] + + with self.assertRaises(KeyError): + _ = setup_helper.global_section + + global_section = ( + 'global', [ + ('not_name', 'other data'), + ('name_not', 'more data'), + ('name', 'prox type'), + ], + ) + + setup_helper._prox_config_data = [ + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('core 1', []), + ('core 2', [ + ('index', 8), + ('mode', ''), + ]), + global_section, + ('core 3', [ + ('index', 5), + ('mode', 'gen'), + ('name', 'tagged'), + ]), + ('section3', [ + ('key1', 'value1'), + ('key2', 'value2'), + ('key3', 'value3'), + ]), + ('core 4', [ + ('index', 7), + ('mode', 'gen'), + ('name', 'udp'), + ]), + ] + + result = setup_helper.global_section + self.assertEqual(result, global_section[1]) + + def test_find_in_section(self): + setup_helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + mock.MagicMock(), mock.MagicMock(), mock.MagicMock()) + + setup_helper._prox_config_data = [ + ('global', [ + ('not_name', 'other data'), + ('name_not', 'more data'), + ('name', 'prox type'), + ]), + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('core 1', []), + ('core 2', [ + ('index', 8), + ('mode', ''), + ]), + ('core 3', [ + ('index', 5), + ('mode', 'gen'), + ('name', 'tagged'), + ]), + ('section3', [ + ('key1', 'value1'), + ('key2', 'value2'), + ('key3', 'value3'), + ]), + ('core 4', [ + ('index', 7), + ('mode', 'gen'), + ('name', 'udp'), + ]), + ] + + expected = 'value3' + result = setup_helper.find_in_section('section3', 'key3') + self.assertEqual(result, expected) + + expected = 'default value' + result = setup_helper.find_in_section('section3', 'key4', 'default value') + self.assertEqual(result, expected) + + with self.assertRaises(KeyError): + setup_helper.find_in_section('section4', 'key1') + + with self.assertRaises(KeyError): + setup_helper.find_in_section('section1', 'key1') + + def test__replace_quoted_with_value(self): + # empty string + input_str = '' + expected = '' + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + _replace_quoted_with_value(input_str, 'cat')) + self.assertEqual(result, expected) + + # no quoted substring + input_str = 'lion tiger bear' + expected = 'lion tiger bear' + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + _replace_quoted_with_value(input_str, 'cat')) + self.assertEqual(result, expected) + + # partially quoted substring + input_str = 'lion "tiger bear' + expected = 'lion "tiger bear' + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + _replace_quoted_with_value(input_str, 'cat')) + self.assertEqual(result, expected) + + # one quoted substring + input_str = 'lion "tiger" bear' + expected = 'lion "cat" bear' + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + _replace_quoted_with_value(input_str, 'cat')) + self.assertEqual(result, expected) + + # two quoted substrings + input_str = 'lion "tiger" bear "shark" whale' + expected = 'lion "cat" bear "shark" whale' + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + _replace_quoted_with_value(input_str, 'cat')) + self.assertEqual(result, expected) + + # two quoted substrings, both replaced + input_str = 'lion "tiger" bear "shark" whale' + expected = 'lion "cat" bear "cat" whale' + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + _replace_quoted_with_value(input_str, 'cat', 2)) + self.assertEqual(result, expected) + + def test__get_tx_port(self): + # no data + input_data = {'section1': []} + expected = -1 + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + _get_tx_port('section1', input_data)) + self.assertEqual(result, expected) + + # data for other section + input_data = { + 'section1': [], + 'section2': [ + ('rx port', '3'), + ('tx port', '4'), + ], + } + expected = -1 + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + _get_tx_port('section1', input_data)) + self.assertEqual(result, expected) + + # data for section + input_data['section1'] = section1 = [ + ('rx port', '4', 'more', 432), + ('tx port', '3'), + ] + expected = 3 + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + _get_tx_port('section1', input_data)) + self.assertEqual(result, expected) + + # more data for section, + section1.extend([ + ('rx port', '2'), + ('tx port', '1', 'and more', 234), + ]) + expected = 1 + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + _get_tx_port('section1', input_data)) + self.assertEqual(result, expected) + + # TODO(elfoley): Split this into several smaller tests + def test_write_prox_config(self): + input_data = {} + expected = '' + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + write_prox_config(input_data)) + self.assertEqual(result, expected) + + input_data = [ + [ + 'section1', + [], + ], + ] + expected = '[section1]' + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + write_prox_config(input_data)) + self.assertEqual(result, expected) + + input_data = [ + [ + 'section1', + [], + ], + [ + 'section2', + [ + ['key1', 'value1'], + ['__name__', 'not this one'], + ['key2', None], + ['key3', 234], + ['key4', 'multi-line\nvalue'], + ], + ], + ] + expected = os.linesep.join([ + '[section1]', + '[section2]', + 'key1=value1', + 'key2', + 'key3=234', + 'key4=multi-line\n\tvalue', + ]) + result = (prox_helpers.ProxDpdkVnfSetupEnvHelper. + write_prox_config(input_data)) + self.assertEqual(result, expected) + + def test_prox_config_data(self): + setup_helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + mock.MagicMock(), mock.MagicMock(), mock.MagicMock()) + + setup_helper.config_queue = config_queue = mock.MagicMock() + config_queue.get.return_value = expected = [('s', [('a', 3), ('b', 45)])] + + result = setup_helper.prox_config_data + self.assertEqual(result, expected) + + @mock.patch.object(utils, 'find_relative_file') + def test_build_config_file_no_additional_file(self, mock_find_path): + vnf1 = { + 'prox_args': {'-c': ""}, + 'prox_path': 'd', + 'prox_config': 'e/f', + 'prox_generate_parameter': False, + } + + mock_find_path.side_effect = ['1', '2'] + + vnfd_helper = mock.MagicMock() + ssh_helper = mock.MagicMock() + scenario_helper = sample_vnf.ScenarioHelper('vnf1') + scenario_helper.scenario_cfg = { + 'task_path': 'a/b', + 'options': { + 'vnf1': vnf1, + }, + } + + helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + helper.copy_to_target = mock.MagicMock(return_value='3') + helper.generate_prox_config_file = mock.MagicMock(return_value='4') + helper.upload_prox_config = mock.MagicMock(return_value='5') + + self.assertEqual(helper.additional_files, {}) + self.assertNotEqual(helper._prox_config_data, '4') + self.assertNotEqual(helper.remote_path, '5') + helper.build_config_file() + self.assertEqual(helper.additional_files, {}) + self.assertEqual(helper._prox_config_data, '4') + self.assertEqual(helper.remote_path, '5') + + @mock.patch.object(utils, 'find_relative_file') + def test_build_config_file_additional_file_string(self, mock_find_path): + vnf1 = { + 'prox_args': {'-c': ""}, + 'prox_path': 'd', + 'prox_config': 'e/f', + 'prox_files': 'g/h.i', + 'prox_generate_parameter': True, + } + + mock_find_path.side_effect = ['1', '2'] + vnfd_helper = mock.MagicMock() + ssh_helper = mock.MagicMock() + scenario_helper = sample_vnf.ScenarioHelper('vnf1') + scenario_helper.scenario_cfg = { + 'task_path': 'a/b', + 'options': { + 'vnf1': vnf1, + }, + } + + vnfd_helper.port_pairs.all_ports = ['xe0', 'xe1', 'xe2', 'xe3'] + helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + helper.copy_to_target = mock.MagicMock(side_effect=['33', '34', '35']) + helper.generate_prox_config_file = mock.MagicMock(return_value='44') + helper.upload_prox_config = mock.MagicMock(return_value='55') + + self.assertEqual(helper.additional_files, {}) + expected = {'h.i': '33'} + helper.build_config_file() + self.assertDictEqual(helper.additional_files, expected) + + @mock.patch.object(utils, 'find_relative_file') + def test_build_config_file_additional_file(self, mock_find_path): + vnf1 = { + 'prox_args': {'-c': ""}, + 'prox_path': 'd', + 'prox_config': 'e/f', + 'prox_files': [ + 'g/h.i', + 'j/k/l', + 'm_n', + ], + } + + mock_find_path.side_effect = ['1', '2'] + [str(i) for i in range(len(vnf1['prox_files']))] + vnfd_helper = mock.MagicMock() + ssh_helper = mock.MagicMock() + scenario_helper = sample_vnf.ScenarioHelper('vnf1') + scenario_helper.scenario_cfg = { + 'task_path': 'a/b', + 'options': { + 'vnf1': vnf1, + }, + } + + helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + helper.copy_to_target = mock.MagicMock(side_effect=['33', '34', '35']) + helper.generate_prox_config_file = mock.MagicMock(return_value='44') + helper.upload_prox_config = mock.MagicMock(return_value='55') + + self.assertEqual(helper.additional_files, {}) + self.assertNotEqual(helper._prox_config_data, '44') + self.assertNotEqual(helper.remote_path, '55') + expected = {'h.i': '33', 'l': '34', 'm_n': '35'} + helper.build_config_file() + self.assertDictEqual(helper.additional_files, expected) + self.assertEqual(helper._prox_config_data, '44') + self.assertEqual(helper.remote_path, '55') + + def test_build_config(self): + vnf1 = { + 'prox_args': {'-f': ""}, + 'prox_path': '/opt/nsb_bin/prox', + 'prox_config': 'configs/gen_l2fwd-2.cfg', + 'prox_files': [ + 'g/h.i', + 'j/k/l', + 'm_n', + ], + } + + vnfd_helper = mock.Mock() + ssh_helper = mock.Mock() + ssh_helper.join_bin_path.return_value = '/opt/nsb_bin/prox' + scenario_helper = sample_vnf.ScenarioHelper('vnf1') + scenario_helper.scenario_cfg = { + 'task_path': 'a/b', + 'options': { + 'vnf1': vnf1, + }, + } + + expected = ("sudo bash -c 'cd /opt/nsb_bin; /opt/nsb_bin/prox -o cli " + "-f -f /tmp/prox.cfg '") + + helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + with mock.patch.object(helper, 'build_config_file') as mock_cfg_file: + helper.remote_path = '/tmp/prox.cfg' + prox_cmd = helper.build_config() + self.assertEqual(prox_cmd, expected) + mock_cfg_file.assert_called_once() + + def test__insert_additional_file(self): + vnfd_helper = mock.MagicMock() + ssh_helper = mock.MagicMock() + scenario_helper = mock.MagicMock() + + helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + helper.additional_files = {"ipv4.lua": "/tmp/ipv4.lua"} + res = helper._insert_additional_file('dofile("ipv4.lua")') + self.assertEqual(res, 'dofile("/tmp/ipv4.lua")') + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.ConfigParser') + def test_generate_prox_config_file(self, mock_parser_type): + def init(*args): + if sections_data: + args[-1].extend(sections_data) + return mock.MagicMock() + + sections_data = [] + + mock_parser_type.side_effect = init + + vnfd_helper = vnf_base.VnfdHelper(self.VNFD0) + ssh_helper = mock.MagicMock() + scenario_helper = mock.MagicMock() + + helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + helper.additional_files = {} + + expected = [] + result = helper.generate_prox_config_file('a/b') + self.assertEqual(result, expected) + + helper.additional_files = {"ipv4.lua": "/tmp/ipv4.lua"} + + helper.remote_prox_file_name = 'remote' + sections_data = [ + [ + 'lua', + [ + ['dofile("ipv4.lua")', ''], + ], + ], + [ + 'port 0', + [ + ['ip', ''], + ['mac', 'foo'], + ['dst mac', '@@1'], + ['tx port', '1'], + ], + ], + [ + 'port 2', + [ + ['ip', ''], + ['$sut_mac0', '@@dst_mac0'], + ['tx port', '0'], + ['single', '@'], + ['user_table', 'dofile("ipv4.lua")'], + ['missing_addtional_file', 'dofile("nosuch")'], + ], + ], + [ + 'core 0', + [ + ['name', 'p0'] + ] + ], + [ + 'core 1-4', + [ + ['name', 'p1'] + ] + ], + [ + 'core 5,6', + [ + ['name', 'p2'] + ] + ], + [ + 'core xx', + [ + ['name', 'p3'] + ] + ], + [ + 'core $x', + [ + ['name', 'p4'] + ] + ] + ] + + expected = [ + [ + 'lua', + [ + ['dofile("/tmp/ipv4.lua")', ''], + ], + ], + [ + 'port 0', + [ + ['ip', ''], + ['mac', 'hardware'], + ['dst mac', '00:00:00:00:00:03'], + ['tx port', '1'], + ], + ], + [ + 'port 2', + [ + ['ip', ''], + ['$sut_mac0', '00 00 00 00 00 04'], + ['tx port', '0'], + ['single', '@'], + ['user_table', 'dofile("/tmp/ipv4.lua")'], + ['missing_addtional_file', 'dofile("nosuch")'], + ], + ], + [ + 'core 0', + [ + ['name', 'p0'] + ] + ], + [ + 'core 1', + [ + ['name', 'p1'] + ] + ], + [ + 'core 2', + [ + ['name', 'p1'] + ] + ], + [ + 'core 3', + [ + ['name', 'p1'] + ] + ], + [ + 'core 4', + [ + ['name', 'p1'] + ] + ], + [ + 'core 5', + [ + ['name', 'p2'] + ] + ], + [ + 'core 6', + [ + ['name', 'p2'] + ] + ], + [ + 'core $x', + [ + ['name', 'p4'] + ] + ] + ] + result = helper.generate_prox_config_file('/c/d/e') + self.assertEqual(result, expected, str(result)) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.ConfigParser') + def test_generate_prox_config_file_negative(self, mock_parser_type): + def init(*args): + args[-1].update(sections_data) + return mock.MagicMock() + + sections_data = {} + + mock_parser_type.side_effect = init + + vnfd_helper = mock.MagicMock() + vnfd_helper.interfaces = [] + ssh_helper = mock.MagicMock() + scenario_helper = mock.MagicMock() + + helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + helper.additional_files = {} + helper.remote_prox_file_name = 'remote' + vnfd_helper.interfaces = [ + { + 'virtual-interface': { + 'dpdk_port_num': 3, + 'dst_mac': '00:00:00:de:ad:88', + }, + }, + { + 'virtual-interface': { + 'dpdk_port_num': 5, + 'dst_mac': '00:00:00:de:ad:ff', + }, + }, + { + 'virtual-interface': { + 'dpdk_port_num': 7, + 'dst_mac': '00:00:00:de:ad:ff', + }, + }, + ] + sections_data = { + 'port 3': [ + ['ip', ''], + ['mac', 'foo'], + ['dst mac', ''], + ], + 'port 5': [ + ['ip', ''], + ['dst mac', ''], + ['tx port', '0'], + ['???', 'dofile "here" 23'], + ], + } + + with self.assertRaises(Exception): + helper.generate_prox_config_file('a/b') + + def test_put_string_to_file(self): + vnfd_helper = mock.MagicMock() + vnfd_helper.interfaces = [] + ssh_helper = mock.MagicMock() + scenario_helper = mock.MagicMock() + + helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + + expected = 'a/b' + result = helper.put_string_to_file('my long string', 'a/b') + self.assertEqual(result, expected) + + def test_copy_to_target(self): + vnfd_helper = mock.MagicMock() + vnfd_helper.interfaces = [] + ssh_helper = mock.MagicMock() + scenario_helper = mock.MagicMock() + + helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + expected = '/tmp/c' + result = helper.copy_to_target('a/b', 'c') + self.assertEqual(result, expected) + + def test_upload_prox_config(self): + vnfd_helper = mock.MagicMock() + vnfd_helper.interfaces = [] + ssh_helper = mock.MagicMock() + scenario_helper = mock.MagicMock() + + helper = prox_helpers.ProxDpdkVnfSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + helper.write_prox_config = mock.MagicMock(return_value='a long string') + expected = '/tmp/a' + result = helper.upload_prox_config('a', {}) + self.assertEqual(result, expected) + + +class TestProxResourceHelper(unittest.TestCase): + + VNFD0 = { + 'short-name': 'ProxVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0', + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'description': 'PROX approximation using DPDK', + 'name': 'proxvnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'id': 'proxvnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'vld_id': 'uplink_0', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.19', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02', + 'ifname': 'xe0', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0', + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'vld_id': 'downlink_0', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01', + 'ifname': 'xe1', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1', + }, + ], + }, + ], + 'description': 'PROX approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'proxvnf-baremetal', + 'host': '1.2.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.2.1.1', + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'id': 'ProxApproxVnf', + 'name': 'ProxVnf', + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD0, + ], + }, + } + + def test_find_pci(self): + input_str_list = [ + 'no target here', + 'nor here', + 'and still not', + ] + result = prox_helpers.ProxResourceHelper.find_pci('target', + input_str_list) + self.assertFalse(result) + + input_str_list = [ + 'no target here', + 'nor here', + 'this is a target', + 'did we miss it', + ] + result = prox_helpers.ProxResourceHelper.find_pci('target', + input_str_list) + self.assertTrue(result) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.RETRY_INTERVAL', 0) + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.ProxSocketHelper') + def test_sut(self, *args): + helper = prox_helpers.ProxResourceHelper(mock.MagicMock()) + self.assertIsNone(helper.client) + result = helper.sut + self.assertIsNotNone(result) + self.assertIs(result, helper.client) + self.assertIs(result, helper.sut) + + def test_test_type(self): + setup_helper = mock.MagicMock() + setup_helper.find_in_section.return_value = expected = 'prox type' + + helper = prox_helpers.ProxResourceHelper(setup_helper) + + self.assertIsNone(helper._test_type) + self.assertEqual(helper.test_type, expected) + self.assertEqual(helper._test_type, expected) + self.assertEqual(helper.test_type, expected) + + def test_collect_collectd_kpi(self): + helper = prox_helpers.ProxResourceHelper(mock.MagicMock()) + helper.resource = resource = mock.MagicMock() + + resource.check_if_system_agent_running.return_value = 0, '1234' + resource.amqp_collect_nfvi_kpi.return_value = 543 + resource.check_if_system_agent_running.return_value = (0, None) + + expected = {'core': 543} + result = helper.collect_collectd_kpi() + self.assertDictEqual(result, expected) + + def test_collect_kpi(self): + helper = prox_helpers.ProxResourceHelper(mock.MagicMock()) + helper._queue = queue = mock.MagicMock() + helper._result = {'z': 123} + + helper.client = mock.MagicMock() + helper.client.hz.return_value = 1 + helper.client.multi_port_stats.return_value = \ + (True, [[0, 1, 2, 3, 4, 5], [1, 1, 2, 3, 4, 5]]) + helper.client.multi_port_stats_diff.return_value = \ + ([0, 1, 2, 3, 4, 5, 6, 7]) + helper.client.multi_port_stats_tuple.return_value = \ + {"xe0": {"in_packets": 1, "out_packets": 2}} + helper.resource = resource = mock.MagicMock() + + vnfd_helper = mock.MagicMock() + vnfd_helper.ports_iter.return_value = [('xe0', 0), ('xe1', 1)] + helper.vnfd_helper = vnfd_helper + + resource.check_if_system_agent_running.return_value = 0, '1234' + resource.amqp_collect_nfvi_kpi.return_value = 543 + resource.check_if_system_agent_running.return_value = (0, None) + + queue.empty.return_value = False + queue.get.return_value = {'a': 789} + + expected = {'z': 123, 'a': 789, + 'collect_stats': {'core': 543}, + 'live_stats': {'xe0': {'in_packets': 1, 'out_packets': 2}}} + result = helper.collect_kpi() + self.assertDictEqual(result, expected) + + def test_collect_kpi_no_hz(self): + helper = prox_helpers.ProxResourceHelper(mock.MagicMock()) + helper._queue = queue = mock.MagicMock() + helper._result = {'z': 123} + + helper.client = mock.MagicMock() + helper.client.multi_port_stats.return_value = \ + (True, [[0, 1, 2, 3, 4, 5], [1, 1, 2, 3, 4, 5]]) + helper.client.multi_port_stats_diff.return_value = \ + ([0, 1, 2, 3, 4, 5, 6, 7]) + helper.client.multi_port_stats_tuple.return_value = \ + {"xe0": {"in_packets": 1, "out_packets": 2}} + helper.resource = resource = mock.MagicMock() + + vnfd_helper = mock.MagicMock() + vnfd_helper.ports_iter.return_value = [('xe0', 0), ('xe1', 1)] + helper.vnfd_helper = vnfd_helper + + resource.check_if_system_agent_running.return_value = 0, '1234' + resource.amqp_collect_nfvi_kpi.return_value = 543 + resource.check_if_system_agent_running.return_value = (0, None) + + queue.empty.return_value = False + queue.get.return_value = {'a': 789} + + expected = {'z': 123, 'a': 789, + 'collect_stats': {'core': 543}, + 'live_stats': {'xe0': {'in_packets': 1, 'out_packets': 2}}} + result = helper.collect_kpi() + self.assertDictEqual(result, expected) + + def test_collect_kpi_bad_data(self): + helper = prox_helpers.ProxResourceHelper(mock.MagicMock()) + helper._queue = queue = mock.MagicMock() + helper._result = {'z': 123} + + helper.client = mock.MagicMock() + helper.client.multi_port_stats.return_value = \ + (False, [[0, 1, 2, 3, 4, 5], [1, 1, 2, 3, 4, 5]]) + helper.client.multi_port_stats_diff.return_value = \ + ([0, 1, 2, 3, 4, 5, 6, 7]) + helper.client.multi_port_stats_tuple.return_value = \ + {"xe0": {"in_packets": 1, "out_packets": 2}} + helper.resource = resource = mock.MagicMock() + + vnfd_helper = mock.MagicMock() + vnfd_helper.ports_iter.return_value = [('xe0', 0), ('xe1', 1)] + helper.vnfd_helper = vnfd_helper + + resource.check_if_system_agent_running.return_value = 0, '1234' + resource.amqp_collect_nfvi_kpi.return_value = 543 + resource.check_if_system_agent_running.return_value = (0, None) + + queue.empty.return_value = False + queue.get.return_value = {'a': 789} + + expected = {'z': 123, 'a': 789, + 'collect_stats': {'core': 543}} + result = helper.collect_kpi() + self.assertDictEqual(result, expected) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.time') + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.ProxSocketHelper') + def test__connect(self, mock_socket_helper_type, *args): + client = mock_socket_helper_type() + client.connect.side_effect = chain(repeat(socket.error, 5), [None]) + + setup_helper = mock.MagicMock() + setup_helper.vnfd_helper.interfaces = [] + + helper = prox_helpers.ProxResourceHelper(setup_helper) + + result = helper._connect() + self.assertIs(result, client) + + client.connect.side_effect = chain(repeat(socket.error, 65), [None]) + + with self.assertRaises(Exception): + helper._connect() + + def test_run_traffic(self): + setup_helper = mock.MagicMock() + helper = prox_helpers.ProxResourceHelper(setup_helper) + traffic_profile = mock.MagicMock() + traffic_profile.done.is_set.return_value = True + helper.run_traffic(traffic_profile) + self.assertEqual(helper._terminated.value, 1) + + def test__run_traffic_once(self): + setup_helper = mock.MagicMock() + helper = prox_helpers.ProxResourceHelper(setup_helper) + traffic_profile = mock.MagicMock() + traffic_profile.done.is_set.return_value = True + helper._run_traffic_once(traffic_profile) + self.assertEqual(helper._terminated.value, 1) + + def test_start_collect(self): + setup_helper = mock.MagicMock() + helper = prox_helpers.ProxResourceHelper(setup_helper) + helper.resource = resource = mock.MagicMock() + self.assertIsNone(helper.start_collect()) + resource.start.assert_called_once() + + def test_terminate(self): + setup_helper = mock.MagicMock() + helper = prox_helpers.ProxResourceHelper(setup_helper) + with self.assertRaises(NotImplementedError): + helper.terminate() + + def test_up_post(self): + setup_helper = mock.MagicMock() + helper = prox_helpers.ProxResourceHelper(setup_helper) + helper.client = expected = mock.MagicMock() + result = helper.up_post() + self.assertEqual(result, expected) + + def test_execute(self): + setup_helper = mock.MagicMock() + helper = prox_helpers.ProxResourceHelper(setup_helper) + helper.client = mock.MagicMock() + + expected = helper.client.my_command() + result = helper.execute('my_command') + self.assertEqual(result, expected) + + # TODO(elfoley): Make this a separate test: test_execute_no_client + helper.client = object() + + result = helper.execute('my_command') + self.assertIsNone(result) + + +class TestProxDataHelper(unittest.TestCase): + + def test_totals_and_pps(self): + pkt_size = 180 + vnfd_helper = mock.MagicMock() + vnfd_helper.port_pairs.all_ports = list(range(4)) + + sut = mock.MagicMock() + sut.multi_port_stats.return_value = (True, + [[0, 1, 2, 3, 4, 5], [1, 1, 2, 3, 4, 5], + [2, 1, 2, 3, 4, 5], [3, 1, 2, 3, 4, 5]]) + + data_helper = prox_helpers.ProxDataHelper( + vnfd_helper, sut, pkt_size, 25, None, + constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + + self.assertEqual(data_helper.rx_total, 4) + self.assertEqual(data_helper.tx_total, 8) + self.assertEqual(data_helper.requested_pps, 6250000.0) + + vnfd_helper = mock.MagicMock() + vnfd_helper.port_pairs.all_ports = [3, 4] + + sut = mock.MagicMock() + sut.multi_port_stats.return_value = (True, + [[3, 1, 2, 3, 4, 5], [4, 1, 2, 3, 4, 5]]) + + data_helper = prox_helpers.ProxDataHelper( + vnfd_helper, sut, pkt_size, 25, None, + constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + + self.assertEqual(data_helper.rx_total, 2) + self.assertEqual(data_helper.tx_total, 4) + self.assertEqual(data_helper.requested_pps, 3125000.0) + + vnfd_helper = mock.MagicMock() + vnfd_helper.port_pairs.all_ports = [0, 1, 2, 3, 4, 6, 7] + + sut = mock.MagicMock() + sut.multi_port_stats.return_value = (True, + [[8, 1, 2, 3, 4, 5], [9, 1, 2, 3, 4, 5]]) + + data_helper = prox_helpers.ProxDataHelper( + vnfd_helper, sut, pkt_size, 25, None, + constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + + self.assertEqual(data_helper.rx_total, 2) + self.assertEqual(data_helper.tx_total, 4) + self.assertEqual(data_helper.requested_pps, 10937500.0) + + vnfd_helper = mock.MagicMock() + vnfd_helper.port_pairs.all_ports = [] + + sut = mock.MagicMock() + sut.multi_port_stats.return_value = (True, + [[8, 1, 2, 3, 4, 5], [9, 1, 2, 3, 4, 5]]) + + data_helper = prox_helpers.ProxDataHelper( + vnfd_helper, sut, pkt_size, 25, None, + constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + + self.assertEqual(data_helper.rx_total, 2) + self.assertEqual(data_helper.tx_total, 4) + self.assertEqual(data_helper.requested_pps, 0.0) + + def test_totals_and_pps2(self): + pkt_size = 180 + vnfd_helper = mock.MagicMock() + vnfd_helper.port_pairs.all_ports = list(range(4)) + + sut = mock.MagicMock() + sut.multi_port_stats.return_value = (True, + [[0, 'A', 2, 3, 4, 5], [1, 'B', 'C', 3, 4, 5], + ['D', 1, 2, 3, 4, 5], [3, 1, 2, 3, 4, 'F']]) + + data_helper = prox_helpers.ProxDataHelper( + vnfd_helper, sut, pkt_size, 25, None, + constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + + self.assertEqual(data_helper.rx_total, 0) + self.assertEqual(data_helper.tx_total, 0) + self.assertEqual(data_helper.requested_pps, 0) + + def test_samples(self): + vnfd_helper = mock.MagicMock() + vnfd_helper.ports_iter.return_value = [('xe0', 0), ('xe1', 1)] + + sut = mock.MagicMock() + sut.multi_port_stats.return_value = (True, [[0, 1, 2, 3, 4, 5], [1, 11, 12, 3, 4, 5]]) + + data_helper = prox_helpers.ProxDataHelper( + vnfd_helper, sut, None, None, None, None) + + expected = { + 'xe0': { + 'in_packets': 1, + 'out_packets': 2, + }, + 'xe1': { + 'in_packets': 11, + 'out_packets': 12, + }, + } + result = data_helper.samples + self.assertDictEqual(result, expected) + + def test_samples2(self): + vnfd_helper = mock.MagicMock() + vnfd_helper.ports_iter.return_value = [('xe1', 3), ('xe2', 7)] + + sut = mock.MagicMock() + sut.multi_port_stats.return_value = (True, [[3, 1, 2, 3, 4, 5], [7, 11, 12, 3, 4, 5]]) + + data_helper = prox_helpers.ProxDataHelper( + vnfd_helper, sut, None, None, None, None) + + expected = { + 'xe1': { + 'in_packets': 1, + 'out_packets': 2, + }, + 'xe2': { + 'in_packets': 11, + 'out_packets': 12, + }, + } + result = data_helper.samples + self.assertDictEqual(result, expected) + + def test___enter__(self): + vnfd_helper = mock.MagicMock() + vnfd_helper.port_pairs.all_ports = list(range(4)) + vnfd_helper.ports_iter.return_value = [('xe1', 3), ('xe2', 7)] + + sut = mock.MagicMock() + + data_helper = prox_helpers.ProxDataHelper(vnfd_helper, sut, None, None, + 5.4, constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + data_helper._totals_and_pps = 12, 32, 4.5 + data_helper.tsc_hz = 9.8 + data_helper.measured_stats = { + 'delta': prox_helpers.TotStatsTuple(6.1, 6.2, 6.3, 6.4)} + data_helper.latency = 7 + + self.assertIsNone(data_helper.result_tuple) + self.assertEqual(data_helper.line_speed, 10000000000) + + expected = prox_helpers.ProxTestDataTuple( + 5.4, 9.8, 6.1, 6.2, 6.3, 7, 12, 32, 4.5) + with data_helper: + pass + + result = data_helper.result_tuple + self.assertEqual(result, expected) + + data_helper.make_tuple() + self.assertIs(data_helper.result_tuple, result) + + def test___enter___negative(self): + vnfd_helper = mock.MagicMock() + + data_helper = prox_helpers.ProxDataHelper( + vnfd_helper, None, None, None, None, None) + + vnfd_helper.port_pairs.all_ports = [] + with self.assertRaises(AssertionError): + with data_helper: + pass + + vnfd_helper.port_pairs.all_ports = [0, 1, 2] + with self.assertRaises(AssertionError): + with data_helper: + pass + + def test_measure_tot_stats(self): + vnfd_helper = mock.MagicMock() + vnfd_helper.port_pairs.all_ports = list(range(4)) + + start = (3, 4, 1, 2) + end = (9, 7, 6, 8) + + sut = prox_helpers.ProxSocketHelper(mock.MagicMock()) + sut.get_all_tot_stats = mock.MagicMock(side_effect=[start, end]) + + data_helper = prox_helpers.ProxDataHelper( + vnfd_helper, sut, None, None, 5.4, None) + + self.assertIsNone(data_helper.measured_stats) + + expected = { + 'start_tot': start, + 'end_tot': end, + 'delta': prox_helpers.TotStatsTuple(6, 3, 5, 6), + } + with data_helper.measure_tot_stats(): + pass + + self.assertEqual(data_helper.measured_stats, expected) + + def test_capture_tsc_hz(self): + vnfd_helper = mock.MagicMock() + vnfd_helper.port_pairs.all_ports = list(range(4)) + + sut = mock.MagicMock() + sut.hz.return_value = '54.6' + + data_helper = prox_helpers.ProxDataHelper( + vnfd_helper, sut, None, None, None, None) + + self.assertIsNone(data_helper.tsc_hz) + + expected = 54.6 + data_helper.capture_tsc_hz() + self.assertEqual(data_helper.tsc_hz, expected) + + +class TestProxProfileHelper(unittest.TestCase): + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.utils') + def test_get_cls(self, mock_utils): + mock_type1 = mock.MagicMock() + mock_type1.__prox_profile_type__ = 'another_type' + mock_type2 = mock.MagicMock() + mock_type2.__prox_profile_type__ = 'my_type' + mock_utils.itersubclasses.return_value = [mock_type1, mock_type2] + + self.assertEqual(prox_helpers.ProxProfileHelper.get_cls('my_type'), + mock_type2) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.utils') + def test_get_cls_default(self, mock_utils): + mock_utils.itersubclasses.return_value = [] + prox_helpers.ProxProfileHelper.get_cls('my_type') + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.SocketTopology') + def test_cpu_topology(self, mock_socket_topology): + mock_socket_topology.parse_cpuinfo.return_value = 432 + + resource_helper = mock.MagicMock() + resource_helper.setup_helper.ssh_helper.execute.return_value = 0, 'output', '' + + helper = prox_helpers.ProxProfileHelper(resource_helper) + self.assertIsNone(helper._cpu_topology) + result = helper.cpu_topology + self.assertEqual(result, 432) + self.assertIs(result, helper._cpu_topology) + self.assertIs(result, helper.cpu_topology) + + # TODO(elfoley): Split this test; there are two sets of inputs/outputs + def test_test_cores(self): + resource_helper = mock.MagicMock() + resource_helper.setup_helper.prox_config_data = [] + + helper = prox_helpers.ProxProfileHelper(resource_helper) + helper._cpu_topology = [] + + expected = [] + result = helper.test_cores + self.assertEqual(result, expected) + + resource_helper.setup_helper.prox_config_data = [ + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('core 1s3', []), + ('core 2s5', [ + ('index', 8), + ('mode', ''), + ]), + ('core 3s1', [ + ('index', 5), + ('mode', 'gen'), + ]), + ('core 4s9h', [ + ('index', 7), + ('mode', 'gen'), + ]), + ] + + helper = prox_helpers.ProxProfileHelper(resource_helper) + helper._cpu_topology = { + 1: { + 3: { + 'key1': (23, 32), + 'key2': (12, 21), + 'key3': (44, 33), + }, + }, + 9: { + 4: { + 'key1': (44, 32), + 'key2': (23, 21), + 'key3': (12, 33), + }, + }, + } + + self.assertIsNone(helper._test_cores) + expected = [3, 4] + result = helper.test_cores + self.assertEqual(result, expected) + self.assertIs(result, helper._test_cores) + self.assertIs(result, helper.test_cores) + + # TODO(elfoley): Split this test; there are two sets of inputs/outputs + def test_latency_cores(self): + resource_helper = mock.MagicMock() + resource_helper.setup_helper.prox_config_data = [] + + helper = prox_helpers.ProxProfileHelper(resource_helper) + helper._cpu_topology = [] + + expected = [] + result = helper.latency_cores + self.assertEqual(result, expected) + + resource_helper.setup_helper.prox_config_data = [ + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('core 1s3', []), + ('core 2s5', [ + ('index', 8), + ('mode', ''), + ]), + ('core 3s1', [ + ('index', 5), + ('mode', 'lat'), + ]), + ('core 4s9h', [ + ('index', 7), + ('mode', 'lat'), + ]), + ] + + helper = prox_helpers.ProxProfileHelper(resource_helper) + helper._cpu_topology = { + 1: { + 3: { + 'key1': (23, 32), + 'key2': (12, 21), + 'key3': (44, 33), + }, + }, + 9: { + 4: { + 'key1': (44, 32), + 'key2': (23, 21), + 'key3': (12, 33), + }, + }, + } + + self.assertIsNone(helper._latency_cores) + expected = [3, 4] + result = helper.latency_cores + self.assertEqual(result, expected) + self.assertIs(result, helper._latency_cores) + self.assertIs(result, helper.latency_cores) + + def test_all_rx_cores(self): + helper = prox_helpers.ProxBngProfileHelper(mock.MagicMock()) + helper._latency_cores = expected = [3, 4, 6] + helper._test_cores = [5, 2, 1] + + result = helper.all_rx_cores + self.assertEqual(result, expected) + + def test_get_cores(self): + resource_helper = mock.MagicMock() + resource_helper.setup_helper.prox_config_data = [ + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('core 1', []), + ('core 2', [ + ('index', 8), + ('mode', ''), + ]), + ('core 3', [ + ('index', 5), + ('mode', 'gen'), + ]), + ('core 4', [ + ('index', 7), + ('mode', 'gen'), + ]), + ] + + helper = prox_helpers.ProxProfileHelper(resource_helper) + helper._cpu_topology = { + 0: { + 1: { + 5: (5, 1, 0) + }, + 2: { + 6: (6, 2, 0) + }, + 3: { + 7: (7, 3, 0) + }, + 4: { + 8: (8, 3, 0) + }, + } + } + + expected = [3, 4] + result = helper.get_cores(helper.PROX_CORE_GEN_MODE) + self.assertEqual(result, expected) + + def test_get_latency(self): + resource_helper = mock.MagicMock() + resource_helper.setup_helper.vnfd_helper.interfaces = [] + + helper = prox_helpers.ProxProfileHelper(resource_helper) + helper._latency_cores = [] + + expected = [] + result = helper.get_latency() + self.assertEqual(result, expected) + + helper._latency_cores = [1, 2] + helper.client = mock.MagicMock() + + expected = helper.sut.lat_stats() + result = helper.get_latency() + self.assertIs(result, expected) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.time') + def test_traffic_context(self, *args): + setup_helper = mock.MagicMock() + setup_helper.vnfd_helper.interfaces = [] + + helper = prox_helpers.ProxProfileHelper(setup_helper) + helper._cpu_topology = { + 0: { + 1: { + 5: (5, 1, 0) + }, + 2: { + 6: (6, 2, 0) + }, + 3: { + 7: (7, 3, 0) + }, + 4: { + 8: (8, 3, 0) + }, + } + } + + setup_helper.prox_config_data = [ + ('global', [ + ('not_name', 'other data'), + ('name_not', 'more data'), + ('name', helper.__prox_profile_type__), + ]), + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('core 1', []), + ('core 2', [ + ('index', 8), + ('mode', ''), + ]), + ('core 3', [ + ('index', 5), + ('mode', 'gen'), + ('name', 'tagged'), + ]), + ('core 4', [ + ('index', 7), + ('mode', 'gen'), + ('name', 'udp'), + ]), + ] + + client = mock.MagicMock() + client.hz.return_value = 2 + + helper.client = client + helper.get_latency = mock.MagicMock(return_value=[3.3, 3.6, 3.8]) + + helper._test_cores = [3, 4] + + with helper.traffic_context(64, 1): + pass + + @mock.patch.object(time, 'sleep') + def test_run_test(self, *args): + resource_helper = mock.MagicMock() + resource_helper.step_delta = 0.4 + resource_helper.vnfd_helper.port_pairs.all_ports = list(range(2)) + resource_helper.sut.multi_port_stats.return_value = (True, [[0, 1, 1, 2, 4, 5], + [1, 1, 2, 3, 4, 5]]) + + helper = prox_helpers.ProxProfileHelper(resource_helper) + + helper.run_test(pkt_size=120, duration=5, value=6.5, tolerated_loss=0.0, + line_speed=constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + self.assertTrue(resource_helper.sut.multi_port_stats.called) + self.assertTrue(resource_helper.sut.stop_all.called) + self.assertTrue(resource_helper.sut.reset_stats.called) + +class TestProxMplsProfileHelper(unittest.TestCase): + + def test_mpls_cores(self): + resource_helper = mock.MagicMock() + resource_helper.setup_helper.prox_config_data = [ + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('core 1', []), + ('core 2', [ + ('index', 8), + ('mode', ''), + ]), + ('core 3', [ + ('index', 5), + ('mode', 'gen'), + ('name', 'tagged'), + ]), + ('core 4', [ + ('index', 7), + ('mode', 'gen'), + ('name', 'udp'), + ]), + ] + + helper = prox_helpers.ProxMplsProfileHelper(resource_helper) + helper._cpu_topology = { + 0: { + 1: { + 5: (5, 1, 0) + }, + 2: { + 6: (6, 2, 0) + }, + 3: { + 7: (7, 3, 0) + }, + 4: { + 8: (8, 3, 0) + }, + } + } + + expected_tagged = [3] + expected_plain = [4] + self.assertIsNone(helper._cores_tuple) + self.assertEqual(helper.tagged_cores, expected_tagged) + self.assertEqual(helper.plain_cores, expected_plain) + self.assertEqual(helper._cores_tuple, (expected_tagged, expected_plain)) + + def test_traffic_context(self): + setup_helper = mock.MagicMock() + helper = prox_helpers.ProxMplsProfileHelper(setup_helper) + + with helper.traffic_context(120, 5.4): + pass + + +class TestProxBngProfileHelper(unittest.TestCase): + + def test_bng_cores(self): + resource_helper = mock.MagicMock() + resource_helper.setup_helper.prox_config_data = [ + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('core 1', []), + ('core 2', [ + ('index', 8), + ('mode', ''), + ]), + ('core 3', [ + ('index', 5), + ('mode', 'gen'), + ('name', 'cpe'), + ]), + ('core 4', [ + ('index', 7), + ('mode', 'gen'), + ('name', 'inet'), + ]), + ('core 6', [ + ('index', 3), + ('mode', 'gen'), + ('name', 'arp_task'), + ]), + ('core 9', [ + ('index', 2), + ('mode', 'gen'), + ('name', 'arp'), + ]), + ] + + helper = prox_helpers.ProxBngProfileHelper(resource_helper) + helper._cpu_topology = { + 0: { + 1: { + 5: (5, 1, 0) + }, + 2: { + 6: (6, 2, 0) + }, + 3: { + 7: (7, 3, 0) + }, + 4: { + 8: (8, 3, 0) + }, + 6: { + 1: (4, 8, 0) + }, + 9: { + 2: (3, 7, 0) + }, + } + } + + expected_cpe = [3] + expected_inet = [4] + expected_arp = [6, 9] + expected_arp_task = [0, 6] + expected_combined = (expected_cpe, expected_inet, expected_arp, expected_arp_task) + + self.assertIsNone(helper._cores_tuple) + self.assertEqual(helper.cpe_cores, expected_cpe) + self.assertEqual(helper.inet_cores, expected_inet) + self.assertEqual(helper.arp_cores, expected_arp) + self.assertEqual(helper.arp_task_cores, expected_arp_task) + self.assertEqual(helper._cores_tuple, expected_combined) + + @mock.patch.object(time, 'sleep') + def test_run_test(self, *args): + resource_helper = mock.MagicMock() + resource_helper.step_delta = 0.4 + resource_helper.vnfd_helper.port_pairs.all_ports = list(range(2)) + resource_helper.sut.multi_port_stats.return_value = (True, [[0, 1, 1, 2, 4, 5], + [1, 1, 2, 3, 4, 5]]) + + helper = prox_helpers.ProxBngProfileHelper(resource_helper) + + helper.run_test(pkt_size=120, duration=5, value=6.5, tolerated_loss=0.0, + line_speed=constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + self.assertTrue(resource_helper.sut.multi_port_stats.called) + self.assertTrue(resource_helper.sut.stop_all.called) + self.assertTrue(resource_helper.sut.reset_stats.called) + + resource_helper.reset_mock() + + # negative pkt_size is the only way to make ratio > 1 + helper.run_test(pkt_size=-1000, duration=5, value=6.5, tolerated_loss=0.0, + line_speed=constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + + self.assertTrue(resource_helper.sut.multi_port_stats.called) + self.assertTrue(resource_helper.sut.stop_all.called) + self.assertTrue(resource_helper.sut.reset_stats.called) + +class TestProxVpeProfileHelper(unittest.TestCase): + + def test_vpe_cores(self): + resource_helper = mock.MagicMock() + resource_helper.setup_helper.prox_config_data = [ + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('core 1', []), + ('core 2', [ + ('index', 8), + ('mode', ''), + ]), + ('core 3', [ + ('index', 5), + ('mode', 'gen'), + ('name', 'cpe'), + ]), + ('core 4', [ + ('index', 7), + ('mode', 'gen'), + ('name', 'inet'), + ]), + ] + + helper = prox_helpers.ProxVpeProfileHelper(resource_helper) + helper._cpu_topology = { + 0: { + 1: { + 5: (5, 1, 0) + }, + 2: { + 6: (6, 2, 0) + }, + 3: { + 7: (7, 3, 0) + }, + 4: { + 8: (8, 3, 0) + }, + } + } + + expected_cpe = [3] + expected_inet = [4] + expected_combined = (expected_cpe, expected_inet) + + self.assertIsNone(helper._cores_tuple) + self.assertEqual(helper.cpe_cores, expected_cpe) + self.assertEqual(helper.inet_cores, expected_inet) + self.assertEqual(helper._cores_tuple, expected_combined) + + def test_vpe_ports(self): + resource_helper = mock.MagicMock() + resource_helper.setup_helper.prox_config_data = [ + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('port 3', [ + ('index', '5'), + ('name', 'cpe'), + ('mac', 'hardware'), + ]), + ('port 4', [ + ('index', '7'), + ('name', 'inet'), + ('mac', 'hardware'), + ]), + ] + + helper = prox_helpers.ProxVpeProfileHelper(resource_helper) + helper._port_list = { + 0: { + 1: { + 5: 'cpe' + }, + 2: { + 6: 'inet' + }, + 3: { + 7: 'cpe' + }, + 4: { + 8: 'inet' + }, + } + } + + expected_cpe = [3] + expected_inet = [4] + expected_combined = (expected_cpe, expected_inet) + + self.assertIsNone(helper._ports_tuple) + self.assertEqual(helper.cpe_ports, expected_cpe) + self.assertEqual(helper.inet_ports, expected_inet) + self.assertEqual(helper._ports_tuple, expected_combined) + + @mock.patch.object(time, 'sleep') + def test_run_test(self, *args): + resource_helper = mock.MagicMock() + resource_helper.step_delta = 0.4 + resource_helper.vnfd_helper.port_pairs.all_ports = list(range(2)) + resource_helper.sut.multi_port_stats.return_value = (True, [[0, 1, 1, 2, 4, 5], + [1, 1, 2, 3, 4, 5]]) + + helper = prox_helpers.ProxVpeProfileHelper(resource_helper) + + helper.run_test(pkt_size=120, duration=5, value=6.5, tolerated_loss=0.0, + line_speed=constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + + # negative pkt_size is the only way to make ratio > 1 + helper.run_test(pkt_size=-1000, duration=5, value=6.5, tolerated_loss=0.0, + line_speed=constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + +class TestProxlwAFTRProfileHelper(unittest.TestCase): + + def test_lwaftr_cores(self): + resource_helper = mock.MagicMock() + resource_helper.setup_helper.prox_config_data = [ + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('core 1', []), + ('core 2', [ + ('index', 8), + ('mode', ''), + ]), + ('core 3', [ + ('index', 5), + ('mode', 'gen'), + ('name', 'tun'), + ]), + ('core 4', [ + ('index', 7), + ('mode', 'gen'), + ('name', 'inet'), + ]), + ] + + helper = prox_helpers.ProxlwAFTRProfileHelper(resource_helper) + helper._cpu_topology = { + 0: { + 1: { + 5: (5, 1, 0) + }, + 2: { + 6: (6, 2, 0) + }, + 3: { + 7: (7, 3, 0) + }, + 4: { + 8: (8, 3, 0) + }, + } + } + + expected_tun = [3] + expected_inet = [4] + expected_combined = (expected_tun, expected_inet) + + self.assertIsNone(helper._cores_tuple) + self.assertEqual(helper.tun_cores, expected_tun) + self.assertEqual(helper.inet_cores, expected_inet) + self.assertEqual(helper._cores_tuple, expected_combined) + + def test_tun_ports(self): + resource_helper = mock.MagicMock() + resource_helper.setup_helper.prox_config_data = [ + ('section1', []), + ('section2', [ + ('a', 'b'), + ('c', 'd'), + ]), + ('port 3', [ + ('index', '5'), + ('name', 'lwB4'), + ('mac', 'hardware'), + ]), + ('port 4', [ + ('index', '7'), + ('name', 'inet'), + ('mac', 'hardware'), + ]), + ] + + helper = prox_helpers.ProxlwAFTRProfileHelper(resource_helper) + helper._port_list = { + 0: { + 1: { + 5: 'lwB4' + }, + 2: { + 6: 'inet' + }, + 3: { + 7: 'lwB4' + }, + 4: { + 8: 'inet' + }, + } + } + + expected_tun = [3] + expected_inet = [4] + expected_combined = (expected_tun, expected_inet) + + self.assertIsNone(helper._ports_tuple) + self.assertEqual(helper.tun_ports, expected_tun) + self.assertEqual(helper.inet_ports, expected_inet) + self.assertEqual(helper._ports_tuple, expected_combined) + + @mock.patch.object(time, 'sleep') + def test_run_test(self, *args): + resource_helper = mock.MagicMock() + resource_helper.step_delta = 0.4 + resource_helper.vnfd_helper.port_pairs.all_ports = list(range(2)) + resource_helper.sut.multi_port_stats.return_value = (True, [[0, 1, 2, 4, 6, 5], + [1, 1, 2, 3, 4, 5]]) + + helper = prox_helpers.ProxlwAFTRProfileHelper(resource_helper) + + helper.run_test(pkt_size=120, duration=5, value=6.5, tolerated_loss=0.0, + line_speed=constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + + # negative pkt_size is the only way to make ratio > 1 + helper.run_test(pkt_size=-1000, duration=5, value=6.5, tolerated_loss=0.0, + line_speed=constants.NIC_GBPS_DEFAULT * constants.ONE_GIGABIT_IN_BITS) + + +class TestProxIrqProfileHelper(unittest.TestCase): + + def test_run_test(self, *args): + resource_helper = mock.MagicMock() + helper = prox_helpers.ProxIrqProfileHelper(resource_helper) + self.assertIsNone(helper._cores_tuple) + self.assertIsNone(helper._ports_tuple) + self.assertIsNone(helper._latency_cores) + self.assertIsNone(helper._test_cores) + self.assertIsNone(helper._cpu_topology) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_prox_irq.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_prox_irq.py new file mode 100644 index 000000000..94197c3be --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_prox_irq.py @@ -0,0 +1,828 @@ +# Copyright (c) 2017-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import mock +import errno + +from yardstick.tests import STL_MOCKS +from yardstick.common import exceptions as y_exceptions +from yardstick.network_services.vnf_generic.vnf.prox_irq import ProxIrqGen +from yardstick.network_services.vnf_generic.vnf.prox_irq import ProxIrqVNF +from yardstick.benchmark.contexts import base as ctx_base + +SSH_HELPER = 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper' + +STLClient = mock.MagicMock() +stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) +stl_patch.start() + +if stl_patch: + from yardstick.network_services.vnf_generic.vnf import prox_vnf + from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh + +VNF_NAME = "vnf__1" + +class TestProxIrqVNF(unittest.TestCase): + + SCENARIO_CFG = { + 'task_path': "", + 'nodes': { + 'tg__1': 'trafficgen_1.yardstick', + 'vnf__1': 'vnf.yardstick'}, + 'runner': { + 'duration': 600, 'type': 'Duration'}, + 'topology': 'prox-tg-topology-2.yaml', + 'traffic_profile': '../../traffic_profiles/prox_binsearch.yaml', + 'type': 'NSPerf', + 'options': { + 'tg__1': {'prox_args': {'-e': '', + '-t': ''}, + 'prox_config': 'configs/l3-gen-2.cfg', + 'prox_path': + '/root/dppd-PROX-v035/build/prox'}, + 'vnf__1': { + 'prox_args': {'-t': ''}, + 'prox_config': 'configs/l3-swap-2.cfg', + 'prox_path': '/root/dppd-PROX-v035/build/prox'}}} + + VNFD_0 = { + 'short-name': 'VpeVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0' + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1' + }, + ], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0' + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1' + }, + ], + 'id': 'vpevnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.100.20', + 'local_mac': '00:00:00:00:00:01' + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0' + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_mac': '00:00:00:00:00:02' + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1' + }, + ], + }, + ], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1' + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh' + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ] + } + } + + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64, + }, + } + + CONTEXT_CFG = { + 'nodes': { + 'tg__2': { + 'member-vnf-index': '3', + 'role': 'TrafficGen', + 'name': 'trafficgen_2.yardstick', + 'vnfd-id-ref': 'tg__2', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens513f0', + 'vld_id': prox_vnf.ProxApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.20', + 'dst_mac': '00:00:00:00:00:01', + 'local_mac': '00:00:00:00:00:03', + 'dst_ip': '152.16.40.19', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens513f1', + 'netmask': '255.255.255.0', + 'network': '202.16.100.0', + 'local_ip': '202.16.100.20', + 'local_mac': '00:1e:67:d0:60:5d', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.1', + 'dpdk_port_num': 1, + }, + }, + 'password': 'r00t', + 'VNF model': 'l3fwd_vnf.yaml', + 'user': 'root', + }, + 'tg__1': { + 'member-vnf-index': '1', + 'role': 'TrafficGen', + 'name': 'trafficgen_1.yardstick', + 'vnfd-id-ref': 'tg__1', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens785f0', + 'vld_id': prox_vnf.ProxApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.20', + 'dst_mac': '00:00:00:00:00:02', + 'local_mac': '00:00:00:00:00:04', + 'dst_ip': '152.16.100.19', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens785f1', + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.21', + 'local_mac': '00:00:00:00:00:01', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + }, + }, + 'password': 'r00t', + 'VNF model': 'tg_rfc2544_tpl.yaml', + 'user': 'root', + }, + 'vnf__1': { + 'name': 'vnf.yardstick', + 'vnfd-id-ref': 'vnf__1', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens786f0', + 'vld_id': prox_vnf.ProxApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.19', + 'dst_mac': '00:00:00:00:00:04', + 'local_mac': '00:00:00:00:00:02', + 'dst_ip': '152.16.100.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens786f1', + 'vld_id': prox_vnf.ProxApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.19', + 'dst_mac': '00:00:00:00:00:03', + 'local_mac': '00:00:00:00:00:01', + 'dst_ip': '152.16.40.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + }, + }, + 'routing_table': [ + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'network': '152.16.100.20', + 'if': 'xe0', + }, + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'network': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'member-vnf-index': '2', + 'host': '1.2.1.1', + 'role': 'vnf', + 'user': 'root', + 'nd_route_tbl': [ + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'password': 'r00t', + 'VNF model': 'prox_vnf.yaml', + }, + }, + } + + def test___init__(self): + prox_irq_vnf = ProxIrqVNF('vnf1', self.VNFD_0) + + self.assertEqual(prox_irq_vnf.name, 'vnf1') + self.assertDictEqual(prox_irq_vnf.vnfd_helper, self.VNFD_0) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch(SSH_HELPER) + def test_collect_kpi(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + resource_helper = mock.MagicMock() + + resource_helper = mock.MagicMock() + + core_1 = {'bucket_1': 1, 'bucket_2': 2, 'bucket_3': 3, 'bucket_4': 4, 'bucket_5': 5, + 'bucket_6': 6, 'bucket_7': 7, 'bucket_8': 8, 'bucket_9': 9, 'bucket_10': 10, + 'bucket_11': 11, 'bucket_12': 12, 'bucket_0': 100, 'cpu': 1, 'max_irq': 12, + 'overflow': 10} + core_2 = {'bucket_1': 1, 'bucket_2': 2, 'bucket_3': 3, 'bucket_4': 4, 'bucket_5': 5, + 'bucket_6': 0, 'bucket_7': 0, 'bucket_8': 0, 'bucket_9': 0, 'bucket_10': 0, + 'bucket_11': 0, 'bucket_12': 0, 'bucket_0': 100, 'cpu': 2, 'max_irq': 12, + 'overflow': 10} + + irq_data = {'core_1': core_1, 'core_2': core_2} + resource_helper.execute.return_value = (irq_data) + + build_config_file = mock.MagicMock() + build_config_file.return_value = None + + prox_irq_vnf = ProxIrqVNF(VNF_NAME, vnfd) + + startup = ["global", [["eal", "-4"]]] + master_0 = ["core 0", [["mode", "master"]]] + core_1 = ["core 1", [["mode", "irq"]]] + core_2 = ["core 2", [["mode", "irq"], ["task", "2"]]] + + prox_irq_vnf.setup_helper._prox_config_data = \ + [startup, master_0, core_1, core_2] + + prox_irq_vnf.scenario_helper.scenario_cfg = self.SCENARIO_CFG + prox_irq_vnf.resource_helper = resource_helper + prox_irq_vnf.setup_helper.build_config_file = build_config_file + + result = prox_irq_vnf.collect_kpi() + self.assertDictEqual(result["collect_stats"], {}) + + result = prox_irq_vnf.collect_kpi() + self.assertFalse('bucket_10' in result["collect_stats"]['core_2']) + self.assertFalse('bucket_11' in result["collect_stats"]['core_2']) + self.assertFalse('bucket_12' in result["collect_stats"]['core_2']) + self.assertEqual(result["collect_stats"]['core_2']['max_irq'], 12) + + + @mock.patch(SSH_HELPER) + def test_vnf_execute_oserror(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + prox_irq_vnf = ProxIrqVNF(VNF_NAME, vnfd) + prox_irq_vnf.resource_helper = resource_helper = mock.Mock() + + resource_helper.execute.side_effect = OSError(errno.EPIPE, "") + prox_irq_vnf.vnf_execute("", _ignore_errors=True) + + resource_helper.execute.side_effect = OSError(errno.ESHUTDOWN, "") + prox_irq_vnf.vnf_execute("", _ignore_errors=True) + + resource_helper.execute.side_effect = OSError(errno.EADDRINUSE, "") + with self.assertRaises(OSError): + prox_irq_vnf.vnf_execute("", _ignore_errors=True) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.socket') + @mock.patch(SSH_HELPER) + def test_terminate(self, ssh, *args): + mock_ssh(ssh) + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + + mock_ssh(ssh, exec_result=(1, "", "")) + prox_irq_vnf = ProxIrqVNF(VNF_NAME, vnfd) + + prox_irq_vnf._terminated = mock.MagicMock() + prox_irq_vnf._traffic_process = mock.MagicMock() + prox_irq_vnf._traffic_process.terminate = mock.Mock() + prox_irq_vnf.ssh_helper = mock.MagicMock() + prox_irq_vnf.setup_helper = mock.MagicMock() + prox_irq_vnf.resource_helper = mock.MagicMock() + prox_irq_vnf._vnf_wrapper.setup_helper = mock.MagicMock() + prox_irq_vnf._vnf_wrapper._vnf_process = mock.MagicMock(**{"is_alive.return_value": False}) + prox_irq_vnf._vnf_wrapper.resource_helper = mock.MagicMock() + + prox_irq_vnf._run_prox = mock.Mock(return_value=0) + prox_irq_vnf.q_in = mock.Mock() + prox_irq_vnf.q_out = mock.Mock() + + self.assertIsNone(prox_irq_vnf.terminate()) + + @mock.patch(SSH_HELPER) + def test_wait_for_instantiate_panic(self, ssh, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + + mock_ssh(ssh, exec_result=(1, "", "")) + prox_irq_vnf = ProxIrqVNF(VNF_NAME, vnfd) + + prox_irq_vnf._terminated = mock.MagicMock() + prox_irq_vnf._traffic_process = mock.MagicMock() + prox_irq_vnf._traffic_process.terminate = mock.Mock() + prox_irq_vnf.ssh_helper = mock.MagicMock() + prox_irq_vnf.setup_helper = mock.MagicMock() + prox_irq_vnf.resource_helper = mock.MagicMock() + prox_irq_vnf._vnf_wrapper.setup_helper = mock.MagicMock() + prox_irq_vnf._vnf_wrapper._vnf_process = mock.MagicMock(**{"is_alive.return_value": False}) + prox_irq_vnf._vnf_wrapper.resource_helper = mock.MagicMock() + + prox_irq_vnf._run_prox = mock.Mock(return_value=0) + prox_irq_vnf.q_in = mock.Mock() + prox_irq_vnf.q_out = mock.Mock() + prox_irq_vnf.WAIT_TIME = 0 + with self.assertRaises(RuntimeError): + prox_irq_vnf.wait_for_instantiate() + +class TestProxIrqGen(unittest.TestCase): + + SCENARIO_CFG = { + 'task_path': "", + 'nodes': { + 'tg__1': 'trafficgen_1.yardstick', + 'vnf__1': 'vnf.yardstick'}, + 'runner': { + 'duration': 600, 'type': 'Duration'}, + 'topology': 'prox-tg-topology-2.yaml', + 'traffic_profile': '../../traffic_profiles/prox_binsearch.yaml', + 'type': 'NSPerf', + 'options': { + 'tg__1': {'prox_args': {'-e': '', + '-t': ''}, + 'prox_config': 'configs/l3-gen-2.cfg', + 'prox_path': + '/root/dppd-PROX-v035/build/prox'}, + 'vnf__1': { + 'prox_args': {'-t': ''}, + 'prox_config': 'configs/l3-swap-2.cfg', + 'prox_path': '/root/dppd-PROX-v035/build/prox'}}} + + VNFD_0 = { + 'short-name': 'VpeVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0' + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1' + }, + ], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0' + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1' + }, + ], + 'id': 'vpevnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.0', + 'driver': 'i40e', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.100.20', + 'local_mac': '00:00:00:00:00:01' + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0' + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.1', + 'driver': 'ixgbe', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_mac': '00:00:00:00:00:02' + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1' + }, + ], + }, + ], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1' + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh' + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ], + }, + } + + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64, + }, + } + + CONTEXT_CFG = { + 'nodes': { + 'tg__2': { + 'member-vnf-index': '3', + 'role': 'TrafficGen', + 'name': 'trafficgen_2.yardstick', + 'vnfd-id-ref': 'tg__2', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens513f0', + 'vld_id': prox_vnf.ProxApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.20', + 'dst_mac': '00:00:00:00:00:01', + 'local_mac': '00:00:00:00:00:03', + 'dst_ip': '152.16.40.19', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens513f1', + 'netmask': '255.255.255.0', + 'network': '202.16.100.0', + 'local_ip': '202.16.100.20', + 'local_mac': '00:1e:67:d0:60:5d', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.1', + 'dpdk_port_num': 1, + }, + }, + 'password': 'r00t', + 'VNF model': 'l3fwd_vnf.yaml', + 'user': 'root', + }, + 'tg__1': { + 'member-vnf-index': '1', + 'role': 'TrafficGen', + 'name': 'trafficgen_1.yardstick', + 'vnfd-id-ref': 'tg__1', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens785f0', + 'vld_id': prox_vnf.ProxApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.20', + 'dst_mac': '00:00:00:00:00:02', + 'local_mac': '00:00:00:00:00:04', + 'dst_ip': '152.16.100.19', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens785f1', + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.21', + 'local_mac': '00:00:00:00:00:01', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + }, + }, + 'password': 'r00t', + 'VNF model': 'tg_rfc2544_tpl.yaml', + 'user': 'root', + }, + 'vnf__1': { + 'name': 'vnf.yardstick', + 'vnfd-id-ref': 'vnf__1', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens786f0', + 'vld_id': prox_vnf.ProxApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.19', + 'dst_mac': '00:00:00:00:00:04', + 'local_mac': '00:00:00:00:00:02', + 'dst_ip': '152.16.100.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens786f1', + 'vld_id': prox_vnf.ProxApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.19', + 'dst_mac': '00:00:00:00:00:03', + 'local_mac': '00:00:00:00:00:01', + 'dst_ip': '152.16.40.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + }, + }, + 'routing_table': [ + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'network': '152.16.100.20', + 'if': 'xe0', + }, + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'network': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'member-vnf-index': '2', + 'host': '1.2.1.1', + 'role': 'vnf', + 'user': 'root', + 'nd_route_tbl': [ + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'password': 'r00t', + 'VNF model': 'prox_vnf.yaml', + }, + }, + } + + + def test__check_status(self): + prox_irq_gen = ProxIrqGen('tg1', self.VNFD_0) + + with self.assertRaises(NotImplementedError): + prox_irq_gen._check_status() + + def test_listen_traffic(self): + prox_irq_gen = ProxIrqGen('tg1', self.VNFD_0) + + prox_irq_gen.listen_traffic(mock.Mock()) + + def test_verify_traffic(self): + prox_irq_gen = ProxIrqGen('tg1', self.VNFD_0) + + prox_irq_gen.verify_traffic(mock.Mock()) + + mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.socket') + @mock.patch(SSH_HELPER) + def test_terminate(self, ssh, *args): + mock_ssh(ssh) + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + prox_traffic_gen = ProxIrqGen(VNF_NAME, vnfd) + prox_traffic_gen._terminated = mock.MagicMock() + prox_traffic_gen._traffic_process = mock.MagicMock() + prox_traffic_gen._traffic_process.terminate = mock.Mock() + prox_traffic_gen.ssh_helper = mock.MagicMock() + prox_traffic_gen.setup_helper = mock.MagicMock() + prox_traffic_gen.resource_helper = mock.MagicMock() + prox_traffic_gen._vnf_wrapper.setup_helper = mock.MagicMock() + prox_traffic_gen._vnf_wrapper._vnf_process = mock.MagicMock() + prox_traffic_gen._vnf_wrapper.resource_helper = mock.MagicMock() + self.assertIsNone(prox_traffic_gen.terminate()) + + def test__wait_for_process(self): + prox_irq_gen = ProxIrqGen('tg1', self.VNFD_0) + with mock.patch.object(prox_irq_gen, '_check_status', + return_value=0) as mock_status, \ + mock.patch.object(prox_irq_gen, '_tg_process') as mock_proc: + mock_proc.is_alive.return_value = True + mock_proc.exitcode = 234 + self.assertEqual(prox_irq_gen._wait_for_process(), 234) + mock_proc.is_alive.assert_called_once() + mock_status.assert_called_once() + + def test__wait_for_process_not_alive(self): + prox_irq_gen = ProxIrqGen('tg1', self.VNFD_0) + with mock.patch.object(prox_irq_gen, '_tg_process') as mock_proc: + mock_proc.is_alive.return_value = False + self.assertRaises(RuntimeError, prox_irq_gen._wait_for_process) + mock_proc.is_alive.assert_called_once() + + def test__wait_for_process_delayed(self): + prox_irq_gen = ProxIrqGen('tg1', self.VNFD_0) + with mock.patch.object(prox_irq_gen, '_check_status', + side_effect=[1, 0]) as mock_status, \ + mock.patch.object(prox_irq_gen, + '_tg_process') as mock_proc: + mock_proc.is_alive.return_value = True + mock_proc.exitcode = 234 + self.assertEqual(prox_irq_gen._wait_for_process(), 234) + mock_proc.is_alive.assert_has_calls([mock.call(), mock.call()]) + mock_status.assert_has_calls([mock.call(), mock.call()]) + + def test_scale(self): + prox_irq_gen = ProxIrqGen('tg1', self.VNFD_0) + self.assertRaises(y_exceptions.FunctionNotImplemented, + prox_irq_gen.scale) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch(SSH_HELPER) + def test_collect_kpi(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + resource_helper = mock.MagicMock() + + core_1 = {'bucket_1': 1, 'bucket_2': 2, 'bucket_3': 3, 'bucket_4': 4, 'bucket_5': 5, + 'bucket_6': 6, 'bucket_7': 7, 'bucket_8': 8, 'bucket_9': 9, 'bucket_10': 10, + 'bucket_11': 11, 'bucket_12': 12, 'bucket_0': 100, 'cpu': 1, 'max_irq': 12, + 'overflow': 10} + core_2 = {'bucket_1': 1, 'bucket_2': 2, 'bucket_3': 3, 'bucket_4': 4, 'bucket_5': 5, + 'bucket_6': 0, 'bucket_7': 0, 'bucket_8': 0, 'bucket_9': 0, 'bucket_10': 0, + 'bucket_11': 0, 'bucket_12': 0, 'bucket_0': 100, 'cpu': 2, 'max_irq': 12, + 'overflow': 10} + + irq_data = {'core_1': core_1, 'core_2': core_2} + resource_helper.sut.irq_core_stats.return_value = (irq_data) + + build_config_file = mock.MagicMock() + build_config_file.return_value = None + + prox_irq_gen = ProxIrqGen(VNF_NAME, vnfd) + + startup = ["global", [["eal", "-4"]]] + master_0 = ["core 0", [["mode", "master"]]] + core_1 = ["core 1", [["mode", "irq"]]] + core_2 = ["core 2", [["mode", "irq"], ["task", "2"]]] + + prox_irq_gen.setup_helper._prox_config_data = \ + [startup, master_0, core_1, core_2] + + prox_irq_gen.scenario_helper.scenario_cfg = self.SCENARIO_CFG + prox_irq_gen.resource_helper = resource_helper + prox_irq_gen.setup_helper.build_config_file = build_config_file + + result = prox_irq_gen.collect_kpi() + self.assertDictEqual(result["collect_stats"], {}) + + result = prox_irq_gen.collect_kpi() + self.assertFalse('bucket_10' in result["collect_stats"]['core_2']) + self.assertFalse('bucket_11' in result["collect_stats"]['core_2']) + self.assertFalse('bucket_12' in result["collect_stats"]['core_2']) + self.assertEqual(result["collect_stats"]['core_2']['max_irq'], 12) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_prox_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_prox_vnf.py new file mode 100644 index 000000000..76fd74dfe --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_prox_vnf.py @@ -0,0 +1,513 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import errno +import os +import unittest +import mock +from copy import deepcopy + +from yardstick.tests import STL_MOCKS +from yardstick.benchmark.contexts import base as ctx_base + + +SSH_HELPER = 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper' + +STLClient = mock.MagicMock() +stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) +stl_patch.start() + +if stl_patch: + from yardstick.network_services.vnf_generic.vnf import prox_vnf + from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh + + +NAME = "vnf__1" + + +@mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.time') +class TestProxApproxVnf(unittest.TestCase): + + VNFD0 = { + 'short-name': 'ProxVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0', + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'description': 'PROX approximation using DPDK', + 'name': 'proxvnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'id': 'proxvnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'vld_id': 'downlink_0', + 'ifname': 'xe1', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0', + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'vld_id': 'uplink_0', + 'ifname': 'xe1', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1', + }, + ], + }, + ], + 'description': 'PROX approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'proxvnf-baremetal', + 'host': '1.2.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.2.1.1', + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + 'curr_packets_fwd', + 'curr_packets_in' + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'ProxApproxVnf', + 'name': 'ProxVnf', + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD0, + ], + }, + } + + SCENARIO_CFG = { + 'task_path': "", + 'nodes': { + 'tg__1': 'trafficgen_1.yardstick', + 'vnf__1': 'vnf.yardstick'}, + 'runner': { + 'duration': 600, 'type': 'Duration'}, + 'topology': 'prox-tg-topology-2.yaml', + 'traffic_profile': '../../traffic_profiles/prox_binsearch.yaml', + 'type': 'NSPerf', + 'options': { + 'tg__1': {'prox_args': {'-e': '', + '-t': ''}, + 'prox_config': 'configs/l3-gen-2.cfg', + 'prox_path': + '/root/dppd-PROX-v035/build/prox'}, + 'vnf__1': { + 'prox_args': {'-t': ''}, + 'prox_config': 'configs/l3-swap-2.cfg', + 'prox_path': '/root/dppd-PROX-v035/build/prox'}}} + + CONTEXT_CFG = { + 'nodes': { + 'tg__2': { + 'member-vnf-index': '3', + 'role': 'TrafficGen', + 'name': 'trafficgen_2.yardstick', + 'vnfd-id-ref': 'tg__2', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens513f0', + 'vld_id': prox_vnf.ProxApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.20', + 'dst_mac': '00:00:00:00:00:01', + 'local_mac': '00:00:00:00:00:03', + 'dst_ip': '152.16.40.19', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens513f1', + 'netmask': '255.255.255.0', + 'network': '202.16.100.0', + 'local_ip': '202.16.100.20', + 'local_mac': '00:1e:67:d0:60:5d', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.1', + 'dpdk_port_num': 1, + }, + }, + 'password': 'r00t', + 'VNF model': 'l3fwd_vnf.yaml', + 'user': 'root', + }, + 'tg__1': { + 'member-vnf-index': '1', + 'role': 'TrafficGen', + 'name': 'trafficgen_1.yardstick', + 'vnfd-id-ref': 'tg__1', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens785f0', + 'vld_id': prox_vnf.ProxApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.20', + 'dst_mac': '00:00:00:00:00:02', + 'local_mac': '00:00:00:00:00:04', + 'dst_ip': '152.16.100.19', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens785f1', + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.21', + 'local_mac': '00:00:00:00:00:01', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + }, + }, + 'password': 'r00t', + 'VNF model': 'tg_rfc2544_tpl.yaml', + 'user': 'root', + }, + 'vnf__1': { + 'name': 'vnf.yardstick', + 'vnfd-id-ref': 'vnf__1', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens786f0', + 'vld_id': prox_vnf.ProxApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.19', + 'dst_mac': '00:00:00:00:00:04', + 'local_mac': '00:00:00:00:00:02', + 'dst_ip': '152.16.100.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens786f1', + 'vld_id': prox_vnf.ProxApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.19', + 'dst_mac': '00:00:00:00:00:03', + 'local_mac': '00:00:00:00:00:01', + 'dst_ip': '152.16.40.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + }, + }, + 'routing_table': [ + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'network': '152.16.100.20', + 'if': 'xe0', + }, + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'network': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'member-vnf-index': '2', + 'host': '1.2.1.1', + 'role': 'vnf', + 'user': 'root', + 'nd_route_tbl': [ + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'password': 'r00t', + 'VNF model': 'prox_vnf.yaml', + }, + }, + } + + @mock.patch(SSH_HELPER) + def test___init__(self, ssh, *args): + mock_ssh(ssh) + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, self.VNFD0) + self.assertIsNone(prox_approx_vnf._vnf_process) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch(SSH_HELPER) + def test_collect_kpi_no_client(self, ssh, *args): + mock_ssh(ssh) + + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, self.VNFD0) + prox_approx_vnf.scenario_helper.scenario_cfg = { + 'nodes': {prox_approx_vnf.name: "mock"} + } + prox_approx_vnf.resource_helper = None + expected = { + 'physical_node': 'mock_node', + 'packets_in': 0, + 'packets_dropped': 0, + 'packets_fwd': 0, + 'curr_packets_in': 0, + 'curr_packets_fwd': 0, + 'collect_stats': {'core': {}} + } + result = prox_approx_vnf.collect_kpi() + self.assertEqual(result, expected) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch(SSH_HELPER) + def test_collect_kpi(self, ssh, *args): + mock_ssh(ssh) + + resource_helper = mock.MagicMock() + resource_helper.execute.return_value = (True, + [[0, 1, 2, 3, 4, 5], [1, 1, 2, 3, 4, 5]]) + resource_helper.collect_collectd_kpi.return_value = {'core': {'result': 234}} + + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, self.VNFD0) + prox_approx_vnf.scenario_helper.scenario_cfg = { + 'nodes': {prox_approx_vnf.name: "mock"} + } + prox_approx_vnf.resource_helper = resource_helper + prox_approx_vnf.tsc_hz = 1000 + + expected = { + 'curr_packets_in': 200, + 'curr_packets_fwd': 400, + 'physical_node': 'mock_node', + 'packets_in': 2, + 'packets_dropped': 2, + 'packets_fwd': 4, + 'collect_stats': {'core': {'result': 234}}, + } + result = prox_approx_vnf.collect_kpi() + self.assertEqual(result['packets_in'], expected['packets_in']) + self.assertEqual(result['packets_dropped'], expected['packets_dropped']) + self.assertEqual(result['packets_fwd'], expected['packets_fwd']) + self.assertEqual(result['curr_packets_in'], expected['curr_packets_in']) + self.assertEqual(result['curr_packets_fwd'], expected['curr_packets_fwd']) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch(SSH_HELPER) + def test_collect_kpi_bad_input(self, ssh, *args): + mock_ssh(ssh) + + resource_helper = mock.MagicMock() + resource_helper.execute.return_value = (True, + [[0, 'A', 'B', 'C', 'D', 'E'], + ['F', 1, 2, 3, 4, 5]]) + + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, self.VNFD0) + prox_approx_vnf.scenario_helper.scenario_cfg = { + 'nodes': {prox_approx_vnf.name: "mock"} + } + prox_approx_vnf.resource_helper = resource_helper + + result = prox_approx_vnf.collect_kpi() + self.assertDictEqual(result, {}) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch(SSH_HELPER) + def test_collect_kpi_bad_input2(self, ssh, *args): + mock_ssh(ssh) + + resource_helper = mock.MagicMock() + resource_helper.execute.return_value = (False, + [[0, 'A', 'B', 'C', 'D', 'E'], + ['F', 1, 2, 3, 4, 5]]) + + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, self.VNFD0) + prox_approx_vnf.scenario_helper.scenario_cfg = { + 'nodes': {prox_approx_vnf.name: "mock"} + } + prox_approx_vnf.resource_helper = resource_helper + + result = prox_approx_vnf.collect_kpi() + self.assertDictEqual(result, {}) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch(SSH_HELPER) + def test_collect_kpi_error(self, ssh, *args): + mock_ssh(ssh) + + resource_helper = mock.MagicMock() + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, deepcopy(self.VNFD0)) + prox_approx_vnf.scenario_helper.scenario_cfg = { + 'nodes': {prox_approx_vnf.name: "mock"} + } + prox_approx_vnf.resource_helper = resource_helper + prox_approx_vnf.vnfd_helper['vdu'][0]['external-interface'] = [] + prox_approx_vnf.vnfd_helper.port_pairs.interfaces = [] + + with self.assertRaises(RuntimeError): + prox_approx_vnf.collect_kpi() + + def _get_file_abspath(self, filename, *args): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + @mock.patch('yardstick.common.utils.open', create=True) + @mock.patch('yardstick.benchmark.scenarios.networking.vnf_generic.open', create=True) + @mock.patch('yardstick.network_services.helpers.iniparser.open', create=True) + @mock.patch(SSH_HELPER) + def test_run_prox(self, ssh, *_): + mock_ssh(ssh) + + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, self.VNFD0) + prox_approx_vnf.scenario_helper.scenario_cfg = self.SCENARIO_CFG + prox_approx_vnf.ssh_helper.join_bin_path.return_value = '/tool_path12/tool_file34' + prox_approx_vnf.setup_helper.remote_path = 'configs/file56.cfg' + + expected = "sudo bash -c 'cd /tool_path12; " \ + "/tool_path12/tool_file34 -o cli -t -f /tmp/l3-swap-2.cfg '" + + prox_approx_vnf._run() + result = prox_approx_vnf.ssh_helper.run.call_args[0][0] + self.assertEqual(result, expected) + + @mock.patch(SSH_HELPER) + def bad_test_instantiate(self, *args): + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, self.VNFD0) + prox_approx_vnf.scenario_helper = mock.MagicMock() + prox_approx_vnf.setup_helper = mock.MagicMock() + # we can't mock super + prox_approx_vnf.instantiate(self.SCENARIO_CFG, self.CONTEXT_CFG) + prox_approx_vnf.setup_helper.build_config.assert_called_once() + + @mock.patch(SSH_HELPER) + def test_wait_for_instantiate_panic(self, ssh, *args): + mock_ssh(ssh, exec_result=(1, "", "")) + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, self.VNFD0) + prox_approx_vnf._vnf_process = mock.MagicMock(**{"is_alive.return_value": True}) + prox_approx_vnf._run_prox = mock.Mock(return_value=0) + prox_approx_vnf.WAIT_TIME = 0 + prox_approx_vnf.q_out.put("PANIC") + with self.assertRaises(RuntimeError): + prox_approx_vnf.wait_for_instantiate() + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.socket') + @mock.patch(SSH_HELPER) + def test_terminate(self, ssh, *args): + mock_ssh(ssh) + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, self.VNFD0) + prox_approx_vnf._vnf_process = mock.MagicMock() + prox_approx_vnf._vnf_process.terminate = mock.Mock() + prox_approx_vnf.ssh_helper = mock.MagicMock() + prox_approx_vnf.setup_helper = mock.Mock() + prox_approx_vnf.resource_helper = mock.MagicMock() + + self.assertIsNone(prox_approx_vnf.terminate()) + + @mock.patch(SSH_HELPER) + def test__vnf_up_post(self, ssh, *args): + mock_ssh(ssh) + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, self.VNFD0) + prox_approx_vnf.resource_helper = resource_helper = mock.Mock() + + prox_approx_vnf._vnf_up_post() + resource_helper.up_post.assert_called_once() + + @mock.patch(SSH_HELPER) + def test_vnf_execute_oserror(self, ssh, *args): + mock_ssh(ssh) + prox_approx_vnf = prox_vnf.ProxApproxVnf(NAME, self.VNFD0) + prox_approx_vnf.resource_helper = resource_helper = mock.Mock() + + resource_helper.execute.side_effect = OSError(errno.EPIPE, "") + prox_approx_vnf.vnf_execute("", _ignore_errors=True) + + resource_helper.execute.side_effect = OSError(errno.ESHUTDOWN, "") + prox_approx_vnf.vnf_execute("", _ignore_errors=True) + + resource_helper.execute.side_effect = OSError(errno.EADDRINUSE, "") + with self.assertRaises(OSError): + prox_approx_vnf.vnf_execute("", _ignore_errors=True) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_router_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_router_vnf.py new file mode 100644 index 000000000..b8f3fcaca --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_router_vnf.py @@ -0,0 +1,262 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import mock + +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.vnf_generic.vnf.router_vnf import RouterVNF + + +TEST_FILE_YAML = 'nsb_test_case.yaml' +SSH_HELPER = 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper' + + +name = 'vnf__1' + + +class TestRouterVNF(unittest.TestCase): + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{'short-name': 'RouterVNF', + 'vdu': + [{'routing_table': [], + 'description': 'RouterVNF', + 'name': 'router-baremetal', + 'nd_route_tbl': [], + 'id': 'router-baremetal', + 'external-interface': + [{'virtual-interface': + {'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02'}, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01'}, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'}]}], + 'description': 'RouterVNF', + 'mgmt-interface': + {'vdu-id': 'router-baremetal', + 'host': '1.2.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.2.1.1'}, + 'benchmark': + {'kpi': ['packets_in', 'packets_fwd', 'packets_dropped']}, + 'connection-point': [{'type': 'VPORT', 'name': 'xe0'}, + {'type': 'VPORT', 'name': 'xe1'}], + 'id': 'RouterVNF', 'name': 'VPEVnfSsh'}]}} + + scenario_cfg = {'nodes': {'cpt__0': 'compute_0.compute_nodes', + 'tg__0': 'trafficgen_1.baremetal', + 'vnf__0': 'vnf.yardstick'}, + 'options': {'flow': {'count': 128000, + 'dst_ip': ['10.0.3.26-10.0.3.105'], + 'dst_port': ['2001-2004'], + 'src_ip': ['10.0.2.26-10.0.2.105'], + 'src_port': ['1234-1238']}, + 'framesize': {'downlink': {'1024B': 100}, + 'uplink': {'1024B': 100}}, + 'rfc2544': {'allowed_drop_rate': '0.0001 - 0.1'}, + 'tg__0': {'queues_per_port': 7}, + 'traffic_type': 4, + 'vnf__0': {'nfvi_enable': True}}, + 'runner': {'interval': 35, + 'iterations': 10, + 'type': 'Iteration'}, + 'topology': 'router-tg-topology.yaml', + 'traffic_profile': '../../traffic_profiles/ipv4_throughput.yaml', + 'type': 'NSPerf'} + + context_cfg = {'nodes': {'tg__1': + {'member-vnf-index': '1', + 'role': 'TrafficGen', + 'name': 'trafficgen_1.yardstick', + 'vnfd-id-ref': 'tg__1', + 'ip': '1.2.1.1', + 'interfaces': + {'xe0': {'local_iface_name': 'ens785f0', + 'vld_id': RouterVNF.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.20', + 'dst_mac': '00:00:00:00:00:02', + 'local_mac': '00:00:00:00:00:04', + 'dst_ip': '152.16.100.19', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0}, + 'xe1': {'local_iface_name': 'ens785f1', + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.21', + 'local_mac': '00:00:00:00:00:01', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1}}, + 'password': 'r00t', + 'VNF model': 'tg_rfc2544_tpl.yaml', + 'user': 'root'}, + 'vnf__1': + {'name': 'vnf.yardstick', + 'vnfd-id-ref': 'vnf__1', + 'ip': '1.2.1.1', + 'interfaces': + {'xe0': {'local_iface_name': 'ens786f0', + 'vld_id': RouterVNF.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.19', + 'dst_mac': '00:00:00:00:00:04', + 'local_mac': '00:00:00:00:00:02', + 'dst_ip': '152.16.100.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0}, + 'xe1': {'local_iface_name': 'ens786f1', + 'vld_id': RouterVNF.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.19', + 'dst_mac': '00:00:00:00:00:03', + 'local_mac': '00:00:00:00:00:01', + 'dst_ip': '152.16.40.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1}}, + 'routing_table': [], + 'member-vnf-index': '2', + 'host': '1.2.1.1', + 'role': 'vnf', + 'user': 'root', + 'nd_route_tbl': [], + 'password': 'r00t', + 'VNF model': 'router_vnf.yaml'}}} + + IP_SHOW_STATS_OUTPUT = """\ +2: em1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000 + link/ether d4:c9:ef:52:7c:4d brd ff:ff:ff:ff:ff:ff + RX: bytes packets errors dropped overrun mcast + 2781945429 3202213 0 0 0 30131 + RX errors: length crc frame fifo missed + 0 0 0 0 0 + TX: bytes packets errors dropped carrier collsns + 646221183 2145799 0 0 0 0 + TX errors: aborted fifo window heartbeat + 0 0 0 0 +""" + STATS = { + 'RX:bytes': '2781945429', + 'RX:dropped': '0', + 'RX:errors': '0', + 'RX:mcast': '30131', + 'RX:overrun': '0', + 'RX:packets': '3202213', + 'RX errors:length': '0', + 'RX errors:crc': '0', + 'RX errors:frame': '0', + 'RX errors:fifo': '0', + 'RX errors:missed': '0', + 'TX:bytes': '646221183', + 'TX:carrier': '0', + 'TX:collsns': '0', + 'TX:dropped': '0', + 'TX:errors': '0', + 'TX:packets': '2145799', + 'TX errors:aborted': '0', + 'TX errors:fifo': '0', + 'TX errors:window': '0', + 'TX errors:heartbeat': '0', + } + + def test___init__(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + router_vnf = RouterVNF(name, vnfd) + self.assertIsNone(router_vnf._vnf_process) + + def test_get_stats(self): + stats = RouterVNF.get_stats(self.IP_SHOW_STATS_OUTPUT) + self.assertDictEqual(stats, self.STATS) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.time") + @mock.patch(SSH_HELPER) + def test_collect_kpi(self, ssh, *args): + m = mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + router_vnf = RouterVNF(name, vnfd) + router_vnf.scenario_helper.scenario_cfg = { + 'nodes': {router_vnf.name: "mock"} + } + router_vnf.ssh_helper = m + result = { + 'physical_node': 'mock_node', + 'packets_dropped': 0, + 'packets_fwd': 0, + 'packets_in': 0, + 'link_stats': {} + } + self.assertEqual(result, router_vnf.collect_kpi()) + + @mock.patch(SSH_HELPER) + def test_run_router(self, ssh): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + router_vnf = RouterVNF(name, vnfd) + router_vnf.scenario_helper.scenario_cfg = self.scenario_cfg + router_vnf._run() + router_vnf.ssh_helper.drop_connection.assert_called_once() + + @mock.patch.object(ctx_base, 'Context') + @mock.patch(SSH_HELPER) + def test_instantiate(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + router_vnf = RouterVNF(name, vnfd) + router_vnf.WAIT_TIME = 0 + router_vnf.INTERFACE_WAIT = 0 + self.scenario_cfg.update({"nodes": {"vnf__1": ""}}) + self.assertIsNone(router_vnf.instantiate(self.scenario_cfg, + self.context_cfg)) + + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.time") + @mock.patch(SSH_HELPER) + def test_terminate(self, ssh, _): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + router_vnf = RouterVNF(name, vnfd) + router_vnf._vnf_process = mock.MagicMock() + router_vnf._vnf_process.terminate = mock.Mock() + self.assertIsNone(router_vnf.terminate()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py new file mode 100644 index 000000000..21f0c5e1f --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_sample_vnf.py @@ -0,0 +1,1532 @@ +# Copyright (c) 2017-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from copy import deepcopy + +import unittest +import mock +import six +import subprocess +import time + +import paramiko + +from yardstick.common import exceptions as y_exceptions +from yardstick.common import utils +from yardstick.network_services.nfvi import resource +from yardstick.network_services.vnf_generic.vnf import base +from yardstick.network_services.vnf_generic.vnf import sample_vnf +from yardstick.network_services.vnf_generic.vnf import vnf_ssh_helper +from yardstick import ssh +from yardstick.tests.unit.network_services.vnf_generic.vnf import test_base +from yardstick.benchmark.contexts import base as ctx_base + + +class MockError(Exception): + pass + + +class TestVnfSshHelper(unittest.TestCase): + + VNFD_0 = { + 'short-name': 'VpeVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0' + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1' + }, + ], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0' + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1' + }, + ], + 'id': 'vpevnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + 'driver': 'i40e', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.100.20', + 'local_mac': '00:00:00:00:00:01', + 'vld_id': 'uplink_0', + 'ifname': 'xe0', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0' + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + 'driver': 'ixgbe', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_mac': '00:00:00:00:00:02', + 'vld_id': 'downlink_0', + 'ifname': 'xe1', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1' + }, + ], + }, + ], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1' + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh' + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ] + } + } + + def setUp(self): + self.ssh_helper = vnf_ssh_helper.VnfSshHelper( + self.VNFD_0['mgmt-interface'], 'my/bin/path') + self.ssh_helper._run = mock.Mock() + + def assertAll(self, iterable, message=None): + self.assertTrue(all(iterable), message) + + def test_get_class(self): + self.assertIs(vnf_ssh_helper.VnfSshHelper.get_class(), + vnf_ssh_helper.VnfSshHelper) + + @mock.patch.object(ssh, 'paramiko') + def test_copy(self, _): + self.ssh_helper.execute('ls') + self.assertTrue(self.ssh_helper.is_connected) + result = self.ssh_helper.copy() + self.assertIsInstance(result, vnf_ssh_helper.VnfSshHelper) + self.assertFalse(result.is_connected) + self.assertEqual(result.bin_path, self.ssh_helper.bin_path) + self.assertEqual(result.host, self.ssh_helper.host) + self.assertEqual(result.port, self.ssh_helper.port) + self.assertEqual(result.user, self.ssh_helper.user) + self.assertEqual(result.password, self.ssh_helper.password) + self.assertEqual(result.key_filename, self.ssh_helper.key_filename) + + @mock.patch.object(paramiko, 'SSHClient') + def test_upload_config_file(self, mock_paramiko): + self.assertFalse(self.ssh_helper.is_connected) + cfg_file = self.ssh_helper.upload_config_file('/my/prefix', 'my content') + self.assertTrue(self.ssh_helper.is_connected) + mock_paramiko.assert_called_once() + self.assertEqual(cfg_file, '/my/prefix') + + @mock.patch.object(paramiko, 'SSHClient') + def test_upload_config_file_path_does_not_exist(self, mock_paramiko): + self.assertFalse(self.ssh_helper.is_connected) + cfg_file = self.ssh_helper.upload_config_file('my/prefix', 'my content') + self.assertTrue(self.ssh_helper.is_connected) + mock_paramiko.assert_called_once() + self.assertTrue(cfg_file.startswith('/tmp')) + + def test_join_bin_path(self): + expected_start = 'my' + expected_middle_list = ['bin'] + expected_end = 'path' + result = self.ssh_helper.join_bin_path() + self.assertTrue(result.startswith(expected_start)) + self.assertAll(middle in result for middle in expected_middle_list) + self.assertTrue(result.endswith(expected_end)) + + expected_middle_list.append(expected_end) + expected_end = 'some_file.sh' + result = self.ssh_helper.join_bin_path('some_file.sh') + self.assertTrue(result.startswith(expected_start)) + self.assertAll(middle in result for middle in expected_middle_list) + self.assertTrue(result.endswith(expected_end)) + + expected_middle_list.append('some_dir') + expected_end = 'some_file.sh' + result = self.ssh_helper.join_bin_path('some_dir', 'some_file.sh') + self.assertTrue(result.startswith(expected_start)) + self.assertAll(middle in result for middle in expected_middle_list) + self.assertTrue(result.endswith(expected_end)) + + @mock.patch.object(paramiko, 'SSHClient') + @mock.patch.object(ssh, 'provision_tool') + def test_provision_tool(self, mock_provision_tool, mock_paramiko): + self.assertFalse(self.ssh_helper.is_connected) + self.ssh_helper.provision_tool() + self.assertTrue(self.ssh_helper.is_connected) + mock_paramiko.assert_called_once() + mock_provision_tool.assert_called_once() + + self.ssh_helper.provision_tool(tool_file='my_tool.sh') + self.assertTrue(self.ssh_helper.is_connected) + mock_paramiko.assert_called_once() + self.assertEqual(mock_provision_tool.call_count, 2) + + self.ssh_helper.provision_tool('tool_path', 'my_tool.sh') + self.assertTrue(self.ssh_helper.is_connected) + mock_paramiko.assert_called_once() + self.assertEqual(mock_provision_tool.call_count, 3) + + +class TestSetupEnvHelper(unittest.TestCase): + + VNFD_0 = TestVnfSshHelper.VNFD_0 + + def setUp(self): + self.setup_env_helper = sample_vnf.SetupEnvHelper( + mock.Mock(), mock.Mock(), mock.Mock()) + + def test_build_config(self): + with self.assertRaises(NotImplementedError): + self.setup_env_helper.build_config() + + def test_setup_vnf_environment(self): + self.assertIsNone(self.setup_env_helper.setup_vnf_environment()) + + def test_tear_down(self): + with self.assertRaises(NotImplementedError): + self.setup_env_helper.tear_down() + + +class TestDpdkVnfSetupEnvHelper(unittest.TestCase): + + VNFD_0 = TestVnfSshHelper.VNFD_0 + + VNFD = TestVnfSshHelper.VNFD + + def setUp(self): + self.vnfd_helper = base.VnfdHelper(deepcopy(self.VNFD_0)) + self.scenario_helper = mock.Mock() + self.ssh_helper = mock.Mock() + self.dpdk_setup_helper = sample_vnf.DpdkVnfSetupEnvHelper( + self.vnfd_helper, self.ssh_helper, self.scenario_helper) + + def test__update_packet_type(self): + ip_pipeline_cfg = 'pkt_type = ipv4' + pkt_type = {'pkt_type': '1'} + + expected = "pkt_type = 1" + result = self.dpdk_setup_helper._update_packet_type( + ip_pipeline_cfg, pkt_type) + self.assertEqual(result, expected) + + def test__update_packet_type_no_op(self): + ip_pipeline_cfg = 'pkt_type = ipv6' + pkt_type = {'pkt_type': '1'} + + expected = "pkt_type = ipv6" + result = self.dpdk_setup_helper._update_packet_type( + ip_pipeline_cfg, pkt_type) + self.assertEqual(result, expected) + + def test__update_packet_type_multi_op(self): + ip_pipeline_cfg = 'pkt_type = ipv4\npkt_type = 1\npkt_type = ipv4' + pkt_type = {'pkt_type': '1'} + expected = 'pkt_type = 1\npkt_type = 1\npkt_type = 1' + + result = self.dpdk_setup_helper._update_packet_type( + ip_pipeline_cfg, pkt_type) + self.assertEqual(result, expected) + + def test__update_traffic_type(self): + ip_pipeline_cfg = 'pkt_type = ipv4' + traffic_options = { + "vnf_type": sample_vnf.DpdkVnfSetupEnvHelper.APP_NAME, + "traffic_type": 4} + expected = "pkt_type = ipv4" + + result = self.dpdk_setup_helper._update_traffic_type( + ip_pipeline_cfg, traffic_options) + self.assertEqual(result, expected) + + def test__update_traffic_type_ipv6(self): + ip_pipeline_cfg = 'pkt_type = ipv4' + traffic_options = { + "vnf_type": sample_vnf.DpdkVnfSetupEnvHelper.APP_NAME, + "traffic_type": 6} + expected = "pkt_type = ipv6" + + result = self.dpdk_setup_helper._update_traffic_type( + ip_pipeline_cfg, traffic_options) + self.assertEqual(result, expected) + + def test__update_traffic_type_not_app_name(self): + ip_pipeline_cfg = 'traffic_type = 4' + vnf_type = ''.join(["Not", sample_vnf.DpdkVnfSetupEnvHelper.APP_NAME]) + traffic_options = {"vnf_type": vnf_type, 'traffic_type': 8} + expected = "traffic_type = 8" + + result = self.dpdk_setup_helper._update_traffic_type( + ip_pipeline_cfg, traffic_options) + self.assertEqual(result, expected) + + @mock.patch.object(six.moves.builtins, 'open') + @mock.patch.object(utils, 'find_relative_file') + @mock.patch.object(sample_vnf, 'MultiPortConfig') + def test_build_config(self, mock_multi_port_config_class, + mock_find, *args): + mock_multi_port_config = mock_multi_port_config_class() + self.scenario_helper.vnf_cfg = {} + self.scenario_helper.options = {} + self.scenario_helper.all_options = {} + + self.dpdk_setup_helper.PIPELINE_COMMAND = expected = 'pipeline command' + result = self.dpdk_setup_helper.build_config() + self.assertEqual(result, expected) + self.assertGreaterEqual(self.ssh_helper.upload_config_file.call_count, 2) + mock_find.assert_called() + mock_multi_port_config.generate_config.assert_called() + mock_multi_port_config.generate_script.assert_called() + + @mock.patch.object(six.moves.builtins, 'open') + @mock.patch.object(utils, 'find_relative_file') + @mock.patch.object(sample_vnf, 'MultiPortConfig') + @mock.patch.object(utils, 'open_relative_file') + def test_build_config2(self, mock_open_rf, mock_multi_port_config_class, + mock_find, *args): + mock_multi_port_config = mock_multi_port_config_class() + self.scenario_helper.options = {'rules': 'fake_file'} + self.scenario_helper.vnf_cfg = {'file': 'fake_file'} + self.scenario_helper.all_options = {} + mock_open_rf.side_effect = mock.mock_open(read_data='fake_data') + self.dpdk_setup_helper.PIPELINE_COMMAND = expected = 'pipeline command' + + result = self.dpdk_setup_helper.build_config() + + mock_open_rf.assert_called() + self.assertEqual(result, expected) + self.assertGreaterEqual(self.ssh_helper.upload_config_file.call_count, 2) + mock_find.assert_called() + mock_multi_port_config.generate_config.assert_called() + mock_multi_port_config.generate_script.assert_called() + + def test__build_pipeline_kwargs(self): + self.ssh_helper.provision_tool.return_value = 'tool_path' + self.dpdk_setup_helper.CFG_CONFIG = 'config' + self.dpdk_setup_helper.CFG_SCRIPT = 'script' + self.dpdk_setup_helper.pipeline_kwargs = {} + self.dpdk_setup_helper.all_ports = [0, 1, 2] + self.dpdk_setup_helper.scenario_helper.vnf_cfg = {'lb_config': 'HW', + 'worker_threads': 1} + + expected = { + 'cfg_file': 'config', + 'script': 'script', + 'port_mask_hex': '0x3', + 'tool_path': 'tool_path', + 'hwlb': ' --hwlb 1', + } + self.dpdk_setup_helper._build_pipeline_kwargs() + self.assertDictEqual(self.dpdk_setup_helper.pipeline_kwargs, expected) + + @mock.patch.object(time, 'sleep') + @mock.patch.object(ssh, 'SSH') + def test_setup_vnf_environment(self, *args): + self.scenario_helper.nodes = [None, None] + + def execute(cmd): + if cmd.startswith('which '): + return exec_failure + return exec_success + + exec_success = (0, 'good output', '') + exec_failure = (1, 'bad output', 'error output') + self.ssh_helper.execute = execute + + self.dpdk_setup_helper._validate_cpu_cfg = mock.Mock(return_value=[]) + + with mock.patch.object(self.dpdk_setup_helper, '_setup_dpdk'): + self.assertIsInstance( + self.dpdk_setup_helper.setup_vnf_environment(), + resource.ResourceProfile) + + @mock.patch.object(utils, 'setup_hugepages') + def test__setup_dpdk(self, mock_setup_hugepages): + self.ssh_helper.execute = mock.Mock() + self.ssh_helper.execute.return_value = (0, 0, 0) + self.scenario_helper.all_options = {'hugepages_gb': 8} + self.dpdk_setup_helper._setup_dpdk() + mock_setup_hugepages.assert_called_once_with( + self.ssh_helper, 8*1024*1024) + self.ssh_helper.execute.assert_has_calls([ + mock.call('sudo modprobe uio && sudo modprobe igb_uio'), + mock.call('lsmod | grep -i igb_uio') + ]) + + @mock.patch.object(ssh, 'SSH') + def test__setup_resources(self, _): + self.dpdk_setup_helper._validate_cpu_cfg = mock.Mock() + self.dpdk_setup_helper.bound_pci = [v['virtual-interface']["vpci"] for v in + self.vnfd_helper.interfaces] + result = self.dpdk_setup_helper._setup_resources() + self.assertIsInstance(result, resource.ResourceProfile) + self.assertEqual(self.dpdk_setup_helper.socket, 0) + + @mock.patch.object(ssh, 'SSH') + def test__setup_resources_socket_1(self, _): + self.vnfd_helper.interfaces[0]['virtual-interface']['vpci'] = \ + '0000:55:00.0' + self.vnfd_helper.interfaces[1]['virtual-interface']['vpci'] = \ + '0000:35:00.0' + + self.dpdk_setup_helper._validate_cpu_cfg = mock.Mock() + self.dpdk_setup_helper.bound_pci = [v['virtual-interface']["vpci"] for v in + self.vnfd_helper.interfaces] + result = self.dpdk_setup_helper._setup_resources() + self.assertIsInstance(result, resource.ResourceProfile) + self.assertEqual(self.dpdk_setup_helper.socket, 1) + + @mock.patch.object(time, 'sleep') + def test__detect_and_bind_drivers(self, *args): + self.scenario_helper.nodes = [None, None] + rv = ['0000:05:00.1', '0000:05:00.0'] + + self.dpdk_setup_helper.dpdk_bind_helper._get_bound_pci_addresses = \ + mock.Mock(return_value=rv) + self.dpdk_setup_helper.dpdk_bind_helper.bind = mock.Mock() + self.dpdk_setup_helper.dpdk_bind_helper.read_status = mock.Mock() + + self.assertIsNone(self.dpdk_setup_helper._detect_and_bind_drivers()) + + intf_0 = self.vnfd_helper.vdu[0]['external-interface'][0]['virtual-interface'] + intf_1 = self.vnfd_helper.vdu[0]['external-interface'][1]['virtual-interface'] + self.assertEqual(0, intf_0['dpdk_port_num']) + self.assertEqual(1, intf_1['dpdk_port_num']) + + def test_tear_down(self): + self.scenario_helper.nodes = [None, None] + + self.dpdk_setup_helper.dpdk_bind_helper.bind = mock.Mock() + self.dpdk_setup_helper.dpdk_bind_helper.used_drivers = { + 'd1': ['0000:05:00.0'], + 'd3': ['0000:05:01.0'], + } + + self.assertIsNone(self.dpdk_setup_helper.tear_down()) + self.dpdk_setup_helper.dpdk_bind_helper.bind.assert_any_call( + ['0000:05:00.0'], 'd1', True) + self.dpdk_setup_helper.dpdk_bind_helper.bind.assert_any_call( + ['0000:05:01.0'], 'd3', True) + + +class TestResourceHelper(unittest.TestCase): + + VNFD_0 = { + 'short-name': 'VpeVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0' + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1' + }, + ], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0' + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1' + }, + ], + 'id': 'vpevnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.0', + 'driver': 'i40e', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.100.20', + 'local_mac': '00:00:00:00:00:01' + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0' + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.1', + 'driver': 'ixgbe', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_mac': '00:00:00:00:00:02' + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1' + }, + ], + }, + ], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1' + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh' + } + + def setUp(self): + self.vnfd_helper = base.VnfdHelper(self.VNFD_0) + self.dpdk_setup_helper = sample_vnf.DpdkVnfSetupEnvHelper( + self.vnfd_helper, mock.Mock(), mock.Mock()) + self.resource_helper = sample_vnf.ResourceHelper(self.dpdk_setup_helper) + + def test_setup(self): + resource = object() + self.dpdk_setup_helper.setup_vnf_environment = ( + mock.Mock(return_value=resource)) + resource_helper = sample_vnf.ResourceHelper(self.dpdk_setup_helper) + + self.assertIsNone(resource_helper.setup()) + self.assertIs(resource_helper.resource, resource) + + def test_generate_cfg(self): + self.assertIsNone(self.resource_helper.generate_cfg()) + + def test_stop_collect(self): + self.resource_helper.resource = mock.Mock() + + self.assertIsNone(self.resource_helper.stop_collect()) + + def test_stop_collect_none(self): + self.resource_helper.resource = None + + self.assertIsNone(self.resource_helper.stop_collect()) + + +class TestClientResourceHelper(unittest.TestCase): + + VNFD_0 = { + 'short-name': 'VpeVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0' + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1' + }, + ], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0' + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1' + }, + ], + 'id': 'vpevnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.0', + 'driver': 'i40e', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.100.20', + 'local_mac': '00:00:00:00:00:01', + 'vld_id': 'uplink_0', + 'ifname': 'xe0', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0' + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.1', + 'driver': 'ixgbe', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_mac': '00:00:00:00:00:02', + 'vld_id': 'downlink_0', + 'ifname': 'xe1', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1' + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:13', + 'vpci': '0000:05:00.2', + 'driver': 'ixgbe', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 2, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.30', + 'local_mac': '00:00:00:00:00:11' + }, + 'vnfd-connection-point-ref': 'xe2', + 'name': 'xe2' + }, + ], + }, + ], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1' + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh' + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ], + }, + } + + def setUp(self): + vnfd_helper = base.VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + dpdk_setup_helper = sample_vnf.DpdkVnfSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + self.client_resource_helper = ( + sample_vnf.ClientResourceHelper(dpdk_setup_helper)) + + @mock.patch.object(sample_vnf, 'LOG') + @mock.patch.object(sample_vnf, 'STLError', new_callable=lambda: MockError) + def test_get_stats_not_connected(self, mock_stl_error, *args): + self.client_resource_helper.client = mock.Mock() + self.client_resource_helper.client.get_stats.side_effect = \ + mock_stl_error + + self.assertEqual(self.client_resource_helper.get_stats(), {}) + self.client_resource_helper.client.get_stats.assert_called_once() + + def test_clear_stats(self): + self.client_resource_helper.client = mock.Mock() + + self.assertIsNone(self.client_resource_helper.clear_stats()) + self.assertEqual( + self.client_resource_helper.client.clear_stats.call_count, 1) + + def test_clear_stats_of_ports(self): + self.client_resource_helper.client = mock.Mock() + + self.assertIsNone(self.client_resource_helper.clear_stats([3, 4])) + self.client_resource_helper.client.clear_stats.assert_called_once() + + def test_start(self): + self.client_resource_helper.client = mock.Mock() + + self.assertIsNone(self.client_resource_helper.start()) + self.client_resource_helper.client.start.assert_called_once() + + def test_start_ports(self): + self.client_resource_helper.client = mock.Mock() + + self.assertIsNone(self.client_resource_helper.start([3, 4])) + self.client_resource_helper.client.start.assert_called_once() + + def test_collect_kpi_with_queue(self): + self.client_resource_helper._result = { + 'existing': 43, + 'replaceable': 12} + self.client_resource_helper._queue = mock.Mock() + self.client_resource_helper._queue.empty.return_value = False + self.client_resource_helper._queue.get.return_value = { + 'incoming': 34, + 'replaceable': 99} + + expected = { + 'existing': 43, + 'incoming': 34, + 'replaceable': 99, + } + result = self.client_resource_helper.collect_kpi() + self.assertEqual(result, expected) + + @mock.patch.object(time, 'sleep') + @mock.patch.object(sample_vnf, 'STLError') + def test__connect_with_failures(self, mock_stl_error, *args): + client = mock.MagicMock() + client.connect.side_effect = mock_stl_error(msg='msg') + + self.assertIs(self.client_resource_helper._connect(client), client) + + +class TestRfc2544ResourceHelper(unittest.TestCase): + + RFC2544_CFG_1 = { + 'latency': True, + 'correlated_traffic': True, + 'allowed_drop_rate': '0.1 - 0.15', + } + + RFC2544_CFG_2 = { + 'allowed_drop_rate': ' 0.25 - 0.05 ', + } + + RFC2544_CFG_3 = { + 'allowed_drop_rate': '0.2', + } + + RFC2544_CFG_4 = { + 'latency': True, + } + + SCENARIO_CFG_1 = { + 'options': { + 'rfc2544': RFC2544_CFG_1, + } + } + + SCENARIO_CFG_2 = { + 'options': { + 'rfc2544': RFC2544_CFG_2, + } + } + + SCENARIO_CFG_3 = { + 'options': { + 'rfc2544': RFC2544_CFG_3, + } + } + + SCENARIO_CFG_4 = { + 'options': { + 'rfc2544': RFC2544_CFG_4, + } + } + + def setUp(self): + self.scenario_helper = sample_vnf.ScenarioHelper('name1') + self.rfc2544_resource_helper = \ + sample_vnf.Rfc2544ResourceHelper(self.scenario_helper) + + def test_property_rfc2544(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_1 + + self.assertIsNone(self.rfc2544_resource_helper._rfc2544) + self.assertEqual(self.rfc2544_resource_helper.rfc2544, + self.RFC2544_CFG_1) + self.assertEqual(self.rfc2544_resource_helper._rfc2544, + self.RFC2544_CFG_1) + # ensure that resource_helper caches + self.scenario_helper.scenario_cfg = {} + self.assertEqual(self.rfc2544_resource_helper.rfc2544, + self.RFC2544_CFG_1) + + def test_property_tolerance_high(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_1 + + self.assertIsNone(self.rfc2544_resource_helper._tolerance_high) + self.assertEqual(self.rfc2544_resource_helper.tolerance_high, 0.15) + self.assertEqual(self.rfc2544_resource_helper._tolerance_high, 0.15) + self.assertEqual(self.rfc2544_resource_helper._tolerance_precision, 2) + # ensure that resource_helper caches + self.scenario_helper.scenario_cfg = {} + self.assertEqual(self.rfc2544_resource_helper.tolerance_high, 0.15) + + def test_property_tolerance_low(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_1 + + self.assertIsNone(self.rfc2544_resource_helper._tolerance_low) + self.assertEqual(self.rfc2544_resource_helper.tolerance_low, 0.1) + self.assertEqual(self.rfc2544_resource_helper._tolerance_low, 0.1) + # ensure that resource_helper caches + self.scenario_helper.scenario_cfg = {} + self.assertEqual(self.rfc2544_resource_helper.tolerance_low, 0.1) + + def test_property_tolerance_high_range_swap(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_2 + + self.assertEqual(self.rfc2544_resource_helper.tolerance_high, 0.25) + + def test_property_tolerance_low_range_swap(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_2 + + self.assertEqual(self.rfc2544_resource_helper.tolerance_low, 0.05) + + def test_property_tolerance_high_not_range(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_3 + + self.assertEqual(self.rfc2544_resource_helper.tolerance_high, 0.2) + self.assertEqual(self.rfc2544_resource_helper._tolerance_precision, 1) + + def test_property_tolerance_low_not_range(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_3 + + self.assertEqual(self.rfc2544_resource_helper.tolerance_low, 0.2) + + def test_property_tolerance_high_default(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_4 + + self.assertEqual(self.rfc2544_resource_helper.tolerance_high, 0.0001) + + def test_property_tolerance_low_default(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_4 + + self.assertEqual(self.rfc2544_resource_helper.tolerance_low, 0.0001) + + def test_property_latency(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_1 + + self.assertIsNone(self.rfc2544_resource_helper._latency) + self.assertTrue(self.rfc2544_resource_helper.latency) + self.assertTrue(self.rfc2544_resource_helper._latency) + # ensure that resource_helper caches + self.scenario_helper.scenario_cfg = {} + self.assertTrue(self.rfc2544_resource_helper.latency) + + def test_property_latency_default(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_2 + + self.assertFalse(self.rfc2544_resource_helper.latency) + + def test_property_correlated_traffic(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_1 + + self.assertIsNone(self.rfc2544_resource_helper._correlated_traffic) + self.assertTrue(self.rfc2544_resource_helper.correlated_traffic) + self.assertTrue(self.rfc2544_resource_helper._correlated_traffic) + # ensure that resource_helper caches + self.scenario_helper.scenario_cfg = {} + self.assertTrue(self.rfc2544_resource_helper.correlated_traffic) + + def test_property_correlated_traffic_default(self): + self.scenario_helper.scenario_cfg = self.SCENARIO_CFG_2 + + self.assertFalse(self.rfc2544_resource_helper.correlated_traffic) + + +class TestSampleVNFDeployHelper(unittest.TestCase): + + def setUp(self): + self._mock_time_sleep = mock.patch.object(time, 'sleep') + self.mock_time_sleep = self._mock_time_sleep.start() + self._mock_check_output = mock.patch.object(subprocess, 'check_output') + self.mock_check_output = self._mock_check_output.start() + self.addCleanup(self._stop_mocks) + + self.ssh_helper = mock.Mock() + self.sample_vnf_deploy_helper = sample_vnf.SampleVNFDeployHelper( + mock.Mock(), self.ssh_helper) + self.ssh_helper.join_bin_path.return_value = 'joined_path' + self.ssh_helper.put.return_value = None + + def _stop_mocks(self): + self._mock_time_sleep.stop() + self._mock_check_output.stop() + + def test_deploy_vnfs_disabled(self): + self.ssh_helper.execute.return_value = 1, 'bad output', 'error output' + + self.sample_vnf_deploy_helper.deploy_vnfs('name1') + self.sample_vnf_deploy_helper.DISABLE_DEPLOY = True + self.assertEqual(self.ssh_helper.execute.call_count, 5) + self.ssh_helper.put.assert_called_once() + + def test_deploy_vnfs(self): + self.ssh_helper.execute.return_value = 1, 'bad output', 'error output' + self.sample_vnf_deploy_helper.DISABLE_DEPLOY = False + + self.sample_vnf_deploy_helper.deploy_vnfs('name1') + self.assertEqual(self.ssh_helper.execute.call_count, 5) + self.ssh_helper.put.assert_called_once() + + def test_deploy_vnfs_early_success(self): + self.ssh_helper.execute.return_value = 0, 'output', '' + self.sample_vnf_deploy_helper.DISABLE_DEPLOY = False + + self.sample_vnf_deploy_helper.deploy_vnfs('name1') + self.ssh_helper.execute.assert_called_once() + self.ssh_helper.put.assert_not_called() + + +class TestScenarioHelper(unittest.TestCase): + + def setUp(self): + self.scenario_helper = sample_vnf.ScenarioHelper('name1') + + def test_property_task_path(self): + self.scenario_helper.scenario_cfg = { + 'task_path': 'my_path', + } + + self.assertEqual(self.scenario_helper.task_path, 'my_path') + + def test_property_nodes(self): + nodes = ['node1', 'node2'] + self.scenario_helper.scenario_cfg = { + 'nodes': nodes, + } + + self.assertEqual(self.scenario_helper.nodes, nodes) + + def test_property_all_options(self): + data = { + 'name1': { + 'key3': 'value3', + }, + 'name2': {} + } + self.scenario_helper.scenario_cfg = { + 'options': data, + } + + self.assertDictEqual(self.scenario_helper.all_options, data) + + def test_property_options(self): + data = { + 'key1': 'value1', + 'key2': 'value2', + } + self.scenario_helper.scenario_cfg = { + 'options': { + 'name1': data, + }, + } + + self.assertDictEqual(self.scenario_helper.options, data) + + def test_property_vnf_cfg(self): + self.scenario_helper.scenario_cfg = { + 'options': { + 'name1': { + 'vnf_config': 'my_config', + }, + }, + } + + self.assertEqual(self.scenario_helper.vnf_cfg, 'my_config') + + def test_property_vnf_cfg_default(self): + self.scenario_helper.scenario_cfg = { + 'options': { + 'name1': {}, + }, + } + + self.assertEqual(self.scenario_helper.vnf_cfg, + sample_vnf.ScenarioHelper.DEFAULT_VNF_CFG) + + def test_property_topology(self): + self.scenario_helper.scenario_cfg = { + 'topology': 'my_topology', + } + + self.assertEqual(self.scenario_helper.topology, 'my_topology') + + +class TestSampleVnf(unittest.TestCase): + + VNFD_0 = { + 'short-name': 'VpeVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0' + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1' + }, + ], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0' + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1' + }, + ], + 'id': 'vpevnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.100.20', + 'local_mac': '00:00:00:00:00:01' + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0' + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_mac': '00:00:00:00:00:02' + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1' + }, + ], + }, + ], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1' + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh' + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ] + } + } + + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64, + }, + } + def setUp(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.vnf = sample_vnf.SampleVNF('vnf1', vnfd) + self.vnf.APP_NAME = 'sample1' + + def test___init__(self): + vnf = sample_vnf.SampleVNF('vnf1', self.VNFD_0) + + self.assertEqual(vnf.name, 'vnf1') + self.assertDictEqual(vnf.vnfd_helper, self.VNFD_0) + + # test the default setup helper is SetupEnvHelper, not subclass + self.assertEqual(type(vnf.setup_helper), + sample_vnf.SetupEnvHelper) + + # test the default resource helper is ResourceHelper, not subclass + self.assertEqual(type(vnf.resource_helper), sample_vnf.ResourceHelper) + + def test___init___alt_types(self): + class MySetupEnvHelper(sample_vnf.SetupEnvHelper): + pass + + class MyResourceHelper(sample_vnf.ResourceHelper): + pass + + vnf = sample_vnf.SampleVNF('vnf1', self.VNFD_0, + MySetupEnvHelper, MyResourceHelper) + + self.assertEqual(vnf.name, 'vnf1') + self.assertDictEqual(vnf.vnfd_helper, self.VNFD_0) + + # test the default setup helper is MySetupEnvHelper, not subclass + self.assertEqual(type(vnf.setup_helper), MySetupEnvHelper) + + # test the default resource helper is MyResourceHelper, not subclass + self.assertEqual(type(vnf.resource_helper), MyResourceHelper) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.Process') + def test__start_vnf(self, *args): + self.vnf._run = mock.Mock() + + self.assertIsNone(self.vnf.queue_wrapper) + self.assertIsNone(self.vnf._vnf_process) + self.vnf._start_vnf() + self.assertIsNotNone(self.vnf.queue_wrapper) + self.assertIsNotNone(self.vnf._vnf_process) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + @mock.patch.object(ssh, "SSH") + def test_instantiate(self, ssh, *args): + test_base.mock_ssh(ssh) + nodes = { + 'vnf1': 'name1', + 'vnf2': 'name2', + } + self.vnf._start_server = mock.Mock(return_value=0) + self.vnf._vnf_process = mock.MagicMock() + self.vnf._vnf_process._is_alive.return_value = 1 + self.vnf.ssh_helper = mock.MagicMock() + self.vnf.deploy_helper = mock.MagicMock() + self.vnf.resource_helper.ssh_helper = mock.MagicMock() + scenario_cfg = { + 'nodes': nodes, + } + + self.assertIsNone(self.vnf.instantiate(scenario_cfg, {})) + + def test__update_collectd_options(self): + scenario_cfg = {'options': + {'collectd': + {'interval': 3, + 'plugins': + {'plugin3': {'param': 3}}}, + 'vnf1': + {'collectd': + {'interval': 2, + 'plugins': + {'plugin3': {'param': 2}, + 'plugin2': {'param': 2}}}}}} + context_cfg = {'nodes': + {'vnf1': + {'collectd': + {'interval': 1, + 'plugins': + {'plugin3': {'param': 1}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}}}}} + expected = {'interval': 1, + 'plugins': + {'plugin3': {'param': 1}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}} + + self.vnf._update_collectd_options(scenario_cfg, context_cfg) + self.assertEqual(self.vnf.setup_helper.collectd_options, expected) + + def test__update_options(self): + options1 = {'interval': 1, + 'param1': 'value1', + 'plugins': + {'plugin3': {'param': 3}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}} + options2 = {'interval': 2, + 'param2': 'value2', + 'plugins': + {'plugin4': {'param': 4}, + 'plugin2': {'param': 2}, + 'plugin1': {'param': 2}}} + expected = {'interval': 1, + 'param1': 'value1', + 'param2': 'value2', + 'plugins': + {'plugin4': {'param': 4}, + 'plugin3': {'param': 3}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}} + + self.vnf._update_options(options2, options1) + self.assertEqual(options2, expected) + + @mock.patch.object(time, 'sleep') + @mock.patch.object(ssh, 'SSH') + def test_wait_for_instantiate_empty_queue(self, ssh, *args): + test_base.mock_ssh(ssh, exec_result=(1, "", "")) + + queue_size_list = [ + 0, + 1, + 0, + 1, + ] + + queue_get_list = [ + 'some output', + 'pipeline> ', + ] + + self.vnf.WAIT_TIME_FOR_SCRIPT = 0 + self.vnf._start_server = mock.Mock(return_value=0) + self.vnf._vnf_process = mock.MagicMock() + self.vnf._vnf_process.exitcode = 0 + self.vnf._vnf_process._is_alive.return_value = 1 + self.vnf.queue_wrapper = mock.Mock() + self.vnf.q_out = mock.Mock() + self.vnf.q_out.qsize.side_effect = iter(queue_size_list) + self.vnf.q_out.get.side_effect = iter(queue_get_list) + self.vnf.ssh_helper = mock.MagicMock() + self.vnf.resource_helper.ssh_helper = mock.MagicMock() + self.vnf.resource_helper.start_collect = mock.MagicMock() + + self.assertEqual(self.vnf.wait_for_instantiate(), 0) + + @mock.patch.object(time, 'sleep') + @mock.patch.object(ssh, 'SSH') + def test_wait_for_initialize(self, ssh, *args): + test_base.mock_ssh(ssh, exec_result=(1, "", "")) + queue_get_list = [ + 'some output', + 'pipeline> ', + 'run non_existent_script_name', + 'Cannot open file "non_existent_script_name"' + ] + queue_size_list = [ + 0, + len(queue_get_list[0]), + 0, + len(queue_get_list[1]), + len(queue_get_list[2]), + 0, + len(queue_get_list[3]) + ] + self.vnf.WAIT_TIME_FOR_SCRIPT = 0 + self.vnf._start_server = mock.Mock(return_value=0) + self.vnf._vnf_process = mock.MagicMock() + self.vnf._vnf_process.exitcode = 0 + self.vnf._vnf_process._is_alive.return_value = 1 + self.vnf.queue_wrapper = mock.Mock() + self.vnf.q_out = mock.Mock() + self.vnf.q_out.qsize.side_effect = iter(queue_size_list) + self.vnf.q_out.get.side_effect = iter(queue_get_list) + self.vnf.ssh_helper = mock.MagicMock() + self.vnf.resource_helper.ssh_helper = mock.MagicMock() + self.vnf.resource_helper.start_collect = mock.MagicMock() + + self.assertEqual(self.vnf.wait_for_initialize(), 0) + + @mock.patch.object(time, "sleep") + def test_vnf_execute_with_queue_data(self, *args): + queue_size_list = [ + 1, + 1, + 0, + ] + + queue_get_list = [ + 'hello ', + 'world' + ] + self.vnf.q_out = mock.Mock() + self.vnf.q_out.qsize.side_effect = iter(queue_size_list) + self.vnf.q_out.get.side_effect = iter(queue_get_list) + + self.assertEqual(self.vnf.vnf_execute('my command'), 'hello world') + + def test_terminate_without_vnf_process(self): + self.vnf.vnf_execute = mock.Mock() + self.vnf.ssh_helper = mock.Mock() + self.vnf._tear_down = mock.Mock() + self.vnf.resource_helper = mock.Mock() + + self.assertIsNone(self.vnf.terminate()) + + def test_get_stats(self): + self.vnf.APP_WORD = 'sample1' + self.vnf.vnf_execute = mock.Mock(return_value='the stats') + + self.assertEqual(self.vnf.get_stats(), 'the stats') + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', + return_value='mock_node') + def test_collect_kpi(self, *args): + self.vnf.scenario_helper.scenario_cfg = { + 'nodes': {self.vnf.name: "mock"} + } + self.vnf.COLLECT_KPI = r'\s(\d+)\D*(\d+)\D*(\d+)' + self.vnf.COLLECT_MAP = { + 'k1': 3, + 'k2': 1, + 'k3': 2, + } + self.vnf.get_stats = mock.Mock(return_value='index0: 34 -- 91, 27') + self.vnf.resource_helper = mock.Mock() + self.vnf.resource_helper.collect_kpi.return_value = {} + + expected = { + 'k1': 27, + 'k2': 34, + 'k3': 91, + 'collect_stats': {}, + 'physical_node': 'mock_node' + } + result = self.vnf.collect_kpi() + self.assertEqual(result, expected) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', + return_value='mock_node') + def test_collect_kpi_default(self, *args): + self.vnf.scenario_helper.scenario_cfg = { + 'nodes': {self.vnf.name: "mock"} + } + self.vnf.COLLECT_KPI = r'\s(\d+)\D*(\d+)\D*(\d+)' + self.vnf.get_stats = mock.Mock(return_value='') + + expected = { + 'physical_node': 'mock_node', + 'packets_in': 0, + 'packets_fwd': 0, + 'packets_dropped': 0, + } + result = self.vnf.collect_kpi() + self.assertEqual(result, expected) + + def test_scale(self): + self.assertRaises(y_exceptions.FunctionNotImplemented, self.vnf.scale) + + def test__run(self): + test_cmd = 'test cmd' + run_kwargs = {'arg1': 'val1', 'arg2': 'val2'} + self.vnf.ssh_helper = mock.Mock() + self.vnf.setup_helper = mock.Mock() + with mock.patch.object(self.vnf, '_build_config', + return_value=test_cmd), \ + mock.patch.object(self.vnf, '_build_run_kwargs'): + self.vnf.run_kwargs = run_kwargs + self.vnf._run() + self.vnf.ssh_helper.drop_connection.assert_called_once() + self.vnf.ssh_helper.run.assert_called_once_with(test_cmd, **run_kwargs) + self.vnf.setup_helper.kill_vnf.assert_called_once() + + +class TestSampleVNFTrafficGen(unittest.TestCase): + + VNFD_0 = TestSampleVnf.VNFD_0 + VNFD = TestSampleVnf.VNFD + + TRAFFIC_PROFILE = TestSampleVnf.TRAFFIC_PROFILE + + def setUp(self): + self.sample_vnf_tg = sample_vnf.SampleVNFTrafficGen( + 'tg1', self.VNFD_0) + + def test__check_status(self): + + with self.assertRaises(NotImplementedError): + self.sample_vnf_tg._check_status() + + def test_listen_traffic(self): + self.sample_vnf_tg.listen_traffic(mock.Mock()) + + def test_verify_traffic(self): + self.sample_vnf_tg.verify_traffic(mock.Mock()) + + def test_terminate(self): + self.sample_vnf_tg._traffic_process = mock.Mock() + self.sample_vnf_tg._tg_process = mock.Mock() + + self.sample_vnf_tg.terminate() + + @mock.patch.object(time, 'sleep') + def test__wait_for_process(self, *args): + with mock.patch.object(self.sample_vnf_tg, '_check_status', + return_value=0) as mock_status, \ + mock.patch.object(self.sample_vnf_tg, '_tg_process') as mock_proc: + mock_proc.is_alive.return_value = True + mock_proc.exitcode = 234 + self.assertEqual(self.sample_vnf_tg._wait_for_process(), 234) + mock_proc.is_alive.assert_called_once() + mock_status.assert_called_once() + + def test__wait_for_process_not_alive(self): + with mock.patch.object(self.sample_vnf_tg, '_tg_process') as mock_proc: + mock_proc.is_alive.return_value = False + self.assertRaises(RuntimeError, self.sample_vnf_tg._wait_for_process) + mock_proc.is_alive.assert_called_once() + + @mock.patch.object(time, 'sleep') + def test__wait_for_process_delayed(self, *args): + with mock.patch.object(self.sample_vnf_tg, '_check_status', + side_effect=[1, 0]) as mock_status, \ + mock.patch.object(self.sample_vnf_tg, + '_tg_process') as mock_proc: + mock_proc.is_alive.return_value = True + mock_proc.exitcode = 234 + self.assertEqual(self.sample_vnf_tg._wait_for_process(), 234) + mock_proc.is_alive.assert_has_calls([mock.call(), mock.call()]) + mock_status.assert_has_calls([mock.call(), mock.call()]) + + def test_scale(self): + self.assertRaises(y_exceptions.FunctionNotImplemented, + self.sample_vnf_tg.scale) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_imsbench_sipp.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_imsbench_sipp.py new file mode 100644 index 000000000..698b1b03f --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_imsbench_sipp.py @@ -0,0 +1,481 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +import unittest +from collections import deque + +from yardstick.network_services.vnf_generic.vnf import tg_imsbench_sipp +from yardstick import ssh + + +class TestSippVnf(unittest.TestCase): + + VNFD = { + "short-name": "SippVnf", + "vdu": [ + { + "id": "sippvnf-baremetal", + "routing_table": "", + "external-interface": [ + { + "virtual-interface": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "vnfd-connection-point-ref": "xe0", + "name": "xe0" + }, + { + "virtual-interface": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe1", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "vnfd-connection-point-ref": "xe1", + "name": "xe1" + } + ], + "name": "sippvnf-baremetal", + "description": "Sipp" + } + ], + "description": "ImsbenchSipp", + "mgmt-interface": { + "vdu-id": "sipp-baremetal", + "password": "r00t", + "user": "root", + "ip": "10.80.3.11" + }, + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "id": "SippVnf", + "name": "SippVnf" + } + + SCENARIO_CFG = { + "task_id": "ba636744-898e-4783-a4aa-0a79c60953cc", + "tc": "tc_vims_baremetal_sipp", + "runner": { + "interval": 1, + "output_config": { + "DEFAULT": { + "debug": "False", + "dispatcher": [ + "influxdb" + ] + }, + "nsb": { + "debug": "False", + "trex_client_lib": "/opt/nsb_bin/trex_client/stl", + "bin_path": "/opt/nsb_bin", + "trex_path": "/opt/nsb_bin/trex/scripts", + "dispatcher": "influxdb" + }, + "dispatcher_influxdb": { + "username": "root", + "target": "http://10.80.3.11:8086", + "db_name": "yardstick", + "timeout": "5", + "debug": "False", + "password": "root", + "dispatcher": "influxdb" + }, + "dispatcher_http": { + "debug": "False", + "dispatcher": "influxdb", + "timeout": "5", + "target": "http://127.0.0.1:8000/results" + }, + "dispatcher_file": { + "debug": "False", + "backup_count": "0", + "max_bytes": "0", + "dispatcher": "influxdb", + "file_path": "/tmp/yardstick.out" + } + }, + "runner_id": 18148, + "duration": 60, + "type": "Vims" + }, + "nodes": { + "vnf__0": "pcscf.yardstick-ba636744", + "vnf__1": "hss.yardstick-ba636744", + "tg__0": "sipp.yardstick-ba636744" + }, + "topology": "vims-topology.yaml", + "type": "NSPerf", + "traffic_profile": "../../traffic_profiles/ipv4_throughput.yaml", + "task_path": "samples/vnf_samples/nsut/vims", + "options": { + "init_reg_max": 5000, + "end_user": 10000, + "reg_cps": 20, + "rereg_cps": 20, + "rereg_step": 10, + "wait_time": 5, + "start_user": 1, + "msgc_cps": 10, + "dereg_step": 10, + "call_cps": 10, + "reg_step": 10, + "init_reg_cps": 50, + "dereg_cps": 20, + "msgc_step": 5, + "call_step": 5, + "hold_time": 15, + "port": 5060, + "run_mode": "nortp" + } + } + CONTEXT_CFG = { + "nodes": { + "tg__0": { + "ip": "10.80.3.11", + "interfaces": { + "xe0": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "xe1": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe1", + "local_mac": "90:e2:ba:7c:30:e8" + } + }, + "user": "root", + "password": "r00t", + "VNF model": "../../vnf_descriptors/tg_sipp_vnfd.yaml", + "name": "sipp.yardstick-a75a3aff", + "vnfd-id-ref": "tg__0", + "member-vnf-index": "1", + "role": "TrafficGen", + "ctx_type": "Node" + }, + "vnf__0": { + "ip": "10.80.3.7", + "interfaces": { + "xe0": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "tg__0": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe1", + "local_mac": "90:e2:ba:7c:30:e8" + } + }, + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + } + }, + "user": "root", + "password": "r00t", + "VNF model": "../../vnf_descriptors/vims_pcscf_vnfd.yaml", + "name": "pcscf.yardstick-a75a3aff", + "vnfd-id-ref": "vnf__0", + "member-vnf-index": "2", + "role": "VirtualNetworkFunction", + "ctx_type": "Node" + }, + "vnf__1": { + "ip": "10.80.3.7", + "interfaces": { + "xe0": { + "vld_id": "ims_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "tg__0": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe1", + "local_mac": "90:e2:ba:7c:30:e8" + } + }, + "node_name": "vnf__1", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:e8" + } + }, + "user": "root", + "password": "r00t", + "VNF model": "../../vnf_descriptors/vims_hss_vnfd.yaml", + "name": "hss.yardstick-a75a3aff", + "vnfd-id-ref": "vnf__1", + "member-vnf-index": "3", + "role": "VirtualNetworkFunction", + "ctx_type": "Node" + } + }, + "networks": {} + } + + FILE = "timestamp:1000 reg:100 reg_saps:0" + + QUEUE = {'reg_saps': 0.0, 'timestamp': 1000.0, 'reg': 100.0} + + TRAFFIC_PROFILE = { + "schema": "nsb:traffic_profile:0.1", + "name": "sip", + "description": "Traffic profile to run sip", + "traffic_profile": { + "traffic_type": "SipProfile", + "frame_rate": 100, # pps + "enable_latency": False + }, + } + + def setUp(self): + self._mock_ssh = mock.patch.object(ssh, 'SSH') + self.mock_ssh = self._mock_ssh.start() + + self.addCleanup(self._stop_mocks) + self.sipp_vnf = tg_imsbench_sipp.SippVnf('tg__0', self.VNFD) + + def _stop_mocks(self): + self._mock_ssh.stop() + + def test___init__(self): + self.assertIsInstance(self.sipp_vnf.resource_helper, + tg_imsbench_sipp.SippResourceHelper) + + def test_wait_for_instantiate(self): + self.assertIsNone(self.sipp_vnf.wait_for_instantiate()) + + @mock.patch('six.moves.builtins.open', new_callable=mock.mock_open, read_data=FILE) + def test_handle_result_files(self, mock_file): + result_deque = deque([self.QUEUE]) + file = "/tmp/test.txt" + test = self.sipp_vnf.handle_result_files(file) + self.assertEqual(result_deque, test) + mock_file.assert_called_with(file, 'r') + + @mock.patch.object(ssh.SSH, 'get') + def test_get_result_files(self, mock_get): + self.sipp_vnf.get_result_files() + mock_get.assert_called() + + def test_collect_kpi(self): + self.sipp_vnf.queue = deque([self.QUEUE]) + self.assertEqual(self.QUEUE, self.sipp_vnf.collect_kpi()) + + def test_collect_kpi_empty(self): + self.sipp_vnf.queue = deque([]) + self.assertEqual({}, self.sipp_vnf.collect_kpi()) + + @mock.patch('six.moves.builtins.open', new_callable=mock.mock_open, read_data=FILE) + def test_count_line_num(self, mock_file): + file = "/tmp/test.txt" + mock_file.return_value.__iter__.return_value = self.FILE.splitlines() + self.assertEqual(1, self.sipp_vnf.count_line_num(file)) + mock_file.assert_called_with(file, 'r') + + @mock.patch('six.moves.builtins.open', new_callable=mock.mock_open, read_data='') + def test_count_line_num_file_empty(self, mock_file): + file = "/tmp/test.txt" + self.assertEqual(0, self.sipp_vnf.count_line_num(file)) + mock_file.assert_called_with(file, 'r') + + @mock.patch('six.moves.builtins.open', new_callable=mock.mock_open, read_data=FILE) + def test_count_line_num_file_error(self, mock_file): + file = "/tmp/test.txt" + mock_file.side_effect = IOError() + self.assertEqual(0, self.sipp_vnf.count_line_num(file)) + + def test_is_ended_false(self): + self.sipp_vnf.count_line_num = mock.Mock(return_value=1) + not_end = self.sipp_vnf.is_ended() + self.assertFalse(not_end) + + def test_is_ended_true(self): + self.sipp_vnf.count_line_num = mock.Mock(return_value=0) + end = self.sipp_vnf.is_ended() + self.assertTrue(end) + + def test_terminate(self): + self.sipp_vnf.ssh_helper = mock.MagicMock() + self.sipp_vnf.resource_helper.ssh_helper = mock.MagicMock() + self.assertIsNone(self.sipp_vnf.terminate()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_ixload.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_ixload.py new file mode 100644 index 000000000..dd1c277c3 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_ixload.py @@ -0,0 +1,287 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import subprocess + +import mock +import six + +from yardstick import ssh +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.common import utils +from yardstick.network_services.vnf_generic.vnf import tg_ixload +from yardstick.network_services.traffic_profile import base as tp_base +from yardstick.tests.unit import base as ut_base + + +NAME = "tg__1" + + +class TestIxLoadTrafficGen(ut_base.BaseUnitTestCase): + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{'short-name': 'VpeVnf', + 'vdu': + [{'routing_table': + [{'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0'}, + {'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1'}], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': + [{'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'id': 'vpevnf-baremetal', + 'external-interface': + [{'virtual-interface': + {'dst_mac': '00:00:00:00:00:04', + 'vld_id': 'uplink_0', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02'}, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:03', + 'vld_id': 'downlink_0', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01'}, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'}]}], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': + {'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1'}, + 'benchmark': + {'kpi': ['packets_in', 'packets_fwd', 'packets_dropped']}, + 'connection-point': [{'type': 'VPORT', 'name': 'xe0'}, + {'type': 'VPORT', 'name': 'xe1'}], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh'}]}} + + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64}} + + def setUp(self): + self._mock_call = mock.patch.object(subprocess, 'call') + self.mock_call = self._mock_call.start() + self._mock_open = mock.patch.object(tg_ixload, 'open') + self.mock_open = self._mock_open.start() + self._mock_ssh = mock.patch.object(ssh, 'SSH') + self.mock_ssh = self._mock_ssh.start() + ssh_obj_mock = mock.Mock(autospec=ssh.SSH) + ssh_obj_mock.execute = mock.Mock(return_value=(0, '', '')) + ssh_obj_mock.run = mock.Mock(return_value=(0, '', '')) + self.mock_ssh.from_node.return_value = ssh_obj_mock + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_call.stop() + self._mock_open.stop() + self._mock_ssh.stop() + + def test___init__(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + ixload_traffic_gen = tg_ixload.IxLoadTrafficGen(NAME, vnfd) + self.assertIsNone(ixload_traffic_gen.resource_helper.data) + + def test_update_gateways(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + ixload_traffic_gen = tg_ixload.IxLoadTrafficGen(NAME, vnfd) + links = {'uplink_0': {'ip': {}}, + 'downlink_1': {'ip': {}}} + + ixload_traffic_gen.update_gateways(links) + + self.assertEqual("152.16.100.20", links["uplink_0"]["ip"]["gateway"]) + self.assertEqual("0.0.0.0", links["downlink_1"]["ip"]["gateway"]) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', + return_value='mock_node') + def test_collect_kpi(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + ixload_traffic_gen = tg_ixload.IxLoadTrafficGen(NAME, vnfd) + ixload_traffic_gen.scenario_helper.scenario_cfg = { + 'nodes': {ixload_traffic_gen.name: "mock"} + } + ixload_traffic_gen.data = {} + result = ixload_traffic_gen.collect_kpi() + + expected = { + 'physical_node': 'mock_node', + 'collect_stats': {}} + self.assertEqual(expected, result) + + def test_listen_traffic(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + ixload_traffic_gen = tg_ixload.IxLoadTrafficGen(NAME, vnfd) + self.assertIsNone(ixload_traffic_gen.listen_traffic({})) + + @mock.patch.object(utils, 'find_relative_file') + @mock.patch.object(utils, 'makedirs') + @mock.patch.object(ctx_base.Context, 'get_context_from_server') + @mock.patch.object(tg_ixload, 'shutil') + def test_instantiate(self, mock_shutil, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + ixload_traffic_gen = tg_ixload.IxLoadTrafficGen(NAME, vnfd) + scenario_cfg = {'tc': "nsb_test_case", + 'ixia_profile': "ixload.cfg", + 'task_path': "/path/to/task"} + ixload_traffic_gen.RESULTS_MOUNT = "/tmp/result" + mock_shutil.copy = mock.Mock() + scenario_cfg.update( + {'options': + {'packetsize': 64, 'traffic_type': 4, + 'rfc2544': {'allowed_drop_rate': '0.8 - 1'}, + 'vnf__1': {'rules': 'acl_1rule.yaml', + 'vnf_config': {'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': + '1C/1T', + 'worker_threads': 1}} + } + } + ) + scenario_cfg.update({'nodes': {ixload_traffic_gen.name: "mock"}}) + with mock.patch.object(six.moves.builtins, 'open', + create=True) as mock_open: + mock_open.return_value = mock.MagicMock() + ixload_traffic_gen.instantiate(scenario_cfg, {}) + + @mock.patch.object(tg_ixload, 'open') + @mock.patch.object(tg_ixload, 'min') + @mock.patch.object(tg_ixload, 'max') + @mock.patch.object(tg_ixload, 'len') + @mock.patch.object(tg_ixload, 'shutil') + def test_run_traffic(self, *args): + mock_traffic_profile = mock.Mock(autospec=tp_base.TrafficProfile) + mock_traffic_profile.get_traffic_definition.return_value = '64' + mock_traffic_profile.get_links_param.return_value = { + 'uplink_0': {'ip': {}}} + mock_traffic_profile.params = self.TRAFFIC_PROFILE + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vnfd['mgmt-interface'].update({'tg-config': {}}) + vnfd['mgmt-interface']['tg-config'].update({'ixchassis': '1.1.1.1'}) + vnfd['mgmt-interface']['tg-config'].update({'py_bin_path': '/root'}) + sut = tg_ixload.IxLoadTrafficGen(NAME, vnfd) + sut.connection = mock.Mock() + sut._traffic_runner = mock.Mock(return_value=0) + result = sut.run_traffic(mock_traffic_profile) + self.assertIsNone(result) + + @mock.patch.object(tg_ixload, 'open') + @mock.patch.object(tg_ixload, 'min') + @mock.patch.object(tg_ixload, 'max') + @mock.patch.object(tg_ixload, 'len') + @mock.patch.object(tg_ixload, 'shutil') + def test_run_traffic_csv(self, *args): + mock_traffic_profile = mock.Mock(autospec=tp_base.TrafficProfile) + mock_traffic_profile.get_traffic_definition.return_value = '64' + mock_traffic_profile.get_links_param.return_value = { + 'uplink_0': {'ip': {}}} + mock_traffic_profile.params = self.TRAFFIC_PROFILE + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vnfd['mgmt-interface'].update({'tg-config': {}}) + vnfd['mgmt-interface']['tg-config'].update({'ixchassis': '1.1.1.1'}) + vnfd['mgmt-interface']['tg-config'].update({'py_bin_path': '/root'}) + sut = tg_ixload.IxLoadTrafficGen(NAME, vnfd) + sut.connection = mock.Mock() + sut._traffic_runner = mock.Mock(return_value=0) + subprocess.call(['touch', '/tmp/1.csv']) + sut.rel_bin_path = mock.Mock(return_value='/tmp/*.csv') + result = sut.run_traffic(mock_traffic_profile) + self.assertIsNone(result) + + def test_terminate(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + ixload_traffic_gen = tg_ixload.IxLoadTrafficGen(NAME, vnfd) + self.assertIsNone(ixload_traffic_gen.terminate()) + + def test_parse_csv_read(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + kpi_data = { + 'HTTP Total Throughput (Kbps)': 1, + 'HTTP Simulated Users': 2, + 'HTTP Concurrent Connections': '3', + 'HTTP Connection Rate': 4.3, + 'HTTP Transaction Rate': True, + } + http_reader = [kpi_data] + ixload_traffic_gen = tg_ixload.IxLoadTrafficGen(NAME, vnfd) + result = ixload_traffic_gen.resource_helper.result + ixload_traffic_gen.resource_helper.parse_csv_read(http_reader) + for k_left, k_right in tg_ixload.IxLoadResourceHelper.KPI_LIST.items(): + self.assertEqual(result[k_left][-1], int(kpi_data[k_right])) + + def test_parse_csv_read_value_error(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + http_reader = [{ + 'HTTP Total Throughput (Kbps)': 1, + 'HTTP Simulated Users': 2, + 'HTTP Concurrent Connections': "not a number", + 'HTTP Connection Rate': 4, + 'HTTP Transaction Rate': 5, + }] + ixload_traffic_gen = tg_ixload.IxLoadTrafficGen(NAME, vnfd) + init_value = ixload_traffic_gen.resource_helper.result + ixload_traffic_gen.resource_helper.parse_csv_read(http_reader) + self.assertDictEqual(ixload_traffic_gen.resource_helper.result, + init_value) + + def test_parse_csv_read_error(self,): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + http_reader = [{ + 'HTTP Total Throughput (Kbps)': 1, + 'HTTP Simulated Users': 2, + 'HTTP Concurrent Connections': 3, + 'HTTP Transaction Rate': 5, + }] + ixload_traffic_gen = tg_ixload.IxLoadTrafficGen(NAME, vnfd) + with self.assertRaises(KeyError): + ixload_traffic_gen.resource_helper.parse_csv_read(http_reader) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_landslide.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_landslide.py new file mode 100644 index 000000000..2d8c01bec --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_landslide.py @@ -0,0 +1,1951 @@ +# Copyright (c) 2018-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import mock +import requests +import time +import unittest + +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.common import exceptions +from yardstick.common import utils as common_utils +from yardstick.common import yaml_loader +from yardstick.network_services import utils as net_serv_utils +from yardstick.network_services.traffic_profile import landslide_profile +from yardstick.network_services.vnf_generic.vnf import sample_vnf +from yardstick.network_services.vnf_generic.vnf import tg_landslide +from yardstick.network_services.vnf_generic.vnf import base as vnf_base + +NAME = "tg__0" + +EXAMPLE_URL = 'http://example.com/' +TCL_SUCCESS_RESPONSE = 'ls_ok' + +TEST_SERVERS = [ + {'ip': '192.168.122.101', + 'phySubnets': [ + {'mask': '/24', + 'base': '10.42.32.100', + 'numIps': 20, + 'name': 'eth1'} + ], + 'role': 'SGW_Node', + 'name': 'TestServer_1'}, + {'ip': '192.168.122.102', + 'phySubnets': [ + {'mask': '/24', + 'base': '10.42.32.1', + 'numIps': 100, + 'name': 'eth1' + }, + {'mask': '/24', + 'base': '10.42.33.1', + 'numIps': 100, + 'name': 'eth2'} + ], + 'preResolvedArpAddress': [ + {'NumNodes': 1, + 'StartingAddress': '10.42.33.5'} + ], + 'role': 'SGW_Nodal', + 'name': 'TestServer_2', + 'thread_model': 'Fireball' + } +] + +TS1_SUTS = [ + {'name': 'SGW - C TestNode', + 'role': 'SgwControlAddr', + 'managementIp': '12.0.1.1', + 'ip': '10.42.32.100', + 'phy': 'eth5', + 'nextHop': '10.42.32.5' + }, + {'name': 'SGW - U TestNode', + 'role': 'SgwUserAddr', + 'managementIp': '12.0.1.2', + 'ip': '10.42.32.101', + 'phy': 'eth5', + 'nextHop': '10.42.32.5' + } +] + +TS2_SUTS = [ + {'name': 'eNodeB TestNode', + 'role': 'EnbUserAddr', + 'managementIp': '12.0.2.1', + 'ip': '10.42.32.2', + 'phy': 'eth5', + 'nextHop': '10.42.32.5' + }, + {'name': 'MME TestNode', + 'role': 'MmeControlAddr', + 'managementIp': '12.0.3.1', + 'ip': '10.42.32.1', + 'phy': 'eth5', + 'nextHop': '10.42.32.5' + }, + {'name': 'NetHost TestNode', + 'role': 'NetworkHostAddrLocal', + 'managementIp': '12.0.4.1', + 'ip': '10.42.33.1', + 'phy': 'eth5', + 'nextHop': '10.42.32.5' + }, + {'name': 'PGW TestNode', + 'role': 'PgwV4Sut', + 'managementIp': '12.0.5.1', + 'ip': '10.42.32.105', + 'phy': 'eth5', + 'nextHop': '10.42.32.5' + }, + {'name': 'SGW - C SUT', + 'role': 'SgwSut', + 'managementIp': '12.0.6.1', + 'ip': '10.42.32.100' + }, + {'name': 'SGW - U SUT', + 'role': 'SgwUserSut', + 'managementIp': '12.0.6.2', + 'ip': '10.42.32.101'} +] + +VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [{ + 'short-name': 'landslide', + 'vdu': [{ + 'description': 'AB client interface details', + 'name': 'abclient-baremetal', + 'id': 'abclient-baremetal', + 'external-interface': []}], + 'description': 'Spirent Landslide traffic generator', + 'config': [{'test_server': TEST_SERVERS[0], 'suts': TS1_SUTS}, + {'test_server': TEST_SERVERS[1], 'suts': TS2_SUTS}], + 'mgmt-interface': { + 'vdu-id': 'landslide-tas', + 'user': 'user', + 'password': 'user', + 'super-user': 'super-user', + 'super-user-password': 'super-user-password', + 'cfguser_password': 'cfguser_password', + 'license': 48, + 'proto': 'http', + 'ip': '1.1.1.1'}, + 'benchmark': { + 'kpi': [ + 'tx_throughput_mbps', + 'rx_throughput_mbps', + 'in_packets', + 'out_packets', + 'activation_rate_sessps', + 'deactivation_rate_sessps']}, + 'id': 'LandslideTrafficGen', + 'name': 'LandslideTrafficGen'}]}} + +TAS_INFO = VNFD['vnfd:vnfd-catalog']['vnfd'][0]['mgmt-interface'] + +DMF_CFG = { + "dmf": { + "library": "test", + "name": "Basic UDP" + }, + "clientPort": { + "clientPort": 2002, + "isClientPortRange": "false" + }, + "dataProtocol": "udp", + "serverPort": 2003 +} + +RESERVATIONS = [ + {'tsName': TEST_SERVERS[0]['name'], + 'phySubnets': TEST_SERVERS[0]['phySubnets'], + 'tsId': TEST_SERVERS[0]['name'], + 'tsIndex': 0}, + {'tsName': TEST_SERVERS[1]['name'], + 'phySubnets': TEST_SERVERS[1]['phySubnets'], + 'tsId': TEST_SERVERS[1]['name'], + 'tsIndex': 1}] + +SESSION_PROFILE = { + 'keywords': '', + 'duration': 60, + 'iterations': 1, + 'description': 'UE default bearer creation test case', + 'name': 'default_bearer_capacity', + 'reportOptions': {'format': 'CSV'}, + 'reservePorts': 'false', + 'tsGroups': [ + { + 'testCases': [{ + 'type': 'SGW_Node', + 'name': '', + 'linked': "false", + 'AssociatedPhys': '', + 'parameters': { + 'SgiPtpTunnelEn': 'false', + 'Gtp2Imsi': '505024101215074', + 'Sessions': '100000', + 'S5Protocol': 'GTPv2', + 'TrafficMtu': '1500', + 'Gtp2Version': '13.6.0', + 'BearerV4AddrPool': '1.0.0.1', + 'Gtp2Imei': '50502410121507', + 'PgwNodeEn': 'true', + 'DedicatedsPerDefaultBearer': '0', + 'DefaultBearers': '1', + 'SgwUserAddr': { + 'numLinksOrNodes': 1, + 'phy': 'eth1', + 'forcedEthInterface': '', + 'ip': 'SGW_USER_IP', + 'class': 'TestNode', + 'ethStatsEnabled': "false", + 'mtu': 1500 + }, + 'SgwControlAddr': { + 'numLinksOrNodes': 1, + 'phy': 'eth1', + 'forcedEthInterface': '', + 'ip': 'SGW_CONTROL_IP', + 'class': 'TestNode', + 'ethStatsEnabled': "false", + 'mtu': 1500, + 'nextHop': 'SGW_CONTROL_NEXT_HOP' + }, + 'BearerAddrPool': '2001::1', + 'TestType': 'SGW-NODE' + } + }], + 'tsId': TEST_SERVERS[0]['name']}, + { + 'testCases': [{ + 'type': 'SGW_Nodal', + 'name': '', + 'parameters': { + 'DataTraffic': 'Continuous', + 'TrafficStartType': 'When All Sessions Established', + 'NetworkHost': 'Local', + 'Gtp2Imsi': '505024101215074', + 'Dmf': { + 'mainflows': [ + { + 'name': 'Basic UDP', + 'library': 'test' + } + ], + 'class': 'Dmf', + 'instanceGroups': [ + { + 'startPaused': "false", + 'rate': 0, + 'mainflowIdx': 0, + 'mixType': '' + } + ] + }, + 'S5Protocol': 'GTPv2', + 'DataUserCfgFileEn': 'false', + 'PgwUserSutEn': 'false', + 'MmeControlAddr': { + 'numLinksOrNodes': 1, + 'phy': 'eth1', + 'forcedEthInterface': '', + 'ip': 'MME_CONTROL_IP', + 'class': 'TestNode', + 'ethStatsEnabled': "false", + 'mtu': 1500 + }, + 'SgwUserSut': { + 'class': 'Sut', + 'name': 'SGW_USER_NAME' + }, + 'TestActivity': 'Capacity Test', + 'NetworkHostAddrLocal': { + 'numLinksOrNodes': 1, + 'phy': 'eth2', + 'forcedEthInterface': '', + 'ip': 'NET_HOST_IP', + 'class': 'TestNode', + 'ethStatsEnabled': "false", + 'mtu': 1500 + }, + 'DedicatedsPerDefaultBearer': '0', + 'DisconnectRate': '1000.0', + 'Sessions': '100000', + 'SgwSut': { + 'class': 'Sut', + 'name': 'SGW_CONTROL_NAME' + }, + 'TrafficMtu': '1500', + 'Gtp2Version': '13.6.0', + 'Gtp2Imei': '50502410121507', + 'PgwNodeEn': 'false', + 'StartRate': '1000.0', + 'PgwV4Sut': { + 'class': 'Sut', + 'name': 'PGW_SUT_NAME' + }, + 'DefaultBearers': '1', + 'EnbUserAddr': { + 'numLinksOrNodes': 1, + 'phy': 'eth1', + 'forcedEthInterface': '', + 'ip': 'ENB_USER_IP', + 'class': 'TestNode', + 'ethStatsEnabled': "false", + 'mtu': 1500 + }, + 'TestType': 'SGW-NODAL' + } + }], + 'tsId': TEST_SERVERS[1]['name'] + } + ] +} + + +class TestLandslideTrafficGen(unittest.TestCase): + SCENARIO_CFG = { + 'session_profile': '/traffic_profiles/landslide/' + 'landslide_session_default_bearer.yaml', + 'task_path': '', + 'runner': { + 'type': 'Iteration', + 'iterations': 1 + }, + 'nodes': { + 'tg__0': 'tg__0.traffic_gen', + 'vnf__0': 'vnf__0.vnf_epc' + }, + 'topology': 'landslide_tg_topology.yaml', + 'type': 'NSPerf', + 'traffic_profile': '../../traffic_profiles/landslide/' + 'landslide_dmf_udp.yaml', + 'options': { + 'traffic_duration': 71, + 'test_cases': [ + { + 'BearerAddrPool': '2002::2', + 'type': 'SGW_Node', + 'BearerV4AddrPool': '2.0.0.2', + 'Sessions': '90000' + }, + { + 'StartRate': '900.0', + 'type': 'SGW_Nodal', + 'DisconnectRate': '900.0', + 'Sessions': '90000' + } + ], + 'dmf': + { + 'transactionRate': 1000, + 'packetSize': 512 + } + } + } + + CONTEXT_CFG = { + 'contexts': [ + { + 'type': 'Node', + 'name': 'traffic_gen', + 'file': '/etc/yardstick/nodes/pod_landslide.yaml' + }, + { + 'type': 'Node', + 'name': 'vnf_epc', + 'file': '/etc/yardstick/nodes/pod_vepc_sut.yaml' + } + ] + } + + TRAFFIC_PROFILE = { + "schema": "nsb:traffic_profile:0.1", + "name": "LandslideProfile", + "description": "Spirent Landslide traffic profile", + "traffic_profile": { + "traffic_type": "LandslideProfile" + }, + "dmf_config": { + "dmf": { + "library": "test", + "name": "Basic UDP" + }, + "description": "Basic data flow using UDP/IP", + "keywords": "UDP", + "dataProtocol": "udp" + } + } + + SUCCESS_CREATED_CODE = 201 + SUCCESS_OK_CODE = 200 + SUCCESS_RECORD_ID = 5 + TEST_USER_ID = 11 + + def setUp(self): + self.mock_lsapi = mock.patch.object(tg_landslide, 'LsApi') + self.mock_lsapi.start() + + self.mock_ssh_helper = mock.patch.object(sample_vnf, 'VnfSshHelper') + self.mock_ssh_helper.start() + self.vnfd = VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.ls_tg = tg_landslide.LandslideTrafficGen( + NAME, self.vnfd) + self.session_profile = copy.deepcopy(SESSION_PROFILE) + self.ls_tg.session_profile = self.session_profile + + self.addCleanup(self._cleanup) + + def _cleanup(self): + self.mock_lsapi.stop() + self.mock_ssh_helper.stop() + + @mock.patch.object(net_serv_utils, 'get_nsb_option') + def test___init__(self, mock_get_nsb_option, *args): + _path_to_nsb = 'path/to/nsb' + mock_get_nsb_option.return_value = _path_to_nsb + ls_tg = tg_landslide.LandslideTrafficGen(NAME, self.vnfd) + self.assertIsInstance(ls_tg.resource_helper, + tg_landslide.LandslideResourceHelper) + mock_get_nsb_option.assert_called_once_with('bin_path') + self.assertEqual(_path_to_nsb, ls_tg.bin_path) + self.assertEqual(NAME, ls_tg.name) + self.assertTrue(ls_tg.runs_traffic) + self.assertFalse(ls_tg.traffic_finished) + self.assertIsNone(ls_tg.session_profile) + + def test_listen_traffic(self): + _traffic_profile = {} + self.assertIsNone(self.ls_tg.listen_traffic(_traffic_profile)) + + def test_terminate(self, *args): + self.ls_tg.resource_helper._tcl = mock.Mock() + self.assertIsNone(self.ls_tg.terminate()) + self.ls_tg.resource_helper._tcl.disconnect.assert_called_once() + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate(self, *args): + self.ls_tg._tg_process = mock.Mock() + self.ls_tg._tg_process.start = mock.Mock() + self.ls_tg.resource_helper.connect = mock.Mock() + self.ls_tg.resource_helper.create_test_servers = mock.Mock() + self.ls_tg.resource_helper.create_suts = mock.Mock() + self.ls_tg._load_session_profile = mock.Mock() + self.assertIsNone(self.ls_tg.instantiate(self.SCENARIO_CFG, + self.CONTEXT_CFG)) + self.ls_tg.resource_helper.connect.assert_called_once() + self.ls_tg.resource_helper.create_test_servers.assert_called_once() + _suts_blocks_num = len([item['suts'] for item in self.vnfd['config']]) + self.assertEqual(_suts_blocks_num, + self.ls_tg.resource_helper.create_suts.call_count) + self.ls_tg._load_session_profile.assert_called_once() + + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'get_running_tests') + def test_run_traffic(self, mock_get_tests, *args): + self.ls_tg.resource_helper._url = EXAMPLE_URL + self.ls_tg.scenario_helper.scenario_cfg = self.SCENARIO_CFG + mock_traffic_profile = mock.Mock( + spec=landslide_profile.LandslideProfile) + mock_traffic_profile.dmf_config = { + 'keywords': 'UDP', + 'dataProtocol': 'udp', + 'dmf': {'library': 'test', 'name': 'name'}} + mock_traffic_profile.params = self.TRAFFIC_PROFILE + self.ls_tg.resource_helper._user_id = self.TEST_USER_ID + mock_get_tests.return_value = [{'id': self.SUCCESS_RECORD_ID, + 'testStateOrStep': 'COMPLETE'}] + mock_post = mock.Mock() + mock_post.status_code = self.SUCCESS_CREATED_CODE + mock_post.json.return_value = {'id': self.SUCCESS_RECORD_ID} + mock_session = mock.Mock(spec=requests.Session) + mock_session.post.return_value = mock_post + self.ls_tg.resource_helper.session = mock_session + self.ls_tg.resource_helper._tcl = mock.Mock() + _tcl = self.ls_tg.resource_helper._tcl + self.assertIsNone(self.ls_tg.run_traffic(mock_traffic_profile)) + self.assertEqual(self.SUCCESS_RECORD_ID, + self.ls_tg.resource_helper.run_id) + mock_traffic_profile.update_dmf.assert_called_with( + self.ls_tg.scenario_helper.all_options) + _tcl.create_dmf.assert_called_with(mock_traffic_profile.dmf_config) + _tcl.create_test_session.assert_called_with(self.session_profile) + + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'check_running_test_state') + def test_collect_kpi(self, mock_check_running_test_state, *args): + self.ls_tg.resource_helper.run_id = self.SUCCESS_RECORD_ID + mock_check_running_test_state.return_value = 'COMPLETE' + self.assertEqual({'done': True}, self.ls_tg.collect_kpi()) + mock_check_running_test_state.assert_called_once() + + def test_wait_for_instantiate(self): + self.assertIsNone(self.ls_tg.wait_for_instantiate()) + self.ls_tg.wait_for_instantiate() + + def test__update_session_suts_no_tc_role(self, *args): + _suts = [{'role': 'epc_role'}] + _testcase = {'parameters': {'diff_epc_role': {'class': 'Sut'}}} + res = self.ls_tg._update_session_suts(_suts, _testcase) + self.assertEqual(_testcase, res) + + def test__update_session_suts(self, *args): + + def get_testnode_param(role, key, session_prof): + """ Get value by key from the deep nested dict to avoid calls like: + e.g. session_prof['tsGroups'][0]['testCases'][1]['parameters'][key] + """ + for group in session_prof['tsGroups']: + for tc in group['testCases']: + tc_params = tc['parameters'] + if tc_params.get(role): + return tc_params[role][key] + + def get_sut_param(role, key, suts): + """ Search list of dicts for one with specific role. + Return the value of related dict by key. Expect key presence. + """ + for sut in suts: + if sut.get('role') == role: + return sut[key] + + # TestNode to verify + testnode_role = 'SgwControlAddr' + # SUT to verify + sut_role = 'SgwUserSut' + + config_suts = [config['suts'] for config in self.vnfd['config']] + session_tcs = [_tc for _ts_group in self.ls_tg.session_profile['tsGroups'] + for _tc in _ts_group['testCases']] + for suts, tc in zip(config_suts, session_tcs): + self.assertEqual(tc, self.ls_tg._update_session_suts(suts, tc)) + + # Verify TestNode class objects keys were updated + for _key in {'ip', 'phy', 'nextHop'}: + self.assertEqual( + get_testnode_param(testnode_role, _key, self.ls_tg.session_profile), + get_sut_param(testnode_role, _key, TS1_SUTS)) + # Verify Sut class objects name was updated + self.assertEqual( + get_testnode_param(sut_role, 'name', self.ls_tg.session_profile), + get_sut_param(sut_role, 'name', TS2_SUTS)) + + def test__update_session_test_servers(self, *args): + for ts_index, ts in enumerate(TEST_SERVERS): + self.assertIsNone( + self.ls_tg._update_session_test_servers(ts, ts_index)) + # Verify preResolvedArpAddress key was added + self.assertTrue(any( + _item.get('preResolvedArpAddress') + for _item in self.ls_tg.session_profile['tsGroups'])) + # Verify reservations key was added to session profile + self.assertEqual(RESERVATIONS, + self.ls_tg.session_profile.get('reservations')) + self.assertEqual('true', + self.ls_tg.session_profile.get('reservePorts')) + + def test__update_session_tc_params_assoc_phys(self): + _tc_options = {'AssociatedPhys': 'eth1'} + _testcase = {} + _testcase_orig = copy.deepcopy(_testcase) + res = self.ls_tg._update_session_tc_params(_tc_options, _testcase) + self.assertNotEqual(_testcase_orig, res) + self.assertEqual(_tc_options, _testcase) + + def test__update_session_tc_params(self, *args): + + def get_session_tc_param_value(param, tc_type, session_prof): + """ Get param value from the deep nested dict to avoid calls like: + session_prof['tsGroups'][0]['testCases'][0]['parameters'][key] + """ + for test_group in session_prof['tsGroups']: + session_tc = test_group['testCases'][0] + if session_tc['type'] == tc_type: + return session_tc['parameters'].get(param) + + session_tcs = [_tc for _ts_group in self.ls_tg.session_profile['tsGroups'] + for _tc in _ts_group['testCases']] + scenario_tcs = [_tc for _tc in + self.SCENARIO_CFG['options']['test_cases']] + for tc_options, tc in zip(scenario_tcs, session_tcs): + self.assertEqual( + tc, + self.ls_tg._update_session_tc_params(tc_options, tc)) + + # Verify that each test case parameter was updated + # Params been compared are deeply nested. Using loops to ease access. + for _tc in self.SCENARIO_CFG['options']['test_cases']: + for _key, _val in _tc.items(): + if _key != 'type': + self.assertEqual( + _val, + get_session_tc_param_value(_key, _tc.get('type'), + self.ls_tg.session_profile)) + + def test__update_session_library_name(self, *args): + _session = copy.deepcopy(SESSION_PROFILE) + _session['tsGroups'].pop(0) + self.ls_tg.vnfd_helper = mock.MagicMock() + self.ls_tg.vnfd_helper.mgmt_interface.__getitem__.side_effect = { + 'user': TAS_INFO['user']} + self.ls_tg._update_session_library_name(_session) + _dmf = _session['tsGroups'][0]['testCases'][0]['parameters']['Dmf'] + # Expect DMF library name updated in Nodal test types + self.assertEqual(TAS_INFO['user'], _dmf['mainflows'][0]['library']) + + def test__update_session_library_name_wrong_tc_type(self, *args): + _session = copy.deepcopy(SESSION_PROFILE) + _session['tsGroups'].pop(1) + self.ls_tg.vnfd_helper = mock.MagicMock() + self.ls_tg.vnfd_helper.mgmt_interface.__getitem__.side_effect = { + 'user': TAS_INFO['user']} + # Expect DMF library name not updated in Node test types + self.assertNotIn('Dmf', + _session['tsGroups'][0]['testCases'][0]['parameters']) + self.ls_tg._update_session_library_name(_session) + + @mock.patch.object(common_utils, 'open_relative_file') + @mock.patch.object(yaml_loader, 'yaml_load') + @mock.patch.object(tg_landslide.LandslideTrafficGen, + '_update_session_test_servers') + @mock.patch.object(tg_landslide.LandslideTrafficGen, + '_update_session_suts') + @mock.patch.object(tg_landslide.LandslideTrafficGen, + '_update_session_tc_params') + def test__load_session_profile(self, mock_upd_ses_tc_params, + mock_upd_ses_suts, mock_upd_ses_ts, + mock_yaml_load, *args): + self.ls_tg.scenario_helper.scenario_cfg = \ + copy.deepcopy(self.SCENARIO_CFG) + mock_yaml_load.return_value = copy.deepcopy(SESSION_PROFILE) + self.assertIsNone(self.ls_tg._load_session_profile()) + self.assertIsNotNone(self.ls_tg.session_profile) + # Number of blocks in configuration files + # Number of test servers, suts and tc params blocks should be equal + _config_files_blocks_num = len([item['test_server'] + for item in self.vnfd['config']]) + self.assertEqual(_config_files_blocks_num, + mock_upd_ses_ts.call_count) + self.assertEqual(_config_files_blocks_num, + mock_upd_ses_suts.call_count) + self.assertEqual(_config_files_blocks_num, + mock_upd_ses_tc_params.call_count) + + @mock.patch.object(common_utils, 'open_relative_file') + @mock.patch.object(yaml_loader, 'yaml_load') + def test__load_session_profile_unequal_num_of_cfg_blocks( + self, mock_yaml_load, *args): + vnfd = copy.deepcopy(VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ls_traffic_gen = tg_landslide.LandslideTrafficGen(NAME, vnfd) + ls_traffic_gen.scenario_helper.scenario_cfg = self.SCENARIO_CFG + mock_yaml_load.return_value = copy.deepcopy(SESSION_PROFILE) + # Delete test_servers item from pod file to make it not valid + ls_traffic_gen.vnfd_helper['config'].pop() + with self.assertRaises(RuntimeError): + ls_traffic_gen._load_session_profile() + + @mock.patch.object(common_utils, 'open_relative_file') + @mock.patch.object(yaml_loader, 'yaml_load') + def test__load_session_profile_test_type_mismatch(self, mock_yaml_load, + *args): + vnfd = copy.deepcopy(VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + # Swap test servers data in pod file + vnfd['config'] = list(reversed(vnfd['config'])) + ls_tg = tg_landslide.LandslideTrafficGen(NAME, vnfd) + ls_tg.scenario_helper.scenario_cfg = self.SCENARIO_CFG + mock_yaml_load.return_value = SESSION_PROFILE + with self.assertRaises(RuntimeError): + ls_tg._load_session_profile() + + +class TestLandslideResourceHelper(unittest.TestCase): + + PROTO_PORT = 8080 + EXAMPLE_URL = ''.join([TAS_INFO['proto'], '://', TAS_INFO['ip'], ':', + str(PROTO_PORT), '/api/']) + SUCCESS_CREATED_CODE = 201 + SUCCESS_OK_CODE = 200 + INVALID_REST_CODE = '400' + NOT_MODIFIED_CODE = 500810 + ERROR_CODE = 500800 + SUCCESS_RECORD_ID = 11 + EXPIRE_DATE = '2020/01/01 12:00 FLE Standard Time' + TEST_USER = 'test' + TEST_TERMINATED = 1 + AUTH_DATA = {'user': TAS_INFO['user'], 'password': TAS_INFO['password']} + TEST_SESSION_NAME = 'default_bearer_capacity' + + USERS_DATA = { + "users": [{ + "url": ''.join([EXAMPLE_URL, 'users/', str(SUCCESS_RECORD_ID)]), + "id": SUCCESS_RECORD_ID, + "level": 1, + "username": TEST_USER + }] + } + + CREATE_USER_DATA = {'username': TAS_INFO['user'], + 'expiresOn': EXPIRE_DATE, + 'level': 1, + 'contactInformation': '', + 'fullName': 'Test User', + 'password': TAS_INFO['password'], + 'isActive': 'true'} + + SUTS_DATA = { + "suts": [ + { + "url": ''.join([EXAMPLE_URL, 'suts/', str(SUCCESS_RECORD_ID)]), + "id": SUCCESS_RECORD_ID, + "name": "10.41.32.1" + }]} + + TEST_SERVERS_DATA = { + "testServers": [ + { + "url": ''.join([EXAMPLE_URL, "testServers/1"]), + "id": 1, + "name": TEST_SERVERS[0]['name'], + "state": "READY", + "version": "16.4.0.10" + }, + { + "url": ''.join([EXAMPLE_URL, "testServers/2"]), + "id": 2, + "name": TEST_SERVERS[1]['name'], + "state": "READY", + "version": "16.4.0.10" + } + + ] + } + + RUN_ID = 3 + + RUNNING_TESTS_DATA = { + "runningTests": [{ + "url": ''.join([EXAMPLE_URL, "runningTests/{}".format(RUN_ID)]), + "measurementsUrl": ''.join( + [EXAMPLE_URL, + "runningTests/{}/measurements".format(RUN_ID)]), + "criteriaUrl": ''.join( + [EXAMPLE_URL, + "runningTests/{}/criteria".format(RUN_ID)]), + "noteToUser": "", + "id": RUN_ID, + "library": SUCCESS_RECORD_ID, + "name": "default_bearer_capacity", + "user": TEST_USER, + "criteriaStatus": "NA", + "testStateOrStep": "COMPLETE" + }]} + + TEST_RESULTS_DATA = { + "interval": 0, + "elapsedTime": 138, + "actualTime": 1521548057296, + "iteration": 1, + "tabs": { + "Test Summary": { + "Start Time": "Tue Mar 20 07:11:55 CDT 2018", + "Actual Dedicated Bearer Session Connects": "100", + "Actual Dedicated Bearer Session Disconnects": "100", + "Actual Disconnect Rate(Sessions / Second)(P - I)": "164.804", + "Average Session Disconnect Time(P - I)": "5.024 s", + "Total Data Sent + Received Packets / Sec(P - I)": "1,452.294" + }}} + + def setUp(self): + self.mock_lsapi = mock.patch.object(tg_landslide, 'LsApi') + self.mock_lsapi.start() + + mock_env_helper = mock.Mock() + self.res_helper = tg_landslide.LandslideResourceHelper(mock_env_helper) + self.res_helper._url = EXAMPLE_URL + + self.addCleanup(self._cleanup) + + def _cleanup(self): + self.mock_lsapi.stop() + self.res_helper._url = None + + def test___init__(self, *args): + self.assertIsInstance(self.res_helper, + tg_landslide.LandslideResourceHelper) + self.assertEqual({}, self.res_helper._result) + self.assertIsNone(self.res_helper.run_id) + + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'stop_running_tests') + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'get_running_tests') + def test_abort_running_tests_no_running_tests(self, mock_get_tests, + mock_stop_tests, *args): + tests_data = [{'id': self.SUCCESS_RECORD_ID, + 'testStateOrStep': 'COMPLETE'}] + mock_get_tests.return_value = tests_data + self.assertIsNone(self.res_helper.abort_running_tests()) + mock_stop_tests.assert_not_called() + + @mock.patch.object(time, 'sleep') + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'stop_running_tests') + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'get_running_tests') + def test_abort_running_tests(self, mock_get_tests, mock_stop_tests, *args): + test_states_seq = iter(['RUNNING', 'COMPLETE']) + + def configure_mock(*args): + return [{'id': self.SUCCESS_RECORD_ID, + 'testStateOrStep': next(test_states_seq)}] + + mock_get_tests.side_effect = configure_mock + self.assertIsNone(self.res_helper.abort_running_tests()) + mock_stop_tests.assert_called_once_with( + running_test_id=self.SUCCESS_RECORD_ID, + force=True) + self.assertEqual(2, mock_get_tests.call_count) + + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'stop_running_tests') + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'get_running_tests') + def test_abort_running_tests_error(self, mock_get_tests, mock_stop_tests, + *args): + tests_data = {'id': self.SUCCESS_RECORD_ID, + 'testStateOrStep': 'RUNNING'} + mock_get_tests.return_value = [tests_data] + with self.assertRaises(RuntimeError): + self.res_helper.abort_running_tests(timeout=1, delay=1) + mock_stop_tests.assert_called_with( + running_test_id=self.SUCCESS_RECORD_ID, + force=True) + + def test__build_url(self, *args): + resource = 'users' + action = {'action': 'userCreate'} + expected_url = ''.join([EXAMPLE_URL, 'users?action=userCreate']) + self.assertEqual(expected_url, + self.res_helper._build_url(resource, action)) + + def test__build_url_error(self, *args): + resource = '' + action = {'action': 'userCreate'} + + with self.assertRaises(ValueError): + self.res_helper._build_url(resource, action) + + def test_get_response_params(self, *args): + method = 'get' + resource = 'users' + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.USERS_DATA} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + resp = self.res_helper.get_response_params(method, resource) + self.assertTrue(resp) + + @mock.patch.object(tg_landslide.LandslideResourceHelper, '_get_users') + @mock.patch.object(time, 'time') + def test__create_user(self, mock_time, mock_get_users, *args): + mock_time.strftime.return_value = self.EXPIRE_DATE + post_resp_data = {'status_code': self.SUCCESS_CREATED_CODE, + 'json.return_value': {'id': self.SUCCESS_RECORD_ID}} + mock_session = mock.Mock(spec=requests.Session) + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + self.assertEqual(self.SUCCESS_RECORD_ID, + self.res_helper._create_user(self.AUTH_DATA)) + mock_get_users.assert_not_called() + + @mock.patch.object(tg_landslide.LandslideResourceHelper, '_modify_user') + @mock.patch.object(time, 'time') + def test__create_user_username_exists(self, mock_time, mock_modify_user, + *args): + mock_time.strftime.return_value = self.EXPIRE_DATE + mock_modify_user.return_value = {'id': self.SUCCESS_RECORD_ID, + 'result': 'No changes requested'} + post_resp_data = { + 'status_code': self.ERROR_CODE, + 'json.return_value': {'id': self.SUCCESS_OK_CODE, + 'apiCode': self.NOT_MODIFIED_CODE}} + mock_session = mock.Mock(spec=requests.Session) + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + res = self.res_helper._create_user(self.AUTH_DATA) + mock_modify_user.assert_called_once_with(TAS_INFO['user'], + {'isActive': 'true'}) + self.assertEqual(self.SUCCESS_RECORD_ID, res) + + @mock.patch.object(time, 'time') + def test__create_user_error(self, mock_time, *args): + mock_time.strftime.return_value = self.EXPIRE_DATE + mock_session = mock.Mock(spec=requests.Session) + post_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': {'apiCode': self.ERROR_CODE}} + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + with self.assertRaises(exceptions.RestApiError): + self.res_helper._create_user(self.AUTH_DATA) + + def test__modify_user(self, *args): + post_data = {'username': 'test_user'} + mock_session = mock.Mock(spec=requests.Session) + post_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': {'id': self.SUCCESS_RECORD_ID}} + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + res = self.res_helper._modify_user(username=self.TEST_USER, + fields=post_data) + self.assertEqual(self.SUCCESS_RECORD_ID, res['id']) + + def test__modify_user_rest_resp_fail(self, *args): + post_data = {'non-existing-key': ''} + mock_session = mock.Mock(spec=requests.Session) + mock_session.post.ok = False + self.res_helper.session = mock_session + self.assertRaises(exceptions.RestApiError, + self.res_helper._modify_user, + username=self.TEST_USER, fields=post_data) + mock_session.post.assert_called_once() + + def test__delete_user(self, *args): + mock_session = mock.Mock(spec=requests.Session) + self.res_helper.session = mock_session + self.assertIsNone(self.res_helper._delete_user( + username=self.TEST_USER)) + + def test__get_users(self, *args): + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.USERS_DATA} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + self.assertEqual(self.USERS_DATA['users'], + self.res_helper._get_users()) + + def test_exec_rest_request(self, *args): + resource = 'testServers' + action = {'action': 'modify'} + expected_url = ''.join([EXAMPLE_URL, 'testServers?action=modify']) + post_resp_data = {'status_code': self.SUCCESS_CREATED_CODE, + 'json.return_value': {'id': self.SUCCESS_RECORD_ID}} + mock_session = mock.Mock(spec=requests.Session) + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + self.res_helper.exec_rest_request('post', resource, action) + self.res_helper.session.post.assert_called_once_with(expected_url, + json={}) + + def test_exec_rest_request_unsupported_method_error(self, *args): + resource = 'testServers' + action = {'action': 'modify'} + with self.assertRaises(ValueError): + self.res_helper.exec_rest_request('patch', resource, action) + + def test_exec_rest_request_missed_action_arg(self, *args): + resource = 'testServers' + with self.assertRaises(ValueError): + self.res_helper.exec_rest_request('post', resource) + + def test_exec_rest_request_raise_exc(self): + resource = 'users' + action = {'action': 'modify'} + post_resp_data = {'status_code': self.ERROR_CODE, + 'json.return_value': { + 'status_code': self.ERROR_CODE}} + mock_session = mock.Mock(spec=requests.Session) + mock_session.post.return_value.configure_mock(**post_resp_data) + self.assertRaises(exceptions.RestApiError, + self.res_helper.exec_rest_request, + 'post', resource, action, raise_exc=True) + + @mock.patch.object(time, 'time') + def test_connect(self, mock_time, *args): + vnfd = VNFD['vnfd:vnfd-catalog']['vnfd'][0] + mock_time.strftime.return_value = self.EXPIRE_DATE + self.res_helper.vnfd_helper = vnfd + + self.res_helper._tcl = mock.Mock() + post_resp_data = {'status_code': self.SUCCESS_CREATED_CODE, + 'json.return_value': {'id': self.SUCCESS_RECORD_ID}} + mock_session = mock.Mock(spec=requests.Session, headers={}) + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + self.assertIsInstance(self.res_helper.connect(), requests.Session) + self.res_helper._tcl.connect.assert_called_once_with( + TAS_INFO['ip'], + TAS_INFO['user'], + TAS_INFO['password']) + + def test_disconnect(self, *args): + self.res_helper._tcl = mock.Mock() + self.assertIsNone(self.res_helper.disconnect()) + self.assertIsNone(self.res_helper.session) + self.res_helper._tcl.disconnect.assert_called_once() + + def test_terminate(self, *args): + self.assertIsNone(self.res_helper.terminate()) + self.assertEqual(self.TEST_TERMINATED, + self.res_helper._terminated.value) + + def test_create_dmf(self, *args): + self.res_helper._tcl = mock.Mock() + self.res_helper.vnfd_helper = mock.Mock(spec=vnf_base.VnfdHelper) + self.res_helper.vnfd_helper.mgmt_interface = {'user': TAS_INFO['user']} + self.assertIsNone(self.res_helper.create_dmf(DMF_CFG)) + self.res_helper._tcl.create_dmf.assert_called_once_with(DMF_CFG) + + def test_create_dmf_as_list(self, *args): + self.res_helper._tcl = mock.Mock() + self.res_helper.vnfd_helper = mock.Mock(spec=vnf_base.VnfdHelper) + self.res_helper.vnfd_helper.mgmt_interface = {'user': TAS_INFO['user']} + self.assertIsNone(self.res_helper.create_dmf([DMF_CFG])) + self.res_helper._tcl.create_dmf.assert_called_once_with(DMF_CFG) + + def test_delete_dmf(self, *args): + self.res_helper._tcl = mock.Mock() + self.assertIsNone(self.res_helper.delete_dmf(DMF_CFG)) + self.res_helper._tcl.delete_dmf.assert_called_once_with(DMF_CFG) + + def test_delete_dmf_as_list(self, *args): + self.res_helper._tcl = mock.Mock() + self.assertIsNone(self.res_helper.delete_dmf([DMF_CFG])) + self.res_helper._tcl.delete_dmf.assert_called_once_with(DMF_CFG) + + @mock.patch.object(tg_landslide.LandslideResourceHelper, 'configure_sut') + def test_create_suts(self, mock_configure_sut, *args): + mock_session = mock.Mock(spec=requests.Session) + post_resp_data = {'status_code': self.SUCCESS_CREATED_CODE} + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + self.assertIsNone(self.res_helper.create_suts(TS1_SUTS)) + mock_configure_sut.assert_not_called() + + @mock.patch.object(tg_landslide.LandslideResourceHelper, 'configure_sut') + def test_create_suts_sut_exists(self, mock_configure_sut, *args): + sut_name = 'test_sut' + suts = [ + {'name': sut_name, + 'role': 'SgwControlAddr', + 'managementIp': '12.0.1.1', + 'ip': '10.42.32.100' + } + ] + mock_session = mock.Mock(spec=requests.Session) + post_resp_data = {'status_code': self.NOT_MODIFIED_CODE} + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + self.assertIsNone(self.res_helper.create_suts(suts)) + mock_configure_sut.assert_called_once_with( + sut_name=sut_name, + json_data={k: v for k, v in suts[0].items() + if k not in {'phy', 'nextHop', 'role', 'name'}}) + + def test_get_suts(self, *args): + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.SUTS_DATA} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + self.assertIsInstance(self.res_helper.get_suts(), list) + + def test_get_suts_single_id(self, *args): + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.SUTS_DATA['suts'][0]} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + self.assertIsInstance(self.res_helper.get_suts(suts_id=2), dict) + + def test_configure_sut(self, *args): + post_data = {'managementIp': '2.2.2.2'} + mock_session = mock.Mock(spec=requests.Session) + post_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': {'id': self.SUCCESS_RECORD_ID}} + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + self.assertIsNone(self.res_helper.configure_sut('test_name', + post_data)) + mock_session.post.assert_called_once() + + def test_configure_sut_error(self, *args): + post_data = {'managementIp': '2.2.2.2'} + mock_session = mock.Mock(spec=requests.Session) + post_resp_data = {'status_code': self.NOT_MODIFIED_CODE} + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + with self.assertRaises(exceptions.RestApiError): + self.res_helper.configure_sut('test_name', post_data) + + def test_delete_suts(self, *args): + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.SUTS_DATA} + delete_resp_data = {'status_code': self.SUCCESS_OK_CODE} + mock_session.get.return_value.configure_mock(**get_resp_data) + mock_session.delete.return_value.configure_mock(**delete_resp_data) + self.res_helper.session = mock_session + self.assertIsNone(self.res_helper.delete_suts()) + mock_session.delete.assert_called_once() + + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'get_test_servers') + def test__check_test_servers_state(self, mock_get_test_servers, *args): + mock_get_test_servers.return_value = \ + self.TEST_SERVERS_DATA['testServers'] + self.res_helper._check_test_servers_state() + mock_get_test_servers.assert_called_once() + + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'get_test_servers') + def test__check_test_servers_state_server_not_ready( + self, mock_get_test_servers, *args): + test_servers_not_ready = [ + { + "url": ''.join([EXAMPLE_URL, "testServers/1"]), + "id": 1, + "name": "TestServer_1", + "state": "NOT_READY", + "version": "16.4.0.10" + } + ] + + mock_get_test_servers.return_value = test_servers_not_ready + with self.assertRaises(RuntimeError): + self.res_helper._check_test_servers_state(timeout=1, delay=0) + + @mock.patch.object(tg_landslide.LandslideResourceHelper, + '_check_test_servers_state') + def test_create_test_servers(self, mock_check_ts_state, *args): + test_servers_ids = [ + ts['id'] for ts in self.TEST_SERVERS_DATA['testServers']] + + self.res_helper.license_data['lic_id'] = TAS_INFO['license'] + self.res_helper._tcl.create_test_server = mock.Mock() + self.res_helper._tcl.create_test_server.side_effect = test_servers_ids + self.assertIsNone(self.res_helper.create_test_servers(TEST_SERVERS)) + mock_check_ts_state.assert_called_once_with(test_servers_ids) + + @mock.patch.object(tg_landslide.LandslideTclClient, + 'resolve_test_server_name') + @mock.patch.object(tg_landslide.LsTclHandler, 'execute') + def test_create_test_servers_error(self, mock_execute, + mock_resolve_ts_name, *args): + self.res_helper.license_data['lic_id'] = TAS_INFO['license'] + # Return message for case test server wasn't created + mock_execute.return_value = 'TS not found' + # Return message for case test server name wasn't resolved + mock_resolve_ts_name.return_value = 'TS not found' + with self.assertRaises(RuntimeError): + self.res_helper.create_test_servers(TEST_SERVERS) + + def test_get_test_servers(self, *args): + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.TEST_SERVERS_DATA} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + res = self.res_helper.get_test_servers() + self.assertEqual(self.TEST_SERVERS_DATA['testServers'], res) + + def test_get_test_servers_by_id(self, *args): + mock_session = mock.Mock(spec=requests.Session) + + _ts = self.TEST_SERVERS_DATA['testServers'][0] + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': _ts} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + res = self.res_helper.get_test_servers(test_server_ids=[_ts['id']]) + self.assertEqual([_ts], res) + + def test_configure_test_servers(self, *args): + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.TEST_SERVERS_DATA} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + res = self.res_helper.configure_test_servers( + action={'action': 'recycle'}) + self.assertEqual( + [x['id'] for x in self.TEST_SERVERS_DATA['testServers']], + res) + self.assertEqual(len(self.TEST_SERVERS_DATA['testServers']), + mock_session.post.call_count) + + def test_delete_test_servers(self, *args): + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.TEST_SERVERS_DATA} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + self.assertIsNone(self.res_helper.delete_test_servers()) + self.assertEqual(len(self.TEST_SERVERS_DATA['testServers']), + mock_session.delete.call_count) + + def test_create_test_session_res_helper(self, *args): + self.res_helper._user_id = self.SUCCESS_RECORD_ID + self.res_helper._tcl = mock.Mock() + self.res_helper.scenario_helper.all_options = {'traffic_duration': 71} + _session = {'name': 'test', 'duration': 60} + self.assertIsNone(self.res_helper.create_test_session(_session)) + self.res_helper._tcl.create_test_session.assert_called_once_with( + {'name': _session['name'], + 'duration': 71, + 'library': self.SUCCESS_RECORD_ID}) + + def test_create_test_session_res_helper_no_traffic_duration(self, *args): + self.res_helper._user_id = self.SUCCESS_RECORD_ID + self.res_helper._tcl = mock.Mock() + self.res_helper.scenario_helper.all_options = {} + _session = {'name': 'test', 'duration': 60} + self.assertIsNone(self.res_helper.create_test_session(_session)) + self.res_helper._tcl.create_test_session.assert_called_once_with( + {'name': _session['name'], + 'duration': 60, + 'library': self.SUCCESS_RECORD_ID}) + + @mock.patch.object(tg_landslide.LandslideTclClient, + 'resolve_test_server_name', + return_value='Not Found') + def test_create_test_session_ts_name_not_found(self, *args): + self.res_helper._user_id = self.SUCCESS_RECORD_ID + test_session = { + 'duration': 60, + 'description': 'UE default bearer creation test case', + 'name': 'default_bearer_capacity', + 'tsGroups': [{'testCases': [{'type': 'SGW_Node', + 'name': ''}], + 'tsId': 'TestServer_3'}] + } + with self.assertRaises(RuntimeError): + self.res_helper.create_test_session(test_session) + + def test_get_test_session(self, *args): + test_session = {"name": self.TEST_SESSION_NAME} + self.res_helper._user_id = self.SUCCESS_RECORD_ID + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': test_session} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + res = self.res_helper.get_test_session(self.TEST_SESSION_NAME) + self.assertEqual(test_session, res) + + def test_configure_test_session(self, *args): + test_session = {'name': self.TEST_SESSION_NAME} + self.res_helper._user_id = self.SUCCESS_RECORD_ID + self.res_helper.user_lib_uri = 'libraries/{{}}/{}'.format( + self.res_helper.test_session_uri) + mock_session = mock.Mock(spec=requests.Session) + self.res_helper.session = mock_session + res = self.res_helper.configure_test_session(self.TEST_SESSION_NAME, + test_session) + self.assertIsNotNone(res) + mock_session.post.assert_called_once() + + def test_delete_test_session(self, *args): + self.res_helper._user_id = self.SUCCESS_RECORD_ID + self.res_helper.user_lib_uri = 'libraries/{{}}/{}'.format( + self.res_helper.test_session_uri) + mock_session = mock.Mock(spec=requests.Session) + self.res_helper.session = mock_session + res = self.res_helper.delete_test_session(self.TEST_SESSION_NAME) + self.assertIsNotNone(res) + mock_session.delete.assert_called_once() + + def test_create_running_tests(self, *args): + self.res_helper._user_id = self.SUCCESS_RECORD_ID + test_session = {'id': self.SUCCESS_RECORD_ID} + mock_session = mock.Mock(spec=requests.Session) + post_resp_data = {'status_code': self.SUCCESS_CREATED_CODE, + 'json.return_value': test_session} + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + self.res_helper.create_running_tests(self.TEST_SESSION_NAME) + self.assertEqual(self.SUCCESS_RECORD_ID, self.res_helper.run_id) + + def test_create_running_tests_error(self, *args): + self.res_helper._user_id = self.SUCCESS_RECORD_ID + mock_session = mock.Mock(spec=requests.Session) + post_resp_data = {'status_code': self.NOT_MODIFIED_CODE} + mock_session.post.return_value.configure_mock(**post_resp_data) + self.res_helper.session = mock_session + with self.assertRaises(exceptions.RestApiError): + self.res_helper.create_running_tests(self.TEST_SESSION_NAME) + + def test_get_running_tests(self, *args): + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.RUNNING_TESTS_DATA} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + res = self.res_helper.get_running_tests() + self.assertEqual(self.RUNNING_TESTS_DATA['runningTests'], res) + + def test_delete_running_tests(self, *args): + mock_session = mock.Mock(spec=requests.Session) + delete_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.RUNNING_TESTS_DATA} + mock_session.delete.return_value.configure_mock(**delete_resp_data) + self.res_helper.session = mock_session + self.assertIsNone(self.res_helper.delete_running_tests()) + + def test__running_tests_action(self, *args): + action = 'abort' + mock_session = mock.Mock(spec=requests.Session) + self.res_helper.session = mock_session + res = self.res_helper._running_tests_action(self.SUCCESS_RECORD_ID, + action) + self.assertIsNone(res) + + @mock.patch.object(tg_landslide.LandslideResourceHelper, + '_running_tests_action') + def test_stop_running_tests(self, mock_tests_action, *args): + res = self.res_helper.stop_running_tests(self.SUCCESS_RECORD_ID) + self.assertIsNone(res) + mock_tests_action.assert_called_once() + + def test_check_running_test_state(self, *args): + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = { + 'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.RUNNING_TESTS_DATA["runningTests"][0]} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + res = self.res_helper.check_running_test_state(self.SUCCESS_RECORD_ID) + self.assertEqual( + self.RUNNING_TESTS_DATA["runningTests"][0]['testStateOrStep'], + res) + + def test_get_running_tests_results(self, *args): + mock_session = mock.Mock(spec=requests.Session) + get_resp_data = {'status_code': self.SUCCESS_OK_CODE, + 'json.return_value': self.TEST_RESULTS_DATA} + mock_session.get.return_value.configure_mock(**get_resp_data) + self.res_helper.session = mock_session + res = self.res_helper.get_running_tests_results( + self.SUCCESS_RECORD_ID) + self.assertEqual(self.TEST_RESULTS_DATA, res) + + def test__write_results(self, *args): + res = self.res_helper._write_results(self.TEST_RESULTS_DATA) + exp_res = { + "Test Summary::Actual Dedicated Bearer Session Connects": 100.0, + "Test Summary::Actual Dedicated Bearer Session Disconnects": 100.0, + "Test Summary::Actual Disconnect Rate(Sessions / Second)(P - I)": 164.804, + "Test Summary::Average Session Disconnect Time(P - I)": 5.024, + "Test Summary::Total Data Sent + Received Packets / Sec(P - I)": 1452.294 + } + self.assertEqual(exp_res, res) + + def test__write_results_no_tabs(self, *args): + _res_data = copy.deepcopy(self.TEST_RESULTS_DATA) + del _res_data['tabs'] + # Return None if tabs not found in test results dict + self.assertIsNone(self.res_helper._write_results(_res_data)) + + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'check_running_test_state') + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'get_running_tests_results') + def test_collect_kpi_test_running(self, mock_tests_results, + mock_tests_state, *args): + self.res_helper.run_id = self.SUCCESS_RECORD_ID + mock_tests_state.return_value = 'RUNNING' + mock_tests_results.return_value = self.TEST_RESULTS_DATA + res = self.res_helper.collect_kpi() + self.assertNotIn('done', res) + mock_tests_state.assert_called_once_with(self.res_helper.run_id) + mock_tests_results.assert_called_once_with(self.res_helper.run_id) + + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'check_running_test_state') + @mock.patch.object(tg_landslide.LandslideResourceHelper, + 'get_running_tests_results') + def test_collect_kpi_test_completed(self, mock_tests_results, + mock_tests_state, *args): + self.res_helper.run_id = self.SUCCESS_RECORD_ID + mock_tests_state.return_value = 'COMPLETE' + res = self.res_helper.collect_kpi() + self.assertIsNotNone(res) + mock_tests_state.assert_called_once_with(self.res_helper.run_id) + mock_tests_results.assert_not_called() + self.assertDictContainsSubset({'done': True}, res) + + +class TestLandslideTclClient(unittest.TestCase): + def setUp(self): + self.mock_tcl_handler = mock.Mock(spec=tg_landslide.LsTclHandler) + self.ls_res_helper = mock.Mock( + spec=tg_landslide.LandslideResourceHelper) + self.ls_tcl_client = tg_landslide.LandslideTclClient( + self.mock_tcl_handler, + self.ls_res_helper) + + def test___init__(self, *args): + self.ls_tcl_client = tg_landslide.LandslideTclClient( + self.mock_tcl_handler, + self.ls_res_helper) + self.assertIsNone(self.ls_tcl_client.tcl_server_ip) + self.assertIsNone(self.ls_tcl_client._user) + self.assertIsNone(self.ls_tcl_client._library_id) + self.assertIsNone(self.ls_tcl_client._basic_library_id) + self.assertEqual(set(), self.ls_tcl_client.ts_ids) + self.assertIsInstance(self.ls_tcl_client._tc_types, set) + self.assertIsNotNone(self.ls_tcl_client._tc_types) + + def test_connect_login_success(self, *args): + lib_id = '123' + exec_responses = ['java0x2', lib_id, lib_id] + auth = ('user', 'password') + self.mock_tcl_handler.execute.side_effect = exec_responses + self.ls_tcl_client.connect(TAS_INFO['ip'], *auth) + self.assertEqual(lib_id, self.ls_tcl_client._library_id) + self.assertEqual(lib_id, self.ls_tcl_client._basic_library_id) + self.assertEqual(TAS_INFO['ip'], self.ls_tcl_client.tcl_server_ip) + self.assertEqual(auth[0], self.ls_tcl_client._user) + self.assertEqual(len(exec_responses), + self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call("ls::login 1.1.1.1 user password"), + mock.call("ls::get [ls::query LibraryInfo -userLibraryName user] -Id"), + ]) + + def test_connect_login_failed(self, *args): + exec_responses = ['Login failed'] + auth = ('user', 'password') + self.mock_tcl_handler.execute.side_effect = exec_responses + self.assertRaises(exceptions.LandslideTclException, + self.ls_tcl_client.connect, + TAS_INFO['ip'], + *auth) + self.assertIsNone(self.ls_tcl_client._library_id) + self.assertIsNone(self.ls_tcl_client._basic_library_id) + self.assertIsNone(self.ls_tcl_client.tcl_server_ip) + self.assertIsNone(self.ls_tcl_client._user) + self.assertEqual(len(exec_responses), + self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_called_with( + "ls::login 1.1.1.1 user password") + + def test_disconnect(self, *args): + self.ls_tcl_client.disconnect() + self.mock_tcl_handler.execute.assert_called_once_with("ls::logout") + self.assertIsNone(self.ls_tcl_client.tcl_server_ip) + self.assertIsNone(self.ls_tcl_client._user) + self.assertIsNone(self.ls_tcl_client._library_id) + self.assertIsNone(self.ls_tcl_client._basic_library_id) + + def test_create_test_server(self, *args): + return_value = '2' + self.ls_tcl_client._ts_context.vnfd_helper = \ + VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.ls_tcl_client._ts_context.license_data = {'lic_id': return_value} + self.mock_tcl_handler.execute.return_value = return_value + self.ls_tcl_client._set_thread_model = mock.Mock() + res = self.ls_tcl_client.create_test_server(TEST_SERVERS[1]) + self.assertEqual(3, self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::query TsId TestServer_2'), + mock.call('set ts [ls::retrieve TsInfo -Name "TestServer_2"]'), + mock.call('ls::get $ts -RequestedLicense'), + ]) + self.ls_tcl_client._set_thread_model.assert_called_once_with( + TEST_SERVERS[1]['name'], + TEST_SERVERS[1]['thread_model']) + self.assertEqual(int(return_value), res) + + def test_create_test_server_fail_limit_reach(self, *args): + self.mock_tcl_handler.execute.side_effect = ['TS not found', + 'Add failed'] + self.assertRaises(RuntimeError, + self.ls_tcl_client.create_test_server, + TEST_SERVERS[0]) + self.assertEqual(2, self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::query TsId TestServer_1'), + mock.call('ls::perform AddTs -Name "TestServer_1" ' + '-Ip "192.168.122.101"'), + ]) + + def test__add_test_server(self): + ts_id = '2' + self.mock_tcl_handler.execute.side_effect = ['TS not found', ts_id] + self.assertEqual(ts_id, + self.ls_tcl_client._add_test_server('name', 'ip')) + self.assertEqual(2, self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::query TsId name'), + mock.call('ls::perform AddTs -Name "name" -Ip "ip"'), + ]) + + def test__add_test_server_failed(self): + self.mock_tcl_handler.execute.side_effect = ['TS not found', + 'Add failed'] + self.assertRaises(RuntimeError, self.ls_tcl_client._add_test_server, + 'name', 'ip') + self.assertEqual(2, self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::query TsId name'), + mock.call('ls::perform AddTs -Name "name" -Ip "ip"'), + ]) + + def test__update_license(self): + curr_lic_id = '111' + new_lic_id = '222' + exec_resp = ['java0x4', + curr_lic_id, + TCL_SUCCESS_RESPONSE, + TCL_SUCCESS_RESPONSE] + self.ls_tcl_client._ts_context.license_data = {'lic_id': new_lic_id} + self.mock_tcl_handler.execute.side_effect = exec_resp + self.ls_tcl_client._update_license('name') + self.assertEqual(len(exec_resp), + self.mock_tcl_handler.execute.call_count) + + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('set ts [ls::retrieve TsInfo -Name "name"]'), + mock.call('ls::get $ts -RequestedLicense'), + mock.call('ls::config $ts -RequestedLicense 222'), + mock.call('ls::perform ModifyTs $ts'), + ]) + + def test__update_license_same_as_current(self): + curr_lic_id = '111' + new_lic_id = '111' + exec_resp = ['java0x4', curr_lic_id] + self.ls_tcl_client._ts_context.license_data = {'lic_id': new_lic_id} + self.mock_tcl_handler.execute.side_effect = exec_resp + self.ls_tcl_client._update_license('name') + self.assertEqual(len(exec_resp), + self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('set ts [ls::retrieve TsInfo -Name "name"]'), + mock.call('ls::get $ts -RequestedLicense'), + ]) + + def test__set_thread_model_update_needed(self): + self.ls_tcl_client._ts_context.vnfd_helper = { + 'mgmt-interface': { + 'cfguser_password': 'cfguser_password' + } + } + exec_resp = ['java0x4', 'V0', '', ''] + self.mock_tcl_handler.execute.side_effect = exec_resp + self.ls_tcl_client._set_thread_model('name', 'Fireball') + self.assertEqual(len(exec_resp), + self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('set tsc [ls::perform RetrieveTsConfiguration ' + '-name "name" cfguser_password]'), + mock.call('ls::get $tsc -ThreadModel'), + mock.call('ls::config $tsc -ThreadModel "V1_FB3"'), + mock.call('ls::perform ApplyTsConfiguration $tsc cfguser_password'), + ]) + + def test__set_thread_model_no_update_needed(self): + self.ls_tcl_client._ts_context.vnfd_helper = { + 'mgmt-interface': { + 'cfguser_password': 'cfguser_password' + } + } + exec_resp = ['java0x4', 'V0'] + self.mock_tcl_handler.execute.side_effect = exec_resp + self.ls_tcl_client._set_thread_model('name', 'Legacy') + self.assertEqual(len(exec_resp), + self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('set tsc [ls::perform RetrieveTsConfiguration ' + '-name "name" cfguser_password]'), + mock.call('ls::get $tsc -ThreadModel'), + ]) + + @mock.patch.object(tg_landslide.LandslideTclClient, + 'resolve_test_server_name', side_effect=['4', '2']) + def test_create_test_session(self, *args): + _session_profile = copy.deepcopy(SESSION_PROFILE) + _session_profile['reservations'] = RESERVATIONS + self.ls_tcl_client._save_test_session = mock.Mock() + self.ls_tcl_client._configure_ts_group = mock.Mock() + self.ls_tcl_client._library_id = 42 + self.ls_tcl_client.create_test_session(_session_profile) + self.assertEqual(17, self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('set test_ [ls::create TestSession]'), + mock.call('ls::config $test_ -Library 42 ' + '-Name "default_bearer_capacity"'), + mock.call('ls::config $test_ -Description ' \ + '"UE default bearer creation test case"'), + mock.call('ls::config $test_ -Keywords ""'), + mock.call('ls::config $test_ -Duration "60"'), + mock.call('ls::config $test_ -Iterations "1"'), + # _configure_reservation + mock.call('set reservation_ [ls::create Reservation -under $test_]'), + mock.call('ls::config $reservation_ -TsIndex 0 ' + '-TsId 4 -TsName "TestServer_1"'), + mock.call('set physubnet_ [ls::create PhySubnet -under $reservation_]'), + mock.call('ls::config $physubnet_ -Name "eth1" -Base "10.42.32.100" ' + '-Mask "/24" -NumIps 20'), + # _configure_reservation + mock.call('set reservation_ [ls::create Reservation -under $test_]'), + mock.call('ls::config $reservation_ -TsIndex 1 ' + '-TsId 2 -TsName "TestServer_2"'), + mock.call('set physubnet_ [ls::create PhySubnet -under $reservation_]'), + mock.call('ls::config $physubnet_ -Name "eth1" -Base "10.42.32.1" ' + '-Mask "/24" -NumIps 100'), + mock.call('set physubnet_ [ls::create PhySubnet -under $reservation_]'), + mock.call('ls::config $physubnet_ -Name "eth2" -Base "10.42.33.1" ' + '-Mask "/24" -NumIps 100'), + # _configure_report_options + mock.call('ls::config $test_.ReportOptions -Format 1 -Ts -3 -Tc -3'), + ]) + + def test_create_dmf(self): + self.mock_tcl_handler.execute.return_value = '2' + self.ls_tcl_client._save_dmf = mock.Mock() + self.ls_tcl_client.create_dmf(copy.deepcopy(DMF_CFG)) + self.assertEqual(6, self.mock_tcl_handler.execute.call_count) + # This is needed because the dictionary is unordered and the arguments + # can come in either order + call1 = mock.call( + 'ls::config $dmf_ -clientPort 2002 -isClientPortRange "false"') + call2 = mock.call( + 'ls::config $dmf_ -isClientPortRange "false" -clientPort 2002') + self.assertTrue( + call1 in self.mock_tcl_handler.execute.mock_calls or + call2 in self.mock_tcl_handler.execute.mock_calls) + + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('set dmf_ [ls::create Dmf]'), + mock.call( + 'ls::get [ls::query LibraryInfo -systemLibraryName user] -Id'), + mock.call('ls::config $dmf_ -Library 2 -Name "Basic UDP"'), + mock.call('ls::config $dmf_ -dataProtocol "udp"'), + # mock.call( + # 'ls::config $dmf_ -clientPort 2002 -isClientPortRange "false"'), + mock.call('ls::config $dmf_ -serverPort 2003'), + ], any_order=True) + + def test_configure_dmf(self): + self.mock_tcl_handler.execute.return_value = '2' + self.ls_tcl_client._save_dmf = mock.Mock() + self.ls_tcl_client.configure_dmf(DMF_CFG) + self.assertEqual(6, self.mock_tcl_handler.execute.call_count) + # This is need because the dictionary is unordered and the arguments + # can come in either order + call1 = mock.call( + 'ls::config $dmf_ -clientPort 2002 -isClientPortRange "false"') + call2 = mock.call( + 'ls::config $dmf_ -isClientPortRange "false" -clientPort 2002') + self.assertTrue( + call1 in self.mock_tcl_handler.execute.mock_calls or + call2 in self.mock_tcl_handler.execute.mock_calls) + + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('set dmf_ [ls::create Dmf]'), + mock.call( + 'ls::get [ls::query LibraryInfo -systemLibraryName user] -Id'), + mock.call('ls::config $dmf_ -Library 2 -Name "Basic UDP"'), + mock.call('ls::config $dmf_ -dataProtocol "udp"'), + # mock.call( + # 'ls::config $dmf_ -clientPort 2002 -isClientPortRange "false"'), + mock.call('ls::config $dmf_ -serverPort 2003'), + ], any_order=True) + + def test_delete_dmf(self): + self.assertRaises(NotImplementedError, + self.ls_tcl_client.delete_dmf, + DMF_CFG) + + def test__save_dmf_valid(self): + exec_resp = [TCL_SUCCESS_RESPONSE, TCL_SUCCESS_RESPONSE] + self.mock_tcl_handler.execute.side_effect = exec_resp + self.ls_tcl_client._save_dmf() + self.assertEqual(len(exec_resp), + self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::perform Validate -Dmf $dmf_'), + mock.call('ls::save $dmf_ -overwrite'), + ]) + + def test__save_dmf_invalid(self): + exec_resp = ['Invalid', 'List of errors and warnings'] + self.mock_tcl_handler.execute.side_effect = exec_resp + self.assertRaises(exceptions.LandslideTclException, + self.ls_tcl_client._save_dmf) + self.assertEqual(len(exec_resp), + self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::perform Validate -Dmf $dmf_'), + mock.call('ls::get $dmf_ -ErrorsAndWarnings'), + ]) + + def test__configure_report_options(self): + _options = {'format': 'CSV', 'PerInterval': 'false'} + self.ls_tcl_client._configure_report_options(_options) + self.assertEqual(2, self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::config $test_.ReportOptions -Format 1 -Ts -3 -Tc -3'), + mock.call('ls::config $test_.ReportOptions -PerInterval false'), + ], + any_order=True) + + def test___configure_ts_group(self, *args): + _ts_group = copy.deepcopy(SESSION_PROFILE['tsGroups'][0]) + self.ls_tcl_client._configure_tc_type = mock.Mock() + self.ls_tcl_client._configure_preresolved_arp = mock.Mock() + self.ls_tcl_client.resolve_test_server_name = mock.Mock( + return_value='2') + self.ls_tcl_client._configure_ts_group(_ts_group, 0) + self.mock_tcl_handler.execute.assert_called_once_with( + 'set tss_ [ls::create TsGroup -under $test_ -tsId 2 ]') + + def test___configure_ts_group_resolve_ts_fail(self, *args): + _ts_group = copy.deepcopy(SESSION_PROFILE['tsGroups'][0]) + self.ls_tcl_client._configure_tc_type = mock.Mock() + self.ls_tcl_client._configure_preresolved_arp = mock.Mock() + self.ls_tcl_client.resolve_test_server_name = mock.Mock( + return_value='TS Not Found') + self.assertRaises(RuntimeError, self.ls_tcl_client._configure_ts_group, + _ts_group, 0) + self.mock_tcl_handler.execute.assert_not_called() + + def test__configure_tc_type(self): + _tc = copy.deepcopy(SESSION_PROFILE['tsGroups'][0]['testCases'][0]) + self.mock_tcl_handler.execute.return_value = TCL_SUCCESS_RESPONSE + self.ls_tcl_client._configure_parameters = mock.Mock() + self.ls_tcl_client._configure_tc_type(_tc, 0) + self.assertEqual(7, self.mock_tcl_handler.execute.call_count) + + def test__configure_tc_type_optional_param_omitted(self): + _tc = copy.deepcopy(SESSION_PROFILE['tsGroups'][0]['testCases'][0]) + del _tc['linked'] + self.mock_tcl_handler.execute.return_value = TCL_SUCCESS_RESPONSE + self.ls_tcl_client._configure_parameters = mock.Mock() + self.ls_tcl_client._configure_tc_type(_tc, 0) + self.assertEqual(6, self.mock_tcl_handler.execute.call_count) + + def test__configure_tc_type_wrong_type(self): + _tc = copy.deepcopy(SESSION_PROFILE['tsGroups'][0]['testCases'][0]) + _tc['type'] = 'not_supported' + self.ls_tcl_client._configure_parameters = mock.Mock() + self.assertRaises(RuntimeError, + self.ls_tcl_client._configure_tc_type, + _tc, 0) + self.mock_tcl_handler.assert_not_called() + + def test__configure_tc_type_not_found_basic_lib(self): + _tc = copy.deepcopy(SESSION_PROFILE['tsGroups'][0]['testCases'][0]) + self.ls_tcl_client._configure_parameters = mock.Mock() + self.mock_tcl_handler.execute.return_value = 'Invalid' + self.assertRaises(RuntimeError, + self.ls_tcl_client._configure_tc_type, + _tc, 0) + + def test__configure_parameters(self): + _params = copy.deepcopy( + SESSION_PROFILE['tsGroups'][0]['testCases'][0]['parameters']) + self.ls_tcl_client._configure_parameters(_params) + self.assertEqual(16, self.mock_tcl_handler.execute.call_count) + + def test__configure_array_param(self): + _array = {"class": "Array", + "array": ["0"]} + self.ls_tcl_client._configure_array_param('name', _array) + self.assertEqual(2, self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::create -Array-name -under $p_ ;'), + mock.call('ls::create ArrayItem -under $p_.name -Value "0"'), + ]) + + def test__configure_test_node_param(self): + _params = copy.deepcopy( + SESSION_PROFILE['tsGroups'][0]['testCases'][0]['parameters']) + self.ls_tcl_client._configure_test_node_param('SgwUserAddr', + _params['SgwUserAddr']) + cmd = ('ls::create -TestNode-SgwUserAddr -under $p_ -Type "eth" ' + '-Phy "eth1" -Ip "SGW_USER_IP" -NumLinksOrNodes 1 ' + '-NextHop "SGW_CONTROL_NEXT_HOP" -Mac "" -MTU 1500 ' + '-ForcedEthInterface "" -EthStatsEnabled false -VlanId 0 ' + '-VlanUserPriority 0 -NumVlan 1 -UniqueVlanAddr false;') + self.mock_tcl_handler.execute.assert_called_once_with(cmd) + + def test__configure_sut_param(self): + _params = {'name': 'name'} + self.ls_tcl_client._configure_sut_param('name', _params) + self.mock_tcl_handler.execute.assert_called_once_with( + 'ls::create -Sut-name -under $p_ -Name "name";') + + def test__configure_dmf_param(self): + _params = {"mainflows": [{"library": '111', + "name": "Basic UDP"}], + "instanceGroups": [{ + "mainflowIdx": 0, + "mixType": "", + "rate": 0.0, + "rows": [{ + "clientPort": 0, + "context": 0, + "node": 0, + "overridePort": "false", + "ratingGroup": 0, + "role": 0, + "serviceId": 0, + "transport": "Any"}] + }]} + self.ls_tcl_client._get_library_id = mock.Mock(return_value='111') + res = self.ls_tcl_client._configure_dmf_param('name', _params) + self.assertEqual(5, self.mock_tcl_handler.execute.call_count) + self.assertIsNone(res) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::create -Dmf-name -under $p_ ;'), + mock.call('ls::perform AddDmfMainflow $p_.Dmf 111 "Basic UDP"'), + mock.call('ls::config $p_.Dmf.InstanceGroup(0) -mixType '), + mock.call('ls::config $p_.Dmf.InstanceGroup(0) -rate 0.0'), + mock.call('ls::config $p_.Dmf.InstanceGroup(0).Row(0) -Node 0 ' + '-OverridePort false -ClientPort 0 -Context 0 -Role 0 ' + '-PreferredTransport Any -RatingGroup 0 ' + '-ServiceID 0'), + ]) + + def test__configure_dmf_param_no_instance_groups(self): + _params = {"mainflows": [{"library": '111', + "name": "Basic UDP"}]} + self.ls_tcl_client._get_library_id = mock.Mock(return_value='111') + res = self.ls_tcl_client._configure_dmf_param('name', _params) + self.assertEqual(2, self.mock_tcl_handler.execute.call_count) + self.assertIsNone(res) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::create -Dmf-name -under $p_ ;'), + mock.call('ls::perform AddDmfMainflow $p_.Dmf 111 "Basic UDP"'), + ]) + + def test__configure_reservation(self): + _reservation = copy.deepcopy(RESERVATIONS[0]) + self.ls_tcl_client.resolve_test_server_name = mock.Mock( + return_value='4') + res = self.ls_tcl_client._configure_reservation(_reservation) + self.assertIsNone(res) + self.assertEqual(4, self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('set reservation_ [ls::create Reservation -under $test_]'), + mock.call('ls::config $reservation_ -TsIndex 0 -TsId 4 ' + \ + '-TsName "TestServer_1"'), + mock.call('set physubnet_ [ls::create PhySubnet -under $reservation_]'), + mock.call('ls::config $physubnet_ -Name "eth1" ' + \ + '-Base "10.42.32.100" -Mask "/24" -NumIps 20'), + ]) + + def test__configure_preresolved_arp(self): + _arp = [{'StartingAddress': '10.81.1.10', + 'NumNodes': 1}] + res = self.ls_tcl_client._configure_preresolved_arp(_arp) + self.mock_tcl_handler.execute.assert_called_once() + self.assertIsNone(res) + self.mock_tcl_handler.execute.assert_called_once_with( + 'ls::create PreResolvedArpAddress -under $tss_ ' + \ + '-StartingAddress "10.81.1.10" -NumNodes 1') + + def test__configure_preresolved_arp_none(self): + res = self.ls_tcl_client._configure_preresolved_arp(None) + self.assertIsNone(res) + self.mock_tcl_handler.execute.assert_not_called() + + def test_delete_test_session(self): + self.assertRaises(NotImplementedError, + self.ls_tcl_client.delete_test_session, {}) + + def test__save_test_session(self): + self.mock_tcl_handler.execute.side_effect = [TCL_SUCCESS_RESPONSE, + TCL_SUCCESS_RESPONSE] + res = self.ls_tcl_client._save_test_session() + self.assertEqual(2, self.mock_tcl_handler.execute.call_count) + self.assertIsNone(res) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::perform Validate -TestSession $test_'), + mock.call('ls::save $test_ -overwrite'), + ]) + + def test__save_test_session_invalid(self): + self.mock_tcl_handler.execute.side_effect = ['Invalid', 'Errors'] + self.assertRaises(exceptions.LandslideTclException, + self.ls_tcl_client._save_test_session) + self.assertEqual(2, self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call('ls::perform Validate -TestSession $test_'), + mock.call('ls::get $test_ -ErrorsAndWarnings'), + ]) + + def test__get_library_id_system_lib(self): + self.mock_tcl_handler.execute.return_value = '111' + res = self.ls_tcl_client._get_library_id('name') + self.mock_tcl_handler.execute.assert_called_once() + self.assertEqual('111', res) + self.mock_tcl_handler.execute.assert_called_with( + 'ls::get [ls::query LibraryInfo -systemLibraryName name] -Id') + + def test__get_library_id_user_lib(self): + self.mock_tcl_handler.execute.side_effect = ['Not found', '222'] + res = self.ls_tcl_client._get_library_id('name') + self.assertEqual(2, self.mock_tcl_handler.execute.call_count) + self.assertEqual('222', res) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call( + 'ls::get [ls::query LibraryInfo -systemLibraryName name] -Id'), + mock.call( + 'ls::get [ls::query LibraryInfo -userLibraryName name] -Id'), + ]) + + def test__get_library_id_exception(self): + self.mock_tcl_handler.execute.side_effect = ['Not found', 'Not found'] + self.assertRaises(exceptions.LandslideTclException, + self.ls_tcl_client._get_library_id, + 'name') + self.assertEqual(2, self.mock_tcl_handler.execute.call_count) + self.mock_tcl_handler.execute.assert_has_calls([ + mock.call( + 'ls::get [ls::query LibraryInfo -systemLibraryName name] -Id'), + mock.call( + 'ls::get [ls::query LibraryInfo -userLibraryName name] -Id'), + ]) + + +class TestLsTclHandler(unittest.TestCase): + + def setUp(self): + self.mock_lsapi = mock.patch.object(tg_landslide, 'LsApi') + self.mock_lsapi.start() + + self.addCleanup(self._cleanup) + + def _cleanup(self): + self.mock_lsapi.stop() + + def test___init__(self, *args): + self.ls_tcl_handler = tg_landslide.LsTclHandler() + self.assertEqual({}, self.ls_tcl_handler.tcl_cmds) + self.ls_tcl_handler._ls.tcl.assert_called_once() + + def test_execute(self, *args): + self.ls_tcl_handler = tg_landslide.LsTclHandler() + self.ls_tcl_handler.execute('command') + self.assertIn('command', self.ls_tcl_handler.tcl_cmds) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_ping.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_ping.py new file mode 100644 index 000000000..a3e4384cf --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_ping.py @@ -0,0 +1,298 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +from multiprocessing import Queue +import multiprocessing + +import mock +import unittest + +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.vnf_generic.vnf.tg_ping import PingParser +from yardstick.network_services.vnf_generic.vnf.tg_ping import PingTrafficGen +from yardstick.network_services.vnf_generic.vnf.tg_ping import PingResourceHelper +from yardstick.network_services.vnf_generic.vnf.tg_ping import PingSetupEnvHelper +from yardstick.network_services.vnf_generic.vnf.vnf_ssh_helper import VnfSshHelper + + +SSH_HELPER = "yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper" + + +class TestPingResourceHelper(unittest.TestCase): + def test___init__(self): + setup_helper = mock.Mock() + helper = PingResourceHelper(setup_helper) + + self.assertIsInstance(helper._queue, multiprocessing.queues.Queue) + self.assertIsInstance(helper._parser, PingParser) + + def test_run_traffic(self): + setup_helper = mock.Mock() + traffic_profile = mock.Mock() + traffic_profile.params = { + 'traffic_profile': { + 'frame_size': 64, + }, + } + + helper = PingResourceHelper(setup_helper) + helper.cmd_kwargs = {'target_ip': '10.0.0.2', + 'local_ip': '10.0.0.1', + 'local_if_name': 'eth0', + } + helper.ssh_helper = mock.Mock() + helper.run_traffic(traffic_profile) + helper.ssh_helper.run.called_with('ping-s 64 10.0.0.2') + + +class TestPingParser(unittest.TestCase): + def test___init__(self): + q_out = Queue() + ping_parser = PingParser(q_out) + self.assertIsNotNone(ping_parser.queue) + + def test_clear(self): + sample_out = """ +64 bytes from 10.102.22.93: icmp_seq=3 ttl=64 time=0.296 ms + """ + q_out = Queue() + ping_parser = PingParser(q_out) + ping_parser.write(sample_out) + ping_parser.clear() + self.assertTrue(q_out.empty()) + + def test_close(self): + q_out = Queue() + ping_parser = PingParser(q_out) + self.assertIsNone(ping_parser.close()) + + def test_write(self): + sample_out = """ +64 bytes from 10.102.22.93: icmp_seq=3 ttl=64 time=0.296 ms + """ + q_out = Queue() + ping_parser = PingParser(q_out) + ping_parser.write(sample_out) + + self.assertEqual({"packets_received": 3.0, "rtt": 0.296}, q_out.get()) + + +class TestPingTrafficGen(unittest.TestCase): + VNFD_0_EXT_IF_0 = { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': u'152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': u'152.16.100.20', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0', + } + + VNFD_0_EXT_IF_1 = { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': u'152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'bandwidth': '10 Gbps', + 'dst_ip': u'152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1', + } + + VNFD_0_EXT_IF_LIST = [ + VNFD_0_EXT_IF_0, + VNFD_0_EXT_IF_1, + ] + + VNFD_0 = { + 'short-name': 'VpeVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': u'152.16.100.20', + 'netmask': u'255.255.255.0', + 'gateway': u'152.16.100.20', + 'if': 'xe0', + }, + { + 'network': u'152.16.40.20', + 'netmask': u'255.255.255.0', + 'gateway': u'152.16.40.20', + 'if': 'xe1', + }, + ], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'id': 'vpevnf-baremetal', + 'external-interface': VNFD_0_EXT_IF_LIST, + }, + ], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1', + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'VpeApproxVnf', + 'name': 'VPEVnfSsh', + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ], + }, + } + + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64, + }, + } + + CMD_KWARGS = { + 'target_ip': u'152.16.100.20', + 'local_ip': u'152.16.100.19', + 'local_if_name': u'xe0_fake', + } + + @mock.patch("yardstick.ssh.SSH") + def test___init__(self, ssh): + ssh.from_node.return_value.execute.return_value = 0, "success", "" + ping_traffic_gen = PingTrafficGen('vnf1', self.VNFD_0) + + self.assertIsInstance(ping_traffic_gen.setup_helper, PingSetupEnvHelper) + self.assertIsInstance(ping_traffic_gen.resource_helper, PingResourceHelper) + self.assertEqual(ping_traffic_gen._result, {}) + + @mock.patch("yardstick.ssh.SSH") + def test__bind_device_kernel_with_failure(self, ssh): + mock_ssh(ssh) + + execute_result_data = [ + (1, 'bad stdout messages', 'error messages'), + (0, '', ''), + (0, 'if_name_1', ''), + (0, 'if_name_2', ''), + ] + ssh.from_node.return_value.execute.side_effect = iter(execute_result_data) + ping_traffic_gen = PingTrafficGen('vnf1', self.VNFD_0) + ext_ifs = ping_traffic_gen.vnfd_helper.interfaces + self.assertNotEqual(ext_ifs[0]['virtual-interface']['local_iface_name'], 'if_name_1') + self.assertNotEqual(ext_ifs[1]['virtual-interface']['local_iface_name'], 'if_name_2') + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch("yardstick.ssh.SSH") + def test_collect_kpi(self, ssh, *args): + mock_ssh(ssh, exec_result=(0, "success", "")) + + ping_traffic_gen = PingTrafficGen('vnf1', self.VNFD_0) + ping_traffic_gen.scenario_helper.scenario_cfg = { + 'nodes': {ping_traffic_gen.name: "mock"} + } + ping_traffic_gen._queue = Queue() + ping_traffic_gen._queue.put({}) + expected = { + 'physical_node': 'mock_node', + 'collect_stats': {} + } + # NOTE: Why we check _result but not collect_kpi() return value + # self.assertEqual(ping_traffic_gen._result, {}) + self.assertEqual(ping_traffic_gen.collect_kpi(), expected) + + + @mock.patch(SSH_HELPER) + def test_instantiate(self, ssh): + mock_ssh(ssh, spec=VnfSshHelper, exec_result=(0, "success", "")) + ping_traffic_gen = PingTrafficGen('vnf1', self.VNFD_0) + ping_traffic_gen.setup_helper.ssh_helper = mock.MagicMock( + **{"execute.return_value": (0, "xe0_fake", "")}) + self.assertIsInstance(ping_traffic_gen.ssh_helper, mock.Mock) + self.assertEqual(ping_traffic_gen._result, {}) + + self.assertIsNone(ping_traffic_gen.instantiate({}, {})) + + self.assertEqual( + ping_traffic_gen.vnfd_helper.interfaces[0]['virtual-interface']['local_iface_name'], + 'xe0_fake') + self.assertEqual(self.CMD_KWARGS, ping_traffic_gen.resource_helper.cmd_kwargs) + self.assertIsNotNone(ping_traffic_gen._result) + + def test_listen_traffic(self): + ping_traffic_gen = PingTrafficGen('vnf1', self.VNFD_0) + self.assertIsNone(ping_traffic_gen.listen_traffic({})) + + @mock.patch("yardstick.ssh.SSH") + def test_terminate(self, ssh): + ssh.from_node.return_value.execute.return_value = 0, "success", "" + ssh.from_node.return_value.run.return_value = 0, "success", "" + + ping_traffic_gen = PingTrafficGen('vnf1', self.VNFD_0) + self.assertIsNone(ping_traffic_gen.terminate()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_pktgen.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_pktgen.py new file mode 100644 index 000000000..1ecb6ffc9 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_pktgen.py @@ -0,0 +1,66 @@ +# Copyright (c) 2018-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock + +from yardstick.common import constants +from yardstick.common import exceptions +from yardstick.network_services.vnf_generic.vnf import base as vnf_base +from yardstick.network_services.vnf_generic.vnf import tg_pktgen +from yardstick.tests.unit import base as ut_base + + +class PktgenTrafficGenTestCase(ut_base.BaseUnitTestCase): + + SERVICE_PORTS = [{'port': constants.LUA_PORT, + 'node_port': '34501'}] + VNFD = {'mgmt-interface': {'ip': '1.2.3.4', + 'service_ports': SERVICE_PORTS}, + 'vdu': [{'external-interface': 'interface'}], + 'benchmark': {'kpi': 'fake_kpi'} + } + + def test__init(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD) + self.assertTrue(isinstance(tg, vnf_base.GenericTrafficGen)) + + def test_run_traffic(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD) + mock_tp = mock.Mock() + with mock.patch.object(tg, '_is_running', return_value=True): + tg.run_traffic(mock_tp) + + mock_tp.init.assert_called_once_with(tg._node_ip, tg._lua_node_port) + + def test__get_lua_node_port(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD) + service_ports = [{'port': constants.LUA_PORT, + 'node_port': '12345'}] + self.assertEqual(12345, tg._get_lua_node_port(service_ports)) + + def test__get_lua_node_port_no_lua_port(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD) + service_ports = [{'port': '333'}] + self.assertIsNone(tg._get_lua_node_port(service_ports)) + + def test__is_running(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD) + with mock.patch.object(tg, '_traffic_profile'): + self.assertTrue(tg._is_running()) + + def test__is_running_exception(self): + tg = tg_pktgen.PktgenTrafficGen('name1', self.VNFD) + with mock.patch.object(tg, '_traffic_profile') as mock_tp: + mock_tp.help.side_effect = exceptions.PktgenActionError() + self.assertFalse(tg._is_running()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_prox.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_prox.py new file mode 100644 index 000000000..0aaf17790 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_prox.py @@ -0,0 +1,441 @@ +# Copyright (c) 2017-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import unittest +import mock + +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.vnf_generic.vnf.tg_prox import ProxTrafficGen +from yardstick.network_services.traffic_profile.base import TrafficProfile + + +SSH_HELPER = 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper' +NAME = 'vnf__1' + + +@mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.time') +class TestProxTrafficGen(unittest.TestCase): + VNFD0 = { + 'short-name': 'ProxVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0', + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'description': 'PROX approximation using DPDK', + 'name': 'proxvnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'id': 'proxvnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'vld_id': '', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0', + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'vld_id': '', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1', + }, + ], + }, + ], + 'description': 'PROX approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'proxvnf-baremetal', + 'host': '1.2.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.2.1.1', + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'ProxApproxVnf', + 'name': 'ProxVnf', + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD0, + ], + }, + } + + SCENARIO_CFG = { + 'task_path': "", + 'nodes': { + 'tg__1': 'trafficgen_1.yardstick', + 'vnf__1': 'vnf.yardstick'}, + 'runner': { + 'duration': 600, 'type': 'Duration'}, + 'topology': 'prox-tg-topology-2.yaml', + 'traffic_profile': '../../traffic_profiles/prox_binsearch.yaml', + 'type': 'NSPerf', + 'options': { + 'tg__1': {'prox_args': {'-e': '', + '-t': ''}, + 'prox_config': 'configs/l3-gen-2.cfg', + 'prox_path': + '/root/dppd-PROX-v035/build/prox'}, + 'vnf__1': { + 'prox_args': {'-t': ''}, + 'prox_config': 'configs/l3-swap-2.cfg', + 'prox_path': '/root/dppd-PROX-v035/build/prox'}}} + + CONTEXT_CFG = { + 'nodes': { + 'tg__2': { + 'member-vnf-index': '3', + 'role': 'TrafficGen', + 'name': 'trafficgen_2.yardstick', + 'vnfd-id-ref': 'tg__2', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens513f0', + 'vld_id': ProxTrafficGen.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.20', + 'dst_mac': '00:00:00:00:00:01', + 'local_mac': '00:00:00:00:00:03', + 'dst_ip': '152.16.40.19', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens513f1', + 'netmask': '255.255.255.0', + 'network': '202.16.100.0', + 'local_ip': '202.16.100.20', + 'local_mac': '00:1e:67:d0:60:5d', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.1', + 'dpdk_port_num': 1, + }, + }, + 'password': 'r00t', + 'VNF model': 'l3fwd_vnf.yaml', + 'user': 'root', + }, + 'tg__1': { + 'member-vnf-index': '1', + 'role': 'TrafficGen', + 'name': 'trafficgen_1.yardstick', + 'vnfd-id-ref': 'tg__1', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens785f0', + 'vld_id': ProxTrafficGen.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.20', + 'dst_mac': '00:00:00:00:00:02', + 'local_mac': '00:00:00:00:00:04', + 'dst_ip': '152.16.100.19', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens785f1', + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.21', + 'local_mac': '00:00:00:00:00:01', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + }, + }, + 'password': 'r00t', + 'VNF model': 'tg_rfc2544_tpl.yaml', + 'user': 'root', + }, + 'vnf__1': { + 'name': 'vnf.yardstick', + 'vnfd-id-ref': 'vnf__1', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens786f0', + 'vld_id': ProxTrafficGen.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.19', + 'dst_mac': '00:00:00:00:00:04', + 'local_mac': '00:00:00:00:00:02', + 'dst_ip': '152.16.100.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens786f1', + 'vld_id': ProxTrafficGen.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.19', + 'dst_mac': '00:00:00:00:00:03', + 'local_mac': '00:00:00:00:00:01', + 'dst_ip': '152.16.40.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + }, + }, + 'routing_table': [ + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'network': '152.16.100.20', + 'if': 'xe0', + }, + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'network': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'member-vnf-index': '2', + 'host': '1.2.1.1', + 'role': 'vnf', + 'user': 'root', + 'nd_route_tbl': [ + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'password': 'r00t', + 'VNF model': 'prox_vnf.yaml', + }, + }, + } + + TRAFFIC_PROFILE = { + 'description': 'Binary search for max no-drop throughput over given packet sizes', + 'name': 'prox_binsearch', + 'schema': 'nsb:traffic_profile:0.1', + 'traffic_profile': { + 'duration': 5, + 'lower_bound': 0.0, + 'packet_sizes': [64, 65], + 'test_precision': 1.0, + 'tolerated_loss': 0.0, + 'traffic_type': 'ProxBinSearchProfile', + 'upper_bound': 100.0}} + + @mock.patch(SSH_HELPER) + def test___init__(self, ssh, *args): + mock_ssh(ssh) + prox_traffic_gen = ProxTrafficGen(NAME, self.VNFD0) + self.assertIsNone(prox_traffic_gen._tg_process) + self.assertIsNone(prox_traffic_gen._traffic_process) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch(SSH_HELPER) + def test_collect_kpi(self, ssh, *args): + mock_ssh(ssh) + prox_traffic_gen = ProxTrafficGen(NAME, self.VNFD0) + prox_traffic_gen.scenario_helper.scenario_cfg = { + 'nodes': {prox_traffic_gen.name: "mock"} + } + prox_traffic_gen._vnf_wrapper.resource_helper.resource = mock.MagicMock( + **{"self.check_if_system_agent_running.return_value": [False]}) + + vnfd_helper = mock.MagicMock() + vnfd_helper.ports_iter.return_value = [('xe0', 0), ('xe1', 1)] + prox_traffic_gen.resource_helper.vnfd_helper = vnfd_helper + + prox_traffic_gen._vnf_wrapper.resource_helper.client = mock.MagicMock() + prox_traffic_gen._vnf_wrapper.resource_helper.client.multi_port_stats.return_value = \ + [[0, 1, 2, 3, 4, 5], [1, 1, 2, 3, 4, 5]] + prox_traffic_gen._vnf_wrapper.resource_helper.client.multi_port_stats_diff.return_value = \ + [0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3, 4, 5, 6, 7] + prox_traffic_gen._vnf_wrapper.resource_helper.client.\ + multi_port_stats_tuple.return_value = \ + {"xe0": {"in_packets": 1, "out_packets": 2}} + + prox_traffic_gen._vnf_wrapper.vnf_execute = mock.Mock(return_value="") + expected = { + 'collect_stats': {'live_stats': {'xe0': {'in_packets': 1, 'out_packets': 2}}}, + 'physical_node': 'mock_node' + } + result = prox_traffic_gen.collect_kpi() + self.assertDictEqual(result, expected) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.find_relative_file') + @mock.patch( + 'yardstick.network_services.vnf_generic.vnf.sample_vnf.CpuSysCores') + @mock.patch(SSH_HELPER) + def bad_test_instantiate(self, ssh, mock_cpu_sys_cores, *args): + mock_ssh(ssh) + + mock_cpu_sys_cores.get_core_socket.return_value = {'0': '01234'} + + mock_traffic_profile = mock.Mock(autospec=TrafficProfile) + mock_traffic_profile.get_traffic_definition.return_value = "64" + mock_traffic_profile.params = self.TRAFFIC_PROFILE + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + prox_traffic_gen = ProxTrafficGen(NAME, vnfd) + ssh_helper = mock.MagicMock( + **{"execute.return_value": (0, "", ""), "bin_path": ""}) + prox_traffic_gen.ssh_helper = ssh_helper + prox_traffic_gen.setup_helper.dpdk_bind_helper.ssh_helper = ssh_helper + prox_traffic_gen.setup_helper._setup_resources = mock.MagicMock() + prox_traffic_gen.setup_hugepages = mock.MagicMock() + prox_traffic_gen.generate_prox_config_file = mock.MagicMock() + prox_traffic_gen.upload_prox_config = mock.MagicMock() + prox_traffic_gen.setup_helper._find_used_drivers = mock.MagicMock() + prox_traffic_gen.setup_helper.used_drivers = {} + prox_traffic_gen.setup_helper.bound_pci = [] + prox_traffic_gen._start_server = mock.Mock(return_value=0) + prox_traffic_gen._tg_process = mock.MagicMock() + prox_traffic_gen._tg_process.start = mock.Mock() + prox_traffic_gen._tg_process.exitcode = 0 + prox_traffic_gen._tg_process._is_alive = mock.Mock(return_value=1) + prox_traffic_gen.ssh_helper = mock.MagicMock() + prox_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + scenario_cfg = { + 'task_path': '', + 'options': {'tg__1': {'prox_args': {'-e': '', + '-t': ''}, + 'prox_config': 'configs/l3-gen-2.cfg', + 'prox_path': '/root/dppd-PROX-v035/build/prox'}, + 'vnf__1': {'prox_args': {'-t': ''}, + 'prox_config': 'configs/l3-swap-2.cfg', + 'prox_path': '/root/dppd-PROX-v035/build/prox'} + } + } + prox_traffic_gen.instantiate(scenario_cfg, {}) + + @mock.patch(SSH_HELPER) + def test__traffic_runner(self, ssh, *args): + mock_ssh(ssh) + + mock_traffic_profile = mock.Mock(autospec=TrafficProfile) + mock_traffic_profile.get_traffic_definition.return_value = "64" + mock_traffic_profile.execute_traffic.return_value = "64" + mock_traffic_profile.params = self.TRAFFIC_PROFILE + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + sut = ProxTrafficGen(NAME, vnfd) + sut._get_socket = mock.MagicMock() + sut.ssh_helper = mock.Mock() + sut.ssh_helper.run = mock.Mock() + sut.setup_helper.prox_config_dict = {} + sut._connect_client = mock.Mock(autospec=mock.Mock()) + sut._connect_client.get_stats = mock.Mock(return_value="0") + sut._traffic_runner(mock_traffic_profile) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.socket') + @mock.patch(SSH_HELPER) + def test_listen_traffic(self, ssh, *args): + mock_ssh(ssh) + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + prox_traffic_gen = ProxTrafficGen(NAME, vnfd) + self.assertIsNone(prox_traffic_gen.listen_traffic(mock.Mock())) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.prox_helpers.socket') + @mock.patch(SSH_HELPER) + def test_terminate(self, ssh, *args): + mock_ssh(ssh) + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + prox_traffic_gen = ProxTrafficGen(NAME, vnfd) + prox_traffic_gen._terminated = mock.MagicMock() + prox_traffic_gen._traffic_process = mock.MagicMock() + prox_traffic_gen._traffic_process.terminate = mock.Mock() + prox_traffic_gen.ssh_helper = mock.MagicMock() + prox_traffic_gen.setup_helper = mock.MagicMock() + prox_traffic_gen.resource_helper = mock.MagicMock() + prox_traffic_gen._vnf_wrapper.setup_helper = mock.MagicMock() + prox_traffic_gen._vnf_wrapper._vnf_process = mock.MagicMock() + prox_traffic_gen._vnf_wrapper.resource_helper = mock.MagicMock() + self.assertIsNone(prox_traffic_gen.terminate()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_ixia.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_ixia.py new file mode 100644 index 000000000..c3f3e5f67 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_ixia.py @@ -0,0 +1,1265 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os + +import mock +import six +import unittest +import ipaddress +import time +from collections import OrderedDict + +from yardstick.common import utils +from yardstick.common import exceptions +from yardstick.benchmark import contexts +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.libs.ixia_libs.ixnet import ixnet_api +from yardstick.network_services.traffic_profile import base as tp_base +from yardstick.network_services.vnf_generic.vnf import tg_rfc2544_ixia +from yardstick.network_services.traffic_profile import ixia_rfc2544 + + +TEST_FILE_YAML = 'nsb_test_case.yaml' + +NAME = "tg__1" + + +class TestIxiaResourceHelper(unittest.TestCase): + + def setUp(self): + self._mock_IxNextgen = mock.patch.object(ixnet_api, 'IxNextgen') + self.mock_IxNextgen = self._mock_IxNextgen.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_IxNextgen.stop() + + def test___init___with_custom_rfc_helper(self): + class MyRfcHelper(tg_rfc2544_ixia.IxiaRfc2544Helper): + pass + + ixia_resource_helper = tg_rfc2544_ixia.IxiaResourceHelper( + mock.Mock(), MyRfcHelper) + self.assertIsInstance(ixia_resource_helper.rfc_helper, MyRfcHelper) + + def test__init_ix_scenario(self): + mock_scenario = mock.Mock() + mock_scenario_helper = mock.Mock() + mock_scenario_helper.scenario_cfg = {'ixia_config': 'TestScenario', + 'options': 'scenario_options'} + mock_setup_helper = mock.Mock(scenario_helper=mock_scenario_helper) + ixia_resource_helper = tg_rfc2544_ixia.IxiaResourceHelper(mock_setup_helper) + ixia_resource_helper._ixia_scenarios = {'TestScenario': mock_scenario} + ixia_resource_helper.client = 'client' + ixia_resource_helper.context_cfg = 'context' + ixia_resource_helper._init_ix_scenario() + mock_scenario.assert_called_once_with('client', 'context', 'scenario_options') + + def test__init_ix_scenario_not_supported_cfg_type(self): + mock_scenario_helper = mock.Mock() + mock_scenario_helper.scenario_cfg = {'ixia_config': 'FakeScenario', + 'options': 'scenario_options'} + mock_setup_helper = mock.Mock(scenario_helper=mock_scenario_helper) + ixia_resource_helper = tg_rfc2544_ixia.IxiaResourceHelper(mock_setup_helper) + ixia_resource_helper._ixia_scenarios = {'TestScenario': mock.Mock()} + with self.assertRaises(RuntimeError): + ixia_resource_helper._init_ix_scenario() + + @mock.patch.object(tg_rfc2544_ixia.IxiaResourceHelper, '_init_ix_scenario') + def test_setup(self, mock__init_ix_scenario): + ixia_resource_helper = tg_rfc2544_ixia.IxiaResourceHelper(mock.Mock()) + ixia_resource_helper.setup() + mock__init_ix_scenario.assert_called_once() + + def test_stop_collect_with_client(self): + mock_client = mock.Mock() + ixia_resource_helper = tg_rfc2544_ixia.IxiaResourceHelper(mock.Mock()) + ixia_resource_helper.client = mock_client + ixia_resource_helper._ix_scenario = mock.Mock() + ixia_resource_helper.stop_collect() + self.assertEqual(1, ixia_resource_helper._terminated.value) + ixia_resource_helper._ix_scenario.stop_protocols.assert_called_once() + + def test_run_traffic(self): + mock_tprofile = mock.Mock() + mock_tprofile.config.duration = 10 + mock_tprofile.get_drop_percentage.return_value = True, 'fake_samples' + ixia_rhelper = tg_rfc2544_ixia.IxiaResourceHelper(mock.Mock()) + ixia_rhelper.rfc_helper = mock.Mock() + ixia_rhelper.vnfd_helper = mock.Mock() + ixia_rhelper._ix_scenario = mock.Mock() + ixia_rhelper.vnfd_helper.port_pairs.all_ports = [] + with mock.patch.object(ixia_rhelper, 'generate_samples'), \ + mock.patch.object(ixia_rhelper, '_build_ports'), \ + mock.patch.object(ixia_rhelper, '_initialize_client'), \ + mock.patch.object(utils, 'wait_until_true'): + ixia_rhelper.run_traffic(mock_tprofile) + + self.assertEqual('fake_samples', ixia_rhelper._queue.get()) + mock_tprofile.update_traffic_profile.assert_called_once() + + def test_run_test(self): + expected_result = {'test': 'fake_samples', 'Iteration': 1} + mock_tprofile = mock.Mock() + mock_tprofile.config.duration = 10 + mock_tprofile.get_drop_percentage.return_value = \ + True, {'test': 'fake_samples', 'Iteration': 1} + ixia_rhelper = tg_rfc2544_ixia.IxiaResourceHelper(mock.Mock()) + tasks_queue = mock.Mock() + tasks_queue.get.return_value = 'RUN_TRAFFIC' + results_queue = mock.Mock() + ixia_rhelper.rfc_helper = mock.Mock() + ixia_rhelper.vnfd_helper = mock.Mock() + ixia_rhelper._ix_scenario = mock.Mock() + ixia_rhelper.vnfd_helper.port_pairs.all_ports = [] + with mock.patch.object(ixia_rhelper, 'generate_samples'), \ + mock.patch.object(ixia_rhelper, '_build_ports'), \ + mock.patch.object(ixia_rhelper, '_initialize_client'), \ + mock.patch.object(utils, 'wait_until_true'): + ixia_rhelper.run_test(mock_tprofile, tasks_queue, results_queue) + + self.assertEqual(expected_result, ixia_rhelper._queue.get()) + mock_tprofile.update_traffic_profile.assert_called_once() + tasks_queue.task_done.assert_called_once() + results_queue.put.assert_called_once_with('COMPLETE') + + +@mock.patch.object(tg_rfc2544_ixia, 'ixnet_api') +class TestIXIATrafficGen(unittest.TestCase): + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{'short-name': 'VpeVnf', + 'vdu': + [{'routing_table': + [{'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0'}, + {'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1'}], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': + [{'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'id': 'vpevnf-baremetal', + 'external-interface': + [{'virtual-interface': + {'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02'}, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01'}, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'}]}], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': + {'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1'}, + 'benchmark': + {'kpi': ['packets_in', 'packets_fwd', + 'packets_dropped']}, + 'connection-point': [{'type': 'VPORT', 'name': 'xe0'}, + {'type': 'VPORT', 'name': 'xe1'}], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh'}]}} + + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64}} + + TC_YAML = {'scenarios': [{'tc_options': + {'rfc2544': {'allowed_drop_rate': '0.8 - 1'}}, + 'runner': {'duration': 400, + 'interval': 35, 'type': 'Duration'}, + 'traffic_options': + {'flow': 'ipv4_1flow_Packets_vpe.yaml', + 'imix': 'imix_voice.yaml'}, + 'vnf_options': {'vpe': {'cfg': 'vpe_config'}}, + 'traffic_profile': 'ipv4_throughput_vpe.yaml', + 'type': 'NSPerf', + 'nodes': {'tg__1': 'trafficgen_1.yardstick', + 'vnf__1': 'vnf.yardstick'}, + 'topology': 'vpe_vnf_topology.yaml'}], + 'context': {'nfvi_type': 'baremetal', + 'type': contexts.CONTEXT_NODE, + 'name': 'yardstick', + 'file': '/etc/yardstick/nodes/pod.yaml'}, + 'schema': 'yardstick:task:0.1'} + + def test___init__(self, *args): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "", "")) + ssh.from_node.return_value = ssh_mock + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + # NOTE(ralonsoh): check the object returned. + tg_rfc2544_ixia.IxiaTrafficGen(NAME, vnfd) + + def test_listen_traffic(self, *args): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "", "")) + ssh.from_node.return_value = ssh_mock + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + ixnet_traffic_gen = tg_rfc2544_ixia.IxiaTrafficGen(NAME, vnfd) + self.assertIsNone(ixnet_traffic_gen.listen_traffic({})) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', return_value='fake_context') + def test_instantiate(self, *args): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "", "")) + ssh_mock.run = \ + mock.Mock(return_value=(0, "", "")) + ssh.from_node.return_value = ssh_mock + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + ixnet_traffic_gen = tg_rfc2544_ixia.IxiaTrafficGen(NAME, vnfd) + scenario_cfg = {'tc': "nsb_test_case", + "topology": ""} + scenario_cfg.update( + { + 'options': { + 'packetsize': 64, + 'traffic_type': 4, + 'rfc2544': { + 'allowed_drop_rate': '0.8 - 1'}, + 'vnf__1': { + 'rules': 'acl_1rule.yaml', + 'vnf_config': { + 'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1}}}}) + scenario_cfg.update({ + 'nodes': {ixnet_traffic_gen.name: "mock"} + }) + ixnet_traffic_gen.topology = "" + ixnet_traffic_gen.get_ixobj = mock.MagicMock() + ixnet_traffic_gen._ixia_traffic_gen = mock.MagicMock() + ixnet_traffic_gen._ixia_traffic_gen._connect = mock.Mock() + self.assertRaises( + IOError, + ixnet_traffic_gen.instantiate(scenario_cfg, {})) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + def test_collect_kpi(self, *args): + with mock.patch("yardstick.ssh.SSH") as ssh: + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "", "")) + ssh.from_node.return_value = ssh_mock + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + ixnet_traffic_gen = tg_rfc2544_ixia.IxiaTrafficGen(NAME, vnfd) + ixnet_traffic_gen.scenario_helper.scenario_cfg = { + 'nodes': {ixnet_traffic_gen.name: "mock"} + } + ixnet_traffic_gen.data = {} + restult = ixnet_traffic_gen.collect_kpi() + + expected = {'collect_stats': {}, + 'physical_node': 'mock_node'} + + self.assertEqual(expected, restult) + + def test_terminate(self, *args): + with mock.patch("yardstick.ssh.SSH") as ssh: + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + ssh_mock = mock.Mock(autospec=ssh.SSH) + ssh_mock.execute = \ + mock.Mock(return_value=(0, "", "")) + ssh.from_node.return_value = ssh_mock + ixnet_traffic_gen = tg_rfc2544_ixia.IxiaTrafficGen( + NAME, vnfd, resource_helper_type=mock.Mock()) + ixnet_traffic_gen._terminated = mock.MagicMock() + ixnet_traffic_gen._terminated.value = 0 + ixnet_traffic_gen._ixia_traffic_gen = mock.MagicMock() + ixnet_traffic_gen._ixia_traffic_gen.ix_stop_traffic = mock.Mock() + ixnet_traffic_gen._traffic_process = mock.MagicMock() + ixnet_traffic_gen._traffic_process.terminate = mock.Mock() + self.assertIsNone(ixnet_traffic_gen.terminate()) + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + def test__check_status(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + sut = tg_rfc2544_ixia.IxiaTrafficGen('vnf1', vnfd) + sut._check_status() + + @mock.patch("yardstick.ssh.SSH") + def test_traffic_runner(self, mock_ssh, *args): + mock_traffic_profile = mock.Mock(autospec=tp_base.TrafficProfile) + mock_traffic_profile.get_traffic_definition.return_value = "64" + mock_traffic_profile.params = self.TRAFFIC_PROFILE + # traffic_profile.ports is standardized on port_num + mock_traffic_profile.ports = [0, 1] + + mock_ssh_instance = mock.Mock(autospec=mock_ssh.SSH) + mock_ssh_instance.execute.return_value = 0, "", "" + mock_ssh_instance.run.return_value = 0, "", "" + + mock_ssh.from_node.return_value = mock_ssh_instance + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vnfd["mgmt-interface"].update({ + 'tg-config': { + "ixchassis": "1.1.1.1", + "py_bin_path": "/root", + } + }) + + samples = {} + name = '' + for ifname in range(1): + name = "xe{}".format(ifname) + samples[name] = { + "Rx_Rate_Kbps": 20, + "Tx_Rate_Kbps": 20, + "Rx_Rate_Mbps": 10, + "Tx_Rate_Mbps": 10, + "RxThroughput": 10, + "TxThroughput": 10, + "Valid_Frames_Rx": 1000, + "Frames_Tx": 1000, + "in_packets": 1000, + "out_packets": 1000, + } + + samples.update({"CurrentDropPercentage": 0.0}) + + last_res = [ + 0, + { + "Rx_Rate_Kbps": [20, 20], + "Tx_Rate_Kbps": [20, 20], + "Rx_Rate_Mbps": [10, 10], + "Tx_Rate_Mbps": [10, 10], + "CurrentDropPercentage": [0, 0], + "RxThroughput": [10, 10], + "TxThroughput": [10, 10], + "Frames_Tx": [1000, 1000], + "in_packets": [1000, 1000], + "Valid_Frames_Rx": [1000, 1000], + "out_packets": [1000, 1000], + }, + ] + + mock_traffic_profile.execute_traffic.return_value = [ + 'Completed', samples] + mock_traffic_profile.get_drop_percentage.return_value = [ + 'Completed', samples] + + sut = tg_rfc2544_ixia.IxiaTrafficGen(name, vnfd) + sut.vnf_port_pairs = [[[0], [1]]] + sut.tc_file_name = self._get_file_abspath(TEST_FILE_YAML) + sut.topology = "" + + sut.ssh_helper = mock.Mock() + sut._traffic_process = mock.MagicMock() + sut.generate_port_pairs = mock.Mock() + + sut._ixia_traffic_gen = mock.MagicMock() + sut._ixia_traffic_gen.ix_get_statistics.return_value = last_res + + sut.resource_helper.client = mock.MagicMock() + sut.resource_helper.client_started = mock.MagicMock() + sut.resource_helper.client_started.value = 1 + sut.resource_helper.rfc_helper.iteration.value = 11 + sut.resource_helper._ix_scenario = mock.Mock() + + sut.scenario_helper.scenario_cfg = { + 'options': { + 'packetsize': 64, + 'traffic_type': 4, + 'rfc2544': { + 'allowed_drop_rate': '0.8 - 1', + 'latency': True + }, + 'vnf__1': { + 'rules': 'acl_1rule.yaml', + 'vnf_config': { + 'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1, + }, + }, + }, + 'task_path': '/path/to/task' + } + + @mock.patch.object(six.moves.builtins, 'open', create=True) + @mock.patch('yardstick.network_services.vnf_generic.vnf.tg_rfc2544_ixia.open', + mock.mock_open(), create=True) + @mock.patch('yardstick.network_services.vnf_generic.vnf.tg_rfc2544_ixia.LOG.exception') + def _traffic_runner(*args): + result = sut._traffic_runner(mock_traffic_profile) + self.assertIsNone(result) + + _traffic_runner() + + def test_run_traffic_once(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + sut = tg_rfc2544_ixia.IxiaTrafficGen('vnf1', vnfd) + sut._init_traffic_process = mock.Mock() + sut._tasks_queue.put = mock.Mock() + sut.resource_helper.client_started.value = 0 + sut.run_traffic_once(self.TRAFFIC_PROFILE) + sut._tasks_queue.put.assert_called_once_with("RUN_TRAFFIC") + sut._init_traffic_process.assert_called_once_with(self.TRAFFIC_PROFILE) + + def test__test_runner(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + sut = tg_rfc2544_ixia.IxiaTrafficGen('vnf1', vnfd) + tasks = 'tasks' + results = 'results' + sut.resource_helper = mock.Mock() + sut._test_runner(self.TRAFFIC_PROFILE, tasks, results) + sut.resource_helper.run_test.assert_called_once_with(self.TRAFFIC_PROFILE, + tasks, results) + + @mock.patch.object(time, 'sleep', return_value=0) + def test__init_traffic_process(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + sut = tg_rfc2544_ixia.IxiaTrafficGen('vnf1', vnfd) + sut._test_runner = mock.Mock(return_value=0) + sut.resource_helper = mock.Mock() + sut.resource_helper.client_started.value = 0 + sut._init_traffic_process(self.TRAFFIC_PROFILE) + + def test_wait_on_traffic(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + sut = tg_rfc2544_ixia.IxiaTrafficGen('vnf1', vnfd) + sut._tasks_queue.join = mock.Mock(return_value=0) + sut._result_queue.get = mock.Mock(return_value='COMPLETE') + result = sut.wait_on_traffic() + sut._tasks_queue.join.assert_called_once() + sut._result_queue.get.assert_called_once() + self.assertEqual(result, 'COMPLETE') + + +class TestIxiaBasicScenario(unittest.TestCase): + + STATS = {'stat_name': ['Card01/Port01', + 'Card02/Port02'], + 'port_name': ['Ethernet - 001', 'Ethernet - 002'], + 'Frames_Tx': ['150', '150'], + 'Valid_Frames_Rx': ['150', '150'], + 'Frames_Tx_Rate': ['0.0', '0.0'], + 'Valid_Frames_Rx_Rate': ['0.0', '0.0'], + 'Bytes_Rx': ['9600', '9600'], + 'Bytes_Tx': ['9600', '9600'], + 'Tx_Rate_Kbps': ['0.0', '0.0'], + 'Rx_Rate_Mbps': ['0.0', '0.0'], + 'Tx_Rate_Mbps': ['0.0', '0.0'], + 'Rx_Rate_Kbps': ['0.0', '0.0'], + 'Store-Forward_Max_latency_ns': ['100', '200'], + 'Store-Forward_Min_latency_ns': ['100', '200'], + 'Store-Forward_Avg_latency_ns': ['100', '200']} + + def setUp(self): + self._mock_IxNextgen = mock.patch.object(ixnet_api, 'IxNextgen') + self.mock_IxNextgen = self._mock_IxNextgen.start() + self.context_cfg = mock.Mock() + self.ixia_cfg = mock.Mock() + self.scenario = tg_rfc2544_ixia.IxiaBasicScenario(self.mock_IxNextgen, + self.context_cfg, + self.ixia_cfg) + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_IxNextgen.stop() + + def test___init___(self): + self.assertIsInstance(self.scenario, tg_rfc2544_ixia.IxiaBasicScenario) + self.assertEqual(self.scenario.client, self.mock_IxNextgen) + + def test_create_traffic_model(self): + self.mock_IxNextgen.get_vports.return_value = [1, 2, 3, 4] + yaml_data = {'traffic_profile': {} + } + traffic_profile = ixia_rfc2544.IXIARFC2544Profile(yaml_data) + self.scenario.create_traffic_model(traffic_profile) + self.scenario.client.get_vports.assert_called_once() + self.scenario.client.create_traffic_model.assert_called_once_with( + [1, 3], [2, 4], traffic_profile) + + def test_apply_config(self): + self.assertIsNone(self.scenario.apply_config()) + + def test_run_protocols(self): + self.assertIsNone(self.scenario.run_protocols()) + + def test_stop_protocols(self): + self.assertIsNone(self.scenario.stop_protocols()) + + def test__get_stats(self): + self.scenario._get_stats() + self.scenario.client.get_statistics.assert_called_once() + + @mock.patch.object(tg_rfc2544_ixia.IxiaBasicScenario, '_get_stats') + def test_generate_samples(self, mock_get_stats): + + expected_samples = {'xe0': { + 'InPackets': 150, + 'OutPackets': 150, + 'InBytes': 9600, + 'OutBytes': 9600, + 'RxThroughput': 5.0, + 'TxThroughput': 5.0, + 'RxThroughputBps': 320.0, + 'TxThroughputBps': 320.0, + 'LatencyMax': 100, + 'LatencyMin': 100, + 'LatencyAvg': 100}, + 'xe1': { + 'InPackets': 150, + 'OutPackets': 150, + 'InBytes': 9600, + 'OutBytes': 9600, + 'RxThroughput': 5.0, + 'TxThroughput': 5.0, + 'RxThroughputBps': 320.0, + 'TxThroughputBps': 320.0, + 'LatencyMax': 200, + 'LatencyMin': 200, + 'LatencyAvg': 200}} + + res_helper = mock.Mock() + res_helper.vnfd_helper.find_interface_by_port.side_effect = \ + [{'name': 'xe0'}, {'name': 'xe1'}] + ports = [0, 1] + duration = 30 + mock_get_stats.return_value = self.STATS + samples = self.scenario.generate_samples(res_helper, ports, duration) + mock_get_stats.assert_called_once() + self.assertEqual(samples, expected_samples) + + +class TestIxiaL3Scenario(TestIxiaBasicScenario): + IXIA_CFG = { + 'flow': { + 'src_ip': ['192.168.0.1-192.168.0.50'], + 'dst_ip': ['192.168.1.1-192.168.1.150'] + } + } + + CONTEXT_CFG = { + 'nodes': { + 'tg__0': { + 'role': 'IxNet', + 'interfaces': { + 'xe0': { + 'vld_id': 'uplink_0', + 'local_ip': '10.1.1.1', + 'local_mac': 'aa:bb:cc:dd:ee:ff', + 'ifname': 'xe0' + }, + 'xe1': { + 'vld_id': 'downlink_0', + 'local_ip': '20.2.2.2', + 'local_mac': 'bb:bb:cc:dd:ee:ee', + 'ifname': 'xe1' + } + }, + 'routing_table': [{ + 'network': "152.16.100.20", + 'netmask': '255.255.0.0', + 'gateway': '152.16.100.21', + 'if': 'xe0' + }] + } + } + } + + def setUp(self): + super(TestIxiaL3Scenario, self).setUp() + self.ixia_cfg = self.IXIA_CFG + self.context_cfg = self.CONTEXT_CFG + self.scenario = tg_rfc2544_ixia.IxiaL3Scenario(self.mock_IxNextgen, + self.context_cfg, + self.ixia_cfg) + + def test___init___(self): + self.assertIsInstance(self.scenario, tg_rfc2544_ixia.IxiaL3Scenario) + self.assertEqual(self.scenario.client, self.mock_IxNextgen) + + def test_create_traffic_model(self): + self.mock_IxNextgen.get_vports.return_value = ['1', '2'] + traffic_profile = 'fake_profile' + self.scenario.create_traffic_model(traffic_profile) + self.scenario.client.get_vports.assert_called_once() + self.scenario.client.create_ipv4_traffic_model.\ + assert_called_once_with(['1/protocols/static'], + ['2/protocols/static'], + 'fake_profile') + + def test_apply_config(self): + self.scenario._add_interfaces = mock.Mock() + self.scenario._add_static_ips = mock.Mock() + self.assertIsNone(self.scenario.apply_config()) + + def test__add_static(self): + self.mock_IxNextgen.get_vports.return_value = ['1', '2'] + self.mock_IxNextgen.get_static_interface.side_effect = ['intf1', + 'intf2'] + + self.scenario._add_static_ips() + + self.mock_IxNextgen.get_static_interface.assert_any_call('1') + self.mock_IxNextgen.get_static_interface.assert_any_call('2') + + self.scenario.client.add_static_ipv4.assert_any_call( + 'intf1', '1', '192.168.0.1', 49, '32') + self.scenario.client.add_static_ipv4.assert_any_call( + 'intf2', '2', '192.168.1.1', 149, '32') + + def test__add_interfaces(self): + self.mock_IxNextgen.get_vports.return_value = ['1', '2'] + + self.scenario._add_interfaces() + + self.mock_IxNextgen.add_interface.assert_any_call('1', + '10.1.1.1', + 'aa:bb:cc:dd:ee:ff', + '152.16.100.21') + self.mock_IxNextgen.add_interface.assert_any_call('2', + '20.2.2.2', + 'bb:bb:cc:dd:ee:ee', + None) + + +class TestIxiaPppoeClientScenario(unittest.TestCase): + + IXIA_CFG = { + 'pppoe_client': { + 'sessions_per_port': 4, + 'sessions_per_svlan': 1, + 's_vlan': 10, + 'c_vlan': 20, + 'ip': ['10.3.3.1', '10.4.4.1'] + }, + 'ipv4_client': { + 'sessions_per_port': 1, + 'sessions_per_vlan': 1, + 'vlan': 101, + 'gateway_ip': ['10.1.1.1', '10.2.2.1'], + 'ip': ['10.1.1.1', '10.2.2.1'], + 'prefix': ['24', '24'] + }, + 'priority': { + 'tos': {'precedence': [0, 4]} + } + } + + CONTEXT_CFG = { + 'nodes': {'tg__0': { + 'interfaces': {'xe0': { + 'local_ip': '10.1.1.1', + 'netmask': '255.255.255.0' + }}}}} + + def setUp(self): + self._mock_IxNextgen = mock.patch.object(ixnet_api, 'IxNextgen') + self.mock_IxNextgen = self._mock_IxNextgen.start() + self.scenario = tg_rfc2544_ixia.IxiaPppoeClientScenario( + self.mock_IxNextgen, self.CONTEXT_CFG, self.IXIA_CFG) + tg_rfc2544_ixia.WAIT_PROTOCOLS_STARTED = 2 + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_IxNextgen.stop() + + def test___init___(self): + self.assertIsInstance(self.scenario, tg_rfc2544_ixia.IxiaPppoeClientScenario) + self.assertEqual(self.scenario.client, self.mock_IxNextgen) + + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, + '_fill_ixia_config') + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, + '_apply_access_network_config') + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, + '_apply_core_network_config') + def test_apply_config(self, mock_apply_core_net_cfg, + mock_apply_access_net_cfg, + mock_fill_ixia_config): + self.mock_IxNextgen.get_vports.return_value = [1, 2, 3, 4] + self.scenario.apply_config() + self.scenario.client.get_vports.assert_called_once() + self.assertEqual(self.scenario._uplink_vports, [1, 3]) + self.assertEqual(self.scenario._downlink_vports, [2, 4]) + mock_fill_ixia_config.assert_called_once() + mock_apply_core_net_cfg.assert_called_once() + mock_apply_access_net_cfg.assert_called_once() + + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, + '_get_endpoints_src_dst_id_pairs') + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, + '_get_endpoints_src_dst_obj_pairs') + def test_create_traffic_model(self, mock_obj_pairs, mock_id_pairs): + uplink_endpoints = ['group1', 'group2'] + downlink_endpoints = ['group3', 'group3'] + mock_id_pairs.return_value = ['xe0', 'xe1', 'xe0', 'xe1'] + mock_obj_pairs.return_value = ['group1', 'group3', 'group2', 'group3'] + mock_tp = mock.Mock() + mock_tp.full_profile = {'uplink_0': 'data', + 'downlink_0': 'data', + 'uplink_1': 'data', + 'downlink_1': 'data' + } + self.scenario.create_traffic_model(mock_tp) + mock_id_pairs.assert_called_once_with(mock_tp.full_profile) + mock_obj_pairs.assert_called_once_with(['xe0', 'xe1', 'xe0', 'xe1']) + self.scenario.client.create_ipv4_traffic_model.assert_called_once_with( + uplink_endpoints, downlink_endpoints, mock_tp) + + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, + '_get_endpoints_src_dst_id_pairs') + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, + '_get_endpoints_src_dst_obj_pairs') + def test_create_traffic_model_topology_based_flows(self, mock_obj_pairs, + mock_id_pairs): + uplink_topologies = ['topology1', 'topology3'] + downlink_topologies = ['topology2', 'topology4'] + mock_id_pairs.return_value = [] + mock_obj_pairs.return_value = [] + mock_tp = mock.Mock() + mock_tp.full_profile = {'uplink_0': 'data', + 'downlink_0': 'data', + 'uplink_1': 'data', + 'downlink_1': 'data' + } + self.scenario._access_topologies = ['topology1', 'topology3'] + self.scenario._core_topologies = ['topology2', 'topology4'] + self.scenario.create_traffic_model(mock_tp) + mock_id_pairs.assert_called_once_with(mock_tp.full_profile) + mock_obj_pairs.assert_called_once_with([]) + self.scenario.client.create_ipv4_traffic_model.assert_called_once_with( + uplink_topologies, downlink_topologies, mock_tp) + + def test__get_endpoints_src_dst_id_pairs(self): + full_tp = OrderedDict([ + ('uplink_0', {'ipv4': {'port': 'xe0'}}), + ('downlink_0', {'ipv4': {'port': 'xe1'}}), + ('uplink_1', {'ipv4': {'port': 'xe0'}}), + ('downlink_1', {'ipv4': {'port': 'xe3'}})]) + endpoints_src_dst_pairs = ['xe0', 'xe1', 'xe0', 'xe3'] + res = self.scenario._get_endpoints_src_dst_id_pairs(full_tp) + self.assertEqual(res, endpoints_src_dst_pairs) + + def test__get_endpoints_src_dst_id_pairs_wrong_flows_number(self): + full_tp = OrderedDict([ + ('uplink_0', {'ipv4': {'port': 'xe0'}}), + ('downlink_0', {'ipv4': {'port': 'xe1'}}), + ('uplink_1', {'ipv4': {'port': 'xe0'}})]) + with self.assertRaises(RuntimeError): + self.scenario._get_endpoints_src_dst_id_pairs(full_tp) + + def test__get_endpoints_src_dst_id_pairs_no_port_key(self): + full_tp = OrderedDict([ + ('uplink_0', {'ipv4': {'id': 1}}), + ('downlink_0', {'ipv4': {'id': 2}})]) + self.assertEqual( + self.scenario._get_endpoints_src_dst_id_pairs(full_tp), []) + + def test__get_endpoints_src_dst_obj_pairs_tp_with_port_key(self): + endpoints_id_pairs = ['xe0', 'xe1', + 'xe0', 'xe1', + 'xe0', 'xe3', + 'xe0', 'xe3'] + ixia_cfg = { + 'pppoe_client': { + 'sessions_per_port': 4, + 'sessions_per_svlan': 1 + }, + 'flow': { + 'src_ip': [{'tg__0': 'xe0'}, {'tg__0': 'xe2'}], + 'dst_ip': [{'tg__0': 'xe1'}, {'tg__0': 'xe3'}] + } + } + + expected_result = ['tp1_dg1', 'tp3_dg1', 'tp1_dg2', 'tp3_dg1', + 'tp1_dg3', 'tp4_dg1', 'tp1_dg4', 'tp4_dg1'] + + self.scenario._ixia_cfg = ixia_cfg + self.scenario._access_topologies = ['topology1', 'topology2'] + self.scenario._core_topologies = ['topology3', 'topology4'] + self.mock_IxNextgen.get_topology_device_groups.side_effect = \ + [['tp1_dg1', 'tp1_dg2', 'tp1_dg3', 'tp1_dg4'], + ['tp2_dg1', 'tp2_dg2', 'tp2_dg3', 'tp2_dg4'], + ['tp3_dg1'], + ['tp4_dg1']] + res = self.scenario._get_endpoints_src_dst_obj_pairs( + endpoints_id_pairs) + self.assertEqual(res, expected_result) + + def test__get_endpoints_src_dst_obj_pairs_default_flows_mapping(self): + endpoints_id_pairs = [] + ixia_cfg = { + 'pppoe_client': { + 'sessions_per_port': 4, + 'sessions_per_svlan': 1 + }, + 'flow': { + 'src_ip': [{'tg__0': 'xe0'}, {'tg__0': 'xe2'}], + 'dst_ip': [{'tg__0': 'xe1'}, {'tg__0': 'xe3'}] + } + } + + self.scenario._ixia_cfg = ixia_cfg + res = self.scenario._get_endpoints_src_dst_obj_pairs( + endpoints_id_pairs) + self.assertEqual(res, []) + + def test_run_protocols(self): + self.scenario.client.is_protocols_running.return_value = True + self.scenario.run_protocols() + self.scenario.client.start_protocols.assert_called_once() + + def test_run_protocols_timeout_exception(self): + self.scenario.client.is_protocols_running.return_value = False + with self.assertRaises(exceptions.WaitTimeout): + self.scenario.run_protocols() + self.scenario.client.start_protocols.assert_called_once() + + def test_stop_protocols(self): + self.scenario.stop_protocols() + self.scenario.client.stop_protocols.assert_called_once() + + def test__get_intf_addr_str_type_input(self): + intf = '192.168.10.2/24' + ip, mask = self.scenario._get_intf_addr(intf) + self.assertEqual(ip, '192.168.10.2') + self.assertEqual(mask, 24) + + def test__get_intf_addr_dict_type_input(self): + intf = {'tg__0': 'xe0'} + ip, mask = self.scenario._get_intf_addr(intf) + self.assertEqual(ip, '10.1.1.1') + self.assertEqual(mask, 24) + + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, '_get_intf_addr') + def test__fill_ixia_config(self, mock_get_intf_addr): + + ixia_cfg = { + 'pppoe_client': { + 'sessions_per_port': 4, + 'sessions_per_svlan': 1, + 's_vlan': 10, + 'c_vlan': 20, + 'ip': ['10.3.3.1/24', '10.4.4.1/24'] + }, + 'ipv4_client': { + 'sessions_per_port': 1, + 'sessions_per_vlan': 1, + 'vlan': 101, + 'gateway_ip': ['10.1.1.1/24', '10.2.2.1/24'], + 'ip': ['10.1.1.1/24', '10.2.2.1/24'] + } + } + + mock_get_intf_addr.side_effect = [ + ('10.3.3.1', '24'), + ('10.4.4.1', '24'), + ('10.1.1.1', '24'), + ('10.2.2.1', '24'), + ('10.1.1.1', '24'), + ('10.2.2.1', '24') + ] + self.scenario._ixia_cfg = ixia_cfg + self.scenario._fill_ixia_config() + self.assertEqual(mock_get_intf_addr.call_count, 6) + self.assertEqual(self.scenario._ixia_cfg['pppoe_client']['ip'], + ['10.3.3.1', '10.4.4.1']) + self.assertEqual(self.scenario._ixia_cfg['ipv4_client']['ip'], + ['10.1.1.1', '10.2.2.1']) + self.assertEqual(self.scenario._ixia_cfg['ipv4_client']['prefix'], + ['24', '24']) + + @mock.patch('yardstick.network_services.libs.ixia_libs.ixnet.ixnet_api.Vlan') + def test__apply_access_network_config_pap_auth(self, mock_vlan): + _ixia_cfg = { + 'pppoe_client': { + 'sessions_per_port': 4, + 'sessions_per_svlan': 1, + 's_vlan': 10, + 'c_vlan': 20, + 'pap_user': 'test_pap', + 'pap_password': 'pap' + }} + pap_user = _ixia_cfg['pppoe_client']['pap_user'] + pap_passwd = _ixia_cfg['pppoe_client']['pap_password'] + self.scenario._ixia_cfg = _ixia_cfg + self.scenario._uplink_vports = [0, 2] + self.scenario.client.add_topology.side_effect = ['Topology 1', 'Topology 2'] + self.scenario.client.add_device_group.side_effect = ['Dg1', 'Dg2', 'Dg3', + 'Dg4', 'Dg5', 'Dg6', + 'Dg7', 'Dg8'] + self.scenario.client.add_ethernet.side_effect = ['Eth1', 'Eth2', 'Eth3', + 'Eth4', 'Eth5', 'Eth6', + 'Eth7', 'Eth8'] + self.scenario._apply_access_network_config() + self.assertEqual(self.scenario.client.add_topology.call_count, 2) + self.assertEqual(self.scenario.client.add_device_group.call_count, 8) + self.assertEqual(self.scenario.client.add_ethernet.call_count, 8) + self.assertEqual(mock_vlan.call_count, 16) + self.assertEqual(self.scenario.client.add_vlans.call_count, 8) + self.assertEqual(self.scenario.client.add_pppox_client.call_count, 8) + self.scenario.client.add_topology.assert_has_calls([ + mock.call('Topology access 0', 0), + mock.call('Topology access 1', 2) + ]) + self.scenario.client.add_device_group.assert_has_calls([ + mock.call('Topology 1', 'SVLAN 10', 1), + mock.call('Topology 1', 'SVLAN 11', 1), + mock.call('Topology 1', 'SVLAN 12', 1), + mock.call('Topology 1', 'SVLAN 13', 1), + mock.call('Topology 2', 'SVLAN 14', 1), + mock.call('Topology 2', 'SVLAN 15', 1), + mock.call('Topology 2', 'SVLAN 16', 1), + mock.call('Topology 2', 'SVLAN 17', 1) + ]) + self.scenario.client.add_ethernet.assert_has_calls([ + mock.call('Dg1', 'Ethernet'), + mock.call('Dg2', 'Ethernet'), + mock.call('Dg3', 'Ethernet'), + mock.call('Dg4', 'Ethernet'), + mock.call('Dg5', 'Ethernet'), + mock.call('Dg6', 'Ethernet'), + mock.call('Dg7', 'Ethernet'), + mock.call('Dg8', 'Ethernet') + ]) + mock_vlan.assert_has_calls([ + mock.call(vlan_id=10), + mock.call(vlan_id=20, vlan_id_step=1), + mock.call(vlan_id=11), + mock.call(vlan_id=20, vlan_id_step=1), + mock.call(vlan_id=12), + mock.call(vlan_id=20, vlan_id_step=1), + mock.call(vlan_id=13), + mock.call(vlan_id=20, vlan_id_step=1), + mock.call(vlan_id=14), + mock.call(vlan_id=20, vlan_id_step=1), + mock.call(vlan_id=15), + mock.call(vlan_id=20, vlan_id_step=1), + mock.call(vlan_id=16), + mock.call(vlan_id=20, vlan_id_step=1), + mock.call(vlan_id=17), + mock.call(vlan_id=20, vlan_id_step=1) + ]) + self.scenario.client.add_pppox_client.assert_has_calls([ + mock.call('Eth1', 'pap', pap_user, pap_passwd), + mock.call('Eth2', 'pap', pap_user, pap_passwd), + mock.call('Eth3', 'pap', pap_user, pap_passwd), + mock.call('Eth4', 'pap', pap_user, pap_passwd), + mock.call('Eth5', 'pap', pap_user, pap_passwd), + mock.call('Eth6', 'pap', pap_user, pap_passwd), + mock.call('Eth7', 'pap', pap_user, pap_passwd), + mock.call('Eth8', 'pap', pap_user, pap_passwd) + ]) + + def test__apply_access_network_config_chap_auth(self): + _ixia_cfg = { + 'pppoe_client': { + 'sessions_per_port': 4, + 'sessions_per_svlan': 1, + 's_vlan': 10, + 'c_vlan': 20, + 'chap_user': 'test_chap', + 'chap_password': 'chap' + }} + chap_user = _ixia_cfg['pppoe_client']['chap_user'] + chap_passwd = _ixia_cfg['pppoe_client']['chap_password'] + self.scenario._ixia_cfg = _ixia_cfg + self.scenario._uplink_vports = [0, 2] + self.scenario.client.add_ethernet.side_effect = ['Eth1', 'Eth2', 'Eth3', + 'Eth4', 'Eth5', 'Eth6', + 'Eth7', 'Eth8'] + self.scenario._apply_access_network_config() + self.assertEqual(self.scenario.client.add_pppox_client.call_count, 8) + self.scenario.client.add_pppox_client.assert_has_calls([ + mock.call('Eth1', 'chap', chap_user, chap_passwd), + mock.call('Eth2', 'chap', chap_user, chap_passwd), + mock.call('Eth3', 'chap', chap_user, chap_passwd), + mock.call('Eth4', 'chap', chap_user, chap_passwd), + mock.call('Eth5', 'chap', chap_user, chap_passwd), + mock.call('Eth6', 'chap', chap_user, chap_passwd), + mock.call('Eth7', 'chap', chap_user, chap_passwd), + mock.call('Eth8', 'chap', chap_user, chap_passwd) + ]) + + @mock.patch('yardstick.network_services.libs.ixia_libs.ixnet.ixnet_api.Vlan') + def test__apply_core_network_config_no_bgp_proto(self, mock_vlan): + self.scenario._downlink_vports = [1, 3] + self.scenario.client.add_topology.side_effect = ['Topology 1', 'Topology 2'] + self.scenario.client.add_device_group.side_effect = ['Dg1', 'Dg2'] + self.scenario.client.add_ethernet.side_effect = ['Eth1', 'Eth2'] + self.scenario._apply_core_network_config() + self.assertEqual(self.scenario.client.add_topology.call_count, 2) + self.assertEqual(self.scenario.client.add_device_group.call_count, 2) + self.assertEqual(self.scenario.client.add_ethernet.call_count, 2) + self.assertEqual(mock_vlan.call_count, 2) + self.assertEqual(self.scenario.client.add_vlans.call_count, 2) + self.assertEqual(self.scenario.client.add_ipv4.call_count, 2) + self.scenario.client.add_topology.assert_has_calls([ + mock.call('Topology core 0', 1), + mock.call('Topology core 1', 3) + ]) + self.scenario.client.add_device_group.assert_has_calls([ + mock.call('Topology 1', 'Core port 0', 1), + mock.call('Topology 2', 'Core port 1', 1) + ]) + self.scenario.client.add_ethernet.assert_has_calls([ + mock.call('Dg1', 'Ethernet'), + mock.call('Dg2', 'Ethernet') + ]) + mock_vlan.assert_has_calls([ + mock.call(vlan_id=101), + mock.call(vlan_id=102) + ]) + self.scenario.client.add_ipv4.assert_has_calls([ + mock.call('Eth1', name='ipv4', addr=ipaddress.IPv4Address('10.1.1.2'), + addr_step='0.0.0.1', prefix='24', gateway='10.1.1.1'), + mock.call('Eth2', name='ipv4', addr=ipaddress.IPv4Address('10.2.2.2'), + addr_step='0.0.0.1', prefix='24', gateway='10.2.2.1') + ]) + self.scenario.client.add_bgp.assert_not_called() + + def test__apply_core_network_config_with_bgp_proto(self): + bgp_params = { + 'bgp': { + 'bgp_type': 'external', + 'dut_ip': '10.0.0.1', + 'as_number': 65000 + } + } + self.scenario._ixia_cfg['ipv4_client'].update(bgp_params) + self.scenario._downlink_vports = [1, 3] + self.scenario.client.add_ipv4.side_effect = ['ipv4_1', 'ipv4_2'] + self.scenario._apply_core_network_config() + self.assertEqual(self.scenario.client.add_bgp.call_count, 2) + self.scenario.client.add_bgp.assert_has_calls([ + mock.call('ipv4_1', dut_ip=bgp_params["bgp"]["dut_ip"], + local_as=bgp_params["bgp"]["as_number"], + bgp_type=bgp_params["bgp"]["bgp_type"]), + mock.call('ipv4_2', dut_ip=bgp_params["bgp"]["dut_ip"], + local_as=bgp_params["bgp"]["as_number"], + bgp_type=bgp_params["bgp"]["bgp_type"]) + ]) + + def test_update_tracking_options_raw_priority(self): + raw_priority = {'raw': 4} + self.scenario._ixia_cfg['priority'] = raw_priority + self.scenario.update_tracking_options() + self.scenario.client.set_flow_tracking.assert_called_once_with( + ['flowGroup0', 'vlanVlanId0', 'ipv4Raw0']) + + def test_update_tracking_options_tos_priority(self): + tos_priority = {'tos': {'precedence': [4, 7]}} + self.scenario._ixia_cfg['priority'] = tos_priority + self.scenario.update_tracking_options() + self.scenario.client.set_flow_tracking.assert_called_once_with( + ['flowGroup0', 'vlanVlanId0', 'ipv4Precedence0']) + + def test_update_tracking_options_dscp_priority(self): + dscp_priority = {'dscp': {'defaultPHB': [4, 7]}} + self.scenario._ixia_cfg['priority'] = dscp_priority + self.scenario.update_tracking_options() + self.scenario.client.set_flow_tracking.assert_called_once_with( + ['flowGroup0', 'vlanVlanId0', 'ipv4DefaultPhb0']) + + def test_update_tracking_options_invalid_priority_data(self): + invalid_priority = {'tos': {'inet-precedence': [4, 7]}} + self.scenario._ixia_cfg['priority'] = invalid_priority + self.scenario.update_tracking_options() + self.scenario.client.set_flow_tracking.assert_called_once_with( + ['flowGroup0', 'vlanVlanId0', 'ipv4Precedence0']) + + def test_get_tc_rfc2544_options(self): + rfc2544_tc_opts = {'allowed_drop_rate': '0.0001 - 0.0001'} + self.scenario._ixia_cfg['rfc2544'] = rfc2544_tc_opts + res = self.scenario.get_tc_rfc2544_options() + self.assertEqual(res, rfc2544_tc_opts) + + def test__get_stats(self): + self.scenario._get_stats() + self.scenario.client.get_pppoe_scenario_statistics.assert_called_once() + + def test_get_flow_id_data(self): + stats = [{'id': 1, 'in_packets': 10, 'out_packets': 20}] + key = "in_packets" + flow_id = 1 + res = self.scenario.get_flow_id_data(stats, flow_id, key) + self.assertEqual(res, 10) + + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, '_get_stats') + @mock.patch.object(tg_rfc2544_ixia.IxiaPppoeClientScenario, + 'get_priority_flows_stats') + def test_generate_samples(self, mock_prio_flow_statistics, + mock_get_stats): + ixia_stats = { + 'flow_statistic': [ + {'Flow_Group': 'RFC2544-1 - Flow Group 0001', + 'Frames_Delta': '0', + 'IP_Priority': '0', + 'Rx_Frames': '3000', + 'Tx_Frames': '3000', + 'VLAN-ID': '100', + 'Tx_Port': 'Ethernet - 001', + 'Store-Forward_Avg_latency_ns': '2', + 'Store-Forward_Min_latency_ns': '2', + 'Store-Forward_Max_latency_ns': '2'}, + {'Flow_Group': 'RFC2544-2 - Flow Group 0001', + 'Frames_Delta': '0', + 'IP_Priority': '0', + 'Rx_Frames': '3000', + 'Tx_Frames': '3000', + 'VLAN-ID': '101', + 'Tx_Port': 'Ethernet - 002', + 'Store-Forward_Avg_latency_ns': '2', + 'Store-Forward_Min_latency_ns': '2', + 'Store-Forward_Max_latency_ns': '2' + }], + 'port_statistics': [ + {'Frames_Tx': '3000', + 'Valid_Frames_Rx': '3000', + 'Bytes_Rx': '192000', + 'Bytes_Tx': '192000', + 'Rx_Rate_Kbps': '0.0', + 'Tx_Rate_Kbps': '0.0', + 'Rx_Rate_Mbps': '0.0', + 'Tx_Rate_Mbps': '0.0', + 'port_name': 'Ethernet - 001'}, + {'Frames_Tx': '3000', + 'Valid_Frames_Rx': '3000', + 'Bytes_Rx': '192000', + 'Bytes_Tx': '192000', + 'Rx_Rate_Kbps': '0.0', + 'Tx_Rate_Kbps': '0.0', + 'Rx_Rate_Mbps': '0.0', + 'Tx_Rate_Mbps': '0.0', + 'port_name': 'Ethernet - 002'}], + 'pppox_client_per_port': [ + {'Sessions_Down': '0', + 'Sessions_Not_Started': '0', + 'Sessions_Total': '1', + 'Sessions_Up': '1', + 'subs_port': 'Ethernet - 001'}]} + + prio_flows_stats = { + '0': { + 'InPackets': 6000, + 'OutPackets': 6000, + 'RxThroughput': 200.0, + 'TxThroughput': 200.0, + 'LatencyAvg': 2, + 'LatencyMax': 2, + 'LatencyMin': 2 + } + } + + expected_result = {'priority_stats': { + '0': {'RxThroughput': 200.0, + 'TxThroughput': 200.0, + 'LatencyAvg': 2, + 'LatencyMax': 2, + 'LatencyMin': 2, + 'InPackets': 6000, + 'OutPackets': 6000}}, + 'xe0': {'RxThroughput': 100.0, + 'LatencyAvg': 2, + 'LatencyMax': 2, + 'LatencyMin': 2, + 'TxThroughput': 100.0, + 'InPackets': 3000, + 'OutPackets': 3000, + 'InBytes': 192000, + 'OutBytes': 192000, + 'RxThroughputBps': 6400.0, + 'TxThroughputBps': 6400.0, + 'SessionsDown': 0, + 'SessionsNotStarted': 0, + 'SessionsTotal': 1, + 'SessionsUp': 1}, + 'xe1': {'RxThroughput': 100.0, + 'LatencyAvg': 2, + 'LatencyMax': 2, + 'LatencyMin': 2, + 'TxThroughput': 100.0, + 'InPackets': 3000, + 'OutPackets': 3000, + 'InBytes': 192000, + 'OutBytes': 192000, + 'RxThroughputBps': 6400.0, + 'TxThroughputBps': 6400.0}} + + mock_get_stats.return_value = ixia_stats + mock_prio_flow_statistics.return_value = prio_flows_stats + ports = [0, 1] + port_names = [{'name': 'xe0'}, {'name': 'xe1'}] + duration = 30 + res_helper = mock.Mock() + res_helper.vnfd_helper.find_interface_by_port.side_effect = \ + port_names + samples = self.scenario.generate_samples(res_helper, ports, duration) + self.assertIsNotNone(samples) + self.assertIsNotNone(samples.get('xe0')) + self.assertIsNotNone(samples.get('xe1')) + self.assertEqual(samples, expected_result) + mock_get_stats.assert_called_once() + mock_prio_flow_statistics.assert_called_once() diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_trex.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_trex.py new file mode 100644 index 000000000..51b1b0d33 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_rfc2544_trex.py @@ -0,0 +1,312 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import time + +import mock +import unittest + +from yardstick.benchmark import contexts +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.traffic_profile import base as tp_base +from yardstick.network_services.vnf_generic.vnf import sample_vnf +from yardstick.network_services.vnf_generic.vnf import tg_rfc2544_trex + + +class TestTrexRfcResouceHelper(unittest.TestCase): + + @mock.patch.object(time, 'sleep') + def test__run_traffic_once(self, *args): + mock_setup_helper = mock.Mock() + mock_traffic_profile = mock.Mock() + mock_traffic_profile.config.duration = 3 + mock_traffic_profile.execute_traffic.return_value = ('fake_ports', + 'port_pg_id_map') + mock_traffic_profile.get_drop_percentage.return_value = (True, + 'percentage') + rfc_rh = tg_rfc2544_trex.TrexRfcResourceHelper(mock_setup_helper) + rfc_rh.TRANSIENT_PERIOD = 0 + rfc_rh.rfc2544_helper = mock.Mock() + + with mock.patch.object(rfc_rh, '_get_samples') as mock_get_samples: + self.assertTrue(rfc_rh._run_traffic_once(mock_traffic_profile)) + + mock_traffic_profile.execute_traffic.assert_called_once_with(rfc_rh) + mock_traffic_profile.stop_traffic.assert_called_once_with(rfc_rh) + mock_traffic_profile.stop_traffic.assert_called_once() + mock_get_samples.assert_has_calls([ + mock.call('fake_ports', port_pg_id='port_pg_id_map'), + mock.call('fake_ports', port_pg_id='port_pg_id_map')]) + + +class TestTrexTrafficGenRFC(unittest.TestCase): + + VNFD_0 = { + 'short-name': 'VpeVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0', + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'id': 'vpevnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'ifname': 'xe0', + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'vld_id': 'uplink_0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:01', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0', + }, + { + 'virtual-interface': { + 'ifname': 'xe1', + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'vld_id': 'downlink_0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:02' + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1', + }, + ], + }, + ], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1', + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'VpeApproxVnf', + 'name': 'VPEVnfSsh', + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ], + }, + } + + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64, + }, + } + + TC_YAML = { + 'scenarios': [ + { + 'tc_options': { + 'rfc2544': { + 'allowed_drop_rate': '0.8 - 1', + }, + }, + 'runner': { + 'duration': 400, + 'interval': 35, + 'type': 'Duration', + }, + 'traffic_options': { + 'flow': 'ipv4_1flow_Packets_vpe.yaml', + 'imix': 'imix_voice.yaml', + }, + 'vnf_options': { + 'vpe': { + 'cfg': 'vpe_config', + }, + }, + 'traffic_profile': 'ipv4_throughput_vpe.yaml', + 'type': 'NSPerf', + 'nodes': { + 'tg__1': 'trafficgen_1.yardstick', + 'vnf__1': 'vnf.yardstick', + }, + 'topology': 'vpe_vnf_topology.yaml', + }, + ], + 'context': { + 'nfvi_type': 'baremetal', + 'type': contexts.CONTEXT_NODE, + 'name': 'yardstick', + 'file': '/etc/yardstick/nodes/pod.yaml', + }, + 'schema': 'yardstick:task:0.1', + } + + def setUp(self): + self._mock_ssh_helper = mock.patch.object(sample_vnf, 'VnfSshHelper') + self.mock_ssh_helper = self._mock_ssh_helper.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_ssh_helper.stop() + + def test___init__(self): + trex_traffic_gen = tg_rfc2544_trex.TrexTrafficGenRFC('vnf1', self.VNFD_0) + self.assertIsNotNone(trex_traffic_gen.resource_helper._terminated.value) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + def test_collect_kpi(self, *args): + trex_traffic_gen = tg_rfc2544_trex.TrexTrafficGenRFC('vnf1', self.VNFD_0) + trex_traffic_gen.scenario_helper.scenario_cfg = { + 'nodes': {trex_traffic_gen.name: "mock"} + } + expected = { + 'physical_node': 'mock_node', + 'collect_stats': {}, + } + self.assertEqual(trex_traffic_gen.collect_kpi(), expected) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', return_value='fake_context') + def test_instantiate(self, *args): + mock_traffic_profile = mock.Mock(autospec=tp_base.TrafficProfile) + mock_traffic_profile.get_traffic_definition.return_value = "64" + mock_traffic_profile.params = self.TRAFFIC_PROFILE + + trex_traffic_gen = tg_rfc2544_trex.TrexTrafficGenRFC('vnf1', self.VNFD_0) + trex_traffic_gen._start_server = mock.Mock(return_value=0) + trex_traffic_gen.resource_helper = mock.MagicMock() + trex_traffic_gen.setup_helper.setup_vnf_environment = mock.MagicMock() + + scenario_cfg = { + "tc": "tc_baremetal_rfc2544_ipv4_1flow_64B", + "topology": 'nsb_test_case.yaml', + 'options': { + 'packetsize': 64, + 'traffic_type': 4, + 'rfc2544': { + 'allowed_drop_rate': '0.8 - 1', + }, + 'vnf__1': { + 'rules': 'acl_1rule.yaml', + 'vnf_config': { + 'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1 + }, + }, + }, + } + tg_rfc2544_trex.WAIT_TIME = 3 + scenario_cfg.update({"nodes": {"tg_1": {}, "vnf1": {}}}) + self.assertIsNone(trex_traffic_gen.instantiate(scenario_cfg, {})) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', return_value='fake_context') + def test_instantiate_error(self, *args): + mock_traffic_profile = mock.Mock(autospec=tp_base.TrafficProfile) + mock_traffic_profile.get_traffic_definition.return_value = "64" + mock_traffic_profile.params = self.TRAFFIC_PROFILE + + trex_traffic_gen = tg_rfc2544_trex.TrexTrafficGenRFC('vnf1', self.VNFD_0) + trex_traffic_gen.resource_helper = mock.MagicMock() + trex_traffic_gen.setup_helper.setup_vnf_environment = mock.MagicMock() + scenario_cfg = { + "tc": "tc_baremetal_rfc2544_ipv4_1flow_64B", + "nodes": { + "tg_1": {}, + "vnf1": {} + }, + "topology": 'nsb_test_case.yaml', + 'options': { + 'packetsize': 64, + 'traffic_type': 4, + 'rfc2544': { + 'allowed_drop_rate': '0.8 - 1', + }, + 'vnf__1': { + 'rules': 'acl_1rule.yaml', + 'vnf_config': { + 'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1, + }, + }, + }, + } + trex_traffic_gen.instantiate(scenario_cfg, {}) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_trex.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_trex.py new file mode 100644 index 000000000..0a441c8ce --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_trex.py @@ -0,0 +1,516 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy + +import mock +import unittest + +from yardstick.network_services.traffic_profile import base as tp_base +from yardstick.network_services.traffic_profile import rfc2544 +from yardstick.network_services.vnf_generic.vnf import sample_vnf +from yardstick.network_services.vnf_generic.vnf import tg_trex +from yardstick.benchmark.contexts import base as ctx_base + + +NAME = 'vnf__1' + + +class TestTrexTrafficGen(unittest.TestCase): + + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{'short-name': 'VpeVnf', + 'vdu': + [{'routing_table': + [{'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0'}, + {'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1'}], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': + [{'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'id': 'vpevnf-baremetal', + 'external-interface': + [{'virtual-interface': + {'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'vld_id': 'downlink_0', + 'ifname': 'xe0', + 'local_mac': '00:00:00:00:00:02'}, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'vld_id': 'uplink_0', + 'ifname': 'xe1', + 'local_mac': '00:00:00:00:00:01'}, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'}]}], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': + {'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1'}, + 'benchmark': + {'kpi': ['packets_in', 'packets_fwd', + 'packets_dropped']}, + 'connection-point': [{'type': 'VPORT', 'name': 'xe0'}, + {'type': 'VPORT', 'name': 'xe1'}], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh'}]}} + + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64 + }, + } + + SCENARIO_CFG = { + "options": { + "packetsize": 64, + "traffic_type": 4, + "rfc2544": { + "allowed_drop_rate": "0.8 - 1", + }, + "vnf__1": { + "rules": "acl_1rule.yaml", + "vnf_config": { + "lb_config": "SW", + "lb_count": 1, + "worker_config": "1C/1T", + "worker_threads": 1, + } + } + }, + "task_id": "a70bdf4a-8e67-47a3-9dc1-273c14506eb7", + "tc": "tc_ipv4_1Mflow_64B_packetsize", + "runner": { + "object": "NetworkServiceTestCase", + "interval": 35, + "output_filename": "/tmp/yardstick.out", + "runner_id": 74476, "duration": 400, + "type": "Duration" + }, + "traffic_profile": "ipv4_throughput_acl.yaml", + "traffic_options": { + "flow": "ipv4_Packets_acl.yaml", + "imix": "imix_voice.yaml" + }, + "type": "ISB", + "nodes": { + "tg__2": "trafficgen_2.yardstick", + "tg__1": "trafficgen_1.yardstick", + "vnf__1": "vnf.yardstick" + }, + "topology": "udpreplay-tg-topology-baremetal.yaml" + } + + CONTEXT_CFG = { + "nodes": { + "vnf__1": { + "vnfd-id-ref": "vnf__1", + "ip": "1.2.1.1", + "interfaces": { + "xe0": { + "local_iface_name": "ens786f0", + "vld_id": tp_base.TrafficProfile.UPLINK, + "netmask": "255.255.255.0", + "vpci": "0000:05:00.0", + "local_ip": "152.16.100.19", + "driver": "i40e", + "dst_ip": "152.16.100.20", + "local_mac": "00:00:00:00:00:02", + "dst_mac": "00:00:00:00:00:04", + "dpdk_port_num": 0 + }, + "xe1": { + "local_iface_name": "ens786f1", + "vld_id": tp_base.TrafficProfile.DOWNLINK, + "netmask": "255.255.255.0", + "vpci": "0000:05:00.1", + "local_ip": "152.16.40.19", + "driver": "i40e", + "dst_ip": "152.16.40.20", + "local_mac": "00:00:00:00:00:01", + "dst_mac": "00:00:00:00:00:03", + "dpdk_port_num": 1 + } + }, + "host": "1.2.1.1", + "user": "root", + "nd_route_tbl": [ + { + "netmask": "112", + "if": "xe0", + "gateway": "0064:ff9b:0:0:0:0:9810:6414", + "network": "0064:ff9b:0:0:0:0:9810:6414" + }, + { + "netmask": "112", + "if": "xe1", + "gateway": "0064:ff9b:0:0:0:0:9810:2814", + "network": "0064:ff9b:0:0:0:0:9810:2814" + } + ], + "password": "r00t", + "VNF model": "udp_replay.yaml", + "name": "vnf.yardstick", + "member-vnf-index": "2", + "routing_table": [ + { + "netmask": "255.255.255.0", + "if": "xe0", + "gateway": "152.16.100.20", + "network": "152.16.100.20" + }, + { + "netmask": "255.255.255.0", + "if": "xe1", + "gateway": "152.16.40.20", + "network": "152.16.40.20" + } + ], + "role": "vnf" + }, + "trafficgen_2.yardstick": { + "member-vnf-index": "3", + "role": "TrafficGen", + "name": "trafficgen_2.yardstick", + "vnfd-id-ref": "tg__2", + "ip": "1.2.1.1", + "interfaces": { + "xe0": { + "local_iface_name": "ens513f0", + "vld_id": tp_base.TrafficProfile.DOWNLINK, + "netmask": "255.255.255.0", + "vpci": "0000:02:00.0", + "local_ip": "152.16.40.20", + "driver": "ixgbe", + "dst_ip": "152.16.40.19", + "local_mac": "00:00:00:00:00:03", + "dst_mac": "00:00:00:00:00:01", + "dpdk_port_num": 0 + }, + "xe1": { + "local_iface_name": "ens513f1", + "netmask": "255.255.255.0", + "network": "202.16.100.0", + "local_ip": "202.16.100.20", + "driver": "ixgbe", + "local_mac": "00:1e:67:d0:60:5d", + "vpci": "0000:02:00.1", + "dpdk_port_num": 1 + } + }, + "password": "r00t", + "VNF model": "l3fwd_vnf.yaml", + "user": "root" + }, + "trafficgen_1.yardstick": { + "member-vnf-index": "1", + "role": "TrafficGen", + "name": "trafficgen_1.yardstick", + "vnfd-id-ref": "tg__1", + "ip": "1.2.1.1", + "interfaces": { + "xe0": { + "local_iface_name": "ens785f0", + "vld_id": tp_base.TrafficProfile.UPLINK, + "netmask": "255.255.255.0", + "vpci": "0000:05:00.0", + "local_ip": "152.16.100.20", + "driver": "i40e", + "dst_ip": "152.16.100.19", + "local_mac": "00:00:00:00:00:04", + "dst_mac": "00:00:00:00:00:02", + "dpdk_port_num": 0 + }, + "xe1": { + "local_ip": "152.16.100.21", + "driver": "i40e", + "vpci": "0000:05:00.1", + "dpdk_port_num": 1, + "local_iface_name": "ens785f1", + "netmask": "255.255.255.0", + "local_mac": "00:00:00:00:00:01" + } + }, + "password": "r00t", + "VNF model": "tg_rfc2544_tpl.yaml", + "user": "root" + } + } + } + + def setUp(self): + self._mock_ssh_helper = mock.patch.object(sample_vnf, 'VnfSshHelper') + self.mock_ssh_helper = self._mock_ssh_helper.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_ssh_helper.stop() + + def test___init__(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex.TrexTrafficGen(NAME, vnfd) + self.assertIsInstance(trex_traffic_gen.resource_helper, + tg_trex.TrexResourceHelper) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + def test_collect_kpi(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex.TrexTrafficGen(NAME, vnfd) + trex_traffic_gen.scenario_helper.scenario_cfg = { + 'nodes': {trex_traffic_gen.name: "mock"} + } + trex_traffic_gen.resource_helper._queue.put({}) + result = trex_traffic_gen.collect_kpi() + expected = { + 'physical_node': 'mock_node', + 'collect_stats': {} + } + self.assertEqual(expected, result) + + def test_listen_traffic(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex.TrexTrafficGen(NAME, vnfd) + self.assertIsNone(trex_traffic_gen.listen_traffic({})) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', return_value='fake_context') + def test_instantiate(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex.TrexTrafficGen(NAME, vnfd) + trex_traffic_gen._start_server = mock.Mock(return_value=0) + trex_traffic_gen._tg_process = mock.MagicMock() + trex_traffic_gen._tg_process.start = mock.Mock() + trex_traffic_gen._tg_process.exitcode = 0 + trex_traffic_gen._tg_process._is_alive = mock.Mock(return_value=1) + trex_traffic_gen.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + trex_traffic_gen.setup_helper.setup_vnf_environment = mock.MagicMock() + self.assertIsNone(trex_traffic_gen.instantiate(self.SCENARIO_CFG, + self.CONTEXT_CFG)) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', return_value='fake_context') + def test_instantiate_error(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex.TrexTrafficGen(NAME, vnfd) + trex_traffic_gen._start_server = mock.Mock(return_value=0) + trex_traffic_gen._tg_process = mock.MagicMock() + trex_traffic_gen._tg_process.start = mock.Mock() + trex_traffic_gen._tg_process._is_alive = mock.Mock(return_value=0) + trex_traffic_gen.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + trex_traffic_gen.setup_helper.setup_vnf_environment = mock.MagicMock() + self.assertIsNone(trex_traffic_gen.instantiate(self.SCENARIO_CFG, + self.CONTEXT_CFG)) + + def test__start_server(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex.TrexTrafficGen(NAME, vnfd) + trex_traffic_gen.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + trex_traffic_gen.scenario_helper.scenario_cfg = {} + self.assertIsNone(trex_traffic_gen._start_server()) + + def test__start_server_multiple_queues(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex.TrexTrafficGen(NAME, vnfd) + trex_traffic_gen.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + trex_traffic_gen.scenario_helper.scenario_cfg = { + "options": {NAME: {"queues_per_port": 2}}} + self.assertIsNone(trex_traffic_gen._start_server()) + + def test__traffic_runner(self): + mock_traffic_profile = mock.Mock(autospec=tp_base.TrafficProfile) + mock_traffic_profile.get_traffic_definition.return_value = "64" + mock_traffic_profile.execute_traffic.return_value = "64" + mock_traffic_profile.params = self.TRAFFIC_PROFILE + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.sut = tg_trex.TrexTrafficGen(NAME, vnfd) + self.sut.ssh_helper = mock.Mock() + self.sut.ssh_helper.run = mock.Mock() + self.sut._connect_client = mock.Mock() + self.sut._connect_client.get_stats = mock.Mock(return_value="0") + self.sut.resource_helper.RUN_DURATION = 0 + self.sut.resource_helper.QUEUE_WAIT_TIME = 0 + # must generate cfg before we can run traffic so Trex port mapping is + # created + self.sut.resource_helper.generate_cfg() + with mock.patch.object(self.sut.resource_helper, 'run_traffic'): + self.sut._traffic_runner(mock_traffic_profile) + + def test__generate_trex_cfg(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex.TrexTrafficGen(NAME, vnfd) + trex_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + self.assertIsNone(trex_traffic_gen.resource_helper.generate_cfg()) + + def test_build_ports_reversed_pci_ordering(self): + vnfd = copy.deepcopy(self.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + vnfd['vdu'][0]['external-interface'] = [ + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 2, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'vld_id': 'downlink_0', + 'ifname': 'xe0', + 'local_mac': '00:00:00:00:00:02'}, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:04:00.0', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'vld_id': 'uplink_0', + 'ifname': 'xe1', + 'local_mac': '00:00:00:00:00:01'}, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'}] + trex_traffic_gen = tg_trex.TrexTrafficGen(NAME, vnfd) + trex_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.generate_cfg() + trex_traffic_gen.resource_helper._build_ports() + self.assertEqual(sorted(trex_traffic_gen.resource_helper.all_ports), + [0, 1]) + # there is a gap in ordering + self.assertEqual( + {0: 0, 2: 1}, + dict(trex_traffic_gen.resource_helper.dpdk_to_trex_port_map)) + + def test_run_traffic(self): + mock_traffic_profile = mock.Mock(autospec=tp_base.TrafficProfile) + mock_traffic_profile.get_traffic_definition.return_value = "64" + mock_traffic_profile.params = self.TRAFFIC_PROFILE + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.sut = tg_trex.TrexTrafficGen(NAME, vnfd) + self.sut.ssh_helper = mock.Mock() + self.sut.ssh_helper.run = mock.Mock() + self.sut._traffic_runner = mock.Mock(return_value=0) + self.sut.resource_helper.client_started.value = 1 + result = self.sut.run_traffic(mock_traffic_profile) + self.sut._traffic_process.terminate() + self.assertIsNotNone(result) + + def test_terminate(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex.TrexTrafficGen(NAME, vnfd) + trex_traffic_gen.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + self.assertIsNone(trex_traffic_gen.terminate()) + + def test__connect_client(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex.TrexTrafficGen(NAME, vnfd) + client = mock.Mock() + client.connect = mock.Mock(return_value=0) + self.assertIsNotNone(trex_traffic_gen.resource_helper._connect(client)) + + +class TrexResourceHelperTestCase(unittest.TestCase): + + def test__get_samples(self): + mock_setup_helper = mock.Mock() + trex_rh = tg_trex.TrexResourceHelper(mock_setup_helper) + trex_rh.vnfd_helper.interfaces = [ + {'name': 'interface1'}, + {'name': 'interface2'}] + stats = { + 10: {'rx_pps': 5, 'ipackets': 200}, + 20: {'rx_pps': 10, 'ipackets': 300}, + 'latency': {1: {'latency': 'latency_port_10_pg_id_1'}, + 2: {'latency': 'latency_port_10_pg_id_2'}, + 3: {'latency': 'latency_port_20_pg_id_3'}, + 4: {'latency': 'latency_port_20_pg_id_4'}} + } + port_pg_id = rfc2544.PortPgIDMap() + port_pg_id.add_port(10) + port_pg_id.increase_pg_id() + port_pg_id.increase_pg_id() + port_pg_id.add_port(20) + port_pg_id.increase_pg_id() + port_pg_id.increase_pg_id() + + with mock.patch.object(trex_rh, 'get_stats') as mock_get_stats, \ + mock.patch.object(trex_rh.vnfd_helper, 'port_num') as \ + mock_port_num: + mock_get_stats.return_value = stats + mock_port_num.side_effect = [10, 20] + output = trex_rh._get_samples([10, 20], port_pg_id=port_pg_id) + + interface = output['interface1'] + self.assertEqual(5.0, interface['rx_throughput_fps']) + self.assertEqual(200, interface['in_packets']) + self.assertEqual('latency_port_10_pg_id_1', interface['latency'][1]) + self.assertEqual('latency_port_10_pg_id_2', interface['latency'][2]) + + interface = output['interface2'] + self.assertEqual(10.0, interface['rx_throughput_fps']) + self.assertEqual(300, interface['in_packets']) + self.assertEqual('latency_port_20_pg_id_3', interface['latency'][3]) + self.assertEqual('latency_port_20_pg_id_4', interface['latency'][4]) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_trex_vpp.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_trex_vpp.py new file mode 100644 index 000000000..ef1ae1182 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_trex_vpp.py @@ -0,0 +1,1130 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +from multiprocessing import Process + +import mock +from trex_stl_lib.trex_stl_exceptions import STLError + +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.traffic_profile import base as tp_base +from yardstick.network_services.traffic_profile import rfc2544 +from yardstick.network_services.vnf_generic.vnf import base, sample_vnf, \ + tg_trex_vpp +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import \ + mock_ssh + + +class TestTrexVppResourceHelper(unittest.TestCase): + TRAFFIC_PROFILE = { + "schema": "isb:traffic_profile:0.1", + "name": "fixed", + "description": "Fixed traffic profile to run UDP traffic", + "traffic_profile": { + "traffic_type": "FixedTraffic", + "frame_rate": 100, # pps + "flow_number": 10, + "frame_size": 64 + }, + } + + def test_fmt_latency(self): + mock_setup_helper = mock.Mock() + vpp_rfc = tg_trex_vpp.TrexVppResourceHelper(mock_setup_helper) + self.assertEqual('10/90/489', vpp_rfc.fmt_latency(10, 90, 489)) + + def test_fmt_latency_error(self): + mock_setup_helper = mock.Mock() + vpp_rfc = tg_trex_vpp.TrexVppResourceHelper(mock_setup_helper) + self.assertEqual('-1/-1/-1', vpp_rfc.fmt_latency('err', 'err', 'err')) + + def test_generate_samples(self): + stats = { + 0: { + "ibytes": 55549120, + "ierrors": 0, + "ipackets": 867955, + "obytes": 55549696, + "oerrors": 0, + "opackets": 867964, + "rx_bps": 104339032.0, + "rx_bps_L1": 136944984.0, + "rx_pps": 203787.2, + "rx_util": 1.36944984, + "tx_bps": 134126008.0, + "tx_bps_L1": 176040392.0, + "tx_pps": 261964.9, + "tx_util": 1.7604039200000001 + }, + 1: { + "ibytes": 55549696, + "ierrors": 0, + "ipackets": 867964, + "obytes": 55549120, + "oerrors": 0, + "opackets": 867955, + "rx_bps": 134119648.0, + "rx_bps_L1": 176032032.0, + "rx_pps": 261952.4, + "rx_util": 1.76032032, + "tx_bps": 104338192.0, + "tx_bps_L1": 136943872.0, + "tx_pps": 203785.5, + "tx_util": 1.36943872 + }, + "flow_stats": { + 1: { + "rx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "rx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "rx_bytes": { + "0": 6400, + "1": 0, + "total": 6400 + }, + "rx_pkts": { + "0": 100, + "1": 0, + "total": 100 + }, + "rx_pps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "tx_bytes": { + "0": 0, + "1": 6400, + "total": 6400 + }, + "tx_pkts": { + "0": 0, + "1": 100, + "total": 100 + }, + "tx_pps": { + "0": 0, + "1": 0, + "total": 0 + } + }, + 2: { + "rx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "rx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "rx_bytes": { + "0": 0, + "1": 6464, + "total": 6464 + }, + "rx_pkts": { + "0": 0, + "1": 101, + "total": 101 + }, + "rx_pps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "tx_bytes": { + "0": 6464, + "1": 0, + "total": 6464 + }, + "tx_pkts": { + "0": 101, + "1": 0, + "total": 101 + }, + "tx_pps": { + "0": 0, + "1": 0, + "total": 0 + } + }, + "global": { + "rx_err": { + "0": 0, + "1": 0 + }, + "tx_err": { + "0": 0, + "1": 0 + } + } + }, + "global": { + "bw_per_core": 45.6, + "cpu_util": 0.1494, + "queue_full": 0, + "rx_bps": 238458672.0, + "rx_cpu_util": 4.751e-05, + "rx_drop_bps": 0.0, + "rx_pps": 465739.6, + "tx_bps": 238464208.0, + "tx_pps": 465750.4 + }, + "latency": { + 1: { + "err_cntrs": { + "dropped": 0, + "dup": 0, + "out_of_order": 0, + "seq_too_high": 0, + "seq_too_low": 0 + }, + "latency": { + "average": 63.375, + "histogram": { + "20": 1, + "30": 18, + "40": 12, + "50": 10, + "60": 12, + "70": 11, + "80": 6, + "90": 10, + "100": 20 + }, + "jitter": 23, + "last_max": 122, + "total_max": 123, + "total_min": 20 + } + }, + 2: { + "err_cntrs": { + "dropped": 0, + "dup": 0, + "out_of_order": 0, + "seq_too_high": 0, + "seq_too_low": 0 + }, + "latency": { + "average": 74, + "histogram": { + "60": 20, + "70": 10, + "80": 3, + "90": 4, + "100": 64 + }, + "jitter": 6, + "last_max": 83, + "total_max": 135, + "total_min": 60 + } + }, + "global": { + "bad_hdr": 0, + "old_flow": 0 + } + }, + "total": { + "ibytes": 111098816, + "ierrors": 0, + "ipackets": 1735919, + "obytes": 111098816, + "oerrors": 0, + "opackets": 1735919, + "rx_bps": 238458680.0, + "rx_bps_L1": 312977016.0, + "rx_pps": 465739.6, + "rx_util": 3.1297701599999996, + "tx_bps": 238464200.0, + "tx_bps_L1": 312984264.0, + "tx_pps": 465750.4, + "tx_util": 3.12984264 + } + } + expected = { + "xe0": { + "in_packets": 867955, + "latency": { + 2: { + "avg_latency": 74.0, + "max_latency": 135.0, + "min_latency": 60.0 + } + }, + "out_packets": 867964, + "rx_throughput_bps": 104339032.0, + "rx_throughput_fps": 203787.2, + "tx_throughput_bps": 134126008.0, + "tx_throughput_fps": 261964.9 + }, + "xe1": { + "in_packets": 867964, + "latency": { + 1: { + "avg_latency": 63.375, + "max_latency": 123.0, + "min_latency": 20.0 + } + }, + "out_packets": 867955, + "rx_throughput_bps": 134119648.0, + "rx_throughput_fps": 261952.4, + "tx_throughput_bps": 104338192.0, + "tx_throughput_fps": 203785.5 + } + } + mock_setup_helper = mock.Mock() + vpp_rfc = tg_trex_vpp.TrexVppResourceHelper(mock_setup_helper) + vpp_rfc.vnfd_helper = base.VnfdHelper(TestTrexTrafficGenVpp.VNFD_0) + port_pg_id = rfc2544.PortPgIDMap() + port_pg_id.add_port(1) + port_pg_id.increase_pg_id() + port_pg_id.add_port(0) + port_pg_id.increase_pg_id() + self.assertEqual(expected, + vpp_rfc.generate_samples(stats, [0, 1], port_pg_id, + True)) + + def test_generate_samples_error(self): + stats = { + 0: { + "ibytes": 55549120, + "ierrors": 0, + "ipackets": 867955, + "obytes": 55549696, + "oerrors": 0, + "opackets": 867964, + "rx_bps": 104339032.0, + "rx_bps_L1": 136944984.0, + "rx_pps": 203787.2, + "rx_util": 1.36944984, + "tx_bps": 134126008.0, + "tx_bps_L1": 176040392.0, + "tx_pps": 261964.9, + "tx_util": 1.7604039200000001 + }, + 1: { + "ibytes": 55549696, + "ierrors": 0, + "ipackets": 867964, + "obytes": 55549120, + "oerrors": 0, + "opackets": 867955, + "rx_bps": 134119648.0, + "rx_bps_L1": 176032032.0, + "rx_pps": 261952.4, + "rx_util": 1.76032032, + "tx_bps": 104338192.0, + "tx_bps_L1": 136943872.0, + "tx_pps": 203785.5, + "tx_util": 1.36943872 + }, + "flow_stats": { + 1: { + "rx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "rx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "rx_bytes": { + "0": 6400, + "1": 0, + "total": 6400 + }, + "rx_pkts": { + "0": 100, + "1": 0, + "total": 100 + }, + "rx_pps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "tx_bytes": { + "0": 0, + "1": 6400, + "total": 6400 + }, + "tx_pkts": { + "0": 0, + "1": 100, + "total": 100 + }, + "tx_pps": { + "0": 0, + "1": 0, + "total": 0 + } + }, + 2: { + "rx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "rx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "rx_bytes": { + "0": 0, + "1": 6464, + "total": 6464 + }, + "rx_pkts": { + "0": 0, + "1": 101, + "total": 101 + }, + "rx_pps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "tx_bytes": { + "0": 6464, + "1": 0, + "total": 6464 + }, + "tx_pkts": { + "0": 101, + "1": 0, + "total": 101 + }, + "tx_pps": { + "0": 0, + "1": 0, + "total": 0 + } + }, + "global": { + "rx_err": { + "0": 0, + "1": 0 + }, + "tx_err": { + "0": 0, + "1": 0 + } + } + }, + "global": { + "bw_per_core": 45.6, + "cpu_util": 0.1494, + "queue_full": 0, + "rx_bps": 238458672.0, + "rx_cpu_util": 4.751e-05, + "rx_drop_bps": 0.0, + "rx_pps": 465739.6, + "tx_bps": 238464208.0, + "tx_pps": 465750.4 + }, + "latency": { + 1: { + "err_cntrs": { + "dropped": 0, + "dup": 0, + "out_of_order": 0, + "seq_too_high": 0, + "seq_too_low": 0 + }, + "latency": { + "average": "err", + "histogram": { + "20": 1, + "30": 18, + "40": 12, + "50": 10, + "60": 12, + "70": 11, + "80": 6, + "90": 10, + "100": 20 + }, + "jitter": 23, + "last_max": 122, + "total_max": "err", + "total_min": "err" + } + }, + 2: { + "err_cntrs": { + "dropped": 0, + "dup": 0, + "out_of_order": 0, + "seq_too_high": 0, + "seq_too_low": 0 + }, + "latency": { + "average": 74, + "histogram": { + "60": 20, + "70": 10, + "80": 3, + "90": 4, + "100": 64 + }, + "jitter": 6, + "last_max": 83, + "total_max": 135, + "total_min": 60 + } + }, + "global": { + "bad_hdr": 0, + "old_flow": 0 + } + }, + "total": { + "ibytes": 111098816, + "ierrors": 0, + "ipackets": 1735919, + "obytes": 111098816, + "oerrors": 0, + "opackets": 1735919, + "rx_bps": 238458680.0, + "rx_bps_L1": 312977016.0, + "rx_pps": 465739.6, + "rx_util": 3.1297701599999996, + "tx_bps": 238464200.0, + "tx_bps_L1": 312984264.0, + "tx_pps": 465750.4, + "tx_util": 3.12984264 + } + } + expected = {'xe0': {'in_packets': 867955, + 'latency': {2: {'avg_latency': 74.0, + 'max_latency': 135.0, + 'min_latency': 60.0}}, + 'out_packets': 867964, + 'rx_throughput_bps': 104339032.0, + 'rx_throughput_fps': 203787.2, + 'tx_throughput_bps': 134126008.0, + 'tx_throughput_fps': 261964.9}, + 'xe1': {'in_packets': 867964, + 'latency': {1: {'avg_latency': -1.0, + 'max_latency': -1.0, + 'min_latency': -1.0}}, + 'out_packets': 867955, + 'rx_throughput_bps': 134119648.0, + 'rx_throughput_fps': 261952.4, + 'tx_throughput_bps': 104338192.0, + 'tx_throughput_fps': 203785.5}} + mock_setup_helper = mock.Mock() + vpp_rfc = tg_trex_vpp.TrexVppResourceHelper(mock_setup_helper) + vpp_rfc.vnfd_helper = base.VnfdHelper(TestTrexTrafficGenVpp.VNFD_0) + vpp_rfc.get_stats = mock.Mock() + vpp_rfc.get_stats.return_value = stats + port_pg_id = rfc2544.PortPgIDMap() + port_pg_id.add_port(1) + port_pg_id.increase_pg_id() + port_pg_id.add_port(0) + port_pg_id.increase_pg_id() + self.assertEqual(expected, + vpp_rfc.generate_samples(stats=None, ports=[0, 1], + port_pg_id=port_pg_id, + latency=True)) + + def test__run_traffic_once(self): + mock_setup_helper = mock.Mock() + mock_traffic_profile = mock.Mock() + vpp_rfc = tg_trex_vpp.TrexVppResourceHelper(mock_setup_helper) + vpp_rfc.TRANSIENT_PERIOD = 0 + vpp_rfc.rfc2544_helper = mock.Mock() + + self.assertTrue(vpp_rfc._run_traffic_once(mock_traffic_profile)) + mock_traffic_profile.execute_traffic.assert_called_once_with(vpp_rfc) + + def test_run_traffic(self): + mock_traffic_profile = mock.Mock(autospec=tp_base.TrafficProfile) + mock_traffic_profile.get_traffic_definition.return_value = "64" + mock_traffic_profile.params = self.TRAFFIC_PROFILE + mock_setup_helper = mock.Mock() + vpp_rfc = tg_trex_vpp.TrexVppResourceHelper(mock_setup_helper) + vpp_rfc.ssh_helper = mock.Mock() + vpp_rfc.ssh_helper.run = mock.Mock() + vpp_rfc._traffic_runner = mock.Mock(return_value=0) + vpp_rfc._build_ports = mock.Mock() + vpp_rfc._connect = mock.Mock() + vpp_rfc.run_traffic(mock_traffic_profile) + + def test_send_traffic_on_tg(self): + stats = { + 0: { + "ibytes": 55549120, + "ierrors": 0, + "ipackets": 867955, + "obytes": 55549696, + "oerrors": 0, + "opackets": 867964, + "rx_bps": 104339032.0, + "rx_bps_L1": 136944984.0, + "rx_pps": 203787.2, + "rx_util": 1.36944984, + "tx_bps": 134126008.0, + "tx_bps_L1": 176040392.0, + "tx_pps": 261964.9, + "tx_util": 1.7604039200000001 + }, + 1: { + "ibytes": 55549696, + "ierrors": 0, + "ipackets": 867964, + "obytes": 55549120, + "oerrors": 0, + "opackets": 867955, + "rx_bps": 134119648.0, + "rx_bps_L1": 176032032.0, + "rx_pps": 261952.4, + "rx_util": 1.76032032, + "tx_bps": 104338192.0, + "tx_bps_L1": 136943872.0, + "tx_pps": 203785.5, + "tx_util": 1.36943872 + }, + "flow_stats": { + 1: { + "rx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "rx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "rx_bytes": { + "0": 6400, + "1": 0, + "total": 6400 + }, + "rx_pkts": { + "0": 100, + "1": 0, + "total": 100 + }, + "rx_pps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "tx_bytes": { + "0": 0, + "1": 6400, + "total": 6400 + }, + "tx_pkts": { + "0": 0, + "1": 100, + "total": 100 + }, + "tx_pps": { + "0": 0, + "1": 0, + "total": 0 + } + }, + 2: { + "rx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "rx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "rx_bytes": { + "0": 0, + "1": 6464, + "total": 6464 + }, + "rx_pkts": { + "0": 0, + "1": 101, + "total": 101 + }, + "rx_pps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps": { + "0": 0, + "1": 0, + "total": 0 + }, + "tx_bps_l1": { + "0": 0.0, + "1": 0.0, + "total": 0.0 + }, + "tx_bytes": { + "0": 6464, + "1": 0, + "total": 6464 + }, + "tx_pkts": { + "0": 101, + "1": 0, + "total": 101 + }, + "tx_pps": { + "0": 0, + "1": 0, + "total": 0 + } + }, + "global": { + "rx_err": { + "0": 0, + "1": 0 + }, + "tx_err": { + "0": 0, + "1": 0 + } + } + }, + "global": { + "bw_per_core": 45.6, + "cpu_util": 0.1494, + "queue_full": 0, + "rx_bps": 238458672.0, + "rx_cpu_util": 4.751e-05, + "rx_drop_bps": 0.0, + "rx_pps": 465739.6, + "tx_bps": 238464208.0, + "tx_pps": 465750.4 + }, + "latency": { + 1: { + "err_cntrs": { + "dropped": 0, + "dup": 0, + "out_of_order": 0, + "seq_too_high": 0, + "seq_too_low": 0 + }, + "latency": { + "average": 63.375, + "histogram": { + "20": 1, + "30": 18, + "40": 12, + "50": 10, + "60": 12, + "70": 11, + "80": 6, + "90": 10, + "100": 20 + }, + "jitter": 23, + "last_max": 122, + "total_max": 123, + "total_min": 20 + } + }, + 2: { + "err_cntrs": { + "dropped": 0, + "dup": 0, + "out_of_order": 0, + "seq_too_high": 0, + "seq_too_low": 0 + }, + "latency": { + "average": 74, + "histogram": { + "60": 20, + "70": 10, + "80": 3, + "90": 4, + "100": 64 + }, + "jitter": 6, + "last_max": 83, + "total_max": 135, + "total_min": 60 + } + }, + "global": { + "bad_hdr": 0, + "old_flow": 0 + } + }, + "total": { + "ibytes": 111098816, + "ierrors": 0, + "ipackets": 1735919, + "obytes": 111098816, + "oerrors": 0, + "opackets": 1735919, + "rx_bps": 238458680.0, + "rx_bps_L1": 312977016.0, + "rx_pps": 465739.6, + "rx_util": 3.1297701599999996, + "tx_bps": 238464200.0, + "tx_bps_L1": 312984264.0, + "tx_pps": 465750.4, + "tx_util": 3.12984264 + } + } + mock_setup_helper = mock.Mock() + vpp_rfc = tg_trex_vpp.TrexVppResourceHelper(mock_setup_helper) + vpp_rfc.vnfd_helper = base.VnfdHelper(TestTrexTrafficGenVpp.VNFD_0) + vpp_rfc.client = mock.Mock() + vpp_rfc.client.get_warnings.return_value = 'get_warnings' + vpp_rfc.client.get_stats.return_value = stats + port_pg_id = rfc2544.PortPgIDMap() + port_pg_id.add_port(1) + port_pg_id.increase_pg_id() + port_pg_id.add_port(0) + port_pg_id.increase_pg_id() + self.assertEqual(stats, + vpp_rfc.send_traffic_on_tg([0, 1], port_pg_id, 30, + 10000, True)) + + def test_send_traffic_on_tg_error(self): + mock_setup_helper = mock.Mock() + vpp_rfc = tg_trex_vpp.TrexVppResourceHelper(mock_setup_helper) + vpp_rfc.vnfd_helper = base.VnfdHelper(TestTrexTrafficGenVpp.VNFD_0) + vpp_rfc.client = mock.Mock() + vpp_rfc.client.get_warnings.return_value = 'get_warnings' + vpp_rfc.client.get_stats.side_effect = STLError('get_stats') + vpp_rfc.client.wait_on_traffic.side_effect = STLError( + 'wait_on_traffic') + port_pg_id = rfc2544.PortPgIDMap() + port_pg_id.add_port(1) + port_pg_id.increase_pg_id() + port_pg_id.add_port(0) + port_pg_id.increase_pg_id() + # with self.assertRaises(RuntimeError) as raised: + vpp_rfc.send_traffic_on_tg([0, 1], port_pg_id, 30, 10000, True) + # self.assertIn('TRex stateless runtime error', str(raised.exception)) + + +class TestTrexTrafficGenVpp(unittest.TestCase): + VNFD_0 = { + "benchmark": { + "kpi": [ + "rx_throughput_fps", + "tx_throughput_fps", + "tx_throughput_mbps", + "rx_throughput_mbps", + "in_packets", + "out_packets", + "min_latency", + "max_latency", + "avg_latency" + ] + }, + "description": "TRex stateless traffic verifier", + "id": "TrexTrafficGenVpp", + "mgmt-interface": { + "ip": "10.10.10.10", + "password": "r00t", + "user": "root", + "vdu-id": "trexgen-baremetal" + }, + "name": "trexverifier", + "short-name": "trexverifier", + "vdu": [ + { + "description": "TRex stateless traffic verifier", + "external-interface": [ + { + "name": "xe0", + "virtual-interface": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:ff:06.0" + }, + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "vnfd-connection-point-ref": "xe0" + }, + { + "name": "xe1", + "virtual-interface": { + "dpdk_port_num": 1, + "driver": "igb_uio", + "dst_ip": "192.168.101.2", + "dst_mac": "90:e2:ba:7c:41:a9", + "ifname": "xe1", + "local_ip": "192.168.101.1", + "local_mac": "90:e2:ba:7c:30:e9", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "192.168.101.1", + "dst_mac": "90:e2:ba:7c:30:e9", + "ifname": "xe0", + "local_ip": "192.168.101.2", + "local_mac": "90:e2:ba:7c:41:a9", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "tg__0", + "vld_id": "downlink_0", + "vpci": "0000:ff:06.0" + }, + "peer_name": "vnf__1", + "vld_id": "downlink_0", + "vpci": "0000:81:00.1" + }, + "vnfd-connection-point-ref": "xe1" + } + ], + "id": "trexgen-baremetal", + "name": "trexgen-baremetal" + } + ] + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ], + }, + } + + def setUp(self): + self._mock_ssh_helper = mock.patch.object(sample_vnf, 'VnfSshHelper') + self.mock_ssh_helper = self._mock_ssh_helper.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_ssh_helper.stop() + + def test___init__(self): + trex_traffic_gen = tg_trex_vpp.TrexTrafficGenVpp( + 'tg0', self.VNFD_0) + self.assertIsNotNone( + trex_traffic_gen.resource_helper._terminated.value) + + def test__check_status(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex_vpp.TrexTrafficGenVpp('tg0', vnfd) + trex_traffic_gen.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.ssh_helper.execute.return_value = 0, '', '' + trex_traffic_gen.scenario_helper.scenario_cfg = {} + self.assertEqual(0, trex_traffic_gen._check_status()) + + def test__start_server(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex_vpp.TrexTrafficGenVpp('tg0', vnfd) + trex_traffic_gen.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + trex_traffic_gen.scenario_helper.scenario_cfg = {} + self.assertIsNone(trex_traffic_gen._start_server()) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', + return_value='mock_node') + def test_collect_kpi(self, *args): + trex_traffic_gen = tg_trex_vpp.TrexTrafficGenVpp( + 'tg0', self.VNFD_0) + trex_traffic_gen.scenario_helper.scenario_cfg = { + 'nodes': {trex_traffic_gen.name: "mock"} + } + expected = { + 'physical_node': 'mock_node', + 'collect_stats': {}, + } + self.assertEqual(trex_traffic_gen.collect_kpi(), expected) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate(self, *args): + trex_traffic_gen = tg_trex_vpp.TrexTrafficGenVpp( + 'tg0', self.VNFD_0) + trex_traffic_gen._start_server = mock.Mock(return_value=0) + trex_traffic_gen.resource_helper = mock.MagicMock() + trex_traffic_gen.setup_helper.setup_vnf_environment = mock.MagicMock() + + scenario_cfg = { + "tc": "tc_baremetal_rfc2544_ipv4_1flow_64B", + "topology": 'nsb_test_case.yaml', + 'options': { + 'packetsize': 64, + 'traffic_type': 4, + 'rfc2544': { + 'allowed_drop_rate': '0.8 - 1', + }, + 'vnf__0': { + 'rules': 'acl_1rule.yaml', + 'vnf_config': { + 'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1 + }, + }, + }, + } + tg_trex_vpp.WAIT_TIME = 3 + scenario_cfg.update({"nodes": {"tg0": {}, "vnf0": {}}}) + self.assertIsNone(trex_traffic_gen.instantiate(scenario_cfg, {})) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate_error(self, *args): + trex_traffic_gen = tg_trex_vpp.TrexTrafficGenVpp( + 'tg0', self.VNFD_0) + trex_traffic_gen.resource_helper = mock.MagicMock() + trex_traffic_gen.setup_helper.setup_vnf_environment = mock.MagicMock() + scenario_cfg = { + "tc": "tc_baremetal_rfc2544_ipv4_1flow_64B", + "nodes": { + "tg0": {}, + "vnf0": {} + }, + "topology": 'nsb_test_case.yaml', + 'options': { + 'packetsize': 64, + 'traffic_type': 4, + 'rfc2544': { + 'allowed_drop_rate': '0.8 - 1', + }, + 'vnf__0': { + 'rules': 'acl_1rule.yaml', + 'vnf_config': { + 'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1, + }, + }, + }, + } + trex_traffic_gen.instantiate(scenario_cfg, {}) + + @mock.patch( + 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper') + def test_wait_for_instantiate(self, ssh, *args): + mock_ssh(ssh) + + mock_process = mock.Mock(autospec=Process) + mock_process.is_alive.return_value = True + mock_process.exitcode = 432 + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + trex_traffic_gen = tg_trex_vpp.TrexTrafficGenVpp('tg0', vnfd) + trex_traffic_gen.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.ssh_helper = mock.MagicMock() + trex_traffic_gen.resource_helper.ssh_helper.execute.return_value = 0, '', '' + trex_traffic_gen.scenario_helper.scenario_cfg = {} + trex_traffic_gen._tg_process = mock_process + self.assertEqual(432, trex_traffic_gen.wait_for_instantiate()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_vcmts_pktgen.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_vcmts_pktgen.py new file mode 100755 index 000000000..3b226d3f1 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_vcmts_pktgen.py @@ -0,0 +1,652 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import mock +import socket +import threading +import time +import os +import copy + +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper +from yardstick.network_services.vnf_generic.vnf import tg_vcmts_pktgen +from yardstick.common import exceptions + + +NAME = "tg__0" + + +class TestPktgenHelper(unittest.TestCase): + + def test___init__(self): + pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000) + self.assertEqual(pktgen_helper.host, "localhost") + self.assertEqual(pktgen_helper.port, 23000) + self.assertFalse(pktgen_helper.connected) + + def _run_fake_server(self): + server_sock = socket.socket() + server_sock.bind(('localhost', 23000)) + server_sock.listen(0) + client_socket, _ = server_sock.accept() + client_socket.close() + server_sock.close() + + def test__connect(self): + pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000) + self.assertFalse(pktgen_helper._connect()) + server_thread = threading.Thread(target=self._run_fake_server) + server_thread.start() + time.sleep(0.5) + self.assertTrue(pktgen_helper._connect()) + pktgen_helper._sock.close() + server_thread.join() + + @mock.patch('yardstick.network_services.vnf_generic.vnf.tg_vcmts_pktgen.time') + def test_connect(self, *args): + pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000) + pktgen_helper.connected = True + self.assertTrue(pktgen_helper.connect()) + pktgen_helper.connected = False + + pktgen_helper._connect = mock.MagicMock(return_value=True) + self.assertTrue(pktgen_helper.connect()) + self.assertTrue(pktgen_helper.connected) + + pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000) + pktgen_helper._connect = mock.MagicMock(return_value=False) + self.assertFalse(pktgen_helper.connect()) + self.assertFalse(pktgen_helper.connected) + + def test_send_command(self): + pktgen_helper = tg_vcmts_pktgen.PktgenHelper("localhost", 23000) + self.assertFalse(pktgen_helper.send_command("")) + + pktgen_helper.connected = True + pktgen_helper._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.assertFalse(pktgen_helper.send_command("")) + + pktgen_helper._sock = mock.MagicMock() + self.assertTrue(pktgen_helper.send_command("")) + + +class TestVcmtsPktgenSetupEnvHelper(unittest.TestCase): + + PKTGEN_PARAMETERS = "export LUA_PATH=/vcmts/Pktgen.lua;"\ + "export CMK_PROC_FS=/host/proc;"\ + " /pktgen-config/setup.sh 0 4 18:02.0 "\ + "18:02.1 18:02.2 18:02.3 00:00.0 00:00.0 "\ + "00:00.0 00:00.0 imix1_100cms_1ofdm.pcap "\ + "imix1_100cms_1ofdm.pcap imix1_100cms_1ofdm.pcap "\ + "imix1_100cms_1ofdm.pcap imix1_100cms_1ofdm.pcap "\ + "imix1_100cms_1ofdm.pcap imix1_100cms_1ofdm.pcap "\ + "imix1_100cms_1ofdm.pcap" + + OPTIONS = { + "pktgen_values": "/tmp/pktgen_values.yaml", + "tg__0": { + "pktgen_id": 0 + }, + "vcmts_influxdb_ip": "10.80.5.150", + "vcmts_influxdb_port": 8086, + "vcmtsd_values": "/tmp/vcmtsd_values.yaml", + "vnf__0": { + "sg_id": 0, + "stream_dir": "us" + }, + "vnf__1": { + "sg_id": 0, + "stream_dir": "ds" + } + } + + def setUp(self): + vnfd_helper = VnfdHelper( + TestVcmtsPktgen.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + + self.setup_helper = tg_vcmts_pktgen.VcmtsPktgenSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + + def test_generate_pcap_filename(self): + pcap_file_name = self.setup_helper.generate_pcap_filename(\ + TestVcmtsPktgen.PKTGEN_POD_VALUES[0]['ports'][0]) + self.assertEquals(pcap_file_name, "imix1_100cms_1ofdm.pcap") + + def test_find_port_cfg(self): + port_cfg = self.setup_helper.find_port_cfg(\ + TestVcmtsPktgen.PKTGEN_POD_VALUES[0]['ports'], "port_0") + self.assertIsNotNone(port_cfg) + + port_cfg = self.setup_helper.find_port_cfg(\ + TestVcmtsPktgen.PKTGEN_POD_VALUES[0]['ports'], "port_8") + self.assertIsNone(port_cfg) + + def test_build_pktgen_parameters(self): + parameters = self.setup_helper.build_pktgen_parameters( + TestVcmtsPktgen.PKTGEN_POD_VALUES[0]) + self.assertEquals(parameters, self.PKTGEN_PARAMETERS) + + def test_start_pktgen(self): + self.setup_helper.ssh_helper = mock.MagicMock() + self.setup_helper.start_pktgen(TestVcmtsPktgen.PKTGEN_POD_VALUES[0]) + self.setup_helper.ssh_helper.send_command.assert_called_with( + self.PKTGEN_PARAMETERS) + + def test_setup_vnf_environment(self): + self.assertIsNone(self.setup_helper.setup_vnf_environment()) + +class TestVcmtsPktgen(unittest.TestCase): + + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{ + "benchmark": { + "kpi": [ + "upstream/bits_per_second" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "vCMTS Pktgen Kubernetes", + "id": "VcmtsPktgen", + "mgmt-interface": { + "ip": "192.168.24.150", + "key_filename": "/tmp/yardstick_key-a3b663c2", + "user": "root", + "vdu-id": "vcmtspktgen-kubernetes" + }, + "name": "vcmtspktgen", + "short-name": "vcmtspktgen", + "vdu": [ + { + "description": "vCMTS Pktgen Kubernetes", + "external-interface": [], + "id": "vcmtspktgen-kubernetes", + "name": "vcmtspktgen-kubernetes" + } + ], + "vm-flavor": { + "memory-mb": "4096", + "vcpu-count": "4" + } + }] + }} + + PKTGEN_POD_VALUES = [ + { + "num_ports": "4", + "pktgen_id": "0", + "ports": [ + { + "net_pktgen": "18:02.0", + "num_ofdm": "1", + "num_subs": "100", + "port_0": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:02.1", + "num_ofdm": "1", + "num_subs": "100", + "port_1": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:02.2", + "num_ofdm": "1", + "num_subs": "100", + "port_2": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:02.3", + "num_ofdm": "1", + "num_subs": "100", + "port_3": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_4": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_5": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_6": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_7": "", + "traffic_type": "imix1" + } + ] + }, + { + "num_ports": 4, + "pktgen_id": 1, + "ports": [ + { + "net_pktgen": "18:0a.0", + "num_ofdm": "1", + "num_subs": "100", + "port_0": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:0a.1", + "num_ofdm": "1", + "num_subs": "100", + "port_1": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:0a.2", + "num_ofdm": "1", + "num_subs": "100", + "port_2": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "18:0a.3", + "num_ofdm": "1", + "num_subs": "100", + "port_3": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_4": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_5": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_6": "", + "traffic_type": "imix1" + }, + { + "net_pktgen": "00:00.0", + "num_ofdm": "1", + "num_subs": "100", + "port_7": "", + "traffic_type": "imix1" + } + ] + } + ] + + SCENARIO_CFG = { + "nodes": { + "tg__0": "pktgen0-k8syardstick-a3b663c2", + "vnf__0": "vnf0us-k8syardstick-a3b663c2", + "vnf__1": "vnf0ds-k8syardstick-a3b663c2" + }, + "options": { + "pktgen_values": "/tmp/pktgen_values.yaml", + "tg__0": { + "pktgen_id": 0 + }, + "vcmts_influxdb_ip": "10.80.5.150", + "vcmts_influxdb_port": 8086, + "vcmtsd_values": "/tmp/vcmtsd_values.yaml", + "vnf__0": { + "sg_id": 0, + "stream_dir": "us" + }, + "vnf__1": { + "sg_id": 0, + "stream_dir": "ds" + } + }, + "task_id": "a3b663c2-e616-4777-b6d0-ec2ea7a06f42", + "task_path": "samples/vnf_samples/nsut/cmts", + "tc": "tc_vcmts_k8s_pktgen", + "topology": "k8s_vcmts_topology.yaml", + "traffic_profile": "../../traffic_profiles/fixed.yaml", + "type": "NSPerf" + } + + CONTEXT_CFG = { + "networks": { + "flannel": { + "name": "flannel" + }, + "xe0": { + "name": "xe0" + }, + "xe1": { + "name": "xe1" + } + }, + "nodes": { + "tg__0": { + "VNF model": "../../vnf_descriptors/tg_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.24.150", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.24.150", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.24.150", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.24.150", + "key_filename": "/tmp/yardstick_key-a3b663c2", + "member-vnf-index": "1", + "name": "pktgen0-k8syardstick-a3b663c2", + "private_ip": "192.168.24.150", + "service_ports": [ + { + "name": "ssh", + "node_port": 60270, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 43619, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 60270, + "user": "root", + "vnfd-id-ref": "tg__0" + }, + "vnf__0": { + "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.100.132", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.100.132", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.100.132", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.100.132", + "key_filename": "/tmp/yardstick_key-a3b663c2", + "member-vnf-index": "3", + "name": "vnf0us-k8syardstick-a3b663c2", + "private_ip": "192.168.100.132", + "service_ports": [ + { + "name": "ssh", + "node_port": 57057, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 29700, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 57057, + "user": "root", + "vnfd-id-ref": "vnf__0" + }, + "vnf__1": { + "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.100.134", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.100.134", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.100.134", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.100.134", + "key_filename": "/tmp/yardstick_key-a3b663c2", + "member-vnf-index": "4", + "name": "vnf0ds-k8syardstick-a3b663c2", + "private_ip": "192.168.100.134", + "service_ports": [ + { + "name": "ssh", + "node_port": 18581, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 18469, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 18581, + "user": "root", + "vnfd-id-ref": "vnf__1" + } + } + } + + PKTGEN_VALUES_PATH = "/tmp/pktgen_values.yaml" + + PKTGEN_VALUES = \ + "serviceAccount: cmk-serviceaccount\n" \ + "images:\n" \ + " vcmts_pktgen: vcmts-pktgen:v18.10\n" \ + "topology:\n" \ + " pktgen_replicas: 8\n" \ + " pktgen_pods:\n" \ + " - pktgen_id: 0\n" \ + " num_ports: 4\n" \ + " ports:\n" \ + " - port_0:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.0\n" \ + " - port_1:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.1\n" \ + " - port_2:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.2\n" \ + " - port_3:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.3\n" \ + " - port_4:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.4\n" \ + " - port_5:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.5\n" \ + " - port_6:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.6\n" \ + " - port_7:\n" \ + " traffic_type: 'imix2'\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " net_pktgen: 8a:02.7\n" + + def setUp(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.vcmts_pktgen = tg_vcmts_pktgen.VcmtsPktgen(NAME, vnfd) + self.vcmts_pktgen._start_server = mock.Mock(return_value=0) + self.vcmts_pktgen.resource_helper = mock.MagicMock() + self.vcmts_pktgen.setup_helper = mock.MagicMock() + + def test___init__(self): + self.assertFalse(self.vcmts_pktgen.traffic_finished) + self.assertIsNotNone(self.vcmts_pktgen.setup_helper) + self.assertIsNotNone(self.vcmts_pktgen.resource_helper) + + def test_extract_pod_cfg(self): + pod_cfg = self.vcmts_pktgen.extract_pod_cfg(self.PKTGEN_POD_VALUES, "0") + self.assertIsNotNone(pod_cfg) + self.assertEqual(pod_cfg["pktgen_id"], "0") + pod_cfg = self.vcmts_pktgen.extract_pod_cfg(self.PKTGEN_POD_VALUES, "4") + self.assertIsNone(pod_cfg) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate_missing_pktgen_values_key(self, *args): + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options'].pop('pktgen_values', None) + with self.assertRaises(KeyError): + self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate_missing_pktgen_values_file(self, *args): + if os.path.isfile(self.PKTGEN_VALUES_PATH): + os.remove(self.PKTGEN_VALUES_PATH) + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options']['pktgen_values'] = self.PKTGEN_VALUES_PATH + with self.assertRaises(RuntimeError): + self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate_empty_pktgen_values_file(self, *args): + yaml_sample = open(self.PKTGEN_VALUES_PATH, 'w') + yaml_sample.write("") + yaml_sample.close() + + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options']['pktgen_values'] = self.PKTGEN_VALUES_PATH + with self.assertRaises(RuntimeError): + self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + if os.path.isfile(self.PKTGEN_VALUES_PATH): + os.remove(self.PKTGEN_VALUES_PATH) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate_invalid_pktgen_id(self, *args): + yaml_sample = open(self.PKTGEN_VALUES_PATH, 'w') + yaml_sample.write(self.PKTGEN_VALUES) + yaml_sample.close() + + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options'][NAME]['pktgen_id'] = 12 + with self.assertRaises(KeyError): + self.vcmts_pktgen.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + if os.path.isfile(self.PKTGEN_VALUES_PATH): + os.remove(self.PKTGEN_VALUES_PATH) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server', + return_value='fake_context') + def test_instantiate_all_valid(self, *args): + yaml_sample = open(self.PKTGEN_VALUES_PATH, 'w') + yaml_sample.write(self.PKTGEN_VALUES) + yaml_sample.close() + + self.vcmts_pktgen.instantiate(self.SCENARIO_CFG, self.CONTEXT_CFG) + self.assertIsNotNone(self.vcmts_pktgen.pod_cfg) + self.assertEqual(self.vcmts_pktgen.pod_cfg["pktgen_id"], "0") + + if os.path.isfile(self.PKTGEN_VALUES_PATH): + os.remove(self.PKTGEN_VALUES_PATH) + + def test_run_traffic_failed_connect(self): + self.vcmts_pktgen.pktgen_helper = mock.MagicMock() + self.vcmts_pktgen.pktgen_helper.connect.return_value = False + with self.assertRaises(exceptions.PktgenActionError): + self.vcmts_pktgen.run_traffic({}) + + def test_run_traffic_successful_connect(self): + self.vcmts_pktgen.pktgen_helper = mock.MagicMock() + self.vcmts_pktgen.pktgen_helper.connect.return_value = True + self.vcmts_pktgen.pktgen_rate = 8.0 + self.assertTrue(self.vcmts_pktgen.run_traffic({})) + self.vcmts_pktgen.pktgen_helper.connect.assert_called_once() + self.vcmts_pktgen.pktgen_helper.send_command.assert_called_with( + 'pktgen.start("all");') diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_udp_replay.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_udp_replay.py new file mode 100644 index 000000000..aabd402a6 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_udp_replay.py @@ -0,0 +1,470 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import mock +import os + +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.vnf_generic.vnf.udp_replay import UdpReplayApproxVnf +from yardstick.network_services.vnf_generic.vnf.sample_vnf import ScenarioHelper + + +SSH_HELPER = 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper' + +TEST_FILE_YAML = 'nsb_test_case.yaml' + +NAME = "vnf__1" + + +@mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.Process") +class TestUdpReplayApproxVnf(unittest.TestCase): + + VNFD_0 = { + 'short-name': 'UdpReplayVnf', + 'vdu': [ + { + 'description': 'UDPReplay approximation using DPDK', + 'routing_table': [ + { + 'netmask': '255.255.255.0', + 'if': 'xe0', + 'network': '152.16.100.20', + 'gateway': '152.16.100.20', + }, + { + 'netmask': '255.255.255.0', + 'if': 'xe1', + 'network': '152.16.40.20', + 'gateway': '152.16.40.20', + } + ], + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'driver': 'i40e', + 'local_iface_name': 'xe0', + 'bandwidth': '10 Gbps', + 'local_ip': '152.16.100.19', + 'local_mac': '00:00:00:00:00:02', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + 'netmask': '255.255.255.0', + 'dst_ip': '152.16.100.20', + 'type': 'PCI-PASSTHROUGH', + 'vld_id': 'uplink_0', + 'ifname': 'xe0', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0', + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'driver': 'i40e', + 'local_iface_name': 'xe1', + 'bandwidth': '10 Gbps', + 'local_ip': '152.16.40.19', + 'local_mac': '00:00:00:00:00:01', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + 'netmask': '255.255.255.0', + 'dst_ip': '152.16.40.20', + 'type': 'PCI-PASSTHROUGH', + 'vld_id': 'downlink_0', + 'ifname': 'xe1', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1', + } + ], + 'nd_route_tbl': [ + { + 'netmask': '112', + 'if': 'xe0', + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + }, + { + 'netmask': '112', + 'if': 'xe1', + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + } + ], + 'id': 'udpreplayvnf-baremetal', + 'name': 'udpreplayvnf-baremetal', + } + ], + 'description': 'UDPReplay approximation using DPDK', + 'name': 'VPEVnfSsh', + 'mgmt-interface': { + 'vdu-id': 'udpreplay-baremetal', + 'host': '1.2.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.2.1.1', + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ] + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + } + ], + 'id': 'UdpReplayApproxVnf', + } + + SCENARIO_CFG = { + "options": { + "packetsize": 64, + "traffic_type": 4, + "rfc2544": { + "allowed_drop_rate": "0.8 - 1", + }, + "vnf__1": { + "rules": "acl_1rule.yaml", + "vnf_config": { + "lb_config": "SW", + "lb_count": 1, + "worker_config": "1C/1T", + "worker_threads": 1, + }, + "hw_csum": "false", + } + }, + "task_id": "a70bdf4a-8e67-47a3-9dc1-273c14506eb7", + "tc": "tc_ipv4_1Mflow_64B_packetsize", + "runner": { + "object": "NetworkServiceTestCase", + "interval": 35, + "output_filename": "/tmp/yardstick.out", + "runner_id": 74476, "duration": 400, + "type": "Duration" + }, + "traffic_profile": "ipv4_throughput_acl.yaml", + "traffic_options": { + "flow": "ipv4_Packets_acl.yaml", + "imix": "imix_voice.yaml" + }, + "type": "ISB", + "nodes": { + "tg__2": "trafficgen_2.yardstick", + "tg__1": "trafficgen_1.yardstick", + "vnf__1": "vnf.yardstick" + }, + "topology": "udpreplay-tg-topology-baremetal.yaml" + } + + CONTEXT_CFG = { + "nodes": { + "vnf__1": { + "vnfd-id-ref": "vnf__1", + "ip": "1.2.1.1", + "interfaces": { + "xe0": { + "local_iface_name": "ens786f0", + "vld_id": UdpReplayApproxVnf.UPLINK, + "netmask": "255.255.255.0", + "vpci": "0000:05:00.0", + "local_ip": "152.16.100.19", + "driver": "i40e", + "dst_ip": "152.16.100.20", + "local_mac": "00:00:00:00:00:02", + "dst_mac": "00:00:00:00:00:04", + "dpdk_port_num": 0 + }, + "xe1": { + "local_iface_name": "ens786f1", + "vld_id": UdpReplayApproxVnf.DOWNLINK, + "netmask": "255.255.255.0", + "vpci": "0000:05:00.1", + "local_ip": "152.16.40.19", + "driver": "i40e", + "dst_ip": "152.16.40.20", + "local_mac": "00:00:00:00:00:01", + "dst_mac": "00:00:00:00:00:03", + "dpdk_port_num": 1 + } + }, + "host": "1.2.1.1", + "user": "root", + "nd_route_tbl": [ + { + "netmask": "112", + "if": "xe0", + "gateway": "0064:ff9b:0:0:0:0:9810:6414", + "network": "0064:ff9b:0:0:0:0:9810:6414" + }, + { + "netmask": "112", + "if": "xe1", + "gateway": "0064:ff9b:0:0:0:0:9810:2814", + "network": "0064:ff9b:0:0:0:0:9810:2814" + } + ], + "password": "r00t", + "VNF model": "udp_replay.yaml", + "name": "vnf.yardstick", + "member-vnf-index": "2", + "routing_table": [ + { + "netmask": "255.255.255.0", + "if": "xe0", + "gateway": "152.16.100.20", + "network": "152.16.100.20" + }, + { + "netmask": "255.255.255.0", + "if": "xe1", + "gateway": "152.16.40.20", + "network": "152.16.40.20" + } + ], + "role": "vnf" + }, + "trafficgen_2.yardstick": { + "member-vnf-index": "3", + "role": "TrafficGen", + "name": "trafficgen_2.yardstick", + "vnfd-id-ref": "tg__2", + "ip": "1.2.1.1", + "interfaces": { + "xe0": { + "local_iface_name": "ens513f0", + "vld_id": UdpReplayApproxVnf.DOWNLINK, + "netmask": "255.255.255.0", + "vpci": "0000:02:00.0", + "local_ip": "152.16.40.20", + "driver": "ixgbe", + "dst_ip": "152.16.40.19", + "local_mac": "00:00:00:00:00:03", + "dst_mac": "00:00:00:00:00:01", + "dpdk_port_num": 0 + }, + "xe1": { + "local_iface_name": "ens513f1", + "netmask": "255.255.255.0", + "network": "202.16.100.0", + "local_ip": "202.16.100.20", + "driver": "ixgbe", + "local_mac": "00:1e:67:d0:60:5d", + "vpci": "0000:02:00.1", + "dpdk_port_num": 1 + } + }, + "password": "r00t", + "VNF model": "l3fwd_vnf.yaml", + "user": "root" + }, + "trafficgen_1.yardstick": { + "member-vnf-index": "1", + "role": "TrafficGen", + "name": "trafficgen_1.yardstick", + "vnfd-id-ref": "tg__1", + "ip": "1.2.1.1", + "interfaces": { + "xe0": { + "local_iface_name": "ens785f0", + "vld_id": UdpReplayApproxVnf.UPLINK, + "netmask": "255.255.255.0", + "vpci": "0000:05:00.0", + "local_ip": "152.16.100.20", + "driver": "i40e", + "dst_ip": "152.16.100.19", + "local_mac": "00:00:00:00:00:04", + "dst_mac": "00:00:00:00:00:02", + "dpdk_port_num": 0 + }, + "xe1": { + "local_ip": "152.16.100.21", + "driver": "i40e", + "vpci": "0000:05:00.1", + "dpdk_port_num": 1, + "local_iface_name": "ens785f1", + "netmask": "255.255.255.0", + "local_mac": "00:00:00:00:00:01" + } + }, + "password": "r00t", + "VNF model": "tg_rfc2544_tpl.yaml", + "user": "root" + } + } + } + + def test___init__(self, *args): + udp_replay_approx_vnf = UdpReplayApproxVnf(NAME, self.VNFD_0) + self.assertIsNone(udp_replay_approx_vnf._vnf_process) + + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.time") + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch(SSH_HELPER) + def test_collect_kpi(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD_0 + get_stats_ret_val = \ + "stats\r\r\n\r\nUDP_Replay stats:\r\n--------------\r\n" \ + "Port\t\tRx Packet\t\tTx Packet\t\tRx Pkt Drop\t\tTx Pkt Drop\t\tarp_pkts \r\n"\ + "0\t\t7374156\t\t7374136\t\t\t0\t\t\t0\t\t\t0\r\n" \ + "1\t\t7374316\t\t7374315\t\t\t0\t\t\t0\t\t\t0\r\n\r\nReplay>\r\r\nReplay>" + udp_replay_approx_vnf = UdpReplayApproxVnf(NAME, vnfd) + udp_replay_approx_vnf.scenario_helper.scenario_cfg = { + 'nodes': {udp_replay_approx_vnf.name: "mock"} + } + udp_replay_approx_vnf.q_in = mock.MagicMock() + udp_replay_approx_vnf.q_out = mock.MagicMock() + udp_replay_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + udp_replay_approx_vnf.all_ports = ["xe0", "xe1"] + udp_replay_approx_vnf.get_stats = mock.Mock(return_value=get_stats_ret_val) + result = { + 'physical_node': 'mock_node', + 'collect_stats': {}, + 'packets_dropped': 0, + 'packets_fwd': 14748451, + 'packets_in': 14748472 + } + self.assertEqual(result, udp_replay_approx_vnf.collect_kpi()) + + @mock.patch(SSH_HELPER) + def test_get_stats(self, ssh, *args): + mock_ssh(ssh) + + udp_replay_approx_vnf = UdpReplayApproxVnf(NAME, self.VNFD_0) + udp_replay_approx_vnf.q_in = mock.MagicMock() + udp_replay_approx_vnf.q_out = mock.MagicMock() + udp_replay_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + mock_result = \ + "CG-NAPT(.*\n)*Received 100, Missed 0, Dropped 0,Translated 100,ingress" + + udp_replay_approx_vnf.vnf_execute = mock.Mock(return_value=mock_result) + + self.assertEqual(mock_result, + udp_replay_approx_vnf.get_stats()) + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + @mock.patch.object(ctx_base.Context, 'get_context_from_server') + @mock.patch(SSH_HELPER) + def test__build_config(self, ssh, mock_get_ctx, *args): + mock_ssh(ssh) + + nfvi_context = mock.Mock() + nfvi_context.attrs = {'nfvi_type': 'baremetal'} + mock_get_ctx.return_value = nfvi_context + + udp_replay_approx_vnf = UdpReplayApproxVnf(NAME, self.VNFD_0) + udp_replay_approx_vnf.queue_wrapper = mock.MagicMock() + udp_replay_approx_vnf.nfvi_context = mock_get_ctx + udp_replay_approx_vnf.nfvi_context.attrs = {'nfvi_type': 'baremetal'} + udp_replay_approx_vnf.setup_helper.bound_pci = [] + udp_replay_approx_vnf.ssh_helper.provision_tool = mock.MagicMock(return_value="tool_path") + udp_replay_approx_vnf.scenario_helper = ScenarioHelper(name='vnf__1') + udp_replay_approx_vnf.scenario_helper.scenario_cfg = self.SCENARIO_CFG + + cmd_line = udp_replay_approx_vnf._build_config() + + expected = \ + "sudo tool_path --log-level=5 -c 0x7 -n 4 -w -- -p 0x3 --config='(0,0,1),(1,0,2)'" + self.assertEqual(cmd_line, expected) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.udp_replay.open') + @mock.patch.object(ctx_base.Context, 'get_context_from_server') + @mock.patch(SSH_HELPER) + def test__build_pipeline_kwargs(self, ssh, mock_get_ctx, *args): + mock_ssh(ssh) + + nfvi_context = mock.Mock() + nfvi_context.attrs = {'nfvi_type': "baremetal"} + mock_get_ctx.return_value = nfvi_context + + udp_replay_approx_vnf = UdpReplayApproxVnf(NAME, self.VNFD_0) + udp_replay_approx_vnf.setup_helper.bound_pci = ['0000:00:0.1', '0000:00:0.3'] + udp_replay_approx_vnf.all_ports = ["xe0", "xe1"] + udp_replay_approx_vnf.ssh_helper.provision_tool = mock.MagicMock(return_value="tool_path") + udp_replay_approx_vnf.scenario_helper = ScenarioHelper(name='vnf__1') + udp_replay_approx_vnf.scenario_helper.scenario_cfg = self.SCENARIO_CFG + + udp_replay_approx_vnf._build_pipeline_kwargs() + + self.assertEqual(udp_replay_approx_vnf.pipeline_kwargs, { + 'config': '(0,0,1),(1,0,2)', + 'cpu_mask_hex': '0x7', + 'hw_csum': '', + 'port_mask_hex': '0x3', + 'tool_path': 'tool_path', + 'whitelist': '0000:00:0.1 -w 0000:00:0.3' + }) + + @mock.patch(SSH_HELPER) + def test_run_udp_replay(self, ssh, *args): + mock_ssh(ssh) + + udp_replay_approx_vnf = UdpReplayApproxVnf(NAME, self.VNFD_0) + udp_replay_approx_vnf._build_config = mock.MagicMock() + udp_replay_approx_vnf.queue_wrapper = mock.MagicMock() + udp_replay_approx_vnf.scenario_helper = mock.MagicMock() + + udp_replay_approx_vnf._run() + + udp_replay_approx_vnf.ssh_helper.run.assert_called_once() + + @mock.patch.object(ctx_base.Context, 'get_context_from_server') + @mock.patch(SSH_HELPER) + def test_instantiate(self, ssh, *args): + mock_ssh(ssh) + + udp_replay_approx_vnf = UdpReplayApproxVnf(NAME, self.VNFD_0) + udp_replay_approx_vnf.q_out.put("Replay>") + udp_replay_approx_vnf.WAIT_TIME = 0 + udp_replay_approx_vnf.setup_helper.setup_vnf_environment = mock.Mock() + + udp_replay_approx_vnf.deploy_helper = mock.MagicMock() + udp_replay_approx_vnf.deploy_vnfs = mock.MagicMock() + self.assertIsNone(udp_replay_approx_vnf.instantiate(self.SCENARIO_CFG, self.CONTEXT_CFG)) + + udp_replay_approx_vnf._vnf_process.is_alive = mock.Mock(return_value=1) + udp_replay_approx_vnf._vnf_process.exitcode = 0 + + self.assertEqual(udp_replay_approx_vnf.wait_for_instantiate(), 0) + + @mock.patch.object(ctx_base.Context, 'get_context_from_server') + @mock.patch('yardstick.ssh.SSH') + @mock.patch(SSH_HELPER) + def test_instantiate_panic(self, *args): + udp_replay_approx_vnf = UdpReplayApproxVnf(NAME, self.VNFD_0) + udp_replay_approx_vnf.WAIT_TIME = 0 + udp_replay_approx_vnf.q_out.put("some text PANIC some text") + udp_replay_approx_vnf.setup_helper.setup_vnf_environment = mock.Mock() + + udp_replay_approx_vnf.deploy_helper = mock.MagicMock() + self.assertIsNone(udp_replay_approx_vnf.instantiate(self.SCENARIO_CFG, self.CONTEXT_CFG)) + with self.assertRaises(RuntimeError): + udp_replay_approx_vnf.wait_for_instantiate() diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vcmts_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vcmts_vnf.py new file mode 100755 index 000000000..11e3d6e17 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vcmts_vnf.py @@ -0,0 +1,651 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import mock +import copy +import os + +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh +from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper +from yardstick.network_services.vnf_generic.vnf import vcmts_vnf +from yardstick.common import exceptions + +from influxdb.resultset import ResultSet + +NAME = "vnf__0" + + +class TestInfluxDBHelper(unittest.TestCase): + + def test___init__(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + self.assertEqual(influxdb_helper._vcmts_influxdb_ip, "localhost") + self.assertEqual(influxdb_helper._vcmts_influxdb_port, 8086) + self.assertIsNotNone(influxdb_helper._last_upstream_rx) + self.assertIsNotNone(influxdb_helper._last_values_time) + + def test_start(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + influxdb_helper.start() + self.assertIsNotNone(influxdb_helper._read_client) + self.assertIsNotNone(influxdb_helper._write_client) + + def test__get_last_value_time(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + self.assertEqual(influxdb_helper._get_last_value_time('cpu_value'), + vcmts_vnf.InfluxDBHelper.INITIAL_VALUE) + + influxdb_helper._last_values_time['cpu_value'] = "RANDOM" + self.assertEqual(influxdb_helper._get_last_value_time('cpu_value'), + "RANDOM") + + def test__set_last_value_time(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + influxdb_helper._set_last_value_time('cpu_value', '00:00') + self.assertEqual(influxdb_helper._last_values_time['cpu_value'], + "'00:00'") + + def test__query_measurement(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + influxdb_helper._read_client = mock.MagicMock() + + resulted_generator = mock.MagicMock() + resulted_generator.keys.return_value = [] + influxdb_helper._read_client.query.return_value = resulted_generator + query_result = influxdb_helper._query_measurement('cpu_value') + self.assertIsNone(query_result) + + resulted_generator = mock.MagicMock() + resulted_generator.keys.return_value = ["", ""] + resulted_generator.get_points.return_value = ResultSet({"":""}) + influxdb_helper._read_client.query.return_value = resulted_generator + query_result = influxdb_helper._query_measurement('cpu_value') + self.assertIsNotNone(query_result) + + def test__rw_measurment(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + influxdb_helper._query_measurement = mock.MagicMock() + influxdb_helper._query_measurement.return_value = None + influxdb_helper._rw_measurment('cpu_value', []) + self.assertEqual(len(influxdb_helper._last_values_time), 0) + + entry = { + "type":"type", + "host":"host", + "time":"time", + "id": "1", + "value": "1.0" + } + influxdb_helper._query_measurement.return_value = [entry] + influxdb_helper._write_client = mock.MagicMock() + influxdb_helper._rw_measurment('cpu_value', ["id", "value"]) + self.assertEqual(len(influxdb_helper._last_values_time), 1) + influxdb_helper._write_client.write_points.assert_called_once() + + def test_copy_kpi(self): + influxdb_helper = vcmts_vnf.InfluxDBHelper("localhost", 8086) + influxdb_helper._rw_measurment = mock.MagicMock() + influxdb_helper.copy_kpi() + influxdb_helper._rw_measurment.assert_called() + + +class TestVcmtsdSetupEnvHelper(unittest.TestCase): + POD_CFG = { + "cm_crypto": "aes", + "cpu_socket_id": "0", + "ds_core_pool_index": "2", + "ds_core_type": "exclusive", + "net_ds": "1a:02.1", + "net_us": "1a:02.0", + "num_ofdm": "1", + "num_subs": "100", + "power_mgmt": "pm_on", + "qat": "qat_off", + "service_group_config": "", + "sg_id": "0", + "vcmtsd_image": "vcmts-d:perf" + } + + OPTIONS = { + "pktgen_values": "/tmp/pktgen_values.yaml", + "tg__0": { + "pktgen_id": 0 + }, + "vcmts_influxdb_ip": "10.80.5.150", + "vcmts_influxdb_port": 8086, + "vcmtsd_values": "/tmp/vcmtsd_values.yaml", + "vnf__0": { + "sg_id": 0, + "stream_dir": "us" + }, + "vnf__1": { + "sg_id": 0, + "stream_dir": "ds" + } + } + + def setUp(self): + vnfd_helper = VnfdHelper( + TestVcmtsVNF.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + + self.setup_helper = vcmts_vnf.VcmtsdSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + + def _build_us_parameters(self): + return vcmts_vnf.VcmtsdSetupEnvHelper.BASE_PARAMETERS + " " \ + + " /opt/bin/cmk isolate --conf-dir=/etc/cmk" \ + + " --socket-id=" + str(self.POD_CFG['cpu_socket_id']) \ + + " --pool=shared" \ + + " /vcmts-config/run_upstream.sh " + self.POD_CFG['sg_id'] \ + + " " + self.POD_CFG['ds_core_type'] \ + + " " + str(self.POD_CFG['num_ofdm']) + "ofdm" \ + + " " + str(self.POD_CFG['num_subs']) + "cm" \ + + " " + self.POD_CFG['cm_crypto'] \ + + " " + self.POD_CFG['qat'] \ + + " " + self.POD_CFG['net_us'] \ + + " " + self.POD_CFG['power_mgmt'] + + def test_build_us_parameters(self): + constructed = self._build_us_parameters() + result = self.setup_helper.build_us_parameters(self.POD_CFG) + self.assertEqual(constructed, result) + + def _build_ds_parameters(self): + return vcmts_vnf.VcmtsdSetupEnvHelper.BASE_PARAMETERS + " " \ + + " /opt/bin/cmk isolate --conf-dir=/etc/cmk" \ + + " --socket-id=" + str(self.POD_CFG['cpu_socket_id']) \ + + " --pool=" + self.POD_CFG['ds_core_type'] \ + + " /vcmts-config/run_downstream.sh " + self.POD_CFG['sg_id'] \ + + " " + self.POD_CFG['ds_core_type'] \ + + " " + str(self.POD_CFG['ds_core_pool_index']) \ + + " " + str(self.POD_CFG['num_ofdm']) + "ofdm" \ + + " " + str(self.POD_CFG['num_subs']) + "cm" \ + + " " + self.POD_CFG['cm_crypto'] \ + + " " + self.POD_CFG['qat'] \ + + " " + self.POD_CFG['net_ds'] \ + + " " + self.POD_CFG['power_mgmt'] + + def test_build_ds_parameters(self): + constructed = self._build_ds_parameters() + result = self.setup_helper.build_ds_parameters(self.POD_CFG) + self.assertEqual(constructed, result) + + def test_build_cmd(self): + us_constructed = self._build_us_parameters() + us_result = self.setup_helper.build_cmd('us', self.POD_CFG) + self.assertEqual(us_constructed, us_result) + ds_constructed = self._build_ds_parameters() + ds_result = self.setup_helper.build_cmd('ds', self.POD_CFG) + self.assertEqual(ds_constructed, ds_result) + + def test_run_vcmtsd(self): + us_constructed = self._build_us_parameters() + + vnfd_helper = VnfdHelper( + TestVcmtsVNF.VNFD['vnfd:vnfd-catalog']['vnfd'][0]) + ssh_helper = mock.MagicMock() + scenario_helper = mock.Mock() + scenario_helper.options = self.OPTIONS + + setup_helper = vcmts_vnf.VcmtsdSetupEnvHelper( + vnfd_helper, ssh_helper, scenario_helper) + + setup_helper.run_vcmtsd('us', self.POD_CFG) + ssh_helper.send_command.assert_called_with(us_constructed) + + def test_setup_vnf_environment(self): + self.assertIsNone(self.setup_helper.setup_vnf_environment()) + +class TestVcmtsVNF(unittest.TestCase): + + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{ + "benchmark": { + "kpi": [ + "upstream/bits_per_second" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "vCMTS Upstream-Downstream Kubernetes", + "id": "VcmtsVNF", + "mgmt-interface": { + "ip": "192.168.100.35", + "key_filename": "/tmp/yardstick_key-81dcca91", + "user": "root", + "vdu-id": "vcmtsvnf-kubernetes" + }, + "name": "vcmtsvnf", + "short-name": "vcmtsvnf", + "vdu": [ + { + "description": "vCMTS Upstream-Downstream Kubernetes", + "external-interface": [], + "id": "vcmtsvnf-kubernetes", + "name": "vcmtsvnf-kubernetes" + } + ], + "vm-flavor": { + "memory-mb": "4096", + "vcpu-count": "4" + } + }] + } + } + + POD_CFG = [ + { + "cm_crypto": "aes", + "cpu_socket_id": "0", + "ds_core_pool_index": "2", + "ds_core_type": "exclusive", + "net_ds": "1a:02.1", + "net_us": "1a:02.0", + "num_ofdm": "1", + "num_subs": "100", + "power_mgmt": "pm_on", + "qat": "qat_off", + "service_group_config": "", + "sg_id": "0", + "vcmtsd_image": "vcmts-d:perf" + }, + ] + + SCENARIO_CFG = { + "nodes": { + "tg__0": "pktgen0-k8syardstick-afae18b2", + "vnf__0": "vnf0us-k8syardstick-afae18b2", + "vnf__1": "vnf0ds-k8syardstick-afae18b2" + }, + "options": { + "pktgen_values": "/tmp/pktgen_values.yaml", + "tg__0": { + "pktgen_id": 0 + }, + "vcmts_influxdb_ip": "10.80.5.150", + "vcmts_influxdb_port": 8086, + "vcmtsd_values": "/tmp/vcmtsd_values.yaml", + "vnf__0": { + "sg_id": 0, + "stream_dir": "us" + }, + "vnf__1": { + "sg_id": 0, + "stream_dir": "ds" + } + }, + "task_id": "afae18b2-9902-477f-8128-49afde7c3040", + "task_path": "samples/vnf_samples/nsut/cmts", + "tc": "tc_vcmts_k8s_pktgen", + "topology": "k8s_vcmts_topology.yaml", + "traffic_profile": "../../traffic_profiles/fixed.yaml", + "type": "NSPerf" + } + + CONTEXT_CFG = { + "networks": { + "flannel": { + "name": "flannel" + }, + "xe0": { + "name": "xe0" + }, + "xe1": { + "name": "xe1" + } + }, + "nodes": { + "tg__0": { + "VNF model": "../../vnf_descriptors/tg_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.24.110", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.24.110", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.24.110", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.24.110", + "key_filename": "/tmp/yardstick_key-afae18b2", + "member-vnf-index": "1", + "name": "pktgen0-k8syardstick-afae18b2", + "private_ip": "192.168.24.110", + "service_ports": [ + { + "name": "ssh", + "node_port": 17153, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 51250, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 17153, + "user": "root", + "vnfd-id-ref": "tg__0" + }, + "vnf__0": { + "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.100.53", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.100.53", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.100.53", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.100.53", + "key_filename": "/tmp/yardstick_key-afae18b2", + "member-vnf-index": "3", + "name": "vnf0us-k8syardstick-afae18b2", + "private_ip": "192.168.100.53", + "service_ports": [ + { + "name": "ssh", + "node_port": 34027, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 32580, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 34027, + "user": "root", + "vnfd-id-ref": "vnf__0" + }, + "vnf__1": { + "VNF model": "../../vnf_descriptors/vnf_vcmts_tpl.yaml", + "interfaces": { + "flannel": { + "local_ip": "192.168.100.52", + "local_mac": None, + "network_name": "flannel" + }, + "xe0": { + "local_ip": "192.168.100.52", + "local_mac": None, + "network_name": "xe0" + }, + "xe1": { + "local_ip": "192.168.100.52", + "local_mac": None, + "network_name": "xe1" + } + }, + "ip": "192.168.100.52", + "key_filename": "/tmp/yardstick_key-afae18b2", + "member-vnf-index": "4", + "name": "vnf0ds-k8syardstick-afae18b2", + "private_ip": "192.168.100.52", + "service_ports": [ + { + "name": "ssh", + "node_port": 58661, + "port": 22, + "protocol": "TCP", + "target_port": 22 + }, + { + "name": "lua", + "node_port": 58233, + "port": 22022, + "protocol": "TCP", + "target_port": 22022 + } + ], + "ssh_port": 58661, + "user": "root", + "vnfd-id-ref": "vnf__1" + }, + } + } + + VCMTSD_VALUES_PATH = "/tmp/vcmtsd_values.yaml" + + VCMTSD_VALUES = \ + "serviceAccount: cmk-serviceaccount\n" \ + "topology:\n" \ + " vcmts_replicas: 16\n" \ + " vcmts_pods:\n" \ + " - service_group_config:\n" \ + " sg_id: 0\n" \ + " net_us: 18:02.0\n" \ + " net_ds: 18:02.1\n" \ + " num_ofdm: 4\n" \ + " num_subs: 300\n" \ + " cm_crypto: aes\n" \ + " qat: qat_off\n" \ + " power_mgmt: pm_on\n" \ + " cpu_socket_id: 0\n" \ + " ds_core_type: exclusive\n" \ + " ds_core_pool_index: 0\n" \ + " vcmtsd_image: vcmts-d:feat" + + VCMTSD_VALUES_INCOMPLETE = \ + "serviceAccount: cmk-serviceaccount\n" \ + "topology:\n" \ + " vcmts_replicas: 16" + + def setUp(self): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + self.vnf = vcmts_vnf.VcmtsVNF(NAME, vnfd) + + def test___init__(self, *args): + self.assertIsNotNone(self.vnf.setup_helper) + + def test_extract_pod_cfg(self): + pod_cfg = self.vnf.extract_pod_cfg(self.POD_CFG, "0") + self.assertIsNotNone(pod_cfg) + self.assertEqual(pod_cfg['sg_id'], '0') + pod_cfg = self.vnf.extract_pod_cfg(self.POD_CFG, "1") + self.assertIsNone(pod_cfg) + + def test_instantiate_missing_influxdb_info(self): + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options'].pop('vcmts_influxdb_ip', None) + with self.assertRaises(KeyError): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + def test_instantiate_missing_vcmtsd_values_file(self): + if os.path.isfile(self.VCMTSD_VALUES_PATH): + os.remove(self.VCMTSD_VALUES_PATH) + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options']['vcmtsd_values'] = self.VCMTSD_VALUES_PATH + with self.assertRaises(RuntimeError): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + def test_instantiate_empty_vcmtsd_values_file(self): + yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w') + yaml_sample.write("") + yaml_sample.close() + + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options']['vcmtsd_values'] = self.VCMTSD_VALUES_PATH + with self.assertRaises(RuntimeError): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + if os.path.isfile(self.VCMTSD_VALUES_PATH): + os.remove(self.VCMTSD_VALUES_PATH) + + def test_instantiate_missing_vcmtsd_values_key(self): + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options'].pop('vcmtsd_values', None) + with self.assertRaises(KeyError): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + def test_instantiate_invalid_vcmtsd_values(self): + yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w') + yaml_sample.write(self.VCMTSD_VALUES_INCOMPLETE) + yaml_sample.close() + + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + with self.assertRaises(KeyError): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + if os.path.isfile(self.VCMTSD_VALUES_PATH): + os.remove(self.VCMTSD_VALUES_PATH) + + def test_instantiate_invalid_sg_id(self): + yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w') + yaml_sample.write(self.VCMTSD_VALUES) + yaml_sample.close() + + err_scenario_cfg = copy.deepcopy(self.SCENARIO_CFG) + err_scenario_cfg['options'][NAME]['sg_id'] = 8 + with self.assertRaises(exceptions.IncorrectConfig): + self.vnf.instantiate(err_scenario_cfg, self.CONTEXT_CFG) + + if os.path.isfile(self.VCMTSD_VALUES_PATH): + os.remove(self.VCMTSD_VALUES_PATH) + + @mock.patch('yardstick.network_services.vnf_generic.vnf.vcmts_vnf.VnfSshHelper') + def test_instantiate_all_valid(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vnf = vcmts_vnf.VcmtsVNF(NAME, vnfd) + + yaml_sample = open(self.VCMTSD_VALUES_PATH, 'w') + yaml_sample.write(self.VCMTSD_VALUES) + yaml_sample.close() + + vnf.instantiate(self.SCENARIO_CFG, self.CONTEXT_CFG) + self.assertEqual(vnf.vcmts_influxdb_ip, "10.80.5.150") + self.assertEqual(vnf.vcmts_influxdb_port, 8086) + + if os.path.isfile(self.VCMTSD_VALUES_PATH): + os.remove(self.VCMTSD_VALUES_PATH) + + def test__update_collectd_options(self): + scenario_cfg = {'options': + {'collectd': + {'interval': 3, + 'plugins': + {'plugin3': {'param': 3}}}, + 'vnf__0': + {'collectd': + {'interval': 2, + 'plugins': + {'plugin3': {'param': 2}, + 'plugin2': {'param': 2}}}}}} + context_cfg = {'nodes': + {'vnf__0': + {'collectd': + {'interval': 1, + 'plugins': + {'plugin3': {'param': 1}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}}}}} + expected = {'interval': 1, + 'plugins': + {'plugin3': {'param': 1}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}} + + self.vnf._update_collectd_options(scenario_cfg, context_cfg) + self.assertEqual(self.vnf.setup_helper.collectd_options, expected) + + def test__update_options(self): + options1 = {'interval': 1, + 'param1': 'value1', + 'plugins': + {'plugin3': {'param': 3}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}} + options2 = {'interval': 2, + 'param2': 'value2', + 'plugins': + {'plugin4': {'param': 4}, + 'plugin2': {'param': 2}, + 'plugin1': {'param': 2}}} + expected = {'interval': 1, + 'param1': 'value1', + 'param2': 'value2', + 'plugins': + {'plugin4': {'param': 4}, + 'plugin3': {'param': 3}, + 'plugin2': {'param': 1}, + 'plugin1': {'param': 1}}} + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vnf = vcmts_vnf.VcmtsVNF('vnf1', vnfd) + vnf._update_options(options2, options1) + self.assertEqual(options2, expected) + + def test_wait_for_instantiate(self): + self.assertIsNone(self.vnf.wait_for_instantiate()) + + def test_terminate(self): + self.assertIsNone(self.vnf.terminate()) + + def test_scale(self): + self.assertIsNone(self.vnf.scale()) + + def test_collect_kpi(self): + self.vnf.influxdb_helper = mock.MagicMock() + self.vnf.collect_kpi() + self.vnf.influxdb_helper.copy_kpi.assert_called_once() + + def test_start_collect(self): + self.vnf.vcmts_influxdb_ip = "localhost" + self.vnf.vcmts_influxdb_port = 8800 + + self.assertIsNone(self.vnf.start_collect()) + self.assertIsNotNone(self.vnf.influxdb_helper) + + def test_stop_collect(self): + self.assertIsNone(self.vnf.stop_collect()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vfw_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vfw_vnf.py new file mode 100644 index 000000000..5334ce18c --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vfw_vnf.py @@ -0,0 +1,371 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import mock +import os + +from yardstick.common import utils +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.vnf_generic.vnf.vfw_vnf import FWApproxVnf +from yardstick.network_services.nfvi.resource import ResourceProfile +from yardstick.network_services.vnf_generic.vnf.vfw_vnf import FWApproxSetupEnvHelper +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh + + +TEST_FILE_YAML = 'nsb_test_case.yaml' +SSH_HELPER = 'yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper' + +name = 'vnf__1' + + +@mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.Process") +class TestFWApproxVnf(unittest.TestCase): + VNFD = {'vnfd:vnfd-catalog': + {'vnfd': + [{'short-name': 'VpeVnf', + 'vdu': + [{'routing_table': + [{'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0'}, + {'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1'}], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': + [{'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'id': 'vpevnf-baremetal', + 'external-interface': + [{'virtual-interface': + {'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02'}, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0'}, + {'virtual-interface': + {'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01'}, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1'}]}], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': + {'vdu-id': 'vpevnf-baremetal', + 'host': '1.2.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.2.1.1'}, + 'benchmark': + {'kpi': ['packets_in', 'packets_fwd', 'packets_dropped']}, + 'connection-point': [{'type': 'VPORT', 'name': 'xe0'}, + {'type': 'VPORT', 'name': 'xe1'}], + 'id': 'FWApproxVnf', 'name': 'VPEVnfSsh'}]}} + + scenario_cfg = {'options': {'packetsize': 64, 'traffic_type': 4, + 'rfc2544': {'allowed_drop_rate': '0.8 - 1'}, + 'vnf__1': {'rules': 'acl_1rule.yaml', + 'vnf_config': {'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': + '1C/1T', + 'worker_threads': 1}} + }, + 'task_id': 'a70bdf4a-8e67-47a3-9dc1-273c14506eb7', + 'task_path': '/tmp', + 'tc': 'tc_ipv4_1Mflow_64B_packetsize', + 'runner': {'object': 'NetworkServiceTestCase', + 'interval': 35, + 'output_filename': '/tmp/yardstick.out', + 'runner_id': 74476, 'duration': 400, + 'type': 'Duration'}, + 'traffic_profile': 'ipv4_throughput_vfw.yaml', + 'traffic_options': {'flow': 'ipv4_Packets_vfw.yaml', + 'imix': 'imix_voice.yaml'}, + 'type': 'ISB', + 'nodes': {'tg__2': 'trafficgen_2.yardstick', + 'tg__1': 'trafficgen_1.yardstick', + 'vnf__1': 'vnf.yardstick'}, + 'topology': 'vpe-tg-topology-baremetal.yaml'} + + context_cfg = {'nodes': {'tg__2': + {'member-vnf-index': '3', + 'role': 'TrafficGen', + 'name': 'trafficgen_2.yardstick', + 'vnfd-id-ref': 'tg__2', + 'ip': '1.2.1.1', + 'interfaces': + {'xe0': {'local_iface_name': 'ens513f0', + 'vld_id': FWApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.20', + 'dst_mac': '00:00:00:00:00:01', + 'local_mac': '00:00:00:00:00:03', + 'dst_ip': '152.16.40.19', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.0', + 'dpdk_port_num': 0}, + 'xe1': {'local_iface_name': 'ens513f1', + 'netmask': '255.255.255.0', + 'network': '202.16.100.0', + 'local_ip': '202.16.100.20', + 'local_mac': '00:1e:67:d0:60:5d', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.1', + 'dpdk_port_num': 1}}, + 'password': 'r00t', + 'VNF model': 'l3fwd_vnf.yaml', + 'user': 'root'}, + 'tg__1': + {'member-vnf-index': '1', + 'role': 'TrafficGen', + 'name': 'trafficgen_1.yardstick', + 'vnfd-id-ref': 'tg__1', + 'ip': '1.2.1.1', + 'interfaces': + {'xe0': {'local_iface_name': 'ens785f0', + 'vld_id': FWApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.20', + 'dst_mac': '00:00:00:00:00:02', + 'local_mac': '00:00:00:00:00:04', + 'dst_ip': '152.16.100.19', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0}, + 'xe1': {'local_iface_name': 'ens785f1', + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.21', + 'local_mac': '00:00:00:00:00:01', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1}}, + 'password': 'r00t', + 'VNF model': 'tg_rfc2544_tpl.yaml', + 'user': 'root'}, + 'vnf__1': + {'name': 'vnf.yardstick', + 'vnfd-id-ref': 'vnf__1', + 'ip': '1.2.1.1', + 'interfaces': + {'xe0': {'local_iface_name': 'ens786f0', + 'vld_id': FWApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.19', + 'dst_mac': '00:00:00:00:00:04', + 'local_mac': '00:00:00:00:00:02', + 'dst_ip': '152.16.100.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0}, + 'xe1': {'local_iface_name': 'ens786f1', + 'vld_id': FWApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.19', + 'dst_mac': '00:00:00:00:00:03', + 'local_mac': '00:00:00:00:00:01', + 'dst_ip': '152.16.40.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1}}, + 'routing_table': + [{'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'network': '152.16.100.20', + 'if': 'xe0'}, + {'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'network': '152.16.40.20', + 'if': 'xe1'}], + 'member-vnf-index': '2', + 'host': '1.2.1.1', + 'role': 'vnf', + 'user': 'root', + 'nd_route_tbl': + [{'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0'}, + {'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1'}], + 'password': 'r00t', + 'VNF model': 'vfw_vnf.yaml'}}} + + def test___init__(self, *args): + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vfw_approx_vnf = FWApproxVnf(name, vnfd) + self.assertIsNone(vfw_approx_vnf._vnf_process) + + STATS = """\ +p vfw stats + +VFW Stats +{"VFW_counters" : {"id" : "PIPELINE4", " pkts_received": 6007180, " pkts_fw_forwarded": 6007180, " pkts_drop_fw": 0, " pkts_acl_forwarded": 6007180, "pkts_drop_without_rule" : 0, "average_pkts_in_batch" : 31, "average_internal_time_in_clocks" : 17427, "average_external_time_in_clocks" : 261120, "total_time_measures" : 189829, "ct_packets_forwarded" : 6007148, "ct_packets_dropped" : 0, "bytes_processed ": 360430800, "ct_sessions" : {"active" : 130050, "open_attempt" : 130050, "re-open_attempt" : 0, "established" : 0, "closed" : 0, "timeout" : 0}, "ct_drops" : {"out_of_window" : 0, "invalid_conn" : 0, "invalid_state_transition" : 0 "RST" : 0}} +VFW TOTAL: pkts_received: 6007180, "pkts_fw_forwarded": 6007180, "pkts_drop_fw": 0, "fw_drops" : {"TTL_zero" : 0, "bad_size" : 0, "fragmented_packet" : 0, "unsupported_packet_types" : 0, "no_arp_entry" : 6007180}, "pkts_acl_forwarded": 6007180, "pkts_drop_without_rule": 0, "packets_last_sec" : 0, "average_packets_per_sec" : 0, "bytes_last_sec" : 0, "average_bytes_per_sec" : 0, "bytes_processed ": 360430800 +"CT TOTAL: ct_packets_forwarded" : 6007180, " ct_packets_dropped" : 0, "ct_sessions" : {"active" : 130050, "open_attempt" : 130050, "re-open_attempt" : 0, "established" : 0, "closed" : 0, "timeout" : 0}, "ct_drops" : {"out_of_window" : 0, "invalid_conn" : 0, "invalid_state_transition" : 0 "RST" : 0} +Action ID: 00, packetCount: 2954633, byteCount: 177277980 +Action ID: 01, packetCount: 3052547, byteCount: 183152820 +pipeline> + +pipeline> +""" # noqa + + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.time") + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') + @mock.patch(SSH_HELPER) + def test_collect_kpi(self, ssh, *args): + mock_ssh(ssh) + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vfw_approx_vnf = FWApproxVnf(name, vnfd) + vfw_approx_vnf.scenario_helper.scenario_cfg = { + 'nodes': {vfw_approx_vnf.name: "mock"} + } + vfw_approx_vnf.q_in = mock.MagicMock() + vfw_approx_vnf.q_out = mock.MagicMock() + vfw_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + vfw_approx_vnf.resource = mock.Mock(autospec=ResourceProfile) + vfw_approx_vnf.resource_helper = mock.MagicMock( + **{'collect_kpi.return_value': {"core": {}}}) + vfw_approx_vnf.vnf_execute = mock.Mock(return_value=self.STATS) + result = { + 'physical_node': 'mock_node', + 'packets_dropped': 0, + 'packets_fwd': 6007180, + 'packets_in': 6007180, + 'collect_stats': {'core': {}}, + } + self.assertEqual(result, vfw_approx_vnf.collect_kpi()) + + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.time") + @mock.patch(SSH_HELPER) + def test_vnf_execute_command(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vfw_approx_vnf = FWApproxVnf(name, vnfd) + vfw_approx_vnf.q_in = mock.MagicMock() + vfw_approx_vnf.q_out = mock.MagicMock() + vfw_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + cmd = "quit" + self.assertEqual(vfw_approx_vnf.vnf_execute(cmd), "") + + @mock.patch(SSH_HELPER) + def test_get_stats(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vfw_approx_vnf = FWApproxVnf(name, vnfd) + vfw_approx_vnf.q_in = mock.MagicMock() + vfw_approx_vnf.q_out = mock.MagicMock() + vfw_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + vfw_approx_vnf.vnf_execute = mock.Mock(return_value=self.STATS) + self.assertEqual(self.STATS, vfw_approx_vnf.get_stats()) + + def _get_file_abspath(self, filename): + curr_path = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(curr_path, filename) + return file_path + + @mock.patch("yardstick.network_services.vnf_generic.vnf.vfw_vnf.hex") + @mock.patch("yardstick.network_services.vnf_generic.vnf.vfw_vnf.eval") + @mock.patch("yardstick.network_services.vnf_generic.vnf.vfw_vnf.open") + @mock.patch(SSH_HELPER) + def test_run_vfw(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vfw_approx_vnf = FWApproxVnf(name, vnfd) + vfw_approx_vnf._build_config = mock.MagicMock() + vfw_approx_vnf.queue_wrapper = mock.MagicMock() + vfw_approx_vnf.ssh_helper = mock.MagicMock() + vfw_approx_vnf.ssh_helper.run = mock.MagicMock() + vfw_approx_vnf.scenario_helper.scenario_cfg = self.scenario_cfg + vfw_approx_vnf.vnf_cfg = {'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1} + vfw_approx_vnf.all_options = {'traffic_type': '4', + 'topology': 'nsb_test_case.yaml'} + vfw_approx_vnf._run() + vfw_approx_vnf.ssh_helper.run.assert_called_once() + + @mock.patch.object(utils, 'find_relative_file') + @mock.patch.object(ctx_base.Context, 'get_context_from_server') + @mock.patch(SSH_HELPER) + def test_instantiate(self, ssh, *args): + mock_ssh(ssh) + + vnfd = self.VNFD['vnfd:vnfd-catalog']['vnfd'][0] + vfw_approx_vnf = FWApproxVnf(name, vnfd) + vfw_approx_vnf.ssh_helper = ssh + vfw_approx_vnf.deploy_helper = mock.MagicMock() + vfw_approx_vnf.resource_helper = mock.MagicMock() + vfw_approx_vnf._build_config = mock.MagicMock() + self.scenario_cfg['vnf_options'] = {'acl': {'cfg': "", + 'rules': ""}} + self.scenario_cfg.update({"nodes": {"vnf__1": ""}}) + self.assertIsNone(vfw_approx_vnf.instantiate(self.scenario_cfg, self.context_cfg)) + + +class TestFWApproxSetupEnvHelper(unittest.TestCase): + + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.open') + @mock.patch.object(utils, 'find_relative_file') + @mock.patch('yardstick.network_services.vnf_generic.vnf.sample_vnf.MultiPortConfig') + @mock.patch.object(utils, 'open_relative_file') + def test_build_config(self, *args): + vnfd_helper = mock.Mock() + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + scenario_helper.vnf_cfg = {'lb_config': 'HW'} + scenario_helper.options = {} + scenario_helper.all_options = {} + + vfw_approx_setup_helper = FWApproxSetupEnvHelper(vnfd_helper, ssh_helper, scenario_helper) + vfw_approx_setup_helper.get_flows_config = mock.Mock() + + vfw_approx_setup_helper.ssh_helper.provision_tool = mock.Mock(return_value='tool_path') + vfw_approx_setup_helper.ssh_helper.all_ports = mock.Mock() + vfw_approx_setup_helper.vnfd_helper.port_nums = mock.Mock(return_value=[0, 1]) + expected = 'sudo tool_path -p 0x3 -f /tmp/vfw_config -s /tmp/vfw_script --hwlb 3' + self.assertEqual(vfw_approx_setup_helper.build_config(), expected) + vfw_approx_setup_helper.get_flows_config.assert_called_once() diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vims_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vims_vnf.py new file mode 100644 index 000000000..d86dab8ad --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vims_vnf.py @@ -0,0 +1,713 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License,Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing,software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest +import mock + +from yardstick.network_services.vnf_generic.vnf import vims_vnf +from yardstick.tests.unit.network_services.vnf_generic.vnf.test_base import mock_ssh + + +class TestVimsPcscfVnf(unittest.TestCase): + + VNFD_0 = { + "short-name": "SippVnf", + "vdu": [ + { + "id": "sippvnf-baremetal", + "routing_table": "", + "external-interface": [ + { + "virtual-interface": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vnf__0": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "vnf__1": { + "vld_id": "ims_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "node_name": "vnf__1", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:e8" + } + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "vnfd-connection-point-ref": "xe0", + "name": "xe0" + }, + { + "virtual-interface": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vnf__0": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "vnf__1": { + "vld_id": "ims_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "node_name": "vnf__1", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:e8" + } + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe1", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "vnfd-connection-point-ref": "xe1", + "name": "xe1" + } + ], + "name": "sippvnf-baremetal", + "description": "Sipp" + } + ], + "description": "ImsbenchSipp", + "mgmt-interface": { + "vdu-id": "sipp-baremetal", + "password": "r00t", + "user": "root", + "ip": "10.80.3.11" + }, + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "id": "SippVnf", + "name": "SippVnf" + } + + def setUp(self): + self.pcscf_vnf = vims_vnf.VimsPcscfVnf('vnf__0', self.VNFD_0) + + def test___init__(self): + self.assertEqual(self.pcscf_vnf.name, 'vnf__0') + self.assertIsInstance(self.pcscf_vnf.resource_helper, + vims_vnf.VimsResourceHelper) + self.assertIsNone(self.pcscf_vnf._vnf_process) + + def test_wait_for_instantiate(self): + self.assertIsNone(self.pcscf_vnf.wait_for_instantiate()) + + def test__run(self): + self.assertIsNone(self.pcscf_vnf._run()) + + def test_start_collect(self): + self.assertIsNone(self.pcscf_vnf.start_collect()) + + def test_collect_kpi(self): + self.assertIsNone(self.pcscf_vnf.collect_kpi()) + + +class TestVimsHssVnf(unittest.TestCase): + + VNFD_1 = { + "short-name": "SippVnf", + "vdu": [ + { + "id": "sippvnf-baremetal", + "routing_table": "", + "external-interface": [ + { + "virtual-interface": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vnf__0": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "vnf__1": { + "vld_id": "ims_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "node_name": "vnf__1", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:e8" + } + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "vnfd-connection-point-ref": "xe0", + "name": "xe0" + }, + { + "virtual-interface": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vnf__0": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "vnf__1": { + "vld_id": "ims_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "node_name": "vnf__1", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:e8" + } + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe1", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "vnfd-connection-point-ref": "xe1", + "name": "xe1" + } + ], + "name": "sippvnf-baremetal", + "description": "Sipp" + } + ], + "description": "ImsbenchSipp", + "mgmt-interface": { + "vdu-id": "sipp-baremetal", + "password": "r00t", + "user": "root", + "ip": "10.80.3.11" + }, + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "id": "SippVnf", + "name": "SippVnf" + } + + SCENARIO_CFG = { + "task_id": "86414e11-5ef5-4426-b175-71baaa00fbd7", + "tc": "tc_vims_baremetal_sipp", + "runner": { + "interval": 1, + "output_config": { + "DEFAULT": { + "debug": "False", + "dispatcher": [ + "influxdb" + ] + }, + "nsb": { + "debug": "False", + "trex_client_lib": "/opt/nsb_bin/trex_client/stl", + "bin_path": "/opt/nsb_bin", + "trex_path": "/opt/nsb_bin/trex/scripts", + "dispatcher": "influxdb" + }, + "dispatcher_influxdb": { + "username": "root", + "target": "http://10.80.3.11:8086", + "db_name": "yardstick", + "timeout": "5", + "debug": "False", + "password": "root", + "dispatcher": "influxdb" + }, + "dispatcher_http": { + "debug": "False", + "dispatcher": "influxdb", + "timeout": "5", + "target": "http://127.0.0.1:8000/results" + }, + "dispatcher_file": { + "debug": "False", + "backup_count": "0", + "max_bytes": "0", + "dispatcher": "influxdb", + "file_path": "/tmp/yardstick.out" + } + }, + "runner_id": 22610, + "duration": 60, + "type": "Vims" + }, + "nodes": { + "vnf__0": "pcscf.yardstick-86414e11", + "vnf__1": "hss.yardstick-86414e11", + "tg__0": "sipp.yardstick-86414e11" + }, + "topology": "vims-topology.yaml", + "type": "NSPerf", + "traffic_profile": "../../traffic_profiles/ipv4_throughput.yaml", + "task_path": "samples/vnf_samples/nsut/vims", + "options": { + "init_reg_max": 5000, + "end_user": 10000, + "reg_cps": 20, + "rereg_cps": 20, + "rereg_step": 10, + "wait_time": 5, + "start_user": 1, + "msgc_cps": 10, + "dereg_step": 10, + "call_cps": 10, + "reg_step": 10, + "init_reg_cps": 50, + "dereg_cps": 20, + "msgc_step": 5, + "call_step": 5, + "hold_time": 15, + "port": 5060, + "run_mode": "nortp" + } + } + + CONTEXT_CFG = { + "nodes": { + "tg__0": { + "ip": "10.80.3.11", + "interfaces": { + "xe0": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vnf__0": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "vnf__1": { + "vld_id": "ims_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "node_name": "vnf__1", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:e8" + } + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "xe1": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vnf__0": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "vnf__1": { + "vld_id": "ims_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "node_name": "vnf__1", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:e8" + } + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe1", + "local_mac": "90:e2:ba:7c:30:e8" + } + }, + "user": "root", + "password": "r00t", + "VNF model": "../../vnf_descriptors/tg_sipp_vnfd.yaml", + "name": "sipp.yardstick-86414e11", + "vnfd-id-ref": "tg__0", + "member-vnf-index": "1", + "role": "TrafficGen", + "ctx_type": "Node" + }, + "vnf__0": { + "ip": "10.80.3.7", + "interfaces": { + "xe0": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "tg__0": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe1", + "local_mac": "90:e2:ba:7c:30:e8" + } + }, + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + } + }, + "user": "root", + "password": "r00t", + "VNF model": "../../vnf_descriptors/vims_pcscf_vnfd.yaml", + "name": "pcscf.yardstick-86414e11", + "vnfd-id-ref": "vnf__0", + "member-vnf-index": "2", + "role": "VirtualNetworkFunction", + "ctx_type": "Node" + }, + "vnf__1": { + "ip": "10.80.3.7", + "interfaces": { + "xe0": { + "vld_id": "ims_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "tg__0": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "peer_intf": { + "vld_id": "data_network", + "peer_ifname": "xe1", + "dst_mac": "90:e2:ba:7c:30:e8", + "network": {}, + "local_ip": "10.80.3.7", + "peer_intf": { + "vld_id": "ims_network", + "peer_ifname": "xe0", + "dst_mac": "90:e2:ba:7c:41:e8", + "network": {}, + "local_ip": "10.80.3.11", + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:30:e8" + }, + "node_name": "vnf__0", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:a8" + }, + "node_name": "tg__0", + "netmask": "255.255.255.0", + "peer_name": "vnf__1", + "dst_ip": "10.80.3.7", + "ifname": "xe1", + "local_mac": "90:e2:ba:7c:30:e8" + } + }, + "node_name": "vnf__1", + "netmask": "255.255.255.0", + "peer_name": "tg__0", + "dst_ip": "10.80.3.11", + "ifname": "xe0", + "local_mac": "90:e2:ba:7c:41:e8" + } + }, + "user": "root", + "password": "r00t", + "VNF model": "../../vnf_descriptors/vims_hss_vnfd.yaml", + "name": "hss.yardstick-86414e11", + "vnfd-id-ref": "vnf__1", + "member-vnf-index": "3", + "role": "VirtualNetworkFunction", + "ctx_type": "Node" + } + }, + "networks": {} + } + + def setUp(self): + self.hss_vnf = vims_vnf.VimsHssVnf('vnf__1', self.VNFD_1) + + def test___init__(self): + self.assertIsInstance(self.hss_vnf.resource_helper, + vims_vnf.VimsResourceHelper) + self.assertIsNone(self.hss_vnf._vnf_process) + + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.VnfSshHelper") + def test_instantiate(self, ssh): + mock_ssh(ssh) + hss_vnf = vims_vnf.VimsHssVnf('vnf__1', self.VNFD_1) + self.assertIsNone(hss_vnf.instantiate(self.SCENARIO_CFG, + self.CONTEXT_CFG)) + + def test_wait_for_instantiate(self): + self.assertIsNone(self.hss_vnf.wait_for_instantiate()) + + def test_start_collect(self): + self.assertIsNone(self.hss_vnf.start_collect()) + + def test_collect_kpi(self): + self.assertIsNone(self.hss_vnf.collect_kpi()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpe_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpe_vnf.py new file mode 100644 index 000000000..8342f5faa --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpe_vnf.py @@ -0,0 +1,744 @@ +# Copyright (c) 2016-2019 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from multiprocessing import Process, Queue +import time + +import mock +import unittest + +from yardstick.benchmark.contexts import base as ctx_base +from yardstick.network_services.nfvi.resource import ResourceProfile +from yardstick.network_services.vnf_generic.vnf import base as vnf_base +from yardstick.network_services.vnf_generic.vnf import sample_vnf +from yardstick.network_services.vnf_generic.vnf import vpe_vnf +from yardstick.tests.unit.network_services.vnf_generic.vnf import test_base + + +TEST_FILE_YAML = 'nsb_test_case.yaml' + +NAME = 'vnf_1' + +PING_OUTPUT_1 = "Pkts in: 101\r\n\tPkts dropped by AH: 100\r\n\tPkts dropped by other: 100" + +MODULE_PATH = test_base.FileAbsPath(__file__) +get_file_abspath = MODULE_PATH.get_path + + +class TestConfigCreate(unittest.TestCase): + + VNFD_0 = { + 'short-name': 'VpeVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0' + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1' + }, + ], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0' + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1' + }, + ], + 'id': 'vpevnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.100.20', + 'local_mac': '00:00:00:00:00:01', + 'vld_id': 'uplink_0', + 'ifname': 'xe0', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0' + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_mac': '00:00:00:00:00:02', + 'vld_id': 'downlink_0', + 'ifname': 'xe1', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1' + }, + ], + }, + ], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'vpevnf-baremetal', + 'host': '1.1.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.1.1.1' + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'VpeApproxVnf', 'name': 'VPEVnfSsh' + } + + def test___init__(self): + vnfd_helper = vnf_base.VnfdHelper(self.VNFD_0) + config_create = vpe_vnf.ConfigCreate(vnfd_helper, 2) + self.assertEqual(config_create.uplink_ports, ['xe0']) + self.assertEqual(config_create.downlink_ports, ['xe1']) + self.assertEqual(config_create.socket, 2) + + def test_generate_vpe_script(self): + vnfd_helper = vnf_base.VnfdHelper(self.VNFD_0) + vpe_config_vnf = vpe_vnf.ConfigCreate(vnfd_helper, 2) + intf = [ + { + "name": 'xe1', + "virtual-interface": { + "dst_ip": "1.1.1.1", + "dst_mac": "00:00:00:00:00:00:02", + }, + }, + { + "name": 'xe2', + "virtual-interface": { + "dst_ip": "1.1.1.1", + "dst_mac": "00:00:00:00:00:00:02", + }, + }, + ] + vpe_config_vnf.downlink_ports = ['xe1'] + vpe_config_vnf.uplink_ports = ['xe2'] + result = vpe_config_vnf.generate_vpe_script(intf) + self.assertIsInstance(result, str) + self.assertNotEqual(result, '') + + +class TestVpeApproxVnf(unittest.TestCase): + + VNFD_0 = { + 'short-name': 'VpeVnf', + 'vdu': [ + { + 'routing_table': [ + { + 'network': '152.16.100.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'if': 'xe0', + }, + { + 'network': '152.16.40.20', + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'description': 'VPE approximation using DPDK', + 'name': 'vpevnf-baremetal', + 'nd_route_tbl': [ + { + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'id': 'vpevnf-baremetal', + 'external-interface': [ + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:04', + 'vpci': '0000:05:00.0', + 'local_ip': '152.16.100.19', + 'type': 'PCI-PASSTHROUGH', + 'netmask': '255.255.255.0', + 'dpdk_port_num': 0, + 'bandwidth': '10 Gbps', + 'driver': "i40e", + 'dst_ip': '152.16.100.20', + 'local_iface_name': 'xe0', + 'local_mac': '00:00:00:00:00:02', + 'vld_id': 'uplink_0', + 'ifname': 'xe0', + }, + 'vnfd-connection-point-ref': 'xe0', + 'name': 'xe0', + }, + { + 'virtual-interface': { + 'dst_mac': '00:00:00:00:00:03', + 'vpci': '0000:05:00.1', + 'local_ip': '152.16.40.19', + 'type': 'PCI-PASSTHROUGH', + 'driver': "i40e", + 'netmask': '255.255.255.0', + 'dpdk_port_num': 1, + 'bandwidth': '10 Gbps', + 'dst_ip': '152.16.40.20', + 'local_iface_name': 'xe1', + 'local_mac': '00:00:00:00:00:01', + 'vld_id': 'downlink_0', + 'ifname': 'xe1', + }, + 'vnfd-connection-point-ref': 'xe1', + 'name': 'xe1', + }, + ], + }, + ], + 'description': 'Vpe approximation using DPDK', + 'mgmt-interface': { + 'vdu-id': 'vpevnf-baremetal', + 'host': '1.2.1.1', + 'password': 'r00t', + 'user': 'root', + 'ip': '1.2.1.1', + }, + 'benchmark': { + 'kpi': [ + 'packets_in', + 'packets_fwd', + 'packets_dropped', + ], + }, + 'connection-point': [ + { + 'type': 'VPORT', + 'name': 'xe0', + }, + { + 'type': 'VPORT', + 'name': 'xe1', + }, + ], + 'id': 'VpeApproxVnf', + 'name': 'VPEVnfSsh', + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ], + }, + } + + SCENARIO_CFG = { + 'options': { + 'packetsize': 64, + 'traffic_type': 4, + 'rfc2544': { + 'allowed_drop_rate': '0.8 - 1', + }, + 'vnf__1': { + 'cfg': 'acl_1rule.yaml', + 'vnf_config': { + 'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': + '1C/1T', + 'worker_threads': 1, + }, + } + }, + 'task_id': 'a70bdf4a-8e67-47a3-9dc1-273c14506eb7', + 'tc': 'tc_ipv4_1Mflow_64B_packetsize', + 'runner': { + 'object': 'NetworkServiceTestCase', + 'interval': 35, + 'output_filename': '/tmp/yardstick.out', + 'runner_id': 74476, + 'duration': 400, + 'type': 'Duration', + }, + 'traffic_profile': 'ipv4_throughput_vpe.yaml', + 'traffic_options': { + 'flow': 'ipv4_Packets_vpe.yaml', + 'imix': 'imix_voice.yaml', + }, + 'type': 'ISB', + 'nodes': { + 'tg__2': 'trafficgen_2.yardstick', + 'tg__1': 'trafficgen_1.yardstick', + 'vnf__1': 'vnf.yardstick', + }, + 'topology': 'vpe-tg-topology-baremetal.yaml', + } + + CONTEXT_CFG = { + 'nodes': { + 'tg__2': { + 'member-vnf-index': '3', + 'role': 'TrafficGen', + 'name': 'trafficgen_2.yardstick', + 'vnfd-id-ref': 'tg__2', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens513f0', + 'vld_id': vpe_vnf.VpeApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.20', + 'dst_mac': '00:00:00:00:00:01', + 'local_mac': '00:00:00:00:00:03', + 'dst_ip': '152.16.40.19', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens513f1', + 'netmask': '255.255.255.0', + 'network': '202.16.100.0', + 'local_ip': '202.16.100.20', + 'local_mac': '00:1e:67:d0:60:5d', + 'driver': 'ixgbe', + 'vpci': '0000:02:00.1', + 'dpdk_port_num': 1, + }, + }, + 'password': 'r00t', + 'VNF model': 'l3fwd_vnf.yaml', + 'user': 'root', + }, + 'tg__1': { + 'member-vnf-index': '1', + 'role': 'TrafficGen', + 'name': 'trafficgen_1.yardstick', + 'vnfd-id-ref': 'tg__1', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens785f0', + 'vld_id': vpe_vnf.VpeApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.20', + 'dst_mac': '00:00:00:00:00:02', + 'local_mac': '00:00:00:00:00:04', + 'dst_ip': '152.16.100.19', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens785f1', + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.21', + 'local_mac': '00:00:00:00:00:01', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + }, + }, + 'password': 'r00t', + 'VNF model': 'tg_rfc2544_tpl.yaml', + 'user': 'root', + }, + 'vnf__1': { + 'name': 'vnf.yardstick', + 'vnfd-id-ref': 'vnf__1', + 'ip': '1.2.1.1', + 'interfaces': { + 'xe0': { + 'local_iface_name': 'ens786f0', + 'vld_id': vpe_vnf.VpeApproxVnf.UPLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.100.19', + 'dst_mac': '00:00:00:00:00:04', + 'local_mac': '00:00:00:00:00:02', + 'dst_ip': '152.16.100.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.0', + 'dpdk_port_num': 0, + }, + 'xe1': { + 'local_iface_name': 'ens786f1', + 'vld_id': vpe_vnf.VpeApproxVnf.DOWNLINK, + 'netmask': '255.255.255.0', + 'local_ip': '152.16.40.19', + 'dst_mac': '00:00:00:00:00:03', + 'local_mac': '00:00:00:00:00:01', + 'dst_ip': '152.16.40.20', + 'driver': 'i40e', + 'vpci': '0000:05:00.1', + 'dpdk_port_num': 1, + }, + }, + 'routing_table': [ + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.100.20', + 'network': '152.16.100.20', + 'if': 'xe0', + }, + { + 'netmask': '255.255.255.0', + 'gateway': '152.16.40.20', + 'network': '152.16.40.20', + 'if': 'xe1', + }, + ], + 'member-vnf-index': '2', + 'host': '1.2.1.1', + 'role': 'vnf', + 'user': 'root', + 'nd_route_tbl': [ + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:6414', + 'network': '0064:ff9b:0:0:0:0:9810:6414', + 'if': 'xe0', + }, + { + 'netmask': '112', + 'gateway': '0064:ff9b:0:0:0:0:9810:2814', + 'network': '0064:ff9b:0:0:0:0:9810:2814', + 'if': 'xe1', + }, + ], + 'password': 'r00t', + 'VNF model': 'vpe_vnf.yaml', + }, + }, + } + + def setUp(self): + self._mock_time_sleep = mock.patch.object(time, 'sleep') + self.mock_time_sleep = self._mock_time_sleep.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_time_sleep.stop() + + def test___init__(self): + vpe_approx_vnf = vpe_vnf.VpeApproxVnf(NAME, self.VNFD_0) + self.assertIsNone(vpe_approx_vnf._vnf_process) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', + return_value='mock_node') + @mock.patch.object(sample_vnf, 'VnfSshHelper') + def test_collect_kpi_sa_not_running(self, ssh, *args): + test_base.mock_ssh(ssh) + + resource = mock.Mock(autospec=ResourceProfile) + resource.check_if_system_agent_running.return_value = 1, '' + resource.amqp_collect_nfvi_kpi.return_value = {'foo': 234} + resource.check_if_system_agent_running.return_value = (1, None) + + vpe_approx_vnf = vpe_vnf.VpeApproxVnf(NAME, self.VNFD_0) + vpe_approx_vnf.scenario_helper.scenario_cfg = { + 'nodes': {vpe_approx_vnf.name: "mock"} + } + vpe_approx_vnf.q_in = mock.MagicMock() + vpe_approx_vnf.q_out = mock.MagicMock() + vpe_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + vpe_approx_vnf.resource_helper.resource = resource + + expected = { + 'physical_node': 'mock_node', + 'pkt_in_down_stream': 0, + 'pkt_in_up_stream': 0, + 'pkt_drop_down_stream': 0, + 'pkt_drop_up_stream': 0, + 'collect_stats': {'core': {}}, + } + self.assertEqual(vpe_approx_vnf.collect_kpi(), expected) + + @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', + return_value='mock_node') + @mock.patch.object(sample_vnf, 'VnfSshHelper') + def test_collect_kpi_sa_running(self, ssh, *args): + test_base.mock_ssh(ssh) + + resource = mock.Mock(autospec=ResourceProfile) + resource.check_if_system_agent_running.return_value = 0, '1234' + resource.amqp_collect_nfvi_kpi.return_value = {'foo': 234} + + vpe_approx_vnf = vpe_vnf.VpeApproxVnf(NAME, self.VNFD_0) + vpe_approx_vnf.scenario_helper.scenario_cfg = { + 'nodes': {vpe_approx_vnf.name: "mock"} + } + vpe_approx_vnf.q_in = mock.MagicMock() + vpe_approx_vnf.q_out = mock.MagicMock() + vpe_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + vpe_approx_vnf.resource_helper.resource = resource + + expected = { + 'physical_node': 'mock_node', + 'pkt_in_down_stream': 0, + 'pkt_in_up_stream': 0, + 'pkt_drop_down_stream': 0, + 'pkt_drop_up_stream': 0, + 'collect_stats': {'core': {'foo': 234}}, + } + self.assertEqual(vpe_approx_vnf.collect_kpi(), expected) + + @mock.patch.object(sample_vnf, 'VnfSshHelper') + def test_vnf_execute(self, ssh): + test_base.mock_ssh(ssh) + vpe_approx_vnf = vpe_vnf.VpeApproxVnf(NAME, self.VNFD_0) + vpe_approx_vnf.q_in = mock.MagicMock() + vpe_approx_vnf.q_out = mock.MagicMock() + vpe_approx_vnf.q_out.qsize = mock.Mock(return_value=0) + self.assertEqual(vpe_approx_vnf.vnf_execute("quit", 0), '') + + @mock.patch.object(sample_vnf, 'VnfSshHelper') + def test_run_vpe(self, ssh): + test_base.mock_ssh(ssh) + + vpe_approx_vnf = vpe_vnf.VpeApproxVnf(NAME, self.VNFD_0) + vpe_approx_vnf.tc_file_name = get_file_abspath(TEST_FILE_YAML) + vpe_approx_vnf.vnf_cfg = { + 'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1, + } + vpe_approx_vnf.scenario_helper.scenario_cfg = { + 'options': { + NAME: { + 'traffic_type': '4', + 'topology': 'nsb_test_case.yaml', + 'vnf_config': 'vpe_config', + } + } + } + vpe_approx_vnf.topology = "nsb_test_case.yaml" + vpe_approx_vnf.nfvi_type = "baremetal" + vpe_approx_vnf._provide_config_file = mock.Mock() + vpe_approx_vnf._build_config = mock.MagicMock() + + self.assertIsInstance(vpe_approx_vnf.ssh_helper, mock.Mock) + self.assertIsInstance(vpe_approx_vnf.ssh_helper, mock.Mock) + self.assertIsNone(vpe_approx_vnf._run()) + + @mock.patch("yardstick.network_services.vnf_generic.vnf.sample_vnf.MultiPortConfig") + @mock.patch("yardstick.network_services.vnf_generic.vnf.vpe_vnf.ConfigCreate") + @mock.patch("six.moves.builtins.open") + @mock.patch.object(sample_vnf, 'VnfSshHelper') + def test_build_config(self, ssh, *args): + test_base.mock_ssh(ssh) + vpe_approx_vnf = vpe_vnf.VpeApproxSetupEnvHelper( + mock.MagicMock(), mock.MagicMock(), mock.MagicMock()) + vpe_approx_vnf.tc_file_name = get_file_abspath(TEST_FILE_YAML) + vpe_approx_vnf.generate_port_pairs = mock.Mock() + vpe_approx_vnf.vnf_cfg = { + 'lb_config': 'SW', + 'lb_count': 1, + 'worker_config': '1C/1T', + 'worker_threads': 1, + } + vpe_approx_vnf.scenario_helper.scenario_cfg = { + 'options': { + NAME: { + 'traffic_type': '4', + 'topology': 'nsb_test_case.yaml', + 'vnf_config': 'vpe_config', + } + } + } + vpe_approx_vnf.topology = "nsb_test_case.yaml" + vpe_approx_vnf.nfvi_type = "baremetal" + vpe_approx_vnf._provide_config_file = mock.Mock() + + vpe_approx_vnf.ssh_helper = mock.MagicMock() + vpe_approx_vnf.scenario_helper = mock.MagicMock() + vpe_approx_vnf.ssh_helper.bin_path = mock.Mock() + vpe_approx_vnf.ssh_helper.upload_config_file = mock.MagicMock() + self.assertIsNone(vpe_approx_vnf._build_vnf_ports()) + + vpe_approx_vnf.ssh_helper.provision_tool = mock.Mock(return_value='tool_path') + vpe_approx_vnf.ssh_helper.all_ports = mock.Mock() + vpe_approx_vnf.vnfd_helper.port_nums = mock.Mock(return_value=[0, 1]) + vpe_approx_vnf.scenario_helper.vnf_cfg = {'lb_config': 'HW'} + + expected = 'sudo tool_path -p 0x3 -f /tmp/vpe_config -s /tmp/vpe_script --hwlb 3' + self.assertEqual(vpe_approx_vnf.build_config(), expected) + + @mock.patch.object(sample_vnf, 'VnfSshHelper') + def test_wait_for_instantiate(self, ssh): + test_base.mock_ssh(ssh) + + mock_process = mock.Mock(autospec=Process) + mock_process.is_alive.return_value = True + mock_process.exitcode = 432 + + mock_q_out = mock.Mock(autospec=Queue) + mock_q_out.get.side_effect = iter(["pipeline>"]) + mock_q_out.qsize.side_effect = range(1, -1, -1) + + mock_resource = mock.MagicMock() + + vpe_approx_vnf = vpe_vnf.VpeApproxVnf(NAME, self.VNFD_0) + vpe_approx_vnf._vnf_process = mock_process + vpe_approx_vnf.q_out = mock_q_out + vpe_approx_vnf.queue_wrapper = mock.Mock( + autospec=vnf_base.QueueFileWrapper) + vpe_approx_vnf.resource_helper.resource = mock_resource + + vpe_approx_vnf.q_out.put("pipeline>") + self.assertEqual(vpe_approx_vnf.wait_for_instantiate(), 432) + + @mock.patch.object(sample_vnf, 'VnfSshHelper') + def test_wait_for_instantiate_fragmented(self, ssh): + test_base.mock_ssh(ssh) + + mock_process = mock.Mock(autospec=Process) + mock_process.is_alive.return_value = True + mock_process.exitcode = 432 + + # test that fragmented pipeline prompt is recognized + mock_q_out = mock.Mock(autospec=Queue) + mock_q_out.get.side_effect = iter(["wow pipel", "ine>"]) + mock_q_out.qsize.side_effect = range(2, -1, -1) + + mock_resource = mock.MagicMock() + + vpe_approx_vnf = vpe_vnf.VpeApproxVnf(NAME, self.VNFD_0) + vpe_approx_vnf._vnf_process = mock_process + vpe_approx_vnf.q_out = mock_q_out + vpe_approx_vnf.queue_wrapper = mock.Mock( + autospec=vnf_base.QueueFileWrapper) + vpe_approx_vnf.resource_helper.resource = mock_resource + + self.assertEqual(vpe_approx_vnf.wait_for_instantiate(), 432) + + @mock.patch.object(sample_vnf, 'VnfSshHelper') + def test_wait_for_instantiate_crash(self, ssh): + test_base.mock_ssh(ssh, exec_result=(1, "", "")) + + mock_process = mock.Mock(autospec=Process) + mock_process.is_alive.return_value = False + mock_process.exitcode = 432 + + mock_resource = mock.MagicMock() + + vpe_approx_vnf = vpe_vnf.VpeApproxVnf(NAME, self.VNFD_0) + vpe_approx_vnf._vnf_process = mock_process + vpe_approx_vnf.resource_helper.resource = mock_resource + + with self.assertRaises(RuntimeError) as raised: + vpe_approx_vnf.wait_for_instantiate() + + self.assertIn('VNF process died', str(raised.exception)) + + @mock.patch.object(sample_vnf, 'VnfSshHelper') + def test_wait_for_instantiate_panic(self, ssh): + test_base.mock_ssh(ssh, exec_result=(1, "", "")) + + mock_process = mock.Mock(autospec=Process) + mock_process.is_alive.return_value = True + mock_process.exitcode = 432 + + mock_resource = mock.MagicMock() + + vpe_approx_vnf = vpe_vnf.VpeApproxVnf(NAME, self.VNFD_0) + vpe_approx_vnf._vnf_process = mock_process + vpe_approx_vnf.resource_helper.resource = mock_resource + + vpe_approx_vnf.q_out.put("PANIC") + with self.assertRaises(RuntimeError) as raised: + vpe_approx_vnf.wait_for_instantiate() + + self.assertIn('Error starting', str(raised.exception)) + + @mock.patch.object(sample_vnf, 'VnfSshHelper') + def test_wait_for_instantiate_panic_fragmented(self, ssh): + test_base.mock_ssh(ssh, exec_result=(1, "", "")) + + mock_process = mock.Mock(autospec=Process) + mock_process.is_alive.return_value = True + mock_process.exitcode = 432 + + # test that fragmented PANIC is recognized + mock_q_out = mock.Mock(autospec=Queue) + mock_q_out.get.side_effect = iter(["omg PA", "NIC this is bad"]) + mock_q_out.qsize.side_effect = range(2, -1, -1) + + mock_resource = mock.MagicMock() + + vpe_approx_vnf = vpe_vnf.VpeApproxVnf(NAME, self.VNFD_0) + vpe_approx_vnf._vnf_process = mock_process + vpe_approx_vnf.q_out = mock_q_out + vpe_approx_vnf.resource_helper.resource = mock_resource + + with self.assertRaises(RuntimeError) as raised: + vpe_approx_vnf.wait_for_instantiate() + + self.assertIn('Error starting', str(raised.exception)) + + @mock.patch.object(sample_vnf, 'VnfSshHelper') + def test_terminate(self, ssh): + test_base.mock_ssh(ssh) + + vpe_approx_vnf = vpe_vnf.VpeApproxVnf(NAME, self.VNFD_0) + vpe_approx_vnf._vnf_process = mock.MagicMock() + vpe_approx_vnf._resource_collect_stop = mock.Mock() + vpe_approx_vnf.resource_helper = mock.MagicMock() + + self.assertIsNone(vpe_approx_vnf.terminate()) diff --git a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpp_helpers.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpp_helpers.py new file mode 100644 index 000000000..cca604f43 --- /dev/null +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpp_helpers.py @@ -0,0 +1,1723 @@ +# Copyright (c) 2019 Viosoft Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import ipaddress +import unittest + +import mock + +from yardstick.common import exceptions +from yardstick.network_services.helpers import cpu +from yardstick.network_services.vnf_generic.vnf import vpp_helpers +from yardstick.network_services.vnf_generic.vnf.base import VnfdHelper +from yardstick.network_services.vnf_generic.vnf.vpp_helpers import \ + VppSetupEnvHelper, VppConfigGenerator, VatTerminal + + +class TestVppConfigGenerator(unittest.TestCase): + + def test_add_config_item(self): + test_item = {} + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_config_item(test_item, '/tmp/vpe.log', + ['unix', 'log']) + self.assertEqual({'unix': {'log': '/tmp/vpe.log'}}, test_item) + + def test_add_config_item_str(self): + test_item = {'unix': ''} + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_config_item(test_item, '/tmp/vpe.log', + ['unix', 'log']) + self.assertEqual({'unix': {'log': '/tmp/vpe.log'}}, test_item) + + def test_add_unix_log(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_unix_log() + self.assertEqual('unix\n{\n log /tmp/vpe.log\n}\n', + vpp_config_generator.dump_config()) + + def test_add_unix_cli_listen(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_unix_cli_listen() + self.assertEqual('unix\n{\n cli-listen /run/vpp/cli.sock\n}\n', + vpp_config_generator.dump_config()) + + def test_add_unix_nodaemon(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_unix_nodaemon() + self.assertEqual('unix\n{\n nodaemon \n}\n', + vpp_config_generator.dump_config()) + + def test_add_unix_coredump(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_unix_coredump() + self.assertEqual('unix\n{\n full-coredump \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_dev(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_dev('0000:00:00.0') + self.assertEqual('dpdk\n{\n dev 0000:00:00.0 \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_cryptodev(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_cryptodev(2, '0000:00:00.0') + self.assertEqual( + 'dpdk\n{\n dev 0000:00:01.0 \n dev 0000:00:01.1 \n uio-driver igb_uio\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_sw_cryptodev(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_sw_cryptodev('aesni_gcm', 0, 2) + self.assertEqual( + 'dpdk\n{\n vdev cryptodev_aesni_gcm_pmd,socket_id=0 \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_dev_default_rxq(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_dev_default_rxq(1) + self.assertEqual( + 'dpdk\n{\n dev default\n {\n num-rx-queues 1\n }\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_dev_default_rxd(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_dev_default_rxd(2048) + self.assertEqual( + 'dpdk\n{\n dev default\n {\n num-rx-desc 2048\n }\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_dev_default_txd(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_dev_default_txd(2048) + self.assertEqual( + 'dpdk\n{\n dev default\n {\n num-tx-desc 2048\n }\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_log_level(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_log_level('debug') + self.assertEqual('dpdk\n{\n log-level debug\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_socketmem(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_socketmem('1024,1024') + self.assertEqual('dpdk\n{\n socket-mem 1024,1024\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_num_mbufs(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_num_mbufs(32768) + self.assertEqual('dpdk\n{\n num-mbufs 32768\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_uio_driver(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_uio_driver('igb_uio') + self.assertEqual('dpdk\n{\n uio-driver igb_uio\n}\n', + vpp_config_generator.dump_config()) + + def test_add_cpu_main_core(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_cpu_main_core('1,2') + self.assertEqual('cpu\n{\n main-core 1,2\n}\n', + vpp_config_generator.dump_config()) + + def test_add_cpu_corelist_workers(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_cpu_corelist_workers('1,2') + self.assertEqual('cpu\n{\n corelist-workers 1,2\n}\n', + vpp_config_generator.dump_config()) + + def test_add_heapsize(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_heapsize('4G') + self.assertEqual('heapsize 4G\n', vpp_config_generator.dump_config()) + + def test_add_ip6_hash_buckets(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_ip6_hash_buckets(2000000) + self.assertEqual('ip6\n{\n hash-buckets 2000000\n}\n', + vpp_config_generator.dump_config()) + + def test_add_ip6_heap_size(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_ip6_heap_size('4G') + self.assertEqual('ip6\n{\n heap-size 4G\n}\n', + vpp_config_generator.dump_config()) + + def test_add_ip_heap_size(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_ip_heap_size('4G') + self.assertEqual('ip\n{\n heap-size 4G\n}\n', + vpp_config_generator.dump_config()) + + def test_add_statseg_size(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_statseg_size('4G') + self.assertEqual('statseg\n{\n size 4G\n}\n', + vpp_config_generator.dump_config()) + + def test_add_plugin(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_plugin('enable', ['dpdk_plugin.so']) + self.assertEqual( + 'plugins\n{\n plugin [\'dpdk_plugin.so\']\n {\n enable \n }\n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_no_multi_seg(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_no_multi_seg() + self.assertEqual('dpdk\n{\n no-multi-seg \n}\n', + vpp_config_generator.dump_config()) + + def test_add_dpdk_no_tx_checksum_offload(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_dpdk_no_tx_checksum_offload() + self.assertEqual('dpdk\n{\n no-tx-checksum-offload \n}\n', + vpp_config_generator.dump_config()) + + def test_dump_config(self): + vpp_config_generator = VppConfigGenerator() + vpp_config_generator.add_unix_log() + self.assertEqual('unix\n{\n log /tmp/vpe.log\n}\n', + vpp_config_generator.dump_config()) + + def test_pci_dev_check(self): + self.assertTrue(VppConfigGenerator.pci_dev_check('0000:00:00.0')) + + def test_pci_dev_check_error(self): + with self.assertRaises(ValueError) as raised: + VppConfigGenerator.pci_dev_check('0000:00:0.0') + self.assertIn( + 'PCI address 0000:00:0.0 is not in valid format xxxx:xx:xx.x', + str(raised.exception)) + + +class TestVppSetupEnvHelper(unittest.TestCase): + VNFD_0 = { + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "VPP IPsec", + "id": "VipsecApproxVnf", + "mgmt-interface": { + "ip": "10.10.10.101", + "password": "r00t", + "user": "root", + "vdu-id": "ipsecvnf-baremetal" + }, + "name": "IpsecVnf", + "short-name": "IpsecVnf", + "vdu": [ + { + "description": "VPP Ipsec", + "external-interface": [ + { + "name": "xe0", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:ff:06.0" + }, + "vnfd-connection-point-ref": "xe0" + }, + { + "name": "xe1", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:ff:07.0" + }, + "vnfd-connection-point-ref": "xe1" + } + ], + "id": "ipsecvnf-baremetal", + "name": "ipsecvnf-baremetal", + "routing_table": [] + } + ] + } + + VNFD_1 = { + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "VPP IPsec", + "id": "VipsecApproxVnf", + "mgmt-interface": { + "ip": "10.10.10.101", + "password": "r00t", + "user": "root", + "vdu-id": "ipsecvnf-baremetal" + }, + "name": "IpsecVnf", + "short-name": "IpsecVnf", + "vdu": [ + { + "description": "VPP Ipsec", + "external-interface": [ + { + "name": "xe0", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:ff:06.0" + }, + "vnfd-connection-point-ref": "xe0" + }, + { + "name": "xe1", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:ff:07.0" + }, + "vnfd-connection-point-ref": "xe1" + } + ], + "id": "ipsecvnf-baremetal", + "name": "ipsecvnf-baremetal", + "routing_table": [] + } + ] + } + + VNFD_2 = { + "benchmark": { + "kpi": [ + "packets_in", + "packets_fwd", + "packets_dropped" + ] + }, + "connection-point": [ + { + "name": "xe0", + "type": "VPORT" + }, + { + "name": "xe1", + "type": "VPORT" + } + ], + "description": "VPP IPsec", + "id": "VipsecApproxVnf", + "mgmt-interface": { + "ip": "10.10.10.101", + "password": "r00t", + "user": "root", + "vdu-id": "ipsecvnf-baremetal" + }, + "name": "IpsecVnf", + "short-name": "IpsecVnf", + "vdu": [ + { + "description": "VPP Ipsec", + "external-interface": [ + { + "name": "xe0", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "192.168.100.1", + "dst_mac": "90:e2:ba:7c:30:e8", + "ifname": "xe0", + "local_ip": "192.168.100.2", + "local_mac": "90:e2:ba:7c:41:a8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe0", + "peer_intf": { + "dpdk_port_num": 0, + "driver": "igb_uio", + "dst_ip": "192.168.100.2", + "dst_mac": "90:e2:ba:7c:41:a8", + "ifname": "xe0", + "local_ip": "192.168.100.1", + "local_mac": "90:e2:ba:7c:30:e8", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "tg__0", + "peer_ifname": "xe0", + "peer_name": "vnf__0", + "vld_id": "uplink_0", + "vpci": "0000:81:00.0" + }, + "peer_name": "tg__0", + "vld_id": "uplink_0", + "vpci": "0000:ff:06.0" + }, + "vnfd-connection-point-ref": "xe0" + }, + { + "name": "xe1", + "virtual-interface": { + "driver": "igb_uio", + "dst_ip": "1.1.1.2", + "dst_mac": "0a:b1:ec:fd:a2:66", + "ifname": "xe1", + "local_ip": "1.1.1.1", + "local_mac": "4e:90:85:d3:c5:13", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__0", + "peer_ifname": "xe1", + "peer_intf": { + "driver": "igb_uio", + "dst_ip": "1.1.1.1", + "dst_mac": "4e:90:85:d3:c5:13", + "ifname": "xe1", + "local_ip": "1.1.1.2", + "local_mac": "0a:b1:ec:fd:a2:66", + "netmask": "255.255.255.0", + "network": {}, + "node_name": "vnf__1", + "peer_ifname": "xe1", + "peer_name": "vnf__0", + "vld_id": "ciphertext", + "vpci": "0000:00:07.0" + }, + "peer_name": "vnf__1", + "vld_id": "ciphertext", + "vpci": "0000:ff:07.0" + }, + "vnfd-connection-point-ref": "xe1" + } + ], + "id": "ipsecvnf-baremetal", + "name": "ipsecvnf-baremetal", + "routing_table": [] + } + ] + } + + VNFD = { + 'vnfd:vnfd-catalog': { + 'vnfd': [ + VNFD_0, + ], + }, + } + + VPP_INTERFACES_DUMP = [ + { + "sw_if_index": 0, + "sup_sw_if_index": 0, + "l2_address_length": 0, + "l2_address": [0, 0, 0, 0, 0, 0, 0, 0], + "interface_name": "local0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 0, + "link_speed": 0, + "mtu": 0, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length": 6, + "l2_address": [144, 226, 186, 124, 65, 168, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 2, + "sup_sw_if_index": 2, + "l2_address_length": 6, + "l2_address": [78, 144, 133, 211, 197, 19, 0, 0], + "interface_name": "VirtualFunctionEthernetff/7/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9206, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + } + ] + + VPP_INTERFACES_DUMP_MAC_ERR = [ + { + "sw_if_index": 0, + "sup_sw_if_index": 0, + "l2_address_length": 0, + "l2_address": [0, 0, 0, 0, 0, 0, 0, 0], + "interface_name": "local0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 0, + "link_speed": 0, + "mtu": 0, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length": 6, + "l2_address": [144, 226, 186, 124, 65, 169, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }, + { + "sw_if_index": 2, + "sup_sw_if_index": 2, + "l2_address_length": 6, + "l2_address": [78, 144, 133, 211, 197, 20, 0, 0], + "interface_name": "VirtualFunctionEthernetff/7/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9206, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + } + ] + + CPU_LAYOUT = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 1, 1, 0]]} + CPU_SMT = {'cpuinfo': [[0, 0, 0, 0, 0, 0, 0, 0], + [1, 0, 0, 0, 0, 1, 1, 0], + [2, 1, 0, 0, 0, 2, 2, 1], + [3, 1, 0, 0, 0, 3, 3, 1], + [4, 2, 0, 0, 0, 4, 4, 2], + [5, 2, 0, 0, 0, 5, 5, 2], + [6, 3, 0, 0, 0, 6, 6, 3], + [7, 3, 0, 0, 0, 7, 7, 3], + [8, 4, 0, 0, 0, 8, 8, 4], + [9, 5, 0, 1, 0, 0, 0, 0], + [10, 6, 0, 1, 0, 1, 1, 0], + [11, 6, 0, 1, 0, 2, 2, 1], + [12, 7, 0, 1, 0, 3, 3, 1], + [13, 7, 0, 1, 0, 4, 4, 2], + [14, 8, 0, 1, 0, 5, 5, 2], + [15, 8, 0, 1, 0, 6, 6, 3], + [16, 9, 0, 1, 0, 7, 7, 3], + [17, 9, 0, 1, 0, 8, 8, 4]]} + + def test_kill_vnf(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, 0, 0 + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.kill_vnf() + + def test_kill_vnf_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 1, 0, 0 + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with self.assertRaises(RuntimeError) as raised: + vpp_setup_env_helper.kill_vnf() + + self.assertIn('Failed to stop service vpp', str(raised.exception)) + + def test_tear_down(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.tear_down() + + def test_start_vpp_service(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, 0, 0 + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.start_vpp_service() + + def test_start_vpp_service_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 1, 0, 0 + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with self.assertRaises(RuntimeError) as raised: + vpp_setup_env_helper.start_vpp_service() + + self.assertIn('Failed to start service vpp', str(raised.exception)) + + def test__update_vnfd_helper(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper._update_vnfd_helper( + {'vpp-data': {'vpp-key': 'vpp-value'}}) + + self.assertEqual({'vpp-key': 'vpp-value'}, + vpp_setup_env_helper.vnfd_helper.get('vpp-data', {})) + + def test__update_vnfd_helper_with_key(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper._update_vnfd_helper({'driver': 'qat'}, 'xe0') + + self.assertEqual('qat', + vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'driver')) + + def test__update_vnfd_helper_dict_without_key(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper._update_vnfd_helper( + {'mgmt-interface': {'name': 'net'}}) + + self.assertEqual({'ip': '10.10.10.101', + 'name': 'net', + 'password': 'r00t', + 'user': 'root', + 'vdu-id': 'ipsecvnf-baremetal'}, + vpp_setup_env_helper.vnfd_helper.get('mgmt-interface', + {})) + + def test_get_value_by_interface_key(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper._update_vnfd_helper( + {'vpp-data': {'vpp-key': 'vpp-value'}}, 'xe0') + + self.assertEqual({'vpp-key': 'vpp-value'}, + vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'vpp-data')) + + def test_get_value_by_interface_key_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper._update_vnfd_helper( + {'vpp-data': {'vpp-key': 'vpp-value'}}, 'xe0') + + self.assertIsNone(vpp_setup_env_helper.get_value_by_interface_key( + 'xe2', 'vpp-err')) + + def test_crypto_device_init(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.dpdk_bind_helper.load_dpdk_driver = mock.Mock() + vpp_setup_env_helper.dpdk_bind_helper.bind = mock.Mock() + + vpp_setup_env_helper.kill_vnf = mock.Mock() + vpp_setup_env_helper.pci_driver_unbind = mock.Mock() + + with mock.patch.object(vpp_setup_env_helper, 'get_pci_dev_driver') as \ + mock_get_pci_dev_driver, \ + mock.patch.object(vpp_setup_env_helper, 'set_sriov_numvfs') as \ + mock_set_sriov_numvfs: + mock_get_pci_dev_driver.return_value = 'igb_uio' + self.assertIsNone( + vpp_setup_env_helper.crypto_device_init('0000:ff:06.0', 32)) + mock_set_sriov_numvfs.assert_called() + + def test_get_sriov_numvfs(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '32', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertEqual(32, + vpp_setup_env_helper.get_sriov_numvfs('0000:ff:06.0')) + + def test_get_sriov_numvfs_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, 'err', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertEqual(0, + vpp_setup_env_helper.get_sriov_numvfs('0000:ff:06.0')) + + def test_set_sriov_numvfs(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.set_sriov_numvfs('0000:ff:06.0') + self.assertEqual(ssh_helper.execute.call_count, 1) + + def test_pci_driver_unbind(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.pci_driver_unbind('0000:ff:06.0') + self.assertEqual(ssh_helper.execute.call_count, 1) + + def test_get_pci_dev_driver(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = \ + 0, 'Slot: ff:07.0\n' \ + 'Class: Ethernet controller\n' \ + 'Vendor: Intel Corporation\n' \ + 'Device: 82599 Ethernet Controller Virtual Function\n' \ + 'SVendor: Intel Corporation\n' \ + 'SDevice: 82599 Ethernet Controller Virtual Function\n' \ + 'Rev: 01\n' \ + 'Driver: igb_uio\n' \ + 'Module: ixgbevf', '' + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertEqual('igb_uio', vpp_setup_env_helper.get_pci_dev_driver( + '0000:ff:06.0')) + + def test_get_pci_dev_driver_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 1, 'err', '' + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with self.assertRaises(RuntimeError) as raised: + vpp_setup_env_helper.get_pci_dev_driver( + '0000:ff:06.0') + + self.assertIn("'lspci -vmmks 0000:ff:06.0' failed", + str(raised.exception)) + + def test_get_pci_dev_driver_output_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = \ + 0, 'Slot: ff:07.0\n' \ + '\n\t' \ + 'Vendor: Intel Corporation\n' \ + 'Device: 82599 Ethernet Controller Virtual Function\n' \ + 'SVendor: Intel Corporation\n' \ + 'SDevice: 82599 Ethernet Controller Virtual Function\n' \ + 'Rev: 01\n' \ + 'Driver_err: igb_uio\n' \ + 'Module: ixgbevf', '' + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertIsNone( + vpp_setup_env_helper.get_pci_dev_driver('0000:ff:06.0')) + + def test_vpp_create_ipsec_tunnels(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + self.assertIsNone( + vpp_setup_env_helper.vpp_create_ipsec_tunnels('10.10.10.2', + '10.10.10.1', 'xe0', + 1, 1, mock.Mock(), + 'crypto_key', + mock.Mock(), + 'integ_key', + '20.20.20.0')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + + def test_vpp_create_ipsec_1000_tunnels(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + self.assertIsNone( + vpp_setup_env_helper.vpp_create_ipsec_tunnels('10.10.10.2', + '10.10.10.1', 'xe0', + 1000, 128000, + mock.Mock(), + 'crypto_key', + mock.Mock(), + 'integ_key', + '20.20.20.0')) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + + def test_apply_config(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertIsNone(vpp_setup_env_helper.apply_config(mock.Mock())) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + + def test_apply_config_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 1, '', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with self.assertRaises(RuntimeError) as raised: + vpp_setup_env_helper.apply_config(mock.Mock()) + + self.assertIn('Writing config file failed', str(raised.exception)) + + def test_vpp_route_add(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertIsNone( + vpp_setup_env_helper.vpp_route_add('xe0', '10.10.10.1', 24)) + + def test_vpp_route_add_without_index(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertIsNone( + vpp_setup_env_helper.vpp_route_add('xe0', '10.10.10.1', 24, + interface='xe0', + use_sw_index=False)) + + def test_add_arp_on_dut(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertEqual('', vpp_setup_env_helper.add_arp_on_dut('xe0', + '10.10.10.1', + '00:00:00:00:00:00')) + + def test_set_ip(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertEqual('', + vpp_setup_env_helper.set_ip('xe0', '10.10.10.1', + 24)) + + def test_set_interface_state(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertEqual('', + vpp_setup_env_helper.set_interface_state('xe0', + 'up')) + + def test_set_interface_state_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + with self.assertRaises(ValueError) as raised: + vpp_setup_env_helper.set_interface_state('xe0', 'error') + self.assertIn('Unexpected interface state: error', + str(raised.exception)) + + def test_set_interface_down_state(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertEqual('', + vpp_setup_env_helper.set_interface_state('xe0', + 'down')) + + def test_vpp_set_interface_mtu(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = '' + self.assertIsNone( + vpp_setup_env_helper.vpp_set_interface_mtu('xe0', 9200)) + + def test_vpp_interfaces_ready_wait(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + self.assertIsNone(vpp_setup_env_helper.vpp_interfaces_ready_wait()) + + def test_vpp_interfaces_ready_wait_timeout(self): + json_output = [[ + { + "sw_if_index": 0, + "sup_sw_if_index": 0, + "l2_address_length": 0, + "l2_address": [0, 0, 0, 0, 0, 0, 0, 0], + "interface_name": "xe0", + "admin_up_down": 1, + "link_up_down": 0, + "link_duplex": 0, + "link_speed": 0, + "mtu": 0, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }]] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + with self.assertRaises(RuntimeError) as raised: + vpp_setup_env_helper.vpp_interfaces_ready_wait(5) + self.assertIn('timeout, not up [\'xe0\']', str(raised.exception)) + + def test_vpp_get_interface_data(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + self.assertEqual(json_output[0], + vpp_setup_env_helper.vpp_get_interface_data()) + + def test_vpp_get_interface_data_ifname(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + self.assertEqual(json_output[0][2], + vpp_setup_env_helper.vpp_get_interface_data( + 'VirtualFunctionEthernetff/7/0')) + + def test_vpp_get_interface_data_ifname_error(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + self.assertEqual({}, vpp_setup_env_helper.vpp_get_interface_data( + 'error')) + + def test_vpp_get_interface_data_ifindex(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + self.assertEqual(json_output[0][1], + vpp_setup_env_helper.vpp_get_interface_data(1)) + + def test_vpp_get_interface_data_error(self): + json_output = [self.VPP_INTERFACES_DUMP] + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + + with mock.patch.object(vpp_helpers.VatTerminal, + 'vat_terminal_exec_cmd_from_template') as \ + mock_vat_terminal_exec_cmd_from_template: + mock_vat_terminal_exec_cmd_from_template.return_value = json_output + with self.assertRaises(TypeError) as raised: + vpp_setup_env_helper.vpp_get_interface_data(1.0) + self.assertEqual('', str(raised.exception)) + + def test_update_vpp_interface_data(self): + output = '{}\n{}'.format(self.VPP_INTERFACES_DUMP, + 'dump_interface_table:6019: JSON output ' \ + 'supported only for VPE API calls and dump_stats_table\n' \ + '/opt/nsb_bin/vpp/templates/dump_interfaces.vat(2): \n' \ + 'dump_interface_table error: Misc') + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, output.replace("\'", "\""), '' + ssh_helper.join_bin_path.return_value = '/opt/nsb_bin/vpp/templates' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertIsNone(vpp_setup_env_helper.update_vpp_interface_data()) + self.assertGreaterEqual(ssh_helper.execute.call_count, 1) + self.assertEqual('TenGigabitEthernetff/6/0', + vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'vpp_name')) + self.assertEqual(1, vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'vpp_sw_index')) + self.assertEqual('VirtualFunctionEthernetff/7/0', + vpp_setup_env_helper.get_value_by_interface_key( + 'xe1', 'vpp_name')) + self.assertEqual(2, vpp_setup_env_helper.get_value_by_interface_key( + 'xe1', 'vpp_sw_index')) + + def test_update_vpp_interface_data_error(self): + output = '{}\n{}'.format(self.VPP_INTERFACES_DUMP_MAC_ERR, + 'dump_interface_table:6019: JSON output ' \ + 'supported only for VPE API calls and dump_stats_table\n' \ + '/opt/nsb_bin/vpp/templates/dump_interfaces.vat(2): \n' \ + 'dump_interface_table error: Misc') + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, output.replace("\'", "\""), '' + ssh_helper.join_bin_path.return_value = '/opt/nsb_bin/vpp/templates' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertIsNone(vpp_setup_env_helper.update_vpp_interface_data()) + self.assertGreaterEqual(ssh_helper.execute.call_count, 1) + + def test_iface_update_numa(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '0', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertIsNone(vpp_setup_env_helper.iface_update_numa()) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + self.assertEqual(0, vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual(0, vpp_setup_env_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + + def test_iface_update_numa_error(self): + vnfd_helper = VnfdHelper(self.VNFD_1) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '-1', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout: + mock_get_cpu_layout.return_value = self.CPU_LAYOUT + sys_cores = cpu.CpuSysCores(ssh_helper) + vpp_setup_env_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + self.assertIsNone(vpp_setup_env_helper.iface_update_numa()) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + self.assertEqual(0, vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertEqual(0, vpp_setup_env_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + + def test_iface_update_without_numa(self): + vnfd_helper = VnfdHelper(self.VNFD_2) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, '-1', '' + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with mock.patch.object(cpu.CpuSysCores, 'get_cpu_layout') as \ + mock_get_cpu_layout: + mock_get_cpu_layout.return_value = self.CPU_SMT + sys_cores = cpu.CpuSysCores(ssh_helper) + vpp_setup_env_helper._update_vnfd_helper( + sys_cores.get_cpu_layout()) + self.assertIsNone(vpp_setup_env_helper.iface_update_numa()) + self.assertGreaterEqual(ssh_helper.execute.call_count, 2) + self.assertIsNone(vpp_setup_env_helper.get_value_by_interface_key( + 'xe0', 'numa_node')) + self.assertIsNone(vpp_setup_env_helper.get_value_by_interface_key( + 'xe1', 'numa_node')) + + def test_execute_script(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + vpp_setup_env_helper.execute_script('dump_interfaces.vat', True, True) + self.assertGreaterEqual(ssh_helper.put_file.call_count, 1) + self.assertGreaterEqual(ssh_helper.execute.call_count, 1) + + def test_execute_script_error(self): + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.side_effect = Exception + + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + with self.assertRaises(Exception) as raised: + vpp_setup_env_helper.execute_script('dump_interfaces.vat', True, + True) + self.assertIn( + 'VAT script execution failed: vpp_api_test json in dump_interfaces.vat script', + str(raised.exception)) + self.assertGreaterEqual(ssh_helper.put_file.call_count, 1) + + def test_execute_script_json_out(self): + json_output = [ + { + "sw_if_index": 0, + "sup_sw_if_index": 0 + }, + { + "l2_address_length": 6, + "l2_address": [144, 226, 186, 124, 65, 168, 0, 0] + }, + { + "interface_name": "VirtualFunctionEthernetff/7/0", + "admin_up_down": 0 + } + ] + output = '{}\n{}'.format(json_output, + 'dump_interface_table:6019: JSON output ' \ + 'supported only for VPE API calls and dump_stats_table\n' \ + '/opt/nsb_bin/vpp/templates/dump_interfaces.vat(2): \n' \ + 'dump_interface_table error: Misc') + vnfd_helper = VnfdHelper(self.VNFD_0) + ssh_helper = mock.Mock() + ssh_helper.execute.return_value = 0, output, '' + ssh_helper.join_bin_path.return_value = '/opt/nsb_bin/vpp/templates' + scenario_helper = mock.Mock() + vpp_setup_env_helper = VppSetupEnvHelper(vnfd_helper, ssh_helper, + scenario_helper) + self.assertEqual(str(json_output), + vpp_setup_env_helper.execute_script_json_out( + 'dump_interfaces.vat')) + + def test_self_cleanup_vat_json_output(self): + json_output = [ + { + "sw_if_index": 0, + "sup_sw_if_index": 0 + }, + { + "l2_address_length": 6, + "l2_address": [144, 226, 186, 124, 65, 168, 0, 0] + }, + { + "interface_name": "VirtualFunctionEthernetff/7/0", + "admin_up_down": 0 + } + ] + + output = '{}\n{}'.format(json_output, + 'dump_interface_table:6019: JSON output ' \ + 'supported only for VPE API calls and dump_stats_table\n' \ + '/opt/nsb_bin/vpp/templates/dump_interfaces.vat(2): \n' \ + 'dump_interface_table error: Misc') + self.assertEqual(str(json_output), + VppSetupEnvHelper.cleanup_vat_json_output(output, + '/opt/nsb_bin/vpp/templates/dump_interfaces.vat')) + + def test__convert_mac_to_number_list(self): + self.assertEqual([144, 226, 186, 124, 65, 168], + VppSetupEnvHelper._convert_mac_to_number_list( + '90:e2:ba:7c:41:a8')) + + def test_get_vpp_interface_by_mac(self): + mac_address = '90:e2:ba:7c:41:a8' + self.assertEqual({'admin_up_down': 0, + 'interface_name': 'TenGigabitEthernetff/6/0', + 'l2_address': [144, 226, 186, 124, 65, 168, 0, 0], + 'l2_address_length': 6, + 'link_duplex': 2, + 'link_speed': 32, + 'link_up_down': 0, + 'mtu': 9202, + 'sub_default': 0, + 'sub_dot1ad': 0, + 'sub_exact_match': 0, + 'sub_id': 0, + 'sub_inner_vlan_id': 0, + 'sub_inner_vlan_id_any': 0, + 'sub_number_of_tags': 0, + 'sub_outer_vlan_id': 0, + 'sub_outer_vlan_id_any': 0, + 'sup_sw_if_index': 1, + 'sw_if_index': 1, + 'vtr_op': 0, + 'vtr_push_dot1q': 0, + 'vtr_tag1': 0, + 'vtr_tag2': 0}, + VppSetupEnvHelper.get_vpp_interface_by_mac( + self.VPP_INTERFACES_DUMP, mac_address)) + + def test_get_vpp_interface_by_mac_error(self): + mac_address = '90:e2:ba:7c:41:a9' + with self.assertRaises(ValueError) as raised: + VppSetupEnvHelper.get_vpp_interface_by_mac( + [{ + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length": 7, + "l2_address": [144, 226, 186, 124, 65, 169, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }], mac_address) + + self.assertIn('l2_address_length value is not 6.', + str(raised.exception)) + + def test_get_vpp_interface_by_mac_l2_error(self): + mac_address = '90:e2:ba:7c:41:a7' + with self.assertRaises(KeyError) as raised: + VppSetupEnvHelper.get_vpp_interface_by_mac( + [{ + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length": 6, + "l2_address_err": [144, 226, 186, 124, 65, 167, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }], mac_address) + + self.assertIn( + 'key l2_address not found in interface dict.Probably input list ' \ + 'is not parsed from correct VAT json output.', + str(raised.exception)) + + def test_get_vpp_interface_by_mac_l2_length_error(self): + mac_address = '90:e2:ba:7c:41:a6' + with self.assertRaises(KeyError) as raised: + VppSetupEnvHelper.get_vpp_interface_by_mac( + [{ + "sw_if_index": 1, + "sup_sw_if_index": 1, + "l2_address_length_err": 6, + "l2_address": [144, 226, 186, 124, 65, 166, 0, 0], + "interface_name": "TenGigabitEthernetff/6/0", + "admin_up_down": 0, + "link_up_down": 0, + "link_duplex": 2, + "link_speed": 32, + "mtu": 9202, + "sub_id": 0, + "sub_dot1ad": 0, + "sub_number_of_tags": 0, + "sub_outer_vlan_id": 0, + "sub_inner_vlan_id": 0, + "sub_exact_match": 0, + "sub_default": 0, + "sub_outer_vlan_id_any": 0, + "sub_inner_vlan_id_any": 0, + "vtr_op": 0, + "vtr_push_dot1q": 0, + "vtr_tag1": 0, + "vtr_tag2": 0 + }], mac_address) + + self.assertIn( + 'key l2_address_length not found in interface dict. Probably ' \ + 'input list is not parsed from correct VAT json output.', + str(raised.exception)) + + def test_get_prefix_length(self): + start_ip = '10.10.10.0' + end_ip = '10.10.10.127' + ips = [ipaddress.ip_address(ip) for ip in + [str(ipaddress.ip_address(start_ip)), str(end_ip)]] + lowest_ip, highest_ip = min(ips), max(ips) + + self.assertEqual(25, + VppSetupEnvHelper.get_prefix_length(int(lowest_ip), + int(highest_ip), + lowest_ip.max_prefixlen)) + + def test_get_prefix_length_zero_prefix(self): + start_ip = '10.0.0.0' + end_ip = '10.0.0.0' + ips = [ipaddress.ip_address(ip) for ip in + [str(ipaddress.ip_address(start_ip)), str(end_ip)]] + lowest_ip, highest_ip = min(ips), max(ips) + + self.assertEqual(0, + VppSetupEnvHelper.get_prefix_length(int(lowest_ip), + int(highest_ip), + 0)) + + +class TestVatTerminal(unittest.TestCase): + + def test___init___error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_open.side_effect = exceptions.SSHTimeout + + with self.assertRaises(RuntimeError) as raised: + VatTerminal(ssh_helper, json_param=True) + self.assertIn('Cannot open interactive terminal', + str(raised.exception)) + + def test___init___exec_error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.side_effect = exceptions.SSHTimeout + VatTerminal(ssh_helper, json_param=True) + + def test_vat_terminal_exec_cmd(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.return_value = str( + {'empty': 'value'}).replace("\'", "\"") + vat_terminal = VatTerminal(ssh_helper, json_param=True) + + self.assertEqual({'empty': 'value'}, + vat_terminal.vat_terminal_exec_cmd( + "hw_interface_set_mtu sw_if_index 1 mtu 9200")) + + def test_vat_terminal_exec_cmd_array(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.return_value = str( + [{'empty': 'value'}]).replace("\'", "\"") + vat_terminal = VatTerminal(ssh_helper, json_param=True) + + self.assertEqual([{'empty': 'value'}], + vat_terminal.vat_terminal_exec_cmd( + "hw_interface_set_mtu sw_if_index 1 mtu 9200")) + + def test_vat_terminal_exec_cmd_without_output(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.return_value = str( + {'empty': 'value'}).replace("\'", "\"") + vat_terminal = VatTerminal(ssh_helper, json_param=False) + + self.assertIsNone(vat_terminal.vat_terminal_exec_cmd( + "hw_interface_set_mtu sw_if_index 1 mtu 9200")) + + def test_vat_terminal_exec_cmd_error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.return_value = str( + {'empty': 'value'}).replace("\'", "\"") + ssh_helper.interactive_terminal_exec_command.side_effect = exceptions.SSHTimeout + + vat_terminal = VatTerminal(ssh_helper, json_param=True) + + with self.assertRaises(RuntimeError) as raised: + vat_terminal.vat_terminal_exec_cmd( + "hw_interface_set_mtu sw_if_index 1 mtu 9200") + self.assertIn( + 'VPP is not running on node. VAT command hw_interface_set_mtu ' \ + 'sw_if_index 1 mtu 9200 execution failed', + str(raised.exception)) + + def test_vat_terminal_exec_cmd_output_error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.return_value = str( + 'empty: value').replace("\'", "\"") + + vat_terminal = VatTerminal(ssh_helper, json_param=True) + + with self.assertRaises(RuntimeError) as raised: + vat_terminal.vat_terminal_exec_cmd( + "hw_interface_set_mtu sw_if_index 1 mtu 9200") + self.assertIn( + 'VAT command hw_interface_set_mtu sw_if_index 1 mtu 9200: no JSON data.', + str(raised.exception)) + + def test_vat_terminal_close(self): + ssh_helper = mock.Mock() + vat_terminal = VatTerminal(ssh_helper, json_param=False) + self.assertIsNone(vat_terminal.vat_terminal_close()) + + def test_vat_terminal_close_error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_exec_command.side_effect = exceptions.SSHTimeout + vat_terminal = VatTerminal(ssh_helper, json_param=False) + with self.assertRaises(RuntimeError) as raised: + vat_terminal.vat_terminal_close() + self.assertIn('Failed to close VAT console', str(raised.exception)) + + def test_vat_terminal_close_vat_error(self): + ssh_helper = mock.Mock() + ssh_helper.interactive_terminal_close.side_effect = exceptions.SSHTimeout + vat_terminal = VatTerminal(ssh_helper, json_param=False) + with self.assertRaises(RuntimeError) as raised: + vat_terminal.vat_terminal_close() + self.assertIn('Cannot close interactive terminal', + str(raised.exception)) + + def test_vat_terminal_exec_cmd_from_template(self): + ssh_helper = mock.Mock() + vat_terminal = VatTerminal(ssh_helper, json_param=False) + + with mock.patch.object(vat_terminal, 'vat_terminal_exec_cmd') as \ + mock_vat_terminal_exec_cmd: + mock_vat_terminal_exec_cmd.return_value = 'empty' + self.assertEqual(['empty'], + vat_terminal.vat_terminal_exec_cmd_from_template( + "hw_interface_set_mtu.vat", sw_if_index=1, + mtu=9200)) diff --git a/yardstick/tests/unit/orchestrator/__init__.py b/yardstick/tests/unit/orchestrator/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/orchestrator/__init__.py diff --git a/yardstick/tests/unit/orchestrator/test_heat.py b/yardstick/tests/unit/orchestrator/test_heat.py new file mode 100644 index 000000000..2e60a72cb --- /dev/null +++ b/yardstick/tests/unit/orchestrator/test_heat.py @@ -0,0 +1,417 @@ +############################################################################## +# Copyright (c) 2017 Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import tempfile + +import munch +import mock +from oslo_serialization import jsonutils +from oslo_utils import uuidutils +import shade +import unittest + +from yardstick.benchmark.contexts import node +from yardstick.common import constants +from yardstick.common import exceptions +from yardstick.orchestrator import heat + + +class FakeStack(object): + + def __init__(self, outputs=None, status=None, id=None): + self.outputs = outputs + self.status = status + self.id = id + + +class HeatStackTestCase(unittest.TestCase): + + def setUp(self): + self.stack_name = 'STACK NAME' + with mock.patch.object(shade, 'openstack_cloud'): + self.heatstack = heat.HeatStack(self.stack_name) + self._mock_stack_create = mock.patch.object(self.heatstack._cloud, + 'create_stack') + self.mock_stack_create = self._mock_stack_create.start() + self._mock_stack_delete = mock.patch.object(self.heatstack._cloud, + 'delete_stack') + self.mock_stack_delete = self._mock_stack_delete.start() + self._mock_stack_get = mock.patch.object(self.heatstack._cloud, + 'get_stack') + self.mock_stack_get = self._mock_stack_get.start() + + self.addCleanup(self._cleanup) + + def _cleanup(self): + self._mock_stack_create.stop() + self._mock_stack_delete.stop() + self._mock_stack_get.stop() + heat._DEPLOYED_STACKS = {} + + @mock.patch.object(shade, 'openstack_cloud') + def test__init(self, mock_openstack_cloud): + os_cloud_config = {'key': 'value'} + heatstack = heat.HeatStack('name', os_cloud_config=os_cloud_config) + self.assertEqual('name', heatstack.name) + os_cloud_config.update(constants.OS_CLOUD_DEFAULT_CONFIG) + mock_openstack_cloud.assert_called_once_with(**os_cloud_config) + + def test_create(self): + template = {'tkey': 'tval'} + heat_parameters = {'pkey': 'pval'} + outputs = [{'output_key': 'okey', 'output_value': 'oval'}] + id = uuidutils.generate_uuid() + self.mock_stack_create.return_value = FakeStack( + outputs=outputs, status=mock.Mock(), id=id) + mock_tfile = mock.Mock() + with mock.patch.object(tempfile._TemporaryFileWrapper, '__enter__', + return_value=mock_tfile): + self.heatstack.create(template, heat_parameters, True, 100) + mock_tfile.write.assert_called_once_with(jsonutils.dump_as_bytes(template)) + mock_tfile.close.assert_called_once() + + self.mock_stack_create.assert_called_once_with( + self.stack_name, template_file=mock_tfile.name, wait=True, + timeout=100, pkey='pval') + self.assertEqual({'okey': 'oval'}, self.heatstack.outputs) + self.assertEqual(heat._DEPLOYED_STACKS[id], self.heatstack._stack) + + def test_stacks_exist(self): + self.assertEqual(0, self.heatstack.stacks_exist()) + heat._DEPLOYED_STACKS['id'] = 'stack' + self.assertEqual(1, self.heatstack.stacks_exist()) + + def test_delete_not_uuid(self): + self.assertIsNone(self.heatstack.delete()) + + def test_delete_existing_uuid(self): + id = uuidutils.generate_uuid() + self.heatstack._stack = FakeStack( + outputs=mock.Mock(), status=mock.Mock(), id=id) + heat._DEPLOYED_STACKS[id] = self.heatstack._stack + delete_return = mock.Mock() + self.mock_stack_delete.return_value = delete_return + + ret = self.heatstack.delete(wait=True) + self.assertEqual(delete_return, ret) + self.assertFalse(heat._DEPLOYED_STACKS) + self.mock_stack_delete.assert_called_once_with(id, wait=True) + + def test_delete_bug_in_shade(self): + id = uuidutils.generate_uuid() + self.heatstack._stack = FakeStack( + outputs=mock.Mock(), status=mock.Mock(), id=id) + heat._DEPLOYED_STACKS[id] = self.heatstack._stack + self.mock_stack_delete.side_effect = TypeError() + + ret = self.heatstack.delete(wait=True) + self.assertTrue(ret) + self.assertFalse(heat._DEPLOYED_STACKS) + self.mock_stack_delete.assert_called_once_with(id, wait=True) + + def test_get(self): + # make sure shade/get_stack is called with the appropriate vars + self.mock_stack_get.return_value = munch.Munch( + id="my-existing-stack-id", + outputs=[ + { + u'output_value': u'b734d06a-dec7-...', + u'output_key': u'ares.demo-test-port-network_id', + u'description': u'' + }, + {u'output_value': u'b08da78c-2218-...', + u'output_key': u'ares.demo-test-port-subnet_id', + u'description': u'' + }, + {u'output_value': u'10.0.1.0/24', + u'output_key': u'demo-test-subnet-cidr', + u'description': u'' + }, + {u'output_value': u'b08da78c-2218-...', + u'output_key': u'demo-test-subnet', + u'description': u'' + }, + {u'output_value': u'b1a03624-aefc-...', + u'output_key': u'ares.demo', + u'description': u'' + }, + {u'output_value': u'266a8088-c630-...', + u'output_key': u'demo-secgroup', + u'description': u'' + }, + {u'output_value': u'10.0.1.5', + u'output_key': u'ares.demo-test-port', + u'description': u'' + }, + {u'output_value': u'10.0.1.1', + u'output_key': u'demo-test-subnet-gateway_ip', + u'description': u'' + }, + {u'output_value': u'', + u'output_key': u'ares.demo-test-port-device_id', + u'description': u'' + }, + {u'output_value': u'172.24.4.7', + u'output_key': u'ares.demo-fip', + u'description': u'' + }, + {u'output_value': u'fa:16:3e:6c:c3:0f', + u'output_key': u'ares.demo-test-port-mac_address', + u'description': u''} + ] + ) + expected_outputs = { + 'ares.demo-test-port-network_id': 'b734d06a-dec7-...', + 'ares.demo-test-port-subnet_id': 'b08da78c-2218-...', + 'demo-test-subnet-cidr': '10.0.1.0/24', + 'demo-test-subnet': 'b08da78c-2218-...', + 'ares.demo': 'b1a03624-aefc-...', + 'demo-secgroup': '266a8088-c630-...', + 'ares.demo-test-port': '10.0.1.5', + 'demo-test-subnet-gateway_ip': '10.0.1.1', + 'ares.demo-test-port-device_id': '', + 'ares.demo-fip': '172.24.4.7', + 'ares.demo-test-port-mac_address': 'fa:16:3e:6c:c3:0f', + } + + stack_id = "my-existing-stack-id" + self.heatstack.name = "my-existing-stack" + self.heatstack.get() + + self.mock_stack_get.assert_called_once_with(self.heatstack.name) + self.assertEqual(expected_outputs, self.heatstack.outputs) + self.assertEqual(1, len(heat._DEPLOYED_STACKS)) + self.assertEqual(self.heatstack._stack, + heat._DEPLOYED_STACKS[stack_id]) + + def test_get_invalid_name(self): + # No context matching this name exists + self.mock_stack_get.return_value = [] + self.heatstack.name = 'not-a-stack' + self.heatstack.get() + self.assertEqual(0, len(heat._DEPLOYED_STACKS)) + + +class HeatTemplateTestCase(unittest.TestCase): + + def setUp(self): + self._os_cloud_config = {'key1': 'value1'} + self.template = heat.HeatTemplate( + 'test', os_cloud_config=self._os_cloud_config) + + def test_add_tenant_network(self): + self.template.add_network('some-network') + + self.assertEqual('OS::Neutron::Net', + self.template.resources['some-network']['type']) + + def test_add_provider_network(self): + self.template.add_network('some-network', 'physnet2', 'sriov') + + self.assertEqual(self.template.resources['some-network']['type'], + 'OS::Neutron::ProviderNet') + self.assertEqual(self.template.resources['some-network'][ + 'properties']['physical_network'], 'physnet2') + + def test_add_subnet(self): + netattrs = {'cidr': '10.0.0.0/24', + 'provider': None, + 'external_network': 'ext_net'} + self.template.add_subnet('some-subnet', "some-network", + netattrs['cidr']) + + self.assertEqual(self.template.resources['some-subnet']['type'], + 'OS::Neutron::Subnet') + self.assertEqual(self.template.resources['some-subnet']['properties'][ + 'cidr'], '10.0.0.0/24') + + def test_add_router(self): + self.template.add_router('some-router', 'ext-net', 'some-subnet') + + self.assertEqual(self.template.resources['some-router']['type'], + 'OS::Neutron::Router') + self.assertIn('some-subnet', + self.template.resources['some-router']['depends_on']) + + def test_add_router_interface(self): + self.template.add_router_interface('some-router-if', 'some-router', + 'some-subnet') + + self.assertEqual(self.template.resources['some-router-if']['type'], + 'OS::Neutron::RouterInterface') + self.assertIn('some-subnet', + self.template.resources['some-router-if']['depends_on']) + + def test_add_servergroup(self): + self.template.add_servergroup('some-server-group', 'anti-affinity') + + self.assertEqual(self.template.resources['some-server-group']['type'], + 'OS::Nova::ServerGroup') + self.assertEqual(self.template.resources['some-server-group'][ + 'properties']['policies'], ['anti-affinity']) + + def test_add_security_group(self): + security_group = { + 'rules': [ + {'remote_ip_prefix': '0.0.0.0/0', + 'port_range_max': 65535, + 'port_range_min': 1, + 'protocol': 'custom'}, + ] + } + self.template.add_security_group('some-security-group', security_group) + + secgroup_rsc = self.template.resources['some-security-group'] + + self.assertEqual(secgroup_rsc['type'], "OS::Neutron::SecurityGroup") + self.assertEqual(secgroup_rsc['properties']['description'], + "Custom security group rules defined by the user") + self.assertEqual(secgroup_rsc['properties']['rules'][0]['protocol'], + 'custom') + + def test__add_resources_to_template_raw(self): + test_context = node.NodeContext() + self.addCleanup(test_context._delete_context) + test_context._name = 'foo' + test_context.template_file = '/tmp/some-heat-file' + test_context.heat_parameters = {'image': 'cirros'} + test_context.key_filename = "/tmp/1234" + test_context.keypair_name = "foo-key" + test_context.secgroup_name = "foo-secgroup" + test_context.key_uuid = "2f2e4997-0a8e-4eb7-9fa4-f3f8fbbc393b" + + test_context.tmpfile = tempfile.NamedTemporaryFile( + delete=True, mode='w+t') + test_context.tmpfile.write("heat_template_version: 2015-04-30") + test_context.tmpfile.flush() + test_context.tmpfile.seek(0) + heat_template = heat.HeatTemplate('template name') + heat_template.resources = {} + + heat_template.add_network("network1") + heat_template.add_network("network2") + heat_template.add_security_group("sec_group1") + heat_template.add_security_group("sec_group2") + heat_template.add_subnet("subnet1", "network1", "cidr1") + heat_template.add_subnet("subnet2", "network2", "cidr2") + heat_template.add_router("router1", "gw1", "subnet1") + heat_template.add_router_interface("router_if1", "router1", "subnet1") + network1 = mock.MagicMock() + network1.stack_name = "network1" + network1.subnet_stack_name = "subnet1" + network1.vnic_type = "normal" + network2 = mock.MagicMock() + network2.stack_name = "network2" + network2.subnet_stack_name = "subnet2" + network2.vnic_type = "normal" + heat_template.add_port("port1", network1) + heat_template.add_port("port2", network2, + sec_group_id="sec_group1", provider="not-sriov") + heat_template.add_port("port3", network2, + sec_group_id="sec_group1", provider="sriov") + heat_template.add_floating_ip("floating_ip1", "network1", "port1", + "router_if1") + heat_template.add_floating_ip("floating_ip2", "network2", "port2", + "router_if2", "foo-secgroup") + heat_template.add_floating_ip_association("floating_ip1_association", + "floating_ip1", "port1") + heat_template.add_servergroup("server_grp2", "affinity") + heat_template.add_servergroup("server_grp3", "anti-affinity") + heat_template.add_security_group("security_group") + heat_template.add_server(name="server1", image="image1", + flavor="flavor1", flavors=[]) + heat_template.add_server_group(name="servergroup", + policies=["policy1", "policy2"]) + heat_template.add_server_group(name="servergroup", + policies="policy1") + heat_template.add_server( + name="server2", image="image1", flavor="flavor1", flavors=[], + ports=["port1", "port2"], networks=["network1", "network2"], + scheduler_hints="hints1", user="user1", key_name="foo-key", + user_data="user", metadata={"cat": 1, "doc": 2}, + additional_properties={"prop1": 1, "prop2": 2}) + heat_template.add_server( + name="server2", image="image1", flavor="flavor1", + flavors=["flavor1", "flavor2"], ports=["port1", "port2"], + networks=["network1", "network2"], scheduler_hints="hints1", + user="user1", key_name="foo-key", user_data="user", + metadata={"cat": 1, "doc": 2}, + additional_properties={"prop1": 1, "prop2": 2}) + heat_template.add_server( + name="server2", image="image1", flavor="flavor1", + flavors=["flavor3", "flavor4"], ports=["port1", "port2"], + networks=["network1", "network2"], scheduler_hints="hints1", + user="user1", key_name="foo-key", user_data="user", + metadata={"cat": 1, "doc": 2}, + additional_properties={"prop1": 1, "prop2": 2}) + heat_template.add_flavor(name="flavor1", vcpus=1, ram=2048, disk=1, + extra_specs={"cat": 1, "dog": 2}) + heat_template.add_flavor(name=None, vcpus=1, ram=2048) + heat_template.add_server( + name="server1", image="image1", flavor="flavor1", flavors=[], + ports=["port1", "port2"], networks=["network1", "network2"], + scheduler_hints="hints1", user="user1", key_name="foo-key", + user_data="user", metadata={"cat": 1, "doc": 2}, + additional_properties={"prop1": 1, "prop2": 2}) + heat_template.add_network("network1") + + heat_template.add_flavor("test") + self.assertEqual(heat_template.resources['test']['type'], + 'OS::Nova::Flavor') + + def test_create_not_block(self): + heat_stack = mock.Mock() + with mock.patch.object(heat, 'HeatStack', return_value=heat_stack) \ + as mock_heatstack: + ret = self.template.create(block=False) + + mock_heatstack.assert_called_once_with( + self.template.name, os_cloud_config=self.template._os_cloud_config) + heat_stack.create.assert_called_once_with( + self.template._template, self.template.heat_parameters, False, + 3600) + self.assertEqual(heat_stack, ret) + + def test_create_block(self): + heat_stack = mock.Mock() + heat_stack.status = self.template.HEAT_STATUS_COMPLETE + with mock.patch.object(heat, 'HeatStack', return_value=heat_stack): + ret = self.template.create(block=False) + heat_stack.create.assert_called_once_with( + self.template._template, self.template.heat_parameters, False, + 3600) + self.assertEqual(heat_stack, ret) + + def test_create_block_status_no_complete(self): + heat_stack = mock.Mock() + heat_stack.status = 'other status' + heat_stack.get_failures.return_value = [] + with mock.patch.object(heat, 'HeatStack', return_value=heat_stack): + self.assertRaises(exceptions.HeatTemplateError, + self.template.create, block=True) + heat_stack.create.assert_called_once_with( + self.template._template, self.template.heat_parameters, True, + 3600) + + def test_create_block_status_no_complete_with_reasons(self): + heat_stack = mock.Mock() + heat_stack.status = 'other status' + heat_stack.get_failures.return_value = [ + mock.Mock(resource_status_reason="A reason"), + mock.Mock(resource_status_reason="Something else") + ] + with mock.patch.object(heat, 'HeatStack', return_value=heat_stack): + with mock.patch.object(heat, 'log') as mock_log: + self.assertRaises(exceptions.HeatTemplateError, + self.template.create, block=True) + mock_log.error.assert_any_call("%s", "A reason") + mock_log.error.assert_any_call("%s", "Something else") + heat_stack.create.assert_called_once_with( + self.template._template, self.template.heat_parameters, True, + 3600) diff --git a/yardstick/tests/unit/orchestrator/test_kubernetes.py b/yardstick/tests/unit/orchestrator/test_kubernetes.py new file mode 100644 index 000000000..2d5c4a26f --- /dev/null +++ b/yardstick/tests/unit/orchestrator/test_kubernetes.py @@ -0,0 +1,643 @@ +############################################################################## +# Copyright (c) 2017 Intel Corporation +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import copy + +import mock + +from yardstick.common import exceptions +from yardstick.common import kubernetes_utils +from yardstick.orchestrator import kubernetes +from yardstick.tests.unit import base + + +class GetTemplateTestCase(base.BaseUnitTestCase): + + def test_get_template(self): + output_t = { + "apiVersion": "v1", + "kind": "ReplicationController", + "metadata": { + "name": "host-k8s-86096c30" + }, + "spec": { + "replicas": 1, + "template": { + "metadata": { + "labels": { + "app": "host-k8s-86096c30" + } + }, + "spec": { + "containers": [ + { + "args": [ + "-c", + "chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \ +service ssh restart;while true ; do sleep 10000; done" + ], + "command": [ + "/bin/bash" + ], + "image": "openretriever/yardstick", + "name": "host-k8s-86096c30-container", + "volumeMounts": [ + { + "mountPath": "/tmp/.ssh/", + "name": "k8s-86096c30-key", + "readOnly": False + } + ] + } + ], + "volumes": [ + { + "configMap": { + "name": "k8s-86096c30-key" + }, + "name": "k8s-86096c30-key" + } + ], + "nodeSelector": { + "kubernetes.io/hostname": "node-01" + }, + "restartPolicy": "Always", + "tolerations": [ + {"operator": "Exists"} + ] + } + } + } + } + input_s = { + 'command': '/bin/bash', + 'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \ +service ssh restart;while true ; do sleep 10000; done'], + 'ssh_key': 'k8s-86096c30-key', + 'nodeSelector': {'kubernetes.io/hostname': 'node-01'}, + 'volumes': [], + 'restartPolicy': 'Always' + } + name = 'host-k8s-86096c30' + output_r = kubernetes.ReplicationControllerObject( + name, **input_s).get_template() + self.assertEqual(output_r, output_t) + + def test_get_template_invalid_restart_policy(self): + input_s = {'restartPolicy': 'invalid_option'} + name = 'host-k8s-86096c30' + with self.assertRaises(exceptions.KubernetesWrongRestartPolicy): + kubernetes.ReplicationControllerObject( + name, **input_s).get_template() + + +class GetRcPodsTestCase(base.BaseUnitTestCase): + + @mock.patch('yardstick.orchestrator.kubernetes.k8s_utils.get_pod_list') + def test_get_rc_pods(self, mock_get_pod_list): + servers = { + 'host': { + 'image': 'openretriever/yardstick', + 'command': '/bin/bash', + 'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \ +service ssh restart;while true ; do sleep 10000; done'] + }, + 'target': { + 'image': 'openretriever/yardstick', + 'command': '/bin/bash', + 'args': ['-c', 'chmod 700 ~/.ssh; chmod 600 ~/.ssh/*; \ +service ssh restart;while true ; do sleep 10000; done'] + } + } + k8s_template = kubernetes.KubernetesTemplate('k8s-86096c30', servers) + mock_get_pod_list.return_value.items = [] + pods = k8s_template.get_rc_pods() + self.assertEqual(pods, []) + + +class ReplicationControllerObjectTestCase(base.BaseUnitTestCase): + + def test__init_one_container(self): + pod_name = 'pod_name' + _kwargs = {'args': ['arg1', 'arg2'], + 'image': 'fake_image', + 'command': 'fake_command'} + k8s_obj = kubernetes.ReplicationControllerObject(pod_name, **_kwargs) + self.assertEqual(1, len(k8s_obj._containers)) + container = k8s_obj._containers[0] + self.assertEqual(['arg1', 'arg2'], container._args) + self.assertEqual('fake_image', container._image) + self.assertEqual(['fake_command'], container._command) + self.assertEqual([], container._volume_mounts) + + def test__init_multipe_containers(self): + pod_name = 'pod_name' + containers = [] + for i in range(5): + containers.append({'args': ['arg1', 'arg2'], + 'image': 'fake_image_%s' % i, + 'command': 'fake_command_%s' % i}) + _kwargs = {'containers': containers} + k8s_obj = kubernetes.ReplicationControllerObject(pod_name, **_kwargs) + self.assertEqual(5, len(k8s_obj._containers)) + for i in range(5): + container = k8s_obj._containers[i] + self.assertEqual(['arg1', 'arg2'], container._args) + self.assertEqual('fake_image_%s' % i, container._image) + self.assertEqual(['fake_command_%s' % i], container._command) + self.assertEqual([], container._volume_mounts) + + def test__add_volumes(self): + volume1 = {'name': 'fake_sshkey', + 'configMap': {'name': 'fake_sshkey'}} + volume2 = {'name': 'volume2', + 'configMap': 'data'} + k8s_obj = kubernetes.ReplicationControllerObject( + 'name', ssh_key='fake_sshkey', volumes=[volume2]) + k8s_obj._add_volumes() + volumes = k8s_obj.template['spec']['template']['spec']['volumes'] + self.assertEqual(sorted([volume1, volume2], key=lambda k: k['name']), + sorted(volumes, key=lambda k: k['name'])) + + def test__add_volumes_no_volumes(self): + volume1 = {'name': 'fake_sshkey', + 'configMap': {'name': 'fake_sshkey'}} + k8s_obj = kubernetes.ReplicationControllerObject( + 'name', ssh_key='fake_sshkey') + k8s_obj._add_volumes() + volumes = k8s_obj.template['spec']['template']['spec']['volumes'] + self.assertEqual([volume1], volumes) + + def test__create_ssh_key_volume(self): + expected = {'name': 'fake_sshkey', + 'configMap': {'name': 'fake_sshkey'}} + k8s_obj = kubernetes.ReplicationControllerObject( + 'name', ssh_key='fake_sshkey') + self.assertEqual(expected, k8s_obj._create_ssh_key_volume()) + + def test__create_volume_item(self): + for vol_type in kubernetes_utils.get_volume_types(): + volume = {'name': 'vol_name', + vol_type: 'data'} + self.assertEqual( + volume, + kubernetes.ReplicationControllerObject. + _create_volume_item(volume)) + + def test__create_volume_item_invalid_type(self): + volume = {'name': 'vol_name', + 'invalid_type': 'data'} + with self.assertRaises(exceptions.KubernetesTemplateInvalidVolumeType): + kubernetes.ReplicationControllerObject._create_volume_item(volume) + + def test__add_security_context(self): + k8s_obj = kubernetes.ReplicationControllerObject('pod_name') + self.assertNotIn('securityContext', + k8s_obj.template['spec']['template']['spec']) + + k8s_obj._security_context = {'key_pod': 'value_pod'} + k8s_obj._add_security_context() + self.assertEqual( + {'key_pod': 'value_pod'}, + k8s_obj.template['spec']['template']['spec']['securityContext']) + + def test__add_security_context_by_init(self): + containers = [] + for i in range(5): + containers.append( + {'securityContext': {'key%s' % i: 'value%s' % i}}) + _kwargs = {'containers': containers, + 'securityContext': {'key_pod': 'value_pod'}} + k8s_obj = kubernetes.ReplicationControllerObject('pod_name', **_kwargs) + self.assertEqual( + {'key_pod': 'value_pod'}, + k8s_obj.template['spec']['template']['spec']['securityContext']) + for i in range(5): + container = ( + k8s_obj.template['spec']['template']['spec']['containers'][i]) + self.assertEqual({'key%s' % i: 'value%s' % i}, + container['securityContext']) + + def test__add_networks(self): + k8s_obj = kubernetes.ReplicationControllerObject( + 'name', networks=['network1', 'network2', 'network3']) + k8s_obj._add_networks() + networks = k8s_obj.\ + template['spec']['template']['metadata']['annotations']['networks'] + expected = ('[{"name": "network1"}, {"name": "network2"}, ' + '{"name": "network3"}]') + self.assertEqual(expected, networks) + + def test__add_tolerations(self): + _kwargs = {'tolerations': [{'key': 'key1', + 'value': 'value2', + 'effect': 'effect3', + 'operator': 'operator4', + 'wrong_key': 'error_key'}] + } + k8s_obj = kubernetes.ReplicationControllerObject('pod_name', **_kwargs) + k8s_obj._add_tolerations() + _tol = k8s_obj.template['spec']['template']['spec']['tolerations'] + self.assertEqual(1, len(_tol)) + self.assertEqual({'key': 'key1', + 'value': 'value2', + 'effect': 'effect3', + 'operator': 'operator4'}, + _tol[0]) + + def test__add_tolerations_default(self): + k8s_obj = kubernetes.ReplicationControllerObject('pod_name') + k8s_obj._add_tolerations() + _tol = k8s_obj.template['spec']['template']['spec']['tolerations'] + self.assertEqual(1, len(_tol)) + self.assertEqual({'operator': 'Exists'}, _tol[0]) + + +class ContainerObjectTestCase(base.BaseUnitTestCase): + + def test__create_volume_mounts(self): + volume_mount = {'name': 'fake_name', + 'mountPath': 'fake_path'} + ssh_vol = {'name': 'fake_ssh_key', + 'mountPath': kubernetes.ContainerObject.SSH_MOUNT_PATH, + 'readOnly': False} + expected = copy.deepcopy(volume_mount) + expected['readOnly'] = False + expected = [expected, ssh_vol] + container_obj = kubernetes.ContainerObject( + 'cname', 'fake_ssh_key', volumeMounts=[volume_mount]) + output = container_obj._create_volume_mounts() + self.assertEqual(expected, output) + + def test__create_volume_mounts_no_volume_mounts(self): + ssh_vol = {'name': 'fake_ssh_key2', + 'mountPath': kubernetes.ContainerObject.SSH_MOUNT_PATH, + 'readOnly': False} + container_obj = kubernetes.ContainerObject('name', 'fake_ssh_key2') + output = container_obj._create_volume_mounts() + self.assertEqual([ssh_vol], output) + + def test__create_volume_mounts_item(self): + volume_mount = {'name': 'fake_name', + 'mountPath': 'fake_path'} + expected = copy.deepcopy(volume_mount) + expected['readOnly'] = False + output = kubernetes.ContainerObject._create_volume_mounts_item( + volume_mount) + self.assertEqual(expected, output) + + def test_get_container_item(self): + volume_mount = {'name': 'fake_name', + 'mountPath': 'fake_path'} + args = ['arg1', 'arg2'] + container_obj = kubernetes.ContainerObject( + 'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount], + args=args) + expected = {'args': args, + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, + 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, + 'name': 'cname-container', + 'volumeMounts': container_obj._create_volume_mounts()} + self.assertEqual(expected, container_obj.get_container_item()) + + def test_get_container_item_with_security_context(self): + volume_mount = {'name': 'fake_name', + 'mountPath': 'fake_path'} + args = ['arg1', 'arg2'] + container_obj = kubernetes.ContainerObject( + 'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount], + args=args, securityContext={'key': 'value'}) + expected = {'args': args, + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, + 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, + 'name': 'cname-container', + 'volumeMounts': container_obj._create_volume_mounts(), + 'securityContext': {'key': 'value'}} + self.assertEqual(expected, container_obj.get_container_item()) + + def test_get_container_item_with_env(self): + volume_mount = {'name': 'fake_name', + 'mountPath': 'fake_path'} + args = ['arg1', 'arg2'] + container_obj = kubernetes.ContainerObject( + 'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount], + args=args, env=[{'name': 'fake_var_name', + 'value': 'fake_var_value'}]) + expected = {'args': args, + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, + 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, + 'name': 'cname-container', + 'volumeMounts': container_obj._create_volume_mounts(), + 'env': [{'name': 'fake_var_name', + 'value': 'fake_var_value'}]} + self.assertEqual(expected, container_obj.get_container_item()) + + def test_get_container_item_with_ports_multi_parameter(self): + volume_mount = {'name': 'fake_name', + 'mountPath': 'fake_path'} + args = ['arg1', 'arg2'] + container_obj = kubernetes.ContainerObject( + 'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount], + args=args, ports=[{'containerPort': 'fake_port_name', + 'hostPort': 'fake_host_port', + 'name': 'fake_name', + 'protocol': 'fake_protocol', + 'invalid_varible': 'fakeinvalid_varible', + 'hostIP': 'fake_port_number'}]) + expected = {'args': args, + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, + 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, + 'name': 'cname-container', + 'volumeMounts': container_obj._create_volume_mounts(), + 'ports': [{'containerPort': 'fake_port_name', + 'hostPort': 'fake_host_port', + 'name': 'fake_name', + 'protocol': 'fake_protocol', + 'hostIP': 'fake_port_number'}]} + self.assertEqual(expected, container_obj.get_container_item()) + + def test_get_container_item_with_ports_no_container_port(self): + with self.assertRaises(exceptions.KubernetesContainerPortNotDefined): + volume_mount = {'name': 'fake_name', + 'mountPath': 'fake_path'} + args = ['arg1', 'arg2'] + container_obj = kubernetes.ContainerObject( + 'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount], + args=args, ports=[{'hostPort': 'fake_host_port', + 'name': 'fake_name', + 'protocol': 'fake_protocol', + 'hostIP': 'fake_port_number'}]) + container_obj.get_container_item() + + def test_get_container_item_with_resources(self): + volume_mount = {'name': 'fake_name', + 'mountPath': 'fake_path'} + args = ['arg1', 'arg2'] + resources = {'requests': {'key1': 'val1'}, + 'limits': {'key2': 'val2'}, + 'other_key': {'key3': 'val3'}} + container_obj = kubernetes.ContainerObject( + 'cname', ssh_key='fake_sshkey', volumeMount=[volume_mount], + args=args, resources=resources) + expected = {'args': args, + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, + 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, + 'name': 'cname-container', + 'volumeMounts': container_obj._create_volume_mounts(), + 'resources': {'requests': {'key1': 'val1'}, + 'limits': {'key2': 'val2'}}} + self.assertEqual(expected, container_obj.get_container_item()) + + def test_get_container_item_image_pull_policy(self): + container_obj = kubernetes.ContainerObject( + 'cname', ssh_key='fake_sshkey', imagePullPolicy='Always') + expected = {'args': [], + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, + 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, + 'name': 'cname-container', + 'volumeMounts': container_obj._create_volume_mounts(), + 'imagePullPolicy':'Always'} + self.assertEqual(expected, container_obj.get_container_item()) + + def test_get_container_item_with_tty_stdin(self): + args = ['arg1', 'arg2'] + container_obj = kubernetes.ContainerObject( + 'cname', 'fake_sshkey', args=args, tty=False, stdin=True) + expected = {'args': args, + 'command': kubernetes.ContainerObject.COMMAND_DEFAULT, + 'image': kubernetes.ContainerObject.IMAGE_DEFAULT, + 'name': 'cname-container', + 'volumeMounts': container_obj._create_volume_mounts(), + 'tty': False, + 'stdin': True} + self.assertEqual(expected, container_obj.get_container_item()) + + def test__parse_commands_string(self): + container_obj = kubernetes.ContainerObject('cname', 'fake_sshkey') + self.assertEqual(['fake command'], + container_obj._parse_commands('fake command')) + + def test__parse_commands_list(self): + container_obj = kubernetes.ContainerObject('cname', 'fake_sshkey') + self.assertEqual(['cmd1', 'cmd2'], + container_obj._parse_commands(['cmd1', 'cmd2'])) + + def test__parse_commands_exception(self): + container_obj = kubernetes.ContainerObject('cname', 'fake_sshkey') + with self.assertRaises(exceptions.KubernetesContainerCommandType): + container_obj._parse_commands({}) + + +class CustomResourceDefinitionObjectTestCase(base.BaseUnitTestCase): + + def test__init(self): + template = { + 'metadata': { + 'name': 'newcrds.ctx_name.com' + }, + 'spec': { + 'group': 'ctx_name.com', + 'version': 'v2', + 'scope': 'scope', + 'names': {'plural': 'newcrds', + 'singular': 'newcrd', + 'kind': 'Newcrd'} + } + } + crd_obj = kubernetes.CustomResourceDefinitionObject( + 'ctx_name', name='newcrd', version='v2', scope='scope') + self.assertEqual('newcrds.ctx_name.com', crd_obj._name) + self.assertEqual(template, crd_obj._template) + + def test__init_missing_parameter(self): + with self.assertRaises(exceptions.KubernetesCRDObjectDefinitionError): + kubernetes.CustomResourceDefinitionObject('ctx_name', + noname='name') + + +class NetworkObjectTestCase(base.BaseUnitTestCase): + + def setUp(self): + self.net_obj = kubernetes.NetworkObject(name='fake_name', + plugin='fake_plugin', + args='fake_args') + + def test__init_missing_parameter(self): + with self.assertRaises( + exceptions.KubernetesNetworkObjectDefinitionError): + kubernetes.NetworkObject('network_name', plugin='plugin') + with self.assertRaises( + exceptions.KubernetesNetworkObjectDefinitionError): + kubernetes.NetworkObject('network_name', args='args') + + @mock.patch.object(kubernetes_utils, 'get_custom_resource_definition') + def test_crd(self, mock_get_crd): + mock_crd = mock.Mock() + mock_get_crd.return_value = mock_crd + net_obj = copy.deepcopy(self.net_obj) + self.assertEqual(mock_crd, net_obj.crd) + + def test_template(self): + net_obj = copy.deepcopy(self.net_obj) + expected = {'apiVersion': 'group.com/v2', + 'kind': kubernetes.NetworkObject.KIND, + 'metadata': { + 'name': 'fake_name'}, + 'plugin': 'fake_plugin', + 'args': 'fake_args'} + crd = mock.Mock() + crd.spec.group = 'group.com' + crd.spec.version = 'v2' + net_obj._crd = crd + self.assertEqual(expected, net_obj.template) + + def test_group(self): + net_obj = copy.deepcopy(self.net_obj) + net_obj._crd = mock.Mock() + net_obj._crd.spec.group = 'fake_group' + self.assertEqual('fake_group', net_obj.group) + + def test_version(self): + net_obj = copy.deepcopy(self.net_obj) + net_obj._crd = mock.Mock() + net_obj._crd.spec.version = 'version_4' + self.assertEqual('version_4', net_obj.version) + + def test_plural(self): + net_obj = copy.deepcopy(self.net_obj) + net_obj._crd = mock.Mock() + net_obj._crd.spec.names.plural = 'name_ending_in_s' + self.assertEqual('name_ending_in_s', net_obj.plural) + + def test_scope(self): + net_obj = copy.deepcopy(self.net_obj) + net_obj._crd = mock.Mock() + net_obj._crd.spec.scope = 'Cluster' + self.assertEqual('Cluster', net_obj.scope) + + @mock.patch.object(kubernetes_utils, 'create_network') + def test_create(self, mock_create_network): + net_obj = copy.deepcopy(self.net_obj) + net_obj._scope = 'scope' + net_obj._group = 'group' + net_obj._version = 'version' + net_obj._plural = 'plural' + net_obj._template = 'template' + net_obj._name = 'fake_name' + net_obj.create() + mock_create_network.assert_called_once_with( + 'scope', 'group', 'version', 'plural', 'template', 'fake_name') + + @mock.patch.object(kubernetes_utils, 'delete_network') + def test_delete(self, mock_delete_network): + net_obj = copy.deepcopy(self.net_obj) + net_obj._scope = 'scope' + net_obj._group = 'group' + net_obj._version = 'version' + net_obj._plural = 'plural' + net_obj._name = 'name' + net_obj.delete() + mock_delete_network.assert_called_once_with( + 'scope', 'group', 'version', 'plural', 'name', skip_codes=[404]) + + +class ServiceNodePortObjectTestCase(base.BaseUnitTestCase): + + def test__init(self): + with mock.patch.object(kubernetes.ServiceNodePortObject, '_add_port') \ + as mock_add_port: + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'port': 80, 'name': 'web'}]) + + mock_add_port.assert_has_calls([mock.call(22, 'ssh', protocol='TCP'), + mock.call(80, 'web')]) + + @mock.patch.object(kubernetes.ServiceNodePortObject, '_add_port') + def test__init_missing_mandatory_parameters(self, *args): + with self.assertRaises( + exceptions.KubernetesServiceObjectDefinitionError): + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'port': 80}]) + with self.assertRaises( + exceptions.KubernetesServiceObjectDefinitionError): + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'name': 'web'}]) + + @mock.patch.object(kubernetes.ServiceNodePortObject, '_add_port') + def test__init_missing_bad_name(self, *args): + with self.assertRaises( + exceptions.KubernetesServiceObjectNameError): + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'port': 80, 'name': '-web'}]) + with self.assertRaises( + exceptions.KubernetesServiceObjectNameError): + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'port': 80, 'name': 'Web'}]) + with self.assertRaises( + exceptions.KubernetesServiceObjectNameError): + kubernetes.ServiceNodePortObject( + 'fake_name', node_ports=[{'port': 80, 'name': 'web-'}]) + + def test__add_port(self): + nodeport_object = kubernetes.ServiceNodePortObject('fake_name') + port_ssh = {'name': 'ssh', + 'port': 22, + 'protocol': 'TCP'} + port_definition = {'port': 80, + 'protocol': 'TCP', + 'name': 'web', + 'targetPort': 10080, + 'nodePort': 30080} + port = copy.deepcopy(port_definition) + _port = port.pop('port') + name = port.pop('name') + nodeport_object._add_port(_port, name, **port) + self.assertEqual([port_ssh, port_definition], + nodeport_object.template['spec']['ports']) + + @mock.patch.object(kubernetes_utils, 'create_service') + def test_create(self, mock_create_service): + nodeport_object = kubernetes.ServiceNodePortObject('fake_name') + nodeport_object.template = 'fake_template' + nodeport_object.create() + mock_create_service.assert_called_once_with('fake_template') + + @mock.patch.object(kubernetes_utils, 'delete_service') + def test_delete(self, mock_delete_service): + nodeport_object = kubernetes.ServiceNodePortObject('fake_name') + nodeport_object.delete() + mock_delete_service.assert_called_once_with('fake_name-service', + skip_codes=[404]) + + +class KubernetesTemplate(base.BaseUnitTestCase): + + def test_get_rc_by_name(self): + ctx_cfg = { + 'servers': { + 'host1': {'args': 'some data'} + } + } + k_template = kubernetes.KubernetesTemplate('k8s_name', ctx_cfg) + rc = k_template.get_rc_by_name('host1-k8s_name') + self.assertTrue(isinstance(rc, kubernetes.ReplicationControllerObject)) + + def test_get_rc_by_name_wrong_name(self): + ctx_cfg = { + 'servers': { + 'host1': {'args': 'some data'} + } + } + k_template = kubernetes.KubernetesTemplate('k8s_name', ctx_cfg) + self.assertIsNone(k_template.get_rc_by_name('wrong_host_name')) + + def test_get_rc_by_name_no_rcs(self): + ctx_cfg = {'servers': {}} + k_template = kubernetes.KubernetesTemplate('k8s_name', ctx_cfg) + self.assertIsNone(k_template.get_rc_by_name('any_host_name')) diff --git a/yardstick/tests/unit/service/__init__.py b/yardstick/tests/unit/service/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/service/__init__.py diff --git a/yardstick/tests/unit/service/test_environment.py b/yardstick/tests/unit/service/test_environment.py new file mode 100644 index 000000000..779e6eaa0 --- /dev/null +++ b/yardstick/tests/unit/service/test_environment.py @@ -0,0 +1,46 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import mock + +from yardstick.common import exceptions +from yardstick.service import environment +from yardstick.tests.unit import base as ut_base + + +class EnvironmentTestCase(ut_base.BaseUnitTestCase): + + def test_get_sut_info(self): + pod_info = { + 'nodes': [ + { + 'name': 'node1', + 'host_name': 'host1', + 'role': 'Controller', + 'ip': '10.1.0.50', + 'user': 'root', + 'passward': 'root' + } + ] + } + + with mock.patch.object(environment.AnsibleCommon, + 'gen_inventory_ini_dict'), \ + mock.patch.object(environment.AnsibleCommon, 'get_sut_info', + return_value={'node1': {}}), \ + mock.patch.object(environment.Environment, '_format_sut_info'): + env = environment.Environment(pod=pod_info) + env.get_sut_info() + + def test_get_sut_info_pod_str(self): + pod_info = 'nodes' + + env = environment.Environment(pod=pod_info) + with self.assertRaises(exceptions.UnsupportedPodFormatError): + env.get_sut_info() diff --git a/yardstick/tests/unit/test_cmd/__init__.py b/yardstick/tests/unit/test_cmd/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/test_cmd/__init__.py diff --git a/yardstick/tests/unit/test_cmd/commands/__init__.py b/yardstick/tests/unit/test_cmd/commands/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/test_cmd/commands/__init__.py diff --git a/yardstick/tests/unit/test_cmd/commands/test_env.py b/yardstick/tests/unit/test_cmd/commands/test_env.py new file mode 100644 index 000000000..5d3520986 --- /dev/null +++ b/yardstick/tests/unit/test_cmd/commands/test_env.py @@ -0,0 +1,69 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import os +import sys + +import mock +import uuid + +from yardstick.cmd.commands import env +from yardstick.tests.unit import base + + +class EnvCommandTestCase(base.BaseUnitTestCase): + + @mock.patch.object(env.EnvCommand, '_start_async_task') + @mock.patch.object(env.EnvCommand, '_check_status') + def test_do_influxdb(self, check_status_mock, start_async_task_mock): + _env = env.EnvCommand() + _env.do_influxdb({}) + start_async_task_mock.assert_called_once() + check_status_mock.assert_called_once() + + @mock.patch.object(env.EnvCommand, '_start_async_task') + @mock.patch.object(env.EnvCommand, '_check_status') + def test_do_grafana(self, check_status_mock, start_async_task_mock): + _env = env.EnvCommand() + _env.do_grafana({}) + start_async_task_mock.assert_called_once() + check_status_mock.assert_called_once() + + @mock.patch.object(env.EnvCommand, '_start_async_task') + @mock.patch.object(env.EnvCommand, '_check_status') + def test_do_prepare(self, check_status_mock, start_async_task_mock): + _env = env.EnvCommand() + _env.do_prepare({}) + start_async_task_mock.assert_called_once() + check_status_mock.assert_called_once() + + @mock.patch.object(env.HttpClient, 'post') + def test_start_async_task(self, post_mock): + data = {'action': 'create_grafana'} + env.EnvCommand()._start_async_task(data) + post_mock.assert_called_once() + + @mock.patch.object(env.HttpClient, 'get') + @mock.patch.object(env.EnvCommand, '_print_status') + def test_check_status(self, mock_print, mock_get): + task_id = str(uuid.uuid4()) + mock_get.return_value = {'status': 2, 'result': 'error'} + self.assertEqual( + 2, env.EnvCommand()._check_status(task_id, 'hello world')) + self.assertEqual(2, mock_print.call_count) + + @mock.patch.object(sys, 'stdout') + @mock.patch.object(os, 'popen') + def test_print_status(self, mock_popen, mock_stdout): + mock_popen_obj = mock.Mock() + mock_popen_obj.read.return_value = '' + mock_popen.return_value = mock_popen_obj + env.EnvCommand()._print_status('hello', 'word') + mock_stdout.write.assert_not_called() + mock_stdout.flush.assert_not_called() diff --git a/yardstick/tests/unit/test_cmd/commands/test_testcase.py b/yardstick/tests/unit/test_cmd/commands/test_testcase.py new file mode 100644 index 000000000..922167614 --- /dev/null +++ b/yardstick/tests/unit/test_cmd/commands/test_testcase.py @@ -0,0 +1,21 @@ +############################################################################## +# Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest +from mock import patch + +from yardstick.cmd.commands.testcase import TestcaseCommands + + +class TestcaseCommandsUT(unittest.TestCase): + @patch('yardstick.cmd.commands.testcase.TestcaseCommands._format_print') + @patch('yardstick.cmd.commands.client') + def test_do_list(self, mock_client, mock_print): + mock_client.get.return_value = {'result': []} + TestcaseCommands().do_list({}) + mock_print.assert_called_once() diff --git a/yardstick/tests/unit/test_cmd/test_NSBperf.py b/yardstick/tests/unit/test_cmd/test_NSBperf.py new file mode 100644 index 000000000..5de892212 --- /dev/null +++ b/yardstick/tests/unit/test_cmd/test_NSBperf.py @@ -0,0 +1,132 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse +import os +import subprocess + +import mock +from six.moves import builtins +import unittest + +from yardstick.cmd import NSBperf + + +@mock.patch('six.moves.input', return_value='0') +class TestHandler(unittest.TestCase): + + def test_handler(self, *args): + subprocess.call = mock.Mock(return_value=0) + self.assertRaises(SystemExit, NSBperf.sigint_handler) + + +class TestYardstickNSCli(unittest.TestCase): + + def setUp(self): + self._mock_print = mock.patch.object(builtins, 'print') + self.mock_print = self._mock_print.start() + self.addCleanup(self._stop_mocks) + + def _stop_mocks(self): + self._mock_print.stop() + + def test___init__(self): + yardstick_ns_cli = NSBperf.YardstickNSCli() + self.assertIsNotNone(yardstick_ns_cli) + + def test_generate_final_report(self): + yardstick_ns_cli = NSBperf.YardstickNSCli() + test_case = "tc_baremetal_rfc2544_ipv4_1flow_1518B.yaml" + if os.path.isfile("/tmp/yardstick.out"): + os.remove('/tmp/yardstick.out') + self.assertIsNone(yardstick_ns_cli.generate_final_report(test_case)) + + def test_generate_kpi_results(self): + yardstick_ns_cli = NSBperf.YardstickNSCli() + tkey = "cpu" + tgen = {"cpu": {"ipc": 0}} + self.assertIsNone(yardstick_ns_cli.generate_kpi_results(tkey, tgen)) + + def test_generate_nfvi_results(self): + yardstick_ns_cli = NSBperf.YardstickNSCli() + nfvi = {"collect_stats": {"cpu": {"ipc": 0, "Hz": 2.6}}} + self.assertIsNone(yardstick_ns_cli.generate_nfvi_results(nfvi)) + + def test_handle_list_options(self): + yardstick_ns_cli = NSBperf.YardstickNSCli() + CLI_PATH = os.path.dirname(os.path.realpath(__file__)) + repo_dir = CLI_PATH + "/../../../" + test_path = os.path.join(repo_dir, "../samples/vnf_samples/nsut/") + args = {"list_vnfs": True, "list": False} + self.assertRaises(SystemExit, yardstick_ns_cli.handle_list_options, + args, test_path) + args = {"list_vnfs": False, "list": True} + self.assertRaises(SystemExit, + yardstick_ns_cli.handle_list_options, + args, test_path) + + def test_main(self): + yardstick_ns_cli = NSBperf.YardstickNSCli() + yardstick_ns_cli.parse_arguments = mock.Mock(return_value=0) + yardstick_ns_cli.handle_list_options = mock.Mock(return_value=0) + yardstick_ns_cli.terminate_if_less_options = mock.Mock(return_value=0) + yardstick_ns_cli.run_test = mock.Mock(return_value=0) + self.assertIsNone(yardstick_ns_cli.main()) + + @mock.patch.object(argparse.ArgumentParser, 'parse_args') + def test_parse_arguments(self, mock_parse): + class DummyArgs(object): + var1 = 'value1' + + mock_parse.return_value = DummyArgs + yardstick_ns_cli = NSBperf.YardstickNSCli() + self.assertIn('var1', yardstick_ns_cli.parse_arguments()) + + def test_run_test(self): + cur_dir = os.getcwd() + CLI_PATH = os.path.dirname(os.path.realpath(__file__)) + YARDSTICK_REPOS_DIR = os.path.join(CLI_PATH + "/../../") + test_path = os.path.join(YARDSTICK_REPOS_DIR, + "../samples/vnf_samples/nsut/") + yardstick_ns_cli = NSBperf.YardstickNSCli() + subprocess.check_output = mock.Mock(return_value=0) + args = {"vnf": "vpe", + "test": "tc_baremetal_rfc2544_ipv4_1flow_1518B.yaml"} + self.assertIsNone(yardstick_ns_cli.run_test(args, test_path)) + os.chdir(cur_dir) + args = {"vnf": "vpe1"} + self.assertIsNone(yardstick_ns_cli.run_test(args, test_path)) + os.chdir(cur_dir) + args = {"vnf": "vpe", + "test": "tc_baremetal_rfc2544_ipv4_1flow_1518B.yaml."} + self.assertIsNone(yardstick_ns_cli.run_test(args, test_path)) + os.chdir(cur_dir) + args = [] + self.assertIsNone(yardstick_ns_cli.run_test(args, test_path)) + os.chdir(cur_dir) + + def test_terminate_if_less_options(self): + yardstick_ns_cli = NSBperf.YardstickNSCli() + args = {"vnf": False} + self.assertRaises(SystemExit, + yardstick_ns_cli.terminate_if_less_options, args) + + def test_validate_input(self): + yardstick_ns_cli = NSBperf.YardstickNSCli() + self.assertEqual(1, yardstick_ns_cli.validate_input("", 4)) + NSBperf.input = lambda _: 'yes' + self.assertEqual(1, yardstick_ns_cli.validate_input(5, 4)) + subprocess.call = mock.Mock(return_value=0) + self.assertEqual(0, yardstick_ns_cli.validate_input(2, 4)) + subprocess.call = mock.Mock(return_value=0) diff --git a/yardstick/tests/unit/test_ssh.py b/yardstick/tests/unit/test_ssh.py index dbaae8c37..374fb6644 100644 --- a/yardstick/tests/unit/test_ssh.py +++ b/yardstick/tests/unit/test_ssh.py @@ -13,20 +13,17 @@ # License for the specific language governing permissions and limitations # under the License. -# yardstick comment: this file is a modified copy of -# rally/tests/unit/common/test_sshutils.py - -from __future__ import absolute_import import os import socket import unittest from io import StringIO +from itertools import count import mock from oslo_utils import encodeutils +from yardstick.common import exceptions from yardstick import ssh -from yardstick.ssh import SSHError from yardstick.ssh import SSH from yardstick.ssh import AutoConnectSSH @@ -126,7 +123,7 @@ class SSHTestCase(unittest.TestCase): dss = mock_paramiko.dsskey.DSSKey rsa.from_private_key.side_effect = mock_paramiko.SSHException dss.from_private_key.side_effect = mock_paramiko.SSHException - self.assertRaises(ssh.SSHError, self.test_client._get_pkey, "key") + self.assertRaises(exceptions.SSHError, self.test_client._get_pkey, "key") @mock.patch("yardstick.ssh.six.moves.StringIO") @mock.patch("yardstick.ssh.paramiko") @@ -193,13 +190,13 @@ class SSHTestCase(unittest.TestCase): test_ssh = ssh.SSH("admin", "example.net", pkey="key") - with self.assertRaises(SSHError) as raised: + with self.assertRaises(exceptions.SSHError) as raised: test_ssh._get_client() - self.assertEqual(mock_paramiko.SSHClient.call_count, 1) - self.assertEqual(mock_paramiko.AutoAddPolicy.call_count, 1) - self.assertEqual(fake_client.set_missing_host_key_policy.call_count, 1) - self.assertEqual(fake_client.connect.call_count, 1) + mock_paramiko.SSHClient.assert_called_once() + mock_paramiko.AutoAddPolicy.assert_called_once() + fake_client.set_missing_host_key_policy.assert_called_once() + fake_client.connect.assert_called_once() exc_str = str(raised.exception) self.assertIn('raised during connect', exc_str) self.assertIn('MyError', exc_str) @@ -241,21 +238,40 @@ class SSHTestCase(unittest.TestCase): self.assertEqual("stdout fake data", stdout) self.assertEqual("stderr fake data", stderr) + @mock.patch("yardstick.ssh.six.moves.StringIO") + def test_execute_raise_on_error_passed(self, mock_string_io): + mock_string_io.side_effect = stdio = [mock.Mock(), mock.Mock()] + stdio[0].read.return_value = "stdout fake data" + stdio[1].read.return_value = "stderr fake data" + with mock.patch.object(self.test_client, "run", return_value=0) \ + as mock_run: + status, stdout, stderr = self.test_client.execute( + "cmd", + stdin="fake_stdin", + timeout=43, + raise_on_error=True) + mock_run.assert_called_once_with( + "cmd", stdin="fake_stdin", stdout=stdio[0], + stderr=stdio[1], timeout=43, raise_on_error=True) + self.assertEqual(0, status) + self.assertEqual("stdout fake data", stdout) + self.assertEqual("stderr fake data", stderr) + @mock.patch("yardstick.ssh.time") def test_wait_timeout(self, mock_time): mock_time.time.side_effect = [1, 50, 150] - self.test_client.execute = mock.Mock(side_effect=[ssh.SSHError, - ssh.SSHError, + self.test_client.execute = mock.Mock(side_effect=[exceptions.SSHError, + exceptions.SSHError, 0]) - self.assertRaises(ssh.SSHTimeout, self.test_client.wait) + self.assertRaises(exceptions.SSHTimeout, self.test_client.wait) self.assertEqual([mock.call("uname")] * 2, self.test_client.execute.mock_calls) @mock.patch("yardstick.ssh.time") def test_wait(self, mock_time): mock_time.time.side_effect = [1, 50, 100] - self.test_client.execute = mock.Mock(side_effect=[ssh.SSHError, - ssh.SSHError, + self.test_client.execute = mock.Mock(side_effect=[exceptions.SSHError, + exceptions.SSHError, 0]) self.test_client.wait() self.assertEqual([mock.call("uname")] * 3, @@ -270,6 +286,48 @@ class SSHTestCase(unittest.TestCase): mock_paramiko_exec_command.assert_called_once_with('cmd', get_pty=True) + @mock.patch("yardstick.ssh.paramiko") + def test_interactive_terminal_open(self, mock_paramiko): + fake_client = mock.Mock() + fake_session = mock.Mock() + fake_session.recv.return_value = ":~# " + fake_transport = mock.Mock() + fake_transport.open_session.return_value = fake_session + fake_client.get_transport.return_value = fake_transport + mock_paramiko.SSHClient.return_value = fake_client + + test_ssh = ssh.SSH("admin", "example.net", pkey="key") + result = test_ssh.interactive_terminal_open() + self.assertEqual(fake_session, result) + + @mock.patch("yardstick.ssh.paramiko") + def test_interactive_terminal_exec_command(self, mock_paramiko): + fake_client = mock.Mock() + fake_session = mock.Mock() + fake_session.recv.return_value = "stdout fake data" + fake_transport = mock.Mock() + fake_transport.open_session.return_value = fake_session + fake_client.get_transport.return_value = fake_transport + mock_paramiko.SSHClient.return_value = fake_client + + test_ssh = ssh.SSH("admin", "example.net", pkey="key") + with mock.patch.object(fake_session, "sendall") \ + as mock_paramiko_send_command: + result = test_ssh.interactive_terminal_exec_command(fake_session, + 'cmd', "vat# ") + self.assertEqual("stdout fake data", result) + mock_paramiko_send_command.assert_called_once_with('cmd\n') + + @mock.patch("yardstick.ssh.paramiko") + def test_interactive_terminal_close(self, _): + fake_session = mock.Mock() + paramiko_sshclient = self.test_client._get_client() + paramiko_sshclient.get_transport.open_session.return_value = fake_session + with mock.patch.object(fake_session, "close") \ + as mock_paramiko_terminal_close: + self.test_client.interactive_terminal_close(fake_session) + mock_paramiko_terminal_close.assert_called_once_with() + class SSHRunTestCase(unittest.TestCase): """Test SSH.run method in different aspects. @@ -332,7 +390,7 @@ class SSHRunTestCase(unittest.TestCase): def test_run_nonzero_status(self, mock_select): mock_select.select.return_value = ([], [], []) self.fake_session.recv_exit_status.return_value = 1 - self.assertRaises(ssh.SSHError, self.test_client.run, "cmd") + self.assertRaises(exceptions.SSHError, self.test_client.run, "cmd") self.assertEqual(1, self.test_client.run("cmd", raise_on_error=False)) @mock.patch("yardstick.ssh.select") @@ -400,7 +458,7 @@ class SSHRunTestCase(unittest.TestCase): def test_run_select_error(self, mock_select): self.fake_session.exit_status_ready.return_value = False mock_select.select.return_value = ([], [], [True]) - self.assertRaises(ssh.SSHError, self.test_client.run, "cmd") + self.assertRaises(exceptions.SSHError, self.test_client.run, "cmd") @mock.patch("yardstick.ssh.time") @mock.patch("yardstick.ssh.select") @@ -408,7 +466,7 @@ class SSHRunTestCase(unittest.TestCase): mock_time.time.side_effect = [1, 3700] mock_select.select.return_value = ([], [], []) self.fake_session.exit_status_ready.return_value = False - self.assertRaises(ssh.SSHTimeout, self.test_client.run, "cmd") + self.assertRaises(exceptions.SSHTimeout, self.test_client.run, "cmd") @mock.patch("yardstick.ssh.open", create=True) def test__put_file_shell(self, mock_open): @@ -508,13 +566,45 @@ class SSHRunTestCase(unittest.TestCase): class TestAutoConnectSSH(unittest.TestCase): - def test__connect_with_wait(self): - auto_connect_ssh = AutoConnectSSH('user1', 'host1', wait=True) - auto_connect_ssh._get_client = mock.Mock() - auto_connect_ssh.wait = mock_wait = mock.Mock() + def test__connect_loop(self): + auto_connect_ssh = AutoConnectSSH('user1', 'host1', wait=0) + auto_connect_ssh._get_client = mock__get_client = mock.Mock() auto_connect_ssh._connect() - self.assertEqual(mock_wait.call_count, 1) + mock__get_client.assert_called_once() + + def test___init___negative(self): + with self.assertRaises(TypeError): + AutoConnectSSH('user1', 'host1', wait=['wait']) + + with self.assertRaises(ValueError): + AutoConnectSSH('user1', 'host1', wait='wait') + + @mock.patch('yardstick.ssh.time') + def test__connect_loop_ssh_error(self, mock_time): + mock_time.time.side_effect = count() + + auto_connect_ssh = AutoConnectSSH('user1', 'host1', wait=10) + auto_connect_ssh._get_client = mock__get_client = mock.Mock() + mock__get_client.side_effect = exceptions.SSHError + + with self.assertRaises(exceptions.SSHTimeout): + auto_connect_ssh._connect() + + self.assertEqual(mock_time.time.call_count, 12) + + def test_get_file_obj(self): + auto_connect_ssh = AutoConnectSSH('user1', 'host1', wait=10) + auto_connect_ssh._get_client = mock__get_client = mock.Mock() + mock_client = mock__get_client() + mock_open_sftp = mock_client.open_sftp() + mock_sftp = mock.Mock() + mock_open_sftp.__enter__ = mock.Mock(return_value=mock_sftp) + mock_open_sftp.__exit__ = mock.Mock() + + auto_connect_ssh.get_file_obj('remote/path', mock.Mock()) + + mock_sftp.getfo.assert_called_once() def test__make_dict(self): auto_connect_ssh = AutoConnectSSH('user1', 'host1') @@ -527,7 +617,7 @@ class TestAutoConnectSSH(unittest.TestCase): 'key_filename': None, 'password': None, 'name': None, - 'wait': True, + 'wait': AutoConnectSSH.DEFAULT_WAIT_TIMEOUT, } result = auto_connect_ssh._make_dict() self.assertDictEqual(result, expected) @@ -537,6 +627,13 @@ class TestAutoConnectSSH(unittest.TestCase): self.assertEqual(auto_connect_ssh.get_class(), AutoConnectSSH) + def test_drop_connection(self): + auto_connect_ssh = AutoConnectSSH('user1', 'host1') + self.assertFalse(auto_connect_ssh._client) + auto_connect_ssh._client = True + auto_connect_ssh.drop_connection() + self.assertFalse(auto_connect_ssh._client) + @mock.patch('yardstick.ssh.SCPClient') def test_put(self, mock_scp_client_type): auto_connect_ssh = AutoConnectSSH('user1', 'host1') @@ -544,7 +641,7 @@ class TestAutoConnectSSH(unittest.TestCase): auto_connect_ssh.put('a', 'z') with mock_scp_client_type() as mock_scp_client: - self.assertEqual(mock_scp_client.put.call_count, 1) + mock_scp_client.put.assert_called_once() @mock.patch('yardstick.ssh.SCPClient') def test_get(self, mock_scp_client_type): @@ -553,7 +650,7 @@ class TestAutoConnectSSH(unittest.TestCase): auto_connect_ssh.get('a', 'z') with mock_scp_client_type() as mock_scp_client: - self.assertEqual(mock_scp_client.get.call_count, 1) + mock_scp_client.get.assert_called_once() def test_put_file(self): auto_connect_ssh = AutoConnectSSH('user1', 'host1') @@ -561,12 +658,27 @@ class TestAutoConnectSSH(unittest.TestCase): auto_connect_ssh._put_file_sftp = mock_put_sftp = mock.Mock() auto_connect_ssh.put_file('a', 'b') - self.assertEqual(mock_put_sftp.call_count, 1) - + mock_put_sftp.assert_called_once() -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + def test_execute(self): + auto_connect_ssh = AutoConnectSSH('user1', 'host1') + auto_connect_ssh._client = mock.Mock() + auto_connect_ssh.run = mock.Mock(return_value=0) + exit_code, _, _ = auto_connect_ssh.execute('') + self.assertEqual(exit_code, 0) + + def _mock_run(self, *args, **kwargs): + if args[0] == 'ls': + if kwargs.get('raise_on_error'): + raise exceptions.SSHError(error_msg='Command error') + return 1 + return 0 + + def test_execute_command_error(self): + auto_connect_ssh = AutoConnectSSH('user1', 'host1') + auto_connect_ssh._client = mock.Mock() + auto_connect_ssh.run = mock.Mock(side_effect=self._mock_run) + self.assertRaises(exceptions.SSHError, auto_connect_ssh.execute, 'ls', + raise_on_error=True) + exit_code, _, _ = auto_connect_ssh.execute('ls') + self.assertNotEqual(exit_code, 0) |