Skip to content

Commit cd4fb89

Browse files
committed
T7995: Add capability to start VPP dataplane during system deployment
1 parent fdda826 commit cd4fb89

File tree

2 files changed

+146
-3
lines changed

2 files changed

+146
-3
lines changed

debian/control

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ Depends:
109109
file,
110110
iproute2 (>= 6.0.0),
111111
linux-cpupower,
112+
kexec-tools,
112113
# ipaddrcheck is widely used in IP value validators
113114
ipaddrcheck,
114115
ethtool (>= 6.10),

src/conf_mode/system_option.py

Lines changed: 145 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,22 @@
1616

1717
import os
1818
import psutil
19+
import shlex
1920

2021
from sys import exit
2122
from time import sleep
2223

23-
24+
from vyos.base import Warning
2425
from vyos.config import Config
2526
from vyos.configverify import verify_source_interface
2627
from vyos.configverify import verify_interface_exists
2728
from vyos.system import grub_util
2829
from vyos.template import render
30+
from vyos.utils.boot import boot_configuration_complete
2931
from vyos.utils.cpu import get_cpus
3032
from vyos.utils.dict import dict_search
3133
from vyos.utils.file import write_file
34+
from vyos.utils.file import read_file
3235
from vyos.utils.kernel import check_kmod
3336
from vyos.utils.process import cmd
3437
from vyos.utils.process import is_systemd_service_running
@@ -58,6 +61,28 @@
5861
'virtual-host': 'virtual-host',
5962
}
6063

64+
# Managed keys
65+
MANAGED_SINGLE = {
66+
'mitigations',
67+
'intel_idle.max_cstate',
68+
'processor.max_cstate',
69+
'initcall_blacklist',
70+
'amd_pstate',
71+
'quiet',
72+
'panic',
73+
'hpet',
74+
'mce',
75+
'nosoftlockup',
76+
'isolcpus',
77+
'nohz_full',
78+
'rcu_nocbs',
79+
'nmi_watchdog',
80+
'numa_balancing',
81+
'default_hugepagesz',
82+
}
83+
84+
MANAGED_HUGEPAGES = {'hugepagesz', 'hugepages'}
85+
6186

6287
def _get_total_hugepages_and_memory(config):
6388
unit_map = {'M': 1 << 20, 'G': 1 << 30}
@@ -112,10 +137,10 @@ def verify(options):
112137
if 'source_address' in config:
113138
address = config['source_address']
114139
if not is_addr_assigned(config['source_address']):
115-
raise ConfigError('No interface with address "{address}" configured!')
140+
raise ConfigError(f'No interface with address "{address}" configured!')
116141

117142
if 'source_interface' in config:
118-
# verify_source_interface reuires key 'ifname'
143+
# verify_source_interface requires key 'ifname'
119144
config['ifname'] = config['source_interface']
120145
verify_source_interface(config)
121146
if 'source_address' in config:
@@ -244,10 +269,127 @@ def generate(options):
244269

245270
grub_util.update_kernel_cmdline_options(' '.join(cmdline_options))
246271

272+
options['cmdline_options'] = cmdline_options
273+
247274
return None
248275

249276

277+
def generate_cmdline_for_kexec(options):
278+
"""
279+
Build an updated kernel cmdline string based on desired options and the
280+
currently running /proc/cmdline.
281+
282+
Returns:
283+
(kexec_required: bool, new_cmdline: str)
284+
- kexec_required is True if kernel options were added, removed,
285+
or modified.
286+
"""
287+
288+
def parse_token(tok):
289+
return tok.split('=', 1) if '=' in tok else [tok, None]
290+
291+
# Read current cmdline
292+
raw_cmdline = read_file('/proc/cmdline').strip()
293+
tokens = shlex.split(raw_cmdline)
294+
295+
# Separate normal keys vs hugepages
296+
current_single_opts = {}
297+
hugepages_current = []
298+
299+
# Split existing cmdline into normal keys and hugepages pairs
300+
i = 0
301+
while i < len(tokens):
302+
key, val = parse_token(tokens[i])
303+
if key == 'hugepagesz':
304+
size = val
305+
count = None
306+
if i + 1 < len(tokens):
307+
nk, nv = parse_token(tokens[i + 1])
308+
if nk == 'hugepages':
309+
count = nv
310+
i += 1
311+
hugepages_current.append([size, count])
312+
else:
313+
current_single_opts[key] = val
314+
i += 1
315+
316+
kexec_required = False
317+
318+
# Extract desired single-value options from options['cmdline_options']
319+
desired_single_opts = {}
320+
for entry in options.get("cmdline_options", []):
321+
for tok in entry.split():
322+
k, v = parse_token(tok)
323+
if k not in MANAGED_HUGEPAGES:
324+
desired_single_opts[k] = v
325+
326+
# Remove obsolete managed options
327+
for k in list(current_single_opts.keys()):
328+
if k in MANAGED_SINGLE and k not in desired_single_opts:
329+
kexec_required = True
330+
current_single_opts.pop(k)
331+
332+
# Add/update managed single-value options
333+
for k, v in desired_single_opts.items():
334+
if current_single_opts.get(k, '') != v:
335+
kexec_required = True
336+
current_single_opts[k] = v
337+
338+
# Build desired hugepages dictionary
339+
hp_cfg = options.get('kernel', {}).get('memory', {}).get('hugepage_size', {})
340+
desired_hp = {size: cfg.get('hugepage_count') for size, cfg in hp_cfg.items()}
341+
342+
new_hugepages = []
343+
seen_sizes = set()
344+
345+
# Update existing hugepages_pairs and remove obsolete
346+
for size, count in hugepages_current:
347+
if size in desired_hp:
348+
if count != desired_hp[size]:
349+
kexec_required = True
350+
count = desired_hp[size]
351+
new_hugepages.append([size, count])
352+
seen_sizes.add(size)
353+
else:
354+
# obsolete hugepages -> remove
355+
kexec_required = True
356+
357+
# Add new hugepages that do not exist in current cmdline
358+
for size, count in desired_hp.items():
359+
if size not in seen_sizes:
360+
new_hugepages.append([size, count])
361+
kexec_required = True
362+
363+
# Reconstruct final cmdline
364+
new_tokens = []
365+
for key, value in current_single_opts.items():
366+
new_tokens.append(f'{key}={value}' if value is not None else key)
367+
368+
# Append hugepages pairs
369+
for size, count in new_hugepages:
370+
new_tokens.append(f'hugepagesz={size}')
371+
if count is not None:
372+
new_tokens.append(f'hugepages={count}')
373+
374+
cmdline_new = ' '.join(new_tokens)
375+
376+
return kexec_required, cmdline_new
377+
378+
250379
def apply(options):
380+
kexec_required, cmdline_new = generate_cmdline_for_kexec(options)
381+
if kexec_required:
382+
if not boot_configuration_complete() and os.environ.get('VYOS_CONFIGD'):
383+
cmd(
384+
f'sudo kexec -l /boot/vmlinuz --initrd=/boot/initrd.img --command-line="{cmdline_new}"'
385+
)
386+
cmd('sudo systemctl kexec')
387+
elif boot_configuration_complete():
388+
Warning(
389+
'Kernel configuration options have changed. '
390+
'To apply these changes, you must save the configuration and reboot the system!'
391+
)
392+
251393
# System bootup beep
252394
beep_service = 'vyos-beep.service'
253395
if 'startup_beep' in options:

0 commit comments

Comments
 (0)