Two drivers from one package, both in the hypervisor role: - vmware_esxi talks to one host directly: facts, vmnics and vmkernel NICs, CDP/LLDP neighbours, sensors, VMs, datastores and port groups. - vmware_vcenter talks to a vCenter: every VM of every host it manages, with the host as the VM's node, plus distributed port groups. It reports no interfaces of its own; the hosts' NICs belong to the hosts. Both implement the HypervisorDriver VM contract: get_vms, get_vm_config, start/stop/reboot/suspend_vm and the four snapshot methods, and emit raw device warnings (maintenance mode, disconnected host, config issues, host managed by a vCenter, free license making the API read-only). Every read is a PropertyCollector query for the explicit paths in paths.py, converted by to_plain() into dicts and lists; the parsers only ever see that. tools/harvest.py dumps exactly those paths to JSON and tools/sanitize.py scrubs the dump, so a real host can become a test fixture without code changes. A VM's vmid is its instance UUID, which survives vMotion and re-registration; a MoRef does not. Tested against govmomi's vcsim in ESXi and vCenter mode, including real power and snapshot tasks. Not yet tested against real hardware.
127 lines
4.0 KiB
Python
127 lines
4.0 KiB
Python
"""Port groups, distributed port groups, and which one a VM NIC is on."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from napalm_vmware.parse.networks import (
|
|
NetworkIndex,
|
|
dv_vlan,
|
|
network_index,
|
|
nic_network,
|
|
virtual_networks,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def vc_index(vcenter_data):
|
|
objs = vcenter_data["objects"]
|
|
return network_index(objs["HostSystem"], objs["DistributedVirtualPortgroup"])
|
|
|
|
|
|
class TestDvVlan:
|
|
@pytest.mark.parametrize(
|
|
("vlan", "expected"),
|
|
[
|
|
({"_type": "VmwareDistributedVirtualSwitchVlanIdSpec", "vlanId": 30}, 30),
|
|
({"_type": "VmwareDistributedVirtualSwitchTrunkVlanSpec", "vlanId": []}, 0),
|
|
({"_type": "VmwareDistributedVirtualSwitchPvlanSpec", "pvlanId": 101}, 101),
|
|
],
|
|
)
|
|
def test_kinds(self, vlan, expected):
|
|
assert dv_vlan({"vlan": vlan}) == expected
|
|
|
|
def test_missing(self):
|
|
assert dv_vlan(None) == 0
|
|
|
|
|
|
class TestNetworkIndex:
|
|
def test_standard_and_distributed(self, vc_index):
|
|
assert vc_index.standard["VM Network"] == 0
|
|
assert vc_index.distributed["dvportgroup-12"] == ("DC0_DVPG0", 0)
|
|
|
|
def test_standard_vlan(self, esxi_host):
|
|
esxi_host["config.network.portgroup"][0]["spec"]["vlanId"] = 40
|
|
index = network_index([esxi_host], [])
|
|
assert index.standard["VM Network"] == 40
|
|
|
|
|
|
class TestNicNetwork:
|
|
def test_standard_backing(self):
|
|
index = NetworkIndex(standard={"VM Network": 20}, distributed={})
|
|
nic = {
|
|
"backing": {
|
|
"_type": "VirtualEthernetCardNetworkBackingInfo",
|
|
"deviceName": "VM Network",
|
|
}
|
|
}
|
|
assert nic_network(nic, index) == ("VM Network", 20)
|
|
|
|
def test_distributed_backing(self, vc_index):
|
|
nic = {
|
|
"backing": {
|
|
"_type": "VirtualEthernetCardDistributedVirtualPortBackingInfo",
|
|
"port": {"portgroupKey": "dvportgroup-12"},
|
|
}
|
|
}
|
|
assert nic_network(nic, vc_index) == ("DC0_DVPG0", 0)
|
|
|
|
def test_unknown_distributed_portgroup_keeps_its_key(self, vc_index):
|
|
nic = {
|
|
"backing": {
|
|
"_type": "VirtualEthernetCardDistributedVirtualPortBackingInfo",
|
|
"port": {"portgroupKey": "dvportgroup-99"},
|
|
}
|
|
}
|
|
assert nic_network(nic, vc_index) == ("dvportgroup-99", 0)
|
|
|
|
def test_nsx_opaque_network(self, vc_index):
|
|
nic = {
|
|
"backing": {
|
|
"_type": "VirtualEthernetCardOpaqueNetworkBackingInfo",
|
|
"opaqueNetworkId": "ls-web",
|
|
}
|
|
}
|
|
assert nic_network(nic, vc_index) == ("ls-web", 0)
|
|
|
|
def test_no_backing(self, vc_index):
|
|
assert nic_network({}, vc_index) == ("", 0)
|
|
|
|
|
|
class TestVirtualNetworks:
|
|
def test_esxi_standard_portgroups(self, esxi_host):
|
|
nets = virtual_networks([esxi_host], [], [])
|
|
assert nets["VM Network"] == {
|
|
"name": "VM Network",
|
|
"type": "portgroup",
|
|
"bridge": "vSwitch0",
|
|
"vlan_id": 0,
|
|
"autostart": True,
|
|
"active": True,
|
|
}
|
|
assert set(nets) == {"VM Network", "Management Network"}
|
|
|
|
def test_vcenter_adds_distributed_portgroups_but_not_uplinks(self, vcenter_data):
|
|
objs = vcenter_data["objects"]
|
|
nets = virtual_networks(
|
|
objs["HostSystem"],
|
|
objs["DistributedVirtualPortgroup"],
|
|
objs["DistributedVirtualSwitch"],
|
|
)
|
|
assert nets["DC0_DVPG0"] == {
|
|
"name": "DC0_DVPG0",
|
|
"type": "dvportgroup",
|
|
"bridge": "DVS0",
|
|
"vlan_id": 0,
|
|
"autostart": True,
|
|
"active": True,
|
|
}
|
|
assert "DVS0-DVUplinks-8" not in nets
|
|
|
|
def test_uplink_flag_wins_over_the_name(self, vcenter_data):
|
|
objs = vcenter_data["objects"]
|
|
pgs = objs["DistributedVirtualPortgroup"]
|
|
pgs[1]["config.uplink"] = True
|
|
nets = virtual_networks([], pgs, objs["DistributedVirtualSwitch"])
|
|
assert "DC0_DVPG0" not in nets
|