Skip to content

Commit

Permalink
Add IBMi Software License field to instance data source and resource (#…
Browse files Browse the repository at this point in the history
…5082)

* Initial ibmi

* ibmi initial, and sync with master

* sync go.mod and go.sum

* sync go.mod and go.sum

* Update instance data source with IBMi licenses

* Update instance resource with IBMi licenses

* Add instance resource IBMi license acceptance test

* Add active check for instance update

* Fix updating licenses

* Update IBMi terraform documentation

* Add wait software licenses update function

* Update IBMi license test

* Update instance data source

* Update instance update license markdown

* Remove duplicate markdown definitions

* Refactor ibmi license code

* Refactor ibmi license code

* Update IBMi documentation

* Fix duplicate constants

* Fix formatting

* Fix formatting

* Fix typo from merge conflict

* Refactor variable names

* Fix ibmi documentation

* Separate ibmi attribute in data source form ibmi argument in resource

---------

Co-authored-by: michaelkad <[email protected]>
Co-authored-by: michael kad <michaelkad>
  • Loading branch information
ismirlia and michaelkad authored Feb 28, 2024
1 parent 74cfa1e commit 3c07f0b
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 19 deletions.
31 changes: 31 additions & 0 deletions ibm/service/power/data_source_ibm_pi_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,26 @@ func DataSourceIBMPIInstance() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
Attr_IBMiCSS: {
Type: schema.TypeBool,
Computed: true,
Description: "IBMi Cloud Storage Solution",
},
Attr_IBMiPHA: {
Type: schema.TypeBool,
Computed: true,
Description: "IBMi Power High Availability",
},
Attr_IBMiRDS: {
Type: schema.TypeBool,
Computed: true,
Description: "IBMi Rational Dev Studio",
},
Attr_IBMiRDSUsers: {
Type: schema.TypeInt,
Computed: true,
Description: "IBMi Rational Dev Studio Number of User Licenses",
},
},
}
}
Expand Down Expand Up @@ -214,5 +234,16 @@ func dataSourceIBMPIInstancesRead(ctx context.Context, d *schema.ResourceData, m
d.Set("health_status", powervmdata.Health.Status)
}

if powervmdata.SoftwareLicenses != nil {
d.Set(Attr_IBMiCSS, powervmdata.SoftwareLicenses.IbmiCSS)
d.Set(Attr_IBMiPHA, powervmdata.SoftwareLicenses.IbmiPHA)
d.Set(Attr_IBMiRDS, powervmdata.SoftwareLicenses.IbmiRDS)
if *powervmdata.SoftwareLicenses.IbmiRDS {
d.Set(Attr_IBMiRDSUsers, powervmdata.SoftwareLicenses.IbmiRDSUsers)
} else {
d.Set(Attr_IBMiRDSUsers, 0)
}
}

return nil
}
15 changes: 12 additions & 3 deletions ibm/service/power/ibm_pi_constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,18 @@ const (
Attr_DhcpStatus = "status"

// Instance
Arg_PVMInstanceId = "pi_instance_id"
Arg_PVMInstanceActionType = "pi_action"
Arg_PVMInstanceHealthStatus = "pi_health_status"
Arg_PVMInstanceId = "pi_instance_id"
Arg_PVMInstanceActionType = "pi_action"
Arg_PVMInstanceHealthStatus = "pi_health_status"
Arg_IBMiCSS = "pi_ibmi_css"
Arg_IBMiPHA = "pi_ibmi_pha"
Arg_IBMiRDSUsers = "pi_ibmi_rds_users"
Attr_IBMiCSS = "ibmi_css"
Attr_IBMiPHA = "ibmi_pha"
Attr_IBMiRDS = "ibmi_rds"
Attr_IBMiRDSUsers = "ibmi_rds_users"
OS_IBMI = "ibmi"

Arg_PIInstanceSharedProcessorPool = "pi_shared_processor_pool"

PVMInstanceHealthOk = "OK"
Expand Down
163 changes: 148 additions & 15 deletions ibm/service/power/resource_ibm_pi_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,28 @@ func ResourceIBMPIInstance() *schema.Resource {
Computed: true,
Description: "Minimum Virtual Cores Assigned to the PVMInstance",
},
Arg_IBMiCSS: {
Type: schema.TypeBool,
Optional: true,
Description: "IBMi Cloud Storage Solution",
},
Arg_IBMiPHA: {
Type: schema.TypeBool,
Optional: true,
Description: "IBMi Power High Availability",
},
Attr_IBMiRDS: {
Type: schema.TypeBool,
Optional: false,
Required: false,
Computed: true,
Description: "IBMi Rational Dev Studio",
},
Arg_IBMiRDSUsers: {
Type: schema.TypeInt,
Optional: true,
Description: "IBMi Rational Dev Studio Number of User Licenses",
},
},
}
}
Expand Down Expand Up @@ -513,7 +535,17 @@ func resourceIBMPIInstanceRead(ctx context.Context, d *schema.ResourceData, meta
d.Set("min_virtual_cores", powervmdata.VirtualCores.Min)
}
d.Set(helpers.PIInstanceLicenseRepositoryCapacity, powervmdata.LicenseRepositoryCapacity)

