Source code for saltext.vcf.clients.vim_resource_pool

"""SOAP ops on resource pools: move + share-level config.

REST ``/api/vcenter/resource-pool`` lacks both move and the share-level
``cpuAllocation``/``memoryAllocation`` config. These go through pyVmomi
``vim.ResourcePool``.
"""

from pyVmomi import vim

from saltext.vcf.utils import vim as soap


def _find_rp(opts, rp_id_or_name, profile=None):
    content = soap.content(opts, profile=profile)
    container = content.viewManager.CreateContainerView(
        content.rootFolder, [vim.ResourcePool], True
    )
    try:
        for rp in container.view:
            if rp_id_or_name in (rp._moId, rp.name):  # noqa: SLF001
                return rp
    finally:
        container.Destroy()
    raise LookupError(f"resource pool {rp_id_or_name!r} not found")


[docs] def move(opts, rp_id_or_name, target_parent, profile=None): """Move *rp_id_or_name* under *target_parent*. Synchronous. *target_parent* may be a resource-pool moId, name, or ``vim.ResourcePool``. """ rp = _find_rp(opts, rp_id_or_name, profile=profile) target = ( target_parent if isinstance(target_parent, vim.ResourcePool) else _find_rp(opts, target_parent, profile=profile) ) target.MoveIntoResourcePool(list=[rp]) return {"resource_pool": rp._moId, "new_parent": target._moId} # noqa: SLF001
def _allocation(info): return { "reservation": int(info.reservation or 0), "expandable_reservation": bool(info.expandableReservation), "limit": int(info.limit if info.limit is not None else -1), "shares_level": str(info.shares.level) if info.shares else None, "shares_value": int(info.shares.shares) if info.shares else None, }
[docs] def get_shares(opts, rp_id_or_name, profile=None): """Return ``{cpu, memory}`` allocation dicts (reservation, limit, shares).""" rp = _find_rp(opts, rp_id_or_name, profile=profile) cfg = rp.config return {"cpu": _allocation(cfg.cpuAllocation), "memory": _allocation(cfg.memoryAllocation)}
def _build_allocation(spec): """Build a *partial* ``ResourceAllocationInfo`` containing only the fields *spec* explicitly names, leaving the rest unset (``None``). ``UpdateConfig`` treats an unset field as "leave unchanged" -- critical here, because a cluster's root resource pool rejects ``reservation``/``limit``/``expandableReservation`` being present in the spec *at all* (only ``shares`` is settable there), regardless of the value. Always back-filling those fields from the pool's current values (the previous behavior) sent them unconditionally and broke shares-only updates against the root pool with ``vmodl.fault.InvalidArgument`` / ``rootSettingDisallowed``. """ info = vim.ResourceAllocationInfo() if "reservation" in spec: info.reservation = int(spec["reservation"]) if "expandable_reservation" in spec: info.expandableReservation = bool(spec["expandable_reservation"]) if "limit" in spec: info.limit = int(spec["limit"]) if "shares_level" in spec or "shares_value" in spec: shares = vim.SharesInfo() if "shares_level" in spec: shares.level = spec["shares_level"] if "shares_value" in spec: shares.shares = int(spec["shares_value"]) info.shares = shares return info
[docs] def set_shares(opts, rp_id_or_name, *, cpu=None, memory=None, profile=None): """Set CPU and/or memory allocation. Each is a dict with any of: ``reservation``, ``expandable_reservation``, ``limit``, ``shares_level`` (``low|normal|high|custom``), ``shares_value`` (int). SOAP ``ResourceConfigSpec`` requires both ``cpuAllocation`` and ``memoryAllocation`` to be present, but only the sub-fields actually named in *cpu*/*memory* are populated -- everything else is left unset so it's left unchanged server-side, rather than being re-sent with the pool's current value (see :func:`_build_allocation`). """ rp = _find_rp(opts, rp_id_or_name, profile=profile) config = vim.ResourceConfigSpec() config.cpuAllocation = _build_allocation(cpu or {}) config.memoryAllocation = _build_allocation(memory or {}) rp.UpdateConfig(name=rp.name, config=config) return get_shares(opts, rp_id_or_name, profile=profile)