From 0bf443acca2985a10ef26c1651992c185d4fd4fa Mon Sep 17 00:00:00 2001
From: zsdc <taras@vyos.io>
Date: Tue, 27 Jun 2023 23:04:14 +0300
Subject: VPP: T1797: Improved PCI address search

Use info from both ethtool and VPP to find PCI address for an
interface.
---
 python/vyos/vpp.py   | 40 ++++++++++++++++++++++++++++++++++++++--
 src/conf_mode/vpp.py | 25 +++++++++++++++++++------
 2 files changed, 57 insertions(+), 8 deletions(-)

diff --git a/python/vyos/vpp.py b/python/vyos/vpp.py
index 9e9471879..d60ecc1b3 100644
--- a/python/vyos/vpp.py
+++ b/python/vyos/vpp.py
@@ -13,6 +13,8 @@
 # You should have received a copy of the GNU Lesser General Public
 # License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
+from re import search as re_search, MULTILINE as re_M
+
 from vpp_papi import VPPApiClient
 
 
@@ -27,17 +29,29 @@ class VPPControl:
         self.vpp_api_client.connect('vpp-vyos')
 
     def __del__(self) -> None:
+        """Disconnect from VPP API (destructor)
+        """
+        self.disconnect()
+
+    def disconnect(self) -> None:
         """Disconnect from VPP API
         """
         self.vpp_api_client.disconnect()
 
-    def cli_cmd(self, command: str) -> None:
+    def cli_cmd(self, command: str, return_output: bool = False) -> str:
         """Send raw CLI command
 
         Args:
             command (str): command to send
+            return_output (bool, optional): Return command output. Defaults to False.
+
+        Returns:
+            str: output of the command, only if it was successful
         """
-        self.vpp_api_client.api.cli_inband(cmd=command)
+        cli_answer = self.vpp_api_client.api.cli_inband(cmd=command)
+        if return_output and cli_answer.retval == 0:
+            return cli_answer.reply
+        return ''
 
     def get_mac(self, ifname: str) -> str:
         """Find MAC address by interface name in VPP
@@ -112,3 +126,25 @@ class VPPControl:
         iface_index = self.get_sw_if_index(iface_name)
         self.vpp_api_client.api.sw_interface_set_rx_mode(
             sw_if_index=iface_index, mode=modes_dict[rx_mode])
+
+    def get_pci_addr(self, ifname: str) -> str:
+        """Find PCI address of interface by interface name in VPP
+
+        Args:
+            ifname (str): interface name inside VPP
+
+        Returns:
+            str: PCI address
+        """
+        hw_info = self.cli_cmd(f'show hardware-interfaces {ifname}',
+                               return_output=True)
+
+        regex_filter = r'^\s+pci: device (?P<device>\w+:\w+) subsystem (?P<subsystem>\w+:\w+) address (?P<address>\w+:\w+:\w+\.\w+) numa (?P<numa>\w+)$'
+        re_obj = re_search(regex_filter, hw_info, re_M)
+
+        # return empty string if no interface or no PCI info was found
+        if not hw_info or not re_obj:
+            return ''
+
+        address = re_obj.groupdict().get('address', '')
+        return address
diff --git a/src/conf_mode/vpp.py b/src/conf_mode/vpp.py
index d541e52ba..54ea54852 100755
--- a/src/conf_mode/vpp.py
+++ b/src/conf_mode/vpp.py
@@ -16,6 +16,7 @@
 
 
 from pathlib import Path
+from re import search as re_search, MULTILINE as re_M
 
 from vyos.config import Config
 from vyos.configdict import dict_merge
@@ -38,14 +39,26 @@ service_conf = Path(f'/run/vpp/{service_name}.conf')
 systemd_override = '/run/systemd/system/vpp.service.d/10-override.conf'
 
 
-def _get_pci_address_by_interface(iface):
+def _get_pci_address_by_interface(iface) -> str:
     from vyos.util import rc_cmd
     rc, out = rc_cmd(f'ethtool -i {iface}')
-    if rc == 0:
-        output_lines = out.split('\n')
-        for line in output_lines:
-            if 'bus-info' in line:
-                return line.split(None, 1)[1].strip()
+    # if ethtool command was successful
+    if rc == 0 and out:
+        regex_filter = r'^bus-info: (?P<address>\w+:\w+:\w+\.\w+)$'
+        re_obj = re_search(regex_filter, out, re_M)
+        # if bus-info with PCI address found
+        if re_obj:
+            address = re_obj.groupdict().get('address', '')
+            return address
+    # use VPP - maybe interface already attached to it
+    vpp_control = VPPControl()
+    pci_addr = vpp_control.get_pci_addr(iface)
+    vpp_control.disconnect()
+    if pci_addr:
+        return pci_addr
+    # return empty string if address was not found
+    return ''
+
 
 
 def get_config(config=None):
-- 
cgit v1.2.3