Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
315 changes: 311 additions & 4 deletions Powershell/Inventory.Collect.ps1

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Terraform/resource schema
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ resource: aetherv_virtual_machine
name: string, mutation forces replacement
host: string, optional, mutation forces replacement - needed if cluster not set
cluster: string, optional, mutation forces replacement - needed if host not set, enables failover
# Note: The 'cluster' parameter in TF resource creation specifies WHERE to place the VM (target cluster).
# The runtime 'clustered' status (whether VM has cluster protection) is read-only state data.
id: GUID - state only
-----------------------------------------------------------------------
cpu_cores: integer
Expand Down
87 changes: 44 additions & 43 deletions Terraform/vm properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,47 @@ Legacy UI is only documented in it's current state and is no longer being develo

| Property | Python Model | APIs | Inventory | Next UI |
|-------------------------------------------------|--------------|------|-----------|---------|
| name: string | x | x | | |
| host: string | x | x | | |
| state: enum | x | x | | |
| cluster: string | x | | | |
| id: Hyper-V ID | x | x | | |
| cpu_cores: integer | x | x | | |
| startup_memory_gb: double | x | x | | |
| dynamic_memory_memory_gb_min: double | x | x | | |
| dynamic_memory_memory_gb_max: double | x | x | | |
| dynamic_memory_memory_prcnt_buffer: integer | x | | | |
| secure_boot_enabled: bool | x | | | |
| secure_boot_template: string | x | | | |
| trusted_platform_module_enabled: bool | x | | | |
| tpm_key_protector: string | x | | | |
| primary_boot_device: string | x | | | |
| disk: object (0+ instances) | x | x | | |
| id: GUID - state only | x | x | | |
| size_gb: double | x | x | | |
| storage_class: related to host resources/path | x | x | | |
| path: string | x | x | | |
| network_if: object (0+ instances) | x | x | | |
| id: GUID - state only | x | x | | |
| adapter_name: string | x | x | | |
| network: string | x | x | | |
| virtual_switch: string | x | x | | |
| vlan_id: integer | x | x | | |
| mac_address: string | x | x | | |
| ip_addresses: string[] | x | x | | |
| dhcp_guard: bool | x | | | |
| router_guard: bool | x | | | |
| mac_spoof_guard: bool | x | | | |
| mac_address_config: "Dynamic" or "Static" | x | | | |
| min_bandwidth_mbps: integer | x | | | |
| max_bandwidth_mbps: integer | x | | | |
| operating_system: string | x | x | | |
| host_recovery_action: enum | x | | | |
| host_stop_action: enum | x | | | |
| integration_svcs_shutdown: bool | x | | | |
| integration_svcs_time: bool | x | | | |
| integration_svcs_data_exchange: bool | x | | | |
| integration_svcs_heartbeat: bool | x | | | |
| integration_svcs_vss_backup: bool | x | | | |
| integration_svcs_guest_services: bool | x | | | |
| name: string | x | x | x | |
| host: string | x | x | x | |
| state: enum | x | x | x | |
| clustered: bool | x | x | x | |
| cluster_name: string | x | x | x | |
| id: Hyper-V ID | x | x | x | |
| cpu_cores: integer | x | x | x | |
| startup_memory_gb: double | x | x | x | |
| dynamic_memory_memory_gb_min: double | x | x | x | |
| dynamic_memory_memory_gb_max: double | x | x | x | |
| dynamic_memory_memory_prcnt_buffer: integer | x | x | x | |
| secure_boot_enabled: bool | x | x | x | |
| secure_boot_template: string | x | x | x | |
| trusted_platform_module_enabled: bool | x | x | x | |
| key_protector_kind: string | x | x | x | |
| primary_boot_device: string | x | x | x | |
| disk: object (0+ instances) | x | x | x | |
| id: GUID - state only | x | x | x | |
| size_gb: double | x | x | x | |
| storage_class: related to host resources/path | x | x | x | |
| path: string | x | x | x | |
| network_if: object (0+ instances) | x | x | x | |
| id: GUID - state only | x | x | x | |
| adapter_name: string | x | x | x | |
| network: string | x | x | x | |
| virtual_switch: string | x | x | x | |
| vlan_id: integer | x | x | x | |
| mac_address: string | x | x | x | |
| ip_addresses: string[] | x | x | x | |
| dhcp_guard: bool | x | x | x | |
| router_guard: bool | x | x | x | |
| mac_spoof_guard: bool | x | x | x | |
| mac_address_config: "Dynamic" or "Static" | x | x | x | |
| min_bandwidth_mbps: integer | x | x | x | |
| max_bandwidth_mbps: integer | x | x | x | |
| operating_system: string | x | x | x | |
| host_recovery_action: enum | x | x | x | |
| host_stop_action: enum | x | x | x | |
| integration_svcs_shutdown: bool | x | x | x | |
| integration_svcs_time: bool | x | x | x | |
| integration_svcs_data_exchange: bool | x | x | x | |
| integration_svcs_heartbeat: bool | x | x | x | |
| integration_svcs_vss_backup: bool | x | x | x | |
| integration_svcs_guest_services: bool | x | x | x | |
6 changes: 2 additions & 4 deletions next-ui/src/lib/stores/inventoryStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,8 @@ export interface VM {
ip_addresses?: string[];
ip_address?: string; // Fallback for single IP
notes?: string | string[];
cluster?: string;
clustered?: boolean;
is_clustered?: boolean;
vm_clustered?: boolean;
clustered?: boolean; // Whether VM has failover cluster protection
cluster_name?: string; // Cluster name if VM is clustered
os_name?: string;
os_family?: string;
generation?: number | string;
Expand Down
32 changes: 12 additions & 20 deletions next-ui/src/lib/views/vm/VmView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -107,23 +107,14 @@
const clusterStatus = $derived(() => {
if (!vm) return { isClustered: false, clusterName: null };

const rawClustered = vm.clustered ?? vm.is_clustered ?? vm.vm_clustered;
const hostCluster = hostInfo?.cluster
? String(hostInfo.cluster).trim()
: "";

if (typeof rawClustered === "boolean") {
return {
isClustered: rawClustered,
clusterName: rawClustered ? hostCluster : null,
};
}

if (hostCluster) {
return { isClustered: true, clusterName: hostCluster };
}

return { isClustered: false, clusterName: null };
// Use the authoritative clustered boolean from inventory
const isClustered = vm.clustered ?? false;
const clusterName = isClustered ? (vm.cluster_name ?? null) : null;

return {
isClustered,
clusterName,
};
});

// Handle action button clicks
Expand Down Expand Up @@ -367,19 +358,20 @@
<span class="vm-overview-label">Clustered</span>
<span class="vm-overview-value">
{#if cluster.isClustered && cluster.clusterName}
{@const clusterName = cluster.clusterName}
Yes (<a
href="{base}/cluster/{encodeURIComponent(
cluster.clusterName,
clusterName,
)}"
class="vm-link"
onclick={(e) => {
e.preventDefault();
goto(
`${base}/cluster/${encodeURIComponent(cluster.clusterName)}`,
`${base}/cluster/${encodeURIComponent(clusterName)}`,
);
}}
>
{cluster.clusterName}
{clusterName}
</a>)
{:else}
{cluster.isClustered ? "Yes" : "No"}
Expand Down
3 changes: 2 additions & 1 deletion server/app/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ def _to_vm_list_item(vm: VM, cluster_by_host: Dict[str, Optional[str]]) -> VMLis
id=vm.id,
name=vm.name,
host=vm.host,
cluster=cluster_by_host.get(vm.host),
clustered=vm.clustered,
cluster_name=vm.cluster_name,
state=_normalize_vm_state(vm.state),
os_name=vm.os_name,
ip_address=ip_address,
Expand Down
8 changes: 5 additions & 3 deletions server/app/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ class VM(BaseModel):
id: Optional[str] = None
name: str
host: str
cluster: Optional[str] = None
clustered: Optional[bool] = None # Whether VM has failover cluster protection
cluster_name: Optional[str] = None # Cluster name if VM is clustered
state: VMState
cpu_cores: int = 0
memory_gb: float = 0.0
Expand All @@ -180,7 +181,7 @@ class VM(BaseModel):
secure_boot_enabled: Optional[bool] = None
secure_boot_template: Optional[str] = None
trusted_platform_module_enabled: Optional[bool] = None
tpm_key_protector: Optional[str] = None
key_protector_kind: Optional[str] = None # Values: 'none', 'host', 'host-guardian-service', 'unknown'
# Boot settings
primary_boot_device: Optional[str] = None
# Host actions
Expand All @@ -204,7 +205,8 @@ class VMListItem(BaseModel):
id: Optional[str] = None
name: str
host: str
cluster: Optional[str] = None
clustered: Optional[bool] = None # Whether VM has failover cluster protection
cluster_name: Optional[str] = None # Cluster name if VM is clustered
state: VMState
os_name: Optional[str] = None
ip_address: Optional[str] = None
Expand Down
Loading