diff options
Diffstat (limited to 'yardstick')
191 files changed, 4752 insertions, 2125 deletions
diff --git a/yardstick/__init__.py b/yardstick/__init__.py index f95b0a906..9eb5db9b1 100644 --- a/yardstick/__init__.py +++ b/yardstick/__init__.py @@ -14,8 +14,9 @@ import errno # this module must only import other modules that do # not require loggers to be created, so this cannot -# include yardstick.common.utils from yardstick.common import constants +from yardstick.common import exceptions + try: # do not use yardstick.common.utils.makedirs diff --git a/yardstick/benchmark/contexts/base.py b/yardstick/benchmark/contexts/base.py index c9b5b51c9..ae8319e37 100644 --- a/yardstick/benchmark/contexts/base.py +++ b/yardstick/benchmark/contexts/base.py @@ -6,17 +6,41 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -from __future__ import absolute_import import abc import six import yardstick.common.utils as utils +class Flags(object): + """Class to represent the status of the flags in a context""" + + _FLAGS = {'no_setup': False, + 'no_teardown': False} + + def __init__(self, **kwargs): + for name, value in self._FLAGS.items(): + setattr(self, name, value) + + for name, value in ((name, value) for (name, value) in kwargs.items() + if name in self._FLAGS): + setattr(self, name, value) + + def parse(self, **kwargs): + """Read in values matching the flags stored in this object""" + if not kwargs: + return + + for name, value in ((name, value) for (name, value) in kwargs.items() + if name in self._FLAGS): + setattr(self, name, value) + + @six.add_metaclass(abc.ABCMeta) class Context(object): """Class that represents a context in the logical model""" list = [] + SHORT_TASK_ID_LEN = 8 @staticmethod def split_name(name, sep='.'): @@ -29,10 +53,28 @@ class Context(object): def __init__(self): Context.list.append(self) + self._flags = Flags() + self._name = None + self._task_id = None - @abc.abstractmethod def init(self, attrs): - """Initiate context.""" + """Initiate context""" + self._name = attrs['name'] + self._task_id = attrs['task_id'] + self._flags.parse(**attrs.get('flags', {})) + self._name_task_id = '{}-{}'.format( + self._name, self._task_id[:self.SHORT_TASK_ID_LEN]) + + @property + def name(self): + if self._flags.no_setup or self._flags.no_teardown: + return self._name + else: + return self._name_task_id + + @property + def assigned_name(self): + return self._name @staticmethod def get_cls(context_type): diff --git a/yardstick/benchmark/contexts/dummy.py b/yardstick/benchmark/contexts/dummy.py index 8ae4b65b8..a9e4564fe 100644 --- a/yardstick/benchmark/contexts/dummy.py +++ b/yardstick/benchmark/contexts/dummy.py @@ -7,33 +7,25 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -from __future__ import absolute_import -import logging - from yardstick.benchmark.contexts.base import Context -LOG = logging.getLogger(__name__) - - class DummyContext(Context): - """Class that handle dummy info""" - - __context_type__ = "Dummy" + """Class that handle dummy info. - def __init__(self): - super(DummyContext, self).__init__() + This class is also used to test the abstract class Context because it + provides a minimal concrete implementation of a subclass. + """ - def init(self, attrs): - pass + __context_type__ = "Dummy" def deploy(self): - """don't need to deploy""" + """Don't need to deploy""" pass def undeploy(self): - """don't need to undeploy""" - super(DummyContext, self).undeploy() + """Don't need to undeploy""" + pass def _get_server(self, attr_name): return None diff --git a/yardstick/benchmark/contexts/heat.py b/yardstick/benchmark/contexts/heat.py index 7b7f1be32..0d1dfb86f 100644 --- a/yardstick/benchmark/contexts/heat.py +++ b/yardstick/benchmark/contexts/heat.py @@ -13,7 +13,6 @@ from __future__ import print_function import collections import logging import os -import uuid import errno from collections import OrderedDict @@ -25,9 +24,12 @@ from yardstick.benchmark.contexts.model import Network from yardstick.benchmark.contexts.model import PlacementGroup, ServerGroup from yardstick.benchmark.contexts.model import Server from yardstick.benchmark.contexts.model import update_scheduler_hints -from yardstick.common.openstack_utils import get_neutron_client -from yardstick.orchestrator.heat import HeatTemplate, get_short_key_uuid +from yardstick.common import exceptions as y_exc +from yardstick.common.openstack_utils import get_shade_client +from yardstick.orchestrator.heat import HeatStack +from yardstick.orchestrator.heat import HeatTemplate from yardstick.common import constants as consts +from yardstick.common import utils from yardstick.common.utils import source_env from yardstick.ssh import SSH @@ -50,7 +52,6 @@ class HeatContext(Context): __context_type__ = "Heat" def __init__(self): - self.name = None self.stack = None self.networks = OrderedDict() self.heat_timeout = None @@ -67,14 +68,9 @@ class HeatContext(Context): self._user = None self.template_file = None self.heat_parameters = None - self.neutron_client = None - # generate an uuid to identify yardstick_key - # the first 8 digits of the uuid will be used - self.key_uuid = uuid.uuid4() + self.shade_client = None self.heat_timeout = None - self.key_filename = ''.join( - [consts.YARDSTICK_ROOT_PATH, 'yardstick/resources/files/yardstick_key-', - get_short_key_uuid(self.key_uuid)]) + self.key_filename = None super(HeatContext, self).__init__() @staticmethod @@ -95,10 +91,10 @@ class HeatContext(Context): return sorted_networks def init(self, attrs): - """initializes itself from the supplied arguments""" - self.check_environment() - self.name = attrs["name"] + """Initializes itself from the supplied arguments""" + super(HeatContext, self).init(attrs) + self.check_environment() self._user = attrs.get("user") self.template_file = attrs.get("heat_template") @@ -137,7 +133,6 @@ class HeatContext(Context): self._server_map[server.dn] = server self.attrs = attrs - SSH.gen_keys(self.key_filename) def check_environment(self): try: @@ -176,10 +171,13 @@ class HeatContext(Context): template.add_flavor(**self.flavor) self.flavors.add(flavor) - template.add_keypair(self.keypair_name, self.key_uuid) + template.add_keypair(self.keypair_name, self.name) template.add_security_group(self.secgroup_name) for network in self.networks.values(): + # Using existing network + if network.is_existing(): + continue template.add_network(network.stack_name, network.physical_network, network.provider, @@ -285,38 +283,65 @@ class HeatContext(Context): scheduler_hints) def get_neutron_info(self): - if not self.neutron_client: - self.neutron_client = get_neutron_client() + if not self.shade_client: + self.shade_client = get_shade_client() - networks = self.neutron_client.list_networks() + networks = self.shade_client.list_networks() for network in self.networks.values(): - for neutron_net in networks['networks']: - if neutron_net['name'] == network.stack_name: + for neutron_net in (net for net in networks if net.name == network.stack_name): network.segmentation_id = neutron_net.get('provider:segmentation_id') # we already have physical_network # network.physical_network = neutron_net.get('provider:physical_network') network.network_type = neutron_net.get('provider:network_type') network.neutron_info = neutron_net + def _create_new_stack(self, heat_template): + try: + return heat_template.create(block=True, + timeout=self.heat_timeout) + except KeyboardInterrupt: + raise y_exc.StackCreationInterrupt + except Exception: + LOG.exception("stack failed") + # let the other failures happen, we want stack trace + raise + + def _retrieve_existing_stack(self, stack_name): + stack = HeatStack(stack_name) + if stack.get(): + return stack + else: + LOG.warning("Stack %s does not exist", self.name) + return None + def deploy(self): """deploys template into a stack using cloud""" LOG.info("Deploying context '%s' START", self.name) + self.key_filename = ''.join( + [consts.YARDSTICK_ROOT_PATH, + 'yardstick/resources/files/yardstick_key-', + self.name]) + # Permissions may have changed since creation; this can be fixed. If we + # overwrite the file, we lose future access to VMs using this key. + # As long as the file exists, even if it is unreadable, keep it intact + if not os.path.exists(self.key_filename): + SSH.gen_keys(self.key_filename) + heat_template = HeatTemplate(self.name, self.template_file, self.heat_parameters) if self.template_file is None: self._add_resources_to_template(heat_template) - try: - self.stack = heat_template.create(block=True, - timeout=self.heat_timeout) - except KeyboardInterrupt: - raise SystemExit("\nStack create interrupted") - except: - LOG.exception("stack failed") - # let the other failures happen, we want stack trace - raise + if self._flags.no_setup: + # Try to get an existing stack, returns a stack or None + self.stack = self._retrieve_existing_stack(self.name) + if not self.stack: + self.stack = self._create_new_stack(heat_template) + + else: + self.stack = self._create_new_stack(heat_template) # TODO: use Neutron to get segmentation-id self.get_neutron_info() @@ -332,18 +357,35 @@ class HeatContext(Context): LOG.info("Deploying context '%s' DONE", self.name) + @staticmethod + def _port_net_is_existing(port_info): + net_flags = port_info.get('net_flags', {}) + return net_flags.get(consts.IS_EXISTING) + + @staticmethod + def _port_net_is_public(port_info): + net_flags = port_info.get('net_flags', {}) + return net_flags.get(consts.IS_PUBLIC) + def add_server_port(self, server): - # use private ip from first port in first network - try: - private_port = next(iter(server.ports.values()))[0] - except IndexError: - LOG.exception("Unable to find first private port in %s", server.ports) - raise - server.private_ip = self.stack.outputs[private_port["stack_name"]] + server_ports = server.ports.values() + for server_port in server_ports: + port_info = server_port[0] + port_ip = self.stack.outputs[port_info["stack_name"]] + port_net_is_existing = self._port_net_is_existing(port_info) + port_net_is_public = self._port_net_is_public(port_info) + if port_net_is_existing and (port_net_is_public or + len(server_ports) == 1): + server.public_ip = port_ip + if not server.private_ip or len(server_ports) == 1: + server.private_ip = port_ip + server.interfaces = {} for network_name, ports in server.ports.items(): for port in ports: # port['port'] is either port name from mapping or default network_name + if self._port_net_is_existing(port): + continue server.interfaces[port['port']] = self.make_interface_dict(network_name, port['port'], port['stack_name'], @@ -380,20 +422,27 @@ class HeatContext(Context): "local_ip": private_ip, } + def _delete_key_file(self): + try: + utils.remove_file(self.key_filename) + utils.remove_file(self.key_filename + ".pub") + except OSError: + LOG.exception("There was an error removing the key file %s", + self.key_filename) + def undeploy(self): """undeploys stack from cloud""" + if self._flags.no_teardown: + LOG.info("Undeploying context '%s' SKIP", self.name) + return + if self.stack: LOG.info("Undeploying context '%s' START", self.name) self.stack.delete() self.stack = None LOG.info("Undeploying context '%s' DONE", self.name) - if os.path.exists(self.key_filename): - try: - os.remove(self.key_filename) - os.remove(self.key_filename + ".pub") - except OSError: - LOG.exception("Key filename %s", self.key_filename) + self._delete_key_file() super(HeatContext, self).undeploy() @@ -429,13 +478,17 @@ class HeatContext(Context): server.private_ip = self.stack.outputs.get( attr_name.get("private_ip_attr", object()), None) else: - server = self._server_map.get(attr_name, None) + try: + server = self._server_map[attr_name] + except KeyError: + attr_name_no_suffix = attr_name.split("-")[0] + server = self._server_map.get(attr_name_no_suffix, None) if server is None: return None pkey = pkg_resources.resource_string( 'yardstick.resources', - h_join('files/yardstick_key', get_short_key_uuid(self.key_uuid))).decode('utf-8') + h_join('files/yardstick_key', self.name)).decode('utf-8') result = { "user": server.context.user, diff --git a/yardstick/benchmark/contexts/kubernetes.py b/yardstick/benchmark/contexts/kubernetes.py index 2334e5076..4bea991ea 100644 --- a/yardstick/benchmark/contexts/kubernetes.py +++ b/yardstick/benchmark/contexts/kubernetes.py @@ -29,7 +29,6 @@ class KubernetesContext(Context): __context_type__ = "Kubernetes" def __init__(self): - self.name = '' self.ssh_key = '' self.key_path = '' self.public_key_path = '' @@ -38,7 +37,7 @@ class KubernetesContext(Context): super(KubernetesContext, self).__init__() def init(self, attrs): - self.name = attrs.get('name', '') + super(KubernetesContext, self).init(attrs) template_cfg = attrs.get('servers', {}) self.template = KubernetesTemplate(self.name, template_cfg) diff --git a/yardstick/benchmark/contexts/model.py b/yardstick/benchmark/contexts/model.py index ae56066ee..a55c11f79 100644 --- a/yardstick/benchmark/contexts/model.py +++ b/yardstick/benchmark/contexts/model.py @@ -18,6 +18,8 @@ import logging from collections import Mapping from six.moves import range +from yardstick.common import constants as consts + LOG = logging.getLogger(__name__) @@ -132,11 +134,28 @@ class Network(Object): if self.gateway_ip is None: self.gateway_ip = "null" - if "external_network" in attrs: - self.router = Router("router", self.name, - context, attrs["external_network"]) - - Network.list.append(self) + self.net_flags = attrs.get('net_flags', {}) + if self.is_existing(): + self.subnet = attrs.get('subnet') + if not self.subnet: + raise Warning('No subnet set in existing netwrok!') + else: + if "external_network" in attrs: + self.router = Router("router", self.name, + context, attrs["external_network"]) + Network.list.append(self) + + def is_existing(self): + net_is_existing = self.net_flags.get(consts.IS_EXISTING) + if net_is_existing and not isinstance(net_is_existing, bool): + raise SyntaxError('Network flags should be bool type!') + return net_is_existing + + def is_public(self): + net_is_public = self.net_flags.get(consts.IS_PUBLIC) + if net_is_public and not isinstance(net_is_public, bool): + raise SyntaxError('Network flags should be bool type!') + return net_is_public def has_route_to(self, network_name): """determines if this network has a route to the named network""" @@ -302,10 +321,13 @@ class Server(Object): # pragma: no cover # otherwise add a port for every network with port name as network name else: ports = [network.name] + net_flags = network.net_flags for port in ports: port_name = "{0}-{1}-port".format(server_name, port) - self.ports.setdefault(network.name, []).append( - {"stack_name": port_name, "port": port}) + port_info = {"stack_name": port_name, "port": port} + if net_flags: + port_info['net_flags'] = net_flags + self.ports.setdefault(network.name, []).append(port_info) # we can't use secgroups if port_security_enabled is False if network.port_security_enabled is False: sec_group_id = None @@ -314,11 +336,14 @@ class Server(Object): # pragma: no cover sec_group_id = self.secgroup_name # don't refactor to pass in network object, that causes JSON # circular ref encode errors - template.add_port(port_name, network.stack_name, network.subnet_stack_name, - network.vnic_type, sec_group_id=sec_group_id, + template.add_port(port_name, network, + sec_group_id=sec_group_id, provider=network.provider, allowed_address_pairs=network.allowed_address_pairs) - port_name_list.append(port_name) + if network.is_public(): + port_name_list.insert(0, port_name) + else: + port_name_list.append(port_name) if self.floating_ip: external_network = self.floating_ip["external_network"] diff --git a/yardstick/benchmark/contexts/node.py b/yardstick/benchmark/contexts/node.py index ffc82c8ed..fa619a9aa 100644 --- a/yardstick/benchmark/contexts/node.py +++ b/yardstick/benchmark/contexts/node.py @@ -35,7 +35,6 @@ class NodeContext(Context): __context_type__ = "Node" def __init__(self): - self.name = None self.file_path = None self.nodes = [] self.networks = {} @@ -60,7 +59,8 @@ class NodeContext(Context): def init(self, attrs): """initializes itself from the supplied arguments""" - self.name = attrs["name"] + super(NodeContext, self).init(attrs) + self.file_path = file_path = attrs.get("file", "pod.yaml") try: @@ -157,7 +157,7 @@ class NodeContext(Context): except StopIteration: pass else: - raise ValueError("Duplicate nodes!!! Nodes: %s %s", + raise ValueError("Duplicate nodes!!! Nodes: %s %s" % (node, duplicate)) node["name"] = attr_name @@ -204,7 +204,7 @@ class NodeContext(Context): self.client._put_file_shell(script_file, '~/{}'.format(script)) cmd = 'sudo bash {} {}'.format(script, options) - status, stdout, stderr = self.client.execute(cmd) + status, _, stderr = self.client.execute(cmd) if status: raise RuntimeError(stderr) diff --git a/yardstick/benchmark/contexts/standalone/ovs_dpdk.py b/yardstick/benchmark/contexts/standalone/ovs_dpdk.py index c931d85d0..a18b42ea5 100644 --- a/yardstick/benchmark/contexts/standalone/ovs_dpdk.py +++ b/yardstick/benchmark/contexts/standalone/ovs_dpdk.py @@ -59,7 +59,6 @@ class OvsDpdkContext(Context): self.first_run = True self.dpdk_devbind = '' self.vm_names = [] - self.name = None self.nfvi_host = [] self.nodes = [] self.networks = {} @@ -74,8 +73,8 @@ class OvsDpdkContext(Context): def init(self, attrs): """initializes itself from the supplied arguments""" + super(OvsDpdkContext, self).init(attrs) - self.name = attrs["name"] self.file_path = attrs.get("file", "pod.yaml") self.nodes, self.nfvi_host, self.host_mgmt = \ diff --git a/yardstick/benchmark/contexts/standalone/sriov.py b/yardstick/benchmark/contexts/standalone/sriov.py index 9cca3e15c..d7620552b 100644 --- a/yardstick/benchmark/contexts/standalone/sriov.py +++ b/yardstick/benchmark/contexts/standalone/sriov.py @@ -43,7 +43,6 @@ class SriovContext(Context): self.first_run = True self.dpdk_devbind = '' self.vm_names = [] - self.name = None self.nfvi_host = [] self.nodes = [] self.networks = {} @@ -57,8 +56,8 @@ class SriovContext(Context): def init(self, attrs): """initializes itself from the supplied arguments""" + super(SriovContext, self).init(attrs) - self.name = attrs["name"] self.file_path = attrs.get("file", "pod.yaml") self.nodes, self.nfvi_host, self.host_mgmt = \ diff --git a/yardstick/benchmark/core/__init__.py b/yardstick/benchmark/core/__init__.py index 3e3aa99a1..3914e3237 100644 --- a/yardstick/benchmark/core/__init__.py +++ b/yardstick/benchmark/core/__init__.py @@ -23,6 +23,7 @@ class Param(object): self.task_args_file = kwargs.get('task-args-file') self.keep_deploy = kwargs.get('keep-deploy') self.parse_only = kwargs.get('parse-only') + self.render_only = kwargs.get('render-only') self.output_file = kwargs.get('output-file', '/tmp/yardstick.out') self.suite = kwargs.get('suite') self.task_id = kwargs.get('task_id') diff --git a/yardstick/benchmark/core/report.py b/yardstick/benchmark/core/report.py index 997a125e7..199602444 100644 --- a/yardstick/benchmark/core/report.py +++ b/yardstick/benchmark/core/report.py @@ -45,7 +45,7 @@ class Report(object): self.task_id = "" def _validate(self, yaml_name, task_id): - if re.match("^[a-z0-9_-]+$", yaml_name): + if re.match(r"^[\w-]+$", yaml_name): self.yaml_name = yaml_name else: raise ValueError("invalid yaml_name", yaml_name) @@ -102,10 +102,12 @@ class Report(object): task_time = str(task_time, 'utf8') key = str(key, 'utf8') task_time = task_time[11:] - head, sep, tail = task_time.partition('.') + head, _, tail = task_time.partition('.') task_time = head + "." + tail[:6] self.Timestamp.append(task_time) - if isinstance(task[key], float) is True: + if task[key] is None: + values.append('') + elif isinstance(task[key], (int, float)) is True: values.append(task[key]) else: values.append(ast.literal_eval(task[key])) diff --git a/yardstick/benchmark/core/task.py b/yardstick/benchmark/core/task.py index f5d2b18ac..e7acde696 100644 --- a/yardstick/benchmark/core/task.py +++ b/yardstick/benchmark/core/task.py @@ -7,10 +7,6 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -""" Handler for yardstick command 'task' """ - -from __future__ import absolute_import -from __future__ import print_function import sys import os from collections import OrderedDict @@ -31,9 +27,10 @@ from yardstick.benchmark.runners import base as base_runner from yardstick.common.constants import CONF_FILE from yardstick.common.yaml_loader import yaml_load from yardstick.dispatcher.base import Base as DispatcherBase -from yardstick.common.task_template import TaskTemplate -from yardstick.common import utils from yardstick.common import constants +from yardstick.common import exceptions as y_exc +from yardstick.common import task_template +from yardstick.common import utils from yardstick.common.html_template import report_template output_file_default = "/tmp/yardstick.out" @@ -57,7 +54,7 @@ class Task(object): # pragma: no cover out_types = [s.strip() for s in dispatchers.split(',')] output_config['DEFAULT']['dispatcher'] = out_types - def start(self, args): + def start(self, args, **kwargs): # pylint: disable=unused-argument """Start a benchmark scenario.""" atexit.register(self.atexit_handler) @@ -89,8 +86,7 @@ class Task(object): # pragma: no cover if args.suite: # 1.parse suite, return suite_params info - task_files, task_args, task_args_fnames = \ - parser.parse_suite() + task_files, task_args, task_args_fnames = parser.parse_suite() else: task_files = [parser.path] task_args = [args.task_args] @@ -103,32 +99,33 @@ class Task(object): # pragma: no cover sys.exit(0) testcases = {} - # parse task_files - for i in range(0, len(task_files)): - one_task_start_time = time.time() - parser.path = task_files[i] - scenarios, run_in_parallel, meet_precondition, contexts = \ - parser.parse_task(self.task_id, task_args[i], - task_args_fnames[i]) - - self.contexts.extend(contexts) + tasks = self._parse_tasks(parser, task_files, args, task_args, + task_args_fnames) - if not meet_precondition: - LOG.info("meet_precondition is %s, please check envrionment", - meet_precondition) + # Execute task files. + for i, _ in enumerate(task_files): + one_task_start_time = time.time() + self.contexts.extend(tasks[i]['contexts']) + if not tasks[i]['meet_precondition']: + LOG.info('"meet_precondition" is %s, please check environment', + tasks[i]['meet_precondition']) continue - case_name = os.path.splitext(os.path.basename(task_files[i]))[0] try: - data = self._run(scenarios, run_in_parallel, output_config) + data = self._run(tasks[i]['scenarios'], + tasks[i]['run_in_parallel'], + output_config) except KeyboardInterrupt: raise except Exception: # pylint: disable=broad-except - LOG.error('Testcase: "%s" FAILED!!!', case_name, exc_info=True) - testcases[case_name] = {'criteria': 'FAIL', 'tc_data': []} + LOG.error('Testcase: "%s" FAILED!!!', tasks[i]['case_name'], + exc_info=True) + testcases[tasks[i]['case_name']] = {'criteria': 'FAIL', + 'tc_data': []} else: - LOG.info('Testcase: "%s" SUCCESS!!!', case_name) - testcases[case_name] = {'criteria': 'PASS', 'tc_data': data} + LOG.info('Testcase: "%s" SUCCESS!!!', tasks[i]['case_name']) + testcases[tasks[i]['case_name']] = {'criteria': 'PASS', + 'tc_data': data} if args.keep_deploy: # keep deployment, forget about stack @@ -151,9 +148,8 @@ class Task(object): # pragma: no cover LOG.info("Total finished in %d secs", total_end_time - total_start_time) - scenario = scenarios[0] - LOG.info("To generate report, execute command " - "'yardstick report generate %(task_id)s %(tc)s'", scenario) + LOG.info('To generate report, execute command "yardstick report ' + 'generate %s <YAML_NAME>"', self.task_id) LOG.info("Task ALL DONE, exiting") return result @@ -314,6 +310,30 @@ class Task(object): # pragma: no cover else: return op + def _parse_tasks(self, parser, task_files, args, task_args, + task_args_fnames): + tasks = [] + + # Parse task_files. + for i, _ in enumerate(task_files): + parser.path = task_files[i] + tasks.append(parser.parse_task(self.task_id, task_args[i], + task_args_fnames[i])) + tasks[i]['case_name'] = os.path.splitext( + os.path.basename(task_files[i]))[0] + + if args.render_only: + utils.makedirs(args.render_only) + for idx, task in enumerate(tasks): + output_file_name = os.path.abspath(os.path.join( + args.render_only, + '{0:03d}-{1}.yml'.format(idx, task['case_name']))) + utils.write_file(output_file_name, task['rendered']) + + sys.exit(0) + + return tasks + def run_one_scenario(self, scenario_cfg, output_config): """run one scenario using context""" runner_cfg = scenario_cfg["runner"] @@ -345,7 +365,7 @@ class Task(object): # pragma: no cover try: config_context_target(item) except KeyError: - pass + LOG.debug("Got a KeyError in config_context_target(%s)", item) else: break @@ -479,33 +499,42 @@ class TaskParser(object): # pragma: no cover return valid_task_files, valid_task_args, valid_task_args_fnames - def parse_task(self, task_id, task_args=None, task_args_file=None): - """parses the task file and return an context and scenario instances""" - LOG.info("Parsing task config: %s", self.path) + def _render_task(self, task_args, task_args_file): + """Render the input task with the given arguments + :param task_args: (dict) arguments to render the task + :param task_args_file: (str) file containing the arguments to render + the task + :return: (str) task file rendered + """ try: kw = {} if task_args_file: with open(task_args_file) as f: - kw.update(parse_task_args("task_args_file", f.read())) - kw.update(parse_task_args("task_args", task_args)) + kw.update(parse_task_args('task_args_file', f.read())) + kw.update(parse_task_args('task_args', task_args)) except TypeError: - raise TypeError() + raise y_exc.TaskRenderArgumentError() + input_task = None try: with open(self.path) as f: - try: - input_task = f.read() - rendered_task = TaskTemplate.render(input_task, **kw) - except Exception as e: - LOG.exception('Failed to render template:\n%s\n', input_task) - raise e - LOG.debug("Input task is:\n%s\n", rendered_task) - - cfg = yaml_load(rendered_task) - except IOError as ioerror: - sys.exit(ioerror) + input_task = f.read() + rendered_task = task_template.TaskTemplate.render(input_task, **kw) + LOG.debug('Input task is:\n%s', rendered_task) + parsed_task = yaml_load(rendered_task) + except (IOError, OSError): + raise y_exc.TaskReadError(task_file=self.path) + except Exception: + raise y_exc.TaskRenderError(input_task=input_task) + + return parsed_task, rendered_task + def parse_task(self, task_id, task_args=None, task_args_file=None): + """parses the task file and return an context and scenario instances""" + LOG.info("Parsing task config: %s", self.path) + + cfg, rendered = self._render_task(task_args, task_args_file) self._check_schema(cfg["schema"], "task") meet_precondition = self._check_precondition(cfg) @@ -519,17 +548,15 @@ class TaskParser(object): # pragma: no cover context_cfgs = [{"type": "Dummy"}] contexts = [] - name_suffix = '-{}'.format(task_id[:8]) for cfg_attrs in context_cfgs: - try: - cfg_attrs['name'] = '{}{}'.format(cfg_attrs['name'], - name_suffix) - except KeyError: - pass + + cfg_attrs['task_id'] = task_id # default to Heat context because we are testing OpenStack context_type = cfg_attrs.get("type", "Heat") context = Context.get(context_type) context.init(cfg_attrs) + # Update the name in case the context has used the name_suffix + cfg_attrs['name'] = context.name contexts.append(context) run_in_parallel = cfg.get("run_in_parallel", False) @@ -543,16 +570,74 @@ class TaskParser(object): # pragma: no cover # relative to task path scenario["task_path"] = os.path.dirname(self.path) - change_server_name(scenario, name_suffix) - - try: - for node in scenario['nodes']: - scenario['nodes'][node] += name_suffix - except KeyError: - pass + self._change_node_names(scenario, contexts) # TODO we need something better here, a class that represent the file - return cfg["scenarios"], run_in_parallel, meet_precondition, contexts + return {'scenarios': cfg['scenarios'], + 'run_in_parallel': run_in_parallel, + 'meet_precondition': meet_precondition, + 'contexts': contexts, + 'rendered': rendered} + + @staticmethod + def _change_node_names(scenario, contexts): + """Change the node names in a scenario, depending on the context config + + The nodes (VMs or physical servers) are referred in the context section + with the name of the server and the name of the context: + <server name>.<context name> + + If the context is going to be undeployed at the end of the test, the + task ID is suffixed to the name to avoid interferences with previous + deployments. If the context needs to be deployed at the end of the + test, the name assigned is kept. + + There are several places where a node name could appear in the scenario + configuration: + scenario: + host: athena.demo + target: kratos.demo + targets: + - athena.demo + - kratos.demo + + scenario: + options: + server_name: # JIRA: YARDSTICK-810 + host: athena.demo + target: kratos.demo + + scenario: + nodes: + tg__0: tg_0.yardstick + vnf__0: vnf_0.yardstick + """ + def qualified_name(name): + node_name, context_name = name.split('.') + try: + ctx = next((context for context in contexts + if context.assigned_name == context_name)) + except StopIteration: + raise y_exc.ScenarioConfigContextNameNotFound( + context_name=context_name) + + return '{}.{}'.format(node_name, ctx.name) + + if 'host' in scenario: + scenario['host'] = qualified_name(scenario['host']) + if 'target' in scenario: + scenario['target'] = qualified_name(scenario['target']) + server_name = scenario.get('options', {}).get('server_name', {}) + if 'host' in server_name: + server_name['host'] = qualified_name(server_name['host']) + if 'target' in server_name: + server_name['target'] = qualified_name(server_name['target']) + if 'targets' in scenario: + for idx, target in enumerate(scenario['targets']): + scenario['targets'][idx] = qualified_name(target) + if 'nodes' in scenario: + for scenario_node, target in scenario['nodes'].items(): + scenario['nodes'][scenario_node] = qualified_name(target) def _check_schema(self, cfg_schema, schema_type): """Check if config file is using the correct schema type""" @@ -686,30 +771,3 @@ def parse_task_args(src_name, args): % {"src": src_name, "src_type": type(kw)}) raise TypeError() return kw - - -def change_server_name(scenario, suffix): - - def add_suffix(cfg, key): - try: - value = cfg[key] - except KeyError: - pass - else: - try: - value['name'] += suffix - except TypeError: - cfg[key] += suffix - - server_name = scenario.get('options', {}).get('server_name', {}) - - add_suffix(scenario, 'host') - add_suffix(scenario, 'target') - add_suffix(server_name, 'host') - add_suffix(server_name, 'target') - - try: - key = 'targets' - scenario[key] = ['{}{}'.format(a, suffix) for a in scenario[key]] - except KeyError: - pass diff --git a/yardstick/benchmark/scenarios/availability/ha_tools/nova/add_server_to_existing_secgroup.bash b/yardstick/benchmark/scenarios/availability/ha_tools/nova/add_server_to_existing_secgroup.bash new file mode 100644 index 000000000..3a50626f5 --- /dev/null +++ b/yardstick/benchmark/scenarios/availability/ha_tools/nova/add_server_to_existing_secgroup.bash @@ -0,0 +1,26 @@ +#!/bin/bash + +############################################################################## +# Copyright (c) 2018 Intracom Telecom 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 +############################################################################## + +# add server to existing security group +# parameters: $1 - server name, $2 - security group name + +set -e + +if [ $OS_INSECURE ] && [ "$(echo $OS_INSECURE | tr '[:upper:]' '[:lower:]')" = "true" ]; then + SECURE="--insecure" +else + SECURE="" +fi + +SECGROUPNAME="$(openstack ${SECURE} security group list -f value -c Name | grep $2)" + +openstack ${SECURE} server add security group $1 ${SECGROUPNAME} + diff --git a/yardstick/benchmark/scenarios/availability/ha_tools/nova/create_instance_from_image.bash b/yardstick/benchmark/scenarios/availability/ha_tools/nova/create_instance_from_image.bash new file mode 100644 index 000000000..5e0b1ccf1 --- /dev/null +++ b/yardstick/benchmark/scenarios/availability/ha_tools/nova/create_instance_from_image.bash @@ -0,0 +1,26 @@ +#!/bin/bash + +############################################################################## +# Copyright (c) 2018 Intracom Telecom 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 +############################################################################## + +# create nova server +# parameters: $1 - server name, $2 - image name, $3 - flavor name, $4 - network name + +set -e + +if [ $OS_INSECURE ] && [ "$(echo $OS_INSECURE | tr '[:upper:]' '[:lower:]')" = "true" ]; then + SECURE="--insecure" +else + SECURE="" +fi + +NETNAME="$(openstack ${SECURE} network list -f value -c Name | grep $4)" + +openstack ${SECURE} server create $1 --image $2 --flavor $3 --network ${NETNAME} + diff --git a/yardstick/benchmark/scenarios/availability/ha_tools/nova/delete_instance.bash b/yardstick/benchmark/scenarios/availability/ha_tools/nova/delete_instance.bash new file mode 100644 index 000000000..008e7f5ff --- /dev/null +++ b/yardstick/benchmark/scenarios/availability/ha_tools/nova/delete_instance.bash @@ -0,0 +1,24 @@ +#!/bin/bash + +############################################################################## +# Copyright (c) 2018 Intracom Telecom 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 +############################################################################## + +# delete nova server +# parameters: $1 - server name + +set -e + +if [ $OS_INSECURE ] && [ "$(echo $OS_INSECURE | tr '[:upper:]' '[:lower:]')" = "true" ]; then + SECURE="--insecure" +else + SECURE="" +fi + +openstack ${SECURE} server delete $1 + diff --git a/yardstick/benchmark/scenarios/availability/ha_tools/nova/get_server_privateip.bash b/yardstick/benchmark/scenarios/availability/ha_tools/nova/get_server_privateip.bash new file mode 100644 index 000000000..7f2bad540 --- /dev/null +++ b/yardstick/benchmark/scenarios/availability/ha_tools/nova/get_server_privateip.bash @@ -0,0 +1,24 @@ +#!/bin/bash + +############################################################################## +# Copyright (c) 2018 Intracom Telecom 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 +############################################################################## + +# get private ip of a server +# parameter: $1 - server name + +set -e + +if [ $OS_INSECURE ] && [ "$(echo $OS_INSECURE | tr '[:upper:]' '[:lower:]')" = "true" ]; then + SECURE="--insecure" +else + SECURE="" +fi + +openstack ${SECURE} server list -f value -c Name -c Networks | grep $1 | awk '{print $2}' | sed -r 's/.*=([0-9\.\:]+)[;,]*/\1/' + diff --git a/yardstick/benchmark/scenarios/availability/ha_tools/nova/remove_server_from_secgroup.bash b/yardstick/benchmark/scenarios/availability/ha_tools/nova/remove_server_from_secgroup.bash new file mode 100644 index 000000000..61d0a2b49 --- /dev/null +++ b/yardstick/benchmark/scenarios/availability/ha_tools/nova/remove_server_from_secgroup.bash @@ -0,0 +1,25 @@ +#!/bin/bash + +############################################################################## +# Copyright (c) 2018 Intracom Telecom 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 +############################################################################## + +# remove server from existing security group +# parameters: $1 - server name, $2 - security group name + +set -e + +if [ $OS_INSECURE ] && [ "$(echo $OS_INSECURE | tr '[:upper:]' '[:lower:]')" = "true" ]; then + SECURE="--insecure" +else + SECURE="" +fi + +SECGROUPNAME="$(openstack ${SECURE} security group list -f value -c Name | grep $2)" + +openstack ${SECURE} server remove security group $1 ${SECGROUPNAME} diff --git a/yardstick/benchmark/scenarios/availability/operation_conf.yaml b/yardstick/benchmark/scenarios/availability/operation_conf.yaml index dc5169196..5f3f6c91e 100644 --- a/yardstick/benchmark/scenarios/availability/operation_conf.yaml +++ b/yardstick/benchmark/scenarios/availability/operation_conf.yaml @@ -35,3 +35,14 @@ get-vip-host: action_script: ha_tools/pacemaker/get_vip_host.bash rollback_script: ha_tools/pacemaker/get_resource_status.bash +start-service: + action_script: ha_tools/start_service.bash + rollback_script: ha_tools/check_process_python.bash + +add-server-to-secgroup: + action_script: ha_tools/nova/add_server_to_existing_secgroup.bash + rollback_script: ha_tools/nova/remove_server_from_secgroup.bash + +get-privateip: + action_script: ha_tools/nova/get_server_privateip.bash + rollback_script: ha_tools/nova/list_servers.bash diff --git a/yardstick/benchmark/scenarios/lib/create_network.py b/yardstick/benchmark/scenarios/lib/create_network.py index cffff132a..734820519 100644 --- a/yardstick/benchmark/scenarios/lib/create_network.py +++ b/yardstick/benchmark/scenarios/lib/create_network.py @@ -7,13 +7,12 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -from __future__ import print_function -from __future__ import absolute_import - import logging from yardstick.benchmark.scenarios import base -import yardstick.common.openstack_utils as op_utils +from yardstick.common import openstack_utils +from yardstick.common import exceptions + LOG = logging.getLogger(__name__) @@ -28,9 +27,14 @@ class CreateNetwork(base.Scenario): self.context_cfg = context_cfg self.options = self.scenario_cfg['options'] - self.openstack = self.options.get("openstack_paras", None) + self.network_name = self.options["network_name"] + self.shared = self.options.get("shared", False) + self.admin_state_up = self.options.get("admin_state_up", True) + self.external = self.options.get("external", False) + self.provider = self.options.get("provider") + self.project_id = self.options.get("project_id") - self.neutron_client = op_utils.get_neutron_client() + self.shade_client = openstack_utils.get_shade_client() self.setup_done = False @@ -45,20 +49,17 @@ class CreateNetwork(base.Scenario): if not self.setup_done: self.setup() - openstack_paras = {'network': self.openstack} - network_id = op_utils.create_neutron_net(self.neutron_client, - openstack_paras) - if network_id: - result.update({"network_create": 1}) - LOG.info("Create network successful!") - else: + network_id = openstack_utils.create_neutron_net( + self.shade_client, self.network_name, shared=self.shared, + admin_state_up=self.admin_state_up, external=self.external, + provider=self.provider, project_id=self.project_id) + if not network_id: result.update({"network_create": 0}) LOG.error("Create network failed!") + raise exceptions.ScenarioCreateNetworkError - try: - keys = self.scenario_cfg.get('output', '').split() - except KeyError: - pass - else: - values = [network_id] - return self._push_to_outputs(keys, values) + result.update({"network_create": 1}) + LOG.info("Create network successful!") + keys = self.scenario_cfg.get('output', '').split() + values = [network_id] + return self._push_to_outputs(keys, values) diff --git a/yardstick/benchmark/scenarios/lib/create_subnet.py b/yardstick/benchmark/scenarios/lib/create_subnet.py index c34af8a9e..e383c99de 100644 --- a/yardstick/benchmark/scenarios/lib/create_subnet.py +++ b/yardstick/benchmark/scenarios/lib/create_subnet.py @@ -7,13 +7,12 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -from __future__ import print_function -from __future__ import absolute_import - import logging from yardstick.benchmark.scenarios import base -import yardstick.common.openstack_utils as op_utils +from yardstick.common import openstack_utils +from yardstick.common import exceptions + LOG = logging.getLogger(__name__) @@ -28,9 +27,23 @@ class CreateSubnet(base.Scenario): self.context_cfg = context_cfg self.options = self.scenario_cfg['options'] - self.openstack = self.options.get("openstack_paras", None) - - self.neutron_client = op_utils.get_neutron_client() + self.network_name_or_id = self.options['network_name_or_id'] + self.cidr = self.options.get('cidr') + self.ip_version = self.options.get('ip_version', 4) + self.enable_dhcp = self.options.get('enable_dhcp', False) + self.subnet_name = self.options.get('subnet_name') + self.tenant_id = self.options.get('tenant_id') + self.allocation_pools = self.options.get('allocation_pools') + self.gateway_ip = self.options.get('gateway_ip') + self.disable_gateway_ip = self.options.get('disable_gateway_ip', False) + self.dns_nameservers = self.options.get('dns_nameservers') + self.host_routes = self.options.get('host_routes') + self.ipv6_ra_mode = self.options.get('ipv6_ra_mode') + self.ipv6_address_mode = self.options.get('ipv6_address_mode') + self.use_default_subnetpool = self.options.get( + 'use_default_subnetpool', False) + + self.shade_client = openstack_utils.get_shade_client() self.setup_done = False @@ -45,22 +58,23 @@ class CreateSubnet(base.Scenario): if not self.setup_done: self.setup() - openstack_paras = {'subnets': [self.openstack]} - subnet_id = op_utils.create_neutron_subnet(self.neutron_client, - openstack_paras) - if subnet_id: - result.update({"subnet_create": 1}) - LOG.info("Create subnet successful!") - else: + subnet_id = openstack_utils.create_neutron_subnet( + self.shade_client, self.network_name_or_id, cidr=self.cidr, + ip_version=self.ip_version, enable_dhcp=self.enable_dhcp, + subnet_name=self.subnet_name, tenant_id=self.tenant_id, + allocation_pools=self.allocation_pools, gateway_ip=self.gateway_ip, + disable_gateway_ip=self.disable_gateway_ip, + dns_nameservers=self.dns_nameservers, host_routes=self.host_routes, + ipv6_ra_mode=self.ipv6_ra_mode, + ipv6_address_mode=self.ipv6_address_mode, + use_default_subnetpool=self.use_default_subnetpool) + if not subnet_id: result.update({"subnet_create": 0}) LOG.error("Create subnet failed!") + raise exceptions.ScenarioCreateSubnetError - check_result = subnet_id - - try: - keys = self.scenario_cfg.get('output', '').split() - except KeyError: - pass - else: - values = [check_result] - return self._push_to_outputs(keys, values) + result.update({"subnet_create": 1}) + LOG.info("Create subnet successful!") + keys = self.scenario_cfg.get('output', '').split() + values = [subnet_id] + return self._push_to_outputs(keys, values) diff --git a/yardstick/benchmark/scenarios/lib/delete_router.py b/yardstick/benchmark/scenarios/lib/delete_router.py index 358fd40cf..5e7467b2c 100644 --- a/yardstick/benchmark/scenarios/lib/delete_router.py +++ b/yardstick/benchmark/scenarios/lib/delete_router.py @@ -7,13 +7,12 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -from __future__ import print_function -from __future__ import absolute_import - import logging +from yardstick.common import openstack_utils +from yardstick.common import exceptions from yardstick.benchmark.scenarios import base -import yardstick.common.openstack_utils as op_utils + LOG = logging.getLogger(__name__) @@ -28,9 +27,9 @@ class DeleteRouter(base.Scenario): self.context_cfg = context_cfg self.options = self.scenario_cfg['options'] - self.router_id = self.options.get("router_id", None) + self.router_id = self.options["router_id"] - self.neutron_client = op_utils.get_neutron_client() + self.shade_client = openstack_utils.get_shade_client() self.setup_done = False @@ -45,11 +44,12 @@ class DeleteRouter(base.Scenario): if not self.setup_done: self.setup() - status = op_utils.delete_neutron_router(self.neutron_client, - router_id=self.router_id) - if status: - result.update({"delete_router": 1}) - LOG.info("Delete router successful!") - else: + status = openstack_utils.delete_neutron_router(self.shade_client, + self.router_id) + if not status: result.update({"delete_router": 0}) LOG.error("Delete router failed!") + raise exceptions.ScenarioDeleteRouterError + + result.update({"delete_router": 1}) + LOG.info("Delete router successful!") diff --git a/yardstick/benchmark/scenarios/networking/moongen_testpmd.bash b/yardstick/benchmark/scenarios/networking/moongen_testpmd.bash new file mode 100644 index 000000000..3e92cc900 --- /dev/null +++ b/yardstick/benchmark/scenarios/networking/moongen_testpmd.bash @@ -0,0 +1,62 @@ +############################################################################## +# 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 +############################################################################## +#!/bin/bash + +set -e + +# Commandline arguments +MOONGEN_PORT1_MAC=$1 # MAC address of the peer port +MOONGEN_PORT2_MAC=$2 # MAC address of the peer port +TESTPMD_QUEUE=$3 + +BIND_ROOT='/opt/nsb_bin' +DRIVER_ROOT='/opt/tempT/dpdk-17.02/' + +load_modules() +{ + if ! lsmod | grep "uio" &> /dev/null; then + modprobe uio + fi + + if ! lsmod | grep "igb_uio" &> /dev/null; then + insmod ${DRIVER_ROOT}/x86_64-native-linuxapp-gcc/kmod/igb_uio.ko + fi + + if ! lsmod | grep "rte_kni" &> /dev/null; then + insmod ${DRIVER_ROOT}/x86_64-native-linuxapp-gcc/kmod/rte_kni.ko + fi +} + +change_permissions() +{ + chmod 777 /sys/bus/pci/drivers/virtio-pci/* + chmod 777 /sys/bus/pci/drivers/igb_uio/* +} + +add_interface_to_dpdk(){ + interfaces=$(lspci |grep Eth |tail -n +2 |awk '{print $1}') + ${BIND_ROOT}/dpdk_nic_bind.py --bind=igb_uio $interfaces &> /dev/null +} + +run_testpmd() +{ + blacklist=$(lspci |grep Eth |awk '{print $1}'|head -1) + cd ${DRIVER_ROOT} + sudo ./x86_64-native-linuxapp-gcc/app/testpmd -c 0x3f -n 4 -b $blacklist -- -a --nb-cores=4 --coremask=0x3c --burst=64 --txd=4096 --rxd=4096 --rxq=$TESTPMD_QUEUE --txq=$TESTPMD_QUEUE --rss-udp --eth-peer=0,$MOONGEN_PORT1_MAC --eth-peer=1,$MOONGEN_PORT2_MAC --forward-mode=mac +} + +main() +{ + load_modules + change_permissions + add_interface_to_dpdk + run_testpmd +} + +main diff --git a/yardstick/benchmark/scenarios/networking/moongen_testpmd.py b/yardstick/benchmark/scenarios/networking/moongen_testpmd.py new file mode 100644 index 000000000..86173c9da --- /dev/null +++ b/yardstick/benchmark/scenarios/networking/moongen_testpmd.py @@ -0,0 +1,378 @@ +# Copyright (c) 2018 Huawei Technologies Co.,Ltd and others. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" VsperfDPDK specific scenario definition """ + +from __future__ import absolute_import +import pkg_resources +import logging +import subprocess +import time +import re +from oslo_serialization import jsonutils + +import yardstick.ssh as ssh +import yardstick.common.utils as utils +from yardstick.benchmark.scenarios import base + +LOG = logging.getLogger(__name__) + + +class MoongenTestPMD(base.Scenario): + """Execute vsperf with defined parameters + + Parameters: + frame_size - a frame size for which test should be executed; + Multiple frame sizes can be tested by modification of sequence runner + section inside TC YAML definition. + type: string + default: "64" + multistream - the number of simulated streams + type: string + default: 0 (disabled) + testpmd_queue - specifies how many queues you will use the VM + only useful when forward_type is true. + type: int + default: 1(one queue) + trafficgen_port1 - specifies device name of 1st interface connected to + the trafficgen + type: string + default: NA + trafficgen_port2 - specifies device name of 2nd interface connected to + the trafficgen + type: string + default: NA + moongen_host_user - specifies moongen host ssh user name + type: string + default: root + moongen_host_passwd - specifies moongen host ssh user password + type: string + default: root + moongen_host_ip - specifies moongen host ssh ip address + type: string + default NA + moongen_dir - specifies where is the moongen installtion dir + type: string + default NA + moongen_runBidirec - specifies moongen will run in one traffic + or two traffic. + type: string + default true + Package_Loss - specifies the package_Loss number in moongen server. + type: int + default 0(0%) + SearchRuntime - specifies the SearchRuntime and validation time + on moongen server. + type: int + default 60(s) + moongen_port1_mac - moongen server port1 mac address. + type: string + default NA + moongen_port2_mac - moongen server port2 mac address. + type: string + default NA + forward_type - VM forward type is l2fwd or testpmd. + type: string + default: testpmd + """ + __scenario_type__ = "MoongenTestPMD" + + TESTPMD_SCRIPT = 'moongen_testpmd.bash' + VSPERF_CONFIG = '/tmp/opnfv-vsperf-cfg.lua' + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.forward_setup_done = False + self.options = scenario_cfg.get('options', {}) + self.moongen_host_user = \ + self.options.get('moongen_host_user', "root") + self.moongen_host_passwd = \ + self.options.get('moongen_host_passwd', "r00t") + self.moongen_dir = \ + self.options.get('moongen_dir', '~/moongen.py') + self.testpmd_queue = \ + self.options.get('testpmd_queue', 1) + self.moongen_host_ip = \ + self.options.get('moongen_host_ip', "127.0.0.1") + self.moongen_port1_mac = \ + self.options.get('moongen_port1_mac', None) + self.moongen_port2_mac = \ + self.options.get('moongen_port2_mac', None) + self.tg_port1 = \ + self.options.get('trafficgen_port1', "enp2s0f0") + self.tg_port2 = \ + self.options.get('trafficgen_port2', "enp2s0f1") + self.forward_type = \ + self.options.get('forward_type', 'testpmd') + self.tgen_port1_mac = None + self.tgen_port2_mac = None + + def setup(self): + """scenario setup""" + host = self.context_cfg['host'] + + task_id = self.scenario_cfg['task_id'] + context_number = task_id.split('-')[0] + self.tg_port1_nw = 'demo' + \ + "-" + context_number + "-" + \ + self.options.get('trafficgen_port1_nw', 'test2') + self.tg_port2_nw = 'demo' + \ + "-" + context_number + "-" + \ + self.options.get('trafficgen_port2_nw', 'test3') + + # copy vsperf conf to VM + self.client = ssh.SSH.from_node(host, defaults={"user": "ubuntu"}) + # traffic generation could last long + self.client.wait(timeout=1800) + + self.server = ssh.SSH( + self.moongen_host_user, + self.moongen_host_ip, + password=self.moongen_host_passwd + ) + # traffic generation could last long + self.server.wait(timeout=1800) + + self.setup_done = True + + def forward_setup(self): + """forward tool setup""" + + # setup forward loopback in VM + self.testpmd_script = pkg_resources.resource_filename( + 'yardstick.benchmark.scenarios.networking', + self.TESTPMD_SCRIPT) + + self.client._put_file_shell(self.testpmd_script, + '~/testpmd_vsperf.sh') + + # disable Address Space Layout Randomization (ASLR) + cmd = "echo 0 | sudo tee /proc/sys/kernel/randomize_va_space" + self.client.send_command(cmd) + + if not self._is_forward_setup(): + self.tgen_port1_ip = \ + utils.get_port_ip(self.client, self.tg_port1) + self.tgen_port1_mac = \ + utils.get_port_mac(self.client, self.tg_port1) + self.client.run("tee ~/.testpmd.ipaddr.port1 > /dev/null", + stdin=self.tgen_port1_ip) + self.client.run("tee ~/.testpmd.macaddr.port1 > /dev/null", + stdin=self.tgen_port1_mac) + self.tgen_port2_ip = \ + utils.get_port_ip(self.client, self.tg_port2) + self.tgen_port2_mac = \ + utils.get_port_mac(self.client, self.tg_port2) + self.client.run("tee ~/.testpmd.ipaddr.port2 > /dev/null", + stdin=self.tgen_port2_ip) + self.client.run("tee ~/.testpmd.macaddr.port2 > /dev/null", + stdin=self.tgen_port2_mac) + else: + cmd = "cat ~/.testpmd.macaddr.port1" + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + self.tgen_port1_mac = stdout + cmd = "cat ~/.testpmd.ipaddr.port1" + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + self.tgen_port1_ip = stdout + cmd = "cat ~/.testpmd.macaddr.port2" + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + self.tgen_port2_mac = stdout + cmd = "cat ~/.testpmd.ipaddr.port2" + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + self.tgen_port2_ip = stdout + + LOG.info("forward type is %s", self.forward_type) + if self.forward_type == 'testpmd': + cmd = "sudo ip link set %s down" % (self.tg_port1) + LOG.debug("Executing command: %s", cmd) + self.client.execute(cmd) + cmd = "sudo ip link set %s down" % (self.tg_port2) + LOG.debug("Executing command: %s", cmd) + self.client.execute(cmd) + cmd = "screen -d -m sudo -E bash ~/testpmd_vsperf.sh %s %s %d" % \ + (self.moongen_port1_mac, self.moongen_port2_mac, + self.testpmd_queue) + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.client.execute(cmd) + if status: + raise RuntimeError(stderr) + + elif self.forward_type == 'l2fwd': + cmd = ('sed -i "s/static char *net1 = \\\"eth1\\\";' + '/static char *net1 = \\\"%s %s %s\\\";/g" /home/l2fwd/l2fwd.c' + % (self.tg_port1, self.tgen_port1_ip, self.moongen_port1_mac)) + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.client.execute(cmd) + + cmd = ('sed -i "s/static char *net2 = \\\"eth2\\\";' + '/static char *net2 = \\\"%s %s %s\\\";/g" /home/l2fwd/l2fwd.c' + % (self.tg_port2, self.tgen_port2_ip, self.moongen_port2_mac)) + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.client.execute(cmd) + + cmd = ('cd /home/l2fwd/;make;./gen_debian_package.sh;' + 'sudo dpkg -i *.deb;' + 'sudo modprobe l2fwd') + LOG.debug("Executing command: %s", cmd) + status, stdout, stderr = self.client.execute(cmd) + + time.sleep(1) + + self.forward_setup_done = True + + def _is_forward_setup(self): + """Is forward already setup in the host?""" + if self.forward_type is 'testpmd': + is_run = True + cmd = "ip a | grep %s 2>/dev/null" % (self.tg_port1) + LOG.debug("Executing command: %s", cmd) + _, stdout, _ = self.client.execute(cmd) + if stdout: + is_run = False + return is_run + elif self.forward_type is 'l2fwd': + cmd = ('sudo lsmod |grep l2fwd') + LOG.debug("Executing command: %s", cmd) + _, stdout, _ = self.client.execute(cmd) + if stdout: + return True + else: + return False + + def generate_config_file(self, frame_size, multistream, + runBidirec, tg_port1_vlan, tg_port2_vlan, + SearchRuntime, Package_Loss): + out_text = """\ +VSPERF { +testType = 'throughput', +nrFlows = %d, +runBidirec = %s, +frameSize = %d, +srcMacs = {\'%s\', \'%s\'}, +dstMacs = {\'%s\', \'%s\'}, +vlanIds = {%d, %d}, +searchRunTime = %d, +validationRunTime = %d, +acceptableLossPct = %d, +ports = {0,1}, +} +""" % (multistream, runBidirec, frame_size, self.moongen_port1_mac, + self.moongen_port2_mac, self.tgen_port1_mac, self.tgen_port2_mac, + tg_port1_vlan, tg_port2_vlan, SearchRuntime, SearchRuntime, Package_Loss) + with open(self.VSPERF_CONFIG, "wt") as out_file: + out_file.write(out_text) + self.CONFIG_FILE = True + + def result_to_data(self, result): + search_pattern = re.compile( + r'\[REPORT\]\s+total\:\s+' + r'Tx\s+frames\:\s+(\d+)\s+' + r'Rx\s+Frames\:\s+(\d+)\s+' + r'frame\s+loss\:\s+(\d+)\,' + r'\s+(\d+\.\d+|\d+)%\s+' + r'Tx\s+Mpps\:\s+(\d+.\d+|\d+)\s+' + r'Rx\s+Mpps\:\s+(\d+\.\d+|\d+)', + re.IGNORECASE) + results_match = search_pattern.search(result) + if results_match: + rx_mpps = float(results_match.group(6)) + tx_mpps = float(results_match.group(5)) + else: + rx_mpps = 0 + tx_mpps = 0 + test_result = {"rx_mpps": rx_mpps, "tx_mpps": tx_mpps} + self.TO_DATA = True + return test_result + + def run(self, result): + """ execute the vsperf benchmark and return test results + within result dictionary + """ + + if not self.setup_done: + self.setup() + + # get vsperf options + multistream = self.options.get("multistream", 1) + + if not self.forward_setup_done: + self.forward_setup() + + if 'frame_size' in self.options: + frame_size = self.options.get("frame_size", 64) + Package_Loss = self.options.get("Package_Loss", 0) + runBidirec = self.options.get("moongen_runBidirec", + "true") + SearchRuntime = self.options.get("SearchRuntime", 10) + + cmd = "openstack network show %s --format json -c " \ + "provider:segmentation_id" % (self.tg_port1_nw) + LOG.debug("Executing command: %s", cmd) + output = subprocess.check_output(cmd, shell=True) + try: + tg_port1_vlan = jsonutils.loads(output).get("provider:segmentation_id", 1) + except TypeError: + tg_port1_vlan = 1 + + cmd = "openstack network show %s --format json -c " \ + "provider:segmentation_id" % (self.tg_port2_nw) + LOG.debug("Executing command: %s", cmd) + output = subprocess.check_output(cmd, shell=True) + try: + tg_port2_vlan = jsonutils.loads(output).get("provider:segmentation_id", 2) + except TypeError: + tg_port2_vlan = 2 + + self.generate_config_file(frame_size, multistream, + runBidirec, tg_port1_vlan, + tg_port2_vlan, SearchRuntime, Package_Loss) + + self.server.execute("rm -f -- %s/opnfv-vsperf-cfg.lua" % + (self.moongen_dir)) + self.server._put_file_shell(self.VSPERF_CONFIG, + "%s/opnfv-vsperf-cfg.lua" + % (self.moongen_dir)) + + # execute moongen + cmd = ("cd %s;./MoonGen/build/MoonGen ./trafficgen.lua" + % (self.moongen_dir)) + status, stdout, stderr = self.server.execute(cmd) + if status: + raise RuntimeError(stderr) + + moongen_result = self.result_to_data(stdout) + LOG.info(moongen_result) + result.update(moongen_result) + + if "sla" in self.scenario_cfg: + throughput_rx_mpps = int( + self.scenario_cfg["sla"]["throughput_rx_mpps"]) + + assert throughput_rx_mpps <= moongen_result["tx_mpps"], \ + "sla_throughput_rx_mpps %f > throughput_rx_mpps(%f); " % \ + (throughput_rx_mpps, moongen_result["tx_mpps"]) + + def teardown(self): + """cleanup after the test execution""" + + # execute external setup script + self.setup_done = False diff --git a/yardstick/benchmark/scenarios/networking/netperf.py b/yardstick/benchmark/scenarios/networking/netperf.py index a8d9010ed..33c02d409 100755 --- a/yardstick/benchmark/scenarios/networking/netperf.py +++ b/yardstick/benchmark/scenarios/networking/netperf.py @@ -104,7 +104,9 @@ class Netperf(base.Scenario): cmd_args = "-H %s -l %s -t %s" % (ipaddr, testlen, testname) # get test specific options - default_args = "-O 'THROUGHPUT,THROUGHPUT_UNITS,MEAN_LATENCY'" + output_opt = options.get( + "output_opt", "THROUGHPUT,THROUGHPUT_UNITS,MEAN_LATENCY") + default_args = "-O %s" % output_opt cmd_args += " -- %s" % default_args option_pair_list = [("send_msg_size", "-m"), ("recv_msg_size", "-M"), diff --git a/yardstick/benchmark/scenarios/networking/sfc_openstack.py b/yardstick/benchmark/scenarios/networking/sfc_openstack.py index d5feabbbe..aaab2131a 100644 --- a/yardstick/benchmark/scenarios/networking/sfc_openstack.py +++ b/yardstick/benchmark/scenarios/networking/sfc_openstack.py @@ -34,11 +34,13 @@ def get_credentials(service): # pragma: no cover # The most common way to pass these info to the script is to do it through # environment variables. + # NOTE(ralonsoh): OS_TENANT_NAME is deprecated. + project_name = os.environ.get('OS_PROJECT_NAME', 'admin') creds.update({ "username": os.environ.get('OS_USERNAME', "admin"), password: os.environ.get("OS_PASSWORD", 'admin'), "auth_url": os.environ.get("OS_AUTH_URL"), - tenant: os.environ.get("OS_TENANT_NAME", "admin"), + tenant: os.environ.get("OS_TENANT_NAME", project_name), }) cacert = os.environ.get("OS_CACERT") if cacert is not None: @@ -59,7 +61,7 @@ def get_instances(nova_client): # pragma: no cover try: instances = nova_client.servers.list(search_opts={'all_tenants': 1}) return instances - except Exception as e: + except Exception as e: # pylint: disable=broad-except print("Error [get_instances(nova_client)]:", e) return None @@ -72,7 +74,7 @@ def get_SFs(nova_client): # pragma: no cover if "sfc_test" not in instance.name: SFs.append(instance) return SFs - except Exception as e: + except Exception as e: # pylint: disable=broad-except print("Error [get_SFs(nova_client)]:", e) return None @@ -93,7 +95,7 @@ def create_floating_ips(neutron_client): # pragma: no cover ip_json = neutron_client.create_floatingip({'floatingip': props}) fip_addr = ip_json['floatingip']['floating_ip_address'] ips.append(fip_addr) - except Exception as e: + except Exception as e: # pylint: disable=broad-except print("Error [create_floating_ip(neutron_client)]:", e) return None return ips @@ -106,7 +108,7 @@ def floatIPtoSFs(SFs, floatips): # pragma: no cover SF.add_floating_ip(floatips[i]) i = i + 1 return True - except Exception as e: + except Exception as e: # pylint: disable=broad-except print(("Error [add_floating_ip(nova_client, '%s', '%s')]:" % (SF, floatips[i]), e)) return False @@ -122,7 +124,3 @@ def get_an_IP(): # pragma: no cover floatips = create_floating_ips(neutron_client) floatIPtoSFs(SFs, floatips) return floatips - - -if __name__ == '__main__': # pragma: no cover - get_an_IP() diff --git a/yardstick/benchmark/scenarios/networking/vnf_generic.py b/yardstick/benchmark/scenarios/networking/vnf_generic.py index b94bfc9ab..0e4785294 100644 --- a/yardstick/benchmark/scenarios/networking/vnf_generic.py +++ b/yardstick/benchmark/scenarios/networking/vnf_generic.py @@ -11,115 +11,38 @@ # 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. -""" NSPerf specific scenario definition """ - -from __future__ import absolute_import +import copy import logging -import errno import ipaddress - -import copy +from itertools import chain import os import sys -import re -from itertools import chain import six import yaml -from collections import defaultdict -from yardstick.benchmark.scenarios import base +from yardstick.benchmark.scenarios import base as scenario_base +from yardstick.error import IncorrectConfig from yardstick.common.constants import LOG_DIR from yardstick.common.process import terminate_children -from yardstick.common.utils import import_modules_from_package, itersubclasses -from yardstick.common.yaml_loader import yaml_load +from yardstick.common import utils from yardstick.network_services.collector.subscriber import Collector from yardstick.network_services.vnf_generic import vnfdgen from yardstick.network_services.vnf_generic.vnf.base import GenericVNF -from yardstick.network_services.traffic_profile.base import TrafficProfile +from yardstick.network_services import traffic_profile +from yardstick.network_services.traffic_profile import base as tprofile_base from yardstick.network_services.utils import get_nsb_option from yardstick import ssh - -LOG = logging.getLogger(__name__) - - -class SSHError(Exception): - """Class handles ssh connection error exception""" - pass - - -class SSHTimeout(SSHError): - """Class handles ssh connection timeout exception""" - pass - - -class IncorrectConfig(Exception): - """Class handles incorrect configuration during setup""" - pass - - -class IncorrectSetup(Exception): - """Class handles incorrect setup during setup""" - pass +traffic_profile.register_modules() -class SshManager(object): - def __init__(self, node, timeout=120): - super(SshManager, self).__init__() - self.node = node - self.conn = None - self.timeout = timeout - - def __enter__(self): - """ - args -> network device mappings - returns -> ssh connection ready to be used - """ - try: - self.conn = ssh.SSH.from_node(self.node) - self.conn.wait(timeout=self.timeout) - except SSHError as error: - LOG.info("connect failed to %s, due to %s", self.node["ip"], error) - # self.conn defaults to None - return self.conn - - def __exit__(self, exc_type, exc_val, exc_tb): - if self.conn: - self.conn.close() - - -def find_relative_file(path, task_path): - """ - Find file in one of places: in abs of path or - relative to TC scenario file. In this order. - - :param path: - :param task_path: - :return str: full path to file - """ - # fixme: create schema to validate all fields have been provided - for lookup in [os.path.abspath(path), os.path.join(task_path, path)]: - try: - with open(lookup): - return lookup - except IOError: - pass - raise IOError(errno.ENOENT, 'Unable to find {} file'.format(path)) - - -def open_relative_file(path, task_path): - try: - return open(path) - except IOError as e: - if e.errno == errno.ENOENT: - return open(os.path.join(task_path, path)) - raise +LOG = logging.getLogger(__name__) -class NetworkServiceTestCase(base.Scenario): +class NetworkServiceTestCase(scenario_base.Scenario): """Class handles Generic framework to do pre-deployment VNF & Network service testing """ @@ -130,16 +53,12 @@ class NetworkServiceTestCase(base.Scenario): self.scenario_cfg = scenario_cfg self.context_cfg = context_cfg - # fixme: create schema to validate all fields have been provided - with open_relative_file(scenario_cfg["topology"], - scenario_cfg['task_path']) as stream: - topology_yaml = yaml_load(stream) - - self.topology = topology_yaml["nsd:nsd-catalog"]["nsd"][0] + self._render_topology() self.vnfs = [] self.collector = None self.traffic_profile = None self.node_netdevs = {} + self.bin_path = get_nsb_option('bin_path', '') def _get_ip_flow_range(self, ip_start_range): @@ -211,37 +130,47 @@ class NetworkServiceTestCase(base.Scenario): def _get_traffic_profile(self): profile = self.scenario_cfg["traffic_profile"] path = self.scenario_cfg["task_path"] - with open_relative_file(profile, path) as infile: + with utils.open_relative_file(profile, path) as infile: + return infile.read() + + def _get_topology(self): + topology = self.scenario_cfg["topology"] + path = self.scenario_cfg["task_path"] + with utils.open_relative_file(topology, path) as infile: return infile.read() def _fill_traffic_profile(self): - traffic_mapping = self._get_traffic_profile() - traffic_map_data = { + tprofile = self._get_traffic_profile() + extra_args = self.scenario_cfg.get('extra_args', {}) + tprofile_data = { 'flow': self._get_traffic_flow(), 'imix': self._get_traffic_imix(), - TrafficProfile.UPLINK: {}, - TrafficProfile.DOWNLINK: {}, + tprofile_base.TrafficProfile.UPLINK: {}, + tprofile_base.TrafficProfile.DOWNLINK: {}, + 'extra_args': extra_args } - traffic_vnfd = vnfdgen.generate_vnfd(traffic_mapping, traffic_map_data) - self.traffic_profile = TrafficProfile.get(traffic_vnfd) - return self.traffic_profile + traffic_vnfd = vnfdgen.generate_vnfd(tprofile, tprofile_data) + self.traffic_profile = tprofile_base.TrafficProfile.get(traffic_vnfd) + + def _render_topology(self): + topology = self._get_topology() + topology_args = self.scenario_cfg.get('extra_args', {}) + topolgy_data = { + 'extra_args': topology_args + } + topology_yaml = vnfdgen.generate_vnfd(topology, topolgy_data) + self.topology = topology_yaml["nsd:nsd-catalog"]["nsd"][0] def _find_vnf_name_from_id(self, vnf_id): return next((vnfd["vnfd-id-ref"] for vnfd in self.topology["constituent-vnfd"] if vnf_id == vnfd["member-vnf-index"]), None) - @staticmethod - def get_vld_networks(networks): - # network name is vld_id - vld_map = {} - for name, n in networks.items(): - try: - vld_map[n['vld_id']] = n - except KeyError: - vld_map[name] = n - return vld_map + def _find_vnfd_from_vnf_idx(self, vnf_id): + return next((vnfd + for vnfd in self.topology["constituent-vnfd"] + if vnf_id == vnfd["member-vnf-index"]), None) @staticmethod def find_node_if(nodes, name, if_name, vld_id): @@ -293,7 +222,9 @@ class NetworkServiceTestCase(base.Scenario): node1_if["peer_ifname"] = node0_if_name # just load the network - vld_networks = self.get_vld_networks(self.context_cfg["networks"]) + vld_networks = {n.get('vld_id', name): n for name, n in + self.context_cfg["networks"].items()} + node0_if["network"] = vld_networks.get(vld["id"], {}) node1_if["network"] = vld_networks.get(vld["id"], {}) @@ -332,10 +263,6 @@ class NetworkServiceTestCase(base.Scenario): node0_if["peer_intf"] = node1_copy node1_if["peer_intf"] = node0_copy - def _find_vnfd_from_vnf_idx(self, vnf_idx): - return next((vnfd for vnfd in self.topology["constituent-vnfd"] - if vnf_idx == vnfd["member-vnf-index"]), None) - def _update_context_with_topology(self): for vnfd in self.topology["constituent-vnfd"]: vnf_idx = vnfd["member-vnf-index"] @@ -343,43 +270,6 @@ class NetworkServiceTestCase(base.Scenario): vnfd = self._find_vnfd_from_vnf_idx(vnf_idx) self.context_cfg["nodes"][vnf_name].update(vnfd) - def _probe_netdevs(self, node, node_dict, timeout=120): - try: - return self.node_netdevs[node] - except KeyError: - pass - - netdevs = {} - cmd = "PATH=$PATH:/sbin:/usr/sbin ip addr show" - - with SshManager(node_dict, timeout=timeout) as conn: - if conn: - exit_status = conn.execute(cmd)[0] - if exit_status != 0: - raise IncorrectSetup("Node's %s lacks ip tool." % node) - exit_status, stdout, _ = conn.execute( - self.FIND_NETDEVICE_STRING) - if exit_status != 0: - raise IncorrectSetup( - "Cannot find netdev info in sysfs" % node) - netdevs = node_dict['netdevs'] = self.parse_netdev_info(stdout) - - self.node_netdevs[node] = netdevs - return netdevs - - @classmethod - def _probe_missing_values(cls, netdevs, network): - - mac_lower = network['local_mac'].lower() - for netdev in netdevs.values(): - if netdev['address'].lower() != mac_lower: - continue - network.update({ - 'driver': netdev['driver'], - 'vpci': netdev['pci_bus_id'], - 'ifindex': netdev['ifindex'], - }) - def _generate_pod_yaml(self): context_yaml = os.path.join(LOG_DIR, "pod-{}.yaml".format(self.scenario_cfg['task_id'])) # convert OrderedDict to a list @@ -405,82 +295,16 @@ class NetworkServiceTestCase(base.Scenario): pass return new_node - TOPOLOGY_REQUIRED_KEYS = frozenset({ - "vpci", "local_ip", "netmask", "local_mac", "driver"}) - def map_topology_to_infrastructure(self): """ This method should verify if the available resources defined in pod.yaml match the topology.yaml file. :return: None. Side effect: context_cfg is updated """ - num_nodes = len(self.context_cfg["nodes"]) - # OpenStack instance creation time is probably proportional to the number - # of instances - timeout = 120 * num_nodes - for node, node_dict in self.context_cfg["nodes"].items(): - - for network in node_dict["interfaces"].values(): - missing = self.TOPOLOGY_REQUIRED_KEYS.difference(network) - if not missing: - continue - - # only ssh probe if there are missing values - # ssh probe won't work on Ixia, so we had better define all our values - try: - netdevs = self._probe_netdevs(node, node_dict, timeout=timeout) - except (SSHError, SSHTimeout): - raise IncorrectConfig( - "Unable to probe missing interface fields '%s', on node %s " - "SSH Error" % (', '.join(missing), node)) - try: - self._probe_missing_values(netdevs, network) - except KeyError: - pass - else: - missing = self.TOPOLOGY_REQUIRED_KEYS.difference( - network) - if missing: - raise IncorrectConfig( - "Require interface fields '%s' not found, topology file " - "corrupted" % ', '.join(missing)) - - # we have to generate pod.yaml here so we have vpci and driver - self._generate_pod_yaml() # 3. Use topology file to find connections & resolve dest address self._resolve_topology() self._update_context_with_topology() - FIND_NETDEVICE_STRING = r"""find /sys/devices/pci* -type d -name net -exec sh -c '{ grep -sH ^ \ -$1/ifindex $1/address $1/operstate $1/device/vendor $1/device/device \ -$1/device/subsystem_vendor $1/device/subsystem_device ; \ -printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \ -' sh \{\}/* \; -""" - BASE_ADAPTER_RE = re.compile( - '^/sys/devices/(.*)/net/([^/]*)/([^:]*):(.*)$', re.M) - - @classmethod - def parse_netdev_info(cls, stdout): - network_devices = defaultdict(dict) - matches = cls.BASE_ADAPTER_RE.findall(stdout) - for bus_path, interface_name, name, value in matches: - dirname, bus_id = os.path.split(bus_path) - if 'virtio' in bus_id: - # for some stupid reason VMs include virtio1/ - # in PCI device path - bus_id = os.path.basename(dirname) - # remove extra 'device/' from 'device/vendor, - # device/subsystem_vendor', etc. - if 'device/' in name: - name = name.split('/')[1] - network_devices[interface_name][name] = value - network_devices[interface_name][ - 'interface_name'] = interface_name - network_devices[interface_name]['pci_bus_id'] = bus_id - # convert back to regular dict - return dict(network_devices) - @classmethod def get_vnf_impl(cls, vnf_model_id): """ Find the implementing class from vnf_model["vnf"]["name"] field @@ -488,13 +312,14 @@ printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \ :param vnf_model_id: parsed vnfd model ID field :return: subclass of GenericVNF """ - import_modules_from_package( + utils.import_modules_from_package( "yardstick.network_services.vnf_generic.vnf") expected_name = vnf_model_id classes_found = [] def impl(): - for name, class_ in ((c.__name__, c) for c in itersubclasses(GenericVNF)): + for name, class_ in ((c.__name__, c) for c in + utils.itersubclasses(GenericVNF)): if name == expected_name: yield class_ classes_found.append(name) @@ -547,7 +372,7 @@ printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \ context_cfg = self.context_cfg vnfs = [] - # we assume OrderedDict for consistenct in instantiation + # we assume OrderedDict for consistency in instantiation for node_name, node in context_cfg["nodes"].items(): LOG.debug(node) try: @@ -556,7 +381,7 @@ printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \ LOG.debug("no model for %s, skipping", node_name) continue file_path = scenario_cfg['task_path'] - with open_relative_file(file_name, file_path) as stream: + with utils.open_relative_file(file_name, file_path) as stream: vnf_model = stream.read() vnfd = vnfdgen.generate_vnfd(vnf_model, node) # TODO: here add extra context_cfg["nodes"] regardless of template @@ -606,6 +431,9 @@ printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \ vnf.terminate() raise + # we have to generate pod.yaml here after VNF has probed so we know vpci and driver + self._generate_pod_yaml() + # 3. Run experiment # Start listeners first to avoid losing packets for traffic_gen in traffic_runners: diff --git a/yardstick/benchmark/scenarios/storage/fio.py b/yardstick/benchmark/scenarios/storage/fio.py index 125bc7ed4..d3ed840d8 100644 --- a/yardstick/benchmark/scenarios/storage/fio.py +++ b/yardstick/benchmark/scenarios/storage/fio.py @@ -124,12 +124,16 @@ class Fio(base.Scenario): if mount_dir: LOG.debug("Formating volume...") - self.client.execute("sudo mkfs.ext4 /dev/vdb") - cmd = "sudo mkdir %s" % mount_dir - self.client.execute(cmd) - LOG.debug("Mounting volume at: %s", mount_dir) - cmd = "sudo mount /dev/vdb %s" % mount_dir - self.client.execute(cmd) + _, stdout, _ = self.client.execute( + "lsblk -dps | grep -m 1 disk | awk '{print $1}'") + block_device = stdout.strip() + if block_device: + self.client.execute("sudo mkfs.ext4 %s" % block_device) + cmd = "sudo mkdir %s" % mount_dir + self.client.execute(cmd) + LOG.debug("Mounting volume at: %s", mount_dir) + cmd = "sudo mount %s %s" % (block_device, mount_dir) + self.client.execute(cmd) self.setup_done = True diff --git a/yardstick/cmd/cli.py b/yardstick/cmd/cli.py index 67ce11451..0bc7c1617 100644 --- a/yardstick/cmd/cli.py +++ b/yardstick/cmd/cli.py @@ -28,6 +28,8 @@ from yardstick.cmd.commands import testcase from yardstick.cmd.commands import plugin from yardstick.cmd.commands import env from yardstick.cmd.commands import report +from yardstick.common import import_tools + CONF = cfg.CONF cli_opts = [ @@ -53,7 +55,8 @@ def find_config_files(path_list): return None -class YardstickCLI(): # pragma: no cover +@import_tools.decorator_banned_modules +class YardstickCLI(object): # pragma: no cover """Command-line interface to yardstick""" # Command categories @@ -108,7 +111,7 @@ class YardstickCLI(): # pragma: no cover # register subcommands to parse additional command line arguments def parser(subparsers): - self._add_command_parsers(YardstickCLI.categories, subparsers) + self._add_command_parsers(self.categories, subparsers) category_opt = cfg.SubCommandOpt("category", title="Command categories", diff --git a/yardstick/cmd/commands/task.py b/yardstick/cmd/commands/task.py index e2e8bf67d..a3488a23d 100644 --- a/yardstick/cmd/commands/task.py +++ b/yardstick/cmd/commands/task.py @@ -7,10 +7,6 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -""" Handler for yardstick command 'task' """ -from __future__ import print_function -from __future__ import absolute_import - import logging from yardstick.benchmark.core.task import Task @@ -42,6 +38,8 @@ class TaskCommands(object): # pragma: no cover action="store_true") @cliargs("--parse-only", help="parse the config file and exit", action="store_true") + @cliargs("--render-only", help="Render the tasks files, store the result " + "in the directory given and exit", type=str, dest="render_only") @cliargs("--output-file", help="file where output is stored, default %s" % output_file_default, default=output_file_default) @cliargs("--suite", help="process test suite file instead of a task file", @@ -54,9 +52,8 @@ class TaskCommands(object): # pragma: no cover LOG.info('Task START') try: result = Task().start(param, **kwargs) - except Exception as e: + except Exception as e: # pylint: disable=broad-except self._write_error_data(e) - LOG.exception("") if result.get('result', {}).get('criteria') == 'PASS': LOG.info('Task SUCCESS') diff --git a/yardstick/common/ansible_common.py b/yardstick/common/ansible_common.py index be262c215..38d2dd7c2 100644 --- a/yardstick/common/ansible_common.py +++ b/yardstick/common/ansible_common.py @@ -31,6 +31,7 @@ import six.moves.configparser as ConfigParser import yaml from six import StringIO from chainmap import ChainMap +from oslo_serialization import jsonutils from yardstick.common.utils import Timer from yardstick.common import constants as consts @@ -508,6 +509,58 @@ class AnsibleCommon(object): timeout = 1200.0 return timeout + def _generate_ansible_cfg(self, directory): + parser = ConfigParser.ConfigParser() + parser.add_section('defaults') + parser.set('defaults', 'host_key_checking', 'False') + + cfg_path = os.path.join(directory, 'setup.cfg') + with open(cfg_path, 'w') as f: + parser.write(f) + + def get_sut_info(self, directory, sut_dir='sut'): + if not os.path.isdir(directory): + raise OSError('No such directory: %s' % directory) + + self._generate_ansible_cfg(directory) + + prefix = 'tmp' + self.gen_inventory_ini_dict() + ini_file = self._gen_ansible_inventory_file(directory, prefix=prefix) + with ini_file as f: + inventory_path = str(f) + + self._exec_get_sut_info_cmd(directory, inventory_path, sut_dir) + + sut_dir = os.path.join(directory, sut_dir) + sut_info = self._gen_sut_info_dict(sut_dir) + + return sut_info + + def _exec_get_sut_info_cmd(self, directory, inventory_path, sut_dir): + cmd = ['ansible', 'all', '-m', 'setup', '-i', + inventory_path, '--tree', sut_dir] + + proc = Popen(cmd, stdout=PIPE, cwd=directory) + output, _ = proc.communicate() + retcode = proc.wait() + LOG.debug("exit status = %s", retcode) + if retcode != 0: + raise CalledProcessError(retcode, cmd, output) + + def _gen_sut_info_dict(self, sut_dir): + sut_info = {} + + if os.path.isdir(sut_dir): + root, _, files = next(os.walk(sut_dir)) + for filename in files: + abs_path = os.path.join(root, filename) + with open(abs_path) as f: + data = jsonutils.load(f) + sut_info[filename] = data + + return sut_info + def execute_ansible(self, playbooks, directory, timeout=None, extra_vars=None, ansible_check=False, prefix='tmp', verbose=False): diff --git a/yardstick/common/constants.py b/yardstick/common/constants.py index 43c2c19cb..153bd4bf4 100644 --- a/yardstick/common/constants.py +++ b/yardstick/common/constants.py @@ -145,6 +145,10 @@ BASE_URL = 'http://localhost:5000' ENV_ACTION_API = BASE_URL + '/yardstick/env/action' ASYNC_TASK_API = BASE_URL + '/yardstick/asynctask' +# flags +IS_EXISTING = 'is_existing' +IS_PUBLIC = 'is_public' + # general TESTCASE_PRE = 'opnfv_yardstick_' TESTSUITE_PRE = 'opnfv_' diff --git a/yardstick/common/exceptions.py b/yardstick/common/exceptions.py index 3e0635e46..633b36f91 100644 --- a/yardstick/common/exceptions.py +++ b/yardstick/common/exceptions.py @@ -59,14 +59,64 @@ class FunctionNotImplemented(YardstickException): '"%(class_name)" class.') +class YardstickBannedModuleImported(YardstickException): + # pragma: no cover + message = 'Module "%(module)s" cannnot be imported. Reason: "%(reason)s"' + + class HeatTemplateError(YardstickException): """Error in Heat during the stack deployment""" message = ('Error in Heat during the creation of the OpenStack stack ' - '"%(stack_name)"') + '"%(stack_name)s"') class IPv6RangeError(YardstickException): message = 'Start IP "%(start_ip)s" is greater than end IP "%(end_ip)s"' + +class TrafficProfileNotImplemented(YardstickException): + message = 'No implementation for traffic profile %(profile_class)s.' + + class DPDKSetupDriverError(YardstickException): message = '"igb_uio" driver is not loaded' + + +class ScenarioConfigContextNameNotFound(YardstickException): + message = 'Context name "%(context_name)s" not found' + + +class StackCreationInterrupt(YardstickException): + message = 'Stack create interrupted.' + + +class TaskRenderArgumentError(YardstickException): + message = 'Error reading the task input arguments' + + +class TaskReadError(YardstickException): + message = 'Failed to read task %(task_file)s' + + +class TaskRenderError(YardstickException): + message = 'Failed to render template:\n%(input_task)s' + + +class ScenarioCreateNetworkError(YardstickException): + message = 'Create Neutron Network Scenario failed' + + +class ScenarioCreateSubnetError(YardstickException): + message = 'Create Neutron Subnet Scenario failed' + + +class ScenarioDeleteRouterError(YardstickException): + message = 'Delete Neutron Router Scenario failed' + + +class MissingPodInfoError(YardstickException): + message = 'Missing pod args, please check' + + +class UnsupportedPodFormatError(YardstickException): + message = 'Failed to load pod info, unsupported format' diff --git a/yardstick/common/import_tools.py b/yardstick/common/import_tools.py new file mode 100644 index 000000000..bf8dc7e07 --- /dev/null +++ b/yardstick/common/import_tools.py @@ -0,0 +1,31 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import sys + +from yardstick.common import exceptions + + +BANNED_MODULES = {'ansible': 'Module with GPLv3 license'} + + +def decorator_banned_modules(cls): + def _class(*args, **kwargs): + for module in sys.modules: + for banned_module, reason in BANNED_MODULES.items(): + if module.startswith(banned_module): + raise exceptions.YardstickBannedModuleImported( + module=banned_module, reason=reason) + return cls(*args, **kwargs) + return _class diff --git a/yardstick/common/openstack_utils.py b/yardstick/common/openstack_utils.py index 8f666e268..84bfbbbb1 100644 --- a/yardstick/common/openstack_utils.py +++ b/yardstick/common/openstack_utils.py @@ -33,38 +33,22 @@ DEFAULT_API_VERSION = '2' # CREDENTIALS # ********************************************* def get_credentials(): - """Returns a creds dictionary filled with parsed from env""" - creds = {} - - keystone_api_version = os.getenv('OS_IDENTITY_API_VERSION') - - if keystone_api_version is None or keystone_api_version == '2': - keystone_v3 = False - tenant_env = 'OS_TENANT_NAME' - tenant = 'tenant_name' - else: - keystone_v3 = True - tenant_env = 'OS_PROJECT_NAME' - tenant = 'project_name' - - # The most common way to pass these info to the script is to do it - # through environment variables. - creds.update({ - "username": os.environ.get("OS_USERNAME"), - "password": os.environ.get("OS_PASSWORD"), - "auth_url": os.environ.get("OS_AUTH_URL"), - tenant: os.environ.get(tenant_env) - }) - - if keystone_v3: - if os.getenv('OS_USER_DOMAIN_NAME') is not None: - creds.update({ - "user_domain_name": os.getenv('OS_USER_DOMAIN_NAME') - }) - if os.getenv('OS_PROJECT_DOMAIN_NAME') is not None: - creds.update({ - "project_domain_name": os.getenv('OS_PROJECT_DOMAIN_NAME') - }) + """Returns a creds dictionary filled with parsed from env + + Keystone API version used is 3; v2 was deprecated in 2014 (Icehouse). Along + with this deprecation, environment variable 'OS_TENANT_NAME' is replaced by + 'OS_PROJECT_NAME'. + """ + creds = {'username': os.environ.get('OS_USERNAME'), + 'password': os.environ.get('OS_PASSWORD'), + 'auth_url': os.environ.get('OS_AUTH_URL'), + 'project_name': os.environ.get('OS_PROJECT_NAME') + } + + if os.getenv('OS_USER_DOMAIN_NAME'): + creds['user_domain_name'] = os.getenv('OS_USER_DOMAIN_NAME') + if os.getenv('OS_PROJECT_DOMAIN_NAME'): + creds['project_domain_name'] = os.getenv('OS_PROJECT_DOMAIN_NAME') return creds @@ -294,8 +278,7 @@ def create_instance_and_wait_for_active(json_body): # pragma: no cover VM_BOOT_TIMEOUT = 180 nova_client = get_nova_client() instance = create_instance(json_body) - count = VM_BOOT_TIMEOUT / SLEEP - for _ in range(count, -1, -1): + for _ in range(int(VM_BOOT_TIMEOUT / SLEEP)): status = get_instance_status(nova_client, instance) if status.lower() == "active": return instance @@ -452,13 +435,29 @@ def get_network_id(shade_client, network_name): return networks[0]['id'] -def create_neutron_net(neutron_client, json_body): # pragma: no cover - try: - network = neutron_client.create_network(body=json_body) - return network['network']['id'] - except Exception: # pylint: disable=broad-except - log.error("Error [create_neutron_net(neutron_client)]") - raise Exception("operation error") +def create_neutron_net(shade_client, network_name, shared=False, + admin_state_up=True, external=False, provider=None, + project_id=None): + """Create a neutron network. + + :param network_name:(string) name of the network being created. + :param shared:(bool) whether the network is shared. + :param admin_state_up:(bool) set the network administrative state. + :param external:(bool) whether this network is externally accessible. + :param provider:(dict) a dict of network provider options. + :param project_id:(string) specify the project ID this network + will be created on (admin-only). + :returns:(string) the network id. + """ + try: + networks = shade_client.create_network( + name=network_name, shared=shared, admin_state_up=admin_state_up, + external=external, provider=provider, project_id=project_id) + return networks['id'] + except exc.OpenStackCloudException as o_exc: + log.error("Error [create_neutron_net(shade_client)]." + "Exception message, '%s'", o_exc.orig_message) + return None def delete_neutron_net(shade_client, network_id): @@ -469,13 +468,55 @@ def delete_neutron_net(shade_client, network_id): return False -def create_neutron_subnet(neutron_client, json_body): # pragma: no cover - try: - subnet = neutron_client.create_subnet(body=json_body) - return subnet['subnets'][0]['id'] - except Exception: # pylint: disable=broad-except - log.error("Error [create_neutron_subnet") - raise Exception("operation error") +def create_neutron_subnet(shade_client, network_name_or_id, cidr=None, + ip_version=4, enable_dhcp=False, subnet_name=None, + tenant_id=None, allocation_pools=None, + gateway_ip=None, disable_gateway_ip=False, + dns_nameservers=None, host_routes=None, + ipv6_ra_mode=None, ipv6_address_mode=None, + use_default_subnetpool=False): + """Create a subnet on a specified network. + + :param network_name_or_id:(string) the unique name or ID of the + attached network. If a non-unique name is + supplied, an exception is raised. + :param cidr:(string) the CIDR. + :param ip_version:(int) the IP version. + :param enable_dhcp:(bool) whether DHCP is enable. + :param subnet_name:(string) the name of the subnet. + :param tenant_id:(string) the ID of the tenant who owns the network. + :param allocation_pools: A list of dictionaries of the start and end + addresses for the allocation pools. + :param gateway_ip:(string) the gateway IP address. + :param disable_gateway_ip:(bool) whether gateway IP address is enabled. + :param dns_nameservers: A list of DNS name servers for the subnet. + :param host_routes: A list of host route dictionaries for the subnet. + :param ipv6_ra_mode:(string) IPv6 Router Advertisement mode. + Valid values are: 'dhcpv6-stateful', + 'dhcpv6-stateless', or 'slaac'. + :param ipv6_address_mode:(string) IPv6 address mode. + Valid values are: 'dhcpv6-stateful', + 'dhcpv6-stateless', or 'slaac'. + :param use_default_subnetpool:(bool) use the default subnetpool for + ``ip_version`` to obtain a CIDR. It is + required to pass ``None`` to the ``cidr`` + argument when enabling this option. + :returns:(string) the subnet id. + """ + try: + subnet = shade_client.create_subnet( + network_name_or_id, cidr=cidr, ip_version=ip_version, + enable_dhcp=enable_dhcp, subnet_name=subnet_name, + tenant_id=tenant_id, allocation_pools=allocation_pools, + gateway_ip=gateway_ip, disable_gateway_ip=disable_gateway_ip, + dns_nameservers=dns_nameservers, host_routes=host_routes, + ipv6_ra_mode=ipv6_ra_mode, ipv6_address_mode=ipv6_address_mode, + use_default_subnetpool=use_default_subnetpool) + return subnet['id'] + except exc.OpenStackCloudException as o_exc: + log.error("Error [create_neutron_subnet(shade_client)]. " + "Exception message: %s", o_exc.orig_message) + return None def create_neutron_router(neutron_client, json_body): # pragma: no cover @@ -487,13 +528,12 @@ def create_neutron_router(neutron_client, json_body): # pragma: no cover raise Exception("operation error") -def delete_neutron_router(neutron_client, router_id): # pragma: no cover +def delete_neutron_router(shade_client, router_id): try: - neutron_client.delete_router(router=router_id) - return True - except Exception: # pylint: disable=broad-except - log.error("Error [delete_neutron_router(neutron_client, '%s')]", - router_id) + return shade_client.delete_router(router_id) + except exc.OpenStackCloudException as o_exc: + log.error("Error [delete_neutron_router(shade_client, '%s')]. " + "Exception message: %s", router_id, o_exc.orig_message) return False diff --git a/yardstick/common/packages.py b/yardstick/common/packages.py new file mode 100644 index 000000000..f20217fdc --- /dev/null +++ b/yardstick/common/packages.py @@ -0,0 +1,87 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import re + +import pip +from pip import exceptions as pip_exceptions +from pip.operations import freeze + +from yardstick.common import privsep + + +LOG = logging.getLogger(__name__) + +ACTION_INSTALL = 'install' +ACTION_UNINSTALL = 'uninstall' + + +@privsep.yardstick_root.entrypoint +def _pip_main(package, action, target=None): + if action == ACTION_UNINSTALL: + cmd = [action, package, '-y'] + elif action == ACTION_INSTALL: + cmd = [action, package, '--upgrade'] + if target: + cmd.append('--target=%s' % target) + return pip.main(cmd) + + +def _pip_execute_action(package, action=ACTION_INSTALL, target=None): + """Execute an action with a PIP package. + + According to [1], a package could be a URL, a local directory, a local dist + file or a requirements file. + + [1] https://pip.pypa.io/en/stable/reference/pip_install/#argument-handling + """ + try: + status = _pip_main(package, action, target) + except pip_exceptions.PipError: + status = 1 + + if not status: + LOG.info('Action "%s" executed, package %s', package, action) + else: + LOG.info('Error executing action "%s", package %s', package, action) + return status + + +def pip_remove(package): + """Remove an installed PIP package""" + return _pip_execute_action(package, action=ACTION_UNINSTALL) + + +def pip_install(package, target=None): + """Install a PIP package""" + return _pip_execute_action(package, action=ACTION_INSTALL, target=target) + + +def pip_list(pkg_name=None): + """Dict of installed PIP packages with version. + + If 'pkg_name' is not None, will return only those packages matching the + name.""" + pip_regex = re.compile(r"(?P<name>.*)==(?P<version>[\w\.]+)") + git_regex = re.compile(r".*@(?P<version>[\w]+)#egg=(?P<name>[\w]+)") + + pkg_dict = {} + for _pkg in freeze.freeze(local_only=True): + match = pip_regex.match(_pkg) or git_regex.match(_pkg) + if match and (not pkg_name or ( + pkg_name and match.group('name').find(pkg_name) != -1)): + pkg_dict[match.group('name')] = match.group('version') + + return pkg_dict diff --git a/yardstick/common/privsep.py b/yardstick/common/privsep.py new file mode 100644 index 000000000..4ae510489 --- /dev/null +++ b/yardstick/common/privsep.py @@ -0,0 +1,23 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from oslo_privsep import capabilities as c +from oslo_privsep import priv_context + +yardstick_root = priv_context.PrivContext( + "yardstick", + cfg_section="yardstick_privileged", + pypath=__name__ + ".yardstick_root", + capabilities=[c.CAP_SYS_ADMIN] +) diff --git a/yardstick/common/utils.py b/yardstick/common/utils.py index 495290122..357f66be8 100644 --- a/yardstick/common/utils.py +++ b/yardstick/common/utils.py @@ -31,6 +31,7 @@ import six from flask import jsonify from six.moves import configparser from oslo_serialization import jsonutils +from oslo_utils import encodeutils import yardstick @@ -65,7 +66,7 @@ def itersubclasses(cls, _seen=None): yield sub -def import_modules_from_package(package): +def import_modules_from_package(package, raise_exception=False): """Import modules given a package name :param: package - Full package name. For example: rally.deploy.engines @@ -86,10 +87,27 @@ def import_modules_from_package(package): for module_name in missing_modules: try: importlib.import_module(module_name) - except (ImportError, SyntaxError): + except (ImportError, SyntaxError) as exc: + if raise_exception: + raise exc logger.exception('Unable to import module %s', module_name) +NON_NONE_DEFAULT = object() + + +def get_key_with_default(data, key, default=NON_NONE_DEFAULT): + value = data.get(key, default) + if value is NON_NONE_DEFAULT: + raise KeyError(key) + return value + + +def make_dict_from_map(data, key_map): + return {dest_key: get_key_with_default(data, src_key, default) + for dest_key, (src_key, default) in key_map.items()} + + def makedirs(d): try: os.makedirs(d) @@ -106,13 +124,12 @@ def remove_file(path): raise -def execute_command(cmd): +def execute_command(cmd, **kwargs): exec_msg = "Executing command: '%s'" % cmd logger.debug(exec_msg) - output = subprocess.check_output(cmd.split()).split(os.linesep) - - return output + output = subprocess.check_output(cmd.split(), **kwargs) + return encodeutils.safe_decode(output, incoming='utf-8').split(os.linesep) def source_env(env_file): @@ -410,3 +427,31 @@ def read_meminfo(ssh_client): output[match[0]] = match[1] return output + + +def find_relative_file(path, task_path): + """ + Find file in one of places: in abs of path or relative to a directory path, + in this order. + + :param path: + :param task_path: + :return str: full path to file + """ + # fixme: create schema to validate all fields have been provided + for lookup in [os.path.abspath(path), os.path.join(task_path, path)]: + try: + with open(lookup): + return lookup + except IOError: + pass + raise IOError(errno.ENOENT, 'Unable to find {} file'.format(path)) + + +def open_relative_file(path, task_path): + try: + return open(path) + except IOError as e: + if e.errno == errno.ENOENT: + return open(os.path.join(task_path, path)) + raise diff --git a/yardstick/error.py b/yardstick/error.py new file mode 100644 index 000000000..9b84de1af --- /dev/null +++ b/yardstick/error.py @@ -0,0 +1,48 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class SSHError(Exception): + """Class handles ssh connection error exception""" + pass + + +class SSHTimeout(SSHError): + """Class handles ssh connection timeout exception""" + pass + + +class IncorrectConfig(Exception): + """Class handles incorrect configuration during setup""" + pass + + +class IncorrectSetup(Exception): + """Class handles incorrect setup during setup""" + pass + + +class IncorrectNodeSetup(IncorrectSetup): + """Class handles incorrect setup during setup""" + pass + + +class ErrorClass(object): + + def __init__(self, *args, **kwargs): + if 'test' not in kwargs: + raise RuntimeError + + def __getattr__(self, item): + raise AttributeError diff --git a/yardstick/network_services/constants.py b/yardstick/network_services/constants.py new file mode 100644 index 000000000..0064b4fc5 --- /dev/null +++ b/yardstick/network_services/constants.py @@ -0,0 +1,19 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +REMOTE_TMP = "/tmp" +DEFAULT_VNF_TIMEOUT = 3600 +PROCESS_JOIN_TIMEOUT = 3 +ONE_GIGABIT_IN_BITS = 1000000000 +NIC_GBPS_DEFAULT = 10 diff --git a/yardstick/network_services/helpers/dpdkbindnic_helper.py b/yardstick/network_services/helpers/dpdkbindnic_helper.py index 8c44b26c2..05b822c2e 100644 --- a/yardstick/network_services/helpers/dpdkbindnic_helper.py +++ b/yardstick/network_services/helpers/dpdkbindnic_helper.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2017 Intel Corporation +# Copyright (c) 2016-2018 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,11 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. import logging +import os import re -import itertools +from collections import defaultdict +from itertools import chain -import six +from yardstick.common.utils import validate_non_string_sequence +from yardstick.error import IncorrectConfig +from yardstick.error import IncorrectSetup +from yardstick.error import IncorrectNodeSetup +from yardstick.error import SSHTimeout +from yardstick.error import SSHError NETWORK_KERNEL = 'network_kernel' NETWORK_DPDK = 'network_dpdk' @@ -25,7 +32,6 @@ CRYPTO_KERNEL = 'crypto_kernel' CRYPTO_DPDK = 'crypto_dpdk' CRYPTO_OTHER = 'crypto_other' - LOG = logging.getLogger(__name__) @@ -33,6 +39,166 @@ class DpdkBindHelperException(Exception): pass +class DpdkInterface(object): + TOPOLOGY_REQUIRED_KEYS = frozenset({ + "vpci", "local_ip", "netmask", "local_mac", "driver"}) + + def __init__(self, dpdk_node, interface): + super(DpdkInterface, self).__init__() + self.dpdk_node = dpdk_node + self.interface = interface + + try: + assert self.local_mac + except (AssertionError, KeyError): + raise IncorrectConfig + + @property + def local_mac(self): + return self.interface['local_mac'] + + @property + def mac_lower(self): + return self.local_mac.lower() + + @property + def missing_fields(self): + return self.TOPOLOGY_REQUIRED_KEYS.difference(self.interface) + + @staticmethod + def _detect_socket(netdev): + try: + socket = netdev['numa_node'] + except KeyError: + # Where is this documented? + # It seems for dual-sockets systems the second socket PCI bridge + # will have an address > 0x0f, e.g. + # Bridge PCI->PCI (P#524320 busid=0000:80:02.0 id=8086:6f04 + if netdev['pci_bus_id'][5] == "0": + socket = 0 + else: + # this doesn't handle quad-sockets + # TODO: fix this for quad-socket + socket = 1 + return socket + + def probe_missing_values(self): + try: + for netdev in self.dpdk_node.netdevs.values(): + if netdev['address'].lower() == self.mac_lower: + socket = self._detect_socket(netdev) + self.interface.update({ + 'vpci': netdev['pci_bus_id'], + 'driver': netdev['driver'], + 'socket': socket, + # don't need ifindex + }) + + except KeyError: + # if we don't find all the keys then don't update + pass + + except (IncorrectNodeSetup, SSHError, SSHTimeout): + raise IncorrectConfig( + "Unable to probe missing interface fields '%s', on node %s " + "SSH Error" % (', '.join(self.missing_fields), self.dpdk_node.node_key)) + + +class DpdkNode(object): + + def __init__(self, node_name, interfaces, ssh_helper, timeout=120): + super(DpdkNode, self).__init__() + self.interfaces = interfaces + self.ssh_helper = ssh_helper + self.node_key = node_name + self.timeout = timeout + self._dpdk_helper = None + self.netdevs = {} + + try: + self.dpdk_interfaces = {intf['name']: DpdkInterface(self, intf['virtual-interface']) + for intf in self.interfaces} + except IncorrectConfig: + template = "MAC address is required for all interfaces, missing on: {}" + errors = (intf['name'] for intf in self.interfaces if + 'local_mac' not in intf['virtual-interface']) + raise IncorrectSetup(template.format(", ".join(errors))) + + @property + def dpdk_helper(self): + if not isinstance(self._dpdk_helper, DpdkBindHelper): + self._dpdk_helper = DpdkBindHelper(self.ssh_helper) + return self._dpdk_helper + + @property + def _interface_missing_iter(self): + return chain.from_iterable(self._interface_missing_map.values()) + + @property + def _interface_missing_map(self): + return {name: intf.missing_fields for name, intf in self.dpdk_interfaces.items()} + + def _probe_netdevs(self): + self.netdevs.update(self.dpdk_helper.find_net_devices()) + + def _force_rebind(self): + return self.dpdk_helper.force_dpdk_rebind() + + def _probe_dpdk_drivers(self): + self.dpdk_helper.probe_real_kernel_drivers() + for pci, driver in self.dpdk_helper.real_kernel_interface_driver_map.items(): + for intf in self.interfaces: + vintf = intf['virtual-interface'] + # stupid substring matches + # don't use netdev use interface + if vintf['vpci'].endswith(pci): + vintf['driver'] = driver + # we can't update netdevs because we may not have netdev info + + def _probe_missing_values(self): + for intf in self.dpdk_interfaces.values(): + intf.probe_missing_values() + + def check(self): + # only ssh probe if there are missing values + # ssh probe won't work on Ixia, so we had better define all our values + try: + missing_fields_set = set(self._interface_missing_iter) + + # if we are only missing driver then maybe we can get kernel module + # this requires vpci + if missing_fields_set == {'driver'}: + self._probe_dpdk_drivers() + # we can't reprobe missing values because we may not have netdev info + + # if there are any other missing then we have to netdev probe + if missing_fields_set.difference({'driver'}): + self._probe_netdevs() + try: + self._probe_missing_values() + except IncorrectConfig: + # ignore for now + pass + + # check again and verify we have all the fields + if set(self._interface_missing_iter): + # last chance fallback, rebind everything and probe + # this probably won't work + self._force_rebind() + self._probe_netdevs() + self._probe_missing_values() + + errors = ("{} missing: {}".format(name, ", ".join(missing_fields)) for + name, missing_fields in self._interface_missing_map.items() if + missing_fields) + errors = "\n".join(errors) + if errors: + raise IncorrectSetup(errors) + + finally: + self._dpdk_helper = None + + class DpdkBindHelper(object): DPDK_STATUS_CMD = "{dpdk_devbind} --status" DPDK_BIND_CMD = "sudo {dpdk_devbind} {force} -b {driver} {vpci}" @@ -42,6 +208,8 @@ class DpdkBindHelper(object): SKIP_RE = re.compile('(====|<none>|^$)') NIC_ROW_FIELDS = ['vpci', 'dev_type', 'iface', 'driver', 'unused', 'active'] + UIO_DRIVER = "uio" + HEADER_DICT_PAIRS = [ (re.compile('^Network.*DPDK.*$'), NETWORK_DPDK), (re.compile('^Network.*kernel.*$'), NETWORK_KERNEL), @@ -51,6 +219,42 @@ class DpdkBindHelper(object): (re.compile('^Other crypto.*$'), CRYPTO_OTHER), ] + FIND_NETDEVICE_STRING = r"""\ +find /sys/devices/pci* -type d -name net -exec sh -c '{ grep -sH ^ \ +$1/ifindex $1/address $1/operstate $1/device/vendor $1/device/device \ +$1/device/subsystem_vendor $1/device/subsystem_device $1/device/numa_node ; \ +printf "%s/driver:" $1 ; basename $(readlink -s $1/device/driver); } \ +' sh \{\}/* \; +""" + + BASE_ADAPTER_RE = re.compile('^/sys/devices/(.*)/net/([^/]*)/([^:]*):(.*)$', re.M) + DPDK_DEVBIND = "dpdk-devbind.py" + + @classmethod + def parse_netdev_info(cls, stdout): + network_devices = defaultdict(dict) + match_iter = (match.groups() for match in cls.BASE_ADAPTER_RE.finditer(stdout)) + for bus_path, interface_name, name, value in match_iter: + dir_name, bus_id = os.path.split(bus_path) + if 'virtio' in bus_id: + # for some stupid reason VMs include virtio1/ + # in PCI device path + bus_id = os.path.basename(dir_name) + + # remove extra 'device/' from 'device/vendor, + # device/subsystem_vendor', etc. + if 'device' in name: + name = name.split('/')[1] + + network_devices[interface_name].update({ + name: value, + 'interface_name': interface_name, + 'pci_bus_id': bus_id, + }) + + # convert back to regular dict + return dict(network_devices) + def clean_status(self): self.dpdk_status = { NETWORK_KERNEL: [], @@ -61,11 +265,17 @@ class DpdkBindHelper(object): CRYPTO_OTHER: [], } - def __init__(self, ssh_helper): + # TODO: add support for driver other than igb_uio + def __init__(self, ssh_helper, dpdk_driver="igb_uio"): + self.ssh_helper = ssh_helper + self.real_kernel_interface_driver_map = {} + self.dpdk_driver = dpdk_driver self.dpdk_status = None self.status_nic_row_re = None - self._dpdk_devbind = None + self.dpdk_devbind = self.ssh_helper.join_bin_path(self.DPDK_DEVBIND) self._status_cmd_attr = None + self.used_drivers = None + self.real_kernel_drivers = {} self.ssh_helper = ssh_helper self.clean_status() @@ -73,15 +283,16 @@ class DpdkBindHelper(object): def _dpdk_execute(self, *args, **kwargs): res = self.ssh_helper.execute(*args, **kwargs) if res[0] != 0: - raise DpdkBindHelperException('{} command failed with rc={}'.format( - self.dpdk_devbind, res[0])) + template = '{} command failed with rc={}' + raise DpdkBindHelperException(template.format(self.dpdk_devbind, res[0])) return res - @property - def dpdk_devbind(self): - if self._dpdk_devbind is None: - self._dpdk_devbind = self.ssh_helper.provision_tool(tool_file="dpdk-devbind.py") - return self._dpdk_devbind + def load_dpdk_driver(self): + cmd_template = "sudo modprobe {} && sudo modprobe {}" + self.ssh_helper.execute(cmd_template.format(self.UIO_DRIVER, self.dpdk_driver)) + + def check_dpdk_driver(self): + return self.ssh_helper.execute("lsmod | grep -i {}".format(self.dpdk_driver))[0] @property def _status_cmd(self): @@ -89,12 +300,14 @@ class DpdkBindHelper(object): self._status_cmd_attr = self.DPDK_STATUS_CMD.format(dpdk_devbind=self.dpdk_devbind) return self._status_cmd_attr - def _addline(self, active_list, line): + def _add_line(self, active_list, line): if active_list is None: return + res = self.NIC_ROW_RE.match(line) if res is None: return + new_data = {k: v for k, v in zip(self.NIC_ROW_FIELDS, res.groups())} new_data['active'] = bool(new_data['active']) self.dpdk_status[active_list].append(new_data) @@ -106,14 +319,14 @@ class DpdkBindHelper(object): return a_dict return active_dict - def parse_dpdk_status_output(self, input): + def _parse_dpdk_status_output(self, output): active_dict = None self.clean_status() - for a_row in input.splitlines(): + for a_row in output.splitlines(): if self.SKIP_RE.match(a_row): continue active_dict = self._switch_active_dict(a_row, active_dict) - self._addline(active_dict, a_row) + self._add_line(active_dict, a_row) return self.dpdk_status def _get_bound_pci_addresses(self, active_dict): @@ -130,31 +343,85 @@ class DpdkBindHelper(object): @property def interface_driver_map(self): return {interface['vpci']: interface['driver'] - for interface in itertools.chain.from_iterable(self.dpdk_status.values())} + for interface in chain.from_iterable(self.dpdk_status.values())} def read_status(self): - return self.parse_dpdk_status_output(self._dpdk_execute(self._status_cmd)[1]) + return self._parse_dpdk_status_output(self._dpdk_execute(self._status_cmd)[1]) + + def find_net_devices(self): + exit_status, stdout, _ = self.ssh_helper.execute(self.FIND_NETDEVICE_STRING) + if exit_status != 0: + return {} + + return self.parse_netdev_info(stdout) def bind(self, pci_addresses, driver, force=True): - # accept single PCI or list of PCI - if isinstance(pci_addresses, six.string_types): - pci_addresses = [pci_addresses] + # accept single PCI or sequence of PCI + pci_addresses = validate_non_string_sequence(pci_addresses, [pci_addresses]) + cmd = self.DPDK_BIND_CMD.format(dpdk_devbind=self.dpdk_devbind, driver=driver, vpci=' '.join(list(pci_addresses)), force='--force' if force else '') LOG.debug(cmd) self._dpdk_execute(cmd) + # update the inner status dict self.read_status() + def probe_real_kernel_drivers(self): + self.read_status() + self.save_real_kernel_interface_driver_map() + + def force_dpdk_rebind(self): + self.load_dpdk_driver() + self.read_status() + self.save_real_kernel_interface_driver_map() + self.save_used_drivers() + + real_driver_map = {} + # only rebind devices that are bound to DPDK + for pci in self.dpdk_bound_pci_addresses: + # messy + real_driver = self.real_kernel_interface_driver_map[pci] + real_driver_map.setdefault(real_driver, []).append(pci) + for real_driver, pcis in real_driver_map.items(): + self.bind(pcis, real_driver, force=True) + def save_used_drivers(self): # invert the map, so we can bind by driver type self.used_drivers = {} - # sort for stabililty + # sort for stability for vpci, driver in sorted(self.interface_driver_map.items()): self.used_drivers.setdefault(driver, []).append(vpci) + KERNEL_DRIVER_RE = re.compile(r"Kernel modules: (\S+)", re.M) + VIRTIO_DRIVER_RE = re.compile(r"Ethernet.*Virtio network device", re.M) + VIRTIO_DRIVER = "virtio-pci" + + def save_real_kernel_drivers(self): + # invert the map, so we can bind by driver type + self.real_kernel_drivers = {} + # sort for stability + for vpci, driver in sorted(self.real_kernel_interface_driver_map.items()): + self.used_drivers.setdefault(driver, []).append(vpci) + + def get_real_kernel_driver(self, pci): + out = self.ssh_helper.execute('lspci -k -s %s' % pci)[1] + match = self.KERNEL_DRIVER_RE.search(out) + if match: + return match.group(1) + + match = self.VIRTIO_DRIVER_RE.search(out) + if match: + return self.VIRTIO_DRIVER + + return None + + def save_real_kernel_interface_driver_map(self): + iter1 = ((pci, self.get_real_kernel_driver(pci)) for pci in self.interface_driver_map) + self.real_kernel_interface_driver_map = {pci: driver for pci, driver in iter1 if driver} + def rebind_drivers(self, force=True): for driver, vpcis in self.used_drivers.items(): self.bind(vpcis, driver, force) diff --git a/yardstick/network_services/traffic_profile/__init__.py b/yardstick/network_services/traffic_profile/__init__.py index e69de29bb..356b36bd9 100644 --- a/yardstick/network_services/traffic_profile/__init__.py +++ b/yardstick/network_services/traffic_profile/__init__.py @@ -0,0 +1,33 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import importlib + + +def register_modules(): + modules = [ + 'yardstick.network_services.traffic_profile.trex_traffic_profile', + 'yardstick.network_services.traffic_profile.fixed', + 'yardstick.network_services.traffic_profile.http', + 'yardstick.network_services.traffic_profile.http_ixload', + 'yardstick.network_services.traffic_profile.ixia_rfc2544', + 'yardstick.network_services.traffic_profile.prox_ACL', + 'yardstick.network_services.traffic_profile.prox_binsearch', + 'yardstick.network_services.traffic_profile.prox_profile', + 'yardstick.network_services.traffic_profile.prox_ramp', + 'yardstick.network_services.traffic_profile.rfc2544', + ] + + for module in modules: + importlib.import_module(module) diff --git a/yardstick/network_services/traffic_profile/base.py b/yardstick/network_services/traffic_profile/base.py index ad256b444..162bab2bc 100644 --- a/yardstick/network_services/traffic_profile/base.py +++ b/yardstick/network_services/traffic_profile/base.py @@ -11,10 +11,9 @@ # 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. -""" Base class for the generic traffic profile implementation """ -from __future__ import absolute_import -from yardstick.common.utils import import_modules_from_package, itersubclasses +from yardstick.common import exceptions +from yardstick.common import utils class TrafficProfile(object): @@ -33,13 +32,12 @@ class TrafficProfile(object): :return: """ profile_class = tp_config["traffic_profile"]["traffic_type"] - import_modules_from_package( - "yardstick.network_services.traffic_profile") try: - return next(c for c in itersubclasses(TrafficProfile) + return next(c for c in utils.itersubclasses(TrafficProfile) if c.__name__ == profile_class)(tp_config) except StopIteration: - raise RuntimeError("No implementation for %s", profile_class) + raise exceptions.TrafficProfileNotImplemented( + profile_class=profile_class) def __init__(self, tp_config): # e.g. RFC2544 start_ip, stop_ip, drop_rate, diff --git a/yardstick/network_services/traffic_profile/ixia_rfc2544.py b/yardstick/network_services/traffic_profile/ixia_rfc2544.py index 3ab157dc7..7f047226b 100644 --- a/yardstick/network_services/traffic_profile/ixia_rfc2544.py +++ b/yardstick/network_services/traffic_profile/ixia_rfc2544.py @@ -15,7 +15,7 @@ from __future__ import absolute_import import logging -from yardstick.network_services.traffic_profile.traffic_profile import \ +from yardstick.network_services.traffic_profile.trex_traffic_profile import \ TrexProfile LOG = logging.getLogger(__name__) diff --git a/yardstick/network_services/traffic_profile/prox_binsearch.py b/yardstick/network_services/traffic_profile/prox_binsearch.py index 5700f98e5..c3277fb12 100644 --- a/yardstick/network_services/traffic_profile/prox_binsearch.py +++ b/yardstick/network_services/traffic_profile/prox_binsearch.py @@ -20,6 +20,7 @@ import datetime import time from yardstick.network_services.traffic_profile.prox_profile import ProxProfile +from yardstick.network_services import constants LOG = logging.getLogger(__name__) @@ -99,9 +100,13 @@ class ProxBinSearchProfile(ProxProfile): # throughput and packet loss from the most recent successful test successful_pkt_loss = 0.0 + line_speed = traffic_gen.scenario_helper.all_options.get( + "interface_speed_gbps", constants.NIC_GBPS_DEFAULT) * constants.ONE_GIGABIT_IN_BITS for test_value in self.bounds_iterator(LOG): result, port_samples = self._profile_helper.run_test(pkt_size, duration, - test_value, self.tolerated_loss) + test_value, + self.tolerated_loss, + line_speed) self.curr_time = time.time() diff_time = self.curr_time - self.prev_time self.prev_time = self.curr_time diff --git a/yardstick/network_services/traffic_profile/prox_profile.py b/yardstick/network_services/traffic_profile/prox_profile.py index 170dfd96f..343ef1da2 100644 --- a/yardstick/network_services/traffic_profile/prox_profile.py +++ b/yardstick/network_services/traffic_profile/prox_profile.py @@ -29,8 +29,22 @@ class ProxProfile(TrafficProfile): """ @staticmethod + def sort_vpci(traffic_gen): + """Return the list of external interfaces ordered by vpci and name + + :param traffic_gen: (ProxTrafficGen) traffic generator + :return: list of ordered interfaces + """ + def key_func(interface): + return interface['virtual-interface']['vpci'], interface['name'] + + return sorted(traffic_gen.vnfd_helper['vdu'][0]['external-interface'], + key=key_func) + + @staticmethod def fill_samples(samples, traffic_gen): - for vpci_idx, intf in enumerate(traffic_gen.vpci_if_name_ascending): + vpci_if_name_ascending = ProxProfile.sort_vpci(traffic_gen) + for vpci_idx, intf in enumerate(vpci_if_name_ascending): name = intf[1] # TODO: VNFDs KPIs values needs to be mapped to TRex structure xe_port = traffic_gen.resource_helper.sut.port_stats([vpci_idx]) diff --git a/yardstick/network_services/traffic_profile/rfc2544.py b/yardstick/network_services/traffic_profile/rfc2544.py index b1ca8a345..83020c85c 100644 --- a/yardstick/network_services/traffic_profile/rfc2544.py +++ b/yardstick/network_services/traffic_profile/rfc2544.py @@ -21,7 +21,7 @@ from trex_stl_lib.trex_stl_client import STLStream from trex_stl_lib.trex_stl_streams import STLFlowLatencyStats from trex_stl_lib.trex_stl_streams import STLTXCont -from yardstick.network_services.traffic_profile.traffic_profile \ +from yardstick.network_services.traffic_profile.trex_traffic_profile \ import TrexProfile LOGGING = logging.getLogger(__name__) diff --git a/yardstick/network_services/traffic_profile/traffic_profile.py b/yardstick/network_services/traffic_profile/trex_traffic_profile.py index 8cde5e4a7..f5e3923d5 100644 --- a/yardstick/network_services/traffic_profile/traffic_profile.py +++ b/yardstick/network_services/traffic_profile/trex_traffic_profile.py @@ -21,7 +21,7 @@ import ipaddress import six from yardstick.common import exceptions as y_exc -from yardstick.network_services.traffic_profile.base import TrafficProfile +from yardstick.network_services.traffic_profile import base from trex_stl_lib.trex_stl_client import STLStream from trex_stl_lib.trex_stl_streams import STLFlowLatencyStats from trex_stl_lib.trex_stl_streams import STLTXCont @@ -48,7 +48,7 @@ TYPE_OF_SERVICE = 'tos' LOG = logging.getLogger(__name__) -class TrexProfile(TrafficProfile): +class TrexProfile(base.TrafficProfile): """ This class handles Trex Traffic profile generation and execution """ PROTO_MAP = { @@ -127,7 +127,7 @@ class TrexProfile(TrafficProfile): self.vm_flow_vars.append(stl_vm_wr_flow_var) return partial - def _dscp_range_action_partial(self, *_): + def _dscp_range_action_partial(self, *args): def partial(min_value, max_value, count): # pylint: disable=unused-argument stl_vm_flow_var = STLVmFlowVar(name="dscp", diff --git a/yardstick/network_services/utils.py b/yardstick/network_services/utils.py index 7a1815eb9..4b987fafe 100644 --- a/yardstick/network_services/utils.py +++ b/yardstick/network_services/utils.py @@ -121,7 +121,6 @@ def provision_tool(connection, tool_path, tool_file=None): tool_path = get_nsb_option('tool_path') if tool_file: tool_path = os.path.join(tool_path, tool_file) - bin_path = get_nsb_option("bin_path") exit_status = connection.execute("which %s > /dev/null 2>&1" % tool_path)[0] if exit_status == 0: return encodeutils.safe_decode(tool_path, incoming='utf-8').rstrip() diff --git a/yardstick/network_services/vnf_generic/vnf/acl_vnf.py b/yardstick/network_services/vnf_generic/vnf/acl_vnf.py index 1390dd02e..f3cafef7a 100644 --- a/yardstick/network_services/vnf_generic/vnf/acl_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/acl_vnf.py @@ -12,11 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import -from __future__ import print_function import logging -from yardstick.benchmark.scenarios.networking.vnf_generic import find_relative_file +from yardstick.common import utils from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF, DpdkVnfSetupEnvHelper from yardstick.network_services.yang_model import YangModel @@ -62,8 +60,9 @@ class AclApproxVnf(SampleVNF): self.acl_rules = None def _start_vnf(self): - yang_model_path = find_relative_file(self.scenario_helper.options['rules'], - self.scenario_helper.task_path) + yang_model_path = utils.find_relative_file( + self.scenario_helper.options['rules'], + self.scenario_helper.task_path) yang_model = YangModel(yang_model_path) self.acl_rules = yang_model.get_rules() super(AclApproxVnf, self)._start_vnf() diff --git a/yardstick/network_services/vnf_generic/vnf/prox_helpers.py b/yardstick/network_services/vnf_generic/vnf/prox_helpers.py index edb29640e..31ed30140 100644 --- a/yardstick/network_services/vnf_generic/vnf/prox_helpers.py +++ b/yardstick/network_services/vnf_generic/vnf/prox_helpers.py @@ -30,12 +30,12 @@ import six from six.moves import cStringIO from six.moves import zip, StringIO -from yardstick.benchmark.scenarios.networking.vnf_generic import find_relative_file from yardstick.common import utils from yardstick.common.utils import SocketTopology, join_non_strings, try_int from yardstick.network_services.helpers.iniparser import ConfigParser from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper from yardstick.network_services.vnf_generic.vnf.sample_vnf import DpdkVnfSetupEnvHelper +from yardstick.network_services import constants PROX_PORT = 8474 @@ -45,7 +45,6 @@ SECTION_CONTENTS = 1 LOG = logging.getLogger(__name__) LOG.setLevel(logging.DEBUG) -TEN_GIGABIT = 1e10 BITS_PER_BYTE = 8 RETRY_SECONDS = 60 RETRY_INTERVAL = 1 @@ -467,13 +466,14 @@ class ProxSocketHelper(object): core_data['current'] = core_data[key1] + core_data[key2] self.set_speed(core_data['cores'], core_data['current']) - def set_pps(self, cores, pps, pkt_size): + def set_pps(self, cores, pps, pkt_size, + line_speed=(constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)): """ set packets per second for specific cores on the remote instance """ msg = "Set packets per sec for core(s) %s to %g%% of line rate (packet size: %d)" LOG.debug(msg, cores, pps, pkt_size) # speed in percent of line-rate - speed = float(pps) * (pkt_size + 20) / TEN_GIGABIT / BITS_PER_BYTE + speed = float(pps) * (pkt_size + 20) / line_speed / BITS_PER_BYTE self._run_template_over_cores("speed {} 0 {}\n", cores, speed) def lat_stats(self, cores, task=0): @@ -812,7 +812,7 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper): options = self.scenario_helper.options config_path = options['prox_config'] config_file = os.path.basename(config_path) - config_path = find_relative_file(config_path, task_path) + config_path = utils.find_relative_file(config_path, task_path) self.additional_files = {} try: @@ -829,7 +829,7 @@ class ProxDpdkVnfSetupEnvHelper(DpdkVnfSetupEnvHelper): prox_files = [prox_files] for key_prox_file in prox_files: base_prox_file = os.path.basename(key_prox_file) - key_prox_path = find_relative_file(key_prox_file, task_path) + key_prox_path = utils.find_relative_file(key_prox_file, task_path) remote_prox_file = self.copy_to_target(key_prox_path, base_prox_file) self.additional_files[base_prox_file] = remote_prox_file @@ -982,12 +982,13 @@ class ProxResourceHelper(ClientResourceHelper): class ProxDataHelper(object): - def __init__(self, vnfd_helper, sut, pkt_size, value, tolerated_loss): + def __init__(self, vnfd_helper, sut, pkt_size, value, tolerated_loss, line_speed): super(ProxDataHelper, self).__init__() self.vnfd_helper = vnfd_helper self.sut = sut self.pkt_size = pkt_size self.value = value + self.line_speed = line_speed self.tolerated_loss = tolerated_loss self.port_count = len(self.vnfd_helper.port_pairs.all_ports) self.tsc_hz = None @@ -1073,9 +1074,7 @@ class ProxDataHelper(object): self.tsc_hz = float(self.sut.hz()) def line_rate_to_pps(self): - # NOTE: to fix, don't hardcode 10Gb/s - return self.port_count * TEN_GIGABIT / BITS_PER_BYTE / (self.pkt_size + 20) - + return self.port_count * self.line_speed / BITS_PER_BYTE / (self.pkt_size + 20) class ProxProfileHelper(object): @@ -1154,8 +1153,10 @@ class ProxProfileHelper(object): return cores - def run_test(self, pkt_size, duration, value, tolerated_loss=0.0): - data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, value, tolerated_loss) + def run_test(self, pkt_size, duration, value, tolerated_loss=0.0, + line_speed=(constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)): + data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, + value, tolerated_loss, line_speed) with data_helper, self.traffic_context(pkt_size, value): with data_helper.measure_tot_stats(): @@ -1411,8 +1412,10 @@ class ProxBngProfileHelper(ProxProfileHelper): time.sleep(3) self.sut.stop(self.all_rx_cores) - def run_test(self, pkt_size, duration, value, tolerated_loss=0.0): - data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, value, tolerated_loss) + def run_test(self, pkt_size, duration, value, tolerated_loss=0.0, + line_speed=(constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)): + data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, + value, tolerated_loss, line_speed) with data_helper, self.traffic_context(pkt_size, value): with data_helper.measure_tot_stats(): @@ -1598,8 +1601,10 @@ class ProxVpeProfileHelper(ProxProfileHelper): time.sleep(3) self.sut.stop(self.all_rx_cores) - def run_test(self, pkt_size, duration, value, tolerated_loss=0.0): - data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, value, tolerated_loss) + def run_test(self, pkt_size, duration, value, tolerated_loss=0.0, + line_speed=(constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)): + data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, + value, tolerated_loss, line_speed) with data_helper, self.traffic_context(pkt_size, value): with data_helper.measure_tot_stats(): @@ -1787,8 +1792,10 @@ class ProxlwAFTRProfileHelper(ProxProfileHelper): time.sleep(3) self.sut.stop(self.all_rx_cores) - def run_test(self, pkt_size, duration, value, tolerated_loss=0.0): - data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, value, tolerated_loss) + def run_test(self, pkt_size, duration, value, tolerated_loss=0.0, + line_speed=(constants.ONE_GIGABIT_IN_BITS * constants.NIC_GBPS_DEFAULT)): + data_helper = ProxDataHelper(self.vnfd_helper, self.sut, pkt_size, + value, tolerated_loss, line_speed) with data_helper, self.traffic_context(pkt_size, value): with data_helper.measure_tot_stats(): diff --git a/yardstick/network_services/vnf_generic/vnf/prox_vnf.py b/yardstick/network_services/vnf_generic/vnf/prox_vnf.py index ee7735972..285e08659 100644 --- a/yardstick/network_services/vnf_generic/vnf/prox_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/prox_vnf.py @@ -21,7 +21,8 @@ import time from yardstick.common.process import check_if_process_failed from yardstick.network_services.vnf_generic.vnf.prox_helpers import ProxDpdkVnfSetupEnvHelper from yardstick.network_services.vnf_generic.vnf.prox_helpers import ProxResourceHelper -from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF, PROCESS_JOIN_TIMEOUT +from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF +from yardstick.network_services import constants LOG = logging.getLogger(__name__) @@ -135,5 +136,5 @@ class ProxApproxVnf(SampleVNF): self._tear_down() if self._vnf_process is not None: LOG.debug("joining before terminate %s", self._vnf_process.name) - self._vnf_process.join(PROCESS_JOIN_TIMEOUT) + self._vnf_process.join(constants.PROCESS_JOIN_TIMEOUT) self._vnf_process.terminate() diff --git a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py index 200930322..addbd9aa4 100644 --- a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py @@ -1,4 +1,4 @@ -# Copyright (c) 2016-2017 Intel Corporation +# Copyright (c) 2016-2018 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ from collections import Mapping import logging from multiprocessing import Queue, Value, Process + import os import posixpath import re @@ -23,78 +24,33 @@ import subprocess import time import six -from six.moves import cStringIO from trex_stl_lib.trex_stl_client import LoggerApi from trex_stl_lib.trex_stl_client import STLClient from trex_stl_lib.trex_stl_exceptions import STLError from yardstick.benchmark.contexts.base import Context -from yardstick.benchmark.scenarios.networking.vnf_generic import find_relative_file from yardstick.common import exceptions as y_exceptions -from yardstick.common import utils from yardstick.common.process import check_if_process_failed -from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkBindHelper -from yardstick.network_services.helpers.samplevnf_helper import PortPairs +from yardstick.common import utils +from yardstick.network_services import constants +from yardstick.network_services.helpers.dpdkbindnic_helper import DpdkBindHelper, DpdkNode from yardstick.network_services.helpers.samplevnf_helper import MultiPortConfig +from yardstick.network_services.helpers.samplevnf_helper import PortPairs from yardstick.network_services.nfvi.resource import ResourceProfile from yardstick.network_services.utils import get_nsb_option -from yardstick.network_services.vnf_generic.vnf.base import GenericVNF from yardstick.network_services.vnf_generic.vnf.base import GenericTrafficGen +from yardstick.network_services.vnf_generic.vnf.base import GenericVNF from yardstick.network_services.vnf_generic.vnf.base import QueueFileWrapper -from yardstick.ssh import AutoConnectSSH - +from yardstick.network_services.vnf_generic.vnf.vnf_ssh_helper import VnfSshHelper -DPDK_VERSION = "dpdk-16.07" LOG = logging.getLogger(__name__) -REMOTE_TMP = "/tmp" -DEFAULT_VNF_TIMEOUT = 3600 -PROCESS_JOIN_TIMEOUT = 3 - - -class VnfSshHelper(AutoConnectSSH): - - def __init__(self, node, bin_path, wait=None): - self.node = node - kwargs = self.args_from_node(self.node) - if wait: - kwargs.setdefault('wait', wait) - - super(VnfSshHelper, self).__init__(**kwargs) - self.bin_path = bin_path - - @staticmethod - def get_class(): - # must return static class name, anything else refers to the calling class - # i.e. the subclass, not the superclass - return VnfSshHelper - - def copy(self): - # this copy constructor is different from SSH classes, since it uses node - return self.get_class()(self.node, self.bin_path) - - def upload_config_file(self, prefix, content): - cfg_file = os.path.join(REMOTE_TMP, prefix) - LOG.debug(content) - file_obj = cStringIO(content) - self.put_file_obj(file_obj, cfg_file) - return cfg_file - - def join_bin_path(self, *args): - return os.path.join(self.bin_path, *args) - - def provision_tool(self, tool_path=None, tool_file=None): - if tool_path is None: - tool_path = self.bin_path - return super(VnfSshHelper, self).provision_tool(tool_path, tool_file) - - class SetupEnvHelper(object): - CFG_CONFIG = os.path.join(REMOTE_TMP, "sample_config") - CFG_SCRIPT = os.path.join(REMOTE_TMP, "sample_script") + CFG_CONFIG = os.path.join(constants.REMOTE_TMP, "sample_config") + CFG_SCRIPT = os.path.join(constants.REMOTE_TMP, "sample_script") DEFAULT_CONFIG_TPL_CFG = "sample.cfg" PIPELINE_COMMAND = '' VNF_TYPE = "SAMPLE" @@ -175,6 +131,7 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): vnf_cfg = self.scenario_helper.vnf_cfg task_path = self.scenario_helper.task_path + config_file = vnf_cfg.get('file') lb_count = vnf_cfg.get('lb_count', 3) lb_config = vnf_cfg.get('lb_config', 'SW') worker_config = vnf_cfg.get('worker_config', '1C/1T') @@ -187,7 +144,8 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): 'vnf_type': self.VNF_TYPE, } - config_tpl_cfg = find_relative_file(self.DEFAULT_CONFIG_TPL_CFG, task_path) + config_tpl_cfg = utils.find_relative_file(self.DEFAULT_CONFIG_TPL_CFG, + task_path) config_basename = posixpath.basename(self.CFG_CONFIG) script_basename = posixpath.basename(self.CFG_SCRIPT) multiport = MultiPortConfig(self.scenario_helper.topology, @@ -202,12 +160,20 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): self.socket) multiport.generate_config() - with open(self.CFG_CONFIG) as handle: - new_config = handle.read() - - new_config = self._update_traffic_type(new_config, traffic_options) - new_config = self._update_packet_type(new_config, traffic_options) - + if config_file: + with utils.open_relative_file(config_file, task_path) as infile: + new_config = ['[EAL]'] + vpci = [] + for port in self.vnfd_helper.port_pairs.all_ports: + interface = self.vnfd_helper.find_interface(name=port) + vpci.append(interface['virtual-interface']["vpci"]) + new_config.extend('w = {0}'.format(item) for item in vpci) + new_config = '\n'.join(new_config) + '\n' + infile.read() + else: + with open(self.CFG_CONFIG) as handle: + new_config = handle.read() + new_config = self._update_traffic_type(new_config, traffic_options) + new_config = self._update_packet_type(new_config, traffic_options) self.ssh_helper.upload_config_file(config_basename, new_config) self.ssh_helper.upload_config_file(script_basename, multiport.generate_script(self.vnfd_helper)) @@ -236,7 +202,6 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): def setup_vnf_environment(self): self._setup_dpdk() - self.bound_pci = [v['virtual-interface']["vpci"] for v in self.vnfd_helper.interfaces] self.kill_vnf() # bind before _setup_resources so we can use dpdk_port_num self._detect_and_bind_drivers() @@ -254,10 +219,11 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): def _setup_dpdk(self): """Setup DPDK environment needed for VNF to run""" self._setup_hugepages() - self.ssh_helper.execute('sudo modprobe uio && sudo modprobe igb_uio') - exit_status = self.ssh_helper.execute('lsmod | grep -i igb_uio')[0] - if exit_status: - raise y_exceptions.DPDKSetupDriverError() + self.dpdk_bind_helper.load_dpdk_driver() + + exit_status = self.dpdk_bind_helper.check_dpdk_driver() + if exit_status == 0: + return def get_collectd_options(self): options = self.scenario_helper.all_options.get("collectd", {}) @@ -284,9 +250,22 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): plugins=plugins, interval=collectd_options.get("interval"), timeout=self.scenario_helper.timeout) + def _check_interface_fields(self): + num_nodes = len(self.scenario_helper.nodes) + # OpenStack instance creation time is probably proportional to the number + # of instances + timeout = 120 * num_nodes + dpdk_node = DpdkNode(self.scenario_helper.name, self.vnfd_helper.interfaces, + self.ssh_helper, timeout) + dpdk_node.check() + def _detect_and_bind_drivers(self): interfaces = self.vnfd_helper.interfaces + self._check_interface_fields() + # check for bound after probe + self.bound_pci = [v['virtual-interface']["vpci"] for v in interfaces] + self.dpdk_bind_helper.read_status() self.dpdk_bind_helper.save_used_drivers() @@ -390,7 +369,7 @@ class ClientResourceHelper(ResourceHelper): try: return self.client.get_stats(*args, **kwargs) except STLError: - LOG.exception("TRex client not connected") + LOG.error('TRex client not connected') return {} def generate_samples(self, ports, key=None, default=None): @@ -628,8 +607,10 @@ class ScenarioHelper(object): @property def timeout(self): - return self.options.get('timeout', DEFAULT_VNF_TIMEOUT) - + test_duration = self.scenario_cfg.get('runner', {}).get('duration', + self.options.get('timeout', constants.DEFAULT_VNF_TIMEOUT)) + test_timeout = self.options.get('timeout', constants.DEFAULT_VNF_TIMEOUT) + return test_duration if test_duration > test_timeout else test_timeout class SampleVNF(GenericVNF): """ Class providing file-like API for generic VNF implementation """ @@ -813,7 +794,7 @@ class SampleVNF(GenericVNF): if self._vnf_process is not None: # be proper and join first before we kill LOG.debug("joining before terminate %s", self._vnf_process.name) - self._vnf_process.join(PROCESS_JOIN_TIMEOUT) + self._vnf_process.join(constants.PROCESS_JOIN_TIMEOUT) self._vnf_process.terminate() # no terminate children here because we share processes with tg @@ -957,12 +938,12 @@ class SampleVNFTrafficGen(GenericTrafficGen): if self._traffic_process is not None: # be proper and try to join before terminating LOG.debug("joining before terminate %s", self._traffic_process.name) - self._traffic_process.join(PROCESS_JOIN_TIMEOUT) + self._traffic_process.join(constants.PROCESS_JOIN_TIMEOUT) self._traffic_process.terminate() if self._tg_process is not None: # be proper and try to join before terminating LOG.debug("joining before terminate %s", self._tg_process.name) - self._tg_process.join(PROCESS_JOIN_TIMEOUT) + self._tg_process.join(constants.PROCESS_JOIN_TIMEOUT) self._tg_process.terminate() # no terminate children here because we share processes with vnf diff --git a/yardstick/network_services/vnf_generic/vnf/tg_ixload.py b/yardstick/network_services/vnf_generic/vnf/tg_ixload.py index 3ab30b53e..02e7803f7 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_ixload.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_ixload.py @@ -22,10 +22,10 @@ import shutil from collections import OrderedDict from subprocess import call -from yardstick.common.utils import makedirs +from yardstick.common import utils from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNFTrafficGen from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper -from yardstick.benchmark.scenarios.networking.vnf_generic import find_relative_file + LOG = logging.getLogger(__name__) @@ -93,9 +93,10 @@ class IxLoadResourceHelper(ClientResourceHelper): def setup(self): # NOTE: fixup scenario_helper to hanlde ixia self.resource_file_name = \ - find_relative_file(self.scenario_helper.scenario_cfg['ixia_profile'], - self.scenario_helper.scenario_cfg["task_path"]) - makedirs(self.RESULTS_MOUNT) + utils.find_relative_file( + self.scenario_helper.scenario_cfg['ixia_profile'], + self.scenario_helper.scenario_cfg["task_path"]) + utils.makedirs(self.RESULTS_MOUNT) cmd = MOUNT_CMD.format(self.vnfd_helper.mgmt_interface, self) LOG.debug(cmd) @@ -103,7 +104,7 @@ class IxLoadResourceHelper(ClientResourceHelper): call(cmd, shell=True) shutil.rmtree(self.RESULTS_MOUNT, ignore_errors=True) - makedirs(self.RESULTS_MOUNT) + utils.makedirs(self.RESULTS_MOUNT) shutil.copy(self.resource_file_name, self.RESULTS_MOUNT) def make_aggregates(self): diff --git a/yardstick/network_services/vnf_generic/vnf/tg_prox.py b/yardstick/network_services/vnf_generic/vnf/tg_prox.py index 151252ce8..282dd92c5 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_prox.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_prox.py @@ -30,20 +30,6 @@ class ProxTrafficGen(SampleVNFTrafficGen): LUA_PARAMETER_NAME = "gen" WAIT_TIME = 1 - @staticmethod - def _sort_vpci(vnfd): - """ - - :param vnfd: vnfd.yaml - :return: trex_cfg.yaml file - """ - - def key_func(interface): - return interface["virtual-interface"]["vpci"], interface["name"] - - ext_intf = vnfd["vdu"][0]["external-interface"] - return sorted(ext_intf, key=key_func) - def __init__(self, name, vnfd, setup_env_helper_type=None, resource_helper_type=None): # don't call superclass, use custom wrapper of ProxApproxVnf self._vnf_wrapper = ProxApproxVnf(name, vnfd, setup_env_helper_type, resource_helper_type) @@ -59,10 +45,6 @@ class ProxTrafficGen(SampleVNFTrafficGen): self._tg_process = None self._traffic_process = None - # used for generating stats - self.vpci_if_name_ascending = self._sort_vpci(vnfd) - self.resource_helper.vpci_if_name_ascending = self._sort_vpci(vnfd) - def terminate(self): self._vnf_wrapper.terminate() super(ProxTrafficGen, self).terminate() 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 12510db96..265d0b7a9 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py @@ -19,11 +19,11 @@ import os import logging import sys -from yardstick.common.utils import ErrorClass +from yardstick.common import utils +from yardstick import error from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNFTrafficGen from yardstick.network_services.vnf_generic.vnf.sample_vnf import ClientResourceHelper from yardstick.network_services.vnf_generic.vnf.sample_vnf import Rfc2544ResourceHelper -from yardstick.benchmark.scenarios.networking.vnf_generic import find_relative_file LOG = logging.getLogger(__name__) @@ -36,7 +36,7 @@ sys.path.append(IXNET_LIB) try: from IxNet import IxNextgen except ImportError: - IxNextgen = ErrorClass + IxNextgen = error.ErrorClass class IxiaRfc2544Helper(Rfc2544ResourceHelper): @@ -122,8 +122,9 @@ class IxiaResourceHelper(ClientResourceHelper): # we don't know client_file_name until runtime as instantiate client_file_name = \ - find_relative_file(self.scenario_helper.scenario_cfg['ixia_profile'], - self.scenario_helper.scenario_cfg["task_path"]) + utils.find_relative_file( + self.scenario_helper.scenario_cfg['ixia_profile'], + self.scenario_helper.scenario_cfg["task_path"]) self.client.ix_load_config(client_file_name) time.sleep(WAIT_AFTER_CFG_LOAD) diff --git a/yardstick/network_services/vnf_generic/vnf/vfw_vnf.py b/yardstick/network_services/vnf_generic/vnf/vfw_vnf.py index 6c95648ce..61e99855f 100644 --- a/yardstick/network_services/vnf_generic/vnf/vfw_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/vfw_vnf.py @@ -12,10 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import absolute_import import logging -from yardstick.benchmark.scenarios.networking.vnf_generic import find_relative_file +from yardstick.common import utils from yardstick.network_services.vnf_generic.vnf.sample_vnf import SampleVNF, DpdkVnfSetupEnvHelper from yardstick.network_services.yang_model import YangModel @@ -60,8 +59,9 @@ class FWApproxVnf(SampleVNF): self.vfw_rules = None def _start_vnf(self): - yang_model_path = find_relative_file(self.scenario_helper.options['rules'], - self.scenario_helper.task_path) + yang_model_path = utils.find_relative_file( + self.scenario_helper.options['rules'], + self.scenario_helper.task_path) yang_model = YangModel(yang_model_path) self.vfw_rules = yang_model.get_rules() super(FWApproxVnf, self)._start_vnf() diff --git a/yardstick/network_services/vnf_generic/vnf/vnf_ssh_helper.py b/yardstick/network_services/vnf_generic/vnf/vnf_ssh_helper.py new file mode 100644 index 000000000..de6fd9329 --- /dev/null +++ b/yardstick/network_services/vnf_generic/vnf/vnf_ssh_helper.py @@ -0,0 +1,61 @@ +# Copyright (c) 2016-2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging +import os + +from six.moves import StringIO + +from yardstick.network_services import constants +from yardstick.ssh import AutoConnectSSH + +LOG = logging.getLogger(__name__) + + +class VnfSshHelper(AutoConnectSSH): + + def __init__(self, node, bin_path, wait=None): + self.node = node + kwargs = self.args_from_node(self.node) + if wait: + # if wait is defined here we want to override + kwargs['wait'] = wait + + super(VnfSshHelper, self).__init__(**kwargs) + self.bin_path = bin_path + + @staticmethod + def get_class(): + # must return static class name, anything else refers to the calling class + # i.e. the subclass, not the superclass + return VnfSshHelper + + def copy(self): + # this copy constructor is different from SSH classes, since it uses node + return self.get_class()(self.node, self.bin_path) + + def upload_config_file(self, prefix, content): + cfg_file = os.path.join(constants.REMOTE_TMP, prefix) + LOG.debug(content) + file_obj = StringIO(content) + self.put_file_obj(file_obj, cfg_file) + return cfg_file + + def join_bin_path(self, *args): + return os.path.join(self.bin_path, *args) + + def provision_tool(self, tool_path=None, tool_file=None): + if tool_path is None: + tool_path = self.bin_path + return super(VnfSshHelper, self).provision_tool(tool_path, tool_file) diff --git a/yardstick/orchestrator/heat.py b/yardstick/orchestrator/heat.py index 3c3d28146..d69f86044 100644 --- a/yardstick/orchestrator/heat.py +++ b/yardstick/orchestrator/heat.py @@ -26,21 +26,16 @@ import shade import yardstick.common.openstack_utils as op_utils from yardstick.common import exceptions from yardstick.common import template_format +from yardstick.common import constants as consts log = logging.getLogger(__name__) -HEAT_KEY_UUID_LENGTH = 8 - PROVIDER_SRIOV = "sriov" _DEPLOYED_STACKS = {} -def get_short_key_uuid(uuid): - return str(uuid)[:HEAT_KEY_UUID_LENGTH] - - class HeatStack(object): """Represents a Heat stack (deployed template) """ @@ -50,6 +45,13 @@ class HeatStack(object): self._cloud = shade.openstack_cloud() self._stack = None + def _update_stack_tracking(self): + outputs = self._stack.outputs + self.outputs = {output['output_key']: output['output_value'] for output + in outputs} + if self.uuid: + _DEPLOYED_STACKS[self.uuid] = self._stack + def create(self, template, heat_parameters, wait, timeout): """Creates an OpenStack stack from a template""" with tempfile.NamedTemporaryFile('wb', delete=False) as template_file: @@ -58,11 +60,21 @@ class HeatStack(object): self._stack = self._cloud.create_stack( self.name, template_file=template_file.name, wait=wait, timeout=timeout, **heat_parameters) - outputs = self._stack.outputs - self.outputs = {output['output_key']: output['output_value'] for output - in outputs} - if self.uuid: - _DEPLOYED_STACKS[self.uuid] = self._stack + + self._update_stack_tracking() + + def get(self): + """Retrieves an existing stack from the target cloud + + Returns a bool indicating whether the stack exists in the target cloud + If the stack exists, it will be stored as self._stack + """ + self._stack = self._cloud.get_stack(self.name) + if not self._stack: + return False + + self._update_stack_tracking() + return True @staticmethod def stacks_exist(): @@ -322,21 +334,24 @@ name (i.e. %s). } } - def add_port(self, name, network_name, subnet_name, vnic_type, sec_group_id=None, + def add_port(self, name, network, sec_group_id=None, provider=None, allowed_address_pairs=None): """add to the template a named Neutron Port """ - log.debug("adding Neutron::Port '%s', network:'%s', subnet:'%s', vnic_type:'%s', " - "secgroup:%s", name, network_name, subnet_name, vnic_type, sec_group_id) + net_is_existing = network.net_flags.get(consts.IS_EXISTING) + depends_on = [] if net_is_existing else [network.subnet_stack_name] + fixed_ips = [{'subnet': network.subnet}] if net_is_existing else [ + {'subnet': {'get_resource': network.subnet_stack_name}}] + network_ = network.name if net_is_existing else { + 'get_resource': network.stack_name} self.resources[name] = { 'type': 'OS::Neutron::Port', - 'depends_on': [subnet_name], + 'depends_on': depends_on, 'properties': { 'name': name, - 'binding:vnic_type': vnic_type, - 'fixed_ips': [{'subnet': {'get_resource': subnet_name}}], - 'network_id': {'get_resource': network_name}, - 'replacement_policy': 'AUTO', + 'binding:vnic_type': network.vnic_type, + 'fixed_ips': fixed_ips, + 'network': network_, } } @@ -353,6 +368,8 @@ name (i.e. %s). self.resources[name]['properties'][ 'allowed_address_pairs'] = allowed_address_pairs + log.debug("adding Neutron::Port %s", self.resources[name]) + self._template['outputs'][name] = { 'description': 'Address for interface %s' % name, 'value': {'get_attr': [name, 'fixed_ips', 0, 'ip_address']} @@ -413,7 +430,7 @@ name (i.e. %s). } } - def add_keypair(self, name, key_uuid): + def add_keypair(self, name, key_id): """add to the template a Nova KeyPair""" log.debug("adding Nova::KeyPair '%s'", name) self.resources[name] = { @@ -425,7 +442,7 @@ name (i.e. %s). pkg_resources.resource_string( 'yardstick.resources', 'files/yardstick_key-' + - get_short_key_uuid(key_uuid) + '.pub'), + key_id + '.pub'), 'utf-8') } } diff --git a/yardstick/service/__init__.py b/yardstick/service/__init__.py new file mode 100644 index 000000000..1c3953de6 --- /dev/null +++ b/yardstick/service/__init__.py @@ -0,0 +1,12 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + + +class Service(object): + pass diff --git a/yardstick/service/environment.py b/yardstick/service/environment.py new file mode 100644 index 000000000..324589f79 --- /dev/null +++ b/yardstick/service/environment.py @@ -0,0 +1,101 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import tempfile +import logging +import collections + +from oslo_serialization import jsonutils + +from yardstick.service import Service +from yardstick.common.exceptions import MissingPodInfoError +from yardstick.common.exceptions import UnsupportedPodFormatError +from yardstick.common.ansible_common import AnsibleCommon + +LOG = logging.getLogger(__name__) + + +class Environment(Service): + def __init__(self, pod=None): + super(Environment, self).__init__() + # pod can be a dict or a json format string + self.pod = pod + + def get_sut_info(self): + temdir = tempfile.mkdtemp(prefix='sut') + + nodes = self._load_pod_info() + ansible = AnsibleCommon(nodes=nodes) + ansible.gen_inventory_ini_dict() + sut_info = ansible.get_sut_info(temdir) + + return self._format_sut_info(sut_info) + + def _load_pod_info(self): + if self.pod is None: + raise MissingPodInfoError + + if isinstance(self.pod, collections.Mapping): + try: + return self.pod['nodes'] + except KeyError: + raise UnsupportedPodFormatError + + try: + return jsonutils.loads(self.pod)['nodes'] + except (ValueError, KeyError): + raise UnsupportedPodFormatError + + def _format_sut_info(self, sut_info): + return {k: self._format_node_info(v) for k, v in sut_info.items()} + + def _format_node_info(self, node_info): + info = [] + facts = node_info.get('ansible_facts', {}) + + info.append(['hostname', facts.get('ansible_hostname')]) + + info.append(['product_name', facts.get('ansible_product_name')]) + info.append(['product_version', facts.get('ansible_product_version')]) + + processors = facts.get('ansible_processor', []) + try: + processor_type = '{} {}'.format(processors[0], processors[1]) + except IndexError: + LOG.exception('No Processor in SUT data') + processor_type = None + + info.append(['processor_type', processor_type]) + info.append(['architecture', facts.get('ansible_architecture')]) + info.append(['processor_cores', facts.get('ansible_processor_cores')]) + info.append(['processor_vcpus', facts.get('ansible_processor_vcpus')]) + + memory = facts.get('ansible_memtotal_mb') + memory = round(memory * 1.0 / 1024, 2) if memory else None + info.append(['memory', '{} GB'.format(memory)]) + + devices = facts.get('ansible_devices', {}) + info.extend([self._get_device_info(k, v) for k, v in devices.items()]) + + lsb_description = facts.get('ansible_lsb', {}).get('description') + info.append(['OS', lsb_description]) + + interfaces = facts.get('ansible_interfaces') + info.append(['interfaces', interfaces]) + if isinstance(interfaces, collections.Sequence): + info.extend([self._get_interface_info(facts, i) for i in interfaces]) + info = [i for i in info if i] + + return info + + def _get_interface_info(self, facts, name): + mac = facts.get('ansible_{}'.format(name), {}).get('macaddress') + return [name, mac] if mac else [] + + def _get_device_info(self, name, info): + return ['disk_{}'.format(name), info.get('size')] diff --git a/yardstick/ssh.py b/yardstick/ssh.py index 6ddf327f2..d7adc0d05 100644 --- a/yardstick/ssh.py +++ b/yardstick/ssh.py @@ -78,7 +78,7 @@ from oslo_utils import encodeutils from scp import SCPClient import six -from yardstick.common.utils import try_int +from yardstick.common.utils import try_int, NON_NONE_DEFAULT, make_dict_from_map from yardstick.network_services.utils import provision_tool @@ -102,6 +102,7 @@ class SSH(object): """Represent ssh connection.""" SSH_PORT = paramiko.config.SSH_PORT + DEFAULT_WAIT_TIMEOUT = 120 @staticmethod def gen_keys(key_filename, bit_count=2048): @@ -120,6 +121,18 @@ class SSH(object): # i.e. the subclass, not the superclass return SSH + @classmethod + def get_arg_key_map(cls): + return { + 'user': ('user', NON_NONE_DEFAULT), + 'host': ('ip', NON_NONE_DEFAULT), + 'port': ('ssh_port', cls.SSH_PORT), + 'pkey': ('pkey', None), + 'key_filename': ('key_filename', None), + 'password': ('password', None), + 'name': ('name', None), + } + def __init__(self, user, host, port=None, pkey=None, key_filename=None, password=None, name=None): """Initialize SSH client. @@ -137,6 +150,7 @@ class SSH(object): else: self.log = logging.getLogger(__name__) + self.wait_timeout = self.DEFAULT_WAIT_TIMEOUT self.user = user self.host = host # everybody wants to debug this in the caller, do it here instead @@ -162,16 +176,9 @@ class SSH(object): overrides = {} if defaults is None: defaults = {} + params = ChainMap(overrides, node, defaults) - return { - 'user': params['user'], - 'host': params['ip'], - 'port': params.get('ssh_port', cls.SSH_PORT), - 'pkey': params.get('pkey'), - 'key_filename': params.get('key_filename'), - 'password': params.get('password'), - 'name': params.get('name'), - } + return make_dict_from_map(params, cls.get_arg_key_map()) @classmethod def from_node(cls, node, overrides=None, defaults=None): @@ -186,7 +193,7 @@ class SSH(object): return key_class.from_private_key(key) except paramiko.SSHException as e: errors.append(e) - raise SSHError("Invalid pkey: %s" % (errors)) + raise SSHError("Invalid pkey: %s" % errors) @property def is_connected(self): @@ -287,7 +294,7 @@ class SSH(object): while True: # Block until data can be read/write. - r, w, e = select.select([session], writes, [session], 1) + e = select.select([session], writes, [session], 1)[2] if session.recv_ready(): data = encodeutils.safe_decode(session.recv(4096), 'utf-8') @@ -361,17 +368,20 @@ class SSH(object): stderr.seek(0) return exit_status, stdout.read(), stderr.read() - def wait(self, timeout=120, interval=1): + def wait(self, timeout=None, interval=1): """Wait for the host will be available via ssh.""" - start_time = time.time() + if timeout is None: + timeout = self.wait_timeout + + end_time = time.time() + timeout while True: try: return self.execute("uname") except (socket.error, SSHError) as e: self.log.debug("Ssh is still unavailable: %r", e) time.sleep(interval) - if time.time() > (start_time + timeout): - raise SSHTimeout("Timeout waiting for '%s'", self.host) + if time.time() > end_time: + raise SSHTimeout("Timeout waiting for '%s'" % self.host) def put(self, files, remote_path=b'.', recursive=False): client = self._get_client() @@ -447,24 +457,40 @@ class SSH(object): class AutoConnectSSH(SSH): + @classmethod + def get_arg_key_map(cls): + arg_key_map = super(AutoConnectSSH, cls).get_arg_key_map() + arg_key_map['wait'] = ('wait', True) + return arg_key_map + # always wait or we will get OpenStack SSH errors def __init__(self, user, host, port=None, pkey=None, key_filename=None, password=None, name=None, wait=True): super(AutoConnectSSH, self).__init__(user, host, port, pkey, key_filename, password, name) - self._wait = wait + if wait and wait is not True: + self.wait_timeout = int(wait) def _make_dict(self): data = super(AutoConnectSSH, self)._make_dict() data.update({ - 'wait': self._wait + 'wait': self.wait_timeout }) return data def _connect(self): if not self.is_connected: - self._get_client() - if self._wait: - self.wait() + interval = 1 + timeout = self.wait_timeout + + end_time = time.time() + timeout + while True: + try: + return self._get_client() + except (socket.error, SSHError) as e: + self.log.debug("Ssh is still unavailable: %r", e) + time.sleep(interval) + if time.time() > end_time: + raise SSHTimeout("Timeout waiting for '%s'" % self.host) def drop_connection(self): """ Don't close anything, just force creation of a new client """ diff --git a/yardstick/tests/__init__.py b/yardstick/tests/__init__.py index e69de29bb..56e3106b8 100644 --- a/yardstick/tests/__init__.py +++ b/yardstick/tests/__init__.py @@ -0,0 +1,75 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock + + +STL_MOCKS = { + 'trex_stl_lib': mock.MagicMock(), + 'trex_stl_lib.base64': mock.MagicMock(), + 'trex_stl_lib.binascii': mock.MagicMock(), + 'trex_stl_lib.collections': mock.MagicMock(), + 'trex_stl_lib.copy': mock.MagicMock(), + 'trex_stl_lib.datetime': mock.MagicMock(), + 'trex_stl_lib.functools': mock.MagicMock(), + 'trex_stl_lib.imp': mock.MagicMock(), + 'trex_stl_lib.inspect': mock.MagicMock(), + 'trex_stl_lib.json': mock.MagicMock(), + 'trex_stl_lib.linecache': mock.MagicMock(), + 'trex_stl_lib.math': mock.MagicMock(), + 'trex_stl_lib.os': mock.MagicMock(), + 'trex_stl_lib.platform': mock.MagicMock(), + 'trex_stl_lib.pprint': mock.MagicMock(), + 'trex_stl_lib.random': mock.MagicMock(), + 'trex_stl_lib.re': mock.MagicMock(), + 'trex_stl_lib.scapy': mock.MagicMock(), + 'trex_stl_lib.socket': mock.MagicMock(), + 'trex_stl_lib.string': mock.MagicMock(), + 'trex_stl_lib.struct': mock.MagicMock(), + 'trex_stl_lib.sys': mock.MagicMock(), + 'trex_stl_lib.threading': mock.MagicMock(), + 'trex_stl_lib.time': mock.MagicMock(), + 'trex_stl_lib.traceback': mock.MagicMock(), + 'trex_stl_lib.trex_stl_async_client': mock.MagicMock(), + 'trex_stl_lib.trex_stl_client': mock.MagicMock(), + 'trex_stl_lib.trex_stl_exceptions': mock.MagicMock(), + 'trex_stl_lib.trex_stl_ext': mock.MagicMock(), + 'trex_stl_lib.trex_stl_jsonrpc_client': mock.MagicMock(), + 'trex_stl_lib.trex_stl_packet_builder_interface': mock.MagicMock(), + 'trex_stl_lib.trex_stl_packet_builder_scapy': mock.MagicMock(), + 'trex_stl_lib.trex_stl_port': mock.MagicMock(), + 'trex_stl_lib.trex_stl_stats': mock.MagicMock(), + 'trex_stl_lib.trex_stl_streams': mock.MagicMock(), + 'trex_stl_lib.trex_stl_types': mock.MagicMock(), + 'trex_stl_lib.types': mock.MagicMock(), + 'trex_stl_lib.utils': mock.MagicMock(), + 'trex_stl_lib.utils.argparse': mock.MagicMock(), + 'trex_stl_lib.utils.collections': mock.MagicMock(), + 'trex_stl_lib.utils.common': mock.MagicMock(), + 'trex_stl_lib.utils.json': mock.MagicMock(), + 'trex_stl_lib.utils.os': mock.MagicMock(), + 'trex_stl_lib.utils.parsing_opts': mock.MagicMock(), + 'trex_stl_lib.utils.pwd': mock.MagicMock(), + 'trex_stl_lib.utils.random': mock.MagicMock(), + 'trex_stl_lib.utils.re': mock.MagicMock(), + 'trex_stl_lib.utils.string': mock.MagicMock(), + 'trex_stl_lib.utils.sys': mock.MagicMock(), + 'trex_stl_lib.utils.text_opts': mock.MagicMock(), + 'trex_stl_lib.utils.text_tables': mock.MagicMock(), + 'trex_stl_lib.utils.texttable': mock.MagicMock(), + 'trex_stl_lib.warnings': mock.MagicMock(), + 'trex_stl_lib.yaml': mock.MagicMock(), + 'trex_stl_lib.zlib': mock.MagicMock(), + 'trex_stl_lib.zmq': mock.MagicMock(), +} diff --git a/yardstick/tests/functional/base.py b/yardstick/tests/functional/base.py new file mode 100644 index 000000000..51be013a1 --- /dev/null +++ b/yardstick/tests/functional/base.py @@ -0,0 +1,46 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import abc +import six + +from oslo_config import cfg +from oslotest import base + + +CONF = cfg.CONF + + +@six.add_metaclass(abc.ABCMeta) +class BaseFunctionalTestCase(base.BaseTestCase): + """Base class for functional tests.""" + + def setUp(self): + super(BaseFunctionalTestCase, self).setUp() + + def config(self, **kw): + """Override some configuration values. + + The keyword arguments are the names of configuration options to + override and their values. + + If a group argument is supplied, the overrides are applied to + the specified configuration option group. + + All overrides are automatically cleared at the end of the current + test by the fixtures cleanup process. + """ + group = kw.pop('group', None) + for k, v in kw.items(): + CONF.set_override(k, v, group) diff --git a/yardstick/tests/functional/benchmark/__init__.py b/yardstick/tests/functional/benchmark/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/benchmark/__init__.py diff --git a/yardstick/tests/functional/benchmark/scenarios/__init__.py b/yardstick/tests/functional/benchmark/scenarios/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/benchmark/scenarios/__init__.py diff --git a/yardstick/tests/functional/benchmark/scenarios/networking/__init__.py b/yardstick/tests/functional/benchmark/scenarios/networking/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/benchmark/scenarios/networking/__init__.py diff --git a/yardstick/tests/functional/benchmark/scenarios/networking/test_vnf_generic.py b/yardstick/tests/functional/benchmark/scenarios/networking/test_vnf_generic.py new file mode 100644 index 000000000..38f1a978d --- /dev/null +++ b/yardstick/tests/functional/benchmark/scenarios/networking/test_vnf_generic.py @@ -0,0 +1,195 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import copy +import sys + +import mock +import unittest + +from yardstick import tests as y_tests +from yardstick.common import utils + + +with mock.patch.dict(sys.modules, y_tests.STL_MOCKS): + from yardstick.benchmark.scenarios.networking import vnf_generic + + +TRAFFIC_PROFILE_1 = """ +schema: nsb:traffic_profile:0.1 +name: rfc2544 +description: Traffic profile to run RFC2544 latency +traffic_profile: + traffic_type : RFC2544Profile + frame_rate : 100 +uplink_0: + ipv4: + id: 1 + outer_l2: + framesize: + 64B: "{{get(imix, 'imix.uplink.64B', '0') }}" + 128B: "{{get(imix, 'imix.uplink.128B', '0') }}" +""" + +TRAFFIC_PROFILE_2 = """ +{% set vports = get(extra_args, 'vports', 1) %} +traffic_profile: + traffic_type : RFC2544Profile +{% for vport in range(vports|int) %} +uplink_{{vport}}: + ipv4: 192.168.0.{{vport}} +{% endfor %} +""" + +TOPOLOGY_PROFILE = """ +{% set vports = get(extra_args, 'vports', 2) %} +nsd:nsd-catalog: + nsd: + - id: 3tg-topology + vld: +{% for vport in range(0,vports,2|int) %} + - id: uplink_{{loop.index0}} + name: tg__0 to vnf__0 link {{vport + 1}} + vnfd-connection-point-ref: + - vnfd-connection-point-ref: xe{{vport}} + - id: downlink_{{loop.index0}} + name: vnf__0 to tg__0 link {{vport + 2}} + vnfd-connection-point-ref: + - vnfd-connection-point-ref: xe{{vport+1}} +{% endfor %} +""" + +class VnfGenericTestCase(unittest.TestCase): + + def setUp(self): + scenario_cfg = {'topology': 'fake_topology', + 'task_path': 'fake_path', + 'traffic_profile': 'fake_fprofile_path'} + context_cfg = {} + topology_yaml = {'nsd:nsd-catalog': {'nsd': [mock.Mock()]}} + + with mock.patch.object(utils, 'open_relative_file') as mock_open_path: + mock_open_path.side_effect = mock.mock_open(read_data=str(topology_yaml)) + self.ns_testcase = vnf_generic.NetworkServiceTestCase(scenario_cfg, + context_cfg) + self.ns_testcase._get_traffic_profile = mock.Mock() + self.ns_testcase._get_topology = mock.Mock() + + def test__fill_traffic_profile_no_args(self): + traffic_profile = copy.deepcopy(TRAFFIC_PROFILE_1) + self.ns_testcase._get_traffic_profile.return_value = traffic_profile + + self.ns_testcase._fill_traffic_profile() + config = self.ns_testcase.traffic_profile.params + self.assertEqual('nsb:traffic_profile:0.1', config['schema']) + self.assertEqual('rfc2544', config['name']) + self.assertEqual('Traffic profile to run RFC2544 latency', + config['description']) + t_profile = {'traffic_type': 'RFC2544Profile', + 'frame_rate': 100} + self.assertEqual(t_profile, config['traffic_profile']) + uplink_0 = { + 'ipv4': {'id': 1, + 'outer_l2': {'framesize': {'128B': '0', '64B': '0'}} + } + } + self.assertEqual(uplink_0, config['uplink_0']) + + def test__fill_traffic_profile_with_args(self): + traffic_profile = copy.deepcopy(TRAFFIC_PROFILE_2) + self.ns_testcase._get_traffic_profile.return_value = traffic_profile + self.ns_testcase.scenario_cfg['extra_args'] = {'vports': 3} + + self.ns_testcase._fill_traffic_profile() + config = self.ns_testcase.traffic_profile.params + self.assertEqual({'ipv4': '192.168.0.0'}, config['uplink_0']) + self.assertEqual({'ipv4': '192.168.0.1'}, config['uplink_1']) + self.assertEqual({'ipv4': '192.168.0.2'}, config['uplink_2']) + self.assertNotIn('uplink_3', config) + + def test__fill_traffic_profile_incorrect_args(self): + traffic_profile = copy.deepcopy(TRAFFIC_PROFILE_2) + self.ns_testcase._get_traffic_profile.return_value = traffic_profile + self.ns_testcase.scenario_cfg['extra_args'] = {'incorrect_vports': 3} + + self.ns_testcase._fill_traffic_profile() + config = self.ns_testcase.traffic_profile.params + self.assertEqual({'ipv4': '192.168.0.0'}, config['uplink_0']) + self.assertNotIn('uplink_1', config) + + def test__render_topology_with_args(self): + topology_profile = copy.deepcopy(TOPOLOGY_PROFILE) + self.ns_testcase._get_topology.return_value = topology_profile + self.ns_testcase.scenario_cfg['extra_args'] = {'vports': 6} + + self.ns_testcase._render_topology() + topology = self.ns_testcase.topology + self.assertEqual("3tg-topology", topology['id']) + vld = self.ns_testcase.topology['vld'] + self.assertEqual(len(vld), 6) + for index, vport in enumerate(range(0, 6, 2)): + self.assertEqual('uplink_{}'.format(index), vld[vport]['id']) + self.assertEqual('tg__0 to vnf__0 link {}'.format(vport + 1), vld[vport]['name']) + self.assertEqual('xe{}'.format(vport), + vld[vport]['vnfd-connection-point-ref'][0] + ['vnfd-connection-point-ref']) + + self.assertEqual('downlink_{}'.format(index), vld[vport + 1]['id']) + self.assertEqual('vnf__0 to tg__0 link {}'.format(vport + 2), vld[vport + 1]['name']) + self.assertEqual('xe{}'.format(vport + 1), + vld[vport + 1]['vnfd-connection-point-ref'][0] + ['vnfd-connection-point-ref']) + + def test__render_topology_incorrect_args(self): + topology_profile = copy.deepcopy(TOPOLOGY_PROFILE) + self.ns_testcase._get_topology.return_value = topology_profile + self.ns_testcase.scenario_cfg['extra_args'] = {'fake_vports': 5} + + self.ns_testcase._render_topology() + + topology = self.ns_testcase.topology + self.assertEqual("3tg-topology", topology['id']) + vld = self.ns_testcase.topology['vld'] + self.assertEqual(len(vld), 2) + + self.assertEqual('uplink_0', vld[0]['id']) + self.assertEqual('tg__0 to vnf__0 link 1', vld[0]['name']) + self.assertEqual('xe0', + vld[0]['vnfd-connection-point-ref'][0]['vnfd-connection-point-ref']) + + self.assertEqual('downlink_0', vld[1]['id']) + self.assertEqual('vnf__0 to tg__0 link 2', vld[1]['name']) + self.assertEqual('xe1', + vld[1]['vnfd-connection-point-ref'][0]['vnfd-connection-point-ref']) + + def test__render_topology_no_args(self): + topology_profile = copy.deepcopy(TOPOLOGY_PROFILE) + self.ns_testcase._get_topology.return_value = topology_profile + + self.ns_testcase._render_topology() + + topology = self.ns_testcase.topology + self.assertEqual("3tg-topology", topology['id']) + vld = self.ns_testcase.topology['vld'] + self.assertEqual(len(vld), 2) + + self.assertEqual('uplink_0', vld[0]['id']) + self.assertEqual('tg__0 to vnf__0 link 1', vld[0]['name']) + self.assertEqual('xe0', + vld[0]['vnfd-connection-point-ref'][0]['vnfd-connection-point-ref']) + + self.assertEqual('downlink_0', vld[1]['id']) + self.assertEqual('vnf__0 to tg__0 link 2', vld[1]['name']) + self.assertEqual('xe1', + vld[1]['vnfd-connection-point-ref'][0]['vnfd-connection-point-ref']) diff --git a/yardstick/tests/functional/common/fake_directory_package/README.md b/yardstick/tests/functional/common/fake_directory_package/README.md new file mode 100644 index 000000000..689e47039 --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/README.md @@ -0,0 +1,2 @@ +# yardstick_new_plugin +Yardstick plugin diff --git a/yardstick/tests/functional/common/fake_directory_package/setup.py b/yardstick/tests/functional/common/fake_directory_package/setup.py new file mode 100644 index 000000000..cf938ef4f --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/setup.py @@ -0,0 +1,29 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from setuptools import setup, find_packages + +setup( + name='yardstick_new_plugin_2', + version='1.0.0', + packages=find_packages(), + include_package_data=True, + url='https://www.opnfv.org', + entry_points={ + 'yardstick.scenarios': [ + 'Dummy2 = yardstick_new_plugin.benchmark.scenarios.dummy2.dummy2:' + 'Dummy2', + ] + }, +) diff --git a/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/__init__.py b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/__init__.py diff --git a/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/__init__.py b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/__init__.py diff --git a/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/__init__.py b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/__init__.py diff --git a/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/__init__.py b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/__init__.py diff --git a/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/dummy2.py b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/dummy2.py new file mode 100644 index 000000000..a2211ec51 --- /dev/null +++ b/yardstick/tests/functional/common/fake_directory_package/yardstick_new_plugin_2/benchmark/scenarios/dummy2/dummy2.py @@ -0,0 +1,40 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import logging + +from yardstick.benchmark.scenarios import base + + +LOG = logging.getLogger(__name__) + + +class Dummy2(base.Scenario): + """Execute Dummy (v2!) echo""" + __scenario_type__ = "Dummy2" + + def __init__(self, scenario_cfg, context_cfg): + self.scenario_cfg = scenario_cfg + self.context_cfg = context_cfg + self.setup_done = False + + def setup(self): + self.setup_done = True + + def run(self, result): + if not self.setup_done: + self.setup() + + result["hello"] = "yardstick" + LOG.info("Dummy (v2!) echo hello yardstick!") diff --git a/yardstick/tests/functional/common/fake_pip_package/yardstick_new_plugin-1.0.0.tar.gz b/yardstick/tests/functional/common/fake_pip_package/yardstick_new_plugin-1.0.0.tar.gz Binary files differnew file mode 100644 index 000000000..e5379a78a --- /dev/null +++ b/yardstick/tests/functional/common/fake_pip_package/yardstick_new_plugin-1.0.0.tar.gz diff --git a/yardstick/tests/functional/common/test_packages.py b/yardstick/tests/functional/common/test_packages.py new file mode 100644 index 000000000..5dead4e55 --- /dev/null +++ b/yardstick/tests/functional/common/test_packages.py @@ -0,0 +1,94 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +from os import path +import re + +from yardstick.common import packages +from yardstick.common import utils +from yardstick.tests.functional import base + + +class PipPackagesTestCase(base.BaseFunctionalTestCase): + + TMP_FOLDER = '/tmp/pip_packages/' + PYTHONPATH = 'PYTHONPATH=%s' % TMP_FOLDER + + def setUp(self): + super(PipPackagesTestCase, self).setUp() + privsep_helper = os.path.join( + os.getenv('VIRTUAL_ENV'), 'bin', 'privsep-helper') + self.config( + helper_command=' '.join(['sudo', '-EH', privsep_helper]), + group='yardstick_privileged') + self.addCleanup(self._cleanup) + + def _cleanup(self): + utils.execute_command('sudo rm -rf %s' % self.TMP_FOLDER) + + def _remove_package(self, package): + os.system('%s pip uninstall %s -y' % (self.PYTHONPATH, package)) + + def _list_packages(self): + pip_list_regex = re.compile( + r"(?P<name>[\w\.-]+) \((?P<version>[\w\d_\.\-]+),*.*\)") + pkg_dict = {} + pkgs = utils.execute_command('pip list', + env={'PYTHONPATH': self.TMP_FOLDER}) + for line in pkgs: + match = pip_list_regex.match(line) + if match and match.group('name'): + pkg_dict[match.group('name')] = match.group('version') + return pkg_dict + + def test_install_from_folder(self): + dirname = path.dirname(__file__) + package_dir = dirname + '/fake_directory_package' + package_name = 'yardstick-new-plugin-2' + self.addCleanup(self._remove_package, package_name) + self._remove_package(package_name) + self.assertFalse(package_name in self._list_packages()) + + self.assertEqual(0, packages.pip_install(package_dir, self.TMP_FOLDER)) + self.assertTrue(package_name in self._list_packages()) + + def test_install_from_pip_package(self): + dirname = path.dirname(__file__) + package_path = (dirname + + '/fake_pip_package/yardstick_new_plugin-1.0.0.tar.gz') + package_name = 'yardstick-new-plugin' + self.addCleanup(self._remove_package, package_name) + self._remove_package(package_name) + self.assertFalse(package_name in self._list_packages()) + + self.assertEqual(0, packages.pip_install(package_path, self.TMP_FOLDER)) + self.assertTrue(package_name in self._list_packages()) + + # NOTE(ralonsoh): an stable test plugin project is needed in OPNFV git + # server to execute this test. + # def test_install_from_url(self): + + def test_pip_freeze(self): + # NOTE (ralonsoh): from requirements.txt file. The best way to test + # this function is to parse requirements.txt and test-requirements.txt + # and check all packages. + pkgs_ref = {'Babel': '2.3.4', + 'SQLAlchemy': '1.1.12', + 'influxdb': '4.1.1', + 'netifaces': '0.10.6', + 'unicodecsv': '0.14.1'} + pkgs = packages.pip_list() + for name, version in (pkgs_ref.items()): + self.assertEqual(version, pkgs[name]) diff --git a/yardstick/tests/unit/__init__.py b/yardstick/tests/unit/__init__.py index a468b272b..c05f91c81 100644 --- a/yardstick/tests/unit/__init__.py +++ b/yardstick/tests/unit/__init__.py @@ -12,65 +12,12 @@ # See the License for the specific language governing permissions and
# limitations under the License.
-from __future__ import absolute_import
+import sys
+
import mock
+from yardstick import tests
+
-STL_MOCKS = {
- 'trex_stl_lib': mock.MagicMock(),
- 'trex_stl_lib.base64': mock.MagicMock(),
- 'trex_stl_lib.binascii': mock.MagicMock(),
- 'trex_stl_lib.collections': mock.MagicMock(),
- 'trex_stl_lib.copy': mock.MagicMock(),
- 'trex_stl_lib.datetime': mock.MagicMock(),
- 'trex_stl_lib.functools': mock.MagicMock(),
- 'trex_stl_lib.imp': mock.MagicMock(),
- 'trex_stl_lib.inspect': mock.MagicMock(),
- 'trex_stl_lib.json': mock.MagicMock(),
- 'trex_stl_lib.linecache': mock.MagicMock(),
- 'trex_stl_lib.math': mock.MagicMock(),
- 'trex_stl_lib.os': mock.MagicMock(),
- 'trex_stl_lib.platform': mock.MagicMock(),
- 'trex_stl_lib.pprint': mock.MagicMock(),
- 'trex_stl_lib.random': mock.MagicMock(),
- 'trex_stl_lib.re': mock.MagicMock(),
- 'trex_stl_lib.scapy': mock.MagicMock(),
- 'trex_stl_lib.socket': mock.MagicMock(),
- 'trex_stl_lib.string': mock.MagicMock(),
- 'trex_stl_lib.struct': mock.MagicMock(),
- 'trex_stl_lib.sys': mock.MagicMock(),
- 'trex_stl_lib.threading': mock.MagicMock(),
- 'trex_stl_lib.time': mock.MagicMock(),
- 'trex_stl_lib.traceback': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_async_client': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_client': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_exceptions': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_ext': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_jsonrpc_client': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_packet_builder_interface': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_packet_builder_scapy': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_port': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_stats': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_streams': mock.MagicMock(),
- 'trex_stl_lib.trex_stl_types': mock.MagicMock(),
- 'trex_stl_lib.types': mock.MagicMock(),
- 'trex_stl_lib.utils': mock.MagicMock(),
- 'trex_stl_lib.utils.argparse': mock.MagicMock(),
- 'trex_stl_lib.utils.collections': mock.MagicMock(),
- 'trex_stl_lib.utils.common': mock.MagicMock(),
- 'trex_stl_lib.utils.json': mock.MagicMock(),
- 'trex_stl_lib.utils.os': mock.MagicMock(),
- 'trex_stl_lib.utils.parsing_opts': mock.MagicMock(),
- 'trex_stl_lib.utils.pwd': mock.MagicMock(),
- 'trex_stl_lib.utils.random': mock.MagicMock(),
- 'trex_stl_lib.utils.re': mock.MagicMock(),
- 'trex_stl_lib.utils.string': mock.MagicMock(),
- 'trex_stl_lib.utils.sys': mock.MagicMock(),
- 'trex_stl_lib.utils.text_opts': mock.MagicMock(),
- 'trex_stl_lib.utils.text_tables': mock.MagicMock(),
- 'trex_stl_lib.utils.texttable': mock.MagicMock(),
- 'trex_stl_lib.warnings': mock.MagicMock(),
- 'trex_stl_lib.yaml': mock.MagicMock(),
- 'trex_stl_lib.zlib': mock.MagicMock(),
- 'trex_stl_lib.zmq': mock.MagicMock(),
-}
+mock_stl = mock.patch.dict(sys.modules, tests.STL_MOCKS)
+mock_stl.start()
diff --git a/yardstick/tests/unit/apiserver/resources/test_env_action.py b/yardstick/tests/unit/apiserver/resources/test_env_action.py index cf646a29c..657841669 100644 --- a/yardstick/tests/unit/apiserver/resources/test_env_action.py +++ b/yardstick/tests/unit/apiserver/resources/test_env_action.py @@ -33,11 +33,3 @@ class EnvTestCase(APITestCase): time.sleep(0) self.assertIn(u'status', resp) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/apiserver/utils/test_influx.py b/yardstick/tests/unit/apiserver/utils/test_influx.py index 883608bb2..dce6c1cec 100644 --- a/yardstick/tests/unit/apiserver/utils/test_influx.py +++ b/yardstick/tests/unit/apiserver/utils/test_influx.py @@ -50,11 +50,3 @@ class QueryTestCase(unittest.TestCase): influx.query(sql) except Exception as e: # pylint: disable=broad-except self.assertIsInstance(e, RuntimeError) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/base.py b/yardstick/tests/unit/base.py new file mode 100644 index 000000000..b943efc2b --- /dev/null +++ b/yardstick/tests/unit/base.py @@ -0,0 +1,23 @@ +# Copyright 2018 Intel Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import abc + +import six +import unittest + + +@six.add_metaclass(abc.ABCMeta) +class BaseUnitTestCase(unittest.TestCase): + """Base class for unit tests""" diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py b/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py index 02a85525a..0223fd3ff 100644 --- a/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_ovs_dpdk.py @@ -26,14 +26,6 @@ class OvsDpdkContextTestCase(unittest.TestCase): NODES_ovs_dpdk_SAMPLE = "nodes_ovs_dpdk_sample.yaml" NODES_DUPLICATE_SAMPLE = "nodes_duplicate_sample.yaml" - ATTRS = { - 'name': 'StandaloneOvsDpdk', - 'file': 'pod', - 'flavor': {}, - 'servers': {}, - 'networks': {}, - } - NETWORKS = { 'mgmt': {'cidr': '152.16.100.10/24'}, 'private_0': { @@ -55,7 +47,17 @@ class OvsDpdkContextTestCase(unittest.TestCase): } def setUp(self): + self.attrs = { + 'name': 'foo', + 'task_id': '1234567890', + 'file': self._get_file_abspath(self.NODES_ovs_dpdk_SAMPLE) + } self.ovs_dpdk = ovs_dpdk.OvsDpdkContext() + self.addCleanup(self._remove_contexts) + + def _remove_contexts(self): + if self.ovs_dpdk in self.ovs_dpdk.list: + self.ovs_dpdk._delete_context() @mock.patch('yardstick.benchmark.contexts.standalone.model.Server') @mock.patch('yardstick.benchmark.contexts.standalone.model.StandaloneContextHelper') @@ -66,9 +68,18 @@ class OvsDpdkContextTestCase(unittest.TestCase): self.assertTrue(self.ovs_dpdk.first_run) def test_init(self): + ATTRS = { + 'name': 'StandaloneOvsDpdk', + 'task_id': '1234567890', + 'file': 'pod', + 'flavor': {}, + 'servers': {}, + 'networks': {}, + } + self.ovs_dpdk.helper.parse_pod_file = mock.Mock( return_value=[{}, {}, {}]) - self.assertIsNone(self.ovs_dpdk.init(self.ATTRS)) + self.assertIsNone(self.ovs_dpdk.init(ATTRS)) def test_setup_ovs(self): with mock.patch("yardstick.ssh.SSH") as ssh: @@ -186,12 +197,7 @@ class OvsDpdkContextTestCase(unittest.TestCase): def test__get_server_with_dic_attr_name(self): - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_ovs_dpdk_SAMPLE) - } - - self.ovs_dpdk.init(attrs) + self.ovs_dpdk.init(self.attrs) attr_name = {'name': 'foo.bar'} result = self.ovs_dpdk._get_server(attr_name) @@ -200,14 +206,9 @@ class OvsDpdkContextTestCase(unittest.TestCase): def test__get_server_not_found(self): - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_ovs_dpdk_SAMPLE) - } - self.ovs_dpdk.helper.parse_pod_file = mock.Mock( return_value=[{}, {}, {}]) - self.ovs_dpdk.init(attrs) + self.ovs_dpdk.init(self.attrs) attr_name = 'bar.foo' result = self.ovs_dpdk._get_server(attr_name) @@ -216,12 +217,7 @@ class OvsDpdkContextTestCase(unittest.TestCase): def test__get_server_mismatch(self): - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_ovs_dpdk_SAMPLE) - } - - self.ovs_dpdk.init(attrs) + self.ovs_dpdk.init(self.attrs) attr_name = 'bar.foo1' result = self.ovs_dpdk._get_server(attr_name) @@ -230,31 +226,23 @@ class OvsDpdkContextTestCase(unittest.TestCase): def test__get_server_duplicate(self): - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE) - } + self.attrs['file'] = self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE) - self.ovs_dpdk.init(attrs) + self.ovs_dpdk.init(self.attrs) - attr_name = 'node1.foo' + attr_name = 'node1.foo-12345678' with self.assertRaises(ValueError): self.ovs_dpdk._get_server(attr_name) def test__get_server_found(self): - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_ovs_dpdk_SAMPLE) - } - - self.ovs_dpdk.init(attrs) + self.ovs_dpdk.init(self.attrs) - attr_name = 'node1.foo' + attr_name = 'node1.foo-12345678' result = self.ovs_dpdk._get_server(attr_name) self.assertEqual(result['ip'], '10.229.47.137') - self.assertEqual(result['name'], 'node1.foo') + self.assertEqual(result['name'], 'node1.foo-12345678') self.assertEqual(result['user'], 'root') self.assertEqual(result['key_filename'], '/root/.yardstick_key') diff --git a/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py b/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py index f323fcd3c..f0953ef55 100644 --- a/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py +++ b/yardstick/tests/unit/benchmark/contexts/standalone/test_sriov.py @@ -29,6 +29,7 @@ class SriovContextTestCase(unittest.TestCase): ATTRS = { 'name': 'StandaloneSriov', + 'task_id': '1234567890', 'file': 'pod', 'flavor': {}, 'servers': {}, @@ -56,7 +57,17 @@ class SriovContextTestCase(unittest.TestCase): } def setUp(self): + self.attrs = { + 'name': 'foo', + 'task_id': '1234567890', + 'file': self._get_file_abspath(self.NODES_SRIOV_SAMPLE) + } self.sriov = sriov.SriovContext() + self.addCleanup(self._remove_contexts) + + def _remove_contexts(self): + if self.sriov in self.sriov.list: + self.sriov._delete_context() @mock.patch('yardstick.benchmark.contexts.standalone.sriov.Libvirt') @mock.patch('yardstick.benchmark.contexts.standalone.model.StandaloneContextHelper') @@ -105,12 +116,7 @@ class SriovContextTestCase(unittest.TestCase): def test__get_server_with_dic_attr_name(self): - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SRIOV_SAMPLE) - } - - self.sriov.init(attrs) + self.sriov.init(self.attrs) attr_name = {'name': 'foo.bar'} result = self.sriov._get_server(attr_name) @@ -119,13 +125,8 @@ class SriovContextTestCase(unittest.TestCase): def test__get_server_not_found(self): - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SRIOV_SAMPLE) - } - self.sriov.helper.parse_pod_file = mock.Mock(return_value=[{}, {}, {}]) - self.sriov.init(attrs) + self.sriov.init(self.attrs) attr_name = 'bar.foo' result = self.sriov._get_server(attr_name) @@ -134,12 +135,7 @@ class SriovContextTestCase(unittest.TestCase): def test__get_server_mismatch(self): - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SRIOV_SAMPLE) - } - - self.sriov.init(attrs) + self.sriov.init(self.attrs) attr_name = 'bar.foo1' result = self.sriov._get_server(attr_name) @@ -148,25 +144,29 @@ class SriovContextTestCase(unittest.TestCase): def test__get_server_duplicate(self): - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE) - } + self.attrs['file'] = self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE) - self.sriov.init(attrs) + self.sriov.init(self.attrs) - attr_name = 'node1.foo' + attr_name = 'node1.foo-12345678' with self.assertRaises(ValueError): self.sriov._get_server(attr_name) def test__get_server_found(self): - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SRIOV_SAMPLE) - } + self.sriov.init(self.attrs) + + attr_name = 'node1.foo-12345678' + result = self.sriov._get_server(attr_name) + + self.assertEqual(result['ip'], '10.229.47.137') + self.assertEqual(result['name'], 'node1.foo-12345678') + self.assertEqual(result['user'], 'root') + self.assertEqual(result['key_filename'], '/root/.yardstick_key') - self.sriov.init(attrs) + def test__get_server_no_task_id(self): + self.attrs['flags'] = {'no_setup': True} + self.sriov.init(self.attrs) attr_name = 'node1.foo' result = self.sriov._get_server(attr_name) diff --git a/yardstick/tests/unit/benchmark/contexts/test_base.py b/yardstick/tests/unit/benchmark/contexts/test_base.py new file mode 100644 index 000000000..153c6a527 --- /dev/null +++ b/yardstick/tests/unit/benchmark/contexts/test_base.py @@ -0,0 +1,43 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from yardstick.benchmark.contexts import base + + +class FlagsTestCase(unittest.TestCase): + + def setUp(self): + self.flags = base.Flags() + + def test___init__(self): + self.assertFalse(self.flags.no_setup) + self.assertFalse(self.flags.no_teardown) + + def test___init__with_flags(self): + flags = base.Flags(no_setup=True) + self.assertTrue(flags.no_setup) + self.assertFalse(flags.no_teardown) + + def test_parse(self): + self.flags.parse(no_setup=True, no_teardown="False") + + self.assertTrue(self.flags.no_setup) + self.assertEqual(self.flags.no_teardown, "False") + + def test_parse_forbidden_flags(self): + self.flags.parse(foo=42) + with self.assertRaises(AttributeError): + _ = self.flags.foo diff --git a/yardstick/tests/unit/benchmark/contexts/test_dummy.py b/yardstick/tests/unit/benchmark/contexts/test_dummy.py index 1a54035df..e393001a1 100644 --- a/yardstick/tests/unit/benchmark/contexts/test_dummy.py +++ b/yardstick/tests/unit/benchmark/contexts/test_dummy.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. # @@ -9,9 +7,6 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -# Unittest for yardstick.benchmark.contexts.dummy - -from __future__ import absolute_import import unittest from yardstick.benchmark.contexts import dummy @@ -20,10 +15,55 @@ from yardstick.benchmark.contexts import dummy class DummyContextTestCase(unittest.TestCase): def setUp(self): + self.attrs = { + 'name': 'foo', + 'task_id': '1234567890', + } self.test_context = dummy.DummyContext() + self.addCleanup(self.test_context._delete_context) + + def test___init__(self): + self.assertFalse(self.test_context._flags.no_setup) + self.assertFalse(self.test_context._flags.no_teardown) + self.assertIsNone(self.test_context._name) + self.assertIsNone(self.test_context._task_id) + + def test_init(self): + self.test_context.init(self.attrs) + self.assertEqual(self.test_context._name, 'foo') + self.assertEqual(self.test_context._task_id, '1234567890') + self.assertFalse(self.test_context._flags.no_setup) + self.assertFalse(self.test_context._flags.no_teardown) + + self.assertEqual(self.test_context.name, 'foo-12345678') + self.assertEqual(self.test_context.assigned_name, 'foo') + + def test_init_flags_no_setup(self): + self.attrs['flags'] = {'no_setup': True, 'no_teardown': False} + + self.test_context.init(self.attrs) + + self.assertEqual(self.test_context._name, 'foo') + self.assertEqual(self.test_context._task_id, '1234567890') + self.assertTrue(self.test_context._flags.no_setup) + self.assertFalse(self.test_context._flags.no_teardown) + + self.assertEqual(self.test_context.name, 'foo') + self.assertEqual(self.test_context.assigned_name, 'foo') + + def test_init_flags_no_teardown(self): + self.attrs['flags'] = {'no_setup': False, 'no_teardown': True} + + self.test_context.init(self.attrs) + + self.assertFalse(self.test_context._flags.no_setup) + self.assertTrue(self.test_context._flags.no_teardown) + + self.assertEqual(self.test_context.name, 'foo') + self.assertEqual(self.test_context.assigned_name, 'foo') def test__get_server(self): - self.test_context.init(None) + self.test_context.init(self.attrs) self.test_context.deploy() result = self.test_context._get_server(None) diff --git a/yardstick/tests/unit/benchmark/contexts/test_heat.py b/yardstick/tests/unit/benchmark/contexts/test_heat.py index 4348bb052..625f97bf4 100644 --- a/yardstick/tests/unit/benchmark/contexts/test_heat.py +++ b/yardstick/tests/unit/benchmark/contexts/test_heat.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Ericsson AB and others. # @@ -13,13 +11,16 @@ from collections import OrderedDict from itertools import count import logging import os -import uuid import mock import unittest +from yardstick.benchmark.contexts import base from yardstick.benchmark.contexts import heat from yardstick.benchmark.contexts import model +from yardstick.common import constants as consts +from yardstick.common import exceptions as y_exc +from yardstick import ssh LOG = logging.getLogger(__name__) @@ -33,10 +34,18 @@ class HeatContextTestCase(unittest.TestCase): def setUp(self): self.test_context = heat.HeatContext() + self.addCleanup(self._remove_contexts) self.mock_context = mock.Mock(spec=heat.HeatContext()) + def _remove_contexts(self): + if self.test_context in self.test_context.list: + self.test_context._delete_context() + def test___init__(self): - self.assertIsNone(self.test_context.name) + self.assertIsNone(self.test_context._name) + self.assertIsNone(self.test_context._task_id) + self.assertFalse(self.test_context._flags.no_setup) + self.assertFalse(self.test_context._flags.no_teardown) self.assertIsNone(self.test_context.stack) self.assertEqual(self.test_context.networks, OrderedDict()) self.assertEqual(self.test_context.servers, []) @@ -50,8 +59,7 @@ class HeatContextTestCase(unittest.TestCase): self.assertIsNone(self.test_context._user) self.assertIsNone(self.test_context.template_file) self.assertIsNone(self.test_context.heat_parameters) - self.assertIsNotNone(self.test_context.key_uuid) - self.assertIsNotNone(self.test_context.key_filename) + self.assertIsNone(self.test_context.key_filename) @mock.patch('yardstick.benchmark.contexts.heat.PlacementGroup') @mock.patch('yardstick.benchmark.contexts.heat.ServerGroup') @@ -64,6 +72,7 @@ class HeatContextTestCase(unittest.TestCase): networks = {'bar': {'cidr': '10.0.1.0/24'}} servers = {'baz': {'floating_ip': True, 'placement': 'pgrp1'}} attrs = {'name': 'foo', + 'task_id': '1234567890', 'placement_groups': pgs, 'server_groups': sgs, 'networks': networks, @@ -71,9 +80,13 @@ class HeatContextTestCase(unittest.TestCase): self.test_context.init(attrs) - self.assertEqual(self.test_context.name, "foo") - self.assertEqual(self.test_context.keypair_name, "foo-key") - self.assertEqual(self.test_context.secgroup_name, "foo-secgroup") + self.assertFalse(self.test_context._flags.no_setup) + self.assertFalse(self.test_context._flags.no_teardown) + self.assertEqual(self.test_context._name, "foo") + self.assertEqual(self.test_context._task_id, '1234567890') + self.assertEqual(self.test_context.name, "foo-12345678") + self.assertEqual(self.test_context.keypair_name, "foo-12345678-key") + self.assertEqual(self.test_context.secgroup_name, "foo-12345678-secgroup") mock_pg.assert_called_with('pgrp1', self.test_context, pgs['pgrp1']['policy']) @@ -90,40 +103,76 @@ class HeatContextTestCase(unittest.TestCase): servers['baz']) self.assertEqual(len(self.test_context.servers), 1) - if os.path.exists(self.test_context.key_filename): - try: - os.remove(self.test_context.key_filename) - os.remove(self.test_context.key_filename + ".pub") - except OSError: - LOG.exception("key_filename: %s", - self.test_context.key_filename) + def test_init_no_name_or_task_id(self): + attrs = {} + self.assertRaises(KeyError, self.test_context.init, attrs) + + def test_name(self): + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.assertEqual(self.test_context.name, 'foo-12345678') + self.assertEqual(self.test_context.assigned_name, 'foo') + + def test_name_flags(self): + self.test_context._flags = base.Flags( + **{"no_setup": True, "no_teardown": True}) + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + + self.assertEqual(self.test_context.name, 'foo') + self.assertEqual(self.test_context.assigned_name, 'foo') + + def test_init_no_setup_no_teardown(self): + + attrs = {'name': 'foo', + 'task_id': '1234567890', + 'placement_groups': {}, + 'server_groups': {}, + 'networks': {}, + 'servers': {}, + 'flags': { + 'no_setup': True, + 'no_teardown': True, + }, + } + + self.test_context.init(attrs) + self.assertTrue(self.test_context._flags.no_setup) + self.assertTrue(self.test_context._flags.no_teardown) @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') def test__add_resources_to_template_no_servers(self, mock_template): - - self.test_context.keypair_name = "foo-key" - self.test_context.secgroup_name = "foo-secgroup" + self.test_context._name = 'ctx' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.test_context.keypair_name = "ctx-key" + self.test_context.secgroup_name = "ctx-secgroup" self.test_context.key_uuid = "2f2e4997-0a8e-4eb7-9fa4-f3f8fbbc393b" - netattrs = {'cidr': '10.0.0.0/24', 'provider': None, 'external_network': 'ext_net'} - self.mock_context.name = 'bar' + netattrs = {'cidr': '10.0.0.0/24', 'provider': None, + 'external_network': 'ext_net'} + self.test_context.networks = OrderedDict( - {"fool-network": model.Network("fool-network", self.mock_context, + {"mynet": model.Network("mynet", self.test_context, netattrs)}) self.test_context._add_resources_to_template(mock_template) mock_template.add_keypair.assert_called_with( - "foo-key", - "2f2e4997-0a8e-4eb7-9fa4-f3f8fbbc393b") - mock_template.add_security_group.assert_called_with("foo-secgroup") -# mock_template.add_network.assert_called_with("bar-fool-network", 'physnet1', None) + "ctx-key", + "ctx-12345678") + mock_template.add_security_group.assert_called_with("ctx-secgroup") + mock_template.add_network.assert_called_with( + "ctx-12345678-mynet", 'physnet1', None, None, None, None) mock_template.add_router.assert_called_with( - "bar-fool-network-router", + "ctx-12345678-mynet-router", netattrs["external_network"], - "bar-fool-network-subnet") + "ctx-12345678-mynet-subnet") mock_template.add_router_interface.assert_called_with( - "bar-fool-network-router-if0", - "bar-fool-network-router", - "bar-fool-network-subnet") + "ctx-12345678-mynet-router-if0", + "ctx-12345678-mynet-router", + "ctx-12345678-mynet-subnet") @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') def test_attrs_get(self, *args): @@ -148,23 +197,162 @@ class HeatContextTestCase(unittest.TestCase): with self.assertRaises(AttributeError): self.test_context.user = 'foo' + def test__create_new_stack(self): + template = mock.Mock() + self.test_context._create_new_stack(template) + template.create.assert_called_once() + + def test__create_new_stack_stack_create_failed(self): + template = mock.Mock() + template.create.side_effect = y_exc.HeatTemplateError + + self.assertRaises(y_exc.HeatTemplateError, + self.test_context._create_new_stack, + template) + + def test__create_new_stack_keyboard_interrupt(self): + template = mock.Mock() + template.create.side_effect = KeyboardInterrupt + self.assertRaises(y_exc.StackCreationInterrupt, + self.test_context._create_new_stack, + template) + + @mock.patch.object(os.path, 'exists', return_value=True) + @mock.patch.object(heat.HeatContext, '_add_resources_to_template') + @mock.patch.object(heat.HeatContext, '_create_new_stack') + def test_deploy_stack_creation_failed(self, mock_create, + mock_resources_template, mock_path_exists): + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = 'foo-12345678' + mock_create.side_effect = y_exc.HeatTemplateError + self.assertRaises(y_exc.HeatTemplateError, + self.test_context.deploy) + + mock_path_exists.assert_called_once() + mock_resources_template.assert_called_once() + + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(ssh.SSH, 'gen_keys') @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') - def test_deploy(self, mock_template): - self.test_context.name = 'foo' + def test_deploy(self, mock_template, mock_genkeys, mock_path_exists): + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) self.test_context.template_file = '/bar/baz/some-heat-file' self.test_context.heat_parameters = {'image': 'cirros'} self.test_context.get_neutron_info = mock.MagicMock() self.test_context.deploy() - mock_template.assert_called_with('foo', + mock_template.assert_called_with('foo-12345678', '/bar/baz/some-heat-file', {'image': 'cirros'}) self.assertIsNotNone(self.test_context.stack) + key_filename = ''.join( + [consts.YARDSTICK_ROOT_PATH, + 'yardstick/resources/files/yardstick_key-', + self.test_context._name_task_id]) + mock_genkeys.assert_called_once_with(key_filename) + mock_path_exists.assert_called_once_with(key_filename) + + @mock.patch.object(heat, 'HeatTemplate') + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(ssh.SSH, 'gen_keys') + @mock.patch.object(heat.HeatContext, '_retrieve_existing_stack') + @mock.patch.object(heat.HeatContext, '_create_new_stack') + def test_deploy_no_setup(self, mock_create_new_stack, + mock_retrieve_existing_stack, mock_genkeys, mock_path_exists, + *args): + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context.template_file = '/bar/baz/some-heat-file' + self.test_context.heat_parameters = {'image': 'cirros'} + self.test_context.get_neutron_info = mock.MagicMock() + self.test_context._flags.no_setup = True + self.test_context.deploy() + + mock_create_new_stack.assert_not_called() + mock_retrieve_existing_stack.assert_called_with(self.test_context.name) + self.assertIsNotNone(self.test_context.stack) + key_filename = ''.join( + [consts.YARDSTICK_ROOT_PATH, + 'yardstick/resources/files/yardstick_key-', + self.test_context._name]) + mock_genkeys.assert_called_once_with(key_filename) + mock_path_exists.assert_called_once_with(key_filename) + + @mock.patch.object(heat, 'HeatTemplate') + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(ssh.SSH, 'gen_keys') + @mock.patch.object(heat.HeatContext, '_create_new_stack') + @mock.patch.object(heat.HeatContext, '_retrieve_existing_stack', + return_value=None) + def test_deploy_try_retrieve_context_does_not_exist(self, + mock_retrieve_stack, mock_create_new_stack, mock_genkeys, + mock_path_exists, *args): + self.test_context._name = 'demo' + self.test_context._task_id = '1234567890' + self.test_context._flags.no_setup = True + self.test_context.template_file = '/bar/baz/some-heat-file' + self.test_context.get_neutron_info = mock.MagicMock() + + self.test_context.deploy() + + mock_retrieve_stack.assert_called_once_with(self.test_context._name) + mock_create_new_stack.assert_called() + key_filename = ''.join( + [consts.YARDSTICK_ROOT_PATH, + 'yardstick/resources/files/yardstick_key-', + self.test_context._name]) + mock_genkeys.assert_called_once_with(key_filename) + mock_path_exists.assert_called_once_with(key_filename) + + @mock.patch.object(heat, 'HeatTemplate', return_value='heat_template') + @mock.patch.object(heat.HeatContext, '_add_resources_to_template') + @mock.patch.object(os.path, 'exists', return_value=False) + @mock.patch.object(ssh.SSH, 'gen_keys') + def test_deploy_ssh_key_before_adding_resources(self, mock_genkeys, + mock_path_exists, mock_add_resources, *args): + mock_manager = mock.Mock() + mock_manager.attach_mock(mock_add_resources, + '_add_resources_to_template') + mock_manager.attach_mock(mock_genkeys, 'gen_keys') + mock_manager.reset_mock() + self.test_context._name_task_id = 'demo-12345678' + self.test_context.get_neutron_info = mock.Mock() + with mock.patch.object(self.test_context, '_create_new_stack') as \ + mock_create_stack, \ + mock.patch.object(self.test_context, 'get_neutron_info') as \ + mock_neutron_info: + self.test_context.deploy() + + mock_neutron_info.assert_called_once() + mock_create_stack.assert_called_once() + key_filename = ''.join( + [consts.YARDSTICK_ROOT_PATH, + 'yardstick/resources/files/yardstick_key-', + self.test_context._name_task_id]) + mock_genkeys.assert_called_once_with(key_filename) + mock_path_exists.assert_called_with(key_filename) + + mock_call_gen_keys = mock.call.gen_keys(key_filename) + mock_call_add_resources = ( + mock.call._add_resources_to_template('heat_template')) + self.assertTrue(mock_manager.mock_calls.index(mock_call_gen_keys) < + mock_manager.mock_calls.index(mock_call_add_resources)) + + def test_check_for_context(self): + pass + # check that the context exists def test_add_server_port(self): network1 = mock.MagicMock() network2 = mock.MagicMock() - self.test_context.name = 'foo' + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) self.test_context.stack = mock.MagicMock() self.test_context.networks = { 'a': network1, @@ -173,15 +361,15 @@ class HeatContextTestCase(unittest.TestCase): self.test_context.stack.outputs = { u'b': u'10.20.30.45', u'b-subnet_id': 1, - u'foo-a-subnet-cidr': u'10.20.0.0/15', - u'foo-a-subnet-gateway_ip': u'10.20.30.1', + u'foo-12345678-a-subnet-cidr': u'10.20.0.0/15', + u'foo-12345678-a-subnet-gateway_ip': u'10.20.30.1', u'b-mac_address': u'00:01', u'b-device_id': u'dev21', u'b-network_id': u'net789', u'd': u'40.30.20.15', u'd-subnet_id': 2, - u'foo-c-subnet-cidr': u'40.30.0.0/18', - u'foo-c-subnet-gateway_ip': u'40.30.20.254', + u'foo-12345678-c-subnet-cidr': u'40.30.0.0/18', + u'foo-12345678-c-subnet-gateway_ip': u'40.30.20.254', u'd-mac_address': u'00:10', u'd-device_id': u'dev43', u'd-network_id': u'net987', @@ -192,6 +380,7 @@ class HeatContextTestCase(unittest.TestCase): u'e-network_id': u'net987', } server = mock.MagicMock() + server.private_ip = None server.ports = OrderedDict([ ('a', [{'stack_name': 'b', 'port': 'port_a'}]), ('c', [{'stack_name': 'd', 'port': 'port_c'}, @@ -218,17 +407,41 @@ class HeatContextTestCase(unittest.TestCase): self.assertEqual(len(server.interfaces), 3) self.assertDictEqual(server.interfaces['port_a'], expected) + @mock.patch('yardstick.benchmark.contexts.heat.os') + @mock.patch.object(heat.HeatContext, '_delete_key_file') @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') - def test_undeploy(self, mock_template): + def test_undeploy(self, mock_template, mock_delete_key, *args): self.test_context.stack = mock_template + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + # mock_os.path.exists.return_value = True + self.test_context.key_filename = 'foo/bar/foobar' self.test_context.undeploy() - self.assertTrue(mock_template.delete.called) + mock_delete_key.assert_called() + mock_template.delete.assert_called_once() + + @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') + def test_undeploy_no_teardown(self, mock_template): + self.test_context.stack = mock_template + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._flags.no_teardown = True + self.test_context.undeploy() + + mock_template.delete.assert_not_called() @mock.patch('yardstick.benchmark.contexts.heat.HeatTemplate') @mock.patch('yardstick.benchmark.contexts.heat.os') def test_undeploy_key_filename(self, mock_os, mock_template): self.test_context.stack = mock_template + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id) mock_os.path.exists.return_value = True + self.test_context.key_filename = 'foo/bar/foobar' self.assertIsNone(self.test_context.undeploy()) @mock.patch("yardstick.benchmark.contexts.heat.pkg_resources") @@ -249,24 +462,27 @@ class HeatContextTestCase(unittest.TestCase): baz3_server.public_ip = '127.0.0.3' baz3_server.context.user = 'zab' - self.test_context.name = 'bar' + self.test_context._name = 'bar' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) self.test_context._user = 'bot' self.test_context.stack = mock.Mock() self.test_context.stack.outputs = { 'private_ip': '10.0.0.1', 'public_ip': '127.0.0.1', } - self.test_context.key_uuid = uuid.uuid4() self.test_context._server_map = { 'baz3': baz3_server, 'foo2': foo2_server, } attr_name = { - 'name': 'foo.bar', + 'name': 'foo.bar-12345678', 'private_ip_attr': 'private_ip', 'public_ip_attr': 'public_ip', } + self.test_context.key_uuid = 'foo-42' result = self.test_context._get_server(attr_name) self.assertEqual(result['user'], 'bot') self.assertEqual(result['ip'], '127.0.0.1') @@ -288,22 +504,26 @@ class HeatContextTestCase(unittest.TestCase): baz3_server.public_ip = '127.0.0.3' baz3_server.context.user = 'zab' - self.test_context.name = 'bar' + self.test_context._name = 'bar' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) self.test_context._user = 'bot' self.test_context.stack = mock.Mock() self.test_context.stack.outputs = { 'private_ip': '10.0.0.1', 'public_ip': '127.0.0.1', } - self.test_context.key_uuid = uuid.uuid4() self.test_context._server_map = { 'baz3': baz3_server, 'foo2': foo2_server, } attr_name = { - 'name': 'foo.bar', + 'name': 'foo.bar-12345678', } + + self.test_context.key_uuid = 'foo-42' result = self.test_context._get_server(attr_name) self.assertEqual(result['user'], 'bot') # no private ip attr mapping in the map results in None value in the result @@ -327,13 +547,14 @@ class HeatContextTestCase(unittest.TestCase): baz3_server.public_ip = None baz3_server.context.user = 'zab' - self.test_context.name = 'bar1' + self.test_context._name = 'bar1' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = 'bar1-12345678' self.test_context.stack = mock.Mock() self.test_context.stack.outputs = { 'private_ip': '10.0.0.1', 'public_ip': '127.0.0.1', } - self.test_context.key_uuid = uuid.uuid4() self.test_context.generate_routing_table = mock.MagicMock(return_value=[]) self.test_context._server_map = { @@ -365,19 +586,19 @@ class HeatContextTestCase(unittest.TestCase): baz3_server.public_ip = None baz3_server.context.user = 'zab' - self.test_context.name = 'bar1' + self.test_context._name = 'bar1' self.test_context.stack = mock.Mock() self.test_context.stack.outputs = { 'private_ip': '10.0.0.1', 'public_ip': '127.0.0.1', } - self.test_context.key_uuid = uuid.uuid4() self.test_context._server_map = { 'baz3': baz3_server, 'foo2': foo2_server, 'wow4': None, } + self.test_context.key_uuid = 'foo-42' attr_name = 'wow4' result = self.test_context._get_server(attr_name) self.assertIsNone(result) @@ -398,18 +619,21 @@ class HeatContextTestCase(unittest.TestCase): baz3_server.public_ip = None baz3_server.context.user = 'zab' - self.test_context.name = 'bar1' + self.test_context._name = 'bar1' + self.test_context._task_id = '1235467890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) self.test_context.stack = mock.Mock() self.test_context.stack.outputs = { 'private_ip': '10.0.0.1', 'public_ip': '127.0.0.1', } - self.test_context.key_uuid = uuid.uuid4() self.test_context._server_map = { 'baz3': baz3_server, 'foo2': foo2_server, } + self.test_context.key_uuid = 'foo-42' attr_name = { 'name': 'foo.wow4', 'private_ip_attr': 'private_ip', @@ -434,18 +658,18 @@ class HeatContextTestCase(unittest.TestCase): baz3_server.public_ip = None baz3_server.context.user = 'zab' - self.mock_context.name = 'bar1' + self.mock_context._name = 'bar1' self.test_context.stack = mock.Mock() self.mock_context.stack.outputs = { 'private_ip': '10.0.0.1', 'public_ip': '127.0.0.1', } - self.mock_context.key_uuid = uuid.uuid4() self.mock_context._server_map = { 'baz3': baz3_server, 'foo2': foo2_server, } + self.test_context.key_uuid = 'foo-42' attr_name = 'foo.wow4' result = self.test_context._get_server(attr_name) self.assertIsNone(result) diff --git a/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py b/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py index e149e0d18..4dd9d40d1 100644 --- a/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py +++ b/yardstick/tests/unit/benchmark/contexts/test_kubernetes.py @@ -10,13 +10,13 @@ import mock import unittest -from yardstick.benchmark.contexts.base import Context from yardstick.benchmark.contexts import kubernetes context_cfg = { 'type': 'Kubernetes', 'name': 'k8s', + 'task_id': '1234567890', 'servers': { 'host': { 'image': 'openretriever/yardstick', @@ -40,11 +40,12 @@ class KubernetesTestCase(unittest.TestCase): def setUp(self): self.k8s_context = kubernetes.KubernetesContext() + self.addCleanup(self._remove_contexts) self.k8s_context.init(context_cfg) - def tearDown(self): - # clear kubernetes contexts from global list so we don't break other tests - Context.list = [] + def _remove_contexts(self): + if self.k8s_context in self.k8s_context.list: + self.k8s_context._delete_context() @mock.patch.object(kubernetes.KubernetesContext, '_delete_services') @mock.patch.object(kubernetes.KubernetesContext, '_delete_ssh_key') @@ -57,10 +58,10 @@ class KubernetesTestCase(unittest.TestCase): mock_delete_services): self.k8s_context.undeploy() - self.assertTrue(mock_delete_ssh.called) - self.assertTrue(mock_delete_rcs.called) - self.assertTrue(mock_delete_pods.called) - self.assertTrue(mock_delete_services.called) + mock_delete_ssh.assert_called_once() + mock_delete_rcs.assert_called_once() + mock_delete_pods.assert_called_once() + mock_delete_services.assert_called_once() @mock.patch.object(kubernetes.KubernetesContext, '_create_services') @mock.patch.object(kubernetes.KubernetesContext, '_wait_until_running') @@ -76,11 +77,11 @@ class KubernetesTestCase(unittest.TestCase): with mock.patch("yardstick.benchmark.contexts.kubernetes.time"): self.k8s_context.deploy() - self.assertTrue(mock_set_ssh_key.called) - self.assertTrue(mock_create_rcs.called) - self.assertTrue(mock_create_services.called) - self.assertTrue(mock_get_rc_pods.called) - self.assertTrue(mock_wait_until_running.called) + mock_set_ssh_key.assert_called_once() + mock_create_rcs.assert_called_once() + mock_create_services.assert_called_once() + mock_get_rc_pods.assert_called_once() + mock_wait_until_running.assert_called_once() @mock.patch.object(kubernetes, 'paramiko', **{"resource_filename.return_value": ""}) @mock.patch.object(kubernetes, 'pkg_resources', **{"resource_filename.return_value": ""}) @@ -92,8 +93,8 @@ class KubernetesTestCase(unittest.TestCase): self.k8s_context._set_ssh_key() self.k8s_context._delete_ssh_key() - self.assertTrue(mock_create.called) - self.assertTrue(mock_delete.called) + mock_create.assert_called_once() + mock_delete.assert_called_once() @mock.patch.object(kubernetes.k8s_utils, 'read_pod_status') def test_wait_until_running(self, mock_read_pod_status): @@ -135,42 +136,34 @@ class KubernetesTestCase(unittest.TestCase): @mock.patch.object(kubernetes.KubernetesContext, '_create_rc') def test_create_rcs(self, mock_create_rc): self.k8s_context._create_rcs() - self.assertTrue(mock_create_rc.called) + mock_create_rc.assert_called() @mock.patch.object(kubernetes.k8s_utils, 'create_replication_controller') def test_create_rc(self, mock_create_replication_controller): self.k8s_context._create_rc({}) - self.assertTrue(mock_create_replication_controller.called) + mock_create_replication_controller.assert_called_once() @mock.patch.object(kubernetes.KubernetesContext, '_delete_rc') def test_delete_rcs(self, mock_delete_rc): self.k8s_context._delete_rcs() - self.assertTrue(mock_delete_rc.called) + mock_delete_rc.assert_called() @mock.patch.object(kubernetes.k8s_utils, 'delete_replication_controller') def test_delete_rc(self, mock_delete_replication_controller): self.k8s_context._delete_rc({}) - self.assertTrue(mock_delete_replication_controller.called) + mock_delete_replication_controller.assert_called_once() @mock.patch.object(kubernetes.k8s_utils, 'get_node_list') def test_get_node_ip(self, mock_get_node_list): self.k8s_context._get_node_ip() - self.assertTrue(mock_get_node_list.called) + mock_get_node_list.assert_called_once() @mock.patch('yardstick.orchestrator.kubernetes.ServiceObject.create') def test_create_services(self, mock_create): self.k8s_context._create_services() - self.assertTrue(mock_create.called) + mock_create.assert_called() @mock.patch('yardstick.orchestrator.kubernetes.ServiceObject.delete') def test_delete_services(self, mock_delete): self.k8s_context._delete_services() - self.assertTrue(mock_delete.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_delete.assert_called() diff --git a/yardstick/tests/unit/benchmark/contexts/test_model.py b/yardstick/tests/unit/benchmark/contexts/test_model.py index 28011d494..20cc00b4e 100644 --- a/yardstick/tests/unit/benchmark/contexts/test_model.py +++ b/yardstick/tests/unit/benchmark/contexts/test_model.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Ericsson AB and others. # @@ -241,6 +239,7 @@ class ServerTestCase(unittest.TestCase): mock_network.vnic_type = 'normal' mock_network.subnet_stack_name = 'some-network-stack-subnet' mock_network.provider = 'sriov' + mock_network.net_flags = {} mock_network.external_network = 'ext_net' mock_network.router = model.Router('some-router', 'some-network', self.mock_context, 'ext_net') @@ -250,9 +249,7 @@ class ServerTestCase(unittest.TestCase): mock_template.add_port.assert_called_with( 'some-server-some-network-port', - mock_network.stack_name, - mock_network.subnet_stack_name, - mock_network.vnic_type, + mock_network, sec_group_id=self.mock_context.secgroup_name, provider=mock_network.provider, allowed_address_pairs=mock_network.allowed_address_pairs) @@ -513,6 +510,7 @@ class ServerTestCase(unittest.TestCase): mock_network = mock.Mock() mock_network.allowed_address_pairs = ["1", "2"] mock_network.vnic_type = 'normal' + mock_network.net_flags = {} mock_network.configure_mock(name='some-network', stack_name='some-network-stack', subnet_stack_name='some-network-stack-subnet', provider='some-provider') @@ -522,9 +520,7 @@ class ServerTestCase(unittest.TestCase): mock_template.add_port.assert_called_with( 'ServerFlavor-2-some-network-port', - mock_network.stack_name, - mock_network.subnet_stack_name, - mock_network.vnic_type, + mock_network, provider=mock_network.provider, sec_group_id=self.mock_context.secgroup_name, allowed_address_pairs=mock_network.allowed_address_pairs) @@ -556,6 +552,7 @@ class ServerTestCase(unittest.TestCase): mock_network.name = 'some-network' mock_network.stack_name = 'some-network-stack' mock_network.subnet_stack_name = 'some-network-stack-subnet' + mock_network.net_flags = {} test_server._add_instance(mock_template, 'ServerFlavor-3', [mock_network], 'hints') diff --git a/yardstick/tests/unit/benchmark/contexts/test_node.py b/yardstick/tests/unit/benchmark/contexts/test_node.py index 5329d30f4..8b232481b 100644 --- a/yardstick/tests/unit/benchmark/contexts/test_node.py +++ b/yardstick/tests/unit/benchmark/contexts/test_node.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015-2017 Huawei Technologies Co.,Ltd and others. # @@ -9,9 +7,6 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -# Unittest for yardstick.benchmark.contexts.node - -from __future__ import absolute_import import os import unittest import errno @@ -21,10 +16,6 @@ from yardstick.common import constants as consts from yardstick.benchmark.contexts import node -# pylint: disable=unused-argument -# disable this for now because I keep forgetting mock patch arg ordering - - class NodeContextTestCase(unittest.TestCase): PREFIX = 'yardstick.benchmark.contexts.node' @@ -34,7 +25,17 @@ class NodeContextTestCase(unittest.TestCase): def setUp(self): self.test_context = node.NodeContext() + self.addCleanup(self._remove_contexts) self.os_path_join = os.path.join + self.attrs = { + 'name': 'foo', + 'task_id': '1234567890', + 'file': self._get_file_abspath(self.NODES_SAMPLE) + } + + def _remove_contexts(self): + if self.test_context in self.test_context.list: + self.test_context._delete_context() def _get_file_abspath(self, filename): curr_path = os.path.dirname(os.path.abspath(__file__)) @@ -42,7 +43,7 @@ class NodeContextTestCase(unittest.TestCase): return file_path def test___init__(self): - self.assertIsNone(self.test_context.name) + self.assertIsNone(self.test_context._name) self.assertIsNone(self.test_context.file_path) self.assertEqual(self.test_context.nodes, []) self.assertEqual(self.test_context.controllers, []) @@ -74,6 +75,7 @@ class NodeContextTestCase(unittest.TestCase): attrs = { 'name': 'foo', + 'task_id': '1234567890', 'file': error_path, } read_mock.side_effect = IOError(errno.EBUSY, 'busy') @@ -97,37 +99,19 @@ class NodeContextTestCase(unittest.TestCase): self.assertEqual(str(raised.exception), str(read_mock.side_effect)) def test_read_config_file(self): - - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SAMPLE) - } - - self.test_context.init(attrs) + self.test_context.init(self.attrs) self.assertIsNotNone(self.test_context.read_config_file()) def test__dispatch_script(self): - - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SAMPLE) - } - - self.test_context.init(attrs) + self.test_context.init(self.attrs) self.test_context.env = {'bash': [{'script': 'dummy'}]} self.test_context._execute_script = mock.Mock() self.assertEqual(self.test_context._dispatch_script('bash'), None) def test__dispatch_ansible(self): - - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SAMPLE) - } - - self.test_context.init(attrs) + self.test_context.init(self.attrs) self.test_context.env = {'ansible': [{'script': 'dummy'}]} self.test_context._do_ansible_job = mock.Mock() @@ -136,19 +120,13 @@ class NodeContextTestCase(unittest.TestCase): self.assertEqual(self.test_context._dispatch_ansible('ansible'), None) @mock.patch("{}.AnsibleCommon".format(PREFIX)) - def test__do_ansible_job(self, mock_ansible): - self.assertEqual(None, self.test_context._do_ansible_job('dummy')) - - def test_successful_init(self): - - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SAMPLE) - } + def test__do_ansible_job(self, *args): + self.assertIsNone(self.test_context._do_ansible_job('dummy')) - self.test_context.init(attrs) + def test_init(self): + self.test_context.init(self.attrs) - self.assertEqual(self.test_context.name, "foo") + self.assertEqual(self.test_context.name, "foo-12345678") self.assertEqual(len(self.test_context.nodes), 4) self.assertEqual(len(self.test_context.controllers), 2) self.assertEqual(len(self.test_context.computes), 1) @@ -156,95 +134,59 @@ class NodeContextTestCase(unittest.TestCase): self.assertEqual(len(self.test_context.baremetals), 1) self.assertEqual(self.test_context.baremetals[0]["name"], "node4") - def test__get_server_with_dic_attr_name(self): - - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SAMPLE) - } - - self.test_context.init(attrs) - - attr_name = {'name': 'foo.bar'} - result = self.test_context._get_server(attr_name) + def test__get_server_with_dict_attr_name(self): + self.test_context.init(self.attrs) + result = self.test_context._get_server({'name': 'node1.foo-12345678'}) - self.assertEqual(result, None) + self.assertIsNone(result, None) def test__get_server_not_found(self): + self.test_context.init(self.attrs) - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SAMPLE) - } - - self.test_context.init(attrs) - - attr_name = 'bar.foo' - result = self.test_context._get_server(attr_name) - - self.assertEqual(result, None) + self.assertIsNone(self.test_context._get_server('bar.foo-12345678')) def test__get_server_mismatch(self): + self.test_context.init(self.attrs) - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SAMPLE) - } - - self.test_context.init(attrs) - - attr_name = 'bar.foo1' - result = self.test_context._get_server(attr_name) - - self.assertEqual(result, None) + self.assertIsNone(self.test_context._get_server('bar.foo1')) def test__get_server_duplicate(self): + self.attrs['file'] = self._get_file_abspath( + self.NODES_DUPLICATE_SAMPLE) + self.test_context.init(self.attrs) - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_DUPLICATE_SAMPLE) - } - - self.test_context.init(attrs) - - attr_name = 'node1.foo' with self.assertRaises(ValueError): - self.test_context._get_server(attr_name) + self.test_context._get_server('node1.foo-12345678') def test__get_server_found(self): + self.test_context.init(self.attrs) - attrs = { - 'name': 'foo', - 'file': self._get_file_abspath(self.NODES_SAMPLE) - } - - self.test_context.init(attrs) - - attr_name = 'node1.foo' - result = self.test_context._get_server(attr_name) + result = self.test_context._get_server('node1.foo-12345678') self.assertEqual(result['ip'], '10.229.47.137') - self.assertEqual(result['name'], 'node1.foo') + self.assertEqual(result['name'], 'node1.foo-12345678') self.assertEqual(result['user'], 'root') self.assertEqual(result['key_filename'], '/root/.yardstick_key') @mock.patch('{}.NodeContext._dispatch_script'.format(PREFIX)) def test_deploy(self, dispatch_script_mock): obj = node.NodeContext() + self.addCleanup(obj._delete_context) obj.env = { 'type': 'script' } obj.deploy() - self.assertTrue(dispatch_script_mock.called) + dispatch_script_mock.assert_called_once() @mock.patch('{}.NodeContext._dispatch_ansible'.format(PREFIX)) def test_deploy_anisible(self, dispatch_ansible_mock): obj = node.NodeContext() + self.addCleanup(obj._delete_context) obj.env = { 'type': 'ansible' } obj.deploy() - self.assertTrue(dispatch_ansible_mock.called) + dispatch_ansible_mock.assert_called_once() @mock.patch('{}.NodeContext._dispatch_script'.format(PREFIX)) def test_undeploy(self, dispatch_script_mock): @@ -253,7 +195,7 @@ class NodeContextTestCase(unittest.TestCase): 'type': 'script' } obj.undeploy() - self.assertTrue(dispatch_script_mock.called) + dispatch_script_mock.assert_called_once() @mock.patch('{}.NodeContext._dispatch_ansible'.format(PREFIX)) def test_undeploy_anisble(self, dispatch_ansible_mock): @@ -262,12 +204,13 @@ class NodeContextTestCase(unittest.TestCase): 'type': 'ansible' } obj.undeploy() - self.assertTrue(dispatch_ansible_mock.called) + dispatch_ansible_mock.assert_called_once() @mock.patch('{}.ssh.SSH._put_file_shell'.format(PREFIX)) @mock.patch('{}.ssh.SSH.execute'.format(PREFIX)) def test_execute_remote_script(self, execute_mock, put_file_mock): obj = node.NodeContext() + self.addCleanup(obj._delete_context) obj.env = {'prefix': 'yardstick.benchmark.scenarios.compute'} node_name_args = 'node5' obj.nodes = [{ @@ -281,35 +224,42 @@ class NodeContextTestCase(unittest.TestCase): execute_mock.return_value = (0, '', '') obj._execute_remote_script('node5', info) - self.assertTrue(put_file_mock.called) - self.assertTrue(execute_mock.called) + put_file_mock.assert_called_once() + execute_mock.assert_called() @mock.patch('{}.NodeContext._execute_local_script'.format(PREFIX)) def test_execute_script_local(self, local_execute_mock): node_name = 'local' info = {} - node.NodeContext()._execute_script(node_name, info) - self.assertTrue(local_execute_mock.called) + obj = node.NodeContext() + self.addCleanup(obj._delete_context) + obj._execute_script(node_name, info) + local_execute_mock.assert_called_once() @mock.patch('{}.NodeContext._execute_remote_script'.format(PREFIX)) def test_execute_script_remote(self, remote_execute_mock): node_name = 'node5' info = {} - node.NodeContext()._execute_script(node_name, info) - self.assertTrue(remote_execute_mock.called) + obj = node.NodeContext() + self.addCleanup(obj._delete_context) + obj._execute_script(node_name, info) + remote_execute_mock.assert_called_once() def test_get_script(self): script_args = 'hello.bash' info_args = { 'script': script_args } - script, options = node.NodeContext()._get_script(info_args) + obj = node.NodeContext() + self.addCleanup(obj._delete_context) + script, options = obj._get_script(info_args) self.assertEqual(script_args, script) self.assertEqual('', options) def test_node_info(self): node_name_args = 'node5' obj = node.NodeContext() + self.addCleanup(obj._delete_context) obj.nodes = [{'name': node_name_args, 'check': node_name_args}] node_info = obj._get_node_info(node_name_args) self.assertEqual(node_info.get('check'), node_name_args) @@ -318,6 +268,7 @@ class NodeContextTestCase(unittest.TestCase): def test_get_client(self, wait_mock): node_name_args = 'node5' obj = node.NodeContext() + self.addCleanup(obj._delete_context) obj.nodes = [{ 'name': node_name_args, 'user': 'ubuntu', @@ -325,29 +276,41 @@ class NodeContextTestCase(unittest.TestCase): 'pwd': 'ubuntu', }] obj._get_client(node_name_args) - self.assertTrue(wait_mock.called) + wait_mock.assert_called_once() def test_get_server(self): - self.test_context.name = 'vnf1' - self.test_context.nodes = [{'name': 'my', 'value': 100}] + self.test_context.init(self.attrs) + self.test_context._name = 'foo' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) + self.assertEqual('foo-12345678', self.test_context.name) + self.assertIsNotNone(self.test_context._task_id) - with self.assertRaises(ValueError): - self.test_context.get_server('my.vnf2') + result = self.test_context.get_server('node1.foo-12345678') - expected = {'name': 'my.vnf1', 'value': 100, 'interfaces': {}} - result = self.test_context.get_server('my.vnf1') - self.assertDictEqual(result, expected) + self.assertEqual(result['ip'], '10.229.47.137') + self.assertEqual(result['name'], 'node1.foo-12345678') + self.assertEqual(result['user'], 'root') + self.assertEqual(result['key_filename'], '/root/.yardstick_key') + + def test_get_server_server_not_in_context(self): + self.test_context.init(self.attrs) + + with self.assertRaises(ValueError): + self.test_context.get_server('my2.foo-12345678') def test_get_context_from_server(self): - self.test_context.name = 'vnf1' + self.test_context._name = 'vnf1' + self.test_context._task_id = '1234567890' + self.test_context._name_task_id = '{}-{}'.format( + self.test_context._name, self.test_context._task_id[:8]) self.test_context.nodes = [{'name': 'my', 'value': 100}] self.test_context.attrs = {'attr1': 200} - with self.assertRaises(ValueError): - self.test_context.get_context_from_server('my.vnf2') - - result = self.test_context.get_context_from_server('my.vnf1') - self.assertIs(result, self.test_context) + self.assertIs( + self.test_context.get_context_from_server('my.vnf1-12345678'), + self.test_context) # TODO: Split this into more granular tests def test__get_network(self): @@ -393,11 +356,3 @@ class NodeContextTestCase(unittest.TestCase): expected = network1 result = self.test_context._get_network(attr_name) self.assertDictEqual(result, expected) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/core/test_report.py b/yardstick/tests/unit/benchmark/core/test_report.py index 3d9a503b6..a684ad750 100644 --- a/yardstick/tests/unit/benchmark/core/test_report.py +++ b/yardstick/tests/unit/benchmark/core/test_report.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2017 Rajesh Kudaka. # diff --git a/yardstick/tests/unit/benchmark/core/test_task.py b/yardstick/tests/unit/benchmark/core/test_task.py index ee00d8826..1ce30eacb 100644 --- a/yardstick/tests/unit/benchmark/core/test_task.py +++ b/yardstick/tests/unit/benchmark/core/test_task.py @@ -7,13 +7,22 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## +import copy +import io import os +import sys import mock +import six import unittest +import uuid +from yardstick.benchmark.contexts import dummy from yardstick.benchmark.core import task from yardstick.common import constants as consts +from yardstick.common import exceptions +from yardstick.common import task_template +from yardstick.common import utils class TaskTestCase(unittest.TestCase): @@ -57,7 +66,7 @@ class TaskTestCase(unittest.TestCase): mock_dispatcher.get = mock.MagicMock(return_value=[dispatcher1, dispatcher2]) - self.assertEqual(None, t._do_output(output_config, {})) + self.assertIsNone(t._do_output(output_config, {})) @mock.patch.object(task, 'Context') def test_parse_networks_from_nodes(self, mock_context): @@ -144,7 +153,7 @@ class TaskTestCase(unittest.TestCase): runner.get_result.return_value = [] mock_base_runner.Runner.get.return_value = runner t._run([scenario], False, "yardstick.out") - self.assertTrue(runner.run.called) + runner.run.assert_called_once() @mock.patch.object(os, 'environ') def test_check_precondition(self, mock_os_environ): @@ -174,7 +183,6 @@ class TaskTestCase(unittest.TestCase): 'tests/opnfv/test_cases/opnfv_yardstick_tc037.yaml')) self.assertEqual(task_files[1], self.change_to_abspath( 'tests/opnfv/test_cases/opnfv_yardstick_tc043.yaml')) - self.assertIsNone(task_args[0]) self.assertIsNone(task_args[1]) self.assertIsNone(task_args_fnames[0]) @@ -256,30 +264,37 @@ class TaskTestCase(unittest.TestCase): actual_result = t._parse_options(options) self.assertEqual(expected_result, actual_result) + def test_parse_options_no_teardown(self): + options = { + 'openstack': { + 'EXTERNAL_NETWORK': '$network' + }, + 'nodes': ['node1', '$node'], + 'host': '$host', + 'contexts' : {'name': "my-context", + 'no_teardown': True} + } - def test_change_server_name_host_str(self): - scenario = {'host': 'demo'} - suffix = '-8' - task.change_server_name(scenario, suffix) - self.assertEqual('demo-8', scenario['host']) - - def test_change_server_name_host_dict(self): - scenario = {'host': {'name': 'demo'}} - suffix = '-8' - task.change_server_name(scenario, suffix) - self.assertEqual('demo-8', scenario['host']['name']) + t = task.Task() + t.outputs = { + 'network': 'ext-net', + 'node': 'node2', + 'host': 'server.yardstick' + } - def test_change_server_name_target_str(self): - scenario = {'target': 'demo'} - suffix = '-8' - task.change_server_name(scenario, suffix) - self.assertEqual('demo-8', scenario['target']) + expected_result = { + 'openstack': { + 'EXTERNAL_NETWORK': 'ext-net' + }, + 'nodes': ['node1', 'node2'], + 'host': 'server.yardstick', + 'contexts': {'name': 'my-context', + 'no_teardown': True, + } + } - def test_change_server_name_target_dict(self): - scenario = {'target': {'name': 'demo'}} - suffix = '-8' - task.change_server_name(scenario, suffix) - self.assertEqual('demo-8', scenario['target']['name']) + actual_result = t._parse_options(options) + self.assertEqual(expected_result, actual_result) @mock.patch('six.moves.builtins.open', side_effect=mock.mock_open()) @mock.patch.object(task, 'utils') @@ -299,9 +314,219 @@ class TaskTestCase(unittest.TestCase): return os.path.join(consts.YARDSTICK_ROOT_PATH, filepath) -def main(): - unittest.main() +class TaskParserTestCase(unittest.TestCase): + + TASK = """ +{% set value1 = value1 or 'var1' %} +{% set value2 = value2 or 'var2' %} +key1: {{ value1 }} +key2: + - {{ value2 }}""" + TASK_RENDERED_1 = u""" -if __name__ == '__main__': - main() + +key1: var1 +key2: + - var2""" + + TASK_RENDERED_2 = u""" + + +key1: var3 +key2: + - var4""" + + def setUp(self): + self.parser = task.TaskParser('fake/path') + self.scenario = { + 'host': 'athena.demo', + 'target': 'kratos.demo', + 'targets': [ + 'ares.demo', 'mars.demo' + ], + 'options': { + 'server_name': { + 'host': 'jupiter.demo', + 'target': 'saturn.demo', + }, + }, + 'nodes': { + 'tg__0': 'tg_0.demo', + 'vnf__0': 'vnf_0.demo', + } + } + + def test__change_node_names(self): + + ctx_attrs = { + 'name': 'demo', + 'task_id': '1234567890', + 'servers': [ + 'athena', 'kratos', + 'ares', 'mars', + 'jupiter', 'saturn', + 'tg_0', 'vnf_0' + ] + } + + my_context = dummy.DummyContext() + my_context.init(ctx_attrs) + + expected_scenario = { + 'host': 'athena.demo-12345678', + 'target': 'kratos.demo-12345678', + 'targets': [ + 'ares.demo-12345678', 'mars.demo-12345678' + ], + 'options': { + 'server_name': { + 'host': 'jupiter.demo-12345678', + 'target': 'saturn.demo-12345678', + }, + }, + 'nodes': { + 'tg__0': 'tg_0.demo-12345678', + 'vnf__0': 'vnf_0.demo-12345678', + } + } + + scenario = copy.deepcopy(self.scenario) + + self.parser._change_node_names(scenario, [my_context]) + self.assertEqual(scenario, expected_scenario) + + def test__change_node_names_context_not_found(self): + scenario = copy.deepcopy(self.scenario) + self.assertRaises(exceptions.ScenarioConfigContextNameNotFound, + self.parser._change_node_names, + scenario, []) + + def test__change_node_names_context_name_unchanged(self): + ctx_attrs = { + 'name': 'demo', + 'task_id': '1234567890', + 'flags': { + 'no_setup': True, + 'no_teardown': True + } + } + + my_context = dummy.DummyContext() + my_context.init(ctx_attrs) + + scenario = copy.deepcopy(self.scenario) + expected_scenario = copy.deepcopy(self.scenario) + + self.parser._change_node_names(scenario, [my_context]) + self.assertEqual(scenario, expected_scenario) + + def test__parse_tasks(self): + task_obj = task.Task() + _uuid = uuid.uuid4() + task_obj.task_id = _uuid + task_files = ['/directory/task_file_name.yml'] + mock_parser = mock.Mock() + mock_parser.parse_task.return_value = {'rendered': 'File content'} + mock_args = mock.Mock() + mock_args.render_only = False + + tasks = task_obj._parse_tasks(mock_parser, task_files, mock_args, + ['arg1'], ['file_arg1']) + self.assertEqual( + [{'rendered': 'File content', 'case_name': 'task_file_name'}], + tasks) + mock_parser.parse_task.assert_called_once_with( + _uuid, 'arg1', 'file_arg1') + + @mock.patch.object(sys, 'exit') + @mock.patch.object(utils, 'write_file') + @mock.patch.object(utils, 'makedirs') + def test__parse_tasks_render_only(self, mock_makedirs, mock_write_file, + mock_exit): + task_obj = task.Task() + _uuid = uuid.uuid4() + task_obj.task_id = _uuid + task_files = ['/directory/task_file_name.yml'] + mock_parser = mock.Mock() + mock_parser.parse_task.return_value = {'rendered': 'File content'} + mock_args = mock.Mock() + mock_args.render_only = '/output_directory' + + task_obj._parse_tasks(mock_parser, task_files, mock_args, + ['arg1'], ['file_arg1']) + mock_makedirs.assert_called_once_with('/output_directory') + mock_write_file.assert_called_once_with( + '/output_directory/000-task_file_name.yml', 'File content') + mock_exit.assert_called_once_with(0) + + def test__render_task_no_args(self): + task_parser = task.TaskParser('task_file') + task_str = io.StringIO(six.text_type(self.TASK)) + with mock.patch.object(six.moves.builtins, 'open', + return_value=task_str) as mock_open: + parsed, rendered = task_parser._render_task(None, None) + + self.assertEqual(self.TASK_RENDERED_1, rendered) + self.assertEqual({'key1': 'var1', 'key2': ['var2']}, parsed) + mock_open.assert_called_once_with('task_file') + + def test__render_task_arguments(self): + task_parser = task.TaskParser('task_file') + task_str = io.StringIO(six.text_type(self.TASK)) + with mock.patch.object(six.moves.builtins, 'open', + return_value=task_str) as mock_open: + parsed, rendered = task_parser._render_task('value1: "var1"', None) + + self.assertEqual(self.TASK_RENDERED_1, rendered) + self.assertEqual({'key1': 'var1', 'key2': ['var2']}, parsed) + mock_open.assert_called_once_with('task_file') + + def test__render_task_file_arguments(self): + task_parser = task.TaskParser('task_file') + with mock.patch.object(six.moves.builtins, 'open') as mock_open: + mock_open.side_effect = ( + io.StringIO(six.text_type('value2: var4')), + io.StringIO(six.text_type(self.TASK)) + ) + parsed, rendered = task_parser._render_task('value1: "var3"', + 'args_file') + + self.assertEqual(self.TASK_RENDERED_2, rendered) + self.assertEqual({'key1': 'var3', 'key2': ['var4']}, parsed) + mock_open.assert_has_calls([mock.call('args_file'), + mock.call('task_file')]) + + def test__render_task_error_arguments(self): + with self.assertRaises(exceptions.TaskRenderArgumentError): + task.TaskParser('task_file')._render_task('value1="var3"', None) + + def test__render_task_error_task_file(self): + task_parser = task.TaskParser('task_file') + with mock.patch.object(six.moves.builtins, 'open') as mock_open: + mock_open.side_effect = ( + io.StringIO(six.text_type('value2: var4')), + IOError() + ) + with self.assertRaises(exceptions.TaskReadError): + task_parser._render_task('value1: "var3"', 'args_file') + + mock_open.assert_has_calls([mock.call('args_file'), + mock.call('task_file')]) + + def test__render_task_render_error(self): + task_parser = task.TaskParser('task_file') + with mock.patch.object(six.moves.builtins, 'open') as mock_open, \ + mock.patch.object(task_template.TaskTemplate, 'render', + side_effect=TypeError) as mock_render: + mock_open.side_effect = ( + io.StringIO(six.text_type('value2: var4')), + io.StringIO(six.text_type(self.TASK)) + ) + with self.assertRaises(exceptions.TaskRenderError): + task_parser._render_task('value1: "var3"', 'args_file') + + mock_open.assert_has_calls([mock.call('args_file'), + mock.call('task_file')]) + mock_render.assert_has_calls( + [mock.call(self.TASK, value1='var3', value2='var4')]) diff --git a/yardstick/tests/unit/benchmark/core/test_testcase.py b/yardstick/tests/unit/benchmark/core/test_testcase.py index 1f5aad75e..119465887 100644 --- a/yardstick/tests/unit/benchmark/core/test_testcase.py +++ b/yardstick/tests/unit/benchmark/core/test_testcase.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. # @@ -35,11 +33,3 @@ class TestcaseUT(unittest.TestCase): casename = Arg() result = t.show(casename) self.assertTrue(result) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/runner/test_base.py b/yardstick/tests/unit/benchmark/runner/test_base.py index 59739c54f..727207f5a 100644 --- a/yardstick/tests/unit/benchmark/runner/test_base.py +++ b/yardstick/tests/unit/benchmark/runner/test_base.py @@ -90,11 +90,3 @@ class RunnerTestCase(unittest.TestCase): with self.assertRaises(NotImplementedError): runner._run_benchmark(mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock()) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/runner/test_search.py b/yardstick/tests/unit/benchmark/runner/test_search.py index 1bc07448d..4e5b4fe77 100644 --- a/yardstick/tests/unit/benchmark/runner/test_search.py +++ b/yardstick/tests/unit/benchmark/runner/test_search.py @@ -17,15 +17,8 @@ import time import mock import unittest -from yardstick.tests.unit import STL_MOCKS - -STLClient = mock.MagicMock() -stl_patch = mock.patch.dict("sys.modules", STL_MOCKS) -stl_patch.start() - -if stl_patch: - from yardstick.benchmark.runners.search import SearchRunner - from yardstick.benchmark.runners.search import SearchRunnerHelper +from yardstick.benchmark.runners.search import SearchRunner +from yardstick.benchmark.runners.search import SearchRunnerHelper class TestSearchRunnerHelper(unittest.TestCase): diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_baremetal.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_baremetal.py index f0921c0f6..d5c95a086 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_baremetal.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_baremetal.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. # diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_general.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_general.py index 612b5a662..c1b3c0d72 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_general.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_general.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Juan Qiu and others # juan_ qiu@tongji.edu.cn diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_process.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_process.py index 0a8e8322a..2e9f1c6bb 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_process.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_attacker_process.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. # diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_basemonitor.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_basemonitor.py index 9bc04ebf4..ce972779d 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_basemonitor.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_basemonitor.py @@ -113,7 +113,3 @@ class BaseMonitorTestCase(unittest.TestCase): except Exception: # pylint: disable=broad-except pass self.assertIsNone(cls) - - -if __name__ == "__main__": - unittest.main() diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_baseresultchecker.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_baseresultchecker.py index ae74d241c..d4df02819 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_baseresultchecker.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_baseresultchecker.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huan Li and others # lihuansse@tongji.edu.cn diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_director.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_director.py index 72ce7b0d5..e49544e1c 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_director.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_director.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huan Li and others # lihuansse@tongji.edu.cn diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_general.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_general.py index 7022ea678..5907c8b6a 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_general.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_general.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huan Li and others # lihuansse@tongji.edu.cn diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_multi.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_multi.py index 0d61d9b15..e9c680257 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_multi.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_multi.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huan Li and others # lihuansse@tongji.edu.cn diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_process.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_process.py index 41ce5445e..a6d2ca398 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_process.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_monitor_process.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. # diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_operation_general.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_operation_general.py index a965f7f64..2b09c0385 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_operation_general.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_operation_general.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huan Li and others # lihuansse@tongji.edu.cn diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_result_checker_general.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_result_checker_general.py index 234adcb6e..324a5bda2 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_result_checker_general.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_result_checker_general.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huan Li and others # lihuansse@tongji.edu.cn diff --git a/yardstick/tests/unit/benchmark/scenarios/availability/test_util.py b/yardstick/tests/unit/benchmark/scenarios/availability/test_util.py index 548efe91b..4d97585d4 100644 --- a/yardstick/tests/unit/benchmark/scenarios/availability/test_util.py +++ b/yardstick/tests/unit/benchmark/scenarios/availability/test_util.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Kanglin Yin and others # 14_ykl@tongji.edu.cn diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_cachestat.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_cachestat.py index b0ddfc6b4..6f66c30f9 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_cachestat.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_cachestat.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. # diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_computecapacity.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_computecapacity.py index 7b9a5ad4a..4bef589f4 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_computecapacity.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_computecapacity.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. # diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_cpuload.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_cpuload.py index 840ac7885..da6e6a22e 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_cpuload.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_cpuload.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Ericsson AB and others. # diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_cyclictest.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_cyclictest.py index 51ffd2488..f24ec24ec 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_cyclictest.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_cyclictest.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and other. # @@ -166,10 +164,3 @@ class CyclictestTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, c.run, result) - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_lmbench.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_lmbench.py index b3152d12c..9640ce000 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_lmbench.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_lmbench.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Ericsson AB and others. # @@ -193,10 +191,3 @@ class LmbenchTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, l.run, self.result) - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_memload.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_memload.py index ebae9993d..8213d4490 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_memload.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_memload.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. # @@ -109,11 +107,3 @@ class MEMLoadTestCase(unittest.TestCase): with open(output) as f: sample_output = f.read() return sample_output - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_plugintest.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_plugintest.py index 680f6ad65..875301729 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_plugintest.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_plugintest.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. # diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py index 26a26cdf7..03003d01f 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_qemumigrate.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and other. # @@ -157,11 +155,3 @@ class QemuMigrateTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, q.run, result) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_ramspeed.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_ramspeed.py index 4f71fbb36..dcc0e810d 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_ramspeed.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_ramspeed.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. # @@ -235,10 +233,3 @@ class RamspeedTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'No such type_id: 30 for \ Ramspeed scenario') self.assertRaises(RuntimeError, r.run, self.result) - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu.py index 74612d7b6..643e1eae2 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. # @@ -75,11 +73,3 @@ class SpecCPUTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, s.run, self.result) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu_for_vm.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu_for_vm.py index c428e1fb8..74ef576b6 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu_for_vm.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_spec_cpu_for_vm.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. # @@ -76,9 +74,3 @@ class SpecCPUforVMTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, s.run, self.result) - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/compute/test_unixbench.py b/yardstick/tests/unit/benchmark/scenarios/compute/test_unixbench.py index fec355b45..6339a2dcd 100644 --- a/yardstick/tests/unit/benchmark/scenarios/compute/test_unixbench.py +++ b/yardstick/tests/unit/benchmark/scenarios/compute/test_unixbench.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and other. # @@ -162,10 +160,3 @@ class UnixbenchTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, u.run, result) - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/dummy/test_dummy.py b/yardstick/tests/unit/benchmark/scenarios/dummy/test_dummy.py index bc5131806..875302da8 100644 --- a/yardstick/tests/unit/benchmark/scenarios/dummy/test_dummy.py +++ b/yardstick/tests/unit/benchmark/scenarios/dummy/test_dummy.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. # diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_add_memory_load.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_add_memory_load.py index bda07f723..af4f0c8ab 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_add_memory_load.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_add_memory_load.py @@ -27,7 +27,7 @@ class AddMemoryLoadTestCase(unittest.TestCase): mock_from_node().execute.return_value = (0, '0 2048 512', '') obj = AddMemoryLoad(scenario_cfg, context_cfg) obj.run({}) - self.assertTrue(mock_from_node.called) + mock_from_node.assert_called() @mock.patch('yardstick.ssh.SSH.from_node') def test_add_memory_load_without_load(self, mock_from_node): @@ -41,7 +41,7 @@ class AddMemoryLoadTestCase(unittest.TestCase): } obj = AddMemoryLoad(scenario_cfg, context_cfg) obj.run({}) - self.assertTrue(mock_from_node.called) + mock_from_node.assert_called_once() @mock.patch('yardstick.ssh.SSH.from_node') def test_add_memory_load_without_args(self, mock_from_node): @@ -54,12 +54,4 @@ class AddMemoryLoadTestCase(unittest.TestCase): } obj = AddMemoryLoad(scenario_cfg, context_cfg) obj.run({}) - self.assertTrue(mock_from_node.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_from_node.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_attach_volume.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_attach_volume.py index 25b911d5e..2964ecc14 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_attach_volume.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_attach_volume.py @@ -23,12 +23,4 @@ class AttachVolumeTestCase(unittest.TestCase): args = {"options": options} obj = AttachVolume(args, {}) obj.run({}) - self.assertTrue(mock_attach_server_volume.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_attach_server_volume.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_check_connectivity.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_connectivity.py index 7188c29d5..a48353a4f 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_check_connectivity.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_connectivity.py @@ -76,11 +76,3 @@ class CheckConnectivityTestCase(unittest.TestCase): obj.setup() mock_ssh.SSH.execute.return_value = (0, '100', '') - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_check_numa_info.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_numa_info.py index f983f9c5b..270c9d3c9 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_check_numa_info.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_numa_info.py @@ -19,7 +19,7 @@ class CheckNumaInfoTestCase(unittest.TestCase): scenario_cfg = {'info1': {}, 'info2': {}} obj = CheckNumaInfo(scenario_cfg, {}) obj.run({}) - self.assertTrue(mock_check_vm2.called) + mock_check_vm2.assert_called_once() def test_check_vm2_status_length_eq_1(self): info1 = { @@ -74,11 +74,3 @@ class CheckNumaInfoTestCase(unittest.TestCase): obj = CheckNumaInfo(scenario_cfg, {}) status = obj._check_vm2_status(info1, info2) self.assertFalse(status) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_check_value.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_value.py index 5a40e7d8f..7a2324b3d 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_check_value.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_check_value.py @@ -33,10 +33,3 @@ class CheckValueTestCase(unittest.TestCase): obj = CheckValue(scenario_cfg, {}) self.assertRaises(AssertionError, obj.run, self.result) self.assertEqual({}, self.result) - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_flavor.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_flavor.py index 036ae952d..0b175fae8 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_flavor.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_flavor.py @@ -26,12 +26,4 @@ class CreateFlavorTestCase(unittest.TestCase): args = {"options": options} obj = CreateFlavor(args, {}) obj.run({}) - self.assertTrue(mock_create_flavor.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_create_flavor.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_image.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_image.py index b26957979..639cf2906 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_image.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_image.py @@ -33,11 +33,3 @@ class CreateImageTestCase(unittest.TestCase): obj.run({}) mock_create_image.assert_called_once() mock_get_glance_client.assert_called_once() - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_keypair.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_keypair.py index 10e351b5e..1c3d6cebc 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_keypair.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_keypair.py @@ -25,11 +25,3 @@ class CreateKeypairTestCase(unittest.TestCase): obj = create_keypair.CreateKeypair(args, {}) obj.run({}) mock_op_utils.create_keypair.assert_called_once() - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_network.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_network.py index e0382851f..17a4ef2e1 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_network.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_network.py @@ -6,33 +6,52 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## + +from oslo_utils import uuidutils import unittest import mock -from yardstick.benchmark.scenarios.lib.create_network import CreateNetwork +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import create_network class CreateNetworkTestCase(unittest.TestCase): - @mock.patch('yardstick.common.openstack_utils.get_neutron_client') - @mock.patch('yardstick.common.openstack_utils.create_neutron_net') - def test_create_network(self, mock_get_neutron_client, mock_create_neutron_net): - options = { - 'openstack_paras': { - 'name': 'yardstick_net', - 'admin_state_up': 'True' - } - } - args = {"options": options} - obj = CreateNetwork(args, {}) - obj.run({}) - self.assertTrue(mock_get_neutron_client.called) - self.assertTrue(mock_create_neutron_net.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + def setUp(self): + + self._mock_create_neutron_net = mock.patch.object( + openstack_utils, 'create_neutron_net') + self.mock_create_neutron_net = self._mock_create_neutron_net.start() + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_network, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'network_name': 'yardstick_net'}} + self.result = {} + + self._cnet_obj = create_network.CreateNetwork(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_create_neutron_net.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self._cnet_obj.scenario_cfg = {'output': 'id'} + self.mock_create_neutron_net.return_value = _uuid + output = self._cnet_obj.run(self.result) + self.assertEqual({"network_create": 1}, self.result) + self.assertEqual({'id': _uuid}, output) + self.mock_log.info.asset_called_once_with('Create network successful!') + + def test_run_fail_exception(self): + self.mock_create_neutron_net.return_value = None + with self.assertRaises(exceptions.ScenarioCreateNetworkError): + self._cnet_obj.run(self.result) + self.assertEqual({"network_create": 0}, self.result) + self.mock_log.error.assert_called_once_with( + 'Create network failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_port.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_port.py index 0f15058da..bea02a630 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_port.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_port.py @@ -24,12 +24,4 @@ class CreatePortTestCase(unittest.TestCase): args = {"options": options} obj = CreatePort(args, {}) obj.run({}) - self.assertTrue(mock_get_neutron_client.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_neutron_client.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_router.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_router.py index 8f3914b83..3469a2a06 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_router.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_router.py @@ -26,13 +26,5 @@ class CreateRouterTestCase(unittest.TestCase): args = {"options": options} obj = CreateRouter(args, {}) obj.run({}) - self.assertTrue(mock_get_neutron_client.called) - self.assertTrue(mock_create_neutron_router.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_neutron_client.assert_called_once() + mock_create_neutron_router.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_sec_group.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_sec_group.py index c1c137cda..21158ab17 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_sec_group.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_sec_group.py @@ -26,13 +26,5 @@ class CreateSecGroupTestCase(unittest.TestCase): args = {"options": options} obj = CreateSecgroup(args, {}) obj.run({}) - self.assertTrue(mock_get_neutron_client.called) - self.assertTrue(mock_create_security_group_full.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_neutron_client.assert_called_once() + mock_create_security_group_full.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_server.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_server.py index 74003b995..9d6d8cb1b 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_server.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_server.py @@ -28,15 +28,7 @@ class CreateServerTestCase(unittest.TestCase): } obj = CreateServer(scenario_cfg, {}) obj.run({}) - self.assertTrue(mock_get_nova_client.called) - self.assertTrue(mock_get_glance_client.called) - self.assertTrue(mock_get_neutron_client.called) - self.assertTrue(mock_create_instance_and_wait_for_active.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_nova_client.assert_called_once() + mock_get_glance_client.assert_called_once() + mock_get_neutron_client.assert_called_once() + mock_create_instance_and_wait_for_active.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_subnet.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_subnet.py index b7f29dfe4..856e985c4 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_subnet.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_subnet.py @@ -6,35 +6,53 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## + +from oslo_utils import uuidutils import unittest import mock -from yardstick.benchmark.scenarios.lib.create_subnet import CreateSubnet +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import create_subnet class CreateSubnetTestCase(unittest.TestCase): - @mock.patch('yardstick.common.openstack_utils.get_neutron_client') - @mock.patch('yardstick.common.openstack_utils.create_neutron_subnet') - def test_create_subnet(self, mock_get_neutron_client, mock_create_neutron_subnet): - options = { - 'openstack_paras': { - 'network_id': '123-123-123', - 'name': 'yardstick_subnet', - 'cidr': '10.10.10.0/24', - 'ip_version': '4' - } - } - args = {"options": options} - obj = CreateSubnet(args, {}) - obj.run({}) - self.assertTrue(mock_get_neutron_client.called) - self.assertTrue(mock_create_neutron_subnet.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + def setUp(self): + + self._mock_create_neutron_subnet = mock.patch.object( + openstack_utils, 'create_neutron_subnet') + self.mock_create_neutron_subnet = ( + self._mock_create_neutron_subnet.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(create_subnet, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'network_name_or_id': 'yardstick_net'}} + self.result = {"subnet_create": 0} + + self._csubnet_obj = create_subnet.CreateSubnet(self.args, mock.ANY) + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_create_neutron_subnet.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + _uuid = uuidutils.generate_uuid() + self._csubnet_obj.scenario_cfg = {'output': 'id'} + self.mock_create_neutron_subnet.return_value = _uuid + output = self._csubnet_obj.run(self.result) + self.assertDictEqual({"subnet_create": 1}, self.result) + self.assertDictEqual({'id': _uuid}, output) + self.mock_log.info.asset_called_once_with('Create subnet successful!') + + def test_run_fail(self): + self._csubnet_obj.scenario_cfg = {'output': 'id'} + self.mock_create_neutron_subnet.return_value = None + with self.assertRaises(exceptions.ScenarioCreateSubnetError): + self._csubnet_obj.run(self.result) + self.assertDictEqual({"subnet_create": 0}, self.result) + self.mock_log.error.assert_called_once_with('Create subnet failed!') diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_volume.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_volume.py index ca055db2f..30333dda8 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_create_volume.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_create_volume.py @@ -94,15 +94,7 @@ class CreateVolumeTestCase(unittest.TestCase): args = {"options": options} scenario = create_volume.CreateVolume(args, {}) scenario.run() - self.assertTrue(mock_create_volume.called) - self.assertTrue(mock_image_id.called) - self.assertTrue(mock_get_glance_client.called) - self.assertTrue(mock_get_cinder_client.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_create_volume.assert_called_once() + mock_image_id.assert_called_once() + mock_get_glance_client.assert_called_once() + mock_get_cinder_client.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_flavor.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_flavor.py index 4a91b8939..24dbf8a16 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_flavor.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_flavor.py @@ -23,13 +23,5 @@ class DeleteFlavorTestCase(unittest.TestCase): args = {"options": options} obj = DeleteFlavor(args, {}) obj.run({}) - self.assertTrue(mock_get_nova_client.called) - self.assertTrue(mock_delete_flavor.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_nova_client.assert_called_once() + mock_delete_flavor.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_floating_ip.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_floating_ip.py index df2321292..3185ec59f 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_floating_ip.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_floating_ip.py @@ -23,13 +23,5 @@ class DeleteFloatingIpTestCase(unittest.TestCase): args = {"options": options} obj = DeleteFloatingIp(args, {}) obj.run({}) - self.assertTrue(mock_get_nova_client.called) - self.assertTrue(mock_delete_floating_ip.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_nova_client.assert_called_once() + mock_delete_floating_ip.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_image.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_image.py index 9edc2ff1d..e382d46fa 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_image.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_image.py @@ -24,14 +24,6 @@ class DeleteImageTestCase(unittest.TestCase): args = {"options": options} obj = DeleteImage(args, {}) obj.run({}) - self.assertTrue(mock_delete_image.called) - self.assertTrue(mock_image_id.called) - self.assertTrue(mock_get_glance_client.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_delete_image.assert_called_once() + mock_image_id.assert_called_once() + mock_get_glance_client.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_keypair.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_keypair.py index 73894a903..6e790ba90 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_keypair.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_keypair.py @@ -23,13 +23,5 @@ class DeleteKeypairTestCase(unittest.TestCase): args = {"options": options} obj = DeleteKeypair(args, {}) obj.run({}) - self.assertTrue(mock_get_nova_client.called) - self.assertTrue(mock_delete_keypair.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_nova_client.assert_called_once() + mock_delete_keypair.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_port.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_port.py index de3179b2d..9fd318580 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_port.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_port.py @@ -22,12 +22,4 @@ class DeletePortTestCase(unittest.TestCase): args = {"options": options} obj = DeletePort(args, {}) obj.run({}) - self.assertTrue(mock_get_neutron_client.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_neutron_client.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router.py index 73cb81278..b76100f19 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router.py @@ -6,30 +6,49 @@ # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## + +from oslo_utils import uuidutils import unittest import mock -from yardstick.benchmark.scenarios.lib.delete_router import DeleteRouter +from yardstick.common import openstack_utils +from yardstick.common import exceptions +from yardstick.benchmark.scenarios.lib import delete_router class DeleteRouterTestCase(unittest.TestCase): - @mock.patch('yardstick.common.openstack_utils.get_neutron_client') - @mock.patch('yardstick.common.openstack_utils.delete_neutron_router') - def test_delete_router(self, mock_get_neutron_client, mock_delete_neutron_router): - options = { - 'router_id': '123-123-123' - } - args = {"options": options} - obj = DeleteRouter(args, {}) - obj.run({}) - self.assertTrue(mock_get_neutron_client.called) - self.assertTrue(mock_delete_neutron_router.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + def setUp(self): + self._mock_delete_neutron_router = mock.patch.object( + openstack_utils, 'delete_neutron_router') + self.mock_delete_neutron_router = ( + self._mock_delete_neutron_router.start()) + self._mock_get_shade_client = mock.patch.object( + openstack_utils, 'get_shade_client') + self.mock_get_shade_client = self._mock_get_shade_client.start() + self._mock_log = mock.patch.object(delete_router, 'LOG') + self.mock_log = self._mock_log.start() + self.args = {'options': {'router_id': uuidutils.generate_uuid()}} + self.result = {"delete_router": 0} + + self._del_obj = delete_router.DeleteRouter(self.args, mock.ANY) + + self.addCleanup(self._stop_mock) + + def _stop_mock(self): + self._mock_delete_neutron_router.stop() + self._mock_get_shade_client.stop() + self._mock_log.stop() + + def test_run(self): + self.mock_delete_neutron_router.return_value = True + self.assertIsNone(self._del_obj.run(self.result)) + self.assertEqual({"delete_router": 1}, self.result) + self.mock_log.info.assert_called_once_with("Delete router successful!") + + def test_run_fail(self): + self.mock_delete_neutron_router.return_value = False + with self.assertRaises(exceptions.ScenarioDeleteRouterError): + self._del_obj.run(self.result) + self.assertEqual({"delete_router": 0}, self.result) + self.mock_log.error.assert_called_once_with("Delete router failed!") diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_gateway.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_gateway.py index 3cfc4ed21..0c9cf7c17 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_gateway.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_gateway.py @@ -23,13 +23,5 @@ class DeleteRouterGatewayTestCase(unittest.TestCase): args = {"options": options} obj = DeleteRouterGateway(args, {}) obj.run({}) - self.assertTrue(mock_get_neutron_client.called) - self.assertTrue(mock_remove_gateway_router.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_neutron_client.assert_called_once() + mock_remove_gateway_router.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_interface.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_interface.py index 67aff1091..9e9c5a5b6 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_interface.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_router_interface.py @@ -24,13 +24,5 @@ class DeleteRouterInterfaceTestCase(unittest.TestCase): args = {"options": options} obj = DeleteRouterInterface(args, {}) obj.run({}) - self.assertTrue(mock_get_neutron_client.called) - self.assertTrue(mock_remove_interface_router.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_neutron_client.assert_called_once() + mock_remove_interface_router.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_server.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_server.py index 622ead5ac..eee565de7 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_server.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_server.py @@ -23,13 +23,5 @@ class DeleteServerTestCase(unittest.TestCase): args = {"options": options} obj = DeleteServer(args, {}) obj.run({}) - self.assertTrue(mock_get_nova_client.called) - self.assertTrue(mock_delete_instance.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_nova_client.assert_called_once() + mock_delete_instance.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_volume.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_volume.py index 9438b077a..93f76e819 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_volume.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_delete_volume.py @@ -23,13 +23,5 @@ class DeleteVolumeTestCase(unittest.TestCase): args = {"options": options} obj = DeleteVolume(args, {}) obj.run({}) - self.assertTrue(mock_get_cinder_client.called) - self.assertTrue(mock_delete_volume.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_cinder_client.assert_called_once() + mock_delete_volume.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_detach_volume.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_detach_volume.py index 87af63a55..9794d2129 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_detach_volume.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_detach_volume.py @@ -23,12 +23,4 @@ class DetachVolumeTestCase(unittest.TestCase): args = {"options": options} obj = DetachVolume(args, {}) obj.run({}) - self.assertTrue(mock_detach_volume.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_detach_volume.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_flavor.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_flavor.py index bf12e0a32..15a6f7c8f 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_flavor.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_flavor.py @@ -22,12 +22,4 @@ class GetFlavorTestCase(unittest.TestCase): args = {"options": options} obj = GetFlavor(args, {}) obj.run({}) - self.assertTrue(mock_get_flavor_by_name.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_flavor_by_name.assert_called_once() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_migrate_target_host.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_migrate_target_host.py index f046c92ea..879b2b988 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_migrate_target_host.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_migrate_target_host.py @@ -25,9 +25,9 @@ class GetMigrateTargetHostTestCase(unittest.TestCase): mock_get_nova_client): obj = GetMigrateTargetHost({}, {}) obj.run({}) - self.assertTrue(mock_get_nova_client.called) - self.assertTrue(mock_get_current_host_name.called) - self.assertTrue(mock_get_migrate_host.called) + mock_get_nova_client.assert_called_once() + mock_get_current_host_name.assert_called_once() + mock_get_migrate_host.assert_called_once() @mock.patch('{}.openstack_utils.get_nova_client'.format(BASE)) def test_get_migrate_host(self, mock_get_nova_client): @@ -39,13 +39,5 @@ class GetMigrateTargetHostTestCase(unittest.TestCase): mock_get_nova_client().hosts.list_all.return_value = [A('compute')] obj = GetMigrateTargetHost({}, {}) host = obj._get_migrate_host('host5') - self.assertTrue(mock_get_nova_client.called) + mock_get_nova_client.assert_called() self.assertEqual(host, 'host4') - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_numa_info.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_numa_info.py index 50d5238d7..bea978b8a 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_numa_info.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_numa_info.py @@ -44,8 +44,8 @@ class GetNumaInfoTestCase(unittest.TestCase): } obj = GetNumaInfo(scenario_cfg, {}) obj.run({}) - self.assertTrue(mock_get_current_host_name.called) - self.assertTrue(mock_check_numa_node.called) + mock_get_current_host_name.assert_called_once() + mock_check_numa_node.assert_called_once() @mock.patch('yardstick.ssh.SSH.from_node') @mock.patch('{}.GetNumaInfo._get_current_host_name'.format(BASE)) @@ -101,11 +101,3 @@ class GetNumaInfoTestCase(unittest.TestCase): obj = GetNumaInfo(scenario_cfg, {}) result = obj._get_current_host_name('1') self.assertEqual(result, 'host5') - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server.py index aebbf5416..83ec903bc 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server.py @@ -25,8 +25,8 @@ class GetServerTestCase(unittest.TestCase): } obj = GetServer(scenario_cfg, {}) obj.run({}) - self.assertTrue(mock_get_nova_client.called) - self.assertTrue(mock_get_server_by_name.called) + mock_get_nova_client.assert_called_once() + mock_get_server_by_name.assert_called_once() @mock.patch('yardstick.common.openstack_utils.get_nova_client') def test_get_server_with_id(self, mock_get_nova_client): @@ -39,12 +39,4 @@ class GetServerTestCase(unittest.TestCase): mock_get_nova_client().servers.get.return_value = None obj = GetServer(scenario_cfg, {}) obj.run({}) - self.assertTrue(mock_get_nova_client.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_get_nova_client.assert_called() diff --git a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server_ip.py b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server_ip.py index 3d20d5439..04fca16aa 100644 --- a/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server_ip.py +++ b/yardstick/tests/unit/benchmark/scenarios/lib/test_get_server_ip.py @@ -31,11 +31,3 @@ class GetServerIpTestCase(unittest.TestCase): obj = GetServerIp(scenario_cfg, {}) result = obj.run({}) self.assertEqual(result, {'ip': '127.0.0.1'}) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_iperf3.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_iperf3.py index 4d3745230..74144afd5 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_iperf3.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_iperf3.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Ericsson AB and others. # @@ -67,7 +65,7 @@ class IperfTestCase(unittest.TestCase): p.target = mock_ssh.SSH.from_node() p.teardown() - self.assertTrue(mock_ssh.SSH.from_node().close.called) + mock_ssh.SSH.from_node().close.assert_called() mock_ssh.SSH.from_node().execute.assert_called_with("pkill iperf3") def test_iperf_successful_no_sla(self, mock_ssh): @@ -193,10 +191,3 @@ class IperfTestCase(unittest.TestCase): with open(output) as f: sample_output = f.read() return sample_output - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_moongen_testpmd.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_moongen_testpmd.py new file mode 100644 index 000000000..620155c7e --- /dev/null +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_moongen_testpmd.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python + +# Copyright 2017 Nokia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Unittest for yardstick.benchmark.scenarios.networking.MoongenTestPMD + +from __future__ import absolute_import +try: + from unittest import mock +except ImportError: + import mock +import unittest + +from yardstick.benchmark.scenarios.networking import moongen_testpmd + + +@mock.patch('yardstick.benchmark.scenarios.networking.moongen_testpmd.subprocess') +class MoongenTestPMDTestCase(unittest.TestCase): + + def setUp(self): + self.ctx = { + "host": { + "ip": "10.229.47.137", + "user": "ubuntu", + "password": "ubuntu", + }, + } + self.TestPMDargs = { + 'task_id': "1234-5678", + 'options': { + 'multistream': 1, + 'frame_size': 1024, + 'testpmd_queue': 2, + 'trafficgen_port1': 'ens5', + 'trafficgen_port2': 'ens6', + 'moongen_host_user': 'root', + 'moongen_host_passwd': 'root', + 'moongen_host_ip': '10.5.201.151', + 'moongen_dir': '/home/lua-trafficgen', + 'moongen_runBidirec': 'true', + 'Package_Loss': 0, + 'SearchRuntime': 60, + 'moongen_port1_mac': '88:cf:98:2f:4d:ed', + 'moongen_port2_mac': '88:cf:98:2f:4d:ee', + 'forward_type': 'testpmd', + }, + 'sla': { + 'metrics': 'throughput_rx_mpps', + 'throughput_rx_mpps': 0.5, + 'action': 'monitor', + } + } + self.L2fwdargs = { + 'task_id': "1234-5678", + 'options': { + 'multistream': 1, + 'frame_size': 1024, + 'testpmd_queue': 2, + 'trafficgen_port1': 'ens5', + 'trafficgen_port2': 'ens6', + 'moongen_host_user': 'root', + 'moongen_host_passwd': 'root', + 'moongen_host_ip': '10.5.201.151', + 'moongen_dir': '/home/lua-trafficgen', + 'moongen_runBidirec': 'true', + 'Package_Loss': 0, + 'SearchRuntime': 60, + 'moongen_port1_mac': '88:cf:98:2f:4d:ed', + 'moongen_port2_mac': '88:cf:98:2f:4d:ee', + 'forward_type': 'l2fwd', + }, + 'sla': { + 'metrics': 'throughput_rx_mpps', + 'throughput_rx_mpps': 0.5, + 'action': 'monitor', + } + } + + self._mock_ssh = mock.patch( + 'yardstick.benchmark.scenarios.networking.moongen_testpmd.ssh') + self.mock_ssh = self._mock_ssh.start() + + self.addCleanup(self._cleanup) + + def _cleanup(self): + self._mock_ssh.stop() + + def test_MoongenTestPMD_setup(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + def test_MoongenTestPMD_teardown(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + p.teardown() + self.assertFalse(p.setup_done) + + def test_MoongenTestPMD_l2fwd_is_forward_setup_no(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.L2fwdargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + result = p._is_forward_setup() + self.assertFalse(result) + + def test_MoongenTestPMD_l2fwd_is_forward_setup_yes(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.L2fwdargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + + result = p._is_forward_setup() + self.assertTrue(result) + + def test_MoongenTestPMD_testpmd_is_forward_setup_no(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + + result = p._is_forward_setup() + self.assertFalse(result) + + def test_MoongenTestPMD_testpmd_is_forward_setup_yes(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + result = p._is_forward_setup() + self.assertTrue(result) + + @mock.patch('time.sleep') + def test_MoongenTestPMD_testpmd_forward_setup_first(self, _, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + + p.forward_setup() + self.assertFalse(p._is_forward_setup()) + self.assertTrue(p.forward_setup_done) + + @mock.patch('time.sleep') + def test_MoongenTestPMD_testpmd_dpdk_setup_next(self, _, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + p.forward_setup() + self.assertTrue(p._is_forward_setup()) + self.assertTrue(p.forward_setup_done) + + @mock.patch('time.sleep') + def test_MoongenTestPMD_l2fwd_forward_setup_first(self, _, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.L2fwdargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # is_dpdk_setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + + p.forward_setup() + self.assertFalse(p._is_forward_setup()) + self.assertTrue(p.forward_setup_done) + + @mock.patch('time.sleep') + def test_MoongenTestPMD_l2fwd_dpdk_setup_next(self, _, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.L2fwdargs, self.ctx) + + # setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, 'dummy', '') + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + p.forward_setup() + self.assertTrue(p._is_forward_setup()) + self.assertTrue(p.forward_setup_done) + + def test_moongen_testpmd_generate_config_file(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + mock_subprocess.call().execute.return_value = None + + p.generate_config_file(frame_size=1, multistream=1, + runBidirec="True", tg_port1_vlan=1, + tg_port2_vlan=2, SearchRuntime=1, + Package_Loss=0) + self.assertTrue(p.CONFIG_FILE) + + def test_moongen_testpmd_result_to_data_match(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + mock_subprocess.call().execute.return_value = None + result = ("[REPORT]Device 1->0: Tx frames: 420161490 Rx Frames: 420161490" + " frame loss: 0, 0.000000% Rx Mpps: 7.002708\n[REPORT] " + "total: Tx frames: 840321216 Rx Frames: 840321216 frame loss: " + "0, 0.000000% Tx Mpps: 14.005388 Rx Mpps: 14.005388\n'") + p.result_to_data(result=result) + self.assertTrue(p.TO_DATA) + + def test_moongen_testpmd_result_to_data_not_match(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + mock_subprocess.call().execute.return_value = None + result = ("") + p.result_to_data(result=result) + self.assertTrue(p.TO_DATA) + + @mock.patch('time.sleep') + def test_moongen_testpmd_run_ok(self, _, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + p.setup_done = True + p.forward_setup_done = True + p.setup() + + # run() specific mocks + p.server = self.mock_ssh.SSH.from_node() + mock_subprocess.call().execute.return_value = None + mock_subprocess.call().execute.return_value = None + result = ("[REPORT]Device 1->0: Tx frames: 420161490 Rx Frames: 420161490" + " frame loss: 0, 0.000000% Rx Mpps: 7.002708\n[REPORT] " + "total: Tx frames: 840321216 Rx Frames: 840321216 frame loss: " + "0, 0.000000% Tx Mpps: 14.005388 Rx Mpps: 14.005388\n'") + self.mock_ssh.SSH.from_node().execute.return_value = ( + 0, result, '') + + test_result = {} + p.run(test_result) + + self.assertEqual(test_result['rx_mpps'], 14.005388) + + def test_moongen_testpmd_run_falied_vsperf_execution(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # run() specific mocks + mock_subprocess.call().execute.return_value = None + mock_subprocess.call().execute.return_value = None + self.mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + result = {} + self.assertRaises(RuntimeError, p.run, result) + + def test_moongen_testpmd_run_falied_csv_report(self, mock_subprocess): + p = moongen_testpmd.MoongenTestPMD(self.TestPMDargs, self.ctx) + + # setup() specific mocks + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + mock_subprocess.call().execute.return_value = None + + p.setup() + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + # run() specific mocks + mock_subprocess.call().execute.return_value = None + mock_subprocess.call().execute.return_value = None + self.mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + self.mock_ssh.SSH.from_node().execute.return_value = (1, '', '') + + result = {} + self.assertRaises(RuntimeError, p.run, result) + +def main(): + unittest.main() + + +if __name__ == '__main__': + main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf.py index d82a00931..5907562c2 100755 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. # @@ -121,10 +119,3 @@ class NetperfTestCase(unittest.TestCase): with open(output) as f: sample_output = f.read() return sample_output - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf_node.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf_node.py index 8be9bb94d..956a9c078 100755 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf_node.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_netperf_node.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. # @@ -121,10 +119,3 @@ class NetperfNodeTestCase(unittest.TestCase): with open(output) as f: sample_output = f.read() return sample_output - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_netutilization.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_netutilization.py index 1227e056e..4cdfde6b1 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_netutilization.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_netutilization.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huawei Technologies Co.,Ltd and other. # diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_networkcapacity.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_networkcapacity.py index 3e7a3c5ee..36e8c8a77 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_networkcapacity.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_networkcapacity.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. # diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_nstat.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_nstat.py index 7dd5351b1..b02d58437 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_nstat.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_nstat.py @@ -103,11 +103,3 @@ class NstatTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, n.run, result) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_ping.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_ping.py index 06353249a..4adfab120 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_ping.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_ping.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Ericsson AB and others. # @@ -92,10 +90,3 @@ class PingTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, p.run, result) - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_ping6.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_ping6.py index d2be6f576..4662c8537 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_ping6.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_ping6.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Ericsson AB and others. # @@ -116,10 +114,3 @@ class PingTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.side_effect = [ (0, 'host1', ''), (1, '', 'FOOBAR')] self.assertRaises(RuntimeError, p.run, result) - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen.py index acd9027d3..6aea03aee 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen.py @@ -665,11 +665,3 @@ class PktgenTestCase(unittest.TestCase): expected_result["packets_received"] = 149300 expected_result["packetsize"] = 60 self.assertEqual(result, expected_result) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk.py index 99399abdc..976087148 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk.py @@ -176,11 +176,3 @@ class PktgenDPDKLatencyTestCase(unittest.TestCase): self.mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, p.run, result) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk_throughput.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk_throughput.py index 1b12bd507..e90fb07c7 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk_throughput.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_pktgen_dpdk_throughput.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python ############################################################################## # Copyright (c) 2017 Nokia and others. # @@ -192,11 +191,3 @@ class PktgenDPDKTestCase(unittest.TestCase): mock_ssh.SSH().execute.assert_called_with( "sudo /dpdk/destdir/bin/dpdk-procinfo -- --stats-reset > /dev/null 2>&1") - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_sfc.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_sfc.py index 78c0352dd..a5e5e39dc 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_sfc.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_sfc.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Ericsson AB and others. # @@ -68,10 +66,3 @@ class SfcTestCase(unittest.TestCase): self.sfc.setup() self.sfc.run(result) self.sfc.teardown() - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() 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 fb55b5ea0..9bfbf0752 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_vnf_generic.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # Copyright (c) 2016-2017 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,27 +11,29 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -# - -# Unittest for yardstick.benchmark.scenarios.networking.test_vnf_generic - -from __future__ import absolute_import +from copy import deepcopy import os -import errno -import unittest -import mock +import sys -from copy import deepcopy +import mock +import unittest -from yardstick.tests.unit import STL_MOCKS -from yardstick.benchmark.scenarios.networking.vnf_generic import \ - SshManager, NetworkServiceTestCase, IncorrectConfig, \ - open_relative_file +from yardstick import tests +from yardstick.common import utils from yardstick.network_services.collector.subscriber import Collector -from yardstick.network_services.vnf_generic.vnf.base import \ - GenericTrafficGen, GenericVNF +from yardstick.network_services.traffic_profile import base +from yardstick.network_services.vnf_generic import vnfdgen +from yardstick.error import IncorrectConfig +from yardstick.network_services.vnf_generic.vnf.base import GenericTrafficGen +from yardstick.network_services.vnf_generic.vnf.base import GenericVNF + + +stl_patch = mock.patch.dict(sys.modules, tests.STL_MOCKS) +stl_patch.start() +if stl_patch: + from yardstick.benchmark.scenarios.networking import vnf_generic # pylint: disable=unused-argument # disable this for now because I keep forgetting mock patch arg ordering @@ -317,6 +317,7 @@ class TestNetworkServiceTestCase(unittest.TestCase): 'task_id': 'a70bdf4a-8e67-47a3-9dc1-273c14506eb7', 'tc': 'tc_ipv4_1Mflow_64B_packetsize', 'traffic_profile': 'ipv4_throughput_vpe.yaml', + 'extra_args': {'arg1': 'value1', 'arg2': 'value2'}, 'type': 'ISB', 'tc_options': { 'rfc2544': { @@ -345,25 +346,16 @@ class TestNetworkServiceTestCase(unittest.TestCase): }, } - self.s = NetworkServiceTestCase(self.scenario_cfg, self.context_cfg) + self.s = vnf_generic.NetworkServiceTestCase(self.scenario_cfg, + self.context_cfg) def _get_file_abspath(self, filename): curr_path = os.path.dirname(os.path.abspath(__file__)) file_path = os.path.join(curr_path, filename) return file_path - def test_ssh_manager(self): - with mock.patch("yardstick.ssh.SSH") as ssh: - ssh_mock = mock.Mock(autospec=ssh.SSH) - ssh_mock.execute = \ - mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, "")) - ssh.from_node.return_value = ssh_mock - for _, node_dict in self.context_cfg["nodes"].items(): - with SshManager(node_dict) as conn: - self.assertIsNotNone(conn) - def test___init__(self): - assert self.topology + self.assertIsNotNone(self.topology) def test__get_ip_flow_range_string(self): self.scenario_cfg["traffic_options"]["flow"] = \ @@ -415,7 +407,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()) def test___get_traffic_flow_error(self): @@ -425,16 +420,16 @@ class TestNetworkServiceTestCase(unittest.TestCase): def test_get_vnf_imp(self): vnfd = COMPLETE_TREX_VNFD['vnfd:vnfd-catalog']['vnfd'][0]['class-name'] - with mock.patch.dict("sys.modules", STL_MOCKS): + with mock.patch.dict(sys.modules, tests.STL_MOCKS): self.assertIsNotNone(self.s.get_vnf_impl(vnfd)) - with self.assertRaises(IncorrectConfig) as raised: - self.s.get_vnf_impl('NonExistentClass') + with self.assertRaises(vnf_generic.IncorrectConfig) as raised: + self.s.get_vnf_impl('NonExistentClass') - exc_str = str(raised.exception) - print(exc_str) - self.assertIn('No implementation', exc_str) - self.assertIn('found in', exc_str) + exc_str = str(raised.exception) + print(exc_str) + self.assertIn('No implementation', exc_str) + self.assertIn('found in', exc_str) def test_load_vnf_models_invalid(self): self.context_cfg["nodes"]['tg__1']['VNF model'] = \ @@ -456,39 +451,37 @@ class TestNetworkServiceTestCase(unittest.TestCase): self.s.load_vnf_models(self.scenario_cfg, self.context_cfg)) def test_map_topology_to_infrastructure(self): - with mock.patch("yardstick.ssh.SSH") as ssh: - ssh_mock = mock.Mock(autospec=ssh.SSH) - ssh_mock.execute = \ - mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, "")) - ssh.from_node.return_value = ssh_mock - self.s.map_topology_to_infrastructure() + self.s.map_topology_to_infrastructure() nodes = self.context_cfg["nodes"] - self.assertEqual( - "../../vnf_descriptors/tg_rfc2544_tpl.yaml", nodes['tg__1']['VNF model']) - self.assertEqual("../../vnf_descriptors/vpe_vnf.yaml", + self.assertEqual('../../vnf_descriptors/tg_rfc2544_tpl.yaml', + nodes['tg__1']['VNF model']) + self.assertEqual('../../vnf_descriptors/vpe_vnf.yaml', nodes['vnf__1']['VNF model']) def test_map_topology_to_infrastructure_insufficient_nodes(self): - del self.context_cfg['nodes']['vnf__1'] - with mock.patch("yardstick.ssh.SSH") as ssh: - ssh_mock = mock.Mock(autospec=ssh.SSH) - ssh_mock.execute = \ - mock.Mock(return_value=(1, SYS_CLASS_NET + IP_ADDR_SHOW, "")) - ssh.from_node.return_value = ssh_mock + cfg = deepcopy(self.context_cfg) + del cfg['nodes']['vnf__1'] + cfg_patch = mock.patch.object(self.s, 'context_cfg', cfg) + with cfg_patch: with self.assertRaises(IncorrectConfig): self.s.map_topology_to_infrastructure() def test_map_topology_to_infrastructure_config_invalid(self): - cfg = dict(self.context_cfg) + ssh_mock = mock.Mock() + ssh_mock.execute.return_value = 0, SYS_CLASS_NET + IP_ADDR_SHOW, "" + + cfg = deepcopy(self.s.context_cfg) + + # delete all, we don't know which will come first del cfg['nodes']['vnf__1']['interfaces']['xe0']['local_mac'] - with mock.patch("yardstick.ssh.SSH") as ssh: - ssh_mock = mock.Mock(autospec=ssh.SSH) - ssh_mock.execute = \ - mock.Mock(return_value=(0, SYS_CLASS_NET + IP_ADDR_SHOW, "")) - ssh.from_node.return_value = ssh_mock + del cfg['nodes']['vnf__1']['interfaces']['xe1']['local_mac'] + del cfg['nodes']['tg__1']['interfaces']['xe0']['local_mac'] + del cfg['nodes']['tg__1']['interfaces']['xe1']['local_mac'] + config_patch = mock.patch.object(self.s, 'context_cfg', cfg) + with config_patch: with self.assertRaises(IncorrectConfig): self.s.map_topology_to_infrastructure() @@ -503,10 +496,8 @@ class TestNetworkServiceTestCase(unittest.TestCase): for interface in self.tg__1['interfaces'].values(): del interface['local_mac'] - with mock.patch( - "yardstick.benchmark.scenarios.networking.vnf_generic.LOG"): - with self.assertRaises(IncorrectConfig) as raised: - self.s._resolve_topology() + with self.assertRaises(vnf_generic.IncorrectConfig) as raised: + self.s._resolve_topology() self.assertIn('not found', str(raised.exception)) @@ -518,10 +509,8 @@ class TestNetworkServiceTestCase(unittest.TestCase): self.s.topology["vld"][0]['vnfd-connection-point-ref'].append( self.s.topology["vld"][0]['vnfd-connection-point-ref'][0]) - with mock.patch( - "yardstick.benchmark.scenarios.networking.vnf_generic.LOG"): - with self.assertRaises(IncorrectConfig) as raised: - self.s._resolve_topology() + with self.assertRaises(vnf_generic.IncorrectConfig) as raised: + self.s._resolve_topology() self.assertIn('wrong endpoint count', str(raised.exception)) @@ -529,10 +518,8 @@ class TestNetworkServiceTestCase(unittest.TestCase): self.s.topology["vld"][0]['vnfd-connection-point-ref'] = \ self.s.topology["vld"][0]['vnfd-connection-point-ref'][:1] - with mock.patch( - "yardstick.benchmark.scenarios.networking.vnf_generic.LOG"): - with self.assertRaises(IncorrectConfig) as raised: - self.s._resolve_topology() + with self.assertRaises(vnf_generic.IncorrectConfig) as raised: + self.s._resolve_topology() self.assertIn('wrong endpoint count', str(raised.exception)) @@ -578,7 +565,7 @@ class TestNetworkServiceTestCase(unittest.TestCase): self.s.load_vnf_models = mock.Mock(return_value=self.s.vnfs) self.s._fill_traffic_profile = \ mock.Mock(return_value=TRAFFIC_PROFILE) - self.assertEqual(None, self.s.setup()) + self.assertIsNone(self.s.setup()) def test_setup_exception(self): with mock.patch("yardstick.ssh.SSH") as ssh: @@ -625,15 +612,48 @@ class TestNetworkServiceTestCase(unittest.TestCase): self.assertEqual({'imix': {'64B': 100}}, self.s._get_traffic_imix()) - def test__fill_traffic_profile(self): - with mock.patch.dict("sys.modules", STL_MOCKS): - self.scenario_cfg["traffic_profile"] = \ - self._get_file_abspath("ipv4_throughput_vpe.yaml") - self.scenario_cfg["traffic_options"]["flow"] = \ - self._get_file_abspath("ipv4_1flow_Packets_vpe.yaml") - self.scenario_cfg["traffic_options"]["imix"] = \ - self._get_file_abspath("imix_voice.yaml") - self.assertIsNotNone(self.s._fill_traffic_profile()) + @mock.patch.object(base.TrafficProfile, 'get') + @mock.patch.object(vnfdgen, 'generate_vnfd') + def test__fill_traffic_profile(self, mock_generate, mock_tprofile_get): + fake_tprofile = mock.Mock() + fake_vnfd = mock.Mock() + with mock.patch.object(self.s, '_get_traffic_profile', + return_value=fake_tprofile) as mock_get_tp: + mock_generate.return_value = fake_vnfd + self.s._fill_traffic_profile() + mock_get_tp.assert_called_once() + mock_generate.assert_called_once_with( + fake_tprofile, + {'downlink': {}, + 'extra_args': {'arg1': 'value1', 'arg2': 'value2'}, + 'flow': {'flow': {}}, + 'imix': {'imix': {'64B': 100}}, + 'uplink': {}} + ) + mock_tprofile_get.assert_called_once_with(fake_vnfd) + + @mock.patch.object(utils, 'open_relative_file') + def test__get_topology(self, mock_open_path): + self.s.scenario_cfg['topology'] = 'fake_topology' + self.s.scenario_cfg['task_path'] = 'fake_path' + mock_open_path.side_effect = mock.mock_open(read_data='fake_data') + self.assertEqual('fake_data', self.s._get_topology()) + mock_open_path.assert_called_once_with('fake_topology', 'fake_path') + + @mock.patch.object(vnfdgen, 'generate_vnfd') + def test__render_topology(self, mock_generate): + fake_topology = 'fake_topology' + mock_generate.return_value = {'nsd:nsd-catalog': {'nsd': ['fake_nsd']}} + with mock.patch.object(self.s, '_get_topology', + return_value=fake_topology) as mock_get_topology: + self.s._render_topology() + mock_get_topology.assert_called_once() + + mock_generate.assert_called_once_with( + fake_topology, + {'extra_args': {'arg1': 'value1', 'arg2': 'value2'}} + ) + self.assertEqual(self.s.topology, 'fake_nsd') def test_teardown(self): vnf = mock.Mock(autospec=GenericVNF) @@ -658,141 +678,3 @@ class TestNetworkServiceTestCase(unittest.TestCase): mock.Mock(return_value=True) with self.assertRaises(RuntimeError): self.s.teardown() - - SAMPLE_NETDEVS = { - 'enp11s0': { - 'address': '0a:de:ad:be:ef:f5', - 'device': '0x1533', - 'driver': 'igb', - 'ifindex': '2', - 'interface_name': 'enp11s0', - 'operstate': 'down', - 'pci_bus_id': '0000:0b:00.0', - 'subsystem_device': '0x1533', - 'subsystem_vendor': '0x15d9', - 'vendor': '0x8086' - }, - 'lan': { - 'address': '0a:de:ad:be:ef:f4', - 'device': '0x153a', - 'driver': 'e1000e', - 'ifindex': '3', - 'interface_name': 'lan', - 'operstate': 'up', - 'pci_bus_id': '0000:00:19.0', - 'subsystem_device': '0x153a', - 'subsystem_vendor': '0x15d9', - 'vendor': '0x8086' - } - } - - SAMPLE_VM_NETDEVS = { - 'eth1': { - 'address': 'fa:de:ad:be:ef:5b', - 'device': '0x0001', - 'driver': 'virtio_net', - 'ifindex': '3', - 'interface_name': 'eth1', - 'operstate': 'down', - 'pci_bus_id': '0000:00:04.0', - 'vendor': '0x1af4' - } - } - - def test_parse_netdev_info(self): - output = """\ -/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/ifindex:2 -/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/address:0a:de:ad:be:ef:f5 -/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/operstate:down -/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/vendor:0x8086 -/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/device:0x1533 -/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/subsystem_vendor:0x15d9 -/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/device/subsystem_device:0x1533 -/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/driver:igb -/sys/devices/pci0000:00/0000:00:1c.3/0000:0b:00.0/net/enp11s0/pci_bus_id:0000:0b:00.0 -/sys/devices/pci0000:00/0000:00:19.0/net/lan/ifindex:3 -/sys/devices/pci0000:00/0000:00:19.0/net/lan/address:0a:de:ad:be:ef:f4 -/sys/devices/pci0000:00/0000:00:19.0/net/lan/operstate:up -/sys/devices/pci0000:00/0000:00:19.0/net/lan/device/vendor:0x8086 -/sys/devices/pci0000:00/0000:00:19.0/net/lan/device/device:0x153a -/sys/devices/pci0000:00/0000:00:19.0/net/lan/device/subsystem_vendor:0x15d9 -/sys/devices/pci0000:00/0000:00:19.0/net/lan/device/subsystem_device:0x153a -/sys/devices/pci0000:00/0000:00:19.0/net/lan/driver:e1000e -/sys/devices/pci0000:00/0000:00:19.0/net/lan/pci_bus_id:0000:00:19.0 -""" - res = NetworkServiceTestCase.parse_netdev_info(output) - assert res == self.SAMPLE_NETDEVS - - def test_parse_netdev_info_virtio(self): - output = """\ -/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/ifindex:3 -/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/address:fa:de:ad:be:ef:5b -/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/operstate:down -/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/device/vendor:0x1af4 -/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/device/device:0x0001 -/sys/devices/pci0000:00/0000:00:04.0/virtio1/net/eth1/driver:virtio_net -""" - res = NetworkServiceTestCase.parse_netdev_info(output) - assert res == self.SAMPLE_VM_NETDEVS - - def test_probe_missing_values(self): - netdevs = self.SAMPLE_NETDEVS.copy() - network = {'local_mac': '0a:de:ad:be:ef:f5'} - NetworkServiceTestCase._probe_missing_values(netdevs, network) - assert network['vpci'] == '0000:0b:00.0' - - network = {'local_mac': '0a:de:ad:be:ef:f4'} - NetworkServiceTestCase._probe_missing_values(netdevs, network) - assert network['vpci'] == '0000:00:19.0' - - # TODO: Split this into several tests, for different IOError sub-types - def test_open_relative_path(self): - mock_open = mock.mock_open() - mock_open_result = mock_open() - mock_open_call_count = 1 # initial call to get result - - module_name = \ - 'yardstick.benchmark.scenarios.networking.vnf_generic.open' - - # test - with mock.patch(module_name, mock_open, create=True): - self.assertEqual(open_relative_file( - 'foo', 'bar'), mock_open_result) - - mock_open_call_count += 1 # one more call expected - self.assertEqual(mock_open.call_count, mock_open_call_count) - self.assertIn('foo', mock_open.call_args_list[-1][0][0]) - self.assertNotIn('bar', mock_open.call_args_list[-1][0][0]) - - def open_effect(*args, **kwargs): - if kwargs.get('name', args[0]) == os.path.join('bar', 'foo'): - return mock_open_result - raise IOError(errno.ENOENT, 'not found') - - mock_open.side_effect = open_effect - self.assertEqual(open_relative_file( - 'foo', 'bar'), mock_open_result) - - mock_open_call_count += 2 # two more calls expected - self.assertEqual(mock_open.call_count, mock_open_call_count) - self.assertIn('foo', mock_open.call_args_list[-1][0][0]) - self.assertIn('bar', mock_open.call_args_list[-1][0][0]) - - # test an IOError of type ENOENT - mock_open.side_effect = IOError(errno.ENOENT, 'not found') - with self.assertRaises(IOError): - # the second call still raises - open_relative_file('foo', 'bar') - - mock_open_call_count += 2 # two more calls expected - self.assertEqual(mock_open.call_count, mock_open_call_count) - self.assertIn('foo', mock_open.call_args_list[-1][0][0]) - self.assertIn('bar', mock_open.call_args_list[-1][0][0]) - - # test an IOError other than ENOENT - mock_open.side_effect = IOError(errno.EBUSY, 'busy') - with self.assertRaises(IOError): - open_relative_file('foo', 'bar') - - mock_open_call_count += 1 # one more call expected - self.assertEqual(mock_open.call_count, mock_open_call_count) diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf.py index be8ac55d0..419605b26 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # Copyright 2016 Intel Corporation. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -126,11 +124,3 @@ class VsperfTestCase(unittest.TestCase): result = {} self.assertRaises(RuntimeError, p.run, result) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py b/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py index 1923960e9..1d2278e21 100644 --- a/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py +++ b/yardstick/tests/unit/benchmark/scenarios/networking/test_vsperf_dpdk.py @@ -211,11 +211,3 @@ class VsperfDPDKTestCase(unittest.TestCase): result = {} self.assertRaises(RuntimeError, self.scenario.run, result) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/parser/test_parser.py b/yardstick/tests/unit/benchmark/scenarios/parser/test_parser.py index ee2bbc07d..9fd5cce38 100644 --- a/yardstick/tests/unit/benchmark/scenarios/parser/test_parser.py +++ b/yardstick/tests/unit/benchmark/scenarios/parser/test_parser.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and other. # @@ -70,10 +68,3 @@ class ParserTestCase(unittest.TestCase): self.mock_call.return_value = 0 self.scenario.teardown() self.assertTrue(self.scenario.teardown_done) - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/test_bonnie.py b/yardstick/tests/unit/benchmark/scenarios/storage/test_bonnie.py index b98dceae7..d78506584 100644 --- a/yardstick/tests/unit/benchmark/scenarios/storage/test_bonnie.py +++ b/yardstick/tests/unit/benchmark/scenarios/storage/test_bonnie.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2017 Huawei Technologies Co.,Ltd and others. # @@ -9,8 +7,6 @@ # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## -# Unittest for yardstick.benchmark.scenarios.storage.bonnie.Bonnie - from __future__ import absolute_import import unittest @@ -65,11 +61,3 @@ class BonnieTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, b.run, self.result) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/test_fio.py b/yardstick/tests/unit/benchmark/scenarios/storage/test_fio.py index 0cffea224..f149cee69 100644 --- a/yardstick/tests/unit/benchmark/scenarios/storage/test_fio.py +++ b/yardstick/tests/unit/benchmark/scenarios/storage/test_fio.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Ericsson AB and others. # @@ -63,6 +61,22 @@ class FioTestCase(unittest.TestCase): } args = {'options': options} p = fio.Fio(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '/dev/vdb', '') + p.setup() + + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') + self.assertIsNotNone(p.client) + self.assertTrue(p.setup_done) + + def test_fio_job_file_no_disk__setup(self, mock_ssh): + + options = { + 'job_file': 'job_file.ini', + 'directory': '/FIO_Test' + } + args = {'options': options} + p = fio.Fio(args, self.ctx) + mock_ssh.SSH.from_node().execute.return_value = (0, '', '') p.setup() mock_ssh.SSH.from_node().execute.return_value = (0, '', '') @@ -263,11 +277,3 @@ class FioTestCase(unittest.TestCase): with open(output) as f: sample_output = f.read() return sample_output - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/test_storagecapacity.py b/yardstick/tests/unit/benchmark/scenarios/storage/test_storagecapacity.py index 095674f72..c1c731b0a 100644 --- a/yardstick/tests/unit/benchmark/scenarios/storage/test_storagecapacity.py +++ b/yardstick/tests/unit/benchmark/scenarios/storage/test_storagecapacity.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. # @@ -99,10 +97,3 @@ class StorageCapacityTestCase(unittest.TestCase): mock_ssh.SSH.from_node().execute.return_value = (1, '', 'FOOBAR') self.assertRaises(RuntimeError, c.run, self.result) - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/storage/test_storperf.py b/yardstick/tests/unit/benchmark/scenarios/storage/test_storperf.py index 52786d7cb..5844746ab 100644 --- a/yardstick/tests/unit/benchmark/scenarios/storage/test_storperf.py +++ b/yardstick/tests/unit/benchmark/scenarios/storage/test_storperf.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2016 Huawei Technologies Co.,Ltd. # @@ -233,10 +231,3 @@ class StorPerfTestCase(unittest.TestCase): s = storperf.StorPerf(args, self.ctx) self.assertRaises(AssertionError, s.teardown(), self.result) - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/benchmark/scenarios/test_base.py b/yardstick/tests/unit/benchmark/scenarios/test_base.py index a95e6bc86..985338532 100644 --- a/yardstick/tests/unit/benchmark/scenarios/test_base.py +++ b/yardstick/tests/unit/benchmark/scenarios/test_base.py @@ -13,12 +13,11 @@ # License for the specific language governing permissions and limitations # under the License. -import unittest - from yardstick.benchmark.scenarios import base +from yardstick.tests.unit import base as ut_base -class ScenarioTestCase(unittest.TestCase): +class ScenarioTestCase(ut_base.BaseUnitTestCase): def test_get_scenario_type(self): scenario_type = 'dummy scenario' @@ -87,7 +86,7 @@ class ScenarioTestCase(unittest.TestCase): str(exc.exception)) -class IterScenarioClassesTestCase(unittest.TestCase): +class IterScenarioClassesTestCase(ut_base.BaseUnitTestCase): def test_no_scenario_type_defined(self): some_existing_scenario_class_names = [ diff --git a/yardstick/tests/unit/common/banned_modules/__init__.py b/yardstick/tests/unit/common/banned_modules/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/common/banned_modules/__init__.py diff --git a/yardstick/tests/unit/common/banned_modules/banned_module.py b/yardstick/tests/unit/common/banned_modules/banned_module.py new file mode 100644 index 000000000..f57f939d2 --- /dev/null +++ b/yardstick/tests/unit/common/banned_modules/banned_module.py @@ -0,0 +1,17 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +class BannedClass(object): + pass diff --git a/yardstick/tests/unit/common/banned_modules/importing_module.py b/yardstick/tests/unit/common/banned_modules/importing_module.py new file mode 100644 index 000000000..fc2542f0c --- /dev/null +++ b/yardstick/tests/unit/common/banned_modules/importing_module.py @@ -0,0 +1,21 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from yardstick.common import import_tools +from yardstick.tests.unit.common.banned_modules import banned_module + + +@import_tools.decorator_banned_modules +class ImportingClass(banned_module.BannedClass): + pass diff --git a/yardstick/tests/unit/common/test_ansible_common.py b/yardstick/tests/unit/common/test_ansible_common.py index 89ea128af..48d8a60c8 100644 --- a/yardstick/tests/unit/common/test_ansible_common.py +++ b/yardstick/tests/unit/common/test_ansible_common.py @@ -17,6 +17,7 @@ from __future__ import absolute_import import os import tempfile +import shutil from collections import defaultdict import mock @@ -99,7 +100,7 @@ class AnsibleNodeTestCase(unittest.TestCase): def test_ansible_node_getattr(self): a = ansible_common.AnsibleNode({"name": "name"}) - self.assertEqual(getattr(a, "nosuch", None), None) + self.assertIsNone(getattr(a, "nosuch", None)) class AnsibleNodeDictTestCase(unittest.TestCase): @@ -145,7 +146,7 @@ class AnsibleNodeDictTestCase(unittest.TestCase): class AnsibleCommonTestCase(unittest.TestCase): def test_get_timeouts(self): - self.assertAlmostEquals(ansible_common.AnsibleCommon.get_timeout(-100), 1200.0) + self.assertAlmostEqual(ansible_common.AnsibleCommon.get_timeout(-100), 1200.0) def test__init__(self): ansible_common.AnsibleCommon({}) @@ -246,3 +247,18 @@ class AnsibleCommonTestCase(unittest.TestCase): a.execute_ansible('', d, ansible_check=True, verbose=True) finally: os.rmdir(d) + + def test_get_sut_info(self): + d = tempfile.mkdtemp() + a = ansible_common.AnsibleCommon({}) + try: + a.get_sut_info(d) + finally: + shutil.rmtree(d) + + def test_get_sut_info_not_exist(self): + a = ansible_common.AnsibleCommon({}) + try: + a.get_sut_info('/hello/world') + except OSError: + pass diff --git a/yardstick/tests/unit/common/test_httpClient.py b/yardstick/tests/unit/common/test_httpClient.py index eb09d1a52..12a8be3a0 100644 --- a/yardstick/tests/unit/common/test_httpClient.py +++ b/yardstick/tests/unit/common/test_httpClient.py @@ -33,11 +33,3 @@ class HttpClientTestCase(unittest.TestCase): url = 'http://localhost:5000/hello' httpClient.HttpClient().get(url) mock_requests.get.assert_called_with(url) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/common/test_import_tools.py b/yardstick/tests/unit/common/test_import_tools.py new file mode 100644 index 000000000..c8fe5fde0 --- /dev/null +++ b/yardstick/tests/unit/common/test_import_tools.py @@ -0,0 +1,46 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from yardstick.common import exceptions as y_exc +from yardstick.common import import_tools + + +@import_tools.decorator_banned_modules +class DummyClass(object): + pass + + +class DecoratorBannedModule(unittest.TestCase): + + MODULE = 'yardstick.tests.unit.common.banned_modules.banned_module' + + def test_passt(self): + self.assertIsNotNone(DummyClass()) + + def test_banned_module(self): + import_tools.BANNED_MODULES[self.MODULE] = 'Banned module!!' + from yardstick.tests.unit.common.banned_modules import importing_module + self.addCleanup(self._remove_module) + + with self.assertRaises(y_exc.YardstickBannedModuleImported) as exc: + importing_module.ImportingClass() + + msg = ('Module "%s" cannnot be imported. Reason: "Banned module!!"' + % self.MODULE) + self.assertEqual(msg, str(exc.exception)) + + def _remove_module(self): + del import_tools.BANNED_MODULES[self.MODULE] diff --git a/yardstick/tests/unit/common/test_openstack_utils.py b/yardstick/tests/unit/common/test_openstack_utils.py index 8a2f5f95b..c6b0f46b2 100644 --- a/yardstick/tests/unit/common/test_openstack_utils.py +++ b/yardstick/tests/unit/common/test_openstack_utils.py @@ -54,7 +54,7 @@ class GetNetworkIdTestCase(unittest.TestCase): output = openstack_utils.get_network_id(mock_shade_client, 'network_name') - self.assertEqual(None, output) + self.assertIsNone(output) class DeleteNeutronNetTestCase(unittest.TestCase): @@ -83,3 +83,81 @@ class DeleteNeutronNetTestCase(unittest.TestCase): 'network_id') self.assertFalse(output) mock_logger.error.assert_called_once() + + +class CreateNeutronNetTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.network_name = 'name' + self.mock_shade_client.create_network = mock.Mock() + + def test_create_neutron_net(self): + _uuid = uuidutils.generate_uuid() + self.mock_shade_client.create_network.return_value = {'id': _uuid} + output = openstack_utils.create_neutron_net(self.mock_shade_client, + self.network_name) + self.assertEqual(_uuid, output) + + @mock.patch.object(openstack_utils, 'log') + def test_create_neutron_net_exception(self, mock_logger): + self.mock_shade_client.create_network.side_effect = ( + exc.OpenStackCloudException('error message')) + + output = openstack_utils.create_neutron_net(self.mock_shade_client, + self.network_name) + mock_logger.error.assert_called_once() + self.assertIsNone(output) + + +class CreateNeutronSubnetTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.network_name_or_id = 'name_or_id' + self.mock_shade_client.create_subnet = mock.Mock() + + def test_create_neutron_subnet(self): + _uuid = uuidutils.generate_uuid() + self.mock_shade_client.create_subnet.return_value = {'id': _uuid} + output = openstack_utils.create_neutron_subnet( + self.mock_shade_client, self.network_name_or_id) + self.assertEqual(_uuid, output) + + @mock.patch.object(openstack_utils, 'log') + def test_create_neutron_subnet_exception(self, mock_logger): + self.mock_shade_client.create_subnet.side_effect = ( + exc.OpenStackCloudException('error message')) + + output = openstack_utils.create_neutron_subnet( + self.mock_shade_client, self.network_name_or_id) + mock_logger.error.assert_called_once() + self.assertIsNone(output) + + +class DeleteNeutronRouterTestCase(unittest.TestCase): + + def setUp(self): + self.mock_shade_client = mock.Mock() + self.mock_shade_client.delete_router = mock.Mock() + + def test_delete_neutron_router(self): + self.mock_shade_client.delete_router.return_value = True + output = openstack_utils.delete_neutron_router(self.mock_shade_client, + 'router_id') + self.assertTrue(output) + + def test_delete_neutron_router_fail(self): + self.mock_shade_client.delete_router.return_value = False + output = openstack_utils.delete_neutron_router(self.mock_shade_client, + 'router_id') + self.assertFalse(output) + + @mock.patch.object(openstack_utils, 'log') + def test_delete_neutron_router_exception(self, mock_logger): + self.mock_shade_client.delete_router.side_effect = ( + exc.OpenStackCloudException('error message')) + output = openstack_utils.delete_neutron_router(self.mock_shade_client, + 'router_id') + mock_logger.error.assert_called_once() + self.assertFalse(output) diff --git a/yardstick/tests/unit/common/test_packages.py b/yardstick/tests/unit/common/test_packages.py new file mode 100644 index 000000000..ba59a3015 --- /dev/null +++ b/yardstick/tests/unit/common/test_packages.py @@ -0,0 +1,88 @@ +# Copyright (c) 2018 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import mock +from pip import exceptions as pip_exceptions +from pip.operations import freeze +import unittest + +from yardstick.common import packages + + +class PipExecuteActionTestCase(unittest.TestCase): + + def setUp(self): + self._mock_pip_main = mock.patch.object(packages, '_pip_main') + self.mock_pip_main = self._mock_pip_main.start() + self.mock_pip_main.return_value = 0 + self._mock_freeze = mock.patch.object(freeze, 'freeze') + self.mock_freeze = self._mock_freeze.start() + self.addCleanup(self._cleanup) + + def _cleanup(self): + self._mock_pip_main.stop() + self._mock_freeze.stop() + + def test_pip_execute_action(self): + self.assertEqual(0, packages._pip_execute_action('test_package')) + + def test_remove(self): + self.assertEqual(0, packages._pip_execute_action('test_package', + action='uninstall')) + + def test_install(self): + self.assertEqual(0, packages._pip_execute_action( + 'test_package', action='install', target='temp_dir')) + + def test_pip_execute_action_error(self): + self.mock_pip_main.return_value = 1 + self.assertEqual(1, packages._pip_execute_action('test_package')) + + def test_pip_execute_action_exception(self): + self.mock_pip_main.side_effect = pip_exceptions.PipError + self.assertEqual(1, packages._pip_execute_action('test_package')) + + def test_pip_list(self): + pkg_input = [ + 'XStatic-Rickshaw==1.5.0.0', + 'xvfbwrapper==0.2.9', + '-e git+https://git.opnfv.org/yardstick@50773a24afc02c9652b662ecca' + '2fc5621ea6097a#egg=yardstick', + 'zope.interface==4.4.3' + ] + pkg_dict = { + 'XStatic-Rickshaw': '1.5.0.0', + 'xvfbwrapper': '0.2.9', + 'yardstick': '50773a24afc02c9652b662ecca2fc5621ea6097a', + 'zope.interface': '4.4.3' + } + self.mock_freeze.return_value = pkg_input + + pkg_output = packages.pip_list() + for pkg_name, pkg_version in pkg_output.items(): + self.assertEqual(pkg_dict.get(pkg_name), pkg_version) + + def test_pip_list_single_package(self): + pkg_input = [ + 'XStatic-Rickshaw==1.5.0.0', + 'xvfbwrapper==0.2.9', + '-e git+https://git.opnfv.org/yardstick@50773a24afc02c9652b662ecca' + '2fc5621ea6097a#egg=yardstick', + 'zope.interface==4.4.3' + ] + self.mock_freeze.return_value = pkg_input + + pkg_output = packages.pip_list(pkg_name='xvfbwrapper') + self.assertEqual(1, len(pkg_output)) + self.assertEqual(pkg_output.get('xvfbwrapper'), '0.2.9') diff --git a/yardstick/tests/unit/common/test_template_format.py b/yardstick/tests/unit/common/test_template_format.py index 44aa80333..56253efbc 100644 --- a/yardstick/tests/unit/common/test_template_format.py +++ b/yardstick/tests/unit/common/test_template_format.py @@ -45,10 +45,3 @@ class TemplateFormatTestCase(unittest.TestCase): "Resources: {}\n" \ "Outputs: {}" self.assertRaises(ValueError, template_format.parse, yaml2) - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/common/test_utils.py b/yardstick/tests/unit/common/test_utils.py index b4907addc..9540a39e8 100644 --- a/yardstick/tests/unit/common/test_utils.py +++ b/yardstick/tests/unit/common/test_utils.py @@ -20,6 +20,7 @@ import unittest import yardstick from yardstick import ssh +import yardstick.error from yardstick.common import utils from yardstick.common import constants @@ -126,6 +127,63 @@ class CommonUtilTestCase(unittest.TestCase): ("=".join(item) for item in sorted(flattened_data.items()))) self.assertEqual(result, line) + def test_get_key_with_default_negative(self): + with self.assertRaises(KeyError): + utils.get_key_with_default({}, 'key1') + + @mock.patch('yardstick.common.utils.open', create=True) + def test_(self, mock_open): + mock_open.side_effect = IOError + + with self.assertRaises(IOError): + utils.find_relative_file('my/path', 'task/path') + + self.assertEqual(mock_open.call_count, 2) + + @mock.patch('yardstick.common.utils.open', create=True) + def test_open_relative_path(self, mock_open): + mock_open_result = mock_open() + mock_open_call_count = 1 # initial call to get result + + self.assertEqual(utils.open_relative_file('foo', 'bar'), mock_open_result) + + mock_open_call_count += 1 # one more call expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + self.assertIn('foo', mock_open.call_args_list[-1][0][0]) + self.assertNotIn('bar', mock_open.call_args_list[-1][0][0]) + + def open_effect(*args, **kwargs): + if kwargs.get('name', args[0]) == os.path.join('bar', 'foo'): + return mock_open_result + raise IOError(errno.ENOENT, 'not found') + + mock_open.side_effect = open_effect + self.assertEqual(utils.open_relative_file('foo', 'bar'), mock_open_result) + + mock_open_call_count += 2 # two more calls expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + self.assertIn('foo', mock_open.call_args_list[-1][0][0]) + self.assertIn('bar', mock_open.call_args_list[-1][0][0]) + + # test an IOError of type ENOENT + mock_open.side_effect = IOError(errno.ENOENT, 'not found') + with self.assertRaises(IOError): + # the second call still raises + utils.open_relative_file('foo', 'bar') + + mock_open_call_count += 2 # two more calls expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + self.assertIn('foo', mock_open.call_args_list[-1][0][0]) + self.assertIn('bar', mock_open.call_args_list[-1][0][0]) + + # test an IOError other than ENOENT + mock_open.side_effect = IOError(errno.EBUSY, 'busy') + with self.assertRaises(IOError): + utils.open_relative_file('foo', 'bar') + + mock_open_call_count += 1 # one more call expected + self.assertEqual(mock_open.call_count, mock_open_call_count) + class TestMacAddressToHex(unittest.TestCase): @@ -247,8 +305,8 @@ power management: """ socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) - assert sorted(socket_map.keys()) == [0] - assert sorted(socket_map[0].keys()) == [2, 3, 4] + self.assertEqual(sorted(socket_map.keys()), [0]) + self.assertEqual(sorted(socket_map[0].keys()), [2, 3, 4]) def test_single_socket_hyperthread(self): cpuinfo = """\ @@ -335,11 +393,11 @@ power management: """ socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) - assert sorted(socket_map.keys()) == [0] - assert sorted(socket_map[0].keys()) == [1, 2, 3] - assert sorted(socket_map[0][1]) == [5] - assert sorted(socket_map[0][2]) == [6] - assert sorted(socket_map[0][3]) == [7] + self.assertEqual(sorted(socket_map.keys()), [0]) + self.assertEqual(sorted(socket_map[0].keys()), [1, 2, 3]) + self.assertEqual(sorted(socket_map[0][1]), [5]) + self.assertEqual(sorted(socket_map[0][2]), [6]) + self.assertEqual(sorted(socket_map[0][3]), [7]) def test_dual_socket_hyperthread(self): cpuinfo = """\ @@ -534,15 +592,15 @@ power management: """ socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) - assert sorted(socket_map.keys()) == [0, 1] - assert sorted(socket_map[0].keys()) == [0, 1, 2] - assert sorted(socket_map[1].keys()) == [26, 27, 28] - assert sorted(socket_map[0][0]) == [44] - assert sorted(socket_map[0][1]) == [1] - assert sorted(socket_map[0][2]) == [2] - assert sorted(socket_map[1][26]) == [85] - assert sorted(socket_map[1][27]) == [86] - assert sorted(socket_map[1][28]) == [43, 87] + self.assertEqual(sorted(socket_map.keys()), [0, 1]) + self.assertEqual(sorted(socket_map[0].keys()), [0, 1, 2]) + self.assertEqual(sorted(socket_map[1].keys()), [26, 27, 28]) + self.assertEqual(sorted(socket_map[0][0]), [44]) + self.assertEqual(sorted(socket_map[0][1]), [1]) + self.assertEqual(sorted(socket_map[0][2]), [2]) + self.assertEqual(sorted(socket_map[1][26]), [85]) + self.assertEqual(sorted(socket_map[1][27]), [86]) + self.assertEqual(sorted(socket_map[1][28]), [43, 87]) def test_dual_socket_no_hyperthread(self): cpuinfo = """\ @@ -738,11 +796,11 @@ power management: """ socket_map = utils.SocketTopology.parse_cpuinfo(cpuinfo) processors = socket_map.processors() - assert processors == [1, 2, 43, 44, 85, 86, 87] + self.assertEqual(processors, [1, 2, 43, 44, 85, 86, 87]) cores = socket_map.cores() - assert cores == [0, 1, 2, 26, 27, 28] + self.assertEqual(cores, [0, 1, 2, 26, 27, 28]) sockets = socket_map.sockets() - assert sockets == [0, 1] + self.assertEqual(sockets, [0, 1]) class ChangeObjToDictTestCase(unittest.TestCase): @@ -931,9 +989,9 @@ class TestUtils(unittest.TestCase): def test_error_class(self): with self.assertRaises(RuntimeError): - utils.ErrorClass() + yardstick.error.ErrorClass() - error_instance = utils.ErrorClass(test='') + error_instance = yardstick.error.ErrorClass(test='') with self.assertRaises(AttributeError): error_instance.get_name() diff --git a/yardstick/tests/unit/common/test_yaml_loader.py b/yardstick/tests/unit/common/test_yaml_loader.py index 6c2beb422..e621dcbc5 100644 --- a/yardstick/tests/unit/common/test_yaml_loader.py +++ b/yardstick/tests/unit/common/test_yaml_loader.py @@ -12,7 +12,6 @@ # yardstick: this file is copied from python-heatclient and slightly modified -from __future__ import absolute_import import unittest from yardstick.common import yaml_loader @@ -23,10 +22,3 @@ class TemplateFormatTestCase(unittest.TestCase): def test_parse_to_value_exception(self): self.assertEqual(yaml_loader.yaml_load("string"), u"string") - - -def main(): - unittest.main() - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/dispatcher/test_influxdb.py b/yardstick/tests/unit/dispatcher/test_influxdb.py index bca94e385..c844d4bbb 100644 --- a/yardstick/tests/unit/dispatcher/test_influxdb.py +++ b/yardstick/tests/unit/dispatcher/test_influxdb.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2015 Huawei Technologies Co.,Ltd and others. # @@ -104,11 +102,3 @@ class InfluxdbDispatcherTestCase(unittest.TestCase): mock_time.time.return_value = 1451461248.925574 self.assertEqual(influxdb._get_nano_timestamp(results), '1451461248925574144') - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/orchestrator/test_heat.py b/yardstick/tests/unit/orchestrator/test_heat.py index e0a353812..aae2487aa 100644 --- a/yardstick/tests/unit/orchestrator/test_heat.py +++ b/yardstick/tests/unit/orchestrator/test_heat.py @@ -9,6 +9,7 @@ import tempfile +import munch import mock from oslo_serialization import jsonutils from oslo_utils import uuidutils @@ -40,12 +41,16 @@ class HeatStackTestCase(unittest.TestCase): self._mock_stack_delete = mock.patch.object(self.heatstack._cloud, 'delete_stack') self.mock_stack_delete = self._mock_stack_delete.start() + self._mock_stack_get = mock.patch.object(self.heatstack._cloud, + 'get_stack') + self.mock_stack_get = self._mock_stack_get.start() self.addCleanup(self._cleanup) def _cleanup(self): self._mock_stack_create.stop() self._mock_stack_delete.stop() + self._mock_stack_get.stop() heat._DEPLOYED_STACKS = {} def test_create(self): @@ -101,6 +106,88 @@ class HeatStackTestCase(unittest.TestCase): self.assertFalse(heat._DEPLOYED_STACKS) self.mock_stack_delete.assert_called_once_with(id, wait=True) + def test_get(self): + # make sure shade/get_stack is called with the appropriate vars + self.mock_stack_get.return_value = munch.Munch( + id="my-existing-stack-id", + outputs=[ + { + u'output_value': u'b734d06a-dec7-...', + u'output_key': u'ares.demo-test-port-network_id', + u'description': u'' + }, + {u'output_value': u'b08da78c-2218-...', + u'output_key': u'ares.demo-test-port-subnet_id', + u'description': u'' + }, + {u'output_value': u'10.0.1.0/24', + u'output_key': u'demo-test-subnet-cidr', + u'description': u'' + }, + {u'output_value': u'b08da78c-2218-...', + u'output_key': u'demo-test-subnet', + u'description': u'' + }, + {u'output_value': u'b1a03624-aefc-...', + u'output_key': u'ares.demo', + u'description': u'' + }, + {u'output_value': u'266a8088-c630-...', + u'output_key': u'demo-secgroup', + u'description': u'' + }, + {u'output_value': u'10.0.1.5', + u'output_key': u'ares.demo-test-port', + u'description': u'' + }, + {u'output_value': u'10.0.1.1', + u'output_key': u'demo-test-subnet-gateway_ip', + u'description': u'' + }, + {u'output_value': u'', + u'output_key': u'ares.demo-test-port-device_id', + u'description': u'' + }, + {u'output_value': u'172.24.4.7', + u'output_key': u'ares.demo-fip', + u'description': u'' + }, + {u'output_value': u'fa:16:3e:6c:c3:0f', + u'output_key': u'ares.demo-test-port-mac_address', + u'description': u''} + ] + ) + expected_outputs = { + 'ares.demo-test-port-network_id': 'b734d06a-dec7-...', + 'ares.demo-test-port-subnet_id': 'b08da78c-2218-...', + 'demo-test-subnet-cidr': '10.0.1.0/24', + 'demo-test-subnet': 'b08da78c-2218-...', + 'ares.demo': 'b1a03624-aefc-...', + 'demo-secgroup': '266a8088-c630-...', + 'ares.demo-test-port': '10.0.1.5', + 'demo-test-subnet-gateway_ip': '10.0.1.1', + 'ares.demo-test-port-device_id': '', + 'ares.demo-fip': '172.24.4.7', + 'ares.demo-test-port-mac_address': 'fa:16:3e:6c:c3:0f', + } + + stack_id = "my-existing-stack-id" + self.heatstack.name = "my-existing-stack" + self.heatstack.get() + + self.mock_stack_get.assert_called_once_with(self.heatstack.name) + self.assertEqual(expected_outputs, self.heatstack.outputs) + self.assertEqual(1, len(heat._DEPLOYED_STACKS)) + self.assertEqual(self.heatstack._stack, + heat._DEPLOYED_STACKS[stack_id]) + + def test_get_invalid_name(self): + # No context matching this name exists + self.mock_stack_get.return_value = [] + self.heatstack.name = 'not-a-stack' + self.heatstack.get() + self.assertEqual(0, len(heat._DEPLOYED_STACKS)) + class HeatTemplateTestCase(unittest.TestCase): @@ -160,7 +247,8 @@ class HeatTemplateTestCase(unittest.TestCase): def test__add_resources_to_template_raw(self): test_context = node.NodeContext() - test_context.name = 'foo' + self.addCleanup(test_context._delete_context) + test_context._name = 'foo' test_context.template_file = '/tmp/some-heat-file' test_context.heat_parameters = {'image': 'cirros'} test_context.key_filename = "/tmp/1234" @@ -184,10 +272,18 @@ class HeatTemplateTestCase(unittest.TestCase): heat_template.add_subnet("subnet2", "network2", "cidr2") heat_template.add_router("router1", "gw1", "subnet1") heat_template.add_router_interface("router_if1", "router1", "subnet1") - heat_template.add_port("port1", "network1", "subnet1", "normal") - heat_template.add_port("port2", "network2", "subnet2", "normal", + network1 = mock.MagicMock() + network1.stack_name = "network1" + network1.subnet_stack_name = "subnet1" + network1.vnic_type = "normal" + network2 = mock.MagicMock() + network2.stack_name = "network2" + network2.subnet_stack_name = "subnet2" + network2.vnic_type = "normal" + heat_template.add_port("port1", network1) + heat_template.add_port("port2", network2, sec_group_id="sec_group1", provider="not-sriov") - heat_template.add_port("port3", "network2", "subnet2", "normal", + heat_template.add_port("port3", network2, sec_group_id="sec_group1", provider="sriov") heat_template.add_floating_ip("floating_ip1", "network1", "port1", "router_if1") diff --git a/yardstick/tests/unit/orchestrator/test_kubernetes.py b/yardstick/tests/unit/orchestrator/test_kubernetes.py index 33fa1dca6..f2bc5b0f4 100644 --- a/yardstick/tests/unit/orchestrator/test_kubernetes.py +++ b/yardstick/tests/unit/orchestrator/test_kubernetes.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - ############################################################################## # Copyright (c) 2017 Intel Corporation # @@ -104,11 +102,3 @@ service ssh restart;while true ; do sleep 10000; done'] mock_get_pod_list.return_value.items = [] pods = k8s_template.get_rc_pods() self.assertEqual(pods, []) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/service/__init__.py b/yardstick/tests/unit/service/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/yardstick/tests/unit/service/__init__.py diff --git a/yardstick/tests/unit/service/test_environment.py b/yardstick/tests/unit/service/test_environment.py new file mode 100644 index 000000000..4af9a3958 --- /dev/null +++ b/yardstick/tests/unit/service/test_environment.py @@ -0,0 +1,49 @@ +############################################################################## +# Copyright (c) 2016 Huawei Technologies Co.,Ltd and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## +import unittest + +import mock + +from yardstick.service.environment import Environment +from yardstick.service.environment import AnsibleCommon +from yardstick.common.exceptions import UnsupportedPodFormatError + + +class EnvironmentTestCase(unittest.TestCase): + + def test_get_sut_info(self): + pod_info = { + 'nodes': [ + { + 'name': 'node1', + 'host_name': 'host1', + 'role': 'Controller', + 'ip': '10.1.0.50', + 'user': 'root', + 'passward': 'root' + } + ] + } + + AnsibleCommon.gen_inventory_ini_dict = mock.MagicMock() + AnsibleCommon.get_sut_info = mock.MagicMock(return_value={'node1': {}}) + + env = Environment(pod=pod_info) + env.get_sut_info() + + def test_get_sut_info_pod_str(self): + pod_info = 'nodes' + + env = Environment(pod=pod_info) + with self.assertRaises(UnsupportedPodFormatError): + env.get_sut_info() + + +if __name__ == '__main__': + unittest.main() diff --git a/yardstick/tests/unit/test_cmd/commands/test_env.py b/yardstick/tests/unit/test_cmd/commands/test_env.py index 13c3ed44a..57dacbcd3 100644 --- a/yardstick/tests/unit/test_cmd/commands/test_env.py +++ b/yardstick/tests/unit/test_cmd/commands/test_env.py @@ -21,30 +21,30 @@ class EnvCommandTestCase(unittest.TestCase): def test_do_influxdb(self, check_status_mock, start_async_task_mock): env = EnvCommand() env.do_influxdb({}) - self.assertTrue(start_async_task_mock.called) - self.assertTrue(check_status_mock.called) + start_async_task_mock.assert_called_once() + check_status_mock.assert_called_once() @mock.patch('yardstick.cmd.commands.env.EnvCommand._start_async_task') @mock.patch('yardstick.cmd.commands.env.EnvCommand._check_status') def test_do_grafana(self, check_status_mock, start_async_task_mock): env = EnvCommand() env.do_grafana({}) - self.assertTrue(start_async_task_mock.called) - self.assertTrue(check_status_mock.called) + start_async_task_mock.assert_called_once() + check_status_mock.assert_called_once() @mock.patch('yardstick.cmd.commands.env.EnvCommand._start_async_task') @mock.patch('yardstick.cmd.commands.env.EnvCommand._check_status') def test_do_prepare(self, check_status_mock, start_async_task_mock): env = EnvCommand() env.do_prepare({}) - self.assertTrue(start_async_task_mock.called) - self.assertTrue(check_status_mock.called) + start_async_task_mock.assert_called_once() + check_status_mock.assert_called_once() @mock.patch('yardstick.cmd.commands.env.HttpClient.post') def test_start_async_task(self, post_mock): data = {'action': 'create_grafana'} EnvCommand()._start_async_task(data) - self.assertTrue(post_mock.called) + post_mock.assert_called_once() @mock.patch('yardstick.cmd.commands.env.HttpClient.get') @mock.patch('yardstick.cmd.commands.env.EnvCommand._print_status') @@ -63,11 +63,3 @@ class EnvCommandTestCase(unittest.TestCase): except Exception as e: # pylint: disable=broad-except # NOTE(ralonsoh): try to reduce the scope of this exception. self.assertIsInstance(e, IndexError) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() diff --git a/yardstick/tests/unit/test_cmd/commands/test_testcase.py b/yardstick/tests/unit/test_cmd/commands/test_testcase.py index 7ef157c19..922167614 100644 --- a/yardstick/tests/unit/test_cmd/commands/test_testcase.py +++ b/yardstick/tests/unit/test_cmd/commands/test_testcase.py @@ -18,12 +18,4 @@ class TestcaseCommandsUT(unittest.TestCase): def test_do_list(self, mock_client, mock_print): mock_client.get.return_value = {'result': []} TestcaseCommands().do_list({}) - self.assertTrue(mock_print.called) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() + mock_print.assert_called_once() diff --git a/yardstick/tests/unit/test_cmd/test_NSBperf.py b/yardstick/tests/unit/test_cmd/test_NSBperf.py index 483e82a13..d64b0c551 100644 --- a/yardstick/tests/unit/test_cmd/test_NSBperf.py +++ b/yardstick/tests/unit/test_cmd/test_NSBperf.py @@ -1,5 +1,3 @@ -#!/usr/bin/env python - # Copyright (c) 2016-2017 Intel Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -91,17 +89,17 @@ class TestYardstickNSCli(unittest.TestCase): subprocess.check_output = mock.Mock(return_value=0) args = {"vnf": "vpe", "test": "tc_baremetal_rfc2544_ipv4_1flow_1518B.yaml"} - self.assertEqual(None, yardstick_ns_cli.run_test(args, test_path)) + self.assertIsNone(yardstick_ns_cli.run_test(args, test_path)) os.chdir(cur_dir) args = {"vnf": "vpe1"} - self.assertEqual(None, yardstick_ns_cli.run_test(args, test_path)) + self.assertIsNone(yardstick_ns_cli.run_test(args, test_path)) os.chdir(cur_dir) args = {"vnf": "vpe", "test": "tc_baremetal_rfc2544_ipv4_1flow_1518B.yaml."} - self.assertEqual(None, yardstick_ns_cli.run_test(args, test_path)) + self.assertIsNone(yardstick_ns_cli.run_test(args, test_path)) os.chdir(cur_dir) args = [] - self.assertEqual(None, yardstick_ns_cli.run_test(args, test_path)) + self.assertIsNone(yardstick_ns_cli.run_test(args, test_path)) os.chdir(cur_dir) def test_terminate_if_less_options(self): diff --git a/yardstick/tests/unit/test_ssh.py b/yardstick/tests/unit/test_ssh.py index dbaae8c37..f92290070 100644 --- a/yardstick/tests/unit/test_ssh.py +++ b/yardstick/tests/unit/test_ssh.py @@ -21,12 +21,13 @@ import os import socket import unittest from io import StringIO +from itertools import count import mock from oslo_utils import encodeutils from yardstick import ssh -from yardstick.ssh import SSHError +from yardstick.ssh import SSHError, SSHTimeout from yardstick.ssh import SSH from yardstick.ssh import AutoConnectSSH @@ -508,13 +509,45 @@ class SSHRunTestCase(unittest.TestCase): class TestAutoConnectSSH(unittest.TestCase): - def test__connect_with_wait(self): - auto_connect_ssh = AutoConnectSSH('user1', 'host1', wait=True) - auto_connect_ssh._get_client = mock.Mock() - auto_connect_ssh.wait = mock_wait = mock.Mock() + def test__connect_loop(self): + auto_connect_ssh = AutoConnectSSH('user1', 'host1', wait=0) + auto_connect_ssh._get_client = mock__get_client = mock.Mock() auto_connect_ssh._connect() - self.assertEqual(mock_wait.call_count, 1) + self.assertEqual(mock__get_client.call_count, 1) + + def test___init___negative(self): + with self.assertRaises(TypeError): + AutoConnectSSH('user1', 'host1', wait=['wait']) + + with self.assertRaises(ValueError): + AutoConnectSSH('user1', 'host1', wait='wait') + + @mock.patch('yardstick.ssh.time') + def test__connect_loop_ssh_error(self, mock_time): + mock_time.time.side_effect = count() + + auto_connect_ssh = AutoConnectSSH('user1', 'host1', wait=10) + auto_connect_ssh._get_client = mock__get_client = mock.Mock() + mock__get_client.side_effect = SSHError + + with self.assertRaises(SSHTimeout): + auto_connect_ssh._connect() + + self.assertEqual(mock_time.time.call_count, 12) + + def test_get_file_obj(self): + auto_connect_ssh = AutoConnectSSH('user1', 'host1', wait=10) + auto_connect_ssh._get_client = mock__get_client = mock.Mock() + mock_client = mock__get_client() + mock_open_sftp = mock_client.open_sftp() + mock_sftp = mock.Mock() + mock_open_sftp.__enter__ = mock.Mock(return_value=mock_sftp) + mock_open_sftp.__exit__ = mock.Mock() + + auto_connect_ssh.get_file_obj('remote/path', mock.Mock()) + + self.assertEqual(mock_sftp.getfo.call_count, 1) def test__make_dict(self): auto_connect_ssh = AutoConnectSSH('user1', 'host1') @@ -527,7 +560,7 @@ class TestAutoConnectSSH(unittest.TestCase): 'key_filename': None, 'password': None, 'name': None, - 'wait': True, + 'wait': AutoConnectSSH.DEFAULT_WAIT_TIMEOUT, } result = auto_connect_ssh._make_dict() self.assertDictEqual(result, expected) @@ -537,6 +570,13 @@ class TestAutoConnectSSH(unittest.TestCase): self.assertEqual(auto_connect_ssh.get_class(), AutoConnectSSH) + def test_drop_connection(self): + auto_connect_ssh = AutoConnectSSH('user1', 'host1') + self.assertFalse(auto_connect_ssh._client) + auto_connect_ssh._client = True + auto_connect_ssh.drop_connection() + self.assertFalse(auto_connect_ssh._client) + @mock.patch('yardstick.ssh.SCPClient') def test_put(self, mock_scp_client_type): auto_connect_ssh = AutoConnectSSH('user1', 'host1') @@ -562,11 +602,3 @@ class TestAutoConnectSSH(unittest.TestCase): auto_connect_ssh.put_file('a', 'b') self.assertEqual(mock_put_sftp.call_count, 1) - - -def main(): - unittest.main() - - -if __name__ == '__main__': - main() |