A bit of cleanup, updated the README, started vhost test. 83/9183/2
authorJohn DeNisco <jdenisco@cisco.com>
Wed, 1 Nov 2017 16:37:47 +0000 (12:37 -0400)
committerChris Luke <chris_luke@comcast.com>
Wed, 1 Nov 2017 18:06:33 +0000 (18:06 +0000)
Change-Id: I49b998644b8b79c778c1186fc09831b1cd8fc015
Signed-off-by: John DeNisco <jdenisco@cisco.com>
extras/vpp_config/README.rst
extras/vpp_config/setup.py
extras/vpp_config/vpp_config.py
extras/vpp_config/vpplib/AutoConfig.py
extras/vpp_config/vpplib/VPPUtil.py
extras/vpp_config/vpplib/VppGrubUtil.py
extras/vpp_config/vpplib/VppPCIUtil.py

index 748f66f..ec9c8e4 100644 (file)
@@ -5,7 +5,7 @@ VPP in a simple and safe manner. The utility takes input from the user and
 then modifies the key configuration files. The user can then examine these files\r
 to be sure they are correct and then actually apply the configuration. The user\r
 can also install a released and stable version of VPP. This is currently\r
-released with release 17.07.\r
+released with release 17.10.\r
 \r
 Use:\r
 \r
@@ -23,9 +23,7 @@ apply the configuration and then inspect the system again and then repeat.
 \r
 Caveats:\r
 \r
-- Only supports Ubuntu, centos7 is coming shortly.\r
-- When Inspecting the system, you may see a Huge page error, inspect the system a\r
-few more times, if the error persists it is real.\r
+- Supports Ubuntu, centos7, RedHat is coming shortly.\r
 \r
 For Developers:\r
 \r
index 8d2a396..32c376d 100644 (file)
@@ -1,7 +1,7 @@
 from setuptools import setup
 
 setup(name="vpp_config",
-      version="17.10.3",
+      version="17.10.5",
       author="John DeNisco",
       author_email="jdenisco@cisco.com",
       description="VPP Configuration Utility",
@@ -9,7 +9,7 @@ setup(name="vpp_config",
       keywords="vppconfig",
       url = 'https://wiki.fd.io/view/VPP',
       py_modules=['vpp_config'],
-      install_requires=['pyyaml'],
+      install_requires=['pyyaml','netaddr'],
       packages=['vpplib'],
       scripts=['scripts/vpp-config'],
       data_files=[('vpp/vpp-config/scripts', ['scripts/dpdk-devbind.py']),
index b8b49a0..da455c1 100755 (executable)
@@ -450,6 +450,16 @@ def autoconfig_ipv4_setup():
     acfg.ipv4_interface_setup()
 
 
+def autoconfig_create_vm():
+    """
+    Setup IPv4 interfaces
+
+    """
+
+    acfg = AutoConfig(rootdir, VPP_AUTO_CONFIGURATION_FILE)
+    acfg.create_and_bridge_virtual_interfaces()
+
+
 def autoconfig_not_implemented():
     """
     This feature is not implemented
@@ -465,10 +475,14 @@ def autoconfig_basic_test_menu():
 
     """
 
+#    basic_menu_text = '\nWhat would you like to do?\n\n\
+# 1) List/Create Simple IPv4 Setup\n\
+# 2) List/Create Create VM and Connect to VPP interfaces\n\
+# 9 or q) Back to main menu.'
+
     basic_menu_text = '\nWhat would you like to do?\n\n\
 1) List/Create Simple IPv4 Setup\n\
 9 or q) Back to main menu.'
-
     print "{}".format(basic_menu_text)
 
     input_valid = False
@@ -506,6 +520,8 @@ def autoconfig_basic_test():
         answer = autoconfig_basic_test_menu()
         if answer == '1':
             autoconfig_ipv4_setup()
+        # elif answer == '2':
+        #    autoconfig_create_vm()
         elif answer == '9' or answer == 'q':
             return
         else:
index ab943e0..7b7d7a7 100644 (file)
@@ -17,7 +17,7 @@ import logging
 import os
 import re
 import yaml
-import ipaddress
+from netaddr import IPAddress
 
 from vpplib.VPPUtil import VPPUtil
 from vpplib.VppPCIUtil import VppPCIUtil
@@ -90,30 +90,25 @@ class AutoConfig(object):
         Asks the user for a number within a range.
         default is returned if return is entered.
 
-        :returns: IP address and prefix len
-        :rtype: tuple
+        :returns: IP address with cidr
+        :rtype: str
         """
 
         while True:
-            answer = raw_input("Please enter the IPv4 Address [n.n.n.n]: ")
+            answer = raw_input("Please enter the IPv4 Address [n.n.n.n/n]: ")
             try:
-                ipaddr = ipaddress.ip_address(u'{}'.format(answer))
+                ipinput = answer.split('/')
+                ipaddr = IPAddress(ipinput[0])
+                if len(ipinput) > 1:
+                    plen = answer.split('/')[1]
+                else:
+                    answer = raw_input("Please enter the netmask [n.n.n.n]: ")
+                    plen = IPAddress(answer).netmask_bits()
+                return '{}/{}'.format(ipaddr, plen)
             except:
                 print "Please enter a valid IPv4 address."
                 continue
 
-            answer = raw_input("Please enter the netmask [n.n.n.n]: ")
-            try:
-                netmask = ipaddress.ip_address(u'{}'.format(answer))
-                pl = ipaddress.ip_network(u'0.0.0.0/{}'.format(netmask))
-                plen = pl.exploded.split('/')[1]
-                break
-            except:
-                print "Please enter a valid IPv4 address and netmask."
-                continue
-
-        return ipaddr, plen
-
     @staticmethod
     def _ask_user_range(question, first, last, default):
         """
@@ -1479,14 +1474,13 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores))
             if name == 'local0':
                 continue
 
-            question = "Would you like an address to interface {} [Y/n]? ".format(name)
+            question = "Would you like add address to interface {} [Y/n]? ".format(name)
             answer = self._ask_user_yn(question, 'y')
             if answer == 'y':
                 address = {}
-                addr, plen = self._ask_user_ipv4()
+                addr = self._ask_user_ipv4()
                 address['name'] = name
                 address['addr'] = addr
-                address['plen'] = plen
                 interfaces_with_ip.append(address)
 
         return interfaces_with_ip
@@ -1527,8 +1521,7 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores))
             for ints in ints_with_addrs:
                 name = ints['name']
                 addr = ints['addr']
