Skip to content

Commit

Permalink
tests: integration for network schema on netplan systems (#4767)
Browse files Browse the repository at this point in the history
On Noble, python netplan bindings are available in python3-netplan deb
package. The presence of they python bindings and netplan.Parser import
allow cloud-int for parse and validate network-config version 2 schema.

Add an integration tests asserting both schema validation and
annotation of invalid network configuration on Ubuntu Noble.

On non-Noble environments, expect Skipping of network-config version: 2
message.
  • Loading branch information
blackboxsw committed Jan 25, 2024
1 parent ba58168 commit 30f05a0
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 3 deletions.
102 changes: 102 additions & 0 deletions tests/integration_tests/cmd/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import pytest

from tests.integration_tests.instances import IntegrationInstance
from tests.integration_tests.releases import CURRENT_RELEASE, NOBLE
from tests.integration_tests.util import verify_clean_log

USER_DATA = """\
Expand All @@ -13,6 +14,48 @@
apt_reboot_if_required: false
"""

NET_CFG_V1 = """\
network:
version: 1
config:
- type: physical
name: eth0
subnets:
- type: dhcp
"""
NET_CFG_V1_INVALID = NET_CFG_V1.replace("config", "junk")
NET_V1_ANNOTATED = """\
network: # E1,E2
version: 1
junk:
- type: physical
name: eth0
subnets:
- type: dhcp
# Errors: -------------
# E1: 'config' is a required property
# E2: Additional properties are not allowed ('junk' was unexpected)"""

NET_CFG_V2 = """\
version: 2
ethernets:
eth0:
dhcp4: true
"""
NET_CFG_V2_INVALID = NET_CFG_V2.replace("true", "bogus")
NET_V2_ANNOTATED = """\
---
network:
ethernets:
eth0:
dhcp4: bogus # E1
version: 2
...
# Errors: -------------
# E1: Invalid netplan schema. Error in network definition: invalid boolean value 'bogus'""" # noqa: E501


@pytest.mark.user_data(USER_DATA)
class TestSchemaDeprecations:
Expand All @@ -24,6 +67,65 @@ def test_clean_log(self, class_client: IntegrationInstance):
assert "apt_update: Default: ``false``. Deprecated in version" in log
assert "apt_upgrade: Default: ``false``. Deprecated in version" in log

def test_network_config_schema_validation(
self, class_client: IntegrationInstance
):
content_responses = {
NET_CFG_V1: {"out": "Valid schema /root/net.yaml"},
NET_CFG_V1_INVALID: {
"out": "Invalid network-config /root/net.yaml",
"err": (
"network: Additional properties are not allowed"
" ('junk' was unexpected)"
),
"annotate": NET_V1_ANNOTATED,
},
}
if CURRENT_RELEASE.series >= NOBLE:
# Support for netplan API available
content_responses[NET_CFG_V2] = {
"out": "Valid schema /root/net.yaml"
}
content_responses[NET_CFG_V2_INVALID] = {
"out": "Invalid network-config /root/net.yaml",
"err": (
"Cloud config schema errors: format-l5.c20:"
" Invalid netplan schema. Error in network definition:"
" invalid boolean value 'bogus'"
),
"annotate": NET_V2_ANNOTATED,
}
else:
# No netplan API available skips validation
content_responses[NET_CFG_V2] = {
"out": (
"Skipping network-config schema validation."
" No network schema for version: 2"
)
}
content_responses[NET_CFG_V2_INVALID] = {
"out": (
"Skipping network-config schema validation."
" No network schema for version: 2"
)
}

for content, responses in content_responses.items():
class_client.write_to_file("/root/net.yaml", content)
result = class_client.execute(
"cloud-init schema --schema-type network-config"
" --config-file /root/net.yaml"
)
assert responses["out"] == result.stdout
if responses.get("err"):
assert responses["err"] in result.stderr
if responses.get("annotate"):
result = class_client.execute(
"cloud-init schema --schema-type network-config"
" --config-file /root/net.yaml --annotate"
)
assert responses["annotate"] in result.stdout

def test_schema_deprecations(self, class_client: IntegrationInstance):
"""Test schema behavior with deprecated configs."""
user_data_fn = "/root/user-data"
Expand Down
61 changes: 58 additions & 3 deletions tests/integration_tests/test_networking.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from tests.integration_tests.clouds import IntegrationCloud
from tests.integration_tests.instances import IntegrationInstance
from tests.integration_tests.integration_settings import PLATFORM
from tests.integration_tests.releases import CURRENT_RELEASE, NOBLE


def _add_dummy_bridge_to_netplan(client: IntegrationInstance):
Expand Down Expand Up @@ -131,6 +132,17 @@ def test_applied(self, client: IntegrationInstance):
eth0:
dhcp4: true
set-name: eth0
match:
macaddress: {mac_addr}
"""

BAD_NETWORK_V2 = """\
version: 2
ethernets:
eth0:
dhcp4: badval
match:
{match_condition}
"""


Expand Down Expand Up @@ -190,11 +202,54 @@ def test_schema_warnings(
expected["network"]["ethernets"]["eth0"]["match"]["macaddress"] = mac_addr
with session_cloud.launch(launch_kwargs=launch_kwargs) as client:
result = client.execute("cloud-init status --format=json")
assert result.failed
assert result.return_code == 2 # Warning
if CURRENT_RELEASE.series < NOBLE:
assert result.ok
assert result.return_code == 0 # Stable release still exit 0
else:
assert result.failed
assert result.return_code == 2 # Warnings exit 2 after 23.4
assert (
'eth01234567890123\\" is wrong: \\"name\\" not a valid ifname'
in result.stdout
)
result = client.execute("cloud-init schema --system")
assert "Invalid network-config" in result.stdout
assert "Invalid network-config " in result.stdout


@pytest.mark.skipif(
PLATFORM not in ("lxd_vm", "lxd_container"),
reason="Test requires lxc exec feature due to broken network config",
)
def test_invalid_network_v2_netplan(session_cloud: IntegrationCloud):
mac_addr = random_mac_address()
if PLATFORM == "lxd_vm":
config_dict = {
"cloud-init.network-config": BAD_NETWORK_V2.format(
match_condition=f"macaddress: {mac_addr}"
),
"volatile.eth0.hwaddr": mac_addr,
}
else:
config_dict = {
"cloud-init.network-config": BAD_NETWORK_V2.format(
match_condition="name: eth0"
)
}
with session_cloud.launch(
launch_kwargs={
"execute_via_ssh": False,
"config_dict": config_dict,
}
) as client:
status_json = client.execute("cloud-init status --format=json")
assert (
"Invalid network-config provided: Please run "
"'sudo cloud-init schema --system' to see the schema errors."
) in status_json
schema_out = client.execute("cloud-init schema --system")
assert "Invalid network-config /var/lib/cloud/instances/" in schema_out
annotate_out = client.execute("cloud-init schema --system --annotate")
assert (
"# E1: Invalid netplan schema. Error in network definition:"
" invalid boolean value 'badval" in annotate_out
)

0 comments on commit 30f05a0

Please sign in to comment.