diff options
Diffstat (limited to 'yardstick')
39 files changed, 1705 insertions, 436 deletions
diff --git a/yardstick/benchmark/contexts/heat.py b/yardstick/benchmark/contexts/heat.py index f118ffc32..c3c5451bd 100644 --- a/yardstick/benchmark/contexts/heat.py +++ b/yardstick/benchmark/contexts/heat.py @@ -59,6 +59,7 @@ class HeatContext(Context): self.server_groups = [] self.keypair_name = None self.secgroup_name = None + self.security_group = None self._server_map = {} self.attrs = {} self._image = None @@ -118,8 +119,11 @@ class HeatContext(Context): return self.keypair_name = h_join(self.name, "key") + self.secgroup_name = h_join(self.name, "secgroup") + self.security_group = attrs.get("security_group") + self._image = attrs.get("image") self._flavor = attrs.get("flavor") @@ -185,7 +189,7 @@ class HeatContext(Context): self.flavors.add(flavor) template.add_keypair(self.keypair_name, self.name) - template.add_security_group(self.secgroup_name) + template.add_security_group(self.secgroup_name, self.security_group) for network in self.networks.values(): # Using existing network diff --git a/yardstick/benchmark/contexts/standalone/model.py b/yardstick/benchmark/contexts/standalone/model.py index ecddcbbe0..fa78fc1eb 100644 --- a/yardstick/benchmark/contexts/standalone/model.py +++ b/yardstick/benchmark/contexts/standalone/model.py @@ -89,6 +89,30 @@ VM_TEMPLATE = """ </devices> </domain> """ + +USER_DATA_TEMPLATE = """ +cat > {user_file} <<EOF +#cloud-config +preserve_hostname: false +hostname: {host} +users: +{user_config} +EOF +""" + +NETWORK_DATA_TEMPLATE = """ +cat > {network_file} <<EOF +#cloud-config +version: 2 +ethernets: + ens3: + match: + mac_address: {mac_address} + addresses: + - {ip_address} +EOF +""" + WAIT_FOR_BOOT = 30 @@ -268,7 +292,7 @@ class Libvirt(object): return vm_image @classmethod - def build_vm_xml(cls, connection, flavor, vm_name, index): + def build_vm_xml(cls, connection, flavor, vm_name, index, cdrom_img): """Build the XML from the configuration parameters""" memory = flavor.get('ram', '4096') extra_spec = flavor.get('extra_specs', {}) @@ -293,6 +317,9 @@ class Libvirt(object): socket=socket, threads=threads, vm_image=image, cpuset=cpuset, cputune=cputune) + # Add CD-ROM device + vm_xml = Libvirt.add_cdrom(cdrom_img, vm_xml) + return vm_xml, mac @staticmethod @@ -320,6 +347,75 @@ class Libvirt(object): et = ET.ElementTree(element=root) et.write(file_name, encoding='utf-8', method='xml') + @classmethod + def add_cdrom(cls, file_path, xml_str): + """Add a CD-ROM disk XML node in 'devices' node + + <devices> + <disk type='file' device='cdrom'> + <driver name='qemu' type='raw'/> + <source file='/var/lib/libvirt/images/data.img'/> + <target dev='hdb'/> + <readonly/> + </disk> + ... + </devices> + """ + + root = ET.fromstring(xml_str) + device = root.find('devices') + + disk = ET.SubElement(device, 'disk') + disk.set('type', 'file') + disk.set('device', 'cdrom') + + driver = ET.SubElement(disk, 'driver') + driver.set('name', 'qemu') + driver.set('type', 'raw') + + source = ET.SubElement(disk, 'source') + source.set('file', file_path) + + target = ET.SubElement(disk, 'target') + target.set('dev', 'hdb') + + ET.SubElement(disk, 'readonly') + return ET.tostring(root) + + @staticmethod + def gen_cdrom_image(connection, file_path, vm_name, vm_user, key_filename, mac, ip): + """Generate ISO image for CD-ROM """ + + user_config = [" - name: {user_name}", + " ssh_authorized_keys:", + " - {pub_key_str}"] + if vm_user != "root": + user_config.append(" sudo: ALL=(ALL) NOPASSWD:ALL") + + meta_data = "/tmp/meta-data" + user_data = "/tmp/user-data" + network_data = "/tmp/network-config" + with open(".".join([key_filename, "pub"]), "r") as pub_key_file: + pub_key_str = pub_key_file.read().rstrip() + user_conf = os.linesep.join(user_config).format(pub_key_str=pub_key_str, user_name=vm_user) + + cmd_lst = [ + "touch %s" % meta_data, + USER_DATA_TEMPLATE.format(user_file=user_data, host=vm_name, user_config=user_conf), + NETWORK_DATA_TEMPLATE.format(network_file=network_data, mac_address=mac, + ip_address=ip), + "genisoimage -output {0} -volid cidata -joliet -r {1} {2} {3}".format(file_path, + meta_data, + user_data, + network_data), + "rm {0} {1} {2}".format(meta_data, user_data, network_data), + ] + for cmd in cmd_lst: + LOG.info(cmd) + status, _, error = connection.execute(cmd) + if status: + raise exceptions.LibvirtQemuImageCreateError(error=error) + class StandaloneContextHelper(object): """ This class handles all the common code for standalone @@ -331,7 +427,7 @@ class StandaloneContextHelper(object): @staticmethod def install_req_libs(connection, extra_pkgs=None): extra_pkgs = extra_pkgs or [] - pkgs = ["qemu-kvm", "libvirt-bin", "bridge-utils", "numactl", "fping"] + pkgs = ["qemu-kvm", "libvirt-bin", "bridge-utils", "numactl", "fping", "genisoimage"] pkgs.extend(extra_pkgs) cmd_template = "dpkg-query -W --showformat='${Status}\\n' \"%s\"|grep 'ok installed'" for pkg in pkgs: @@ -457,6 +553,27 @@ class StandaloneContextHelper(object): node["ip"] = ip return nodes + @classmethod + def check_update_key(cls, connection, node, vm_name, id_name, cdrom_img, mac): + # Generate public/private keys if private key file is not provided + user_name = node.get('user') + if not user_name: + node['user'] = 'root' + user_name = node.get('user') + if not node.get('key_filename'): + key_filename = ''.join( + [constants.YARDSTICK_ROOT_PATH, + 'yardstick/resources/files/yardstick_key-', + id_name]) + ssh.SSH.gen_keys(key_filename) + node['key_filename'] = key_filename + # Update image with public key + key_filename = node.get('key_filename') + ip_netmask = "{0}/{1}".format(node.get('ip'), node.get('netmask')) + Libvirt.gen_cdrom_image(connection, cdrom_img, vm_name, user_name, key_filename, mac, + ip_netmask) + return node + class Server(object): """ This class handles geting vnf nodes @@ -469,7 +586,7 @@ class Server(object): for key, vfs in vnf["network_ports"].items(): if key == "mgmt": - mgmtip = str(IPNetwork(vfs['cidr']).ip) + mgmt_cidr = IPNetwork(vfs['cidr']) continue vf = ports[vfs[0]] @@ -486,14 +603,15 @@ class Server(object): }) index = index + 1 - return mgmtip, interfaces + return mgmt_cidr, interfaces @classmethod def generate_vnf_instance(cls, flavor, ports, ip, key, vnf, mac): - mgmtip, interfaces = cls.build_vnf_interfaces(vnf, ports) + mgmt_cidr, interfaces = cls.build_vnf_interfaces(vnf, ports) result = { - "ip": mgmtip, + "ip": str(mgmt_cidr.ip), + "netmask": str(mgmt_cidr.netmask), "mac": mac, "host": ip, "user": flavor.get('user', 'root'), diff --git a/yardstick/benchmark/contexts/standalone/ovs_dpdk.py b/yardstick/benchmark/contexts/standalone/ovs_dpdk.py index 88ad598c3..73311f0c2 100644 --- a/yardstick/benchmark/contexts/standalone/ovs_dpdk.py +++ b/yardstick/benchmark/contexts/standalone/ovs_dpdk.py @@ -394,13 +394,14 @@ class OvsDpdkContext(base.Context): for index, (key, vnf) in enumerate(collections.OrderedDict( self.servers).items()): cfg = '/tmp/vm_ovs_%d.xml' % index - vm_name = "vm_%d" % index + vm_name = "vm-%d" % index + cdrom_img = "/var/lib/libvirt/images/cdrom-%d.img" % index # 1. Check and delete VM if already exists model.Libvirt.check_if_vm_exists_and_delete(vm_name, self.connection) xml_str, mac = model.Libvirt.build_vm_xml( - self.connection, self.vm_flavor, vm_name, index) + self.connection, self.vm_flavor, vm_name, index, cdrom_img) # 2: Cleanup already available VMs for vfs in [vfs for vfs_name, vfs in vnf["network_ports"].items() @@ -411,16 +412,25 @@ class OvsDpdkContext(base.Context): model.Libvirt.write_file(cfg, xml_str) self.connection.put(cfg, cfg) + node = self.vnf_node.generate_vnf_instance(self.vm_flavor, + self.networks, + self.host_mgmt.get('ip'), + key, vnf, mac) + # Generate public/private keys if password or private key file is not provided + node = model.StandaloneContextHelper.check_update_key(self.connection, + node, + vm_name, + self.name, + cdrom_img, + mac) + + # store vnf node details + nodes.append(node) + # NOTE: launch through libvirt LOG.info("virsh create ...") model.Libvirt.virsh_create_vm(self.connection, cfg) self.vm_names.append(vm_name) - # build vnf node details - nodes.append(self.vnf_node.generate_vnf_instance(self.vm_flavor, - self.networks, - self.host_mgmt.get('ip'), - key, vnf, mac)) - return nodes diff --git a/yardstick/benchmark/contexts/standalone/sriov.py b/yardstick/benchmark/contexts/standalone/sriov.py index 3da12a9a8..f1b67a2da 100644 --- a/yardstick/benchmark/contexts/standalone/sriov.py +++ b/yardstick/benchmark/contexts/standalone/sriov.py @@ -225,13 +225,14 @@ class SriovContext(base.Context): for index, (key, vnf) in enumerate(collections.OrderedDict( self.servers).items()): cfg = '/tmp/vm_sriov_%s.xml' % str(index) - vm_name = "vm_%s" % str(index) + vm_name = "vm-%s" % str(index) + cdrom_img = "/var/lib/libvirt/images/cdrom-%d.img" % index # 1. Check and delete VM if already exists model.Libvirt.check_if_vm_exists_and_delete(vm_name, self.connection) xml_str, mac = model.Libvirt.build_vm_xml( - self.connection, self.vm_flavor, vm_name, index) + self.connection, self.vm_flavor, vm_name, index, cdrom_img) # 2: Cleanup already available VMs network_ports = collections.OrderedDict( @@ -243,17 +244,27 @@ class SriovContext(base.Context): model.Libvirt.write_file(cfg, xml_str) self.connection.put(cfg, cfg) + node = self.vnf_node.generate_vnf_instance(self.vm_flavor, + self.networks, + self.host_mgmt.get('ip'), + key, vnf, mac) + # Generate public/private keys if password or private key file is not provided + node = model.StandaloneContextHelper.check_update_key(self.connection, + node, + vm_name, + self.name, + cdrom_img, + mac) + + # store vnf node details + nodes.append(node) + # NOTE: launch through libvirt LOG.info("virsh create ...") model.Libvirt.virsh_create_vm(self.connection, cfg) self.vm_names.append(vm_name) - # build vnf node details - nodes.append(self.vnf_node.generate_vnf_instance( - self.vm_flavor, self.networks, self.host_mgmt.get('ip'), - key, vnf, mac)) - return nodes def _get_vf_data(self, value, vfmac, pfif): diff --git a/yardstick/benchmark/scenarios/availability/attacker/attacker_baremetal.py b/yardstick/benchmark/scenarios/availability/attacker/attacker_baremetal.py index 53abd586b..4c79a4931 100644 --- a/yardstick/benchmark/scenarios/availability/attacker/attacker_baremetal.py +++ b/yardstick/benchmark/scenarios/availability/attacker/attacker_baremetal.py @@ -34,6 +34,8 @@ class BaremetalAttacker(BaseAttacker): __attacker_type__ = 'bare-metal-down' def setup(self): + # baremetal down need to recover even sla pass + self.mandatory = True LOG.debug("config:%s context:%s", self._config, self._context) host = self._context.get(self._config['host'], None) diff --git a/yardstick/benchmark/scenarios/availability/attacker/baseattacker.py b/yardstick/benchmark/scenarios/availability/attacker/baseattacker.py index d67a16b98..7871cc918 100644 --- a/yardstick/benchmark/scenarios/availability/attacker/baseattacker.py +++ b/yardstick/benchmark/scenarios/availability/attacker/baseattacker.py @@ -63,6 +63,7 @@ class BaseAttacker(object): self.data = {} self.setup_done = False self.intermediate_variables = {} + self.mandatory = False @staticmethod def get_attacker_cls(attacker_cfg): diff --git a/yardstick/benchmark/scenarios/availability/ha_tools/fault_process_kill.bash b/yardstick/benchmark/scenarios/availability/ha_tools/fault_process_kill.bash index d34ce9338..cda469cf9 100755 --- a/yardstick/benchmark/scenarios/availability/ha_tools/fault_process_kill.bash +++ b/yardstick/benchmark/scenarios/availability/ha_tools/fault_process_kill.bash @@ -16,7 +16,7 @@ set -e process_name=$1 if [ "$process_name" = "keystone" ]; then - for pid in $(ps aux | grep "keystone" | grep -iv heartbeat | grep -iv monitor | grep -v grep | grep -v /bin/sh | awk '{print $2}'); \ + for pid in $(ps aux | grep "keystone" | grep -iv monitor | grep -v grep | grep -v /bin/sh | awk '{print $2}'); \ do kill -9 "${pid}" done @@ -26,7 +26,7 @@ elif [ "$process_name" = "haproxy" ]; then kill -9 "${pid}" done else - for pid in $(pgrep -fa [^-_a-zA-Z0-9]${process_name} | grep -iv heartbeat | awk '{print $1}'); + for pid in $(pgrep -fa [^-_a-zA-Z0-9]${process_name} | awk '{print $1}'); do kill -9 "${pid}" done diff --git a/yardstick/benchmark/scenarios/availability/serviceha.py b/yardstick/benchmark/scenarios/availability/serviceha.py index 7f976fdbc..fdfe7cbbe 100755 --- a/yardstick/benchmark/scenarios/availability/serviceha.py +++ b/yardstick/benchmark/scenarios/availability/serviceha.py @@ -88,9 +88,9 @@ class ServiceHA(base.Scenario): def teardown(self): """scenario teardown""" - # only recover when sla not pass - if not self.sla_pass: - for attacker in self.attackers: + # recover when mandatory or sla not pass + for attacker in self.attackers: + if attacker.mandatory or not self.sla_pass: attacker.recover() diff --git a/yardstick/benchmark/scenarios/networking/vnf_generic.py b/yardstick/benchmark/scenarios/networking/vnf_generic.py index 10f10d4e6..6d68c5e64 100644 --- a/yardstick/benchmark/scenarios/networking/vnf_generic.py +++ b/yardstick/benchmark/scenarios/networking/vnf_generic.py @@ -117,8 +117,15 @@ class NetworkServiceTestCase(scenario_base.Scenario): for index, dst_port in enumerate(fflow.get("dst_port", [])): flow["dst_port_{}".format(index)] = dst_port - flow["count"] = fflow["count"] - flow["seed"] = fflow["seed"] + if "count" in fflow: + flow["count"] = fflow["count"] + + if "srcseed" in fflow: + flow["srcseed"] = fflow["srcseed"] + + if "dstseed" in fflow: + flow["dstseed"] = fflow["dstseed"] + except KeyError: flow = {} return {"flow": flow} diff --git a/yardstick/benchmark/scenarios/storage/storperf.py b/yardstick/benchmark/scenarios/storage/storperf.py index f0b2361d6..8093cd2d2 100644 --- a/yardstick/benchmark/scenarios/storage/storperf.py +++ b/yardstick/benchmark/scenarios/storage/storperf.py @@ -8,15 +8,16 @@ ############################################################################## from __future__ import absolute_import -import os import logging +import os import time -import requests from oslo_serialization import jsonutils +import requests from yardstick.benchmark.scenarios import base + LOG = logging.getLogger(__name__) @@ -43,12 +44,6 @@ class StorPerf(base.Scenario): wr: 100% Write, random access rw: 70% Read / 30% write, random access - nossd (Optional): - Do not perform SSD style preconditioning. - - nowarm (Optional): - Do not perform a warmup prior to measurements. - report = [job_id] (Optional): Query the status of the supplied job_id and report on metrics. If a workload is supplied, will report on only that subset. @@ -79,10 +74,13 @@ class StorPerf(base.Scenario): setup_query_content = jsonutils.loads( setup_query.content) - if setup_query_content["stack_created"]: - self.setup_done = True + if ("stack_created" in setup_query_content and + setup_query_content["stack_created"]): LOG.debug("stack_created: %s", setup_query_content["stack_created"]) + return True + + return False def setup(self): """Set the configuration.""" @@ -111,9 +109,13 @@ class StorPerf(base.Scenario): elif setup_res.status_code == 200: LOG.info("stack_id: %s", setup_res_content["stack_id"]) - while not self.setup_done: - self._query_setup_state() - time.sleep(self.query_interval) + while not self._query_setup_state(): + time.sleep(self.query_interval) + + # We do not want to load the results of the disk initialization, + # so it is not added to the results here. + self.initialize_disks() + self.setup_done = True def _query_job_state(self, job_id): """Query the status of the supplied job_id and report on metrics""" @@ -149,7 +151,8 @@ class StorPerf(base.Scenario): if not self.setup_done: self.setup() - metadata = {"build_tag": "latest", "test_case": "opnfv_yardstick_tc074"} + metadata = {"build_tag": "latest", + "test_case": "opnfv_yardstick_tc074"} metadata_payload_dict = {"pod_name": "NODE_NAME", "scenario_name": "DEPLOY_SCENARIO", "version": "YARDSTICK_BRANCH"} @@ -162,7 +165,9 @@ class StorPerf(base.Scenario): job_args = {"metadata": metadata} job_args_payload_list = ["block_sizes", "queue_depths", "deadline", - "target", "nossd", "nowarm", "workload"] + "target", "workload", "workloads", + "agent_count", "steady_state_samples"] + job_args["deadline"] = self.options["timeout"] for job_argument in job_args_payload_list: try: @@ -170,8 +175,16 @@ class StorPerf(base.Scenario): except KeyError: pass + api_version = "v1.0" + + if ("workloads" in job_args and + job_args["workloads"] is not None and + len(job_args["workloads"])) > 0: + api_version = "v2.0" + LOG.info("Starting a job with parameters %s", job_args) - job_res = requests.post('http://%s:5000/api/v1.0/jobs' % self.target, + job_res = requests.post('http://%s:5000/api/%s/jobs' % (self.target, + api_version), json=job_args) job_res_content = jsonutils.loads(job_res.content) @@ -187,15 +200,6 @@ class StorPerf(base.Scenario): self._query_job_state(job_id) time.sleep(self.query_interval) - terminate_res = requests.delete('http://%s:5000/api/v1.0/jobs' % - self.target) - - if terminate_res.status_code != 200: - terminate_res_content = jsonutils.loads( - terminate_res.content) - raise RuntimeError("Failed to start a job, error message:", - terminate_res_content["message"]) - # TODO: Support using ETA to polls for completion. # Read ETA, next poll in 1/2 ETA time slot. # If ETA is greater than the maximum allowed job time, @@ -216,14 +220,46 @@ class StorPerf(base.Scenario): result.update(result_res_content) + def initialize_disks(self): + """Fills the target with random data prior to executing workloads""" + + job_args = {} + job_args_payload_list = ["target"] + + for job_argument in job_args_payload_list: + try: + job_args[job_argument] = self.options[job_argument] + except KeyError: + pass + + LOG.info("Starting initialization with parameters %s", job_args) + job_res = requests.post('http://%s:5000/api/v1.0/initializations' % + self.target, json=job_args) + + job_res_content = jsonutils.loads(job_res.content) + + if job_res.status_code != 200: + raise RuntimeError( + "Failed to start initialization job, error message:", + job_res_content["message"]) + elif job_res.status_code == 200: + job_id = job_res_content["job_id"] + LOG.info("Started initialization as job id: %s...", job_id) + + while not self.job_completed: + self._query_job_state(job_id) + time.sleep(self.query_interval) + + self.job_completed = False + def teardown(self): """Deletes the agent configuration and the stack""" - teardown_res = requests.delete('http://%s:5000/api/v1.0/\ - configurations' % self.target) + teardown_res = requests.delete( + 'http://%s:5000/api/v1.0/configurations' % self.target) if teardown_res.status_code == 400: teardown_res_content = jsonutils.loads( - teardown_res.content) + teardown_res.json_data) raise RuntimeError("Failed to reset environment, error message:", teardown_res_content['message']) diff --git a/yardstick/common/constants.py b/yardstick/common/constants.py index 3d775d48e..03733b6da 100644 --- a/yardstick/common/constants.py +++ b/yardstick/common/constants.py @@ -176,3 +176,7 @@ SCOPE_CLUSTER = 'Cluster' # VNF definition SSH_PORT = 22 LUA_PORT = 22022 + +# IMIX mode +DISTRIBUTION_IN_PACKETS = 'mode_DIP' +DISTRIBUTION_IN_BYTES = 'mode_DIB' diff --git a/yardstick/common/exceptions.py b/yardstick/common/exceptions.py index b39a0af9c..10c1f3f27 100644 --- a/yardstick/common/exceptions.py +++ b/yardstick/common/exceptions.py @@ -79,6 +79,10 @@ class FunctionNotImplemented(YardstickException): '"%(class_name)" class.') +class InvalidType(YardstickException): + message = 'Type "%(type_to_convert)s" is not valid' + + class InfluxDBConfigurationMissing(YardstickException): message = ('InfluxDB configuration is not available. Add "influxdb" as ' 'a dispatcher and the configuration section') diff --git a/yardstick/common/utils.py b/yardstick/common/utils.py index c019cd264..31885c073 100644 --- a/yardstick/common/utils.py +++ b/yardstick/common/utils.py @@ -21,6 +21,7 @@ import importlib import ipaddress import logging import os +import pydoc import random import re import signal @@ -578,3 +579,24 @@ def send_socket_command(host, port, command): finally: sock.close() return ret + + +def safe_cast(value, type_to_convert, default_value): + """Convert value to type, in case of error return default_value + + :param value: value to convert + :param type_to_convert: type to convert, could be "type" or "string" + :param default_value: default value to return + :return: converted value or default_value + """ + if isinstance(type_to_convert, type): + _type = type_to_convert + else: + _type = pydoc.locate(type_to_convert) + if not _type: + raise exceptions.InvalidType(type_to_convert=type_to_convert) + + try: + return _type(value) + except ValueError: + return default_value 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 8274ff9ce..1428c38f4 100644 --- a/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py +++ b/yardstick/network_services/libs/ixia_libs/ixnet/ixnet_api.py @@ -225,11 +225,16 @@ class IxNextgen(object): # pragma: no cover zip(self._cfg['cards'], self._cfg['ports'])] log.info('Create and assign vports: %s', ports) - for port in ports: - vport = self.ixnet.add(self.ixnet.getRoot(), 'vport') - self.ixnet.commit() - self.ixnet.execute('assignPorts', [port], [], [vport], True) + + vports = [] + for _ in ports: + vports.append(self.ixnet.add(self.ixnet.getRoot(), 'vport')) self.ixnet.commit() + + self.ixnet.execute('assignPorts', ports, [], vports, True) + self.ixnet.commit() + + for vport in vports: if self.ixnet.getAttribute(vport, '-state') != 'up': log.warning('Port %s is down', vport) @@ -372,8 +377,8 @@ class IxNextgen(object): # pragma: no cover tp_base.TrafficProfileConfig.RATE_FPS else 'percentLineRate') weighted_range_pairs = self._parse_framesize( traffic_param['outer_l2']['framesize']) - srcmac = str(traffic_param.get('srcmac', '00:00:00:00:00:01')) - dstmac = str(traffic_param.get('dstmac', '00:00:00:00:00:02')) + srcmac = str(traffic_param['outer_l2'].get('srcmac', '00:00:00:00:00:01')) + dstmac = str(traffic_param['outer_l2'].get('dstmac', '00:00:00:00:00:02')) if traffic_param['outer_l2']['QinQ']: s_vlan = traffic_param['outer_l2']['QinQ']['S-VLAN'] @@ -477,16 +482,17 @@ class IxNextgen(object): # pragma: no cover count = traffic_param['outer_l3']['count'] srcip = str(traffic_param['outer_l3']['srcip']) dstip = str(traffic_param['outer_l3']['dstip']) - seed = traffic_param['outer_l3']['seed'] + srcseed = traffic_param['outer_l3']['srcseed'] + dstseed = traffic_param['outer_l3']['dstseed'] srcmask = traffic_param['outer_l3']['srcmask'] or IP_VERSION_4_MASK dstmask = traffic_param['outer_l3']['dstmask'] or IP_VERSION_4_MASK self._update_ipv4_address( self._get_stack_item(fg_id, PROTO_IPV4)[0], - 'srcIp', srcip, seed, srcmask, count) + 'srcIp', srcip, srcseed, srcmask, count) self._update_ipv4_address( self._get_stack_item(fg_id, PROTO_IPV4)[0], - 'dstIp', dstip, seed, dstmask, count) + 'dstIp', dstip, dstseed, dstmask, count) def update_l4(self, traffic): """Update the L4 headers @@ -586,3 +592,114 @@ class IxNextgen(object): # pragma: no cover self.ixnet.execute('start', '/traffic') # pylint: disable=unnecessary-lambda utils.wait_until_true(lambda: self.is_traffic_running()) + + def add_topology(self, name, vports): + log.debug("add_topology: name='%s' ports='%s'", name, vports) + obj = self.ixnet.add(self.ixnet.getRoot(), 'topology') + self.ixnet.setMultiAttribute(obj, '-name', name, '-vports', vports) + self.ixnet.commit() + return obj + + def add_device_group(self, topology, name, multiplier): + log.debug("add_device_group: tpl='%s', name='%s', multiplier='%s'", + topology, name, multiplier) + + obj = self.ixnet.add(topology, 'deviceGroup') + self.ixnet.setMultiAttribute(obj, '-name', name, '-multiplier', + multiplier) + self.ixnet.commit() + return obj + + def add_ethernet(self, dev_group, name): + log.debug( + "add_ethernet: device_group='%s' name='%s'", dev_group, name) + obj = self.ixnet.add(dev_group, 'ethernet') + self.ixnet.setMultiAttribute(obj, '-name', name) + self.ixnet.commit() + return obj + + def add_ipv4(self, ethernet, name='', + addr=None, addr_step=None, addr_direction='increment', + prefix=None, prefix_step=None, prefix_direction='increment', + gateway=None, gw_step=None, gw_direction='increment'): + log.debug("add_ipv4: ethernet='%s' name='%s'", ethernet, name) + obj = self.ixnet.add(ethernet, 'ipv4') + if name != '': + self.ixnet.setAttribute(obj, '-name', name) + self.ixnet.commit() + + if addr_step is not None: + # handle counter pattern + _address = self.ixnet.getAttribute(obj, '-address') + self.ixnet.setMultiAttribute(_address, '-clearOverlays', 'true', + '-pattern', 'counter') + + address_counter = self.ixnet.add(_address, 'counter') + self.ixnet.setMultiAttribute(address_counter, + '-start', addr, + '-step', addr_step, + '-direction', addr_direction) + elif addr is not None: + # handle single value + _address = self.ixnet.getAttribute(obj, '-address') + self.ixnet.setMultiAttribute(_address + '/singleValue', '-value', + addr) + + if prefix_step is not None: + # handle counter pattern + _prefix = self.ixnet.getAttribute(obj, '-prefix') + self.ixnet.setMultiAttribute(_prefix, '-clearOverlays', 'true', + '-pattern', 'counter') + prefix_counter = self.ixnet.add(_prefix, 'counter') + self.ixnet.setMultiAttribute(prefix_counter, + '-start', prefix, + '-step', prefix_step, + '-direction', prefix_direction) + elif prefix is not None: + # handle single value + _prefix = self.ixnet.getAttribute(obj, '-prefix') + self.ixnet.setMultiAttribute(_prefix + '/singleValue', '-value', + prefix) + + if gw_step is not None: + # handle counter pattern + _gateway = self.ixnet.getAttribute(obj, '-gatewayIp') + self.ixnet.setMultiAttribute(_gateway, '-clearOverlays', 'true', + '-pattern', 'counter') + + gateway_counter = self.ixnet.add(_gateway, 'counter') + self.ixnet.setMultiAttribute(gateway_counter, + '-start', gateway, + '-step', gw_step, + '-direction', gw_direction) + elif gateway is not None: + # handle single value + _gateway = self.ixnet.getAttribute(obj, '-gatewayIp') + self.ixnet.setMultiAttribute(_gateway + '/singleValue', '-value', + gateway) + + self.ixnet.commit() + return obj + + def add_pppox_client(self, xproto, auth, user, pwd): + log.debug( + "add_pppox_client: xproto='%s', auth='%s', user='%s', pwd='%s'", + xproto, auth, user, pwd) + obj = self.ixnet.add(xproto, 'pppoxclient') + self.ixnet.commit() + + if auth == 'pap': + auth_type = self.ixnet.getAttribute(obj, '-authType') + self.ixnet.setMultiAttribute(auth_type + '/singleValue', '-value', + auth) + pap_user = self.ixnet.getAttribute(obj, '-papUser') + self.ixnet.setMultiAttribute(pap_user + '/singleValue', '-value', + user) + pap_pwd = self.ixnet.getAttribute(obj, '-papPassword') + self.ixnet.setMultiAttribute(pap_pwd + '/singleValue', '-value', + pwd) + else: + raise NotImplementedError() + + self.ixnet.commit() + return obj diff --git a/yardstick/network_services/traffic_profile/base.py b/yardstick/network_services/traffic_profile/base.py index a8f950b7b..4fbceea9b 100644 --- a/yardstick/network_services/traffic_profile/base.py +++ b/yardstick/network_services/traffic_profile/base.py @@ -44,6 +44,7 @@ class TrafficProfileConfig(object): self.lower_bound = tprofile.get('lower_bound') self.upper_bound = tprofile.get('upper_bound') self.step_interval = tprofile.get('step_interval') + self.enable_latency = tprofile.get('enable_latency', False) def _parse_rate(self, rate): """Parse traffic profile rate diff --git a/yardstick/network_services/traffic_profile/ixia_rfc2544.py b/yardstick/network_services/traffic_profile/ixia_rfc2544.py index 26dc1fe04..44bf2eafc 100644 --- a/yardstick/network_services/traffic_profile/ixia_rfc2544.py +++ b/yardstick/network_services/traffic_profile/ixia_rfc2544.py @@ -96,7 +96,8 @@ class IXIARFC2544Profile(trex_traffic_profile.TrexProfile): 'count': ip['count'], 'dscp': ip['dscp'], 'ttl': ip['ttl'], - 'seed': ip['seed'], + 'srcseed': ip.get('srcseed', 1), + 'dstseed': ip.get('dstseed', 1), 'srcip': srcip, 'dstip': dstip, 'srcmask': srcmask, @@ -110,7 +111,7 @@ class IXIARFC2544Profile(trex_traffic_profile.TrexProfile): 'srcportmask': src_port_mask, 'dstportmask': dst_port_mask, 'count': outer_l4['count'], - 'seed': outer_l4['seed'], + 'seed': outer_l4.get('seed', 1) } } @@ -168,12 +169,8 @@ class IXIARFC2544Profile(trex_traffic_profile.TrexProfile): [samples[iface]['in_packets'] for iface in samples]) out_packets_sum = sum( [samples[iface]['out_packets'] for iface in samples]) - rx_throughput = sum( - [samples[iface]['RxThroughput'] for iface in samples]) - rx_throughput = round(float(rx_throughput), 2) - tx_throughput = sum( - [samples[iface]['TxThroughput'] for iface in samples]) - tx_throughput = round(float(tx_throughput), 2) + rx_throughput = round(float(in_packets_sum) / duration, 3) + tx_throughput = round(float(out_packets_sum) / duration, 3) packet_drop = abs(out_packets_sum - in_packets_sum) try: @@ -183,10 +180,6 @@ class IXIARFC2544Profile(trex_traffic_profile.TrexProfile): except ZeroDivisionError: LOG.info('No traffic is flowing') - samples['TxThroughput'] = tx_throughput - samples['RxThroughput'] = rx_throughput - samples['DropPercentage'] = drop_percent - if first_run: completed = True if drop_percent <= tolerance else False if (first_run and @@ -200,4 +193,21 @@ class IXIARFC2544Profile(trex_traffic_profile.TrexProfile): else: completed = True + latency_ns_avg = float( + sum([samples[iface]['Store-Forward_Avg_latency_ns'] + for iface in samples])) / num_ifaces + latency_ns_min = float( + sum([samples[iface]['Store-Forward_Min_latency_ns'] + for iface in samples])) / num_ifaces + latency_ns_max = float( + sum([samples[iface]['Store-Forward_Max_latency_ns'] + for iface in samples])) / num_ifaces + + samples['TxThroughput'] = tx_throughput + samples['RxThroughput'] = rx_throughput + samples['DropPercentage'] = drop_percent + samples['latency_ns_avg'] = latency_ns_avg + samples['latency_ns_min'] = latency_ns_min + samples['latency_ns_max'] = latency_ns_max + return completed, samples diff --git a/yardstick/network_services/traffic_profile/prox_binsearch.py b/yardstick/network_services/traffic_profile/prox_binsearch.py index 506a880e0..16a0411ec 100644 --- a/yardstick/network_services/traffic_profile/prox_binsearch.py +++ b/yardstick/network_services/traffic_profile/prox_binsearch.py @@ -25,6 +25,14 @@ from yardstick.common import constants as overall_constants LOG = logging.getLogger(__name__) +STATUS_SUCCESS = "Success" +STATUS_FAIL = "Failure" +STATUS_RESULT = "Result" +STEP_CONFIRM = "Confirm retry" +STEP_INCREASE_LOWER = "Increase lower" +STEP_DECREASE_LOWER = "Decrease lower" +STEP_DECREASE_UPPER = "Decrease upper" + class ProxBinSearchProfile(ProxProfile): """ @@ -85,18 +93,16 @@ class ProxBinSearchProfile(ProxProfile): # success, the binary search will complete on an integer multiple # of the precision, rather than on a fraction of it. - theor_max_thruput = actual_max_thruput = 0 + theor_max_thruput = 0 result_samples = {} - # Store one time only value in influxdb - single_samples = { + test_data = { "test_duration": traffic_gen.scenario_helper.scenario_cfg["runner"]["duration"], "test_precision": self.params["traffic_profile"]["test_precision"], "tolerated_loss": self.params["traffic_profile"]["tolerated_loss"], "duration": duration } - self.queue.put(single_samples) self.prev_time = time.time() # throughput and packet loss from the most recent successful test @@ -110,85 +116,88 @@ class ProxBinSearchProfile(ProxProfile): neg_retry = 0 total_retry = 0 - LOG.info("Checking MAX %s MIN %s TEST %s", - self.current_upper, self.lower_bound, test_value) + LOG.info("Checking MAX %s MIN %s TEST %s", self.current_upper, + self.lower_bound, test_value) + while (pos_retry <= ok_retry) and (neg_retry <= ok_retry): total_retry = total_retry + 1 + result, port_samples = self._profile_helper.run_test(pkt_size, duration, test_value, self.tolerated_loss, line_speed) - if (total_retry > (ok_retry * 3)) and (ok_retry is not 0): - LOG.info("Failure.!! .. RETRY EXCEEDED ... decrease lower bound") + if (total_retry > (ok_retry * 3)) and (ok_retry is not 0): + status = STATUS_FAIL + next_step = STEP_DECREASE_LOWER successful_pkt_loss = result.pkt_loss - samples = result.get_samples(pkt_size, successful_pkt_loss, port_samples) - self.current_upper = test_value neg_retry = total_retry elif result.success: if (pos_retry < ok_retry) and (ok_retry is not 0): - neg_retry = 0 - LOG.info("Success! ... confirm retry") - + status = STATUS_SUCCESS + next_step = STEP_CONFIRM successful_pkt_loss = result.pkt_loss - samples = result.get_samples(pkt_size, successful_pkt_loss, port_samples) - + neg_retry = 0 else: - LOG.info("Success! Increasing lower bound") + status = STATUS_SUCCESS + next_step = STEP_INCREASE_LOWER self.current_lower = test_value - successful_pkt_loss = result.pkt_loss - samples = result.get_samples(pkt_size, successful_pkt_loss, port_samples) - - # store results with success tag in influxdb - success_samples = \ - {'Success_' + key: value for key, value in samples.items()} - - success_samples["Success_rx_total"] = int(result.rx_total) - success_samples["Success_tx_total"] = int(result.tx_total) - success_samples["Success_can_be_lost"] = int(result.can_be_lost) - success_samples["Success_drop_total"] = int(result.drop_total) - success_samples["Success_RxThroughput"] = samples["RxThroughput"] - success_samples["Success_RxThroughput_gbps"] = \ - (samples["RxThroughput"] / 1000) * ((pkt_size + 20)* 8) - LOG.info(">>>##>>Collect SUCCESS TG KPIs %s %s", - datetime.datetime.now(), success_samples) - self.queue.put(success_samples, True, overall_constants.QUEUE_PUT_TIMEOUT) - - # Store Actual throughput for result samples - actual_max_thruput = success_samples["Success_RxThroughput"] pos_retry = pos_retry + 1 else: if (neg_retry < ok_retry) and (ok_retry is not 0): - + status = STATUS_FAIL + next_step = STEP_CONFIRM pos_retry = 0 - LOG.info("failure! ... confirm retry") else: - LOG.info("Failure... Decreasing upper bound") + status = STATUS_FAIL + next_step = STEP_DECREASE_UPPER self.current_upper = test_value neg_retry = neg_retry + 1 - samples = result.get_samples(pkt_size, successful_pkt_loss, port_samples) + + LOG.info( + "Status = '%s' Next_Step = '%s'", status, next_step) + + samples = result.get_samples(pkt_size, successful_pkt_loss, port_samples) if theor_max_thruput < samples["TxThroughput"]: theor_max_thruput = samples['TxThroughput'] - self.queue.put({'theor_max_throughput': theor_max_thruput}) - - LOG.info(">>>##>>Collect TG KPIs %s %s", datetime.datetime.now(), samples) + samples['theor_max_throughput'] = theor_max_thruput + + samples["rx_total"] = int(result.rx_total) + samples["tx_total"] = int(result.tx_total) + samples["can_be_lost"] = int(result.can_be_lost) + samples["drop_total"] = int(result.drop_total) + samples["RxThroughput_gbps"] = \ + (samples["RxThroughput"] / 1000) * ((pkt_size + 20) * 8) + samples['Status'] = status + samples['Next_Step'] = next_step samples["MAX_Rate"] = self.current_upper samples["MIN_Rate"] = self.current_lower samples["Test_Rate"] = test_value samples["Step_Id"] = step_id samples["Confirmation_Retry"] = total_retry + + samples.update(test_data) + + if status == STATUS_SUCCESS and next_step == STEP_INCREASE_LOWER: + # Store success samples for result samples + result_samples = samples + + LOG.info(">>>##>>Collect TG KPIs %s %s", datetime.datetime.now(), samples) + self.queue.put(samples, True, overall_constants.QUEUE_PUT_TIMEOUT) - LOG.info(">>>##>> Result Reached PktSize %s Theor_Max_Thruput %s Actual_throughput %s", - pkt_size, theor_max_thruput, actual_max_thruput) - result_samples["Result_pktSize"] = pkt_size - result_samples["Result_theor_max_throughput"] = theor_max_thruput - result_samples["Result_Actual_throughput"] = actual_max_thruput + LOG.info( + ">>>##>> Result Reached PktSize %s Theor_Max_Thruput %s Actual_throughput %s", + pkt_size, theor_max_thruput, result_samples.get("RxThroughput", 0)) + result_samples["Status"] = STATUS_RESULT + result_samples["Next_Step"] = "" + result_samples["Actual_throughput"] = result_samples.get("RxThroughput", 0) + result_samples["theor_max_throughput"] = theor_max_thruput self.queue.put(result_samples) diff --git a/yardstick/network_services/traffic_profile/rfc2544.py b/yardstick/network_services/traffic_profile/rfc2544.py index b54fc575f..e33c437c9 100644 --- a/yardstick/network_services/traffic_profile/rfc2544.py +++ b/yardstick/network_services/traffic_profile/rfc2544.py @@ -19,6 +19,7 @@ from trex_stl_lib import trex_stl_client from trex_stl_lib import trex_stl_packet_builder_scapy from trex_stl_lib import trex_stl_streams +from yardstick.common import constants from yardstick.network_services.traffic_profile import trex_traffic_profile @@ -118,7 +119,8 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile): ports.append(port_num) port_pg_id.add_port(port_num) profile = self._create_profile(profile_data, - self.rate, port_pg_id) + self.rate, port_pg_id, + self.config.enable_latency) self.generator.client.add_streams(profile, ports=[port_num]) self.generator.client.start(ports=ports, @@ -126,7 +128,7 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile): force=True) return ports, port_pg_id - def _create_profile(self, profile_data, rate, port_pg_id): + def _create_profile(self, profile_data, rate, port_pg_id, enable_latency): """Create a STL profile (list of streams) for a port""" streams = [] for packet_name in profile_data: @@ -134,11 +136,13 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile): get('outer_l2', {}).get('framesize')) imix_data = self._create_imix_data(imix) self._create_vm(profile_data[packet_name]) - _streams = self._create_streams(imix_data, rate, port_pg_id) + _streams = self._create_streams(imix_data, rate, port_pg_id, + enable_latency) streams.extend(_streams) return trex_stl_streams.STLProfile(streams) - def _create_imix_data(self, imix): + def _create_imix_data(self, imix, + weight_mode=constants.DISTRIBUTION_IN_PACKETS): """Generate the IMIX distribution for a STL profile The input information is the framesize dictionary in a test case @@ -157,6 +161,20 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile): E.g.: imix_count = {64: 25, 128: 75} + The weight mode is described in [1]. There are two ways to describe the + weight of the packets: + - Distribution in packets: the weight defines the percentage of + packets sent per packet size. IXIA uses this definition. + - Distribution in bytes: the weight defines the percentage of bytes + sent per packet size. + + Packet size # packets D. in packets Bytes D. in bytes + 40 7 58.33% 280 7% + 576 4 33.33% 2304 56% + 1500 1 8.33% 1500 37% + + [1] https://en.wikipedia.org/wiki/Internet_Mix + :param imix: (dict) IMIX size and weight """ imix_count = {} @@ -171,8 +189,16 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile): imix_sum = 100 weight_normalize = float(imix_sum) / 100 - return {size: float(weight) / weight_normalize - for size, weight in imix_count.items()} + imix_dip = {size: float(weight) / weight_normalize + for size, weight in imix_count.items()} + + if weight_mode == constants.DISTRIBUTION_IN_BYTES: + return imix_dip + + byte_total = sum([int(size) * weight + for size, weight in imix_dip.items()]) + return {size: (int(size) * weight * 100) / byte_total + for size, weight in imix_dip.items()} def _create_vm(self, packet_definition): """Create the STL Raw instructions""" @@ -213,7 +239,7 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile): return trex_stl_packet_builder_scapy.STLPktBuilder( pkt=base_pkt / pad, vm=self.trex_vm) - def _create_streams(self, imix_data, rate, port_pg_id): + def _create_streams(self, imix_data, rate, port_pg_id, enable_latency): """Create a list of streams per packet size The STL TX mode speed of the generated streams will depend on the frame @@ -237,7 +263,8 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile): in imix_data.items() if float(weight) > 0): packet = self._create_single_packet(size) pg_id = port_pg_id.increase_pg_id() - stl_flow = trex_stl_streams.STLFlowLatencyStats(pg_id=pg_id) + stl_flow = (trex_stl_streams.STLFlowLatencyStats(pg_id=pg_id) if + enable_latency else None) mode = trex_stl_streams.STLTXCont(percentage=weight * rate / 100) streams.append(trex_stl_client.STLStream( packet=packet, flow_stats=stl_flow, mode=mode)) @@ -247,19 +274,16 @@ class RFC2544Profile(trex_traffic_profile.TrexProfile): correlated_traffic): """Calculate the drop percentage and run the traffic""" completed = False - tx_rate_fps = 0 - rx_rate_fps = 0 - for sample in samples: - tx_rate_fps += sum( - port['tx_throughput_fps'] for port in sample.values()) - rx_rate_fps += sum( - port['rx_throughput_fps'] for port in sample.values()) - tx_rate_fps = round(float(tx_rate_fps) / len(samples), 2) - rx_rate_fps = round(float(rx_rate_fps) / len(samples), 2) - - # TODO(esm): RFC2544 doesn't tolerate packet loss, why do we? - out_packets = sum(port['out_packets'] for port in samples[-1].values()) - in_packets = sum(port['in_packets'] for port in samples[-1].values()) + out_pkt_end = sum(port['out_packets'] for port in samples[-1].values()) + in_pkt_end = sum(port['in_packets'] for port in samples[-1].values()) + out_pkt_ini = sum(port['out_packets'] for port in samples[0].values()) + in_pkt_ini = sum(port['in_packets'] for port in samples[0].values()) + time_diff = (list(samples[-1].values())[0]['timestamp'] - + list(samples[0].values())[0]['timestamp']).total_seconds() + out_packets = out_pkt_end - out_pkt_ini + in_packets = in_pkt_end - in_pkt_ini + tx_rate_fps = float(out_packets) / time_diff + rx_rate_fps = float(in_packets) / time_diff drop_percent = 100.0 # https://tools.ietf.org/html/rfc2544#section-26.3 diff --git a/yardstick/network_services/vnf_generic/vnf/tg_prox.py b/yardstick/network_services/vnf_generic/vnf/tg_prox.py index 854319a21..d12c42ec8 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_prox.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_prox.py @@ -12,9 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import - import logging +import copy from yardstick.network_services.utils import get_nsb_option from yardstick.network_services.vnf_generic.vnf.prox_vnf import ProxApproxVnf @@ -32,7 +31,9 @@ class ProxTrafficGen(SampleVNFTrafficGen): def __init__(self, name, vnfd, task_id, setup_env_helper_type=None, resource_helper_type=None): - # don't call superclass, use custom wrapper of ProxApproxVnf + vnfd_cpy = copy.deepcopy(vnfd) + super(ProxTrafficGen, self).__init__(name, vnfd_cpy, task_id) + self._vnf_wrapper = ProxApproxVnf( name, vnfd, task_id, setup_env_helper_type, resource_helper_type) self.bin_path = get_nsb_option('bin_path', '') 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 4d3bc2ce5..94ab06980 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py @@ -60,7 +60,7 @@ class IxiaResourceHelper(ClientResourceHelper): def stop_collect(self): self._terminated.value = 1 - def generate_samples(self, ports, key=None): + def generate_samples(self, ports, duration): stats = self.get_stats() samples = {} @@ -70,27 +70,23 @@ class IxiaResourceHelper(ClientResourceHelper): try: # reverse lookup port name from port_num so the stats dict is descriptive intf = self.vnfd_helper.find_interface_by_port(port_num) - port_name = intf["name"] + port_name = intf['name'] + avg_latency = stats['Store-Forward_Avg_latency_ns'][port_num] + min_latency = stats['Store-Forward_Min_latency_ns'][port_num] + max_latency = stats['Store-Forward_Max_latency_ns'][port_num] samples[port_name] = { - "rx_throughput_kps": float(stats["Rx_Rate_Kbps"][port_num]), - "tx_throughput_kps": float(stats["Tx_Rate_Kbps"][port_num]), - "rx_throughput_mbps": float(stats["Rx_Rate_Mbps"][port_num]), - "tx_throughput_mbps": float(stats["Tx_Rate_Mbps"][port_num]), - "in_packets": int(stats["Valid_Frames_Rx"][port_num]), - "out_packets": int(stats["Frames_Tx"][port_num]), - # NOTE(ralonsoh): we need to make the traffic injection - # time variable. - "RxThroughput": int(stats["Valid_Frames_Rx"][port_num]) / 30, - "TxThroughput": int(stats["Frames_Tx"][port_num]) / 30, + 'rx_throughput_kps': float(stats['Rx_Rate_Kbps'][port_num]), + 'tx_throughput_kps': float(stats['Tx_Rate_Kbps'][port_num]), + 'rx_throughput_mbps': float(stats['Rx_Rate_Mbps'][port_num]), + 'tx_throughput_mbps': float(stats['Tx_Rate_Mbps'][port_num]), + 'in_packets': int(stats['Valid_Frames_Rx'][port_num]), + 'out_packets': int(stats['Frames_Tx'][port_num]), + 'RxThroughput': float(stats['Valid_Frames_Rx'][port_num]) / duration, + 'TxThroughput': float(stats['Frames_Tx'][port_num]) / duration, + 'Store-Forward_Avg_latency_ns': utils.safe_cast(avg_latency, int, 0), + 'Store-Forward_Min_latency_ns': utils.safe_cast(min_latency, int, 0), + 'Store-Forward_Max_latency_ns': utils.safe_cast(max_latency, int, 0) } - if key: - avg_latency = stats["Store-Forward_Avg_latency_ns"][port_num] - min_latency = stats["Store-Forward_Min_latency_ns"][port_num] - max_latency = stats["Store-Forward_Max_latency_ns"][port_num] - samples[port_name][key] = \ - {"Store-Forward_Avg_latency_ns": avg_latency, - "Store-Forward_Min_latency_ns": min_latency, - "Store-Forward_Max_latency_ns": max_latency} except IndexError: pass @@ -129,13 +125,11 @@ class IxiaResourceHelper(ClientResourceHelper): self, self.client, mac) self.client_started.value = 1 # pylint: disable=unnecessary-lambda - utils.wait_until_true(lambda: self.client.is_traffic_stopped()) - samples = self.generate_samples(traffic_profile.ports) + utils.wait_until_true(lambda: self.client.is_traffic_stopped(), + timeout=traffic_profile.config.duration * 2) + samples = self.generate_samples(traffic_profile.ports, + traffic_profile.config.duration) - # NOTE(ralonsoh): the traffic injection duration is fixed to 30 - # seconds. This parameter is configurable and must be retrieved - # from the traffic_profile.full_profile information. - # Every flow must have the same duration. completed, samples = traffic_profile.get_drop_percentage( samples, min_tol, max_tol, first_run=first_run) self._queue.put(samples) diff --git a/yardstick/network_services/vnf_generic/vnf/tg_trex.py b/yardstick/network_services/vnf_generic/vnf/tg_trex.py index 58b73488b..4296da84c 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_trex.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_trex.py @@ -11,8 +11,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -""" Trex acts as traffic generation and vnf definitions based on IETS Spec """ +import datetime import logging import os @@ -167,6 +167,7 @@ class TrexResourceHelper(ClientResourceHelper): def _get_samples(self, ports, port_pg_id=None): stats = self.get_stats(ports) + timestamp = datetime.datetime.now() samples = {} for pname in (intf['name'] for intf in self.vnfd_helper.interfaces): port_num = self.vnfd_helper.port_num(pname) @@ -178,6 +179,7 @@ class TrexResourceHelper(ClientResourceHelper): 'tx_throughput_bps': float(port_stats.get('tx_bps', 0.0)), 'in_packets': int(port_stats.get('ipackets', 0)), 'out_packets': int(port_stats.get('opackets', 0)), + 'timestamp': timestamp } pg_id_list = port_pg_id.get_pg_ids(port_num) diff --git a/yardstick/orchestrator/heat.py b/yardstick/orchestrator/heat.py index 99a5760a3..9da4948dd 100644 --- a/yardstick/orchestrator/heat.py +++ b/yardstick/orchestrator/heat.py @@ -471,68 +471,77 @@ name (i.e. %s). 'value': {'get_resource': name} } - def add_security_group(self, name): + def add_security_group(self, name, security_group=None): """add to the template a Neutron SecurityGroup""" log.debug("adding Neutron::SecurityGroup '%s'", name) + description = ("Group allowing IPv4 and IPv6 for icmp and upd/tcp on" + "all ports") + rules = [ + {'remote_ip_prefix': '0.0.0.0/0', + 'protocol': 'tcp', + 'port_range_min': '1', + 'port_range_max': '65535'}, + {'remote_ip_prefix': '0.0.0.0/0', + 'protocol': 'udp', + 'port_range_min': '1', + 'port_range_max': '65535'}, + {'remote_ip_prefix': '0.0.0.0/0', + 'protocol': 'icmp'}, + {'remote_ip_prefix': '::/0', + 'ethertype': 'IPv6', + 'protocol': 'tcp', + 'port_range_min': '1', + 'port_range_max': '65535'}, + {'remote_ip_prefix': '::/0', + 'ethertype': 'IPv6', + 'protocol': 'udp', + 'port_range_min': '1', + 'port_range_max': '65535'}, + {'remote_ip_prefix': '::/0', + 'ethertype': 'IPv6', + 'protocol': 'ipv6-icmp'}, + {'remote_ip_prefix': '0.0.0.0/0', + 'direction': 'egress', + 'protocol': 'tcp', + 'port_range_min': '1', + 'port_range_max': '65535'}, + {'remote_ip_prefix': '0.0.0.0/0', + 'direction': 'egress', + 'protocol': 'udp', + 'port_range_min': '1', + 'port_range_max': '65535'}, + {'remote_ip_prefix': '0.0.0.0/0', + 'direction': 'egress', + 'protocol': 'icmp'}, + {'remote_ip_prefix': '::/0', + 'direction': 'egress', + 'ethertype': 'IPv6', + 'protocol': 'tcp', + 'port_range_min': '1', + 'port_range_max': '65535'}, + {'remote_ip_prefix': '::/0', + 'direction': 'egress', + 'ethertype': 'IPv6', + 'protocol': 'udp', + 'port_range_min': '1', + 'port_range_max': '65535'}, + {'remote_ip_prefix': '::/0', + 'direction': 'egress', + 'ethertype': 'IPv6', + 'protocol': 'ipv6-icmp'}, + ] + if security_group: + description = "Custom security group rules defined by the user" + rules = security_group.get('rules') + + log.debug("The security group rules is %s", rules) + self.resources[name] = { 'type': 'OS::Neutron::SecurityGroup', 'properties': { 'name': name, - 'description': "Group allowing IPv4 and IPv6 for icmp and upd/tcp on all ports", - 'rules': [ - {'remote_ip_prefix': '0.0.0.0/0', - 'protocol': 'tcp', - 'port_range_min': '1', - 'port_range_max': '65535'}, - {'remote_ip_prefix': '0.0.0.0/0', - 'protocol': 'udp', - 'port_range_min': '1', - 'port_range_max': '65535'}, - {'remote_ip_prefix': '0.0.0.0/0', - 'protocol': 'icmp'}, - {'remote_ip_prefix': '::/0', - 'ethertype': 'IPv6', - 'protocol': 'tcp', - 'port_range_min': '1', - 'port_range_max': '65535'}, - {'remote_ip_prefix': '::/0', - 'ethertype': 'IPv6', - 'protocol': 'udp', - 'port_range_min': '1', - 'port_range_max': '65535'}, - {'remote_ip_prefix': '::/0', - 'ethertype': 'IPv6', - 'protocol': 'ipv6-icmp'}, - {'remote_ip_prefix': '0.0.0.0/0', - 'direction': 'egress', - 'protocol': 'tcp', - 'port_range_min': '1', - 'port_range_max': '65535'}, - {'remote_ip_prefix': '0.0.0.0/0', - 'direction': 'egress', - 'protocol': 'udp', - 'port_range_min': '1', - 'port_range_max': '65535'}, - {'remote_ip_prefix': '0.0.0.0/0', - 'direction': 'egress', - 'protocol': 'icmp'}, - {'remote_ip_prefix': '::/0', - 'direction': 'egress', - 'ethertype': 'IPv6', - 'protocol': 'tcp', - 'port_range_min': '1', - 'port_range_max': '65535'}, - {'remote_ip_prefix': '::/0', - 'direction': 'egress', - 'ethertype': 'IPv6', - 'protocol': 'udp', - 'port_range_min': '1', - 'port_range_max': '65535'}, - {'remote_ip_prefix': '::/0', - 'direction': 'egress', - 'ethertype': 'IPv6', - 'protocol': 'ipv6-icmp'}, - ] + 'description': description, + 'rules': rules } } diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py b/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py index 8ad581918..98d2b1836 100644 --- a/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_model.py @@ -46,6 +46,16 @@ XML_SAMPLE_INTERFACE = """<?xml version="1.0"?> class ModelLibvirtTestCase(unittest.TestCase): + XML_STR = model.VM_TEMPLATE.format( + vm_name="vm_name", + random_uuid=uuid.uuid4(), + mac_addr="00:01:02:03:04:05", + memory=2048, vcpu=2, cpu=2, + numa_cpus=0 - 10, + socket=1, threads=1, + vm_image="/var/lib/libvirt/images/yardstick-nsb-image.img", + cpuset=2 - 10, cputune='') + def setUp(self): self.pci_address_str = '0001:04:03.2' self.pci_address = utils.PciAddress(self.pci_address_str) @@ -66,34 +76,34 @@ class ModelLibvirtTestCase(unittest.TestCase): ssh_mock.execute = mock.Mock(return_value=(0, "a", "")) ssh.return_value = ssh_mock # NOTE(ralonsoh): this test doesn't cover function execution. - model.Libvirt.check_if_vm_exists_and_delete("vm_0", ssh_mock) + model.Libvirt.check_if_vm_exists_and_delete('vm-0', ssh_mock) def test_virsh_create_vm(self): self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0)) - model.Libvirt.virsh_create_vm(self.mock_ssh, 'vm_0') - self.mock_ssh.execute.assert_called_once_with('virsh create vm_0') + model.Libvirt.virsh_create_vm(self.mock_ssh, 'vm-0') + self.mock_ssh.execute.assert_called_once_with('virsh create vm-0') def test_virsh_create_vm_error(self): self.mock_ssh.execute = mock.Mock(return_value=(1, 0, 'error_create')) with self.assertRaises(exceptions.LibvirtCreateError) as exc: - model.Libvirt.virsh_create_vm(self.mock_ssh, 'vm_0') + model.Libvirt.virsh_create_vm(self.mock_ssh, 'vm-0') self.assertEqual('Error creating the virtual machine. Error: ' 'error_create.', str(exc.exception)) - self.mock_ssh.execute.assert_called_once_with('virsh create vm_0') + self.mock_ssh.execute.assert_called_once_with('virsh create vm-0') def test_virsh_destroy_vm(self): self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0)) - model.Libvirt.virsh_destroy_vm('vm_0', self.mock_ssh) - self.mock_ssh.execute.assert_called_once_with('virsh destroy vm_0') + model.Libvirt.virsh_destroy_vm('vm-0', self.mock_ssh) + self.mock_ssh.execute.assert_called_once_with('virsh destroy vm-0') @mock.patch.object(model, 'LOG') def test_virsh_destroy_vm_error(self, mock_logger): self.mock_ssh.execute = mock.Mock(return_value=(1, 0, 'error_destroy')) mock_logger.warning = mock.Mock() - model.Libvirt.virsh_destroy_vm('vm_0', self.mock_ssh) + model.Libvirt.virsh_destroy_vm('vm-0', self.mock_ssh) mock_logger.warning.assert_called_once_with( - 'Error destroying VM %s. Error: %s', 'vm_0', 'error_destroy') - self.mock_ssh.execute.assert_called_once_with('virsh destroy vm_0') + 'Error destroying VM %s. Error: %s', 'vm-0', 'error_destroy') + self.mock_ssh.execute.assert_called_once_with('virsh destroy vm-0') def test_add_interface_address(self): xml = ElementTree.ElementTree( @@ -171,6 +181,63 @@ class ModelLibvirtTestCase(unittest.TestCase): self.assertEqual('0x' + vm_pci.split(':')[2].split('.')[1], interface_address.get('function')) + def test_add_cdrom(self): + xml_input = copy.deepcopy(XML_SAMPLE) + xml_output = model.Libvirt.add_cdrom('/var/lib/libvirt/images/data.img', xml_input) + + root = ElementTree.fromstring(xml_output) + et_out = ElementTree.ElementTree(element=root) + disk = et_out.find('devices').find('disk') + self.assertEqual('file', disk.get('type')) + self.assertEqual('cdrom', disk.get('device')) + driver = disk.find('driver') + self.assertEqual('qemu', driver.get('name')) + self.assertEqual('raw', driver.get('type')) + source = disk.find('source') + self.assertEqual('/var/lib/libvirt/images/data.img', source.get('file')) + target = disk.find('target') + self.assertEqual('hdb', target.get('dev')) + self.assertIsNotNone(disk.find('readonly')) + + def test_gen_cdrom_image(self): + self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0)) + root = ElementTree.fromstring(self.XML_STR) + hostname = root.find('name').text + meta_data = "/tmp/meta-data" + user_data = "/tmp/user-data" + network_data = "/tmp/network-config" + file_path = "/tmp/cdrom-0.img" + key_filename = "id_rsa" + pub_key_str = "KEY" + user = 'root' + mac = "00:11:22:33:44:55" + ip = "1.1.1.7/24" + user_config = [" - name: {user_name}", + " ssh_authorized_keys:", + " - {pub_key_str}"] + + user_conf = os.linesep.join(user_config).format(pub_key_str=pub_key_str, user_name=user) + with mock.patch('six.moves.builtins.open', mock.mock_open(read_data=pub_key_str), + create=True) as mock_file: + with open(key_filename, "r") as h: + result = h.read() + model.Libvirt.gen_cdrom_image(self.mock_ssh, file_path, hostname, user, key_filename, + mac, ip) + mock_file.assert_called_with(".".join([key_filename, "pub"]), "r") + self.assertEqual(result, pub_key_str) + + self.mock_ssh.execute.assert_has_calls([ + mock.call("touch %s" % meta_data), + mock.call(model.USER_DATA_TEMPLATE.format(user_file=user_data, host=hostname, + user_config=user_conf)), + mock.call(model.NETWORK_DATA_TEMPLATE.format(network_file=network_data, + mac_address=mac, ip_address=ip)), + mock.call("genisoimage -output {0} -volid cidata" + " -joliet -r {1} {2} {3}".format(file_path, meta_data, user_data, + network_data)), + mock.call("rm {0} {1} {2}".format(meta_data, user_data, network_data)) + ]) + def test_create_snapshot_qemu(self): self.mock_ssh.execute = mock.Mock(return_value=(0, 0, 0)) index = 1 @@ -211,6 +278,25 @@ class ModelLibvirtTestCase(unittest.TestCase): self.mock_ssh.put_file.assert_called_once_with(base_image, '/tmp/base_image') + @mock.patch.object(model.Libvirt, 'gen_cdrom_image') + def test_check_update_key(self, mock_gen_cdrom_image): + node = { + 'user': 'defuser', + 'key_filename': '/home/ubuntu/id_rsa', + 'ip': '1.1.1.7', + 'netmask': '255.255.255.0'} + cdrom_img = "/var/lib/libvirt/images/data.img" + id_name = 'fake_name' + key_filename = node.get('key_filename') + root = ElementTree.fromstring(self.XML_STR) + hostname = root.find('name').text + mac = "00:11:22:33:44:55" + ip = "{0}/{1}".format(node.get('ip'), node.get('netmask')) + model.StandaloneContextHelper.check_update_key(self.mock_ssh, node, hostname, id_name, + cdrom_img, mac) + mock_gen_cdrom_image.assert_called_once_with(self.mock_ssh, cdrom_img, hostname, + node.get('user'), key_filename, mac, ip) + @mock.patch.object(os, 'access', return_value=False) def test_create_snapshot_qemu_no_image_local(self, mock_os_access): self.mock_ssh.execute = mock.Mock(side_effect=[(0, 0, 0), (1, 0, 0)]) @@ -253,18 +339,20 @@ class ModelLibvirtTestCase(unittest.TestCase): mac = model.StandaloneContextHelper.get_mac_address(0x00) _uuid = uuid.uuid4() connection = mock.Mock() + cdrom_img = '/tmp/cdrom-0.img' with mock.patch.object(model.StandaloneContextHelper, 'get_mac_address', return_value=mac) as \ mock_get_mac_address, \ mock.patch.object(uuid, 'uuid4', return_value=_uuid): xml_out, mac = model.Libvirt.build_vm_xml( - connection, flavor, 'vm_name', 100) + connection, flavor, 'vm_name', 100, cdrom_img) xml_ref = model.VM_TEMPLATE.format(vm_name='vm_name', random_uuid=_uuid, mac_addr=mac, memory='1024', vcpu='8', cpu='4', numa_cpus='0-7', socket='3', threads='2', vm_image='qemu_image', cpuset='4,5', cputune='cool') - self.assertEqual(xml_ref, xml_out) + xml_ref = model.Libvirt.add_cdrom(cdrom_img, xml_ref) + self.assertEqual(xml_out, xml_ref) mock_get_mac_address.assert_called_once_with(0x00) mock_create_snapshot_qemu.assert_called_once_with( connection, 100, 'images') @@ -296,6 +384,7 @@ class ModelLibvirtTestCase(unittest.TestCase): status = model.Libvirt.pin_vcpu_for_perf(ssh_mock, 4) self.assertIsNotNone(status) + class StandaloneContextHelperTestCase(unittest.TestCase): NODE_SAMPLE = "nodes_sample.yaml" @@ -463,7 +552,7 @@ class ServerTestCase(unittest.TestCase): } } status = self.server.generate_vnf_instance( - {}, self.NETWORKS, '1.1.1.1/24', 'vm_0', vnf, '00:00:00:00:00:01') + {}, self.NETWORKS, '1.1.1.1/24', 'vm-0', vnf, '00:00:00:00:00:01') self.assertIsNotNone(status) 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 69779d3e0..6cc8b11f3 100644 --- a/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py @@ -231,8 +231,8 @@ class OvsDpdkContextTestCase(unittest.TestCase): def test_undeploy(self, mock_libvirt): self.ovs_dpdk.vm_deploy = True self.ovs_dpdk.connection = mock.Mock() - self.ovs_dpdk.vm_names = ['vm_0', 'vm_1'] - self.ovs_dpdk.drivers = ['vm_0', 'vm_1'] + self.ovs_dpdk.vm_names = ['vm-0', 'vm-1'] + self.ovs_dpdk.drivers = ['vm-0', 'vm-1'] self.ovs_dpdk.cleanup_ovs_dpdk_env = mock.Mock() self.ovs_dpdk.networks = self.NETWORKS self.ovs_dpdk.undeploy() @@ -370,7 +370,7 @@ class OvsDpdkContextTestCase(unittest.TestCase): ssh.return_value = ssh_mock self.ovs_dpdk.vm_deploy = True self.ovs_dpdk.connection = ssh_mock - self.ovs_dpdk.vm_names = ['vm_0', 'vm_1'] + self.ovs_dpdk.vm_names = ['vm-0', 'vm-1'] self.ovs_dpdk.drivers = [] self.ovs_dpdk.networks = self.NETWORKS self.ovs_dpdk.helper.get_mac_address = mock.Mock(return_value="") @@ -381,7 +381,7 @@ class OvsDpdkContextTestCase(unittest.TestCase): def test__enable_interfaces(self, mock_add_ovs_interface): self.ovs_dpdk.vm_deploy = True self.ovs_dpdk.connection = mock.Mock() - self.ovs_dpdk.vm_names = ['vm_0', 'vm_1'] + self.ovs_dpdk.vm_names = ['vm-0', 'vm-1'] self.ovs_dpdk.drivers = [] self.ovs_dpdk.networks = self.NETWORKS self.ovs_dpdk.ovs_properties = {'vpath': 'fake_path'} @@ -391,15 +391,16 @@ class OvsDpdkContextTestCase(unittest.TestCase): 'fake_path', 0, self.NETWORKS['private_0']['vpci'], self.NETWORKS['private_0']['mac'], 'test') + @mock.patch.object(model.StandaloneContextHelper, 'check_update_key') @mock.patch.object(model.Libvirt, 'write_file') @mock.patch.object(model.Libvirt, 'build_vm_xml') @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete') @mock.patch.object(model.Libvirt, 'virsh_create_vm') - def test_setup_ovs_dpdk_context(self, mock_create_vm, mock_check_if_exists, - mock_build_xml, mock_write_file): + def test_setup_ovs_dpdk_context(self, mock_create_vm, mock_check_if_exists, mock_build_xml, + mock_write_file, mock_check_update_key): self.ovs_dpdk.vm_deploy = True self.ovs_dpdk.connection = mock.Mock() - self.ovs_dpdk.vm_names = ['vm_0', 'vm_1'] + self.ovs_dpdk.vm_names = ['vm-0', 'vm-1'] self.ovs_dpdk.drivers = [] self.ovs_dpdk.servers = { 'vnf_0': { @@ -413,23 +414,34 @@ class OvsDpdkContextTestCase(unittest.TestCase): self.ovs_dpdk.networks = self.NETWORKS self.ovs_dpdk.host_mgmt = {} self.ovs_dpdk.flavor = {} + self.ovs_dpdk.file_path = '/var/lib/libvirt/images/cdrom-0.img' self.ovs_dpdk.configure_nics_for_ovs_dpdk = mock.Mock(return_value="") - xml_str = mock.Mock() - mock_build_xml.return_value = (xml_str, '00:00:00:00:00:01') + self.ovs_dpdk._name_task_id = 'fake_name' + xml_str = 'vm-0' + self.ovs_dpdk.mac = '00:00:00:00:00:01' + mock_build_xml.return_value = (xml_str, self.ovs_dpdk.mac) self.ovs_dpdk._enable_interfaces = mock.Mock(return_value=xml_str) vnf_instance = mock.Mock() + vnf_instance_2 = mock.Mock() + mock_check_update_key.return_value = vnf_instance_2 self.ovs_dpdk.vnf_node.generate_vnf_instance = mock.Mock( return_value=vnf_instance) - self.assertEqual([vnf_instance], + self.assertEqual([vnf_instance_2], self.ovs_dpdk.setup_ovs_dpdk_context()) mock_create_vm.assert_called_once_with( self.ovs_dpdk.connection, '/tmp/vm_ovs_0.xml') mock_check_if_exists.assert_called_once_with( - 'vm_0', self.ovs_dpdk.connection) + 'vm-0', self.ovs_dpdk.connection) mock_build_xml.assert_called_once_with( - self.ovs_dpdk.connection, self.ovs_dpdk.vm_flavor, 'vm_0', 0) + self.ovs_dpdk.connection, self.ovs_dpdk.vm_flavor, 'vm-0', 0, self.ovs_dpdk.file_path) mock_write_file.assert_called_once_with('/tmp/vm_ovs_0.xml', xml_str) + mock_check_update_key.assert_called_once_with(self.ovs_dpdk.connection, + vnf_instance, + xml_str, + self.ovs_dpdk._name_task_id, + self.ovs_dpdk.file_path, + self.ovs_dpdk.mac) @mock.patch.object(io, 'BytesIO') def test__check_hugepages(self, mock_bytesio): diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py b/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py index 74c31569c..316aca72a 100644 --- a/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py @@ -113,8 +113,8 @@ class SriovContextTestCase(unittest.TestCase): self.sriov.vm_deploy = True self.sriov.connection = mock_ssh - self.sriov.vm_names = ['vm_0', 'vm_1'] - self.sriov.drivers = ['vm_0', 'vm_1'] + self.sriov.vm_names = ['vm-0', 'vm-1'] + self.sriov.drivers = ['vm-0', 'vm-1'] self.assertIsNone(self.sriov.undeploy()) def _get_file_abspath(self, filename): @@ -254,7 +254,7 @@ class SriovContextTestCase(unittest.TestCase): ssh.return_value = ssh_mock self.sriov.vm_deploy = True self.sriov.connection = ssh_mock - self.sriov.vm_names = ['vm_0', 'vm_1'] + self.sriov.vm_names = ['vm-0', 'vm-1'] self.sriov.drivers = [] self.sriov.networks = self.NETWORKS self.sriov.helper.get_mac_address = mock.Mock(return_value="") @@ -267,7 +267,7 @@ class SriovContextTestCase(unittest.TestCase): def test__enable_interfaces(self, mock_add_sriov, mock_ssh): self.sriov.vm_deploy = True self.sriov.connection = mock_ssh - self.sriov.vm_names = ['vm_0', 'vm_1'] + self.sriov.vm_names = ['vm-0', 'vm-1'] self.sriov.drivers = [] self.sriov.networks = self.NETWORKS self.assertEqual( @@ -276,12 +276,13 @@ class SriovContextTestCase(unittest.TestCase): mock_add_sriov.assert_called_once_with( '0000:00:0a.0', 0, self.NETWORKS['private_0']['mac'], 'test') + @mock.patch.object(model.StandaloneContextHelper, 'check_update_key') @mock.patch.object(model.Libvirt, 'build_vm_xml') @mock.patch.object(model.Libvirt, 'check_if_vm_exists_and_delete') @mock.patch.object(model.Libvirt, 'write_file') @mock.patch.object(model.Libvirt, 'virsh_create_vm') - def test_setup_sriov_context(self, mock_create_vm, mock_write_file, - mock_check, mock_build_vm_xml): + def test_setup_sriov_context(self, mock_create_vm, mock_write_file, mock_check, + mock_build_vm_xml, mock_check_update_key): self.sriov.servers = { 'vnf_0': { 'network_ports': { @@ -297,24 +298,31 @@ class SriovContextTestCase(unittest.TestCase): self.sriov.vm_flavor = 'flavor' self.sriov.networks = 'networks' self.sriov.configure_nics_for_sriov = mock.Mock() + self.sriov._name_task_id = 'fake_name' cfg = '/tmp/vm_sriov_0.xml' - vm_name = 'vm_0' + vm_name = 'vm-0' + mac = '00:00:00:00:00:01' xml_out = mock.Mock() - mock_build_vm_xml.return_value = (xml_out, '00:00:00:00:00:01') + mock_build_vm_xml.return_value = (xml_out, mac) + mock_check_update_key.return_value = 'node_2' + cdrom_img = '/var/lib/libvirt/images/cdrom-0.img' with mock.patch.object(self.sriov, 'vnf_node') as mock_vnf_node, \ mock.patch.object(self.sriov, '_enable_interfaces') as \ mock_enable_interfaces: mock_enable_interfaces.return_value = 'out_xml' mock_vnf_node.generate_vnf_instance = mock.Mock( - return_value='node') + return_value='node_1') nodes_out = self.sriov.setup_sriov_context() - self.assertEqual(['node'], nodes_out) + mock_check_update_key.assert_called_once_with(connection, 'node_1', vm_name, + self.sriov._name_task_id, cdrom_img, + mac) + self.assertEqual(['node_2'], nodes_out) mock_vnf_node.generate_vnf_instance.assert_called_once_with( 'flavor', 'networks', '1.2.3.4', 'vnf_0', self.sriov.servers['vnf_0'], '00:00:00:00:00:01') mock_build_vm_xml.assert_called_once_with( - connection, 'flavor', vm_name, 0) + connection, 'flavor', vm_name, 0, cdrom_img) mock_create_vm.assert_called_once_with(connection, cfg) mock_check.assert_called_once_with(vm_name, connection) mock_write_file.assert_called_once_with(cfg, 'out_xml') @@ -332,7 +340,7 @@ class SriovContextTestCase(unittest.TestCase): ssh.return_value = ssh_mock self.sriov.vm_deploy = True self.sriov.connection = ssh_mock - self.sriov.vm_names = ['vm_0', 'vm_1'] + self.sriov.vm_names = ['vm-0', 'vm-1'] self.sriov.drivers = [] self.sriov.servers = { 'vnf_0': { diff --git a/yardstick/tests/unit/benchmark/contexts/test_heat.py b/yardstick/tests/unit/benchmark/contexts/test_heat.py index 7782d96bd..3ccae44c7 100644 --- a/yardstick/tests/unit/benchmark/contexts/test_heat.py +++ b/yardstick/tests/unit/benchmark/contexts/test_heat.py @@ -73,6 +73,7 @@ class HeatContextTestCase(unittest.TestCase): self.assertEqual(self.test_context.server_groups, []) self.assertIsNone(self.test_context.keypair_name) self.assertIsNone(self.test_context.secgroup_name) + self.assertIsNone(self.test_context.security_group) self.assertEqual(self.test_context._server_map, {}) self.assertIsNone(self.test_context._image) self.assertIsNone(self.test_context._flavor) @@ -192,7 +193,7 @@ class HeatContextTestCase(unittest.TestCase): mock_template.add_keypair.assert_called_with( "ctx-key", "ctx-12345678") - mock_template.add_security_group.assert_called_with("ctx-secgroup") + mock_template.add_security_group.assert_called_with("ctx-secgroup", None) mock_template.add_network.assert_called_with( "ctx-12345678-mynet", 'physnet1', None, None, None, None) mock_template.add_router.assert_called_with( diff --git a/yardstick/tests/unit/benchmark/runner/test_arithmetic.py b/yardstick/tests/unit/benchmark/runner/test_arithmetic.py index 7b1e1e976..35d935cd5 100644 --- a/yardstick/tests/unit/benchmark/runner/test_arithmetic.py +++ b/yardstick/tests/unit/benchmark/runner/test_arithmetic.py @@ -14,16 +14,26 @@ import os import time from yardstick.benchmark.runners import arithmetic +from yardstick.common import exceptions as y_exc class ArithmeticRunnerTest(unittest.TestCase): class MyMethod(object): - def __init__(self): + SLA_VALIDATION_ERROR_SIDE_EFFECT = 1 + BROAD_EXCEPTION_SIDE_EFFECT = 2 + + def __init__(self, side_effect=0): self.count = 101 + self.side_effect = side_effect def __call__(self, data): self.count += 1 data['my_key'] = self.count + if self.side_effect == self.SLA_VALIDATION_ERROR_SIDE_EFFECT: + raise y_exc.SLAValidationError(case_name='My Case', + error_msg='my error message') + elif self.side_effect == self.BROAD_EXCEPTION_SIDE_EFFECT: + raise y_exc.YardstickException return self.count def setUp(self): @@ -218,3 +228,219 @@ class ArithmeticRunnerTest(unittest.TestCase): self.assertEqual(result['sequence'], count) self.assertGreater(result['timestamp'], timestamp) timestamp = result['timestamp'] + + def test__worker_process_except_sla_validation_error_no_sla_cfg(self): + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.call_count, 8) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + + def test__worker_process_output_on_sla_validation_error_no_sla_cfg(self): + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.SLA_VALIDATION_ERROR_SIDE_EFFECT) + + queue = multiprocessing.Queue() + output_queue = multiprocessing.Queue() + timestamp = time.time() + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 109) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + count = 0 + while not queue.empty(): + count += 1 + result = queue.get() + self.assertEqual(result['errors'], '') + self.assertEqual(result['data'], {'my_key': count + 101}) + self.assertEqual(result['sequence'], count) + self.assertGreater(result['timestamp'], timestamp) + timestamp = result['timestamp'] + self.assertEqual(count, 8) + self.assertTrue(output_queue.empty()) + + def test__worker_process_except_sla_validation_error_sla_cfg_monitor(self): + self.scenario_cfg['sla'] = {'action': 'monitor'} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.call_count, 8) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + + def test__worker_process_output_sla_validation_error_sla_cfg_monitor(self): + self.scenario_cfg['sla'] = {'action': 'monitor'} + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.SLA_VALIDATION_ERROR_SIDE_EFFECT) + + queue = multiprocessing.Queue() + output_queue = multiprocessing.Queue() + timestamp = time.time() + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 109) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + count = 0 + while not queue.empty(): + count += 1 + result = queue.get() + self.assertEqual(result['errors'], + ('My Case SLA validation failed. ' + 'Error: my error message',)) + self.assertEqual(result['data'], {'my_key': count + 101}) + self.assertEqual(result['sequence'], count) + self.assertGreater(result['timestamp'], timestamp) + timestamp = result['timestamp'] + self.assertEqual(count, 8) + self.assertTrue(output_queue.empty()) + + def test__worker_process_raise_sla_validation_error_sla_cfg_assert(self): + self.scenario_cfg['sla'] = {'action': 'assert'} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.SLAValidationError) + + with self.assertRaises(y_exc.SLAValidationError): + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.my_method.assert_called_once() + self.benchmark.setup.assert_called_once() + self.benchmark.teardown.assert_not_called() + + def test__worker_process_output_sla_validation_error_sla_cfg_assert(self): + self.scenario_cfg['sla'] = {'action': 'assert'} + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.SLA_VALIDATION_ERROR_SIDE_EFFECT) + + queue = multiprocessing.Queue() + output_queue = multiprocessing.Queue() + with self.assertRaisesRegexp( + y_exc.SLAValidationError, + 'My Case SLA validation failed. Error: my error message'): + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self.benchmark_cls.assert_called_once_with(self.scenario_cfg, {}) + self.benchmark.setup.assert_called_once() + self.assertEqual(self.benchmark.my_method.count, 102) + self.benchmark.teardown.assert_not_called() + self.assertTrue(queue.empty()) + self.assertTrue(output_queue.empty()) + + def test__worker_process_broad_exception_no_sla_cfg_early_exit(self): + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.YardstickException) + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.benchmark.my_method.assert_called_once() + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 64, 'size': 500}) + + def test__worker_process_output_on_broad_exception_no_sla_cfg(self): + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.BROAD_EXCEPTION_SIDE_EFFECT) + + queue = multiprocessing.Queue() + output_queue = multiprocessing.Queue() + timestamp = time.time() + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 102) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 64, 'size': 500}) + self.assertEqual(queue.qsize(), 1) + result = queue.get() + self.assertGreater(result['timestamp'], timestamp) + self.assertEqual(result['data'], {'my_key': 102}) + self.assertRegexpMatches( + result['errors'], + 'YardstickException: An unknown exception occurred.') + self.assertEqual(result['sequence'], 1) + self.assertTrue(output_queue.empty()) + + def test__worker_process_broad_exception_sla_cfg_not_none(self): + self.scenario_cfg['sla'] = {'action': 'some action'} + self.benchmark.my_method = mock.Mock( + side_effect=y_exc.YardstickException) + + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.call_count, 8) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + + def test__worker_process_output_on_broad_exception_sla_cfg_not_none(self): + self.scenario_cfg['sla'] = {'action': 'some action'} + self.benchmark.my_method = self.MyMethod( + side_effect=self.MyMethod.BROAD_EXCEPTION_SIDE_EFFECT) + + queue = multiprocessing.Queue() + output_queue = multiprocessing.Queue() + timestamp = time.time() + arithmetic._worker_process(queue, self.benchmark_cls, 'my_method', + self.scenario_cfg, {}, + multiprocessing.Event(), output_queue) + time.sleep(0.01) + + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.count, 109) + self.assertDictEqual(self.scenario_cfg['options'], + {'stride': 128, 'size': 2000}) + self.assertTrue(output_queue.empty()) + count = 0 + while not queue.empty(): + count += 1 + result = queue.get() + self.assertGreater(result['timestamp'], timestamp) + self.assertEqual(result['data'], {'my_key': count + 101}) + self.assertRegexpMatches( + result['errors'], + 'YardstickException: An unknown exception occurred.') + self.assertEqual(result['sequence'], count) + + def test__worker_process_benchmark_teardown_on_broad_exception(self): + self.benchmark.teardown = mock.Mock( + side_effect=y_exc.YardstickException) + + with self.assertRaises(SystemExit) as raised: + arithmetic._worker_process(mock.Mock(), self.benchmark_cls, + 'my_method', self.scenario_cfg, {}, + multiprocessing.Event(), mock.Mock()) + self.assertEqual(raised.exception.code, 1) + self._assert_defaults__worker_process_run_setup_and_teardown() + self.assertEqual(self.benchmark.my_method.call_count, 8) diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_baseattacker.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_baseattacker.py new file mode 100644 index 000000000..74f86983b --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_baseattacker.py @@ -0,0 +1,36 @@ +############################################################################## +# Copyright (c) 2018 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import unittest + +from yardstick.benchmark.scenarios.availability.attacker import baseattacker + + +class BaseAttackerTestCase(unittest.TestCase): + + def setUp(self): + self.attacker_cfg = { + 'fault_type': 'test-attacker', + 'action_parameter': {'process_name': 'nova_api'}, + 'rollback_parameter': {'process_name': 'nova_api'}, + 'key': 'stop-service', + 'attack_key': 'stop-service', + 'host': 'node1', + } + self.base_attacker = baseattacker.BaseAttacker({}, {}) + + def test__init__(self): + self.assertEqual(self.base_attacker.data, {}) + self.assertFalse(self.base_attacker.mandatory) + self.assertEqual(self.base_attacker.intermediate_variables, {}) + self.assertFalse(self.base_attacker.mandatory) + + def test_get_attacker_cls(self): + with self.assertRaises(RuntimeError): + baseattacker.BaseAttacker.get_attacker_cls(self.attacker_cfg) diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_serviceha.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_serviceha.py index ec0e5973c..d61fa67c7 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_serviceha.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_serviceha.py @@ -109,6 +109,23 @@ class ServicehaTestCase(unittest.TestCase): ret = {} p.run(ret) attacker = mock.Mock() + attacker.mandatory = False p.attackers = [attacker] p.teardown() attacker.recover.assert_not_called() + + @mock.patch.object(serviceha, 'baseattacker') + @mock.patch.object(serviceha, 'basemonitor') + def test__serviceha_teardown_when_mandatory(self, mock_monitor, + *args): + p = serviceha.ServiceHA(self.args, self.ctx) + p.setup() + self.assertTrue(p.setup_done) + mock_monitor.MonitorMgr().verify_SLA.return_value = True + ret = {} + p.run(ret) + attacker = mock.Mock() + attacker.mandatory = True + p.attackers = [attacker] + p.teardown() + attacker.recover.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py index 49578b383..6bf2f2c2f 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py @@ -405,7 +405,6 @@ class TestNetworkServiceTestCase(unittest.TestCase): def test___get_traffic_flow(self): self.scenario_cfg["traffic_options"]["flow"] = \ self._get_file_abspath("ipv4_1flow_Packets_vpe.yaml") - self.scenario_cfg["options"] = {} self.scenario_cfg['options'] = { 'flow': { 'src_ip': [ @@ -421,11 +420,10 @@ class TestNetworkServiceTestCase(unittest.TestCase): 'public_ip': ['1.1.1.1'], }, } - # NOTE(ralonsoh): check the expected output. This test could be - # incorrect - # result = {'flow': {'dst_ip0': '152.16.40.2-152.16.40.254', - # 'src_ip0': '152.16.100.2-152.16.100.254'}} - self.assertEqual({'flow': {}}, self.s._get_traffic_flow()) + expected_flow = {'flow': {'dst_ip_0': '152.16.40.2-152.16.40.254', + 'public_ip_0': '1.1.1.1', + 'src_ip_0': '152.16.100.2-152.16.100.254'}} + self.assertEqual(expected_flow, self.s._get_traffic_flow()) def test___get_traffic_flow_error(self): self.scenario_cfg["traffic_options"]["flow"] = \ diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/test_storperf.py b/yardstick/tests/unit/benchmark/scenarios/storage/test_storperf.py index 5844746ab..2ba53cb93 100644 --- a/yardstick/tests/unit/benchmark/scenarios/storage/test_storperf.py +++ b/yardstick/tests/unit/benchmark/scenarios/storage/test_storperf.py @@ -11,18 +11,18 @@ from __future__ import absolute_import +import json import unittest import mock from oslo_serialization import jsonutils +import requests from yardstick.benchmark.scenarios.storage import storperf # pylint: disable=unused-argument # disable this for now because I keep forgetting mock patch arg ordering - - def mocked_requests_config_post(*args, **kwargs): class MockResponseConfigPost(object): @@ -32,10 +32,24 @@ def mocked_requests_config_post(*args, **kwargs): return MockResponseConfigPost( '{"stack_id": "dac27db1-3502-4300-b301-91c64e6a1622",' - '"stack_created": "false"}', + '"stack_created": false}', 200) +def mocked_requests_config_post_fail(*args, **kwargs): + class MockResponseConfigPost(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseConfigPost( + '{"message": "ERROR: Parameter \'public_network\' is invalid: ' + + 'Error validating value \'foo\': Unable to find network with ' + + 'name or id \'foo\'"}', + 400) + + def mocked_requests_config_get(*args, **kwargs): class MockResponseConfigGet(object): @@ -45,10 +59,47 @@ def mocked_requests_config_get(*args, **kwargs): return MockResponseConfigGet( '{"stack_id": "dac27db1-3502-4300-b301-91c64e6a1622",' - '"stack_created": "true"}', + '"stack_created": true}', 200) +def mocked_requests_config_get_not_created(*args, **kwargs): + class MockResponseConfigGet(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseConfigGet( + '{"stack_id": "",' + '"stack_created": false}', + 200) + + +def mocked_requests_config_get_no_payload(*args, **kwargs): + class MockResponseConfigGet(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseConfigGet( + '{}', + 200) + + +def mocked_requests_initialize_post_fail(*args, **kwargs): + class MockResponseJobPost(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseJobPost( + '{"message": "ERROR: Stack StorPerfAgentGroup does not exist"}', + 400) + + def mocked_requests_job_get(*args, **kwargs): class MockResponseJobGet(object): @@ -73,6 +124,18 @@ def mocked_requests_job_post(*args, **kwargs): "d46bfb8c-36f4-4a40-813b-c4b4a437f728"}', 200) +def mocked_requests_job_post_fail(*args, **kwargs): + class MockResponseJobPost(object): + + def __init__(self, json_data, status_code): + self.content = json_data + self.status_code = status_code + + return MockResponseJobPost( + '{"message": "ERROR: Stack StorPerfAgentGroup does not exist"}', + 400) + + def mocked_requests_job_delete(*args, **kwargs): class MockResponseJobDelete(object): @@ -100,10 +163,7 @@ def mocked_requests_delete_failed(*args, **kwargs): self.json_data = json_data self.status_code = status_code - if args[0] == "http://172.16.0.137:5000/api/v1.0/configurations": - return MockResponseDeleteFailed('{"message": "Teardown failed"}', 400) - - return MockResponseDeleteFailed('{}', 404) + return MockResponseDeleteFailed('{"message": "Teardown failed"}', 400) class StorPerfTestCase(unittest.TestCase): @@ -119,11 +179,14 @@ class StorPerfTestCase(unittest.TestCase): self.result = {} - @mock.patch('yardstick.benchmark.scenarios.storage.storperf.requests.post', - side_effect=mocked_requests_config_post) - @mock.patch('yardstick.benchmark.scenarios.storage.storperf.requests.get', - side_effect=mocked_requests_config_get) - def test_successful_setup(self, mock_post, mock_get): + @mock.patch.object(requests, 'post') + @mock.patch.object(requests, 'get') + def test_setup(self, mock_get, mock_post): + mock_post.side_effect = [mocked_requests_config_post(), + mocked_requests_job_post()] + mock_get.side_effect = [mocked_requests_config_get(), + mocked_requests_job_get()] + options = { "agent_count": 8, "public_network": 'ext-net', @@ -146,14 +209,47 @@ class StorPerfTestCase(unittest.TestCase): self.assertTrue(s.setup_done) - @mock.patch('yardstick.benchmark.scenarios.storage.storperf.requests.post', - side_effect=mocked_requests_job_post) - @mock.patch('yardstick.benchmark.scenarios.storage.storperf.requests.get', - side_effect=mocked_requests_job_get) - @mock.patch( - 'yardstick.benchmark.scenarios.storage.storperf.requests.delete', - side_effect=mocked_requests_job_delete) - def test_successful_run(self, mock_post, mock_get, mock_delete): + @mock.patch.object(requests, 'get') + def test_query_setup_state_unsuccessful(self, mock_get): + mock_get.side_effect = mocked_requests_config_get_not_created + args = { + "options": {} + } + s = storperf.StorPerf(args, self.ctx) + result = s._query_setup_state() + self.assertFalse(result) + + @mock.patch.object(requests, 'get') + def test_query_setup_state_no_payload(self, mock_get): + mock_get.side_effect = mocked_requests_config_get_no_payload + args = { + "options": {} + } + s = storperf.StorPerf(args, self.ctx) + result = s._query_setup_state() + self.assertFalse(result) + + @mock.patch.object(requests, 'post') + @mock.patch.object(requests, 'get') + def test_setup_config_post_failed(self, mock_get, mock_post): + mock_post.side_effect = mocked_requests_config_post_fail + + args = { + "options": { + "public_network": "foo" + } + } + + s = storperf.StorPerf(args, self.ctx) + + self.assertRaises(RuntimeError, s.setup) + + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + def test_run_v1_successful(self, mock_post, mock_get): + mock_post.side_effect = mocked_requests_job_post + mock_get.side_effect = mocked_requests_job_get + options = { "agent_count": 8, "public_network": 'ext-net', @@ -165,6 +261,74 @@ class StorPerfTestCase(unittest.TestCase): "query_interval": 0, "timeout": 60 } + expected_post = { + 'metadata': { + 'build_tag': 'latest', + 'test_case': 'opnfv_yardstick_tc074' + }, + 'deadline': 60, + 'block_sizes': 4096, + 'queue_depths': 4, + "workload": "rs", + 'agent_count': 8 + } + + args = { + "options": options + } + + s = storperf.StorPerf(args, self.ctx) + s.setup_done = True + + sample_output = '{"Status": "Completed",\ + "_ssd_preconditioning.queue-depth.8.block-size.16384.duration": 6}' + + expected_result = jsonutils.loads(sample_output) + + s.run(self.result) + + mock_post.assert_called_once_with( + 'http://192.168.23.2:5000/api/v1.0/jobs', + json=jsonutils.loads(json.dumps(expected_post))) + + self.assertEqual(self.result, expected_result) + + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + def test_run_v2_successful(self, mock_post, mock_get): + mock_post.side_effect = mocked_requests_job_post + mock_get.side_effect = mocked_requests_job_get + + options = { + "agent_count": 8, + "public_network": 'ext-net', + "volume_size": 10, + "block_sizes": 4096, + "queue_depths": 4, + "workloads": { + "read_sequential": { + "rw": "rs" + } + }, + "StorPerf_ip": "192.168.23.2", + "query_interval": 0, + "timeout": 60 + } + expected_post = { + 'metadata': { + 'build_tag': 'latest', + 'test_case': 'opnfv_yardstick_tc074' + }, + 'deadline': 60, + 'block_sizes': 4096, + 'queue_depths': 4, + 'workloads': { + 'read_sequential': { + 'rw': 'rs' + } + }, + 'agent_count': 8 + } args = { "options": options @@ -179,13 +343,126 @@ class StorPerfTestCase(unittest.TestCase): expected_result = jsonutils.loads(sample_output) s.run(self.result) + mock_post.assert_called_once_with( + 'http://192.168.23.2:5000/api/v2.0/jobs', + json=expected_post) self.assertEqual(self.result, expected_result) - @mock.patch( - 'yardstick.benchmark.scenarios.storage.storperf.requests.delete', - side_effect=mocked_requests_delete) - def test_successful_teardown(self, mock_delete): + @mock.patch('time.sleep') + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + def test_run_failed(self, mock_post, mock_get, _): + mock_post.side_effect = mocked_requests_job_post_fail + mock_get.side_effect = mocked_requests_job_get + + options = { + "agent_count": 8, + "public_network": 'ext-net', + "volume_size": 10, + "block_sizes": 4096, + "queue_depths": 4, + "workloads": { + "read_sequential": { + "rw": "rs" + } + }, + "StorPerf_ip": "192.168.23.2", + "query_interval": 0, + "timeout": 60 + } + expected_post = { + 'metadata': { + 'build_tag': 'latest', + 'test_case': 'opnfv_yardstick_tc074' + }, + 'deadline': 60, + 'block_sizes': 4096, + 'queue_depths': 4, + 'workloads': { + 'read_sequential': { + 'rw': 'rs' + } + }, + 'agent_count': 8 + } + + args = { + "options": options + } + + s = storperf.StorPerf(args, self.ctx) + s.setup_done = True + + self.assertRaises(RuntimeError, s.run, self.ctx) + mock_post.assert_called_once_with( + 'http://192.168.23.2:5000/api/v2.0/jobs', + json=expected_post) + + @mock.patch('time.sleep') + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + @mock.patch.object(storperf.StorPerf, 'setup') + def test_run_calls_setup(self, mock_setup, mock_post, mock_get, _): + mock_post.side_effect = mocked_requests_job_post + mock_get.side_effect = mocked_requests_job_get + + args = { + "options": { + 'timeout': 60, + } + } + + s = storperf.StorPerf(args, self.ctx) + + s.run(self.result) + + mock_setup.assert_called_once() + + @mock.patch('time.sleep') + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + def test_initialize_disks(self, mock_post, mock_get, _): + mock_post.side_effect = mocked_requests_job_post + mock_get.side_effect = mocked_requests_job_get + + args = { + "options": { + "StorPerf_ip": "192.168.23.2" + } + } + + s = storperf.StorPerf(args, self.ctx) + + s.initialize_disks() + + mock_post.assert_called_once_with( + 'http://192.168.23.2:5000/api/v1.0/initializations', + json={}) + + @mock.patch('time.sleep') + @mock.patch.object(requests, 'get') + @mock.patch.object(requests, 'post') + def test_initialize_disks_post_failed(self, mock_post, mock_get, _): + mock_post.side_effect = mocked_requests_initialize_post_fail + mock_get.side_effect = mocked_requests_job_get + + args = { + "options": { + "StorPerf_ip": "192.168.23.2" + } + } + + s = storperf.StorPerf(args, self.ctx) + + self.assertRaises(RuntimeError, s.initialize_disks) + mock_post.assert_called_once_with( + 'http://192.168.23.2:5000/api/v1.0/initializations', + json={}) + + @mock.patch.object(requests, 'delete') + def test_teardown(self, mock_delete): + mock_delete.side_effect = mocked_requests_job_delete options = { "agent_count": 8, "public_network": 'ext-net', @@ -207,11 +484,12 @@ class StorPerfTestCase(unittest.TestCase): s.teardown() self.assertFalse(s.setup_done) + mock_delete.assert_called_once_with( + 'http://192.168.23.2:5000/api/v1.0/configurations') - @mock.patch( - 'yardstick.benchmark.scenarios.storage.storperf.requests.delete', - side_effect=mocked_requests_delete_failed) - def test_failed_teardown(self, mock_delete): + @mock.patch.object(requests, 'delete') + def test_teardown_request_delete_failed(self, mock_delete): + mock_delete.side_effect = mocked_requests_delete_failed options = { "agent_count": 8, "public_network": 'ext-net', @@ -230,4 +508,6 @@ class StorPerfTestCase(unittest.TestCase): s = storperf.StorPerf(args, self.ctx) - self.assertRaises(AssertionError, s.teardown(), self.result) + self.assertRaises(RuntimeError, s.teardown) + mock_delete.assert_called_once_with( + 'http://192.168.23.2:5000/api/v1.0/configurations') diff --git a/yardstick/tests/unit/common/test_utils.py b/yardstick/tests/unit/common/test_utils.py index ef4142148..3cf6c4d05 100644 --- a/yardstick/tests/unit/common/test_utils.py +++ b/yardstick/tests/unit/common/test_utils.py @@ -1391,3 +1391,19 @@ class GetPortIPTestCase(unittest.TestCase): def test_return_value(self): self.assertEqual('foo', utils.get_port_ip(self.ssh_client, 99)) + + +class SafeCaseTestCase(unittest.TestCase): + + def test_correct_type_int(self): + self.assertEqual(35, utils.safe_cast('35', int, 0)) + + def test_correct_int_as_string(self): + self.assertEqual(25, utils.safe_cast('25', 'int', 0)) + + def test_incorrect_type_as_string(self): + with self.assertRaises(exceptions.InvalidType): + utils.safe_cast('100', 'intt', 0) + + def test_default_value(self): + self.assertEqual(0, utils.safe_cast('', 'int', 0)) 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 e078d70ad..3077e3c17 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 @@ -38,7 +38,8 @@ TRAFFIC_PARAMETERS = { }, 'outer_l3': { 'count': 512, - 'seed': 1, + 'srcseed': 10, + 'dstseed': 20, 'dscp': 0, 'proto': 'udp', 'ttl': 32, @@ -69,7 +70,8 @@ TRAFFIC_PARAMETERS = { }, 'outer_l3': { 'count': 1024, - 'seed': 1, + 'srcseed': 30, + 'dstseed': 40, 'dscp': 0, 'proto': 'udp', 'ttl': 32, @@ -184,6 +186,113 @@ class TestIxNextgen(unittest.TestCase): self.assertIn([64, 64, 75], output) self.assertIn([512, 512, 25], output) + def test_add_topology(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.add_topology('topology 1', 'vports') + self.ixnet_gen.ixnet.add.assert_called_once_with('my_root', 'topology') + self.ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'obj', '-name', 'topology 1', '-vports', 'vports') + self.ixnet_gen.ixnet.commit.assert_called_once() + + def test_add_device_group(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.add_device_group('topology', 'device group 1', '1') + self.ixnet_gen.ixnet.add.assert_called_once_with('topology', + 'deviceGroup') + self.ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'obj', '-name', 'device group 1', '-multiplier', '1') + self.ixnet_gen.ixnet.commit.assert_called_once() + + def test_add_ethernet(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.add_ethernet('device_group', 'ethernet 1') + self.ixnet_gen.ixnet.add.assert_called_once_with('device_group', + 'ethernet') + self.ixnet_gen.ixnet.setMultiAttribute.assert_called_once_with( + 'obj', '-name', 'ethernet 1') + self.ixnet_gen.ixnet.commit.assert_called_once() + + def test_add_ipv4(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.add_ipv4('ethernet 1', name='ipv4 1') + self.ixnet_gen.ixnet.add.assert_called_once_with('ethernet 1', 'ipv4') + self.ixnet_gen.ixnet.setAttribute.assert_called_once_with('obj', + '-name', + 'ipv4 1') + self.assertEqual(self.ixnet.commit.call_count, 2) + + def test_add_ipv4_single(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.ixnet.getAttribute.return_value = 'attr' + self.ixnet_gen.add_ipv4('ethernet 1', name='ipv4 1', addr='100.1.1.100', + prefix='24', gateway='100.1.1.200') + self.ixnet_gen.ixnet.add.assert_called_once_with('ethernet 1', 'ipv4') + self.ixnet_gen.ixnet.setAttribute.assert_called_once_with('obj', + '-name', + 'ipv4 1') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', '100.1.1.100') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', '24') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', '100.1.1.200') + + self.assertEqual(self.ixnet.commit.call_count, 2) + + def test_add_ipv4_counter(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.ixnet.getAttribute.return_value = 'attr' + self.ixnet_gen.add_ipv4('ethernet 1', name='ipv4 1', + addr='100.1.1.100', + addr_step='1', + addr_direction='increment', + prefix='24', + gateway='100.1.1.200', + gw_step='1', + gw_direction='increment') + self.ixnet_gen.ixnet.add.assert_any_call('ethernet 1', 'ipv4') + self.ixnet_gen.ixnet.setAttribute.assert_called_once_with('obj', + '-name', + 'ipv4 1') + self.ixnet_gen.ixnet.add.assert_any_call('attr', 'counter') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call('obj', '-start', + '100.1.1.100', + '-step', '1', + '-direction', + 'increment') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', '24') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call('obj', '-start', + '100.1.1.200', + '-step', '1', + '-direction', + 'increment') + self.assertEqual(self.ixnet.commit.call_count, 2) + + def test_add_pppox_client(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.ixnet.getAttribute.return_value = 'attr' + self.ixnet_gen.add_pppox_client('ethernet 1', 'pap', 'user', 'pwd') + self.ixnet_gen.ixnet.add.assert_called_once_with('ethernet 1', + 'pppoxclient') + + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', 'pap') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', 'user') + self.ixnet_gen.ixnet.setMultiAttribute.assert_any_call( + 'attr/singleValue', '-value', 'pwd') + + self.assertEqual(self.ixnet.commit.call_count, 2) + + def test_add_pppox_client_invalid_auth(self): + self.ixnet_gen.ixnet.add.return_value = 'obj' + self.ixnet_gen.ixnet.getAttribute.return_value = 'attr' + self.assertRaises(NotImplementedError, self.ixnet_gen.add_pppox_client, + 'ethernet 1', 'invalid_auth', 'user', 'pwd') + + self.ixnet_gen.ixnet.setMultiAttribute.assert_not_called() + @mock.patch.object(IxNetwork, 'IxNet') def test_connect(self, mock_ixnet): mock_ixnet.return_value = self.ixnet @@ -237,8 +346,8 @@ class TestIxNextgen(unittest.TestCase): self.ixnet_gen._cfg = config self.assertIsNone(self.ixnet_gen.assign_ports()) - self.assertEqual(self.ixnet.execute.call_count, 2) - self.assertEqual(self.ixnet.commit.call_count, 4) + self.assertEqual(self.ixnet.execute.call_count, 1) + self.assertEqual(self.ixnet.commit.call_count, 3) self.assertEqual(self.ixnet.getAttribute.call_count, 2) @mock.patch.object(ixnet_api, 'log') diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py b/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py index 6f76eb77c..0759ecebd 100644 --- a/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_ixia_rfc2544.py @@ -575,87 +575,110 @@ class TestIXIARFC2544Profile(unittest.TestCase): def test_get_drop_percentage_completed(self): samples = {'iface_name_1': - {'RxThroughput': 10, 'TxThroughput': 10, - 'in_packets': 1000, 'out_packets': 1000}, + {'in_packets': 1000, 'out_packets': 1000, + 'Store-Forward_Avg_latency_ns': 20, + 'Store-Forward_Min_latency_ns': 15, + 'Store-Forward_Max_latency_ns': 25}, 'iface_name_2': - {'RxThroughput': 11, 'TxThroughput': 13, - 'in_packets': 1005, 'out_packets': 1007} + {'in_packets': 1005, 'out_packets': 1007, + 'Store-Forward_Avg_latency_ns': 23, + 'Store-Forward_Min_latency_ns': 13, + 'Store-Forward_Max_latency_ns': 28} } rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) completed, samples = rfc2544_profile.get_drop_percentage(samples, 0, 1) self.assertTrue(completed) - self.assertEqual(23.0, samples['TxThroughput']) - self.assertEqual(21.0, samples['RxThroughput']) + self.assertEqual(66.9, samples['TxThroughput']) + self.assertEqual(66.833, samples['RxThroughput']) self.assertEqual(0.099651, samples['DropPercentage']) + self.assertEqual(21.5, samples['latency_ns_avg']) + self.assertEqual(14.0, samples['latency_ns_min']) + self.assertEqual(26.5, samples['latency_ns_max']) def test_get_drop_percentage_over_drop_percentage(self): samples = {'iface_name_1': - {'RxThroughput': 10, 'TxThroughput': 10, - 'in_packets': 1000, 'out_packets': 1000}, + {'in_packets': 1000, 'out_packets': 1000, + 'Store-Forward_Avg_latency_ns': 20, + 'Store-Forward_Min_latency_ns': 15, + 'Store-Forward_Max_latency_ns': 25}, 'iface_name_2': - {'RxThroughput': 11, 'TxThroughput': 13, - 'in_packets': 1005, 'out_packets': 1007} + {'in_packets': 1005, 'out_packets': 1007, + 'Store-Forward_Avg_latency_ns': 20, + 'Store-Forward_Min_latency_ns': 15, + 'Store-Forward_Max_latency_ns': 25} } rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) rfc2544_profile.rate = 1000 completed, samples = rfc2544_profile.get_drop_percentage( samples, 0, 0.05) self.assertFalse(completed) - self.assertEqual(23.0, samples['TxThroughput']) - self.assertEqual(21.0, samples['RxThroughput']) + self.assertEqual(66.9, samples['TxThroughput']) + self.assertEqual(66.833, samples['RxThroughput']) self.assertEqual(0.099651, samples['DropPercentage']) self.assertEqual(rfc2544_profile.rate, rfc2544_profile.max_rate) def test_get_drop_percentage_under_drop_percentage(self): samples = {'iface_name_1': - {'RxThroughput': 10, 'TxThroughput': 10, - 'in_packets': 1000, 'out_packets': 1000}, + {'in_packets': 1000, 'out_packets': 1000, + 'Store-Forward_Avg_latency_ns': 20, + 'Store-Forward_Min_latency_ns': 15, + 'Store-Forward_Max_latency_ns': 25}, 'iface_name_2': - {'RxThroughput': 11, 'TxThroughput': 13, - 'in_packets': 1005, 'out_packets': 1007} + {'in_packets': 1005, 'out_packets': 1007, + 'Store-Forward_Avg_latency_ns': 20, + 'Store-Forward_Min_latency_ns': 15, + 'Store-Forward_Max_latency_ns': 25} } rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) rfc2544_profile.rate = 1000 completed, samples = rfc2544_profile.get_drop_percentage( samples, 0.2, 1) self.assertFalse(completed) - self.assertEqual(23.0, samples['TxThroughput']) - self.assertEqual(21.0, samples['RxThroughput']) + self.assertEqual(66.9, samples['TxThroughput']) + self.assertEqual(66.833, samples['RxThroughput']) self.assertEqual(0.099651, samples['DropPercentage']) self.assertEqual(rfc2544_profile.rate, rfc2544_profile.min_rate) @mock.patch.object(ixia_rfc2544.LOG, 'info') def test_get_drop_percentage_not_flow(self, *args): samples = {'iface_name_1': - {'RxThroughput': 0, 'TxThroughput': 10, - 'in_packets': 1000, 'out_packets': 0}, + {'in_packets': 1000, 'out_packets': 0, + 'Store-Forward_Avg_latency_ns': 20, + 'Store-Forward_Min_latency_ns': 15, + 'Store-Forward_Max_latency_ns': 25}, 'iface_name_2': - {'RxThroughput': 0, 'TxThroughput': 13, - 'in_packets': 1005, 'out_packets': 0} + {'in_packets': 1005, 'out_packets': 0, + 'Store-Forward_Avg_latency_ns': 20, + 'Store-Forward_Min_latency_ns': 15, + 'Store-Forward_Max_latency_ns': 25} } rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) rfc2544_profile.rate = 1000 completed, samples = rfc2544_profile.get_drop_percentage( samples, 0.2, 1) self.assertFalse(completed) - self.assertEqual(23.0, samples['TxThroughput']) - self.assertEqual(0, samples['RxThroughput']) + self.assertEqual(0.0, samples['TxThroughput']) + self.assertEqual(66.833, samples['RxThroughput']) self.assertEqual(100, samples['DropPercentage']) self.assertEqual(rfc2544_profile.rate, rfc2544_profile.max_rate) def test_get_drop_percentage_first_run(self): samples = {'iface_name_1': - {'RxThroughput': 10, 'TxThroughput': 10, - 'in_packets': 1000, 'out_packets': 1000}, + {'in_packets': 1000, 'out_packets': 1000, + 'Store-Forward_Avg_latency_ns': 20, + 'Store-Forward_Min_latency_ns': 15, + 'Store-Forward_Max_latency_ns': 25}, 'iface_name_2': - {'RxThroughput': 11, 'TxThroughput': 13, - 'in_packets': 1005, 'out_packets': 1007} + {'in_packets': 1005, 'out_packets': 1007, + 'Store-Forward_Avg_latency_ns': 20, + 'Store-Forward_Min_latency_ns': 15, + 'Store-Forward_Max_latency_ns': 25} } rfc2544_profile = ixia_rfc2544.IXIARFC2544Profile(self.TRAFFIC_PROFILE) completed, samples = rfc2544_profile.get_drop_percentage( samples, 0, 1, first_run=True) self.assertTrue(completed) - self.assertEqual(23.0, samples['TxThroughput']) - self.assertEqual(21.0, samples['RxThroughput']) + self.assertEqual(66.9, samples['TxThroughput']) + self.assertEqual(66.833, samples['RxThroughput']) self.assertEqual(0.099651, samples['DropPercentage']) self.assertEqual(33.45, rfc2544_profile.rate) 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 c062308e8..c09903377 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 @@ -71,38 +71,43 @@ class TestProxBinSearchProfile(unittest.TestCase): self.assertEqual(len(runs), 77) # Result Samples inc theor_max - result_tuple = {'Result_Actual_throughput': 5e-07, - 'Result_theor_max_throughput': 7.5e-07, - 'Result_pktSize': 200} - - profile.queue.put.assert_called_with(result_tuple) - - success_result_tuple = {"Success_CurrentDropPackets": 0.5, - "Success_DropPackets": 0.5, - "Success_LatencyAvg": 5.3, - "Success_LatencyMax": 5.2, - "Success_LatencyMin": 5.1, - "Success_PktSize": 200, - "Success_RxThroughput": 7.5e-07, - "Success_Throughput": 7.5e-07, - "Success_TxThroughput": 0.00012340000000000002} + result_tuple = {'Actual_throughput': 5e-07, + 'theor_max_throughput': 7.5e-07, + 'PktSize': 200, + 'Status': 'Result'} + + test_results = profile.queue.put.call_args[0] + for k in result_tuple: + self.assertEqual(result_tuple[k], test_results[0][k]) + + success_result_tuple = {"CurrentDropPackets": 0.5, + "DropPackets": 0.5, + "LatencyAvg": 5.3, + "LatencyMax": 5.2, + "LatencyMin": 5.1, + "PktSize": 200, + "RxThroughput": 7.5e-07, + "Throughput": 7.5e-07, + "TxThroughput": 0.00012340000000000002, + "Status": 'Success'} calls = profile.queue.put(success_result_tuple) profile.queue.put.assert_has_calls(calls) - success_result_tuple2 = {"Success_CurrentDropPackets": 0.5, - "Success_DropPackets": 0.5, - "Success_LatencyAvg": 5.3, - "Success_LatencyMax": 5.2, - "Success_LatencyMin": 5.1, - "Success_PktSize": 200, - "Success_RxThroughput": 7.5e-07, - "Success_Throughput": 7.5e-07, - "Success_TxThroughput": 123.4, - "Success_can_be_lost": 409600, - "Success_drop_total": 20480, - "Success_rx_total": 4075520, - "Success_tx_total": 4096000} + success_result_tuple2 = {"CurrentDropPackets": 0.5, + "DropPackets": 0.5, + "LatencyAvg": 5.3, + "LatencyMax": 5.2, + "LatencyMin": 5.1, + "PktSize": 200, + "RxThroughput": 7.5e-07, + "Throughput": 7.5e-07, + "TxThroughput": 123.4, + "can_be_lost": 409600, + "drop_total": 20480, + "rx_total": 4075520, + "tx_total": 4096000, + "Status": 'Success'} calls = profile.queue.put(success_result_tuple2) profile.queue.put.assert_has_calls(calls) @@ -183,17 +188,16 @@ class TestProxBinSearchProfile(unittest.TestCase): # Result Samples - result_tuple = {'Result_Actual_throughput': 0, "Result_theor_max_throughput": 0, - "Result_pktSize": 200} + result_tuple = {'Actual_throughput': 0, 'theor_max_throughput': 0, + "Status": 'Result', "Next_Step": ''} profile.queue.put.assert_called_with(result_tuple) # Check for success_ tuple (None expected) calls = profile.queue.put.mock_calls for call in calls: for call_detail in call[1]: - for k in call_detail: - if "Success_" in k: - self.assertRaises(AttributeError) + if call_detail["Status"] == 'Success': + self.assertRaises(AttributeError) def test_execute_4(self): @@ -237,38 +241,43 @@ class TestProxBinSearchProfile(unittest.TestCase): self.assertEqual(len(runs), 7) # Result Samples inc theor_max - result_tuple = {'Result_Actual_throughput': 5e-07, - 'Result_theor_max_throughput': 7.5e-07, - 'Result_pktSize': 200} - - profile.queue.put.assert_called_with(result_tuple) - - success_result_tuple = {"Success_CurrentDropPackets": 0.5, - "Success_DropPackets": 0.5, - "Success_LatencyAvg": 5.3, - "Success_LatencyMax": 5.2, - "Success_LatencyMin": 5.1, - "Success_PktSize": 200, - "Success_RxThroughput": 7.5e-07, - "Success_Throughput": 7.5e-07, - "Success_TxThroughput": 0.00012340000000000002} + result_tuple = {'Actual_throughput': 5e-07, + 'theor_max_throughput': 7.5e-07, + 'PktSize': 200, + "Status": 'Result'} + + test_results = profile.queue.put.call_args[0] + for k in result_tuple: + self.assertEqual(result_tuple[k], test_results[0][k]) + + success_result_tuple = {"CurrentDropPackets": 0.5, + "DropPackets": 0.5, + "LatencyAvg": 5.3, + "LatencyMax": 5.2, + "LatencyMin": 5.1, + "PktSize": 200, + "RxThroughput": 7.5e-07, + "Throughput": 7.5e-07, + "TxThroughput": 0.00012340000000000002, + "Status": 'Success'} calls = profile.queue.put(success_result_tuple) profile.queue.put.assert_has_calls(calls) - success_result_tuple2 = {"Success_CurrentDropPackets": 0.5, - "Success_DropPackets": 0.5, - "Success_LatencyAvg": 5.3, - "Success_LatencyMax": 5.2, - "Success_LatencyMin": 5.1, - "Success_PktSize": 200, - "Success_RxThroughput": 7.5e-07, - "Success_Throughput": 7.5e-07, - "Success_TxThroughput": 123.4, - "Success_can_be_lost": 409600, - "Success_drop_total": 20480, - "Success_rx_total": 4075520, - "Success_tx_total": 4096000} + success_result_tuple2 = {"CurrentDropPackets": 0.5, + "DropPackets": 0.5, + "LatencyAvg": 5.3, + "LatencyMax": 5.2, + "LatencyMin": 5.1, + "PktSize": 200, + "RxThroughput": 7.5e-07, + "Throughput": 7.5e-07, + "TxThroughput": 123.4, + "can_be_lost": 409600, + "drop_total": 20480, + "rx_total": 4075520, + "tx_total": 4096000, + "Status": 'Success'} calls = profile.queue.put(success_result_tuple2) profile.queue.put.assert_has_calls(calls) diff --git a/yardstick/tests/unit/network_services/traffic_profile/test_rfc2544.py b/yardstick/tests/unit/network_services/traffic_profile/test_rfc2544.py index 2e0331e8e..b8fbc6344 100644 --- a/yardstick/tests/unit/network_services/traffic_profile/test_rfc2544.py +++ b/yardstick/tests/unit/network_services/traffic_profile/test_rfc2544.py @@ -12,13 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -import mock +import datetime +import mock from trex_stl_lib import api as Pkt from trex_stl_lib import trex_stl_client from trex_stl_lib import trex_stl_packet_builder_scapy from trex_stl_lib import trex_stl_streams +from yardstick.common import constants from yardstick.network_services.traffic_profile import rfc2544 from yardstick.tests.unit import base @@ -102,10 +104,10 @@ class TestRFC2544Profile(base.BaseUnitTestCase): mock_create_profile: rfc2544_profile.execute_traffic(traffic_generator=mock_generator) mock_create_profile.assert_has_calls([ - mock.call('profile1', rfc2544_profile.rate, mock.ANY), - mock.call('profile1', rfc2544_profile.rate, mock.ANY), - mock.call('profile2', rfc2544_profile.rate, mock.ANY), - mock.call('profile2', rfc2544_profile.rate, mock.ANY)]) + mock.call('profile1', rfc2544_profile.rate, mock.ANY, False), + mock.call('profile1', rfc2544_profile.rate, mock.ANY, False), + mock.call('profile2', rfc2544_profile.rate, mock.ANY, False), + mock.call('profile2', rfc2544_profile.rate, mock.ANY, False)]) mock_generator.client.add_streams.assert_has_calls([ mock.call(mock.ANY, ports=[10]), mock.call(mock.ANY, ports=[20]), @@ -129,25 +131,55 @@ class TestRFC2544Profile(base.BaseUnitTestCase): mock_create_streams: mock_create_imix.return_value = 'imix_data' mock_create_streams.return_value = ['stream1'] - rfc2544_profile._create_profile(profile_data, rate, port_pg_id) + rfc2544_profile._create_profile(profile_data, rate, port_pg_id, + True) mock_create_imix.assert_called_once_with('imix_info') mock_create_vm.assert_called_once_with( {'outer_l2': {'framesize': 'imix_info'}}) mock_create_streams.assert_called_once_with('imix_data', 100, - port_pg_id) + port_pg_id, True) mock_stl_profile.assert_called_once_with(['stream1']) - def test__create_imix_data(self): + def test__create_imix_data_mode_DIB(self): rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) data = {'64B': 50, '128B': 50} - self.assertEqual({'64': 50.0, '128': 50.0}, - rfc2544_profile._create_imix_data(data)) + self.assertEqual( + {'64': 50.0, '128': 50.0}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_BYTES)) data = {'64B': 1, '128b': 3} - self.assertEqual({'64': 25.0, '128': 75.0}, - rfc2544_profile._create_imix_data(data)) + self.assertEqual( + {'64': 25.0, '128': 75.0}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_BYTES)) + data = {} + self.assertEqual( + {}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_BYTES)) + + def test__create_imix_data_mode_DIP(self): + rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) + data = {'64B': 25, '128B': 25, '512B': 25, '1518B': 25} + byte_total = 64 * 25 + 128 * 25 + 512 * 25 + 1518 * 25 + self.assertEqual( + {'64': 64 * 25.0 * 100 / byte_total, + '128': 128 * 25.0 * 100 / byte_total, + '512': 512 * 25.0 * 100 / byte_total, + '1518': 1518 * 25.0 * 100/ byte_total}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_PACKETS)) data = {} - self.assertEqual({}, rfc2544_profile._create_imix_data(data)) + self.assertEqual( + {}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_PACKETS)) + data = {'64B': 100} + self.assertEqual( + {'64': 100.0}, + rfc2544_profile._create_imix_data( + data, weight_mode=constants.DISTRIBUTION_IN_PACKETS)) def test__create_vm(self): packet = {'outer_l2': 'l2_definition'} @@ -208,7 +240,7 @@ class TestRFC2544Profile(base.BaseUnitTestCase): rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) with mock.patch.object(rfc2544_profile, '_create_single_packet'): output = rfc2544_profile._create_streams(imix_data, rate, - port_pg_id) + port_pg_id, True) self.assertEqual(['stream1', 'stream2'], output) mock_latency.assert_has_calls([ mock.call(pg_id=1), mock.call(pg_id=2)]) @@ -219,34 +251,38 @@ class TestRFC2544Profile(base.BaseUnitTestCase): def test_get_drop_percentage(self): rfc2544_profile = rfc2544.RFC2544Profile(self.TRAFFIC_PROFILE) samples = [ - {'xe1': {'tx_throughput_fps': 100, + {'xe1': {'tx_throughput_fps': 110, 'rx_throughput_fps': 101, - 'out_packets': 2000, - 'in_packets': 2010}, - 'xe2': {'tx_throughput_fps': 200, + 'out_packets': 2100, + 'in_packets': 2010, + 'timestamp': datetime.datetime(2000, 1, 1, 1, 1, 1, 1)}, + 'xe2': {'tx_throughput_fps': 210, 'rx_throughput_fps': 201, - 'out_packets': 4000, - 'in_packets': 4010}}, - {'xe1': {'tx_throughput_fps': 106, + 'out_packets': 4100, + 'in_packets': 4010, + 'timestamp': datetime.datetime(2000, 1, 1, 1, 1, 1, 1)}}, + {'xe1': {'tx_throughput_fps': 156, 'rx_throughput_fps': 108, - 'out_packets': 2031, + 'out_packets': 2110, 'in_packets': 2040, - 'latency': 'Latency1'}, - 'xe2': {'tx_throughput_fps': 203, + 'latency': 'Latency1', + 'timestamp': datetime.datetime(2000, 1, 1, 1, 1, 1, 31)}, + 'xe2': {'tx_throughput_fps': 253, 'rx_throughput_fps': 215, - 'out_packets': 4025, - 'in_packets': 4040, - 'latency': 'Latency2'}} + 'out_packets': 4150, + 'in_packets': 4010, + 'latency': 'Latency2', + 'timestamp': datetime.datetime(2000, 1, 1, 1, 1, 1, 31)}} ] completed, output = rfc2544_profile.get_drop_percentage( samples, 0, 0, False) - expected = {'DropPercentage': 0.3963, + expected = {'DropPercentage': 50.0, 'Latency': {'xe1': 'Latency1', 'xe2': 'Latency2'}, - 'RxThroughput': 312.5, - 'TxThroughput': 304.5, - 'CurrentDropPercentage': 0.3963, + 'RxThroughput': 1000000.0, + 'TxThroughput': 2000000.0, + 'CurrentDropPercentage': 50.0, 'Rate': 100.0, - 'Throughput': 312.5} + 'Throughput': 1000000.0} self.assertEqual(expected, output) self.assertFalse(completed) 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 5ad182f22..a7e61da0f 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 @@ -317,6 +317,7 @@ class TestProxTrafficGen(unittest.TestCase): prox_traffic_gen = ProxTrafficGen(NAME, self.VNFD0, 'task_id') self.assertIsNone(prox_traffic_gen._tg_process) self.assertIsNone(prox_traffic_gen._traffic_process) + self.assertIsNone(prox_traffic_gen._mq_producer) @mock.patch.object(ctx_base.Context, 'get_physical_node_from_server', return_value='mock_node') @mock.patch(SSH_HELPER) 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 ddb63242e..ec0e6aa6d 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 @@ -18,6 +18,7 @@ import mock import six import unittest +from yardstick.common import utils 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 @@ -57,6 +58,7 @@ class TestIxiaResourceHelper(unittest.TestCase): def test_run_traffic(self): mock_tprofile = mock.Mock() + mock_tprofile.config.duration = 10 mock_tprofile.get_drop_percentage.return_value = True, 'fake_samples' ixia_rhelper = tg_rfc2544_ixia.IxiaResourceHelper(mock.Mock()) ixia_rhelper.rfc_helper = mock.Mock() @@ -64,7 +66,8 @@ class TestIxiaResourceHelper(unittest.TestCase): ixia_rhelper.vnfd_helper.port_pairs.all_ports = [] with mock.patch.object(ixia_rhelper, 'generate_samples'), \ mock.patch.object(ixia_rhelper, '_build_ports'), \ - mock.patch.object(ixia_rhelper, '_initialize_client'): + mock.patch.object(ixia_rhelper, '_initialize_client'), \ + mock.patch.object(utils, 'wait_until_true'): ixia_rhelper.run_traffic(mock_tprofile) self.assertEqual('fake_samples', ixia_rhelper._queue.get()) diff --git a/yardstick/tests/unit/orchestrator/test_heat.py b/yardstick/tests/unit/orchestrator/test_heat.py index 3ec59a3c2..2e60a72cb 100644 --- a/yardstick/tests/unit/orchestrator/test_heat.py +++ b/yardstick/tests/unit/orchestrator/test_heat.py @@ -256,6 +256,25 @@ class HeatTemplateTestCase(unittest.TestCase): self.assertEqual(self.template.resources['some-server-group'][ 'properties']['policies'], ['anti-affinity']) + def test_add_security_group(self): + security_group = { + 'rules': [ + {'remote_ip_prefix': '0.0.0.0/0', + 'port_range_max': 65535, + 'port_range_min': 1, + 'protocol': 'custom'}, + ] + } + self.template.add_security_group('some-security-group', security_group) + + secgroup_rsc = self.template.resources['some-security-group'] + + self.assertEqual(secgroup_rsc['type'], "OS::Neutron::SecurityGroup") + self.assertEqual(secgroup_rsc['properties']['description'], + "Custom security group rules defined by the user") + self.assertEqual(secgroup_rsc['properties']['rules'][0]['protocol'], + 'custom') + def test__add_resources_to_template_raw(self): test_context = node.NodeContext() self.addCleanup(test_context._delete_context) |