Add support for 18.01.
[vpp.git] / extras / vpp_config / vpplib / VPPUtil.py
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:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
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.
13
14 """VPP util library"""
15 import logging
16 import re
17 import subprocess
18 import platform
19
20 from collections import Counter
21
22 # VPP_VERSION = '1707'
23 # VPP_VERSION = '1710'
24 VPP_VERSION = '1801'
25
26
27 class VPPUtil(object):
28     """General class for any VPP related methods/functions."""
29
30     @staticmethod
31     def exec_command(cmd, timeout=None):
32         """Execute a command on the local node.
33
34         :param cmd: Command to run locally.
35         :param timeout: Timeout value
36         :type cmd: str
37         :type timeout: int
38         :return return_code, stdout, stderr
39         :rtype: tuple(int, str, str)
40         """
41
42         logging.info(" Local Command: {}".format(cmd))
43         out = ''
44         err = ''
45         prc = subprocess.Popen(cmd, shell=True, bufsize=1,
46                                stdin=subprocess.PIPE,
47                                stdout=subprocess.PIPE,
48                                stderr=subprocess.PIPE)
49
50         with prc.stdout:
51             for line in iter(prc.stdout.readline, b''):
52                 logging.info("  {}".format(line.strip('\n')))
53                 out += line
54
55         with prc.stderr:
56             for line in iter(prc.stderr.readline, b''):
57                 logging.warn("  {}".format(line.strip('\n')))
58                 err += line
59
60         ret = prc.wait()
61
62         return ret, out, err
63
64     def _autoconfig_backup_file(self, filename):
65         """
66         Create a backup file.
67
68         :param filename: The file to backup
69         :type filename: str
70         """
71
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))
75         if ret != 0:
76             logging.debug(stderr)
77             if stdout.strip('\n') != ofile:
78                 cmd = 'sudo cp {} {}'.format(filename, ofile)
79                 (ret, stdout, stderr) = self.exec_command(cmd)
80                 if ret != 0:
81                     logging.debug(stderr)
82
83     def _install_vpp_pkg_ubuntu(self, node, pkg):
84         """
85         Install the VPP packages
86
87         :param node: Node dictionary
88         :param pkg: The vpp packages
89         :type node: dict
90         :type pkg: string
91         """
92
93         cmd = 'apt-get -y install {}'.format(pkg)
94         (ret, stdout, stderr) = self.exec_command(cmd)
95         if ret != 0:
96             raise RuntimeError('{} failed on node {} {} {}'.format(
97                 cmd, node['host'], stdout, stderr))
98
99     def _install_vpp_pkg_centos(self, node, pkg):
100         """
101         Install the VPP packages
102
103         :param node: Node dictionary
104         :param pkg: The vpp packages
105         :type node: dict
106         :type pkg: string
107         """
108
109         cmd = 'yum -y install {}'.format(pkg)
110         (ret, stdout, stderr) = self.exec_command(cmd)
111         if ret != 0:
112             raise RuntimeError('{} failed on node {} {} {}'.format(
113                 cmd, node['host'], stdout, stderr))
114
115     def _install_vpp_ubuntu(self, node, fdio_release=VPP_VERSION,
116                             ubuntu_version='xenial'):
117         """
118         Install the VPP packages
119
120         :param node: Node dictionary with cpuinfo.
121         :param fdio_release: VPP release number
122         :param ubuntu_version: Ubuntu Version
123         :type node: dict
124         :type fdio_release: string
125         :type ubuntu_version: string
126         """
127
128         # Modify the sources list
129         sfile = '/etc/apt/sources.list.d/99fd.io.list'
130
131         # Backup the sources list
132         self._autoconfig_backup_file(sfile)
133
134         # Remove the current file
135         cmd = 'rm {}'.format(sfile)
136         (ret, stdout, stderr) = self.exec_command(cmd)
137         if ret != 0:
138             logging.debug('{} failed on node {} {}'.format(
139                 cmd,
140                 node['host'],
141                 stderr))
142
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)
146         # When using release
147         reps += 'repositories/fd.io.ubuntu.{}.main/ ./\n'.format(ubuntu_version)
148
149         cmd = 'echo "{0}" | sudo tee {1}'.format(reps, sfile)
150         (ret, stdout, stderr) = self.exec_command(cmd)
151         if ret != 0:
152             raise RuntimeError('{} failed on node {} {}'.format(
153                 cmd,
154                 node['host'],
155                 stderr))
156
157         # Install the package
158         cmd = 'apt-get -y update'
159         (ret, stdout, stderr) = self.exec_command(cmd)
160         if ret != 0:
161             raise RuntimeError('{} apt-get update failed on node {} {}'.format(
162                 cmd,
163                 node['host'],
164                 stderr))
165
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')
176
177     def _install_vpp_centos(self, node, fdio_release=VPP_VERSION,
178                             centos_version='centos7'):
179         """
180         Install the VPP packages
181
182         :param node: Node dictionary with cpuinfo.
183         :param fdio_release: VPP release number
184         :param centos_version: Ubuntu Version
185         :type node: dict
186         :type fdio_release: string
187         :type centos_version: string
188         """
189
190         # Modify the sources list
191         sfile = '/etc/yum.repos.d/fdio-release.repo'
192
193         # Backup the sources list
194         self._autoconfig_backup_file(sfile)
195
196         # Remove the current file
197         cmd = 'rm {}'.format(sfile)
198         (ret, stdout, stderr) = self.exec_command(cmd)
199         if ret != 0:
200             logging.debug('{} failed on node {} {}'.format(
201                 cmd,
202                 node['host'],
203                 stderr))
204
205         reps = '[fdio-stable-{}]\n'.format(fdio_release)
206         reps += 'name=fd.io stable/{} branch latest merge\n'.format(fdio_release)
207         # When using stable
208         # reps += 'baseurl=https://nexus.fd.io/content/repositories/fd.io.stable.{}.{}/\n'.\
209         #     format(fdio_release, centos_version)
210         # When using release
211         reps += 'baseurl=https://nexus.fd.io/content/repositories/fd.io.{}/\n'.format(centos_version)
212         reps += 'enabled=1\n'
213         reps += 'gpgcheck=0'
214
215         cmd = 'echo "{0}" | sudo tee {1}'.format(reps, sfile)
216         (ret, stdout, stderr) = self.exec_command(cmd)
217         if ret != 0:
218             raise RuntimeError('{} failed on node {} {}'.format(
219                 cmd,
220                 node['host'],
221                 stderr))
222
223         # Install the packages
224  
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')
234
235     def install_vpp(self, node):
236         """
237         Install the VPP packages
238
239         :param node: Node dictionary with cpuinfo.
240         :type node: dict
241         """
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)
248         else:
249             return
250
251     def _uninstall_vpp_pkg_ubuntu(self, node, pkg):
252         """
253         Uninstall the VPP packages
254
255         :param node: Node dictionary
256         :param pkg: The vpp packages
257         :type node: dict
258         :type pkg: string
259         """
260         cmd = 'dpkg --purge {}'.format(pkg)
261         (ret, stdout, stderr) = self.exec_command(cmd)
262         if ret != 0:
263             raise RuntimeError('{} failed on node {} {} {}'.format(
264                 cmd, node['host'], stdout, stderr))
265
266     def _uninstall_vpp_pkg_centos(self, node, pkg):
267         """
268         Uninstall the VPP packages
269
270         :param node: Node dictionary
271         :param pkg: The vpp packages
272         :type node: dict
273         :type pkg: string
274         """
275         cmd = 'yum -y remove {}'.format(pkg)
276         (ret, stdout, stderr) = self.exec_command(cmd)
277         if ret != 0:
278             raise RuntimeError('{} failed on node {} {} {}'.format(
279                 cmd, node['host'], stdout, stderr))
280
281     def _uninstall_vpp_ubuntu(self, node):
282         """
283         Uninstall the VPP packages
284
285         :param node: Node dictionary with cpuinfo.
286         :type node: dict
287         """
288         pkgs = self.get_installed_vpp_pkgs()
289
290         if len(pkgs) > 0:
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')
303             else:
304                 logging.info("Uninstall locally installed Ubuntu Packages")
305                 for pkg in pkgs:
306                     self._uninstall_vpp_pkg_ubuntu(node, pkg['name'])
307         else:
308             logging.error("There are no Ubuntu packages installed")
309
310     def _uninstall_vpp_centos(self, node):
311         """
312         Uninstall the VPP packages
313
314         :param node: Node dictionary with cpuinfo.
315         :type node: dict
316             """
317
318         pkgs = self.get_installed_vpp_pkgs()
319
320         if len(pkgs) > 0:
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')
331             else:
332                 logging.info("Uninstall locally installed CentOS Packages")
333                 for pkg in pkgs:
334                     self._uninstall_vpp_pkg_centos(node, pkg['name'])
335         else:
336             logging.error("There are no CentOS packages installed")
337
338     def uninstall_vpp(self, node):
339         """
340         Uninstall the VPP packages
341
342         :param node: Node dictionary with cpuinfo.
343         :type node: dict
344         """
345
346         # First stop VPP
347         self.stop(node)
348
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)
355         else:
356             return
357
358     def show_vpp_settings(self, *additional_cmds):
359         """
360         Print default VPP settings. In case others are needed, can be
361         accepted as next parameters (each setting one parameter), preferably
362         in form of a string.
363
364         :param additional_cmds: Additional commands that the vpp should print
365         settings for.
366         :type additional_cmds: tuple
367         """
368         def_setting_tb_displayed = {
369             'IPv6 FIB': 'ip6 fib',
370             'IPv4 FIB': 'ip fib',
371             'Interface IP': 'int addr',
372             'Interfaces': 'int',
373             'ARP': 'ip arp',
374             'Errors': 'err'
375         }
376
377         if additional_cmds:
378             for cmd in additional_cmds:
379                 def_setting_tb_displayed['Custom Setting: {}'.format(cmd)] \
380                     = cmd
381
382                 for _, value in def_setting_tb_displayed.items():
383                     self.exec_command('vppctl sh {}'.format(value))
384
385     @staticmethod
386     def get_vms(node):
387         """
388         Get a list of VMs that are connected to VPP interfaces
389
390         :param node: VPP node.
391         :type node: dict
392         :returns: Dictionary containing a list of VMs and the interfaces that are connected to VPP
393         :rtype: dictionary
394         """
395
396         vmdict = {}
397
398         print "Need to implement get vms"
399         
400         return vmdict
401
402     @staticmethod
403     def get_int_ip(node):
404         """
405         Get the VPP interfaces and IP addresses
406
407         :param node: VPP node.
408         :type node: dict
409         :returns: Dictionary containing VPP interfaces and IP addresses
410         :rtype: dictionary
411         """
412         interfaces = {}
413         cmd = 'vppctl show int addr'
414         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
415         if ret != 0:
416             return interfaces
417
418         lines = stdout.split('\n')
419         if len(lines[0]) is not 0:
420             if lines[0].split(' ')[0] == 'FileNotFoundError':
421                 return interfaces
422
423         name = ''
424         for line in lines:
425             if len(line) is 0:
426                 continue
427
428             # If the first character is not whitespace
429             # create a new interface
430             if len(re.findall(r'\s', line[0])) is 0:
431                 spl = line.split()
432                 name = spl[0]
433                 if name == 'local0':
434                     continue
435                 interfaces[name] = {}
436                 interfaces[name]['state'] = spl[1].lstrip('(').rstrip('):\r')
437             else:
438                 interfaces[name]['address'] = line.lstrip(' ').rstrip('\r')
439
440         return interfaces
441
442     @staticmethod
443     def get_hardware(node):
444         """
445         Get the VPP hardware information and return it in a
446         dictionary
447
448         :param node: VPP node.
449         :type node: dict
450         :returns: Dictionary containing VPP hardware information
451         :rtype: dictionary
452         """
453
454         interfaces = {}
455         cmd = 'vppctl show hard'
456         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
457         if ret != 0:
458             return interfaces
459
460         lines = stdout.split('\n')
461         if len(lines[0]) is not 0:
462             if lines[0].split(' ')[0] == 'FileNotFoundError':
463                 return interfaces
464
465         for line in lines:
466             if len(line) is 0:
467                 continue
468
469             # If the first character is not whitespace
470             # create a new interface
471             if len(re.findall(r'\s', line[0])) is 0:
472                 spl = line.split()
473                 name = spl[0]
474                 interfaces[name] = {}
475                 interfaces[name]['index'] = spl[1]
476                 interfaces[name]['state'] = spl[2]
477
478             # Ethernet address
479             rfall = re.findall(r'Ethernet address', line)
480             if rfall:
481                 spl = line.split()
482                 interfaces[name]['mac'] = spl[2]
483
484             # Carrier
485             rfall = re.findall(r'carrier', line)
486             if rfall:
487                 spl = line.split('carrier ')
488                 interfaces[name]['carrier'] = spl[1]
489
490             # Socket
491             rfall = re.findall(r'cpu socket', line)
492             if rfall:
493                 spl = line.split('cpu socket ')
494                 interfaces[name]['cpu socket'] = spl[1]
495
496             # Queues and Descriptors
497             rfall = re.findall(r'rx queues', line)
498             if rfall:
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]
504
505         return interfaces
506
507     def _get_installed_vpp_pkgs_ubuntu(self):
508         """
509         Get the VPP hardware information and return it in a
510         dictionary
511
512         :returns: List of the packages installed
513         :rtype: list
514         """
515
516         pkgs = []
517         cmd = 'dpkg -l | grep vpp'
518         (ret, stdout, stderr) = self.exec_command(cmd)
519         if ret != 0:
520             return pkgs
521
522         lines = stdout.split('\n')
523         for line in lines:
524             items = line.split()
525             if len(items) < 2:
526                 continue
527             pkg = {'name': items[1], 'version': items[2]}
528             pkgs.append(pkg)
529
530         return pkgs
531
532     def _get_installed_vpp_pkgs_centos(self):
533         """
534         Get the VPP hardware information and return it in a
535         dictionary
536
537         :returns: List of the packages installed
538         :rtype: list
539         """
540
541         pkgs = []
542         cmd = 'rpm -qa | grep vpp'
543         (ret, stdout, stderr) = self.exec_command(cmd)
544         if ret != 0:
545             return pkgs
546
547         lines = stdout.split('\n')
548         for line in lines:
549             if len(line) == 0:
550                 continue
551
552             items = line.split()
553             if len(items) < 2:
554                 pkg = {'name': items[0]}
555             else:
556                 pkg = {'name': items[1], 'version': items[2]}
557
558             pkgs.append(pkg)
559
560         return pkgs
561
562     def get_installed_vpp_pkgs(self):
563         """
564         Get the VPP hardware information and return it in a
565         dictionary
566
567         :returns: List of the packages installed
568         :rtype: list
569         """
570
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()
576         else:
577             return []
578
579         return pkgs
580
581     @staticmethod
582     def get_interfaces_numa_node(node, *iface_keys):
583         """Get numa node on which are located most of the interfaces.
584
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.
591
592         :param node: Node from DICT__nodes.
593         :param iface_keys: Interface keys for lookup.
594         :type node: dict
595         :type iface_keys: strings
596         """
597         numa_list = []
598         for if_key in iface_keys:
599             try:
600                 numa_list.append(node['interfaces'][if_key].get('numa_node'))
601             except KeyError:
602                 pass
603
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]
610
611         return 0
612
613     @staticmethod
614     def restart(node):
615         """
616
617         Starts vpp for a given node
618
619         :param node: VPP node.
620         :type node: dict
621         """
622
623         cmd = 'service vpp restart'
624         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
625         if ret != 0:
626             raise RuntimeError('{} failed on node {} {} {}'.
627                                format(cmd, node['host'],
628                                       stdout, stderr))
629
630     @staticmethod
631     def start(node):
632         """
633
634         Starts vpp for a given node
635
636         :param node: VPP node.
637         :type node: dict
638         """
639
640         cmd = 'service vpp start'
641         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
642         if ret != 0:
643             raise RuntimeError('{} failed on node {} {} {}'.
644                                format(cmd, node['host'],
645                                       stdout, stderr))
646
647     @staticmethod
648     def stop(node):
649         """
650
651         Stops vpp for a given node
652
653         :param node: VPP node.
654         :type node: dict
655         """
656
657         cmd = 'service vpp stop'
658         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
659         if ret != 0:
660             raise RuntimeError('{} failed on node {} {} {}'.
661                                format(cmd, node['host'],
662                                       stdout, stderr))
663
664     # noinspection RegExpRedundantEscape
665     @staticmethod
666     def status(node):
667         """
668
669         Gets VPP status
670
671         :param: node
672         :type node: dict
673         :returns: status, errors
674         :rtype: tuple(str, list)
675         """
676         errors = []
677         vutil = VPPUtil()
678         pkgs = vutil.get_installed_vpp_pkgs()
679         if len(pkgs) == 0:
680             return "Not Installed", errors
681
682         cmd = 'service vpp status'
683         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
684
685         # Get the active status
686         state = re.findall(r'Active:[\w (\)]+', stdout)[0].split(' ')
687         if len(state) > 2:
688             statestr = "{} {}".format(state[1], state[2])
689         else:
690             statestr = "Invalid"
691
692         # For now we won't look for DPDK errors
693         # lines = stdout.split('\n')
694         # for line in lines:
695         #    if 'EAL' in line or \
696         #                     'FAILURE' in line or \
697         #                     'failed' in line or \
698         #                     'Failed' in line:
699         #         errors.append(line.lstrip(' '))
700
701         return statestr, errors
702
703     @staticmethod
704     def get_linux_distro():
705         """
706         Get the linux distribution and check if it is supported
707
708         :returns: linux distro, None if the distro is not supported
709         :rtype: list
710         """
711
712         distro = platform.linux_distribution()
713         if distro[0] == 'Ubuntu' or \
714                         distro[0] == 'CentOS Linux' or \
715                         distro[:26] == 'Linux Distribution Red Hat':
716             return distro
717         else:
718             raise RuntimeError('Linux Distribution {} is not supported'.format(distro[0]))
719
720     @staticmethod
721     def version():
722         """
723
724         Gets VPP Version information
725
726         :returns: version
727         :rtype: dict
728         """
729
730         version = {}
731         cmd = 'vppctl show version verbose'
732         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
733         if ret != 0:
734             return version
735
736         lines = stdout.split('\n')
737         if len(lines[0]) is not 0:
738             if lines[0].split(' ')[0] == 'FileNotFoundError':
739                 return version
740
741         for line in lines:
742             if len(line) is 0:
743                 continue
744             dct = line.split(':')
745             version[dct[0]] = dct[1].lstrip(' ')
746
747         return version
748
749     @staticmethod
750     def show_bridge(node):
751         """
752         Shows the current bridge configuration
753
754         :param node: VPP node.
755         :type node: dict
756         """
757
758         cmd = 'vppctl show bridge'
759         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
760         if ret != 0:
761             raise RuntimeError('{} failed on node {} {} {}'.
762                                format(cmd, node['host'],
763                                       stdout, stderr))
764         lines = stdout.split('\r\n')
765         bridges = []
766         for line in lines:
767             if line == 'no bridge-domains in use':
768                 print line
769                 return
770             if len(line) == 0:
771                 continue
772
773             lspl = line.lstrip(' ').split()
774             if lspl[0] != 'BD-ID':
775                 bridges.append(lspl[0])
776
777         for bridge in bridges:
778             cmd = 'vppctl show bridge {} detail'.format(bridge)
779             (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
780             if ret != 0:
781                 raise RuntimeError('{} failed on node {} {} {}'.
782                                    format(cmd, node['host'],
783                                           stdout, stderr))
784             print stdout