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