Skip to content

Commit

Permalink
Add Bulk Attach, Delete & Detach Volume (#313)
Browse files Browse the repository at this point in the history
* Add Bulk Delete & Detach Volume

- Add bulk volume delete
- Add bulk volume detach
- Add example for bulk delete and detach

* update example formatting

* replace math/rand with crypto/rand

* Add bulk attach

* Change params naming

* Update to model the swagger update
  • Loading branch information
michaelkad authored Feb 13, 2024
1 parent f83a39d commit 6448e2c
Show file tree
Hide file tree
Showing 4 changed files with 380 additions and 0 deletions.
46 changes: 46 additions & 0 deletions clients/instance/ibm-pi-volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,3 +254,49 @@ func (f *IBMPIVolumeClient) GetVolumeFlashCopyMappings(id string) (models.FlashC
}
return resp.Payload, nil
}

// Bulk volume detach
func (f *IBMPIVolumeClient) BulkVolumeDetach(pvmID string, body *models.VolumesDetach) (*models.VolumesDetachmentResponse, error) {
params := p_cloud_volumes.NewPcloudV2PvminstancesVolumesDeleteParams().
WithContext(f.ctx).WithTimeout(helpers.PIDeleteTimeOut).WithCloudInstanceID(f.cloudInstanceID).WithPvmInstanceID(pvmID).
WithBody(body)
resp, err := f.session.Power.PCloudVolumes.PcloudV2PvminstancesVolumesDelete(params, f.session.AuthInfo(f.cloudInstanceID))
if err != nil {
return nil, ibmpisession.SDKFailWithAPIError(err, fmt.Errorf(errors.DetachVolumesOperationFailed, pvmID, f.cloudInstanceID, err))
}
if resp == nil || resp.Payload == nil {
return nil, fmt.Errorf("failed detaching volumes for %s", pvmID)
}
return resp.Payload, nil
}

// Bulk volume delete
func (f *IBMPIVolumeClient) BulkVolumeDelete(body *models.VolumesDelete) (*models.VolumesDeleteResponse, error) {
params := p_cloud_volumes.NewPcloudV2VolumesDeleteParams().WithContext(f.ctx).WithTimeout(helpers.PIDeleteTimeOut).
WithCloudInstanceID(f.cloudInstanceID).WithBody(body)
respOk, respPartial, err := f.session.Power.PCloudVolumes.PcloudV2VolumesDelete(params, f.session.AuthInfo(f.cloudInstanceID))
if err != nil {
return nil, ibmpisession.SDKFailWithAPIError(err, fmt.Errorf(errors.DeleteVolumeOperationFailed, body.VolumeIDs, err))
}
if respOk != nil && respOk.Payload != nil {
return respOk.Payload, nil
}
if respPartial != nil && respPartial.Payload != nil {
return respPartial.Payload, nil
}
return nil, fmt.Errorf("failed Deleting volumes : %s", body.VolumeIDs)
}

// Bulk volutme attach
func (f *IBMPIVolumeClient) BulkVolumeAttach(pvmID string, body *models.VolumesAttach) (*models.VolumesAttachmentResponse, error) {
params := p_cloud_volumes.NewPcloudV2PvminstancesVolumesPostParams().WithContext(f.ctx).WithTimeout(helpers.PICreateTimeOut).
WithCloudInstanceID(f.cloudInstanceID).WithPvmInstanceID(pvmID).WithBody(body)
resp, err := f.session.Power.PCloudVolumes.PcloudV2PvminstancesVolumesPost(params, f.session.AuthInfo(f.cloudInstanceID))
if err != nil {
return nil, ibmpisession.SDKFailWithAPIError(err, fmt.Errorf(errors.AttachVolumesOperationFailed, body.VolumeIDs, err))
}
if resp == nil || resp.Payload == nil {
return nil, fmt.Errorf("failed to Attach volumes %s to server %s", body.VolumeIDs, pvmID)
}
return resp.Payload, nil
}
2 changes: 2 additions & 0 deletions errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ const AttachVolumeOperationFailed = "failed to perform the Attach volume Operati
const DetachVolumeOperationFailed = "failed to perform the Detach volume Operation for volume %s with error %w"
const GetVolumeRemoteCopyRelationshipsOperationFailed = "failed to Get remote copy relationships of a volume %s for the cloud instance %s with error %w"
const GetVolumeFlashCopyMappingOperationFailed = "failed to Get flash copy mapping of a volume %s for the cloud instance %s with error %w"
const DetachVolumesOperationFailed = "failed to perfome the Detach volumes Operation V2 for pvminstance %s in cloud instance %s with error %w"
const AttachVolumesOperationFailed = "failed to perform the Attach volume Operation for volumes %s with error %w"

// start of volume onboarding
const GetVolumeOnboardingOperationFailed = "failed to perform the Get Volume Onboarding Operation for volume-onboarding ID:%s for the cloud instance %s with error %w"
Expand Down
124 changes: 124 additions & 0 deletions examples/bulk-volume-delete/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package main

import (
"context"
"crypto/rand"
"fmt"
"log"
"math/big"
"time"

v "github.com/IBM-Cloud/power-go-client/clients/instance"
ps "github.com/IBM-Cloud/power-go-client/ibmpisession"
"github.com/IBM-Cloud/power-go-client/power/models"
"github.com/IBM/go-sdk-core/v5/core"
)

func main() {
//session Inputs
// < IAM TOKEN >
token := ""
region := " < REGION > "
zone := " < ZONE > "
accountID := " < ACCOUNT ID > "
url := region + ".power-iaas.test.cloud.ibm.com"

// volume inputs
randomNumber, err := rand.Int(rand.Reader, big.NewInt(100))
if err != nil {
fmt.Println("Error generating random number:", err)
return
}
randomNumber2, err := rand.Int(rand.Reader, big.NewInt(100))
if err != nil {
fmt.Println("Error generating random number:", err)
return
}
piID := " < POWER INSTANCE ID > "
name := fmt.Sprintf("power-go-test-volume-%d", randomNumber)
name2 := fmt.Sprintf("power-go-test-volume-%d", randomNumber2)
size := 20.0
vtype := "tier3"
sharable := true
replicationEnabled := false

authenticator := &core.BearerTokenAuthenticator{
BearerToken: token,
}
// authenticator := &core.IamAuthenticator{
// ApiKey: "< API KEY >",
// // Uncomment for test environment
// URL: "https://iam.test.cloud.ibm.com",
// }
// Create the session
options := &ps.IBMPIOptions{
Authenticator: authenticator,
UserAccount: accountID,
Zone: zone,
URL: url,
Debug: true,
}
session, err := ps.NewIBMPISession(options)
if err != nil {
log.Fatal(err)
}
powerClientVolume := v.NewIBMPIVolumeClient(context.Background(), session, piID)
if err != nil {
log.Fatal(err)
}
bodyVolume := &models.CreateDataVolume{
Name: &name,
Size: &size,
DiskType: vtype,
Shareable: &sharable,
ReplicationEnabled: &replicationEnabled,
}
bodyVolume2 := &models.CreateDataVolume{
Name: &name2,
Size: &size,
DiskType: vtype,
Shareable: &sharable,
ReplicationEnabled: &replicationEnabled,
}
log.Print("Creating first Volume\n")
createRespOk, err := powerClientVolume.CreateVolume(bodyVolume)
if err != nil {
log.Fatal(err)
}
log.Printf("***************[1]****************** %+v\n", *createRespOk)

volumeID := *createRespOk.VolumeID
getResp, err := powerClientVolume.Get(volumeID)
if err != nil {
log.Fatal(err)
}
log.Printf("***************[2]****************** %+v \n", *getResp)
log.Print("Creating Second Volume\n")
createRespOk2, err := powerClientVolume.CreateVolume(bodyVolume2)
if err != nil {
log.Fatal(err)
}
log.Printf("***************[3]****************** %+v\n", *createRespOk2)

volumeID2 := *createRespOk2.VolumeID
getResp2, err := powerClientVolume.Get(volumeID2)
if err != nil {
log.Fatal(err)
}
log.Printf("***************[4]****************** %+v \n", *getResp2)
time.Sleep(2 * time.Minute)
log.Print("Deleting Volumes\n")
body := &models.VolumesDelete{VolumeIDs: []string{volumeID, volumeID2}}
delResp, err := powerClientVolume.BulkVolumeDelete(body)
if err != nil {
log.Fatal(err)
}
if delResp == nil {
log.Print("***************[5]****************** Deleted\n")

} else {

log.Printf("***************[5]****************** %+v\n", *delResp)
}

}
208 changes: 208 additions & 0 deletions examples/bulk-volume-detach/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package main

import (
"context"
"crypto/rand"
"fmt"
"log"
"math/big"
"time"

v "github.com/IBM-Cloud/power-go-client/clients/instance"
"github.com/IBM-Cloud/power-go-client/helpers"
ps "github.com/IBM-Cloud/power-go-client/ibmpisession"
"github.com/IBM-Cloud/power-go-client/power/models"
"github.com/IBM/go-sdk-core/v5/core"
)

