"""What provisioning a VM from a cloud image needs to know about the guest, the same for every hypervisor (NetOrk/netork#794).""" from __future__ import annotations import pytest from napalm_device_types import HypervisorDriver from napalm_device_types.provisioning import ( QEMU_GUEST_AGENTS, network_config, split_compression, ) class TestGuestAgentDeclaration: """netOrk's cloud-init installs the agent through which the hypervisor reads the new VM's IP. Which agent depends on the hypervisor and on the guest; the guests a driver lists are the ones it can provision.""" def test_the_base_provisions_linux_with_qemu_guest_agent(self): assert HypervisorDriver.GUEST_AGENTS == { "linux": (("qemu-guest-agent",), ("systemctl enable --now qemu-guest-agent",)), } def test_the_declaration_is_data_netork_reads_off_the_class(self): assert not callable(vars(HypervisorDriver)["GUEST_AGENTS"]) def test_the_per_hypervisor_attributes_are_gone(self): assert not hasattr(HypervisorDriver, "GUEST_AGENT_PACKAGES") assert not hasattr(HypervisorDriver, "GUEST_AGENT_RUNCMD") class TestQemuGuestAgents: """Verified on FreeBSD 15.1 and OpenBSD 7.9 cloud images (#793).""" def test_linux_is_the_base_default(self): assert QEMU_GUEST_AGENTS["linux"] == HypervisorDriver.GUEST_AGENTS["linux"] def test_freebsd(self): assert QEMU_GUEST_AGENTS["freebsd"] == ( ("qemu-guest-agent",), ("sysrc qemu_guest_agent_enable=YES", "service qemu-guest-agent start"), ) def test_openbsd(self): assert QEMU_GUEST_AGENTS["openbsd"] == ( ("qemu-ga",), ("rcctl enable qemu_ga", "rcctl start qemu_ga"), ) @pytest.mark.parametrize("guest_os", ["freebsd", "openbsd"]) def test_no_bsd_guest_is_told_to_use_systemd(self, guest_os): _, runcmd = QEMU_GUEST_AGENTS[guest_os] assert not any("systemctl" in command for command in runcmd) class TestNetworkConfig: """cloud-init's network-config v2. FreeBSD's nuageinit reads only this version; the v1 Proxmox generates makes it skip runcmd (#793).""" def test_dhcp_nics_matched_by_mac(self): cfg = network_config([("00:50:56:aa:bb:cc", True), ("00:50:56:aa:bb:dd", False)]) assert cfg == { "version": 2, "ethernets": { "nic0": {"match": {"macaddress": "00:50:56:aa:bb:cc"}, "dhcp4": True}, }, } def test_no_dhcp_nic_means_no_network_config(self): assert network_config([("00:50:56:aa:bb:cc", False)]) is None def test_macs_are_written_in_lower_case(self): """Proxmox reports them upper-case; nuageinit compares them as given.""" cfg = network_config([("BC:24:11:AA:BB:02", True)]) assert cfg["ethernets"]["nic0"]["match"]["macaddress"] == "bc:24:11:aa:bb:02" class TestSplitCompression: """Official FreeBSD images come packed; hypervisors import unpacked ones.""" def test_xz(self): assert split_compression("FreeBSD-15.1-RELEASE-amd64-BASIC-CLOUDINIT-ufs.qcow2.xz") == ( "FreeBSD-15.1-RELEASE-amd64-BASIC-CLOUDINIT-ufs.qcow2", "xz -dc", ) @pytest.mark.parametrize( ("name", "command"), [("a.raw.gz", "gzip -dc"), ("a.raw.bz2", "bzip2 -dc"), ("a.qcow2.zst", "zstd -dc")], ) def test_other_packers(self, name, command): assert split_compression(name) == (name.rsplit(".", 1)[0], command) @pytest.mark.parametrize( "name", ["debian-13-genericcloud-amd64.qcow2", "ubuntu-24.04-server-cloudimg-amd64.img"] ) def test_an_unpacked_image_is_left_as_it_is(self, name): assert split_compression(name) == (name, None) def test_the_suffix_is_matched_case_insensitively(self): assert split_compression("IMAGE.QCOW2.XZ") == ("IMAGE.QCOW2", "xz -dc")