Source code for saltext.vcf.clients.vcenter_content_library

"""vCenter Content Library — libraries + items + OVF deploy."""

import requests

from saltext.vcf.utils import vcenter

LIBRARY = "/api/content/library"
LOCAL_LIBRARY = "/api/content/local-library"
SUBSCRIBED_LIBRARY = "/api/content/subscribed-library"
ITEM = "/api/content/library/item"
UPDATE_SESSION = "/api/content/library/item/update-session"
OVF_ITEM = "/api/vcenter/ovf/library-item"
VM_TEMPLATE = "/api/vcenter/vm-template/library-items"


[docs] def list_(opts, profile=None): """List all content library IDs.""" return vcenter.api_get(opts, LIBRARY, profile=profile)
def get(opts, library_id, profile=None): return vcenter.api_get(opts, f"{LIBRARY}/{library_id}", profile=profile) def get_or_none(opts, library_id, profile=None): try: return get(opts, library_id, profile=profile) except requests.HTTPError as exc: if exc.response is not None and exc.response.status_code == 404: return None raise def list_local(opts, profile=None): return vcenter.api_get(opts, LOCAL_LIBRARY, profile=profile)
[docs] def create_local(opts, name, storage_backings, profile=None, **spec): """Create a local content library. *storage_backings* is a list like ``[{"type": "DATASTORE", "datastore_id": "datastore-12"}]``. """ body = { "name": name, "type": "LOCAL", "storage_backings": list(storage_backings), } publish_info = spec.pop("publish_info", None) if publish_info is not None: body["publish_info"] = publish_info body.update(spec) return vcenter.api_post(opts, LOCAL_LIBRARY, body=body, profile=profile)
def delete_local(opts, library_id, profile=None): return vcenter.api_delete(opts, f"{LOCAL_LIBRARY}/{library_id}", profile=profile) def list_subscribed(opts, profile=None): return vcenter.api_get(opts, SUBSCRIBED_LIBRARY, profile=profile)
[docs] def create_subscribed(opts, name, subscription_url, storage_backings, profile=None, **spec): """Create a subscribed content library.""" body = { "name": name, "type": "SUBSCRIBED", "subscription_info": { "subscription_url": subscription_url, **spec.pop("subscription_info", {}), }, "storage_backings": list(storage_backings), } body.update(spec) return vcenter.api_post(opts, SUBSCRIBED_LIBRARY, body=body, profile=profile)
def delete_subscribed(opts, library_id, profile=None): return vcenter.api_delete(opts, f"{SUBSCRIBED_LIBRARY}/{library_id}", profile=profile)
[docs] def list_items(opts, library_id, profile=None): """List item IDs in a content library.""" return vcenter.api_get(opts, ITEM, params={"library_id": library_id}, profile=profile)
def get_item(opts, item_id, profile=None): return vcenter.api_get(opts, f"{ITEM}/{item_id}", profile=profile) def get_item_or_none(opts, item_id, profile=None): try: return get_item(opts, item_id, profile=profile) except requests.HTTPError as exc: if exc.response is not None and exc.response.status_code == 404: return None raise def delete_item(opts, item_id, profile=None): return vcenter.api_delete(opts, f"{ITEM}/{item_id}", profile=profile) # -- library write paths --------------------------------------------------
[docs] def update_local(opts, library_id, spec, profile=None): """PATCH a local content library (rename, change publish_info, etc.).""" return vcenter.api_patch( opts, f"{LOCAL_LIBRARY}/{library_id}", body={"update_spec": dict(spec)}, profile=profile )
[docs] def update_subscribed(opts, library_id, spec, profile=None): """PATCH a subscribed content library (change url, password, auto sync...).""" return vcenter.api_patch( opts, f"{SUBSCRIBED_LIBRARY}/{library_id}", body={"update_spec": dict(spec)}, profile=profile, )
[docs] def sync_subscribed(opts, library_id, profile=None): """Trigger an immediate sync of a subscribed library.""" return vcenter.api_post( opts, f"{SUBSCRIBED_LIBRARY}/{library_id}", params={"action": "sync"}, profile=profile )
[docs] def publish_library(opts, library_id, subscriptions=None, profile=None): """Publish a local library to its subscribers. *subscriptions* is an optional list of subscription IDs; if None, publish to all. """ body = {} if subscriptions is not None: body["subscriptions"] = [{"id": s} for s in subscriptions] return vcenter.api_post( opts, f"{LOCAL_LIBRARY}/{library_id}", body=body or None, params={"action": "publish"}, profile=profile, )
[docs] def find_libraries(opts, name=None, type=None, profile=None): # pylint: disable=redefined-builtin """Search for libraries by name/type.""" spec = {} if name is not None: spec["name"] = name if type is not None: spec["type"] = type return vcenter.api_post(opts, LIBRARY, body=spec, params={"action": "find"}, profile=profile)
# -- item write paths -----------------------------------------------------
[docs] def create_item( opts, library_id, name, type, profile=None, **spec ): # pylint: disable=redefined-builtin """Create an empty library item; populate via an update session.""" body = {"library_id": library_id, "name": name, "type": type} body.update(spec) return vcenter.api_post(opts, ITEM, body=body, profile=profile)
[docs] def update_item(opts, item_id, spec, profile=None): """PATCH a library item (rename, change description, change type).""" return vcenter.api_patch( opts, f"{ITEM}/{item_id}", body={"update_spec": dict(spec)}, profile=profile )
def find_items( opts, library_id=None, name=None, type=None, profile=None ): # pylint: disable=redefined-builtin spec = {} if library_id is not None: spec["library_id"] = library_id if name is not None: spec["name"] = name if type is not None: spec["type"] = type return vcenter.api_post(opts, ITEM, body=spec, params={"action": "find"}, profile=profile) # -- update sessions ------------------------------------------------------
[docs] def update_session_create(opts, item_id, profile=None, **spec): """Open an update session against *item_id*. Returns the session id.""" body = {"library_item_id": item_id} body.update(spec) return vcenter.api_post(opts, UPDATE_SESSION, body=body, profile=profile)
def update_session_get(opts, session_id, profile=None): return vcenter.api_get(opts, f"{UPDATE_SESSION}/{session_id}", profile=profile) def update_session_complete(opts, session_id, profile=None): return vcenter.api_post( opts, f"{UPDATE_SESSION}/{session_id}", params={"action": "complete"}, profile=profile ) def update_session_cancel(opts, session_id, profile=None): return vcenter.api_post( opts, f"{UPDATE_SESSION}/{session_id}", params={"action": "cancel"}, profile=profile ) def update_session_fail(opts, session_id, error, profile=None): return vcenter.api_post( opts, f"{UPDATE_SESSION}/{session_id}", body={"client_error_message": error}, params={"action": "fail"}, profile=profile, ) def update_session_keep_alive(opts, session_id, profile=None): return vcenter.api_post( opts, f"{UPDATE_SESSION}/{session_id}", params={"action": "keep-alive"}, profile=profile )
[docs] def update_session_add_file(opts, session_id, name, source_type="PUSH", profile=None, **spec): """Register a file for upload into an update session. Returns a structure with an ``upload_endpoint`` URI the caller PUTs bytes to (when *source_type* is ``PUSH``). """ # The modern vSphere REST surface is path-style: # POST /api/content/library/item/update-session/{session_id}/file?action=add # with the file spec as the body. The legacy # ``POST /updatesession/file`` + ``{update_session_id, file_spec}`` form # 404s on vSphere 9. body = {"name": name, "source_type": source_type} body.update(spec) return vcenter.api_post( opts, f"{UPDATE_SESSION}/{session_id}/file", body=body, params={"action": "add"}, profile=profile, )
def update_session_list_files(opts, session_id, profile=None): # The modern vSphere REST surface is path-style: # GET /api/content/library/item/update-session/{session_id}/file # The legacy POST ``?action=list`` form was removed in 7.0+. return vcenter.api_get( opts, f"{UPDATE_SESSION}/{session_id}/file", profile=profile, ) # -- OVF deploy / create_from_vm ------------------------------------------
[docs] def ovf_deploy( opts, library_item_id, deployment_target, deployment_spec, profile=None, timeout=1800, ): """Deploy an OVF library item to a target resource pool/folder/host. *deployment_target* — ``{"resource_pool_id": ..., "folder_id": ..., "host_id": ...}`` *deployment_spec* — ``{"name": ..., "accept_all_eula": true, ...}`` *timeout* — request timeout in seconds. Defaults to 1800 (30 min) because vCenter holds the HTTP connection open for the full deploy, which routinely exceeds the global 30s default. """ body = { "target": deployment_target, "deployment_spec": deployment_spec, } return vcenter.api_post( opts, f"{OVF_ITEM}/{library_item_id}", body=body, params={"action": "deploy"}, profile=profile, timeout=timeout, )
[docs] def ovf_filter(opts, library_item_id, deployment_target, profile=None): """Return the OVF item's network/storage/disk inventory bound to a target.""" return vcenter.api_post( opts, f"{OVF_ITEM}/{library_item_id}", body={"target": deployment_target}, params={"action": "filter"}, profile=profile, )
[docs] def ovf_create_from_vm(opts, vm_id, create_spec, profile=None): """Export *vm_id* as a new OVF library item.""" body = {"source": {"type": "VirtualMachine", "id": vm_id}, "create_spec": create_spec} return vcenter.api_post( opts, OVF_ITEM, body=body, params={"action": "create_from_vm_template"}, profile=profile )
# -- VM template library items (vSphere 6.7+) ----------------------------- def vm_template_get(opts, item_id, profile=None): return vcenter.api_get(opts, f"{VM_TEMPLATE}/{item_id}", profile=profile) def vm_template_get_or_none(opts, item_id, profile=None): try: return vm_template_get(opts, item_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 vm_template_create(opts, create_spec, profile=None): """Capture a VM as a VM-template library item. *create_spec* must include ``name``, ``library``, ``source_vm`` and a ``placement`` block. """ return vcenter.api_post(opts, VM_TEMPLATE, body={"spec": create_spec}, profile=profile)
[docs] def vm_template_deploy(opts, item_id, deploy_spec, profile=None): """Deploy a new VM from a VM-template library item.""" return vcenter.api_post( opts, f"{VM_TEMPLATE}/{item_id}", body={"spec": deploy_spec}, params={"action": "deploy"}, profile=profile, )