Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IBMi Software License field to instance data source and resource #5082

Merged
merged 31 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a1c8988
Initial ibmi
michaelkad Nov 6, 2023
96e6e56
ibmi initial, and sync with master
michaelkad Nov 17, 2023
b8a1e9c
sync go.mod and go.sum
Jan 10, 2024
6da86d8
sync go.mod and go.sum
Jan 10, 2024
4080e04
Update instance data source with IBMi licenses
ismirlia Jan 11, 2024
cada08f
Update instance resource with IBMi licenses
ismirlia Jan 11, 2024
5128fad
Add instance resource IBMi license acceptance test
ismirlia Jan 11, 2024
656af92
Add active check for instance update
ismirlia Jan 12, 2024
317bbfe
Fix updating licenses
ismirlia Jan 12, 2024
f8e43d3
Update IBMi terraform documentation
ismirlia Jan 19, 2024
4554254
Add wait software licenses update function
ismirlia Jan 25, 2024
9b4713c
Update IBMi license test
ismirlia Jan 26, 2024
63fc536
Update instance data source
ismirlia Jan 26, 2024
38c6c2d
Update instance update license markdown
ismirlia Jan 26, 2024
478bf9e
Merge branch 'master' into add_ibm_soft_license
ismirlia Jan 26, 2024
9bcd4f3
Remove duplicate markdown definitions
ismirlia Jan 26, 2024
f636d6d
Refactor ibmi license code
ismirlia Jan 29, 2024
11f7b22
Refactor ibmi license code
ismirlia Jan 29, 2024
093c1e4
Update IBMi documentation
ismirlia Feb 5, 2024
0765213
Merge branch 'master' of github.com:powervs-ibm/terraform-provider-ib…
ismirlia Feb 6, 2024
87a0af3
Merge branch 'master' into add_ibm_soft_license
ismirlia Feb 6, 2024
d3f94bf
Merge branch 'add_ibm_soft_license' of github.com:powervs-ibm/terrafo…
ismirlia Feb 6, 2024
8fd5882
Fix duplicate constants
ismirlia Feb 7, 2024
edea06f
Merge branch 'master' of github.com:powervs-ibm/terraform-provider-ib…
ismirlia Feb 14, 2024
3ad81bb
Fix formatting
ismirlia Feb 14, 2024
77fe080
Merge branch 'master' into add_ibm_soft_license
ismirlia Feb 14, 2024
a92033e
Fix formatting
ismirlia Feb 14, 2024
d62aa7f
Fix typo from merge conflict
ismirlia Feb 15, 2024
45840c2
Refactor variable names
ismirlia Feb 15, 2024
daaf139
Fix ibmi documentation
ismirlia Feb 16, 2024
ed52a74
Separate ibmi attribute in data source form ibmi argument in resource
ismirlia Feb 19, 2024
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
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,
},
PIInstanceIBMiCSS: {
Type: schema.TypeBool,
Computed: true,
Description: "IBMi Cloud Storage Solution",
},
PIInstanceIBMiPHA: {
Type: schema.TypeBool,
Computed: true,
Description: "IBMi Power High Availability",
},
PIInstanceIBMiRDS: {
Type: schema.TypeBool,
Computed: true,
Description: "IBMi Rational Dev Studio",
},
PIInstanceIBMiRDSUsers: {
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(PIInstanceIBMiCSS, powervmdata.SoftwareLicenses.IbmiCSS)
d.Set(PIInstanceIBMiPHA, powervmdata.SoftwareLicenses.IbmiPHA)
d.Set(PIInstanceIBMiRDS, powervmdata.SoftwareLicenses.IbmiRDS)
if *powervmdata.SoftwareLicenses.IbmiRDS {
d.Set(PIInstanceIBMiRDSUsers, powervmdata.SoftwareLicenses.IbmiRDSUsers)
} else {
d.Set(PIInstanceIBMiRDSUsers, 0)
}
}

