diff options
Diffstat (limited to 'yardstick')
23 files changed, 1538 insertions, 251 deletions
diff --git a/yardstick/benchmark/contexts/standalone/model.py b/yardstick/benchmark/contexts/standalone/model.py index fda667137..aa5fdd391 100644 --- a/yardstick/benchmark/contexts/standalone/model.py +++ b/yardstick/benchmark/contexts/standalone/model.py @@ -45,7 +45,7 @@ VM_TEMPLATE = """ <vcpu cpuset='{cpuset}'>{vcpu}</vcpu> {cputune} <os> - <type arch="x86_64" machine="pc-i440fx-xenial">hvm</type> + <type arch="x86_64" machine="{machine}">hvm</type> <boot dev="hd" /> </os> <features> @@ -306,6 +306,7 @@ class Libvirt(object): cpuset = Libvirt.pin_vcpu_for_perf(connection, hw_socket) cputune = extra_spec.get('cputune', '') + machine = extra_spec.get('machine_type', 'pc-i440fx-xenial') mac = StandaloneContextHelper.get_mac_address(0x00) image = cls.create_snapshot_qemu(connection, index, flavor.get("images", None)) @@ -316,7 +317,8 @@ class Libvirt(object): memory=memory, vcpu=vcpu, cpu=cpu, numa_cpus=numa_cpus, socket=socket, threads=threads, - vm_image=image, cpuset=cpuset, cputune=cputune) + vm_image=image, cpuset=cpuset, + machine=machine, cputune=cputune) # Add CD-ROM device vm_xml = Libvirt.add_cdrom(cdrom_img, vm_xml) diff --git a/yardstick/benchmark/contexts/standalone/ovs_dpdk.py b/yardstick/benchmark/contexts/standalone/ovs_dpdk.py index 42a275455..c6e19f614 100644 --- a/yardstick/benchmark/contexts/standalone/ovs_dpdk.py +++ b/yardstick/benchmark/contexts/standalone/ovs_dpdk.py @@ -74,6 +74,11 @@ class OvsDpdkContext(base.Context): self.wait_for_vswitchd = 10 super(OvsDpdkContext, self).__init__() + def get_dpdk_socket_mem_size(self, socket_id): + """Get the size of OvS DPDK socket memory (Mb)""" + ram = self.ovs_properties.get("ram", {}) + return ram.get('socket_%d' % (socket_id), 2048) + def init(self, attrs): """initializes itself from the supplied arguments""" super(OvsDpdkContext, self).init(attrs) @@ -134,9 +139,6 @@ class OvsDpdkContext(base.Context): if pmd_cpu_mask: pmd_mask = pmd_cpu_mask - socket0 = self.ovs_properties.get("ram", {}).get("socket_0", "2048") - socket1 = self.ovs_properties.get("ram", {}).get("socket_1", "2048") - ovs_other_config = "ovs-vsctl {0}set Open_vSwitch . other_config:{1}" detach_cmd = "ovs-vswitchd unix:{0}{1} --pidfile --detach --log-file={2}" @@ -154,7 +156,9 @@ class OvsDpdkContext(base.Context): ("ovsdb-server --remote=punix:/{0}/{1} --remote=ptcp:6640" " --pidfile --detach").format(vpath, ovs_sock_path), ovs_other_config.format("--no-wait ", "dpdk-init=true"), - ovs_other_config.format("--no-wait ", "dpdk-socket-mem='%s,%s'" % (socket0, socket1)), + ovs_other_config.format("--no-wait ", "dpdk-socket-mem='%d,%d'" % ( + self.get_dpdk_socket_mem_size(0), + self.get_dpdk_socket_mem_size(1))), lcore_mask, detach_cmd.format(vpath, ovs_sock_path, log_path), ovs_other_config.format("", "pmd-cpu-mask=%s" % pmd_mask), @@ -168,12 +172,12 @@ class OvsDpdkContext(base.Context): def setup_ovs_bridge_add_flows(self): dpdk_args = "" - dpdk_list = [] vpath = self.ovs_properties.get("vpath", "/usr/local") version = self.ovs_properties.get('version', {}) ovs_ver = [int(x) for x in version.get('ovs', self.DEFAULT_OVS).split('.')] ovs_add_port = ('ovs-vsctl add-port {br} {port} -- ' - 'set Interface {port} type={type_}{dpdk_args}{dpdk_rxq}') + 'set Interface {port} type={type_}{dpdk_args}' + '{dpdk_rxq}{pmd_rx_aff}') chmod_vpath = 'chmod 0777 {0}/var/run/openvswitch/dpdkvhostuser*' cmd_list = [ @@ -187,23 +191,38 @@ class OvsDpdkContext(base.Context): if queues: dpdk_rxq = " options:n_rxq={queue}".format(queue=queues) - ordered_network = collections.OrderedDict(self.networks) + # Sorting the array to make sure we execute dpdk0... in the order + ordered_network = collections.OrderedDict( + sorted(self.networks.items(), key=lambda t: t[1].get('port_num', 0))) + pmd_rx_aff_ports = self.ovs_properties.get("dpdk_pmd-rxq-affinity", {}) for index, vnf in enumerate(ordered_network.values()): if ovs_ver >= [2, 7, 0]: dpdk_args = " options:dpdk-devargs=%s" % vnf.get("phy_port") - dpdk_list.append(ovs_add_port.format( + affinity = pmd_rx_aff_ports.get(vnf.get("port_num", -1), "") + if affinity: + pmd_rx_aff = ' other_config:pmd-rxq-affinity=' \ + '"{affinity}"'.format(affinity=affinity) + else: + pmd_rx_aff = "" + cmd_list.append(ovs_add_port.format( br=MAIN_BRIDGE, port='dpdk%s' % vnf.get("port_num", 0), - type_='dpdk', dpdk_args=dpdk_args, dpdk_rxq=dpdk_rxq)) - - # Sorting the array to make sure we execute dpdk0... in the order - list.sort(dpdk_list) - cmd_list.extend(dpdk_list) + type_='dpdk', dpdk_args=dpdk_args, dpdk_rxq=dpdk_rxq, + pmd_rx_aff=pmd_rx_aff)) # Need to do two for loop to maintain the dpdk/vhost ports. + pmd_rx_aff_ports = self.ovs_properties.get("vhost_pmd-rxq-affinity", + {}) for index, _ in enumerate(ordered_network): + affinity = pmd_rx_aff_ports.get(index) + if affinity: + pmd_rx_aff = ' other_config:pmd-rxq-affinity=' \ + '"{affinity}"'.format(affinity=affinity) + else: + pmd_rx_aff = "" cmd_list.append(ovs_add_port.format( br=MAIN_BRIDGE, port='dpdkvhostuser%s' % index, - type_='dpdkvhostuser', dpdk_args="", dpdk_rxq="")) + type_='dpdkvhostuser', dpdk_args="", dpdk_rxq=dpdk_rxq, + pmd_rx_aff=pmd_rx_aff)) ovs_flow = ("ovs-ofctl add-flow {0} in_port=%s,action=output:%s". format(MAIN_BRIDGE)) @@ -399,7 +418,9 @@ class OvsDpdkContext(base.Context): self.configure_nics_for_ovs_dpdk() hp_total_mb = int(self.vm_flavor.get('ram', '4096')) * len(self.servers) - common_utils.setup_hugepages(self.connection, hp_total_mb * 1024) + common_utils.setup_hugepages(self.connection, (hp_total_mb + \ + self.get_dpdk_socket_mem_size(0) + \ + self.get_dpdk_socket_mem_size(1)) * 1024) self._check_hugepages() diff --git a/yardstick/benchmark/core/report.py b/yardstick/benchmark/core/report.py index 199602444..a484a49f3 100644 --- a/yardstick/benchmark/core/report.py +++ b/yardstick/benchmark/core/report.py @@ -1,7 +1,7 @@ -############################################################################# -# Copyright (c) 2017 Rajesh Kudaka +############################################################################## +# Copyright (c) 2017 Rajesh Kudaka <4k.rajesh@gmail.com> +# Copyright (c) 2018 Intel Corporation. # -# Author: Rajesh Kudaka 4k.rajesh@gmail.com # 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 @@ -10,27 +10,76 @@ """ Handler for yardstick command 'report' """ -from __future__ import print_function - -from __future__ import absolute_import - import ast import re import uuid +import jinja2 from api.utils import influx - -from django.conf import settings -from django.template import Context -from django.template import Template - from oslo_utils import encodeutils from oslo_utils import uuidutils from yardstick.common import constants as consts -from yardstick.common.html_template import template from yardstick.common.utils import cliargs -settings.configure() + +class JSTree(object): + """Data structure to parse data for use with the JS library jsTree""" + def __init__(self): + self._created_nodes = ['#'] + self.jstree_data = [] + + def _create_node(self, _id): + """Helper method for format_for_jstree to create each node. + + Creates the node (and any required parents) and keeps track + of the created nodes. + + :param _id: (string) id of the node to be created + :return: None + """ + components = _id.split(".") + + if len(components) == 1: + text = components[0] + parent_id = "#" + else: + text = components[-1] + parent_id = ".".join(components[:-1]) + # make sure the parent has been created + if not parent_id in self._created_nodes: + self._create_node(parent_id) + + self.jstree_data.append({"id": _id, "text": text, "parent": parent_id}) + self._created_nodes.append(_id) + + def format_for_jstree(self, data): + """Format the data into the required format for jsTree. + + The data format expected is a list of key-value pairs which represent + the data and name for each metric e.g.: + + [{'data': [0, ], 'name': 'tg__0.DropPackets'}, + {'data': [548, ], 'name': 'tg__0.LatencyAvg.5'},] + + This data is converted into the format required for jsTree to group and + display the metrics in a hierarchial fashion, including creating a + number of parent nodes e.g.:: + + [{"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"},] + + :param data: (list) data to be converted + :return: list + """ + self._created_nodes = ['#'] + self.jstree_data = [] + + for item in data: + self._create_node(item["name"]) + + return self.jstree_data class Report(object): @@ -64,7 +113,7 @@ class Report(object): if query_exec: return query_exec else: - raise KeyError("Task ID or Test case not found..") + raise KeyError("Test case not found.") def _get_tasks(self): task_cmd = "select * from \"%s\" where task_id= '%s'" @@ -73,7 +122,7 @@ class Report(object): if query_exec: return query_exec else: - raise KeyError("Task ID or Test case not found..") + raise KeyError("Task ID or Test case not found.") @cliargs("task_id", type=str, help=" task id", nargs=1) @cliargs("yaml_name", type=str, help=" Yaml file Name", nargs=1) @@ -117,12 +166,22 @@ class Report(object): series['data'] = values temp_series.append(series) - Template_html = Template(template) - Context_html = Context({"series": temp_series, - "Timestamp": self.Timestamp, - "task_id": self.task_id, - "table": table_vals}) + template_dir = consts.YARDSTICK_ROOT_PATH + "yardstick/common" + template_environment = jinja2.Environment( + autoescape=False, + loader=jinja2.FileSystemLoader(template_dir), + trim_blocks=False) + + context = { + "series": temp_series, + "Timestamps": self.Timestamp, + "task_id": self.task_id, + "table": table_vals, + } + + template_html = template_environment.get_template("report.html.j2") + with open(consts.DEFAULT_HTML_FILE, "w") as file_open: - file_open.write(Template_html.render(Context_html)) + file_open.write(template_html.render(context)) print("Report generated. View /tmp/yardstick.htm") diff --git a/yardstick/benchmark/scenarios/storage/storperf.py b/yardstick/benchmark/scenarios/storage/storperf.py index 7c8e5fe66..5b8b00075 100644 --- a/yardstick/benchmark/scenarios/storage/storperf.py +++ b/yardstick/benchmark/scenarios/storage/storperf.py @@ -227,8 +227,8 @@ class StorPerf(base.Scenario): LOG.info("Job %s completed with steady state %s", job_id, steady_state) - result_res = requests.get('http://%s:5000/api/v1.0/jobs?' - 'type=status&id=%s' % (self.target, job_id)) + result_res = requests.get('http://%s:5000/api/v1.0/jobs?id=%s' % + (self.target, job_id)) result_res_content = jsonutils.loads( result_res.content) result.update(result_res_content) diff --git a/yardstick/common/html_template.py b/yardstick/common/html_template.py index e17c76637..c15dd8238 100644 --- a/yardstick/common/html_template.py +++ b/yardstick/common/html_template.py @@ -8,130 +8,6 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################# -template = """ -<html> -<body> -<head> -<meta charset="utf-8"> -<meta name="viewport" content="width=device-width, initial-scale=1"> -<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7\ -/css/bootstrap.min.css"> -<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1\ -/jquery.min.js"></script> -<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7\ -/js/bootstrap.min.js"></script> -<script src="https://code.highcharts.com/highcharts.js"></script> -<script src="jquery.min.js"></script> -<script src="highcharts.js"></script> -</head> -<style> - -table{ - overflow-y: scroll; - height: 360px; - display: block; - } - - header,h3{ - font-family:Frutiger; - clear: left; - text-align: center; -} -</style> -<header class="jumbotron text-center"> - <h1>Yardstick User Interface</h1> - <h4>Report of {{task_id}} Generated</h4> -</header> - -<div class="container"> - <div class="row"> - <div class="col-md-4"> - <div class="table-responsive" > - <table class="table table-hover" > </table> - </div> - </div> - <div class="col-md-8" > - <div id="container" ></div> - </div> - </div> -</div> -<script> - var arr, tab, th, tr, td, tn, row, col, thead, tbody; - arr={{table|safe}} - tab = document.getElementsByTagName('table')[0]; - thead=document.createElement('thead'); - tr = document.createElement('tr'); - for(row=0;row<Object.keys(arr).length;row++) - { - th = document.createElement('th'); - tn = document.createTextNode(Object.keys(arr).sort()[row]); - th.appendChild(tn); - tr.appendChild(th); - thead.appendChild(tr); - } - tab.appendChild(thead); - tbody=document.createElement('tbody'); - - for (col = 0; col < arr[Object.keys(arr)[0]].length; col++){ - tr = document.createElement('tr'); - for(row=0;row<Object.keys(arr).length;row++) - { - td = document.createElement('td'); - tn = document.createTextNode(arr[Object.keys(arr).sort()[row]][col]); - td.appendChild(tn); - tr.appendChild(td); - } - tbody.appendChild(tr); - } -tab.appendChild(tbody); - -</script> - -<script language="JavaScript"> - -$(function() { - $('#container').highcharts({ - title: { - text: 'Yardstick test results', - x: -20 //center - }, - subtitle: { - text: 'Report of {{task_id}} Task Generated', - x: -20 - }, - xAxis: { - title: { - text: 'Timestamp' - }, - categories:{{Timestamp|safe}} - }, - yAxis: { - - plotLines: [{ - value: 0, - width: 1, - color: '#808080' - }] - }, - tooltip: { - valueSuffix: '' - }, - legend: { - layout: 'vertical', - align: 'right', - verticalAlign: 'middle', - borderWidth: 0 - }, - series: {{series|safe}} - }); -}); - -</script> - - -</body> -</html>""" - report_template = """ <html> <head> diff --git a/yardstick/common/nsb_report.css b/yardstick/common/nsb_report.css new file mode 100644 index 000000000..0c47791e2 --- /dev/null +++ b/yardstick/common/nsb_report.css @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2017 Rajesh Kudaka <4k.rajesh@gmail.com> + * Copyright (c) 2018 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 + ******************************************************************************/ + +body { + font-size: 16pt; +} + +table { + overflow-y: scroll; + height: 360px; + display: block; +} + +header { + font-family: Frutiger; + clear: left; + text-align: center; +} + +.control-pane { + font-size: 10pt; +} diff --git a/yardstick/common/nsb_report.html.j2 b/yardstick/common/nsb_report.html.j2 new file mode 100644 index 000000000..f1b4ae1c2 --- /dev/null +++ b/yardstick/common/nsb_report.html.j2 @@ -0,0 +1,160 @@ +<!DOCTYPE html> +<html> + +<!-- + Copyright (c) 2017 Rajesh Kudaka <4k.rajesh@gmail.com> + Copyright (c) 2018 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 +--> + + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.5/themes/default/style.min.css"> + <link rel="stylesheet" href="{{template_dir}}/nsb_report.css"> + <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> + <script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/jstree.min.js"></script> + <script src="https://code.highcharts.com/highcharts.js"></script> + </head> + + <body> + <div class="container" style="width:80%"> + <div class="row"> + <header class="jumbotron"> + <h1>Yardstick User Interface</h1> + <h4>Report of {{task_id}} Generated</h4> + </header> + </div> + <div class="row" style="height:500px"> + <div class="col-md-2 control-pane"> + <div id="data_selector"></div> + </div> + <div class="col-md-10 data-pane"> + <div id="graph"></div> + </div> + </div> + <div class="row"> + <div class="col-md-12 table-responsive"> + <table class="table table-hover"></table> + </div> + </div> + </div> + + <script> + var arr, tab, tr, td, tbody, keys, key, curr_data; + arr = {{table|safe}}; + + tab = document.getElementsByTagName('table')[0]; + tbody = document.createElement('tbody'); + keys = Object.keys(arr); + // for each metric + for (var i = 0; i < keys.length; i++) { + tr = document.createElement('tr'); + td = document.createElement('td'); + key = keys[i]; + td.append(key); + tr.append(td); + curr_data = arr[key]; + // add each piece of data as its own column + for (var j = 0; j < curr_data.length; j++) { + td = document.createElement('td'); + td.append(curr_data[j]); + tr.append(td); + } + tbody.append(tr); + } + tab.appendChild(tbody); + + $(function() { + $('#data_selector').jstree({ + plugins: ['checkbox'], + checkbox: { + three_state: false, + whole_node: true, + tie_selection: false, + }, + core: { + themes: { + icons: false, + stripes: true, + }, + data: {{jstree_nodes|safe}}, + }, + }); + + $('#data_selector').on('check_node.jstree uncheck_node.jstree', function(e, data) { + var selected_leaves = []; + for (var i = 0; i < data.selected.length; i++) { + var node = data.instance.get_node(data.selected[i]); + if (node.children.length == 0) { + var point = {name: node.id, data: arr[node.id]}; + selected_leaves.push(point); + } + } + + $('#graph').highcharts({ + title: { + text: 'Yardstick Graphs', + x: -20, //center + }, + chart: { + marginLeft: 400, + zoomType: 'x', + type: 'spline', + }, + xAxis: { + crosshair: { + width: 1, + color: 'black', + }, + title: { + text: 'Timestamp', + }, + categories: {{Timestamps|safe}}, + }, + yAxis: { + crosshair: { + width: 1, + color: 'black', + }, + plotLines: [{ + value: 0, + width: 1, + color: '#808080', + }], + }, + plotOptions: { + series: { + showCheckbox: false, + visible: false, + }, + }, + tooltip: { + valueSuffix: '', + }, + legend: { + enabled: true, + }, + series: selected_leaves, + }); + + var chart = $('#graph').highcharts(); + for (var i = 0; i < chart.series.length; i++) { + var series = chart.series[i]; + if (series.visible) { + series.hide(); + } else { + series.show(); + } + } + }); + }); + </script> + </body> +</html> diff --git a/yardstick/common/report.html.j2 b/yardstick/common/report.html.j2 new file mode 100644 index 000000000..ab76510ca --- /dev/null +++ b/yardstick/common/report.html.j2 @@ -0,0 +1,121 @@ +<!DOCTYPE html> +<html> + +<!-- + Copyright (c) 2017 Rajesh Kudaka <4k.rajesh@gmail.com> + Copyright (c) 2018 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 +--> + + <head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> + <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> + <script src="https://code.highcharts.com/highcharts.js"></script> + + <style> + table { + overflow-y: scroll; + height: 360px; + display: block; + } + header { + font-family: Frutiger; + clear: left; + text-align: center; + } + </style> + </head> + + <body> + <header class="jumbotron text-center"> + <h1>Yardstick User Interface</h1> + <h4>Report of {{task_id}} Generated</h4> + </header> + + <div class="container"> + <div class="row"> + <div class="col-md-4"> + <div class="table-responsive"> + <table class="table table-hover"></table> + </div> + </div> + <div class="col-md-8"> + <div id="container"></div> + </div> + </div> + </div> + + <script> + var arr, tab, th, tr, td, tn, row, col, thead, tbody; + arr = {{table|safe}}; + tab = document.getElementsByTagName('table')[0]; + + thead = document.createElement('thead'); + tr = document.createElement('tr'); + for (col = 0; col < Object.keys(arr).length; col++) { + th = document.createElement('th'); + tn = document.createTextNode(Object.keys(arr).sort()[col]); + th.appendChild(tn); + tr.appendChild(th); + thead.appendChild(tr); + } + tab.appendChild(thead); + + tbody = document.createElement('tbody'); + for (row = 0; row < arr[Object.keys(arr)[0]].length; row++) { + tr = document.createElement('tr'); + for (col = 0; col < Object.keys(arr).length; col++) { + td = document.createElement('td'); + tn = document.createTextNode(arr[Object.keys(arr).sort()[col]][row]); + td.appendChild(tn); + tr.appendChild(td); + } + tbody.appendChild(tr); + } + tab.appendChild(tbody); + + $(function() { + $('#container').highcharts({ + title: { + text: 'Yardstick test results', + x: -20, //center + }, + subtitle: { + text: 'Report of {{task_id}} Task Generated', + x: -20, + }, + xAxis: { + title: { + text: 'Timestamp', + }, + categories: {{Timestamps|safe}}, + }, + yAxis: { + plotLines: [{ + value: 0, + width: 1, + color: '#808080', + }], + }, + tooltip: { + valueSuffix: '', + }, + legend: { + layout: 'vertical', + align: 'right', + verticalAlign: 'middle', + borderWidth: 0, + }, + series: {{series|safe}}, + }); + }); + </script> + </body> +</html> diff --git a/yardstick/common/utils.py b/yardstick/common/utils.py index 205247947..51313ef47 100644 --- a/yardstick/common/utils.py +++ b/yardstick/common/utils.py @@ -30,6 +30,7 @@ import subprocess import sys import time import threading +import math import six from flask import jsonify @@ -505,7 +506,7 @@ def setup_hugepages(ssh_client, size_kb): NR_HUGEPAGES_PATH = '/proc/sys/vm/nr_hugepages' meminfo = read_meminfo(ssh_client) hp_size_kb = int(meminfo['Hugepagesize']) - hp_number = int(abs(size_kb / hp_size_kb)) + hp_number = int(math.ceil(size_kb / float(hp_size_kb))) ssh_client.execute( 'echo %s | sudo tee %s' % (hp_number, NR_HUGEPAGES_PATH)) hp = six.BytesIO() diff --git a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py index 1f465bde5..b5e4172a9 100644 --- a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py +++ b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py @@ -49,6 +49,19 @@ PROTOCOL_STATUS_DOWN = ['down', 'notStarted'] SUPPORTED_PROTO = [PROTO_UDP] +SUPPORTED_DSCP_CLASSES = [ + 'defaultPHB', + 'classSelectorPHB', + 'assuredForwardingPHB', + 'expeditedForwardingPHB'] + +SUPPORTED_TOS_FIELDS = [ + 'precedence', + 'delay', + 'throughput', + 'reliability' +] + class Vlan(object): def __init__(self, @@ -198,9 +211,10 @@ class IxNextgen(object): # pragma: no cover :param proto: IxNet protocol str representation, e.g.: '::ixNet::OBJ-/topology:2/deviceGroup:1/ethernet:1/ipv4:L14' - :return: (str) protocol status: 'up', 'down' or 'notStarted' + :return: (list) protocol status: list of sessions protocol + statuses which include states 'up', 'down' and 'notStarted' """ - return self.ixnet.getAttribute(proto, '-sessionStatus')[0] + return self.ixnet.getAttribute(proto, '-sessionStatus') def is_traffic_running(self): """Returns true if traffic state == TRAFFIC_STATUS_STARTED""" @@ -218,8 +232,8 @@ class IxNextgen(object): # pragma: no cover :return: (bool) True if all protocols status is 'up', False if any protocol status is 'down' or 'notStarted' """ - return all(self._get_protocol_status(proto) == PROTOCOL_STATUS_UP - for proto in protocols) + return all(session_status is PROTOCOL_STATUS_UP for proto in protocols + for session_status in self._get_protocol_status(proto)) def is_protocols_stopped(self, protocols): """Returns true if all protocols statuses are in PROTOCOL_STATUS_DOWN @@ -229,8 +243,8 @@ class IxNextgen(object): # pragma: no cover :return: (bool) True if all protocols status is 'down' or 'notStarted', False if any protocol status is 'up' """ - return all(self._get_protocol_status(proto) in PROTOCOL_STATUS_DOWN - for proto in protocols) + return all(session_status in PROTOCOL_STATUS_DOWN for proto in protocols + for session_status in self._get_protocol_status(proto)) @staticmethod def _parse_framesize(framesize): @@ -597,23 +611,25 @@ class IxNextgen(object): # pragma: no cover 'precedence': [1, 4, 7] } """ - if 'raw' in priority: + if priority.get('raw'): priority_field = self._get_field_in_stack_item(ip_descriptor, 'priority.raw') self._set_priority_field(priority_field, priority['raw']) - elif 'dscp' in priority: + elif priority.get('dscp'): for field, value in priority['dscp'].items(): - priority_field = self._get_field_in_stack_item( - ip_descriptor, - 'priority.ds.phb.{field}.{field}'.format(field=field)) - self._set_priority_field(priority_field, value) + if field in SUPPORTED_DSCP_CLASSES: + priority_field = self._get_field_in_stack_item( + ip_descriptor, + 'priority.ds.phb.{field}.{field}'.format(field=field)) + self._set_priority_field(priority_field, value) - elif 'tos' in priority: + elif priority.get('tos'): for field, value in priority['tos'].items(): - priority_field = self._get_field_in_stack_item( - ip_descriptor, 'priority.tos.' + field) - self._set_priority_field(priority_field, value) + if field in SUPPORTED_TOS_FIELDS: + priority_field = self._get_field_in_stack_item( + ip_descriptor, 'priority.tos.' + field) + self._set_priority_field(priority_field, value) def _set_priority_field(self, field_descriptor, value): """Set the priority field described by field_descriptor @@ -938,7 +954,7 @@ class IxNextgen(object): # pragma: no cover self.ixnet.commit() return obj - def add_pppox_client(self, xproto, auth, user, pwd): + def add_pppox_client(self, xproto, auth, user, pwd, enable_redial=True): log.debug( "add_pppox_client: xproto='%s', auth='%s', user='%s', pwd='%s'", xproto, auth, user, pwd) @@ -958,6 +974,10 @@ class IxNextgen(object): # pragma: no cover else: raise NotImplementedError() + if enable_redial: + redial = self.ixnet.getAttribute(obj, '-enableRedial') + self.ixnet.setAttribute(redial + '/singleValue', '-value', 'true') + self.ixnet.commit() return obj diff --git a/yardstick/network_services/traffic_profile/http_ixload.py b/yardstick/network_services/traffic_profile/http_ixload.py index c64e7511f..b88aadff7 100644 --- a/yardstick/network_services/traffic_profile/http_ixload.py +++ b/yardstick/network_services/traffic_profile/http_ixload.py @@ -278,8 +278,12 @@ class IXLOADHttpTest(object): :param param: (dict) http_client section from traffic profile :return: """ - self.update_page_size(net_traffic, param["page_object"]) - self.update_user_count(net_traffic, param["simulated_users"]) + page = param.get("page_object") + if page: + self.update_page_size(net_traffic, page) + users = param.get("simulated_users") + if users: + self.update_user_count(net_traffic, users) def update_page_size(self, net_traffic, page_object): """Update page_object field in http client object in net_traffic diff --git a/yardstick/network_services/traffic_profile/prox_binsearch.py b/yardstick/network_services/traffic_profile/prox_binsearch.py index f924cf419..402bf741c 100644 --- a/yardstick/network_services/traffic_profile/prox_binsearch.py +++ b/yardstick/network_services/traffic_profile/prox_binsearch.py @@ -168,8 +168,8 @@ class ProxBinSearchProfile(ProxProfile): samples = result.get_samples(pkt_size, successful_pkt_loss, port_samples) - if theor_max_thruput < samples["TxThroughput"]: - theor_max_thruput = samples['TxThroughput'] + if theor_max_thruput < samples["RequestedTxThroughput"]: + theor_max_thruput = samples['RequestedTxThroughput'] samples['theor_max_throughput'] = theor_max_thruput samples["rx_total"] = int(result.rx_total) diff --git a/yardstick/network_services/vnf_generic/vnf/prox_helpers.py b/yardstick/network_services/vnf_generic/vnf/prox_helpers.py index 8d721c045..e9d83623b 100644 --- a/yardstick/network_services/vnf_generic/vnf/prox_helpers.py +++ b/yardstick/network_services/vnf_generic/vnf/prox_helpers.py @@ -601,6 +601,99 @@ class ProxSocketHelper(object): LOG.debug("Multi port packet ..OK.. %s", tot_result) return True, tot_result + @staticmethod + def multi_port_stats_tuple(stats, ports): + """ + Create a statistics tuple from port stats. + + Returns a dict with contains the port stats indexed by port name + + :param stats: (List) - List of List of port stats in pps + :param ports (Iterator) - to List of Ports + + :return: (Dict) of port stats indexed by port_name + """ + + samples = {} + port_names = {} + try: + port_names = {port_num: port_name for port_name, port_num in ports} + except (TypeError, IndexError, KeyError): + LOG.critical("Ports are not initialized or number of port is ZERO ... CRITICAL ERROR") + return {} + + try: + for stat in stats: + port_num = stat[0] + samples[port_names[port_num]] = { + "in_packets": stat[1], + "out_packets": stat[2]} + except (TypeError, IndexError, KeyError): + LOG.error("Ports data and samples data is incompatable ....") + return {} + + return samples + + @staticmethod + def multi_port_stats_diff(prev_stats, new_stats, hz): + """ + Create a statistics tuple from difference between prev port stats + and current port stats. And store results in pps. + + :param prev_stats: (List) - Previous List of port statistics + :param new_stats: (List) - Current List of port statistics + :param hz (float) - speed of system in Hz + + :return: sample (List) - Difference of prev_port_stats and + new_port_stats in pps + """ + + RX_TOTAL_INDEX = 1 + TX_TOTAL_INDEX = 2 + TSC_INDEX = 5 + + stats = [] + + if len(prev_stats) is not len(new_stats): + for port_index, stat in enumerate(new_stats): + stats.append([port_index, float(0), float(0), 0, 0, 0]) + return stats + + try: + for port_index, stat in enumerate(new_stats): + if stat[RX_TOTAL_INDEX] > prev_stats[port_index][RX_TOTAL_INDEX]: + rx_total = stat[RX_TOTAL_INDEX] - \ + prev_stats[port_index][RX_TOTAL_INDEX] + else: + rx_total = stat[RX_TOTAL_INDEX] + + if stat[TX_TOTAL_INDEX] > prev_stats[port_index][TX_TOTAL_INDEX]: + tx_total = stat[TX_TOTAL_INDEX] - prev_stats[port_index][TX_TOTAL_INDEX] + else: + tx_total = stat[TX_TOTAL_INDEX] + + if stat[TSC_INDEX] > prev_stats[port_index][TSC_INDEX]: + tsc = stat[TSC_INDEX] - prev_stats[port_index][TSC_INDEX] + else: + tsc = stat[TSC_INDEX] + + if tsc is 0: + rx_total = tx_total = float(0) + else: + if hz is 0: + LOG.error("HZ is ZERO ..") + rx_total = tx_total = float(0) + else: + rx_total = float(rx_total * hz / tsc) + tx_total = float(tx_total * hz / tsc) + + stats.append([port_index, rx_total, tx_total, 0, 0, tsc]) + except (TypeError, IndexError, KeyError): + stats = [] + LOG.info("Current Port Stats incompatable to previous Port stats .. Discarded") + + return stats + def port_stats(self, ports): """get counter values from a specific port""" tot_result = [0] * 12 @@ -968,6 +1061,8 @@ class ProxResourceHelper(ClientResourceHelper): self.step_delta = 1 self.step_time = 0.5 self._test_type = None + self.prev_multi_port = [] + self.prev_hz = 0 @property def sut(self): @@ -1006,11 +1101,40 @@ class ProxResourceHelper(ClientResourceHelper): def collect_collectd_kpi(self): return self._collect_resource_kpi() + def collect_live_stats(self): + ports = [] + for _, port_num in self.vnfd_helper.ports_iter(): + ports.append(port_num) + + ok, curr_port_stats = self.sut.multi_port_stats(ports) + if not ok: + return False, {} + + hz = self.sut.hz() + if hz is 0: + hz = self.prev_hz + else: + self.prev_hz = hz + + new_all_port_stats = \ + self.sut.multi_port_stats_diff(self.prev_multi_port, curr_port_stats, hz) + + self.prev_multi_port = curr_port_stats + + live_stats = self.sut.multi_port_stats_tuple(new_all_port_stats, + self.vnfd_helper.ports_iter()) + return True, live_stats + def collect_kpi(self): result = super(ProxResourceHelper, self).collect_kpi() # add in collectd kpis manually if result: result['collect_stats'] = self._collect_resource_kpi() + + ok, live_stats = self.collect_live_stats() + if ok: + result.update({'live_stats': live_stats}) + return result def terminate(self): diff --git a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py index 1cb5cdd8f..8833b88f2 100644 --- a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py @@ -898,6 +898,8 @@ class SampleVNFTrafficGen(GenericTrafficGen): self.scenario_helper.nodes[self.name] ) + self.resource_helper.context_cfg = context_cfg + self.resource_helper.setup() # must generate_cfg after DPDK bind because we need port number self.resource_helper.generate_cfg() diff --git a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py index 89f8194c0..4c13112be 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py @@ -12,7 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. +import ipaddress import logging +import six from yardstick.common import utils from yardstick.network_services.libs.ixia_libs.ixnet import ixnet_api @@ -25,6 +27,183 @@ LOG = logging.getLogger(__name__) WAIT_AFTER_CFG_LOAD = 10 WAIT_FOR_TRAFFIC = 30 +WAIT_PROTOCOLS_STARTED = 360 + + +class IxiaBasicScenario(object): + def __init__(self, client, context_cfg, ixia_cfg): + + self.client = client + self.context_cfg = context_cfg + self.ixia_cfg = ixia_cfg + + self._uplink_vports = None + self._downlink_vports = None + + def apply_config(self): + pass + + def create_traffic_model(self): + vports = self.client.get_vports() + self._uplink_vports = vports[::2] + self._downlink_vports = vports[1::2] + self.client.create_traffic_model(self._uplink_vports, + self._downlink_vports) + + def run_protocols(self): + pass + + def stop_protocols(self): + pass + + +class IxiaPppoeClientScenario(object): + def __init__(self, client, context_cfg, ixia_cfg): + + self.client = client + + self._uplink_vports = None + self._downlink_vports = None + + self._access_topologies = [] + self._core_topologies = [] + + self._context_cfg = context_cfg + self._ixia_cfg = ixia_cfg + self.protocols = [] + + def apply_config(self): + vports = self.client.get_vports() + self._uplink_vports = vports[::2] + self._downlink_vports = vports[1::2] + self._fill_ixia_config() + self._apply_access_network_config() + self._apply_core_network_config() + + def create_traffic_model(self): + self.client.create_ipv4_traffic_model(self._access_topologies, + self._core_topologies) + + def run_protocols(self): + LOG.info('PPPoE Scenario - Start Protocols') + self.client.start_protocols() + utils.wait_until_true( + lambda: self.client.is_protocols_running(self.protocols), + timeout=WAIT_PROTOCOLS_STARTED, sleep=2) + + def stop_protocols(self): + LOG.info('PPPoE Scenario - Stop Protocols') + self.client.stop_protocols() + + def _get_intf_addr(self, intf): + """Retrieve interface IP address and mask + + :param intf: could be the string which represents IP address + with mask (e.g 192.168.10.2/24) or a dictionary with the host + name and the port (e.g. {'tg__0': 'xe1'}) + :return: (tuple) pair of ip address and mask + """ + if isinstance(intf, six.string_types): + ip, mask = tuple(intf.split('/')) + return ip, int(mask) + + node_name, intf_name = next(iter(intf.items())) + node = self._context_cfg["nodes"].get(node_name, {}) + interface = node.get("interfaces", {})[intf_name] + ip = interface["local_ip"] + mask = interface["netmask"] + ipaddr = ipaddress.ip_network(six.text_type('{}/{}'.format(ip, mask)), + strict=False) + return ip, ipaddr.prefixlen + + def _fill_ixia_config(self): + pppoe = self._ixia_cfg["pppoe_client"] + ipv4 = self._ixia_cfg["ipv4_client"] + + _ip = [self._get_intf_addr(intf)[0] for intf in pppoe["ip"]] + self._ixia_cfg["pppoe_client"]["ip"] = _ip + + _ip = [self._get_intf_addr(intf)[0] for intf in ipv4["gateway_ip"]] + self._ixia_cfg["ipv4_client"]["gateway_ip"] = _ip + + addrs = [self._get_intf_addr(intf) for intf in ipv4["ip"]] + _ip = [addr[0] for addr in addrs] + _prefix = [addr[1] for addr in addrs] + + self._ixia_cfg["ipv4_client"]["ip"] = _ip + self._ixia_cfg["ipv4_client"]["prefix"] = _prefix + + def _apply_access_network_config(self): + pppoe = self._ixia_cfg["pppoe_client"] + sessions_per_port = pppoe['sessions_per_port'] + sessions_per_svlan = pppoe['sessions_per_svlan'] + svlan_count = int(sessions_per_port / sessions_per_svlan) + + # add topology per uplink port (access network) + for access_tp_id, vport in enumerate(self._uplink_vports): + name = 'Topology access {}'.format(access_tp_id) + tp = self.client.add_topology(name, vport) + self._access_topologies.append(tp) + # add device group per svlan + for dg_id in range(svlan_count): + s_vlan_id = int(pppoe['s_vlan']) + dg_id + access_tp_id * svlan_count + s_vlan = ixnet_api.Vlan(vlan_id=s_vlan_id) + c_vlan = ixnet_api.Vlan(vlan_id=pppoe['c_vlan'], vlan_id_step=1) + name = 'SVLAN {}'.format(s_vlan_id) + dg = self.client.add_device_group(tp, name, sessions_per_svlan) + # add ethernet layer to device group + ethernet = self.client.add_ethernet(dg, 'Ethernet') + self.protocols.append(ethernet) + self.client.add_vlans(ethernet, [s_vlan, c_vlan]) + # add ppp over ethernet + if 'pap_user' in pppoe: + ppp = self.client.add_pppox_client(ethernet, 'pap', + pppoe['pap_user'], + pppoe['pap_password']) + else: + ppp = self.client.add_pppox_client(ethernet, 'chap', + pppoe['chap_user'], + pppoe['chap_password']) + self.protocols.append(ppp) + + def _apply_core_network_config(self): + ipv4 = self._ixia_cfg["ipv4_client"] + sessions_per_port = ipv4['sessions_per_port'] + sessions_per_vlan = ipv4['sessions_per_vlan'] + vlan_count = int(sessions_per_port / sessions_per_vlan) + + # add topology per downlink port (core network) + for core_tp_id, vport in enumerate(self._downlink_vports): + name = 'Topology core {}'.format(core_tp_id) + tp = self.client.add_topology(name, vport) + self._core_topologies.append(tp) + # add device group per vlan + for dg_id in range(vlan_count): + name = 'Core port {}'.format(core_tp_id) + dg = self.client.add_device_group(tp, name, sessions_per_vlan) + # add ethernet layer to device group + ethernet = self.client.add_ethernet(dg, 'Ethernet') + self.protocols.append(ethernet) + if 'vlan' in ipv4: + vlan_id = int(ipv4['vlan']) + dg_id + core_tp_id * vlan_count + vlan = ixnet_api.Vlan(vlan_id=vlan_id) + self.client.add_vlans(ethernet, [vlan]) + # add ipv4 layer + gw_ip = ipv4['gateway_ip'][core_tp_id] + # use gw addr to generate ip addr from the same network + ip_addr = ipaddress.IPv4Address(gw_ip) + 1 + ipv4_obj = self.client.add_ipv4(ethernet, name='ipv4', + addr=ip_addr, + addr_step='0.0.0.1', + prefix=ipv4['prefix'][core_tp_id], + gateway=gw_ip) + self.protocols.append(ipv4_obj) + if ipv4.get("bgp"): + bgp_peer_obj = self.client.add_bgp(ipv4_obj, + dut_ip=ipv4["bgp"]["dut_ip"], + local_as=ipv4["bgp"]["as_number"], + bgp_type=ipv4["bgp"].get("bgp_type")) + self.protocols.append(bgp_peer_obj) class IxiaRfc2544Helper(Rfc2544ResourceHelper): @@ -41,6 +220,11 @@ class IxiaResourceHelper(ClientResourceHelper): super(IxiaResourceHelper, self).__init__(setup_helper) self.scenario_helper = setup_helper.scenario_helper + self._ixia_scenarios = { + "IxiaBasic": IxiaBasicScenario, + "IxiaPppoeClient": IxiaPppoeClientScenario, + } + self.client = ixnet_api.IxNextgen() if rfc_helper_type is None: @@ -49,6 +233,8 @@ class IxiaResourceHelper(ClientResourceHelper): self.rfc_helper = rfc_helper_type(self.scenario_helper) self.uplink_ports = None self.downlink_ports = None + self.context_cfg = None + self._ix_scenario = None self._connect() def _connect(self, client=None): @@ -57,7 +243,12 @@ class IxiaResourceHelper(ClientResourceHelper): def get_stats(self, *args, **kwargs): return self.client.get_statistics() + def setup(self): + super(IxiaResourceHelper, self).setup() + self._init_ix_scenario() + def stop_collect(self): + self._ix_scenario.stop_protocols() self._terminated.value = 1 def generate_samples(self, ports, duration): @@ -92,14 +283,24 @@ class IxiaResourceHelper(ClientResourceHelper): return samples + def _init_ix_scenario(self): + ixia_config = self.scenario_helper.scenario_cfg.get('ixia_config', 'IxiaBasic') + + if ixia_config in self._ixia_scenarios: + scenario_type = self._ixia_scenarios[ixia_config] + + self._ix_scenario = scenario_type(self.client, self.context_cfg, + self.scenario_helper.scenario_cfg['options']) + else: + raise RuntimeError( + "IXIA config type '{}' not supported".format(ixia_config)) + def _initialize_client(self): """Initialize the IXIA IxNetwork client and configure the server""" self.client.clear_config() self.client.assign_ports() - vports = self.client.get_vports() - uplink_vports = vports[::2] - downlink_vports = vports[1::2] - self.client.create_traffic_model(uplink_vports, downlink_vports) + self._ix_scenario.apply_config() + self._ix_scenario.create_traffic_model() def run_traffic(self, traffic_profile, *args): if self._terminated.value: @@ -123,6 +324,8 @@ class IxiaResourceHelper(ClientResourceHelper): mac["src_mac_{}".format(port_num)] = virt_intf.get("local_mac", default) mac["dst_mac_{}".format(port_num)] = virt_intf.get("dst_mac", default) + self._ix_scenario.run_protocols() + try: while not self._terminated.value: first_run = traffic_profile.execute_traffic( @@ -144,6 +347,7 @@ class IxiaResourceHelper(ClientResourceHelper): except Exception: # pylint: disable=broad-except LOG.exception('Run Traffic terminated') + self._ix_scenario.stop_protocols() self._terminated.value = 1 def collect_kpi(self): diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py b/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py index d880b4169..e76a3ca27 100644 --- a/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py @@ -55,7 +55,7 @@ class ModelLibvirtTestCase(unittest.TestCase): numa_cpus=0 - 10, socket=1, threads=1, vm_image="/var/lib/libvirt/images/yardstick-nsb-image.img", - cpuset=2 - 10, cputune='') + cpuset=2 - 10, cputune='', machine='pc') def setUp(self): self.pci_address_str = '0001:04:03.2' @@ -352,7 +352,8 @@ class ModelLibvirtTestCase(unittest.TestCase): 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') + 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) diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py b/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py index 190e83d5f..413bb68b7 100644 --- a/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py @@ -160,6 +160,13 @@ class OvsDpdkContextTestCase(unittest.TestCase): } 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): @@ -430,7 +437,8 @@ class OvsDpdkContextTestCase(unittest.TestCase): self.assertEqual([vnf_instance_2], self.ovs_dpdk.setup_ovs_dpdk_context()) - mock_setup_hugepages.assert_called_once_with(self.ovs_dpdk.connection, 1024 * 1024) + 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') diff --git a/yardstick/tests/unit/benchmark/core/test_report.py b/yardstick/tests/unit/benchmark/core/test_report.py index 524302f92..3e80dcc45 100644 --- a/yardstick/tests/unit/benchmark/core/test_report.py +++ b/yardstick/tests/unit/benchmark/core/test_report.py @@ -1,5 +1,6 @@ ############################################################################## # Copyright (c) 2017 Rajesh Kudaka. +# Copyright (c) 2018 Intel Corporation. # # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 @@ -7,30 +8,93 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -# Unittest for yardstick.benchmark.core.report - -from __future__ import print_function - -from __future__ import absolute_import - +import mock import unittest import uuid -try: - from unittest import mock -except ImportError: - import mock - +from api.utils import influx from yardstick.benchmark.core import report from yardstick.cmd.commands import change_osloobj_to_paras -FAKE_YAML_NAME = 'fake_name' -FAKE_TASK_ID = str(uuid.uuid4()) -FAKE_DB_FIELDKEYS = [{'fieldKey': 'fake_key'}] -FAKE_TIME = '0000-00-00T00:00:00.000000Z' -FAKE_DB_TASK = [{'fake_key': 0.000, 'time': FAKE_TIME}] -FAKE_TIMESTAMP = ['fake_time'] -DUMMY_TASK_ID = 'aaaaaa-aaaaaaaa-aaaaaaaaaa-aaaaaa' +GOOD_YAML_NAME = 'fake_name' +GOOD_TASK_ID = str(uuid.uuid4()) +GOOD_DB_FIELDKEYS = [{'fieldKey': 'fake_key'}] +GOOD_DB_TASK = [{ + 'fake_key': 0.000, + 'time': '0000-00-00T00:00:00.000000Z', + }] +GOOD_TIMESTAMP = ['00:00:00.000000'] +BAD_YAML_NAME = 'F@KE_NAME' +BAD_TASK_ID = 'aaaaaa-aaaaaaaa-aaaaaaaaaa-aaaaaa' + + +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 = [ + {'data': [0, ], 'name': 'tg__0.DropPackets'}, + {'data': [548, ], 'name': 'tg__0.LatencyAvg.5'}, + {'data': [1172, ], 'name': 'tg__0.LatencyAvg.6'}, + {'data': [1001, ], 'name': 'tg__0.LatencyMax.5'}, + {'data': [1468, ], 'name': 'tg__0.LatencyMax.6'}, + {'data': [18.11, ], 'name': 'tg__0.RxThroughput'}, + {'data': [18.11, ], 'name': 'tg__0.TxThroughput'}, + {'data': [0, ], 'name': 'tg__1.DropPackets'}, + {'data': [548, ], 'name': 'tg__1.LatencyAvg.5'}, + {'data': [1172, ], 'name': 'tg__1.LatencyAvg.6'}, + {'data': [1001, ], 'name': 'tg__1.LatencyMax.5'}, + {'data': [1468, ], 'name': 'tg__1.LatencyMax.6'}, + {'data': [18.1132084505, ], 'name': 'tg__1.RxThroughput'}, + {'data': [18.1157260383, ], 'name': 'tg__1.TxThroughput'}, + {'data': [9057888, ], 'name': 'vnf__0.curr_packets_in'}, + {'data': [0, ], 'name': 'vnf__0.packets_dropped'}, + {'data': [617825443, ], 'name': '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): @@ -38,37 +102,69 @@ class ReportTestCase(unittest.TestCase): def setUp(self): super(ReportTestCase, self).setUp() self.param = change_osloobj_to_paras({}) - self.param.yaml_name = [FAKE_YAML_NAME] - self.param.task_id = [FAKE_TASK_ID] + 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 self.assertRaisesRegexp(ValueError, "yaml*"): + self.rep._validate(BAD_YAML_NAME, GOOD_TASK_ID) + + def test__validate_invalid_task_id(self): + with self.assertRaisesRegexp(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 + self.assertRaisesRegexp(KeyError, "Test case", self.rep._get_fieldkeys) + + @mock.patch.object(influx, 'query') + def test__get_tasks(self, mock_query): + mock_query.return_value = GOOD_DB_TASK + self.rep.yaml_name = GOOD_YAML_NAME + self.rep.task_id = GOOD_TASK_ID + self.assertEqual(GOOD_DB_TASK, self.rep._get_tasks()) + + @mock.patch.object(influx, 'query') + def test__get_tasks_task_not_found(self, mock_query): + mock_query.return_value = [] + self.rep.yaml_name = GOOD_YAML_NAME + self.rep.task_id = GOOD_TASK_ID + self.assertRaisesRegexp(KeyError, "Task ID", self.rep._get_tasks) + @mock.patch.object(report.Report, '_get_tasks') @mock.patch.object(report.Report, '_get_fieldkeys') @mock.patch.object(report.Report, '_validate') - def test_generate_success(self, mock_valid, mock_keys, mock_tasks): - mock_tasks.return_value = FAKE_DB_TASK - mock_keys.return_value = FAKE_DB_FIELDKEYS + def test_generate(self, mock_valid, mock_keys, mock_tasks): + mock_tasks.return_value = GOOD_DB_TASK + mock_keys.return_value = GOOD_DB_FIELDKEYS self.rep.generate(self.param) - mock_valid.assert_called_once_with(FAKE_YAML_NAME, FAKE_TASK_ID) + mock_valid.assert_called_once_with(GOOD_YAML_NAME, GOOD_TASK_ID) mock_tasks.assert_called_once_with() mock_keys.assert_called_once_with() - - # pylint: disable=deprecated-method - def test_invalid_yaml_name(self): - self.assertRaisesRegexp(ValueError, "yaml*", self.rep._validate, - 'F@KE_NAME', FAKE_TASK_ID) - - # pylint: disable=deprecated-method - def test_invalid_task_id(self): - self.assertRaisesRegexp(ValueError, "task*", self.rep._validate, - FAKE_YAML_NAME, DUMMY_TASK_ID) - - @mock.patch('api.utils.influx.query') - def test_task_not_found(self, mock_query): - mock_query.return_value = [] - self.rep.yaml_name = FAKE_YAML_NAME - self.rep.task_id = FAKE_TASK_ID - # pylint: disable=deprecated-method - self.assertRaisesRegexp(KeyError, "Task ID", self.rep._get_fieldkeys) - self.assertRaisesRegexp(KeyError, "Task ID", self.rep._get_tasks) - # pylint: enable=deprecated-method + self.assertEqual(GOOD_TIMESTAMP, self.rep.Timestamp) 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 index 5e2578b1f..bf613ca52 100644 --- 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 @@ -673,7 +673,23 @@ class TestIxNextgen(unittest.TestCase): return_value='field_desc'): self.ixnet_gen._update_ipv4_priority('field_desc', priority) - self.assertEqual(self.ixnet_gen._set_priority_field.call_count, 0) + 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] @@ -818,13 +834,13 @@ class TestIxNextgen(unittest.TestCase): @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' + 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' + mock_ixnextgen_get_protocol_status.return_value = ['down', 'down'] result = self.ixnet_gen.is_protocols_running(['ethernet', 'ipv4']) self.assertFalse(result) 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 index 4524eb7e6..f17656328 100644 --- a/yardstick/tests/unit/network_services/traffic_profile/test_prox_binsearch.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_prox_binsearch.py @@ -21,6 +21,8 @@ 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() @@ -80,7 +82,7 @@ class TestProxBinSearchProfile(unittest.TestCase): # Result Samples inc theor_max result_tuple = {'Actual_throughput': 5e-07, - 'theor_max_throughput': 7.5e-07, + 'theor_max_throughput': self.THEOR_MAX_THROUGHPUT, 'PktSize': 200, 'Status': 'Result'} @@ -96,7 +98,7 @@ class TestProxBinSearchProfile(unittest.TestCase): "PktSize": 200, "RxThroughput": 7.5e-07, "Throughput": 7.5e-07, - "TxThroughput": 0.00012340000000000002, + "TxThroughput": self.THEOR_MAX_THROUGHPUT, "Status": 'Success'} calls = profile.queue.put(success_result_tuple) @@ -222,6 +224,7 @@ class TestProxBinSearchProfile(unittest.TestCase): raise RuntimeError(' '.join([str(args), str(runs)])) if args[2] > 75.0: return fail_tuple, {} + return success_tuple, {} tp_config = { @@ -258,7 +261,7 @@ class TestProxBinSearchProfile(unittest.TestCase): # Result Samples inc theor_max result_tuple = {'Actual_throughput': 5e-07, - 'theor_max_throughput': 7.5e-07, + 'theor_max_throughput': self.THEOR_MAX_THROUGHPUT, 'PktSize': 200, "Status": 'Result'} @@ -274,7 +277,7 @@ class TestProxBinSearchProfile(unittest.TestCase): "PktSize": 200, "RxThroughput": 7.5e-07, "Throughput": 7.5e-07, - "TxThroughput": 0.00012340000000000002, + "TxThroughput": self.THEOR_MAX_THROUGHPUT, "Status": 'Success'} calls = profile.queue.put(success_result_tuple) 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 index 894b16e13..6d1d8c6a1 100644 --- 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 @@ -648,6 +648,82 @@ class TestProxSocketHelper(unittest.TestCase): 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)), @@ -1568,8 +1644,83 @@ class TestProxResourceHelper(unittest.TestCase): 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) @@ -1577,7 +1728,8 @@ class TestProxResourceHelper(unittest.TestCase): queue.empty.return_value = False queue.get.return_value = {'a': 789} - expected = {'z': 123, 'a': 789, 'collect_stats': {'core': 543}} + expected = {'z': 123, 'a': 789, + 'collect_stats': {'core': 543}} result = helper.collect_kpi() self.assertDictEqual(result, expected) 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 index a7e61da0f..935d3fa30 100644 --- 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 @@ -329,13 +329,27 @@ class TestProxTrafficGen(unittest.TestCase): } 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': {}, + 'collect_stats': {'live_stats': {'xe0': {'in_packets': 1, 'out_packets': 2}}}, 'physical_node': 'mock_node' } - self.assertEqual(prox_traffic_gen.collect_kpi(), expected) - + 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( 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 index 741201fdb..e22398847 100644 --- 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 @@ -17,8 +17,10 @@ import os import mock import six import unittest +import ipaddress 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 @@ -49,12 +51,43 @@ class TestIxiaResourceHelper(unittest.TestCase): 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() @@ -63,6 +96,7 @@ class TestIxiaResourceHelper(unittest.TestCase): 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'), \ @@ -261,8 +295,8 @@ class TestIXIATrafficGen(unittest.TestCase): ssh_mock.execute = \ mock.Mock(return_value=(0, "", "")) ssh.from_node.return_value = ssh_mock - ixnet_traffic_gen = tg_rfc2544_ixia.IxiaTrafficGen(NAME, vnfd, - 'task_id') + ixnet_traffic_gen = tg_rfc2544_ixia.IxiaTrafficGen( + NAME, vnfd, 'task_id', 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() @@ -360,6 +394,7 @@ class TestIXIATrafficGen(unittest.TestCase): 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': { @@ -392,3 +427,342 @@ class TestIXIATrafficGen(unittest.TestCase): self.assertIsNone(result) _traffic_runner() + + +class TestIxiaBasicScenario(unittest.TestCase): + + 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_apply_config(self): + self.assertIsNone(self.scenario.apply_config()) + + def test_create_traffic_model(self): + self.mock_IxNextgen.get_vports.return_value = [1, 2, 3, 4] + self.scenario.create_traffic_model() + self.scenario.client.get_vports.assert_called_once() + self.scenario.client.create_traffic_model.assert_called_once_with([1, 3], [2, 4]) + + def test_run_protocols(self): + self.assertIsNone(self.scenario.run_protocols()) + + def test_stop_protocols(self): + self.assertIsNone(self.scenario.stop_protocols()) + + +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'] + } + } + + 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() + + def test_create_traffic_model(self): + self.scenario._access_topologies = 'access' + self.scenario._core_topologies = 'core' + self.scenario.create_traffic_model() + self.scenario.client.create_ipv4_traffic_model.assert_called_once_with( + 'access', 'core') + + 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"]) + ]) |