diff options
-rw-r--r-- | INFO | 1 | ||||
-rw-r--r-- | INFO.yaml | 4 | ||||
-rwxr-xr-x | docs/testing/developer/devguide/devguide.rst | 140 | ||||
-rw-r--r-- | docs/testing/user/userguide/04-installation.rst | 109 | ||||
-rw-r--r-- | docs/testing/user/userguide/13-nsb-installation.rst | 4 | ||||
-rw-r--r-- | docs/testing/user/userguide/glossary.rst | 75 | ||||
-rw-r--r-- | samples/vnf_samples/nsut/vepc/tc_epc_default_bearer_create_landslide.yaml | 1 | ||||
-rw-r--r-- | yardstick/network_services/vnf_generic/vnf/tg_landslide.py | 33 | ||||
-rw-r--r-- | yardstick/tests/unit/network_services/vnf_generic/vnf/test_tg_landslide.py | 59 |
9 files changed, 400 insertions, 26 deletions
@@ -22,6 +22,7 @@ chenjiankun1@huawei.com rodolfo.alonso.hernandez@intel.com emma.l.foley@intel.com abhijit.sinha@intel.com +volodymyrx.mytnyk@intel.com Link to TSC approval: http://meetbot.opnfv.org/meetings/ Link to approval of additional submitters: @@ -74,6 +74,10 @@ committers: email: 'ross.b.brattain@intel.com' id: 'rbbratta' company: 'intel.com' + - name: 'Volodymyr Mytnyk' + email: 'volodymyrx.mytnyk@intel.com' + id: 'vmytnykx' + company: 'intel.com' tsc: # yamllint disable rule:line-length approval: 'http//meetbot.opnfv.org/meetings/' diff --git a/docs/testing/developer/devguide/devguide.rst b/docs/testing/developer/devguide/devguide.rst index 91f2c2148..4fe01c12b 100755 --- a/docs/testing/developer/devguide/devguide.rst +++ b/docs/testing/developer/devguide/devguide.rst @@ -449,6 +449,10 @@ Verify your patch:: It is used in CI but also by the CLI. +For more details on ``tox`` and tests, please refer to the `Running tests`_ +and `working with tox`_ sections below, which describe the different available +environments. + Submit the code with Git ++++++++++++++++++++++++ @@ -566,6 +570,142 @@ The process for backporting is as follows: A backported change needs a ``+1`` and a ``+2`` from a committer who didn’t propose the change (i.e. minimum 3 people involved). +Development guidelines +---------------------- +This section provides guidelines and best practices for feature development +and bug fixing in Yardstick. + +In general, bug fixes should be submitted as a single patch. + +When developing larger features, all commits on the local topic branch can be +submitted together, by running ``git review`` on the tip of the branch. This +creates a chain of related patches in gerrit. + +Each commit should contain one logical change and the author should aim for no +more than 300 lines of code per commit. This helps to make the changes easier +to review. + +Each feature should have the following: + +* Feature/bug fix code +* Unit tests (both positive and negative) +* Functional tests (optional) +* Sample testcases (if applicable) +* Documentation +* Update to release notes + +Coding style +~~~~~~~~~~~~ +.. _`OpenStack Style Guidelines`: https://docs.openstack.org/hacking/latest/user/hacking.html +.. _`OPNFV coding guidelines`: https://wiki.opnfv.org/display/DEV/Contribution+Guidelines + +Please follow the `OpenStack Style Guidelines`_ for code contributions (the +section on Internationalization (i18n) Strings is not applicable). + +When writing commit message, the `OPNFV coding guidelines`_ on git commit +message style should also be used. + +Running tests +~~~~~~~~~~~~~ +Once your patch has been submitted, a number of tests will be run by Jenkins +CI to verify the patch. Before submitting your patch, you should run these +tests locally. You can do this using ``tox``, which has a number of different +test environments defined in ``tox.ini``. +Calling ``tox`` without any additional arguments runs the default set of +tests (unit tests, functional tests, coverage and pylint). + +If some tests are failing, you can save time and select test environments +individually, by passing one or more of the following command-line options to +``tox``: + +* ``-e py27``: Unit tests using Python 2.7 +* ``-e py3``: Unit tests using Python 3 +* ``-e pep8``: Linter and style checks on updated files +* ``-e functional``: Functional tests using Python 2.7 +* ``-e functional-py3``: Functional tests using Python 3 +* ``-e coverage``: Code coverage checks + +.. note:: You need to stage your changes prior to running coverage for those + changes to be checked. + +In addition to the tests run by Jenkins (listed above), there are a number of +other test environments defined. + +* ``-e pep8-full``: Linter and style checks are run on the whole repo (not + just on updated files) +* ``-e os-requirements``: Check that the requirements are compatible with + OpenStack requirements. + +Working with tox +++++++++++++++++ +.. _virtualenv: https://virtualenv.pypa.io/en/stable/ + +``tox`` uses `virtualenv`_ to create isolated Python environments to run the +tests in. The test environments are located at +``.tox/<environment_name>`` e.g. ``.tox/py27``. + +If requirements are changed, you will need to recreate the tox test +environment to make sure the new requirements are installed. This is done by +passing the additional ``-r`` command-line option to ``tox``:: + + tox -r -e ... + +This can also be achieved by deleting the test environments manually before +running ``tox``:: + + rm -rf .tox/<environment_name> + rm -rf .tox/py27 + +Writing unit tests +~~~~~~~~~~~~~~~~~~ +For each change submitted, a set of unit tests should be submitted, which +should include both positive and negative testing. + +In order to help identify which tests are needed, follow the guidelines below. + +* In general, there should be a separate test for each branching point, return + value and input set. +* Negative tests should be written to make sure exceptions are raised and/or + handled appropriately. + +The following convention should be used for naming tests:: + + test_<method_name>_<some_comment> + +The comment gives more information on the nature of the test, the side effect +being checked, or the parameter being modified:: + + test_my_method_runtime_error + test_my_method_invalid_credentials + test_my_method_param1_none + +Mocking ++++++++ +The ``mock`` library is used for unit testing to stub out external libraries. + +The following conventions are used in Yardstick: + +* Use ``mock.patch.object`` instead of ``mock.patch``. + +* When naming mocked classes/functions, use ``mock_<class_and_function_name>`` + e.g. ``mock_subprocess_call`` + +* Avoid decorating classes with mocks. Apply the mocking in ``setUp()``:: + + @mock.patch.object(ssh, 'SSH') + class MyClassTestCase(unittest.TestCase): + + should be:: + + class MyClassTestCase(unittest.TestCase): + def setUp(self): + 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() Plugins ------- diff --git a/docs/testing/user/userguide/04-installation.rst b/docs/testing/user/userguide/04-installation.rst index a4846230e..d97078909 100644 --- a/docs/testing/user/userguide/04-installation.rst +++ b/docs/testing/user/userguide/04-installation.rst @@ -444,6 +444,115 @@ These configuration files can be found in the ``samples`` directory. Default location for the output is ``/tmp/yardstick.out``. +Automatic installation of Yardstick using ansible +------------------------------------------------- + +Automatic installation can be used as an alternative to the manual. +Yardstick can be installed on the bare metal and to the container. Yardstick +container can be either pulled or built. + +Bare metal installation +^^^^^^^^^^^^^^^^^^^^^^^ + +Use ansible script ``install.yaml`` to install Yardstick on Ubuntu server: + +.. code-block:: console + + ansible-playbook -i install-inventory.ini install.yaml \ + -e YARDSTICK_DIR=<path to Yardstick folder> + +.. note:: By default ``INSTALLATION_MODE`` is ``baremetal``. + +.. note:: By default Ubuntu 16.04 is chosen (xenial). It can be changed to + Ubuntu 18.04 (bionic) by passing ``-e OS_RELEASE=bionic`` parameter. + +.. note:: To install Yardstick in virtual environment pass parameter + ``-e VIRTUAL_ENVIRONMENT=True``. + +To build Yardstick NSB image pass ``IMG_PROPERTY=nsb`` as input parameter: + +.. code-block:: console + + ansible-playbook -i install-inventory.ini install.yaml \ + -e IMAGE_PROPERTY=nsb \ + -e YARDSTICK_DIR=<path to Yardstick folder> + +.. note:: In this ``INSTALLATION_MODE`` mode either Yardstick image or SampleVNF + images will be built. Image type is defined by parameter ``IMAGE_PROPERTY``. + By default Yardstick image will be built. + +Container installation +^^^^^^^^^^^^^^^^^^^^^^ + +Use ansible script ``install.yaml`` to pull or build Yardstick +container. To pull Yardstick image and start container run: + +.. code-block:: console + + ansible-playbook -i install-inventory.ini install.yaml \ + -e YARDSTICK_DIR=<path to Yardstick folder> \ + -e INSTALLATION_MODE=container_pull + +.. note:: In this ``INSTALLATION_MODE`` mode either Yardstick image or SampleVNF + images will be built. Image type is defined by variable ``IMG_PROPERTY`` in + file ``ansible/group_vars/all.yml``. By default Yardstick image will be + built. + +.. note:: Open question: How to know if Docker image is built on Ubuntu 16.04 and 18.04? + Do we need separate tag to be used? + +To build Yardstick image run: + +.. code-block:: console + + ansible-playbook -i install-inventory.ini install.yaml \ + -e YARDSTICK_DIR=<path to Yardstick folder> \ + -e INSTALLATION_MODE=container + +.. note:: In this ``INSTALLATION_MODE`` mode neither Yardstick image nor SampleVNF + image will be built. + +.. note:: By default Ubuntu 16.04 is chosen (xenial). It can be changed to + Ubuntu 18.04 (bionic) by passing ``-e OS_RELEASE=bionic`` parameter. + +Parameters for ``install.yaml`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Description of the parameters used with ``install.yaml`` script + + +-------------------------+-------------------------------------------------+ + | Parameters | Detail | + +=========================+=================================================+ + | -i install-inventory.ini| Installs package dependency to remote servers | + | | Mandatory parameter | + | | By default no remote servers are provided | + | | Needed packages will be installed on localhost | + +-------------------------+-------------------------------------------------+ + | -e YARDSTICK_DIR | Path to Yardstick folder | + | | Mandatory parameter | + +-------------------------+-------------------------------------------------+ + | -e INSTALLATION_MODE | baremetal: Yardstick is installed to the bare | + | | metal | + | | Default parameter | + | +-------------------------------------------------+ + | | container: Yardstick is installed in container | + | | Container is built from Dockerfile | + | +-------------------------------------------------+ + | | container_pull: Yardstick is installed in | + | | container | + | | Container is pulled from docker hub | + +-------------------------+-------------------------------------------------+ + | -e OS_RELEASE | xenial or bionic: Ubuntu version to be used | + | | Default is Ubuntu 16.04 (xenial) | + +-------------------------+-------------------------------------------------+ + | -e IMAGE_PROPERTY | normal or nsb: Type of the VM image to be built | + | | Default image is Yardstick | + +-------------------------+-------------------------------------------------+ + | -e VIRTUAL_ENVIRONMENT | False or True: Whether install in virtualenv | + | | Default is False | + +-------------------------+-------------------------------------------------+ + + Deploy InfluxDB and Grafana using Docker ---------------------------------------- diff --git a/docs/testing/user/userguide/13-nsb-installation.rst b/docs/testing/user/userguide/13-nsb-installation.rst index fb68fbf21..0b76cdd30 100644 --- a/docs/testing/user/userguide/13-nsb-installation.rst +++ b/docs/testing/user/userguide/13-nsb-installation.rst @@ -168,6 +168,10 @@ It will also automatically download all the packages needed for NSB Testing setup. Refer chapter :doc:`04-installation` for more on docker **Install Yardstick using Docker (recommended)** +Another way to execute an installation for a Bare-Metal or a Standalone context +is to use ansible script ``install.yaml``. Refer chapter :doc:`04-installation` +for more details. + System Topology: ================ diff --git a/docs/testing/user/userguide/glossary.rst b/docs/testing/user/userguide/glossary.rst index be98aa6c0..6a153943c 100644 --- a/docs/testing/user/userguide/glossary.rst +++ b/docs/testing/user/userguide/glossary.rst @@ -13,6 +13,11 @@ Glossary API Application Programming Interface + Docker + Docker provisions and manages containers. Yardstick and many other OPNFV + projects are deployed in containers. Docker is required to launch the + containerized versions of these projects. + DPI Deep Packet Inspection @@ -27,36 +32,80 @@ Glossary IOPS Input/Output Operations Per Second + A performance measurement used to benchmark storage devices. + + KPI + Key Performance Indicator + + Kubernetes + k8s + Kubernetes is an open-source container-orchestration system for automating + deployment, scaling and management of containerized applications. + It is one of the contexts supported in Yardstick. + + NFV + Network Function Virtualization + NFV is an initiative to take network services which were traditionally run + on proprietary, dedicated hardware, and virtualize them to run on general + purpose hardware. + + NFVI + Network Function Virtualization Infrastructure + The servers, routers, switches, etc on which the NFV system runs. NIC Network Interface Controller + OpenStack + OpenStack is a cloud operating system that controls pools of compute, + storage, and networking resources. OpenStack is an open source project + licensed under the Apache License 2.0. + PBFS Packet Based per Flow State + PROX + Packet pROcessing eXecution engine + QoS Quality of Service + The ability to guarantee certain network or storage requirements to + satisfy a Service Level Agreement (SLA) between an application provider + and end users. + Typically includes performance requirements like networking bandwidth, + latency, jitter correction, and reliability as well as storage + performance in Input/Output Operations Per Second (IOPS), throttling + agreements, and performance expectations at peak load + + SLA + Service Level Agreement + An SLA is an agreement between a service provider and a customer to + provide a certain level of service/performance. + + SR-IOV + Single Root IO Virtualization + A specification that, when implemented by a physical PCIe + device, enables it to appear as multiple separate PCIe devices. This + enables multiple virtualized guests to share direct access to the + physical device. + + SUT + System Under Test + + ToS + Type of Service VLAN - Virtual LAN + Virtual LAN (Local Area Network) VM Virtual Machine + An operating system instance that runs on top of a hypervisor. + Multiple VMs can run at the same time on the same physical + host. VNF Virtual Network Function VNFC Virtual Network Function Component - - NFVI - Network Function Virtualization Infrastructure - - SR-IOV - Single Root IO Virtualization - - SUT - System Under Test - - ToS - Type of Service diff --git a/samples/vnf_samples/nsut/vepc/tc_epc_default_bearer_create_landslide.yaml b/samples/vnf_samples/nsut/vepc/tc_epc_default_bearer_create_landslide.yaml index 0b94d313f..ee17df138 100644 --- a/samples/vnf_samples/nsut/vepc/tc_epc_default_bearer_create_landslide.yaml +++ b/samples/vnf_samples/nsut/vepc/tc_epc_default_bearer_create_landslide.yaml @@ -27,6 +27,7 @@ scenarios: tg__0: tg__0.traffic_gen vnf__0: vnf__0.vnf_epc options: + traffic_duration: 70 dmf: transactionRate: 5 packetSize: 512 diff --git a/yardstick/network_services/vnf_generic/vnf/tg_landslide.py b/yardstick/network_services/vnf_generic/vnf/tg_landslide.py index a146b72ca..2fba89b22 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_landslide.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_landslide.py @@ -129,6 +129,17 @@ class LandslideTrafficGen(sample_vnf.SampleVNFTrafficGen): self.session_profile['reservePorts'] = 'true' self.session_profile['reservations'] = [reservation] + def _update_session_library_name(self, test_session): + """Update DMF library name in session profile""" + for _ts_group in test_session['tsGroups']: + for _tc in _ts_group['testCases']: + try: + for _mainflow in _tc['parameters']['Dmf']['mainflows']: + _mainflow['library'] = \ + self.vnfd_helper.mgmt_interface['user'] + except KeyError: + pass + @staticmethod def _update_session_tc_params(tc_options, testcase): for _param_key in tc_options: @@ -206,6 +217,8 @@ class LandslideTrafficGen(sample_vnf.SampleVNFTrafficGen): _testcase_idx].update( self._update_session_tc_params(tc_options, _testcase)) + self._update_session_library_name(self.session_profile) + class LandslideResourceHelper(sample_vnf.ClientResourceHelper): """Landslide TG helper class""" @@ -459,11 +472,14 @@ class LandslideResourceHelper(sample_vnf.ClientResourceHelper): self._terminated.value = 1 def create_dmf(self, dmf): - if isinstance(dmf, list): - for _dmf in dmf: - self._tcl.create_dmf(_dmf) - else: - self._tcl.create_dmf(dmf) + if isinstance(dmf, dict): + dmf = [dmf] + for _dmf in dmf: + # Update DMF library name in traffic profile + _dmf['dmf'].update( + {'library': self.vnfd_helper.mgmt_interface['user']}) + # Create DMF on Landslide server + self._tcl.create_dmf(_dmf) def delete_dmf(self, dmf): if isinstance(dmf, list): @@ -600,6 +616,13 @@ class LandslideResourceHelper(sample_vnf.ClientResourceHelper): def create_test_session(self, test_session): # Use tcl client to create session test_session['library'] = self._user_id + + # If no traffic duration set in test case, use predefined default value + # in session profile + test_session['duration'] = self.scenario_helper.all_options.get( + 'traffic_duration', + test_session['duration']) + LOG.debug("Creating session='%s'", test_session['name']) self._tcl.create_test_session(test_session) 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 index 53439972a..1736d0f17 100644 --- 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 @@ -27,7 +27,7 @@ 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" @@ -337,6 +337,7 @@ class TestLandslideTrafficGen(unittest.TestCase): 'traffic_profile': '../../traffic_profiles/landslide/' 'landslide_dmf_udp.yaml', 'options': { + 'traffic_duration': 71, 'test_cases': [ { 'BearerAddrPool': '2002::2', @@ -465,8 +466,10 @@ class TestLandslideTrafficGen(unittest.TestCase): 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'} + 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, @@ -598,6 +601,28 @@ class TestLandslideTrafficGen(unittest.TestCase): 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, @@ -986,11 +1011,15 @@ class TestLandslideResourceHelper(unittest.TestCase): 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) @@ -1178,10 +1207,24 @@ class TestLandslideResourceHelper(unittest.TestCase): def test_create_test_session_res_helper(self, *args): self.res_helper._user_id = self.SUCCESS_RECORD_ID self.res_helper._tcl = mock.Mock() - test_session = {'name': 'test'} - self.assertIsNone(self.res_helper.create_test_session(test_session)) + 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': 'test', 'library': self.SUCCESS_RECORD_ID}) + {'name': _session['name'], + 'duration': 60, + 'library': self.SUCCESS_RECORD_ID}) @mock.patch.object(tg_landslide.LandslideTclClient, 'resolve_test_server_name', @@ -1597,7 +1640,7 @@ class TestLandslideTclClient(unittest.TestCase): self.mock_tcl_handler.execute.assert_has_calls([ mock.call('set dmf_ [ls::create Dmf]'), mock.call( - 'ls::get [ls::query LibraryInfo -systemLibraryName test] -Id'), + '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( @@ -1623,7 +1666,7 @@ class TestLandslideTclClient(unittest.TestCase): self.mock_tcl_handler.execute.assert_has_calls([ mock.call('set dmf_ [ls::create Dmf]'), mock.call( - 'ls::get [ls::query LibraryInfo -systemLibraryName test] -Id'), + '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( |