aboutsummaryrefslogtreecommitdiffstats
path: root/yardstick/benchmark/contexts/standalone
diff options
context:
space:
mode:
authorDeepak S <deepak.s@linux.intel.com>2017-09-19 01:37:02 -0700
committerDeepak S <deepak.s@linux.intel.com>2017-09-29 16:03:13 +0000
commit88da842a5d5f02fa45c0f6787ac249978a3cb8d1 (patch)
tree18399e165a9f9e7cc94d3dd5ff7d710fd4201cc3 /yardstick/benchmark/contexts/standalone
parent9cefa935afa908cede3ab1bd8b6ca91f1e36ad2d (diff)
Enabling multi_VM & multi port launch in standalone context
new context names: - SRIOV - StandaloneSriov - OvsDpdk - StandaloneOvsDpdk - Seperate helper, libvirt, server info class - Allow multi-port and multi-VM support. Change-Id: I3c65e4535082fa0e2f4c6ee11c3bca9ccfdc01b8 Signed-off-by: Deepak S <deepak.s@linux.intel.com> Signed-off-by: Martin Banszel <martinx.banszel@intel.com>
Diffstat (limited to 'yardstick/benchmark/contexts/standalone')
-rw-r--r--yardstick/benchmark/contexts/standalone/__init__.py211
-rw-r--r--yardstick/benchmark/contexts/standalone/model.py493
-rw-r--r--yardstick/benchmark/contexts/standalone/ovs_dpdk.py383
-rw-r--r--yardstick/benchmark/contexts/standalone/ovsdpdk.py369
-rw-r--r--yardstick/benchmark/contexts/standalone/sriov.py624
5 files changed, 1103 insertions, 977 deletions
diff --git a/yardstick/benchmark/contexts/standalone/__init__.py b/yardstick/benchmark/contexts/standalone/__init__.py
index f0ef1d560..e69de29bb 100644
--- a/yardstick/benchmark/contexts/standalone/__init__.py
+++ b/yardstick/benchmark/contexts/standalone/__init__.py
@@ -1,211 +0,0 @@
-# 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.
-"""This module handle non managed standalone virtualization node."""
-
-from __future__ import absolute_import
-import logging
-import os
-import errno
-import collections
-import time
-
-from yardstick.benchmark.contexts.base import Context
-from yardstick.common.constants import YARDSTICK_ROOT_PATH
-from yardstick.common.utils import import_modules_from_package, itersubclasses
-from yardstick.common.yaml_loader import yaml_load
-
-LOG = logging.getLogger(__name__)
-
-
-class StandaloneContext(Context):
- """ This class handles standalone nodes - VM running on Non-Managed NFVi
- Configuration: vswitch, ovs, ovs-dpdk, sr-iov, linuxbridge
- """
-
- __context_type__ = "Standalone"
-
- def __init__(self):
- self.name = None
- self.file_path = None
- self.nodes = []
- self.networks = {}
- self.nfvi_node = []
- self.nfvi_obj = None
- self.attrs = {}
- super(StandaloneContext, self).__init__()
-
- def read_config_file(self):
- """Read from config file"""
-
- with open(self.file_path) as stream:
- LOG.info("Parsing pod file: %s", self.file_path)
- cfg = yaml_load(stream)
- return cfg
-
- def get_nfvi_obj(self):
- print("{0}".format(self.nfvi_node[0]['role']))
- context_type = self.get_context_impl(self.nfvi_node[0]['role'])
- nfvi_obj = context_type()
- nfvi_obj.__init__()
- nfvi_obj.parse_pod_and_get_data(self.file_path)
- return nfvi_obj
-
- def init(self, attrs):
- """initializes itself from the supplied arguments"""
-
- self.name = attrs["name"]
- self.file_path = file_path = attrs.get("file", "pod.yaml")
-
- try:
- cfg = self.read_config_file()
- except IOError as io_error:
- if io_error.errno != errno.ENOENT:
- raise
- self.file_path = os.path.join(YARDSTICK_ROOT_PATH, file_path)
- cfg = self.read_config_file()
-
- self.vm_deploy = attrs.get("vm_deploy", True)
- self.nodes.extend([node for node in cfg["nodes"]
- if str(node["role"]) != "Sriov" and
- str(node["role"]) != "Ovsdpdk"])
- for node in cfg["nodes"]:
- if str(node["role"]) == "Sriov":
- self.nfvi_node.extend([node for node in cfg["nodes"]
- if str(node["role"]) == "Sriov"])
- if str(node["role"]) == "Ovsdpdk":
- self.nfvi_node.extend([node for node in cfg["nodes"]
- if str(node["role"]) == "Ovsdpdk"])
- LOG.info("{0}".format(node["role"]))
- else:
- LOG.debug("Node role is other than SRIOV and OVS")
- self.nfvi_obj = self.get_nfvi_obj()
- self.attrs = attrs
- # add optional static network definition
- self.networks.update(cfg.get("networks", {}))
- self.nfvi_obj = self.get_nfvi_obj()
- LOG.debug("Nodes: %r", self.nodes)
- LOG.debug("NFVi Node: %r", self.nfvi_node)
- LOG.debug("Networks: %r", self.networks)
-
- def deploy(self):
- """don't need to deploy"""
-
- # Todo: NFVi deploy (sriov, vswitch, ovs etc) based on the config.
- if not self.vm_deploy:
- return
-
- # Todo: NFVi deploy (sriov, vswitch, ovs etc) based on the config.
- self.nfvi_obj.ssh_remote_machine()
- if self.nfvi_obj.first_run is True:
- self.nfvi_obj.install_req_libs()
-
- nic_details = self.nfvi_obj.get_nic_details()
- print("{0}".format(nic_details))
-
- if self.nfvi_node[0]["role"] == "Sriov":
- self.nfvi_obj.setup_sriov_context(
- self.nfvi_obj.sriov[0]['phy_ports'],
- nic_details,
- self.nfvi_obj.sriov[0]['phy_driver'])
- if self.nfvi_node[0]["role"] == "Ovsdpdk":
- self.nfvi_obj.setup_ovs(self.nfvi_obj.ovs[0]["phy_ports"])
- self.nfvi_obj.start_ovs_serverswitch()
- time.sleep(5)
- self.nfvi_obj.setup_ovs_bridge()
- self.nfvi_obj.add_oflows()
- self.nfvi_obj.setup_ovs_context(
- self.nfvi_obj.ovs[0]['phy_ports'],
- nic_details,
- self.nfvi_obj.ovs[0]['phy_driver'])
- pass
-
- def undeploy(self):
- """don't need to undeploy"""
-
- if not self.vm_deploy:
- return
- # Todo: NFVi undeploy (sriov, vswitch, ovs etc) based on the config.
- # self.nfvi_obj = self.get_nfvi_obj()
- self.nfvi_obj.ssh_remote_machine()
- self.nfvi_obj.destroy_vm()
- pass
-
- def _get_server(self, attr_name):
- """lookup server info by name from context
-
- Keyword arguments:
- attr_name -- A name for a server listed in nodes config file
- """
- node_name, name = self.split_name(attr_name)
- if name is None or self.name != name:
- return None
-
- matching_nodes = (n for n in self.nodes if n["name"] == node_name)
- try:
- # A clone is created in order to avoid affecting the
- # original one.
- node = dict(next(matching_nodes))
- except StopIteration:
- return None
-
- try:
- duplicate = next(matching_nodes)
- except StopIteration:
- pass
- else:
- raise ValueError("Duplicate nodes!!! Nodes: %s %s",
- (node, duplicate))
-
- node["name"] = attr_name
- return node
-
- def _get_network(self, attr_name):
- if not isinstance(attr_name, collections.Mapping):
- network = self.networks.get(attr_name)
-
- else:
- # Don't generalize too much Just support vld_id
- vld_id = attr_name.get('vld_id', {})
- # for standalone context networks are dicts
- iter1 = (n for n in self.networks.values() if n.get('vld_id') == vld_id)
- network = next(iter1, None)
-
- if network is None:
- return None
-
- result = {
- # name is required
- "name": network["name"],
- "vld_id": network.get("vld_id"),
- "segmentation_id": network.get("segmentation_id"),
- "network_type": network.get("network_type"),
- "physical_network": network.get("physical_network"),
- }
- return result
-
- def get_context_impl(self, nfvi_type):
- """ Find the implementing class from vnf_model["vnf"]["name"] field
-
- :param vnf_model: dictionary containing a parsed vnfd
- :return: subclass of GenericVNF
- """
- import_modules_from_package(
- "yardstick.benchmark.contexts")
- expected_name = nfvi_type
- impl = [c for c in itersubclasses(StandaloneContext)
- if c.__name__ == expected_name]
- try:
- return next(iter(impl))
- except StopIteration:
- raise ValueError("No implementation for %s", expected_name)
diff --git a/yardstick/benchmark/contexts/standalone/model.py b/yardstick/benchmark/contexts/standalone/model.py
new file mode 100644
index 000000000..4491660e0
--- /dev/null
+++ b/yardstick/benchmark/contexts/standalone/model.py
@@ -0,0 +1,493 @@
+# 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.
+
+from __future__ import absolute_import
+import os
+import re
+import time
+import glob
+import uuid
+import random
+import logging
+import itertools
+import errno
+
+from netaddr import IPNetwork
+import xml.etree.ElementTree as ET
+
+from yardstick import ssh
+from yardstick.common.constants import YARDSTICK_ROOT_PATH
+from yardstick.common.yaml_loader import yaml_load
+from yardstick.network_services.utils import PciAddress
+from yardstick.common.utils import write_file
+
+LOG = logging.getLogger(__name__)
+
+VM_TEMPLATE = """
+<domain type="kvm">
+ <name>{vm_name}</name>
+ <uuid>{random_uuid}</uuid>
+ <memory unit="MB">{memory}</memory>
+ <currentMemory unit="MB">{memory}</currentMemory>
+ <memoryBacking>
+ <hugepages />
+ </memoryBacking>
+ <vcpu placement="static">{vcpu}</vcpu>
+ <os>
+ <type arch="x86_64" machine="pc-i440fx-utopic">hvm</type>
+ <boot dev="hd" />
+ </os>
+ <features>
+ <acpi />
+ <apic />
+ <pae />
+ </features>
+ <cpu mode='host-passthrough'>
+ <topology cores="{cpu}" sockets="{socket}" threads="{threads}" />
+ <numa>
+ <cell id='0' cpus='{numa_cpus}' memory='{memory}' unit='MB' memAccess='shared'/>
+ </numa>
+ </cpu>
+ <clock offset="utc">
+ <timer name="rtc" tickpolicy="catchup" />
+ <timer name="pit" tickpolicy="delay" />
+ <timer name="hpet" present="no" />
+ </clock>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>restart</on_crash>
+ <devices>
+ <emulator>/usr/bin/kvm-spice</emulator>
+ <disk device="disk" type="file">
+ <driver name="qemu" type="qcow2" />
+ <source file="{vm_image}"/>
+ <target bus="virtio" dev="vda" />
+ </disk>
+ <graphics autoport="yes" listen="0.0.0.0" port="-1" type="vnc" />
+ <interface type="bridge">
+ <mac address='{mac_addr}'/>
+ <source bridge="br-int" />
+ <model type='virtio'/>
+ </interface>
+ </devices>
+</domain>
+"""
+WAIT_FOR_BOOT = 30
+
+
+class Libvirt(object):
+ """ This class handles all the libvirt updates to lauch VM
+ """
+
+ @staticmethod
+ def check_if_vm_exists_and_delete(vm_name, connection):
+ cmd_template = "virsh list --name | grep -i %s"
+ status = connection.execute(cmd_template % vm_name)[0]
+ if status == 0:
+ LOG.info("VM '%s' is already present.. destroying" % vm_name)
+ connection.execute("virsh destroy %s" % vm_name)
+
+ @staticmethod
+ def virsh_create_vm(connection, cfg):
+ err = connection.execute("virsh create %s" % cfg)[0]
+ LOG.info("VM create status: %s" % (err))
+
+ @staticmethod
+ def virsh_destroy_vm(vm_name, connection):
+ connection.execute("virsh destroy %s" % vm_name)
+
+ @staticmethod
+ def add_interface_address(interface, pci_address):
+ vm_pci = ET.SubElement(interface, 'address')
+ vm_pci.set('type', 'pci')
+ vm_pci.set('domain', '0x%s' % pci_address.domain)
+ vm_pci.set('bus', '0x%s' % pci_address.bus)
+ vm_pci.set('slot', '0x%s' % pci_address.slot)
+ vm_pci.set('function', '0x%s' % pci_address.function)
+ return vm_pci
+
+ @classmethod
+ def add_ovs_interface(cls, vpath, port_num, vpci, vports_mac, xml):
+ vhost_path = '{0}/var/run/openvswitch/dpdkvhostuser{1}'
+ root = ET.parse(xml)
+ pci_address = PciAddress.parse_address(vpci.strip(), multi_line=True)
+ device = root.find('devices')
+
+ interface = ET.SubElement(device, 'interface')
+ interface.set('type', 'vhostuser')
+ mac = ET.SubElement(interface, 'mac')
+ mac.set('address', vports_mac)
+
+ source = ET.SubElement(interface, 'source')
+ source.set('type', 'unix')
+ source.set('path', vhost_path.format(vpath, port_num))
+ source.set('mode', 'client')
+
+ model = ET.SubElement(interface, 'model')
+ model.set('type', 'virtio')
+
+ driver = ET.SubElement(interface, 'driver')
+ driver.set('queues', '4')
+
+ host = ET.SubElement(driver, 'host')
+ host.set('mrg_rxbuf', 'off')
+
+ cls.add_interface_address(interface, pci_address)
+
+ root.write(xml)
+
+ @classmethod
+ def add_sriov_interfaces(cls, vm_pci, vf_pci, vfmac, xml):
+ root = ET.parse(xml)
+ pci_address = PciAddress.parse_address(vf_pci.strip(), multi_line=True)
+ device = root.find('devices')
+
+ interface = ET.SubElement(device, 'interface')
+ interface.set('managed', 'yes')
+ interface.set('type', 'hostdev')
+
+ mac = ET.SubElement(interface, 'mac')
+ mac.set('address', vfmac)
+ source = ET.SubElement(interface, 'source')
+
+ addr = ET.SubElement(source, "address")
+ addr.set('domain', "0x0")
+ addr.set('bus', "{0}".format(pci_address.bus))
+ addr.set('function', "{0}".format(pci_address.function))
+ addr.set('slot', "0x{0}".format(pci_address.slot))
+ addr.set('type', "pci")
+
+ pci_vm_address = PciAddress.parse_address(vm_pci.strip(), multi_line=True)
+ cls.add_interface_address(interface, pci_vm_address)
+
+ root.write(xml)
+
+ @staticmethod
+ def create_snapshot_qemu(connection, index, vm_image):
+ # build snapshot image
+ image = "/var/lib/libvirt/images/%s.qcow2" % index
+ connection.execute("rm %s" % image)
+ qemu_template = "qemu-img create -f qcow2 -o backing_file=%s %s"
+ connection.execute(qemu_template % (vm_image, image))
+
+ return image
+
+ @classmethod
+ def build_vm_xml(cls, connection, flavor, cfg, vm_name, index):
+ memory = flavor.get('ram', '4096')
+ extra_spec = flavor.get('extra_specs', {})
+ cpu = extra_spec.get('hw:cpu_cores', '2')
+ socket = extra_spec.get('hw:cpu_sockets', '1')
+ threads = extra_spec.get('hw:cpu_threads', '2')
+ vcpu = int(cpu) * int(threads)
+ numa_cpus = '0-%s' % (vcpu - 1)
+
+ mac = StandaloneContextHelper.get_mac_address(0x00)
+ image = cls.create_snapshot_qemu(connection, index,
+ flavor.get("images", None))
+ vm_xml = VM_TEMPLATE.format(
+ vm_name=vm_name,
+ random_uuid=uuid.uuid4(),
+ mac_addr=mac,
+ memory=memory, vcpu=vcpu, cpu=cpu,
+ numa_cpus=numa_cpus,
+ socket=socket, threads=threads,
+ vm_image=image)
+
+ write_file(cfg, vm_xml)
+
+ return [vcpu, mac]
+
+ @staticmethod
+ def split_cpu_list(cpu_list):
+ if not cpu_list:
+ return []
+
+ ranges = cpu_list.split(',')
+ bounds = ([int(b) for b in r.split('-')] for r in ranges)
+ range_objects = \
+ (range(bound[0], bound[1] + 1 if len(bound) == 2
+ else bound[0] + 1) for bound in bounds)
+
+ return sorted(itertools.chain.from_iterable(range_objects))
+
+ @classmethod
+ def get_numa_nodes(cls):
+ nodes_sysfs = glob.iglob("/sys/devices/system/node/node*")
+ nodes = {}
+ for node_sysfs in nodes_sysfs:
+ num = os.path.basename(node_sysfs).replace("node", "")
+ with open(os.path.join(node_sysfs, "cpulist")) as cpulist_file:
+ cpulist = cpulist_file.read().strip()
+ nodes[num] = cls.split_cpu_list(cpulist)
+ LOG.info("nodes: {0}".format(nodes))
+ return nodes
+
+ @staticmethod
+ def update_interrupts_hugepages_perf(connection):
+ connection.execute("echo 1 > /sys/module/kvm/parameters/allow_unsafe_assigned_interrupts")
+ connection.execute("echo never > /sys/kernel/mm/transparent_hugepage/enabled")
+
+ @classmethod
+ def pin_vcpu_for_perf(cls, connection, vm_name, cpu):
+ nodes = cls.get_numa_nodes()
+ num_nodes = len(nodes)
+ vcpi_pin_template = "virsh vcpupin {0} {1} {2}"
+ for i in range(0, int(cpu)):
+ core = nodes[str(num_nodes - 1)][i % len(nodes[str(num_nodes - 1)])]
+ connection.execute(vcpi_pin_template.format(vm_name, i, core))
+ cls.update_interrupts_hugepages_perf(connection)
+
+
+class StandaloneContextHelper(object):
+ """ This class handles all the common code for standalone
+ """
+ def __init__(self):
+ self.file_path = None
+ super(StandaloneContextHelper, self).__init__()
+
+ @staticmethod
+ def install_req_libs(connection, extra_pkgs=[]):
+ pkgs = ["qemu-kvm", "libvirt-bin", "bridge-utils", "numactl", "fping"]
+ pkgs.extend(extra_pkgs)
+ cmd_template = "dpkg-query -W --showformat='${Status}\\n' \"%s\"|grep 'ok installed'"
+ for pkg in pkgs:
+ if connection.execute(cmd_template % pkg)[0]:
+ connection.execute("apt-get update")
+ connection.execute("apt-get -y install %s" % pkg)
+ else:
+ # all installed
+ return
+
+ @staticmethod
+ def get_kernel_module(connection, pci, driver):
+ if not driver:
+ out = connection.execute("lspci -k -s %s" % pci)[1]
+ driver = out.split("Kernel modules:").pop().strip()
+ return driver
+
+ @classmethod
+ def get_nic_details(cls, connection, networks, dpdk_nic_bind):
+ for key, ports in networks.items():
+ if key == "mgmt":
+ continue
+
+ phy_ports = ports['phy_port']
+ phy_driver = ports.get('phy_driver', None)
+ driver = cls.get_kernel_module(connection, phy_ports, phy_driver)
+
+ # Make sure that ports are bound to kernel drivers e.g. i40e/ixgbe
+ bind_cmd = "{dpdk_nic_bind} --force -b {driver} {port}"
+ lshw_cmd = "lshw -c network -businfo | grep '{port}'"
+ link_show_cmd = "ip -s link show {interface}"
+
+ cmd = bind_cmd.format(dpdk_nic_bind=dpdk_nic_bind,
+ driver=driver, port=ports['phy_port'])
+ connection.execute(cmd)
+
+ out = connection.execute(lshw_cmd.format(port=phy_ports))[1]
+ interface = out.split()[1]
+
+ connection.execute(link_show_cmd.format(interface=interface))
+
+ ports.update({
+ 'interface': str(interface),
+ 'driver': driver
+ })
+ LOG.info("{0}".format(networks))
+
+ return networks
+
+ @staticmethod
+ def get_virtual_devices(connection, pci):
+ cmd = "cat /sys/bus/pci/devices/{0}/virtfn0/uevent"
+ output = connection.execute(cmd.format(pci))[1]
+
+ pattern = "PCI_SLOT_NAME=({})".format(PciAddress.PCI_PATTERN_STR)
+ m = re.search(pattern, output, re.MULTILINE)
+
+ pf_vfs = {}
+ if m:
+ pf_vfs = {pci: m.group(1).rstrip()}
+
+ LOG.info("pf_vfs:\n%s", pf_vfs)
+
+ return pf_vfs
+
+ def read_config_file(self):
+ """Read from config file"""
+
+ with open(self.file_path) as stream:
+ LOG.info("Parsing pod file: %s", self.file_path)
+ cfg = yaml_load(stream)
+ return cfg
+
+ def parse_pod_file(self, file_path, nfvi_role='Sriov'):
+ self.file_path = file_path
+ nodes = []
+ nfvi_host = []
+ try:
+ cfg = self.read_config_file()
+ except IOError as io_error:
+ if io_error.errno != errno.ENOENT:
+ raise
+ self.file_path = os.path.join(YARDSTICK_ROOT_PATH, file_path)
+ cfg = self.read_config_file()
+
+ nodes.extend([node for node in cfg["nodes"] if str(node["role"]) != nfvi_role])
+ nfvi_host.extend([node for node in cfg["nodes"] if str(node["role"]) == nfvi_role])
+ if not nfvi_host:
+ raise("Node role is other than SRIOV")
+
+ host_mgmt = {'user': nfvi_host[0]['user'],
+ 'ip': str(IPNetwork(nfvi_host[0]['ip']).ip),
+ 'password': nfvi_host[0]['password'],
+ 'ssh_port': nfvi_host[0].get('ssh_port', 22),
+ 'key_filename': nfvi_host[0].get('key_filename')}
+
+ return [nodes, nfvi_host, host_mgmt]
+
+ @staticmethod
+ def get_mac_address(end=0x7f):
+ mac = [0x52, 0x54, 0x00,
+ random.randint(0x00, end),
+ random.randint(0x00, 0xff),
+ random.randint(0x00, 0xff)]
+ mac_address = ':'.join('%02x' % x for x in mac)
+ return mac_address
+
+ @staticmethod
+ def get_mgmt_ip(connection, mac, cidr, node):
+ mgmtip = None
+ times = 10
+ while not mgmtip and times:
+ connection.execute("fping -c 1 -g %s > /dev/null 2>&1" % cidr)
+ out = connection.execute("ip neighbor | grep '%s'" % mac)[1]
+ LOG.info("fping -c 1 -g %s > /dev/null 2>&1" % cidr)
+ if out.strip():
+ mgmtip = str(out.split(" ")[0]).strip()
+ client = ssh.SSH.from_node(node, overrides={"ip": mgmtip})
+ client.wait()
+ break
+
+ time.sleep(WAIT_FOR_BOOT) # FixMe: How to find if VM is booted?
+ times = times - 1
+ return mgmtip
+
+ @classmethod
+ def wait_for_vnfs_to_start(cls, connection, servers, nodes):
+ for node in nodes:
+ vnf = servers[node["name"]]
+ mgmtip = vnf["network_ports"]["mgmt"]["cidr"]
+ ip = cls.get_mgmt_ip(connection, node["mac"], mgmtip, node)
+ if ip:
+ node["ip"] = ip
+ return nodes
+
+
+class Server(object):
+ """ This class handles geting vnf nodes
+ """
+
+ @staticmethod
+ def build_vnf_interfaces(vnf, ports):
+ interfaces = {}
+ index = 0
+
+ for key, vfs in vnf["network_ports"].items():
+ if key == "mgmt":
+ mgmtip = str(IPNetwork(vfs['cidr']).ip)
+ continue
+
+ vf = ports[vfs[0]]
+ ip = IPNetwork(vf['cidr'])
+ interfaces.update({
+ key: {
+ 'vpci': vf['vpci'],
+ 'driver': "%svf" % vf['driver'],
+ 'local_mac': vf['mac'],
+ 'dpdk_port_num': index,
+ 'local_ip': str(ip.ip),
+ 'netmask': str(ip.netmask)
+ },
+ })
+ index = index + 1
+
+ return mgmtip, interfaces
+
+ @classmethod
+ def generate_vnf_instance(cls, flavor, ports, ip, key, vnf, mac):
+ mgmtip, interfaces = cls.build_vnf_interfaces(vnf, ports)
+
+ result = {
+ "ip": mgmtip,
+ "mac": mac,
+ "host": ip,
+ "user": flavor.get('user', 'root'),
+ "interfaces": interfaces,
+ "routing_table": [],
+ # empty IPv6 routing table
+ "nd_route_tbl": [],
+ "name": key, "role": key
+ }
+
+ try:
+ result['key_filename'] = flavor['key_filename']
+ except KeyError:
+ pass
+
+ try:
+ result['password'] = flavor['password']
+ except KeyError:
+ pass
+ LOG.info(result)
+ return result
+
+
+class OvsDeploy(object):
+ """ This class handles deploy of ovs dpdk
+ Configuration: ovs_dpdk
+ """
+
+ OVS_DEPLOY_SCRIPT = "ovs_deploy.bash"
+
+ def __init__(self, connection, bin_path, ovs_properties):
+ self.connection = connection
+ self.bin_path = bin_path
+ self.ovs_properties = ovs_properties
+
+ def prerequisite(self):
+ pkgs = ["git", "build-essential", "pkg-config", "automake",
+ "autotools-dev", "libltdl-dev", "cmake", "libnuma-dev",
+ "libpcap-dev"]
+ StandaloneContextHelper.install_req_libs(self.connection, pkgs)
+
+ def ovs_deploy(self):
+ ovs_deploy = os.path.join(YARDSTICK_ROOT_PATH,
+ "yardstick/resources/scripts/install/",
+ self.OVS_DEPLOY_SCRIPT)
+ if os.path.isfile(ovs_deploy):
+ self.prerequisite()
+ remote_ovs_deploy = os.path.join(self.bin_path, self.OVS_DEPLOY_SCRIPT)
+ LOG.info(remote_ovs_deploy)
+ self.connection.put(ovs_deploy, remote_ovs_deploy)
+
+ http_proxy = os.environ.get('http_proxy', '')
+ ovs_details = self.ovs_properties.get("version", {})
+ ovs = ovs_details.get("ovs", "2.6.0")
+ dpdk = ovs_details.get("dpdk", "16.11.1")
+
+ cmd = "sudo -E %s --ovs='%s' --dpdk='%s' -p='%s'" % (remote_ovs_deploy,
+ ovs, dpdk, http_proxy)
+ self.connection.execute(cmd)
diff --git a/yardstick/benchmark/contexts/standalone/ovs_dpdk.py b/yardstick/benchmark/contexts/standalone/ovs_dpdk.py
new file mode 100644
index 000000000..833c3fb80
--- /dev/null
+++ b/yardstick/benchmark/contexts/standalone/ovs_dpdk.py
@@ -0,0 +1,383 @@
+# 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.
+
+from __future__ import absolute_import
+import os
+import logging
+import collections
+import time
+
+from collections import OrderedDict
+
+from yardstick import ssh
+from yardstick.network_services.utils import get_nsb_option
+from yardstick.network_services.utils import provision_tool
+from yardstick.benchmark.contexts.base import Context
+from yardstick.benchmark.contexts.standalone.model import Libvirt
+from yardstick.benchmark.contexts.standalone.model import StandaloneContextHelper
+from yardstick.benchmark.contexts.standalone.model import Server
+from yardstick.benchmark.contexts.standalone.model import OvsDeploy
+from yardstick.network_services.utils import PciAddress
+
+LOG = logging.getLogger(__name__)
+
+
+class OvsDpdkContext(Context):
+ """ This class handles OVS standalone nodes - VM running on Non-Managed NFVi
+ Configuration: ovs_dpdk
+ """
+
+ __context_type__ = "StandaloneOvsDpdk"
+
+ SUPPORTED_OVS_TO_DPDK_MAP = {
+ '2.6.0': '16.07.1',
+ '2.6.1': '16.07.2',
+ '2.7.0': '16.11.1',
+ '2.7.1': '16.11.2',
+ '2.7.2': '16.11.3',
+ '2.8.0': '17.05.2'
+ }
+
+ DEFAULT_OVS = '2.6.0'
+
+ PKILL_TEMPLATE = "pkill %s %s"
+
+ def __init__(self):
+ self.file_path = None
+ self.sriov = []
+ self.first_run = True
+ self.dpdk_nic_bind = ""
+ self.vm_names = []
+ self.name = None
+ self.nfvi_host = []
+ self.nodes = []
+ self.networks = {}
+ self.attrs = {}
+ self.vm_flavor = None
+ self.servers = None
+ self.helper = StandaloneContextHelper()
+ self.vnf_node = Server()
+ self.ovs_properties = {}
+ self.wait_for_vswitchd = 10
+ super(OvsDpdkContext, self).__init__()
+
+ def init(self, attrs):
+ """initializes itself from the supplied arguments"""
+
+ self.name = attrs["name"]
+ self.file_path = attrs.get("file", "pod.yaml")
+
+ self.nodes, self.nfvi_host, self.host_mgmt = \
+ self.helper.parse_pod_file(self.file_path, 'OvsDpdk')
+
+ self.attrs = attrs
+ self.vm_flavor = attrs.get('flavor', {})
+ self.servers = attrs.get('servers', {})
+ self.vm_deploy = attrs.get("vm_deploy", True)
+ self.ovs_properties = attrs.get('ovs_properties', {})
+ # add optional static network definition
+ self.networks = attrs.get("networks", {})
+
+ LOG.debug("Nodes: %r", self.nodes)
+ LOG.debug("NFVi Node: %r", self.nfvi_host)
+ LOG.debug("Networks: %r", self.networks)
+
+ def setup_ovs(self):
+ vpath = self.ovs_properties.get("vpath", "/usr/local")
+ xargs_kill_cmd = self.PKILL_TEMPLATE % ('-9', 'ovs')
+
+ create_from = os.path.join(vpath, 'etc/openvswitch/conf.db')
+ create_to = os.path.join(vpath, 'share/openvswitch/vswitch.ovsschema')
+
+ cmd_list = [
+ "chmod 0666 /dev/vfio/*",
+ "chmod a+x /dev/vfio",
+ "pkill -9 ovs",
+ xargs_kill_cmd,
+ "killall -r 'ovs*'",
+ "mkdir -p {0}/etc/openvswitch".format(vpath),
+ "mkdir -p {0}/var/run/openvswitch".format(vpath),
+ "rm {0}/etc/openvswitch/conf.db".format(vpath),
+ "ovsdb-tool create {0} {1}".format(create_from, create_to),
+ "modprobe vfio-pci",
+ "chmod a+x /dev/vfio",
+ "chmod 0666 /dev/vfio/*",
+ ]
+ for cmd in cmd_list:
+ self.connection.execute(cmd)
+ bind_cmd = "{dpdk_nic_bind} --force -b {driver} {port}"
+ phy_driver = "vfio-pci"
+ for key, port in self.networks.items():
+ vpci = port.get("phy_port")
+ self.connection.execute(bind_cmd.format(dpdk_nic_bind=self.dpdk_nic_bind,
+ driver=phy_driver, port=vpci))
+
+ def start_ovs_serverswitch(self):
+ vpath = self.ovs_properties.get("vpath")
+ pmd_nums = int(self.ovs_properties.get("pmd_threads", 2))
+ ovs_sock_path = '/var/run/openvswitch/db.sock'
+ log_path = '/var/log/openvswitch/ovs-vswitchd.log'
+
+ pmd_mask = hex(sum(2 ** num for num in range(pmd_nums)) << 1)
+ socket0 = self.ovs_properties.get("ram", {}).get("socket_0", "2048")
+ socket1 = self.ovs_properties.get("ram", {}).get("socket_1", "2048")
+
+ ovs_other_config = "ovs-vsctl {0}set Open_vSwitch . other_config:{1}"
+ detach_cmd = "ovs-vswitchd unix:{0}{1} --pidfile --detach --log-file={2}"
+
+ cmd_list = [
+ "mkdir -p /usr/local/var/run/openvswitch",
+ "ovsdb-server --remote=punix:/{0}/{1} --pidfile --detach".format(vpath,
+ ovs_sock_path),
+ ovs_other_config.format("--no-wait ", "dpdk-init=true"),
+ ovs_other_config.format("--no-wait ", "dpdk-socket-mem='%s,%s'" % (socket0, socket1)),
+ detach_cmd.format(vpath, ovs_sock_path, log_path),
+ ovs_other_config.format("", "pmd-cpu-mask=%s" % pmd_mask),
+ ]
+
+ for cmd in cmd_list:
+ LOG.info(cmd)
+ self.connection.execute(cmd)
+ time.sleep(self.wait_for_vswitchd)
+
+ def setup_ovs_bridge_add_flows(self):
+ dpdk_args = ""
+ dpdk_list = []
+ vpath = self.ovs_properties.get("vpath", "/usr/local")
+ version = self.ovs_properties.get('version', {})
+ ovs_ver = [int(x) for x in version.get('ovs', self.DEFAULT_OVS).split('.')]
+ ovs_add_port = \
+ "ovs-vsctl add-port {br} {port} -- set Interface {port} type={type_}{dpdk_args}"
+ ovs_add_queue = "ovs-vsctl set Interface {port} options:n_rxq={queue}"
+ chmod_vpath = "chmod 0777 {0}/var/run/openvswitch/dpdkvhostuser*"
+
+ cmd_dpdk_list = [
+ "ovs-vsctl del-br br0",
+ "rm -rf /usr/local/var/run/openvswitch/dpdkvhostuser*",
+ "ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev",
+ ]
+
+ ordered_network = OrderedDict(self.networks)
+ for index, (key, vnf) in enumerate(ordered_network.items()):
+ if ovs_ver >= [2, 7, 0]:
+ dpdk_args = " options:dpdk-devargs=%s" % vnf.get("phy_port")
+ dpdk_list.append(ovs_add_port.format(br='br0', port='dpdk%s' % vnf.get("port_num", 0),
+ type_='dpdk', dpdk_args=dpdk_args))
+ dpdk_list.append(ovs_add_queue.format(port='dpdk%s' % vnf.get("port_num", 0),
+ queue=self.ovs_properties.get("queues", 4)))
+
+ # Sorting the array to make sure we execute dpdk0... in the order
+ list.sort(dpdk_list)
+ cmd_dpdk_list.extend(dpdk_list)
+
+ # Need to do two for loop to maintain the dpdk/vhost ports.
+ for index, _ in enumerate(ordered_network):
+ cmd_dpdk_list.append(ovs_add_port.format(br='br0', port='dpdkvhostuser%s' % index,
+ type_='dpdkvhostuser', dpdk_args=""))
+
+ for cmd in cmd_dpdk_list:
+ LOG.info(cmd)
+ self.connection.execute(cmd)
+
+ # Fixme: add flows code
+ ovs_flow = "ovs-ofctl add-flow br0 in_port=%s,action=output:%s"
+
+ network_count = len(ordered_network) + 1
+ for in_port, out_port in zip(range(1, network_count),
+ range(network_count, network_count * 2)):
+ self.connection.execute(ovs_flow % (in_port, out_port))
+ self.connection.execute(ovs_flow % (out_port, in_port))
+
+ self.connection.execute(chmod_vpath.format(vpath))
+
+ def cleanup_ovs_dpdk_env(self):
+ self.connection.execute("ovs-vsctl del-br br0")
+ self.connection.execute("pkill -9 ovs")
+
+ def check_ovs_dpdk_env(self):
+ self.cleanup_ovs_dpdk_env()
+
+ version = self.ovs_properties.get("version", {})
+ ovs_ver = version.get("ovs", self.DEFAULT_OVS)
+ dpdk_ver = version.get("dpdk", "16.07.2").split('.')
+
+ supported_version = self.SUPPORTED_OVS_TO_DPDK_MAP.get(ovs_ver, None)
+ if supported_version is None or supported_version.split('.')[:2] != dpdk_ver[:2]:
+ raise Exception("Unsupported ovs '{}'. Please check the config...".format(ovs_ver))
+
+ status = self.connection.execute("ovs-vsctl -V | grep -i '%s'" % ovs_ver)[0]
+ if status:
+ deploy = OvsDeploy(self.connection,
+ get_nsb_option("bin_path"),
+ self.ovs_properties)
+ deploy.ovs_deploy()
+
+ def deploy(self):
+ """don't need to deploy"""
+
+ # Todo: NFVi deploy (sriov, vswitch, ovs etc) based on the config.
+ if not self.vm_deploy:
+ return
+
+ self.connection = ssh.SSH.from_node(self.host_mgmt)
+ self.dpdk_nic_bind = provision_tool(
+ self.connection,
+ os.path.join(get_nsb_option("bin_path"), "dpdk-devbind.py"))
+
+ # Check dpdk/ovs version, if not present install
+ self.check_ovs_dpdk_env()
+ # Todo: NFVi deploy (sriov, vswitch, ovs etc) based on the config.
+ StandaloneContextHelper.install_req_libs(self.connection)
+ self.networks = StandaloneContextHelper.get_nic_details(self.connection,
+ self.networks,
+ self.dpdk_nic_bind)
+
+ self.setup_ovs()
+ self.start_ovs_serverswitch()
+ self.setup_ovs_bridge_add_flows()
+ self.nodes = self.setup_ovs_dpdk_context()
+ LOG.debug("Waiting for VM to come up...")
+ self.nodes = StandaloneContextHelper.wait_for_vnfs_to_start(self.connection,
+ self.servers,
+ self.nodes)
+
+ def undeploy(self):
+
+ if not self.vm_deploy:
+ return
+
+ # Cleanup the ovs installation...
+ self.cleanup_ovs_dpdk_env()
+
+ # Bind nics back to kernel
+ bind_cmd = "{dpdk_nic_bind} --force -b {driver} {port}"
+ for key, port in self.networks.items():
+ vpci = port.get("phy_port")
+ phy_driver = port.get("driver")
+ self.connection.execute(bind_cmd.format(dpdk_nic_bind=self.dpdk_nic_bind,
+ driver=phy_driver, port=vpci))
+
+ # Todo: NFVi undeploy (sriov, vswitch, ovs etc) based on the config.
+ for vm in self.vm_names:
+ Libvirt.check_if_vm_exists_and_delete(vm, self.connection)
+
+ def _get_server(self, attr_name):
+ """lookup server info by name from context
+
+ Keyword arguments:
+ attr_name -- A name for a server listed in nodes config file
+ """
+ node_name, name = self.split_name(attr_name)
+ if name is None or self.name != name:
+ return None
+
+ matching_nodes = (n for n in self.nodes if n["name"] == node_name)
+ try:
+ # A clone is created in order to avoid affecting the
+ # original one.
+ node = dict(next(matching_nodes))
+ except StopIteration:
+ return None
+
+ try:
+ duplicate = next(matching_nodes)
+ except StopIteration:
+ pass
+ else:
+ raise ValueError("Duplicate nodes!!! Nodes: %s %s",
+ (node, duplicate))
+
+ node["name"] = attr_name
+ return node
+
+ def _get_network(self, attr_name):
+ if not isinstance(attr_name, collections.Mapping):
+ network = self.networks.get(attr_name)
+
+ else:
+ # Don't generalize too much Just support vld_id
+ vld_id = attr_name.get('vld_id', {})
+ # for standalone context networks are dicts
+ iter1 = (n for n in self.networks.values() if n.get('vld_id') == vld_id)
+ network = next(iter1, None)
+
+ if network is None:
+ return None
+
+ result = {
+ # name is required
+ "name": network["name"],
+ "vld_id": network.get("vld_id"),
+ "segmentation_id": network.get("segmentation_id"),
+ "network_type": network.get("network_type"),
+ "physical_network": network.get("physical_network"),
+ }
+ return result
+
+ def configure_nics_for_ovs_dpdk(self):
+ portlist = OrderedDict(self.networks)
+ for key, ports in portlist.items():
+ mac = StandaloneContextHelper.get_mac_address()
+ portlist[key].update({'mac': mac})
+ self.networks = portlist
+ LOG.info("Ports %s" % self.networks)
+
+ def _enable_interfaces(self, index, vfs, cfg):
+ vpath = self.ovs_properties.get("vpath", "/usr/local")
+ vf = self.networks[vfs[0]]
+ port_num = vf.get('port_num', 0)
+ vpci = PciAddress.parse_address(vf['vpci'].strip(), multi_line=True)
+ # Generate the vpci for the interfaces
+ slot = index + port_num + 10
+ vf['vpci'] = \
+ "{}:{}:{:02x}.{}".format(vpci.domain, vpci.bus, slot, vpci.function)
+ Libvirt.add_ovs_interface(vpath, port_num, vf['vpci'], vf['mac'], str(cfg))
+
+ def setup_ovs_dpdk_context(self):
+ nodes = []
+
+ self.configure_nics_for_ovs_dpdk()
+
+ for index, (key, vnf) in enumerate(OrderedDict(self.servers).items()):
+ cfg = '/tmp/vm_ovs_%d.xml' % index
+ vm_name = "vm_%d" % index
+
+ # 1. Check and delete VM if already exists
+ Libvirt.check_if_vm_exists_and_delete(vm_name, self.connection)
+
+ vcpu, mac = Libvirt.build_vm_xml(self.connection, self.vm_flavor, cfg, vm_name, index)
+ # 2: Cleanup already available VMs
+ for idx, (vkey, vfs) in enumerate(OrderedDict(vnf["network_ports"]).items()):
+ if vkey == "mgmt":
+ continue
+ self._enable_interfaces(index, vfs, cfg)
+
+ # copy xml to target...
+ self.connection.put(cfg, cfg)
+
+ # FIXME: launch through libvirt
+ LOG.info("virsh create ...")
+ Libvirt.virsh_create_vm(self.connection, cfg)
+
+ # 5: Tunning for better performace
+ Libvirt.pin_vcpu_for_perf(self.connection, vm_name, vcpu)
+ self.vm_names.append(vm_name)
+
+ # build vnf node details
+ nodes.append(self.vnf_node.generate_vnf_instance(self.vm_flavor,
+ self.networks,
+ self.host_mgmt.get('ip'),
+ key, vnf, mac))
+
+ return nodes
diff --git a/yardstick/benchmark/contexts/standalone/ovsdpdk.py b/yardstick/benchmark/contexts/standalone/ovsdpdk.py
deleted file mode 100644
index cf5529d89..000000000
--- a/yardstick/benchmark/contexts/standalone/ovsdpdk.py
+++ /dev/null
@@ -1,369 +0,0 @@
-# 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.
-
-from __future__ import absolute_import
-import os
-import yaml
-import time
-import glob
-import itertools
-import logging
-from yardstick import ssh
-from yardstick.benchmark.contexts.standalone import StandaloneContext
-
-BIN_PATH = "/opt/isb_bin/"
-DPDK_NIC_BIND = "dpdk_nic_bind.py"
-
-log = logging.getLogger(__name__)
-
-VM_TEMPLATE = """
-<domain type='kvm'>
- <name>vm1</name>
- <uuid>18230c0c-635d-4c50-b2dc-a213d30acb34</uuid>
- <memory unit='KiB'>20971520</memory>
- <currentMemory unit="KiB">20971520</currentMemory>
- <memoryBacking>
- <hugepages/>
- </memoryBacking>
- <vcpu placement='static'>20</vcpu>
- <os>
- <type arch='x86_64' machine='pc'>hvm</type>
- <boot dev='hd'/>
- </os>
- <features>
- <acpi/>
- <apic/>
- </features>
- <cpu match="exact" mode='host-model'>
- <model fallback='allow'/>
- <topology sockets='1' cores='10' threads='2'/>
- </cpu>
- <on_poweroff>destroy</on_poweroff>
- <on_reboot>restart</on_reboot>
- <on_crash>destroy</on_crash>
- <devices>
- <emulator>/usr/bin/qemu-system-x86_64</emulator>
- <disk type='file' device='disk'>
- <driver name='qemu' type='qcow2' cache='none'/>
- <source file="{vm_image}"/>
- <target dev='vda' bus='virtio'/>
- <address bus="0x00" domain="0x0000"
- function="0x0" slot="0x04" type="pci" />
- </disk>
- <!--disk type='dir' device='disk'>
- <driver name='qemu' type='fat'/>
- <source dir='/opt/isb_bin/dpdk'/>
- <target dev='vdb' bus='virtio'/>
- <readonly/>
- </disk-->
- <interface type="bridge">
- <mac address="00:00:00:ab:cd:ef" />
- <source bridge="br-int" />
- </interface>
- <interface type='vhostuser'>
- <mac address='00:00:00:00:00:01'/>
- <source type='unix' path='/usr/local/var/run/openvswitch/dpdkvhostuser0' mode='client'/>
- <model type='virtio'/>
- <driver queues='4'>
- <host mrg_rxbuf='off'/>
- </driver>
- </interface>
- <interface type='vhostuser'>
- <mac address='00:00:00:00:00:02'/>
- <source type='unix' path='/usr/local/var/run/openvswitch/dpdkvhostuser1' mode='client'/>
- <model type='virtio'/>
- <driver queues='4'>
- <host mrg_rxbuf='off'/>
- </driver>
- </interface>
- <serial type='pty'>
- <target port='0'/>
- </serial>
- <console type='pty'>
- <target type='serial' port='0'/>
- </console>
- <graphics autoport="yes" listen="0.0.0.0" port="1" type="vnc" />
- </devices>
-</domain>
-"""
-
-
-class Ovsdpdk(StandaloneContext):
- def __init__(self):
- self.name = None
- self.file_path = None
- self.nodes = []
- self.vm_deploy = False
- self.ovs = []
- self.first_run = True
- self.dpdk_nic_bind = BIN_PATH + DPDK_NIC_BIND
- self.user = ""
- self.ssh_ip = ""
- self.passwd = ""
- self.ssh_port = ""
- self.auth_type = ""
-
- def init(self):
- '''initializes itself'''
- log.debug("In init")
- self.parse_pod_and_get_data()
-
- def parse_pod_and_get_data(self, file_path):
- self.file_path = file_path
- print("parsing pod file: {0}".format(self.file_path))
- try:
- with open(self.file_path) as stream:
- cfg = yaml.load(stream)
- except IOError:
- print("File {0} does not exist".format(self.file_path))
- raise
-
- self.ovs.extend([node for node in cfg["nodes"]
- if node["role"] == "Ovsdpdk"])
- self.user = self.ovs[0]['user']
- self.ssh_ip = self.ovs[0]['ip']
- if self.ovs[0]['auth_type'] == "password":
- self.passwd = self.ovs[0]['password']
- else:
- self.ssh_port = self.ovs[0]['ssh_port']
- self.key_filename = self.ovs[0]['key_filename']
-
- def ssh_remote_machine(self):
- if self.ovs[0]['auth_type'] == "password":
- self.connection = ssh.SSH(
- self.user,
- self.ssh_ip,
- password=self.passwd)
- self.connection.wait()
- else:
- if self.ssh_port is not None:
- ssh_port = self.ssh_port
- else:
- ssh_port = ssh.DEFAULT_PORT
- self.connection = ssh.SSH(
- self.user,
- self.ssh_ip,
- port=ssh_port,
- key_filename=self.key_filename)
- self.connection.wait()
-
- def get_nic_details(self):
- nic_details = {}
- nic_details['interface'] = {}
- nic_details['pci'] = self.ovs[0]['phy_ports']
- nic_details['phy_driver'] = self.ovs[0]['phy_driver']
- nic_details['vports_mac'] = self.ovs[0]['vports_mac']
- # Make sure that ports are bound to kernel drivers e.g. i40e/ixgbe
- for i, _ in enumerate(nic_details['pci']):
- err, out, _ = self.connection.execute(
- "{dpdk_nic_bind} --force -b {driver} {port}".format(
- dpdk_nic_bind=self.dpdk_nic_bind,
- driver=self.ovs[0]['phy_driver'],
- port=self.ovs[0]['phy_ports'][i]))
- err, out, _ = self.connection.execute(
- "lshw -c network -businfo | grep '{port}'".format(
- port=self.ovs[0]['phy_ports'][i]))
- a = out.split()[1]
- err, out, _ = self.connection.execute(
- "ip -s link show {interface}".format(
- interface=out.split()[1]))
- nic_details['interface'][i] = str(a)
- print("{0}".format(nic_details))
- return nic_details
-
- def install_req_libs(self):
- if self.first_run:
- err, out, _ = self.connection.execute("apt-get update")
- print("{0}".format(out))
- err, out, _ = self.connection.execute(
- "apt-get -y install qemu-kvm libvirt-bin")
- print("{0}".format(out))
- err, out, _ = self.connection.execute(
- "apt-get -y install libvirt-dev bridge-utils numactl")
- print("{0}".format(out))
- self.first_run = False
-
- def setup_ovs(self, vpcis):
- self.connection.execute("/usr/bin/chmod 0666 /dev/vfio/*")
- self.connection.execute("/usr/bin/chmod a+x /dev/vfio")
- self.connection.execute("pkill -9 ovs")
- self.connection.execute("ps -ef | grep ovs | grep -v grep | "
- "awk '{print $2}' | xargs -r kill -9")
- self.connection.execute("killall -r 'ovs*'")
- self.connection.execute(
- "mkdir -p {0}/etc/openvswitch".format(self.ovs[0]["vpath"]))
- self.connection.execute(
- "mkdir -p {0}/var/run/openvswitch".format(self.ovs[0]["vpath"]))
- self.connection.execute(
- "rm {0}/etc/openvswitch/conf.db".format(self.ovs[0]["vpath"]))
- self.connection.execute(
- "ovsdb-tool create {0}/etc/openvswitch/conf.db "
- "{0}/share/openvswitch/"
- "vswitch.ovsschema".format(self.ovs[0]["vpath"]))
- self.connection.execute("modprobe vfio-pci")
- self.connection.execute("chmod a+x /dev/vfio")
- self.connection.execute("chmod 0666 /dev/vfio/*")
- for vpci in vpcis:
- self.connection.execute(
- "/opt/isb_bin/dpdk_nic_bind.py "
- "--bind=vfio-pci {0}".format(vpci))
-
- def start_ovs_serverswitch(self):
- self.connection.execute("mkdir -p /usr/local/var/run/openvswitch")
- self.connection.execute(
- "ovsdb-server --remote=punix:"
- "/usr/local/var/run/openvswitch/db.sock --pidfile --detach")
- self.connection.execute(
- "ovs-vsctl --no-wait set "
- "Open_vSwitch . other_config:dpdk-init=true")
- self.connection.execute(
- "ovs-vsctl --no-wait set "
- "Open_vSwitch . other_config:dpdk-lcore-mask=0x3")
- self.connection.execute(
- "ovs-vsctl --no-wait set "
- "Open_vSwitch . other_config:dpdk-socket-mem='2048,0'")
- self.connection.execute(
- "ovs-vswitchd unix:{0}/"
- "var/run/openvswitch/db.sock --pidfile --detach "
- "--log-file=/var/log/openvswitch/"
- "ovs-vswitchd.log".format(
- self.ovs[0]["vpath"]))
- self.connection.execute(
- "ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=2C")
-
- def setup_ovs_bridge(self):
- self.connection.execute("ovs-vsctl del-br br0")
- self.connection.execute(
- "rm -rf /usr/local/var/run/openvswitch/dpdkvhostuser*")
- self.connection.execute(
- "ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev")
- self.connection.execute(
- "ovs-vsctl add-port br0 dpdk0 -- set Interface dpdk0 type=dpdk")
- self.connection.execute(
- "ovs-vsctl add-port br0 dpdk1 -- set Interface dpdk1 type=dpdk")
- self.connection.execute(
- "ovs-vsctl add-port br0 dpdkvhostuser0 -- set Interface "
- "dpdkvhostuser0 type=dpdkvhostuser")
- self.connection.execute("ovs-vsctl add-port br0 dpdkvhostuser1 "
- "-- set Interface dpdkvhostuser1 "
- "type=dpdkvhostuser")
- self.connection.execute(
- "chmod 0777 {0}/var/run/"
- "openvswitch/dpdkvhostuser*".format(self.ovs[0]["vpath"]))
-
- def add_oflows(self):
- self.connection.execute("ovs-ofctl del-flows br0")
- for flow in self.ovs[0]["flow"]:
- self.connection.execute(flow)
- self.connection.execute("ovs-ofctl dump-flows br0")
- self.connection.execute(
- "ovs-vsctl set Interface dpdk0 options:n_rxq=4")
- self.connection.execute(
- "ovs-vsctl set Interface dpdk1 options:n_rxq=4")
-
- def setup_ovs_context(self, pcis, nic_details, host_driver):
-
- ''' 1: Setup vm_ovs.xml to launch VM.'''
- cfg_ovs = '/tmp/vm_ovs.xml'
- vm_ovs_xml = VM_TEMPLATE.format(vm_image=self.ovs[0]["images"])
- with open(cfg_ovs, 'w') as f:
- f.write(vm_ovs_xml)
-
- ''' 2: Create and start the VM'''
- self.connection.put(cfg_ovs, cfg_ovs)
- time.sleep(10)
- err, out = self.check_output("virsh list --name | grep -i vm1")
- if out == "vm1":
- print("VM is already present")
- else:
- ''' FIXME: launch through libvirt'''
- print("virsh create ...")
- err, out, _ = self.connection.execute(
- "virsh create /tmp/vm_ovs.xml")
- time.sleep(10)
- print("err : {0}".format(err))
- print("{0}".format(_))
- print("out : {0}".format(out))
-
- ''' 3: Tuning for better performace.'''
- self.pin_vcpu(pcis)
- self.connection.execute(
- "echo 1 > /sys/module/kvm/parameters/"
- "allow_unsafe_assigned_interrupts")
- self.connection.execute(
- "echo never > /sys/kernel/mm/transparent_hugepage/enabled")
- print("After tuning performance ...")
-
- ''' This is roughly compatible with check_output function in subprocess
- module which is only available in python 2.7.'''
- def check_output(self, cmd, stderr=None):
- '''Run a command and capture its output'''
- err, out, _ = self.connection.execute(cmd)
- return err, out
-
- def read_from_file(self, filename):
- data = ""
- with open(filename, 'r') as the_file:
- data = the_file.read()
- return data
-
- def write_to_file(self, filename, content):
- with open(filename, 'w') as the_file:
- the_file.write(content)
-
- def pin_vcpu(self, pcis):
- nodes = self.get_numa_nodes()
- print("{0}".format(nodes))
- num_nodes = len(nodes)
- for i in range(0, 10):
- self.connection.execute(
- "virsh vcpupin vm1 {0} {1}".format(
- i, nodes[str(num_nodes - 1)][i % len(nodes[str(num_nodes - 1)])]))
-
- def get_numa_nodes(self):
- nodes_sysfs = glob.iglob("/sys/devices/system/node/node*")
- nodes = {}
- for node_sysfs in nodes_sysfs:
- num = os.path.basename(node_sysfs).replace("node", "")
- with open(os.path.join(node_sysfs, "cpulist")) as cpulist_file:
- cpulist = cpulist_file.read().strip()
- print("cpulist: {0}".format(cpulist))
- nodes[num] = self.split_cpu_list(cpulist)
- print("nodes: {0}".format(nodes))
- return nodes
-
- def split_cpu_list(self, cpu_list):
- if cpu_list:
- ranges = cpu_list.split(',')
- bounds = ([int(b) for b in r.split('-')] for r in ranges)
- range_objects =\
- (range(bound[0], bound[1] + 1 if len(bound) == 2
- else bound[0] + 1) for bound in bounds)
-
- return sorted(itertools.chain.from_iterable(range_objects))
- else:
- return []
-
- def destroy_vm(self):
- host_driver = self.ovs[0]['phy_driver']
- err, out = self.check_output("virsh list --name | grep -i vm1")
- print("{0}".format(out))
- if err == 0:
- self.connection.execute("virsh shutdown vm1")
- self.connection.execute("virsh destroy vm1")
- self.check_output("rmmod {0}".format(host_driver))[1].splitlines()
- self.check_output("modprobe {0}".format(host_driver))[
- 1].splitlines()
- else:
- print("error : ", err)
diff --git a/yardstick/benchmark/contexts/standalone/sriov.py b/yardstick/benchmark/contexts/standalone/sriov.py
index fe27d2579..55d7057a9 100644
--- a/yardstick/benchmark/contexts/standalone/sriov.py
+++ b/yardstick/benchmark/contexts/standalone/sriov.py
@@ -14,418 +14,248 @@
from __future__ import absolute_import
import os
-import yaml
-import re
-import time
-import glob
-import uuid
-import random
import logging
-import itertools
-import xml.etree.ElementTree as ET
+import collections
+from collections import OrderedDict
+
from yardstick import ssh
from yardstick.network_services.utils import get_nsb_option
from yardstick.network_services.utils import provision_tool
-from yardstick.benchmark.contexts.standalone import StandaloneContext
-
-log = logging.getLogger(__name__)
-
-VM_TEMPLATE = """
-<domain type="kvm">
- <name>vm1</name>
- <uuid>{random_uuid}</uuid>
- <memory unit="KiB">102400</memory>
- <currentMemory unit="KiB">102400</currentMemory>
- <memoryBacking>
- <hugepages />
- </memoryBacking>
- <vcpu placement="static">20</vcpu>
- <os>
- <type arch="x86_64" machine="pc-i440fx-utopic">hvm</type>
- <boot dev="hd" />
- </os>
- <features>
- <acpi />
- <apic />
- <pae />
- </features>
- <cpu match="exact" mode="custom">
- <model fallback="allow">SandyBridge</model>
- <topology cores="10" sockets="1" threads="2" />
- </cpu>
- <clock offset="utc">
- <timer name="rtc" tickpolicy="catchup" />
- <timer name="pit" tickpolicy="delay" />
- <timer name="hpet" present="no" />
- </clock>
- <on_poweroff>destroy</on_poweroff>
- <on_reboot>restart</on_reboot>
- <on_crash>restart</on_crash>
- <devices>
- <emulator>/usr/bin/kvm-spice</emulator>
- <disk device="disk" type="file">
- <driver name="qemu" type="qcow2" />
- <source file="{vm_image}"/>
- <target bus="virtio" dev="vda" />
- <address bus="0x00" domain="0x0000"
-function="0x0" slot="0x04" type="pci" />
- </disk>
- <controller index="0" model="ich9-ehci1" type="usb">
- <address bus="0x00" domain="0x0000"
-function="0x7" slot="0x05" type="pci" />
- </controller>
- <controller index="0" model="ich9-uhci1" type="usb">
- <master startport="0" />
- <address bus="0x00" domain="0x0000" function="0x0"
-multifunction="on" slot="0x05" type="pci" />
- </controller>
- <controller index="0" model="ich9-uhci2" type="usb">
- <master startport="2" />
- <address bus="0x00" domain="0x0000"
-function="0x1" slot="0x05" type="pci" />
- </controller>
- <controller index="0" model="ich9-uhci3" type="usb">
- <master startport="4" />
- <address bus="0x00" domain="0x0000"
-function="0x2" slot="0x05" type="pci" />
- </controller>
- <controller index="0" model="pci-root" type="pci" />
- <serial type="pty">
- <target port="0" />
- </serial>
- <console type="pty">
- <target port="0" type="serial" />
- </console>
- <input bus="usb" type="tablet" />
- <input bus="ps2" type="mouse" />
- <input bus="ps2" type="keyboard" />
- <graphics autoport="yes" listen="0.0.0.0" port="-1" type="vnc" />
- <video>
- <model heads="1" type="cirrus" vram="16384" />
- <address bus="0x00" domain="0x0000"
-function="0x0" slot="0x02" type="pci" />
- </video>
- <memballoon model="virtio">
- <address bus="0x00" domain="0x0000"
-function="0x0" slot="0x06" type="pci" />
- </memballoon>
- <interface type="bridge">
- <mac address="{mac_addr}" />
- <source bridge="virbr0" />
- </interface>
- </devices>
-</domain>
-"""
-
-
-class Sriov(StandaloneContext):
+from yardstick.benchmark.contexts.base import Context
+from yardstick.benchmark.contexts.standalone.model import Libvirt
+from yardstick.benchmark.contexts.standalone.model import StandaloneContextHelper
+from yardstick.benchmark.contexts.standalone.model import Server
+from yardstick.network_services.utils import PciAddress
+
+LOG = logging.getLogger(__name__)
+
+
+class SriovContext(Context):
+ """ This class handles SRIOV standalone nodes - VM running on Non-Managed NFVi
+ Configuration: sr-iov
+ """
+
+ __context_type__ = "StandaloneSriov"
+
def __init__(self):
- self.name = None
self.file_path = None
- self.nodes = []
- self.vm_deploy = False
self.sriov = []
self.first_run = True
self.dpdk_nic_bind = ""
- self.user = ""
- self.ssh_ip = ""
- self.passwd = ""
- self.ssh_port = ""
- self.auth_type = ""
-
- def init(self):
- log.debug("In init")
- self.parse_pod_and_get_data(self.file_path)
-
- def parse_pod_and_get_data(self, file_path):
- self.file_path = file_path
- log.debug("parsing pod file: {0}".format(self.file_path))
- try:
- with open(self.file_path) as stream:
- cfg = yaml.load(stream)
- except IOError:
- log.error("File {0} does not exist".format(self.file_path))
- raise
-
- self.sriov.extend([node for node in cfg["nodes"]
- if node["role"] == "Sriov"])
- self.user = self.sriov[0]['user']
- self.ssh_ip = self.sriov[0]['ip']
- if self.sriov[0]['auth_type'] == "password":
- self.passwd = self.sriov[0]['password']
- else:
- self.ssh_port = self.sriov[0]['ssh_port']
- self.key_filename = self.sriov[0]['key_filename']
-
- def ssh_remote_machine(self):
- if self.sriov[0]['auth_type'] == "password":
- self.connection = ssh.SSH(
- self.user,
- self.ssh_ip,
- password=self.passwd)
- self.connection.wait()
- else:
- if self.ssh_port is not None:
- ssh_port = self.ssh_port
- else:
- ssh_port = ssh.DEFAULT_PORT
- self.connection = ssh.SSH(
- self.user,
- self.ssh_ip,
- port=ssh_port,
- key_filename=self.key_filename)
- self.connection.wait()
+ self.vm_names = []
+ self.name = None
+ self.nfvi_host = []
+ self.nodes = []
+ self.networks = {}
+ self.attrs = {}
+ self.vm_flavor = None
+ self.servers = None
+ self.helper = StandaloneContextHelper()
+ self.vnf_node = Server()
+ self.drivers = []
+ super(SriovContext, self).__init__()
+
+ def init(self, attrs):
+ """initializes itself from the supplied arguments"""
+
+ self.name = attrs["name"]
+ self.file_path = attrs.get("file", "pod.yaml")
+
+ self.nodes, self.nfvi_host, self.host_mgmt = \
+ self.helper.parse_pod_file(self.file_path, 'Sriov')
+
+ self.attrs = attrs
+ self.vm_flavor = attrs.get('flavor', {})
+ self.servers = attrs.get('servers', {})
+ self.vm_deploy = attrs.get("vm_deploy", True)
+ # add optional static network definition
+ self.networks = attrs.get("networks", {})
+
+ LOG.debug("Nodes: %r", self.nodes)
+ LOG.debug("NFVi Node: %r", self.nfvi_host)
+ LOG.debug("Networks: %r", self.networks)
+
+ def deploy(self):
+ """don't need to deploy"""
+
+ # Todo: NFVi deploy (sriov, vswitch, ovs etc) based on the config.
+ if not self.vm_deploy:
+ return
+
+ self.connection = ssh.SSH.from_node(self.host_mgmt)
self.dpdk_nic_bind = provision_tool(
self.connection,
os.path.join(get_nsb_option("bin_path"), "dpdk_nic_bind.py"))
- def get_nic_details(self):
- nic_details = {}
- nic_details = {
- 'interface': {},
- 'pci': self.sriov[0]['phy_ports'],
- 'phy_driver': self.sriov[0]['phy_driver'],
- 'vf_macs': self.sriov[0]['vf_macs']
- }
- # Make sure that ports are bound to kernel drivers e.g. i40e/ixgbe
- for i, _ in enumerate(nic_details['pci']):
- err, out, _ = self.connection.execute(
- "{dpdk_nic_bind} --force -b {driver} {port}".format(
- dpdk_nic_bind=self.dpdk_nic_bind,
- driver=self.sriov[0]['phy_driver'],
- port=self.sriov[0]['phy_ports'][i]))
- err, out, _ = self.connection.execute(
- "lshw -c network -businfo | grep '{port}'".format(
- port=self.sriov[0]['phy_ports'][i]))
- a = out.split()[1]
- err, out, _ = self.connection.execute(
- "ip -s link show {interface}".format(
- interface=out.split()[1]))
- nic_details['interface'][i] = str(a)
- log.info("{0}".format(nic_details))
- return nic_details
-
- def install_req_libs(self):
- if self.first_run:
- log.info("Installing required libraries...")
- err, out, _ = self.connection.execute("apt-get update")
- log.debug("{0}".format(out))
- err, out, _ = self.connection.execute(
- "apt-get -y install qemu-kvm libvirt-bin")
- log.debug("{0}".format(out))
- err, out, _ = self.connection.execute(
- "apt-get -y install libvirt-dev bridge-utils numactl")
- log.debug("{0}".format(out))
- self.first_run = False
-
- def configure_nics_for_sriov(self, host_driver, nic_details):
- vf_pci = [[], []]
- self.connection.execute(
- "rmmod {0}".format(host_driver))[1].splitlines()
- self.connection.execute(
- "modprobe {0} num_vfs=1".format(host_driver))[1].splitlines()
- nic_details['vf_pci'] = {}
- for i in range(len(nic_details['pci'])):
- self.connection.execute(
- "echo 1 > /sys/bus/pci/devices/{0}/sriov_numvfs".format(
- nic_details['pci'][i]))
- err, out, _ = self.connection.execute(
- "ip link set {interface} vf 0 mac {mac}".format(
- interface=nic_details['interface'][i],
- mac=nic_details['vf_macs'][i]))
- time.sleep(3)
- vf_pci[i] = self.get_vf_datas(
- 'vf_pci',
- nic_details['pci'][i],
- nic_details['vf_macs'][i])
- nic_details['vf_pci'][i] = vf_pci[i]
- log.debug("NIC DETAILS : {0}".format(nic_details))
- return nic_details
-
- def setup_sriov_context(self, pcis, nic_details, host_driver):
- blacklist = "/etc/modprobe.d/blacklist.conf"
-
- # 1 : Blacklist the vf driver in /etc/modprobe.d/blacklist.conf
- vfnic = "{0}vf".format(host_driver)
- lines = self.read_from_file(blacklist)
- if vfnic not in lines:
- vfblacklist = "blacklist {vfnic}".format(vfnic=vfnic)
- self.connection.execute(
- "echo {vfblacklist} >> {blacklist}".format(
- vfblacklist=vfblacklist,
- blacklist=blacklist))
-
- # 2 : modprobe host_driver with num_vfs
- nic_details = self.configure_nics_for_sriov(host_driver, nic_details)
-
- # 3: Setup vm_sriov.xml to launch VM
- cfg_sriov = '/tmp/vm_sriov.xml'
- mac = [0x00, 0x24, 0x81,
- random.randint(0x00, 0x7f),
- random.randint(0x00, 0xff),
- random.randint(0x00, 0xff)]
- mac_address = ':'.join(map(lambda x: "%02x" % x, mac))
- vm_sriov_xml = VM_TEMPLATE.format(
- random_uuid=uuid.uuid4(),
- mac_addr=mac_address,
- vm_image=self.sriov[0]["images"])
- with open(cfg_sriov, 'w') as f:
- f.write(vm_sriov_xml)
-
- vf = nic_details['vf_pci']
- for index in range(len(nic_details['vf_pci'])):
- self.add_sriov_interface(
- index,
- vf[index]['vf_pci'],
- mac_address,
- "/tmp/vm_sriov.xml")
- self.connection.execute(
- "ifconfig {interface} up".format(
- interface=nic_details['interface'][index]))
-
- # 4: Create and start the VM
- self.connection.put(cfg_sriov, cfg_sriov)
- time.sleep(10)
- err, out = self.check_output("virsh list --name | grep -i vm1")
+ # Todo: NFVi deploy (sriov, vswitch, ovs etc) based on the config.
+ StandaloneContextHelper.install_req_libs(self.connection)
+ self.networks = StandaloneContextHelper.get_nic_details(self.connection,
+ self.networks,
+ self.dpdk_nic_bind)
+ self.nodes = self.setup_sriov_context()
+
+ LOG.debug("Waiting for VM to come up...")
+ self.nodes = StandaloneContextHelper.wait_for_vnfs_to_start(self.connection,
+ self.servers,
+ self.nodes)
+
+ def undeploy(self):
+ """don't need to undeploy"""
+
+ if not self.vm_deploy:
+ return
+
+ # Todo: NFVi undeploy (sriov, vswitch, ovs etc) based on the config.
+ for vm in self.vm_names:
+ Libvirt.check_if_vm_exists_and_delete(vm, self.connection)
+
+ # Bind nics back to kernel
+ for key, ports in self.networks.items():
+ # enable VFs for given...
+ build_vfs = "echo 0 > /sys/bus/pci/devices/{0}/sriov_numvfs"
+ self.connection.execute(build_vfs.format(ports.get('phy_port')))
+
+ def _get_server(self, attr_name):
+ """lookup server info by name from context
+
+ Keyword arguments:
+ attr_name -- A name for a server listed in nodes config file
+ """
+ node_name, name = self.split_name(attr_name)
+ if name is None or self.name != name:
+ return None
+
+ matching_nodes = (n for n in self.nodes if n["name"] == node_name)
try:
- if out == "vm1":
- log.info("VM is already present")
- else:
- # FIXME: launch through libvirt
- log.info("virsh create ...")
- err, out, _ = self.connection.execute(
- "virsh create /tmp/vm_sriov.xml")
- time.sleep(10)
- log.error("err : {0}".format(err))
- log.error("{0}".format(_))
- log.debug("out : {0}".format(out))
- except ValueError:
- raise
-
- # 5: Tunning for better performace
- self.pin_vcpu(pcis)
- self.connection.execute(
- "echo 1 > /sys/module/kvm/parameters/"
- "allow_unsafe_assigned_interrupts")
- self.connection.execute(
- "echo never > /sys/kernel/mm/transparent_hugepage/enabled")
-
- def add_sriov_interface(self, index, vf_pci, vfmac, xml):
- root = ET.parse(xml)
- pattern = "0000:(\d+):(\d+).(\d+)"
- m = re.search(pattern, vf_pci, re.MULTILINE)
- device = root.find('devices')
-
- interface = ET.SubElement(device, 'interface')
- interface.set('managed', 'yes')
- interface.set('type', 'hostdev')
-
- mac = ET.SubElement(interface, 'mac')
- mac.set('address', vfmac)
- source = ET.SubElement(interface, 'source')
-
- addr = ET.SubElement(source, "address")
- addr.set('domain', "0x0")
- addr.set('bus', "{0}".format(m.group(1)))
- addr.set('function', "{0}".format(m.group(3)))
- addr.set('slot', "{0}".format(m.group(2)))
- addr.set('type', "pci")
-
- vf_pci = ET.SubElement(interface, 'address')
- vf_pci.set('type', 'pci')
- vf_pci.set('domain', '0x0000')
- vf_pci.set('bus', '0x00')
- vf_pci.set('slot', '0x0{0}'.format(index + 7))
- vf_pci.set('function', '0x00')
-
- root.write(xml)
-
- # This is roughly compatible with check_output function in subprocess
- # module which is only available in python 2.7
- def check_output(self, cmd, stderr=None):
- # Run a command and capture its output
- err, out, _ = self.connection.execute(cmd)
- return err, out
-
- def get_virtual_devices(self, pci):
- pf_vfs = {}
- err, extra_info = self.check_output(
- "cat /sys/bus/pci/devices/{0}/virtfn0/uevent".format(pci))
- pattern = "PCI_SLOT_NAME=(?P<name>[0-9:.\s.]+)"
- m = re.search(pattern, extra_info, re.MULTILINE)
-
- if m:
- pf_vfs.update({pci: str(m.group(1).rstrip())})
- log.info("pf_vfs : {0}".format(pf_vfs))
- return pf_vfs
-
- def get_vf_datas(self, key, value, vfmac):
- vfret = {}
- pattern = "0000:(\d+):(\d+).(\d+)"
-
- vfret["mac"] = vfmac
- vfs = self.get_virtual_devices(value)
- log.info("vfs: {0}".format(vfs))
- for k, v in vfs.items():
- m = re.search(pattern, k, re.MULTILINE)
- m1 = re.search(pattern, value, re.MULTILINE)
- if m.group(1) == m1.group(1):
- vfret["vf_pci"] = str(v)
- break
+ # A clone is created in order to avoid affecting the
+ # original one.
+ node = dict(next(matching_nodes))
+ except StopIteration:
+ return None
- return vfret
-
- def read_from_file(self, filename):
- data = ""
- with open(filename, 'r') as the_file:
- data = the_file.read()
- return data
-
- def write_to_file(self, filename, content):
- with open(filename, 'w') as the_file:
- the_file.write(content)
-
- def pin_vcpu(self, pcis):
- nodes = self.get_numa_nodes()
- log.info("{0}".format(nodes))
- num_nodes = len(nodes)
- for i in range(0, 10):
- self.connection.execute(
- "virsh vcpupin vm1 {0} {1}".format(
- i, nodes[str(num_nodes - 1)][i % len(nodes[str(num_nodes - 1)])]))
-
- def get_numa_nodes(self):
- nodes_sysfs = glob.iglob("/sys/devices/system/node/node*")
- nodes = {}
- for node_sysfs in nodes_sysfs:
- num = os.path.basename(node_sysfs).replace("node", "")
- with open(os.path.join(node_sysfs, "cpulist")) as cpulist_file:
- cpulist = cpulist_file.read().strip()
- nodes[num] = self.split_cpu_list(cpulist)
- log.info("nodes: {0}".format(nodes))
- return nodes
+ try:
+ duplicate = next(matching_nodes)
+ except StopIteration:
+ pass
+ else:
+ raise ValueError("Duplicate nodes!!! Nodes: %s %s",
+ (node, duplicate))
- def split_cpu_list(self, cpu_list):
- if cpu_list:
- ranges = cpu_list.split(',')
- bounds = ([int(b) for b in r.split('-')] for r in ranges)
- range_objects =\
- (range(bound[0], bound[1] + 1 if len(bound) == 2
- else bound[0] + 1) for bound in bounds)
+ node["name"] = attr_name
+ return node
+
+ def _get_network(self, attr_name):
+ if not isinstance(attr_name, collections.Mapping):
+ network = self.networks.get(attr_name)
- return sorted(itertools.chain.from_iterable(range_objects))
- else:
- return []
-
- def destroy_vm(self):
- host_driver = self.sriov[0]["phy_driver"]
- err, out = self.check_output("virsh list --name | grep -i vm1")
- log.info("{0}".format(out))
- if err == 0:
- self.connection.execute("virsh shutdown vm1")
- self.connection.execute("virsh destroy vm1")
- self.check_output("rmmod {0}".format(host_driver))[1].splitlines()
- self.check_output("modprobe {0}".format(host_driver))[
- 1].splitlines()
else:
- log.error("error : {0}".format(err))
+ # Don't generalize too much Just support vld_id
+ vld_id = attr_name.get('vld_id', {})
+ # for standalone context networks are dicts
+ iter1 = (n for n in self.networks.values() if n.get('vld_id') == vld_id)
+ network = next(iter1, None)
+
+ if network is None:
+ return None
+
+ result = {
+ # name is required
+ "name": network["name"],
+ "vld_id": network.get("vld_id"),
+ "segmentation_id": network.get("segmentation_id"),
+ "network_type": network.get("network_type"),
+ "physical_network": network.get("physical_network"),
+ }
+ return result
+
+ def configure_nics_for_sriov(self):
+ vf_cmd = "ip link set {0} vf 0 mac {1}"
+ for key, ports in self.networks.items():
+ vf_pci = []
+ host_driver = ports.get('driver')
+ if host_driver not in self.drivers:
+ self.connection.execute("rmmod %svf" % host_driver)
+ self.drivers.append(host_driver)
+
+ # enable VFs for given...
+ build_vfs = "echo 1 > /sys/bus/pci/devices/{0}/sriov_numvfs"
+ self.connection.execute(build_vfs.format(ports.get('phy_port')))
+
+ # configure VFs...
+ mac = StandaloneContextHelper.get_mac_address()
+ interface = ports.get('interface')
+ if interface is not None:
+ self.connection.execute(vf_cmd.format(interface, mac))
+
+ vf_pci = self.get_vf_data('vf_pci', ports.get('phy_port'), mac, interface)
+ ports.update({
+ 'vf_pci': vf_pci,
+ 'mac': mac
+ })
+
+ LOG.info("Ports %s" % self.networks)
+
+ def _enable_interfaces(self, index, idx, vfs, cfg):
+ vf = self.networks[vfs[0]]
+ vpci = PciAddress.parse_address(vf['vpci'].strip(), multi_line=True)
+ # Generate the vpci for the interfaces
+ slot = index + idx + 10
+ vf['vpci'] = \
+ "{}:{}:{:02x}.{}".format(vpci.domain, vpci.bus, slot, vpci.function)
+ Libvirt.add_sriov_interfaces(
+ vf['vpci'], vf['vf_pci']['vf_pci'], vf['mac'], str(cfg))
+ self.connection.execute("ifconfig %s up" % vf['interface'])
+
+ def setup_sriov_context(self):
+ nodes = []
+
+ # 1 : modprobe host_driver with num_vfs
+ self.configure_nics_for_sriov()
+
+ for index, (key, vnf) in enumerate(OrderedDict(self.servers).items()):
+ cfg = '/tmp/vm_sriov_%s.xml' % str(index)
+ vm_name = "vm_%s" % str(index)
+
+ # 1. Check and delete VM if already exists
+ Libvirt.check_if_vm_exists_and_delete(vm_name, self.connection)
+
+ vcpu, mac = Libvirt.build_vm_xml(self.connection, self.vm_flavor, cfg, vm_name, index)
+ # 2: Cleanup already available VMs
+ for idx, (vkey, vfs) in enumerate(OrderedDict(vnf["network_ports"]).items()):
+ if vkey == "mgmt":
+ continue
+ self._enable_interfaces(index, idx, vfs, cfg)
+
+ # copy xml to target...
+ self.connection.put(cfg, cfg)
+
+ # FIXME: launch through libvirt
+ LOG.info("virsh create ...")
+ Libvirt.virsh_create_vm(self.connection, cfg)
+
+ # 5: Tunning for better performace
+ Libvirt.pin_vcpu_for_perf(self.connection, vm_name, vcpu)
+ self.vm_names.append(vm_name)
+
+ # build vnf node details
+ nodes.append(self.vnf_node.generate_vnf_instance(self.vm_flavor,
+ self.networks,
+ self.host_mgmt.get('ip'),
+ key, vnf, mac))
+
+ return nodes
+
+ def get_vf_data(self, key, value, vfmac, pfif):
+ vf_data = {
+ "mac": vfmac,
+ "pf_if": pfif
+ }
+ vfs = StandaloneContextHelper.get_virtual_devices(self.connection, value)
+ for k, v in vfs.items():
+ m = PciAddress.parse_address(k.strip(), multi_line=True)
+ m1 = PciAddress.parse_address(value.strip(), multi_line=True)
+ if m.bus == m1.bus:
+ vf_data.update({"vf_pci": str(v)})
+ break
+
+ return vf_data