summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorNataliia Solomko <natalirs1985@gmail.com>2026-04-14 18:07:41 +0300
committerNataliia Solomko <natalirs1985@gmail.com>2026-04-21 16:45:43 +0300
commit9cb6f4c001fa5d4f4ed26850fc99dde4c3692c8f (patch)
tree5309c08fd789a8560426c1d45e45050c1ad8ac22 /python
parentd9c7cf3bc0e76661f48bd0ce13acfc7be182a326 (diff)
downloadvyos-1x-9cb6f4c001fa5d4f4ed26850fc99dde4c3692c8f.tar.gz
vyos-1x-9cb6f4c001fa5d4f4ed26850fc99dde4c3692c8f.zip
vpp: T8460: Use isolated cpus for VPP cpu-cores
VPP CPU core assignment now uses kernel-isolated CPUs (from /sys/devices/system/cpu/isolated) with explicit corelist-workers instead of computing offsets from available cores with skip-cores/workers. Added validation that enough CPUs are actually isolated before VPP starts, and that isolate-cpus config only references existing CPU IDs. Moved smoketest for kernel option 'isolate-cpus' to test_vpp.py
Diffstat (limited to 'python')
-rw-r--r--python/vyos/utils/convert.py53
-rw-r--r--python/vyos/vpp/config_verify.py73
2 files changed, 106 insertions, 20 deletions
diff --git a/python/vyos/utils/convert.py b/python/vyos/utils/convert.py
index ea07f9514..bdc9fd549 100644
--- a/python/vyos/utils/convert.py
+++ b/python/vyos/utils/convert.py
@@ -261,3 +261,56 @@ def encode_to_base64(input_string):
# Decode the base64 bytes back to a string
return encoded_string.decode('utf-8')
+
+
+def range_str_to_list(data: str) -> list[int]:
+ """
+ Convert a string of ranges to a sorted list of unique integers
+
+ Args:
+ data (str): Comma-separated ranges, e.g. '1-3,5,7-9'
+
+ Returns:
+ Sorted list of unique integers, e.g. ``[1, 2, 3, 5, 7, 8, 9]``.
+ """
+ if not data:
+ return []
+ result = []
+ for part in data.split(','):
+ if '-' in part:
+ start, end = part.split('-')
+ result.extend(range(int(start), int(end) + 1))
+ else:
+ result.append(int(part))
+ return sorted(set(result))
+
+
+def list_to_range_str(nums: list[int]) -> str:
+ """
+ Convert a list of integers to a compact string of ranges
+
+ Args:
+ nums: List of integers, e.g. [1, 2, 3, 5, 7, 8, 9].
+ Duplicates are removed and the list is sorted internally.
+
+ Returns:
+ Compact range string, e.g. '1-3,5,7-9'.
+ Returns '' for an empty input.
+ """
+ nums = sorted(set(nums))
+ if not nums:
+ return ''
+
+ ranges = []
+ start = end = nums[0]
+
+ for n in nums[1:]:
+ if n == end + 1:
+ end = n
+ else:
+ ranges.append(str(start) if start == end else f'{start}-{end}')
+ start = end = n
+
+ ranges.append(str(start) if start == end else f'{start}-{end}')
+
+ return ','.join(ranges)
diff --git a/python/vyos/vpp/config_verify.py b/python/vyos/vpp/config_verify.py
index da8e3d24b..228fcbc09 100644
--- a/python/vyos/vpp/config_verify.py
+++ b/python/vyos/vpp/config_verify.py
@@ -20,8 +20,10 @@ import psutil
from vyos import ConfigError
from vyos.base import Warning
+from vyos.utils.convert import range_str_to_list
from vyos.utils.cpu import get_core_count as total_core_count, get_cpus
from vyos.utils.dict import dict_search
+from vyos.utils.file import read_file
from vyos.vpp.config_resource_checks import memory as mem_checks
from vyos.vpp.config_resource_checks.resource_defaults import default_resource_map
@@ -54,6 +56,9 @@ _VPP_MEMBER_INTERFACE_REFS = [
_VPP_INTERFACE_REFS = _VPP_FEATURE_INTERFACE_REFS + _VPP_MEMBER_INTERFACE_REFS
+# Line width used by ConfigError for message formatting
+LINE_WIDTH = 72
+
def vpp_interface_in_use(
iface: str, config: dict, match_vlans: bool = False, refs: list = None
@@ -217,12 +222,12 @@ def create_cpu_error_message(cpus_required: int, cpus_available: int = None) ->
available_str = (
(
- '---'.ljust(72)
- + 'Reserved:'.ljust(72)
- + f'For system: {reserved_cpus}'.ljust(72)
- + f'VPP main thread: 1'.ljust(72)
- + '---'.ljust(72)
- + 'Available:'.ljust(72)
+ '---'.ljust(LINE_WIDTH)
+ + 'Reserved:'.ljust(LINE_WIDTH)
+ + f'For system: {reserved_cpus}'.ljust(LINE_WIDTH)
+ + f'VPP main thread: 1'.ljust(LINE_WIDTH)
+ + '---'.ljust(LINE_WIDTH)
+ + 'Available:'.ljust(LINE_WIDTH)
+ f'Physical cores: {max(cpus_available, 0)}'
)
if cpus_available is not None
@@ -230,13 +235,13 @@ def create_cpu_error_message(cpus_required: int, cpus_available: int = None) ->
)
message = (
- '---'.ljust(72)
- + 'Total in the system:'.ljust(72)
- + f'Physical cores: {total_core_count()}'.ljust(72)
- + f'Logical cores: {logical_cores}'.ljust(72)
- + '---'.ljust(72)
- + 'Required:'.ljust(72)
- + f'Physical cores: {cpus_required}'.ljust(72)
+ '---'.ljust(LINE_WIDTH)
+ + 'Total in the system:'.ljust(LINE_WIDTH)
+ + f'Physical cores: {total_core_count()}'.ljust(LINE_WIDTH)
+ + f'Logical cores: {logical_cores}'.ljust(LINE_WIDTH)
+ + '---'.ljust(LINE_WIDTH)
+ + 'Required:'.ljust(LINE_WIDTH)
+ + f'Physical cores: {cpus_required}'.ljust(LINE_WIDTH)
+ available_str
)
@@ -251,7 +256,7 @@ def verify_vpp_minimum_cpus():
min_cpus = default_resource_map.get('min_cpus')
if total_core_count() < min_cpus:
raise ConfigError(
- 'This system does not meet minimal requirements for VPP. '.ljust(72)
+ 'This system does not meet minimal requirements for VPP. '.ljust(LINE_WIDTH)
+ create_cpu_error_message(min_cpus)
)
@@ -329,10 +334,10 @@ def verify_vpp_memory(config: dict):
if errors:
raise ConfigError(
- 'Not enough free memory to start VPP! '.ljust(72)
- + '. '.join([line.ljust(72) for line in errors.values()])
+ 'Not enough free memory to start VPP! '.ljust(LINE_WIDTH)
+ + '. '.join([line.ljust(LINE_WIDTH) for line in errors.values()])
+ (
- 'To add HugePages memory please use command '.ljust(72)
+ 'To add HugePages memory please use command '.ljust(LINE_WIDTH)
+ '"set system option kernel memory hugepage-size ..." and reboot!'
if any(k in errors for k in ('2M', '1G'))
else ''
@@ -342,8 +347,19 @@ def verify_vpp_memory(config: dict):
def verify_vpp_cpu_cores(cpu_cores: int):
"""
- Verify that the system has enough available CPU cores
- to run a given amount of worker processes (1 worker/core)
+ Verify that the system has enough available and isolated CPU cores.
+
+ Checks performed:
+ 1. The host has enough physical cores (minus reserved) for the requested
+ cpu_cores count.
+ 2. The kernel actually has at least cpu_cores isolated CPUs
+ (read from /sys/devices/system/cpu/isolated).
+
+ Args:
+ cpu_cores: Total number of VPP CPU cores (1 main + N-1 workers).
+
+ Raises:
+ ConfigError: When any of the checks fail.
"""
total_cores = total_core_count()
reserved_cpus = default_resource_map.get('reserved_cpu_cores')
@@ -351,10 +367,27 @@ def verify_vpp_cpu_cores(cpu_cores: int):
if cpu_cores > available_cores:
raise ConfigError(
- f'Not enough free physical CPU cores for {cpu_cores} "cpu-cores" '.ljust(72)
+ f'Not enough free physical CPU cores for {cpu_cores} "cpu-cores" '.ljust(
+ LINE_WIDTH
+ )
+ create_cpu_error_message(cpu_cores, available_cores)
)
+ # Read the set of CPUs the running kernel has actually isolated
+ isolated = read_file('/sys/devices/system/cpu/isolated')
+ isolated_cpus = range_str_to_list(isolated)
+
+ if cpu_cores > len(isolated_cpus):
+ raise ConfigError(
+ 'Not enough isolated CPU cores available: '.ljust(LINE_WIDTH)
+ + f'{cpu_cores} requested, but only {len(isolated_cpus)} isolated'
+ f'{f" (CPU#{isolated})" if len(isolated_cpus) > 0 else ""}. '.ljust(
+ LINE_WIDTH
+ )
+ + 'To isolate CPUs please use command '.ljust(LINE_WIDTH)
+ + '"set system option kernel cpu isolate-cpus ..." save and reboot!'
+ )
+
def verify_vpp_statseg_size(settings: dict):
statseg_size = mem_checks.statseg_size(settings)