CSIT-209 Add option enable-vhost-user into startup config
[csit.git] / resources / libraries / python / VppConfigGenerator.py
index 6085882..f255266 100644 (file)
 
 """VPP Configuration File Generator library"""
 
+import re
+import time
+
 from robot.api import logger
 
 from resources.libraries.python.ssh import SSH
 from resources.libraries.python.topology import NodeType
 from resources.libraries.python.topology import Topology
 
-import re
-import time
-
 __all__ = ['VppConfigGenerator']
 
 #
@@ -52,8 +52,13 @@ cpu {{
 
 dpdk {{
   socket-mem {socketmemconfig}
+  dev default {{
+  {rxqueuesconfig}
+  {txqueuesconfig}
+  }}
 {pciconfig}
-{rssconfig}
+{nomultiseg}
+{enablevhostuser}
 }}
 """
 # End VPP configuration template.
@@ -65,109 +70,151 @@ class VppConfigGenerator(object):
     def __init__(self):
         self._nodeconfig = {}
 
-    def add_pci_device(self, node, pci_device=None):
-        """Add PCI device configuration for node.
+    def add_pci_all_devices(self, node):
+        """Add all PCI devices from topology file to startup config
 
         :param node: DUT node
-        :param pci_device: PCI device (format 0000:00:00.0 or 00:00.0).
-        If none given, all PCI devices for this node as per topology will be
-        added.
         :type node: dict
-        :type pci_device: string
+        :return: nothing
+        """
+        for port in node['interfaces'].keys():
+            pci_addr = Topology.get_interface_pci_addr(node, port)
+            if pci_addr:
+                self.add_pci_device(node, pci_addr)
+
+
+    def add_pci_device(self, node, *pci_devices):
+        """Add PCI device configuration for node.
+
+        :param node: DUT node.
+        :param pci_device: PCI devices (format 0000:00:00.0 or 00:00.0)
+        :type node: dict
+        :type pci_devices: tuple
         :return: nothing
         """
         if node['type'] != NodeType.DUT:
             raise ValueError('Node type is not a DUT')
 
-        if pci_device is None:
-            # No PCI device was given. Add all device from topology.
-            for port in node['interfaces'].values():
-                port_name = port.get('name')
-                pci_addr = Topology.get_interface_pci_addr(node, port_name)
-                if pci_addr:
-                    self.add_pci_device(node, pci_addr)
-        else:
-            # Specific device was given.
-            hostname = Topology.get_node_hostname(node)
-
-            pattern = re.compile("^[0-9A-Fa-f]{4}:[0-9A-Fa-f]{2}:"\
-                "[0-9A-Fa-f]{2}\\.[0-9A-Fa-f]$")
+        # Specific device was given.
+        hostname = Topology.get_node_hostname(node)
+
+        pattern = re.compile("^[0-9A-Fa-f]{4}:[0-9A-Fa-f]{2}:"
+                             "[0-9A-Fa-f]{2}\\.[0-9A-Fa-f]$")
+        for pci_device in pci_devices:
             if not pattern.match(pci_device):
-                raise ValueError('PCI address {} to be added to host {} '\
-                    'is not in valid format xxxx:xx:xx.x'.\
-                    format(pci_device, hostname))
+                raise ValueError('PCI address {} to be added to host {} '
+                                 'is not in valid format xxxx:xx:xx.x'.
+                                 format(pci_device, hostname))
 
-            if not hostname in self._nodeconfig:
+            if hostname not in self._nodeconfig:
                 self._nodeconfig[hostname] = {}
-            if not 'pci_addrs' in self._nodeconfig[hostname]:
+            if 'pci_addrs' not in self._nodeconfig[hostname]:
                 self._nodeconfig[hostname]['pci_addrs'] = []
             self._nodeconfig[hostname]['pci_addrs'].append(pci_device)
-            logger.debug('Adding PCI device {1} to {0}'.format(hostname,\
-               pci_device))
+            logger.debug('Adding PCI device {1} to {0}'.format(hostname,
+                                                               pci_device))
 
     def add_cpu_config(self, node, cpu_config):
         """Add CPU configuration for node.
 
-        :param node: DUT node
-        :param cpu_config: CPU configuration option, as a string
+        :param node: DUT node.
+        :param cpu_config: CPU configuration option, as a string.
         :type node: dict
-        :type cpu_config: string
+        :type cpu_config: str
         :return: nothing
         """
         if node['type'] != NodeType.DUT:
             raise ValueError('Node type is not a DUT')
         hostname = Topology.get_node_hostname(node)
-        if not hostname in self._nodeconfig:
+        if hostname not in self._nodeconfig:
             self._nodeconfig[hostname] = {}
-        if not 'cpu_config' in self._nodeconfig[hostname]:
+        if 'cpu_config' not in self._nodeconfig[hostname]:
             self._nodeconfig[hostname]['cpu_config'] = []
         self._nodeconfig[hostname]['cpu_config'].append(cpu_config)
-        logger.debug('Adding {} to hostname {} CPU config'.format(hostname, \
-            cpu_config))
+        logger.debug('Adding {} to hostname {} CPU config'.format(hostname,
+                                                                  cpu_config))
 
     def add_socketmem_config(self, node, socketmem_config):
         """Add Socket Memory configuration for node.
 
-        :param node: DUT node
-        :param socketmem_config: Socket Memory configuration option, as a string
+        :param node: DUT node.
+        :param socketmem_config: Socket Memory configuration option,
+        as a string.
         :type node: dict
-        :type cpu_config: string
+        :type socketmem_config: str
         :return: nothing
         """
         if node['type'] != NodeType.DUT:
             raise ValueError('Node type is not a DUT')
         hostname = Topology.get_node_hostname(node)
-        if not hostname in self._nodeconfig:
+        if hostname not in self._nodeconfig:
             self._nodeconfig[hostname] = {}
         self._nodeconfig[hostname]['socketmem_config'] = socketmem_config
-        logger.debug('Setting hostname {} Socket Memory config to {}'.\
-            format(hostname, socketmem_config))
+        logger.debug('Setting hostname {} Socket Memory config to {}'.
+                     format(hostname, socketmem_config))
 
     def add_heapsize_config(self, node, heapsize_config):
         """Add Heap Size configuration for node.
 
-        :param node: DUT node
-        :param heapsize_config: Heap Size configuration, as a string
+        :param node: DUT node.
+        :param heapsize_config: Heap Size configuration, as a string.
         :type node: dict
-        :type cpu_config: string
+        :type heapsize_config: str
         :return: nothing
         """
         if node['type'] != NodeType.DUT:
             raise ValueError('Node type is not a DUT')
         hostname = Topology.get_node_hostname(node)
-        if not hostname in self._nodeconfig:
+        if hostname not in self._nodeconfig:
             self._nodeconfig[hostname] = {}
         self._nodeconfig[hostname]['heapsize_config'] = heapsize_config
-        logger.debug('Setting hostname {} Heap Size config to {}'.\
-            format(hostname, heapsize_config))
+        logger.debug('Setting hostname {} Heap Size config to {}'.
+                     format(hostname, heapsize_config))
 
-    def add_rss_config(self, node, rss_config):
-        """Add RSS configuration for node.
+    def add_rxqueues_config(self, node, rxqueues_config):
+        """Add Rx Queues configuration for node.
 
-        :param node: DUT node
-        :param rss_config: RSS configuration, as a string
+        :param node: DUT node.
+        :param rxqueues_config: Rxqueues configuration, as a string.
+        :type node: dict
+        :type rxqueues_config: str
+        :return: nothing
+        """
+        if node['type'] != NodeType.DUT:
+            raise ValueError('Node type is not a DUT')
+        hostname = Topology.get_node_hostname(node)
+        if not hostname in self._nodeconfig:
+            self._nodeconfig[hostname] = {}
+        if not 'rxqueues_config' in self._nodeconfig[hostname]:
+            self._nodeconfig[hostname]['rxqueues_config'] = []
+        self._nodeconfig[hostname]['rxqueues_config'].append(rxqueues_config)
+        logger.debug('Setting hostname {} rxqueues config to {}'.\
+            format(hostname, rxqueues_config))
+
+    def add_no_multi_seg_config(self, node):
+        """Add No Multi Seg configuration for node.
+
+        :param node: DUT node.
+        :type node: dict
+        :return: nothing
+        """
+        if node['type'] != NodeType.DUT:
+            raise ValueError('Node type is not a DUT')
+        hostname = Topology.get_node_hostname(node)
+        if not hostname in self._nodeconfig:
+            self._nodeconfig[hostname] = {}
+        if not 'no_multi_seg_config' in self._nodeconfig[hostname]:
+            self._nodeconfig[hostname]['no_multi_seg_config'] = []
+        self._nodeconfig[hostname]['no_multi_seg_config'].append(
+            "no-multi-seg")
+        logger.debug('Setting hostname {} config with {}'.\
+            format(hostname, "no-multi-seg"))
+
+    def add_enable_vhost_user_config(self, node):
+        """Add enable-vhost-user configuration for node.
+
+        :param node: DUT node.
         :type node: dict
-        :type rss_config: string
         :return: nothing
         """
         if node['type'] != NodeType.DUT:
@@ -175,17 +222,18 @@ class VppConfigGenerator(object):
         hostname = Topology.get_node_hostname(node)
         if not hostname in self._nodeconfig:
             self._nodeconfig[hostname] = {}
-        if not 'rss_config' in self._nodeconfig[hostname]:
-            self._nodeconfig[hostname]['rss_config'] = []
-        self._nodeconfig[hostname]['rss_config'].append(rss_config)
-        logger.debug('Setting hostname {} RSS config to {}'.\
-            format(hostname, rss_config))
+        if not 'enable_vhost_user' in self._nodeconfig[hostname]:
+            self._nodeconfig[hostname]['enable_vhost_user'] = []
+        self._nodeconfig[hostname]['enable_vhost_user'].append(
+            "enable-vhost-user")
+        logger.debug('Setting hostname {} config with {}'.\
+            format(hostname, "enable-vhost-user"))
 
     def remove_all_pci_devices(self, node):
         """Remove PCI device configuration from node.
 
-        :param node: DUT node
-        :type: node: dict
+        :param node: DUT node.
+        :type node: dict
         :return: nothing
         """
         if node['type'] != NodeType.DUT:
@@ -193,14 +241,14 @@ class VppConfigGenerator(object):
         hostname = Topology.get_node_hostname(node)
         if hostname in self._nodeconfig:
             self._nodeconfig[hostname]['pci_addrs'] = []
-        logger.debug('Clearing all PCI devices for hostname {}.'.\
-            format(hostname))
+        logger.debug('Clearing all PCI devices for hostname {}.'.
+                     format(hostname))
 
     def remove_all_cpu_config(self, node):
         """Remove CPU configuration from node.
 
-        :param node: DUT node
-        :type: node: dict
+        :param node: DUT node.
+        :type node: dict
         :return: nothing
         """
         if node['type'] != NodeType.DUT:
@@ -208,14 +256,14 @@ class VppConfigGenerator(object):
         hostname = Topology.get_node_hostname(node)
         if hostname in self._nodeconfig:
             self._nodeconfig[hostname]['cpu_config'] = []
-        logger.debug('Clearing all CPU config for hostname {}.'.\
-            format(hostname))
+        logger.debug('Clearing all CPU config for hostname {}.'.
+                     format(hostname))
 
     def remove_socketmem_config(self, node):
         """Remove Socket Memory configuration from node.
 
-        :param node: DUT node
-        :type: node: dict
+        :param node: DUT node.
+        :type node: dict
         :return: nothing
         """
         if node['type'] != NodeType.DUT:
@@ -223,14 +271,14 @@ class VppConfigGenerator(object):
         hostname = Topology.get_node_hostname(node)
         if hostname in self._nodeconfig:
             self._nodeconfig[hostname].pop('socketmem_config', None)
-        logger.debug('Clearing Socket Memory config for hostname {}.'.\
-            format(hostname))
+        logger.debug('Clearing Socket Memory config for hostname {}.'.
+                     format(hostname))
 
     def remove_heapsize_config(self, node):
         """Remove Heap Size configuration from node.
 
-        :param node: DUT node
-        :type: node: dict
+        :param node: DUT node.
+        :type node: dict
         :return: nothing
         """
         if node['type'] != NodeType.DUT:
@@ -238,22 +286,52 @@ class VppConfigGenerator(object):
         hostname = Topology.get_node_hostname(node)
         if hostname in self._nodeconfig:
             self._nodeconfig[hostname].pop('heapsize_config', None)
-        logger.debug('Clearing Heap Size config for hostname {}.'.\
+        logger.debug('Clearing Heap Size config for hostname {}.'.
+                     format(hostname))
+
+    def remove_rxqueues_config(self, node):
+        """Remove Rxqueues configuration from node.
+
+        :param node: DUT node.
+        :type node: dict
+        :return: nothing
+        """
+        if node['type'] != NodeType.DUT:
+            raise ValueError('Node type is not a DUT')
+        hostname = Topology.get_node_hostname(node)
+        if hostname in self._nodeconfig:
+            self._nodeconfig[hostname]['rxqueues_config'] = []
+        logger.debug('Clearing rxqueues config for hostname {}.'.\
             format(hostname))
 
-    def remove_rss_config(self, node):
-        """Remove RSS configuration from node.
+    def remove_no_multi_seg_config(self, node):
+        """Remove No Multi Seg configuration from node.
 
-        :param node: DUT node
-        :type: node: dict
+        :param node: DUT node.
+        :type node: dict
         :return: nothing
         """
         if node['type'] != NodeType.DUT:
             raise ValueError('Node type is not a DUT')
         hostname = Topology.get_node_hostname(node)
         if hostname in self._nodeconfig:
-            self._nodeconfig[hostname]['rss_config'] = []
-        logger.debug('Clearing RSS config for hostname {}.'.\
+            self._nodeconfig[hostname]['no_multi_seg_config'] = []
+        logger.debug('Clearing No Multi Seg config for hostname {}.'.\
+            format(hostname))
+
+    def remove_enable_vhost_user_config(self, node):
+        """Remove enable-vhost-user configuration from node.
+
+        :param node: DUT node.
+        :type node: dict
+        :return: nothing
+        """
+        if node['type'] != NodeType.DUT:
+            raise ValueError('Node type is not a DUT')
+        hostname = Topology.get_node_hostname(node)
+        if hostname in self._nodeconfig:
+            self._nodeconfig[hostname]['enable_vhost_user'] = []
+        logger.debug('Clearing enable-vhost-user config for hostname {}.'.\
             format(hostname))
 
     def apply_config(self, node, waittime=5, retries=12):
@@ -262,9 +340,9 @@ class VppConfigGenerator(object):
         Use data from calls to this class to form a startup.conf file and
         replace /etc/vpp/startup.conf with it on node.
 
-        :param node: DUT node
-        :param waittime: time to wait for VPP to restart (default 5 seconds)
-        :param retries: number of times (default 12) to re-try waiting
+        :param node: DUT node.
+        :param waittime: Time to wait for VPP to restart (default 5 seconds).
+        :param retries: Number of times (default 12) to re-try waiting.
         :type node: dict
         :type waittime: int
         :type retries: int
@@ -278,7 +356,10 @@ class VppConfigGenerator(object):
         pciconfig = ""
         socketmemconfig = DEFAULT_SOCKETMEM_CONFIG
         heapsizeconfig = ""
-        rssconfig = ""
+        rxqueuesconfig = ""
+        txqueuesconfig = ""
+        nomultiseg = ""
+        enablevhostuser = ""
 
         if hostname in self._nodeconfig:
             cfg = self._nodeconfig[hostname]
@@ -295,17 +376,25 @@ class VppConfigGenerator(object):
                 heapsizeconfig = "\nheapsize {}\n".\
                     format(cfg['heapsize_config'])
 
-            if 'rss_config' in cfg:
-                rssconfig = "  " + "\n  ".join(cfg['rss_config'])
+            if 'rxqueues_config' in cfg:
+                rxqueuesconfig = "  " + "\n  ".join(cfg['rxqueues_config'])
+
+            if 'no_multi_seg_config' in cfg:
+                nomultiseg = "  " + "\n  ".join(cfg['no_multi_seg_config'])
+
+            if 'enable_vhost_user' in cfg:
+                enablevhostuser = "  " + "\n  ".join(cfg['enable_vhost_user'])
 
         vppconfig = VPP_CONFIG_TEMPLATE.format(cpuconfig=cpuconfig,
                                                pciconfig=pciconfig,
                                                socketmemconfig=socketmemconfig,
-                                               heapsizeconfig=heapsizeconfig,
-                                               rssconfig=rssconfig)
+                                               rxqueuesconfig=rxqueuesconfig,
+                                               txqueuesconfig=txqueuesconfig,
+                                               nomultiseg=nomultiseg,
+                                               enablevhostuser=enablevhostuser)
 
-        logger.debug('Writing VPP config to host {}: "{}"'.format(hostname,\
-               vppconfig))
+        logger.debug('Writing VPP config to host {}: "{}"'.format(hostname,
+                                                                  vppconfig))
 
         ssh = SSH()
         ssh.connect(node)
@@ -314,16 +403,16 @@ class VppConfigGenerator(object):
         # a sudo'd outut ("sudo echo xxx > /path/to/file") does not
         # work on most platforms...
         (ret, stdout, stderr) = \
-            ssh.exec_command('echo "{0}" | sudo tee {1}'.\
-            format(vppconfig, VPP_CONFIG_FILENAME))
+            ssh.exec_command('echo "{0}" | sudo tee {1}'.
+                             format(vppconfig, VPP_CONFIG_FILENAME))
 
         if ret != 0:
-            logger.debug('Writing config file failed to node {}'.\
-                format(hostname))
+            logger.debug('Writing config file failed to node {}'.
+                         format(hostname))
             logger.debug('stdout: {}'.format(stdout))
             logger.debug('stderr: {}'.format(stderr))
-            raise RuntimeError('Writing config file failed to node {}'.\
-                format(hostname))
+            raise RuntimeError('Writing config file failed to node {}'.
+                               format(hostname))
 
         # Instead of restarting, we'll do separate start and stop
         # actions. This way we don't care whether VPP was running
@@ -332,12 +421,12 @@ class VppConfigGenerator(object):
         (ret, stdout, stderr) = \
             ssh.exec_command('sudo initctl start {}'.format(VPP_SERVICE_NAME))
         if ret != 0:
-            logger.debug('Restarting VPP failed on node {}'.\
-                format(hostname))
+            logger.debug('Restarting VPP failed on node {}'.
+                         format(hostname))
             logger.debug('stdout: {}'.format(stdout))
             logger.debug('stderr: {}'.format(stderr))
-            raise RuntimeError('Restarting VPP failed on node {}'.\
-                format(hostname))
+            raise RuntimeError('Restarting VPP failed on node {}'.
+                               format(hostname))
 
         # Sleep <waittime> seconds, up to <retry> times,
         # and verify if VPP is running.
@@ -357,16 +446,16 @@ class VppConfigGenerator(object):
             # healthy or not, or a call that waits (up to a defined length
             # of time) and returns immediately if VPP is or becomes healthy.
             (ret, stdout, stderr) = \
-                ssh.exec_command('echo show hardware-interfaces | '\
-                    'nc 0 5002')
+                ssh.exec_command('echo show hardware-interfaces | '
+                                 'nc 0 5002')
 
             if ret == 0:
                 vpp_is_running = True
             else:
-                logger.debug('VPP not yet running, {} retries left'.\
-                    format(retries_left))
+                logger.debug('VPP not yet running, {} retries left'.
+                             format(retries_left))
         if retries_left == 0:
-            raise RuntimeError('VPP failed to restart on node {}'.\
-                format(hostname))
-        logger.debug('VPP interfaces found on node {}'.\
-           format(stdout))
+            raise RuntimeError('VPP failed to restart on node {}'.
+                               format(hostname))
+        logger.debug('VPP interfaces found on node {}'.
+                     format(stdout))