tests: replace pycodestyle with black
[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 from __future__ import print_function
15
16 """VPP util library"""
17 import logging
18 import re
19 import subprocess
20 import requests
21
22 from collections import Counter
23
24 import distro
25
26 ubuntu_pkgs = {
27     "release": [
28         "vpp",
29         "vpp-plugin-core",
30         "vpp-plugin-dpdk",
31         "vpp-api-python",
32         "python3-vpp-api",
33         "vpp-dbg",
34         "vpp-dev",
35         "vpp-ext-deps",
36     ],
37     "master": [
38         "vpp",
39         "vpp-plugin-core",
40         "vpp-plugin-dpdk",
41         "vpp-api-python",
42         "python3-vpp-api",
43         "vpp-dbg",
44         "vpp-dev",
45         "vpp-ext-deps",
46     ],
47 }
48
49 centos_pkgs = {
50     "release": [
51         "vpp",
52         "vpp-selinux-policy",
53         "vpp-plugins",
54         "vpp-api-lua",
55         "vpp-api-python",
56         "vpp-debuginfo",
57         "vpp-devel",
58         "libvpp0",
59         "vpp-ext-deps",
60     ],
61     "master": [
62         "vpp",
63         "vpp-selinux-policy",
64         "vpp-plugins",
65         "vpp-api-lua",
66         "vpp-api-python",
67         "vpp-debuginfo",
68         "vpp-devel",
69         "libvpp0",
70         "vpp-ext-deps",
71     ],
72 }
73
74
75 class VPPUtil(object):
76     """General class for any VPP related methods/functions."""
77
78     @staticmethod
79     def exec_command(cmd, timeout=None):
80         """Execute a command on the local node.
81
82         :param cmd: Command to run locally.
83         :param timeout: Timeout value
84         :type cmd: str
85         :type timeout: int
86         :return return_code, stdout, stderr
87         :rtype: tuple(int, str, str)
88         """
89
90         logging.info(" Local Command: {}".format(cmd))
91         out = ""
92         err = ""
93         prc = subprocess.Popen(
94             cmd,
95             shell=True,
96             bufsize=1,
97             stdin=subprocess.PIPE,
98             stdout=subprocess.PIPE,
99             stderr=subprocess.PIPE,
100         )
101
102         with prc.stdout:
103             lines = prc.stdout.readlines()
104             for line in lines:
105                 if type(line) != str:
106                     line = line.decode()
107                 logging.info("  {}".format(line.strip("\n")))
108                 out += line
109
110         with prc.stderr:
111             lines = prc.stderr.readlines()
112             for line in lines:
113                 if type(line) != str:
114                     line = line.decode()
115                 logging.warning("  {}".format(line.strip("\n")))
116                 err += line
117
118         ret = prc.wait()
119
120         return ret, out, err
121
122     def _autoconfig_backup_file(self, filename):
123         """
124         Create a backup file.
125
126         :param filename: The file to backup
127         :type filename: str
128         """
129
130         # Does a copy of the file exist, if not create one
131         ofile = filename + ".orig"
132         (ret, stdout, stderr) = self.exec_command("ls {}".format(ofile))
133         if ret != 0:
134             logging.debug(stderr)
135             if stdout.strip("\n") != ofile:
136                 cmd = "sudo cp {} {}".format(filename, ofile)
137                 (ret, stdout, stderr) = self.exec_command(cmd)
138                 if ret != 0:
139                     logging.debug(stderr)
140
141     def _install_vpp_ubuntu(self, node, branch, ubuntu_version="xenial"):
142         """
143         Install the VPP packages
144
145         :param node: Node dictionary with cpuinfo.
146         :param branch: VPP branch
147         :param ubuntu_version: Ubuntu Version
148         :type node: dict
149         :type branch: string
150         :type ubuntu_version: string
151         """
152
153         # Modify the sources list
154         sfile = "/etc/apt/sources.list.d/99fd.io.list"
155
156         # Backup the sources list
157         self._autoconfig_backup_file(sfile)
158
159         reps = "deb [trusted=yes] https://packagecloud.io/fdio/"
160         reps += "{}/ubuntu {} main\n".format(branch, ubuntu_version)
161
162         with open(sfile, "w") as sfd:
163             sfd.write(reps)
164             sfd.close()
165
166         # Add the key
167
168         key = requests.get("https://packagecloud.io/fdio/{}/gpgkey".format(branch))
169         cmd = 'echo "{}" | apt-key add -'.format(key.content.decode(key.encoding))
170         (ret, stdout, stderr) = self.exec_command(cmd)
171         if ret != 0:
172             raise RuntimeError(
173                 "{} failed on node {} {}".format(cmd, node["host"], stderr)
174             )
175
176         # Install the package
177         cmd = "apt-get -y update"
178         (ret, stdout, stderr) = self.exec_command(cmd)
179         if ret != 0:
180             raise RuntimeError(
181                 "{} apt-get update failed on node {} {}".format(
182                     cmd, node["host"], stderr
183                 )
184             )
185
186         # Get the package list
187         pkgstr = ""
188         for ps in ubuntu_pkgs[branch]:
189             pkgstr += ps + " "
190
191         cmd = "apt-get -y install {}".format(pkgstr)
192         (ret, stdout, stderr) = self.exec_command(cmd)
193         if ret != 0:
194             raise RuntimeError(
195                 "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
196             )
197
198     def _install_vpp_centos(self, node, branch):
199         """
200         Install the VPP packages
201
202         :param node: Node dictionary with cpuinfo.
203         :param branch: The branch name  release or master
204         :type node: dict
205         :type branch: string
206         """
207
208         # Be sure the correct system packages are installed
209         cmd = "yum -y update"
210         (ret, stdout, stderr) = self.exec_command(cmd)
211         if ret != 0:
212             logging.debug("{} failed on node {} {}".format(cmd, node["host"], stderr))
213
214         cmd = "yum -y install pygpgme yum-utils"
215         (ret, stdout, stderr) = self.exec_command(cmd)
216         if ret != 0:
217             logging.debug("{} failed on node {} {}".format(cmd, node["host"], stderr))
218
219         # Modify the sources list
220         sfile = "/etc/yum.repos.d/fdio-release.repo"
221
222         # Backup the sources list
223         self._autoconfig_backup_file(sfile)
224
225         # Remove the current file
226         cmd = "rm {}".format(sfile)
227         (ret, stdout, stderr) = self.exec_command(cmd)
228         if ret != 0:
229             logging.debug("{} failed on node {} {}".format(cmd, node["host"], stderr))
230
231         # Get the file contents
232
233         reps = "\n".join(
234             [
235                 "[fdio_{}]".format(branch),
236                 "name=fdio_{}".format(branch),
237                 "baseurl=https://packagecloud.io/fdio/{}/el/7/$basearch".format(branch),
238                 "repo_gpgcheck=1",
239                 "gpgcheck=0",
240                 "enabled=1",
241                 "gpgkey=https://packagecloud.io/fdio/{}/gpgkey".format(branch),
242                 "sslverify=1",
243                 "sslcacert=/etc/pki/tls/certs/ca-bundle.crt",
244                 "metadata_expire=300\n",
245                 "[fdio_{}-source]".format(branch),
246                 "name=fdio_release-{}".format(branch),
247                 "baseurl=https://packagecloud.io/fdio/{}/el/7/SRPMS".format(branch),
248                 "repo_gpgcheck=1",
249                 "gpgcheck=0",
250                 "enabled=1",
251                 "gpgkey=https://packagecloud.io/fdio/{}/gpgkey".format(branch),
252                 "sslverify =1",
253                 "sslcacert=/etc/pki/tls/certs/ca-bundle.crt",
254                 "metadata_expire=300\n",
255             ]
256         )
257         with open(sfile, "w") as sfd:
258             sfd.write(reps)
259             sfd.close()
260
261         # Update the fdio repo
262         cmd = "yum clean all"
263         (ret, stdout, stderr) = self.exec_command(cmd)
264         if ret != 0:
265             logging.debug("{} failed on node {} {}".format(cmd, node["host"], stderr))
266
267         cmd = "yum -q makecache -y --disablerepo='*' " "--enablerepo='fdio_{}'".format(
268             branch
269         )
270         (ret, stdout, stderr) = self.exec_command(cmd)
271         if ret != 0:
272             logging.debug("{} failed on node {} {}".format(cmd, node["host"], stderr))
273
274         # Get the package list
275         pkgstr = ""
276         for ps in centos_pkgs[branch]:
277             pkgstr += ps + " "
278
279         cmd = "yum -y install {}".format(pkgstr)
280         (ret, stdout, stderr) = self.exec_command(cmd)
281         if ret != 0:
282             raise RuntimeError(
283                 "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
284             )
285
286     def install_vpp(self, node, branch):
287         """
288         Install the VPP packages
289
290         :param node: Node dictionary with cpuinfo.
291         :param branch: The branch name
292         :type node: dict
293         :type branch: string
294
295         """
296         distro = self.get_linux_distro()
297         logging.info("  {}".format(distro[0]))
298         if distro[0] == "Ubuntu":
299             logging.info("Install Ubuntu")
300             self._install_vpp_ubuntu(node, branch, ubuntu_version=distro[2])
301         elif distro[0] == "CentOS Linux":
302             logging.info("Install CentOS")
303             self._install_vpp_centos(node, branch)
304         else:
305             logging.info("Install CentOS (default)")
306             self._install_vpp_centos(node, branch)
307         return
308
309     def _uninstall_vpp_ubuntu(self, node):
310         """
311         Uninstall the VPP packages
312
313         :param node: Node dictionary with cpuinfo.
314         :type node: dict
315         """
316
317         # get the package list
318         pkgstr = ""
319         pkgs = self.get_installed_vpp_pkgs()
320         for pkg in pkgs:
321             pkgname = pkg["name"]
322             pkgstr += pkgname + " "
323
324         cmd = "dpkg --purge {}".format(pkgstr)
325         (ret, stdout, stderr) = self.exec_command(cmd)
326         if ret != 0:
327             raise RuntimeError(
328                 "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
329             )
330
331     def _uninstall_vpp_centos(self, node):
332         """
333         Uninstall the VPP packages
334
335         :param node: Node dictionary with cpuinfo.
336         :type node: dict
337         """
338
339         pkgstr = ""
340         pkgs = self.get_installed_vpp_pkgs()
341         for pkg in pkgs:
342             pkgname = pkg["name"]
343             pkgstr += pkgname + " "
344
345         logging.info("Uninstalling {}".format(pkgstr))
346         cmd = "yum -y remove {}".format(pkgstr)
347         (ret, stdout, stderr) = self.exec_command(cmd)
348         if ret != 0:
349             raise RuntimeError(
350                 "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
351             )
352
353     def uninstall_vpp(self, node):
354         """
355         Uninstall the VPP packages
356
357         :param node: Node dictionary with cpuinfo.
358         :type node: dict
359         """
360
361         # First stop VPP
362         self.stop(node)
363         distro = self.get_linux_distro()
364         if distro[0] == "Ubuntu":
365             logging.info("Uninstall Ubuntu")
366             self._uninstall_vpp_ubuntu(node)
367         elif distro[0] == "CentOS Linux":
368             logging.info("Uninstall CentOS")
369             self._uninstall_vpp_centos(node)
370         else:
371             logging.info("Uninstall CentOS (Default)")
372             self._uninstall_vpp_centos(node)
373             return
374
375     def show_vpp_settings(self, *additional_cmds):
376         """
377         Print default VPP settings. In case others are needed, can be
378         accepted as next parameters (each setting one parameter), preferably
379         in form of a string.
380
381         :param additional_cmds: Additional commands that the vpp should print
382         settings for.
383         :type additional_cmds: tuple
384         """
385         def_setting_tb_displayed = {
386             "IPv6 FIB": "ip6 fib",
387             "IPv4 FIB": "ip fib",
388             "Interface IP": "int addr",
389             "Interfaces": "int",
390             "ARP": "ip arp",
391             "Errors": "err",
392         }
393
394         if additional_cmds:
395             for cmd in additional_cmds:
396                 def_setting_tb_displayed["Custom Setting: {}".format(cmd)] = cmd
397
398                 for _, value in def_setting_tb_displayed.items():
399                     self.exec_command("vppctl sh {}".format(value))
400
401     @staticmethod
402     def get_vms(node):
403         """
404         Get a list of VMs that are connected to VPP interfaces
405
406         :param node: VPP node.
407         :type node: dict
408         :returns: Dictionary containing a list of VMs and the interfaces
409                   that are connected to VPP
410         :rtype: dictionary
411         """
412
413         vmdict = {}
414
415         print("Need to implement get vms")
416
417         return vmdict
418
419     @staticmethod
420     def get_int_ip(node):
421         """
422         Get the VPP interfaces and IP addresses
423
424         :param node: VPP node.
425         :type node: dict
426         :returns: Dictionary containing VPP interfaces and IP addresses
427         :rtype: dictionary
428         """
429         interfaces = {}
430         cmd = "vppctl show int addr"
431         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
432         if ret != 0:
433             return interfaces
434
435         lines = stdout.split("\n")
436         if len(lines[0]) != 0:
437             if lines[0].split(" ")[0] == "FileNotFoundError":
438                 return interfaces
439
440         name = ""
441         for line in lines:
442             if len(line) == 0:
443                 continue
444
445             # If the first character is not whitespace
446             # create a new interface
447             if len(re.findall(r"\s", line[0])) == 0:
448                 spl = line.split()
449                 name = spl[0]
450                 if name == "local0":
451                     continue
452                 interfaces[name] = {}
453                 interfaces[name]["state"] = spl[1].lstrip("(").rstrip("):\r")
454             else:
455                 interfaces[name]["address"] = line.lstrip(" ").rstrip("\r")
456
457         return interfaces
458
459     @staticmethod
460     def get_hardware(node):
461         """
462         Get the VPP hardware information and return it in a
463         dictionary
464
465         :param node: VPP node.
466         :type node: dict
467         :returns: Dictionary containing VPP hardware information
468         :rtype: dictionary
469         """
470
471         interfaces = {}
472         cmd = "vppctl show hard"
473         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
474         if ret != 0:
475             return interfaces
476
477         lines = stdout.split("\n")
478         if len(lines[0]) != 0:
479             if lines[0].split(" ")[0] == "FileNotFoundError":
480                 return interfaces
481
482         for line in lines:
483             if len(line) == 0:
484                 continue
485
486             # If the first character is not whitespace
487             # create a new interface
488             if len(re.findall(r"\s", line[0])) == 0:
489                 spl = line.split()
490                 name = spl[0]
491                 interfaces[name] = {}
492                 interfaces[name]["index"] = spl[1]
493                 interfaces[name]["state"] = spl[2]
494
495             # Ethernet address
496             rfall = re.findall(r"Ethernet address", line)
497             if rfall:
498                 spl = line.split()
499                 interfaces[name]["mac"] = spl[2]
500
501             # Carrier
502             rfall = re.findall(r"carrier", line)
503             if rfall:
504                 spl = line.split("carrier ")
505                 interfaces[name]["carrier"] = spl[1]
506
507             # Socket
508             spl = ""
509             rfall = re.findall(r"numa \d+", line)
510             if rfall:
511                 spl = rfall[0].split()
512                 interfaces[name]["numa"] = rfall[0].split()[1]
513
514             # Queues and Descriptors
515             rfall = re.findall(r"rx\: queues \d+", line)
516             if rfall:
517                 interfaces[name]["rx queues"] = rfall[0].split()[2]
518                 rdesc = re.findall(r"desc \d+", line)
519                 if rdesc:
520                     interfaces[name]["rx descs"] = rdesc[0].split()[1]
521
522             rfall = re.findall(r"tx\: queues \d+", line)
523             if rfall:
524                 interfaces[name]["tx queues"] = rfall[0].split()[2]
525                 rdesc = re.findall(r"desc \d+", line)
526                 if rdesc:
527                     interfaces[name]["tx descs"] = rdesc[0].split()[1]
528
529         return interfaces
530
531     def _get_installed_vpp_pkgs_ubuntu(self):
532         """
533         Get the VPP hardware information and return it in a
534         dictionary
535
536         :returns: List of the packages installed
537         :rtype: list
538         """
539
540         pkgs = []
541         cmd = "dpkg -l | grep vpp"
542         (ret, stdout, stderr) = self.exec_command(cmd)
543         if ret != 0:
544             return pkgs
545
546         lines = stdout.split("\n")
547         for line in lines:
548             items = line.split()
549             if len(items) < 2:
550                 continue
551             pkg = {"name": items[1], "version": items[2]}
552             pkgs.append(pkg)
553
554         return pkgs
555
556     def _get_installed_vpp_pkgs_centos(self):
557         """
558         Get the VPP hardware information and return it in a
559         dictionary
560
561         :returns: List of the packages installed
562         :rtype: list
563         """
564
565         pkgs = []
566         cmd = "rpm -qa | grep vpp"
567         (ret, stdout, stderr) = self.exec_command(cmd)
568         if ret != 0:
569             return pkgs
570
571         lines = stdout.split("\n")
572         for line in lines:
573             if len(line) == 0:
574                 continue
575
576             items = line.split()
577             if len(items) < 2:
578                 pkg = {"name": items[0]}
579             else:
580                 pkg = {"name": items[1], "version": items[2]}
581
582             pkgs.append(pkg)
583
584         return pkgs
585
586     def get_installed_vpp_pkgs(self):
587         """
588         Get the VPP hardware information and return it in a
589         dictionary
590
591         :returns: List of the packages installed
592         :rtype: list
593         """
594
595         distro = self.get_linux_distro()
596         if distro[0] == "Ubuntu":
597             pkgs = self._get_installed_vpp_pkgs_ubuntu()
598         elif distro[0] == "CentOS Linux":
599             pkgs = self._get_installed_vpp_pkgs_centos()
600         else:
601             pkgs = self._get_installed_vpp_pkgs_centos()
602             return []
603
604         return pkgs
605
606     @staticmethod
607     def get_interfaces_numa_node(node, *iface_keys):
608         """Get numa node on which are located most of the interfaces.
609
610         Return numa node with highest count of interfaces provided as
611         arguments.
612         Return 0 if the interface does not have numa_node information
613         available.
614         If all interfaces have unknown location (-1), then return 0.
615         If most of interfaces have unknown location (-1), but there are
616         some interfaces with known location, then return the second most
617         location of the provided interfaces.
618
619         :param node: Node from DICT__nodes.
620         :param iface_keys: Interface keys for lookup.
621         :type node: dict
622         :type iface_keys: strings
623         """
624         numa_list = []
625         for if_key in iface_keys:
626             try:
627                 numa_list.append(node["interfaces"][if_key].get("numa_node"))
628             except KeyError:
629                 pass
630
631         numa_cnt_mc = Counter(numa_list).most_common()
632         numa_cnt_mc_len = len(numa_cnt_mc)
633         if numa_cnt_mc_len > 0 and numa_cnt_mc[0][0] != -1:
634             return numa_cnt_mc[0][0]
635         elif numa_cnt_mc_len > 1 and numa_cnt_mc[0][0] == -1:
636             return numa_cnt_mc[1][0]
637
638         return 0
639
640     @staticmethod
641     def restart(node):
642         """
643
644         Starts vpp for a given node
645
646         :param node: VPP node.
647         :type node: dict
648         """
649
650         cmd = "service vpp restart"
651         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
652         if ret != 0:
653             raise RuntimeError(
654                 "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
655             )
656
657     @staticmethod
658     def start(node):
659         """
660
661         Starts vpp for a given node
662
663         :param node: VPP node.
664         :type node: dict
665         """
666
667         cmd = "service vpp start"
668         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
669         if ret != 0:
670             raise RuntimeError(
671                 "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
672             )
673
674     @staticmethod
675     def stop(node):
676         """
677
678         Stops vpp for a given node
679
680         :param node: VPP node.
681         :type node: dict
682         """
683
684         cmd = "service vpp stop"
685         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
686         if ret != 0:
687             logging.debug(
688                 "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
689             )
690
691     # noinspection RegExpRedundantEscape
692     @staticmethod
693     def status(node):
694         """
695
696         Gets VPP status
697
698         :param: node
699         :type node: dict
700         :returns: status, errors
701         :rtype: tuple(str, list)
702         """
703         errors = []
704         vutil = VPPUtil()
705         pkgs = vutil.get_installed_vpp_pkgs()
706         if len(pkgs) == 0:
707             return "Not Installed", errors
708
709         cmd = "service vpp status"
710         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
711
712         # Get the active status
713         state = re.findall(r"Active:[\w (\)]+", stdout)[0].split(" ")
714         if len(state) > 2:
715             statestr = "{} {}".format(state[1], state[2])
716         else:
717             statestr = "Invalid"
718
719         # For now we won't look for DPDK errors
720         # lines = stdout.split('\n')
721         # for line in lines:
722         #    if 'EAL' in line or \
723         #                     'FAILURE' in line or \
724         #                     'failed' in line or \
725         #                     'Failed' in line:
726         #         errors.append(line.lstrip(' '))
727
728         return statestr, errors
729
730     @staticmethod
731     def get_linux_distro():
732         """
733         Get the linux distribution and check if it is supported
734
735         :returns: linux distro, None if the distro is not supported
736         :rtype: list
737         """
738
739         dist = distro.linux_distribution()
740         if dist[0] == "Ubuntu" or dist[0] == "CentOS Linux" or dist[:7] == "Red Hat":
741             return dist
742         else:
743             raise RuntimeError("Linux Distribution {} is not supported".format(dist[0]))
744
745     @staticmethod
746     def version():
747         """
748
749         Gets VPP Version information
750
751         :returns: version
752         :rtype: dict
753         """
754
755         version = {}
756         cmd = "vppctl show version verbose"
757         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
758         if ret != 0:
759             return version
760
761         lines = stdout.split("\n")
762         if len(lines[0]) != 0:
763             if lines[0].split(" ")[0] == "FileNotFoundError":
764                 return version
765
766         for line in lines:
767             if len(line) == 0:
768                 continue
769             dct = line.split(":")
770             version[dct[0]] = dct[1].lstrip(" ")
771
772         return version
773
774     @staticmethod
775     def show_bridge(node):
776         """
777         Shows the current bridge configuration
778
779         :param node: VPP node.
780         :type node: dict
781         :returns: A list of interfaces
782         """
783
784         ifaces = []
785         cmd = "vppctl show bridge"
786         (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
787         if ret != 0:
788             raise RuntimeError(
789                 "{} failed on node {} {} {}".format(cmd, node["host"], stdout, stderr)
790             )
791         lines = stdout.split("\r\n")
792         bridges = []
793         for line in lines:
794             if line == "no bridge-domains in use":
795                 print(line)
796                 return ifaces
797             if len(line) == 0:
798                 continue
799
800             lspl = line.lstrip(" ").split()
801             if lspl[0] != "BD-ID":
802                 bridges.append(lspl[0])
803
804         for bridge in bridges:
805             cmd = "vppctl show bridge {} detail".format(bridge)
806             (ret, stdout, stderr) = VPPUtil.exec_command(cmd)
807             if ret != 0:
808                 raise RuntimeError(
809                     "{} failed on node {} {} {}".format(
810                         cmd, node["host"], stdout, stderr
811                     )
812                 )
813
814         lines = stdout.split("\r\n")
815         for line in lines:
816             iface = re.findall(r"[a-zA-z]+\d+/\d+/\d+", line)
817             if len(iface):
818                 ifcidx = {"name": iface[0], "index": line.split()[1]}
819                 ifaces.append(ifcidx)
820
821         print(stdout)
822         return ifaces