mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-31 21:13:00 +02:00
Merge pull request #876 from kernelkit/change-boot-order
Change boot order
This commit is contained in:
@@ -13,6 +13,8 @@ All notable changes to the project are documented in this file.
|
||||
as "reflector" and filtering of reflected services. Issue #678
|
||||
- Review of default `sysctl` settings, issue #829
|
||||
- Upgrade Linux kernel to 6.12.3 (LTS)
|
||||
- Add the possibility to change the boot order for the system with a
|
||||
RPC and add boot order to operational datastore.
|
||||
|
||||
### Fixes
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "core.h"
|
||||
#include "rauc-installer.h"
|
||||
|
||||
|
||||
static RaucInstaller *infix_system_sw_new_rauc(void)
|
||||
{
|
||||
RaucInstaller *rauc;
|
||||
@@ -51,12 +50,49 @@ static int infix_system_sw_install(sr_session_ctx_t *session, uint32_t sub_id,
|
||||
return srerr;
|
||||
}
|
||||
|
||||
/*
|
||||
boot order can only be: primary, secondary and net, limited by model
|
||||
*/
|
||||
static int infix_system_sw_set_boot_order(sr_session_ctx_t *session, uint32_t sub_id,
|
||||
const char *path, const sr_val_t *input,
|
||||
const size_t input_cnt, sr_event_t event,
|
||||
unsigned request_id, sr_val_t **output,
|
||||
size_t *output_cnt, void *priv) {
|
||||
char boot_order[23] = "";
|
||||
for (size_t i = 0; i < input_cnt; i++) {
|
||||
const sr_val_t *val = &input[i];
|
||||
|
||||
if (i != 0)
|
||||
strlcat(boot_order, " ", sizeof(boot_order));
|
||||
strlcat(boot_order, val->data.string_val, sizeof(boot_order));
|
||||
}
|
||||
|
||||
if (fexist("/sys/firmware/devicetree/base/chosen/u-boot,version")) {
|
||||
if (systemf("fw_setenv BOOT_ORDER %s", boot_order)) {
|
||||
ERROR("Set-boot-order: Failed to set boot order in U-Boot");
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
} else if (fexist("/mnt/aux/grub/grubenv")) {
|
||||
if (systemf("grub-editenv /mnt/aux/grub/grubenv set ORDER=\"%s\"", boot_order)) {
|
||||
ERROR("Set-boot-order: Failed to set boot order in Grub");
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
} else {
|
||||
ERROR("No supported boot loader found");
|
||||
return SR_ERR_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
int infix_system_sw_init(struct confd *confd)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
REGISTER_RPC(confd->session, "/infix-system:install-bundle",
|
||||
infix_system_sw_install, NULL, &confd->sub);
|
||||
REGISTER_RPC(confd->session, "/infix-system:set-boot-order",
|
||||
infix_system_sw_set_boot_order, NULL, &confd->sub);
|
||||
|
||||
fail:
|
||||
return rc;
|
||||
|
||||
@@ -20,6 +20,10 @@ submodule infix-system-software {
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "Software status and upgrade.";
|
||||
|
||||
revision 2024-12-16 {
|
||||
description "Add boot-order operational data";
|
||||
reference "Internal";
|
||||
}
|
||||
revision 2023-06-27 {
|
||||
description "Initial revision.";
|
||||
reference "internal";
|
||||
@@ -39,6 +43,18 @@ submodule infix-system-software {
|
||||
}
|
||||
}
|
||||
|
||||
grouping boot-order {
|
||||
leaf-list boot-order {
|
||||
type enumeration {
|
||||
enum "primary";
|
||||
enum "secondary";
|
||||
enum "net";
|
||||
}
|
||||
ordered-by user;
|
||||
min-elements 1; // At least one value is required
|
||||
max-elements 3; // Ensure reasonable maximum size if needed
|
||||
}
|
||||
}
|
||||
grouping installer-state {
|
||||
leaf operation {
|
||||
type string;
|
||||
@@ -64,7 +80,6 @@ submodule infix-system-software {
|
||||
"The last error encountered by the installer service.";
|
||||
}
|
||||
}
|
||||
|
||||
augment "/sys:system-state" {
|
||||
container software {
|
||||
description
|
||||
@@ -95,6 +110,7 @@ submodule infix-system-software {
|
||||
description
|
||||
"Slot from which the system was booted.";
|
||||
}
|
||||
uses boot-order;
|
||||
|
||||
container installer {
|
||||
description
|
||||
@@ -184,7 +200,7 @@ submodule infix-system-software {
|
||||
}
|
||||
|
||||
rpc install-bundle {
|
||||
nacm:default-deny-all;
|
||||
nacm:default-deny-all;
|
||||
description
|
||||
"Upgrade the system's software by installing the specified bundle.";
|
||||
input {
|
||||
@@ -198,4 +214,17 @@ nacm:default-deny-all;
|
||||
}
|
||||
}
|
||||
}
|
||||
rpc set-boot-order {
|
||||
nacm:default-deny-all;
|
||||
description
|
||||
"Set order of boot partitions";
|
||||
input {
|
||||
uses boot-order;
|
||||
must "count(boot-order[.='primary']) <= 1 and
|
||||
count(boot-order[.='secondary']) <= 1 and
|
||||
count(boot-order[.='net']) <= 1" {
|
||||
error-message "Not possible to have duplicate targets in boot order.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@
|
||||
sysrepocfg -d operational -X -f json -x /ietf-system:system-state/infix-system:software | \
|
||||
jq -r '.[][].slot |= sort_by(.name)' > "$tmp"
|
||||
if [ -n "$KLISH_PARAM_name" ]; then
|
||||
cat "$tmp" | /usr/libexec/statd/cli-pretty "infix-system" -n "$KLISH_PARAM_name"
|
||||
cat "$tmp" | /usr/libexec/statd/cli-pretty "show-software" -n "$KLISH_PARAM_name"
|
||||
else
|
||||
cat "$tmp" | /usr/libexec/statd/cli-pretty "show-software"
|
||||
fi
|
||||
|
||||
@@ -691,12 +691,21 @@ def show_software(json, name):
|
||||
print("Error, cannot find infix-system:software")
|
||||
sys.exit(1)
|
||||
|
||||
slots = get_json_data({}, json, 'ietf-system:system-state', 'infix-system:software', 'slot')
|
||||
software = get_json_data({}, json, 'ietf-system:system-state', 'infix-system:software')
|
||||
slots = software.get("slot")
|
||||
boot_order = software.get("boot-order", ["Unknown"])
|
||||
if name:
|
||||
slot = find_slot(slots, name)
|
||||
if slot:
|
||||
slot.detail()
|
||||
else:
|
||||
print(Decore.invert("BOOT ORDER"))
|
||||
order=""
|
||||
for boot in boot_order:
|
||||
order+=f"{boot.strip()} "
|
||||
print(order)
|
||||
print("")
|
||||
|
||||
hdr = (f"{'NAME':<{PadSoftware.name}}"
|
||||
f"{'STATE':<{PadSoftware.state}}"
|
||||
f"{'VERSION':<{PadSoftware.version}}"
|
||||
@@ -773,10 +782,12 @@ def main():
|
||||
parser_show_software = subparsers.add_parser('show-software', help='Show software versions')
|
||||
parser_show_software.add_argument('-n', '--name', help='Slotname')
|
||||
|
||||
parser_show_routing_table = subparsers.add_parser('show-hardware', help='Show USB ports')
|
||||
parser_show_hardware = subparsers.add_parser('show-hardware', help='Show USB ports')
|
||||
|
||||
parser_show_ntp_sources = subparsers.add_parser('show-ntp', help='Show NTP sources')
|
||||
|
||||
parser_show_boot_order = subparsers.add_parser('show-boot-order', help='Show NTP sources')
|
||||
|
||||
args = parser.parse_args()
|
||||
UNIT_TEST = args.test
|
||||
|
||||
@@ -793,7 +804,7 @@ def main():
|
||||
elif args.command == "show-ntp":
|
||||
show_ntp(json_data)
|
||||
else:
|
||||
print(f"Error, unknown command {args.command}")
|
||||
print(f"Error, unknown command '{args.command}'")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,23 @@ def datetime_now():
|
||||
return datetime(2023, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
def uboot_get_boot_order():
|
||||
data = run_cmd("fw_printenv BOOT_ORDER".split(), "boot-order.txt")
|
||||
for line in data:
|
||||
if "BOOT_ORDER" in line:
|
||||
return line.strip().split("=")[1].split()
|
||||
|
||||
raise Exception
|
||||
|
||||
def grub_get_boot_order():
|
||||
data = run_cmd("grub-editenv /mnt/aux/grub/grubenv list".split(), None) # No need for testfile, will be returned from uboot
|
||||
|
||||
for line in data:
|
||||
if "ORDER" in line:
|
||||
return line.split("=")[1].strip().split()
|
||||
|
||||
raise Exception
|
||||
|
||||
def json_get_yang_type(iface_in):
|
||||
if iface_in['link_type'] == "loopback":
|
||||
return "infix-if-type:loopback"
|
||||
@@ -1091,28 +1108,46 @@ def add_system_software_slots(out, data):
|
||||
new["class"] = slot[key].get("class")
|
||||
new["state"] = slot[key].get("state")
|
||||
new["bundle"] = {}
|
||||
slot_status=value.get("slot_status", {})
|
||||
if slot_status.get("bundle", {}).get("compatible"):
|
||||
new["bundle"]["compatible"] = slot_status.get("bundle", {}).get("compatible")
|
||||
if slot_status.get("bundle", {}).get("version"):
|
||||
new["bundle"]["version"] = slot_status.get("bundle", {}).get("version")
|
||||
if slot_status.get("checksum", {}).get("size"):
|
||||
new["size"] = str(slot_status.get("checksum", {}).get("size"))
|
||||
if slot_status.get("checksum", {}).get("sha256"):
|
||||
new["sha256"] = slot_status.get("checksum", {}).get("sha256")
|
||||
|
||||
if value.get("slot_status",{}).get("bundle", {}).get("compatible"):
|
||||
new["bundle"]["compatible"] = value.get("slot_status",{}).get("bundle", {}).get("compatible")
|
||||
if value.get("slot_status", {}).get("bundle", {}).get("version"):
|
||||
new["bundle"]["version"] = value.get("slot_status", {}).get("bundle", {}).get("version")
|
||||
if value.get("checksum", {}).get("size"):
|
||||
new["size"] = value.get("checksum", {}).get("size")
|
||||
if value.get("checksum", {}).get("sha256"):
|
||||
new["sha256"] = value.get("checksum", {}).get("sha256")
|
||||
new["installed"] = {}
|
||||
if value.get("installed", {}).get("timestamp"):
|
||||
new["installed"]["datetime"] = value.get("installed", {}).get("timestamp")
|
||||
if value.get("installed", {}).get("count"):
|
||||
new["installed"]["count"] = value.get("installed", {}).get("count")
|
||||
if slot_status.get("installed", {}).get("timestamp"):
|
||||
new["installed"]["datetime"] = slot_status.get("installed", {}).get("timestamp")
|
||||
|
||||
if slot_status.get("installed", {}).get("count"):
|
||||
new["installed"]["count"] = slot_status.get("installed", {}).get("count")
|
||||
|
||||
new["activated"] = {}
|
||||
if value.get("activated", {}).get("timestamp"):
|
||||
new["activated"]["datetime"] = value.get("activated", {}).get("timestamp")
|
||||
if value.get("activated", {}).get("count"):
|
||||
new["activated"]["count"] = value.get("activated", {}).get("count")
|
||||
if slot_status.get("activated", {}).get("timestamp"):
|
||||
new["activated"]["datetime"] = slot_status.get("activated", {}).get("timestamp")
|
||||
|
||||
if slot_status.get("activated", {}).get("count"):
|
||||
new["activated"]["count"] = slot_status.get("activated", {}).get("count")
|
||||
slots.append(new)
|
||||
out["slot"] = slots
|
||||
|
||||
def get_system_software_boot_order():
|
||||
order = None
|
||||
try:
|
||||
order = uboot_get_boot_order()
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
if order is None:
|
||||
order = grub_get_boot_order()
|
||||
except:
|
||||
pass
|
||||
|
||||
return order
|
||||
|
||||
def add_system_software(out):
|
||||
software = {}
|
||||
try:
|
||||
@@ -1120,6 +1155,9 @@ def add_system_software(out):
|
||||
software["compatible"] = data["compatible"]
|
||||
software["variant"] = data["variant"]
|
||||
software["booted"] = data["booted"]
|
||||
boot_order = get_system_software_boot_order()
|
||||
if not boot_order is None:
|
||||
software["boot-order"] = boot_order
|
||||
add_system_software_slots(software, data)
|
||||
except subprocess.CalledProcessError:
|
||||
pass # Maybe an upgrade i progress, then rauc does not respond
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[7mBOOT ORDER[0m
|
||||
net primary secondary
|
||||
|
||||
[7mNAME STATE VERSION DATE [0m
|
||||
secondary inactive pr873.2a39a38 2024-12-16T14:40:54Z
|
||||
primary inactive pr871.a4ef38f 2024-12-13T20:00:42Z
|
||||
+14
-2
@@ -60,7 +60,9 @@ if [ $# -eq 2 ] && [ $1 = "update" ]; then
|
||||
elif [ $2 = "show-bridge-mdb" ]; then
|
||||
"$SR_EMULATOR_TOOL" | "$CLI_PRETTY_TOOL" "show-bridge-mdb" > "$CLI_OUTPUT_PATH/show-bridge-mdb.txt"
|
||||
elif [ $2 = "show-ntp" ]; then
|
||||
"$SR_EMULATOR_TOOL" | "$CLI_PRETTY_TOOL" "show-ntp" > "$CLI_OUTPUT_PATH/show-ntp.txt"
|
||||
"$SR_EMULATOR_TOOL" | "$CLI_PRETTY_TOOL" "show-ntp" > "$CLI_OUTPUT_PATH/show-ntp.txt"
|
||||
elif [ $2 = "show-software" ]; then
|
||||
"$SR_EMULATOR_TOOL" | "$CLI_PRETTY_TOOL" "show-software" > "$CLI_OUTPUT_PATH/show-software.txt"
|
||||
else
|
||||
echo "Unsupported cli-pretty command $2"
|
||||
exit 1
|
||||
@@ -69,7 +71,7 @@ if [ $# -eq 2 ] && [ $1 = "update" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "1..11"
|
||||
echo "1..12"
|
||||
echo "# Running:"
|
||||
|
||||
# Show interfaces
|
||||
@@ -101,6 +103,16 @@ if ! diff -u "$CLI_OUTPUT_PATH/show-ntp.txt" "$CLI_OUTPUT_FILE"; then
|
||||
fi
|
||||
ok "\"show ntp\" output looks intact"
|
||||
|
||||
# Show software
|
||||
echo "# $SR_EMULATOR_TOOL | $CLI_PRETTY_TOOL show-software"
|
||||
"$SR_EMULATOR_TOOL" | "$CLI_PRETTY_TOOL" "show-software" > "$CLI_OUTPUT_FILE"
|
||||
|
||||
if ! diff -u "$CLI_OUTPUT_PATH/show-software.txt" "$CLI_OUTPUT_FILE"; then
|
||||
print_update_txt
|
||||
fail "\"show software\" output has changed"
|
||||
fi
|
||||
ok "\"show software\" output looks intact"
|
||||
|
||||
# Show ipv4 routes
|
||||
echo "# $SR_EMULATOR_TOOL | $CLI_PRETTY_TOOL -t show-routing-table -i ipv4"
|
||||
"$SR_EMULATOR_TOOL" | "$CLI_PRETTY_TOOL" "-t" "show-routing-table" -i "ipv4" > "$CLI_OUTPUT_FILE"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
BOOT_ORDER=net primary secondary
|
||||
@@ -1 +1 @@
|
||||
{"compatible":"infix-x86_64","variant":"","booted":"primary","boot_primary":"rootfs.0","slots":[{"rootfs.1":{"class":"rootfs","device":"/dev/disk/by-partlabel/secondary","type":"raw","bootname":"secondary","state":"inactive","parent":null,"mountpoint":null,"boot_status":"good"}},{"rootfs.0":{"class":"rootfs","device":"/dev/disk/by-partlabel/primary","type":"raw","bootname":"primary","state":"booted","parent":null,"mountpoint":null,"boot_status":"good"}},{"net.0":{"class":"net","device":"/dev/ram0","type":"raw","bootname":"net","state":"inactive","parent":null,"mountpoint":null,"boot_status":"good"}}]}
|
||||
{"compatible":"infix-aarch64","variant":"","booted":"net","boot_primary":"net.0","slots":[{"rootfs.1":{"class":"rootfs","device":"/dev/disk/by-partlabel/secondary","type":"raw","bootname":"secondary","state":"inactive","parent":null,"mountpoint":null,"boot_status":"bad","slot_status":{"bundle":{"compatible":"infix-aarch64","version":"pr873.2a39a38","hash":"99a9543fe85a2b32a4a28409f7103099fd8cfce3f3bbccb5175ace74e7bf9ea6"},"checksum":{"sha256":"d2595e8af8468c19a5da6c3fffbf27ff8e00d7ac2559a87f60591829151fd7e3","size":59707392},"installed":{"timestamp":"2024-12-16T14:40:54Z","count":12},"activated":{"timestamp":"2024-12-16T14:40:54Z","count":12},"status":"ok"}}},{"rootfs.0":{"class":"rootfs","device":"/dev/disk/by-partlabel/primary","type":"raw","bootname":"primary","state":"inactive","parent":null,"mountpoint":null,"boot_status":"bad","slot_status":{"bundle":{"compatible":"infix-aarch64","version":"pr871.a4ef38f","hash":"d36189afff31ac1193ee7eda161f7e9b2007df08ccdc0014cfa231f9a836780a"},"checksum":{"sha256":"d78a9ef52f08238972b6b9151c381327dd0c40e410f9ed4634e379056f9bacd8","size":59699200},"installed":{"timestamp":"2024-12-13T20:00:42Z","count":3},"activated":{"timestamp":"2024-12-13T20:00:42Z","count":4},"status":"ok"}}},{"net.0":{"class":"net","device":"/dev/ram0","type":"raw","bootname":"net","state":"booted","parent":null,"mountpoint":null,"boot_status":"good","slot_status":{"bundle":{"compatible":null}}}}]}
|
||||
|
||||
@@ -20,8 +20,6 @@ endif::topdoc[]
|
||||
. Wait for upgrade to finish
|
||||
. Verify boot order has changed and reboot
|
||||
. Verify that the partition is the booted
|
||||
. Restore boot order to original configured
|
||||
. Verify the boot order is the orignal configured
|
||||
|
||||
|
||||
<<<
|
||||
|
||||
@@ -25,30 +25,30 @@ PKGPATH = os.path.join(
|
||||
BUNDLEDIR,
|
||||
"package"
|
||||
)
|
||||
def get_boot_order(target):
|
||||
oper = target.get_dict("/system-state/software")
|
||||
return " ".join(oper["system-state"]["software"]["boot-order"])
|
||||
|
||||
class Uboot:
|
||||
def __init__(self, ssh):
|
||||
self.ssh=ssh
|
||||
def set_boot_order(target, order):
|
||||
target.call_dict("infix-system", {
|
||||
"set-boot-order": {
|
||||
"boot-order": order.split(" ")
|
||||
}
|
||||
})
|
||||
|
||||
def get_boot_order(self):
|
||||
order=self.ssh.runsh("sudo fw_printenv BOOT_ORDER").stdout.split("=")
|
||||
return order[1].strip()
|
||||
|
||||
def set_boot_order(self, order):
|
||||
return self.ssh.run(f"sudo fw_setenv BOOT_ORDER '{order}'".split()).returncode
|
||||
def cleanup(env, old_bootorder):
|
||||
print(f"Restore boot order to {old_bootorder}")
|
||||
target = env.attach("target", "mgmt", "netconf")
|
||||
set_boot_order(target, old_bootorder)
|
||||
target.reboot()
|
||||
if not wait_boot(target, env):
|
||||
test.fail()
|
||||
target = env.attach("target", "mgmt", "netconf")
|
||||
|
||||
class Grub:
|
||||
def __init__(self, ssh):
|
||||
self.ssh = ssh
|
||||
|
||||
def get_boot_order(self):
|
||||
lines=self.ssh.runsh("grub-editenv /mnt/aux/grub/grubenv list").stdout.split("\n")
|
||||
for line in lines:
|
||||
if "ORDER" in line:
|
||||
return line.split("=")[1].strip()
|
||||
|
||||
def set_boot_order(self, order):
|
||||
return self.ssh.run(f"sudo grub-editenv /mnt/aux/grub/grubenv set ORDER='{order}'".split()).returncode
|
||||
print("Verify the boot order is the orignal configured")
|
||||
order = get_boot_order(target)
|
||||
assert order == old_bootorder, f"Unexpected bootorder: {repr(order)}"
|
||||
|
||||
with infamy.Test() as test:
|
||||
with test.step("Set up topology and attach to target DUT"):
|
||||
@@ -65,20 +65,13 @@ with infamy.Test() as test:
|
||||
os.symlink(os.path.abspath(env.args.package), PKGPATH)
|
||||
|
||||
target = env.attach("target", "mgmt", "netconf")
|
||||
target_ssh = env.attach("target", "mgmt", "ssh")
|
||||
if target_ssh.run("test -e /sys/firmware/devicetree/base/chosen/u-boot,version".split()).returncode == 0:
|
||||
bootloader=Uboot(target_ssh)
|
||||
elif target_ssh.run("test -e /mnt/aux/grub/grubenv".split()).returncode == 0:
|
||||
bootloader=Grub(target_ssh)
|
||||
else:
|
||||
print("No supported bootloader found")
|
||||
test.skip()
|
||||
|
||||
old_bootorder=bootloader.get_boot_order()
|
||||
old_bootorder=get_boot_order(target)
|
||||
print(f"Initial bootorder: {repr(old_bootorder)}")
|
||||
|
||||
_, hport = env.ltop.xlate("host", "data")
|
||||
_, tport = env.ltop.xlate("target", "data")
|
||||
test.push_test_cleanup(lambda: cleanup(env, old_bootorder))
|
||||
|
||||
netns = infamy.IsolatedMacVlan(hport).start()
|
||||
netns.addip("192.168.0.1")
|
||||
@@ -130,7 +123,9 @@ with infamy.Test() as test:
|
||||
test.fail()
|
||||
|
||||
with test.step("Verify boot order has changed and reboot"):
|
||||
assert(old_bootorder != bootloader.get_boot_order())
|
||||
print(get_boot_order(target))
|
||||
print(old_bootorder)
|
||||
assert(old_bootorder != get_boot_order(target))
|
||||
target.reboot()
|
||||
|
||||
if not wait_boot(target, env):
|
||||
@@ -139,24 +134,10 @@ with infamy.Test() as test:
|
||||
|
||||
|
||||
with test.step("Verify that the partition is the booted"):
|
||||
should_boot=bootloader.get_boot_order().split()[0]
|
||||
should_boot=get_boot_order(target).split()[0]
|
||||
oper = target.get_dict("/system-state/software")
|
||||
booted = oper["system-state"]["software"]["booted"]
|
||||
print(f"Should boot: {should_boot}, booted: {booted}")
|
||||
assert(booted == should_boot)
|
||||
|
||||
with test.step("Restore boot order to original configured"):
|
||||
print(f"Restore boot order to {old_bootorder}")
|
||||
if bootloader.set_boot_order(old_bootorder) != 0:
|
||||
test.fail()
|
||||
target = env.attach("target", "mgmt", "netconf")
|
||||
target.reboot()
|
||||
if not wait_boot(target, env):
|
||||
test.fail()
|
||||
target = env.attach("target", "mgmt", "netconf")
|
||||
|
||||
with test.step("Verify the boot order is the orignal configured"):
|
||||
order = bootloader.get_boot_order()
|
||||
assert order == old_bootorder, f"Unexpected bootorder: {repr(order)}"
|
||||
|
||||
test.succeed()
|
||||
|
||||
@@ -2,32 +2,41 @@
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
|
||||
<!-- Title: 1x1 Pages: 1 -->
|
||||
<svg width="274pt" height="45pt"
|
||||
viewBox="0.00 0.00 274.02 45.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 41)">
|
||||
<title>1x1</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-41 270.02,-41 270.02,4 -4,4"/>
|
||||
<!-- Title: 1x2 Pages: 1 -->
|
||||
<svg width="424pt" height="55pt"
|
||||
viewBox="0.00 0.00 424.03 55.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 51)">
|
||||
<title>1x2</title>
|
||||
<polygon fill="white" stroke="transparent" points="-4,4 -4,-51 420.03,-51 420.03,4 -4,4"/>
|
||||
<!-- host -->
|
||||
<g id="node1" class="node">
|
||||
<title>host</title>
|
||||
<polygon fill="none" stroke="black" points="0,-0.5 0,-36.5 100,-36.5 100,-0.5 0,-0.5"/>
|
||||
<text text-anchor="middle" x="25" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
|
||||
<polyline fill="none" stroke="black" points="50,-0.5 50,-36.5 "/>
|
||||
<text text-anchor="middle" x="75" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polygon fill="none" stroke="black" points="0,-0.5 0,-46.5 100,-46.5 100,-0.5 0,-0.5"/>
|
||||
<text text-anchor="middle" x="25" y="-19.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">host</text>
|
||||
<polyline fill="none" stroke="black" points="50,-0.5 50,-46.5 "/>
|
||||
<text text-anchor="middle" x="75" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="50,-23.5 100,-23.5 "/>
|
||||
<text text-anchor="middle" x="75" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
</g>
|
||||
<!-- target -->
|
||||
<g id="node2" class="node">
|
||||
<title>target</title>
|
||||
<polygon fill="none" stroke="black" points="150.02,-0.5 150.02,-36.5 266.02,-36.5 266.02,-0.5 150.02,-0.5"/>
|
||||
<text text-anchor="middle" x="175.02" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="200.02,-0.5 200.02,-36.5 "/>
|
||||
<text text-anchor="middle" x="233.02" y="-14.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">target</text>
|
||||
<polygon fill="none" stroke="black" points="300.03,-0.5 300.03,-46.5 416.03,-46.5 416.03,-0.5 300.03,-0.5"/>
|
||||
<text text-anchor="middle" x="325.03" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
|
||||
<polyline fill="none" stroke="black" points="300.03,-23.5 350.03,-23.5 "/>
|
||||
<text text-anchor="middle" x="325.03" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
|
||||
<polyline fill="none" stroke="black" points="350.03,-0.5 350.03,-46.5 "/>
|
||||
<text text-anchor="middle" x="383.03" y="-19.8" font-family="DejaVu Sans Mono, Book" font-size="14.00">target</text>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge1" class="edge">
|
||||
<title>host:mgmt--target:mgmt</title>
|
||||
<path fill="none" stroke="lightgrey" stroke-width="2" d="M100,-18.5C100,-18.5 150.02,-18.5 150.02,-18.5"/>
|
||||
<path fill="none" stroke="lightgrey" stroke-width="2" d="M100,-35.5C100,-35.5 300.03,-35.5 300.03,-35.5"/>
|
||||
</g>
|
||||
<!-- host--target -->
|
||||
<g id="edge2" class="edge">
|
||||
<title>host:data--target:data</title>
|
||||
<path fill="none" stroke="black" stroke-width="2" d="M100,-11.5C100,-11.5 300.03,-11.5 300.03,-11.5"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -302,6 +302,9 @@ class Device(Transport):
|
||||
|
||||
return self.put_datastore("running", data)
|
||||
|
||||
def call_dict(self, model, call):
|
||||
pass # Need implementation
|
||||
|
||||
def call_rpc(self, rpc):
|
||||
"""Actually send a POST to RESTCONF server"""
|
||||
url = f"{self.rpc_url}/{rpc}"
|
||||
|
||||
+10
-1
@@ -13,8 +13,17 @@ class Test:
|
||||
if self.out == sys.stdout:
|
||||
sys.stdout = self.commenter
|
||||
|
||||
self.test_cleanup=[]
|
||||
self.steps = 0
|
||||
|
||||
def push_test_cleanup(self, fn):
|
||||
self.test_cleanup.append(fn)
|
||||
|
||||
def cleanup(self):
|
||||
infamy.netns.IsolatedMacVlans.Cleanup()
|
||||
for test_cleanup in reversed(self.test_cleanup):
|
||||
test_cleanup()
|
||||
|
||||
def __enter__(self):
|
||||
now = datetime.datetime.now().strftime("%F %T")
|
||||
self.out.write(f"# Starting ({now})\n")
|
||||
@@ -26,7 +35,7 @@ class Test:
|
||||
self.out.write(f"# Exiting ({now})\n")
|
||||
self.out.flush()
|
||||
|
||||
infamy.netns.IsolatedMacVlans.Cleanup()
|
||||
self.cleanup()
|
||||
|
||||
if not e:
|
||||
self._not_ok("Missing explicit test result\n")
|
||||
|
||||
@@ -47,6 +47,10 @@ class Transport(ABC):
|
||||
"""Needed since libyang is too nice and removes the original offset"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def call_dict(self, module, call):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def call_action(self, xpath):
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user