diff options
Diffstat (limited to 'yardstick')
21 files changed, 885 insertions, 454 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 26a37d0c0..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), @@ -414,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/pipeline.py b/yardstick/network_services/pipeline.py index 7155480d4..4fbe7967f 100644 --- a/yardstick/network_services/pipeline.py +++ b/yardstick/network_services/pipeline.py @@ -22,7 +22,7 @@ from yardstick.common import utils FIREWALL_ADD_DEFAULT = "p {0} firewall add default 1"
FIREWALL_ADD_PRIO = """\
-p {0} firewall add priority 1 ipv4 {1} 24 0.0.0.0 0 0 65535 0 65535 6 0xFF port 0"""
+p {0} firewall add priority 1 ipv4 {1} 24 0.0.0.0 0 0 65535 0 65535 17 0xFF port 0"""
FLOW_ADD_QINQ_RULES = """\
p {0} flow add qinq 128 512 port 0 id 1
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 b2c5a98bc..673344f4e 100644 --- a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py @@ -180,7 +180,7 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): """No actions/rules (flows) by default""" return None - def _build_pipeline_kwargs(self): + def _build_pipeline_kwargs(self, cfg_file=None): tool_path = self.ssh_helper.provision_tool(tool_file=self.APP_NAME) # count the number of actual ports in the list of pairs # remove duplicate ports @@ -200,7 +200,7 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): hwlb = ' --hwlb %s' % worker_threads self.pipeline_kwargs = { - 'cfg_file': self.CFG_CONFIG, + 'cfg_file': cfg_file if cfg_file else self.CFG_CONFIG, 'script': self.CFG_SCRIPT, 'port_mask_hex': ports_mask_hex, 'tool_path': tool_path, diff --git a/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py b/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py index b7cf8b35e..349ef7888 100644 --- a/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py @@ -17,13 +17,11 @@ from __future__ import absolute_import from __future__ import print_function -import os import logging import re import posixpath -from six.moves import configparser, zip - +from yardstick.common import utils from yardstick.common.process import check_if_process_failed from yardstick.network_services.helpers.samplevnf_helper import PortPairs from yardstick.network_services.pipeline import PipelineRules @@ -43,15 +41,6 @@ Pkts in:\\s(\\d+)\r\n\ class ConfigCreate(object): - @staticmethod - def vpe_tmq(config, index): - tm_q = 'TM{0}'.format(index) - config.add_section(tm_q) - config.set(tm_q, 'burst_read', '24') - config.set(tm_q, 'burst_write', '32') - config.set(tm_q, 'cfg', '/tmp/full_tm_profile_10G.cfg') - return config - def __init__(self, vnfd_helper, socket): super(ConfigCreate, self).__init__() self.sw_q = -1 @@ -64,141 +53,6 @@ class ConfigCreate(object): self.socket = socket self._dpdk_port_to_link_id_map = None - @property - def dpdk_port_to_link_id_map(self): - # we need interface name -> DPDK port num (PMD ID) -> LINK ID - # LINK ID -> PMD ID is governed by the port mask - # LINK instances are created implicitly based on the PORT_MASK application startup - # argument. LINK0 is the first port enabled in the PORT_MASK, port 1 is the next one, - # etc. The LINK ID is different than the DPDK PMD-level NIC port ID, which is the actual - # position in the bitmask mentioned above. For example, if bit 5 is the first bit set - # in the bitmask, then LINK0 is having the PMD ID of 5. This mechanism creates a - # contiguous LINK ID space and isolates the configuration file against changes in the - # board PCIe slots where NICs are plugged in. - if self._dpdk_port_to_link_id_map is None: - self._dpdk_port_to_link_id_map = {} - for link_id, port_name in enumerate(sorted(self.vnfd_helper.port_pairs.all_ports, - key=self.vnfd_helper.port_num)): - self._dpdk_port_to_link_id_map[port_name] = link_id - return self._dpdk_port_to_link_id_map - - def vpe_initialize(self, config): - config.add_section('EAL') - config.set('EAL', 'log_level', '0') - - config.add_section('PIPELINE0') - config.set('PIPELINE0', 'type', 'MASTER') - config.set('PIPELINE0', 'core', 's%sC0' % self.socket) - - config.add_section('MEMPOOL0') - config.set('MEMPOOL0', 'pool_size', '256K') - - config.add_section('MEMPOOL1') - config.set('MEMPOOL1', 'pool_size', '2M') - return config - - def vpe_rxq(self, config): - for port in self.downlink_ports: - new_section = 'RXQ{0}.0'.format(self.dpdk_port_to_link_id_map[port]) - config.add_section(new_section) - config.set(new_section, 'mempool', 'MEMPOOL1') - - return config - - def get_sink_swq(self, parser, pipeline, k, index): - sink = "" - pktq = parser.get(pipeline, k) - if "SINK" in pktq: - self.sink_q += 1 - sink = " SINK{0}".format(self.sink_q) - if "TM" in pktq: - sink = " TM{0}".format(index) - pktq = "SWQ{0}{1}".format(self.sw_q, sink) - return pktq - - def vpe_upstream(self, vnf_cfg, index=0): # pragma: no cover - # NOTE(ralonsoh): this function must be covered in UTs. - parser = configparser.ConfigParser() - parser.read(os.path.join(vnf_cfg, 'vpe_upstream')) - - for pipeline in parser.sections(): - for k, v in parser.items(pipeline): - if k == "pktq_in": - if "RXQ" in v: - port = self.dpdk_port_to_link_id_map[self.uplink_ports[index]] - value = "RXQ{0}.0".format(port) - else: - value = self.get_sink_swq(parser, pipeline, k, index) - - parser.set(pipeline, k, value) - - elif k == "pktq_out": - if "TXQ" in v: - port = self.dpdk_port_to_link_id_map[self.downlink_ports[index]] - value = "TXQ{0}.0".format(port) - else: - self.sw_q += 1 - value = self.get_sink_swq(parser, pipeline, k, index) - - parser.set(pipeline, k, value) - - new_pipeline = 'PIPELINE{0}'.format(self.n_pipeline) - if new_pipeline != pipeline: - parser._sections[new_pipeline] = parser._sections[pipeline] - parser._sections.pop(pipeline) - self.n_pipeline += 1 - return parser - - def vpe_downstream(self, vnf_cfg, index): # pragma: no cover - # NOTE(ralonsoh): this function must be covered in UTs. - parser = configparser.ConfigParser() - parser.read(os.path.join(vnf_cfg, 'vpe_downstream')) - for pipeline in parser.sections(): - for k, v in parser.items(pipeline): - - if k == "pktq_in": - port = self.dpdk_port_to_link_id_map[self.downlink_ports[index]] - if "RXQ" not in v: - value = self.get_sink_swq(parser, pipeline, k, index) - elif "TM" in v: - value = "RXQ{0}.0 TM{1}".format(port, index) - else: - value = "RXQ{0}.0".format(port) - - parser.set(pipeline, k, value) - - if k == "pktq_out": - port = self.dpdk_port_to_link_id_map[self.uplink_ports[index]] - if "TXQ" not in v: - self.sw_q += 1 - value = self.get_sink_swq(parser, pipeline, k, index) - elif "TM" in v: - value = "TXQ{0}.0 TM{1}".format(port, index) - else: - value = "TXQ{0}.0".format(port) - - parser.set(pipeline, k, value) - - new_pipeline = 'PIPELINE{0}'.format(self.n_pipeline) - if new_pipeline != pipeline: - parser._sections[new_pipeline] = parser._sections[pipeline] - parser._sections.pop(pipeline) - self.n_pipeline += 1 - return parser - - def create_vpe_config(self, vnf_cfg): - config = configparser.ConfigParser() - vpe_cfg = os.path.join("/tmp/vpe_config") - with open(vpe_cfg, 'w') as cfg_file: - config = self.vpe_initialize(config) - config = self.vpe_rxq(config) - config.write(cfg_file) - for index, _ in enumerate(self.uplink_ports): - config = self.vpe_upstream(vnf_cfg, index) - config.write(cfg_file) - config = self.vpe_downstream(vnf_cfg, index) - config = self.vpe_tmq(config, index) - config.write(cfg_file) def generate_vpe_script(self, interfaces): rules = PipelineRules(pipeline_id=1) @@ -231,16 +85,10 @@ class ConfigCreate(object): return rules.get_string() - def generate_tm_cfg(self, vnf_cfg): - vnf_cfg = os.path.join(vnf_cfg, "full_tm_profile_10G.cfg") - if os.path.exists(vnf_cfg): - return open(vnf_cfg).read() - class VpeApproxSetupEnvHelper(DpdkVnfSetupEnvHelper): APP_NAME = 'vPE_vnf' - CFG_CONFIG = "/tmp/vpe_config" CFG_SCRIPT = "/tmp/vpe_script" TM_CONFIG = "/tmp/full_tm_profile_10G.cfg" CORES = ['0', '1', '2', '3', '4', '5'] @@ -253,33 +101,44 @@ class VpeApproxSetupEnvHelper(DpdkVnfSetupEnvHelper): self.all_ports = self._port_pairs.all_ports def build_config(self): + vnf_cfg = self.scenario_helper.vnf_cfg + task_path = self.scenario_helper.task_path + action_bulk_file = vnf_cfg.get('action_bulk_file', '/tmp/action_bulk_512.txt') + full_tm_profile_file = vnf_cfg.get('full_tm_profile_file', '/tmp/full_tm_profile_10G.cfg') + config_file = vnf_cfg.get('file', '/tmp/vpe_config') vpe_vars = { "bin_path": self.ssh_helper.bin_path, "socket": self.socket, } - self._build_vnf_ports() vpe_conf = ConfigCreate(self.vnfd_helper, self.socket) - vpe_conf.create_vpe_config(self.scenario_helper.vnf_cfg) - config_basename = posixpath.basename(self.CFG_CONFIG) + config_basename = posixpath.basename(config_file) script_basename = posixpath.basename(self.CFG_SCRIPT) - tm_basename = posixpath.basename(self.TM_CONFIG) - with open(self.CFG_CONFIG) as handle: - vpe_config = handle.read() - self.ssh_helper.upload_config_file(config_basename, vpe_config.format(**vpe_vars)) + with utils.open_relative_file(action_bulk_file, task_path) as handle: + action_bulk = handle.read() + with utils.open_relative_file(full_tm_profile_file, task_path) as handle: + full_tm_profile = handle.read() + + with utils.open_relative_file(config_file, task_path) as handle: + vpe_config = handle.read() + + # vpe_script needs to be autogenerated vpe_script = vpe_conf.generate_vpe_script(self.vnfd_helper.interfaces) + # upload the 4 config files to the target server + self.ssh_helper.upload_config_file(config_basename, vpe_config.format(**vpe_vars)) self.ssh_helper.upload_config_file(script_basename, vpe_script.format(**vpe_vars)) - - tm_config = vpe_conf.generate_tm_cfg(self.scenario_helper.vnf_cfg) - self.ssh_helper.upload_config_file(tm_basename, tm_config) + self.ssh_helper.upload_config_file(posixpath.basename(action_bulk_file), + action_bulk.format(**vpe_vars)) + self.ssh_helper.upload_config_file(posixpath.basename(full_tm_profile_file), + full_tm_profile.format(**vpe_vars)) LOG.info("Provision and start the %s", self.APP_NAME) - LOG.info(self.CFG_CONFIG) + LOG.info(config_file) LOG.info(self.CFG_SCRIPT) - self._build_pipeline_kwargs() + self._build_pipeline_kwargs(cfg_file='/tmp/' + config_basename) return self.PIPELINE_COMMAND.format(**self.pipeline_kwargs) 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 ab9e632be..413bb68b7 100644 --- a/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py @@ -437,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/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_vpe_vnf.py b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpe_vnf.py index 7b937dfb5..8d49cb3f4 100644 --- a/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpe_vnf.py +++ b/yardstick/tests/unit/network_services/vnf_generic/vnf/test_vpe_vnf.py @@ -16,7 +16,6 @@ from multiprocessing import Process, Queue import time import mock -from six.moves import configparser import unittest from yardstick.benchmark.contexts import base as ctx_base @@ -147,48 +146,6 @@ class TestConfigCreate(unittest.TestCase): self.assertEqual(config_create.downlink_ports, ['xe1']) self.assertEqual(config_create.socket, 2) - def test_dpdk_port_to_link_id(self): - vnfd_helper = vnf_base.VnfdHelper(self.VNFD_0) - config_create = vpe_vnf.ConfigCreate(vnfd_helper, 2) - self.assertEqual(config_create.dpdk_port_to_link_id_map, {'xe0': 0, 'xe1': 1}) - - def test_vpe_initialize(self): - vnfd_helper = vnf_base.VnfdHelper(self.VNFD_0) - config_create = vpe_vnf.ConfigCreate(vnfd_helper, 2) - config = configparser.ConfigParser() - config_create.vpe_initialize(config) - self.assertEqual(config.get('EAL', 'log_level'), '0') - self.assertEqual(config.get('PIPELINE0', 'type'), 'MASTER') - self.assertEqual(config.get('PIPELINE0', 'core'), 's2C0') - self.assertEqual(config.get('MEMPOOL0', 'pool_size'), '256K') - self.assertEqual(config.get('MEMPOOL1', 'pool_size'), '2M') - - def test_vpe_rxq(self): - vnfd_helper = vnf_base.VnfdHelper(self.VNFD_0) - config_create = vpe_vnf.ConfigCreate(vnfd_helper, 2) - config = configparser.ConfigParser() - config_create.downlink_ports = ['xe0'] - config_create.vpe_rxq(config) - self.assertEqual(config.get('RXQ0.0', 'mempool'), 'MEMPOOL1') - - def test_get_sink_swq(self): - vnfd_helper = vnf_base.VnfdHelper(self.VNFD_0) - config_create = vpe_vnf.ConfigCreate(vnfd_helper, 2) - config = configparser.ConfigParser() - config.add_section('PIPELINE0') - config.set('PIPELINE0', 'key1', 'value1') - config.set('PIPELINE0', 'key2', 'value2 SINK') - config.set('PIPELINE0', 'key3', 'TM value3') - config.set('PIPELINE0', 'key4', 'value4') - config.set('PIPELINE0', 'key5', 'the SINK value5') - - self.assertEqual(config_create.get_sink_swq(config, 'PIPELINE0', 'key1', 5), 'SWQ-1') - self.assertEqual(config_create.get_sink_swq(config, 'PIPELINE0', 'key2', 5), 'SWQ-1 SINK0') - self.assertEqual(config_create.get_sink_swq(config, 'PIPELINE0', 'key3', 5), 'SWQ-1 TM5') - config_create.sw_q += 1 - self.assertEqual(config_create.get_sink_swq(config, 'PIPELINE0', 'key4', 5), 'SWQ0') - self.assertEqual(config_create.get_sink_swq(config, 'PIPELINE0', 'key5', 5), 'SWQ0 SINK1') - def test_generate_vpe_script(self): vnfd_helper = vnf_base.VnfdHelper(self.VNFD_0) vpe_config_vnf = vpe_vnf.ConfigCreate(vnfd_helper, 2) @@ -214,36 +171,6 @@ class TestConfigCreate(unittest.TestCase): self.assertIsInstance(result, str) self.assertNotEqual(result, '') - def test_create_vpe_config(self): - vnfd_helper = vnf_base.VnfdHelper(self.VNFD_0) - config_create = vpe_vnf.ConfigCreate(vnfd_helper, 23) - config_create.uplink_ports = ['xe1'] - with mock.patch.object(config_create, 'vpe_upstream') as mock_up, \ - mock.patch.object(config_create, 'vpe_downstream') as \ - mock_down, \ - mock.patch.object(config_create, 'vpe_tmq') as mock_tmq, \ - mock.patch.object(config_create, 'vpe_initialize') as \ - mock_ini, \ - mock.patch.object(config_create, 'vpe_rxq') as mock_rxq: - mock_ini_obj = mock.Mock() - mock_rxq_obj = mock.Mock() - mock_up_obj = mock.Mock() - mock_down_obj = mock.Mock() - mock_tmq_obj = mock.Mock() - mock_ini.return_value = mock_ini_obj - mock_rxq.return_value = mock_rxq_obj - mock_up.return_value = mock_up_obj - mock_down.return_value = mock_down_obj - mock_tmq.return_value = mock_tmq_obj - config_create.create_vpe_config('fake_config_file') - - mock_rxq.assert_called_once_with(mock_ini_obj) - mock_up.assert_called_once_with('fake_config_file', 0) - mock_down.assert_called_once_with('fake_config_file', 0) - mock_tmq.assert_called_once_with(mock_down_obj, 0) - mock_up_obj.write.assert_called_once() - mock_tmq_obj.write.assert_called_once() - class TestVpeApproxVnf(unittest.TestCase): |