github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/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/gophercloud/gophercloud" 9 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" 10 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 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] Retrieved 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 v := d.Get("name").(string) 211 updateOpts.Name = &v 212 } 213 214 if d.HasChange("pool_id") { 215 v := d.Get("pool_id").(string) 216 updateOpts.PoolID = &v 217 } 218 219 if d.HasChange("description") { 220 v := d.Get("description").(string) 221 updateOpts.Description = &v 222 } 223 224 if d.HasChange("conn_limit") { 225 updateOpts.ConnLimit = gophercloud.MaybeInt(d.Get("conn_limit").(int)) 226 } 227 228 if d.HasChange("floating_ip") { 229 portID := d.Get("port_id").(string) 230 231 // Searching for a floating IP assigned to the VIP 232 listOpts := floatingips.ListOpts{ 233 PortID: portID, 234 } 235 page, err := floatingips.List(networkingClient, listOpts).AllPages() 236 if err != nil { 237 return err 238 } 239 240 fips, err := floatingips.ExtractFloatingIPs(page) 241 if err != nil { 242 return err 243 } 244 245 // If a floating IP is found we unassign it 246 if len(fips) == 1 { 247 portID := "" 248 updateOpts := floatingips.UpdateOpts{ 249 PortID: &portID, 250 } 251 if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil { 252 return err 253 } 254 } 255 256 // Assign the updated floating IP 257 floatingIP := d.Get("floating_ip").(string) 258 if floatingIP != "" { 259 lbVipV1AssignFloatingIP(floatingIP, portID, networkingClient) 260 } 261 } 262 263 if d.HasChange("admin_state_up") { 264 asu := d.Get("admin_state_up").(bool) 265 updateOpts.AdminStateUp = &asu 266 } 267 268 // Persistence has to be included, even if it hasn't changed. 269 updateOpts.Persistence = resourceVipPersistenceV1(d) 270 271 log.Printf("[DEBUG] Updating OpenStack LB VIP %s with options: %+v", d.Id(), updateOpts) 272 273 _, err = vips.Update(networkingClient, d.Id(), updateOpts).Extract() 274 if err != nil { 275 return fmt.Errorf("Error updating OpenStack LB VIP: %s", err) 276 } 277 278 return resourceLBVipV1Read(d, meta) 279 } 280 281 func resourceLBVipV1Delete(d *schema.ResourceData, meta interface{}) error { 282 config := meta.(*Config) 283 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 284 if err != nil { 285 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 286 } 287 288 stateConf := &resource.StateChangeConf{ 289 Pending: []string{"ACTIVE", "PENDING_DELETE"}, 290 Target: []string{"DELETED"}, 291 Refresh: waitForLBVIPDelete(networkingClient, d.Id()), 292 Timeout: 2 * time.Minute, 293 Delay: 5 * time.Second, 294 MinTimeout: 3 * time.Second, 295 } 296 297 _, err = stateConf.WaitForState() 298 if err != nil { 299 return fmt.Errorf("Error deleting OpenStack LB VIP: %s", err) 300 } 301 302 d.SetId("") 303 return nil 304 } 305 306 func resourceVipPersistenceV1(d *schema.ResourceData) *vips.SessionPersistence { 307 rawP := d.Get("persistence").(interface{}) 308 rawMap := rawP.(map[string]interface{}) 309 if len(rawMap) != 0 { 310 p := vips.SessionPersistence{} 311 if t, ok := rawMap["type"]; ok { 312 p.Type = t.(string) 313 } 314 if c, ok := rawMap["cookie_name"]; ok { 315 p.CookieName = c.(string) 316 } 317 return &p 318 } 319 return nil 320 } 321 322 func lbVipV1AssignFloatingIP(floatingIP, portID string, networkingClient *gophercloud.ServiceClient) error { 323 log.Printf("[DEBUG] Assigning floating IP %s to VIP %s", floatingIP, portID) 324 325 listOpts := floatingips.ListOpts{ 326 FloatingIP: floatingIP, 327 } 328 page, err := floatingips.List(networkingClient, listOpts).AllPages() 329 if err != nil { 330 return err 331 } 332 333 fips, err := floatingips.ExtractFloatingIPs(page) 334 if err != nil { 335 return err 336 } 337 if len(fips) != 1 { 338 return fmt.Errorf("Unable to retrieve floating IP '%s'", floatingIP) 339 } 340 341 updateOpts := floatingips.UpdateOpts{ 342 PortID: &portID, 343 } 344 if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil { 345 return err 346 } 347 348 return nil 349 } 350 351 func waitForLBVIPActive(networkingClient *gophercloud.ServiceClient, vipId string) resource.StateRefreshFunc { 352 return func() (interface{}, string, error) { 353 p, err := vips.Get(networkingClient, vipId).Extract() 354 if err != nil { 355 return nil, "", err 356 } 357 358 log.Printf("[DEBUG] OpenStack LB VIP: %+v", p) 359 if p.Status == "ACTIVE" { 360 return p, "ACTIVE", nil 361 } 362 363 return p, p.Status, nil 364 } 365 } 366 367 func waitForLBVIPDelete(networkingClient *gophercloud.ServiceClient, vipId string) resource.StateRefreshFunc { 368 return func() (interface{}, string, error) { 369 log.Printf("[DEBUG] Attempting to delete OpenStack LB VIP %s", vipId) 370 371 p, err := vips.Get(networkingClient, vipId).Extract() 372 if err != nil { 373 if _, ok := err.(gophercloud.ErrDefault404); ok { 374 log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId) 375 return p, "DELETED", nil 376 } 377 return p, "ACTIVE", err 378 } 379 380 log.Printf("[DEBUG] OpenStack LB VIP: %+v", p) 381 err = vips.Delete(networkingClient, vipId).ExtractErr() 382 if err != nil { 383 if _, ok := err.(gophercloud.ErrDefault404); ok { 384 log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId) 385 return p, "DELETED", nil 386 } 387 return p, "ACTIVE", err 388 } 389 390 log.Printf("[DEBUG] OpenStack LB VIP %s still active.", vipId) 391 return p, "ACTIVE", nil 392 } 393 394 }