From 2f863a1426b39be30736dbc222bb5ef2ba28baac Mon Sep 17 00:00:00 2001 From: Ujjwal Kumar Date: Mon, 2 May 2022 15:12:56 +0530 Subject: [PATCH] fix(reserved ip) : bare metal server reserved ip multi nic changes, doc fix --- .../data_source_ibm_is_subnet_reserved_ip.go | 31 +- .../data_source_ibm_is_subnet_reserved_ips.go | 33 +- .../vpc/resource_ibm_is_bare_metal_server.go | 354 +++++++++++++----- ...esource_ibm_is_bare_metal_server_action.go | 4 +- ibm/service/vpc/resource_ibm_is_instance.go | 5 +- .../vpc/resource_ibm_is_subnet_reserved_ip.go | 31 +- ...resource_ibm_is_subnet_reserved_ip_test.go | 43 +++ website/docs/r/is_bare_metal_server.markdown | 17 - website/docs/r/is_instance.html.markdown | 61 ++- .../r/is_subnet_reserved_ip.html.markdown | 22 +- 10 files changed, 462 insertions(+), 139 deletions(-) diff --git a/ibm/service/vpc/data_source_ibm_is_subnet_reserved_ip.go b/ibm/service/vpc/data_source_ibm_is_subnet_reserved_ip.go index 87160da4b0..52552240a2 100644 --- a/ibm/service/vpc/data_source_ibm_is_subnet_reserved_ip.go +++ b/ibm/service/vpc/data_source_ibm_is_subnet_reserved_ip.go @@ -5,6 +5,7 @@ package vpc import ( "fmt" + "reflect" "github.com/IBM/vpc-go-sdk/vpcv1" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -132,9 +133,33 @@ func dataSdataSourceIBMISReservedIPRead(d *schema.ResourceData, meta interface{} } d.Set(isReservedIPType, *reserveIP.ResourceType) if reserveIP.Target != nil { - target, ok := reserveIP.Target.(*vpcv1.ReservedIPTarget) - if ok { - d.Set(isReservedIPTarget, target.ID) + targetIntf := reserveIP.Target + switch reflect.TypeOf(targetIntf).String() { + case "*vpcv1.ReservedIPTargetEndpointGatewayReference": + { + target := targetIntf.(*vpcv1.ReservedIPTargetEndpointGatewayReference) + d.Set(isReservedIPTarget, target.ID) + } + case "*vpcv1.ReservedIPTargetNetworkInterfaceReferenceTargetContext": + { + target := targetIntf.(*vpcv1.ReservedIPTargetNetworkInterfaceReferenceTargetContext) + d.Set(isReservedIPTarget, target.ID) + } + case "*vpcv1.ReservedIPTargetLoadBalancerReference": + { + target := targetIntf.(*vpcv1.ReservedIPTargetLoadBalancerReference) + d.Set(isReservedIPTarget, target.ID) + } + case "*vpcv1.ReservedIPTargetVPNGatewayReference": + { + target := targetIntf.(*vpcv1.ReservedIPTargetVPNGatewayReference) + d.Set(isReservedIPTarget, target.ID) + } + case "*vpcv1.ReservedIPTarget": + { + target := targetIntf.(*vpcv1.ReservedIPTarget) + d.Set(isReservedIPTarget, target.ID) + } } } return nil // By default there should be no error diff --git a/ibm/service/vpc/data_source_ibm_is_subnet_reserved_ips.go b/ibm/service/vpc/data_source_ibm_is_subnet_reserved_ips.go index 1a5382d6a3..d241681702 100644 --- a/ibm/service/vpc/data_source_ibm_is_subnet_reserved_ips.go +++ b/ibm/service/vpc/data_source_ibm_is_subnet_reserved_ips.go @@ -5,6 +5,7 @@ package vpc import ( "fmt" + "reflect" "time" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" @@ -152,9 +153,35 @@ func dataSdataSourceIBMISReservedIPsRead(d *schema.ResourceData, meta interface{ ipsOutput[isReservedIPName] = *data.Name ipsOutput[isReservedIPOwner] = *data.Owner ipsOutput[isReservedIPType] = *data.ResourceType - target, ok := data.Target.(*vpcv1.ReservedIPTarget) - if ok { - ipsOutput[isReservedIPTarget] = target.ID + if data.Target != nil { + targetIntf := data.Target + switch reflect.TypeOf(targetIntf).String() { + case "*vpcv1.ReservedIPTargetEndpointGatewayReference": + { + target := targetIntf.(*vpcv1.ReservedIPTargetEndpointGatewayReference) + ipsOutput[isReservedIPTarget] = target.ID + } + case "*vpcv1.ReservedIPTargetNetworkInterfaceReferenceTargetContext": + { + target := targetIntf.(*vpcv1.ReservedIPTargetNetworkInterfaceReferenceTargetContext) + ipsOutput[isReservedIPTarget] = target.ID + } + case "*vpcv1.ReservedIPTargetLoadBalancerReference": + { + target := targetIntf.(*vpcv1.ReservedIPTargetLoadBalancerReference) + ipsOutput[isReservedIPTarget] = target.ID + } + case "*vpcv1.ReservedIPTargetVPNGatewayReference": + { + target := targetIntf.(*vpcv1.ReservedIPTargetVPNGatewayReference) + ipsOutput[isReservedIPTarget] = target.ID + } + case "*vpcv1.ReservedIPTarget": + { + target := targetIntf.(*vpcv1.ReservedIPTarget) + ipsOutput[isReservedIPTarget] = target.ID + } + } } reservedIPs = append(reservedIPs, ipsOutput) } diff --git a/ibm/service/vpc/resource_ibm_is_bare_metal_server.go b/ibm/service/vpc/resource_ibm_is_bare_metal_server.go index dd81c7fb77..4531ece6bb 100644 --- a/ibm/service/vpc/resource_ibm_is_bare_metal_server.go +++ b/ibm/service/vpc/resource_ibm_is_bare_metal_server.go @@ -80,7 +80,13 @@ func ResourceIBMIsBareMetalServer() *schema.Resource { ReadContext: resourceIBMISBareMetalServerRead, UpdateContext: resourceIBMISBareMetalServerUpdate, DeleteContext: resourceIBMISBareMetalServerDelete, - Importer: &schema.ResourceImporter{}, + Importer: &schema.ResourceImporter{ + State: func(d *schema.ResourceData, meta interface{}) (result []*schema.ResourceData, err error) { + log.Printf("[INFO] Bare metal server (%s) importing", d.Id()) + d.Set(isBareMetalServerDeleteType, "hard") + return []*schema.ResourceData{d}, nil + }, + }, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(30 * time.Minute), @@ -329,9 +335,9 @@ func ResourceIBMIsBareMetalServer() *schema.Resource { }, isBareMetalServerNetworkInterfaces: { - Type: schema.TypeList, - Optional: true, - DiffSuppressFunc: flex.ApplyOnce, + Type: schema.TypeList, + Optional: true, + Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { @@ -350,17 +356,20 @@ func ResourceIBMIsBareMetalServer() *schema.Resource { Computed: true, Description: "The user-defined name for this network interface. If unspecified, the name will be a hyphenated list of randomly-selected words", }, + isBareMetalServerNicPortSpeed: { + Type: schema.TypeInt, + Computed: true, + }, isBareMetalServerNicHref: { Type: schema.TypeString, Computed: true, Description: "The URL for this network interface", }, isBareMetalServerNicEnableInfraNAT: { - Type: schema.TypeBool, - Optional: true, - Computed: true, - DiffSuppressFunc: flex.ApplyOnce, - Description: "If true, the VPC infrastructure performs any needed NAT operations. If false, the packet is passed unmodified to/from the network interface, allowing the workload to perform any needed NAT operations.", + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "If true, the VPC infrastructure performs any needed NAT operations. If false, the packet is passed unmodified to/from the network interface, allowing the workload to perform any needed NAT operations.", }, isBareMetalServerNicInterfaceType: { Type: schema.TypeString, @@ -388,7 +397,7 @@ func ResourceIBMIsBareMetalServer() *schema.Resource { Description: "The URL for this reserved IP", }, isBareMetalServerNicIpAutoDelete: { - Type: schema.TypeString, + Type: schema.TypeBool, Optional: true, Computed: true, Description: "Indicates whether this reserved IP member will be automatically deleted when either target is deleted, or the reserved IP is unbound.", @@ -422,11 +431,10 @@ func ResourceIBMIsBareMetalServer() *schema.Resource { Description: "Collection of security group ids", }, isBareMetalServerNicSubnet: { - Type: schema.TypeString, - Required: true, - ForceNew: false, - DiffSuppressFunc: flex.ApplyOnce, - Description: "The associated subnet", + Type: schema.TypeString, + Required: true, + ForceNew: false, + Description: "The associated subnet", }, isBareMetalServerNicAllowedVlans: { Type: schema.TypeSet, @@ -718,6 +726,7 @@ func resourceIBMISBareMetalServerCreate(context context.Context, d *schema.Resou if nicsintf, ok := d.GetOk(isBareMetalServerNetworkInterfaces); ok { nics := nicsintf.([]interface{}) + inlinenicobj := make([]vpcv1.BareMetalServerNetworkInterfacePrototypeIntf, 0) for _, resource := range nics { nic := resource.(map[string]interface{}) interfaceType := "" @@ -803,6 +812,7 @@ func resourceIBMISBareMetalServerCreate(context context.Context, d *schema.Resou nicobj.SecurityGroups = secgrpobjs } } + inlinenicobj = append(inlinenicobj, nicobj) } else { interfaceType = "vlan" var nicobj = &vpcv1.BareMetalServerNetworkInterfacePrototypeBareMetalServerNetworkInterfaceByVlanPrototype{} @@ -887,8 +897,10 @@ func resourceIBMISBareMetalServerCreate(context context.Context, d *schema.Resou nicobj.SecurityGroups = secgrpobjs } } + inlinenicobj = append(inlinenicobj, nicobj) } } + options.NetworkInterfaces = inlinenicobj } if rgrp, ok := d.GetOk(isBareMetalServerResourceGroup); ok { @@ -1003,6 +1015,32 @@ func bareMetalServerGet(context context.Context, d *schema.ResourceData, meta in d.Set(isBareMetalServerHref, *bms.Href) d.Set(isBareMetalServerMemory, *bms.Memory) d.Set(isBareMetalServerName, *bms.Name) + + // get initialization + getBmsInitialization := &vpcv1.GetBareMetalServerInitializationOptions{ + ID: bms.ID, + } + bmsinitialization, response, err := sess.GetBareMetalServerInitializationWithContext(context, getBmsInitialization) + if err != nil { + if response != nil && response.StatusCode == 404 { + d.SetId("") + return nil + } + return fmt.Errorf("[ERROR] Error getting Bare Metal Server (%s) initialization: %s\n%s", id, err, response) + } + if bmsinitialization != nil && bmsinitialization.Image.ID != nil { + d.Set(isBareMetalServerImage, *bmsinitialization.Image.ID) + } + if bmsinitialization != nil && bmsinitialization.Keys != nil { + keyList := []string{} + if len(bmsinitialization.Keys) != 0 { + for i := 0; i < len(bmsinitialization.Keys); i++ { + keyList = append(keyList, string(*(bmsinitialization.Keys[i].ID))) + } + } + d.Set(isBareMetalServerKeys, keyList) + } + //pni if bms.PrimaryNetworkInterface != nil { @@ -1019,7 +1057,7 @@ func bareMetalServerGet(context context.Context, d *schema.ResourceData, meta in bmsnic, response, err := sess.GetBareMetalServerNetworkInterfaceWithContext(context, getnicoptions) if err != nil { - return fmt.Errorf("[ERROR] Error getting network interfaces attached to the bare metal server %s\n%s", err, response) + return fmt.Errorf("[ERROR] Error getting primary network interface attached to the bare metal server %s\n%s", err, response) } if bms.PrimaryNetworkInterface.PrimaryIP != nil { @@ -1098,93 +1136,106 @@ func bareMetalServerGet(context context.Context, d *schema.ResourceData, meta in } //ni - - interfacesList := make([]map[string]interface{}, 0) - for _, intfc := range bms.NetworkInterfaces { - if *intfc.ID != *bms.PrimaryNetworkInterface.ID { - currentNic := map[string]interface{}{} - currentNic["id"] = *intfc.ID - currentNic[isBareMetalServerNicName] = *intfc.Name - getnicoptions := &vpcv1.GetBareMetalServerNetworkInterfaceOptions{ - BareMetalServerID: &id, - ID: intfc.ID, - } - bmsnicintf, response, err := sess.GetBareMetalServerNetworkInterfaceWithContext(context, getnicoptions) - if err != nil { - return fmt.Errorf("[ERROR] Error getting network interfaces attached to the bare metal server %s\n%s", err, response) - } - if intfc.PrimaryIP != nil { - primaryIpList := make([]map[string]interface{}, 0) - currentIP := map[string]interface{}{} - if intfc.PrimaryIP.Href != nil { - currentIP[isBareMetalServerNicIpAddress] = *intfc.PrimaryIP.Address - } - if intfc.PrimaryIP.Href != nil { - currentIP[isBareMetalServerNicIpHref] = *intfc.PrimaryIP.Href - } - if intfc.PrimaryIP.Name != nil { - currentIP[isBareMetalServerNicIpName] = *intfc.PrimaryIP.Name - } - if intfc.PrimaryIP.ID != nil { - currentIP[isBareMetalServerNicIpID] = *intfc.PrimaryIP.ID + if bms.NetworkInterfaces != nil { + interfacesList := make([]map[string]interface{}, 0) + for _, intfc := range bms.NetworkInterfaces { + if *intfc.ID != *bms.PrimaryNetworkInterface.ID { + currentNic := map[string]interface{}{} + subnetId := *intfc.Subnet.ID + ripId := "" + nicId := *intfc.ID + currentNic["id"] = nicId + currentNic[isBareMetalServerNicName] = *intfc.Name + currentNic[isBareMetalServerNicHref] = *intfc.Href + currentNic[isBareMetalServerNicSubnet] = subnetId + + getnicoptions := &vpcv1.GetBareMetalServerNetworkInterfaceOptions{ + BareMetalServerID: &id, + ID: &nicId, } - if intfc.PrimaryIP.ResourceType != nil { - currentIP[isBareMetalServerNicResourceType] = *intfc.PrimaryIP.ResourceType - } - getripoptions := &vpcv1.GetSubnetReservedIPOptions{ - SubnetID: bms.PrimaryNetworkInterface.Subnet.ID, - ID: bms.PrimaryNetworkInterface.PrimaryIP.ID, - } - bmsRip, response, err := sess.GetSubnetReservedIP(getripoptions) + bmsnicintf, response, err := sess.GetBareMetalServerNetworkInterfaceWithContext(context, getnicoptions) if err != nil { - return fmt.Errorf("[ERROR] Error getting network interface reserved ip(%s) attached to the bare metal server network interface(%s): %s\n%s", *bms.PrimaryNetworkInterface.PrimaryIP.ID, *bms.PrimaryNetworkInterface.ID, err, response) + return fmt.Errorf("[ERROR] Error getting network interface(%s) attached to the bare metal server(%s) %s\n%s", nicId, id, err, response) } - currentIP[isBareMetalServerNicIpAutoDelete] = bmsRip.AutoDelete - - primaryIpList = append(primaryIpList, currentIP) - currentNic[isBareMetalServerNicPrimaryIP] = primaryIpList - } - - switch reflect.TypeOf(bmsnicintf).String() { - case "*vpcv1.BareMetalServerNetworkInterfaceByPci": - { - bmsnic := bmsnicintf.(*vpcv1.BareMetalServerNetworkInterfaceByPci) - currentNic[isBareMetalServerNicAllowIPSpoofing] = *bmsnic.AllowIPSpoofing - currentNic[isBareMetalServerNicEnableInfraNAT] = *bmsnic.EnableInfrastructureNat - currentNic[isBareMetalServerNicSubnet] = *bmsnic.Subnet.ID - currentNic[isBareMetalServerNicPortSpeed] = *bmsnic.PortSpeed - currentNic[isBareMetalServerNicInterfaceType] = "pci" - if len(bmsnic.SecurityGroups) != 0 { - secgrpList := []string{} - for i := 0; i < len(bmsnic.SecurityGroups); i++ { - secgrpList = append(secgrpList, string(*(bmsnic.SecurityGroups[i].ID))) + if intfc.PrimaryIP != nil { + primaryIpList := make([]map[string]interface{}, 0) + currentIP := map[string]interface{}{} + if intfc.PrimaryIP.Href != nil { + currentIP[isBareMetalServerNicIpAddress] = *intfc.PrimaryIP.Address + } + if intfc.PrimaryIP.Href != nil { + currentIP[isBareMetalServerNicIpHref] = *intfc.PrimaryIP.Href + } + if intfc.PrimaryIP.Name != nil { + currentIP[isBareMetalServerNicIpName] = *intfc.PrimaryIP.Name + } + if intfc.PrimaryIP.ID != nil { + ripId = *intfc.PrimaryIP.ID + currentIP[isBareMetalServerNicIpID] = ripId + getripoptions := &vpcv1.GetSubnetReservedIPOptions{ + SubnetID: &subnetId, + ID: &ripId, + } + bmsRip, response, err := sess.GetSubnetReservedIP(getripoptions) + if err != nil { + return fmt.Errorf("[ERROR] Error getting network interface reserved ip(%s) attached to the bare metal server network interface(%s): %s\n%s", ripId, nicId, err, response) } - currentNic[isBareMetalServerNicSecurityGroups] = flex.NewStringSet(schema.HashString, secgrpList) + if bmsRip.AutoDelete != nil { + currentIP[isBareMetalServerNicIpAutoDelete] = *bmsRip.AutoDelete + } + } + if intfc.PrimaryIP.ResourceType != nil { + currentIP[isBareMetalServerNicResourceType] = *intfc.PrimaryIP.ResourceType } + primaryIpList = append(primaryIpList, currentIP) + currentNic[isBareMetalServerNicPrimaryIP] = primaryIpList } - case "*vpcv1.BareMetalServerNetworkInterfaceByVlan": - { - bmsnic := bmsnicintf.(*vpcv1.BareMetalServerNetworkInterfaceByVlan) - currentNic[isBareMetalServerNicAllowIPSpoofing] = *bmsnic.AllowIPSpoofing - currentNic[isBareMetalServerNicEnableInfraNAT] = *bmsnic.EnableInfrastructureNat - currentNic[isBareMetalServerNicSubnet] = *bmsnic.Subnet.ID - currentNic[isBareMetalServerNicPortSpeed] = *bmsnic.PortSpeed - currentNic[isBareMetalServerNicInterfaceType] = "vlan" - - if len(bmsnic.SecurityGroups) != 0 { - secgrpList := []string{} - for i := 0; i < len(bmsnic.SecurityGroups); i++ { - secgrpList = append(secgrpList, string(*(bmsnic.SecurityGroups[i].ID))) + + switch reflect.TypeOf(bmsnicintf).String() { + case "*vpcv1.BareMetalServerNetworkInterfaceByPci": + { + bmsnic := bmsnicintf.(*vpcv1.BareMetalServerNetworkInterfaceByPci) + currentNic[isBareMetalServerNicAllowIPSpoofing] = *bmsnic.AllowIPSpoofing + currentNic[isBareMetalServerNicEnableInfraNAT] = *bmsnic.EnableInfrastructureNat + currentNic[isBareMetalServerNicPortSpeed] = *bmsnic.PortSpeed + currentNic[isBareMetalServerNicInterfaceType] = "pci" + if len(bmsnic.SecurityGroups) != 0 { + secgrpList := []string{} + for i := 0; i < len(bmsnic.SecurityGroups); i++ { + secgrpList = append(secgrpList, string(*(bmsnic.SecurityGroups[i].ID))) + } + currentNic[isBareMetalServerNicSecurityGroups] = flex.NewStringSet(schema.HashString, secgrpList) + } + if bmsnic.AllowedVlans != nil { + var out = make([]interface{}, len(bmsnic.AllowedVlans), len(bmsnic.AllowedVlans)) + for i, v := range bmsnic.AllowedVlans { + out[i] = int(v) + } + currentNic[isBareMetalServerNicAllowedVlans] = schema.NewSet(schema.HashInt, out) + } + } + case "*vpcv1.BareMetalServerNetworkInterfaceByVlan": + { + bmsnic := bmsnicintf.(*vpcv1.BareMetalServerNetworkInterfaceByVlan) + currentNic[isBareMetalServerNicAllowIPSpoofing] = *bmsnic.AllowIPSpoofing + currentNic[isBareMetalServerNicEnableInfraNAT] = *bmsnic.EnableInfrastructureNat + currentNic[isBareMetalServerNicPortSpeed] = *bmsnic.PortSpeed + currentNic[isBareMetalServerNicInterfaceType] = "vlan" + + if len(bmsnic.SecurityGroups) != 0 { + secgrpList := []string{} + for i := 0; i < len(bmsnic.SecurityGroups); i++ { + secgrpList = append(secgrpList, string(*(bmsnic.SecurityGroups[i].ID))) + } + currentNic[isBareMetalServerNicSecurityGroups] = flex.NewStringSet(schema.HashString, secgrpList) } - currentNic[isBareMetalServerNicSecurityGroups] = flex.NewStringSet(schema.HashString, secgrpList) } } + interfacesList = append(interfacesList, currentNic) } - interfacesList = append(interfacesList, currentNic) } + d.Set(isBareMetalServerNetworkInterfaces, interfacesList) } - d.Set(isBareMetalServerNetworkInterfaces, interfacesList) - d.Set(isBareMetalServerProfile, *bms.Profile.Name) if bms.ResourceGroup != nil { d.Set(isBareMetalServerResourceGroup, *bms.ResourceGroup.ID) @@ -1261,6 +1312,127 @@ func bareMetalServerUpdate(context context.Context, d *schema.ResourceData, meta } } + if d.HasChange(isBareMetalServerNetworkInterfaces) { + nics := d.Get(isBareMetalServerNetworkInterfaces).([]interface{}) + for i := range nics { + securitygrpKey := fmt.Sprintf("network_interfaces.%d.security_groups", i) + networkNameKey := fmt.Sprintf("network_interfaces.%d.name", i) + ipSpoofingKey := fmt.Sprintf("network_interfaces.%d.allow_ip_spoofing", i) + infraNatKey := fmt.Sprintf("network_interfaces.%d.enable_infrastructure_nat", i) + allowedVlans := fmt.Sprintf("network_interfaces.%d.allowed_vlans", i) + primaryipname := fmt.Sprintf("network_interfaces.%d.primary_ip.0.name", i) + primaryipauto := fmt.Sprintf("network_interfaces.%d.primary_ip.0.auto_delete", i) + primaryiprip := fmt.Sprintf("network_interfaces.%d.primary_ip.0.reserved_ip", i) + if d.HasChange(primaryipname) || d.HasChange(primaryipauto) { + subnetId := d.Get(isBareMetalServerNicSubnet).(string) + ripId := d.Get(primaryiprip).(string) + updateripoptions := &vpcv1.UpdateSubnetReservedIPOptions{ + SubnetID: &subnetId, + ID: &ripId, + } + reservedIpPath := &vpcv1.ReservedIPPatch{} + if d.HasChange(primaryipname) { + name := d.Get(primaryipname).(string) + reservedIpPath.Name = &name + } + if d.HasChange(primaryipauto) { + auto := d.Get(primaryipauto).(bool) + reservedIpPath.AutoDelete = &auto + } + reservedIpPathAsPatch, err := reservedIpPath.AsPatch() + if err != nil { + return fmt.Errorf("[ERROR] Error calling reserved ip as patch \n%s", err) + } + updateripoptions.ReservedIPPatch = reservedIpPathAsPatch + _, response, err := sess.UpdateSubnetReservedIP(updateripoptions) + if err != nil { + return fmt.Errorf("[ERROR] Error updating bare metal server network interface reserved ip(%s): %s\n%s", ripId, err, response) + } + } + + if d.HasChange(securitygrpKey) { + ovs, nvs := d.GetChange(securitygrpKey) + ov := ovs.(*schema.Set) + nv := nvs.(*schema.Set) + remove := flex.ExpandStringList(ov.Difference(nv).List()) + add := flex.ExpandStringList(nv.Difference(ov).List()) + if len(add) > 0 { + networkIDKey := fmt.Sprintf("network_interfaces.%d.id", i) + networkID := d.Get(networkIDKey).(string) + for i := range add { + createsgnicoptions := &vpcv1.CreateSecurityGroupTargetBindingOptions{ + SecurityGroupID: &add[i], + ID: &networkID, + } + _, response, err := sess.CreateSecurityGroupTargetBinding(createsgnicoptions) + if err != nil { + return fmt.Errorf("[ERROR] Error while creating security group %q for network interface of bare metal server %s\n%s: %q", add[i], d.Id(), err, response) + } + } + + } + if len(remove) > 0 { + networkIDKey := fmt.Sprintf("network_interfaces.%d.id", i) + networkID := d.Get(networkIDKey).(string) + for i := range remove { + deletesgnicoptions := &vpcv1.DeleteSecurityGroupTargetBindingOptions{ + SecurityGroupID: &remove[i], + ID: &networkID, + } + response, err := sess.DeleteSecurityGroupTargetBinding(deletesgnicoptions) + if err != nil { + return fmt.Errorf("[ERROR] Error while removing security group %q for network interface of instance %s\n%s: %q", remove[i], d.Id(), err, response) + } + } + } + + } + + if d.HasChange(allowedVlans) || d.HasChange(networkNameKey) || d.HasChange(ipSpoofingKey) || d.HasChange(infraNatKey) { + newName := d.Get(networkNameKey).(string) + networkIDKey := fmt.Sprintf("network_interfaces.%d.id", i) + networkID := d.Get(networkIDKey).(string) + ipSpoofing := d.Get(ipSpoofingKey).(bool) + infraNat := d.Get(infraNatKey).(bool) + updatepnicfoptions := &vpcv1.UpdateBareMetalServerNetworkInterfaceOptions{ + BareMetalServerID: &id, + ID: &networkID, + } + + bmsPatchModel := &vpcv1.BareMetalServerNetworkInterfacePatch{} + if d.HasChange(networkNameKey) { + bmsPatchModel.Name = &newName + } + if d.HasChange(allowedVlans) { + if allowedVlansOk, ok := d.GetOk(allowedVlans); ok { + allowedVlansList := allowedVlansOk.(*schema.Set).List() + allowedVlans := make([]int64, 0, len(allowedVlansList)) + for _, k := range allowedVlansList { + allowedVlans = append(allowedVlans, int64(k.(int))) + } + bmsPatchModel.AllowedVlans = allowedVlans + } + } + if d.HasChange(ipSpoofingKey) { + bmsPatchModel.AllowIPSpoofing = &ipSpoofing + } + if d.HasChange(infraNatKey) { + bmsPatchModel.EnableInfrastructureNat = &infraNat + } + networkInterfacePatch, err := bmsPatchModel.AsPatch() + if err != nil { + return fmt.Errorf("[ERROR] Error calling asPatch for BareMetalServerNetworkInterfacePatch: %s", err) + } + updatepnicfoptions.BareMetalServerNetworkInterfacePatch = networkInterfacePatch + + _, response, err := sess.UpdateBareMetalServerNetworkInterface(updatepnicfoptions) + if err != nil { + return fmt.Errorf("[ERROR] Error while updating network interface(%s) of bar emetal server(%s) \n%s: %q", networkID, d.Id(), err, response) + } + } + } + } + options := &vpcv1.UpdateBareMetalServerOptions{ ID: &id, } diff --git a/ibm/service/vpc/resource_ibm_is_bare_metal_server_action.go b/ibm/service/vpc/resource_ibm_is_bare_metal_server_action.go index 0f3288bb82..ba3f2b89ed 100644 --- a/ibm/service/vpc/resource_ibm_is_bare_metal_server_action.go +++ b/ibm/service/vpc/resource_ibm_is_bare_metal_server_action.go @@ -145,7 +145,7 @@ func resourceIBMISBareMetalServerActionCreate(context context.Context, d *schema if err != nil { return diag.FromErr(err) } - _, waitErr := isWaitForBareMetalServerActionStop(sess, d.Timeout(schema.TimeoutDelete), bareMetalServerId, d) + _, waitErr := isWaitForBareMetalServerActionStop(sess, d.Timeout(schema.TimeoutCreate), bareMetalServerId, d) if waitErr != nil { return diag.FromErr(waitErr) } @@ -259,7 +259,7 @@ func resourceIBMISBareMetalServerActionUpdate(context context.Context, d *schema if err != nil { return diag.FromErr(err) } - _, waitErr := isWaitForBareMetalServerActionStop(sess, d.Timeout(schema.TimeoutDelete), bareMetalServerId, d) + _, waitErr := isWaitForBareMetalServerActionStop(sess, d.Timeout(schema.TimeoutUpdate), bareMetalServerId, d) if waitErr != nil { return diag.FromErr(waitErr) } diff --git a/ibm/service/vpc/resource_ibm_is_instance.go b/ibm/service/vpc/resource_ibm_is_instance.go index 5ac5bcef42..b61eb5f316 100644 --- a/ibm/service/vpc/resource_ibm_is_instance.go +++ b/ibm/service/vpc/resource_ibm_is_instance.go @@ -2629,7 +2629,7 @@ func instanceUpdate(d *schema.ResourceData, meta interface{}) error { } if d.HasChange("primary_network_interface.0.primary_ip.0.name") || d.HasChange("primary_network_interface.0.primary_ip.0.auto_delete") { - subnetId := d.Get(isBareMetalServerNicSubnet).(string) + subnetId := d.Get("primary_network_interface.0.subnet").(string) ripId := d.Get("primary_network_interface.0.primary_ip.0.reserved_ip").(string) updateripoptions := &vpcv1.UpdateSubnetReservedIPOptions{ SubnetID: &subnetId, @@ -2689,12 +2689,13 @@ func instanceUpdate(d *schema.ResourceData, meta interface{}) error { for i := range nics { securitygrpKey := fmt.Sprintf("network_interfaces.%d.security_groups", i) networkNameKey := fmt.Sprintf("network_interfaces.%d.name", i) + subnetKey := fmt.Sprintf("network_interfaces.%d.subnet", i) ipSpoofingKey := fmt.Sprintf("network_interfaces.%d.allow_ip_spoofing", i) primaryipname := fmt.Sprintf("network_interfaces.%d.primary_ip.0.name", i) primaryipauto := fmt.Sprintf("network_interfaces.%d.primary_ip.0.auto_delete", i) primaryiprip := fmt.Sprintf("network_interfaces.%d.primary_ip.0.reserved_ip", i) if d.HasChange(primaryipname) || d.HasChange(primaryipauto) { - subnetId := d.Get(isBareMetalServerNicSubnet).(string) + subnetId := d.Get(subnetKey).(string) ripId := d.Get(primaryiprip).(string) updateripoptions := &vpcv1.UpdateSubnetReservedIPOptions{ SubnetID: &subnetId, diff --git a/ibm/service/vpc/resource_ibm_is_subnet_reserved_ip.go b/ibm/service/vpc/resource_ibm_is_subnet_reserved_ip.go index b520acbd1c..3c6e85bbe8 100644 --- a/ibm/service/vpc/resource_ibm_is_subnet_reserved_ip.go +++ b/ibm/service/vpc/resource_ibm_is_subnet_reserved_ip.go @@ -6,6 +6,7 @@ package vpc import ( "fmt" "log" + "reflect" "time" "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" @@ -206,9 +207,33 @@ func resourceIBMISReservedIPRead(d *schema.ResourceData, meta interface{}) error d.Set(isReservedIPOwner, *rip.Owner) d.Set(isReservedIPType, *rip.ResourceType) if rip.Target != nil { - target, ok := rip.Target.(*vpcv1.ReservedIPTarget) - if ok { - d.Set(isReservedIPTarget, target.ID) + targetIntf := rip.Target + switch reflect.TypeOf(targetIntf).String() { + case "*vpcv1.ReservedIPTargetEndpointGatewayReference": + { + target := targetIntf.(*vpcv1.ReservedIPTargetEndpointGatewayReference) + d.Set(isReservedIPTarget, target.ID) + } + case "*vpcv1.ReservedIPTargetNetworkInterfaceReferenceTargetContext": + { + target := targetIntf.(*vpcv1.ReservedIPTargetNetworkInterfaceReferenceTargetContext) + d.Set(isReservedIPTarget, target.ID) + } + case "*vpcv1.ReservedIPTargetLoadBalancerReference": + { + target := targetIntf.(*vpcv1.ReservedIPTargetLoadBalancerReference) + d.Set(isReservedIPTarget, target.ID) + } + case "*vpcv1.ReservedIPTargetVPNGatewayReference": + { + target := targetIntf.(*vpcv1.ReservedIPTargetVPNGatewayReference) + d.Set(isReservedIPTarget, target.ID) + } + case "*vpcv1.ReservedIPTarget": + { + target := targetIntf.(*vpcv1.ReservedIPTarget) + d.Set(isReservedIPTarget, target.ID) + } } } } diff --git a/ibm/service/vpc/resource_ibm_is_subnet_reserved_ip_test.go b/ibm/service/vpc/resource_ibm_is_subnet_reserved_ip_test.go index 995a1d8198..34bd6bc8c3 100644 --- a/ibm/service/vpc/resource_ibm_is_subnet_reserved_ip_test.go +++ b/ibm/service/vpc/resource_ibm_is_subnet_reserved_ip_test.go @@ -47,6 +47,29 @@ func TestAccIBMISSubnetReservedIPResource_basic(t *testing.T) { }, }) } +func TestAccIBMISSubnetReservedIPResource_address(t *testing.T) { + var reservedIPID string + vpcName := fmt.Sprintf("tfresip-vpc-%d", acctest.RandIntRange(10, 100)) + subnetName := fmt.Sprintf("tfresip-subnet-%d", acctest.RandIntRange(10, 100)) + reservedIPName := fmt.Sprintf("tfresip-reservedip-%d", acctest.RandIntRange(10, 100)) + terraformTag := "ibm_is_subnet_reserved_ip.resIP1" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckisSubnetReservedIPDestroy, + Steps: []resource.TestStep{ + { + // Tests create + Config: testAccCheckISSubnetReservedIPConfigAddress(vpcName, subnetName, reservedIPName), + Check: resource.ComposeTestCheckFunc( + testAccCheckISSubnetReservedIPExists(terraformTag, &reservedIPID), + resource.TestCheckResourceAttr(terraformTag, "name", reservedIPName), + resource.TestCheckResourceAttr(terraformTag, "address", "10.240.0.14"), + ), + }, + }, + }) +} func testAccCheckisSubnetReservedIPDestroy(s *terraform.State) error { sess, err := acc.TestAccProvider.Meta().(conns.ClientSession).VpcV1API() @@ -131,3 +154,23 @@ func testAccCheckISSubnetReservedIPConfigBasic(vpcName, subnetName, resIPName st } `, vpcName, subnetName, resIPName) } +func testAccCheckISSubnetReservedIPConfigAddress(vpcName, subnetName, resIPName string) string { + return fmt.Sprintf(` + resource "ibm_is_vpc" "vpc1" { + name = "%s" + } + + resource "ibm_is_subnet" "subnet1" { + name = "%s" + vpc = ibm_is_vpc.vpc1.id + zone = "%s" + ipv4_cidr_block = "%s" + } + + resource "ibm_is_subnet_reserved_ip" "resIP1" { + subnet = ibm_is_subnet.subnet1.id + name = "%s" + address = "${replace(ibm_is_subnet.subnet1.ipv4_cidr_block, "0/24", "14")}" + } + `, vpcName, subnetName, acc.ISZoneName, acc.ISCIDR, resIPName) +} diff --git a/website/docs/r/is_bare_metal_server.markdown b/website/docs/r/is_bare_metal_server.markdown index 16f4f7261d..8c7649a094 100644 --- a/website/docs/r/is_bare_metal_server.markdown +++ b/website/docs/r/is_bare_metal_server.markdown @@ -60,23 +60,6 @@ resource "ibm_is_bare_metal_server" "example" { ``` ### Reserved ip example ```terraform - -resource "ibm_is_vpc" "example" { - name = "example-vpc" -} - -resource "ibm_is_subnet" "example" { - name = "example-subnet" - vpc = ibm_is_vpc.example.id - zone = "us-south-3" - ipv4_cidr_block = "10.240.129.0/24" -} - -resource "ibm_is_ssh_key" "example" { - name = "example-ssh" - public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCKVmnMOlHKcZK8tpt3MP1lqOLAcqcJzhsvJcjscgVERRN7/9484SOBJ3HSKxxNG5JN8owAjy5f9yYwcUg+JaUVuytn5Pv3aeYROHGGg+5G346xaq3DAwX6Y5ykr2fvjObgncQBnuU5KHWCECO/4h8uWuwh/kfniXPVjFToc+gnkqA+3RKpAecZhFXwfalQ9mMuYGFxn+fwn8cYEApsJbsEmb0iJwPiZ5hjFC8wREuiTlhPHDgkBLOiycd20op2nXzDbHfCHInquEe/gYxEitALONxm0swBOwJZwlTDOB7C6y2dzlrtxr1L59m7pCkWI4EtTRLvleehBoj3u7jB4usR" -} - resource "ibm_is_bare_metal_server" "bms" { profile = "mx2d-metal-32x192" name = "example-bms" diff --git a/website/docs/r/is_instance.html.markdown b/website/docs/r/is_instance.html.markdown index ad5bfae819..5a77de8db6 100644 --- a/website/docs/r/is_instance.html.markdown +++ b/website/docs/r/is_instance.html.markdown @@ -76,6 +76,45 @@ resource "ibm_is_instance" "example" { delete = "15m" } } + + +// Example to provision instance using subnet reserved ip + +resource "ibm_is_subnet_reserved_ip" "example" { + subnet = ibm_is_subnet.example.id + name = "example-reserved-ip1" + address = "${replace(ibm_is_subnet.example.ipv4_cidr_block, "0/24", "13")}" +} + +resource "ibm_is_instance" "example1" { + name = "example-instance-reserved-ip" + image = ibm_is_image.example.id + profile = "bc1-2x8" + metadata_service_enabled = false + + primary_network_interface { + name = "eth0" + subnet = ibm_is_subnet.example.id + primary_ip { + reserved_ip = ibm_is_subnet_reserved_ip.example.reserved_ip + } + } + network_interfaces { + name = "eth1" + subnet = ibm_is_subnet.example.id + allow_ip_spoofing = false + primary_ip { + name = "example-reserved-ip1" + auto_delete = true + address = "${replace(ibm_is_subnet.example.ipv4_cidr_block, "0/24", "14")}" + } + } + + vpc = ibm_is_vpc.example.id + zone = "us-south-1" + keys = [ibm_is_ssh_key.example.id] +} + ``` ### Sample for creating an instance with custom security group rules. @@ -291,11 +330,11 @@ Review the argument references that you can specify for your resource. - `force_recovery_time` - (Optional, Integer) Define timeout (in minutes), to force the `is_instance` to recover from a perpetual "starting" state, during provisioning. And to force the is_instance to recover from a perpetual "stopping" state, during removal of user access. ~>**Note:** The force_recovery_time is used to retry multiple times until timeout. -- `image` - (Optional, String) The ID of the virtual server image that you want to use. To list supported images, run `ibmcloud is images` or use `ibm_is_images` datasource. +- `image` - (Required, String) The ID of the virtual server image that you want to use. To list supported images, run `ibmcloud is images` or use `ibm_is_images` datasource. ~> **Note:** - `image` conflicts with `boot_volume.0.snapshot` -- `keys` - (Optional, List) A comma-separated list of SSH keys that you want to add to your instance. + `image` conflicts with `boot_volume.0.snapshot`, not required when creating instance using `instance_template` +- `keys` - (Required, List) A comma-separated list of SSH keys that you want to add to your instance. - `metadata_service_enabled` - (Optional, Boolean) Indicates whether the metadata service endpoint is available to the virtual server instance. Default value : **false** - `name` - (Optional, String) The instance name. - `network_interfaces` (Optional, Forces new resource, List) A list of more network interfaces that are set up for the instance. @@ -313,11 +352,11 @@ Review the argument references that you can specify for your resource. - `address` - (Optional, String) The IP address. If the address has not yet been selected, the value will be 0.0.0.0. This property may add support for IPv6 addresses in the future. When processing a value in this property, verify that the address is in an expected format. If it is not, log an error. Optionally halt processing and surface the error, or bypass the resource on which the unexpected IP address format was encountered. - `name`- (Optional, String) The user-defined or system-provided name for this reserved IP - `reserved_ip`- (Optional, String) The unique identifier for this reserved IP - - `primary_ipv4_address` - (Optional, Forces new resource, String) The IPV4 address of the interface. + - `primary_ipv4_address` - (Optional, Deprecated, Forces new resource, String) The IPV4 address of the interface. `primary_ipv4_address` is depreated, use `primary_ip` instead. - `subnet` - (Required, String) The ID of the subnet. - `security_groups`- (Optional, List of strings)A comma separated list of security groups to add to the primary network interface. - `placement_group` - (Optional, string) Unique Identifier of the Placement Group for restricting the placement of the instance -- `primary_network_interface` - (Optional, List) A nested block describes the primary network interface of this instance. Only one primary network interface can be specified for an instance. +- `primary_network_interface` - (Required, List) A nested block describes the primary network interface of this instance. Only one primary network interface can be specified for an instance. When using `instance_template`, `primary_network_interface` is not required. Nested scheme for `primary_network_interface`: - `allow_ip_spoofing`- (Optional, Bool) Indicates whether IP spoofing is allowed on the interface. If **false**, IP spoofing is prevented on the interface. If **true**, IP spoofing is allowed on the interface. @@ -333,26 +372,26 @@ Review the argument references that you can specify for your resource. - `address` - (Optional, String) The IP address. If the address has not yet been selected, the value will be 0.0.0.0. This property may add support for IPv6 addresses in the future. When processing a value in this property, verify that the address is in an expected format. If it is not, log an error. Optionally halt processing and surface the error, or bypass the resource on which the unexpected IP address format was encountered. - `name`- (Optional, String) The user-defined or system-provided name for this reserved IP - `reserved_ip`- (Optional, String) The unique identifier for this reserved IP - - `primary_ipv4_address` - (Optional, Deprecated, Forces new resource, String) The IPV4 address of the interface. Use `primary_ip` instead. + - `primary_ipv4_address` - (Optional, Deprecated, Forces new resource, String) The IPV4 address of the interface.`primary_ipv4_address` is depreated, use `primary_ip` instead. - `subnet` - (Required, String) The ID of the subnet. - `security_groups`-List of strings-Optional-A comma separated list of security groups to add to the primary network interface. -- `profile` - (Optional, String) The name of the profile that you want to use for your instance. To list supported profiles, run `ibmcloud is instance-profiles`. +- `profile` - (Required, String) The name of the profile that you want to use for your instance. Not required when using `instance_template`. To list supported profiles, run `ibmcloud is instance-profiles` or `ibm_is_instance_profiles` datasource. **NOTE:** When the `profile` is changed, the VSI is restarted. The new profile must: 1. Have matching instance disk support. Any disks associated with the current profile will be deleted, and any disks associated with the requested profile will be created. 2. Be compatible with any placement_target(`dedicated_host`, `dedicated_host_group`, `placement_group`) constraints. For example, if the instance is placed on a dedicated host, the requested profile family must be the same as the dedicated host family. - `resource_group` - (Optional, Forces new resource, String) The ID of the resource group where you want to create the instance. -- `instance_template` - (Optional, String) ID of the source template. +- `instance_template` - (Optional, String) ID of the instance template to create the instance from. To create an instance template, use `ibm_is_instance_template` resource. ~> **Note:** - `instance_template` conflicts with `boot_volume.0.snapshot` + `instance_template` conflicts with `boot_volume.0.snapshot`. When creating an instance using `instance_template`, [`image `, `primary_network_interface`, `vpc`, `zone`] are not required. - `tags` (Optional, Array of Strings) A list of tags that you want to add to your instance. Tags can help you find your instance more easily later. - `total_volume_bandwidth` - (Optional, Integer) The amount of bandwidth (in megabits per second) allocated exclusively to instance storage volumes - `user_data` - (Optional, String) User data to transfer to the instance. - `volumes` (Optional, List) A comma separated list of volume IDs to attach to the instance. -- `vpc` - (Optional, Forces new resource, String) The ID of the VPC where you want to create the instance. -- `zone` - (Optional, Forces new resource, String) The name of the VPC zone where you want to create the instance. +- `vpc` - (Required, Forces new resource, String) The ID of the VPC where you want to create the instance. When using `instance_template`, `vpc` is not required. +- `zone` - (Required, Forces new resource, String) The name of the VPC zone where you want to create the instance. When using `instance_template`, `zone` is not required. ## Attribute reference diff --git a/website/docs/r/is_subnet_reserved_ip.html.markdown b/website/docs/r/is_subnet_reserved_ip.html.markdown index 0a153883fe..8e90bb9deb 100644 --- a/website/docs/r/is_subnet_reserved_ip.html.markdown +++ b/website/docs/r/is_subnet_reserved_ip.html.markdown @@ -47,7 +47,7 @@ resource "ibm_is_subnet_reserved_ip" "example" { // Subnet ID with a given name resource "ibm_is_subnet_reserved_ip" "example1" { subnet = ibm_is_subnet.example.id - name = "example-subnet-reserved-ip" + name = "example-subnet-reserved-ip1" } // Subnet ID with auto_delete @@ -59,7 +59,15 @@ resource "ibm_is_subnet_reserved_ip" "example2" { // Subnet ID with both name and auto_delete resource "ibm_is_subnet_reserved_ip" "example3" { subnet = ibm_is_subnet.example.id - name = "example-subnet-reserved-ip" + name = "example-subnet-reserved-ip3" + auto_delete = true +} + +// Subnet ID with address, name and auto_delete +resource "ibm_is_subnet_reserved_ip" "example4" { + subnet = ibm_is_subnet.example.id + address = "${replace(ibm_is_subnet.example.ipv4_cidr_block, "0/24", "14")}" + name = "example-subnet-reserved-ip4" auto_delete = true } @@ -72,9 +80,9 @@ resource "ibm_is_virtual_endpoint_gateway" "example" { } vpc = ibm_is_vpc.example.id } -resource "ibm_is_subnet_reserved_ip" "example4" { +resource "ibm_is_subnet_reserved_ip" "example5" { subnet = ibm_is_subnet.example.id - name = "example-subnet-reserved-ip" + name = "example-subnet-reserved-ip5" target = ibm_is_virtual_endpoint_gateway.example.id } ``` @@ -82,7 +90,7 @@ resource "ibm_is_subnet_reserved_ip" "example4" { ## Argument reference Review the argument references that you can specify for your resource. -- `address` - (Optional, String) The IP address. +- `address` - (Optional, Forces new resource, String) The IP address. - `auto_delete`- (Optional, Bool) If reserved IP is auto deleted. - `name` - (Optional, String) The name of the reserved IP. ~> **NOTE:** raise error if name is given with a prefix `ibm- `. - `subnet` - (Required, Forces new resource, String) The subnet ID for the reserved IP. @@ -98,7 +106,7 @@ In addition to all argument reference list, you can access the following attribu - `owner` - (String) The owner of a reserved IP, defining whether it is managed by the user or the provider. - `reserved_ip` - (String) The reserved IP. - `resource_type` - (String) The resource type. -- `target` - (String) The ID for the target endpoint gateway for the reserved IP. +- `target` - (String) The ID for the target for the reserved IP. ## Import The `ibm_is_subnet_reserved_ip` and `ibm_is_subnet` resource can be imported by using subnet ID and reserved IP ID separated by **/**. @@ -106,7 +114,7 @@ The `ibm_is_subnet_reserved_ip` and `ibm_is_subnet` resource can be imported by **Syntax** ``` -$ terraform import ibm_is_subnet.example +$ terraform import ibm_is_subnet.example / ``` **Example**