Skip to content

Commit a0b37b2

Browse files
committed
cli: add 'show boot-order' and 'set boot-order a b c' commands
Fixes #1032 Signed-off-by: Joachim Wiberg <[email protected]>
1 parent 40a197d commit a0b37b2

File tree

5 files changed

+176
-0
lines changed

5 files changed

+176
-0
lines changed

doc/ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ All notable changes to the project are documented in this file.
3838
kernel default (*inherit*), which in turn fixes reported issues with dropped
3939
OSPF Hello frames in GRE tunnels
4040
- [Document][bpi-r3-emmc-documentation] how to go from SD card to eMMC on bpi-r3
41+
- Add CLI commands for managing boot partition order: `show boot-order` and
42+
`set boot-order` allow viewing and changing the boot order from the CLI,
43+
complementing the existing YANG RPC support, issue #1032
4144

4245
### Fixes
4346

doc/upgrade.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,79 @@ YANG support for upgrading Infix, inspecting and _modifying_ the
2323
boot-order, is defined in [infix-system-software][2].
2424

2525

26+
## Changing Boot Order
27+
28+
The boot order can be manually changed using the `set boot-order` command.
29+
This is useful for rolling back to a previous version or changing the
30+
preferred boot source.
31+
32+
The command accepts one to three boot targets as separate arguments, in the
33+
desired boot order. Valid boot targets are:
34+
35+
- `primary` - The primary partition
36+
- `secondary` - The secondary partition
37+
- `net` - Network boot (if supported by bootloader)
38+
39+
The CLI provides tab-completion for boot targets, making it easy to enter
40+
valid values.
41+
42+
Example: View current boot order and change it:
43+
44+
```
45+
admin@example:/> show boot-order
46+
primary secondary net
47+
admin@example:/> set boot-order secondary primary net
48+
admin@example:/> show boot-order
49+
secondary primary net
50+
admin@example:/>
51+
```
52+
53+
Example: Set boot order to only try primary partition:
54+
55+
```
56+
admin@example:/> show boot-order
57+
secondary primary net
58+
admin@example:/> set boot-order primary
59+
admin@example:/> show boot-order
60+
primary
61+
admin@example:/>
62+
```
63+
64+
Example: Using tab-completion (press TAB to see available options):
65+
66+
```
67+
admin@example:/> set boot-order <TAB>
68+
net primary secondary
69+
admin@example:/> set boot-order secondary <TAB>
70+
net primary secondary
71+
admin@example:/> set boot-order secondary primary
72+
admin@example:/> show boot-order
73+
secondary primary
74+
admin@example:/>
75+
```
76+
77+
The new boot order takes effect on the next reboot and can be verified
78+
with `show boot-order` or `show software`:
79+
80+
```
81+
admin@example:/> show software
82+
BOOT ORDER
83+
secondary primary
84+
85+
NAME STATE VERSION DATE
86+
primary booted v25.01.0 2025-04-25T10:15:00+00:00
87+
secondary inactive v25.01.0 2025-04-25T10:07:20+00:00
88+
admin@example:/>
89+
```
90+
91+
> [!NOTE]
92+
> The boot order is automatically updated when performing an upgrade.
93+
> The newly installed image will be set as the first boot target.
94+
>
95+
> Duplicate boot targets are not allowed. The CLI will reject attempts to
96+
> specify the same target multiple times.
97+
98+
2699
## Upgrading
27100

28101
Upgrading Infix is done one partition at a time. If the system has

src/klish-plugin-infix/src/infix.c

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,69 @@ int infix_shell(kcontext_t *ctx)
263263
return rc;
264264
}
265265

