github.com/gabrielperezs/terraform@v0.7.0-rc2.0.20160715084931-f7da2612946f/builtin/providers/openstack/resource_openstack_lb_vip_v1.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 "github.com/rackspace/gophercloud" 11 "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" 12 "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips" 13 ) 14 15 func resourceLBVipV1() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceLBVipV1Create, 18 Read: resourceLBVipV1Read, 19 Update: resourceLBVipV1Update, 20 Delete: resourceLBVipV1Delete, 21 Importer: &schema.ResourceImporter{ 22 State: schema.ImportStatePassthrough, 23 }, 24 25 Schema: map[string]*schema.Schema{ 26 "region": &schema.Schema{ 27 Type: schema.TypeString, 28 Required: true, 29 ForceNew: true, 30 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 31 }, 32 "name": &schema.Schema{ 33 Type: schema.TypeString, 34 Required: true, 35 ForceNew: false, 36 }, 37 "subnet_id": &schema.Schema{ 38 Type: schema.TypeString, 39 Required: true, 40 ForceNew: true, 41 }, 42 "protocol": &schema.Schema{ 43 Type: schema.TypeString, 44 Required: true, 45 ForceNew: true, 46 }, 47 "port": &schema.Schema{ 48 Type: schema.TypeInt, 49 Required: true, 50 ForceNew: true, 51 }, 52 "pool_id": &schema.Schema{ 53 Type: schema.TypeString, 54 Required: true, 55 ForceNew: false, 56 }, 57 "tenant_id": &schema.Schema{ 58 Type: schema.TypeString, 59 Optional: true, 60 Computed: true, 61 ForceNew: true, 62 }, 63 "address": &schema.Schema{ 64 Type: schema.TypeString, 65 Optional: true, 66 Computed: true, 67 ForceNew: true, 68 }, 69 "description": &schema.Schema{ 70 Type: schema.TypeString, 71 Optional: true, 72 Computed: true, 73 ForceNew: false, 74 }, 75 "persistence": &schema.Schema{ 76 Type: schema.TypeMap, 77 Optional: true, 78 ForceNew: false, 79 }, 80 "conn_limit": &schema.Schema{ 81 Type: schema.TypeInt, 82 Optional: true, 83 Computed: true, 84 ForceNew: false, 85 }, 86 "port_id": &schema.Schema{ 87 Type: schema.TypeString, 88 Computed: true, 89 ForceNew: false, 90 }, 91 "floating_ip": &schema.Schema{ 92 Type: schema.TypeString, 93 Optional: true, 94 ForceNew: false, 95 }, 96 "admin_state_up": &schema.Schema{ 97 Type: schema.TypeBool, 98 Optional: true, 99 Computed: true, 100 ForceNew: false, 101 }, 102 }, 103 } 104 } 105 106 func resourceLBVipV1Create(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 := vips.CreateOpts{ 114 Name: d.Get("name").(string), 115 SubnetID: d.Get("subnet_id").(string), 116 Protocol: d.Get("protocol").(string), 117 ProtocolPort: d.Get("port").(int), 118 PoolID: d.Get("pool_id").(string), 119 TenantID: d.Get("tenant_id").(string), 120 Address: d.Get("address").(string), 121 Description: d.Get("description").(string), 122 Persistence: resourceVipPersistenceV1(d), 123 ConnLimit: gophercloud.MaybeInt(d.Get("conn_limit").(int)), 124 } 125 126 asu := d.Get("admin_state_up").(bool) 127 createOpts.AdminStateUp = &asu 128 129 log.Printf("[DEBUG] Create Options: %#v", createOpts) 130 p, err := vips.Create(networkingClient, createOpts).Extract() 131 if err != nil { 132 return fmt.Errorf("Error creating OpenStack LB VIP: %s", err) 133 } 134 log.Printf("[INFO] LB VIP ID: %s", p.ID) 135 136 log.Printf("[DEBUG] Waiting for OpenStack LB VIP (%s) to become available.", p.ID) 137 138 stateConf := &resource.StateChangeConf{ 139 Pending: []string{"PENDING_CREATE"}, 140 Target: []string{"ACTIVE"}, 141 Refresh: waitForLBVIPActive(networkingClient, p.ID), 142 Timeout: 2 * time.Minute, 143 Delay: 5 * time.Second, 144 MinTimeout: 3 * time.Second, 145 } 146 147 _, err = stateConf.WaitForState() 148 if err != nil { 149 return err 150 } 151 152 floatingIP := d.Get("floating_ip").(string) 153 if floatingIP != "" { 154 lbVipV1AssignFloatingIP(floatingIP, p.PortID, networkingClient) 155 } 156 157 d.SetId(p.ID) 158 159 return resourceLBVipV1Read(d, meta) 160 } 161 162 func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error { 163 config := meta.(*Config) 164 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 165 if err != nil { 166 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 167 } 168 169 p, err := vips.Get(networkingClient, d.Id()).Extract() 170 if err != nil { 171 return CheckDeleted(d, err, "LB VIP") 172 } 173 174 log.Printf("[DEBUG] Retreived OpenStack LB VIP %s: %+v", d.Id(), p) 175 176 d.Set("name", p.Name) 177 d.Set("subnet_id", p.SubnetID) 178 d.Set("protocol", p.Protocol) 179 d.Set("port", p.ProtocolPort) 180 d.Set("pool_id", p.PoolID) 181 d.Set("port_id", p.PortID) 182 d.Set("tenant_id", p.TenantID) 183 d.Set("address", p.Address) 184 d.Set("description", p.Description) 185 d.Set("conn_limit", p.ConnLimit) 186 d.Set("admin_state_up", p.AdminStateUp) 187 188 // Set the persistence method being used 189 persistence := make(map[string]interface{}) 190 if p.Persistence.Type != "" { 191 persistence["type"] = p.Persistence.Type 192 } 193 if p.Persistence.CookieName != "" { 194 persistence["cookie_name"] = p.Persistence.CookieName 195 } 196 d.Set("persistence", persistence) 197 198 return nil 199 } 200 201 func resourceLBVipV1Update(d *schema.ResourceData, meta interface{}) error { 202 config := meta.(*Config) 203 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 204 if err != nil { 205 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 206 } 207 208 var updateOpts vips.UpdateOpts 209 if d.HasChange("name") { 210 updateOpts.Name = d.Get("name").(string) 211 } 212 if d.HasChange("pool_id") { 213 updateOpts.PoolID = d.Get("pool_id").(string) 214 } 215 if d.HasChange("description") { 216 updateOpts.Description = d.Get("description").(string) 217 } 218 if d.HasChange("persistence") { 219 updateOpts.Persistence = resourceVipPersistenceV1(d) 220 } 221 if d.HasChange("conn_limit") { 222 updateOpts.ConnLimit = gophercloud.MaybeInt(d.Get("conn_limit").(int)) 223 } 224 if d.HasChange("floating_ip") { 225 portID := d.Get("port_id").(string) 226 227 // Searching for a floating IP assigned to the VIP 228 listOpts := floatingips.ListOpts{ 229 PortID: portID, 230 } 231 page, err := floatingips.List(networkingClient, listOpts).AllPages() 232 if err != nil { 233 return err 234 } 235 236 fips, err := floatingips.ExtractFloatingIPs(page) 237 if err != nil { 238 return err 239 } 240 241 // If a floating IP is found we unassign it 242 if len(fips) == 1 { 243 updateOpts := floatingips.UpdateOpts{ 244 PortID: "", 245 } 246 if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil { 247 return err 248 } 249 } 250 251 // Assign the updated floating IP 252 floatingIP := d.Get("floating_ip").(string) 253 if floatingIP != "" { 254 lbVipV1AssignFloatingIP(floatingIP, portID, networkingClient) 255 } 256 } 257 if d.HasChange("admin_state_up") { 258 asu := d.Get("admin_state_up").(bool) 259 updateOpts.AdminStateUp = &asu 260 } 261 262 log.Printf("[DEBUG] Updating OpenStack LB VIP %s with options: %+v", d.Id(), updateOpts) 263 264 _, err = vips.Update(networkingClient, d.Id(), updateOpts).Extract() 265 if err != nil { 266 return fmt.Errorf("Error updating OpenStack LB VIP: %s", err) 267 } 268 269 return resourceLBVipV1Read(d, meta) 270 } 271 272 func resourceLBVipV1Delete(d *schema.ResourceData, meta interface{}) error { 273 config := meta.(*Config) 274 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 275 if err != nil { 276 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 277 } 278 279 stateConf := &resource.StateChangeConf{ 280 Pending: []string{"ACTIVE", "PENDING_DELETE"}, 281 Target: []string{"DELETED"}, 282 Refresh: waitForLBVIPDelete(networkingClient, d.Id()), 283 Timeout: 2 * time.Minute, 284 Delay: 5 * time.Second, 285 MinTimeout: 3 * time.Second, 286 } 287 288 _, err = stateConf.WaitForState() 289 if err != nil { 290 return fmt.Errorf("Error deleting OpenStack LB VIP: %s", err) 291 } 292 293 d.SetId("") 294 return nil 295 } 296 297 func resourceVipPersistenceV1(d *schema.ResourceData) *vips.SessionPersistence { 298 rawP := d.Get("persistence").(interface{}) 299 rawMap := rawP.(map[string]interface{}) 300 if len(rawMap) != 0 { 301 p := vips.SessionPersistence{} 302 if t, ok := rawMap["type"]; ok { 303 p.Type = t.(string) 304 } 305 if c, ok := rawMap["cookie_name"]; ok { 306 p.CookieName = c.(string) 307 } 308 return &p 309 } 310 return nil 311 } 312 313 func lbVipV1AssignFloatingIP(floatingIP, portID string, networkingClient *gophercloud.ServiceClient) error { 314 log.Printf("[DEBUG] Assigning floating IP %s to VIP %s", floatingIP, portID) 315 316 listOpts := floatingips.ListOpts{ 317 FloatingIP: floatingIP, 318 } 319 page, err := floatingips.List(networkingClient, listOpts).AllPages() 320 if err != nil { 321 return err 322 } 323 324 fips, err := floatingips.ExtractFloatingIPs(page) 325 if err != nil { 326 return err 327 } 328 if len(fips) != 1 { 329 return fmt.Errorf("Unable to retrieve floating IP '%s'", floatingIP) 330 } 331 332 updateOpts := floatingips.UpdateOpts{ 333 PortID: portID, 334 } 335 if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil { 336 return err 337 } 338 339 return nil 340 } 341 342 func waitForLBVIPActive(networkingClient *gophercloud.ServiceClient, vipId string) resource.StateRefreshFunc { 343 return func() (interface{}, string, error) { 344 p, err := vips.Get(networkingClient, vipId).Extract() 345 if err != nil { 346 return nil, "", err 347 } 348 349 log.Printf("[DEBUG] OpenStack LB VIP: %+v", p) 350 if p.Status == "ACTIVE" { 351 return p, "ACTIVE", nil 352 } 353 354 return p, p.Status, nil 355 } 356 } 357 358 func waitForLBVIPDelete(networkingClient *gophercloud.ServiceClient, vipId string) resource.StateRefreshFunc { 359 return func() (interface{}, string, error) { 360 log.Printf("[DEBUG] Attempting to delete OpenStack LB VIP %s", vipId) 361 362 p, err := vips.Get(networkingClient, vipId).Extract() 363 if err != nil { 364 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 365 if !ok { 366 return p, "ACTIVE", err 367 } 368 if errCode.Actual == 404 { 369 log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId) 370 return p, "DELETED", nil 371 } 372 } 373 374 log.Printf("[DEBUG] OpenStack LB VIP: %+v", p) 375 err = vips.Delete(networkingClient, vipId).ExtractErr() 376 if err != nil { 377 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 378 if !ok { 379 return p, "ACTIVE", err 380 } 381 if errCode.Actual == 404 { 382 log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId) 383 return p, "DELETED", nil 384 } 385 } 386 387 log.Printf("[DEBUG] OpenStack LB VIP %s still active.", vipId) 388 return p, "ACTIVE", nil 389 } 390 391 }