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'
26 class VPPUtil(object):
27 """General class for any VPP related methods/functions."""
30 def exec_command(cmd, timeout=None):
31 """Execute a command on the local node.
33 :param cmd: Command to run locally.
34 :param timeout: Timeout value
37 :return return_code, stdout, stderr
38 :rtype: tuple(int, str, str)
41 logging.info(" Local Command: {}".format(cmd))
44 prc = subprocess.Popen(cmd, shell=True, bufsize=1,
45 stdin=subprocess.PIPE,
46 stdout=subprocess.PIPE,
47 stderr=subprocess.PIPE)
50 for line in iter(prc.stdout.readline, b''):
51 logging.info(" {}".format(line.strip('\n')))
55 for line in iter(prc.stderr.readline, b''):
56 logging.warn(" {}".format(line.strip('\n')))
63 def _autoconfig_backup_file(self, filename):
67 :param filename: The file to backup
71 # Does a copy of the file exist, if not create one
72 ofile = filename + '.orig'
73 (ret, stdout, stderr) = self.exec_command('ls {}'.format(ofile))
76 if stdout.strip('\n') != ofile:
77 cmd = 'sudo cp {} {}'.format(filename, ofile)
78 (ret, stdout, stderr) = self.exec_command(cmd)
82 def _install_vpp_pkg_ubuntu(self, node, pkg):
84 Install the VPP packages
86 :param node: Node dictionary
87 :param pkg: The vpp packages
92 cmd = 'apt-get -y install {}'.format(pkg)
93 (ret, stdout, stderr) = self.exec_command(cmd)
95 raise RuntimeError('{} failed on node {} {} {}'.format(
96 cmd, node['host'], stdout, stderr))
98 def _install_vpp_pkg_centos(self, node, pkg):
100 Install the VPP packages
102 :param node: Node dictionary
103 :param pkg: The vpp packages
108 cmd = 'yum -y install {}'.format(pkg)
109 (ret, stdout, stderr) = self.exec_command(cmd)
111 raise RuntimeError('{} failed on node {} {} {}'.format(
112 cmd, node['host'], stdout, stderr))
114 def _install_vpp_ubuntu(self, node, fdio_release=VPP_VERSION,
115 ubuntu_version='xenial'):
117 Install the VPP packages
119 :param node: Node dictionary with cpuinfo.
120 :param fdio_release: VPP release number
121 :param ubuntu_version: Ubuntu Version
123 :type fdio_release: string
124 :type ubuntu_version: string
127 # Modify the sources list
128 sfile = '/etc/apt/sources.list.d/99fd.io.list'
130 # Backup the sources list
131 self._autoconfig_backup_file(sfile)
133 # Remove the current file
134 cmd = 'rm {}'.format(sfile)
135 (ret, stdout, stderr) = self.exec_command(cmd)
137 logging.debug('{} failed on node {} {}'.format(
142 reps = 'deb [trusted=yes] https://nexus.fd.io/content/'
143 reps += 'repositories/fd.io.stable.{}.ubuntu.{}.main/ ./\n' \
144 .format(fdio_release, ubuntu_version)
146 cmd = 'echo "{0}" | sudo tee {1}'.format(reps, sfile)
147 (ret, stdout, stderr) = self.exec_command(cmd)
149 raise RuntimeError('{} failed on node {} {}'.format(
154 # Install the package
155 cmd = 'apt-get -y update'
156 (ret, stdout, stderr) = self.exec_command(cmd)
158 raise RuntimeError('{} apt-get update failed on node {} {}'.format(
163 self._install_vpp_pkg_ubuntu(node, 'vpp-lib')
164 self._install_vpp_pkg_ubuntu(node, 'vpp')
165 self._install_vpp_pkg_ubuntu(node, 'vpp-plugins')
166 self._install_vpp_pkg_ubuntu(node, 'vpp-dpdk-dkms')
167 self._install_vpp_pkg_ubuntu(node, 'vpp-dpdk-dev')
168 self._install_vpp_pkg_ubuntu(node, 'vpp-api-python')
169 self._install_vpp_pkg_ubuntu(node, 'vpp-api-java')
170 self._install_vpp_pkg_ubuntu(node, 'vpp-api-lua')
171 self._install_vpp_pkg_ubuntu(node, 'vpp-dev')
172 self._install_vpp_pkg_ubuntu(node, 'vpp-dbg')
174 def _install_vpp_centos(self, node, fdio_release=VPP_VERSION,
175 centos_version='centos7'):
177 Install the VPP packages
179 :param node: Node dictionary with cpuinfo.
180 :param fdio_release: VPP release number
181 :param centos_version: Ubuntu Version
183 :type fdio_release: string
184 :type centos_version: string
187 # Modify the sources list
188 sfile = '/etc/yum.repos.d/fdio-release.repo'
190 # Backup the sources list
191 self._autoconfig_backup_file(sfile)
193 # Remove the current file
194 cmd = 'rm {}'.format(sfile)
195 (ret, stdout, stderr) = self.exec_command(cmd)
197 logging.debug('{} failed on node {} {}'.format(
202 reps = '[fdio-stable-{}]\n'.format(fdio_release)
203 reps += 'name=fd.io stable/{} branch latest merge\n'.format(fdio_release)
204 reps += 'baseurl=https://nexus.fd.io/content/repositories/fd.io.stable.{}.{}/\n'.\
205 format(fdio_release, centos_version)
206 reps += 'enabled=1\n'
209 cmd = 'echo "{0}" | sudo tee {1}'.format(reps, sfile)
210 (ret, stdout, stderr) = self.exec_command(cmd)
212 raise RuntimeError('{} failed on node {} {}'.format(
217 # Install the packages
219 self._install_vpp_pkg_centos(node, 'vpp-lib')
220 self._install_vpp_pkg_centos(node, 'vpp')
221 self._install_vpp_pkg_centos(node, 'vpp-plugins')
222 # jadfix Check with Ole
223 # self._install_vpp_pkg_centos(node, 'vpp-dpdk-devel')
224 self._install_vpp_pkg_centos(node, 'vpp-api-python')
225 self._install_vpp_pkg_centos(node, 'vpp-api-java')
226 self._install_vpp_pkg_centos(node, 'vpp-api-lua')
227 self._install_vpp_pkg_centos(node, 'vpp-devel')
229 def install_vpp(self, node):
231 Install the VPP packages
233 :param node: Node dictionary with cpuinfo.
236 distro = self.get_linux_distro()
237 if distro[0] == 'Ubuntu':
238 self._install_vpp_ubuntu(node)
239 elif distro[0] == 'CentOS Linux':
240 logging.info("Install CentOS")
241 self._install_vpp_centos(node)
245 def _uninstall_vpp_pkg_ubuntu(self, node, pkg):
247 Uninstall the VPP packages
249 :param node: Node dictionary
250 :param pkg: The vpp packages
254 cmd = 'dpkg --purge {}'.format(pkg)
255 (ret, stdout, stderr) = self.exec_command(cmd)
257 raise RuntimeError('{} failed on node {} {} {}'.format(
258 cmd, node['host'], stdout, stderr))
260 def _uninstall_vpp_pkg_centos(self, node, pkg):
262 Uninstall the VPP packages
264 :param node: Node dictionary
265 :param pkg: The vpp packages
269 cmd = 'yum -y remove {}'.format(pkg)
270 (ret, stdout, stderr) = self.exec_command(cmd)
272 raise RuntimeError('{} failed on node {} {} {}'.format(
273 cmd, node['host'], stdout, stderr))
275 def _uninstall_vpp_ubuntu(self, node):
277 Uninstall the VPP packages
279 :param node: Node dictionary with cpuinfo.
282 pkgs = self.get_installed_vpp_pkgs()
285 if 'version' in pkgs[0]:
286 logging.info("Uninstall Ubuntu Packages")
287 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-api-python')
288 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-api-java')
289 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-api-lua')
290 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-plugins')
291 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-dpdk-dev')
292 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-dpdk-dkms')
293 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-dev')
294 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-dbg')
295 self._uninstall_vpp_pkg_ubuntu(node, 'vpp')
296 self._uninstall_vpp_pkg_ubuntu(node, 'vpp-lib')
298 logging.info("Uninstall locally installed Ubuntu Packages")
300 self._uninstall_vpp_pkg_ubuntu(node, pkg['name'])
302 logging.error("There are no Ubuntu packages installed")
304 def _uninstall_vpp_centos(self, node):
306 Uninstall the VPP packages
308 :param node: Node dictionary with cpuinfo.
312 pkgs = self.get_installed_vpp_pkgs()
315 if 'version' in pkgs[0]:
316 logging.info("Uninstall CentOS Packages")
317 self._uninstall_vpp_pkg_centos(node, 'vpp-api-python')
318 self._uninstall_vpp_pkg_centos(node, 'vpp-api-java')
319 self._uninstall_vpp_pkg_centos(node, 'vpp-api-lua')
320 self._uninstall_vpp_pkg_centos(node, 'vpp-plugins')
321 self._uninstall_vpp_pkg_centos(node, 'vpp-dpdk-devel')
322 self._uninstall_vpp_pkg_centos(node, 'vpp-devel')
323 self._uninstall_vpp_pkg_centos(node, 'vpp')
324 self._uninstall_vpp_pkg_centos(node, 'vpp-lib')
326 logging.info("Uninstall locally installed CentOS Packages")
328 self._uninstall_vpp_pkg_centos(node, pkg['name'])
330 logging.error("There are no CentOS packages installed")
332 def uninstall_vpp(self, node):
334 Uninstall the VPP packages
336 :param node: Node dictionary with cpuinfo.
339 distro = self.get_linux_distro()
340 if distro[0] == 'Ubuntu':
341 self._uninstall_vpp_ubuntu(node)
342 elif distro[0] == 'CentOS Linux':
343 logging.info("Uninstall CentOS")
344 self._uninstall_vpp_centos(node)
348 def show_vpp_settings(self, *additional_cmds):
350 Print default VPP settings. In case others are needed, can be
351 accepted as next parameters (each setting one parameter), preferably
354 :param additional_cmds: Additional commands that the vpp should print
356 :type additional_cmds: tuple
358 def_setting_tb_displayed = {
359 'IPv6 FIB': 'ip6 fib',
360 'IPv4 FIB': 'ip fib',
361 'Interface IP': 'int addr',
368 for cmd in additional_cmds:
369 def_setting_tb_displayed['Custom Setting: {}'.format(cmd)] \
372 for _, value in def_setting_tb_displayed.items():
373 self.exec_command('vppctl sh {}'.format(value))
376 def get_hardware(node):
378 Get the VPP hardware information and return it in a
381 :param node: VPP node.
383 :returns: Dictionary containing improtant VPP information
388 cmd = 'vppctl show hard'
389 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
393 lines = stdout.split('\n')
394 if len(lines[0]) is not 0:
395 if lines[0].split(' ')[0] == 'FileNotFoundError':
402 # If the first character is not whitespace
403 # create a new interface
404 if len(re.findall(r'\s', line[0])) is 0:
407 interfaces[name] = {}
408 interfaces[name]['index'] = spl[1]
409 interfaces[name]['state'] = spl[2]
412 rfall = re.findall(r'Ethernet address', line)
415 interfaces[name]['mac'] = spl[2]
418 rfall = re.findall(r'carrier', line)
420 spl = line.split('carrier ')
421 interfaces[name]['carrier'] = spl[1]
424 rfall = re.findall(r'cpu socket', line)
426 spl = line.split('cpu socket ')
427 interfaces[name]['cpu socket'] = spl[1]
429 # Queues and Descriptors
430 rfall = re.findall(r'rx queues', line)
432 spl = line.split(',')
433 interfaces[name]['rx queues'] = spl[0].lstrip(' ').split(' ')[2]
434 interfaces[name]['rx descs'] = spl[1].split(' ')[3]
435 interfaces[name]['tx queues'] = spl[2].split(' ')[3]
436 interfaces[name]['tx descs'] = spl[3].split(' ')[3]
440 def _get_installed_vpp_pkgs_ubuntu(self):
442 Get the VPP hardware information and return it in a
445 :returns: List of the packages installed
450 cmd = 'dpkg -l | grep vpp'
451 (ret, stdout, stderr) = self.exec_command(cmd)
455 lines = stdout.split('\n')
460 pkg = {'name': items[1], 'version': items[2]}
465 def _get_installed_vpp_pkgs_centos(self):
467 Get the VPP hardware information and return it in a
470 :returns: List of the packages installed
475 cmd = 'rpm -qa | grep vpp'
476 (ret, stdout, stderr) = self.exec_command(cmd)
480 lines = stdout.split('\n')
487 pkg = {'name': items[0]}
489 pkg = {'name': items[1], 'version': items[2]}
495 def get_installed_vpp_pkgs(self):
497 Get the VPP hardware information and return it in a
500 :returns: List of the packages installed
504 distro = self.get_linux_distro()
505 if distro[0] == 'Ubuntu':
506 pkgs = self._get_installed_vpp_pkgs_ubuntu()
507 elif distro[0] == 'CentOS Linux':
508 pkgs = self._get_installed_vpp_pkgs_centos()
515 def get_interfaces_numa_node(node, *iface_keys):
516 """Get numa node on which are located most of the interfaces.
518 Return numa node with highest count of interfaces provided as arguments.
519 Return 0 if the interface does not have numa_node information available.
520 If all interfaces have unknown location (-1), then return 0.
521 If most of interfaces have unknown location (-1), but there are
522 some interfaces with known location, then return the second most
523 location of the provided interfaces.
525 :param node: Node from DICT__nodes.
526 :param iface_keys: Interface keys for lookup.
528 :type iface_keys: strings
531 for if_key in iface_keys:
533 numa_list.append(node['interfaces'][if_key].get('numa_node'))
537 numa_cnt_mc = Counter(numa_list).most_common()
538 numa_cnt_mc_len = len(numa_cnt_mc)
539 if numa_cnt_mc_len > 0 and numa_cnt_mc[0][0] != -1:
540 return numa_cnt_mc[0][0]
541 elif numa_cnt_mc_len > 1 and numa_cnt_mc[0][0] == -1:
542 return numa_cnt_mc[1][0]
550 Starts vpp for a given node
552 :param node: VPP node.
556 cmd = 'service vpp start'
557 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
559 raise RuntimeError('{} failed on node {} {} {}'.
560 format(cmd, node['host'],
567 Stops vpp for a given node
569 :param node: VPP node.
573 cmd = 'service vpp stop'
574 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
576 raise RuntimeError('{} failed on node {} {} {}'.
577 format(cmd, node['host'],
588 :returns: status, errors
589 :rtype: tuple(str, list)
593 pkgs = vutil.get_installed_vpp_pkgs()
595 return "Not Installed", errors
597 cmd = 'service vpp status'
598 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
600 # Get the active status
601 state = re.findall(r'Active:[\w (\)]+', stdout)[0].split(' ')
603 statestr = "{} {}".format(state[1], state[2])
607 # For now we won't look for DPDK errors
608 # lines = stdout.split('\n')
610 # if 'EAL' in line or \
611 # 'FAILURE' in line or \
612 # 'failed' in line or \
614 # errors.append(line.lstrip(' '))
616 return statestr, errors
619 def get_linux_distro():
621 Get the linux distribution and check if it is supported
623 :returns: linux distro, None if the distro is not supported
627 distro = platform.linux_distribution()
628 if distro[0] == 'Ubuntu' or \
629 distro[0] == 'CentOS Linux' or \
630 distro[:26] == 'Linux Distribution Red Hat':
633 raise RuntimeError('Linux Distribution {} is not supported'.format(distro[0]))
639 Gets VPP Version information
646 cmd = 'vppctl show version verbose'
647 (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
651 lines = stdout.split('\n')
652 if len(lines[0]) is not 0:
653 if lines[0].split(' ')[0] == 'FileNotFoundError':
659 dct = line.split(':')
660 version[dct[0]] = dct[1].lstrip(' ')