func main() {
//session Inputs
// < IAM TOKEN >
token := ""
region := " < REGION > "
zone := " < ZONE > "
accountID := " < ACCOUNT ID > "
url := region + ".power-iaas.test.cloud.ibm.com"

// volume inputs
randomNumber, err := rand.Int(rand.Reader, big.NewInt(100))
if err != nil {
fmt.Println("Error generating random number:", err)
return
}
randomNumber2, err := rand.Int(rand.Reader, big.NewInt(100))
if err != nil {
fmt.Println("Error generating random number:", err)
return
}
piID := " < POWER INSTANCE ID > "
name := fmt.Sprintf("power-go-test-volume-%d", randomNumber)
name2 := fmt.Sprintf("power-go-test-volume-%d", randomNumber2)
size := 20.0
vtype := "tier3"
sharable := true
replicationEnabled := false

// VM inputs
serverName := fmt.Sprintf("power-go-test-%d", randomNumber)
imageID := "cd447165-f45d-48f4-b07f-7c9a11ec84c7"
networkID := "f324429e-5716-40c1-a01b-4d36ec8b82e3"
memory := 4.0
processors := 2.0
procType := "shared"
sysType := "s922"

authenticator := &core.BearerTokenAuthenticator{
BearerToken: token,
}
// authenticator := &core.IamAuthenticator{
// ApiKey: "< API KEY >",
// // Uncomment for test environment
// URL: "https://iam.test.cloud.ibm.com",
// }
// Create the session
options := &ps.IBMPIOptions{
Authenticator: authenticator,
UserAccount: accountID,
Zone: zone,
URL: url,
Debug: true,
}
session, err := ps.NewIBMPISession(options)
if err != nil {
log.Fatal(err)
}
powerClientVolume := v.NewIBMPIVolumeClient(context.Background(), session, piID)
if err != nil {
log.Fatal(err)
}
bodyVolume := &models.CreateDataVolume{
Name: &name,
Size: &size,
DiskType: vtype,
Shareable: &sharable,
ReplicationEnabled: &replicationEnabled,
}
bodyVolume2 := &models.CreateDataVolume{
Name: &name2,
Size: &size,
DiskType: vtype,
Shareable: &sharable,
ReplicationEnabled: &replicationEnabled,
}
log.Print("Creating first Volume\n")
createRespOk, err := powerClientVolume.CreateVolume(bodyVolume)
if err != nil {
log.Fatal(err)
}
log.Printf("***************[1]****************** %+v\n", *createRespOk)

volumeID := *createRespOk.VolumeID
getResp, err := powerClientVolume.Get(volumeID)
if err != nil {
log.Fatal(err)
}
log.Printf("***************[2]****************** %+v \n", *getResp)
log.Print("Creating Second Volume\n")
createRespOk2, err := powerClientVolume.CreateVolume(bodyVolume2)
if err != nil {
log.Fatal(err)
}
log.Printf("***************[3]****************** %+v\n", *createRespOk2)

volumeID2 := *createRespOk2.VolumeID
getResp2, err := powerClientVolume.Get(volumeID2)
if err != nil {
log.Fatal(err)
}
log.Printf("***************[4]****************** %+v \n", *getResp2)

powerClientVM := v.NewIBMPIInstanceClient(context.Background(), session, piID)
if err != nil {
log.Fatal(err)
}
bodyVM := &models.PVMInstanceCreate{
ImageID: &imageID,
NetworkIDs: []string{networkID},
ServerName: &serverName,
Memory: &memory,
Processors: &processors,
ProcType: &procType,
SysType: sysType,
}

log.Print("Creating VM\n")
createRespOkVM, err := powerClientVM.Create(bodyVM)
if err != nil {
log.Fatal(err)
}
if len(*createRespOkVM) == 0 {
log.Fatal("create response is empty")
}
log.Printf("***************[5]****************** %+v\n", *createRespOk)

insID := ""
for _, in := range *createRespOkVM {
insID = *in.PvmInstanceID
}
if insID == "" {
log.Fatal("instance ID is empty")
}

getRespVM, err := powerClientVM.Get(insID)
if err != nil {
log.Fatal(err)
}
log.Printf("***************[6]****************** %+v \n", *getRespVM)
waitForInstanceAvailable(insID, powerClientVM)

log.Print("Attaching Volumes\n")
attachBody := &models.VolumesAttach{
VolumeIDs: []string{volumeID, volumeID2},
}
attachVolsResp, err := powerClientVolume.BulkVolumeAttach(insID, attachBody)
if err != nil {
log.Fatal(err)
}
log.Printf("***************[7]****************** %+v\n", *attachVolsResp)
time.Sleep(2 * time.Minute)

log.Print("Detaching Volumes\n")
detachBody := &models.VolumesDetach{
VolumeIDs: []string{volumeID, volumeID2},
}
createRespOkDetach, err := powerClientVolume.BulkVolumeDetach(insID, detachBody)
if err != nil {
log.Fatal(err)
}
log.Printf("***************[8]****************** %+v\n", *createRespOkDetach)
log.Print("Deleting VM\n")
err = powerClientVM.Delete(insID)
if err != nil {
log.Fatal(err)
}
log.Print("Deleting First Volume\n")
err = powerClientVolume.DeleteVolume(volumeID)
if err != nil {
log.Fatal(err)
}
log.Print("Deleting Second Volume\n")
err = powerClientVolume.DeleteVolume(volumeID2)
if err != nil {
log.Fatal(err)
}

}

func waitForInstanceAvailable(id string, powerClientVM *v.IBMPIInstanceClient) {
for start := time.Now(); time.Since(start) < time.Minute*30; {
time.Sleep(60 * time.Second)
pvm, err := powerClientVM.Get(id)
if err != nil {
log.Fatal(err)
}
if *pvm.Status == helpers.PIInstanceAvailable && pvm.Health.Status == helpers.PIInstanceHealthOk {
break
}
}
}

0 comments on commit 6448e2c

Please sign in to comment.