Add retry and timeout for VPP interface discovery
VPP sometimes takes some time to come up, this causes VPP interface detection to fail. This patch adds retries for interface discovery so we can properly identify state of VPP interfaces. Also contains minor fixes for regex for VPP configs generation code to prevent cases where we might edit configs that are commented out. Change-Id: I915d5455acb8d496438b9c9e851639d3a43e6fa9 Signed-off-by: Feng Pan <fpan@redhat.com>
This commit is contained in:
parent
0b8cd76dbd
commit
d6e5314e6f
|
@ -14,6 +14,7 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import os
|
||||
import os.path
|
||||
import random
|
||||
|
@ -354,6 +355,12 @@ class TestUtils(base.TestCase):
|
|||
self.assertRaises(utils.VppException,
|
||||
utils._get_vpp_interface_name, '0000:09.0')
|
||||
|
||||
@mock.patch('os_net_config.utils.processutils.execute',
|
||||
return_value=('', None))
|
||||
def test_get_vpp_interface_name_multiple_iterations(self, mock_execute):
|
||||
self.assertIsNone(utils._get_vpp_interface_name('0000:00:09.0', 2, 1))
|
||||
self.assertEqual(4, mock_execute.call_count)
|
||||
|
||||
def test_generate_vpp_config(self):
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
config_path = os.path.join(tmpdir, 'startup.conf')
|
||||
|
@ -406,7 +413,7 @@ dpdk {
|
|||
return None, None
|
||||
self.stubs.Set(processutils, 'execute', test_execute)
|
||||
|
||||
def test_get_vpp_interface_name(pci_dev):
|
||||
def test_get_vpp_interface_name(pci_dev, tries, timeout):
|
||||
return 'GigabitEthernet0/9/0'
|
||||
|
||||
self.stubs.Set(utils, '_get_vpp_interface_name',
|
||||
|
|
|
@ -18,6 +18,7 @@ import glob
|
|||
import logging
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import yaml
|
||||
|
||||
from oslo_concurrency import processutils
|
||||
|
@ -354,49 +355,51 @@ def restart_vpp(vpp_interfaces):
|
|||
processutils.execute('systemctl', 'restart', 'vpp')
|
||||
|
||||
|
||||
def _get_vpp_interface_name(pci_addr):
|
||||
def _get_vpp_interface_name(pci_addr, tries=1, timeout=5):
|
||||
"""Get VPP interface name from a given PCI address
|
||||
|
||||
From a running VPP instance, attempt to find the interface name from
|
||||
a given PCI address of a NIC.
|
||||
|
||||
VppException will be raised if pci_addr is not formatted correctly.
|
||||
ProcessExecutionError will be raised if VPP interface mapped to pci_addr
|
||||
is not found.
|
||||
|
||||
:param pci_addr: PCI address to lookup, in the form of DDDD:BB:SS.F, where
|
||||
- DDDD = Domain
|
||||
- BB = Bus Number
|
||||
- SS = Slot number
|
||||
- F = Function
|
||||
:param tries: Number of tries for getting vppctl output. Defaults to 1.
|
||||
:param timeout: Timeout in seconds between tries. Defaults to 5.
|
||||
:return: VPP interface name. None if an interface is not found.
|
||||
"""
|
||||
if not pci_addr:
|
||||
return None
|
||||
|
||||
try:
|
||||
processutils.execute('systemctl', 'is-active', 'vpp')
|
||||
out, err = processutils.execute('vppctl', 'show', 'interfaces')
|
||||
m = re.search(r':([0-9a-fA-F]{2}):([0-9a-fA-F]{2}).([0-9a-fA-F])',
|
||||
pci_addr)
|
||||
if m:
|
||||
formatted_pci = "%x/%x/%x" % (int(m.group(1), 16),
|
||||
int(m.group(2), 16),
|
||||
int(m.group(3), 16))
|
||||
else:
|
||||
raise VppException('Invalid PCI address format: %s' % pci_addr)
|
||||
for _ in range(tries):
|
||||
try:
|
||||
timestamp = time.time()
|
||||
processutils.execute('systemctl', 'is-active', 'vpp')
|
||||
out, err = processutils.execute('vppctl', 'show', 'interface')
|
||||
logger.debug("vppctl show interface\n%s\n%s\n" % (out, err))
|
||||
m = re.search(r':([0-9a-fA-F]{2}):([0-9a-fA-F]{2}).([0-9a-fA-F])',
|
||||
pci_addr)
|
||||
if m:
|
||||
formatted_pci = "%x/%x/%x" % (int(m.group(1), 16),
|
||||
int(m.group(2), 16),
|
||||
int(m.group(3), 16))
|
||||
else:
|
||||
raise VppException('Invalid PCI address format: %s' % pci_addr)
|
||||
|
||||
m = re.search(r'^(\w+%s)\s+' % formatted_pci, out, re.MULTILINE)
|
||||
if m:
|
||||
logger.debug('VPP interface found: %s' % m.group(1))
|
||||
return m.group(1)
|
||||
else:
|
||||
logger.debug('Interface with pci address %s not bound to VPP'
|
||||
% pci_addr)
|
||||
return None
|
||||
except processutils.ProcessExecutionError:
|
||||
logger.debug('Interface with pci address %s not bound to vpp' %
|
||||
pci_addr)
|
||||
m = re.search(r'^(\w+%s)\s+' % formatted_pci, out, re.MULTILINE)
|
||||
if m:
|
||||
logger.debug('VPP interface found: %s' % m.group(1))
|
||||
return m.group(1)
|
||||
except processutils.ProcessExecutionError:
|
||||
pass
|
||||
|
||||
time.sleep(max(0, (timestamp + timeout) - time.time()))
|
||||
else:
|
||||
logger.info('Interface with pci address %s not bound to vpp' %
|
||||
pci_addr)
|
||||
return None
|
||||
|
||||
|
||||
def generate_vpp_config(vpp_config_path, vpp_interfaces):
|
||||
|
@ -442,11 +445,12 @@ def generate_vpp_config(vpp_config_path, vpp_interfaces):
|
|||
# If such config line is found, we will replace the line with
|
||||
# appropriate configuration, otherwise, add a new config line
|
||||
# in 'dpdk' section of the config.
|
||||
m = re.search(r'^\s*dev\s+%s\s*(\{[^}]*\})?\s*'
|
||||
m = re.search(r'^\s*dev\s+%s\s*(\{[^}]*\})?\s*$'
|
||||
% vpp_interface.pci_dev, data,
|
||||
re.IGNORECASE | re.MULTILINE)
|
||||
if m:
|
||||
data = re.sub(m.group(0), ' dev %s\n' % int_cfg, data)
|
||||
data = re.sub(m.group(0), ' dev %s\n' % int_cfg, data,
|
||||
flags=re.MULTILINE)
|
||||
else:
|
||||
data = re.sub(r'(^\s*dpdk\s*\{)',
|
||||
r'\1\n dev %s\n' % int_cfg,
|
||||
|
@ -459,13 +463,15 @@ def generate_vpp_config(vpp_config_path, vpp_interfaces):
|
|||
# configuration, otherwise, add a new line in 'dpdk' section.
|
||||
m = re.search(r'^\s*uio-driver.*$', data, re.MULTILINE)
|
||||
if m:
|
||||
data = re.sub(m.group(0), r' uio-driver %s'
|
||||
% vpp_interface.uio_driver, data)
|
||||
data = re.sub(r'^\s*uio-driver.*$', ' uio-driver %s'
|
||||
% vpp_interface.uio_driver, data,
|
||||
flags=re.MULTILINE)
|
||||
else:
|
||||
data = re.sub(r'(dpdk\s*\{)',
|
||||
data = re.sub(r'(^\s*dpdk\s*\{)',
|
||||
r'\1\n uio-driver %s'
|
||||
% vpp_interface.uio_driver,
|
||||
data)
|
||||
data,
|
||||
flags=re.MULTILINE)
|
||||
else:
|
||||
logger.debug('pci address not found for interface %s, may have'
|
||||
'already been bound to vpp' % vpp_interface.name)
|
||||
|
@ -516,7 +522,8 @@ def update_vpp_mapping(vpp_interfaces):
|
|||
# only trying one more time, can turn into a retry_counter if needed
|
||||
# in the future.
|
||||
for i in range(2):
|
||||
vpp_name = _get_vpp_interface_name(vpp_int.pci_dev)
|
||||
vpp_name = _get_vpp_interface_name(vpp_int.pci_dev,
|
||||
tries=12, timeout=5)
|
||||
if not vpp_name:
|
||||
restart_vpp(vpp_interfaces)
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue