Source code for saltext.vcf.clients.vcenter_folder

"""vCenter folder (organizational hierarchy).

REST (``/api/vcenter/folder``) is read-only — list/get/list_by_type only.
vCenter has never exposed folder create/delete through the modern vAPI
surface; the only path is SOAP ``Folder.CreateFolder`` / ``Folder.Destroy_Task``,
so :func:`create` and :func:`delete` go through :mod:`saltext.vcf.utils.vim`
instead of :mod:`saltext.vcf.utils.vcenter`.
"""

import requests
from pyVmomi import vim

from saltext.vcf.utils import vcenter
from saltext.vcf.utils import vim as soap

PATH = "/api/vcenter/folder"

# Maps the REST ``type`` filter value to the vim.Datacenter attribute that
# holds that type's top-level folder.
_ROOT_ATTR = {
    "VIRTUAL_MACHINE": "vmFolder",
    "HOST": "hostFolder",
    "NETWORK": "networkFolder",
    "DATASTORE": "datastoreFolder",
}


def list_(opts, profile=None):
    return vcenter.api_get(opts, PATH, profile=profile)


def get(opts, folder_id, profile=None):
    return vcenter.api_get(opts, f"{PATH}/{folder_id}", profile=profile)


def get_or_none(opts, folder_id, profile=None):
    try:
        return get(opts, folder_id, profile=profile)
    except requests.HTTPError as exc:
        if exc.response is not None and exc.response.status_code == 404:
            return None
        raise


[docs] def list_by_type(opts, folder_type, profile=None): """``folder_type``: DATACENTER, DATASTORE, HOST, NETWORK, VIRTUAL_MACHINE.""" return vcenter.api_get(opts, PATH, params={"type": folder_type}, profile=profile)
[docs] def find_by_name(opts, name, profile=None): """Return ``{"folder": <moId>, "name": name}`` for the first folder named *name*, or ``None``. Resolved via SOAP since the REST list responses don't carry ``name``. """ obj = _find_folder_object(opts, name, profile=profile) if obj is None: return None return {"folder": obj._moId, "name": obj.name} # noqa: SLF001
[docs] def create(opts, name, folder_type, parent=None, datacenter=None, profile=None): """Create a folder named *name* of *folder_type* and return its moId. *folder_type* is one of ``VIRTUAL_MACHINE``, ``HOST``, ``NETWORK``, ``DATASTORE`` (top-level datacenter folders aren't created this way — use ``vim.Folder.CreateFolder`` on ``content.rootFolder`` directly if that's ever needed). *parent* nests the new folder under an existing folder found by name; otherwise it's created at *datacenter*'s *folder_type* root. """ parent_obj = _resolve_parent( opts, folder_type, parent=parent, datacenter=datacenter, profile=profile ) new_folder = parent_obj.CreateFolder(name=name) return new_folder._moId # noqa: SLF001
[docs] def delete(opts, folder_id, profile=None): """Delete the folder with moId *folder_id*. Fails if the folder isn't empty.""" obj = _folder_by_moid(opts, folder_id, profile=profile) task = obj.Destroy_Task() soap.wait_for_task(task)
def _folder_by_moid(opts, moid, profile=None): content = soap.content(opts, profile=profile) container = content.viewManager.CreateContainerView(content.rootFolder, [vim.Folder], True) try: for f in container.view: if f._moId == moid: # noqa: SLF001 return f finally: container.Destroy() raise LookupError(f"folder {moid!r} not found") def _find_folder_object(opts, name, profile=None): content = soap.content(opts, profile=profile) container = content.viewManager.CreateContainerView(content.rootFolder, [vim.Folder], True) try: for f in container.view: if f.name == name: return f finally: container.Destroy() return None def _find_datacenter(opts, name, profile=None): content = soap.content(opts, profile=profile) container = content.viewManager.CreateContainerView(content.rootFolder, [vim.Datacenter], True) try: for dc in container.view: if name in (dc._moId, dc.name): # noqa: SLF001 return dc finally: container.Destroy() raise LookupError(f"datacenter {name!r} not found") def _resolve_parent(opts, folder_type, parent=None, datacenter=None, profile=None): if parent: obj = _find_folder_object(opts, parent, profile=profile) if obj is None: raise LookupError(f"parent folder {parent!r} not found") return obj attr = _ROOT_ATTR.get(folder_type) if attr is None: raise ValueError(f"unknown folder_type {folder_type!r}") if not datacenter: raise ValueError(f"datacenter is required to create a top-level {folder_type} folder") dc = _find_datacenter(opts, datacenter, profile=profile) return getattr(dc, attr)