github.com/andresvia/terraform@v0.6.15-0.20160412045437-d51c75946785/builtin/providers/openstack/resource_openstack_lb_pool_v1.go (about) 1 package openstack 2 3 import ( 4 "bytes" 5 "fmt" 6 "log" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/hashcode" 10 "github.com/hashicorp/terraform/helper/resource" 11 "github.com/hashicorp/terraform/helper/schema" 12 13 "github.com/rackspace/gophercloud" 14 "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members" 15 "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools" 16 "github.com/rackspace/gophercloud/pagination" 17 ) 18 19 func resourceLBPoolV1() *schema.Resource { 20 return &schema.Resource{ 21 Create: resourceLBPoolV1Create, 22 Read: resourceLBPoolV1Read, 23 Update: resourceLBPoolV1Update, 24 Delete: resourceLBPoolV1Delete, 25 26 Schema: map[string]*schema.Schema{ 27 "region": &schema.Schema{ 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 32 }, 33 "name": &schema.Schema{ 34 Type: schema.TypeString, 35 Required: true, 36 ForceNew: false, 37 }, 38 "protocol": &schema.Schema{ 39 Type: schema.TypeString, 40 Required: true, 41 ForceNew: true, 42 }, 43 "subnet_id": &schema.Schema{ 44 Type: schema.TypeString, 45 Required: true, 46 ForceNew: true, 47 }, 48 49 "lb_method": &schema.Schema{ 50 Type: schema.TypeString, 51 Required: true, 52 ForceNew: false, 53 }, 54 "tenant_id": &schema.Schema{ 55 Type: schema.TypeString, 56 Optional: true, 57 ForceNew: true, 58 Computed: true, 59 }, 60 "member": &schema.Schema{ 61 Type: schema.TypeSet, 62 Optional: true, 63 Elem: &schema.Resource{ 64 Schema: map[string]*schema.Schema{ 65 "region": &schema.Schema{ 66 Type: schema.TypeString, 67 Required: true, 68 ForceNew: true, 69 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 70 }, 71 "tenant_id": &schema.Schema{ 72 Type: schema.TypeString, 73 Optional: true, 74 ForceNew: true, 75 }, 76 "address": &schema.Schema{ 77 Type: schema.TypeString, 78 Required: true, 79 ForceNew: true, 80 }, 81 "port": &schema.Schema{ 82 Type: schema.TypeInt, 83 Required: true, 84 ForceNew: true, 85 }, 86 "admin_state_up": &schema.Schema{ 87 Type: schema.TypeBool, 88 Required: true, 89 ForceNew: false, 90 }, 91 }, 92 }, 93 Set: resourceLBMemberV1Hash, 94 }, 95 "monitor_ids": &schema.Schema{ 96 Type: schema.TypeSet, 97 Optional: true, 98 ForceNew: false, 99 Elem: &schema.Schema{Type: schema.TypeString}, 100 Set: schema.HashString, 101 }, 102 }, 103 } 104 } 105 106 func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error { 107 config := meta.(*Config) 108 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 109 if err != nil { 110 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 111 } 112 113 createOpts := pools.CreateOpts{ 114 Name: d.Get("name").(string), 115 Protocol: d.Get("protocol").(string), 116 SubnetID: d.Get("subnet_id").(string), 117 LBMethod: d.Get("lb_method").(string), 118 TenantID: d.Get("tenant_id").(string), 119 } 120 121 log.Printf("[DEBUG] Create Options: %#v", createOpts) 122 p, err := pools.Create(networkingClient, createOpts).Extract() 123 if err != nil { 124 return fmt.Errorf("Error creating OpenStack LB pool: %s", err) 125 } 126 log.Printf("[INFO] LB Pool ID: %s", p.ID) 127 128 log.Printf("[DEBUG] Waiting for OpenStack LB pool (%s) to become available.", p.ID) 129 130 stateConf := &resource.StateChangeConf{ 131 Pending: []string{"PENDING_CREATE"}, 132 Target: []string{"ACTIVE"}, 133 Refresh: waitForLBPoolActive(networkingClient, p.ID), 134 Timeout: 2 * time.Minute, 135 Delay: 5 * time.Second, 136 MinTimeout: 3 * time.Second, 137 } 138 139 _, err = stateConf.WaitForState() 140 if err != nil { 141 return err 142 } 143 144 d.SetId(p.ID) 145 146 if mIDs := resourcePoolMonitorIDsV1(d); mIDs != nil { 147 for _, mID := range mIDs { 148 _, err := pools.AssociateMonitor(networkingClient, p.ID, mID).Extract() 149 if err != nil { 150 return fmt.Errorf("Error associating monitor (%s) with OpenStack LB pool (%s): %s", mID, p.ID, err) 151 } 152 } 153 } 154 155 if memberOpts := resourcePoolMembersV1(d); memberOpts != nil { 156 for _, memberOpt := range memberOpts { 157 _, err := members.Create(networkingClient, memberOpt).Extract() 158 if err != nil { 159 return fmt.Errorf("Error creating OpenStack LB member: %s", err) 160 } 161 } 162 } 163 164 return resourceLBPoolV1Read(d, meta) 165 } 166 167 func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error { 168 config := meta.(*Config) 169 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 170 if err != nil { 171 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 172 } 173 174 p, err := pools.Get(networkingClient, d.Id()).Extract() 175 if err != nil { 176 return CheckDeleted(d, err, "LB pool") 177 } 178 179 log.Printf("[DEBUG] Retreived OpenStack LB Pool %s: %+v", d.Id(), p) 180 181 d.Set("name", p.Name) 182 d.Set("protocol", p.Protocol) 183 d.Set("subnet_id", p.SubnetID) 184 d.Set("lb_method", p.LBMethod) 185 d.Set("tenant_id", p.TenantID) 186 d.Set("monitor_ids", p.MonitorIDs) 187 d.Set("member_ids", p.MemberIDs) 188 189 return nil 190 } 191 192 func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error { 193 config := meta.(*Config) 194 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 195 if err != nil { 196 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 197 } 198 199 var updateOpts pools.UpdateOpts 200 // If either option changed, update both. 201 // Gophercloud complains if one is empty. 202 if d.HasChange("name") || d.HasChange("lb_method") { 203 updateOpts.Name = d.Get("name").(string) 204 updateOpts.LBMethod = d.Get("lb_method").(string) 205 } 206 207 log.Printf("[DEBUG] Updating OpenStack LB Pool %s with options: %+v", d.Id(), updateOpts) 208 209 _, err = pools.Update(networkingClient, d.Id(), updateOpts).Extract() 210 if err != nil { 211 return fmt.Errorf("Error updating OpenStack LB Pool: %s", err) 212 } 213 214 if d.HasChange("monitor_ids") { 215 oldMIDsRaw, newMIDsRaw := d.GetChange("security_groups") 216 oldMIDsSet, newMIDsSet := oldMIDsRaw.(*schema.Set), newMIDsRaw.(*schema.Set) 217 monitorsToAdd := newMIDsSet.Difference(oldMIDsSet) 218 monitorsToRemove := oldMIDsSet.Difference(newMIDsSet) 219 220 log.Printf("[DEBUG] Monitors to add: %v", monitorsToAdd) 221 222 log.Printf("[DEBUG] Monitors to remove: %v", monitorsToRemove) 223 224 for _, m := range monitorsToAdd.List() { 225 _, err := pools.AssociateMonitor(networkingClient, d.Id(), m.(string)).Extract() 226 if err != nil { 227 return fmt.Errorf("Error associating monitor (%s) with OpenStack server (%s): %s", m.(string), d.Id(), err) 228 } 229 log.Printf("[DEBUG] Associated monitor (%s) with pool (%s)", m.(string), d.Id()) 230 } 231 232 for _, m := range monitorsToRemove.List() { 233 _, err := pools.DisassociateMonitor(networkingClient, d.Id(), m.(string)).Extract() 234 if err != nil { 235 return fmt.Errorf("Error disassociating monitor (%s) from OpenStack server (%s): %s", m.(string), d.Id(), err) 236 } 237 log.Printf("[DEBUG] Disassociated monitor (%s) from pool (%s)", m.(string), d.Id()) 238 } 239 } 240 241 if d.HasChange("member") { 242 oldMembersRaw, newMembersRaw := d.GetChange("member") 243 oldMembersSet, newMembersSet := oldMembersRaw.(*schema.Set), newMembersRaw.(*schema.Set) 244 membersToAdd := newMembersSet.Difference(oldMembersSet) 245 membersToRemove := oldMembersSet.Difference(newMembersSet) 246 247 log.Printf("[DEBUG] Members to add: %v", membersToAdd) 248 249 log.Printf("[DEBUG] Members to remove: %v", membersToRemove) 250 251 for _, m := range membersToRemove.List() { 252 oldMember := resourcePoolMemberV1(d, m) 253 listOpts := members.ListOpts{ 254 PoolID: d.Id(), 255 Address: oldMember.Address, 256 ProtocolPort: oldMember.ProtocolPort, 257 } 258 err = members.List(networkingClient, listOpts).EachPage(func(page pagination.Page) (bool, error) { 259 extractedMembers, err := members.ExtractMembers(page) 260 if err != nil { 261 return false, err 262 } 263 for _, member := range extractedMembers { 264 err := members.Delete(networkingClient, member.ID).ExtractErr() 265 if err != nil { 266 return false, fmt.Errorf("Error deleting member (%s) from OpenStack LB pool (%s): %s", member.ID, d.Id(), err) 267 } 268 log.Printf("[DEBUG] Deleted member (%s) from pool (%s)", member.ID, d.Id()) 269 } 270 return true, nil 271 }) 272 } 273 274 for _, m := range membersToAdd.List() { 275 createOpts := resourcePoolMemberV1(d, m) 276 newMember, err := members.Create(networkingClient, createOpts).Extract() 277 if err != nil { 278 return fmt.Errorf("Error creating LB member: %s", err) 279 } 280 log.Printf("[DEBUG] Created member (%s) in OpenStack LB pool (%s)", newMember.ID, d.Id()) 281 } 282 } 283 284 return resourceLBPoolV1Read(d, meta) 285 } 286 287 func resourceLBPoolV1Delete(d *schema.ResourceData, meta interface{}) error { 288 config := meta.(*Config) 289 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 290 if err != nil { 291 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 292 } 293 294 stateConf := &resource.StateChangeConf{ 295 Pending: []string{"ACTIVE", "PENDING_DELETE"}, 296 Target: []string{"DELETED"}, 297 Refresh: waitForLBPoolDelete(networkingClient, d.Id()), 298 Timeout: 2 * time.Minute, 299 Delay: 5 * time.Second, 300 MinTimeout: 3 * time.Second, 301 } 302 303 _, err = stateConf.WaitForState() 304 if err != nil { 305 return fmt.Errorf("Error deleting OpenStack LB Pool: %s", err) 306 } 307 308 d.SetId("") 309 return nil 310 } 311 312 func resourcePoolMonitorIDsV1(d *schema.ResourceData) []string { 313 mIDsRaw := d.Get("monitor_ids").(*schema.Set) 314 mIDs := make([]string, mIDsRaw.Len()) 315 for i, raw := range mIDsRaw.List() { 316 mIDs[i] = raw.(string) 317 } 318 return mIDs 319 } 320 321 func resourcePoolMembersV1(d *schema.ResourceData) []members.CreateOpts { 322 memberOptsRaw := d.Get("member").(*schema.Set) 323 memberOpts := make([]members.CreateOpts, memberOptsRaw.Len()) 324 for i, raw := range memberOptsRaw.List() { 325 rawMap := raw.(map[string]interface{}) 326 memberOpts[i] = members.CreateOpts{ 327 TenantID: rawMap["tenant_id"].(string), 328 Address: rawMap["address"].(string), 329 ProtocolPort: rawMap["port"].(int), 330 PoolID: d.Id(), 331 } 332 } 333 return memberOpts 334 } 335 336 func resourcePoolMemberV1(d *schema.ResourceData, raw interface{}) members.CreateOpts { 337 rawMap := raw.(map[string]interface{}) 338 return members.CreateOpts{ 339 TenantID: rawMap["tenant_id"].(string), 340 Address: rawMap["address"].(string), 341 ProtocolPort: rawMap["port"].(int), 342 PoolID: d.Id(), 343 } 344 } 345 346 func resourceLBMemberV1Hash(v interface{}) int { 347 var buf bytes.Buffer 348 m := v.(map[string]interface{}) 349 buf.WriteString(fmt.Sprintf("%s-", m["region"].(string))) 350 buf.WriteString(fmt.Sprintf("%s-", m["tenant_id"].(string))) 351 buf.WriteString(fmt.Sprintf("%s-", m["address"].(string))) 352 buf.WriteString(fmt.Sprintf("%d-", m["port"].(int))) 353 354 return hashcode.String(buf.String()) 355 } 356 357 func waitForLBPoolActive(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc { 358 return func() (interface{}, string, error) { 359 p, err := pools.Get(networkingClient, poolId).Extract() 360 if err != nil { 361 return nil, "", err 362 } 363 364 log.Printf("[DEBUG] OpenStack LB Pool: %+v", p) 365 if p.Status == "ACTIVE" { 366 return p, "ACTIVE", nil 367 } 368 369 return p, p.Status, nil 370 } 371 } 372 373 func waitForLBPoolDelete(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc { 374 return func() (interface{}, string, error) { 375 log.Printf("[DEBUG] Attempting to delete OpenStack LB Pool %s", poolId) 376 377 p, err := pools.Get(networkingClient, poolId).Extract() 378 if err != nil { 379 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 380 if !ok { 381 return p, "ACTIVE", err 382 } 383 if errCode.Actual == 404 { 384 log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId) 385 return p, "DELETED", nil 386 } 387 } 388 389 log.Printf("[DEBUG] OpenStack LB Pool: %+v", p) 390 err = pools.Delete(networkingClient, poolId).ExtractErr() 391 if err != nil { 392 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 393 if !ok { 394 return p, "ACTIVE", err 395 } 396 if errCode.Actual == 404 { 397 log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId) 398 return p, "DELETED", nil 399 } 400 } 401 402 log.Printf("[DEBUG] OpenStack LB Pool %s still active.", poolId) 403 return p, "ACTIVE", nil 404 } 405 406 }