266+
int infix_set_boot_order(kcontext_t *ctx)
267+
{
268+
const char *first = NULL, *second = NULL, *third = NULL;
269+
char tmpfile[] = "/tmp/boot-order-XXXXXX";
270+
kpargv_t *pargv = kcontext_pargv(ctx);
271+
const kparg_t *parg;
272+
char json[512];
273+
int fd, rc = 0;
274+
FILE *fp;
275+
276+
parg = kpargv_find(pargv, "first");
277+
if (parg)
278+
first = kparg_value(parg);
279+
280+
parg = kpargv_find(pargv, "second");
281+
if (parg)
282+
second = kparg_value(parg);
283+
284+
parg = kpargv_find(pargv, "third");
285+
if (parg)
286+
third = kparg_value(parg);
287+
288+
if (!first) {
289+
fprintf(stderr, ERRMSG "missing boot target\n");
290+
return -1;
291+
}
292+
293+
if (third && second)
294+
snprintf(json, sizeof(json),
295+
"{\"infix-system:set-boot-order\":{\"boot-order\":[\"%s\",\"%s\",\"%s\"]}}",
296+
first, second, third);
297+
else if (second)
298+
snprintf(json, sizeof(json),
299+
"{\"infix-system:set-boot-order\":{\"boot-order\":[\"%s\",\"%s\"]}}",
300+
first, second);
301+
else
302+
snprintf(json, sizeof(json),
303+
"{\"infix-system:set-boot-order\":{\"boot-order\":[\"%s\"]}}",
304+
first);
305+
306+
fd = mkstemp(tmpfile);
307+
if (fd == -1) {
308+
fprintf(stderr, ERRMSG "failed to create temporary file\n");
309+
return -1;
310+
}
311+
312+
fp = fdopen(fd, "w");
313+
if (!fp) {
314+
fprintf(stderr, ERRMSG "failed to open temporary file\n");
315+
close(fd);
316+
unlink(tmpfile);
317+
return -1;
318+
}
319+
320+
fprintf(fp, "%s\n", json);
321+
fclose(fp);
322+
323+
rc = systemf("sysrepocfg -R %s -fjson 2>&1", tmpfile);
324+
unlink(tmpfile);
325+
326+
return rc;
327+
}
328+
266329
int kplugin_infix_fini(kcontext_t *ctx)
267330
{
268331
(void)ctx;
@@ -282,6 +345,7 @@ int kplugin_infix_init(kcontext_t *ctx)
282345
kplugin_add_syms(plugin, ksym_new("firewall_zones", infix_firewall_zones));
283346
kplugin_add_syms(plugin, ksym_new("firewall_policies", infix_firewall_policies));
284347
kplugin_add_syms(plugin, ksym_new("firewall_services", infix_firewall_services));
348+
kplugin_add_syms(plugin, ksym_new("set_boot_order", infix_set_boot_order));
285349
kplugin_add_syms(plugin, ksym_new("shell", infix_shell));
286350

287351
return 0;

src/klish-plugin-infix/xml/infix.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,15 @@
134134
<ACTION sym="STRING"/>
135135
</PTYPE>
136136

137+
<PTYPE name="BOOT_TARGET">
138+
<COMPL>
139+
<ACTION sym="printl">primary</ACTION>
140+
<ACTION sym="printl">secondary</ACTION>
141+
<ACTION sym="printl">net</ACTION>
142+
</COMPL>
143+
<ACTION sym="STRING"/>
144+
</PTYPE>
145+
137146
<VIEW name="main">
138147
<HOTKEY key="^D" cmd="exit"/>
139148

@@ -243,6 +252,13 @@
243252
<PARAM name="current-datetime" ptype="/STRING" help="yyyy-mm-ddThh:mm:ss(Z|+/-hh:mm)"/>
244253
<ACTION sym="srp_rpc@sysrepo">/ietf-system:set-current-datetime</ACTION>
245254
</COMMAND>
255+
256+
<COMMAND name="boot-order" help="Set boot partition order">
257+
<PARAM name="first" ptype="/BOOT_TARGET" help="First boot target"/>
258+
<PARAM name="second" ptype="/BOOT_TARGET" help="Second boot target" min="0"/>
259+
<PARAM name="third" ptype="/BOOT_TARGET" help="Third boot target" min="0"/>
260+
<ACTION sym="set_boot_order@infix"/>
261+
</COMMAND>
246262
</COMMAND>
247263

248264
<COMMAND name="dhcp-server" help="DHCP server tools" mode="switch">
@@ -353,6 +369,12 @@
353369
</ACTION>
354370
</COMMAND>
355371

372+
<COMMAND name="boot-order" help="Show boot partition order">
373+
<ACTION sym="script">
374+
show boot-order
375+
</ACTION>
376+
</COMMAND>
377+
356378
<COMMAND name="system" help="Show system overview">
357379
<ACTION sym="script" in="tty" out="tty" interrupt="true">
358380
show system |pager

src/show/show.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,19 @@ def software(args: List[str]) -> None:
142142
else:
143143
print("Too many arguments provided. Only one name is expected.")
144144

145+
def boot_order(args: List[str]) -> None:
146+
data = run_sysrepocfg("/ietf-system:system-state/infix-system:software")
147+
if not data:
148+
print("No software data retrieved.")
149+
return
150+
151+
try:
152+
boot_order_list = data.get("ietf-system:system-state", {}).get("infix-system:software", {}).get("boot-order", [])
153+
if boot_order_list:
154+
print(" ".join(boot_order_list))
155+
except (KeyError, TypeError):
156+
print("No boot order data available.")
157+
145158
def services(args: List[str]) -> None:
146159
data = run_sysrepocfg("/ietf-system:system-state/infix-system:services")
147160
if not data:
@@ -425,6 +438,7 @@ def human_readable(kib_val):
425438
def execute_command(command: str, args: List[str]):
426439
command_mapping = {
427440
'bfd': bfd,
441+
'boot-order': boot_order,
428442
'dhcp': dhcp,
429443
'hardware': hardware,
430444
'interface': interface,

0 commit comments

Comments
 (0)