diff --git a/content/nutanix/docs/nutanix-aiops/python/DOC.md b/content/nutanix/docs/nutanix-aiops/python/DOC.md new file mode 100644 index 00000000..24aab7f9 --- /dev/null +++ b/content/nutanix/docs/nutanix-aiops/python/DOC.md @@ -0,0 +1,244 @@ +--- +name: nutanix-aiops +description: "Nutanix AIOps (Intelligent Operations) Python SDK - capacity planning, what-if scenarios, simulations, runway projections, cluster metrics, rightsizing" +metadata: + languages: "python" + versions: "4.2.1b1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,aiops,capacity-planning,what-if,scenario,simulation,runway,rightsizing,metrics" +--- + +# Nutanix AIOps (Intelligent Operations) Python SDK v4.2.1b1 + +**NOTE: This is a BETA package (v4.2.1b1). APIs may change in future releases.** + +The official SDK reference site only lists v4.0.a1 with 1 API class. The installed v4.2.1b1 beta is significantly ahead with 2 API classes and 18 methods. + +## Golden Rule + +Package: `ntnx_aiops_py_client` + +```python +import ntnx_aiops_py_client as aiops_client +``` + +## Authentication Setup + +```python +config = aiops_client.Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False + +api_client = aiops_client.ApiClient(configuration=config) +``` + +## API Classes (2 total, 18 methods) + +| API Class | Methods | Purpose | +|-----------|---------|---------| +| ScenariosApi | 14 | Capacity planning scenarios, simulations, runway, recommendations, reports | +| StatsApi | 4 | Entity metrics, entity types, entity descriptors, data sources | + +## Core Operations + +### 1. Create a Capacity Planning Scenario + +Scenarios model "what-if" situations to predict cluster capacity needs. + +```python +scenario_api = aiops_client.ScenariosApi(api_client=api_client) + +scenario = aiops_client.Scenario( + name="add-50-vms-scenario", + description="What if we add 50 new VMs to the production cluster?" +) + +task_response = scenario_api.create_scenario(body=scenario) +task_ext_id = task_response.data.ext_id + +# Poll task to completion +task_data = wait_for_task(prism_client, task_ext_id, timeout=300) +scenario_ext_id = task_data.completion_details[0].value +``` + +### 2. Create a Simulation within a Scenario + +Simulations define the specific workload changes (add VMs, change resources) to model. + +```python +simulation = aiops_client.Simulation( + scenario_ext_id=scenario_ext_id +) + +task_response = scenario_api.create_simulation(body=simulation) +task_ext_id = task_response.data.ext_id + +task_data = wait_for_task(prism_client, task_ext_id, timeout=300) +simulation_ext_id = task_data.completion_details[0].value +``` + +### 3. Generate Runway Projection + +Runway calculates how long before a cluster runs out of resources (CPU, memory, storage). + +```python +task_response = scenario_api.generate_runway(extId=scenario_ext_id) +task_ext_id = task_response.data.ext_id + +# Runway generation can take several minutes +task_data = wait_for_task(prism_client, task_ext_id, timeout=600) +``` + +### 4. Generate Recommendation + +Get recommendations for capacity additions based on the scenario analysis. + +```python +task_response = scenario_api.generate_recommendation(extId=scenario_ext_id) +task_ext_id = task_response.data.ext_id + +task_data = wait_for_task(prism_client, task_ext_id, timeout=600) +``` + +### 5. Generate Report + +Create a downloadable capacity planning report. + +```python +task_response = scenario_api.generate_report(extId=scenario_ext_id) +task_ext_id = task_response.data.ext_id + +task_data = wait_for_task(prism_client, task_ext_id, timeout=300) +``` + +### 6. Get Scenario Report + +Retrieve a previously generated report. + +```python +report = scenario_api.get_scenario_report(extId=scenario_ext_id) +``` + +### 7. List Scenarios + +```python +scenarios = scenario_api.list_scenarios() +for s in scenarios.data: + print(f"Scenario: {s.name}, ID: {s.ext_id}") +``` + +### 8. Get Scenario Details + +```python +scenario = scenario_api.get_scenario_by_id(extId=scenario_ext_id) +print(f"Name: {scenario.data.name}") +print(f"Description: {scenario.data.description}") +``` + +### 9. Update a Scenario + +```python +scenario_api.update_scenario_by_id( + extId=scenario_ext_id, + body=updated_scenario +) +``` + +### 10. List and Get Simulations + +```python +simulations = scenario_api.list_simulations() +for sim in simulations.data: + print(f"Simulation: {sim.ext_id}") + +sim = scenario_api.get_simulation_by_id(extId=simulation_ext_id) +``` + +### 11. Delete Scenario and Simulation + +```python +scenario_api.delete_simulation_by_id(extId=simulation_ext_id) +scenario_api.delete_scenario_by_id(extId=scenario_ext_id) +``` + +## Stats API: Cluster Metrics and Entity Data + +The StatsApi provides access to time-series metrics and entity metadata. + +### Get Available Data Sources + +```python +stats_api = aiops_client.StatsApi(api_client=api_client) + +sources = stats_api.get_sources_v4() +for source in sources.data: + print(f"Source: {source.name}, ID: {source.ext_id}") +``` + +### Get Entity Types + +```python +entity_types = stats_api.get_entity_types_v4() +for et in entity_types.data: + print(f"Entity Type: {et.name}") +``` + +### Get Entity Descriptors + +Entity descriptors define available metrics for each entity type. + +```python +descriptors = stats_api.get_entity_descriptors_v4() +for desc in descriptors.data: + print(f"Descriptor: {desc.name}, Unit: {desc.unit}") +``` + +### Get Entity Metrics + +Retrieve time-series metrics for specific entities. + +```python +metrics = stats_api.get_entity_metrics_v4() +``` + +## Key Models + +| Model | Props | Description | +|-------|-------|-------------| +| Scenario | 14 | Capacity planning scenario with name, description, cluster scope | +| ClusterMetrics | 27 | Comprehensive cluster resource metrics (CPU, memory, storage, IOPS) | +| ClusterProjection | 14 | Projected cluster resource usage over time | +| MetricDescriptor | 11 | Metadata describing an available metric (name, unit, type) | +| EntityDescriptor | 8 | Describes entity attributes available for metrics queries | +| Runway | 5 | Days remaining before resource exhaustion per resource type | +| Simulation | 5 | Workload simulation within a scenario | +| NodeProjection | 5 | Projected resource usage per node | +| Source | 4 | Data source for metrics collection | +| EntityType | 4 | Entity type classification for metrics | + +## Typical Workflow: Capacity Planning + +1. **Create scenario**: Define the what-if question (e.g., "add 50 VMs"). +2. **Create simulation**: Specify the workload details within the scenario. +3. **Generate runway**: Calculate when resources will be exhausted. +4. **Generate recommendation**: Get node/resource addition suggestions. +5. **Generate report**: Create a shareable capacity planning document. +6. **Review and clean up**: Delete scenarios when no longer needed. + +## Common Mistakes + +1. **Beta API instability**: As a beta package, APIs may change. Pin the version and test after upgrades. +2. **Insufficient timeout for runway**: Runway calculations analyze historical trends and can take several minutes on large clusters. +3. **Not deleting old scenarios**: Scenarios consume storage. Clean up completed planning exercises. +4. **Confusing scenario vs. simulation**: A scenario is the top-level container; simulations are the specific workload models within it. + +## Cross-Namespace Dependencies + +- **clustermgmt**: Cluster ext_ids for scoping scenarios and metrics to specific clusters. +- **vmm**: VM resource data used in simulation workload definitions. +- **prism (TasksApi)**: Poll async tasks for runway generation and recommendations. diff --git a/content/nutanix/docs/nutanix-clustermgmt/python/DOC.md b/content/nutanix/docs/nutanix-clustermgmt/python/DOC.md new file mode 100644 index 00000000..c636ef75 --- /dev/null +++ b/content/nutanix/docs/nutanix-clustermgmt/python/DOC.md @@ -0,0 +1,244 @@ +--- +name: nutanix-clustermgmt +description: "Nutanix Cluster Management Python SDK - cluster operations, hosts, storage containers, disks, SNMP, rsyslog, node management, cluster expansion" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,cluster,host,storage-container,disk,snmp,node,cvm,bmc" +--- + +# Nutanix Cluster Management Python SDK (v4.2.1) + +10 API classes, 84 methods, 408 models. + +## Golden Rule + +- **Package**: `ntnx_clustermgmt_py_client` +- **Install**: `pip install ntnx-clustermgmt-py-client==4.2.1` + +## Initialization + +```python +import ntnx_clustermgmt_py_client +from ntnx_clustermgmt_py_client import Configuration, ApiClient +from ntnx_clustermgmt_py_client.rest import ApiException +import ntnx_clustermgmt_py_client.models.clustermgmt.v4.config as v4ClusterConfig + +config = Configuration() +config.host = "10.0.0.1" # Prism Central IP (no https://, no port) +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False +config.max_retry_attempts = 3 +config.backoff_factor = 2 + +client = ApiClient(configuration=config) +clusters_api = ntnx_clustermgmt_py_client.ClustersApi(api_client=client) +storage_api = ntnx_clustermgmt_py_client.StorageContainersApi(api_client=client) +``` + +## Core Operations + +### 1. List AOS Clusters + +The cluster list returns BOTH AOS clusters and Prism Central. Always filter for AOS: + +```python +# List only AOS clusters (excludes Prism Central) +response = clusters_api.list_clusters( + _filter="config/clusterFunction/any(a:a eq Clustermgmt.Config.ClusterFunctionRef'AOS')" +) +clusters = response.data +for cluster in clusters: + print(f"{cluster.name} -> ext_id={cluster.ext_id}") +``` + +### 2. List Prism Central Clusters + +```python +response = clusters_api.list_clusters( + _filter="config/clusterFunction/any(a:a eq Clustermgmt.Config.ClusterFunctionRef'PRISM_CENTRAL')" +) +pc_clusters = response.data +``` + +### 3. List Storage Containers + +```python +# List storage containers for a specific cluster +response = storage_api.list_storage_containers( + _filter=f"clusterExtId eq '{cluster_ext_id}'" +) +containers = response.data +for sc in containers: + # NOTE: Use sc.container_ext_id, NOT sc.ext_id + print(f"{sc.name} -> container_ext_id={sc.container_ext_id}") +``` + +### 4. List Hosts by Cluster + +```python +response = clusters_api.list_hosts_by_cluster_id( + clusterExtId=cluster_ext_id, + _select="hostName,hypervisor,controllerVm" +) +hosts = response.data +for host in hosts: + print(f"{host.host_name} -> ext_id={host.ext_id}") +``` + +### 5. Get Cluster by ID + +```python +response = clusters_api.get_cluster_by_id(extId=cluster_ext_id) +cluster = response.data +print(f"Name: {cluster.name}") +print(f"Nodes: {cluster.config.cluster_function}") +# Access the ETag for update operations +etag = client.get_etag(response) +``` + +## Key Patterns + +### Cluster ext_id Is Used Everywhere + +The cluster `ext_id` from this namespace is referenced across ALL other namespaces: + +- **VMM**: `ClusterReference` when creating VMs requires cluster ext_id +- **Networking**: Subnet `cluster_reference` requires cluster ext_id +- **Lifecycle**: `X_Cluster_Id` header for LCM operations +- **Volumes**: Volume group cluster association + +Always retrieve cluster ext_ids first before working with other namespaces. + +### Pagination + +```python +# ClustersApi supports standard OData pagination +response = clusters_api.list_clusters( + _page=0, + _limit=50, + _orderby="name asc" +) +``` + +### Filtering and Selection + +```python +# OData filter examples +clusters_api.list_clusters( + _filter="name eq 'my-cluster'", + _select="name,config,network" +) + +# Storage containers with free space filter +storage_api.list_storage_containers( + _orderby="name asc", + _limit=100 +) +``` + +### ETag for Updates (Optimistic Concurrency) + +```python +# GET the resource first to obtain the ETag +get_response = clusters_api.get_cluster_by_id(extId=cluster_ext_id) +etag = client.get_etag(get_response) + +# Use If-Match header for update +clusters_api.update_cluster_by_id( + extId=cluster_ext_id, + body=updated_cluster, + If_Match=etag +) +``` + +## API Classes Reference + +| API Class | Methods | Description | +|-----------|---------|-------------| +| ClustersApi | 47 | Cluster CRUD, hosts, SNMP, rsyslog, fault tolerance, search, expand, node removal | +| StorageContainersApi | 10 | Storage container CRUD, stats, mount/unmount | +| ClusterProfilesApi | 7 | Cluster profile CRUD, apply/disassociate profiles | +| DisksApi | 6 | Disk listing, details, power actions by cluster | +| VcenterExtensionsApi | 4 | vCenter registration and management | +| CvmsApi | 3 | Controller VM info and management | +| BmcApi | 2 | BMC (baseboard management controller) info and updates | +| PasswordManagerApi | 2 | Cluster password management | +| SSLCertificateApi | 2 | SSL certificate operations | +| PcieDevicesApi | 1 | PCIe device listing | + +### ClustersApi Key Methods (47 total) + +Most-used methods: + +| Method | Parameters | Description | +|--------|-----------|-------------| +| `list_clusters` | _page, _limit, _filter, _orderby, _select | List all registered clusters | +| `get_cluster_by_id` | extId | Get single cluster details | +| `list_hosts_by_cluster_id` | clusterExtId, _page, _limit, _filter, _select | List hosts in a cluster | +| `get_host_by_id` | clusterExtId, extId | Get single host details | +| `list_host_gpus` | clusterExtId, extId | List GPUs on a host | +| `get_snmp_config_by_cluster_id` | clusterExtId | Get SNMP configuration | +| `get_rsyslog_server_by_id` | clusterExtId, extId | Get rsyslog config | +| `search_clusters` | body | Search clusters with criteria | +| `discover_unconfigured_nodes` | body | Discover nodes for expansion | +| `validate_node` | clusterExtId, body | Validate node before adding | +| `expand_cluster` | clusterExtId, body | Add nodes to cluster | +| `remove_node` | clusterExtId, body | Remove nodes from cluster | + +### StorageContainersApi Key Methods (10 total) + +| Method | Parameters | Description | +|--------|-----------|-------------| +| `list_storage_containers` | _page, _limit, _filter, _orderby | List storage containers | +| `get_storage_container_by_id` | extId | Get container details | +| `create_storage_container` | body | Create new container | +| `update_storage_container_by_id` | extId, body, If_Match | Update container | +| `delete_storage_container_by_id` | extId, If_Match | Delete container | +| `get_storage_container_stats` | extId | Get container statistics | +| `mount_storage_container` | extId, body | Mount on a cluster | +| `unmount_storage_container` | extId, body | Unmount from a cluster | + +## Key Models + +| Model | Notable Properties | +|-------|-------------------| +| Cluster | name, ext_id, config, network, nodes, inefficient_vm_count | +| Host | host_name, ext_id, hypervisor, controller_vm, block, cpu_model, num_cpu_cores, memory_size_bytes | +| StorageContainer | name, container_ext_id, cluster_ext_id, max_capacity_bytes, logical_used_bytes, replication_factor | +| Disk | ext_id, disk_size_bytes, storage_tier, status, serial_number, location | +| ClusterProfile | ext_id, name, node_count | + +## Common Mistakes + +1. **Not filtering for AOS clusters** - `list_clusters()` without a filter returns both AOS and Prism Central clusters. Always use the `ClusterFunctionRef'AOS'` filter unless you specifically need PC. + +2. **StorageContainer identifier** - StorageContainer uses `container_ext_id` as its unique identifier, not `ext_id`. The `ext_id` field may not exist or may be different. + +3. **Host listing requires cluster** - `list_hosts_by_cluster_id` requires `clusterExtId`. There is no global host listing endpoint. + +4. **Missing If-Match on updates** - All update/delete operations require the `If_Match` ETag header obtained from a prior GET call. + +5. **Cluster function filter syntax** - The filter uses a `any()` lambda syntax because `clusterFunction` is a collection: + ``` + config/clusterFunction/any(a:a eq Clustermgmt.Config.ClusterFunctionRef'AOS') + ``` + This is NOT standard OData - it is Nutanix-specific. + +## Model Import Paths + +```python +# Config models (Cluster, Host, StorageContainer, Disk, etc.) +import ntnx_clustermgmt_py_client.models.clustermgmt.v4.config as v4ClusterConfig + +# Operation models (expand, node removal, etc.) +import ntnx_clustermgmt_py_client.models.clustermgmt.v4.operations as v4ClusterOps + +# Common models +import ntnx_clustermgmt_py_client.models.common.v1.config as v1CommonConfig +``` diff --git a/content/nutanix/docs/nutanix-clustermgmt/python/references/clusters-api-complete.md b/content/nutanix/docs/nutanix-clustermgmt/python/references/clusters-api-complete.md new file mode 100644 index 00000000..4bec99eb --- /dev/null +++ b/content/nutanix/docs/nutanix-clustermgmt/python/references/clusters-api-complete.md @@ -0,0 +1,814 @@ +# ClustersApi Complete Reference — ntnx_clustermgmt_py_client v4.2.1 + +> 47 methods on `ClustersApi` + 10 on `StorageContainersApi` + supporting API classes. +> Import: `from ntnx_clustermgmt_py_client.api import ClustersApi, StorageContainersApi` + +--- + +## Authentication & Client Setup + +```python +import ntnx_clustermgmt_py_client as clustermgmt + +config = clustermgmt.Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "secret" +config.verify_ssl = False + +client = clustermgmt.ApiClient(configuration=config) +cluster_api = ClustersApi(api_client=client) +``` + +--- + +## ClustersApi — 47 Methods by Category + +### Cluster CRUD & Configuration (6 methods) + +#### create_cluster +Create a new cluster registration in Prism Central. +```python +cluster = clustermgmt.Cluster( + name="prod-cluster-01", + config=clustermgmt.ClusterConfigReference( + cluster_function=["AOS"], + authorized_public_key_list=[] + ), + network=clustermgmt.ClusterNetwork( + external_address=clustermgmt.IPAddressOrFQDN( + ipv4=clustermgmt.IPv4Address(value="10.0.0.100") + ) + ) +) +response = cluster_api.create_cluster(body=cluster) +task_ext_id = response.data.ext_id # async — poll task +``` + +#### get_cluster_by_id +Retrieve a single cluster by its external ID. +```python +cluster = cluster_api.get_cluster_by_id(extId="00061de6-1234-abcd-0000-000000000000") +print(cluster.data.name, cluster.data.config.build_info.version) +# ETag returned in response headers for update operations +etag = client.get_etag(cluster) +``` + +#### update_cluster_by_id +Update cluster configuration. Requires `If-Match` ETag header. +```python +# 1. GET current cluster to obtain ETag +existing = cluster_api.get_cluster_by_id(extId=cluster_ext_id) +etag = client.get_etag(existing) + +# 2. Modify fields +existing.data.name = "prod-cluster-01-renamed" + +# 3. PUT with ETag +response = cluster_api.update_cluster_by_id( + extId=cluster_ext_id, + body=existing.data, + if_match=etag +) +``` + +#### delete_cluster_by_id +Unregister a cluster from Prism Central. +```python +response = cluster_api.delete_cluster_by_id(extId=cluster_ext_id) +``` + +#### list_clusters +List all clusters with optional OData filtering and pagination. +```python +# Basic listing +clusters = cluster_api.list_clusters() + +# With OData filters +clusters = cluster_api.list_clusters( + _filter="name eq 'prod-cluster-01'", + _orderby="name asc", + _select="name,extId,config/clusterFunction", + _page=0, + _limit=50 +) +for c in clusters.data: + print(c.name, c.ext_id) +``` + +#### get_cluster_stats +Retrieve cluster performance statistics. +```python +stats = cluster_api.get_cluster_stats( + clusterExtId=cluster_ext_id, + _startTime="2024-01-01T00:00:00Z", + _endTime="2024-01-02T00:00:00Z", + _select="hypervisorCpuUsagePpm,storageUsageBytes" +) +``` + +--- + +### Host Management (16 methods) + +#### get_host_by_id +Get details of a specific host. +```python +host = cluster_api.get_host_by_id(extId=host_ext_id) +print(host.data.host_name, host.data.hypervisor.type) +``` + +#### list_hosts +List all hosts across all clusters. +```python +hosts = cluster_api.list_hosts( + _filter="hypervisor/type eq 'AHV'", + _page=0, + _limit=100 +) +``` + +#### list_hosts_by_cluster_id +List hosts belonging to a specific cluster. +```python +hosts = cluster_api.list_hosts_by_cluster_id( + clusterExtId=cluster_ext_id, + _page=0, + _limit=50 +) +``` + +#### enter_host_maintenance +Put a host into maintenance mode (migrates VMs off). +```python +maintenance_config = clustermgmt.HostMaintenanceConfig( + should_evacuate_vms=True, + non_migratable_vm_option="BLOCK" # or "POWER_OFF", "SKIP" +) +response = cluster_api.enter_host_maintenance( + extId=host_ext_id, + body=maintenance_config +) +task_ext_id = response.data.ext_id +``` + +#### exit_host_maintenance +Remove a host from maintenance mode. +```python +response = cluster_api.exit_host_maintenance(extId=host_ext_id) +task_ext_id = response.data.ext_id +``` + +#### get_host_stats +Retrieve host performance statistics. +```python +stats = cluster_api.get_host_stats( + hostExtId=host_ext_id, + _startTime="2024-01-01T00:00:00Z", + _endTime="2024-01-02T00:00:00Z" +) +``` + +#### get_host_nic_by_id +Get details of a specific physical NIC on a host. +```python +nic = cluster_api.get_host_nic_by_id( + hostExtId=host_ext_id, + extId=nic_ext_id +) +print(nic.data.name, nic.data.mac_address, nic.data.link_speed_kbps) +``` + +#### list_host_nics +List all physical NICs across all hosts. +```python +nics = cluster_api.list_host_nics(_page=0, _limit=100) +``` + +#### list_host_nics_by_host_id +List physical NICs for a specific host. +```python +nics = cluster_api.list_host_nics_by_host_id( + hostExtId=host_ext_id +) +for nic in nics.data: + print(nic.name, nic.mac_address) +``` + +#### get_virtual_nic_by_id +Get details of a virtual NIC on a host. +```python +vnic = cluster_api.get_virtual_nic_by_id( + hostExtId=host_ext_id, + extId=vnic_ext_id +) +``` + +#### list_virtual_nics_by_host_id +List virtual NICs for a specific host. +```python +vnics = cluster_api.list_virtual_nics_by_host_id( + hostExtId=host_ext_id +) +``` + +--- + +### Node Management (6 methods) + +#### discover_unconfigured_nodes +Discover nodes not yet added to any cluster. +```python +discovery_params = clustermgmt.NodeDiscoveryParams( + address_list=[ + clustermgmt.IPAddressOrFQDN( + ipv4=clustermgmt.IPv4Address(value="10.0.0.50") + ) + ] +) +response = cluster_api.discover_unconfigured_nodes(body=discovery_params) +task_ext_id = response.data.ext_id +``` + +#### expand_cluster +Add discovered nodes to an existing cluster. +```python +expand_params = clustermgmt.ExpandClusterParams( + node_list=[ + clustermgmt.NodeParam( + block_serial="BLOCK-SERIAL-123", + node_serial="NODE-SERIAL-456", + hypervisor_type="AHV", + node_position="A", + digital_certificate_map_list=[], + cvm_ip=clustermgmt.IPAddressOrFQDN( + ipv4=clustermgmt.IPv4Address(value="10.0.0.51") + ), + hypervisor_ip=clustermgmt.IPAddressOrFQDN( + ipv4=clustermgmt.IPv4Address(value="10.0.0.52") + ), + ipmi_ip=clustermgmt.IPAddressOrFQDN( + ipv4=clustermgmt.IPv4Address(value="10.0.0.53") + ) + ) + ] +) +response = cluster_api.expand_cluster( + clusterExtId=cluster_ext_id, + body=expand_params +) +task_ext_id = response.data.ext_id +``` + +#### remove_node +Remove a node from a cluster. +```python +remove_params = clustermgmt.NodeRemovalParams( + node_ext_ids=[node_ext_id], + skip_space_check=False +) +response = cluster_api.remove_node( + clusterExtId=cluster_ext_id, + body=remove_params +) +task_ext_id = response.data.ext_id +``` + +#### validate_node +Pre-validate a node before adding to a cluster. +```python +response = cluster_api.validate_node( + clusterExtId=cluster_ext_id, + body=expand_params # same shape as expand_cluster +) +task_ext_id = response.data.ext_id +``` + +#### fetch_node_networking_details +Fetch networking details for unconfigured nodes. +```python +response = cluster_api.fetch_node_networking_details( + clusterExtId=cluster_ext_id, + body=networking_params +) +task_ext_id = response.data.ext_id +``` + +#### check_hypervisor_requirements +Validate hypervisor requirements before cluster expansion. +```python +response = cluster_api.check_hypervisor_requirements( + clusterExtId=cluster_ext_id, + body=hypervisor_check_params +) +``` + +--- + +### SNMP Configuration (12 methods) + +#### add_snmp_transport +Add an SNMP transport (listener) to a cluster. +```python +transport = clustermgmt.SnmpTransport( + port=161, + protocol="UDP" +) +response = cluster_api.add_snmp_transport( + clusterExtId=cluster_ext_id, + body=transport +) +``` + +#### remove_snmp_transport +Remove an SNMP transport from a cluster. +```python +response = cluster_api.remove_snmp_transport( + clusterExtId=cluster_ext_id, + body=transport +) +``` + +#### get_snmp_config_by_cluster_id +Get the full SNMP configuration for a cluster. +```python +snmp = cluster_api.get_snmp_config_by_cluster_id( + clusterExtId=cluster_ext_id +) +print(snmp.data.is_enabled, snmp.data.transport_list) +``` + +#### create_snmp_trap +Create an SNMP trap destination. +```python +trap = clustermgmt.SnmpTrap( + address=clustermgmt.IPAddressOrFQDN( + ipv4=clustermgmt.IPv4Address(value="10.0.0.200") + ), + port=162, + protocol="UDP", + version="V2C", + community_string="public" +) +response = cluster_api.create_snmp_trap( + clusterExtId=cluster_ext_id, + body=trap +) +``` + +#### get_snmp_trap_by_id +Get a specific SNMP trap destination. +```python +trap = cluster_api.get_snmp_trap_by_id( + clusterExtId=cluster_ext_id, + extId=trap_ext_id +) +``` + +#### update_snmp_trap_by_id +Update an SNMP trap destination. Requires ETag. +```python +existing = cluster_api.get_snmp_trap_by_id( + clusterExtId=cluster_ext_id, extId=trap_ext_id +) +etag = client.get_etag(existing) +existing.data.port = 163 +response = cluster_api.update_snmp_trap_by_id( + clusterExtId=cluster_ext_id, + extId=trap_ext_id, + body=existing.data, + if_match=etag +) +``` + +#### delete_snmp_trap_by_id +Delete an SNMP trap destination. +```python +response = cluster_api.delete_snmp_trap_by_id( + clusterExtId=cluster_ext_id, + extId=trap_ext_id +) +``` + +#### create_snmp_user +Create an SNMPv3 user. +```python +user = clustermgmt.SnmpUser( + username="monitor_user", + auth_type="SHA", + auth_key="authpassword123", + priv_type="AES", + priv_key="privpassword123" +) +response = cluster_api.create_snmp_user( + clusterExtId=cluster_ext_id, + body=user +) +``` + +#### get_snmp_user_by_id +Get a specific SNMPv3 user. +```python +user = cluster_api.get_snmp_user_by_id( + clusterExtId=cluster_ext_id, + extId=user_ext_id +) +``` + +#### update_snmp_user_by_id +Update an SNMPv3 user. Requires ETag. +```python +existing = cluster_api.get_snmp_user_by_id( + clusterExtId=cluster_ext_id, extId=user_ext_id +) +etag = client.get_etag(existing) +existing.data.auth_type = "SHA256" +response = cluster_api.update_snmp_user_by_id( + clusterExtId=cluster_ext_id, + extId=user_ext_id, + body=existing.data, + if_match=etag +) +``` + +#### delete_snmp_user_by_id +Delete an SNMPv3 user. +```python +response = cluster_api.delete_snmp_user_by_id( + clusterExtId=cluster_ext_id, + extId=user_ext_id +) +``` + +#### update_snmp_status +Enable or disable SNMP on a cluster. +```python +snmp_status = clustermgmt.SnmpStatusParam(is_enabled=True) +response = cluster_api.update_snmp_status( + clusterExtId=cluster_ext_id, + body=snmp_status +) +``` + +--- + +### Rsyslog Configuration (5 methods) + +#### create_rsyslog_server +Add a remote syslog server to a cluster. +```python +rsyslog = clustermgmt.RsyslogServer( + server_name="syslog-prod", + ip_address=clustermgmt.IPAddressOrFQDN( + ipv4=clustermgmt.IPv4Address(value="10.0.0.210") + ), + port=514, + network_protocol="UDP", + module_list=["ACROPOLIS", "GENESIS", "PRISM"] +) +response = cluster_api.create_rsyslog_server( + clusterExtId=cluster_ext_id, + body=rsyslog +) +``` + +#### get_rsyslog_server_by_id +Get a specific rsyslog server configuration. +```python +rsyslog = cluster_api.get_rsyslog_server_by_id( + clusterExtId=cluster_ext_id, + extId=rsyslog_ext_id +) +``` + +#### update_rsyslog_server_by_id +Update an rsyslog server. Requires ETag. +```python +existing = cluster_api.get_rsyslog_server_by_id( + clusterExtId=cluster_ext_id, extId=rsyslog_ext_id +) +etag = client.get_etag(existing) +existing.data.port = 1514 +response = cluster_api.update_rsyslog_server_by_id( + clusterExtId=cluster_ext_id, + extId=rsyslog_ext_id, + body=existing.data, + if_match=etag +) +``` + +#### delete_rsyslog_server_by_id +Remove an rsyslog server from a cluster. +```python +response = cluster_api.delete_rsyslog_server_by_id( + clusterExtId=cluster_ext_id, + extId=rsyslog_ext_id +) +``` + +#### list_rsyslog_servers_by_cluster_id +List all rsyslog servers configured on a cluster. +```python +servers = cluster_api.list_rsyslog_servers_by_cluster_id( + clusterExtId=cluster_ext_id +) +for s in servers.data: + print(s.server_name, s.ip_address, s.port) +``` + +--- + +### Category Association (2 methods) + +#### associate_categories_to_cluster +Tag a cluster with one or more categories. +```python +category_refs = clustermgmt.CategoryEntityReferences( + category_ext_ids=["cat-ext-id-1", "cat-ext-id-2"] +) +response = cluster_api.associate_categories_to_cluster( + clusterExtId=cluster_ext_id, + body=category_refs +) +``` + +#### disassociate_categories_from_cluster +Remove category tags from a cluster. +```python +response = cluster_api.disassociate_categories_from_cluster( + clusterExtId=cluster_ext_id, + body=category_refs +) +``` + +--- + +### GPU Profiles (2 methods) + +#### list_physical_gpu_profiles +List physical GPU profiles available across clusters. +```python +gpus = cluster_api.list_physical_gpu_profiles( + _page=0, + _limit=50 +) +for gpu in gpus.data: + print(gpu.gpu_type, gpu.gpu_mode, gpu.device_name) +``` + +#### list_virtual_gpu_profiles +List virtual GPU (vGPU) profiles available for VM assignment. +```python +vgpus = cluster_api.list_virtual_gpu_profiles( + _page=0, + _limit=50 +) +for vgpu in vgpus.data: + print(vgpu.profile_name, vgpu.frame_buffer_size_mib) +``` + +--- + +### Rackable Units (2 methods) + +#### list_rackable_units_by_cluster_id +List rackable units (physical blocks/chassis) in a cluster. +```python +units = cluster_api.list_rackable_units_by_cluster_id( + clusterExtId=cluster_ext_id +) +for u in units.data: + print(u.rackable_unit_model, u.serial) +``` + +#### get_rackable_unit_by_id +Get details of a specific rackable unit. +```python +unit = cluster_api.get_rackable_unit_by_id( + clusterExtId=cluster_ext_id, + extId=rackable_unit_ext_id +) +``` + +--- + +### Task Response (1 method) + +#### fetch_task_response +Fetch the response payload of a completed async task. +```python +response = cluster_api.fetch_task_response( + extId=task_ext_id +) +``` + +--- + +## StorageContainersApi — 10 Methods + +```python +from ntnx_clustermgmt_py_client.api import StorageContainersApi +sc_api = StorageContainersApi(api_client=client) +``` + +### list_storage_containers +List all storage containers across clusters. +```python +containers = sc_api.list_storage_containers( + _filter="name eq 'default-container'", + _page=0, + _limit=50 +) +``` + +### get_storage_container_by_id +Get a specific storage container. +```python +container = sc_api.get_storage_container_by_id(extId=container_ext_id) +print(container.data.name, container.data.max_capacity_bytes) +``` + +### create_storage_container +Create a new storage container on a cluster. +```python +container = clustermgmt.StorageContainer( + name="new-container", + cluster_ext_id=cluster_ext_id, + replication_factor=2, + erasure_code_status="OFF", + is_compression_enabled=True, + compression_delay_secs=0 +) +response = sc_api.create_storage_container(body=container) +task_ext_id = response.data.ext_id +``` + +### update_storage_container_by_id +Update a storage container. Requires ETag. +```python +existing = sc_api.get_storage_container_by_id(extId=container_ext_id) +etag = client.get_etag(existing) +existing.data.is_compression_enabled = False +response = sc_api.update_storage_container_by_id( + extId=container_ext_id, + body=existing.data, + if_match=etag +) +``` + +### delete_storage_container_by_id +Delete a storage container (must be empty). +```python +response = sc_api.delete_storage_container_by_id(extId=container_ext_id) +``` + +### get_storage_container_stats +Get storage container performance stats. +```python +stats = sc_api.get_storage_container_stats( + extId=container_ext_id, + _startTime="2024-01-01T00:00:00Z", + _endTime="2024-01-02T00:00:00Z" +) +``` + +### list_storage_containers_by_cluster_id +List storage containers on a specific cluster. +```python +containers = sc_api.list_storage_containers_by_cluster_id( + clusterExtId=cluster_ext_id +) +``` + +### associate_categories_to_storage_container +Tag a storage container with categories. +```python +response = sc_api.associate_categories_to_storage_container( + extId=container_ext_id, + body=category_refs +) +``` + +### disassociate_categories_from_storage_container +Remove category tags from a storage container. +```python +response = sc_api.disassociate_categories_from_storage_container( + extId=container_ext_id, + body=category_refs +) +``` + +### mount_storage_container +Mount a storage container to hosts (datastore visibility). +```python +mount_params = clustermgmt.StorageContainerMountParams( + host_ext_ids=[host_ext_id_1, host_ext_id_2] +) +response = sc_api.mount_storage_container( + extId=container_ext_id, + body=mount_params +) +``` + +--- + +## Other API Classes in clustermgmt + +### DisksApi +Manage physical disks in clusters. +- `list_disks()` — List all disks +- `get_disk_by_id(extId)` — Get disk details +- `list_disks_by_cluster_id(clusterExtId)` — Disks on a specific cluster + +### DomainFaultToleranceApi +Query fault tolerance status. +- `get_domain_fault_tolerance_by_cluster_id(clusterExtId)` — Get FT status + +### RackableUnitsApi +Standalone rackable unit operations. +- `list_rackable_units()` — List all rackable units +- `get_rackable_unit_by_id(extId)` — Get rackable unit details + +### HostGpusApi +GPU operations on hosts. +- `list_host_gpus()` — List all GPUs across hosts +- `get_host_gpu_by_id(extId)` — Get GPU details + +### SnmpApi +Cluster-independent SNMP operations. +- `get_snmp_config_by_cluster_id(clusterExtId)` +- Additional SNMP endpoints + +### SearchApi +Search across cluster management entities. +- `search(body)` — Full-text search + +### StatsApi +Aggregated cluster statistics. +- `get_cluster_stats(clusterExtId)` — Cluster-level metrics + +--- + +## Common OData Filter Examples + +```python +# Clusters by name +_filter="name eq 'prod-cluster'" + +# Hosts by hypervisor type +_filter="hypervisor/type eq 'AHV'" + +# Clusters with specific function +_filter="config/clusterFunction/any(f: f eq 'AOS')" + +# Storage containers above size threshold +_filter="maxCapacityBytes gt 1099511627776" + +# Hosts in maintenance +_filter="maintenanceState eq 'IN_MAINTENANCE'" +``` + +## Pagination Pattern + +```python +def paginate_all(api_method, page_size=50, **kwargs): + """Generic paginator for any list method.""" + all_items = [] + page = 0 + while True: + response = api_method(_page=page, _limit=page_size, **kwargs) + if not response.data: + break + all_items.extend(response.data) + if len(response.data) < page_size: + break + page += 1 + return all_items + +# Usage +all_clusters = paginate_all(cluster_api.list_clusters) +all_hosts = paginate_all(cluster_api.list_hosts, _filter="hypervisor/type eq 'AHV'") +``` + +## Async Task Polling + +```python +import time +from ntnx_prism_py_client.api import TasksApi + +def wait_for_task(prism_client, task_ext_id, timeout=600, interval=5): + """Poll a task until completion or timeout.""" + tasks_api = TasksApi(api_client=prism_client) + elapsed = 0 + while elapsed < timeout: + task = tasks_api.get_task_by_id(extId=task_ext_id) + status = task.data.status + if status == "SUCCEEDED": + return task.data + elif status == "FAILED": + raise Exception(f"Task {task_ext_id} failed: {task.data.error_messages}") + time.sleep(interval) + elapsed += interval + raise TimeoutError(f"Task {task_ext_id} timed out after {timeout}s") +``` diff --git a/content/nutanix/docs/nutanix-datapolicies/python/DOC.md b/content/nutanix/docs/nutanix-datapolicies/python/DOC.md new file mode 100644 index 00000000..3b186f85 --- /dev/null +++ b/content/nutanix/docs/nutanix-datapolicies/python/DOC.md @@ -0,0 +1,247 @@ +--- +name: nutanix-datapolicies +description: "Nutanix Data Policies Python SDK - protection policies, recovery plans, storage policies, RPO/retention configuration, DR planning" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,data-policies,protection-policy,recovery-plan,storage-policy,rpo,retention,dr" +--- + +# Nutanix Data Policies Python SDK v4.2.1 + +## Golden Rule + +Package: `ntnx_datapolicies_py_client` + +```python +import ntnx_datapolicies_py_client as dp_client +``` + +## Authentication Setup + +```python +config = dp_client.Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False + +api_client = dp_client.ApiClient(configuration=config) +``` + +## API Classes (4 total, 43 methods) + +| API Class | Methods | Purpose | +|-----------|---------|---------| +| ProtectionPoliciesApi | 10 | Create/update/delete protection policies and consistency rules | +| RecoveryPlansApi | 25 | Create/manage recovery plans, network mappings, recovery stages, recovery settings | +| StoragePoliciesApi | 5 | Create/update/delete storage policies (compression, encryption, QoS) | +| EntitySyncPoliciesApi | 3 | Sync entities to remote domain managers | + +## Core Operations + +### 1. Create a Protection Policy + +Protection policies define RPO schedules, replication targets, and retention for VMs/categories. + +```python +pp_api = dp_client.ProtectionPoliciesApi(api_client=api_client) + +protection_policy = dp_client.ProtectionPolicy( + name="gold-tier-protection", + description="RPO 1 hour with cross-site replication" +) + +task_response = pp_api.create_protection_policy(body=protection_policy) +task_ext_id = task_response.data.ext_id + +# Poll task to completion +task_data = wait_for_task(prism_client, task_ext_id, timeout=120) +pp_ext_id = task_data.completion_details[0].value +``` + +### 2. Add a Consistency Rule to a Protection Policy + +Consistency rules define the RPO schedule and replication configuration within a policy. + +```python +consistency_rule = dp_client.ConsistencyRule( + schedule=dp_client.Schedule( + snapshot_interval_type=dp_client.SnapshotIntervalType.HOURLY + ), + replication_configurations=[ + dp_client.ReplicationConfiguration( + replication_location=dp_client.ReplicationLocation( + replication_sub_location=dp_client.NutanixCluster( + cluster_ext_id="remote-cluster-ext-id" + ) + ) + ) + ] +) + +task_response = pp_api.create_consistency_rule( + protectionPolicyExtId=pp_ext_id, + body=consistency_rule +) +``` + +### 3. List Protection Policies + +```python +pp_api = dp_client.ProtectionPoliciesApi(api_client=api_client) + +policies = pp_api.list_protection_policies() +for policy in policies.data: + print(f"Policy: {policy.name}, ID: {policy.ext_id}") + +# Filter by name +filtered = pp_api.list_protection_policies( + _filter="name eq 'gold-tier-protection'" +) +``` + +### 4. Create a Recovery Plan + +Recovery plans define the ordered sequence for recovering VMs/apps during failover. + +```python +rp_api = dp_client.RecoveryPlansApi(api_client=api_client) + +recovery_plan = dp_client.RecoveryPlan( + name="app-tier-recovery-plan", + description="Recovery plan for application tier" +) + +task_response = rp_api.create_recovery_plan(body=recovery_plan) +task_ext_id = task_response.data.ext_id + +task_data = wait_for_task(prism_client, task_ext_id, timeout=120) +rp_ext_id = task_data.completion_details[0].value +``` + +### 5. Add Network Mapping to Recovery Plan + +Network mappings define how source networks map to target networks at the recovery site. + +```python +network_mapping = dp_client.NetworkMapping( + name="prod-to-dr-mapping" +) + +task_response = rp_api.create_network_mapping( + recoveryPlanExtId=rp_ext_id, + body=network_mapping +) +``` + +### 6. Add Recovery Stage to Recovery Plan + +Recovery stages define ordered groups of entities to recover (boot order). + +```python +recovery_stage = dp_client.RecoveryStage( + stage_action=dp_client.StageAction( + delay_action=dp_client.DelayAction( + delay_seconds=30 + ) + ) +) + +task_response = rp_api.create_recovery_stage( + recoveryPlanExtId=rp_ext_id, + body=recovery_stage +) +``` + +### 7. Add Recovery Setting to Recovery Plan + +```python +recovery_setting = dp_client.RecoverySetting() + +task_response = rp_api.create_recovery_setting( + recoveryPlanExtId=rp_ext_id, + body=recovery_setting +) +``` + +### 8. List Recovery Plans + +```python +plans = rp_api.list_recovery_plans() +for plan in plans.data: + print(f"Plan: {plan.name}, ID: {plan.ext_id}") +``` + +## Storage Policies + +Storage policies configure compression, encryption, and QoS for storage containers. + +```python +sp_api = dp_client.StoragePoliciesApi(api_client=api_client) + +storage_policy = dp_client.StoragePolicy( + name="high-performance-policy", + compression_spec=dp_client.CompressionSpec( + compression_state=dp_client.CompressionState.INLINE + ), + encryption_spec=dp_client.EncryptionSpec( + encryption_state=dp_client.EncryptionState.ENABLED + ) +) + +task_response = sp_api.create_storage_policy(body=storage_policy) + +# List storage policies +policies = sp_api.list_storage_policies() +``` + +## Entity Sync Policies + +Sync configuration entities (categories, protection policies) to remote Prism Central instances. + +```python +esp_api = dp_client.EntitySyncPoliciesApi(api_client=api_client) + +# List entity sync policies +sync_policies = esp_api.list_entity_sync_policies() +for sp in sync_policies.data: + print(f"Sync Policy: {sp.ext_id}") + +# Trigger sync for a specific policy +esp_api.sync_entity_sync_policy_by_id(extId=sync_policy_ext_id) +``` + +## Key Models + +| Model | Props | Description | +|-------|-------|-------------| +| ProtectionPolicy | 10 | Top-level policy: name, description, entity assignments | +| RecoveryPlan | 9 | DR recovery plan with stages, network mappings, settings | +| StoragePolicy | 10 | Compression, encryption, QoS, replication factor settings | +| RecoveryStage | 8 | Ordered group of entities to recover in a plan | +| NetworkMapping | 8 | Source-to-target network mapping for DR failover | +| Schedule | 8 | RPO schedule with interval type and retention | +| ConsistencyRule | 5 | Schedule + replication config within a protection policy | +| RecoverySetting | 5 | Recovery-specific settings within a plan | +| ReplicationConfiguration | 3 | Defines replication target location | + +## Common Mistakes + +1. **Confusing datapolicies with dataprotection**: This namespace defines *policies and plans*. The `dataprotection` namespace *executes* failovers and creates snapshots. +2. **Missing network mappings in recovery plans**: Recovery plans without network mappings will fail during failover if VMs need different networks at the recovery site. +3. **Forgetting recovery stages**: Without stages, the recovery plan has no ordered boot sequence for VMs. +4. **Not syncing entity policies**: When using multi-site PC-PC setups, entity sync policies must be configured to replicate protection policies to the remote Prism Central. +5. **Missing ETag on updates/deletes**: Update and delete operations require the `If-Match` header with the current ETag. + +## Cross-Namespace Dependencies + +- **dataprotection**: Executes the policies and plans defined here (failover, snapshots, restore). +- **clustermgmt**: Remote cluster ext_ids for replication targets. +- **prism (CategoriesApi)**: Category ext_ids used in protection policy entity assignments. +- **prism (TasksApi)**: Poll async task completion. +- **networking**: Subnet ext_ids for network mappings in recovery plans. diff --git a/content/nutanix/docs/nutanix-dataprotection/python/DOC.md b/content/nutanix/docs/nutanix-dataprotection/python/DOC.md new file mode 100644 index 00000000..e29319ee --- /dev/null +++ b/content/nutanix/docs/nutanix-dataprotection/python/DOC.md @@ -0,0 +1,300 @@ +--- +name: nutanix-dataprotection +description: "Nutanix Data Protection Python SDK - recovery points, snapshots, consistency groups, failover (planned/unplanned/test), DR execution, restore" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,data-protection,snapshot,recovery-point,failover,dr,disaster-recovery,consistency-group,replicate,restore" +--- + +# Nutanix Data Protection Python SDK v4.2.1 + +## Golden Rule + +Package: `ntnx_dataprotection_py_client` + +```python +import ntnx_dataprotection_py_client as dp_client +import ntnx_dataprotection_py_client.models.dataprotection.v4.config as v4DpConfig +import ntnx_dataprotection_py_client.models.dataprotection.v4.common as v4DpCommon +``` + +## Authentication Setup + +```python +config = dp_client.Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False + +api_client = dp_client.ApiClient(configuration=config) +``` + +## API Classes (6 total) + +| API Class | Methods | Purpose | +|-----------|---------|---------| +| RecoveryPointsApi | 12 | Create/list/get/delete recovery points (snapshots), replicate, restore | +| RecoveryPlanActionsApi | 5 | Execute failover (planned, unplanned, test), cleanup test failover | +| RecoveryPlanJobsApi | 5 | Monitor and manage recovery plan job execution | +| ConsistencyGroupsApi | 5 | Group VMs/volume groups for crash-consistent snapshots | +| ProtectedResourcesApi | 3 | List and get resources under protection | +| DataProtectionClusterCapabilitiesApi | 1 | Query cluster DR capabilities | + +## Core Operations + +### 1. Create a Recovery Point (Snapshot) + +```python +rp_api = dp_client.RecoveryPointsApi(api_client=api_client) + +# Create a VM recovery point +recovery_point = v4DpConfig.RecoveryPoint( + name="pre-upgrade-snapshot", + recovery_point_type=v4DpConfig.RecoveryPointType.CRASH_CONSISTENT, + vm_recovery_points=[ + v4DpConfig.VmRecoveryPoint( + vm_ext_id="vm-ext-id-here" + ) + ], + expiration_time="2026-04-16T00:00:00Z" +) + +task_response = rp_api.create_recovery_point(body=recovery_point) +task_ext_id = task_response.data.ext_id + +# Poll task to completion +task_data = wait_for_task(prism_client, task_ext_id, timeout=300) +# Extract recovery point ext_id from completion_details +rp_ext_id = task_data.completion_details[0].value +``` + +### 2. List Recovery Points + +```python +rp_api = dp_client.RecoveryPointsApi(api_client=api_client) + +# List all recovery points +recovery_points = rp_api.list_recovery_points() +for rp in recovery_points.data: + print(f"RP: {rp.name}, Type: {rp.recovery_point_type}, " + f"Created: {rp.creation_time}") + +# Filter by name +filtered = rp_api.list_recovery_points( + _filter="name eq 'pre-upgrade-snapshot'" +) +``` + +### 3. Get Recovery Point Details + +```python +rp = rp_api.get_recovery_point_by_id(extId=rp_ext_id) +print(f"Name: {rp.data.name}") +print(f"Status: {rp.data.status}") +print(f"VM count: {len(rp.data.vm_recovery_points)}") +``` + +### 4. Restore from Recovery Point + +```python +# Restore a VM from a recovery point +restore_spec = v4DpConfig.RecoveryPointRestoreSpec( + vm_ext_id="vm-ext-id-here" +) + +task_response = rp_api.restore_recovery_point( + extId=rp_ext_id, + body=restore_spec +) +wait_for_task(prism_client, task_response.data.ext_id, timeout=600) +``` + +### 5. Replicate Recovery Point to Remote Site + +```python +replicate_spec = v4DpConfig.RecoveryPointReplicateSpec( + cluster_ext_id="remote-cluster-ext-id" +) + +task_response = rp_api.replicate_recovery_point( + extId=rp_ext_id, + body=replicate_spec +) +wait_for_task(prism_client, task_response.data.ext_id, timeout=1800) +``` + +### 6. Delete a Recovery Point + +```python +# Get ETag for concurrency control +rp = rp_api.get_recovery_point_by_id(extId=rp_ext_id) +etag = rp.headers.get("ETag") + +task_response = rp_api.delete_recovery_point_by_id( + extId=rp_ext_id, + if_match=etag +) +``` + +## Failover Operations + +### Planned Failover + +Used for scheduled DR testing or site migration. Source site is available. + +```python +rpa_api = dp_client.RecoveryPlanActionsApi(api_client=api_client) + +# Execute planned failover +failover_spec = v4DpConfig.PlannedFailoverSpec( + recovery_plan_ext_id="recovery-plan-ext-id" +) + +task_response = rpa_api.perform_planned_failover(body=failover_spec) +wait_for_task(prism_client, task_response.data.ext_id, timeout=3600) +``` + +### Unplanned Failover + +Used when the primary site is down. Data loss may occur. + +```python +failover_spec = v4DpConfig.UnplannedFailoverSpec( + recovery_plan_ext_id="recovery-plan-ext-id" +) + +task_response = rpa_api.perform_unplanned_failover(body=failover_spec) +wait_for_task(prism_client, task_response.data.ext_id, timeout=3600) +``` + +### Test Failover + +Non-disruptive DR test. Creates isolated copies at the recovery site. + +```python +test_spec = v4DpConfig.TestFailoverSpec( + recovery_plan_ext_id="recovery-plan-ext-id" +) + +task_response = rpa_api.perform_test_failover(body=test_spec) +wait_for_task(prism_client, task_response.data.ext_id, timeout=3600) + +# After testing, clean up the test failover +cleanup_spec = v4DpConfig.TestFailoverCleanupSpec( + recovery_plan_ext_id="recovery-plan-ext-id" +) +rpa_api.cleanup_test_failover(body=cleanup_spec) +``` + +## Recovery Plan Jobs + +Monitor and manage recovery plan execution. + +```python +rpj_api = dp_client.RecoveryPlanJobsApi(api_client=api_client) + +# List all recovery plan jobs +jobs = rpj_api.list_recovery_plan_jobs() +for job in jobs.data: + print(f"Job: {job.ext_id}, Status: {job.status}, " + f"Type: {job.job_type}") + +# Get details of a specific job +job = rpj_api.get_recovery_plan_job_by_id(extId=job_ext_id) +``` + +## Consistency Groups + +Group VMs and volume groups for crash-consistent snapshots. + +```python +cg_api = dp_client.ConsistencyGroupsApi(api_client=api_client) + +# Create a consistency group +cg = v4DpConfig.ConsistencyGroup( + name="app-tier-cg", + description="Application tier VMs for consistent snapshots", + members=[ + v4DpConfig.ConsistencyGroupMember( + entity_ext_id="vm-ext-id-1", + entity_type=v4DpConfig.ConsistencyGroupMemberType.VM + ), + v4DpConfig.ConsistencyGroupMember( + entity_ext_id="vm-ext-id-2", + entity_type=v4DpConfig.ConsistencyGroupMemberType.VM + ) + ] +) + +task_response = cg_api.create_consistency_group(body=cg) + +# List consistency groups +groups = cg_api.list_consistency_groups() +``` + +## Protected Resources + +```python +pr_api = dp_client.ProtectedResourcesApi(api_client=api_client) + +# List all protected resources +protected = pr_api.list_protected_resources() +for resource in protected.data: + print(f"Resource: {resource.entity_ext_id}, " + f"Type: {resource.entity_type}, " + f"Protection status: {resource.protection_status}") +``` + +## Cluster DR Capabilities + +```python +cap_api = dp_client.DataProtectionClusterCapabilitiesApi(api_client=api_client) + +capabilities = cap_api.get_cluster_capabilities() +``` + +## Key Models + +| Model | Props | Description | +|-------|-------|-------------| +| RecoveryPoint | 19 | Snapshot with name, type, expiration, VM/VG references | +| VmRecoveryPoint | 19 | VM-specific recovery point data | +| RecoveryPlanJob | 20 | Execution record for a recovery plan action | +| ProtectedResource | 13 | Entity under data protection | +| ConsistencyGroup | - | Group of VMs/VGs for crash-consistent snapshots | +| PlannedFailoverSpec | - | Input for planned failover execution | +| UnplannedFailoverSpec | - | Input for unplanned (disaster) failover | +| TestFailoverSpec | - | Input for non-disruptive test failover | +| RecoveryPointRestoreSpec | - | Input for restoring from a recovery point | +| RecoveryPointReplicateSpec | - | Input for replicating to a remote cluster | + +## Related: datapolicies Namespace + +The `ntnx_datapolicies_py_client` package handles **policy-driven** protection: + +- **ProtectionPoliciesApi**: Define RPO schedules and replication targets +- **RecoveryPlansApi**: Define ordered recovery plans for failover + +Data protection (this namespace) handles **execution**: creating snapshots, running failovers, and managing recovery point lifecycle. Use both together for a complete DR solution. + +## Common Mistakes + +1. **Not setting expiration_time**: Recovery points without expiration accumulate and consume storage. +2. **Skipping test failover**: Always validate DR plans with test failover before relying on them. +3. **Forgetting cleanup after test failover**: Test failover creates isolated copies that consume resources until cleaned up. +4. **Insufficient timeout for replication**: Cross-site replication depends on data size and WAN bandwidth; allow generous timeouts. +5. **Missing ETag on delete**: Delete operations require the `If-Match` header with the current ETag. + +## Cross-Namespace Dependencies + +- **datapolicies**: Define protection policies and recovery plans that this namespace executes. +- **vmm**: VM ext_ids referenced in recovery points come from the vmm namespace. +- **volumes**: Volume group ext_ids for VG-level protection. +- **clustermgmt**: Remote cluster ext_ids for replication targets. +- **prism (TasksApi)**: Poll async task completion. diff --git a/content/nutanix/docs/nutanix-licensing/python/DOC.md b/content/nutanix/docs/nutanix-licensing/python/DOC.md new file mode 100644 index 00000000..69051b7a --- /dev/null +++ b/content/nutanix/docs/nutanix-licensing/python/DOC.md @@ -0,0 +1,201 @@ +--- +name: nutanix-licensing +description: "Nutanix Licensing Python SDK - license management, license keys, EULA, compliance, entitlements, violations" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,licensing,license,eula,compliance,entitlement" +--- + +# Nutanix Licensing Python SDK v4.2.1 + +## Golden Rule + +Package: `ntnx_licensing_py_client` + +```python +import ntnx_licensing_py_client as lic_client +``` + +## Authentication Setup + +```python +config = lic_client.Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False + +api_client = lic_client.ApiClient(configuration=config) +``` + +## API Classes (3 total, 19 methods) + +| API Class | Methods | Purpose | +|-----------|---------|---------| +| LicensesApi | 9 | List licenses, allowances, compliances, entitlements, features, recommendations, settings, violations; sync state | +| LicenseKeysApi | 8 | Add/delete/assign/associate/reclaim license keys | +| EndUserLicenseAgreementApi | 2 | Get and accept EULA | + +## Core Operations + +### 1. Get EULA Status + +```python +eula_api = lic_client.EndUserLicenseAgreementApi(api_client=api_client) + +eula = eula_api.get_eula() +print(f"EULA accepted: {eula.data is not None}") +``` + +### 2. Accept EULA + +```python +eula_api = lic_client.EndUserLicenseAgreementApi(api_client=api_client) + +end_user = lic_client.EndUser( + username="admin", + company_name="Example Corp" +) + +eula_api.add_user(body=end_user) +``` + +### 3. List Licenses + +```python +lic_api = lic_client.LicensesApi(api_client=api_client) + +licenses = lic_api.list_licenses() +for license in licenses.data: + print(f"License: {license.ext_id}, Type: {license.license_type}") +``` + +### 4. List License Keys + +```python +lk_api = lic_client.LicenseKeysApi(api_client=api_client) + +keys = lk_api.list_license_keys() +for key in keys.data: + print(f"Key: {key.ext_id}, Status: {key.status}") +``` + +### 5. Add a License Key + +```python +lk_api = lic_client.LicenseKeysApi(api_client=api_client) + +license_key = lic_client.LicenseKey( + key="XXXXX-XXXXX-XXXXX-XXXXX" +) + +# Dry run first to validate +result = lk_api.add_license_key(body=license_key, _dryrun=True) + +# Then apply for real +result = lk_api.add_license_key(body=license_key) +``` + +### 6. Assign License Keys to a Cluster + +```python +lk_api = lic_client.LicenseKeysApi(api_client=api_client) + +assignment = lic_client.LicenseKeyAssignmentSpec( + cluster_ext_id="cluster-ext-id-here", + license_key_ext_ids=["key-ext-id-1", "key-ext-id-2"] +) + +lk_api.assign_license_keys(body=assignment) +``` + +### 7. Check Compliance + +```python +lic_api = lic_client.LicensesApi(api_client=api_client) + +compliances = lic_api.list_compliances() +for c in compliances.data: + print(f"Cluster: {c.cluster_ext_id}, Compliant: {c.is_compliant}") +``` + +### 8. List Entitlements + +```python +entitlements = lic_api.list_entitlements() +for e in entitlements.data: + print(f"Entitlement: {e.ext_id}, Feature: {e.feature_name}") +``` + +### 9. List Violations + +```python +violations = lic_api.list_violations() +for v in violations.data: + print(f"Violation: {v.ext_id}, Severity: {v.severity}") +``` + +### 10. Check Allowances + +```python +allowances = lic_api.list_allowances() +for a in allowances.data: + print(f"Allowance: {a.ext_id}") +``` + +### 11. List Available Features + +```python +features = lic_api.list_features() +for f in features.data: + print(f"Feature: {f.name}") +``` + +### 12. Sync License State + +Force a sync of licensing state with the Nutanix licensing portal. + +```python +lic_api.sync_license_state() +``` + +### 13. Reclaim a License Key + +```python +lk_api = lic_client.LicenseKeysApi(api_client=api_client) + +lk_api.reclaim_license_key(extId=key_ext_id) + +# List reclaim tokens +tokens = lk_api.list_reclaim_license_tokens() +``` + +## Key Models + +| Model | Props | Description | +|-------|-------|-------------| +| LicenseKey | 15 | License key with status, type, expiration, cluster assignment | +| License | 13 | License record with type, tier, capacity, features | +| Entitlement | 9 | Licensed feature entitlement details | +| Compliance | 7 | Cluster compliance status | +| Allowance | 7 | Permitted resource/feature allowance | +| Violation | 7 | License violation details | +| EndUser | - | EULA acceptance: username and company | +| LicenseKeyAssignmentSpec | - | Cluster-to-key assignment input | + +## Common Mistakes + +1. **Not accepting EULA first**: Many cluster operations require EULA acceptance. Check and accept before applying licenses. +2. **Skipping dry run for license keys**: Use `_dryrun=True` to validate a license key before committing. +3. **Forgetting to sync state**: After adding/removing keys, call `sync_license_state()` to ensure Prism Central reflects the latest state. +4. **Confusing license vs. license key**: A License is the entitlement record; a LicenseKey is the actual key string that grants it. + +## Cross-Namespace Dependencies + +- **clustermgmt**: Cluster ext_ids for license key assignment and compliance checks. +- **prism (TasksApi)**: Some operations may return async tasks. diff --git a/content/nutanix/docs/nutanix-lifecycle/python/DOC.md b/content/nutanix/docs/nutanix-lifecycle/python/DOC.md new file mode 100644 index 00000000..62f4da2d --- /dev/null +++ b/content/nutanix/docs/nutanix-lifecycle/python/DOC.md @@ -0,0 +1,266 @@ +--- +name: nutanix-lifecycle +description: "Nutanix Lifecycle Management (LCM) Python SDK - firmware/software updates, AOS upgrades, inventory, upgrade prechecks, recommendations, bundles" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,lcm,lifecycle,firmware,upgrade,inventory,precheck,recommendation,bundle" +--- + +# Nutanix Lifecycle Management (LCM) Python SDK v4.2.1 + +## Golden Rule + +Package: `ntnx_lifecycle_py_client` + +```python +import ntnx_lifecycle_py_client as lifecycle_client +import ntnx_lifecycle_py_client.models.lifecycle.v4.common as v4LifecycleCommon +import ntnx_lifecycle_py_client.models.lifecycle.v4.resources as v4LifecycleResources +``` + +## CRITICAL: X_Cluster_Id Header Required + +All LCM API calls require the `X_Cluster_Id` header targeting the specific Prism Element cluster. +Omitting this header will result in errors. Obtain the cluster ext_id from the clustermgmt namespace. + +```python +# Every LCM call needs this parameter +result = inventory_api.perform_inventory(X_Cluster_Id=cluster_ext_id) +``` + +## Authentication Setup + +```python +config = lifecycle_client.Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False + +api_client = lifecycle_client.ApiClient(configuration=config) +``` + +## API Classes (13 total) + +| API Class | Methods | Purpose | +|-----------|---------|---------| +| InventoryApi | 1 | Trigger LCM inventory scan | +| RecommendationsApi | 2 | Compute and retrieve upgrade recommendations | +| NotificationsApi | 2 | Compute and retrieve upgrade notifications/warnings | +| UpgradesApi | 1 | Perform firmware/software upgrades | +| EntitiesApi | 4 | List/get LCM-managed entities (firmware, software) | +| BundlesApi | 4 | Upload/manage LCM update bundles | +| PrechecksApi | 1 | Run upgrade prechecks before applying updates | +| FrameworkConfigApi | 2 | LCM framework configuration | +| ModuleConfigApi | 3 | Per-module LCM configuration | +| StatusApi | 1 | LCM operation status | +| ResourceConfigApi | 3 | Resource-level LCM configuration | +| NodePrioritiesApi | 3 | Node upgrade ordering | +| SystemAutoMgmtApi | 3 | Automatic update management settings | + +## Core Workflow: 4-Step LCM Upgrade + +### Step 1: Run Inventory + +Inventory discovers all manageable entities (firmware, AOS, hypervisor, etc.) on the target cluster. + +```python +inventory_api = lifecycle_client.InventoryApi(api_client=api_client) + +# Trigger inventory - returns a task +task_response = inventory_api.perform_inventory(X_Cluster_Id=cluster_ext_id) +task_ext_id = task_response.data.ext_id + +# Poll task to completion (inventory can take 5-15 minutes) +wait_for_task(prism_client, task_ext_id, timeout=900) +``` + +### Step 2: Get Recommendations + +After inventory completes, compute what updates are available. + +```python +rec_api = lifecycle_client.RecommendationsApi(api_client=api_client) + +# Build recommendation spec - empty spec gets all available updates +rec_spec = v4LifecycleResources.RecommendationSpec() + +# Compute recommendations +task_response = rec_api.compute_recommendations( + body=rec_spec, + X_Cluster_Id=cluster_ext_id +) +task_ext_id = task_response.data.ext_id + +# Poll task - CRITICAL: extract recommendation_id from completion_details +task_data = wait_for_task(prism_client, task_ext_id, timeout=300) +recommendation_id = task_data.completion_details[0].value +``` + +To retrieve the full recommendation details: + +```python +recommendation = rec_api.get_recommendation_by_id( + extId=recommendation_id, + X_Cluster_Id=cluster_ext_id +) +# Inspect recommended entities and target versions +for entity_rec in recommendation.data.entity_update_specs: + print(f"Entity: {entity_rec.entity_ext_id}, Target: {entity_rec.target_version}") +``` + +### Step 3: Compute Notifications (Pre-upgrade Warnings) + +Check for any warnings or required actions before upgrading. + +```python +notify_api = lifecycle_client.NotificationsApi(api_client=api_client) + +notify_spec = v4LifecycleResources.NotificationsSpec( + recommendation_ext_id=recommendation_id +) + +task_response = notify_api.compute_notifications( + body=notify_spec, + X_Cluster_Id=cluster_ext_id +) +task_ext_id = task_response.data.ext_id + +# Poll task +task_data = wait_for_task(prism_client, task_ext_id, timeout=300) +notification_id = task_data.completion_details[0].value + +# Retrieve notifications to check warnings +notifications = notify_api.get_notification_by_id( + extId=notification_id, + X_Cluster_Id=cluster_ext_id +) +# Review any warnings before proceeding +if notifications.data.warnings: + for warning in notifications.data.warnings: + print(f"Warning: {warning.message}") +``` + +### Step 4: Perform Upgrade + +Apply the recommended updates. + +```python +upgrade_api = lifecycle_client.UpgradesApi(api_client=api_client) + +upgrade_spec = v4LifecycleResources.UpgradeSpec( + recommendation_ext_id=recommendation_id +) + +task_response = upgrade_api.perform_upgrade( + body=upgrade_spec, + X_Cluster_Id=cluster_ext_id +) +task_ext_id = task_response.data.ext_id + +# Poll task - upgrades can take 30-120+ minutes depending on scope +wait_for_task(prism_client, task_ext_id, timeout=7200) +``` + +## Entity Management + +### List All LCM Entities + +```python +entities_api = lifecycle_client.EntitiesApi(api_client=api_client) + +# List all managed entities on a cluster +entities = entities_api.list_entities(X_Cluster_Id=cluster_ext_id) +for entity in entities.data: + print(f"{entity.entity_class}: {entity.entity_model} " + f"v{entity.installed_version} -> {entity.available_version}") +``` + +### Get Entity by ID + +```python +entity = entities_api.get_entity_by_id( + extId=entity_ext_id, + X_Cluster_Id=cluster_ext_id +) +``` + +## Bundle Management + +### Upload Update Bundle (Dark Site) + +For air-gapped environments, upload LCM bundles manually. + +```python +bundles_api = lifecycle_client.BundlesApi(api_client=api_client) + +# List existing bundles +bundles = bundles_api.list_bundles(X_Cluster_Id=cluster_ext_id) + +# Upload a bundle (dark site scenario) +# bundle_spec = v4LifecycleResources.BundleSpec(...) +# bundles_api.upload_bundle(body=bundle_spec, X_Cluster_Id=cluster_ext_id) +``` + +## Prechecks + +Run prechecks before committing to an upgrade. + +```python +prechecks_api = lifecycle_client.PrechecksApi(api_client=api_client) + +# Prechecks use the recommendation_id from step 2 +task_response = prechecks_api.perform_prechecks( + body=v4LifecycleResources.PrecheckSpec( + recommendation_ext_id=recommendation_id + ), + X_Cluster_Id=cluster_ext_id +) +``` + +## Automatic Update Management + +```python +auto_mgmt_api = lifecycle_client.SystemAutoMgmtApi(api_client=api_client) + +# Get current auto-management settings +settings = auto_mgmt_api.get_system_auto_mgmt_config(X_Cluster_Id=cluster_ext_id) + +# Update auto-management flag +flag = v4LifecycleCommon.SystemAutoMgmtFlag(is_enabled=True) +auto_mgmt_api.update_system_auto_mgmt_config( + body=flag, + X_Cluster_Id=cluster_ext_id +) +``` + +## Key Models + +| Model | Description | +|-------|-------------| +| Entity | LCM-managed entity (firmware, software component) | +| RecommendationSpec | Input for computing upgrade recommendations | +| NotificationsSpec | Input for computing pre-upgrade notifications | +| UpgradeSpec | Input for performing upgrades | +| PrecheckSpec | Input for running upgrade prechecks | +| SystemAutoMgmtFlag | Toggle for automatic LCM management | +| Bundle | LCM update bundle metadata | + +## Common Mistakes + +1. **Forgetting X_Cluster_Id**: Every LCM API call requires this header. Without it, calls fail. +2. **Not extracting recommendation_id**: After `compute_recommendations`, the recommendation ID is in `task.completion_details[0].value`, not in the direct response. +3. **Insufficient timeout for inventory**: Inventory scans can take 15+ minutes on large clusters. +4. **Skipping prechecks**: Always run prechecks before upgrades in production. +5. **Not checking notifications**: Notifications may contain critical warnings that require manual intervention before upgrade. + +## Cross-Namespace Dependencies + +- **clustermgmt**: Obtain `cluster_ext_id` values via `ClustersApi.list_clusters()` +- **prism**: Poll tasks via `TasksApi.get_task_by_id()` +- **monitoring**: Check alerts after upgrades via `AlertsApi` diff --git a/content/nutanix/docs/nutanix-microseg/python/DOC.md b/content/nutanix/docs/nutanix-microseg/python/DOC.md new file mode 100644 index 00000000..78a8c01a --- /dev/null +++ b/content/nutanix/docs/nutanix-microseg/python/DOC.md @@ -0,0 +1,257 @@ +--- +name: nutanix-microseg +description: "Nutanix Microsegmentation (Flow) Python SDK - network security policies, address groups, service groups, isolation policies, firewall rules" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,microseg,flow,security-policy,firewall,isolation,zero-trust,address-group,service-group" +--- + +# Nutanix Microsegmentation (Flow) Python SDK v4.2.1 + +## Golden Rule + +Package: `ntnx_microseg_py_client` + +```python +import ntnx_microseg_py_client as microseg_client +import ntnx_microseg_py_client.models.microseg.v4.config as v4MicrosegConfig +``` + +## Authentication Setup + +```python +config = microseg_client.Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False + +api_client = microseg_client.ApiClient(configuration=config) +``` + +## API Classes (5 total) + +| API Class | Methods | Purpose | +|-----------|---------|---------| +| NetworkSecurityPoliciesApi | 8 | Create/update/delete/list security policies | +| AddressGroupsApi | 5 | Manage IP address groups for policy rules | +| ServiceGroupsApi | 5 | Manage service (protocol/port) groups | +| DirectoryServerConfigsApi | 10 | Configure directory services for identity-based policies | +| EntityGroupsApi | 5 | Manage entity groups | + +## Core Operations + +### 1. Create Two-Environment Isolation Policy + +Isolation policies prevent communication between two groups of VMs categorized in Prism Central. + +```python +nsp_api = microseg_client.NetworkSecurityPoliciesApi(api_client=api_client) + +# Categories must already exist in Prism Central (use prism CategoriesApi) +# Obtain category ext_ids from the prism namespace +env_a_category_ext_id = "cat-ext-id-for-production" +env_b_category_ext_id = "cat-ext-id-for-development" + +# Build isolation rule spec +isolation_rule = v4MicrosegConfig.TwoEnvIsolationRuleSpec( + first_isolation_group=[env_a_category_ext_id], + second_isolation_group=[env_b_category_ext_id] +) + +# Build the security policy +policy = v4MicrosegConfig.NetworkSecurityPolicy( + name="Isolate-Prod-from-Dev", + description="Prevent all traffic between Production and Development VMs", + type=v4MicrosegConfig.SecurityPolicyType.ISOLATION, + state=v4MicrosegConfig.SecurityPolicyState.MONITOR, + rules=[ + v4MicrosegConfig.NetworkSecurityPolicyRule( + type=v4MicrosegConfig.RuleType.TWO_ENV_ISOLATION, + spec=isolation_rule + ) + ] +) + +# Create the policy - returns a task +task_response = nsp_api.create_network_security_policy(body=policy) +task_ext_id = task_response.data.ext_id + +# Poll task to completion +wait_for_task(prism_client, task_ext_id, timeout=120) +``` + +### 2. List Security Policies + +```python +nsp_api = microseg_client.NetworkSecurityPoliciesApi(api_client=api_client) + +# List all policies +policies = nsp_api.list_network_security_policies() +for policy in policies.data: + print(f"Policy: {policy.name}, Type: {policy.type}, State: {policy.state}") + +# Filter by type +isolation_policies = nsp_api.list_network_security_policies( + _filter="type eq 'ISOLATION'" +) +``` + +### 3. Get and Update a Security Policy + +```python +# Get policy by ID +policy = nsp_api.get_network_security_policy_by_id(extId=policy_ext_id) + +# Extract ETag for optimistic concurrency +etag = policy.headers.get("ETag") + +# Switch from MONITOR to ENFORCE +policy.data.state = v4MicrosegConfig.SecurityPolicyState.ENFORCE + +# Update with If-Match header +nsp_api.update_network_security_policy_by_id( + extId=policy_ext_id, + body=policy.data, + if_match=etag +) +``` + +### 4. Delete a Security Policy + +```python +# Get current ETag first +policy = nsp_api.get_network_security_policy_by_id(extId=policy_ext_id) +etag = policy.headers.get("ETag") + +nsp_api.delete_network_security_policy_by_id( + extId=policy_ext_id, + if_match=etag +) +``` + +### 5. Create Address Group + +Address groups define sets of IP addresses/subnets used in policy rules. + +```python +ag_api = microseg_client.AddressGroupsApi(api_client=api_client) + +address_group = v4MicrosegConfig.AddressGroup( + name="Web-Servers-Subnet", + description="Subnet for web server tier", + ip_ranges=[ + v4MicrosegConfig.IPv4Range( + start_ip="10.0.1.0", + end_ip="10.0.1.255" + ) + ], + ip_subnets=[ + v4MicrosegConfig.IPv4Subnet( + ip="10.0.2.0", + prefix_length=24 + ) + ] +) + +task_response = ag_api.create_address_group(body=address_group) +``` + +### 6. List Address Groups + +```python +ag_api = microseg_client.AddressGroupsApi(api_client=api_client) + +address_groups = ag_api.list_address_groups() +for ag in address_groups.data: + print(f"Address Group: {ag.name}, ID: {ag.ext_id}") +``` + +### 7. Create Service Group + +Service groups define protocol/port combinations for policy rules. + +```python +sg_api = microseg_client.ServiceGroupsApi(api_client=api_client) + +service_group = v4MicrosegConfig.ServiceGroup( + name="Web-Services", + description="HTTP and HTTPS services", + tcp_services=[ + v4MicrosegConfig.TcpPortRangeSpec( + start_port=80, + end_port=80 + ), + v4MicrosegConfig.TcpPortRangeSpec( + start_port=443, + end_port=443 + ) + ], + udp_services=[ + v4MicrosegConfig.UdpPortRangeSpec( + start_port=53, + end_port=53 + ) + ] +) + +task_response = sg_api.create_service_group(body=service_group) +``` + +### 8. List Service Groups + +```python +sg_api = microseg_client.ServiceGroupsApi(api_client=api_client) + +service_groups = sg_api.list_service_groups() +for sg in service_groups.data: + print(f"Service Group: {sg.name}, ID: {sg.ext_id}") +``` + +## Key Types and Enums + +| Type | Values | Description | +|------|--------|-------------| +| SecurityPolicyType | ISOLATION, APPLICATION | Policy classification | +| SecurityPolicyState | MONITOR, ENFORCE | Monitor logs only; Enforce blocks traffic | +| RuleType | TWO_ENV_ISOLATION, APPLICATION | Rule classification within a policy | + +## Key Models + +| Model | Description | +|-------|-------------| +| NetworkSecurityPolicy | Top-level policy object with name, type, state, rules | +| NetworkSecurityPolicyRule | Individual rule within a policy | +| TwoEnvIsolationRuleSpec | Defines two category groups to isolate | +| AddressGroup | Named set of IP ranges/subnets | +| ServiceGroup | Named set of TCP/UDP port ranges | +| IPv4Range | Start/end IP address range | +| IPv4Subnet | IP + prefix length subnet definition | +| TcpPortRangeSpec | TCP port range (start_port, end_port) | +| UdpPortRangeSpec | UDP port range (start_port, end_port) | + +## Policy Lifecycle + +1. **Create in MONITOR mode**: Policy logs traffic matches but does not block. +2. **Review flow logs**: Verify the policy matches expected traffic patterns. +3. **Switch to ENFORCE mode**: Policy actively blocks traffic that violates rules. + +Always start with MONITOR to avoid accidental network disruptions. + +## Cross-Namespace Dependencies + +- **prism (CategoriesApi)**: Isolation groups reference category `ext_id` values from Prism Central. Create categories first, then use their ext_ids in isolation rule specs. +- **vmm**: VMs are assigned categories which determine which policies apply to them. +- **prism (TasksApi)**: Poll async task completion after create/update/delete operations. + +## Common Mistakes + +1. **Using category names instead of ext_ids**: Isolation rules require category `ext_id` values, not names. +2. **Enforcing without monitoring first**: Always start policies in MONITOR state to validate before enforcing. +3. **Missing ETag on updates**: Update and delete operations require the `If-Match` header with the current ETag value. +4. **Forgetting task polling**: Create/update/delete operations return tasks that must be polled to confirm completion. diff --git a/content/nutanix/docs/nutanix-monitoring/python/DOC.md b/content/nutanix/docs/nutanix-monitoring/python/DOC.md new file mode 100644 index 00000000..def39e97 --- /dev/null +++ b/content/nutanix/docs/nutanix-monitoring/python/DOC.md @@ -0,0 +1,274 @@ +--- +name: nutanix-monitoring +description: "Nutanix Monitoring Python SDK - alerts, events, audits, alert policies, alert email configuration, cluster logs, health checks" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,monitoring,alerts,events,audits,health-check,alert-policy" +--- + +# Nutanix Monitoring Python SDK v4.2.1 + +## Golden Rule + +Package: `ntnx_monitoring_py_client` + +```python +import ntnx_monitoring_py_client as mon_client +``` + +## Authentication Setup + +```python +config = mon_client.Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False + +api_client = mon_client.ApiClient(configuration=config) +``` + +## API Classes (9 total, 22 methods) + +| API Class | Methods | Purpose | +|-----------|---------|---------| +| AlertsApi | 2 | Get and list alerts | +| EventsApi | 2 | Get and list events | +| AuditsApi | 2 | Get and list audit records | +| UserDefinedPoliciesApi | 6 | CRUD for user-defined alert policies, find conflicts | +| SystemDefinedPoliciesApi | 5 | Get/list system-defined alert policies, manage per-cluster config | +| ManageAlertsApi | 1 | Acknowledge, resolve, or auto-resolve alerts | +| AlertEmailConfigurationApi | 2 | Get and update alert email notification settings | +| ClusterLogsApi | 2 | Collect cluster logs and list available tags | +| SystemDefinedChecksApi | 1 | Run system-defined health checks on demand | + +## Core Operations + +### 1. List Alerts + +```python +alerts_api = mon_client.AlertsApi(api_client=api_client) + +# List all alerts +alerts = alerts_api.list_alerts() +for alert in alerts.data: + print(f"Alert: {alert.title}, Severity: {alert.severity}, " + f"Resolved: {alert.is_resolved}") + +# Filter by severity +critical_alerts = alerts_api.list_alerts( + _filter="severity eq 'CRITICAL'" +) + +# Filter unresolved alerts +unresolved = alerts_api.list_alerts( + _filter="isResolved eq false" +) + +# Order by creation time, most recent first +recent = alerts_api.list_alerts( + _orderby="creationTime desc", + _limit=10 +) +``` + +### 2. Get Alert Details + +```python +alert = alerts_api.get_alert_by_id(extId=alert_ext_id) +print(f"Title: {alert.data.title}") +print(f"Severity: {alert.data.severity}") +print(f"Source: {alert.data.source_entity}") +print(f"Created: {alert.data.creation_time}") +print(f"Message: {alert.data.alert_message}") +``` + +### 3. Manage Alerts (Acknowledge/Resolve) + +```python +manage_api = mon_client.ManageAlertsApi(api_client=api_client) + +# Acknowledge or resolve an alert +manage_spec = mon_client.AlertActionSpec( + alert_ext_ids=[alert_ext_id], + action=mon_client.AlertAction.ACKNOWLEDGE +) + +manage_api.manage_alert(body=manage_spec) +``` + +### 4. List Events + +```python +events_api = mon_client.EventsApi(api_client=api_client) + +events = events_api.list_events() +for event in events.data: + print(f"Event: {event.title}, Type: {event.type}, " + f"Created: {event.creation_time}") + +# Filter by type +filtered = events_api.list_events( + _filter="type eq 'VM_POWER_ON'" +) +``` + +### 5. Get Event Details + +```python +event = events_api.get_event_by_id(extId=event_ext_id) +print(f"Title: {event.data.title}") +print(f"Parameters: {event.data.parameters}") +``` + +### 6. List Audits + +```python +audits_api = mon_client.AuditsApi(api_client=api_client) + +audits = audits_api.list_audits() +for audit in audits.data: + print(f"Audit: {audit.operation_type}, User: {audit.user_name}, " + f"Time: {audit.creation_time}") + +# Filter by user +user_audits = audits_api.list_audits( + _filter="userName eq 'admin'" +) +``` + +### 7. Get Audit Details + +```python +audit = audits_api.get_audit_by_id(extId=audit_ext_id) +print(f"Operation: {audit.data.operation_type}") +print(f"User: {audit.data.user_name}") +print(f"Entity: {audit.data.affected_entity}") +``` + +## Alert Email Configuration + +```python +email_api = mon_client.AlertEmailConfigurationApi(api_client=api_client) + +# Get current configuration +email_config = email_api.get_alert_email_configuration() + +# Update email configuration +updated_config = mon_client.AlertEmailConfiguration( + is_enabled=True, + email_contacts=["admin@example.com", "ops@example.com"], + default_nutanix_email="noreply@nutanix.example.com" +) + +email_api.update_alert_email_configuration(body=updated_config) +``` + +## User-Defined Alert Policies + +Create custom alert policies based on metrics thresholds. + +```python +uda_api = mon_client.UserDefinedPoliciesApi(api_client=api_client) + +# Create a custom alert policy +policy = mon_client.UserDefinedPolicy( + name="high-cpu-alert", + description="Alert when VM CPU exceeds 90% for 30 minutes", + is_enabled=True +) + +task_response = uda_api.create_uda_policy(body=policy) + +# List all user-defined policies +policies = uda_api.list_uda_policies() +for p in policies.data: + print(f"Policy: {p.name}, Enabled: {p.is_enabled}") + +# Find conflicting policies +conflicts = uda_api.find_conflicting_uda_policies(body=policy) + +# Update a policy +uda_api.update_uda_policy_by_id(extId=policy_ext_id, body=updated_policy) + +# Delete a policy +uda_api.delete_uda_policy_by_id(extId=policy_ext_id) +``` + +## System-Defined Alert Policies + +```python +sda_api = mon_client.SystemDefinedPoliciesApi(api_client=api_client) + +# List all system-defined policies +sda_policies = sda_api.list_sda_policies() +for p in sda_policies.data: + print(f"Policy: {p.ext_id}") + +# Get per-cluster config for a system-defined policy +cluster_configs = sda_api.list_cluster_configs_by_sda_id( + sdaPolicyExtId=sda_policy_ext_id +) + +# Update cluster-specific config (enable/disable per cluster) +sda_api.update_cluster_config_by_id( + sdaPolicyExtId=sda_policy_ext_id, + extId=cluster_config_ext_id, + body=updated_cluster_config +) +``` + +## Cluster Log Collection + +```python +logs_api = mon_client.ClusterLogsApi(api_client=api_client) + +# List available log tags +tags = logs_api.list_tags() + +# Collect cluster logs (returns a task) +log_spec = mon_client.LogCollectionSpec( + cluster_ext_ids=["cluster-ext-id"] +) + +task_response = logs_api.collect_logs(body=log_spec) +wait_for_task(prism_client, task_response.data.ext_id, timeout=600) +``` + +## System-Defined Health Checks + +```python +checks_api = mon_client.SystemDefinedChecksApi(api_client=api_client) + +# Run health checks on demand +task_response = checks_api.run_system_defined_checks() +``` + +## Key Models + +| Model | Props | Description | +|-------|-------|-------------| +| Alert | 31 | Alert with title, severity, source entity, resolution status, message | +| UserDefinedPolicy | 17 | Custom alert policy with thresholds and conditions | +| SystemDefinedPolicy | 17 | Built-in alert policy (read-only definition, per-cluster config) | +| Event | 16 | System event with type, title, parameters | +| Audit | 16 | Audit record: operation, user, entity, timestamp | +| AlertEmailConfiguration | 14 | Email notification settings for alerts | + +## Common Mistakes + +1. **Not filtering alerts**: Without filters, `list_alerts` returns all alerts including resolved ones. Use `_filter="isResolved eq false"` for active alerts. +2. **Confusing events and alerts**: Events are informational records of what happened. Alerts are actionable notifications requiring attention. +3. **Not using pagination**: Large clusters generate many alerts/events. Use `_page` and `_limit` for efficient retrieval. +4. **Ignoring audit trail**: Audits are essential for compliance. Filter by time range and user for security investigations. + +## Cross-Namespace Dependencies + +- **clustermgmt**: Cluster ext_ids for log collection and per-cluster alert policy configuration. +- **prism (TasksApi)**: Poll async tasks for log collection and health checks. +- **vmm**: VM ext_ids referenced in alert source entities. diff --git a/content/nutanix/docs/nutanix-networking/python/DOC.md b/content/nutanix/docs/nutanix-networking/python/DOC.md new file mode 100644 index 00000000..f608ee5a --- /dev/null +++ b/content/nutanix/docs/nutanix-networking/python/DOC.md @@ -0,0 +1,348 @@ +--- +name: nutanix-networking +description: "Nutanix Networking Python SDK - subnets, VPCs, floating IPs, gateways, virtual switches, VPN, BGP, routing, IPFIX, load balancers, traffic mirrors" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,networking,subnet,vlan,vpc,floating-ip,gateway,vpn,bgp,virtual-switch,load-balancer" +--- + +# Nutanix Networking Python SDK (v4.2.1) + +35 API classes, 118 methods, 375 models. Largest namespace by API class count. + +## Golden Rule + +- **Package**: `ntnx_networking_py_client` +- **Install**: `pip install ntnx-networking-py-client==4.2.1` + +## Initialization + +```python +import ntnx_networking_py_client +from ntnx_networking_py_client import Configuration, ApiClient +from ntnx_networking_py_client.rest import ApiException +import ntnx_networking_py_client.models.networking.v4.config as v4NetConfig +import ntnx_networking_py_client.models.common.v1.config as v1CommonConfig + +config = Configuration() +config.host = "10.0.0.1" # Prism Central IP (no https://, no port) +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False +config.max_retry_attempts = 3 +config.backoff_factor = 2 + +client = ApiClient(configuration=config) +subnets_api = ntnx_networking_py_client.SubnetsApi(api_client=client) +vpcs_api = ntnx_networking_py_client.VpcsApi(api_client=client) +fips_api = ntnx_networking_py_client.FloatingIpsApi(api_client=client) +vswitches_api = ntnx_networking_py_client.VirtualSwitchesApi(api_client=client) +gateways_api = ntnx_networking_py_client.GatewaysApi(api_client=client) +``` + +## Model Imports + +```python +# Networking config models (Subnet, Vpc, FloatingIp, etc.) +import ntnx_networking_py_client.models.networking.v4.config as v4NetConfig + +# Common models (IPv4Address, IPv6Address, etc.) +import ntnx_networking_py_client.models.common.v1.config as v1CommonConfig +``` + +## Core Operations + +### 1. Create Managed VLAN Subnet with DHCP + +```python +# Step 1: Get cluster ext_id from clustermgmt namespace first +cluster_ext_id = "abcd-1234-..." # from ClustersApi.list_clusters() + +# Step 2: Build IP configuration +ip_pool = v4NetConfig.IPv4Pool( + start_ip=v1CommonConfig.IPv4Address( + value="10.0.1.100", + prefix_length=32 # REQUIRED - always 32 for a single address + ), + end_ip=v1CommonConfig.IPv4Address( + value="10.0.1.200", + prefix_length=32 # REQUIRED - always 32 for a single address + ) +) + +dhcp_config = v4NetConfig.DhcpServerReference( + address=v1CommonConfig.IPv4Address( + value="10.0.1.1", + prefix_length=32 + ), + dns_servers=[ + v1CommonConfig.IPv4Address(value="8.8.8.8", prefix_length=32), + v1CommonConfig.IPv4Address(value="8.8.4.4", prefix_length=32) + ], + domain_name="example.com", + boot_file_name=None +) + +ipv4_config = v4NetConfig.IPv4Config( + ip_pools=[ip_pool], + default_gateway_ip=v1CommonConfig.IPv4Address( + value="10.0.1.1", + prefix_length=32 + ), + dhcp_server_address=v1CommonConfig.IPv4Address( + value="10.0.1.1", + prefix_length=32 + ), + subnet_ip=v1CommonConfig.IPv4Address( + value="10.0.1.0", + prefix_length=24 # NOTE: 24 for the subnet CIDR, not 32 + ) +) + +ip_config = v4NetConfig.IPConfig(ipv4=ipv4_config) + +# Step 3: Build and create the subnet +subnet = v4NetConfig.Subnet( + name="my-vlan-subnet", + description="Managed VLAN subnet with DHCP", + subnet_type=v4NetConfig.SubnetType.VLAN, + network_id=100, # VLAN ID + cluster_reference=cluster_ext_id, # From clustermgmt + ip_config=[ip_config], + is_advanced_networking=False +) + +response = subnets_api.create_subnet(body=subnet) +task_ext_id = response.data.ext_id # Returns a task +print(f"Subnet creation task: {task_ext_id}") +``` + +### 2. List Subnets with Filter + +```python +# List all VLAN subnets +response = subnets_api.list_subnets( + _filter="subnetType eq Networking.Config.SubnetType'VLAN'", + _orderby="name asc", + _limit=50 +) +subnets = response.data +for subnet in subnets: + print(f"{subnet.name} -> VLAN {subnet.network_id}, ext_id={subnet.ext_id}") + +# List subnets by name +response = subnets_api.list_subnets( + _filter="name eq 'my-vlan-subnet'" +) + +# List OVERLAY subnets (VPC-attached) +response = subnets_api.list_subnets( + _filter="subnetType eq Networking.Config.SubnetType'OVERLAY'" +) +``` + +### 3. Create VPC + +```python +vpc = v4NetConfig.Vpc( + name="my-vpc", + description="Production VPC", + external_subnets=[ + v4NetConfig.ExternalSubnet( + subnet_reference=external_subnet_ext_id, + active_gateway_node=None + ) + ], + externally_routable_prefixes=[ + v4NetConfig.IPSubnet( + ip=v1CommonConfig.IPv4Address(value="10.100.0.0", prefix_length=16), + prefix_length=16 + ) + ] +) + +response = vpcs_api.create_vpc(body=vpc) +task_ext_id = response.data.ext_id +``` + +### 4. List Floating IPs + +```python +response = fips_api.list_floating_ips( + _limit=100, + _orderby="floating_ip asc" +) +for fip in response.data: + print(f"FIP: {fip.floating_ip} -> VM NIC: {fip.vm_nic_reference}") +``` + +### 5. Create Virtual Switch + +```python +vswitch = v4NetConfig.VirtualSwitch( + name="vs0", + description="Default virtual switch", + bond_mode=v4NetConfig.BondModeType.ACTIVE_BACKUP, + mtu=9000, + cluster_references=[cluster_ext_id] +) + +response = vswitches_api.create_virtual_switch(body=vswitch) +task_ext_id = response.data.ext_id +``` + +## Key Patterns + +### Subnet Creation Requires cluster_reference + +Every VLAN subnet must have a `cluster_reference` set to a valid cluster `ext_id` obtained from the clustermgmt namespace. Overlay subnets reference a VPC instead. + +### IPv4Address Always Needs prefix_length AND value + +Every `IPv4Address` object requires both `value` and `prefix_length`. For individual host addresses (gateways, DNS servers, pool endpoints), use `prefix_length=32`. For subnet addresses, use the actual CIDR prefix (e.g., 24). + +```python +# Correct - individual address +v1CommonConfig.IPv4Address(value="10.0.1.1", prefix_length=32) + +# Correct - subnet address +v1CommonConfig.IPv4Address(value="10.0.1.0", prefix_length=24) + +# WRONG - missing prefix_length (will fail) +v1CommonConfig.IPv4Address(value="10.0.1.1") +``` + +### SubnetType Enum + +```python +v4NetConfig.SubnetType.VLAN # Traditional VLAN-backed subnet +v4NetConfig.SubnetType.OVERLAY # VPC overlay subnet +``` + +### OData Filtering + +```python +# Filter by subnet type +_filter="subnetType eq Networking.Config.SubnetType'VLAN'" + +# Filter by name +_filter="name eq 'my-subnet'" + +# Filter by VLAN ID +_filter="networkId eq 100" +``` + +### ETag for Updates + +```python +get_response = subnets_api.get_subnet_by_id(extId=subnet_ext_id) +etag = client.get_etag(get_response) + +subnets_api.update_subnet_by_id( + extId=subnet_ext_id, + body=updated_subnet, + If_Match=etag +) +``` + +## API Classes Reference + +### Primary APIs (most commonly used) + +| API Class | Methods | Description | +|-----------|---------|-------------| +| SubnetsApi | 6 | VLAN and overlay subnet CRUD, list | +| VpcsApi | 5 | VPC CRUD | +| FloatingIpsApi | 5 | Floating IP CRUD, assignment | +| GatewaysApi | 6 | Gateway CRUD (local, VPN) | +| VirtualSwitchesApi | 5 | Virtual switch CRUD | +| VpnConnectionsApi | 7 | VPN connection CRUD, stats | +| BgpSessionsApi | 5 | BGP session CRUD | +| RoutingPoliciesApi | 5 | Routing policy CRUD | +| RoutesApi | 5 | Static route CRUD | +| NetworkControllersApi | 5 | Network controller CRUD | + +### Advanced APIs + +| API Class | Methods | Description | +|-----------|---------|-------------| +| IPFIXExportersApi | 5 | IPFIX flow export configuration | +| Layer2StretchesApi | 5 | L2 stretch for cross-site networking | +| LoadBalancerSessionsApi | 5 | Load balancer session CRUD | +| TrafficMirrorsApi | 5 | Traffic mirror CRUD | +| NetworkFunctionsApi | 5 | Network function chain management | +| NicProfilesApi | 7 | NIC profile CRUD | +| RemoteEntitiesApi | 6 | Remote subnet/VPN entity management | +| SubnetIPReservationApi | 3 | Reserve/unreserve IPs in a subnet | +| SubnetMigrationsApi | 2 | Migrate subnets between clusters | + +### Auxiliary / Stats APIs + +| API Class | Methods | Description | +|-----------|---------|-------------| +| RouteTablesApi | 2 | Route table operations | +| BgpRoutesApi | 2 | BGP route inspection | +| MacAddressesApi | 2 | MAC address operations | +| UplinkBondsApi | 2 | Uplink bond configuration | +| VpcVirtualSwitchMappingsApi | 2 | VPC-to-vswitch mappings | +| AwsSubnetsApi | 1 | NC2 on AWS subnet listing | +| AwsVpcsApi | 1 | NC2 on AWS VPC listing | +| BridgesApi | 1 | Bridge listing | +| ClusterCapabilitiesApi | 1 | Cluster networking capabilities | +| Layer2StretchStatsApi | 1 | L2 stretch statistics | +| LoadBalancerSessionStatsApi | 1 | Load balancer statistics | +| RoutingPolicyStatsApi | 1 | Routing policy statistics | +| TrafficMirrorStatsApi | 1 | Traffic mirror statistics | +| VirtualSwitchNodesInfoApi | 1 | Per-node vswitch info | +| VpcNsStatsApi | 1 | VPC network statistics | +| VpnConnectionStatsApi | 1 | VPN connection statistics | + +## Key Models + +| Model | Notable Properties | +|-------|-------------------| +| Subnet | name, ext_id, subnet_type, network_id, cluster_reference, ip_config, vpc_reference, is_advanced_networking, bridge_name | +| FloatingIp | ext_id, floating_ip, vm_nic_reference, vpc_reference, private_ip | +| Vpc | name, ext_id, external_subnets, externally_routable_prefixes, common_domain_name_server_ip_list | +| VpnConnection | name, ext_id, local_gateway_reference, remote_gateway_reference, ipsec_config, dynamic_route_priority | +| Gateway | name, ext_id, gateway_type, vlan_subnet_reference, vpc_reference | +| VirtualSwitch | name, ext_id, bond_mode, mtu, cluster_references | +| IPConfig | ipv4 (IPv4Config), ipv6 (IPv6Config) | +| IPv4Config | ip_pools, default_gateway_ip, dhcp_server_address, subnet_ip | + +## Common Mistakes + +1. **Forgetting prefix_length=32 on IPv4Address objects** - Every `IPv4Address` must have `prefix_length` set. For individual addresses (gateways, pool endpoints, DNS), always use 32. Omitting it causes silent failures or API errors. + +2. **Not setting cluster_reference on VLAN subnets** - VLAN subnets must specify which cluster they belong to via `cluster_reference`. Get the cluster ext_id from `ntnx_clustermgmt_py_client.ClustersApi.list_clusters()` first. + +3. **Confusing SubnetType.VLAN vs SubnetType.OVERLAY** - VLAN subnets are tied to a physical cluster and VLAN ID. OVERLAY subnets exist inside a VPC and use Geneve encapsulation. They have different required fields: + - VLAN: requires `cluster_reference` and `network_id` (VLAN ID) + - OVERLAY: requires `vpc_reference`, no `network_id` + +4. **Using wrong filter enum format** - Subnet type filters must use the fully qualified enum: + ``` + # Correct + _filter="subnetType eq Networking.Config.SubnetType'VLAN'" + + # Wrong + _filter="subnetType eq 'VLAN'" + ``` + +5. **Ignoring task responses** - Create/update/delete operations return a task ext_id, not the resource directly. Poll the task via `ntnx_prism_py_client.TasksApi` to get the final resource ext_id. + +6. **Missing X_Cluster_Id for AWS APIs** - The `AwsSubnetsApi` and `AwsVpcsApi` require the `X_Cluster_Id` header parameter for NC2-on-AWS operations. + +## Cross-Namespace Dependencies + +| Dependency | Source Namespace | What You Need | +|-----------|-----------------|---------------| +| Cluster ext_id for subnet creation | clustermgmt | `ClustersApi.list_clusters()` -> `cluster.ext_id` | +| Task polling for async operations | prism | `TasksApi.get_task_by_id(task_ext_id)` | +| VM NIC for floating IP assignment | vmm | `VmApi.list_vms()` -> VM NIC ext_id | +| Category assignment | prism | Category ext_ids for tagging subnets/VPCs | diff --git a/content/nutanix/docs/nutanix-networking/python/references/api-classes.md b/content/nutanix/docs/nutanix-networking/python/references/api-classes.md new file mode 100644 index 00000000..50adf9ec --- /dev/null +++ b/content/nutanix/docs/nutanix-networking/python/references/api-classes.md @@ -0,0 +1,330 @@ +# Networking API Classes - Complete Reference + +**Package:** `ntnx_networking_py_client` v4.2.1 +**Namespace:** `networking.v4.config` / `networking.v4.stats` +**Total API Classes:** 35 +**Total Methods:** 117 + +--- + +## 1. AwsSubnetsApi (1 method) + +- `list_aws_subnets(X_Cluster_Id, _page, _limit, _filter, _orderby, _select)` - Get NC2A subnets + +## 2. AwsVpcsApi (1 method) + +- `list_aws_vpcs(X_Cluster_Id)` - Get NC2A VPCs + +## 3. BgpRoutesApi (2 methods) + +- `get_route_for_bgp_session_by_id(extId, bgpSessionExtId)` - Get the specified route of the specified BGP session +- `list_routes_by_bgp_session_id(bgpSessionExtId, _page, _limit, _filter, _orderby)` - Lists routes of the specified BGP session + +## 4. BgpSessionsApi (5 methods) + +- `create_bgp_session(body)` - Create BGP session +- `get_bgp_session_by_id(extId)` - Get BGP session request +- `update_bgp_session_by_id(extId, body)` - Update BGP session request +- `delete_bgp_session_by_id(extId)` - Delete BGP session request +- `list_bgp_sessions(_page, _limit, _filter, _orderby, _expand)` - List BGP sessions request + +## 5. BridgesApi (1 method) + +- `migrate_bridge(body, X_Cluster_Id)` - Create a Virtual Switch from an existing bridge + +## 6. ClusterCapabilitiesApi (1 method) + +- `list_cluster_capabilities(_page, _limit, _filter, _orderby)` - Get cluster capabilities + +## 7. FloatingIpsApi (5 methods) + +- `create_floating_ip(body)` - Create a floating IP +- `get_floating_ip_by_id(extId)` - Get the floating IP for this extId +- `update_floating_ip_by_id(extId, body)` - Update the floating IP for this extId +- `delete_floating_ip_by_id(extId)` - Delete the floating IP corresponding to the extId +- `list_floating_ips(_page, _limit, _filter, _orderby, _expand)` - Get a list of floating IPs + +## 8. GatewaysApi (6 methods) + +- `create_gateway(body)` - Create network gateway +- `get_gateway_by_id(extId)` - Get network gateway +- `update_gateway_by_id(extId, body)` - Update network gateway +- `delete_gateway_by_id(extId)` - Delete network gateway +- `list_gateways(_page, _limit, _filter, _orderby, _expand, _select)` - List network gateways +- `upgrade_gateway_by_id(extId)` - Upgrade network gateway + +## 9. IPFIXExportersApi (5 methods) + +- `create_ipfix_exporter(body)` - Create an IPFIX Exporter +- `get_ipfix_exporter_by_id(extId)` - Get the specified IPFIX Exporter +- `update_ipfix_exporter_by_id(extId, body)` - Update the specified IPFIX Exporter +- `delete_ipfix_exporter_by_id(extId)` - Delete the specified IPFIX Exporter +- `list_ipfix_exporters(_page, _limit, _filter, _orderby)` - Get the list of existing IPFIX Exporters + +## 10. Layer2StretchStatsApi (1 method) + +- `get_layer2_stretch_stats(extId, _startTime, _endTime, _samplingInterval, _statType, _page, _limit, _select)` - Get Layer2Stretch statistics + +## 11. Layer2StretchesApi (5 methods) + +- `create_layer2_stretch(body)` - Create a Layer2Stretch configuration +- `get_layer2_stretch_by_id(extId)` - Get the Layer2Stretch configuration for the specified reference +- `update_layer2_stretch_by_id(extId, body)` - Update the specified Layer2Stretch configuration +- `delete_layer2_stretch_by_id(extId)` - Delete the specified Layer2Stretch configuration +- `list_layer2_stretches(_page, _limit, _filter, _orderby)` - Get the list of existing Layer2Stretch configurations + +## 12. LoadBalancerSessionStatsApi (1 method) + +- `get_load_balancer_session_stats(extId, _startTime, _endTime, _samplingInterval, _statType, _select)` - Get load balancer session listener and target statistics + +## 13. LoadBalancerSessionsApi (5 methods) + +- `create_load_balancer_session(body)` - Create a load balancer session +- `get_load_balancer_session_by_id(extId, _select)` - Get the load balancer session with the specified UUID +- `update_load_balancer_session_by_id(extId, body)` - Update the specified load balancer session +- `delete_load_balancer_session_by_id(extId)` - Delete the specified load balancer session +- `list_load_balancer_sessions(_page, _limit, _filter, _orderby, _select)` - Get the list of existing load balancer sessions + +## 14. MacAddressesApi (2 methods) + +- `get_learned_mac_address_for_layer2_stretch_by_id(layer2StretchExtId, extId)` - Get a specified learned MAC address of the specified Layer2Stretch +- `list_learned_mac_addresses_by_layer2_stretch_id(layer2StretchExtId, _page, _limit, _filter, _orderby)` - Get learned MAC addresses of the specified Layer2Stretch + +## 15. NetworkControllersApi (5 methods) + +- `create_network_controller(body)` - Create a network controller +- `get_network_controller_by_id(extId)` - Get the network controller with the specified UUID +- `update_network_controller_by_id(extId, body)` - Update a network controller summary +- `delete_network_controller_by_id(extId)` - Delete a network controller +- `list_network_controllers(_page, _limit)` - Gets the list of existing network controllers + +## 16. NetworkFunctionsApi (5 methods) + +- `create_network_function(body)` - Create a network function +- `get_network_function_by_id(extId)` - Get the network function with the specified UUID +- `update_network_function_by_id(extId, body)` - Update the specified network function +- `delete_network_function_by_id(extId)` - Delete the specified network function +- `list_network_functions(_page, _limit, _filter, _orderby)` - Get the list of existing network functions + +## 17. NicProfilesApi (7 methods) + +- `create_nic_profile(body)` - Create a NIC Profile +- `get_nic_profile_by_id(extId)` - Get a NIC Profile +- `update_nic_profile_by_id(extId, body)` - Update a NIC Profile +- `delete_nic_profile_by_id(extId)` - Delete a NIC Profile +- `list_nic_profiles(_page, _limit, _filter, _orderby, _select)` - Lists all NIC Profiles +- `associate_host_nic_to_nic_profile(extId, body)` - Associate a NIC Profile +- `disassociate_host_nic_from_nic_profile(extId, body)` - Disassociate a Host NIC from a NIC Profile + +## 18. RemoteEntitiesApi (6 methods) + +- `get_remote_subnet_for_cluster_by_id(clusterExtId, extId)` - Get remote subnet +- `list_remote_subnets_by_cluster_id(clusterExtId, _page, _limit, _filter, _orderby)` - List remote subnets +- `get_remote_vpn_connection_for_cluster_by_id(clusterExtId, extId)` - Get a remote VPN connection +- `list_remote_vpn_connections_by_cluster_id(clusterExtId, _page, _limit, _filter, _orderby)` - List remote VPN connections +- `get_remote_vtep_gateway_for_cluster_by_id(clusterExtId, extId)` - Get a remote VTEP gateway +- `list_remote_vtep_gateways_by_cluster_id(clusterExtId, _page, _limit, _filter, _orderby)` - List remote VTEP gateways + +## 19. RouteTablesApi (2 methods) + +- `get_route_table_by_id(extId)` - Get route table +- `list_route_tables(_page, _limit, _filter, _orderby)` - List route tables + +## 20. RoutesApi (5 methods) + +- `create_route_for_route_table(routeTableExtId, body)` - Create a route for the specified route table +- `get_route_for_route_table_by_id(extId, routeTableExtId)` - Get the specified route of the specified route table +- `update_route_for_route_table_by_id(extId, routeTableExtId, body)` - Update the specified route of the specified route table +- `delete_route_for_route_table_by_id(extId, routeTableExtId)` - Delete the specified route of the specified route table +- `list_routes_by_route_table_id(routeTableExtId, _page, _limit, _filter, _orderby)` - Lists routes of the specified route table + +## 21. RoutingPoliciesApi (5 methods) + +- `create_routing_policy(body)` - Create a Routing Policy +- `get_routing_policy_by_id(extId)` - Get a single Routing Policy corresponding to the extId +- `update_routing_policy_by_id(extId, body)` - Update the Routing Policy corresponding to the extId +- `delete_routing_policy_by_id(extId)` - Delete the Routing Policy corresponding to the extId +- `list_routing_policies(_page, _limit, _filter, _orderby, _expand, _select)` - Get a list of Routing Policies + +## 22. RoutingPolicyStatsApi (1 method) + +- `clear_routing_policy_counters(body)` - Clear the value in packet and byte counters of all Routing Policies in the chosen VPC or particular routing policy + +## 23. SubnetIPReservationApi (3 methods) + +- `reserve_ips_by_subnet_id(extId, body)` - Reserve IP addresses on a subnet +- `unreserve_ips_by_subnet_id(extId, body)` - Unreserve IP addresses on a subnet +- `list_reserved_ips_by_subnet_id(subnetExtId, _page, _limit, _filter, _orderby, _select)` - List reserved IPs of a managed subnet + +## 24. SubnetMigrationsApi (2 methods) + +- `migrate_subnets(body)` - Migrate VLAN subnets from VLAN basic to VLAN advanced +- `migrate_vnic_by_id(extId, body)` - Migrate vNICs from Acropolis to Atlas or vice-versa + +## 25. SubnetsApi (6 methods) + +- `create_subnet(body)` - Create a subnet +- `get_subnet_by_id(extId)` - Get the subnet with the specified UUID +- `update_subnet_by_id(extId, body)` - Update the specified subnet +- `delete_subnet_by_id(extId)` - Delete the specified subnet +- `list_subnets(_page, _limit, _filter, _orderby, _expand, _select)` - Get the list of existing subnets +- `list_vnics_by_subnet_id(subnetExtId, _page, _limit, _filter, _orderby, _select)` - List virtual NICs on a subnet + +## 26. TrafficMirrorStatsApi (1 method) + +- `get_traffic_mirror_stats(extId, _startTime, _endTime, _samplingInterval, _statType, _select)` - Get Traffic mirror session statistics + +## 27. TrafficMirrorsApi (5 methods) + +- `create_traffic_mirror(body)` - Create Traffic mirror session +- `get_traffic_mirror_by_id(extId)` - Get Traffic mirror session +- `update_traffic_mirror_by_id(extId, body)` - Update a Traffic mirror session for the provided UUID +- `delete_traffic_mirror_by_id(extId)` - Delete Traffic mirror session +- `list_traffic_mirrors(_page, _limit, _filter, _orderby)` - List Traffic mirror sessions + +## 28. UplinkBondsApi (2 methods) + +- `get_uplink_bond_by_id(extId)` - Get uplink bond +- `list_uplink_bonds(_page, _limit, _filter, _orderby)` - List uplink bonds + +## 29. VirtualSwitchNodesInfoApi (1 method) + +- `list_node_schedulable_status(X_Cluster_Id, _page, _limit, _filter, _orderby)` - Check whether a node in a cluster is a storage-only node or not + +## 30. VirtualSwitchesApi (5 methods) + +- `create_virtual_switch(body, X_Cluster_Id)` - Create a Virtual Switch +- `get_virtual_switch_by_id(extId, X_Cluster_Id)` - Get single Virtual Switch given its UUID +- `update_virtual_switch_by_id(extId, body, X_Cluster_Id)` - Update a Virtual Switch +- `delete_virtual_switch_by_id(extId, X_Cluster_Id)` - Delete a Virtual Switch +- `list_virtual_switches(X_Cluster_Id, _page, _limit, _filter, _orderby)` - Get list of Virtual Switches + +## 31. VpcNsStatsApi (1 method) + +- `get_vpc_ns_stats(vpcExtId, extId, _startTime, _endTime, _samplingInterval, _statType, _page, _limit, _select)` - Get VPC North-South statistics + +## 32. VpcVirtualSwitchMappingsApi (2 methods) + +- `create_vpc_virtual_switch_mapping(body)` - Set VPC for virtual switch mappings traffic config +- `list_vpc_virtual_switch_mappings(_page, _limit, _filter, _orderby)` - Get the VPC for virtual switch mappings config + +## 33. VpcsApi (5 methods) + +- `create_vpc(body)` - Create a VPC +- `get_vpc_by_id(extId)` - Get the VPC with the specified UUID +- `update_vpc_by_id(extId, body)` - Update the specified VPC +- `delete_vpc_by_id(extId)` - Delete the specified VPC +- `list_vpcs(_page, _limit, _filter, _orderby, _select)` - Get the list of existing VPCs + +## 34. VpnConnectionStatsApi (1 method) + +- `get_vpn_connection_stats(extId, _startTime, _endTime, _samplingInterval, _statType, _page, _limit, _select)` - Get VPN connection statistics + +## 35. VpnConnectionsApi (7 methods) + +- `create_vpn_connection(body)` - Create a VPN connection +- `get_vpn_connection_by_id(extId)` - Get VPN connection +- `update_vpn_connection_by_id(extId, body)` - Update VPN connection +- `delete_vpn_connection_by_id(extId)` - Delete VPN connection +- `list_vpn_connections(_page, _limit, _filter, _orderby)` - List the VPN connections +- `get_vpn_appliance_for_vpn_connection_by_id(vpnConnectionExtId, extId)` - Get third-party VPN appliance configuration +- `list_vpn_appliances_by_vpn_connection_id(vpnConnectionExtId, _page, _limit, _filter, _orderby)` - List of third-party VPN appliances for which configurations are available to download + +--- + +## Common Parameters + +### Standard Query Parameters (most list methods) + +| Parameter | Type | Description | +|-----------|------|-------------| +| `_page` | `int` | Page number (0-based) for pagination | +| `_limit` | `int` | Number of results per page | +| `_filter` | `str` | OData filter expression | +| `_orderby` | `str` | OData sort expression | +| `_select` | `str` | OData sparse fieldsets | +| `_expand` | `str` | OData expand related entities | + +### Identity Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `extId` | `str` | Entity external ID (UUID) | +| `body` | model | Request body (varies by method) | +| `X_Cluster_Id` | `str` | Target cluster UUID (required for cluster-scoped APIs) | + +### Statistics Parameters (stats APIs) + +| Parameter | Type | Description | +|-----------|------|-------------| +| `_startTime` | `datetime` | Start time of the statistics period | +| `_endTime` | `datetime` | End time of the statistics period | +| `_samplingInterval` | `int` | Sampling interval in seconds | +| `_statType` | `str` | Type of statistic to retrieve | + +### Common kwargs (all methods) + +| kwarg | Description | +|-------|-------------| +| `async_req` | Execute asynchronously, returns `ApplyResult` | +| `_return_http_data_only` | Return only response data (default: `True`) | +| `_preload_content` | Deserialize response (default: `True`) | +| `_request_timeout` | Timeout in seconds | + +--- + +## API Classes by Category + +### Core Networking +- **SubnetsApi** (6) - Subnet CRUD and vNIC listing +- **VpcsApi** (5) - Virtual Private Cloud management +- **FloatingIpsApi** (5) - Floating IP address management +- **VirtualSwitchesApi** (5) - Virtual switch management +- **RouteTablesApi** (2) - Route table management +- **RoutesApi** (5) - Route CRUD within route tables +- **RoutingPoliciesApi** (5) - Routing policy management + +### Connectivity +- **VpnConnectionsApi** (7) - VPN connection management and appliance config +- **BgpSessionsApi** (5) - BGP session management +- **BgpRoutesApi** (2) - BGP route inspection +- **GatewaysApi** (6) - Network gateway management and upgrade +- **Layer2StretchesApi** (5) - Layer 2 stretch configuration + +### Load Balancing +- **LoadBalancerSessionsApi** (5) - Load balancer session management +- **LoadBalancerSessionStatsApi** (1) - Load balancer statistics + +### Network Functions & Security +- **NetworkControllersApi** (5) - Network controller management +- **NetworkFunctionsApi** (5) - Network function chain management +- **TrafficMirrorsApi** (5) - Traffic mirror session management +- **IPFIXExportersApi** (5) - IPFIX flow exporter management + +### Infrastructure +- **NicProfilesApi** (7) - Physical NIC profile management +- **UplinkBondsApi** (2) - Uplink bond inspection +- **BridgesApi** (1) - Bridge to virtual switch migration +- **SubnetMigrationsApi** (2) - Subnet and vNIC migration +- **SubnetIPReservationApi** (3) - IP address reservation management +- **VpcVirtualSwitchMappingsApi** (2) - VPC to virtual switch mapping + +### Cloud (NC2) +- **AwsSubnetsApi** (1) - NC2 on AWS subnet listing +- **AwsVpcsApi** (1) - NC2 on AWS VPC listing + +### Remote / Multi-Site +- **RemoteEntitiesApi** (6) - Remote subnets, VPN connections, VTEP gateways + +### Monitoring & Stats +- **Layer2StretchStatsApi** (1) - Layer 2 stretch statistics +- **TrafficMirrorStatsApi** (1) - Traffic mirror statistics +- **VpcNsStatsApi** (1) - VPC North-South traffic statistics +- **VpnConnectionStatsApi** (1) - VPN connection statistics +- **LoadBalancerSessionStatsApi** (1) - Load balancer statistics +- **RoutingPolicyStatsApi** (1) - Routing policy counter clearing +- **MacAddressesApi** (2) - Learned MAC address inspection + +### Node Info +- **VirtualSwitchNodesInfoApi** (1) - Node schedulable status +- **ClusterCapabilitiesApi** (1) - Cluster networking capabilities diff --git a/content/nutanix/docs/nutanix-prism/python/DOC.md b/content/nutanix/docs/nutanix-prism/python/DOC.md new file mode 100644 index 00000000..cbd3a39e --- /dev/null +++ b/content/nutanix/docs/nutanix-prism/python/DOC.md @@ -0,0 +1,386 @@ +--- +name: nutanix-prism +description: "Nutanix Prism Python SDK - task monitoring, categories, batch operations, Prism Central management (foundational namespace for all Nutanix SDK operations)" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,prism,tasks,categories,batch,prism-central,domain-manager" +--- + +# Nutanix Prism Python SDK v4.2.1 + +The Prism namespace is the **foundational namespace** required by ALL other Nutanix SDK namespaces. Every mutating operation across the entire Nutanix v4 SDK (VMM, Clustermgmt, Networking, etc.) returns a `TaskReference` that must be polled via the Prism `TasksApi`. This namespace also provides category management, batch operations, and Prism Central domain management. + +## Golden Rule + +- **Package**: `ntnx_prism_py_client` +- **Install**: `pip install ntnx-prism-py-client==4.2.1` +- **CRITICAL**: This namespace is REQUIRED by ALL other Nutanix SDK namespaces for monitoring async tasks. +- **SDK Reference**: https://developers.nutanix.com/sdk-reference?namespace=prism&version=v4.2&language=python + +## Installation + +```bash +pip install ntnx-prism-py-client==4.2.1 +``` + +## Initialization + +```python +from ntnx_prism_py_client import Configuration, ApiClient +from ntnx_prism_py_client.api import TasksApi, CategoriesApi, BatchesApi + +config = Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False + +client = ApiClient(configuration=config) +client.add_default_header("Accept-Encoding", "gzip, deflate, br") + +tasks_api = TasksApi(api_client=client) +categories_api = CategoriesApi(api_client=client) +batches_api = BatchesApi(api_client=client) +``` + +## Core Operations + +### 1. Task Monitoring (wait_for_task pattern) + +This is the most important pattern in the entire Nutanix SDK. Every create, update, delete, and power operation across all namespaces returns a `TaskReference`. You must poll the task to confirm completion and extract the created entity's UUID. + +```python +import time +from ntnx_prism_py_client.api import TasksApi + +tasks_api = TasksApi(api_client=client) + +def wait_for_task(tasks_api, task_ext_id, timeout=600, poll_interval=5): + """Poll a task until completion or timeout.""" + elapsed = 0 + while elapsed < timeout: + task = tasks_api.get_task_by_id(task_ext_id, async_req=False) + status = task.data.status + + if status == "SUCCEEDED": + return task.data + elif status == "FAILED": + error_msgs = task.data.error_messages or [] + errors = "; ".join(str(e) for e in error_msgs) + raise RuntimeError( + f"Task {task_ext_id} failed: {errors}" + ) + elif status == "CANCELED": + raise RuntimeError(f"Task {task_ext_id} was canceled") + + # Still RUNNING or QUEUED + time.sleep(poll_interval) + elapsed += poll_interval + + raise TimeoutError(f"Task {task_ext_id} did not complete within {timeout}s") + + +# Usage after any mutating SDK call: +# response = vm_api.create_vm(body=vm, async_req=False) +# task_ext_id = response.data.ext_id +# completed_task = wait_for_task(tasks_api, task_ext_id) +# new_entity_id = completed_task.entities_affected[0].ext_id +``` + +### 2. Create a Category + +```python +import ntnx_prism_py_client +from ntnx_prism_py_client.models.prism.v4.config.Category import Category +from ntnx_prism_py_client.models.prism.v4.config.CategoryType import CategoryType + +categories_api = ntnx_prism_py_client.api.CategoriesApi(api_client=client) + +new_category = Category( + key="Environment", + value="Production", + description="Production environment category", + type=CategoryType.USER, +) + +result = categories_api.create_category(async_req=False, body=new_category) +category_ext_id = result.data.ext_id +print(f"Created category: {category_ext_id}") +``` + +### 3. List Categories with OData Filter + +```python +# List USER categories, excluding Calm-managed ones +category_list = categories_api.list_categories( + async_req=False, + _filter="type eq Prism.Config.CategoryType'USER' and not contains(key, 'Calm')", +) + +total = category_list.metadata.total_available_results +for cat in category_list.data: + print(f"Key: {cat.key}, Value: {cat.value}, ext_id: {cat.ext_id}") + +# Other filter examples: +# _filter="key eq 'Environment'" +# _filter="startswith(key, 'App')" +# _filter="value in ('Production','Staging')" +``` + +### 4. Batch CREATE Operation (create multiple VMs) + +Batch operations let you submit multiple API calls in a single request. The `uri` field specifies which API endpoint to target, and each payload item contains the request body. + +```python +import ntnx_vmm_py_client.models.vmm.v4.ahv.config as AhvVmConfig +from ntnx_prism_py_client.models.prism.v4.operations.BatchSpec import BatchSpec +from ntnx_prism_py_client.models.prism.v4.operations.BatchSpecMetadata import BatchSpecMetadata +from ntnx_prism_py_client.models.prism.v4.operations.BatchSpecPayload import BatchSpecPayload +from ntnx_prism_py_client.models.prism.v4.operations.ActionType import ActionType + +batches_api = ntnx_prism_py_client.api.BatchesApi(api_client=client) + +# Build one payload per VM to create +payloads = [] +for i in range(5): + vm = AhvVmConfig.Vm.Vm( + name=f"batch-vm-{i}", + num_sockets=1, + num_cores_per_socket=1, + memory_size_bytes=1073741824, # 1 GiB + cluster=AhvVmConfig.ClusterReference.ClusterReference( + ext_id="" + ), + ) + payloads.append(BatchSpecPayload(data=vm)) + +batch_spec = BatchSpec( + metadata=BatchSpecMetadata( + action=ActionType.CREATE, + name="batch-vm-create", + uri="/api/vmm/v4.2/ahv/config/vms", + stop_on_error=True, + chunk_size=20, + ), + payload=payloads, +) + +response = batches_api.submit_batch(async_req=False, body=batch_spec) +batch_task_ext_id = response.data.ext_id +# Poll this task via TasksApi -- sub_tasks will contain per-item results +``` + +### 5. Batch ACTION Operation (assign categories with per-item ETags) + +ACTION batches target operations that require path parameters and ETags for each item. The `uri` uses `{paramName}` placeholders, and each payload provides its own metadata with headers and path values. + +```python +from ntnx_prism_py_client.models.prism.v4.operations.BatchSpecPayloadMetadata import BatchSpecPayloadMetadata +from ntnx_prism_py_client.models.prism.v4.operations.BatchSpecPayloadMetadataHeader import BatchSpecPayloadMetadataHeader +from ntnx_prism_py_client.models.prism.v4.operations.BatchSpecPayloadMetadataPath import BatchSpecPayloadMetadataPath +from ntnx_vmm_py_client.models.vmm.v4.ahv.config.AssociateVmCategoriesParams import AssociateVmCategoriesParams +from ntnx_vmm_py_client.models.vmm.v4.ahv.config.CategoryReference import CategoryReference + +from ntnx_vmm_py_client.api import VmApi +vm_api = VmApi(api_client=vmm_client) + +# Build per-item payloads with individual ETags and path params +payloads = [] +for vm in vm_list.data: + # Each item needs its own ETag from a fresh GET + existing = vm_api.get_vm_by_id(vm.ext_id, async_req=False) + etag = vmm_client.get_etag(existing) + + payloads.append(BatchSpecPayload( + data=AssociateVmCategoriesParams( + categories=[CategoryReference(ext_id="")] + ), + metadata=BatchSpecPayloadMetadata( + headers=[ + BatchSpecPayloadMetadataHeader(name="If-Match", value=etag) + ], + path=[ + BatchSpecPayloadMetadataPath(name="extId", value=vm.ext_id) + ], + ), + )) + +batch_spec = BatchSpec( + metadata=BatchSpecMetadata( + action=ActionType.ACTION, + name="assign-categories", + stop_on_error=True, + chunk_size=1, + # URI with {extId} placeholder filled per-item from path metadata + uri="/api/vmm/v4.2/ahv/config/vms/{extId}/$actions/associate-categories", + ), + payload=payloads, +) + +response = batches_api.submit_batch(async_req=False, body=batch_spec) +batch_task_ext_id = response.data.ext_id +``` + +## Batch URI Format Reference + +The `uri` in `BatchSpecMetadata` must match the v4.2 API path. Common patterns: + +| Operation | Action Type | URI Pattern | +|-----------|-------------|-------------| +| Create VMs | CREATE | `/api/vmm/v4.2/ahv/config/vms` | +| Create subnets | CREATE | `/api/networking/v4.2/config/subnets` | +| Create categories | CREATE | `/api/prism/v4.2/config/categories` | +| Associate VM categories | ACTION | `/api/vmm/v4.2/ahv/config/vms/{extId}/$actions/associate-categories` | +| Power on VMs | ACTION | `/api/vmm/v4.2/ahv/config/vms/{extId}/$actions/power-on` | +| Update VMs | UPDATE | `/api/vmm/v4.2/ahv/config/vms/{extId}` | + +## Key Patterns + +### Task Status Values + +```python +# Task status progression (TaskStatus enum): +# QUEUED -> RUNNING -> SUCCEEDED | FAILED | CANCELED +# +# task.data.status values: +# "QUEUED" - waiting to start +# "RUNNING" - in progress +# "SUCCEEDED" - completed successfully +# "FAILED" - completed with error +# "CANCELED" - canceled by user +# "CANCELING" - cancellation in progress +# "SUSPENDED" - task suspended +``` + +### Extracting Created Entity ID from Task + +```python +task = tasks_api.get_task_by_id(task_ext_id, async_req=False) +if task.data.status == "SUCCEEDED": + # The created/affected entity UUID + entity_id = task.data.entities_affected[0].ext_id + entity_type = task.data.entities_affected[0].rel # e.g., "vmm:ahv:config:vm" +``` + +### Batch Task Hierarchy + +Batch operations create a parent task with sub-tasks for each item: + +```python +parent_task = tasks_api.get_task_by_id(batch_task_ext_id, async_req=False) +# parent_task.data.sub_tasks -> list of TaskReferenceInternal +# parent_task.data.number_of_subtasks -> int +# Each sub-task can be polled individually +for sub in parent_task.data.sub_tasks: + sub_task = tasks_api.get_task_by_id(sub.ext_id, async_req=False) + print(f"Sub-task {sub.ext_id}: {sub_task.data.status}") +``` + +## API Classes Summary + +| Class | Methods | Purpose | +|-------|---------|---------| +| TasksApi | 6 | Task monitoring -- used by ALL namespaces. `get_task_by_id`, `list_tasks`, `cancel_task`, `list_task_entities`, `list_task_jobs`, `get_task_job_by_id` | +| CategoriesApi | 5 | Category CRUD -- `create_category`, `get_category_by_id`, `list_categories`, `update_category_by_id`, `delete_category_by_id` | +| BatchesApi | 3 | Batch operations -- `submit_batch`, `get_batch_by_id`, `list_batches` | +| DomainManagerApi | 8 | Prism Central management -- register/unregister PEs, manage products | +| DomainManagerBackupsApi | 12 | PC backup/restore -- backup targets, restore sources, restore points | +| RegistrationApi | 2 | PE registration queries -- `get_registration_by_id`, `list_registrations` | +| ExternalStoragesApi | 1 | External storage info -- `get_external_storage_by_id` | + +**Total: 7 API classes, 37 methods** + +## Key Models + +### Task (`prism.v4.config.Task`) -- 30 properties + +| Property | Type | Notes | +|----------|------|-------| +| `ext_id` | str | Task UUID | +| `operation` | str | Operation name | +| `operation_description` | str | Human-readable description | +| `status` | TaskStatus | QUEUED, RUNNING, SUCCEEDED, FAILED, CANCELED, CANCELING, SUSPENDED | +| `progress_percentage` | int | 0-100 | +| `created_time` | datetime | When task was created | +| `started_time` | datetime | When execution began | +| `completed_time` | datetime | When task finished | +| `entities_affected` | list[EntityReference] | Created/modified entities (ext_id + rel) | +| `sub_tasks` | list[TaskReferenceInternal] | Child tasks (for batch ops) | +| `sub_steps` | list[TaskStep] | Step-by-step progress | +| `error_messages` | list[AppMessage] | Error details on failure | +| `is_cancelable` | bool | Whether task supports cancellation | +| `cluster_ext_ids` | list[str] | Clusters involved | +| `number_of_subtasks` | int | Count of sub-tasks | + +### Category (`prism.v4.config.Category`) -- 13 properties + +| Property | Type | Notes | +|----------|------|-------| +| `key` | str | Category key (e.g., "Environment") | +| `value` | str | Category value (e.g., "Production") | +| `type` | CategoryType | USER or SYSTEM | +| `description` | str | | +| `owner_uuid` | str | Owner user UUID | +| `associations` | list[AssociationSummary] | What entities use this category | +| `ext_id` | str | Category UUID | + +### BatchSpec (`prism.v4.operations.BatchSpec`) + +| Property | Type | Notes | +|----------|------|-------| +| `metadata` | BatchSpecMetadata | Action type, URI, name, stop_on_error, chunk_size | +| `payload` | list[BatchSpecPayload] | List of per-item request bodies | + +### BatchSpecMetadata (`prism.v4.operations.BatchSpecMetadata`) + +| Property | Type | Notes | +|----------|------|-------| +| `action` | ActionType | CREATE, UPDATE, DELETE, ACTION | +| `name` | str | Batch operation name | +| `uri` | str | API endpoint path (with `{param}` placeholders for ACTION/UPDATE) | +| `stop_on_error` | bool | Halt on first failure | +| `chunk_size` | int | Number of items to process in parallel | + +### BatchSpecPayload (`prism.v4.operations.BatchSpecPayload`) + +| Property | Type | Notes | +|----------|------|-------| +| `data` | object | The request body (e.g., Vm, Category, AssociateVmCategoriesParams) | +| `metadata` | BatchSpecPayloadMetadata | Per-item headers (If-Match) and path params | + +### DomainManager (`prism.v4.config.DomainManager`) -- 13 properties + +| Property | Type | Notes | +|----------|------|-------| +| `config` | DomainManagerClusterConfig | PC cluster configuration | +| `network` | DomainManagerNetwork | PC network settings | +| `hosting_cluster_ext_id` | str | PE cluster hosting this PC | +| `should_enable_high_availability` | bool | HA setting | +| `node_ext_ids` | list[str] | PC VM node UUIDs | +| `ext_id` | str | PC domain manager UUID | + +## Common Mistakes + +1. **Not polling tasks** -- Every mutating operation across ALL Nutanix SDK namespaces returns a TaskReference. You must use `TasksApi.get_task_by_id()` to confirm success and get created entity UUIDs. + +2. **Ignoring task failure details** -- When `task.data.status == "FAILED"`, check `task.data.error_messages` for the specific error. Also check `legacy_error_message` as a fallback. + +3. **Wrong URI format in batch operations** -- The `uri` must be the full v4.2 API path (e.g., `/api/vmm/v4.2/ahv/config/vms`). Using the wrong path or version results in 404 errors. + +4. **Missing per-item metadata in ACTION batches** -- ACTION and UPDATE batch types require each `BatchSpecPayload` to include `metadata` with `headers` (for If-Match/ETag) and `path` (for URI parameter substitution). + +5. **Category type enum syntax in filters** -- Category type filters require the fully qualified enum: `type eq Prism.Config.CategoryType'USER'`, not just `type eq 'USER'`. + +## Response Pattern + +```python +response.data # Task, Category, list[Category], BatchSpec, etc. +response.metadata # ApiResponseMetadata + # .total_available_results (int, for list calls) + # .flags, .links, .messages +``` diff --git a/content/nutanix/docs/nutanix-sdk/python/DOC.md b/content/nutanix/docs/nutanix-sdk/python/DOC.md new file mode 100644 index 00000000..7b252477 --- /dev/null +++ b/content/nutanix/docs/nutanix-sdk/python/DOC.md @@ -0,0 +1,451 @@ +--- +name: nutanix-sdk +description: "Nutanix v4.2 Python SDK - unified client for managing Nutanix infrastructure (VMs, clusters, networking, storage, DR, monitoring) via Prism Central" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,prism,hyperconverged,hci,virtualization,infrastructure,cloud,sdk" +--- + +# Nutanix v4.2 Python SDK Overview + +The Nutanix v4.2 Python SDK provides programmatic access to Prism Central for managing +hyperconverged infrastructure: VMs, clusters, networking, storage, data protection, +lifecycle management, monitoring, and more. The SDK is split into 12 namespace-specific +packages that share identical authentication and request patterns. + +## Golden Rule + +- **Package naming**: `ntnx_{namespace}_py_client` (e.g., `ntnx_vmm_py_client`) +- **Install (pip)**: `pip install ntnx-vmm-py-client ntnx-prism-py-client ntnx-clustermgmt-py-client ntnx-networking-py-client` (etc.) +- **Import pattern**: `from ntnx_vmm_py_client import Configuration, ApiClient` then `from ntnx_vmm_py_client.api import VmApi` +- **Each namespace needs its own Configuration + ApiClient** -- they cannot be shared across namespaces. + +## Installation + +Install all 12 packages at once: + +```bash +pip install \ + ntnx-vmm-py-client==4.2.1 \ + ntnx-prism-py-client==4.2.1 \ + ntnx-clustermgmt-py-client==4.2.1 \ + ntnx-networking-py-client==4.2.1 \ + ntnx-lifecycle-py-client==4.2.1 \ + ntnx-microseg-py-client==4.2.1 \ + ntnx-datapolicies-py-client==4.2.1 \ + ntnx-dataprotection-py-client==4.2.1 \ + ntnx-volumes-py-client==4.2.1 \ + ntnx-licensing-py-client==4.2.1 \ + ntnx-monitoring-py-client==4.2.1 \ + ntnx-aiops-py-client==4.2.1b1 +``` + +Or install only what you need. The `prism` package is almost always required (task +monitoring), plus whichever domain namespaces your workflow touches. + +## Initialization + +### Configuration Parameters + +| Parameter | Description | Required | Default | +|-----------|-------------|----------|---------| +| host | Prism Central IPv4/IPv6/FQDN | Yes | N/A | +| port | Connection port | No | 9440 | +| username | Cluster username | Yes | N/A | +| password | Cluster password | Yes | N/A | +| verify_ssl | Verify SSL certificate | No | True | +| max_retry_attempts | Max retry attempts | No | 5 | +| backoff_factor | Retry backoff multiplier | No | 3 | +| connect_timeout | Connection timeout (ms) | No | 30000 | +| read_timeout | Read timeout (ms) | No | 30000 | + +### Single-Namespace Auth + +```python +import ntnx_vmm_py_client +from ntnx_vmm_py_client.api import VmApi + +config = ntnx_vmm_py_client.Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False + +client = ntnx_vmm_py_client.ApiClient(configuration=config) +client.add_default_header("Accept-Encoding", "gzip, deflate, br") + +vm_api = VmApi(api_client=client) +``` + +### Multi-Namespace Client Setup + +When your workflow spans multiple namespaces, create a client per namespace in a loop: + +```python +import ntnx_vmm_py_client +import ntnx_prism_py_client +import ntnx_clustermgmt_py_client +import ntnx_networking_py_client + +packages = { + "vmm": ntnx_vmm_py_client, + "prism": ntnx_prism_py_client, + "clustermgmt": ntnx_clustermgmt_py_client, + "networking": ntnx_networking_py_client, +} + +clients = {} +for name, pkg in packages.items(): + config = pkg.Configuration() + config.host = "prism-central.example.com" + config.port = 9440 + config.username = "admin" + config.password = "password" + config.verify_ssl = False + client = pkg.ApiClient(configuration=config) + client.add_default_header("Accept-Encoding", "gzip, deflate, br") + clients[name] = client + +# Use each client with its namespace's API classes +from ntnx_vmm_py_client.api import VmApi +from ntnx_prism_py_client.api import TasksApi +from ntnx_clustermgmt_py_client.api import ClustersApi +from ntnx_networking_py_client.api import SubnetsApi + +vm_api = VmApi(api_client=clients["vmm"]) +tasks_api = TasksApi(api_client=clients["prism"]) +clusters_api = ClustersApi(api_client=clients["clustermgmt"]) +subnets_api = SubnetsApi(api_client=clients["networking"]) +``` + +### API Key Auth (alternative) + +```python +config = ntnx_vmm_py_client.Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.api_key = {"X-ntnx-api-key": "your-api-key-here"} +``` + +## Core Operations + +### 1. Task Monitoring (required for ALL mutating operations) + +Every create, update, delete, and power operation is asynchronous. The API returns a +`TaskReference` containing `ext_id`. You MUST poll via the prism `TasksApi` to confirm +success and retrieve created entity IDs. + +```python +import time +from ntnx_prism_py_client.api import TasksApi + +def wait_for_task(prism_client, task_ext_id, timeout=600, poll_interval=5): + """Poll a Prism task until it completes, fails, or times out.""" + tasks_api = TasksApi(api_client=prism_client) + elapsed = 0 + while elapsed < timeout: + task = tasks_api.get_task_by_id(task_ext_id) + status = task.data.status + if status == "SUCCEEDED": + return task.data + elif status in ("FAILED", "CANCELED"): + raise Exception( + f"Task {task_ext_id} {status}: {task.data.error_messages}" + ) + time.sleep(poll_interval) + elapsed += poll_interval + raise TimeoutError(f"Task {task_ext_id} timed out after {timeout}s") + +# Usage after a mutating call: +response = vm_api.create_vm(body=vm_spec) +task_ext_id = response.data.ext_id +task_data = wait_for_task(clients["prism"], task_ext_id) + +# Extract created entity ID from completed task +new_vm_ext_id = task_data.entities_affected[0].ext_id +``` + +### 2. ETag Concurrency (required for updates and power ops) + +All update and power-state operations require optimistic concurrency via the `If-Match` +header. Fetch the resource first, extract the ETag, set the header, perform the update, +then clean up. + +```python +# Step 1: Fetch resource to get ETag +response = vm_api.get_vm_by_id(extId=vm_ext_id) +vm = response.data +etag = clients["vmm"].get_etag(response) + +# Step 2: Set If-Match header +clients["vmm"].add_default_header("If-Match", etag) + +# Step 3: Modify and update +vm.name = "renamed-vm" +update_response = vm_api.update_vm_by_id(extId=vm_ext_id, body=vm) + +# Step 4: Clean up header +clients["vmm"].default_headers.pop("If-Match", None) + +# Step 5: Wait for task +task_data = wait_for_task(clients["prism"], update_response.data.ext_id) +``` + +### 3. OData Filtering + +List operations support OData query parameters for server-side filtering, sorting, field +selection, and pagination. + +```python +# Equality +_filter = "name eq 'my-vm'" + +# Inequality +_filter = "name ne 'not-this'" + +# String functions +_filter = "contains(name, 'default')" +_filter = "startswith(name, 'A')" + +# IN clause +_filter = "name in ('vm1','vm2')" + +# Compound filters +_filter = "field1 eq 'x' and field2 eq 'y'" + +# Enum values (namespace-qualified) +_filter = "type eq Prism.Config.CategoryType'USER'" + +# Sorting +_orderby = "name asc" + +# Field selection (reduce payload) +_select = "name,extId,powerState" + +# Pagination +_limit = 50 # max 100 per page +_page = 0 # 0-indexed +``` + +### 4. Pagination + +For large result sets, iterate through pages: + +```python +def list_all(api_method, page_size=50, **kwargs): + """Paginate through all results from a list endpoint.""" + all_items = [] + page = 0 + while True: + response = api_method(_page=page, _limit=page_size, **kwargs) + if response.data: + all_items.extend(response.data) + total = response.metadata.total_available_results + if len(all_items) >= total or not response.data: + break + page += 1 + return all_items + +# Usage +all_vms = list_all(vm_api.list_vms, _filter="powerState eq 'ON'") +``` + +### 5. Response Pattern + +All SDK responses follow a consistent structure: + +```python +response.data # The result: single object, list, or TaskReference +response.metadata # ApiResponseMetadata with .total_available_results +``` + +## Key Patterns + +### Model Import Aliasing + +Models are deeply nested in the package hierarchy. Use aliasing for readability: + +```python +# VMM models +import ntnx_vmm_py_client.models.vmm.v4.ahv.config as AhvVmConfig +# AhvVmConfig.Vm.Vm(), AhvVmConfig.Disk.Disk(), AhvVmConfig.Nic.Nic() + +# Networking models +import ntnx_networking_py_client.models.networking.v4.config as v4NetConfig +import ntnx_networking_py_client.models.common.v1.config as v1CommonConfig + +# Prism models +import ntnx_prism_py_client.models.prism.v4.config as PrismConfig +import ntnx_prism_py_client.models.prism.v4.operations as PrismOps + +# Clustermgmt models +import ntnx_clustermgmt_py_client.models.clustermgmt.v4.config as ClusterConfig +``` + +### Version Negotiation + +Version negotiation is enabled by default. The SDK automatically handles API version +compatibility. Disable if needed: + +```python +client = ntnx_vmm_py_client.ApiClient( + configuration=config, + allow_version_negotiation=False +) +``` + +### Auto-Retry + +The SDK automatically retries on transient errors: HTTP 408, 429, 502, 503, and 302. +Backoff formula: `backoff_factor * (2 * (retries - 1))` + +Defaults: 5 retries with factor 3 = waits of 0, 3, 6, 12, 24 seconds. + +## Common Mistakes + +1. **Forgetting ETag for updates** -- All update and power operations require fetching + the resource first to get the ETag via `client.get_etag(response)`, then setting the + `If-Match` header. Without it, the API returns 412 Precondition Failed. + +2. **Not monitoring tasks** -- ALL mutating operations (create, update, delete, power) + are asynchronous and return a `TaskReference`. You MUST poll via prism `TasksApi` to + confirm completion. The operation is not done when the API call returns. + +3. **Using wrong import paths** -- Models are deeply nested. Use aliasing: + `import ntnx_vmm_py_client.models.vmm.v4.ahv.config as AhvVmConfig` + +4. **Missing Accept-Encoding header** -- Always add + `client.add_default_header("Accept-Encoding", "gzip, deflate, br")` for compressed + responses. Large list queries can be 10x smaller with compression. + +5. **Sharing Configuration across namespaces** -- Each namespace needs its own + `Configuration()` and `ApiClient()` instance. They are not interchangeable. + +6. **Not cleaning up If-Match header** -- After an update call, remove the `If-Match` + header with `client.default_headers.pop("If-Match", None)` or subsequent calls to + the same client will send a stale ETag. + +7. **Exceeding page size** -- `_limit` maximum is 100. Requesting more silently caps + at 100. Use pagination for full result sets. + +## Namespace Reference + +| Namespace | Package | Version | Key APIs | Methods | Models | +|-----------|---------|---------|----------|---------|--------| +| vmm | ntnx_vmm_py_client | 4.2.1 | VmApi (69), ImagesApi, TemplatesApi | 179 | 714 | +| prism | ntnx_prism_py_client | 4.2.1 | TasksApi, CategoriesApi, BatchesApi | 37 | 297 | +| clustermgmt | ntnx_clustermgmt_py_client | 4.2.1 | ClustersApi (47), StorageContainersApi | 84 | 551 | +| networking | ntnx_networking_py_client | 4.2.1 | SubnetsApi, VpcsApi, FloatingIpsApi | 118 | 663 | +| lifecycle | ntnx_lifecycle_py_client | 4.2.1 | EntitiesApi, InventoryApi, UpgradesApi | 29 | 198 | +| microseg | ntnx_microseg_py_client | 4.2.1 | NetworkSecurityPoliciesApi | 33 | 111 | +| datapolicies | ntnx_datapolicies_py_client | 4.2.1 | ProtectionPoliciesApi, RecoveryPlansApi | 43 | 158 | +| dataprotection | ntnx_dataprotection_py_client | 4.2.1 | RecoveryPointsApi, ConsistencyGroupsApi | 31 | 117 | +| volumes | ntnx_volumes_py_client | 4.2.1 | VolumeGroupsApi (25) | 30 | 87 | +| licensing | ntnx_licensing_py_client | 4.2.1 | LicensesApi, LicenseKeysApi | 19 | 72 | +| monitoring | ntnx_monitoring_py_client | 4.2.1 | AlertsApi, EventsApi, AuditsApi | 23 | 67 | +| aiops | ntnx_aiops_py_client | 4.2.1b1 | ScenariosApi, StatsApi | 18 | 40 | + +> **Version note**: The `aiops` namespace does not have a stable v4.2.1 release on PyPI — only `4.2.1b1` (beta) is available. All other namespaces are stable v4.2.1 releases. + +**Totals: 112 API classes, 644 methods, 3,075 models** + +### Excluded Namespaces (no v4.2 release available) + +These namespaces do not yet have v4.2 packages and are excluded: +- iam (4.1.1b2), storage (4.0.2a3), opsmgmt (4.0.3), files (4.0.1), objects (4.0.2), security (4.1.1) + +## Cross-Namespace Workflow Map + +Most real-world operations span multiple namespaces. The prism namespace (TasksApi) is +required for virtually every mutating workflow. + +| Workflow | Namespaces Required | Notes | +|----------|-------------------|-------| +| Create VM | vmm + clustermgmt + networking + prism | Need cluster/subnet ext_ids | +| Clone VM | vmm + prism | Clone from existing VM or template | +| VM Snapshot | vmm + dataprotection + prism | Create recovery point | +| Create Subnet | networking + clustermgmt + prism | Need cluster ext_id | +| Create VPC | networking + prism | Virtual private cloud | +| LCM Inventory | lifecycle + prism | Discover updatable entities | +| LCM Update | lifecycle + clustermgmt + prism | Upgrade AOS/AHV/firmware | +| DR Setup | datapolicies + dataprotection + prism | Protection policies + recovery plans | +| Security Policy | microseg + prism | Categories for policy rules | +| Batch Operations | prism + target namespace | BatchesApi for bulk actions | +| Alert Management | monitoring + prism | Alerts, events, audit trails | +| Capacity Planning | aiops + clustermgmt + prism | Scenarios and stats | +| Volume Management | volumes + clustermgmt + prism | iSCSI volume groups | +| License Management | licensing + prism | Apply/check cluster licenses | +| Category Tagging | prism + target namespace | Categories apply to VMs, etc. | +| ANY mutating op | + prism (TasksApi) | Always needed for task polling | + +## Example: End-to-End VM Creation + +This example demonstrates a complete VM creation workflow spanning four namespaces: + +```python +import ntnx_vmm_py_client +import ntnx_prism_py_client +import ntnx_clustermgmt_py_client +import ntnx_networking_py_client +from ntnx_vmm_py_client.api import VmApi +from ntnx_prism_py_client.api import TasksApi +from ntnx_clustermgmt_py_client.api import ClustersApi +from ntnx_networking_py_client.api import SubnetsApi +import ntnx_vmm_py_client.models.vmm.v4.ahv.config as AhvVmConfig + +# 1. Set up clients (abbreviated -- use the multi-namespace pattern above) +# clients = { "vmm": ..., "prism": ..., "clustermgmt": ..., "networking": ... } + +# 2. Find cluster ext_id +clusters_api = ClustersApi(api_client=clients["clustermgmt"]) +clusters = clusters_api.list_clusters(_filter="name eq 'MyCluster'") +cluster_ext_id = clusters.data[0].ext_id + +# 3. Find subnet ext_id +subnets_api = SubnetsApi(api_client=clients["networking"]) +subnets = subnets_api.list_subnets(_filter="name eq 'vm-network'") +subnet_ext_id = subnets.data[0].ext_id + +# 4. Build VM spec +vm = AhvVmConfig.Vm.Vm() +vm.name = "my-new-vm" +vm.num_sockets = 2 +vm.num_cores_per_socket = 2 +vm.memory_size_bytes = 4 * 1024 * 1024 * 1024 # 4 GiB + +nic = AhvVmConfig.Nic.Nic() +nic.network_info = AhvVmConfig.NicNetworkInfo.NicNetworkInfo() +nic.network_info.subnet = AhvVmConfig.SubnetReference.SubnetReference() +nic.network_info.subnet.ext_id = subnet_ext_id +vm.nics = [nic] + +vm.cluster = AhvVmConfig.ClusterReference.ClusterReference() +vm.cluster.ext_id = cluster_ext_id + +# 5. Create VM and wait for task +vm_api = VmApi(api_client=clients["vmm"]) +response = vm_api.create_vm(body=vm) +task_data = wait_for_task(clients["prism"], response.data.ext_id) +new_vm_ext_id = task_data.entities_affected[0].ext_id + +# 6. Power on (requires ETag) +get_resp = vm_api.get_vm_by_id(extId=new_vm_ext_id) +etag = clients["vmm"].get_etag(get_resp) +clients["vmm"].add_default_header("If-Match", etag) +power_resp = vm_api.power_on_vm(extId=new_vm_ext_id) +clients["vmm"].default_headers.pop("If-Match", None) +wait_for_task(clients["prism"], power_resp.data.ext_id) +``` + +## Further Reading + +Each namespace has its own detailed reference document covering all API classes, methods, +and models. See the per-namespace docs for method signatures, request/response models, +and namespace-specific patterns. + +SDK reference: `https://developers.nutanix.com/sdk-reference?namespace={ns}&version=v4.2&language=python` diff --git a/content/nutanix/docs/nutanix-vmm/python/DOC.md b/content/nutanix/docs/nutanix-vmm/python/DOC.md new file mode 100644 index 00000000..3f80aace --- /dev/null +++ b/content/nutanix/docs/nutanix-vmm/python/DOC.md @@ -0,0 +1,491 @@ +--- +name: nutanix-vmm +description: "Nutanix VMM Python SDK - VM lifecycle management (create, power, clone, snapshot, disk/NIC/GPU management, templates, images, OVAs)" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,vm,virtual-machine,ahv,vmm,hypervisor,images,templates,snapshots" +--- + +# Nutanix VMM Python SDK v4.2.1 + +The VMM (Virtual Machine Management) namespace is the largest Nutanix SDK namespace with 15 API classes, 69 VmApi methods, and models for full AHV VM lifecycle management. + +## Golden Rule + +- **Package**: `ntnx_vmm_py_client` +- **Install**: `pip install ntnx-vmm-py-client==4.2.1` +- **AHV config models**: `import ntnx_vmm_py_client.models.vmm.v4.ahv.config as AhvVmConfig` +- **Content models**: `import ntnx_vmm_py_client.models.vmm.v4.content as VmmContent` +- **Policy models**: `import ntnx_vmm_py_client.models.vmm.v4.ahv.policies` +- **SDK Reference**: https://developers.nutanix.com/sdk-reference?namespace=vmm&version=v4.2&language=python + +## Installation + +```bash +pip install ntnx-vmm-py-client==4.2.1 +``` + +## Initialization + +```python +from ntnx_vmm_py_client import Configuration, ApiClient +from ntnx_vmm_py_client.api import VmApi, ImagesApi, TemplatesApi +import ntnx_vmm_py_client.models.vmm.v4.ahv.config as AhvVmConfig +import ntnx_vmm_py_client.models.vmm.v4.content as VmmContent + +config = Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False + +client = ApiClient(configuration=config) +client.add_default_header("Accept-Encoding", "gzip, deflate, br") + +vm_api = VmApi(api_client=client) +images_api = ImagesApi(api_client=client) +templates_api = TemplatesApi(api_client=client) +``` + +## Core Operations + +### 1. Create a VM (with disk from image, NIC, UEFI boot, cloud-init) + +```python +from base64 import b64encode +import ntnx_vmm_py_client.models.vmm.v4.ahv.config as AhvVmConfig + +# Cluster reference +cluster_ref = AhvVmConfig.ClusterReference.ClusterReference() +cluster_ref.ext_id = "" + +# Disk cloned from an image +cloned_disk = AhvVmConfig.Disk.Disk( + backing_info=AhvVmConfig.VmDisk.VmDisk( + data_source=AhvVmConfig.DataSource.DataSource( + reference=AhvVmConfig.ImageReference.ImageReference( + image_ext_id="" + ) + ) + ), + disk_address=AhvVmConfig.DiskAddress.DiskAddress( + bus_type=AhvVmConfig.DiskBusType.DiskBusType.SCSI, index=0 + ), +) + +# Empty data disk +empty_disk = AhvVmConfig.Disk.Disk( + backing_info=AhvVmConfig.VmDisk.VmDisk( + disk_size_bytes=42949672960, # 40 GiB + storage_container=AhvVmConfig.VmDiskContainerReference.VmDiskContainerReference( + ext_id="" + ), + ), + disk_address=AhvVmConfig.DiskAddress.DiskAddress( + bus_type=AhvVmConfig.DiskBusType.DiskBusType.SCSI, index=1 + ), +) + +# NIC with DHCP +vm_nic = AhvVmConfig.Nic.Nic( + backing_info=AhvVmConfig.EmulatedNic.EmulatedNic( + model=AhvVmConfig.EmulatedNicModel.EmulatedNicModel.VIRTIO, + is_connected=True, + ), + network_info=AhvVmConfig.NicNetworkInfo.NicNetworkInfo( + nic_type=AhvVmConfig.NicType.NicType.NORMAL_NIC, + subnet=AhvVmConfig.SubnetReference.SubnetReference(ext_id=""), + ipv4_config=AhvVmConfig.Ipv4Config.Ipv4Config(should_assign_ip=True), + ), +) + +# Cloud-init userdata +userdata_encoded = b64encode(b"#cloud-config\npassword: nutanix\n").decode("ascii") + +# Assemble the VM +new_vm = AhvVmConfig.Vm.Vm( + name="my-vm", + description="VM created using Nutanix v4 Python SDK", + num_sockets=2, + num_cores_per_socket=2, + num_threads_per_core=1, + memory_size_bytes=8589934592, # 8 GiB + machine_type=AhvVmConfig.MachineType.MachineType.Q35, + cluster=cluster_ref, + disks=[cloned_disk, empty_disk], + nics=[vm_nic], + cd_roms=[ + AhvVmConfig.CdRom.CdRom( + disk_address=AhvVmConfig.CdRomAddress.CdRomAddress( + bus_type=AhvVmConfig.CdRomBusType.CdRomBusType.SATA, index=0 + ) + ) + ], + boot_config=AhvVmConfig.UefiBoot.UefiBoot( + boot_order=[AhvVmConfig.BootDeviceType.BootDeviceType.DISK], + is_secure_boot_enabled=False, + ), + is_branding_enabled=True, + guest_customization=AhvVmConfig.GuestCustomizationParams.GuestCustomizationParams( + config=AhvVmConfig.CloudInit.CloudInit( + cloud_init_script=AhvVmConfig.Userdata.Userdata(value=userdata_encoded), + datasource_type=AhvVmConfig.CloudInitDataSourceType.CloudInitDataSourceType.CONFIG_DRIVE_V2, + ) + ), +) + +vm_api = VmApi(api_client=client) +response = vm_api.create_vm(async_req=False, body=new_vm) +task_ext_id = response.data.ext_id # TaskReference -> poll via prism TasksApi +``` + +### 2. List VMs with Filtering and Pagination + +```python +# OData filtering with pagination and field selection +response = vm_api.list_vms( + async_req=False, + _page=0, + _limit=50, + _filter="name eq 'my-vm'", + _orderby="name asc", + _select="name,extId,powerState", +) +vms = response.data # list of Vm objects +total = response.metadata.total_available_results + +for vm in vms: + print(f"{vm.name} ({vm.ext_id}) - {vm.power_state}") + +# Other filter examples: +# _filter="startswith(name, 'prod-')" +# _filter="name in ('vm-a','vm-b')" +# _filter="powerState eq 'ON'" +``` + +### 3. Power On a VM (requires ETag) + +```python +# Step 1: GET the VM to obtain its ETag +existing_vm = vm_api.get_vm_by_id(vm_ext_id, async_req=False) +etag = client.get_etag(existing_vm) + +# Step 2: Set the If-Match header and power on +client.add_default_header("If-Match", etag) +power_response = vm_api.power_on_vm(vm_ext_id, async_req=False) +task_ext_id = power_response.data.ext_id + +# Other power operations (all require ETag): +# vm_api.shutdown_vm(ext_id) # ACPI graceful shutdown +# vm_api.power_off_vm(ext_id) # Force power off +# vm_api.reboot_vm(ext_id) # ACPI reboot +# vm_api.reset_vm(ext_id) # Hard reset +# vm_api.power_cycle_vm(ext_id) # Force power cycle +``` + +### 4. Add a Disk to an Existing VM + +```python +import ntnx_vmm_py_client.models.vmm.v4.ahv.config as AhvVmConfig + +disk = AhvVmConfig.Disk.Disk() +disk.disk_address = AhvVmConfig.DiskAddress.DiskAddress( + bus_type=AhvVmConfig.DiskBusType.DiskBusType.SCSI, index=1 +) +vm_disk = AhvVmConfig.VmDisk.VmDisk() +vm_disk.disk_size_bytes = 107374182400 # 100 GiB +vm_disk.storage_container = AhvVmConfig.VmDiskContainerReference.VmDiskContainerReference( + ext_id="" +) +disk.backing_info = vm_disk + +response = vm_api.create_disk(vmExtId="", body=disk, async_req=False) +task_ext_id = response.data.ext_id +``` + +### 5. Create an Image from URL + +```python +import ntnx_vmm_py_client.models.vmm.v4.content as VmmContent +import ntnx_vmm_py_client.models.vmm.v4.ahv.config as AhvVmConfig + +new_image = VmmContent.Image.Image() +new_image.name = "rocky_linux_10_cloud" +new_image.desc = "Rocky Linux 10 Cloud Image" +new_image.type = "DISK_IMAGE" + +image_source = VmmContent.UrlSource.UrlSource() +image_source.url = "https://dl.rockylinux.org/pub/rocky/10/images/x86_64/Rocky-10-GenericCloud-Base.latest.x86_64.qcow2" +image_source.allow_insecure = False +new_image.source = image_source + +# Pin to a specific cluster +image_cluster = AhvVmConfig.ClusterReference.ClusterReference() +image_cluster.ext_id = "" +new_image.initial_cluster_locations = [image_cluster] + +images_api = ntnx_vmm_py_client.api.ImagesApi(api_client=client) +result = images_api.create_image(async_req=False, body=new_image) +task_ext_id = result.data.ext_id +``` + +## Key Patterns + +### Model Aliasing (recommended) + +Models are deeply nested. Use module-level aliases for readability: + +```python +import ntnx_vmm_py_client.models.vmm.v4.ahv.config as AhvVmConfig + +# Then reference as: +# AhvVmConfig.Vm.Vm() +# AhvVmConfig.Disk.Disk() +# AhvVmConfig.Nic.Nic() +# AhvVmConfig.ClusterReference.ClusterReference() +# AhvVmConfig.DiskBusType.DiskBusType.SCSI +``` + +### All Mutating Operations Return TaskReference + +Every create, update, delete, and power operation returns a `TaskReference`. You must poll the task via the Prism `TasksApi` to confirm completion and retrieve the created entity's `ext_id`: + +```python +task_ext_id = response.data.ext_id # from any mutating call +# Use prism TasksApi.get_task_by_id(task_ext_id) to poll +# task.data.entities_affected[0].ext_id -> new entity UUID +``` + +### ETag / If-Match for Updates and Power Ops + +All update and power operations require an ETag obtained from a prior GET: + +```python +get_resp = vm_api.get_vm_by_id(extId="", async_req=False) +etag = client.get_etag(get_resp) +client.add_default_header("If-Match", etag) +vm_api.update_vm_by_id(extId="", body=modified_vm, async_req=False) +``` + +### Sub-resource APIs for Disk/NIC/GPU + +Disks, NICs, GPUs, CD-ROMs, serial ports, and PCIe devices are managed via dedicated sub-resource methods on `VmApi` rather than embedding them in `update_vm_by_id`: + +- `create_disk(vmExtId, body)` / `delete_disk_by_id(vmExtId, extId)` +- `create_nic(vmExtId, body)` / `delete_nic_by_id(vmExtId, extId)` +- `create_gpu(vmExtId, body)` / `delete_gpu_by_id(vmExtId, extId)` + +## Common Mistakes + +1. **Missing ETag for power operations** -- `power_on_vm`, `shutdown_vm`, and all power methods require a GET first to obtain the ETag via `client.get_etag(response)`, then set `If-Match` header. Omitting this returns a 412 Precondition Failed. + +2. **Wrong model import path** -- Models are nested: `ntnx_vmm_py_client.models.vmm.v4.ahv.config`, not directly under `ntnx_vmm_py_client`. Use the aliasing pattern above. + +3. **Forgetting `async_req=False`** -- By default, API calls return an `AsyncResult`. Pass `async_req=False` for synchronous behavior. + +4. **Missing `storage_container` on new disks** -- New empty disks require a `VmDiskContainerReference` with the storage container `ext_id`. Omitting it causes a 400 error. + +5. **Using deprecated NIC fields** -- Use `nic_backing_info` / `nic_network_info` (new OneOf fields) instead of the legacy `backing_info` / `network_info` on the `Nic` model. The legacy fields still work but may be removed in future versions. + +6. **Not polling the task** -- All mutating operations are async. The response only contains a `TaskReference`, not the created entity. You must poll `TasksApi.get_task_by_id()` and extract `entities_affected[0].ext_id`. + +## API Classes Summary + +| Class | Methods | Purpose | +|-------|---------|---------| +| VmApi | 69 | Full VM lifecycle -- CRUD, power, clone, disk/NIC/GPU/CD-ROM, migration, guest tools, categories | +| ImagesApi | 7 | Image CRUD, download (`get_file_by_image_id`), import from PE | +| TemplatesApi | 13 | VM template CRUD, deploy, publish, version management, guest update | +| OvasApi | 7 | OVA CRUD, deploy, download | +| StatsApi | 4 | VM, disk, and NIC statistics with time ranges | +| EsxiVmApi | 18 | ESXi VM operations (parallel to VmApi for ESXi clusters) | +| EsxiStatsApi | 4 | ESXi VM statistics | +| VmRecoveryPointsApi | 5 | VM snapshots -- create, list, get, delete, restore | +| VmAntiAffinityPoliciesApi | 8 | VM-to-VM anti-affinity rules | +| VmHostAffinityPoliciesApi | 7 | VM-to-host affinity rules | +| VmStartupPoliciesApi | 14 | VM startup ordering and dependencies | +| VmGuestCustomizationProfilesApi | 5 | Reusable guest customization profiles | +| ImagePlacementPoliciesApi | 7 | Control image replication across clusters | +| ImageRateLimitPoliciesApi | 6 | Throttle image transfer bandwidth | +| TemplatePlacementPoliciesApi | 5 | Control template placement across clusters | + +## Key Models + +### Vm (`vmm.v4.ahv.config.Vm`) -- 55 properties + +Core properties for create/update: + +| Property | Type | Notes | +|----------|------|-------| +| `name` | str | VM name | +| `description` | str | | +| `num_sockets` | int | CPU sockets | +| `num_cores_per_socket` | int | Cores per socket | +| `num_threads_per_core` | int | Threads per core | +| `memory_size_bytes` | int | RAM in bytes | +| `cluster` | ClusterReference | Target cluster (`ext_id`) | +| `disks` | list[Disk] | Disk devices | +| `nics` | list[Nic] | Network interfaces | +| `gpus` | list[Gpu] | GPU passthrough/vGPU | +| `cd_roms` | list[CdRom] | CD-ROM devices | +| `boot_config` | OneOf[LegacyBoot, UefiBoot] | Boot mode | +| `machine_type` | MachineType | PC or Q35 | +| `guest_customization` | GuestCustomizationParams | Cloud-init / Sysprep | +| `power_state` | PowerState | Read-only: ON, OFF, PAUSED, SUSPENDED | +| `categories` | list[CategoryReference] | Assigned categories | +| `ext_id` | str | Read-only UUID | + +### Disk (`vmm.v4.ahv.config.Disk`) + +- `disk_address` -- DiskAddress with `bus_type` (SCSI, IDE, PCI, SATA, SPAPR) and `index` +- `backing_info` -- OneOf[VmDisk, ADSFVolumeGroupReference] + +### VmDisk (`vmm.v4.ahv.config.VmDisk`) + +- `disk_size_bytes` -- Size for new disks +- `storage_container` -- VmDiskContainerReference (`ext_id`) +- `data_source` -- DataSource with reference to ImageReference, VmDiskReference, or VmDiskRecoveryPointReference + +### Nic (`vmm.v4.ahv.config.Nic`) + +- `backing_info` / `nic_backing_info` -- EmulatedNic (model: VIRTIO/E1000, mac_address, is_connected) +- `network_info` / `nic_network_info` -- NicNetworkInfo (nic_type, subnet, ipv4_config, vlan_mode) + +### Image (`vmm.v4.content.Image`) -- 19 properties + +- `name`, `description`, `type` (DISK_IMAGE, ISO_IMAGE) +- `source` -- OneOf[UrlSource, VmDiskSource] +- `size_bytes`, `checksum`, `cluster_location_ext_ids` + +### Key Enums + +| Enum | Values | +|------|--------| +| PowerState | ON, OFF, PAUSED, SUSPENDED | +| DiskBusType | SCSI, IDE, PCI, SATA, SPAPR | +| MachineType | PC, Q35 | +| GpuMode | PASSTHROUGH_GRAPHICS, PASSTHROUGH_COMPUTE, VIRTUAL | +| GpuVendor | NVIDIA, AMD, INTEL | +| NicType | NORMAL_NIC, DIRECT_NIC, NETWORK_FUNCTION_NIC, SPAN_DESTINATION_NIC | +| EmulatedNicModel | VIRTIO, E1000 | +| ImageType | DISK_IMAGE, ISO_IMAGE | +| RecoveryPointType | CRASH_CONSISTENT, APPLICATION_CONSISTENT | + +## Response Pattern + +All API responses follow this structure: + +```python +response.data # Vm, list[Vm], TaskReference, Image, etc. +response.metadata # ApiResponseMetadata + # .total_available_results (int, for list calls) + # .flags, .links, .messages, .extra_info +``` + +## Additional Operations + +### Update a VM (requires ETag) + +```python +get_resp = vm_api.get_vm_by_id(extId="", async_req=False) +vm = get_resp.data +etag = client.get_etag(get_resp) + +vm.memory_size_bytes = 8 * 1024 * 1024 * 1024 # 8 GiB +vm.num_sockets = 4 + +client.add_default_header("If-Match", etag) +response = vm_api.update_vm_by_id(extId=vm.ext_id, body=vm, async_req=False) +``` + +### Clone a VM + +```python +clone_params = AhvVmConfig.CloneOverrideParams.CloneOverrideParams() +clone_params.name = "cloned-vm" +clone_params.num_sockets = 4 +clone_params.memory_size_bytes = 8 * 1024 * 1024 * 1024 + +response = vm_api.clone_vm(extId="", body=clone_params, async_req=False) +``` + +### Create and Restore Snapshots + +```python +from ntnx_vmm_py_client.api import VmRecoveryPointsApi + +rp_api = VmRecoveryPointsApi(api_client=client) + +# Create snapshot +rp = AhvVmConfig.VmRecoveryPoint.VmRecoveryPoint() +rp.name = "pre-update-snapshot" +rp.vm_ext_id = "" +response = rp_api.create_vm_recovery_point(body=rp, async_req=False) + +# Restore to new VM from snapshot +override = AhvVmConfig.VmConfigOverrideSpecification.VmConfigOverrideSpecification() +override.name = "restored-vm" +response = rp_api.restore_vm_recovery_point( + extId="", body=override, async_req=False +) + +# Revert VM in-place to snapshot +revert = AhvVmConfig.RevertParams.RevertParams() +revert.vm_recovery_point_ext_id = "" +response = vm_api.revert_vm(extId="", body=revert, async_req=False) +``` + +### Deploy VM from Template + +```python +from ntnx_vmm_py_client import TemplatesApi, TemplateDeployment, VmConfigOverride + +templates_api = TemplatesApi(api_client=client) + +# Find template +templates = templates_api.list_templates( + async_req=False, _filter="templateName eq 'my-template'" +) +template_ext_id = templates.data[0].ext_id + +# Get active version +versions = templates_api.list_template_versions(template_ext_id, async_req=False) +version_ext_id = versions.data[0].ext_id + +# Deploy +deployment = TemplateDeployment( + version_id=version_ext_id, + number_of_vms=1, + cluster_reference="", + override_vm_config_map={ + 0: VmConfigOverride(name="vm-from-template") + }, +) +result = templates_api.deploy_template( + extId=template_ext_id, body=deployment, async_req=False +) +``` + +### Get VM Statistics + +```python +from datetime import datetime, timedelta +from ntnx_vmm_py_client import DownSamplingOperator + +stats_api = ntnx_vmm_py_client.api.StatsApi(api_client=client) + +stats = stats_api.get_vm_stats_by_id( + "", + _startTime=datetime.now() - timedelta(minutes=120), + _endTime=datetime.now(), + _samplingInterval=30, + _statType=DownSamplingOperator.AVG, + _select="stats/hypervisorCpuUsagePpm,extId", + async_req=False, +) +``` diff --git a/content/nutanix/docs/nutanix-vmm/python/references/vm-api-complete.md b/content/nutanix/docs/nutanix-vmm/python/references/vm-api-complete.md new file mode 100644 index 00000000..ad2c9bc7 --- /dev/null +++ b/content/nutanix/docs/nutanix-vmm/python/references/vm-api-complete.md @@ -0,0 +1,493 @@ +# VmApi Complete Method Reference + +**Package:** `ntnx_vmm_py_client` v4.2.1 +**API Class:** `ntnx_vmm_py_client.api.VmApi` +**Namespace:** `vmm.v4.ahv.config` +**Total Methods:** 69 + +--- + +## VM CRUD (6 methods) + +- `create_vm(body)` - Create a VM +- `get_vm_by_id(extId)` - Get VM configuration details +- `update_vm_by_id(extId, body)` - Update configuration for a VM +- `delete_vm_by_id(extId)` - Delete a VM +- `list_vms(_page, _limit, _filter, _orderby, _select)` - List VMs +- `clone_vm(extId, body)` - Clone a VM + +## Power Operations (9 methods) + +- `power_on_vm(extId)` - Turn on a VM +- `power_off_vm(extId)` - Force power off a VM +- `shutdown_vm(extId)` - Shutdown the VM using ACPI +- `reboot_vm(extId)` - Reboot a VM using ACPI +- `reset_vm(extId)` - Reset a VM immediately +- `power_cycle_vm(extId)` - Force a power cycle for a VM +- `shutdown_guest_vm(extId, body)` - Shutdown the VM using NGT +- `reboot_guest_vm(extId, body)` - Restart the VM using NGT +- `revert_vm(extId, body)` - Revert the AHV VM + +## Disk Management (10 methods) + +- `create_disk(vmExtId, body)` - Create a disk device for a VM +- `get_disk_by_id(vmExtId, extId)` - Get configuration details for the provided disk device +- `update_disk_by_id(vmExtId, extId, body)` - Update the configuration for the provided disk device +- `delete_disk_by_id(vmExtId, extId)` - Removes the specified disk device from a virtual machine +- `list_disks_by_vm_id(vmExtId, _page, _limit)` - List disks attached to a VM +- `migrate_vm_disks(extId, body)` - VmDisk migration between storage containers +- `enable_vm_disk_hydration(vmExtId, extId)` - Enable hydration for a VM disk +- `disable_vm_disk_hydration(vmExtId, extId)` - Disable hydration for a VM disk +- `add_vm_disk_custom_attributes(vmExtId, extId, body)` - Add to the VM disk's custom attributes +- `remove_vm_disk_custom_attributes(vmExtId, extId, body)` - Remove from the VM disk's custom attributes + +## NIC Management (8 methods) + +- `create_nic(vmExtId, body)` - Create a network device for a VM +- `get_nic_by_id(vmExtId, extId)` - Get configuration details for the provided network device +- `update_nic_by_id(vmExtId, extId, body)` - Update the configuration for the provided network device +- `delete_nic_by_id(vmExtId, extId)` - Remove a network device from a VM +- `list_nics_by_vm_id(vmExtId, _page, _limit, _filter)` - List network devices attached to a VM +- `assign_ip_by_id(vmExtId, extId, body)` - Assign an IP address to the provided network device +- `release_ip_by_id(vmExtId, extId)` - Release an assigned IP address from the provided network device +- `migrate_nic_by_id(vmExtId, extId, body)` - Migrate a network device to another subnet + +## GPU Management (4 methods) + +- `create_gpu(vmExtId, body)` - Attach a GPU device to a VM +- `get_gpu_by_id(vmExtId, extId)` - Get configuration details for the provided GPU device +- `delete_gpu_by_id(vmExtId, extId)` - Remove a GPU device from a VM +- `list_gpus_by_vm_id(vmExtId, _page, _limit, _filter)` - List GPUs attached to a VM + +## CD-ROM Management (7 methods) + +- `create_cd_rom(vmExtId, body)` - Create a CD-ROM device for a VM +- `get_cd_rom_by_id(vmExtId, extId)` - Get configuration details for the provided CD-ROM +- `delete_cd_rom_by_id(vmExtId, extId)` - Remove a CD-ROM device from a VM +- `list_cd_roms_by_vm_id(vmExtId, _page, _limit)` - List CD-ROMs attached to a VM +- `insert_cd_rom_by_id(vmExtId, extId, body)` - Inserts an ISO in the provided CD-ROM device +- `eject_cd_rom_by_id(vmExtId, extId)` - Ejects an ISO from the provided CD-ROM device +- `enable_vm_cd_rom_hydration(vmExtId, extId)` - Enable hydration for a VM CD-ROM +- `disable_vm_cd_rom_hydration(vmExtId, extId)` - Disable hydration for a VM CD-ROM + +## Serial Port Management (5 methods) + +- `create_serial_port(vmExtId, body)` - Create a serial port for a VM +- `get_serial_port_by_id(vmExtId, extId)` - Get configuration details for the provided serial port +- `update_serial_port_by_id(vmExtId, extId, body)` - Update the configuration for the provided serial port +- `delete_serial_port_by_id(vmExtId, extId)` - Remove a serial port from a VM +- `list_serial_ports_by_vm_id(vmExtId, _page, _limit)` - List serial ports attached to a VM + +## PCIe Device Management (4 methods) + +- `create_pcie_device(vmExtId, body)` - Create a PCIe device for a VM +- `get_pcie_device_by_id(vmExtId, extId)` - Get configuration details for the provided PCIe device +- `delete_pcie_device_by_id(vmExtId, extId)` - Remove a PCIe device from a VM +- `list_pcie_devices_by_vm_id(vmExtId, _page, _limit)` - List PCIe devices attached to a VM + +## Guest Tools / NGT (6 methods) + +- `get_guest_tools_by_id(extId)` - Get VM NGT configuration +- `update_guest_tools_by_id(extId, body)` - Update NGT configuration for a VM +- `insert_vm_guest_tools(extId, body)` - Insert NGT ISO into an available CD-ROM for a VM +- `install_vm_guest_tools(extId, body)` - Install NGT in a VM +- `uninstall_vm_guest_tools(extId)` - Uninstall NGT from a VM +- `upgrade_vm_guest_tools(extId, body)` - Upgrade NGT inside a VM + +## Categories & Ownership (3 methods) + +- `associate_categories(extId, body)` - Associate categories to a VM +- `disassociate_categories(extId, body)` - Disassociate categories from a VM +- `assign_vm_owner(extId, body)` - Assign owner of a VM + +## Migration (2 methods) + +- `migrate_vm_to_host(extId, body)` - Host to host VM migration +- `cross_cluster_migrate_vm(extId, body, _dryrun)` - Migrate a VM across clusters + +## Other (5 methods) + +- `customize_guest_vm(extId, body)` - Stage guest customization configuration details +- `generate_console_token_by_id(extId)` - Generate VM console token +- `add_vm_custom_attributes(extId, body)` - Add to the VM's custom attributes +- `remove_vm_custom_attributes(extId, body)` - Remove from the VM's custom attributes + +--- + +## Parameter Reference + +### Common Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `extId` | `str` | VM external ID (UUID) | +| `vmExtId` | `str` | Parent VM external ID (for sub-resource methods) | +| `body` | model | Request body (model type varies by method) | +| `_page` | `int` | Page number (0-based) for pagination | +| `_limit` | `int` | Number of results per page | +| `_filter` | `str` | OData filter expression (e.g., `"name eq 'my-vm'"`) | +| `_orderby` | `str` | OData sort expression (e.g., `"name asc"`) | +| `_select` | `str` | OData select for sparse fieldsets | +| `_dryrun` | `bool` | Dry-run mode (cross-cluster migration) | + +### Common kwargs (all methods) + +| kwarg | Description | +|-------|-------------| +| `async_req` | Execute asynchronously, returns `ApplyResult` | +| `_return_http_data_only` | Return only response data (default: `True`) | +| `_preload_content` | Deserialize response (default: `True`) | +| `_request_timeout` | Timeout in seconds | + +--- + +## Key Models + +### Vm (55 properties) + +The primary VM configuration model from `ntnx_vmm_py_client.models.vmm.v4.ahv.config.Vm`. + +| Property | Description | +|----------|-------------| +| `ext_id` | VM external ID (UUID, read-only) | +| `name` | VM display name | +| `description` | VM description | +| `power_state` | Current power state (PowerState enum) | +| `machine_type` | Hardware machine type (MachineType enum) | +| `num_sockets` | Number of CPU sockets | +| `num_cores_per_socket` | CPU cores per socket | +| `num_threads_per_core` | Threads per core | +| `num_numa_nodes` | NUMA node count | +| `memory_size_bytes` | Memory allocation in bytes | +| `disks` | List of Disk objects | +| `nics` | List of Nic objects | +| `gpus` | List of Gpu objects | +| `cd_roms` | List of CdRom objects | +| `serial_ports` | List of SerialPort objects | +| `pcie_devices` | List of PCIe device objects | +| `boot_config` | Boot configuration (BootConfig) | +| `guest_customization` | Guest customization spec | +| `guest_tools` | NGT configuration | +| `storage_config` | VM storage configuration | +| `vtpm_config` | Virtual TPM configuration | +| `apc_config` | APC (Advanced Power Config) | +| `categories` | Associated category references | +| `cluster` | Cluster reference | +| `host` | Host reference | +| `availability_zone` | Availability zone reference | +| `project` | Project reference | +| `ownership_info` | Owner information | +| `protection_policy_state` | Data protection policy state | +| `protection_type` | Protection type (ProtectionType enum) | +| `source` | VM source reference | +| `custom_attributes` | Custom key-value attributes | +| `bios_uuid` | BIOS UUID | +| `generation_uuid` | Generation UUID | +| `hardware_clock_timezone` | Hardware clock timezone | +| `is_agent_vm` | Whether VM is an agent VM | +| `is_branding_enabled` | Nutanix branding on VGA console | +| `is_cpu_hotplug_enabled` | CPU hot-plug support | +| `is_cpu_passthrough_enabled` | CPU passthrough mode | +| `is_cross_cluster_migration_in_progress` | Migration in progress flag | +| `is_gpu_console_enabled` | GPU console enabled | +| `is_live_migrate_capable` | Live migration capability | +| `is_memory_overcommit_enabled` | Memory overcommit | +| `is_scsi_controller_enabled` | SCSI controller support | +| `is_vcpu_hard_pinning_enabled` | vCPU hard pinning | +| `is_vga_console_enabled` | VGA console enabled | +| `vm_guest_customization_status` | Guest customization status | +| `create_time` | Creation timestamp (read-only) | +| `update_time` | Last update timestamp (read-only) | +| `tenant_id` | Tenant identifier | +| `links` | HATEOAS links (read-only) | + +### Disk (9 properties) + +| Property | Description | +|----------|-------------| +| `ext_id` | Disk external ID (UUID) | +| `disk_address` | DiskAddress (bus_type + index) | +| `backing_info` | VmDisk or ADSFVolumeGroupReference | +| `custom_attributes` | Custom key-value attributes | +| `tenant_id` | Tenant identifier | +| `links` | HATEOAS links | + +### DiskAddress (5 properties) + +| Property | Description | +|----------|-------------| +| `bus_type` | DiskBusType enum (SCSI, IDE, PCI, SATA, SPAPR) | +| `index` | Device index on the bus | + +### VmDisk (10 properties) + +| Property | Description | +|----------|-------------| +| `disk_size_bytes` | Disk size in bytes | +| `storage_container` | Storage container reference | +| `storage_config` | Disk storage configuration | +| `data_source` | Data source for disk creation (image, snapshot) | +| `disk_ext_id` | External disk reference ID | +| `is_migration_in_progress` | Migration in progress flag | +| `vm_disk_hydration_info` | Hydration information | + +### DataSource (4 properties) + +| Property | Description | +|----------|-------------| +| `reference` | Reference to image or VM disk (ImageReference, VmDiskReference) | + +### Nic (10 properties) + +| Property | Description | +|----------|-------------| +| `ext_id` | NIC external ID (UUID) | +| `backing_info` | EmulatedNic or DirectNicSpec | +| `network_info` | NicNetworkInfo | +| `nic_backing_info` | NIC backing info | +| `nic_network_info` | NIC network info | +| `tenant_id` | Tenant identifier | +| `links` | HATEOAS links | + +### EmulatedNic (7 properties) + +| Property | Description | +|----------|-------------| +| `model` | EmulatedNicModel enum (VIRTIO, E1000) | +| `mac_address` | MAC address string | +| `is_connected` | Connection state | +| `num_queues` | Number of queues | + +### NicNetworkInfo (12 properties) + +| Property | Description | +|----------|-------------| +| `nic_type` | NicType enum (NORMAL_NIC, DIRECT_NIC, NETWORK_FUNCTION_NIC, SPAN_DESTINATION_NIC) | +| `vlan_mode` | VlanMode enum (ACCESS, TRUNK) | +| `subnet` | Subnet reference | +| `ipv4_config` | Ipv4Config object | +| `ipv4_info` | Read-only IPv4 information | +| `trunked_vlans` | List of trunked VLAN IDs | +| `network_function_chain` | Network function chain reference | +| `network_function_nic_type` | NetworkFunctionNicType enum | +| `should_allow_unknown_macs` | Allow unknown MAC addresses | + +### Ipv4Config (6 properties) + +| Property | Description | +|----------|-------------| +| `ip_address` | Primary IPv4 address | +| `secondary_ip_address_list` | List of secondary IPs | +| `should_assign_ip` | Auto-assign IP from IPAM | + +### Gpu (15 properties) + +| Property | Description | +|----------|-------------| +| `ext_id` | GPU external ID (UUID) | +| `name` | GPU device name | +| `mode` | GpuMode enum (PASSTHROUGH_GRAPHICS, PASSTHROUGH_COMPUTE, VIRTUAL) | +| `vendor` | GpuVendor enum (NVIDIA, AMD, INTEL) | +| `device_id` | PCI device ID | +| `pci_address` | PCI address | +| `fraction` | vGPU fraction | +| `frame_buffer_size_bytes` | Frame buffer size | +| `num_virtual_display_heads` | Virtual display heads | +| `guest_driver_version` | Guest driver version | +| `tenant_id` | Tenant identifier | +| `links` | HATEOAS links | + +### CdRom (9 properties) + +| Property | Description | +|----------|-------------| +| `ext_id` | CD-ROM external ID (UUID) | +| `disk_address` | DiskAddress (bus_type + index) | +| `backing_info` | VmDisk backing reference | +| `iso_type` | IsoType enum (OTHER, GUEST_TOOLS) | +| `tenant_id` | Tenant identifier | +| `links` | HATEOAS links | + +### SerialPort (8 properties) + +| Property | Description | +|----------|-------------| +| `ext_id` | Serial port external ID (UUID) | +| `index` | Port index | +| `is_connected` | Connection state | +| `tenant_id` | Tenant identifier | +| `links` | HATEOAS links | + +### CloneOverrideParams (12 properties) + +| Property | Description | +|----------|-------------| +| `name` | Cloned VM name | +| `num_sockets` | Override CPU sockets | +| `num_cores_per_socket` | Override cores per socket | +| `num_threads_per_core` | Override threads per core | +| `memory_size_bytes` | Override memory size | +| `nics` | Override NIC configuration | +| `boot_config` | Override boot configuration | +| `guest_customization` | Guest customization for clone | +| `guest_customization_profile_config` | Guest customization profile | + +### RevertParams (5 properties) + +| Property | Description | +|----------|-------------| +| `vm_recovery_point_ext_id` | Recovery point to revert to | +| `override_spec` | Override spec for revert | + +### Image (19 properties) - from vmm.v4.content + +| Property | Description | +|----------|-------------| +| `ext_id` | Image external ID (UUID) | +| `name` | Image name | +| `description` | Image description | +| `type` | ImageType enum (DISK_IMAGE, ISO_IMAGE) | +| `source` | Image source (URL or file) | +| `size_bytes` | Image size in bytes | +| `checksum` | Image checksum | +| `owner_ext_id` | Owner external ID | +| `owner_name` | Owner name | +| `category_ext_ids` | Associated category IDs | +| `cluster_location_ext_ids` | Cluster placement IDs | +| `placement_policy_status` | Placement policy status | +| `create_time` | Creation timestamp | +| `last_update_time` | Last update timestamp | +| `tenant_id` | Tenant identifier | +| `links` | HATEOAS links | + +### VmRecoveryPoint (19 properties) - from vmm.v4.ahv.config + +| Property | Description | +|----------|-------------| +| `ext_id` | Recovery point external ID (UUID) | +| `name` | Recovery point name | +| `vm_ext_id` | Source VM external ID | +| `vm` | VM snapshot reference | +| `vm_categories` | Categories at snapshot time | +| `consistency_group_ext_id` | Consistency group ID | +| `recovery_point_type` | RecoveryPointType enum (APPLICATION_CONSISTENT, CRASH_CONSISTENT) | +| `status` | RecoveryPointStatus enum | +| `creation_time` | Creation timestamp | +| `expiration_time` | Expiration timestamp | +| `disk_recovery_points` | List of disk recovery points | +| `location_agnostic_id` | Location-agnostic identifier | +| `total_exclusive_usage_bytes` | Exclusive storage usage | +| `application_consistent_properties` | App-consistent properties | +| `tenant_id` | Tenant identifier | +| `links` | HATEOAS links | + +--- + +## Enums + +### PowerState +VM power state values. +- `ON` - VM is powered on +- `OFF` - VM is powered off +- `PAUSED` - VM is paused +- `SUSPENDED` - VM is suspended + +### MachineType +Hardware machine type. +- `PC` - Standard PC (i440fx) +- `Q35` - Q35 chipset +- `PSERIES` - IBM POWER pSeries + +### DiskBusType +Disk bus interface type. +- `SCSI` - SCSI bus +- `IDE` - IDE bus +- `PCI` - PCI bus +- `SATA` - SATA bus +- `SPAPR` - SPAPR (pSeries) + +### CdRomBusType +CD-ROM bus interface type. +- `IDE` - IDE bus +- `SATA` - SATA bus +- `SPAPR` - SPAPR + +### GpuMode +GPU attachment mode. +- `PASSTHROUGH_GRAPHICS` - GPU passthrough for graphics +- `PASSTHROUGH_COMPUTE` - GPU passthrough for compute +- `VIRTUAL` - Virtual GPU (vGPU) + +### GpuVendor +GPU hardware vendor. +- `NVIDIA` - NVIDIA GPU +- `AMD` - AMD GPU +- `INTEL` - Intel GPU + +### NicType +Network interface type. +- `NORMAL_NIC` - Standard virtual NIC +- `DIRECT_NIC` - Direct/SR-IOV NIC +- `NETWORK_FUNCTION_NIC` - Network function NIC +- `SPAN_DESTINATION_NIC` - SPAN destination NIC + +### EmulatedNicModel +Emulated NIC hardware model. +- `VIRTIO` - VirtIO paravirtual NIC +- `E1000` - Intel E1000 emulated NIC + +### VlanMode +VLAN mode for NIC. +- `ACCESS` - Access mode (single VLAN) +- `TRUNK` - Trunk mode (multiple VLANs) + +### ImageType +Image content type. +- `DISK_IMAGE` - Disk image (QCOW2, RAW, VMDK) +- `ISO_IMAGE` - ISO image + +### IsoType +CD-ROM ISO type. +- `OTHER` - User-provided ISO +- `GUEST_TOOLS` - NGT ISO + +### ProtectionType +VM protection type. +- `UNPROTECTED` - No protection +- `PROTECTED` - Protected by policy + +### AdapterType +NIC adapter type (ESXi). +- `E1000` - Intel E1000 +- `E1000E` - Intel E1000E +- `PCNET32` - AMD PCNet32 +- `VMXNET` - VMware VMXNET +- `VMXNET2` - VMware VMXNET2 +- `VMXNET3` - VMware VMXNET3 + +### NetworkFunctionNicType +Network function NIC role. +- `INGRESS` - Ingress NIC +- `EGRESS` - Egress NIC +- `TAP` - TAP NIC + +--- + +## Other VMM API Classes (for reference) + +| API Class | Methods | Description | +|-----------|---------|-------------| +| EsxiStatsApi | 4 | ESXi VM statistics (disk, NIC, VM stats) | +| EsxiVmApi | 18 | ESXi VM management (mirrors VmApi for ESXi) | +| ImagesApi | 7 | Image CRUD, import, and management | +| ImagePlacementPoliciesApi | 7 | Image placement policy management | +| ImageRateLimitPoliciesApi | 6 | Image rate limit policy management | +| OvasApi | 7 | OVA import and management | +| StatsApi | 4 | AHV VM statistics | +| TemplatesApi | 13 | VM template CRUD, versioning, deploy | +| TemplatePlacementPoliciesApi | 5 | Template placement policies | +| VmAntiAffinityPoliciesApi | 8 | VM anti-affinity policy management | +| VmHostAffinityPoliciesApi | 7 | VM-host affinity policy management | +| VmStartupPoliciesApi | 14 | VM startup policy management | +| VmGuestCustomizationProfilesApi | 5 | Guest customization profiles CRUD | +| VmRecoveryPointsApi | 5 | VM recovery point management | diff --git a/content/nutanix/docs/nutanix-volumes/python/DOC.md b/content/nutanix/docs/nutanix-volumes/python/DOC.md new file mode 100644 index 00000000..6ab32b05 --- /dev/null +++ b/content/nutanix/docs/nutanix-volumes/python/DOC.md @@ -0,0 +1,245 @@ +--- +name: nutanix-volumes +description: "Nutanix Volumes Python SDK - volume groups, iSCSI/NVMe-oF block storage, volume disks, VM-to-volume attachments" +metadata: + languages: "python" + versions: "4.2.1" + revision: 1 + updated-on: "2026-03-16" + source: community + tags: "nutanix,volumes,volume-group,iscsi,nvme,block-storage,volume-disk" +--- + +# Nutanix Volumes Python SDK v4.2.1 + +## Golden Rule + +Package: `ntnx_volumes_py_client` + +```python +import ntnx_volumes_py_client as vol_client +``` + +## Authentication Setup + +```python +config = vol_client.Configuration() +config.host = "prism-central.example.com" +config.port = 9440 +config.username = "admin" +config.password = "password" +config.verify_ssl = False + +api_client = vol_client.ApiClient(configuration=config) +``` + +## API Classes (3 total, 30 methods) + +| API Class | Methods | Purpose | +|-----------|---------|---------| +| VolumeGroupsApi | 25 | Full CRUD for volume groups, disks, VM/iSCSI/NVMf attachments, stats, categories | +| IscsiClientsApi | 3 | Get, list, and update iSCSI client configurations | +| NvmfClientsApi | 2 | Get and list NVMe-TCP client configurations | + +## Core Operations + +### 1. Create a Volume Group + +```python +vg_api = vol_client.VolumeGroupsApi(api_client=api_client) + +volume_group = vol_client.VolumeGroup( + name="app-data-vg", + description="Volume group for application data", + sharing_status=vol_client.SharingStatus.SHARED, + iscsi_features=vol_client.IscsiFeatures( + enabled_authentications=vol_client.AuthenticationType.CHAP, + target_secret="chap-secret-here" + ) +) + +task_response = vg_api.create_volume_group(body=volume_group) +task_ext_id = task_response.data.ext_id + +# Poll task to completion +task_data = wait_for_task(prism_client, task_ext_id, timeout=120) +vg_ext_id = task_data.completion_details[0].value +``` + +### 2. List Volume Groups + +```python +vg_api = vol_client.VolumeGroupsApi(api_client=api_client) + +volume_groups = vg_api.list_volume_groups() +for vg in volume_groups.data: + print(f"VG: {vg.name}, ID: {vg.ext_id}, " + f"Sharing: {vg.sharing_status}") + +# Filter by name +filtered = vg_api.list_volume_groups( + _filter="name eq 'app-data-vg'" +) +``` + +### 3. Get Volume Group Details + +```python +vg = vg_api.get_volume_group_by_id(extId=vg_ext_id) +print(f"Name: {vg.data.name}") +print(f"Status: {vg.data.sharing_status}") +``` + +### 4. Create a Volume Disk + +Add a virtual disk to a volume group. + +```python +volume_disk = vol_client.VolumeDisk( + disk_size_bytes=107374182400, # 100 GB + description="Data disk 1" +) + +task_response = vg_api.create_volume_disk( + volumeGroupExtId=vg_ext_id, + body=volume_disk +) +wait_for_task(prism_client, task_response.data.ext_id, timeout=120) +``` + +### 5. Attach a VM to a Volume Group + +```python +vm_attachment = vol_client.VmAttachment( + vm_ext_id="vm-ext-id-here" +) + +task_response = vg_api.attach_vm( + extId=vg_ext_id, + body=vm_attachment +) +wait_for_task(prism_client, task_response.data.ext_id, timeout=60) +``` + +### 6. Detach a VM from a Volume Group + +```python +vm_detach = vol_client.VmAttachment( + vm_ext_id="vm-ext-id-here" +) + +task_response = vg_api.detach_vm( + extId=vg_ext_id, + body=vm_detach +) +wait_for_task(prism_client, task_response.data.ext_id, timeout=60) +``` + +### 7. Attach an iSCSI Client + +```python +iscsi_attachment = vol_client.IscsiClient( + iscsi_initiator_name="iqn.2024-01.com.example:initiator1", + client_secret="chap-secret" +) + +task_response = vg_api.attach_iscsi_client( + extId=vg_ext_id, + body=iscsi_attachment +) +``` + +### 8. Attach an NVMe-TCP Client + +```python +nvmf_attachment = vol_client.NvmfClient( + host_nqn="nqn.2024-01.com.example:host1" +) + +task_response = vg_api.attach_nvmf_client( + extId=vg_ext_id, + body=nvmf_attachment +) +``` + +### 9. List VM Attachments + +```python +attachments = vg_api.list_vm_attachments_by_volume_group_id( + volumeGroupExtId=vg_ext_id +) +for att in attachments.data: + print(f"VM: {att.vm_ext_id}") +``` + +### 10. Get Volume Group Stats + +```python +stats = vg_api.get_volume_group_stats(extId=vg_ext_id) +``` + +### 11. Delete a Volume Group + +```python +# Get ETag for concurrency control +vg = vg_api.get_volume_group_by_id(extId=vg_ext_id) +etag = vg.headers.get("ETag") + +task_response = vg_api.delete_volume_group_by_id( + extId=vg_ext_id, + if_match=etag +) +``` + +### 12. Revert a Volume Group + +Revert to a previous state (from a recovery point). + +```python +task_response = vg_api.revert_volume_group(extId=vg_ext_id) +``` + +## iSCSI Client Management + +```python +iscsi_api = vol_client.IscsiClientsApi(api_client=api_client) + +# List all iSCSI clients +clients = iscsi_api.list_iscsi_clients() +for client in clients.data: + print(f"iSCSI Client: {client.iscsi_initiator_name}") + +# Get a specific client +client = iscsi_api.get_iscsi_client_by_id(extId=client_ext_id) + +# Update client +iscsi_api.update_iscsi_client_by_id( + extId=client_ext_id, + body=updated_client +) +``` + +## Key Models + +| Model | Props | Description | +|-------|-------|-------------| +| VolumeGroup | 21 | Top-level: name, sharing status, iSCSI features, storage config | +| IscsiClient | 11 | iSCSI initiator with name, network, authentication | +| VolumeDisk | 9 | Virtual disk within a volume group (size, storage container) | +| NvmfClient | 6 | NVMe-TCP client with host NQN | +| IscsiFeatures | - | Authentication type and target secret for iSCSI | +| VmAttachment | - | VM-to-volume-group attachment reference | + +## Common Mistakes + +1. **Not detaching VMs before deleting a volume group**: Volume groups with active attachments cannot be deleted. +2. **Missing ETag on updates/deletes**: Update and delete operations require the `If-Match` header. +3. **Disk size in bytes, not GB**: The `disk_size_bytes` field expects bytes (e.g., 107374182400 for 100 GB). +4. **Forgetting iSCSI authentication**: Production volume groups should always have CHAP authentication enabled. + +## Cross-Namespace Dependencies + +- **vmm**: VM ext_ids for attach/detach operations. +- **clustermgmt**: Storage container references for volume disk placement. +- **dataprotection**: Volume groups can be included in recovery points and consistency groups. +- **prism (TasksApi)**: Poll async task completion.