Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update upgrade test #846

Merged
merged 2 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion test/case/ietf_system/bundles/package

This file was deleted.

3 changes: 3 additions & 0 deletions test/case/ietf_system/ietf_system.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@

- name: ntp_client
case: ntp_client/test.py

- name: upgrade
case: upgrade/test.py
4 changes: 4 additions & 0 deletions test/case/ietf_system/upgrade/Readme.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ endif::topdoc[]
. Set up topology and attach to target DUT
. Start installation of selected package
. 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


<<<
Expand Down
114 changes: 103 additions & 11 deletions test/case/ietf_system/upgrade/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@

Verify it is possible to upgrade.
"""
# NOTE: THIS TEST IS HARDCODED TO NETCONF
# There is a bug somewhere in the restconf-code (infamy or rousette)
import os
import time
import netifaces
import infamy
import infamy.file_server as srv
from infamy.util import wait_boot, until

SRVPORT = 8008

Expand All @@ -17,11 +20,36 @@
"bundles"
)

bootloader=None
PKGPATH = os.path.join(
BUNDLEDIR,
"package"
)

class Uboot:
def __init__(self, ssh):
self.ssh=ssh

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

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

with infamy.Test() as test:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to add some way for a test implementation to register cleanup functions. Such that the boot order is restored even if we break the test in the middle (e.g., pressing C-c)

with test.step("Set up topology and attach to target DUT"):
env = infamy.Env()
Expand All @@ -33,22 +61,55 @@
os.unlink(PKGPATH)
except FileNotFoundError:
pass
#os.unlink(PKGPATH)
os.symlink(os.path.abspath(env.args.package), PKGPATH)

target = env.attach("target", "mgmt")
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()
mattiaswal marked this conversation as resolved.
Show resolved Hide resolved
print(f"Initial bootorder: {repr(old_bootorder)}")

_, hport = env.ltop.xlate("host", "mgmt")
_, tport = env.ltop.xlate("target", "mgmt")
hip = netifaces.ifaddresses(hport)[netifaces.AF_INET6][0]["addr"]
hip = hip.replace(f"%{hport}", f"%{tport}")
_, hport = env.ltop.xlate("host", "data")
_, tport = env.ltop.xlate("target", "data")

with srv.FileServer(("::", SRVPORT), BUNDLEDIR):
netns = infamy.IsolatedMacVlan(hport).start()
netns.addip("192.168.0.1")

target.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": tport,
"ipv4": {
"address": [
{
"ip": "192.168.0.2",
"prefix-length": 24
}
]
}
}
]
}
}
})
netns.must_reach("192.168.0.2")
with srv.FileServer(netns, "192.168.0.1", SRVPORT, BUNDLEDIR):
with test.step("Start installation of selected package"):
print(f"Installing {os.path.basename(env.args.package)}")

target.call_dict("infix-system", {
"install-bundle": {
"url": f"http://[{hip}]:{SRVPORT}/package",
"url": f"http://192.168.0.1:{SRVPORT}/package",
}
})

Expand All @@ -61,9 +122,40 @@
print("Install failed:", installer["last-error"])
test.fail()

test.succeed()

break
time.sleep(1)
else:
print("Timeout, last state:", oper)
test.fail()

with test.step("Verify boot order has changed and reboot"):
assert(old_bootorder != bootloader.get_boot_order())
target.reboot()

if not wait_boot(target, env):
test.fail()
target = env.attach("target", "mgmt", "netconf")


with test.step("Verify that the partition is the booted"):
should_boot=bootloader.get_boot_order().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)}"

print("Timeout, last state:", oper)
test.fail()
test.succeed()
23 changes: 0 additions & 23 deletions test/case/ietf_system/upgrade/topology.dot

This file was deleted.

1 change: 1 addition & 0 deletions test/case/ietf_system/upgrade/topology.dot
44 changes: 31 additions & 13 deletions test/infamy/file_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,42 @@
import functools
import http.server
import socket
from infamy.util import until


class FileServer(http.server.HTTPServer):
class FileServer:
"""Open web server on (address, port) serving files from directory"""
class RequestHandler(http.server.SimpleHTTPRequestHandler):
def log_message(self, *args, **kwargs):
pass
def __init__(self, netns, address, port, directory):
self.address = address
self.port = port
self.directory = directory
self.netns = netns
self.process = None
self.check_addres = None


def start(self):
"""start HTTP file server"""
cmd = f"httpd -p {self.address}:{self.port} -f -h {self.directory}"
self.process = self.netns.popen(cmd.split(" "))
if self.address == "[::]":
check_address = "::1"
elif self.address == "0.0.0.0":
check_address = "127.0.0.1"
else:
check_address=self.address
cmd = f"nc -z {check_address} {self.port}".split()
until(lambda: self.netns.run(cmd).returncode == 0)

address_family = socket.AF_INET6

def __init__(self, server_address, directory):
rh = functools.partial(FileServer.RequestHandler, directory=directory)
self.__tp = concurrent.futures.ThreadPoolExecutor(max_workers=1)
super().__init__(server_address, rh)
def stop(self):
"""Stop HTTP file server"""
if self.process:
self.process.terminate()
self.process.wait()
self.process = None

def __enter__(self):
self.__tp.submit(self.serve_forever)
self.start()

def __exit__(self, _, __, ___):
self.shutdown()
self.__tp.shutdown()
self.stop()
7 changes: 4 additions & 3 deletions test/test.mk
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ mode-run := -t $(BINARIES_DIR)/qemu.dot
mode := $(mode-$(TEST_MODE))

INFIX_IMAGE_ID := $(call qstrip,$(INFIX_IMAGE_ID))
binaries-$(ARCH) := $(addprefix $(INFIX_IMAGE_ID),.img -disk.img .pkg)
binaries-$(ARCH) := $(addprefix $(INFIX_IMAGE_ID),.img -disk.img)
pkg-$(ARCH) := -p $(O)/images/$(addprefix $(INFIX_IMAGE_ID),.pkg)
binaries-x86_64 += OVMF.fd
binaries := $(foreach bin,$(binaries-$(ARCH)),-f $(BINARIES_DIR)/$(bin))

Expand All @@ -32,10 +33,10 @@ export INFAMY_ARGS := --transport=netconf
endif

test:
$(test-dir)/env -r $(base) $(mode) $(binaries) $(ninepm) $(TESTS)
$(test-dir)/env -r $(base) $(mode) $(binaries) $(pkg-$(ARCH)) $(ninepm) $(TESTS)

test-sh:
$(test-dir)/env $(base) $(mode) $(binaries) -i /bin/sh
$(test-dir)/env $(base) $(mode) $(binaries) $(pkg-$(ARCH)) -i /bin/sh

test-spec:
@esc_infix_name="$(echo $(INFIX_NAME) | sed 's/\//\\\//g')"; \
Expand Down