1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
import json
import pytest
import yaml
from tests.integration_tests.instances import IntegrationInstance
from tests.integration_tests.integration_settings import PLATFORM
from tests.integration_tests.releases import CURRENT_RELEASE, IS_UBUNTU
from tests.integration_tests.util import (
lxd_has_nocloud,
verify_clean_boot,
verify_clean_log,
)
def _customize_environment(client: IntegrationInstance):
# Assert our platform can detect LXD during systemd generator timeframe.
ds_id_log = client.execute("cat /run/cloud-init/ds-identify.log").stdout
assert "check for 'LXD' returned found" in ds_id_log
if client.settings.PLATFORM == "lxd_vm":
# ds-identify runs at systemd generator time before /dev/lxd/sock.
# Assert we can expected artifact which indicates LXD is viable.
result = client.execute("cat /sys/class/dmi/id/board_name")
if not result.ok:
raise AssertionError(
"Missing expected /sys/class/dmi/id/board_name"
)
if "LXD" != result.stdout:
raise AssertionError(f"DMI board_name is not LXD: {result.stdout}")
# Having multiple datasources prevents ds-identify from short-circuiting
# detection logic with a log like:
# single entry in datasource_list (LXD) use that.
# Also, NoCloud is detected during init-local timeframe.
# If there is a race on VMs where /dev/lxd/sock is not setup in init-local
# cloud-init will fallback to NoCloud and fail this test.
client.write_to_file(
"/etc/cloud/cloud.cfg.d/99-detect-lxd-first.cfg",
"datasource_list: [LXD, NoCloud]\n",
)
# This is also to ensure that NoCloud can be detected
if CURRENT_RELEASE.series == "jammy":
# Add nocloud-net seed files because Jammy no longer delivers NoCloud
# (LP: #1958460).
client.execute("mkdir -p /var/lib/cloud/seed/nocloud-net")
client.write_to_file("/var/lib/cloud/seed/nocloud-net/meta-data", "")
client.write_to_file(
"/var/lib/cloud/seed/nocloud-net/user-data", "#cloud-config\n{}"
)
client.execute("cloud-init clean --logs")
client.restart()
@pytest.mark.skipif(not IS_UBUNTU, reason="Netplan usage")
@pytest.mark.skipif(
PLATFORM not in ["lxd_container", "lxd_vm"],
reason="Test is LXD specific",
)
def test_lxd_datasource_discovery(client: IntegrationInstance):
"""Test that DataSourceLXD is detected instead of NoCloud."""
_customize_environment(client)
result = client.execute("cloud-init status --wait --long")
if not result.ok:
raise AssertionError("cloud-init failed:\n%s", result.stderr)
if "DataSourceLXD" not in result.stdout:
raise AssertionError(
"cloud-init did not discover DataSourceLXD", result.stdout
)
netplan_yaml = client.execute("cat /etc/netplan/50-cloud-init.yaml")
netplan_cfg = yaml.safe_load(netplan_yaml)
platform = client.settings.PLATFORM
nic_dev = "eth0" if platform == "lxd_container" else "enp5s0"
assert {
"network": {"ethernets": {nic_dev: {"dhcp4": True}}, "version": 2}
} == netplan_cfg
log = client.read_from_file("/var/log/cloud-init.log")
verify_clean_log(log)
verify_clean_boot(client)
result = client.execute("cloud-id")
if result.stdout != "lxd":
raise AssertionError(
"cloud-id didn't report lxd. Result: %s", result.stdout
)
# Validate config instance data represented
data = json.loads(
client.read_from_file("/run/cloud-init/instance-data.json")
)
v1 = data["v1"]
ds_cfg = data["ds"]
assert "lxd" == v1["platform"]
assert "LXD socket API v. 1.0 (/dev/lxd/sock)" == v1["subplatform"]
ds_cfg = json.loads(client.execute("cloud-init query ds").stdout)
assert [
"_doc",
"_metadata_api_version",
"config",
"devices",
"meta-data",
] == sorted(list(ds_cfg.keys()))
if (
client.settings.PLATFORM == "lxd_vm"
and CURRENT_RELEASE.series == "bionic"
):
# pycloudlib injects user.vendor_data for lxd_vm on bionic
# to start the lxd-agent.
# https://github.com/canonical/pycloudlib/blob/main/pycloudlib/\
# lxd/defaults.py#L13-L27
# Underscore-delimited aliases exist for any keys containing hyphens or
# dots.
lxd_config_keys = ["user.meta-data", "user.vendor-data"]
else:
lxd_config_keys = ["user.meta-data"]
assert "1.0" == ds_cfg["_metadata_api_version"]
assert lxd_config_keys == list(ds_cfg["config"].keys())
assert {"public-keys": v1["public_ssh_keys"][0]} == (
yaml.safe_load(ds_cfg["config"]["user.meta-data"])
)
assert "instance-id" in ds_cfg["meta-data"]
# Some series no longer provide nocloud-net seed files (LP: #1958460)
if lxd_has_nocloud(client):
# Assert NoCloud seed files are still present in non-Jammy images
# and that NoCloud seed files provide the same content as LXD socket.
nocloud_metadata = yaml.safe_load(
client.read_from_file("/var/lib/cloud/seed/nocloud-net/meta-data")
)
assert client.instance.name == nocloud_metadata["instance-id"]
assert (
nocloud_metadata["instance-id"]
== nocloud_metadata["local-hostname"]
)
assert v1["public_ssh_keys"][0] == nocloud_metadata["public-keys"]
|