-                plen = ints['plen']
-                setipstr = 'set int ip address {} {}/{}\n'.format(name, addr, plen)
+                setipstr = 'set int ip address {} {}\n'.format(name, addr)
                 setintupstr = 'set int state {} up\n'.format(name)
                 content += setipstr + setintupstr
 
@@ -1546,4 +1539,118 @@ other than VPP? [0-{}][0]? '.format(str(max_other_cores))
 
             print("\nA script as been created at {}".format(filename))
             print("This script can be run using the following:")
-            print("vppctl exec {}\n".format(filename))
\ No newline at end of file
+            print("vppctl exec {}\n".format(filename))
+
+    def _create_vints_questions(self, node):
+        """
+        Ask the user some questions and get a list of interfaces
+        and IPv4 addresses associated with those interfaces
+
+        :param node: Node dictionary.
+        :type node: dict
+        :returns: A list or interfaces with ip addresses
+        :rtype: list
+        """
+
+        vpputl = VPPUtil()
+        interfaces = vpputl.get_hardware(node)
+        if interfaces == {}:
+            return []
+
+        # First delete all the Virtual interfaces
+        for intf in sorted(interfaces.items()):
+            name = intf[0]
+            if name[:7] == 'Virtual':
+                cmd = 'vppctl delete vhost-user {}'.format(name)
+                (ret, stdout, stderr) = vpputl.exec_command(cmd)
+                if ret != 0:
+                    logging.debug('{} failed on node {} {}'.format(
+                        cmd, node['host'], stderr))
+
+        # Create a virtual interface, for each interface the user wants to use
+        interfaces = vpputl.get_hardware(node)
+        if interfaces == {}:
+            return []
+        interfaces_with_virtual_interfaces = []
+        inum = 1
+        for intf in sorted(interfaces.items()):
+            name = intf[0]
+            if name == 'local0':
+                continue
+
+            question = "Would you like connect this interface {} to the VM [Y/n]? ".format(name)
+            answer = self._ask_user_yn(question, 'y')
+            if answer == 'y':
+                sockfilename = '/tmp/sock{}.sock'.format(inum)
+                if os.path.exists(sockfilename):
+                    os.remove(sockfilename)
+                cmd = 'vppctl create vhost-user socket {} server'.format(sockfilename)
+                (ret, stdout, stderr) = vpputl.exec_command(cmd)
+                if ret != 0:
+                    raise RuntimeError("Create vhost failed on node {} {}."
+                                       .format(node['host'], stderr))
+                vintname = stdout.rstrip('\r\n')
+
+                interface = {'name': name, 'virtualinterface': '{}'.format(vintname),
+                             'bridge': '{}'.format(inum)}
+                inum += 1
+                interfaces_with_virtual_interfaces.append(interface)
+
+        return interfaces_with_virtual_interfaces
+
+    def create_and_bridge_virtual_interfaces(self):
+        """
+        After asking the user some questions, create a VM and connect the interfaces
+        to VPP interfaces
+
+        """
+
+        for i in self._nodes.items():
+            node = i[1]
+
+            # Show the current bridge and interface configuration
+            print "\nThis the current bridge configuration:"
+            VPPUtil.show_bridge(node)
+            question = "\nWould you like to keep this configuration [Y/n]? "
+            answer = self._ask_user_yn(question, 'y')
+            if answer == 'y':
+                continue
+
+            # Create a script that builds a bridge configuration with physical interfaces
+            # and virtual interfaces
+            ints_with_vints = self._create_vints_questions(node)
+            content = ''
+            for intf in ints_with_vints:
+                vhoststr = 'comment { The following command creates the socket }\n'
+                vhoststr += 'comment { and returns a virtual interface }\n'
+                vhoststr += 'comment {{ create vhost-user socket /tmp/sock{}.sock server }}\n'. \
+                    format(intf['bridge'])
+
+                setintdnstr = 'set interface state {} down\n'.format(intf['name'])
+
+                setintbrstr = 'set interface l2 bridge {} {}\n'.format(intf['name'], intf['bridge'])
+                setvintbrstr = 'set interface l2 bridge {} {}\n'.format(intf['virtualinterface'], intf['bridge'])
+
+                # set interface state VirtualEthernet/0/0/0 up
+                setintvststr = 'set interface state {} up\n'.format(intf['virtualinterface'])
+
+                # set interface state VirtualEthernet/0/0/0 down
+                setintupstr = 'set interface state {} up\n'.format(intf['name'])
+
+                content += vhoststr + setintdnstr + setintbrstr + setvintbrstr + setintvststr + setintupstr
+
+            # Write the content to the script
+            rootdir = node['rootdir']
+            filename = rootdir + '/vpp/vpp-config/scripts/create_vms_and_connect_to_vpp'
+            with open(filename, 'w+') as sfile:
+                sfile.write(content)
+
+            # Execute the script
+            cmd = 'vppctl exec {}'.format(filename)
+            (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
+            if ret != 0:
+                logging.debug(stderr)
+
+            print("\nA script as been created at {}".format(filename))
+            print("This script can be run using the following:")
+            print("vppctl exec {}\n".format(filename))
index f042e80..4551cf4 100644 (file)
@@ -140,8 +140,11 @@ class VPPUtil(object):
                 stderr))
 
         reps = 'deb [trusted=yes] https://nexus.fd.io/content/'
