diff --git a/docs/data-sources/host.md b/docs/data-sources/host.md new file mode 100644 index 0000000..15f89b0 --- /dev/null +++ b/docs/data-sources/host.md @@ -0,0 +1,141 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "vcf_host Data Source - terraform-provider-vcf" +subcategory: "" +description: |- + +--- + +# vcf_host (Data Source) + +The `vcf_host` data source provides information about an ESXi host in a VMware Cloud Foundation environment. + + +## Schema + +### Required + +- `fqdn` (String) The fully qualified domain name of the ESXi host. + +### Read-Only + +- `cluster` (List of Object) The cluster information of the ESXi host. (see [below for nested schema](#nestedatt--cluster)) +- `cpu` (List of Object) The CPU information of the ESXi host. (see [below for nested schema](#nestedatt--cpu)) +- `domain` (List of Object) The workload domain information of the ESXi host. (see [below for nested schema](#nestedatt--domain)) +- `hardware` (List of Object) The hardware information of the ESXi host. (see [below for nested schema](#nestedatt--hardware)) +- `id` (String) The ID of the ESXi host. +- `ip_addresses` (List of Object) The IP addresses information of the ESXi host. (see [below for nested schema](#nestedatt--ip_addresses)) +- `memory` (List of Object) The memory information of the ESXi host. (see [below for nested schema](#nestedatt--memory)) +- `network_pool` (List of Object) The network pool associated with the ESXi host. (see [below for nested schema](#nestedatt--network_pool)) +- `physical_nics` (List of Object) The physical NICs information of the ESXi host. (see [below for nested schema](#nestedatt--physical_nics)) +- `status` (String) The status of the ESXi host. +- `storage` (List of Object) The storage information of the ESXi host. (see [below for nested schema](#nestedatt--storage)) +- `storage_type` (String) The storage type of the ESXi host. +- `version` (String) The version of the ESXi running on the host. + + + +### Nested Schema for `cluster` + +Read-Only: + +- `id` (String) + + + +### Nested Schema for `cpu` + +Read-Only: + +- `cores` (Number) +- `cpu_cores` (List of Object) (see [below for nested schema](#nestedobjatt--cpu--cpu_cores)) +- `frequency_mhz` (Number) +- `used_frequency_mhz` (Number) + + + +### Nested Schema for `cpu.cpu_cores` + +Read-Only: + +- `frequency_mhz` (Number) +- `manufacturer` (String) +- `model` (String) + + + +### Nested Schema for `domain` + +Read-Only: + +- `id` (String) +- `name` (String) + + + +### Nested Schema for `hardware` + +Read-Only: + +- `hybrid` (Boolean) +- `model` (String) +- `vendor` (String) + + + +### Nested Schema for `ip_addresses` + +Read-Only: + +- `ip_address` (String) +- `type` (String) + + + +### Nested Schema for `memory` + +Read-Only: + +- `total_capacity_mb` (Number) +- `used_capacity_mb` (Number) + + + +### Nested Schema for `network_pool` + +Read-Only: + +- `id` (String) +- `name` (String) + + + +### Nested Schema for `physical_nics` + +Read-Only: + +- `device_name` (String) +- `mac_address` (String) +- `speed` (Number) +- `unit` (String) + + + +### Nested Schema for `storage` + +Read-Only: + +- `disks` (List of Object) (see [below for nested schema](#nestedobjatt--storage--disks)) +- `total_capacity_mb` (Number) +- `used_capacity_mb` (Number) + + + +### Nested Schema for `storage.disks` + +Read-Only: + +- `capacity_mb` (Number) +- `disk_type` (String) +- `manufacturer` (String) +- `model` (String) diff --git a/examples/data-sources/host/variables.tf b/examples/data-sources/host/variables.tf new file mode 100644 index 0000000..9e69411 --- /dev/null +++ b/examples/data-sources/host/variables.tf @@ -0,0 +1,22 @@ +variable "sddc_manager_host" { + type = string + description = "The fully qualified domain name of the SDDC Manager instance." +} + +variable "sddc_manager_username" { + type = string + description = "The username to authenticate to the SDDC Manager instance." + sensitive = true +} + +variable "sddc_manager_password" { + type = string + description = "The password to authenticate to the SDDC Manager instance." + sensitive = true +} + +variable "host_fqdn" { + type = string + description = "The fully qualified domain name of the ESXi host." + default = "sfo-w01-esx01.sfo.rainpole.io" +} diff --git a/examples/data-sources/host/vcf_host.tf b/examples/data-sources/host/vcf_host.tf new file mode 100644 index 0000000..b8f3132 --- /dev/null +++ b/examples/data-sources/host/vcf_host.tf @@ -0,0 +1,131 @@ +terraform { + required_providers { + vcf = { + source = "vmware/vcf" + } + } +} + +provider "vcf" { + sddc_manager_username = var.sddc_manager_username + sddc_manager_password = var.sddc_manager_password + sddc_manager_host = var.sddc_manager_host +} + +data "vcf_host" "example" { + fqdn = var.host_fqdn +} + +output "host_id" { + value = data.vcf_host.example.id +} + +output "host_fqdn" { + value = data.vcf_host.example.fqdn +} + +output "host_hardware" { + value = [ + for hw in data.vcf_host.example.hardware : { + hybrid = hw.hybrid + model = hw.model + vendor = hw.vendor + } + ] +} + +output "host_version" { + value = data.vcf_host.example.version +} + +output "host_status" { + value = data.vcf_host.example.status +} + +output "host_domain" { + value = [ + for domain in data.vcf_host.example.domain : { + id = domain.id + } + ] +} + +output "host_cluster" { + value = [ + for cluster in data.vcf_host.example.cluster : { + id = cluster.id + } + ] +} + +output "host_network_pool" { + value = [ + for pool in data.vcf_host.example.network_pool : { + id = pool.id + name = pool.name + } + ] +} + +output "host_cpu" { + value = [ + for cpu in coalesce(tolist(data.vcf_host.example.cpu), []) : { + cores = cpu.cores + frequency_mhz = cpu.frequency_mhz + used_frequency_mhz = cpu.used_frequency_mhz + cpu_cores = [ + for core in coalesce(tolist(cpu.cpu_cores), []) : { + frequency_mhz = core.frequency_mhz + manufacturer = core.manufacturer + model = core.model + } + ] + } + ] +} + +output "host_memory" { + value = [ + for mem in data.vcf_host.example.memory : { + total_capacity_mb = mem.total_capacity_mb + used_capacity_mb = mem.used_capacity_mb + } + ] +} + +output "host_storage" { + value = [ + for storage in coalesce(tolist(data.vcf_host.example.storage), []) : { + total_capacity_mb = storage.total_capacity_mb + used_capacity_mb = storage.used_capacity_mb + disks = [ + for disk in coalesce(tolist(storage.disks), []) : { + capacity_mb = disk.capacity_mb + disk_type = disk.disk_type + manufacturer = disk.manufacturer + model = disk.model + } + ] + } + ] +} + +output "host_physical_nics" { + value = [ + for nic in data.vcf_host.example.physical_nics : { + device_name = nic.device_name + mac_address = nic.mac_address + speed = nic.speed + unit = nic.unit + } + ] +} + +output "host_ip_addresses" { + value = [ + for ip in data.vcf_host.example.ip_addresses : { + ip_address = ip.ip_address + type = ip.type + } + ] +} diff --git a/internal/provider/data_host.go b/internal/provider/data_host.go new file mode 100644 index 0000000..933dfc6 --- /dev/null +++ b/internal/provider/data_host.go @@ -0,0 +1,455 @@ +// © Broadcom. All Rights Reserved. +// The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "context" + "errors" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/vmware/vcf-sdk-go/client" + "github.com/vmware/vcf-sdk-go/client/hosts" + "github.com/vmware/vcf-sdk-go/models" + + "github.com/vmware/terraform-provider-vcf/internal/api_client" + "github.com/vmware/terraform-provider-vcf/internal/constants" +) + +func DataSourceHost() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceHostRead, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The ID of the ESXi host.", + }, + "fqdn": { + Type: schema.TypeString, + Required: true, + Description: "The fully qualified domain name of the ESXi host.", + }, + "hardware": { + Type: schema.TypeList, + Computed: true, + Description: "The hardware information of the ESXi host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "model": { + Type: schema.TypeString, + Computed: true, + Description: "The hardware model of the ESXi host.", + }, + "vendor": { + Type: schema.TypeString, + Computed: true, + Description: "The hardware vendor of the ESXi host.", + }, + "hybrid": { + Type: schema.TypeBool, + Computed: true, + Description: "Indicates if the ESXi host is hybrid.", + }, + }, + }, + }, + "version": { + Type: schema.TypeString, + Computed: true, + Description: "The version of the ESXi running on the host.", + }, + "status": { + Type: schema.TypeString, + Computed: true, + Description: "The status of the ESXi host.", + }, + "domain": { + Type: schema.TypeList, + Computed: true, + Description: "The workload domain information of the ESXi host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The ID of the workload domain.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the workload domain.", + }, + }, + }, + }, + "cluster": { + Type: schema.TypeList, + Computed: true, + Description: "The cluster information of the ESXi host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The ID of the cluster.", + }, + }, + }, + }, + "network_pool": { + Type: schema.TypeList, + Computed: true, + Description: "The network pool associated with the ESXi host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the network pool.", + }, + "id": { + Type: schema.TypeString, + Computed: true, + Description: "The ID of the network pool.", + }, + }, + }, + }, + "cpu": { + Type: schema.TypeList, + Computed: true, + Description: "The CPU information of the ESXi host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cores": { + Type: schema.TypeInt, + Computed: true, + Description: "Number of CPU cores.", + }, + "cpu_cores": { + Type: schema.TypeList, + Computed: true, + Description: "Information about each of the CPU cores.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "frequency_mhz": { + Type: schema.TypeFloat, + Computed: true, + Description: "The frequency of the CPU core in MHz.", + }, + "manufacturer": { + Type: schema.TypeString, + Computed: true, + Description: "The manufacturer of the CPU.", + }, + "model": { + Type: schema.TypeString, + Computed: true, + Description: "The model of the CPU.", + }, + }, + }, + }, + "frequency_mhz": { + Type: schema.TypeFloat, + Computed: true, + Description: "Total CPU frequency in MHz.", + }, + "used_frequency_mhz": { + Type: schema.TypeFloat, + Computed: true, + Description: "Used CPU frequency in MHz.", + }, + }, + }, + }, + "memory": { + Type: schema.TypeList, + Computed: true, + Description: "The memory information of the ESXi host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "total_capacity_mb": { + Type: schema.TypeFloat, + Computed: true, + Description: "The total memory capacity in MB.", + }, + "used_capacity_mb": { + Type: schema.TypeFloat, + Computed: true, + Description: "The used memory capacity in MB.", + }, + }, + }, + }, + "storage_type": { + Type: schema.TypeString, + Computed: true, + Description: "The storage type of the ESXi host.", + }, + "storage": { + Type: schema.TypeList, + Computed: true, + Description: "The storage information of the ESXi host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "total_capacity_mb": { + Type: schema.TypeFloat, + Computed: true, + Description: "The total storage capacity in MB.", + }, + "used_capacity_mb": { + Type: schema.TypeFloat, + Computed: true, + Description: "The used storage capacity in MB.", + }, + "disks": { + Type: schema.TypeList, + Computed: true, + Description: "The disks information of the ESXi host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "capacity_mb": { + Type: schema.TypeFloat, + Computed: true, + Description: "The capacity of the disk in MB.", + }, + "disk_type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of the disk.", + }, + "manufacturer": { + Type: schema.TypeString, + Computed: true, + Description: "The manufacturer of the disk.", + }, + "model": { + Type: schema.TypeString, + Computed: true, + Description: "The model of the disk.", + }, + }, + }, + }, + }, + }, + }, + "physical_nics": { + Type: schema.TypeList, + Computed: true, + Description: "The physical NICs information of the ESXi host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "device_name": { + Type: schema.TypeString, + Computed: true, + Description: "The device name of the NIC.", + }, + "mac_address": { + Type: schema.TypeString, + Computed: true, + Description: "The MAC address of the NIC.", + }, + "speed": { + Type: schema.TypeFloat, + Computed: true, + Description: "The speed of the NIC.", + }, + "unit": { + Type: schema.TypeString, + Computed: true, + Description: "The unit of the NIC speed.", + }, + }, + }, + }, + "ip_addresses": { + Type: schema.TypeList, + Computed: true, + Description: "The IP addresses information of the ESXi host.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "ip_address": { + Type: schema.TypeString, + Computed: true, + Description: "The IP address.", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of the IP address.", + }, + }, + }, + }, + }, + } +} + +func dataSourceHostRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + apiClient := meta.(*api_client.SddcManagerClient).ApiClient + + fqdn := d.Get("fqdn").(string) + host, err := getHostByFqdn(ctx, apiClient, fqdn) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(host.ID) + + // Fully qualified domain name information. + _ = d.Set("fqdn", host.Fqdn) + + // Hardware information. + hardware := []map[string]interface{}{ + { + "model": host.HardwareModel, + "vendor": host.HardwareVendor, + "hybrid": host.Hybrid, + }, + } + _ = d.Set("hardware", hardware) + + // ESXi version information. + _ = d.Set("version", host.EsxiVersion) + + // Status information. + _ = d.Set("status", host.Status) + + // Domain information. + domain := []map[string]interface{}{ + { + "id": host.Domain.ID, + "name": host.Domain.Name, + }, + } + _ = d.Set("domain", domain) + + // Cluster information. + cluster := []map[string]interface{}{ + { + "id": host.Cluster.ID, + }, + } + _ = d.Set("cluster", cluster) + + // Network pool information. + networkPool := []map[string]interface{}{ + { + "id": host.Networkpool.ID, + "name": host.Networkpool.Name, + }, + } + _ = d.Set("network_pool", networkPool) + + // CPU information. + cpuCores := []map[string]interface{}{} + for _, core := range host.CPU.CPUCores { + cpuCore := map[string]interface{}{ + "frequency_mhz": core.FrequencyMHz, + "manufacturer": core.Manufacturer, + "model": core.Model, + } + cpuCores = append(cpuCores, cpuCore) + } + + cpu := map[string]interface{}{ + "cores": host.CPU.Cores, + "cpu_cores": cpuCores, + "frequency_mhz": host.CPU.FrequencyMHz, + "used_frequency_mhz": host.CPU.UsedFrequencyMHz, + } + + if err := d.Set("cpu", []interface{}{cpu}); err != nil { + return diag.FromErr(err) + } + + // Memory information. + memory := []map[string]interface{}{ + { + "total_capacity_mb": host.Memory.TotalCapacityMB, + "used_capacity_mb": host.Memory.UsedCapacityMB, + }, + } + _ = d.Set("memory", memory) + + // Compatible storage type information. + _ = d.Set("compatible_storage_type", host.CompatibleStorageType) + + // Storage information. + disks := []map[string]interface{}{} + for _, disk := range host.Storage.Disks { + diskInfo := map[string]interface{}{ + "capacity_mb": disk.CapacityMB, + "disk_type": disk.DiskType, + "manufacturer": disk.Manufacturer, + "model": disk.Model, + } + disks = append(disks, diskInfo) + } + + storage := []map[string]interface{}{ + { + "total_capacity_mb": host.Storage.TotalCapacityMB, + "used_capacity_mb": host.Storage.UsedCapacityMB, + "disks": disks, + }, + } + _ = d.Set("storage", storage) + + // Physical NICs information. + physicalNics := []map[string]interface{}{} + for _, nic := range host.PhysicalNics { + physicalNic := map[string]interface{}{ + "device_name": nic.DeviceName, + "mac_address": nic.MacAddress, + "speed": nic.Speed, + "unit": nic.Unit, + } + physicalNics = append(physicalNics, physicalNic) + } + _ = d.Set("physical_nics", physicalNics) + + // IP addresses information. + ipAddresses := []map[string]interface{}{} + for _, ip := range host.IPAddresses { + ipAddress := map[string]interface{}{ + "ip_address": ip.IPAddress, + "type": ip.Type, + } + ipAddresses = append(ipAddresses, ipAddress) + } + _ = d.Set("ip_addresses", ipAddresses) + + return nil +} + +func getHostByFqdn(ctx context.Context, apiClient *client.VcfClient, fqdn string) (*models.Host, error) { + params := hosts.NewGetHostsParamsWithContext(ctx). + WithTimeout(constants.DefaultVcfApiCallTimeout) + + hostPayload, err := apiClient.Hosts.GetHosts(params) + if err != nil { + return nil, err + } + + if hostPayload.Payload == nil || len(hostPayload.Payload.Elements) == 0 { + return nil, errors.New("no hosts found") + } + + for _, element := range hostPayload.Payload.Elements { + if element == nil { + continue + } + + if element.Fqdn == fqdn { + return element, nil + } + } + + return nil, errors.New("host not found") +} diff --git a/internal/provider/data_host_test.go b/internal/provider/data_host_test.go new file mode 100644 index 0000000..97bf334 --- /dev/null +++ b/internal/provider/data_host_test.go @@ -0,0 +1,56 @@ +// © Broadcom. All Rights Reserved. +// The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. +// SPDX-License-Identifier: MPL-2.0 + +package provider + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/vmware/terraform-provider-vcf/internal/constants" +) + +func TestAccDataSourceVcfHost(t *testing.T) { + hosts := []string{ + constants.VcfTestHost1Fqdn, + constants.VcfTestHost2Fqdn, + constants.VcfTestHost3Fqdn, + constants.VcfTestHost4Fqdn, + } + + var steps []resource.TestStep + for _, fqdn := range hosts { + steps = append(steps, resource.TestStep{ + Config: testAccDataSourceVcfHostConfig(fqdn), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.vcf_host.test_host", "id"), + resource.TestCheckResourceAttr("data.vcf_host.test_host", "fqdn", fqdn), + ), + }) + } + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: muxedFactories(), + Steps: steps, + }) +} + +func testAccDataSourceVcfHostConfig(hostFqdn string) string { + return fmt.Sprintf(` + resource "vcf_host" "test_host" { + fqdn = %q + username = "root" + password = "password" + network_pool_id = "test_network_pool_id" + storage_type = "VSAN" + } + + data "vcf_host" "test_host" { + fqdn = vcf_host.test_host.fqdn + } + `, hostFqdn) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 2633b4f..2967085 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -76,8 +76,9 @@ func Provider() *schema.Provider { DataSourcesMap: map[string]*schema.Resource{ "vcf_cluster": DataSourceCluster(), - "vcf_domain": DataSourceDomain(), "vcf_credentials": DataSourceCredentials(), + "vcf_domain": DataSourceDomain(), + "vcf_host": DataSourceHost(), "vcf_network_pool": DataSourceNetworkPool(), "vcf_certificate": DataSourceCertificate(), },