__init__
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import unittest
|
||||
from ipaddress import IPv4Address, IPv4Network
|
||||
|
||||
from uvm.config import Settings
|
||||
from uvm.domain import VmRecord
|
||||
from uvm.errors import UvmError, ValidationError
|
||||
from uvm.network import NetworkManager
|
||||
from uvm.system import CommandResult
|
||||
|
||||
|
||||
class FakeRunner:
|
||||
def __init__(self) -> None:
|
||||
self.calls: list[tuple[str, ...]] = []
|
||||
self.existing_links: set[str] = set()
|
||||
self.link_details: dict[str, str] = {}
|
||||
|
||||
def run(self, command, *, check=True, capture=False, timeout=None) -> CommandResult:
|
||||
del check, capture, timeout
|
||||
args = tuple(str(part) for part in command)
|
||||
self.calls.append(args)
|
||||
if args[:3] == ("ip", "link", "show") and args[3] in self.existing_links:
|
||||
return CommandResult(args=args, returncode=0)
|
||||
if args[:6] == ("ip", "-j", "-d", "link", "show", "dev"):
|
||||
return CommandResult(args=args, returncode=0, stdout=self.link_details[args[6]])
|
||||
if args[:4] == ("ip", "link", "show", "uvm0"):
|
||||
return CommandResult(args=args, returncode=1)
|
||||
if args == ("ip", "route", "show", "default"):
|
||||
return CommandResult(args=args, returncode=0, stdout="default via 192.0.2.1 dev eth0\n")
|
||||
if len(args) > 3 and args[0] == "iptables" and "-C" in args:
|
||||
return CommandResult(args=args, returncode=1)
|
||||
return CommandResult(args=args, returncode=0)
|
||||
|
||||
|
||||
class NetworkManagerTests(unittest.TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.runner = FakeRunner()
|
||||
self.network = NetworkManager(Settings(), runner=self.runner) # type: ignore[arg-type]
|
||||
|
||||
def test_allocates_first_available_guest_address(self) -> None:
|
||||
self.assertEqual(self.network.allocate_ip([], None), IPv4Address("10.42.0.2"))
|
||||
|
||||
def test_rejects_gateway_and_used_requested_addresses(self) -> None:
|
||||
vm = VmRecord(
|
||||
id="vm-test",
|
||||
cpu=1,
|
||||
ram_mib=512,
|
||||
guest_ip="10.42.0.2",
|
||||
gateway="10.42.0.1",
|
||||
tap="uvm-test",
|
||||
mac="02:fc:00:00:00:01",
|
||||
socket="/tmp/firecracker.sock",
|
||||
config="/tmp/config.json",
|
||||
log="/tmp/firecracker.log",
|
||||
)
|
||||
with self.assertRaises(ValidationError):
|
||||
self.network.allocate_ip([vm], IPv4Address("10.42.0.1"))
|
||||
with self.assertRaises(ValidationError):
|
||||
self.network.allocate_ip([vm], IPv4Address("10.42.0.2"))
|
||||
with self.assertRaises(ValidationError):
|
||||
self.network.allocate_ip([], IPv4Address("10.42.0.0"))
|
||||
with self.assertRaises(ValidationError):
|
||||
self.network.allocate_ip([], IPv4Address("10.42.0.255"))
|
||||
|
||||
def test_mac_and_tap_names_are_bounded_and_deterministic(self) -> None:
|
||||
self.assertEqual(NetworkManager.mac_for(1), "02:fc:00:00:00:01")
|
||||
tap = NetworkManager.tap_name("vm-0123456789abcdef")
|
||||
self.assertLessEqual(len(tap), 15)
|
||||
self.assertTrue(tap.startswith("uvm-"))
|
||||
|
||||
def test_bridge_setup_adds_a_missing_bridge_and_one_nat_rule(self) -> None:
|
||||
self.network.ensure_bridge()
|
||||
|
||||
self.assertIn(("ip", "link", "add", "uvm0", "type", "bridge"), self.runner.calls)
|
||||
self.assertIn(
|
||||
("ip", "addr", "replace", "10.42.0.1/24", "dev", "uvm0"),
|
||||
self.runner.calls,
|
||||
)
|
||||
self.assertIn(
|
||||
(
|
||||
"iptables",
|
||||
"-A",
|
||||
"FORWARD",
|
||||
"-i",
|
||||
"uvm0",
|
||||
"-o",
|
||||
"eth0",
|
||||
"-s",
|
||||
"10.42.0.0/24",
|
||||
"-j",
|
||||
"ACCEPT",
|
||||
),
|
||||
self.runner.calls,
|
||||
)
|
||||
checks = [
|
||||
call
|
||||
for call in self.runner.calls
|
||||
if call[:5] == ("iptables", "-t", "nat", "-C", "POSTROUTING")
|
||||
]
|
||||
self.assertEqual(len(checks), 1)
|
||||
self.assertIn(
|
||||
(
|
||||
"iptables",
|
||||
"-t",
|
||||
"nat",
|
||||
"-A",
|
||||
"POSTROUTING",
|
||||
"-s",
|
||||
"10.42.0.0/24",
|
||||
"-o",
|
||||
"eth0",
|
||||
"-j",
|
||||
"MASQUERADE",
|
||||
),
|
||||
self.runner.calls,
|
||||
)
|
||||
|
||||
def test_bridge_and_guest_network_support_non_default_prefixes(self) -> None:
|
||||
settings = Settings(
|
||||
network=IPv4Network("10.50.0.0/16"),
|
||||
gateway=IPv4Address("10.50.0.1"),
|
||||
)
|
||||
runner = FakeRunner()
|
||||
NetworkManager(settings, runner=runner).ensure_bridge() # type: ignore[arg-type]
|
||||
|
||||
self.assertIn(
|
||||
("ip", "addr", "replace", "10.50.0.1/16", "dev", "uvm0"),
|
||||
runner.calls,
|
||||
)
|
||||
|
||||
def test_refuses_to_adopt_an_existing_tap_device(self) -> None:
|
||||
self.runner.existing_links.add("uvm-existing")
|
||||
|
||||
with self.assertRaises(UvmError):
|
||||
self.network.create_tap("uvm-existing")
|
||||
|
||||
self.assertNotIn(
|
||||
("ip", "tuntap", "add", "dev", "uvm-existing", "mode", "tap"),
|
||||
self.runner.calls,
|
||||
)
|
||||
|
||||
def test_refuses_to_reconfigure_an_existing_non_bridge_interface(self) -> None:
|
||||
self.runner.existing_links.add("uvm0")
|
||||
self.runner.link_details["uvm0"] = '[{"linkinfo": {"info_kind": "dummy"}}]'
|
||||
|
||||
with self.assertRaises(UvmError):
|
||||
self.network.ensure_bridge()
|
||||
|
||||
self.assertNotIn(
|
||||
("ip", "addr", "replace", "10.42.0.1/24", "dev", "uvm0"),
|
||||
self.runner.calls,
|
||||
)
|
||||
Reference in New Issue
Block a user