"""Where a new VM goes: host, datastore, resource pool, folder, networks.""" from __future__ import annotations import pytest from napalm_vmware.provision.placement import Inventory as Inv from napalm_vmware.provision.placement import choose_placement def _host(moref, name, *, datastores, networks, parent="domain-c1", state="connected", maint=False): return { "_moref": moref, "name": name, "parent": parent, "datastore": datastores, "network": networks, "runtime.connectionState": state, "runtime.inMaintenanceMode": maint, } def _ds(moref, name, free, *, accessible=True, maintenance="normal"): return { "_moref": moref, "name": name, "summary": {"accessible": accessible, "freeSpace": free, "maintenanceMode": maintenance}, } @pytest.fixture def inv(): return Inv( hosts=[ _host("host-1", "esx01", datastores=["ds-1", "ds-2"], networks=["net-1", "dvpg-1"]), _host("host-2", "esx02", datastores=["ds-2", "ds-3"], networks=["net-2"]), ], datastores=[_ds("ds-1", "local1", 50), _ds("ds-2", "san", 900), _ds("ds-3", "local2", 100)], networks=[ {"_moref": "net-1", "name": "VM Network"}, {"_moref": "net-2", "name": "VM Network"}, {"_moref": "dvpg-1", "name": "DC0_DVPG0"}, ], compute_resources=[ {"_moref": "domain-c1", "parent": "group-h1", "resourcePool": "resgroup-1"} ], folders=[ {"_moref": "group-h1", "parent": "datacenter-1"}, {"_moref": "group-v1", "parent": "datacenter-1"}, ], datacenters=[{"_moref": "datacenter-1", "name": "DC0", "vmFolder": "group-v1"}], ) def test_most_free_space_wins_when_no_storage_is_named(inv): p = choose_placement(inv, storage=None, networks=["VM Network"]) assert (p.host, p.host_name, p.datastore, p.datastore_name) == ( "host-1", "esx01", "ds-2", "san", ) assert (p.resource_pool, p.folder, p.datacenter_name) == ("resgroup-1", "group-v1", "DC0") def test_the_network_moref_is_the_one_on_the_chosen_host(inv): p = choose_placement(inv, storage="local2", networks=["VM Network"]) assert (p.host, p.networks) == ("host-2", ["net-2"]) def test_a_host_must_see_every_requested_network(inv): p = choose_placement(inv, storage=None, networks=["VM Network", "DC0_DVPG0"]) assert p.host == "host-1" assert p.networks == ["net-1", "dvpg-1"] def test_named_datastore(inv): assert choose_placement(inv, storage="local1", networks=["VM Network"]).datastore == "ds-1" @pytest.mark.parametrize( ("change", "message"), [ (lambda i: None, "no usable datastore named 'nope'"), ], ) def test_unknown_datastore(inv, change, message): change(inv) with pytest.raises(ValueError, match=message): choose_placement(inv, storage="nope", networks=["VM Network"]) def test_unknown_network(inv): with pytest.raises(ValueError, match="no host"): choose_placement(inv, storage=None, networks=["Missing"]) def test_hosts_in_maintenance_or_disconnected_are_skipped(inv): inv.hosts[0]["runtime.inMaintenanceMode"] = True inv.hosts[1]["runtime.connectionState"] = "notResponding" with pytest.raises(ValueError, match="no host"): choose_placement(inv, storage=None, networks=["VM Network"]) def test_inaccessible_datastores_are_skipped(inv): inv.datastores[1]["summary"]["accessible"] = False p = choose_placement(inv, storage=None, networks=["VM Network"]) assert p.datastore_name == "local2" def test_datacenter_is_found_through_nested_folders(inv): inv.compute_resources[0]["parent"] = "group-h2" inv.folders.append({"_moref": "group-h2", "parent": "group-h1"}) assert choose_placement(inv, storage=None, networks=["VM Network"]).datacenter_name == "DC0"