return nil
}
12 changes: 9 additions & 3 deletions ibm/service/power/ibm_pi_constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,15 @@ 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"
PIInstanceIBMiCSS = "pi_ibmi_css"
PIInstanceIBMiPHA = "pi_ibmi_pha"
PIInstanceIBMiRDS = "pi_ibmi_rds"
PIInstanceIBMiRDSUsers = "pi_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",
},
PIInstanceIBMiCSS: {
Type: schema.TypeBool,
Optional: true,
Description: "IBMi Cloud Storage Solution",
},
PIInstanceIBMiPHA: {
Type: schema.TypeBool,
Optional: true,
Description: "IBMi Power High Availability",
},
PIInstanceIBMiRDS: {
Type: schema.TypeBool,
Optional: false,
Required: false,
Comment on lines +368 to +369
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kind of contradicting.
Here is the doc:

	// Required indicates whether the practitioner must enter a value in the
	// configuration for this attribute. Required cannot be used with Computed
	// Default, DefaultFunc, DiffSuppressFunc, DiffSuppressOnRefresh,
	// InputDefault, Optional, or StateFunc. At least one of Required,
	// Optional, Optional and Computed, or Computed must be enabled.
	Required [bool](https://pkg.go.dev/builtin#bool)

	// Optional indicates whether the practitioner can choose to not enter
	// a value in the configuration for this attribute. Optional cannot be used
	// with Required.
	Optional [bool](https://pkg.go.dev/builtin#bool)

	// Computed indicates whether the provider may return its own value for
	// this attribute or not. Computed cannot be used with Required. If
	// Required and Optional are both false, the attribute will be considered
	// "read only" for the practitioner, with only the provider able to set
	// its value.
	Computed [bool](https://pkg.go.dev/builtin#bool)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yussufsh I believe there is no contradiction, since the attribute is set by the provider as mention above if both false fir Required and Optional

If Required and Optional are both false, the attribute will be considered "read only" for the practitioner, with only the provider able to set its value.

Copy link
Collaborator

@yussufsh yussufsh Feb 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Computed cannot be used with Required.
Just Computed = true was enough.

Just common logic:
Required = true (cannot be Optional)
Optional = true (cannot be Required)

Computed: true,
Description: "IBMi Rational Dev Studio",
},
PIInstanceIBMiRDSUsers: {
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(PIInstanceIBMiCSS, powervmdata.SoftwareLicenses.IbmiCSS)
d.Set(PIInstanceIBMiPHA, powervmdata.SoftwareLicenses.IbmiPHA)
d.Set(PIInstanceIBMiRDS, powervmdata.SoftwareLicenses.IbmiRDS)
if *powervmdata.SoftwareLicenses.IbmiRDS {
d.Set(PIInstanceIBMiRDSUsers, powervmdata.SoftwareLicenses.IbmiRDSUsers)
} else {
d.Set(PIInstanceIBMiRDSUsers, 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(PIInstanceIBMiCSS, PIInstanceIBMiPHA, PIInstanceIBMiRDSUsers) {
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(PIInstanceIBMiCSS).(bool))
sl.IbmiPHA = flex.PtrToBool(d.Get(PIInstanceIBMiPHA).(bool))
ibmrdsUsers := d.Get(PIInstanceIBMiRDSUsers).(int)
if ibmrdsUsers < 0 {
return diag.Errorf("request with IBMi Rational Dev Studio property requires IBMi Rational Dev Studio number of users")
}
Comment on lines +818 to +820
Copy link
Collaborator

@yussufsh yussufsh Feb 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a requirement but we should be using ValidateFunc (ValidateDiagFunc)
https://developer.hashicorp.com/terraform/plugin/sdkv2/schemas/schema-behaviors#validatefunc

Copy link
Collaborator Author

@ismirlia ismirlia Feb 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably a bit more than I want to change for this PR. Probably mark it as an enhancement.

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(PIInstanceIBMiCSS); ok {
sl.IbmiCSS = flex.PtrToBool(ibmiCSS.(bool))
}
if ibmiPHA, ok := d.GetOk(PIInstanceIBMiPHA); ok {
sl.IbmiPHA = flex.PtrToBool(ibmiPHA.(bool))
}
if ibmrdsUsers, ok := d.GetOk(PIInstanceIBMiRDSUsers); ok {
if ibmrdsUsers.(int) < 0 {
return nil, fmt.Errorf("request with IBMi Rational Dev Studio property requires IBMi Rational Dev Studio number of users")
}
Comment on lines +1483 to +1485
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
6 changes: 6 additions & 0 deletions website/docs/d/pi_instance.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,9 @@ In addition to all argument reference list, you can access the following attribu
- `storage_type` - (String) The storage type where server is deployed.
- `virtual_cores_assigned` - (Integer) The virtual cores that are assigned to the instance.
- `volumes` - (List of strings) The list of volume IDs that are attached to the instance.

**Notes** Ibmi software licenses for IBMi virtual server instances -- only for IBMi instances
- `pi_ibmi_css` - (Boolean) IBMi Cloud Storage Solution.
- `pi_ibmi_pha` - (Boolean) IBMi Power High Availability.
- `pi_ibmi_rds` - (Boolean) IBMi Rational Dev Studio.
- `pi_ibmi_rds_users` - (Integer) IBMi Rational Dev Studio Number of User Licenses.
yussufsh marked this conversation as resolved.
Show resolved Hide resolved
Loading
Loading