github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/openstack/resource_openstack_lb_pool_v2.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 11 "github.com/gophercloud/gophercloud" 12 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" 13 ) 14 15 func resourcePoolV2() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourcePoolV2Create, 18 Read: resourcePoolV2Read, 19 Update: resourcePoolV2Update, 20 Delete: resourcePoolV2Delete, 21 22 Timeouts: &schema.ResourceTimeout{ 23 Create: schema.DefaultTimeout(10 * time.Minute), 24 Delete: schema.DefaultTimeout(10 * time.Minute), 25 }, 26 27 Schema: map[string]*schema.Schema{ 28 "region": &schema.Schema{ 29 Type: schema.TypeString, 30 Required: true, 31 ForceNew: true, 32 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 33 }, 34 35 "tenant_id": &schema.Schema{ 36 Type: schema.TypeString, 37 Optional: true, 38 Computed: true, 39 ForceNew: true, 40 }, 41 42 "name": &schema.Schema{ 43 Type: schema.TypeString, 44 Optional: true, 45 }, 46 47 "description": &schema.Schema{ 48 Type: schema.TypeString, 49 Optional: true, 50 }, 51 52 "protocol": &schema.Schema{ 53 Type: schema.TypeString, 54 Required: true, 55 ForceNew: true, 56 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 57 value := v.(string) 58 if value != "TCP" && value != "HTTP" && value != "HTTPS" { 59 errors = append(errors, fmt.Errorf( 60 "Only 'TCP', 'HTTP', and 'HTTPS' are supported values for 'protocol'")) 61 } 62 return 63 }, 64 }, 65 66 // One of loadbalancer_id or listener_id must be provided 67 "loadbalancer_id": &schema.Schema{ 68 Type: schema.TypeString, 69 Optional: true, 70 ForceNew: true, 71 }, 72 73 // One of loadbalancer_id or listener_id must be provided 74 "listener_id": &schema.Schema{ 75 Type: schema.TypeString, 76 Optional: true, 77 ForceNew: true, 78 }, 79 80 "lb_method": &schema.Schema{ 81 Type: schema.TypeString, 82 Required: true, 83 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 84 value := v.(string) 85 if value != "ROUND_ROBIN" && value != "LEAST_CONNECTIONS" && value != "SOURCE_IP" { 86 errors = append(errors, fmt.Errorf( 87 "Only 'ROUND_ROBIN', 'LEAST_CONNECTIONS', and 'SOURCE_IP' are supported values for 'lb_method'")) 88 } 89 return 90 }, 91 }, 92 93 "persistence": &schema.Schema{ 94 Type: schema.TypeList, 95 Optional: true, 96 ForceNew: true, 97 Elem: &schema.Resource{ 98 Schema: map[string]*schema.Schema{ 99 "type": &schema.Schema{ 100 Type: schema.TypeString, 101 Required: true, 102 ForceNew: true, 103 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 104 value := v.(string) 105 if value != "SOURCE_IP" && value != "HTTP_COOKIE" && value != "APP_COOKIE" { 106 errors = append(errors, fmt.Errorf( 107 "Only 'SOURCE_IP', 'HTTP_COOKIE', and 'APP_COOKIE' are supported values for 'persistence'")) 108 } 109 return 110 }, 111 }, 112 113 "cookie_name": &schema.Schema{ 114 Type: schema.TypeString, 115 Required: true, 116 ForceNew: true, 117 }, 118 }, 119 }, 120 }, 121 122 "admin_state_up": &schema.Schema{ 123 Type: schema.TypeBool, 124 Default: true, 125 Optional: true, 126 }, 127 128 "id": &schema.Schema{ 129 Type: schema.TypeString, 130 Optional: true, 131 Computed: true, 132 }, 133 }, 134 } 135 } 136 137 func resourcePoolV2Create(d *schema.ResourceData, meta interface{}) error { 138 config := meta.(*Config) 139 networkingClient, err := config.networkingV2Client(GetRegion(d)) 140 if err != nil { 141 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 142 } 143 144 adminStateUp := d.Get("admin_state_up").(bool) 145 var persistence pools.SessionPersistence 146 if p, ok := d.GetOk("persistence"); ok { 147 pV := (p.([]interface{}))[0].(map[string]interface{}) 148 149 persistence = pools.SessionPersistence{ 150 Type: pV["type"].(string), 151 CookieName: pV["cookie_name"].(string), 152 } 153 } 154 createOpts := pools.CreateOpts{ 155 TenantID: d.Get("tenant_id").(string), 156 Name: d.Get("name").(string), 157 Description: d.Get("description").(string), 158 Protocol: pools.Protocol(d.Get("protocol").(string)), 159 LoadbalancerID: d.Get("loadbalancer_id").(string), 160 ListenerID: d.Get("listener_id").(string), 161 LBMethod: pools.LBMethod(d.Get("lb_method").(string)), 162 AdminStateUp: &adminStateUp, 163 } 164 // Must omit if not set 165 if persistence != (pools.SessionPersistence{}) { 166 createOpts.Persistence = &persistence 167 } 168 169 log.Printf("[DEBUG] Create Options: %#v", createOpts) 170 171 var pool *pools.Pool 172 err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError { 173 var err error 174 log.Printf("[DEBUG] Attempting to create LBaaSV2 pool") 175 pool, err = pools.Create(networkingClient, createOpts).Extract() 176 if err != nil { 177 switch errCode := err.(type) { 178 case gophercloud.ErrDefault500: 179 log.Printf("[DEBUG] OpenStack LBaaSV2 pool is still creating.") 180 return resource.RetryableError(err) 181 case gophercloud.ErrUnexpectedResponseCode: 182 if errCode.Actual == 409 { 183 log.Printf("[DEBUG] OpenStack LBaaSV2 pool is still creating.") 184 return resource.RetryableError(err) 185 } 186 default: 187 return resource.NonRetryableError(err) 188 } 189 } 190 return nil 191 }) 192 193 if err != nil { 194 return fmt.Errorf("Error creating OpenStack LBaaSV2 pool: %s", err) 195 } 196 197 log.Printf("[INFO] pool ID: %s", pool.ID) 198 199 log.Printf("[DEBUG] Waiting for Openstack LBaaSV2 pool (%s) to become available.", pool.ID) 200 201 stateConf := &resource.StateChangeConf{ 202 Pending: []string{"PENDING_CREATE"}, 203 Target: []string{"ACTIVE"}, 204 Refresh: waitForPoolActive(networkingClient, pool.ID), 205 Timeout: d.Timeout(schema.TimeoutCreate), 206 Delay: 5 * time.Second, 207 MinTimeout: 3 * time.Second, 208 } 209 210 _, err = stateConf.WaitForState() 211 if err != nil { 212 return err 213 } 214 215 d.SetId(pool.ID) 216 217 return resourcePoolV2Read(d, meta) 218 } 219 220 func resourcePoolV2Read(d *schema.ResourceData, meta interface{}) error { 221 config := meta.(*Config) 222 networkingClient, err := config.networkingV2Client(GetRegion(d)) 223 if err != nil { 224 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 225 } 226 227 pool, err := pools.Get(networkingClient, d.Id()).Extract() 228 if err != nil { 229 return CheckDeleted(d, err, "LBV2 Pool") 230 } 231 232 log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 Pool %s: %+v", d.Id(), pool) 233 234 d.Set("lb_method", pool.LBMethod) 235 d.Set("protocol", pool.Protocol) 236 d.Set("description", pool.Description) 237 d.Set("tenant_id", pool.TenantID) 238 d.Set("admin_state_up", pool.AdminStateUp) 239 d.Set("name", pool.Name) 240 d.Set("id", pool.ID) 241 d.Set("persistence", pool.Persistence) 242 243 return nil 244 } 245 246 func resourcePoolV2Update(d *schema.ResourceData, meta interface{}) error { 247 config := meta.(*Config) 248 networkingClient, err := config.networkingV2Client(GetRegion(d)) 249 if err != nil { 250 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 251 } 252 253 var updateOpts pools.UpdateOpts 254 if d.HasChange("lb_method") { 255 updateOpts.LBMethod = pools.LBMethod(d.Get("lb_method").(string)) 256 } 257 if d.HasChange("name") { 258 updateOpts.Name = d.Get("name").(string) 259 } 260 if d.HasChange("description") { 261 updateOpts.Description = d.Get("description").(string) 262 } 263 if d.HasChange("admin_state_up") { 264 asu := d.Get("admin_state_up").(bool) 265 updateOpts.AdminStateUp = &asu 266 } 267 268 log.Printf("[DEBUG] Updating OpenStack LBaaSV2 Pool %s with options: %+v", d.Id(), updateOpts) 269 270 _, err = pools.Update(networkingClient, d.Id(), updateOpts).Extract() 271 if err != nil { 272 return fmt.Errorf("Error updating OpenStack LBaaSV2 Pool: %s", err) 273 } 274 275 return resourcePoolV2Read(d, meta) 276 } 277 278 func resourcePoolV2Delete(d *schema.ResourceData, meta interface{}) error { 279 config := meta.(*Config) 280 networkingClient, err := config.networkingV2Client(GetRegion(d)) 281 if err != nil { 282 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 283 } 284 285 stateConf := &resource.StateChangeConf{ 286 Pending: []string{"ACTIVE", "PENDING_DELETE"}, 287 Target: []string{"DELETED"}, 288 Refresh: waitForPoolDelete(networkingClient, d.Id()), 289 Timeout: d.Timeout(schema.TimeoutDelete), 290 Delay: 5 * time.Second, 291 MinTimeout: 3 * time.Second, 292 } 293 294 _, err = stateConf.WaitForState() 295 if err != nil { 296 return fmt.Errorf("Error deleting OpenStack LBaaSV2 Pool: %s", err) 297 } 298 299 d.SetId("") 300 return nil 301 } 302 303 func waitForPoolActive(networkingClient *gophercloud.ServiceClient, poolID string) resource.StateRefreshFunc { 304 return func() (interface{}, string, error) { 305 pool, err := pools.Get(networkingClient, poolID).Extract() 306 if err != nil { 307 return nil, "", err 308 } 309 310 // The pool resource has no Status attribute, so a successful Get is the best we can do 311 log.Printf("[DEBUG] OpenStack LBaaSV2 Pool: %+v", pool) 312 return pool, "ACTIVE", nil 313 } 314 } 315 316 func waitForPoolDelete(networkingClient *gophercloud.ServiceClient, poolID string) resource.StateRefreshFunc { 317 return func() (interface{}, string, error) { 318 log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 Pool %s", poolID) 319 320 pool, err := pools.Get(networkingClient, poolID).Extract() 321 if err != nil { 322 if _, ok := err.(gophercloud.ErrDefault404); ok { 323 log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Pool %s", poolID) 324 return pool, "DELETED", nil 325 } 326 return pool, "ACTIVE", err 327 } 328 329 log.Printf("[DEBUG] Openstack LBaaSV2 Pool: %+v", pool) 330 err = pools.Delete(networkingClient, poolID).ExtractErr() 331 if err != nil { 332 if _, ok := err.(gophercloud.ErrDefault404); ok { 333 log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Pool %s", poolID) 334 return pool, "DELETED", nil 335 } 336 337 if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok { 338 if errCode.Actual == 409 { 339 log.Printf("[DEBUG] OpenStack LBaaSV2 Pool (%s) is still in use.", poolID) 340 return pool, "ACTIVE", nil 341 } 342 } 343 344 return pool, "ACTIVE", err 345 } 346 347 log.Printf("[DEBUG] OpenStack LBaaSV2 Pool %s still active.", poolID) 348 return pool, "ACTIVE", nil 349 } 350 }