-        reps += 'repositories/fd.io.stable.{}.ubuntu.{}.main/ ./\n' \
-            .format(fdio_release, ubuntu_version)
+        # When using a stable branch
+        # reps += 'repositories/fd.io.stable.{}.ubuntu.{}.main/ ./\n' \
+        #    .format(fdio_release, ubuntu_version)
+        reps += 'repositories/fd.io.ubuntu.{}.main/ ./\n' \
+            .format(ubuntu_version)
 
         cmd = 'echo "{0}" | sudo tee {1}'.format(reps, sfile)
         (ret, stdout, stderr) = self.exec_command(cmd)
@@ -201,8 +204,11 @@ class VPPUtil(object):
 
         reps = '[fdio-stable-{}]\n'.format(fdio_release)
         reps += 'name=fd.io stable/{} branch latest merge\n'.format(fdio_release)
-        reps += 'baseurl=https://nexus.fd.io/content/repositories/fd.io.stable.{}.{}/\n'.\
-            format(fdio_release, centos_version)
+        # When using stable
+        # reps += 'baseurl=https://nexus.fd.io/content/repositories/fd.io.stable.{}.{}/\n'.\
+        #     format(fdio_release, centos_version)
+        reps += 'baseurl=https://nexus.fd.io/content/repositories/fd.io.{}/\n'.\
+            format(centos_version)
         reps += 'enabled=1\n'
         reps += 'gpgcheck=0'
 
