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