diff --git a/go.mod b/go.mod index de81d322f1..dca38c5847 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ toolchain go1.22.5 require ( github.com/IBM-Cloud/bluemix-go v0.0.0-20240719075425-078fcb3a55be - github.com/IBM-Cloud/container-services-go-sdk v0.0.0-20240725064144-454a2ae23113 - github.com/IBM-Cloud/power-go-client v1.7.0 + github.com/IBM-Cloud/container-services-go-sdk v0.0.0-20240216115622-a311507b4b5b + github.com/IBM-Cloud/power-go-client v1.8.1 github.com/IBM/apigateway-go-sdk v0.0.0-20210714141226-a5d5d49caaca github.com/IBM/appconfiguration-go-admin-sdk v0.3.0 github.com/IBM/appid-management-go-sdk v0.0.0-20210908164609-dd0e0eaf732f diff --git a/go.sum b/go.sum index 4b4ae2f1f9..4834028f2c 100644 --- a/go.sum +++ b/go.sum @@ -115,11 +115,11 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/zstd v1.4.4/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/IBM-Cloud/bluemix-go v0.0.0-20240719075425-078fcb3a55be h1:USOcBHkYQ4o/ccoEvoHinrba8NQthLJpFXnAoBY+MI4= github.com/IBM-Cloud/bluemix-go v0.0.0-20240719075425-078fcb3a55be/go.mod h1:/7hMjdZA6fEpd/dQAOEABxKEwN0t72P3PlpEDu0Y7bE= -github.com/IBM-Cloud/container-services-go-sdk v0.0.0-20240725064144-454a2ae23113 h1:f2Erqfea1dKpaTFagTJM6W/wnD3JGq/Vn9URh8nuRwk= -github.com/IBM-Cloud/container-services-go-sdk v0.0.0-20240725064144-454a2ae23113/go.mod h1:xUQL9SGAjoZFd4GNjrjjtEpjpkgU7RFXRyHesbKTjiY= +github.com/IBM-Cloud/container-services-go-sdk v0.0.0-20240216115622-a311507b4b5b h1:Wnq0BuprazpP41+nQlRpxpmAs8+8jyOqU50KrvFdJQ4= +github.com/IBM-Cloud/container-services-go-sdk v0.0.0-20240216115622-a311507b4b5b/go.mod h1:xUQL9SGAjoZFd4GNjrjjtEpjpkgU7RFXRyHesbKTjiY= github.com/IBM-Cloud/ibm-cloud-cli-sdk v0.5.3/go.mod h1:RiUvKuHKTBmBApDMUQzBL14pQUGKcx/IioKQPIcRQjs= -github.com/IBM-Cloud/power-go-client v1.7.0 h1:/GuGwPMTKoCZACfnwt7b6wKr4v32q1VO1AMFGNETRN4= -github.com/IBM-Cloud/power-go-client v1.7.0/go.mod h1:9izycYAmNQ+NAdVPXDC3fHYxqWLjlR2YiwqKYveMv5Y= +github.com/IBM-Cloud/power-go-client v1.8.1 h1:tx1aPJmIQrNru1MD1VHGNasGx3eRIs0zzPZ0KvdFQrg= +github.com/IBM-Cloud/power-go-client v1.8.1/go.mod h1:N4RxrsMUvBQjSQ/qPk0iMZ8zK+fZPRTnHi/gTaASw0g= github.com/IBM-Cloud/softlayer-go v1.0.5-tf h1:koUAyF9b6X78lLLruGYPSOmrfY2YcGYKOj/Ug9nbKNw= github.com/IBM-Cloud/softlayer-go v1.0.5-tf/go.mod h1:6HepcfAXROz0Rf63krk5hPZyHT6qyx2MNvYyHof7ik4= github.com/IBM/apigateway-go-sdk v0.0.0-20210714141226-a5d5d49caaca h1:crniVcf+YcmgF03NmmfonXwSQ73oJF+IohFYBwknMxs= @@ -138,8 +138,6 @@ github.com/IBM/container-registry-go-sdk v1.1.0 h1:sYyknIod8R4RJZQqAheiduP6wbSTp github.com/IBM/container-registry-go-sdk v1.1.0/go.mod h1:4TwsCnQtVfZ4Vkapy/KPvQBKFc3VOyUZYkwRU4FTPrs= github.com/IBM/continuous-delivery-go-sdk v1.6.0 h1:eAL/jIWHrDFlWDF+Qd9Y5UN99Pr5Mjd/H/bvTbXUbz4= github.com/IBM/continuous-delivery-go-sdk v1.6.0/go.mod h1:nZdKUnubXNLo+zo28R4Rd+TGDqiJ/xoE8WO/A3kLw1E= -github.com/IBM/event-notifications-go-admin-sdk v0.8.0 h1:xk2CYTayQtKi6LSgGGFRxFJfWUxyM5SY8Rs64ducAhw= -github.com/IBM/event-notifications-go-admin-sdk v0.8.0/go.mod h1:OByvqfrNVxs7G6ggv8pwQCEVw10/TBJCLh7NM3z707w= github.com/IBM/event-notifications-go-admin-sdk v0.9.0 h1:eaCd+GkxhNyot+8rA9WkAQdlVYrRD20LYiXjEytFO6M= github.com/IBM/event-notifications-go-admin-sdk v0.9.0/go.mod h1:OByvqfrNVxs7G6ggv8pwQCEVw10/TBJCLh7NM3z707w= github.com/IBM/eventstreams-go-sdk v1.4.0 h1:yS/Ns29sBOe8W2tynQmz9HTKqQZ0ckse4Py5Oy/F2rM= @@ -187,8 +185,6 @@ github.com/IBM/scc-go-sdk/v5 v5.4.1 h1:RXIuxOo9/hxkWyHCI69ae+KIJgSbXcAkJwTEl+fO3 github.com/IBM/scc-go-sdk/v5 v5.4.1/go.mod h1:2xQTDgNXG5QMEfQxBDKB067z+5ha6OgcaKCTcdGDAo8= github.com/IBM/schematics-go-sdk v0.2.3 h1:lgTt0Sbudii3cuSk1YSQgrtiZAXDbBABAoVj3eQuBrU= github.com/IBM/schematics-go-sdk v0.2.3/go.mod h1:Tw2OSAPdpC69AxcwoyqcYYaGTTW6YpERF9uNEU+BFRQ= -github.com/IBM/secrets-manager-go-sdk/v2 v2.0.5 h1:VMc/Zd6RzB8j60CqZekkwYT2wQsCfrkGV2n01Gviuaw= -github.com/IBM/secrets-manager-go-sdk/v2 v2.0.5/go.mod h1:5kUgJ1dG9cdiAcPDqVz46m362bPnoqZQSth24NiowSg= github.com/IBM/secrets-manager-go-sdk/v2 v2.0.6 h1:bF6bAdI4wDZSje6+Yx1mJxvirboxO+uMuKhzgfRCNxE= github.com/IBM/secrets-manager-go-sdk/v2 v2.0.6/go.mod h1:XWYnbcc5vN1RnKwk/fCzfD8aZd7At/Y1/b6c+oDyliU= github.com/IBM/vmware-go-sdk v0.1.2 h1:5lKWFyInWz9e2hwGsoFTEoLa1jYkD30SReN0fQ10w9M= diff --git a/ibm/acctest/acctest.go b/ibm/acctest/acctest.go index f04f347419..27420cf502 100644 --- a/ibm/acctest/acctest.go +++ b/ibm/acctest/acctest.go @@ -236,6 +236,11 @@ var ( PiSAPProfileID string PiStoragePool string PiStorageType string + Pi_shared_processor_pool_id string + Pi_target_storage_tier string + Pi_volume_clone_task_id string + Pi_resource_group_id string + Pi_instance_id string ) var ( @@ -1128,6 +1133,11 @@ func init() { fmt.Println("[INFO] Set the environment variable PI_PVM_INSTANCE_ID for testing Pi_instance_name resource else it is set to default value 'terraform-test-power'") } + Pi_instance_id = os.Getenv("PI_PVM_INSTANCE_ID") + if Pi_instance_id == "" { + fmt.Println("[INFO] Set the environment variable PI_PVM_INSTANCE_ID for testing resource else it is not set") + } + Pi_dhcp_id = os.Getenv("PI_DHCP_ID") if Pi_dhcp_id == "" { Pi_dhcp_id = "terraform-test-power" diff --git a/ibm/provider/provider.go b/ibm/provider/provider.go index 2d94cf08f0..d178840f19 100644 --- a/ibm/provider/provider.go +++ b/ibm/provider/provider.go @@ -1310,6 +1310,8 @@ func Provider() *schema.Provider { "ibm_pi_volume_group": power.ResourceIBMPIVolumeGroup(), "ibm_pi_volume_onboarding": power.ResourceIBMPIVolumeOnboarding(), "ibm_pi_volume": power.ResourceIBMPIVolume(), + "ibm_pi_volumes_attach": power.ResourceIBMPIVolumesAttach(), + "ibm_pi_volumes_detach": power.ResourceIBMPIVolumesDetach(), "ibm_pi_vpn_connection": power.ResourceIBMPIVPNConnection(), "ibm_pi_workspace": power.ResourceIBMPIWorkspace(), diff --git a/ibm/service/power/ibm_pi_constants.go b/ibm/service/power/ibm_pi_constants.go index 6f3fff8509..4638b8caed 100644 --- a/ibm/service/power/ibm_pi_constants.go +++ b/ibm/service/power/ibm_pi_constants.go @@ -10,6 +10,7 @@ const ( Arg_AffinityVolume = "pi_affinity_volume" Arg_AntiAffinityInstances = "pi_anti_affinity_instances" Arg_AntiAffinityVolumes = "pi_anti_affinity_volumes" + Arg_BootVolumeID = "pi_boot_volume_id" Arg_Cidr = "pi_cidr" Arg_CloudConnectionID = "pi_cloud_connection_id" Arg_CloudConnectionName = "pi_cloud_connection_name" @@ -20,6 +21,11 @@ const ( Arg_DeploymentTarget = "pi_deployment_target" Arg_DeploymentType = "pi_deployment_type" Arg_Description = "pi_description" + Arg_DetachAllVolumes = "pi_detach_all_volumes" + Arg_DetachPrimaryBootVolume = "pi_detach_primary_boot_volume" + Arg_DhcpCidr = "pi_cidr" + Arg_DhcpCloudConnectionID = "pi_cloud_connection_id" + Arg_DhcpDnsServer = "pi_dns_server" Arg_DhcpID = "pi_dhcp_id" Arg_DhcpName = "pi_dhcp_name" Arg_DhcpSnatEnabled = "pi_dhcp_snat_enabled" diff --git a/ibm/service/power/resource_ibm_pi_volumes_attach.go b/ibm/service/power/resource_ibm_pi_volumes_attach.go new file mode 100644 index 0000000000..5a36c07a2d --- /dev/null +++ b/ibm/service/power/resource_ibm_pi_volumes_attach.go @@ -0,0 +1,139 @@ +// Copyright IBM Corp. 2024 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package power + +import ( + "context" + "fmt" + "log" + "strings" + "time" + + st "github.com/IBM-Cloud/power-go-client/clients/instance" + "github.com/IBM-Cloud/power-go-client/power/models" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceIBMPIVolumesAttach() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceIBMPIVolumesAttachCreate, + ReadContext: resourceIBMPIVolumesAttachRead, + DeleteContext: resourceIBMPIVolumesAttachDelete, + Importer: &schema.ResourceImporter{}, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + // Arguments + Arg_CloudInstanceID: { + Description: "The GUID of the service instance associated with an account.", + ForceNew: true, + Required: true, + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + Arg_InstanceID: { + Description: "The unique identifier of the instance.", + ForceNew: true, + Required: true, + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + Arg_VolumeIDs: { + Description: "List of volumes to be detached from a pi_instance; required if detachAllVolumes is not provided.", + Elem: &schema.Schema{Type: schema.TypeString}, + ForceNew: true, + Required: true, + Type: schema.TypeList, + }, + Arg_BootVolumeID: { + Description: "Primary Boot Volume Id.", + ForceNew: true, + Optional: true, + Type: schema.TypeString, + }, + }, + } +} + +func resourceIBMPIVolumesAttachCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + sess, err := meta.(conns.ClientSession).IBMPISession() + if err != nil { + return diag.FromErr(err) + } + + body := &models.VolumesAttach{} + pvmInstanceID := d.Get(Arg_InstanceID).(string) + cloudInstanceID := d.Get(Arg_CloudInstanceID).(string) + + var volumeIDs []string + if _, ok := d.GetOk(Arg_VolumeIDs); ok { + for _, v := range d.Get(Arg_VolumeIDs).([]interface{}) { + volumeIDsItem := v.(string) + volumeIDs = append(volumeIDs, volumeIDsItem) + } + body.VolumeIDs = volumeIDs + } + + if _, ok := d.GetOk(Arg_BootVolumeID); ok { + body.BootVolumeID = d.Get(Arg_BootVolumeID).(string) + } + volClient := st.NewIBMPIVolumeClient(ctx, sess, cloudInstanceID) + volinfo, err := volClient.BulkVolumeAttach(pvmInstanceID, body) + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] Volumes attach accepted: %s", *volinfo.Summary) + + d.SetId(fmt.Sprintf("%s/%s/%s", cloudInstanceID, pvmInstanceID, strings.Join(volumeIDs, "/"))) + for _, volumeID := range volumeIDs { + _, err = isWaitForIBMPIVolumeAttachAvailable(ctx, volClient, volumeID, cloudInstanceID, pvmInstanceID, d.Timeout(schema.TimeoutCreate)) + if err != nil { + return diag.FromErr(err) + } + } + return resourceIBMPIVolumesAttachRead(ctx, d, meta) +} + +func resourceIBMPIVolumesAttachRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return nil +} + +func resourceIBMPIVolumesAttachDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + sess, err := meta.(conns.ClientSession).IBMPISession() + if err != nil { + return diag.FromErr(err) + } + ids, err := flex.IdParts(d.Id()) + if err != nil { + return diag.FromErr(err) + } + cloudInstanceID, pvmInstanceID := ids[0], ids[1] + volClient := st.NewIBMPIVolumeClient(ctx, sess, cloudInstanceID) + body := &models.VolumesDetach{ + VolumeIDs: ids[2:], + } + volinfo, err := volClient.BulkVolumeDetach(pvmInstanceID, body) + if err != nil { + return diag.FromErr(err) + } + log.Printf("[DEBUG] Volumes delete accepted: %s", *volinfo.Summary) + for _, volumeID := range ids[2:] { + _, err = isWaitForIBMPIVolumeDetach(ctx, volClient, volumeID, cloudInstanceID, pvmInstanceID, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return diag.FromErr(err) + } + } + + d.SetId("") + return nil +} diff --git a/ibm/service/power/resource_ibm_pi_volumes_attach_test.go b/ibm/service/power/resource_ibm_pi_volumes_attach_test.go new file mode 100644 index 0000000000..3f82d66971 --- /dev/null +++ b/ibm/service/power/resource_ibm_pi_volumes_attach_test.go @@ -0,0 +1,170 @@ +// Copyright IBM Corp. 2024 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package power_test + +import ( + "context" + "errors" + "fmt" + "log" + "testing" + + "github.com/IBM-Cloud/power-go-client/clients/instance" + acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccIBMPIVolumesAttach(t *testing.T) { + name := fmt.Sprintf("tf-pi-instance-%d", acctest.RandIntRange(10, 100)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckVolumesAttachDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMPIVolumesAttachConfig(name, "WARNING"), + Check: resource.ComposeTestCheckFunc( + testAccCheckIBMPIVolumesAttachExists("ibm_pi_volumes_attach.pi_volumes_attach_instance"), + resource.TestCheckResourceAttrSet("ibm_pi_volumes_attach.pi_volumes_attach_instance", "id"), + ), + }, + }, + }) +} + +func testAccCheckIBMPIVolumesAttachConfig(name, instanceHealthStatus string) string { + return fmt.Sprintf(` + resource "ibm_pi_key" "key" { + pi_cloud_instance_id = "%[1]s" + pi_key_name = "%[2]s" + pi_ssh_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR" + } + data "ibm_pi_image" "power_image" { + pi_cloud_instance_id = "%[1]s" + pi_image_name = "%[3]s" + } + data "ibm_pi_network" "power_networks" { + pi_cloud_instance_id = "%[1]s" + pi_network_name = "%[4]s" + } + resource "ibm_pi_volume" "power_volume" { + pi_cloud_instance_id = "%[1]s" + pi_volume_name = "%[2]s" + pi_volume_pool = data.ibm_pi_image.power_image.storage_pool + pi_volume_shareable = true + pi_volume_size = 20 + pi_volume_type = "%[6]s" + } + resource "ibm_pi_volume" "power_volume_2" { + pi_cloud_instance_id = "%[1]s" + pi_volume_name = "%[2]s-2" + pi_volume_pool = data.ibm_pi_image.power_image.storage_pool + pi_volume_shareable = true + pi_volume_size = 20 + pi_volume_type = "%[6]s" + } + resource "ibm_pi_volume" "power_volume_3" { + pi_cloud_instance_id = "%[1]s" + pi_volume_name = "%[2]s-3" + pi_volume_pool = data.ibm_pi_image.power_image.storage_pool + pi_volume_shareable = true + pi_volume_size = 20 + pi_volume_type = "%[6]s" + } + resource "ibm_pi_instance" "power_instance" { + pi_cloud_instance_id = "%[1]s" + pi_health_status = "%[5]s" + pi_image_id = data.ibm_pi_image.power_image.id + pi_instance_name = "%[2]s" + pi_key_pair_name = ibm_pi_key.key.name + pi_memory = "2" + pi_proc_type = "shared" + pi_processors = "0.25" + pi_storage_pool = data.ibm_pi_image.power_image.storage_pool + pi_storage_type = "%[6]s" + pi_sys_type = "s922" + pi_volume_ids = [ibm_pi_volume.power_volume.volume_id] + pi_network { + network_id = data.ibm_pi_network.power_networks.id + } + } + data "ibm_pi_instance" "power_instance" { + pi_cloud_instance_id = "%[1]s" + pi_instance_name = resource.ibm_pi_instance.power_instance.pi_instance_name + } + resource "ibm_pi_volumes_attach" "pi_volumes_attach_instance" { + pi_cloud_instance_id = "%[1]s" + pi_instance_id = data.ibm_pi_instance.power_instance.id + pi_volume_ids = [ibm_pi_volume.power_volume_2.volume_id, ibm_pi_volume.power_volume_3.volume_id] + } + `, acc.Pi_cloud_instance_id, name, acc.Pi_image, acc.Pi_network_name, instanceHealthStatus, acc.PiStorageType) +} + +func testAccCheckVolumesAttachDestroy(s *terraform.State) error { + sess, err := acc.TestAccProvider.Meta().(conns.ClientSession).IBMPISession() + if err != nil { + return err + } + for _, rs := range s.RootModule().Resources { + if rs.Type != "ibm_pi_volumes_attach" { + continue + } + + ids, err := flex.IdParts(rs.Primary.ID) + if err != nil { + return err + } + cloudInstanceID, pvmInstanceID := ids[0], ids[1] + client := instance.NewIBMPIVolumeClient(context.Background(), sess, cloudInstanceID) + for _, volumeID := range ids[2:] { + volumeAttach, err := client.CheckVolumeAttach(pvmInstanceID, volumeID) + if err == nil { + log.Println("volume attach*****", volumeAttach.State) + return fmt.Errorf("PI Volume Attach still exists: %s", rs.Primary.ID) + } + } + + } + + return nil +} + +func testAccCheckIBMPIVolumesAttachExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + + rs, ok := s.RootModule().Resources[n] + + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return errors.New("No Record ID is set") + } + + sess, err := acc.TestAccProvider.Meta().(conns.ClientSession).IBMPISession() + if err != nil { + return err + } + + ids, err := flex.IdParts(rs.Primary.ID) + if err != nil { + return err + } + cloudInstanceID, pvmInstanceID := ids[0], ids[1] + client := instance.NewIBMPIVolumeClient(context.Background(), sess, cloudInstanceID) + for _, volumeID := range ids[2:] { + _, err = client.CheckVolumeAttach(pvmInstanceID, volumeID) + if err != nil { + return err + } + } + + return nil + } +} diff --git a/ibm/service/power/resource_ibm_pi_volumes_detach.go b/ibm/service/power/resource_ibm_pi_volumes_detach.go new file mode 100644 index 0000000000..b9f3b65441 --- /dev/null +++ b/ibm/service/power/resource_ibm_pi_volumes_detach.go @@ -0,0 +1,124 @@ +// Copyright IBM Corp. 2024 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package power + +import ( + "context" + "fmt" + "log" + "time" + + st "github.com/IBM-Cloud/power-go-client/clients/instance" + "github.com/IBM-Cloud/power-go-client/power/models" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceIBMPIVolumesDetach() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceIBMPIVolumesDetachCreate, + ReadContext: resourceIBMPIVolumesDetachRead, + DeleteContext: resourceIBMPIVolumesDetachDelete, + Importer: &schema.ResourceImporter{}, + + Timeouts: &schema.ResourceTimeout{ + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + // Arguments + Arg_CloudInstanceID: { + Description: "The GUID of the service instance associated with an account.", + ForceNew: true, + Required: true, + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + Arg_InstanceID: { + Description: "The unique identifier of the instance.", + ForceNew: true, + Required: true, + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + Arg_DetachAllVolumes: { + ConflictsWith: []string{Arg_VolumeIDs}, + Description: "Indicates if all volumes, except primary boot volume, attached to the pi_instance should be detached (default=false); required if volumeIDs is not provided.", + ForceNew: true, + Optional: true, + Type: schema.TypeBool, + }, + Arg_DetachPrimaryBootVolume: { + Description: "Indicates if primary boot volume attached to the pi_instance should be detached (default=false).", + ForceNew: true, + Optional: true, + Type: schema.TypeBool, + }, + Arg_VolumeIDs: { + ConflictsWith: []string{Arg_DetachAllVolumes}, + Description: "List of volumes to be detached from a pi_instance; required if detachAllVolumes is not provided.", + Elem: &schema.Schema{Type: schema.TypeString}, + ForceNew: true, + Optional: true, + Type: schema.TypeList, + }, + }, + } +} + +func resourceIBMPIVolumesDetachCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + sess, err := meta.(conns.ClientSession).IBMPISession() + if err != nil { + return diag.FromErr(err) + } + + pvmInstanceID := d.Get(Arg_InstanceID).(string) + cloudInstanceID := d.Get(Arg_CloudInstanceID).(string) + body := &models.VolumesDetach{} + var volumeIDs []string + if _, ok := d.GetOk(Arg_VolumeIDs); ok { + for _, v := range d.Get(Arg_VolumeIDs).([]interface{}) { + volumeIDsItem := v.(string) + volumeIDs = append(volumeIDs, volumeIDsItem) + } + body.VolumeIDs = volumeIDs + } + + if _, ok := d.GetOk(Arg_DetachAllVolumes); ok { + body.DetachAllVolumes = flex.PtrToBool(d.Get(Arg_DetachAllVolumes).(bool)) + } + if _, ok := d.GetOk(Arg_DetachPrimaryBootVolume); ok { + body.DetachPrimaryBootVolume = flex.PtrToBool(d.Get(Arg_DetachPrimaryBootVolume).(bool)) + } + + volClient := st.NewIBMPIVolumeClient(ctx, sess, cloudInstanceID) + volinfo, err := volClient.BulkVolumeDetach(pvmInstanceID, body) + if err != nil { + return diag.FromErr(err) + } + + log.Printf("[DEBUG] Volumes detach accepted: %s", *volinfo.Summary) + for _, volumeID := range volumeIDs { + _, err = isWaitForIBMPIVolumeDetach(ctx, volClient, volumeID, cloudInstanceID, pvmInstanceID, d.Timeout(schema.TimeoutDelete)) + if err != nil { + return diag.FromErr(err) + } + } + + d.SetId(fmt.Sprintf("%s/%s", cloudInstanceID, pvmInstanceID)) + + return resourceIBMPIVolumesAttachRead(ctx, d, meta) +} + +func resourceIBMPIVolumesDetachRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + return nil +} + +func resourceIBMPIVolumesDetachDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + d.SetId("") + return nil +} diff --git a/ibm/service/power/resource_ibm_pi_volumes_detach_test.go b/ibm/service/power/resource_ibm_pi_volumes_detach_test.go new file mode 100644 index 0000000000..770a0d662c --- /dev/null +++ b/ibm/service/power/resource_ibm_pi_volumes_detach_test.go @@ -0,0 +1,131 @@ +// Copyright IBM Corp. 2024 All Rights Reserved. +// Licensed under the Mozilla Public License v2.0 + +package power_test + +import ( + "context" + "fmt" + "testing" + + "github.com/IBM-Cloud/power-go-client/clients/instance" + acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccIBMPIVolumesDetach(t *testing.T) { + name := fmt.Sprintf("tf-pi-instance-%d", acctest.RandIntRange(10, 100)) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMPIVolumesDetachDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMPIVolumesDetachAllVolumesConfig(name, "WARNING"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("ibm_pi_volumes_detach.pi_volumes_detach_instance", "id"), + ), + }, + }, + }) +} + +func testAccCheckIBMPIVolumesDetachAllVolumesConfig(name, instanceHealthStatus string) string { + return fmt.Sprintf(` + resource "ibm_pi_key" "key" { + pi_cloud_instance_id = "%[1]s" + pi_key_name = "%[2]s" + pi_ssh_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR" + } + data "ibm_pi_image" "power_image" { + pi_cloud_instance_id = "%[1]s" + pi_image_name = "%[3]s" + } + data "ibm_pi_network" "power_networks" { + pi_cloud_instance_id = "%[1]s" + pi_network_name = "%[4]s" + } + resource "ibm_pi_volume" "power_volume" { + pi_cloud_instance_id = "%[1]s" + pi_volume_name = "%[2]s" + pi_volume_pool = data.ibm_pi_image.power_image.storage_pool + pi_volume_shareable = true + pi_volume_size = 20 + pi_volume_type = "%[6]s" + } + resource "ibm_pi_volume" "power_volume_2" { + pi_cloud_instance_id = "%[1]s" + pi_volume_name = "%[2]s-2" + pi_volume_pool = data.ibm_pi_image.power_image.storage_pool + pi_volume_shareable = true + pi_volume_size = 20 + pi_volume_type = "%[6]s" + } + resource "ibm_pi_volume" "power_volume_3" { + pi_cloud_instance_id = "%[1]s" + pi_volume_name = "%[2]s-3" + pi_volume_pool = data.ibm_pi_image.power_image.storage_pool + pi_volume_shareable = true + pi_volume_size = 20 + pi_volume_type = "%[6]s" + } + resource "ibm_pi_instance" "power_instance" { + pi_cloud_instance_id = "%[1]s" + pi_health_status = "%[5]s" + pi_image_id = data.ibm_pi_image.power_image.id + pi_instance_name = "%[2]s" + pi_key_pair_name = ibm_pi_key.key.name + pi_memory = "2" + pi_proc_type = "shared" + pi_processors = "0.25" + pi_storage_pool = data.ibm_pi_image.power_image.storage_pool + pi_storage_type = "%[6]s" + pi_sys_type = "s922" + pi_volume_ids = [ibm_pi_volume.power_volume.volume_id, ibm_pi_volume.power_volume_2.volume_id, ibm_pi_volume.power_volume_3.volume_id] + pi_network { + network_id = data.ibm_pi_network.power_networks.id + } + } + data "ibm_pi_instance" "power_instance" { + pi_cloud_instance_id = "%[1]s" + pi_instance_name = resource.ibm_pi_instance.power_instance.pi_instance_name + } + resource "ibm_pi_volumes_detach" "pi_volumes_detach_instance" { + pi_cloud_instance_id = "%[1]s" + pi_detach_all_volumes = true + pi_instance_id = data.ibm_pi_instance.power_instance.id + } + `, acc.Pi_cloud_instance_id, name, acc.Pi_image, acc.Pi_network_name, instanceHealthStatus, acc.PiStorageType) +} + +func testAccCheckIBMPIVolumesDetachDestroy(s *terraform.State) error { + sess, err := acc.TestAccProvider.Meta().(conns.ClientSession).IBMPISession() + if err != nil { + return err + } + for _, rs := range s.RootModule().Resources { + if rs.Type != "ibm_pi_volume_attach" { + continue + } + + ids, err := flex.IdParts(rs.Primary.ID) + if err != nil { + return err + } + cloudInstanceID, pvmInstanceID := ids[0], ids[1] + client := instance.NewIBMPIInstanceClient(context.Background(), sess, cloudInstanceID) + pvm, err := client.Get(pvmInstanceID) + if err == nil { + if len(pvm.VolumeIDs) > 1 { + return fmt.Errorf("PI Volumes Attach still exists: %s", rs.Primary.ID) + } + + } + } + + return nil +} diff --git a/website/docs/r/pi_instance.html.markdown b/website/docs/r/pi_instance.html.markdown index efc06f6aab..13eeec0cad 100644 --- a/website/docs/r/pi_instance.html.markdown +++ b/website/docs/r/pi_instance.html.markdown @@ -111,7 +111,7 @@ Review the argument references that you can specify for your resource. - `pi_sap_deployment_type` - (Optional, String) Custom SAP deployment type information (For Internal Use Only). - `pi_shared_processor_pool` - (Optional, String) The shared processor pool for instance deployment. Conflicts with `pi_sap_profile_id`. - `pi_storage_pool` - (Optional, String) Storage Pool for server deployment; if provided then `pi_affinity_policy` will be ignored; Only valid when you deploy one of the IBM supplied stock images. Storage pool for a custom image (an imported image or an image that is created from a VM capture) defaults to the storage pool the image was created in. -- `pi_storage_pool_affinity` - (Optional, Boolean) Indicates if all volumes attached to the server must reside in the same storage pool. The default value is `true`. To attach data volumes from a different storage pool (mixed storage) set to `false` and use `pi_volume_attach` resource. Once set to `false`, cannot be set back to `true` unless all volumes attached reside in the same storage type and pool. +- `pi_storage_pool_affinity` - (Optional, Boolean) Indicates if all volumes attached to the server must reside in the same storage pool. The default value is `true`. To attach data volumes from a different storage pool (mixed storage) set to `false` and use `ibm_pi_volume_attach` or `ibm_pi_volumes_attach` resource. Once set to `false`, cannot be set back to `true` unless all volumes attached reside in the same storage type and pool. - `pi_storage_type` - (Optional, String) - Storage type for server deployment; If storage type is not provided the storage type will default to `tier3`. To get a list of available storage types, please use the [ibm_pi_storage_types_capacity](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/data-sources/pi_storage_types_capacity) data source. - `pi_storage_connection` - (Optional, String) - Storage Connectivity Group (SCG) for server deployment. Only supported value is `vSCSI`. - `pi_sys_type` - (Optional, String) The type of system on which to create the VM (e880/e980/e1080/s922/s1022). diff --git a/website/docs/r/pi_volumes_attach.html.markdown b/website/docs/r/pi_volumes_attach.html.markdown new file mode 100644 index 0000000000..a9331ca691 --- /dev/null +++ b/website/docs/r/pi_volumes_attach.html.markdown @@ -0,0 +1,61 @@ +--- +subcategory: "Power Systems" +layout: "ibm" +page_title: "IBM : ibm_pi_volumes_attach" +description: |- + Manages volumes in Power Virtual Server cloud. +--- + +# ibm_pi_volumes_attach + +Attach volumes to a Power Systems Virtual Server instance, see [volumes](https://cloud.ibm.com/apidocs/power-cloud#pcloud-v2-pvminstances-volumes-post) + +## Example Usage + +The following example attach volumes to power virtual instance. + +```terraform +resource "ibm_pi_volumes_attach" "pi_volumes_attach_instance" { + pi_cloud_instance_id = "" + pi_instance_id = "" + pi_volume_ids = ["volume ids"] +} +``` + +### Notes + +* Please find [supported Regions](https://cloud.ibm.com/apidocs/power-cloud#endpoint) for endpoints. +* If a Power cloud instance is provisioned at `lon04`, The provider level attributes should be as follows: + * `region` - `lon` + * `zone` - `lon04` + +Example usage: + + ```terraform + provider "ibm" { + region = "lon" + zone = "lon04" + } + ``` + +## Timeouts + +`pi_volumes_attach` provides the following [Timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) configuration options: + +* `create` - (Default 10 minutes) Used for attaching volumes to a power virtual server instance. +* `delete` - (Default 10 minutes) Used for detaching volumes to a power virtual server instance. + +## Argument Reference + +You can specify the following arguments for this resource. + +* `pi_boot_volume_id` - (Optional, String) Primary Boot Volume ID. +* `pi_cloud_instance_id` - (Required, String) The GUID of the service instance associated with an account. +* `pi_instance_id` - (Required, String) The unique identifier of an instance. +* `pi_volume_ids` - (Required, List) List of volumes to be attached to a `pi_instance`. + +## Attribute Reference + +After your resource is created, you can read values from the listed arguments and the following attributes. + +* `id` - The unique identifier of volumes attach. The ID is composed of `//` diff --git a/website/docs/r/pi_volumes_detach.hmtl.markdown b/website/docs/r/pi_volumes_detach.hmtl.markdown new file mode 100644 index 0000000000..d66372e8fe --- /dev/null +++ b/website/docs/r/pi_volumes_detach.hmtl.markdown @@ -0,0 +1,61 @@ +--- +subcategory: "Power Systems" +layout: "ibm" +page_title: "IBM : ibm_pi_volumes_detach" +description: |- + Manages volumes in Power Virtual Server cloud. +--- + +# ibm_pi_volumes_attach + +Detach volumes to a Power Systems Virtual Server instance, see [volumes](https://cloud.ibm.com/apidocs/power-cloud#pcloud-v2-pvminstances-volumes-post) + +## Example Usage + +The following example attach volumes to power virtual instance. + +```terraform +resource "ibm_pi_volumes_detach" "pi_volumes_detach_instance" { + pi_cloud_instance_id = "" + pi_instance_id = "" + pi_detach_all_volumes = true +} +``` + +### Notes + +* Please find [supported Regions](https://cloud.ibm.com/apidocs/power-cloud#endpoint) for endpoints. +* If a Power cloud instance is provisioned at `lon04`, The provider level attributes should be as follows: + * `region` - `lon` + * `zone` - `lon04` + +Example usage: + + ```terraform + provider "ibm" { + region = "lon" + zone = "lon04" + } + ``` + +## Timeouts + +`pi_volumes_detach` provides the following [Timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) configuration options: + +* `delete` - (Default 10 minutes) Used for detaching volumes from a power virtual server instance. + +## Argument Reference + +You can specify the following arguments for this resource. + +* `pi_cloud_instance_id` - (Required, String) The GUID of the service instance associated with an account. +* `pi_detach_all_volumes` - (Optional, Boolean) Indicates if all volumes, except primary boot volume, attached to the `pi_instance` should be detached (default=`false`); required if `pi_volume_ids` is not provided. +* `pi_detach_primary_boot_volume` - (Optional, Boolean) Indicates if primary boot volume attached to the `pi_instance` should be detached (default=`false`). +* `pi_instance_id` - (Required, String) The unique identifier of an instance. +* `pi_volume_ids` - (Optional, List) List of volumes to be detached from a `pi_instance`; required if `pi_detach_all_volumes` is not provided. + +## Attribute Reference + +After your resource is created, you can read values from the listed arguments and the following attributes. + +* `id` - The unique identifier of volumes detach. The ID is composed of `/`