d.Set(PIInstanceDeploymentType, powervmdata.DeploymentType)
if powervmdata.SoftwareLicenses != nil {
d.Set(Arg_IBMiCSS, powervmdata.SoftwareLicenses.IbmiCSS)
d.Set(Arg_IBMiPHA, powervmdata.SoftwareLicenses.IbmiPHA)
d.Set(Attr_IBMiRDS, powervmdata.SoftwareLicenses.IbmiRDS)
if *powervmdata.SoftwareLicenses.IbmiRDS {
d.Set(Arg_IBMiRDSUsers, powervmdata.SoftwareLicenses.IbmiRDSUsers)
} else {
d.Set(Arg_IBMiRDSUsers, 0)
}
}
return nil
}

Expand Down Expand Up @@ -568,7 +600,6 @@ func resourceIBMPIInstanceUpdate(ctx context.Context, d *schema.ResourceData, me
}

if d.HasChange(helpers.PIInstanceProcType) {

// Stop the lpar
if d.Get("status") == "SHUTOFF" {
log.Printf("the lpar is in the shutoff state. Nothing to do . Moving on ")
Expand Down Expand Up @@ -677,7 +708,6 @@ func resourceIBMPIInstanceUpdate(ctx context.Context, d *schema.ResourceData, me
// License repository capacity will be updated only if service instance is a vtl instance
// might need to check if lrc was set
if d.HasChange(helpers.PIInstanceLicenseRepositoryCapacity) {

lrc := d.Get(helpers.PIInstanceLicenseRepositoryCapacity).(int64)
body := &models.PVMInstanceUpdate{
LicenseRepositoryCapacity: lrc,
Expand Down Expand Up @@ -738,7 +768,6 @@ func resourceIBMPIInstanceUpdate(ctx context.Context, d *schema.ResourceData, me
}

if d.HasChange(helpers.PIPlacementGroupID) {

pgClient := st.NewIBMPIPlacementGroupClient(ctx, sess, cloudInstanceID)

oldRaw, newRaw := d.GetChange(helpers.PIPlacementGroupID)
Expand Down Expand Up @@ -772,8 +801,37 @@ func resourceIBMPIInstanceUpdate(ctx context.Context, d *schema.ResourceData, me
}
}
}
return resourceIBMPIInstanceRead(ctx, d, meta)
if d.HasChanges(Arg_IBMiCSS, Arg_IBMiPHA, Arg_IBMiRDSUsers) {
if d.Get("status") == "ACTIVE" {
log.Printf("the lpar is in the Active state, continuing with update")
} else {
_, err = isWaitForPIInstanceAvailable(ctx, client, instanceID, "OK")
if err != nil {
return diag.FromErr(err)
}
}

sl := &models.SoftwareLicenses{}
sl.IbmiCSS = flex.PtrToBool(d.Get(Arg_IBMiCSS).(bool))
sl.IbmiPHA = flex.PtrToBool(d.Get(Arg_IBMiPHA).(bool))
ibmrdsUsers := d.Get(Arg_IBMiRDSUsers).(int)
if ibmrdsUsers < 0 {
return diag.Errorf("request with IBMi Rational Dev Studio property requires IBMi Rational Dev Studio number of users")
}
sl.IbmiRDS = flex.PtrToBool(ibmrdsUsers > 0)
sl.IbmiRDSUsers = int64(ibmrdsUsers)

updatebody := &models.PVMInstanceUpdate{SoftwareLicenses: sl}
_, err = client.Update(instanceID, updatebody)
if err != nil {
return diag.FromErr(err)
}
_, err = isWaitForPIInstanceSoftwareLicenses(ctx, client, instanceID, sl)
if err != nil {
return diag.FromErr(err)
}
}
return resourceIBMPIInstanceRead(ctx, d, meta)
}

func resourceIBMPIInstanceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
Expand Down Expand Up @@ -873,6 +931,59 @@ func isPIInstanceRefreshFunc(client *st.IBMPIInstanceClient, id, instanceReadySt
}
}

func isWaitForPIInstanceSoftwareLicenses(ctx context.Context, client *st.IBMPIInstanceClient, id string, softwareLicenses *models.SoftwareLicenses) (interface{}, error) {
log.Printf("Waiting for PIInstance Software Licenses (%s) to be updated ", id)

queryTimeOut := activeTimeOut

stateConf := &resource.StateChangeConf{
Pending: []string{"notdone"},
Target: []string{"done"},
Refresh: isPIInstanceSoftwareLicensesRefreshFunc(client, id, softwareLicenses),
Delay: 90 * time.Second,
MinTimeout: queryTimeOut,
Timeout: 120 * time.Minute,
}

return stateConf.WaitForStateContext(ctx)
}

func isPIInstanceSoftwareLicensesRefreshFunc(client *st.IBMPIInstanceClient, id string, softwareLicenses *models.SoftwareLicenses) resource.StateRefreshFunc {
return func() (interface{}, string, error) {

pvm, err := client.Get(id)
if err != nil {
return nil, "", err
}

// Check that each software license we modified has been updated
if softwareLicenses.IbmiCSS != nil {
if *softwareLicenses.IbmiCSS != *pvm.SoftwareLicenses.IbmiCSS {
return pvm, "notdone", nil
}
}

if softwareLicenses.IbmiPHA != nil {
if *softwareLicenses.IbmiPHA != *pvm.SoftwareLicenses.IbmiPHA {
return pvm, "notdone", nil
}
}

if softwareLicenses.IbmiRDS != nil {
// If the update set IBMiRDS to false, don't check IBMiRDSUsers as it will be updated on the terraform side on the read
if !*softwareLicenses.IbmiRDS {
if *softwareLicenses.IbmiRDS != *pvm.SoftwareLicenses.IbmiRDS {
return pvm, "notdone", nil
}
} else if (*softwareLicenses.IbmiRDS != *pvm.SoftwareLicenses.IbmiRDS) || (softwareLicenses.IbmiRDSUsers != pvm.SoftwareLicenses.IbmiRDSUsers) {
return pvm, "notdone", nil
}
}

return pvm, "done", nil
}
}

func isWaitForPIInstanceShutoff(ctx context.Context, client *st.IBMPIInstanceClient, id string, instanceReadyStatus string) (interface{}, error) {
log.Printf("Waiting for PIInstance (%s) to be shutoff and health active ", id)

Expand Down Expand Up @@ -1336,18 +1447,15 @@ func createPVMInstance(d *schema.ResourceData, client *st.IBMPIInstanceClient, i
if spp, ok := d.GetOk(Arg_PIInstanceSharedProcessorPool); ok {
body.SharedProcessorPool = spp.(string)
}

if lrc, ok := d.GetOk(helpers.PIInstanceLicenseRepositoryCapacity); ok {
// check if using vtl image
// check if vtl image is stock image
imageData, err := imageClient.GetStockImage(imageid)
imageData, err := imageClient.GetStockImage(imageid)
if err != nil {
// check if vtl image is cloud instance image
imageData, err = imageClient.Get(imageid)
if err != nil {
// check if vtl image is cloud instance image
imageData, err = imageClient.Get(imageid)
if err != nil {
return nil, fmt.Errorf("image doesn't exist. %e", err)
}
return nil, fmt.Errorf("image doesn't exist. %e", err)
}
}
if lrc, ok := d.GetOk(helpers.PIInstanceLicenseRepositoryCapacity); ok {

if imageData.Specifications.ImageType == "stock-vtl" {
body.LicenseRepositoryCapacity = int64(lrc.(int))
Expand All @@ -1356,6 +1464,31 @@ func createPVMInstance(d *schema.ResourceData, client *st.IBMPIInstanceClient, i
}
}

if imageData.Specifications.OperatingSystem == OS_IBMI {
// Default value
falseBool := false
sl := &models.SoftwareLicenses{
IbmiCSS: &falseBool,
IbmiPHA: &falseBool,
IbmiRDS: &falseBool,
IbmiRDSUsers: 0,
}
if ibmiCSS, ok := d.GetOk(Arg_IBMiCSS); ok {
sl.IbmiCSS = flex.PtrToBool(ibmiCSS.(bool))
}
if ibmiPHA, ok := d.GetOk(Arg_IBMiPHA); ok {
sl.IbmiPHA = flex.PtrToBool(ibmiPHA.(bool))
}
if ibmrdsUsers, ok := d.GetOk(Arg_IBMiRDSUsers); ok {
if ibmrdsUsers.(int) < 0 {
return nil, fmt.Errorf("request with IBMi Rational Dev Studio property requires IBMi Rational Dev Studio number of users")
}
sl.IbmiRDS = flex.PtrToBool(ibmrdsUsers.(int) > 0)
sl.IbmiRDSUsers = int64(ibmrdsUsers.(int))
}
body.SoftwareLicenses = sl
}

pvmList, err := client.Create(body)

if err != nil {
Expand Down
69 changes: 69 additions & 0 deletions ibm/service/power/resource_ibm_pi_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,41 @@ func testAccCheckIBMPIInstanceDeploymentTypeConfig(name, instanceHealthStatus, e
`, acc.Pi_cloud_instance_id, name, acc.Pi_image, acc.Pi_network_name, instanceHealthStatus, epic, systype, acc.PiStorageType)
}

func testAccCheckIBMPIInstanceIBMiLicense(name, instanceHealthStatus string, IBMiCSS bool, IBMiRDSUsers int) string {
return fmt.Sprintf(`
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_size = 1
pi_volume_name = "%[2]s"
pi_volume_type = "tier3"
}
resource "ibm_pi_instance" "power_instance" {
pi_memory = "2"
pi_processors = "0.25"
pi_instance_name = "%[2]s"
pi_proc_type = "shared"
pi_image_id = data.ibm_pi_image.power_image.id
pi_sys_type = "s922"
pi_cloud_instance_id = "%[1]s"
pi_storage_pool = data.ibm_pi_image.power_image.storage_pool
pi_health_status = "%[5]s"
pi_volume_ids = [ibm_pi_volume.power_volume.volume_id]
pi_network {
network_id = data.ibm_pi_network.power_networks.id
}
pi_ibmi_css = %[6]t
pi_ibmi_rds_users = %[7]d
}`, acc.Pi_cloud_instance_id, name, acc.Pi_image, acc.Pi_network_name, instanceHealthStatus, IBMiCSS, IBMiRDSUsers)
}

func testAccIBMPIInstanceNetworkConfig(name, privateNetIP string) string {
return fmt.Sprintf(`
resource "ibm_pi_key" "key" {
Expand Down Expand Up @@ -259,6 +294,40 @@ func TestAccIBMPIInstanceDeploymentType(t *testing.T) {
})
}

func TestAccIBMPIInstanceIBMiLicense(t *testing.T) {
instanceRes := "ibm_pi_instance.power_instance"
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: testAccCheckIBMPIInstanceDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckIBMPIInstanceIBMiLicense(name, helpers.PIInstanceHealthOk, true, 2),
Check: resource.ComposeTestCheckFunc(
testAccCheckIBMPIInstanceExists(instanceRes),
resource.TestCheckResourceAttr(instanceRes, "pi_instance_name", name),
resource.TestCheckResourceAttr(instanceRes, "status", "ACTIVE"),
resource.TestCheckResourceAttr(instanceRes, "pi_ibmi_css", "true"),
resource.TestCheckResourceAttr(instanceRes, "pi_ibmi_rds", "true"),
resource.TestCheckResourceAttr(instanceRes, "pi_ibmi_rds_users", "2"),
),
},
{
Config: testAccCheckIBMPIInstanceIBMiLicense(name, helpers.PIInstanceHealthOk, false, 0),
Check: resource.ComposeTestCheckFunc(
testAccCheckIBMPIInstanceExists(instanceRes),
testAccCheckIBMPIInstanceStatus(instanceRes, "ACTIVE"),
resource.TestCheckResourceAttr(instanceRes, "pi_instance_name", name),
resource.TestCheckResourceAttr(instanceRes, "pi_ibmi_css", "false"),
resource.TestCheckResourceAttr(instanceRes, "pi_ibmi_rds", "false"),
resource.TestCheckResourceAttr(instanceRes, "pi_ibmi_rds_users", "0"),
),
},
},
})
}

func TestAccIBMPIInstanceNetwork(t *testing.T) {
instanceRes := "ibm_pi_instance.power_instance"
name := fmt.Sprintf("tf-pi-instance-%d", acctest.RandIntRange(10, 100))
Expand Down
7 changes: 7 additions & 0 deletions website/docs/d/pi_instance.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ In addition to all argument reference list, you can access the following attribu

- `deployment_type` - (String) The custom deployment type.
- `health_status` - (String) The health of the instance.

**Notes** IBMi software licenses for IBMi virtual server instances -- only for IBMi instances
- `ibmi_css` - (Boolean) IBMi Cloud Storage Solution.
- `ibmi_pha` - (Boolean) IBMi Power High Availability.
- `ibmi_rds` - (Boolean) IBMi Rational Dev Studio.
- `ibmi_rds_users` - (Integer) IBMi Rational Dev Studio Number of User Licenses.
- `id` - (String) The unique identifier of the instance.
- `license_repository_capacity` - The VTL license repository capacity TB value. Only available with VTL instances.
- `memory` - (Float) The amount of memory that is allocated to the instance.
Expand All @@ -64,6 +70,7 @@ In addition to all argument reference list, you can access the following attribu
- `network_id` - (String) The network ID of the instance.
- `network_name` - (String) The network name of the instance.
- `type` - (String) The type of the network.

- `placement_group_id`- (String) The ID of the placement group that the instance is a member.
- `processors` - (Float) The number of processors that are allocated to the instance.
- `proctype` - (String) The procurement type of the instance. Supported values are `shared` and `dedicated`.
Expand Down
Loading

0 comments on commit 3c07f0b

Please sign in to comment.