github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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: envDefaultFuncAllowMissing("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: envDefaultFuncAllowMissing("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: func(v interface{}) int { 101 return hashcode.String(v.(string)) 102 }, 103 }, 104 }, 105 } 106 } 107 108 func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error { 109 config := meta.(*Config) 110 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 111 if err != nil { 112 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 113 } 114 115 createOpts := pools.CreateOpts{ 116 Name: d.Get("name").(string), 117 Protocol: d.Get("protocol").(string), 118 SubnetID: d.Get("subnet_id").(string), 119 LBMethod: d.Get("lb_method").(string), 120 TenantID: d.Get("tenant_id").(string), 121 } 122 123 log.Printf("[DEBUG] Create Options: %#v", createOpts) 124 p, err := pools.Create(networkingClient, createOpts).Extract() 125 if err != nil { 126 return fmt.Errorf("Error creating OpenStack LB pool: %s", err) 127 } 128 log.Printf("[INFO] LB Pool ID: %s", p.ID) 129 130 log.Printf("[DEBUG] Waiting for OpenStack LB pool (%s) to become available.", p.ID) 131 132 stateConf := &resource.StateChangeConf{ 133 Target: "ACTIVE", 134 Refresh: waitForLBPoolActive(networkingClient, p.ID), 135 Timeout: 2 * time.Minute, 136 Delay: 5 * time.Second, 137 MinTimeout: 3 * time.Second, 138 } 139 140 _, err = stateConf.WaitForState() 141 if err != nil { 142 return err 143 } 144 145 d.SetId(p.ID) 146 147 if mIDs := resourcePoolMonitorIDsV1(d); mIDs != nil { 148 for _, mID := range mIDs { 149 _, err := pools.AssociateMonitor(networkingClient, p.ID, mID).Extract() 150 if err != nil { 151 return fmt.Errorf("Error associating monitor (%s) with OpenStack LB pool (%s): %s", mID, p.ID, err) 152 } 153 } 154 } 155 156 if memberOpts := resourcePoolMembersV1(d); memberOpts != nil { 157 for _, memberOpt := range memberOpts { 158 _, err := members.Create(networkingClient, memberOpt).Extract() 159 if err != nil { 160 return fmt.Errorf("Error creating OpenStack LB member: %s", err) 161 } 162 } 163 } 164 165 return resourceLBPoolV1Read(d, meta) 166 } 167 168 func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error { 169 config := meta.(*Config) 170 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 171 if err != nil { 172 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 173 } 174 175 p, err := pools.Get(networkingClient, d.Id()).Extract() 176 if err != nil { 177 return CheckDeleted(d, err, "LB pool") 178 } 179 180 log.Printf("[DEBUG] Retreived OpenStack LB Pool %s: %+v", d.Id(), p) 181 182 d.Set("name", p.Name) 183 d.Set("protocol", p.Protocol) 184 d.Set("subnet_id", p.SubnetID) 185 d.Set("lb_method", p.LBMethod) 186 d.Set("tenant_id", p.TenantID) 187 d.Set("monitor_ids", p.MonitorIDs) 188 d.Set("member_ids", p.MemberIDs) 189 190 return nil 191 } 192 193 func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error { 194 config := meta.(*Config) 195 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 196 if err != nil { 197 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 198 } 199 200 var updateOpts pools.UpdateOpts 201 // If either option changed, update both. 202 // Gophercloud complains if one is empty. 203 if d.HasChange("name") || d.HasChange("lb_method") { 204 updateOpts.Name = d.Get("name").(string) 205 updateOpts.LBMethod = d.Get("lb_method").(string) 206 } 207 208 log.Printf("[DEBUG] Updating OpenStack LB Pool %s with options: %+v", d.Id(), updateOpts) 209 210 _, err = pools.Update(networkingClient, d.Id(), updateOpts).Extract() 211 if err != nil { 212 return fmt.Errorf("Error updating OpenStack LB Pool: %s", err) 213 } 214 215 if d.HasChange("monitor_ids") { 216 oldMIDsRaw, newMIDsRaw := d.GetChange("security_groups") 217 oldMIDsSet, newMIDsSet := oldMIDsRaw.(*schema.Set), newMIDsRaw.(*schema.Set) 218 monitorsToAdd := newMIDsSet.Difference(oldMIDsSet) 219 monitorsToRemove := oldMIDsSet.Difference(newMIDsSet) 220 221 log.Printf("[DEBUG] Monitors to add: %v", monitorsToAdd) 222 223 log.Printf("[DEBUG] Monitors to remove: %v", monitorsToRemove) 224 225 for _, m := range monitorsToAdd.List() { 226 _, err := pools.AssociateMonitor(networkingClient, d.Id(), m.(string)).Extract() 227 if err != nil { 228 return fmt.Errorf("Error associating monitor (%s) with OpenStack server (%s): %s", m.(string), d.Id(), err) 229 } 230 log.Printf("[DEBUG] Associated monitor (%s) with pool (%s)", m.(string), d.Id()) 231 } 232 233 for _, m := range monitorsToRemove.List() { 234 _, err := pools.DisassociateMonitor(networkingClient, d.Id(), m.(string)).Extract() 235 if err != nil { 236 return fmt.Errorf("Error disassociating monitor (%s) from OpenStack server (%s): %s", m.(string), d.Id(), err) 237 } 238 log.Printf("[DEBUG] Disassociated monitor (%s) from pool (%s)", m.(string), d.Id()) 239 } 240 } 241 242 if d.HasChange("member") { 243 oldMembersRaw, newMembersRaw := d.GetChange("member") 244 oldMembersSet, newMembersSet := oldMembersRaw.(*schema.Set), newMembersRaw.(*schema.Set) 245 membersToAdd := newMembersSet.Difference(oldMembersSet) 246 membersToRemove := oldMembersSet.Difference(newMembersSet) 247 248 log.Printf("[DEBUG] Members to add: %v", membersToAdd) 249 250 log.Printf("[DEBUG] Members to remove: %v", membersToRemove) 251 252 for _, m := range membersToRemove.List() { 253 oldMember := resourcePoolMemberV1(d, m) 254 listOpts := members.ListOpts{ 255 PoolID: d.Id(), 256 Address: oldMember.Address, 257 ProtocolPort: oldMember.ProtocolPort, 258 } 259 err = members.List(networkingClient, listOpts).EachPage(func(page pagination.Page) (bool, error) { 260 extractedMembers, err := members.ExtractMembers(page) 261 if err != nil { 262 return false, err 263 } 264 for _, member := range extractedMembers { 265 err := members.Delete(networkingClient, member.ID).ExtractErr() 266 if err != nil { 267 return false, fmt.Errorf("Error deleting member (%s) from OpenStack LB pool (%s): %s", member.ID, d.Id(), err) 268 } 269 log.Printf("[DEBUG] Deleted member (%s) from pool (%s)", member.ID, d.Id()) 270 } 271 return true, nil 272 }) 273 } 274 275 for _, m := range membersToAdd.List() { 276 createOpts := resourcePoolMemberV1(d, m) 277 newMember, err := members.Create(networkingClient, createOpts).Extract() 278 if err != nil { 279 return fmt.Errorf("Error creating LB member: %s", err) 280 } 281 log.Printf("[DEBUG] Created member (%s) in OpenStack LB pool (%s)", newMember.ID, d.Id()) 282 } 283 } 284 285 return resourceLBPoolV1Read(d, meta) 286 } 287 288 func resourceLBPoolV1Delete(d *schema.ResourceData, meta interface{}) error { 289 config := meta.(*Config) 290 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 291 if err != nil { 292 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 293 } 294 295 stateConf := &resource.StateChangeConf{ 296 Pending: []string{"ACTIVE"}, 297 Target: "DELETED", 298 Refresh: waitForLBPoolDelete(networkingClient, d.Id()), 299 Timeout: 2 * time.Minute, 300 Delay: 5 * time.Second, 301 MinTimeout: 3 * time.Second, 302 } 303 304 _, err = stateConf.WaitForState() 305 if err != nil { 306 return fmt.Errorf("Error deleting OpenStack LB Pool: %s", err) 307 } 308 309 d.SetId("") 310 return nil 311 } 312 313 func resourcePoolMonitorIDsV1(d *schema.ResourceData) []string { 314 mIDsRaw := d.Get("monitor_ids").(*schema.Set) 315 mIDs := make([]string, mIDsRaw.Len()) 316 for i, raw := range mIDsRaw.List() { 317 mIDs[i] = raw.(string) 318 } 319 return mIDs 320 } 321 322 func resourcePoolMembersV1(d *schema.ResourceData) []members.CreateOpts { 323 memberOptsRaw := d.Get("member").(*schema.Set) 324 memberOpts := make([]members.CreateOpts, memberOptsRaw.Len()) 325 for i, raw := range memberOptsRaw.List() { 326 rawMap := raw.(map[string]interface{}) 327 memberOpts[i] = members.CreateOpts{ 328 TenantID: rawMap["tenant_id"].(string), 329 Address: rawMap["address"].(string), 330 ProtocolPort: rawMap["port"].(int), 331 PoolID: d.Id(), 332 } 333 } 334 return memberOpts 335 } 336 337 func resourcePoolMemberV1(d *schema.ResourceData, raw interface{}) members.CreateOpts { 338 rawMap := raw.(map[string]interface{}) 339 return members.CreateOpts{ 340 TenantID: rawMap["tenant_id"].(string), 341 Address: rawMap["address"].(string), 342 ProtocolPort: rawMap["port"].(int), 343 PoolID: d.Id(), 344 } 345 } 346 347 func resourceLBMemberV1Hash(v interface{}) int { 348 var buf bytes.Buffer 349 m := v.(map[string]interface{}) 350 buf.WriteString(fmt.Sprintf("%s-", m["region"].(string))) 351 buf.WriteString(fmt.Sprintf("%s-", m["tenant_id"].(string))) 352 buf.WriteString(fmt.Sprintf("%s-", m["address"].(string))) 353 buf.WriteString(fmt.Sprintf("%d-", m["port"].(int))) 354 355 return hashcode.String(buf.String()) 356 } 357 358 func waitForLBPoolActive(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc { 359 return func() (interface{}, string, error) { 360 p, err := pools.Get(networkingClient, poolId).Extract() 361 if err != nil { 362 return nil, "", err 363 } 364 365 log.Printf("[DEBUG] OpenStack LB Pool: %+v", p) 366 if p.Status == "ACTIVE" { 367 return p, "ACTIVE", nil 368 } 369 370 return p, p.Status, nil 371 } 372 } 373 374 func waitForLBPoolDelete(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc { 375 return func() (interface{}, string, error) { 376 log.Printf("[DEBUG] Attempting to delete OpenStack LB Pool %s", poolId) 377 378 p, err := pools.Get(networkingClient, poolId).Extract() 379 if err != nil { 380 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 381 if !ok { 382 return p, "ACTIVE", err 383 } 384 if errCode.Actual == 404 { 385 log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId) 386 return p, "DELETED", nil 387 } 388 } 389 390 log.Printf("[DEBUG] OpenStack LB Pool: %+v", p) 391 err = pools.Delete(networkingClient, poolId).ExtractErr() 392 if err != nil { 393 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 394 if !ok { 395 return p, "ACTIVE", err 396 } 397 if errCode.Actual == 404 { 398 log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId) 399 return p, "DELETED", nil 400 } 401 } 402 403 log.Printf("[DEBUG] OpenStack LB Pool %s still active.", poolId) 404 return p, "ACTIVE", nil 405 } 406 407 }