"""VM NIC lifecycle via SOAP ``VirtualMachine.ReconfigVM_Task``."""
from pyVmomi import vim
from saltext.vcf.utils import vim as soap
_NIC_TYPES = {
"vmxnet3": vim.vm.device.VirtualVmxnet3,
"vmxnet2": vim.vm.device.VirtualVmxnet2,
"e1000": vim.vm.device.VirtualE1000,
"e1000e": vim.vm.device.VirtualE1000e,
"pcnet32": vim.vm.device.VirtualPCNet32,
"sriov": vim.vm.device.VirtualSriovEthernetCard,
}
def _vm(opts, vm_id_or_name, profile=None):
content = soap.content(opts, profile=profile)
container = content.viewManager.CreateContainerView(
content.rootFolder, [vim.VirtualMachine], True
)
try:
for vm in container.view:
if vm_id_or_name in (vm._moId, vm.name): # noqa: SLF001
return vm
finally:
container.Destroy()
raise LookupError(f"VM {vm_id_or_name!r} not found")
[docs]
def list_(opts, vm_id_or_name, profile=None):
"""Return every Ethernet device on the VM as a list of dicts."""
vm = _vm(opts, vm_id_or_name, profile=profile)
out = []
for dev in vm.config.hardware.device or []:
if not isinstance(dev, vim.vm.device.VirtualEthernetCard):
continue
backing = dev.backing
backing_kind = type(backing).__name__ if backing else None
network = getattr(backing, "network", None)
portgroup_key = getattr(getattr(backing, "port", None), "portgroupKey", None)
out.append(
{
"key": dev.key,
"label": dev.deviceInfo.label,
"summary": dev.deviceInfo.summary,
"mac_address": dev.macAddress,
"mac_type": dev.addressType,
"device_type": type(dev).__name__,
"connected": dev.connectable.connected if dev.connectable else None,
"start_connected": (dev.connectable.startConnected if dev.connectable else None),
"backing_kind": backing_kind,
"network_moid": network._moId if network else None, # noqa: SLF001
"portgroup_key": portgroup_key,
}
)
return out
[docs]
def add(
opts,
vm_id_or_name,
*,
nic_type="vmxnet3",
network_moid=None,
portgroup_key=None,
dvs_uuid=None,
mac_address=None,
start_connected=True,
profile=None,
):
"""Add a new NIC.
Either *network_moid* (legacy port group) or
*portgroup_key* + *dvs_uuid* (distributed port group) must be set.
"""
if not network_moid and not (portgroup_key and dvs_uuid):
raise ValueError("provide network_moid OR (portgroup_key AND dvs_uuid)")
nic_cls = _NIC_TYPES.get(nic_type.lower())
if nic_cls is None:
raise ValueError(f"unknown nic_type {nic_type!r}; valid: {sorted(_NIC_TYPES)}")
nic = nic_cls(key=-1)
if portgroup_key and dvs_uuid:
backing = vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo()
backing.port = vim.dvs.PortConnection(portgroupKey=portgroup_key, switchUuid=dvs_uuid)
else:
backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
backing.network = vim.Network(network_moid, None)
backing.deviceName = ""
nic.backing = backing
if mac_address:
nic.macAddress = mac_address
nic.addressType = "manual"
else:
nic.addressType = "assigned"
nic.connectable = vim.vm.device.VirtualDevice.ConnectInfo(
startConnected=start_connected, allowGuestControl=True
)
spec = vim.vm.ConfigSpec(
deviceChange=[vim.vm.device.VirtualDeviceSpec(operation="add", device=nic)]
)
vm = _vm(opts, vm_id_or_name, profile=profile)
task = vm.ReconfigVM_Task(spec=spec)
return task._moId # noqa: SLF001
[docs]
def update_backing(
opts,
vm_id_or_name,
nic_key,
*,
network_moid=None,
portgroup_key=None,
dvs_uuid=None,
profile=None,
):
"""Reattach an existing NIC to a different network / port group."""
vm = _vm(opts, vm_id_or_name, profile=profile)
nic = _find_nic(vm, nic_key)
if portgroup_key and dvs_uuid:
backing = vim.vm.device.VirtualEthernetCard.DistributedVirtualPortBackingInfo()
backing.port = vim.dvs.PortConnection(portgroupKey=portgroup_key, switchUuid=dvs_uuid)
elif network_moid:
backing = vim.vm.device.VirtualEthernetCard.NetworkBackingInfo()
backing.network = vim.Network(network_moid, None)
backing.deviceName = ""
else:
raise ValueError("provide network_moid OR (portgroup_key AND dvs_uuid)")
nic.backing = backing
spec = vim.vm.ConfigSpec(
deviceChange=[vim.vm.device.VirtualDeviceSpec(operation="edit", device=nic)]
)
task = vm.ReconfigVM_Task(spec=spec)
return task._moId # noqa: SLF001
[docs]
def set_connected(opts, vm_id_or_name, nic_key, connected, profile=None):
"""Hot-toggle a NIC's connected state."""
vm = _vm(opts, vm_id_or_name, profile=profile)
nic = _find_nic(vm, nic_key)
if nic.connectable is None:
nic.connectable = vim.vm.device.VirtualDevice.ConnectInfo()
nic.connectable.connected = bool(connected)
nic.connectable.startConnected = bool(connected)
spec = vim.vm.ConfigSpec(
deviceChange=[vim.vm.device.VirtualDeviceSpec(operation="edit", device=nic)]
)
task = vm.ReconfigVM_Task(spec=spec)
return task._moId # noqa: SLF001
def remove(opts, vm_id_or_name, nic_key, profile=None):
vm = _vm(opts, vm_id_or_name, profile=profile)
nic = _find_nic(vm, nic_key)
spec = vim.vm.ConfigSpec(
deviceChange=[vim.vm.device.VirtualDeviceSpec(operation="remove", device=nic)]
)
task = vm.ReconfigVM_Task(spec=spec)
return task._moId # noqa: SLF001
def _find_nic(vm, key):
for dev in vm.config.hardware.device or []:
if isinstance(dev, vim.vm.device.VirtualEthernetCard) and dev.key == int(key):
return dev
raise LookupError(f"NIC key {key!r} not found on VM {vm.name!r}")