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.
106 lines
3.4 KiB
Python
106 lines
3.4 KiB
Python
"""End-to-end against govmomi's vcsim: real sessions, real tasks, no mocks.
|
|
|
|
Skipped unless the simulators are running::
|
|
|
|
docker run -d --rm -p 127.0.0.1:8989:8989 vmware/vcsim -l 0.0.0.0:8989
|
|
docker run -d --rm -p 127.0.0.1:8990:8989 vmware/vcsim -esx -l 0.0.0.0:8989
|
|
VCSIM_VCENTER_PORT=8989 VCSIM_ESXI_PORT=8990 pytest -m vcsim
|
|
|
|
vcsim accepts any credentials. It is a simulator: this proves the drivers speak
|
|
the vSphere API correctly, not that they understand a particular real host.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
import pytest
|
|
from napalm.base.exceptions import ConnectionException
|
|
|
|
from napalm_vmware import VmwareEsxiDriver, VmwareVcenterDriver
|
|
|
|
pytestmark = pytest.mark.vcsim
|
|
|
|
|
|
def _driver(cls, env):
|
|
port = os.environ.get(env)
|
|
if not port:
|
|
pytest.skip(f"{env} not set; vcsim not running")
|
|
driver = cls("127.0.0.1", "user", "pass", optional_args={"port": port, "verify_ssl": False})
|
|
driver.open()
|
|
yield driver
|
|
driver.close()
|
|
|
|
|
|
@pytest.fixture
|
|
def esxi():
|
|
yield from _driver(VmwareEsxiDriver, "VCSIM_ESXI_PORT")
|
|
|
|
|
|
@pytest.fixture
|
|
def vcenter():
|
|
yield from _driver(VmwareVcenterDriver, "VCSIM_VCENTER_PORT")
|
|
|
|
|
|
def test_esxi_reads(esxi):
|
|
assert esxi.is_alive() == {"is_alive": True}
|
|
assert esxi.get_facts()["os_version"].startswith("VMware ESXi")
|
|
assert "vmnic0" in esxi.get_interfaces()
|
|
assert esxi.get_vms()
|
|
assert esxi.get_vm_storage_pools()
|
|
assert esxi.get_virtual_networks()
|
|
assert "cpu" in esxi.get_environment()
|
|
assert isinstance(esxi.get_device_warnings(), list)
|
|
assert isinstance(esxi.get_lldp_neighbors(), dict)
|
|
|
|
|
|
def test_vcenter_reads(vcenter):
|
|
assert vcenter.get_facts()["model"] == "VMware vCenter Server"
|
|
vms = vcenter.get_vms()
|
|
assert len({vm["node"] for vm in vms}) > 1
|
|
assert vcenter.get_vm_config(vms[0]["vmid"])["vmid"] == vms[0]["vmid"]
|
|
|
|
|
|
def test_power_cycle(esxi):
|
|
vm = esxi.get_vms()[0]
|
|
esxi.stop_vm(vm["vmid"], force=True)
|
|
assert _status(esxi, vm) == "stopped"
|
|
with pytest.raises(RuntimeError, match="not running"):
|
|
esxi.suspend_vm(vm["vmid"])
|
|
esxi.start_vm(vm["vmid"])
|
|
assert _status(esxi, vm) == "running"
|
|
esxi.suspend_vm(vm["vmid"])
|
|
assert _status(esxi, vm) == "suspended"
|
|
esxi.start_vm(vm["vmid"])
|
|
esxi.reboot_vm(vm["vmid"], force=True)
|
|
assert _status(esxi, vm) == "running"
|
|
|
|
|
|
def test_snapshot_lifecycle(vcenter):
|
|
vm = vcenter.get_vms()[0]
|
|
vcenter.create_vm_snapshot(vm["vmid"], "netork-test", description="from pytest")
|
|
try:
|
|
(snap,) = [s for s in vcenter.get_vm_snapshots(vm["vmid"]) if s["name"] == "netork-test"]
|
|
assert snap["description"] == "from pytest"
|
|
with pytest.raises(ValueError, match="already"):
|
|
vcenter.create_vm_snapshot(vm["vmid"], "netork-test")
|
|
vcenter.rollback_vm_snapshot(vm["vmid"], "netork-test")
|
|
finally:
|
|
vcenter.delete_vm_snapshot(vm["vmid"], "netork-test")
|
|
assert not [s for s in vcenter.get_vm_snapshots(vm["vmid"]) if s["name"] == "netork-test"]
|
|
|
|
|
|
def test_wrong_driver_is_refused():
|
|
port = os.environ.get("VCSIM_VCENTER_PORT")
|
|
if not port:
|
|
pytest.skip("VCSIM_VCENTER_PORT not set")
|
|
driver = VmwareEsxiDriver(
|
|
"127.0.0.1", "u", "p", optional_args={"port": port, "verify_ssl": False}
|
|
)
|
|
with pytest.raises(ConnectionException, match="vmware_vcenter"):
|
|
driver.open()
|
|
|
|
|
|
def _status(driver, vm):
|
|
return next(v["status"] for v in driver.get_vms() if v["vmid"] == vm["vmid"])
|