diff options
Diffstat (limited to 'yardstick')
16 files changed, 495 insertions, 289 deletions
diff --git a/yardstick/benchmark/contexts/kubernetes.py b/yardstick/benchmark/contexts/kubernetes.py index a39f63137..2334e5076 100644 --- a/yardstick/benchmark/contexts/kubernetes.py +++ b/yardstick/benchmark/contexts/kubernetes.py @@ -54,6 +54,7 @@ class KubernetesContext(Context): LOG.info('Launch containers') self._create_rcs() + self._create_services() time.sleep(1) self.template.get_rc_pods() @@ -63,6 +64,7 @@ class KubernetesContext(Context): self._delete_ssh_key() self._delete_rcs() self._delete_pods() + self._delete_services() super(KubernetesContext, self).undeploy() @@ -80,6 +82,14 @@ class KubernetesContext(Context): return False return True + def _create_services(self): + for obj in self.template.service_objs: + obj.create() + + def _delete_services(self): + for obj in self.template.service_objs: + obj.delete() + def _create_rcs(self): for obj in self.template.k8s_objs: self._create_rc(obj.get_template()) @@ -126,15 +136,22 @@ class KubernetesContext(Context): utils.remove_file(self.public_key_path) def _get_server(self, name): - resp = k8s_utils.get_pod_list() - hosts = ({'name': n.metadata.name, - 'ip': n.status.pod_ip, - 'user': 'root', - 'key_filename': self.key_path, - 'private_ip': n.status.pod_ip} - for n in resp.items if n.metadata.name.startswith(name)) - - return next(hosts, None) + service_name = '{}-service'.format(name) + service = k8s_utils.get_service_by_name(service_name).ports[0] + + host = { + 'name': service.name, + 'ip': self._get_node_ip(), + 'private_ip': k8s_utils.get_pod_by_name(name).status.pod_ip, + 'ssh_port': service.node_port, + 'user': 'root', + 'key_filename': self.key_path, + } + + return host + + def _get_node_ip(self): + return k8s_utils.get_node_list().items[0].status.addresses[0].address def _get_network(self, attr_name): return None diff --git a/yardstick/benchmark/core/task.py b/yardstick/benchmark/core/task.py index 75703cf50..c175a950b 100644 --- a/yardstick/benchmark/core/task.py +++ b/yardstick/benchmark/core/task.py @@ -125,9 +125,10 @@ class Task(object): # pragma: no cover except KeyboardInterrupt: raise except Exception: - LOG.exception("Running test case %s failed!", case_name) + LOG.error('Testcase: "%s" FAILED!!!', case_name, exc_info=True) testcases[case_name] = {'criteria': 'FAIL', 'tc_data': []} else: + LOG.info('Testcase: "%s" SUCCESS!!!', case_name) testcases[case_name] = {'criteria': 'PASS', 'tc_data': data} if args.keep_deploy: @@ -272,7 +273,9 @@ class Task(object): # pragma: no cover runner = self.run_one_scenario(scenario, output_file) status = runner_join(runner) if status != 0: - LOG.error('Scenario: %s ERROR', scenario.get('type')) + LOG.error('Scenario NO.%s: "%s" ERROR!', + scenarios.index(scenario) + 1, + scenario.get('type')) raise RuntimeError self.outputs.update(runner.get_output()) result.extend(runner.get_result()) @@ -325,23 +328,30 @@ class Task(object): # pragma: no cover # TODO support get multi hosts/vms info context_cfg = {} - if "host" in scenario_cfg: - context_cfg['host'] = Context.get_server(scenario_cfg["host"]) + server_name = scenario_cfg.get('options', {}).get('server_name', {}) - if "target" in scenario_cfg: - if is_ip_addr(scenario_cfg["target"]): - context_cfg['target'] = {} - context_cfg['target']["ipaddr"] = scenario_cfg["target"] + def config_context_target(cfg): + target = cfg['target'] + if is_ip_addr(target): + context_cfg['target'] = {"ipaddr": target} else: - context_cfg['target'] = Context.get_server( - scenario_cfg["target"]) - if self._is_same_heat_context(scenario_cfg["host"], - scenario_cfg["target"]): - context_cfg["target"]["ipaddr"] = \ - context_cfg["target"]["private_ip"] + context_cfg['target'] = Context.get_server(target) + if self._is_same_context(cfg["host"], target): + context_cfg['target']["ipaddr"] = context_cfg['target']["private_ip"] else: - context_cfg["target"]["ipaddr"] = \ - context_cfg["target"]["ip"] + context_cfg['target']["ipaddr"] = context_cfg['target']["ip"] + + host_name = server_name.get('host', scenario_cfg.get('host')) + if host_name: + context_cfg['host'] = Context.get_server(host_name) + + for item in [server_name, scenario_cfg]: + try: + config_context_target(item) + except KeyError: + pass + else: + break if "targets" in scenario_cfg: ip_list = [] @@ -351,8 +361,8 @@ class Task(object): # pragma: no cover context_cfg['target'] = {} else: context_cfg['target'] = Context.get_server(target) - if self._is_same_heat_context(scenario_cfg["host"], - target): + if self._is_same_context(scenario_cfg["host"], + target): ip_list.append(context_cfg["target"]["private_ip"]) else: ip_list.append(context_cfg["target"]["ip"]) @@ -370,7 +380,7 @@ class Task(object): # pragma: no cover return runner - def _is_same_heat_context(self, host_attr, target_attr): + def _is_same_context(self, host_attr, target_attr): """check if two servers are in the same heat context host_attr: either a name for a server created by yardstick or a dict with attribute name mapping when using external heat templates @@ -378,7 +388,7 @@ class Task(object): # pragma: no cover with attribute name mapping when using external heat templates """ for context in self.contexts: - if context.__context_type__ != "Heat": + if context.__context_type__ not in {"Heat", "Kubernetes"}: continue host = context._get_server(host_attr) @@ -669,25 +679,24 @@ def parse_task_args(src_name, args): def change_server_name(scenario, suffix): - try: - host = scenario['host'] - except KeyError: - pass - else: - try: - host['name'] += suffix - except TypeError: - scenario['host'] += suffix - try: - target = scenario['target'] - except KeyError: - pass - else: + def add_suffix(cfg, key): try: - target['name'] += suffix - except TypeError: - scenario['target'] += suffix + 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' diff --git a/yardstick/benchmark/scenarios/availability/attacker/attacker_baremetal.py b/yardstick/benchmark/scenarios/availability/attacker/attacker_baremetal.py index 50d44c1ca..979e3ab14 100644 --- a/yardstick/benchmark/scenarios/availability/attacker/attacker_baremetal.py +++ b/yardstick/benchmark/scenarios/availability/attacker/attacker_baremetal.py @@ -40,6 +40,21 @@ class BaremetalAttacker(BaseAttacker): self.connection = ssh.SSH.from_node(host, defaults={"user": "root"}) self.connection.wait(timeout=600) LOG.debug("ssh host success!") + + jump_host_name = self._config.get("jump_host", None) + self.jump_connection = None + if jump_host_name is not None: + jump_host = self._context.get(jump_host_name, None) + + LOG.debug("jump_host ip:%s user:%s", jump_host['ip'], jump_host['user']) + self.jump_connection = ssh.SSH.from_node( + jump_host, + # why do we allow pwd for password? + defaults={"user": "root", "password": jump_host.get("pwd")} + ) + self.jump_connection.wait(timeout=600) + LOG.debug("ssh jump host success!") + self.host_ip = host['ip'] self.ipmi_ip = host.get("ipmi_ip", None) @@ -49,6 +64,7 @@ class BaremetalAttacker(BaseAttacker): self.fault_cfg = BaseAttacker.attacker_cfgs.get('bare-metal-down') self.check_script = self.get_script_fullpath( self.fault_cfg['check_script']) + self.inject_script = self.get_script_fullpath(self.fault_cfg['inject_script']) self.recovery_script = self.get_script_fullpath( self.fault_cfg['recovery_script']) @@ -70,39 +86,27 @@ class BaremetalAttacker(BaseAttacker): return True def inject_fault(self): - exit_status, stdout, stderr = self.connection.execute( - "sudo shutdown -h now") - LOG.debug("inject fault ret: %s out:%s err:%s", - exit_status, stdout, stderr) - if not exit_status: - LOG.info("inject fault success") + LOG.info("Inject fault START") + cmd = "sudo /bin/bash -s {0} {1} {2} {3}".format( + self.ipmi_ip, self.ipmi_user, self.ipmi_pwd, "off") + with open(self.inject_script, "r") as stdin_file: + if self.jump_connection is not None: + LOG.info("Power off node via IPMI") + self.jump_connection.execute(cmd, stdin=stdin_file) + else: + _execute_shell_command(cmd, stdin=stdin_file) + LOG.info("Inject fault END") def recover(self): - jump_host_name = self._config.get("jump_host", None) - self.jump_connection = None - if jump_host_name is not None: - host = self._context.get(jump_host_name, None) - - LOG.debug("jump_host ip:%s user:%s", host['ip'], host['user']) - self.jump_connection = ssh.SSH.from_node( - host, - # why do we allow pwd for password? - defaults={"user": "root", "password": host.get("pwd")} - ) - self.jump_connection.wait(timeout=600) - LOG.debug("ssh jump host success!") - - if self.jump_connection is not None: - with open(self.recovery_script, "r") as stdin_file: - self.jump_connection.execute( - "sudo /bin/bash -s {0} {1} {2} {3}".format( - self.ipmi_ip, self.ipmi_user, self.ipmi_pwd, "on"), - stdin=stdin_file) - else: - _execute_shell_command( - "sudo /bin/bash -s {0} {1} {2} {3}".format( - self.ipmi_ip, self.ipmi_user, self.ipmi_pwd, "on"), - stdin=open(self.recovery_script, "r")) + LOG.info("Recover fault START") + cmd = "sudo /bin/bash -s {0} {1} {2} {3}".format( + self.ipmi_ip, self.ipmi_user, self.ipmi_pwd, "on") + with open(self.recovery_script, "r") as stdin_file: + if self.jump_connection is not None: + self.jump_connection.execute(cmd, stdin=stdin_file) + else: + _execute_shell_command(cmd, stdin=stdin_file) + LOG.info("Recover fault END") def _test(): # pragma: no cover diff --git a/yardstick/benchmark/scenarios/availability/attacker_conf.yaml b/yardstick/benchmark/scenarios/availability/attacker_conf.yaml index ee7ea7d83..5f43a701a 100644 --- a/yardstick/benchmark/scenarios/availability/attacker_conf.yaml +++ b/yardstick/benchmark/scenarios/availability/attacker_conf.yaml @@ -23,6 +23,7 @@ kill-lxc-process: bare-metal-down: check_script: ha_tools/check_host_ping.bash + inject_script: ha_tools/ipmi_power.bash recovery_script: ha_tools/ipmi_power.bash stop-service: diff --git a/yardstick/benchmark/scenarios/networking/netperf.py b/yardstick/benchmark/scenarios/networking/netperf.py index 08d5dd166..a8d9010ed 100755 --- a/yardstick/benchmark/scenarios/networking/netperf.py +++ b/yardstick/benchmark/scenarios/networking/netperf.py @@ -114,6 +114,10 @@ class Netperf(base.Scenario): cmd_args += " %s %s" % (option_pair[1], options[option_pair[0]]) + # Enable IP routing for UDP_STREAM test + if testname == "UDP_STREAM": + cmd_args += " -R 1" + cmd = "sudo bash netperf.sh %s" % (cmd_args) LOG.debug("Executing command: %s", cmd) status, stdout, stderr = self.client.execute(cmd) diff --git a/yardstick/common/kubernetes_utils.py b/yardstick/common/kubernetes_utils.py index e4c232830..0cf7b9eab 100644 --- a/yardstick/common/kubernetes_utils.py +++ b/yardstick/common/kubernetes_utils.py @@ -28,6 +28,60 @@ def get_core_api(): # pragma: no cover return client.CoreV1Api() +def get_node_list(**kwargs): # pragma: no cover + core_v1_api = get_core_api() + try: + return core_v1_api.list_node(**kwargs) + except ApiException: + LOG.exception('Get node list failed') + raise + + +def create_service(template, + namespace='default', + wait=False, + **kwargs): # pragma: no cover + core_v1_api = get_core_api() + metadata = client.V1ObjectMeta(**template.get('metadata', {})) + + ports = [client.V1ServicePort(**port) for port in + template.get('spec', {}).get('ports', [])] + template['spec']['ports'] = ports + spec = client.V1ServiceSpec(**template.get('spec', {})) + + service = client.V1Service(metadata=metadata, spec=spec) + + try: + core_v1_api.create_namespaced_service('default', service) + except ApiException: + LOG.exception('Create Service failed') + raise + + +def delete_service(name, + namespace='default', + **kwargs): # pragma: no cover + core_v1_api = get_core_api() + try: + core_v1_api.delete_namespaced_service(name, namespace, **kwargs) + except ApiException: + LOG.exception('Delete Service failed') + + +def get_service_list(namespace='default', **kwargs): + core_v1_api = get_core_api() + try: + return core_v1_api.list_namespaced_service(namespace, **kwargs) + except ApiException: + LOG.exception('Get Service list failed') + raise + + +def get_service_by_name(name): # pragma: no cover + service_list = get_service_list() + return next((s.spec for s in service_list.items if s.metadata.name == name), None) + + def create_replication_controller(template, namespace='default', wait=False, @@ -135,3 +189,8 @@ def get_pod_list(namespace='default'): # pragma: no cover except ApiException: LOG.exception('Get pod list failed') raise + + +def get_pod_by_name(name): # pragma: no cover + pod_list = get_pod_list() + return next((n for n in pod_list.items if n.metadata.name.startswith(name)), None) diff --git a/yardstick/network_services/libs/ixia_libs/IxNet/IxNet.py b/yardstick/network_services/libs/ixia_libs/IxNet/IxNet.py index 4b906508c..358e6e761 100644 --- a/yardstick/network_services/libs/ixia_libs/IxNet/IxNet.py +++ b/yardstick/network_services/libs/ixia_libs/IxNet/IxNet.py @@ -113,10 +113,10 @@ class IxNextgen(object): } MODE_SEEDS_MAP = { - 0: ('uplink_0', ['256', '2048']), + 0: ('uplink', ['256', '2048']), } - MODE_SEEDS_DEFAULT = 'downlink_0', ['2048', '256'] + MODE_SEEDS_DEFAULT = 'downlink', ['2048', '256'] @staticmethod def find_view_obj(view_name, views): @@ -125,24 +125,27 @@ class IxNextgen(object): @staticmethod def get_config(tg_cfg): + card = [] + port = [] external_interface = tg_cfg["vdu"][0]["external-interface"] - card_port0 = external_interface[0]["virtual-interface"]["vpci"] - card_port1 = external_interface[1]["virtual-interface"]["vpci"] - card0, port0 = card_port0.split(':')[:2] - card1, port1 = card_port1.split(':')[:2] + for intf in external_interface: + card_port0 = intf["virtual-interface"]["vpci"] + card0, port0 = card_port0.split(':')[:2] + card.append(card0) + port.append(port0) + cfg = { 'py_lib_path': tg_cfg["mgmt-interface"]["tg-config"]["py_lib_path"], 'machine': tg_cfg["mgmt-interface"]["ip"], 'port': tg_cfg["mgmt-interface"]["tg-config"]["tcl_port"], 'chassis': tg_cfg["mgmt-interface"]["tg-config"]["ixchassis"], - 'card1': card0, - 'port1': port0, - 'card2': card1, - 'port2': port1, + 'cards': card, + 'ports': port, 'output_dir': tg_cfg["mgmt-interface"]["tg-config"]["dut_result_dir"], 'version': tg_cfg["mgmt-interface"]["tg-config"]["version"], 'bidir': True, } + return cfg def __init__(self, ixnet=None): @@ -183,9 +186,13 @@ class IxNextgen(object): self.set_random_ip_multi_attribute(ip, seeds[1], fixed_bits, random_mask, l3_count) def add_ip_header(self, params, version): - for it, ep, i in self.iter_over_get_lists('/traffic', 'trafficItem', "configElement"): - mode, seeds = self.MODE_SEEDS_MAP.get(i, self.MODE_SEEDS_DEFAULT) - l3 = params[mode]['outer_l3'] + for it, ep, i in self.iter_over_get_lists('/traffic', 'trafficItem', "configElement", 1): + iter1 = (v['outer_l3'] for v in params.values() if str(v['id']) == str(i)) + try: + l3 = next(iter1, {}) + seeds = self.MODE_SEEDS_MAP.get(i, self.MODE_SEEDS_DEFAULT)[1] + except (KeyError, IndexError): + continue for ip, ip_bits, _ in self.iter_over_get_lists(ep, 'stack', 'field'): self.set_random_ip_multi_attributes(ip_bits, version, seeds, l3) @@ -222,10 +229,11 @@ class IxNextgen(object): def ix_assign_ports(self): vports = self.ixnet.getList(self.ixnet.getRoot(), 'vport') - ports = [ - (self._cfg['chassis'], self._cfg['card1'], self._cfg['port1']), - (self._cfg['chassis'], self._cfg['card2'], self._cfg['port2']), - ] + ports = [] + + chassis = self._cfg['chassis'] + ports = [(chassis, card, port) for card, port in + zip(self._cfg['cards'], self._cfg['ports'])] vport_list = self.ixnet.getList("/", "vport") self.ixnet.execute('assignPorts', ports, [], vport_list, True) @@ -276,10 +284,10 @@ class IxNextgen(object): def update_ether_multi_attributes(self, ether, l2): if "ethernet.header.destinationAddress" in ether: - self.update_ether_multi_attribute(ether, str(l2['dstmac'])) + self.update_ether_multi_attribute(ether, str(l2.get('dstmac', "00:00:00:00:00:02"))) if "ethernet.header.sourceAddress" in ether: - self.update_ether_multi_attribute(ether, str(l2['srcmac'])) + self.update_ether_multi_attribute(ether, str(l2.get('srcmac', "00:00:00:00:00:01"))) def ix_update_ether(self, params): for ti, ep, index in self.iter_over_get_lists('/traffic', 'trafficItem', diff --git a/yardstick/network_services/nfvi/collectd.conf b/yardstick/network_services/nfvi/collectd.conf index 3928dcbca..22bd5d49d 100644 --- a/yardstick/network_services/nfvi/collectd.conf +++ b/yardstick/network_services/nfvi/collectd.conf @@ -15,7 +15,7 @@ Hostname "nsb_stats" FQDNLookup true -Interval {interval} +Interval {{ interval }} ############################################################################## # LoadPlugin section # @@ -24,7 +24,9 @@ Interval {interval} ############################################################################## #LoadPlugin syslog -{loadplugin} +{% for plugin in loadplugins %} +LoadPlugin {{ plugin }} +{% endfor %} ############################################################################## # Plugin configuration # @@ -38,42 +40,31 @@ Interval {interval} #</Plugin> <Plugin amqp> - <Publish "name"> - Host "0.0.0.0" - Port "5672" - VHost "/" - User "admin" - Password "admin" - Exchange "amq.fanout" - RoutingKey "collectd" - Persistent false - StoreRates false - ConnectionRetryDelay 0 - </Publish> + <Publish "name"> + Host "0.0.0.0" + Port "5672" + VHost "/" + User "admin" + Password "admin" + Exchange "amq.fanout" + RoutingKey "collectd" + Persistent false + StoreRates false + ConnectionRetryDelay 0 + </Publish> </Plugin> <Plugin cpu> - ReportByCpu true - ReportByState true - ValuesPercentage true + ReportByCpu true + ReportByState true + ValuesPercentage true </Plugin> <Plugin memory> - ValuesAbsolute true - ValuesPercentage false -</Plugin> - -<Plugin "intel_rdt"> - Cores "" + ValuesAbsolute true + ValuesPercentage false </Plugin> -<Plugin intel_pmu> - ReportHardwareCacheEvents true - ReportKernelPMUEvents true - ReportSoftwareEvents true - EventList "/root/.cache/pmu-events/GenuineIntel-6-2D-core.json" - HardwareEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS" "L2_RQSTS.ALL_CODE_RD" -</Plugin> <Plugin hugepages> ReportPerNodeHP true @@ -83,15 +74,25 @@ Interval {interval} ValuesPercentage false </Plugin> -<Plugin hugepages> - ReportPerNodeHP true - ReportRootHP true - ValuesPages true - ValuesBytes false - ValuesPercentage false + +{% if "intel_rdt" in plugins %} +<Plugin "intel_rdt"> + Cores "" +</Plugin> +{% endif %} + +{% if "intel_pmu" in plugins %} +<Plugin intel_pmu> + ReportHardwareCacheEvents true + ReportKernelPMUEvents true + ReportSoftwareEvents true + EventList "/root/.cache/pmu-events/GenuineIntel-6-2D-core.json" + HardwareEvents "L2_RQSTS.CODE_RD_HIT,L2_RQSTS.CODE_RD_MISS" "L2_RQSTS.ALL_CODE_RD" </Plugin> +{% endif %} -<Plugin dpdkstat> +{% if "dpdkstat" in plugins %} +<Plugin "dpdkstat"> <EAL> Coremask "0x1" MemoryChannels "4" @@ -100,20 +101,24 @@ Interval {interval} </EAL> SharedMemObj "dpdk_collectd_stats_0" EnabledPortMask 0xffff - {dpdk_interface} +{% for port_name in port_names %} + PortName {{ port_name }} +{% endfor %} </Plugin> +{% endif %} -<Plugin virt> - Domain "samplevnf" +{% if "virt" in plugins %} +<Plugin "virt"> +# monitor all domains </Plugin> +{% endif %} -<Plugin ovs_stats> +{% if "ovs_stats" in plugins %} +<Plugin "ovs_stats"> Port "6640" Address "127.0.0.1" Socket "/usr/local/var/run/openvswitch/db.sock" - Bridges "br0" "br_ext" +# don't specify bridges, monitor all bridges </Plugin> +{% endif %} -<Include "/etc/collectd/collectd.conf.d"> - Filter "*.conf" -</Include> diff --git a/yardstick/network_services/nfvi/collectd.sh b/yardstick/network_services/nfvi/collectd.sh index 296c4a213..bdc5abd03 100755 --- a/yardstick/network_services/nfvi/collectd.sh +++ b/yardstick/network_services/nfvi/collectd.sh @@ -142,7 +142,8 @@ else fi modprobe msr -cp $INSTALL_NSB_BIN/collectd.conf /opt/collectd/etc/ +# we overwrite the config file during _start_collectd so don't copy it +#cp $INSTALL_NSB_BIN/collectd.conf /opt/nsb_bin/collectd/etc/ sudo service rabbitmq-server restart echo "Check if admin user already created" rabbitmqctl list_users | grep '^admin$' > /dev/null diff --git a/yardstick/network_services/nfvi/resource.py b/yardstick/network_services/nfvi/resource.py index fa32a4dcf..d807f5e46 100644 --- a/yardstick/network_services/nfvi/resource.py +++ b/yardstick/network_services/nfvi/resource.py @@ -15,16 +15,22 @@ from __future__ import absolute_import from __future__ import print_function -import tempfile + import logging +from itertools import chain + +import jinja2 import os import os.path import re import multiprocessing +import pkg_resources from oslo_config import cfg +from oslo_utils.encodeutils import safe_decode from yardstick import ssh +from yardstick.common.task_template import finalize_for_yaml from yardstick.common.utils import validate_non_string_sequence from yardstick.network_services.nfvi.collectd import AmqpConsumer from yardstick.network_services.utils import get_nsb_option @@ -34,26 +40,36 @@ LOG = logging.getLogger(__name__) CONF = cfg.CONF ZMQ_OVS_PORT = 5567 ZMQ_POLLING_TIME = 12000 -LIST_PLUGINS_ENABLED = ["amqp", "cpu", "cpufreq", "intel_rdt", "memory", - "hugepages", "dpdkstat", "virt", "ovs_stats", "intel_pmu"] +LIST_PLUGINS_ENABLED = ["amqp", "cpu", "cpufreq", "memory", + "hugepages"] class ResourceProfile(object): """ This profile adds a resource at the beginning of the test session """ + COLLECTD_CONF = "collectd.conf" + AMPQ_PORT = 5672 + DEFAULT_INTERVAL = 25 - def __init__(self, mgmt, interfaces=None, cores=None): + def __init__(self, mgmt, port_names=None, cores=None, plugins=None, interval=None): + if plugins is None: + self.plugins = {} + else: + self.plugins = plugins + if interval is None: + self.interval = self.DEFAULT_INTERVAL + else: + self.interval = interval self.enable = True self.cores = validate_non_string_sequence(cores, default=[]) self._queue = multiprocessing.Queue() self.amqp_client = None - self.interfaces = validate_non_string_sequence(interfaces, default=[]) + self.port_names = validate_non_string_sequence(port_names, default=[]) - # why the host or ip? - self.vnfip = mgmt.get("host", mgmt["ip"]) - self.connection = ssh.SSH.from_node(mgmt, overrides={"ip": self.vnfip}) - self.connection.wait() + # we need to save mgmt so we can connect to port 5672 + self.mgmt = mgmt + self.connection = ssh.AutoConnectSSH.from_node(mgmt) def check_if_sa_running(self, process): """ verify if system agent is running """ @@ -62,7 +78,7 @@ class ResourceProfile(object): def run_collectd_amqp(self): """ run amqp consumer to collect the NFVi data """ - amqp_url = 'amqp://admin:admin@{}:5672/%2F'.format(self.vnfip) + amqp_url = 'amqp://admin:admin@{}:{}/%2F'.format(self.mgmt['ip'], self.AMPQ_PORT) amqp = AmqpConsumer(amqp_url, self._queue) try: amqp.run() @@ -124,7 +140,9 @@ class ResourceProfile(object): } testcase = "" - for key, value in metrics.items(): + # unicode decode + decoded = ((safe_decode(k, 'utf-8'), safe_decode(v, 'utf-8')) for k, v in metrics.items()) + for key, value in decoded: key_split = key.split("/") res_key_iter = (key for key in key_split if "nsb_stats" not in key) res_key0 = next(res_key_iter) @@ -176,35 +194,36 @@ class ResourceProfile(object): msg = self.parse_collectd_result(metric, self.cores) return msg - def _provide_config_file(self, bin_path, nfvi_cfg, kwargs): - with open(os.path.join(bin_path, nfvi_cfg), 'r') as cfg: - template = cfg.read() - cfg, cfg_content = tempfile.mkstemp() - with os.fdopen(cfg, "w+") as cfg: - cfg.write(template.format(**kwargs)) - cfg_file = os.path.join(bin_path, nfvi_cfg) - self.connection.put(cfg_content, cfg_file) - - def _prepare_collectd_conf(self, bin_path): + def _provide_config_file(self, config_file_path, nfvi_cfg, template_kwargs): + template = pkg_resources.resource_string("yardstick.network_services.nfvi", + nfvi_cfg).decode('utf-8') + cfg_content = jinja2.Template(template, trim_blocks=True, lstrip_blocks=True, + finalize=finalize_for_yaml).render( + **template_kwargs) + # cfg_content = io.StringIO(template.format(**template_kwargs)) + cfg_file = os.path.join(config_file_path, nfvi_cfg) + # must write as root, so use sudo + self.connection.execute("cat | sudo tee {}".format(cfg_file), stdin=cfg_content) + + def _prepare_collectd_conf(self, config_file_path): """ Prepare collectd conf """ - loadplugin = "\n".join("LoadPlugin {0}".format(plugin) - for plugin in LIST_PLUGINS_ENABLED) - - interfaces = "\n".join("PortName '{0[name]}'".format(interface) - for interface in self.interfaces) kwargs = { - "interval": '25', - "loadplugin": loadplugin, - "dpdk_interface": interfaces, + "interval": self.interval, + "loadplugins": set(chain(LIST_PLUGINS_ENABLED, self.plugins.keys())), + # Optional fields PortName is descriptive only, use whatever is present + "port_names": self.port_names, + # "ovs_bridge_interfaces": ["br-int"], + "plugins": self.plugins, } - self._provide_config_file(bin_path, 'collectd.conf', kwargs) + self._provide_config_file(config_file_path, self.COLLECTD_CONF, kwargs) def _start_collectd(self, connection, bin_path): LOG.debug("Starting collectd to collect NFVi stats") - connection.execute('sudo pkill -9 collectd') + connection.execute('sudo pkill -x -9 collectd') bin_path = get_nsb_option("bin_path") - collectd_path = os.path.join(bin_path, "collectd", "collectd") + collectd_path = os.path.join(bin_path, "collectd", "sbin", "collectd") + config_file_path = os.path.join(bin_path, "collectd", "etc") exit_status = connection.execute("which %s > /dev/null 2>&1" % collectd_path)[0] if exit_status != 0: LOG.warning("%s is not present disabling", collectd_path) @@ -217,7 +236,9 @@ class ResourceProfile(object): # collectd_installer, http_proxy, https_proxy)) return LOG.debug("Starting collectd to collect NFVi stats") - self._prepare_collectd_conf(bin_path) + # ensure collectd.conf.d exists to avoid error/warning + connection.execute("sudo mkdir -p /etc/collectd/collectd.conf.d") + self._prepare_collectd_conf(config_file_path) # Reset amqp queue LOG.debug("reset and setup amqp to collect data from collectd") @@ -228,7 +249,7 @@ class ResourceProfile(object): connection.execute("sudo rabbitmqctl start_app") connection.execute("sudo service rabbitmq-server restart") - LOG.debug("Creating amdin user for rabbitmq in order to collect data from collectd") + LOG.debug("Creating admin user for rabbitmq in order to collect data from collectd") connection.execute("sudo rabbitmqctl delete_user guest") connection.execute("sudo rabbitmqctl add_user admin admin") connection.execute("sudo rabbitmqctl authenticate_user admin admin") @@ -241,7 +262,11 @@ class ResourceProfile(object): def initiate_systemagent(self, bin_path): """ Start system agent for NFVi collection on host """ if self.enable: - self._start_collectd(self.connection, bin_path) + try: + self._start_collectd(self.connection, bin_path) + except Exception: + LOG.exception("Exception during collectd start") + raise def start(self): """ start nfvi collection """ diff --git a/yardstick/network_services/traffic_profile/ixia_rfc2544.py b/yardstick/network_services/traffic_profile/ixia_rfc2544.py index cb8a34796..28480b8e9 100644 --- a/yardstick/network_services/traffic_profile/ixia_rfc2544.py +++ b/yardstick/network_services/traffic_profile/ixia_rfc2544.py @@ -43,35 +43,39 @@ class IXIARFC2544Profile(TrexProfile): traffic = static_traffic[traffickey] # outer_l2 index = 0 - for key, value in profile_data[traffickey].items(): - framesize = value['outer_l2']['framesize'] - traffic['outer_l2']['framesize'] = framesize - traffic['framesPerSecond'] = True - traffic['bidir'] = False - traffic['outer_l2']['srcmac'] = \ - mac["src_mac_{}".format(traffic['id'])] - traffic['outer_l2']['dstmac'] = \ - mac["dst_mac_{}".format(traffic['id'])] - - # outer_l3 - if "outer_l3v6" in list(value.keys()): - traffic['outer_l3'] = value['outer_l3v6'] - srcip4 = value['outer_l3v6']['srcip6'] - traffic['outer_l3']['srcip4'] = srcip4.split("-")[0] - dstip4 = value['outer_l3v6']['dstip6'] - traffic['outer_l3']['dstip4'] = dstip4.split("-")[0] - else: - traffic['outer_l3'] = value['outer_l3v4'] - srcip4 = value['outer_l3v4']['srcip4'] - traffic['outer_l3']['srcip4'] = srcip4.split("-")[0] - dstip4 = value['outer_l3v4']['dstip4'] - traffic['outer_l3']['dstip4'] = dstip4.split("-")[0] - - traffic['outer_l3']['type'] = key - traffic['outer_l3']['count'] = value['outer_l3v4']['count'] - # outer_l4 - traffic['outer_l4'] = value['outer_l4'] - index = index + 1 + try: + for key, value in profile_data[traffickey].items(): + framesize = value['outer_l2']['framesize'] + traffic['outer_l2']['framesize'] = framesize + traffic['framesPerSecond'] = True + traffic['bidir'] = False + traffic['outer_l2']['srcmac'] = \ + mac["src_mac_{}".format(traffic['id'])] + traffic['outer_l2']['dstmac'] = \ + mac["dst_mac_{}".format(traffic['id'])] + + # outer_l3 + if "outer_l3v6" in list(value.keys()): + traffic['outer_l3'] = value['outer_l3v6'] + srcip4 = value['outer_l3v6']['srcip6'] + traffic['outer_l3']['srcip4'] = srcip4.split("-")[0] + dstip4 = value['outer_l3v6']['dstip6'] + traffic['outer_l3']['dstip4'] = dstip4.split("-")[0] + else: + traffic['outer_l3'] = value['outer_l3v4'] + srcip4 = value['outer_l3v4']['srcip4'] + traffic['outer_l3']['srcip4'] = srcip4.split("-")[0] + dstip4 = value['outer_l3v4']['dstip4'] + traffic['outer_l3']['dstip4'] = dstip4.split("-")[0] + + traffic['outer_l3']['type'] = key + traffic['outer_l3']['count'] = value['outer_l3v4']['count'] + # outer_l4 + traffic['outer_l4'] = value['outer_l4'] + index = index + 1 + except Exception: + continue + result.update({traffickey: traffic}) return result @@ -103,7 +107,9 @@ class IXIARFC2544Profile(TrexProfile): self.ports = [port for port in port_generator()] - def execute_traffic(self, traffic_generator, ixia_obj, mac={}, xfile=None): + def execute_traffic(self, traffic_generator, ixia_obj, mac=None, xfile=None): + if mac is None: + mac = {} if self.first_run: self.full_profile = {} self.pg_id = 0 @@ -121,15 +127,18 @@ class IXIARFC2544Profile(TrexProfile): return str(multiplier) def start_ixia_latency(self, traffic_generator, ixia_obj, - mac={}, xfile=None): + mac=None, xfile=None): + if mac is None: + mac = {} self.update_traffic_profile(traffic_generator) traffic = \ self._get_ixia_traffic_profile(self.full_profile, mac, xfile) - self._ixia_traffic_generate(traffic_generator, traffic, - ixia_obj, xfile) + self._ixia_traffic_generate(traffic_generator, traffic, ixia_obj) def get_drop_percentage(self, traffic_generator, samples, tol_min, - tolerance, ixia_obj, mac={}, xfile=None): + tolerance, ixia_obj, mac=None, xfile=None): + if mac is None: + mac = {} status = 'Running' drop_percent = 100 in_packets = sum([samples[iface]['in_packets'] for iface in samples]) diff --git a/yardstick/network_services/vnf_generic/vnf/base.py b/yardstick/network_services/vnf_generic/vnf/base.py index 42e3d2a48..56c57a94b 100644 --- a/yardstick/network_services/vnf_generic/vnf/base.py +++ b/yardstick/network_services/vnf_generic/vnf/base.py @@ -106,15 +106,18 @@ class VnfdHelper(dict): if int(virtual_intf['dpdk_port_num']) == port: return interface - def port_num(self, name): + def port_num(self, port): # we need interface name -> DPDK port num (PMD ID) -> LINK ID # LINK ID -> PMD ID is governed by the port mask """ :rtype: int - :type name: str + :type port: str """ - intf = self.find_interface(name=name) + if isinstance(port, dict): + intf = port + else: + intf = self.find_interface(name=port) return int(intf["virtual-interface"]["dpdk_port_num"]) def port_nums(self, intfs): diff --git a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py index 557009d30..91530860e 100644 --- a/yardstick/network_services/vnf_generic/vnf/sample_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/sample_vnf.py @@ -282,9 +282,11 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): def setup_vnf_environment(self): self._setup_dpdk() - resource = self._setup_resources() + 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() + resource = self._setup_resources() return resource def kill_vnf(self): @@ -307,10 +309,13 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): if exit_status != 0: self.ssh_helper.execute("bash %s dpdk >/dev/null 2>&1" % dpdk_setup) - def _setup_resources(self): - interfaces = self.vnfd_helper.interfaces - self.bound_pci = [v['virtual-interface']["vpci"] for v in interfaces] + def get_collectd_options(self): + options = self.scenario_helper.all_options.get("collectd", {}) + # override with specific node settings + options.update(self.scenario_helper.options.get("collectd", {})) + return options + def _setup_resources(self): # what is this magic? how do we know which socket is for which port? # what about quad-socket? if any(v[5] == "0" for v in self.bound_pci): @@ -319,8 +324,14 @@ class DpdkVnfSetupEnvHelper(SetupEnvHelper): self.socket = 1 cores = self._validate_cpu_cfg() - return ResourceProfile(self.vnfd_helper.mgmt_interface, - interfaces=self.vnfd_helper.interfaces, cores=cores) + # implicit ordering, presumably by DPDK port num, so pre-sort by port_num + # this won't work because we don't have DPDK port numbers yet + ports = sorted(self.vnfd_helper.interfaces, key=self.vnfd_helper.port_num) + port_names = (intf["name"] for intf in ports) + collectd_options = self.get_collectd_options() + plugins = collectd_options.get("plugins", {}) + return ResourceProfile(self.vnfd_helper.mgmt_interface, port_names=port_names, cores=cores, + plugins=plugins, interval=collectd_options.get("interval")) def _detect_and_bind_drivers(self): interfaces = self.vnfd_helper.interfaces 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 449f22296..22aaf6dfb 100644 --- a/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py +++ b/yardstick/network_services/vnf_generic/vnf/tg_rfc2544_ixia.py @@ -13,6 +13,8 @@ # limitations under the License. from __future__ import absolute_import + +import json import time import os import logging @@ -79,31 +81,31 @@ class IxiaResourceHelper(ClientResourceHelper): latency = stats[0] samples = {} - for interface in self.vnfd_helper.interfaces: + # this is not DPDK port num, but this is whatever number we gave + # when we selected ports and programmed the profile + for port_num in ports: try: - name = interface["name"] - # this is not DPDK port num, but this is whatever number we gave - # when we selected ports and programmed the profile - port = self.vnfd_helper.port_num(name) - if port in ports: - samples[name] = { - "rx_throughput_kps": float(last_result["Rx_Rate_Kbps"][port]), - "tx_throughput_kps": float(last_result["Tx_Rate_Kbps"][port]), - "rx_throughput_mbps": float(last_result["Rx_Rate_Mbps"][port]), - "tx_throughput_mbps": float(last_result["Tx_Rate_Mbps"][port]), - "in_packets": int(last_result["Valid_Frames_Rx"][port]), - "out_packets": int(last_result["Frames_Tx"][port]), - "RxThroughput": int(last_result["Valid_Frames_Rx"][port]) / 30, - "TxThroughput": int(last_result["Frames_Tx"][port]) / 30, - } - if key: - avg_latency = latency["Store-Forward_Avg_latency_ns"][port] - min_latency = latency["Store-Forward_Min_latency_ns"][port] - max_latency = latency["Store-Forward_Max_latency_ns"][port] - samples[name][key] = \ - {"Store-Forward_Avg_latency_ns": avg_latency, - "Store-Forward_Min_latency_ns": min_latency, - "Store-Forward_Max_latency_ns": max_latency} + # reverse lookup port name from port_num so the stats dict is descriptive + intf = self.vnfd_helper.find_interface_by_port(port_num) + port_name = intf["name"] + samples[port_name] = { + "rx_throughput_kps": float(last_result["Rx_Rate_Kbps"][port_num]), + "tx_throughput_kps": float(last_result["Tx_Rate_Kbps"][port_num]), + "rx_throughput_mbps": float(last_result["Rx_Rate_Mbps"][port_num]), + "tx_throughput_mbps": float(last_result["Tx_Rate_Mbps"][port_num]), + "in_packets": int(last_result["Valid_Frames_Rx"][port_num]), + "out_packets": int(last_result["Frames_Tx"][port_num]), + "RxThroughput": int(last_result["Valid_Frames_Rx"][port_num]) / 30, + "TxThroughput": int(last_result["Frames_Tx"][port_num]) / 30, + } + if key: + avg_latency = latency["Store-Forward_Avg_latency_ns"][port_num] + min_latency = latency["Store-Forward_Min_latency_ns"][port_num] + max_latency = latency["Store-Forward_Max_latency_ns"][port_num] + samples[port_name][key] = \ + {"Store-Forward_Avg_latency_ns": avg_latency, + "Store-Forward_Min_latency_ns": min_latency, + "Store-Forward_Max_latency_ns": max_latency} except IndexError: pass @@ -128,19 +130,27 @@ class IxiaResourceHelper(ClientResourceHelper): self.client.ix_assign_ports() + ixia_file = find_relative_file("ixia_traffic.cfg", + self.scenario_helper.scenario_cfg["task_path"]) + + static_traffic = {} + with open(ixia_file) as stream: + try: + static_traffic = json.load(stream) + except Exception: + LOG.exception("") mac = {} - # TODO: shouldn't this index map to port number we used to generate the profile - for index, interface in enumerate(self.vnfd_helper.interfaces, 1): - virt_intf = interface["virtual-interface"] - mac.update({ - "src_mac_{}".format(index): virt_intf.get("local_mac", default), - "dst_mac_{}".format(index): virt_intf.get("dst_mac", default), - }) + for vld_id, traffic in static_traffic.items(): + intfs = self.vnfd_helper.port_pairs.networks.get(vld_id, []) + interface = next(iter(intfs), None) + if interface: + virt_intf = self.vnfd_helper.find_interface(name=interface)["virtual-interface"] + # we only know static traffic id by reading the json + # this is used by _get_ixia_traffic_profile + mac["src_mac_{}".format(traffic["id"])] = virt_intf.get("local_mac", default) + mac["dst_mac_{}".format(traffic["id"])] = virt_intf.get("dst_mac", default) samples = {} - - ixia_file = find_relative_file("ixia_traffic.cfg", - self.scenario_helper.scenario_cfg["task_path"]) # Generate ixia traffic config... try: while not self._terminated.value: @@ -160,25 +170,25 @@ class IxiaResourceHelper(ClientResourceHelper): self.client.ix_stop_traffic() self._queue.put(samples) - except Exception: - LOG.info("Run Traffic terminated") - pass - if not self.rfc_helper.is_done(): - self._terminated.value = 1 - return + if not self.rfc_helper.is_done(): + self._terminated.value = 1 + return + + traffic_profile.execute_traffic(self, self.client, mac, ixia_file) + for _ in range(5): + time.sleep(self.LATENCY_TIME_SLEEP) + self.client.ix_stop_traffic() + samples = self.generate_samples(traffic_profile.ports, 'latency', {}) + self._queue.put(samples) + traffic_profile.start_ixia_latency(self, self.client, mac, ixia_file) + if self._terminated.value: + break - traffic_profile.execute_traffic(self, self.client, mac, ixia_file) - for _ in range(5): - time.sleep(self.LATENCY_TIME_SLEEP) self.client.ix_stop_traffic() - samples = self.generate_samples(traffic_profile.ports, 'latency', {}) - self._queue.put(samples) - traffic_profile.start_ixia_latency(self, self.client, mac, ixia_file) - if self._terminated.value: - break + except Exception: + LOG.exception("Run Traffic terminated") - self.client.ix_stop_traffic() self._terminated.value = 1 def collect_kpi(self): diff --git a/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py b/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py index cd4a008ce..5f1c4d4d3 100644 --- a/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py +++ b/yardstick/network_services/vnf_generic/vnf/vpe_vnf.py @@ -50,13 +50,14 @@ class ConfigCreate(object): config.set(tm_q, 'cfg', '/tmp/full_tm_profile_10G.cfg') return config - def __init__(self, uplink_ports, downlink_ports, socket): + def __init__(self, vnfd_helper, socket): super(ConfigCreate, self).__init__() self.sw_q = -1 self.sink_q = -1 self.n_pipeline = 1 - self.uplink_ports = uplink_ports - self.downlink_ports = downlink_ports + self.vnfd_helper = vnfd_helper + self.uplink_ports = self.vnfd_helper.port_pairs.uplink_ports + self.downlink_ports = self.vnfd_helper.port_pairs.downlink_ports self.pipeline_per_port = 9 self.socket = socket @@ -77,7 +78,7 @@ class ConfigCreate(object): def vpe_rxq(self, config): for port in self.downlink_ports: - new_section = 'RXQ{0}.0'.format(port) + new_section = 'RXQ{0}.0'.format(self.vnfd_helper.port_num(port)) config.add_section(new_section) config.set(new_section, 'mempool', 'MEMPOOL1') @@ -102,7 +103,8 @@ class ConfigCreate(object): for k, v in parser.items(pipeline): if k == "pktq_in": if "RXQ" in v: - value = "RXQ{0}.0".format(self.uplink_ports[index]) + port = self.vnfd_helper.port_num(self.uplink_ports[index]) + value = "RXQ{0}.0".format(port) else: value = self.get_sink_swq(parser, pipeline, k, index) @@ -110,7 +112,8 @@ class ConfigCreate(object): elif k == "pktq_out": if "TXQ" in v: - value = "TXQ{0}.0".format(self.downlink_ports[index]) + port = self.vnfd_helper.port_num(self.downlink_ports[index]) + value = "TXQ{0}.0".format(port) else: self.sw_q += 1 value = self.get_sink_swq(parser, pipeline, k, index) @@ -131,23 +134,25 @@ class ConfigCreate(object): for k, v in parser.items(pipeline): if k == "pktq_in": + port = self.vnfd_helper.port_num(self.downlink_ports[index]) if "RXQ" not in v: value = self.get_sink_swq(parser, pipeline, k, index) elif "TM" in v: - value = "RXQ{0}.0 TM{1}".format(self.downlink_ports[index], index) + value = "RXQ{0}.0 TM{1}".format(port, index) else: - value = "RXQ{0}.0".format(self.downlink_ports[index]) + value = "RXQ{0}.0".format(port) parser.set(pipeline, k, value) if k == "pktq_out": + port = self.vnfd_helper.port_num(self.uplink_ports[index]) if "TXQ" not in v: self.sw_q += 1 value = self.get_sink_swq(parser, pipeline, k, index) elif "TM" in v: - value = "TXQ{0}.0 TM{1}".format(self.uplink_ports[index], index) + value = "TXQ{0}.0 TM{1}".format(port, index) else: - value = "TXQ{0}.0".format(self.uplink_ports[index]) + value = "TXQ{0}.0".format(port) parser.set(pipeline, k, value) @@ -174,14 +179,19 @@ class ConfigCreate(object): def generate_vpe_script(self, interfaces): rules = PipelineRules(pipeline_id=1) - for priv_port, pub_port in zip(self.uplink_ports, self.downlink_ports): - priv_intf = interfaces[priv_port]["virtual-interface"] - pub_intf = interfaces[pub_port]["virtual-interface"] + for uplink_port, downlink_port in zip(self.uplink_ports, self.downlink_ports): - dst_port0_ip = priv_intf["dst_ip"] - dst_port1_ip = pub_intf["dst_ip"] - dst_port0_mac = priv_intf["dst_mac"] - dst_port1_mac = pub_intf["dst_mac"] + uplink_intf = \ + next(intf["virtual-interface"] for intf in interfaces + if intf["name"] == uplink_port) + downlink_intf = \ + next(intf["virtual-interface"] for intf in interfaces + if intf["name"] == downlink_port) + + dst_port0_ip = uplink_intf["dst_ip"] + dst_port1_ip = downlink_intf["dst_ip"] + dst_port0_mac = uplink_intf["dst_mac"] + dst_port1_mac = downlink_intf["dst_mac"] rules.add_firewall_script(dst_port0_ip) rules.next_pipeline() @@ -226,8 +236,7 @@ class VpeApproxSetupEnvHelper(DpdkVnfSetupEnvHelper): } self._build_vnf_ports() - vpe_conf = ConfigCreate(self.vnfd_helper.port_pairs.uplink_ports, - self.vnfd_helper.port_pairs.downlink_ports, self.socket) + vpe_conf = ConfigCreate(self.vnfd_helper, self.socket) vpe_conf.create_vpe_config(self.scenario_helper.vnf_cfg) config_basename = posixpath.basename(self.CFG_CONFIG) diff --git a/yardstick/orchestrator/kubernetes.py b/yardstick/orchestrator/kubernetes.py index 3d4548527..198eeac6d 100644 --- a/yardstick/orchestrator/kubernetes.py +++ b/yardstick/orchestrator/kubernetes.py @@ -38,7 +38,7 @@ class KubernetesObject(object): "template": { "metadata": { "labels": { - "app": "" + "app": name } }, "spec": { @@ -114,6 +114,35 @@ class KubernetesObject(object): self._add_volume(key_volume) +class ServiceObject(object): + + def __init__(self, name): + self.name = '{}-service'.format(name) + self.template = { + 'metadata': { + 'name': '{}-service'.format(name) + }, + 'spec': { + 'type': 'NodePort', + 'ports': [ + { + 'port': 22, + 'protocol': 'TCP' + } + ], + 'selector': { + 'app': name + } + } + } + + def create(self): + k8s_utils.create_service(self.template) + + def delete(self): + k8s_utils.delete_service(self.name) + + class KubernetesTemplate(object): def __init__(self, name, template_cfg): @@ -125,6 +154,8 @@ class KubernetesTemplate(object): ssh_key=self.ssh_key, **cfg) for rc, cfg in template_cfg.items()] + self.service_objs = [ServiceObject(s) for s in self.rcs] + self.pods = [] def _get_rc_name(self, rc_name): |