@@ -336,6 +342,10 @@ class VPPUtil(object):
         :param node: Node dictionary with cpuinfo.
         :type node: dict
         """
+
+        # First stop VPP
+        self.stop(node)
+
         distro = self.get_linux_distro()
         if distro[0] == 'Ubuntu':
             self._uninstall_vpp_ubuntu(node)
@@ -372,6 +382,23 @@ class VPPUtil(object):
                 for _, value in def_setting_tb_displayed.items():
                     self.exec_command('vppctl sh {}'.format(value))
 
+    @staticmethod
+    def get_vms(node):
+        """
+        Get a list of VMs that are connected to VPP interfaces
+
+        :param node: VPP node.
+        :type node: dict
+        :returns: Dictionary containing a list of VMs and the interfaces that are connected to VPP
+        :rtype: dictionary
+        """
+
+        vmdict = {}
+
+        print "Need to implement get vms"
+        
+        return vmdict
+
     @staticmethod
     def get_int_ip(node):
         """
@@ -393,6 +420,7 @@ class VPPUtil(object):
             if lines[0].split(' ')[0] == 'FileNotFoundError':
                 return interfaces
 
+        name = ''
         for line in lines:
             if len(line) is 0:
                 continue
@@ -582,6 +610,23 @@ class VPPUtil(object):
 
         return 0
 
+    @staticmethod
+    def restart(node):
+        """
+
+        Starts vpp for a given node
+
+        :param node: VPP node.
+        :type node: dict
+        """
+
+        cmd = 'service vpp restart'
+        (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
+        if ret != 0:
+            raise RuntimeError('{} failed on node {} {} {}'.
+                               format(cmd, node['host'],
+                                      stdout, stderr))
+
     @staticmethod
     def start(node):
         """
@@ -699,3 +744,40 @@ class VPPUtil(object):
             version[dct[0]] = dct[1].lstrip(' ')
 
         return version
+
+    @staticmethod
+    def show_bridge(node):
+        """
+        Shows the current bridge configuration
+
+        :param node: VPP node.
+        :type node: dict
+        """
+
+        cmd = 'vppctl show bridge'
+        (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
+        if ret != 0:
+            raise RuntimeError('{} failed on node {} {} {}'.
+                               format(cmd, node['host'],
+                                      stdout, stderr))
+        lines = stdout.split('\r\n')
+        bridges = []
+        for line in lines:
+            if line == 'no bridge-domains in use':
+                print line
+                return
+            if len(line) == 0:
+                continue
+
+            lspl = line.lstrip(' ').split()
+            if lspl[0] != 'BD-ID':
+                bridges.append(lspl[0])
+
+        for bridge in bridges:
+            cmd = 'vppctl show bridge {} detail'.format(bridge)
+            (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
+            if ret != 0:
+                raise RuntimeError('{} failed on node {} {} {}'.
+                                   format(cmd, node['host'],
+                                          stdout, stderr))
+            print stdout
index 4aac427..1723170 100644 (file)
@@ -101,12 +101,13 @@ class VppGrubUtil(object):
         value = cmdline.split('{}='.format(grubcmdline))[1]
         value = value.rstrip('"').lstrip('"')
 
-        iommu = re.findall(r'iommu=\w+', value)
-        pstate = re.findall(r'intel_pstate=\w+', value)
+        # jadfix intel_pstate=disable sometimes cause networks to hang on reboot
+        # iommu = re.findall(r'iommu=\w+', value)
+        # pstate = re.findall(r'intel_pstate=\w+', value)
         # If there is already some iommu commands set, leave them,
         # if not use ours
-        if iommu == [] and pstate == []:
-            value = '{} intel_pstate=disable'.format(value)
+        if iommu == [] and pstate == []:
+        #    value = '{} intel_pstate=disable'.format(value)
 
         # Replace isolcpus with ours
         isolcpus = re.findall(r'isolcpus=[\w+\-,]+', value)
index 829d66a..591dfab 100644 (file)
@@ -251,11 +251,12 @@ class VppPCIUtil(object):
 
         :param devices: A list of devices
         :param show_interfaces: show the kernel information
+        :param show_header: Display the header if true
         :type devices: dict
         :type show_interfaces: bool
+        :type show_header: bool
         """
 
-
         if show_interfaces:
             header = "{:15} {:25} {:50}".format("PCI ID",
                                                 "Kernel Interface(s)",
@@ -265,8 +266,7 @@ class VppPCIUtil(object):
                                           "Description")
         dashseparator = ("-" * (len(header) - 2))
 
-
-        if show_header == True:
+        if show_header is True:
             print header
             print dashseparator
         for dit in devices.items():
@@ -298,7 +298,6 @@ class VppPCIUtil(object):
         :type device_id: string
         """
 
-
         rootdir = node['rootdir']
         dpdk_script = rootdir + DPDK_SCRIPT
         cmd = dpdk_script + ' -u ' + ' ' + device_id