1 # Copyright (c) 2016 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """VPP util library"""
20 from collections import Counter
22 # VPP_VERSION = '1707'
23 # VPP_VERSION = '1710'
27 class VPPUtil(object):
28 """General class for any VPP related methods/functions."""
31 def exec_command(cmd, timeout=None):
32 """Execute a command on the local node.
34 :param cmd: Command to run locally.
35 :param timeout: Timeout value
38 :return return_code, stdout, stderr
39 :rtype: tuple(int, str, str)
42 logging.info(" Local Command: {}".format(cmd))
45 prc = subprocess.Popen(cmd, shell=True, bufsize=1,
46 stdin=subprocess.PIPE,
47 stdout=subprocess.PIPE,
48 stderr=subprocess.PIPE)
51 for line in iter(prc.stdout.readline, b''):
52 logging.info(" {}".format(line.strip('\n')))
56 for line in iter(prc.stderr.readline, b''):
57 logging.warn(" {}".format(line.strip('\n')))
64 def _autoconfig_backup_file(self, filename):
68 :param filename: The file to backup
72 # Does a copy of the file exist, if not create one
73 ofile = filename + '.orig'
74 (ret, stdout, stderr) = self.exec_command('ls {}'.format(ofile))
77 if stdout.strip('\n') != ofile:
78 cmd = 'sudo cp {} {}'.format(filename, ofile)
79 (ret, stdout, stderr) = self.exec_command(cmd)
83 def _install_vpp_pkg_ubuntu(self, node, pkg):
85 Install the VPP packages
87 :param node: Node dictionary
88 :param pkg: The vpp packages
93 cmd = 'apt-get -y install {}'.format(pkg)
94 (ret, stdout, stderr) = self.exec_command(cmd)
96 raise RuntimeError('{} failed on node {} {} {}'.format(
97 cmd, node['host'], stdout, stderr))
99 def _install_vpp_pkg_centos(self, node, pkg):
101 Install the VPP packages
103 :param node: Node dictionary
104 :param pkg: The vpp packages
109 cmd = 'yum -y install {}'.format(pkg)
110 (ret, stdout, stderr) = self.exec_command(cmd)
112 raise RuntimeError('{} failed on node {} {} {}'.format(
113 cmd, node['host'], stdout, stderr))
115 def _install_vpp_ubuntu(self, node, fdio_release=VPP_VERSION,
116 ubuntu_version='xenial'):
118 Install the VPP packages
120 :param node: Node dictionary with cpuinfo.
121 :param fdio_release: VPP release number
122 :param ubuntu_version: Ubuntu Version
124 :type fdio_release: string
125 :type ubuntu_version: string
128 # Modify the sources list
129 sfile = '/etc/apt/sources.list.d/99fd.io.list'
131 # Backup the sources list
132 self._autoconfig_backup_file(sfile)
134 # Remove the current file
135 cmd = 'rm {}'.format(sfile)
136 (ret, stdout, stderr) = self.exec_command(cmd)
138 logging.debug('{} failed on node {} {}'.format(
143 reps = 'deb [trusted=yes] https://nexus.fd.io/content/'
144 # When using a stable branch
145 # reps += 'repositories/fd.io.stable.{}.ubuntu.{}.main/ ./\n'.format(fdio_release, ubuntu_version)
147 reps += 'repositories/fd.io.ubuntu.{}.main/ ./\n'.format(ubuntu_version)
149 cmd = 'echo "{0}" | sudo tee {1}'.format(reps, sfile)
150 (ret, stdout, stderr) = self.exec_command(cmd)
152 raise RuntimeError('{} failed on node {} {}'.format(
157 # Install the package
158 cmd = 'apt-get -y update'
159 (ret, stdout, stderr) = self.exec_command(cmd)
161 raise RuntimeError('{} apt-get update failed on node {} {}'.format(
166 self._install_vpp_pkg_ubuntu(node, 'vpp-lib')
167 self._install_vpp_pkg_ubuntu(node, 'vpp')
168 self._install_vpp_pkg_ubuntu(node, 'vpp-plugins')
169 self._install_vpp_pkg_ubuntu(node, 'vpp-dpdk-dkms')
170 self._install_vpp_pkg_ubuntu(node, 'vpp-dpdk-dev')
171 self._install_vpp_pkg_ubuntu(node, 'vpp-api-python')
172 self._install_vpp_pkg_ubuntu(node, 'vpp-api-java')
173 self._install_vpp_pkg_ubuntu(node, 'vpp-api-lua')
174 self._install_vpp_pkg_ubuntu(node, 'vpp-dev')
175 self._install_vpp_pkg_ubuntu(node, 'vpp-dbg')
177 def _install_vpp_centos(self, node, fdio_release=VPP_VERSION,
178 centos_version='centos7'):
180 Install the VPP packages
182 :param node: Node dictionary with cpuinfo.
183 :param fdio_release: VPP release number
184 :param centos_version: Ubuntu Version
186 :type fdio_release: string
187 :type centos_version: string
190 # Modify the sources list
191 sfile = '/etc/yum.repos.d/fdio-release.repo'
193 # Backup the sources list
194 self._autoconfig_backup_file(sfile)
196 # Remove the current file
197 cmd = 'rm {}'.format(sfile)
198 (ret, stdout, stderr) = self.exec_command(cmd)
200 logging.debug('{} failed on node {} {}'.format(
205 reps = '[fdio-stable-{}]\n'.format(fdio_release)
206 reps += 'name=fd.io stable/{} branch latest merge\n'.format(fdio_release)
208 # reps += 'baseurl=https://nexus.fd.io/content/repositories/fd.io.stable.{}.{}/\n'.\
209 # format(fdio_release, centos_version)
211 reps += 'baseurl=https://nexus.fd.io/content/repositories/fd.io.{}/\n'.format(centos_version)
212 reps += 'enabled=1\n'
215 cmd = 'echo "{0}" | sudo tee {1}'.format(reps, sfile)
216 (ret, stdout, stderr) = self.exec_command(cmd)
218 raise RuntimeError('{} failed on node {} {}'.format(
223 # Install the packages
225 self._install_vpp_pkg_centos(node, 'vpp-lib')
226 self._install_vpp_pkg_centos(node, 'vpp')
227 self._install_vpp_pkg_centos(node, 'vpp-plugins')
228 # jadfix Check with Ole
229 # self._install_vpp_pkg_centos(node, 'vpp-dpdk-devel')
230 self._install_vpp_pkg_centos(node, 'vpp-api-python')
231 self._install_vpp_pkg_centos(node, 'vpp-api-java')
232 self._install_vpp_pkg_centos(node, 'vpp-api-lua')
233 self._install_vpp_pkg_centos(node, 'vpp-devel')
235 def install_vpp(self, node):
237 Install the VPP packages
239 :param node: Node dictionary with cpuinfo.
242 distro = self.get_linux_distro()
243 if distro[0] == 'Ubuntu':
244 self._install_vpp_ubuntu(node)
245 elif distro[0] == 'CentOS Linux':
246 logging.info("Install CentOS")
247 self._install_vpp_centos(node)
251 def _uninstall_vpp_pkg_ubuntu(self, node, pkg):
253 Uninstall the VPP packages
255 :param node: Node dictionary
256 :param pkg: The vpp packages
260 cmd = 'dpkg --purge {}'.format(pkg)
261 (ret, stdout, stderr) = self.exec_command(cmd)
263 raise RuntimeError('{} failed on node {} {} {}'.format(
264 cmd, node['host'], stdout, stderr))
266 def _uninstall_vpp_pkg_centos(self, node, pkg):
268 Uninstall the VPP packages
270 :param node: Node dictionary
271 :param pkg: The vpp packages
275 cmd = 'yum -y remove {}'.format(pkg)
276 (ret, stdout, stderr) = self.exec_command(cmd)
278 raise RuntimeError('{} failed on node {} {} {}'.format(
279 cmd, node['host'], stdout, stderr))
281 def _uninstall_vpp_ubuntu(self, node):
283 Uninstall the VPP packages
285 :param node: Node dictionary with cpuinfo.
288 pkgs = self.get_installed_vpp_pkgs()
291 if 'version' in pkgs[0]:
292 logging.info("Uninstall Ubuntu Packages")
293 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-api-python')
294 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-api-java')
295 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-api-lua')
296 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-plugins')
297 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-dpdk-dev')
298 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-dpdk-dkms')
299 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-dev')
300 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-dbg')
301 self._uninstall_vpp_pkg_ubuntu(node, 'vpp')
302 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-lib')
304 logging.info("Uninstall locally installed Ubuntu Packages")
306 self._uninstall_vpp_pkg_ubuntu(node, pkg['name'])
308 logging.error("There are no Ubuntu packages installed")
310 def _uninstall_vpp_centos(self, node):
312 Uninstall the VPP packages
314 :param node: Node dictionary with cpuinfo.
318 pkgs = self.get_installed_vpp_pkgs()
321 if 'version' in pkgs[0]:
322 logging.info("Uninstall CentOS Packages")
323 self._uninstall_vpp_pkg_centos(node, 'vpp-api-python')
324 self._uninstall_vpp_pkg_centos(node, 'vpp-api-java')
325 self._uninstall_vpp_pkg_centos(node, 'vpp-api-lua')
326 self._uninstall_vpp_pkg_centos(node, 'vpp-plugins')
327 self._uninstall_vpp_pkg_centos(node, 'vpp-dpdk-devel')
328 self._uninstall_vpp_pkg_centos(node, 'vpp-devel')
329 self._uninstall_vpp_pkg_centos(node, 'vpp')
330 self._uninstall_vpp_pkg_centos(node, 'vpp-lib')
332 logging.info("Uninstall locally installed CentOS Packages")
334 self._uninstall_vpp_pkg_centos(node, pkg['name'])
336 logging.error("There are no CentOS packages installed")
338 def uninstall_vpp(self, node):
340 Uninstall the VPP packages
342 :param node: Node dictionary with cpuinfo.
349 distro = self.get_linux_distro()
350 if distro[0] == 'Ubuntu':
351 self._uninstall_vpp_ubuntu(node)
352 elif distro[0] == 'CentOS Linux':
353 logging.info("Uninstall CentOS")
354 self._uninstall_vpp_centos(node)
358 def show_vpp_settings(self, *additional_cmds):
360 Print default VPP settings. In case others are needed, can be
361 accepted as next parameters (each setting one parameter), preferably
364 :param additional_cmds: Additional commands that the vpp should print
366 :type additional_cmds: tuple
368 def_setting_tb_displayed = {
369 'IPv6 FIB': 'ip6 fib',
370 'IPv4 FIB': 'ip fib',
371 'Interface IP': 'int addr',
378 for cmd in additional_cmds:
379 def_setting_tb_displayed['Custom Setting: {}'.format(cmd)] \
382 for _, value in def_setting_tb_displayed.items():
383 self.exec_command('vppctl sh {}'.format(value))
388 Get a list of VMs that are connected to VPP interfaces
390 :param node: VPP node.
392 :returns: Dictionary containing a list of VMs and the interfaces that are connected to VPP
398 print "Need to implement get vms"
403 def get_int_ip(node):
405 Get the VPP interfaces and IP addresses
407 :param node: VPP node.
409 :returns: Dictionary containing VPP interfaces and IP addresses
413 cmd = 'vppctl show int addr'
414 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
418 lines = stdout.split('\n')
419 if len(lines[0]) is not 0:
420 if lines[0].split(' ')[0] == 'FileNotFoundError':
428 # If the first character is not whitespace
429 # create a new interface
430 if len(re.findall(r'\s', line[0])) is 0:
435 interfaces[name] = {}
436 interfaces[name]['state'] = spl[1].lstrip('(').rstrip('):\r')
438 interfaces[name]['address'] = line.lstrip(' ').rstrip('\r')
443 def get_hardware(node):
445 Get the VPP hardware information and return it in a
448 :param node: VPP node.
450 :returns: Dictionary containing VPP hardware information
455 cmd = 'vppctl show hard'
456 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
460 lines = stdout.split('\n')
461 if len(lines[0]) is not 0:
462 if lines[0].split(' ')[0] == 'FileNotFoundError':
469 # If the first character is not whitespace
470 # create a new interface
471 if len(re.findall(r'\s', line[0])) is 0:
474 interfaces[name] = {}
475 interfaces[name]['index'] = spl[1]
476 interfaces[name]['state'] = spl[2]
479 rfall = re.findall(r'Ethernet address', line)
482 interfaces[name]['mac'] = spl[2]
485 rfall = re.findall(r'carrier', line)
487 spl = line.split('carrier ')
488 interfaces[name]['carrier'] = spl[1]
491 rfall = re.findall(r'cpu socket', line)
493 spl = line.split('cpu socket ')
494 interfaces[name]['cpu socket'] = spl[1]
496 # Queues and Descriptors
497 rfall = re.findall(r'rx queues', line)
499 spl = line.split(',')
500 interfaces[name]['rx queues'] = spl[0].lstrip(' ').split(' ')[2]
501 interfaces[name]['rx descs'] = spl[1].split(' ')[3]
502 interfaces[name]['tx queues'] = spl[2].split(' ')[3]
503 interfaces[name]['tx descs'] = spl[3].split(' ')[3]
507 def _get_installed_vpp_pkgs_ubuntu(self):
509 Get the VPP hardware information and return it in a
512 :returns: List of the packages installed
517 cmd = 'dpkg -l | grep vpp'
518 (ret, stdout, stderr) = self.exec_command(cmd)
522 lines = stdout.split('\n')
527 pkg = {'name': items[1], 'version': items[2]}
532 def _get_installed_vpp_pkgs_centos(self):
534 Get the VPP hardware information and return it in a
537 :returns: List of the packages installed
542 cmd = 'rpm -qa | grep vpp'
543 (ret, stdout, stderr) = self.exec_command(cmd)
547 lines = stdout.split('\n')
554 pkg = {'name': items[0]}
556 pkg = {'name': items[1], 'version': items[2]}
562 def get_installed_vpp_pkgs(self):
564 Get the VPP hardware information and return it in a
567 :returns: List of the packages installed
571 distro = self.get_linux_distro()
572 if distro[0] == 'Ubuntu':
573 pkgs = self._get_installed_vpp_pkgs_ubuntu()
574 elif distro[0] == 'CentOS Linux':
575 pkgs = self._get_installed_vpp_pkgs_centos()
582 def get_interfaces_numa_node(node, *iface_keys):
583 """Get numa node on which are located most of the interfaces.
585 Return numa node with highest count of interfaces provided as arguments.
586 Return 0 if the interface does not have numa_node information available.
587 If all interfaces have unknown location (-1), then return 0.
588 If most of interfaces have unknown location (-1), but there are
589 some interfaces with known location, then return the second most
590 location of the provided interfaces.
592 :param node: Node from DICT__nodes.
593 :param iface_keys: Interface keys for lookup.
595 :type iface_keys: strings
598 for if_key in iface_keys:
600 numa_list.append(node['interfaces'][if_key].get('numa_node'))
604 numa_cnt_mc = Counter(numa_list).most_common()
605 numa_cnt_mc_len = len(numa_cnt_mc)
606 if numa_cnt_mc_len > 0 and numa_cnt_mc[0][0] != -1:
607 return numa_cnt_mc[0][0]
608 elif numa_cnt_mc_len > 1 and numa_cnt_mc[0][0] == -1:
609 return numa_cnt_mc[1][0]
617 Starts vpp for a given node
619 :param node: VPP node.
623 cmd = 'service vpp restart'
624 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
626 raise RuntimeError('{} failed on node {} {} {}'.
627 format(cmd, node['host'],
634 Starts vpp for a given node
636 :param node: VPP node.
640 cmd = 'service vpp start'
641 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
643 raise RuntimeError('{} failed on node {} {} {}'.
644 format(cmd, node['host'],
651 Stops vpp for a given node
653 :param node: VPP node.
657 cmd = 'service vpp stop'
658 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
660 raise RuntimeError('{} failed on node {} {} {}'.
661 format(cmd, node['host'],
664 # noinspection RegExpRedundantEscape
673 :returns: status, errors
674 :rtype: tuple(str, list)
678 pkgs = vutil.get_installed_vpp_pkgs()
680 return "Not Installed", errors
682 cmd = 'service vpp status'
683 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
685 # Get the active status
686 state = re.findall(r'Active:[\w (\)]+', stdout)[0].split(' ')
688 statestr = "{} {}".format(state[1], state[2])
692 # For now we won't look for DPDK errors
693 # lines = stdout.split('\n')
695 # if 'EAL' in line or \
696 # 'FAILURE' in line or \
697 # 'failed' in line or \
699 # errors.append(line.lstrip(' '))
701 return statestr, errors
704 def get_linux_distro():
706 Get the linux distribution and check if it is supported
708 :returns: linux distro, None if the distro is not supported
712 distro = platform.linux_distribution()
713 if distro[0] == 'Ubuntu' or \
714 distro[0] == 'CentOS Linux' or \
715 distro[:26] == 'Linux Distribution Red Hat':
718 raise RuntimeError('Linux Distribution {} is not supported'.format(distro[0]))
724 Gets VPP Version information
731 cmd = 'vppctl show version verbose'
732 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
736 lines = stdout.split('\n')
737 if len(lines[0]) is not 0:
738 if lines[0].split(' ')[0] == 'FileNotFoundError':
744 dct = line.split(':')
745 version[dct[0]] = dct[1].lstrip(' ')
750 def show_bridge(node):
752 Shows the current bridge configuration
754 :param node: VPP node.
756 :returns: A list of interfaces
760 cmd = 'vppctl show bridge'
761 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
763 raise RuntimeError('{} failed on node {} {} {}'.
764 format(cmd, node['host'],
766 lines = stdout.split('\r\n')
769 if line == 'no bridge-domains in use':
775 lspl = line.lstrip(' ').split()
776 if lspl[0] != 'BD-ID':
777 bridges.append(lspl[0])
779 for bridge in bridges:
780 cmd = 'vppctl show bridge {} detail'.format(bridge)
781 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
783 raise RuntimeError('{} failed on node {} {} {}'.
784 format(cmd, node['host'],
787 lines = stdout.split('\r\n')
789 iface = re.findall(r'[a-zA-z]+\d+/\d+/\d+', line)
791 ifcidx ={'name': iface[0], 'index': line.split()[1] }
792 ifaces.append(ifcidx)