github.com/recobe182/terraform@v0.8.5-0.20170117231232-49ab22a935b7/builtin/providers/openstack/resource_openstack_lb_member_v2.go (about) 1 package openstack 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/hashicorp/terraform/helper/resource" 9 "github.com/hashicorp/terraform/helper/schema" 10 11 "github.com/gophercloud/gophercloud" 12 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" 13 ) 14 15 func resourceMemberV2() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceMemberV2Create, 18 Read: resourceMemberV2Read, 19 Update: resourceMemberV2Update, 20 Delete: resourceMemberV2Delete, 21 22 Schema: map[string]*schema.Schema{ 23 "region": &schema.Schema{ 24 Type: schema.TypeString, 25 Required: true, 26 ForceNew: true, 27 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 28 }, 29 30 "name": &schema.Schema{ 31 Type: schema.TypeString, 32 Optional: true, 33 }, 34 35 "tenant_id": &schema.Schema{ 36 Type: schema.TypeString, 37 Optional: true, 38 Computed: true, 39 ForceNew: true, 40 }, 41 42 "address": &schema.Schema{ 43 Type: schema.TypeString, 44 Required: true, 45 ForceNew: true, 46 }, 47 48 "protocol_port": &schema.Schema{ 49 Type: schema.TypeInt, 50 Required: true, 51 ForceNew: true, 52 }, 53 54 "weight": &schema.Schema{ 55 Type: schema.TypeInt, 56 Optional: true, 57 Computed: true, 58 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 59 value := v.(int) 60 if value < 1 { 61 errors = append(errors, fmt.Errorf( 62 "Only numbers greater than 0 are supported values for 'weight'")) 63 } 64 return 65 }, 66 }, 67 68 "subnet_id": &schema.Schema{ 69 Type: schema.TypeString, 70 Required: true, 71 ForceNew: true, 72 }, 73 74 "admin_state_up": &schema.Schema{ 75 Type: schema.TypeBool, 76 Default: true, 77 Optional: true, 78 }, 79 80 "pool_id": &schema.Schema{ 81 Type: schema.TypeString, 82 Required: true, 83 ForceNew: true, 84 }, 85 86 "id": &schema.Schema{ 87 Type: schema.TypeString, 88 Optional: true, 89 Computed: true, 90 }, 91 }, 92 } 93 } 94 95 func resourceMemberV2Create(d *schema.ResourceData, meta interface{}) error { 96 config := meta.(*Config) 97 networkingClient, err := config.networkingV2Client(GetRegion(d)) 98 if err != nil { 99 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 100 } 101 102 adminStateUp := d.Get("admin_state_up").(bool) 103 createOpts := pools.CreateMemberOpts{ 104 Name: d.Get("name").(string), 105 TenantID: d.Get("tenant_id").(string), 106 Address: d.Get("address").(string), 107 ProtocolPort: d.Get("protocol_port").(int), 108 Weight: d.Get("weight").(int), 109 AdminStateUp: &adminStateUp, 110 } 111 112 // Must omit if not set 113 if v, ok := d.GetOk("subnet_id"); ok { 114 createOpts.SubnetID = v.(string) 115 } 116 117 poolID := d.Get("pool_id").(string) 118 119 log.Printf("[DEBUG] Create Options: %#v", createOpts) 120 121 var member *pools.Member 122 err = resource.Retry(10*time.Minute, func() *resource.RetryError { 123 var err error 124 log.Printf("[DEBUG] Attempting to create LBaaSV2 member") 125 member, err = pools.CreateMember(networkingClient, poolID, createOpts).Extract() 126 if err != nil { 127 if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok { 128 if errCode.Actual == 409 || errCode.Actual == 500 { 129 log.Printf("[DEBUG] OpenStack LBaaSV2 member is still creating.") 130 return resource.RetryableError(err) 131 } 132 } 133 return resource.NonRetryableError(err) 134 } 135 136 return nil 137 }) 138 139 if err != nil { 140 return fmt.Errorf("Error creating OpenStack LBaaSV2 member: %s", err) 141 } 142 log.Printf("[INFO] member ID: %s", member.ID) 143 144 log.Printf("[DEBUG] Waiting for Openstack LBaaSV2 member (%s) to become available.", member.ID) 145 146 stateConf := &resource.StateChangeConf{ 147 Pending: []string{"PENDING_CREATE"}, 148 Target: []string{"ACTIVE"}, 149 Refresh: waitForMemberActive(networkingClient, poolID, member.ID), 150 Timeout: 2 * time.Minute, 151 Delay: 5 * time.Second, 152 MinTimeout: 3 * time.Second, 153 } 154 155 _, err = stateConf.WaitForState() 156 if err != nil { 157 return err 158 } 159 160 d.SetId(member.ID) 161 162 return resourceMemberV2Read(d, meta) 163 } 164 165 func resourceMemberV2Read(d *schema.ResourceData, meta interface{}) error { 166 config := meta.(*Config) 167 networkingClient, err := config.networkingV2Client(GetRegion(d)) 168 if err != nil { 169 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 170 } 171 172 member, err := pools.GetMember(networkingClient, d.Get("pool_id").(string), d.Id()).Extract() 173 if err != nil { 174 return CheckDeleted(d, err, "LBV2 Member") 175 } 176 177 log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 Member %s: %+v", d.Id(), member) 178 179 d.Set("name", member.Name) 180 d.Set("weight", member.Weight) 181 d.Set("admin_state_up", member.AdminStateUp) 182 d.Set("tenant_id", member.TenantID) 183 d.Set("subnet_id", member.SubnetID) 184 d.Set("address", member.Address) 185 d.Set("protocol_port", member.ProtocolPort) 186 d.Set("id", member.ID) 187 188 return nil 189 } 190 191 func resourceMemberV2Update(d *schema.ResourceData, meta interface{}) error { 192 config := meta.(*Config) 193 networkingClient, err := config.networkingV2Client(GetRegion(d)) 194 if err != nil { 195 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 196 } 197 198 var updateOpts pools.UpdateMemberOpts 199 if d.HasChange("name") { 200 updateOpts.Name = d.Get("name").(string) 201 } 202 if d.HasChange("weight") { 203 updateOpts.Weight = d.Get("weight").(int) 204 } 205 if d.HasChange("admin_state_up") { 206 asu := d.Get("admin_state_up").(bool) 207 updateOpts.AdminStateUp = &asu 208 } 209 210 log.Printf("[DEBUG] Updating OpenStack LBaaSV2 Member %s with options: %+v", d.Id(), updateOpts) 211 212 _, err = pools.UpdateMember(networkingClient, d.Get("pool_id").(string), d.Id(), updateOpts).Extract() 213 if err != nil { 214 return fmt.Errorf("Error updating OpenStack LBaaSV2 Member: %s", err) 215 } 216 217 return resourceMemberV2Read(d, meta) 218 } 219 220 func resourceMemberV2Delete(d *schema.ResourceData, meta interface{}) error { 221 config := meta.(*Config) 222 networkingClient, err := config.networkingV2Client(GetRegion(d)) 223 if err != nil { 224 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 225 } 226 227 stateConf := &resource.StateChangeConf{ 228 Pending: []string{"ACTIVE", "PENDING_DELETE"}, 229 Target: []string{"DELETED"}, 230 Refresh: waitForMemberDelete(networkingClient, d.Get("pool_id").(string), d.Id()), 231 Timeout: 2 * time.Minute, 232 Delay: 5 * time.Second, 233 MinTimeout: 3 * time.Second, 234 } 235 236 _, err = stateConf.WaitForState() 237 if err != nil { 238 return fmt.Errorf("Error deleting OpenStack LBaaSV2 Member: %s", err) 239 } 240 241 d.SetId("") 242 return nil 243 } 244 245 func waitForMemberActive(networkingClient *gophercloud.ServiceClient, poolID string, memberID string) resource.StateRefreshFunc { 246 return func() (interface{}, string, error) { 247 member, err := pools.GetMember(networkingClient, poolID, memberID).Extract() 248 if err != nil { 249 return nil, "", err 250 } 251 252 // The member resource has no Status attribute, so a successful Get is the best we can do 253 log.Printf("[DEBUG] OpenStack LBaaSV2 Member: %+v", member) 254 return member, "ACTIVE", nil 255 } 256 } 257 258 func waitForMemberDelete(networkingClient *gophercloud.ServiceClient, poolID string, memberID string) resource.StateRefreshFunc { 259 return func() (interface{}, string, error) { 260 log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 Member %s", memberID) 261 262 member, err := pools.GetMember(networkingClient, poolID, memberID).Extract() 263 if err != nil { 264 if _, ok := err.(gophercloud.ErrDefault404); ok { 265 log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID) 266 return member, "DELETED", nil 267 } 268 return member, "ACTIVE", err 269 } 270 271 log.Printf("[DEBUG] Openstack LBaaSV2 Member: %+v", member) 272 err = pools.DeleteMember(networkingClient, poolID, memberID).ExtractErr() 273 if err != nil { 274 if _, ok := err.(gophercloud.ErrDefault404); ok { 275 log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID) 276 return member, "DELETED", nil 277 } 278 279 if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok { 280 if errCode.Actual == 409 { 281 log.Printf("[DEBUG] OpenStack LBaaSV2 Member (%s) is still in use.", memberID) 282 return member, "PENDING_DELETE", nil 283 } 284 } 285 286 return member, "ACTIVE", err 287 } 288 289 log.Printf("[DEBUG] OpenStack LBaaSV2 Member %s still active.", memberID) 290 return member, "ACTIVE", nil 291 } 292 }