github.com/minamijoyo/terraform@v0.7.8-0.20161029001309-18b3736ba44b/builtin/providers/openstack/resource_openstack_lb_member_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 resourceMemberV2() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceMemberV2Create, 18 Read: resourceMemberV2Read, 19 Update: resourceMemberV2Update, 20 Delete: resourceMemberV2Delete, 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 "name": &schema.Schema{ 31 Type: schema.TypeString, 32 Optional: true, 33 }, 34 35 "tenant_id": &schema.Schema{ 36 Type: schema.TypeString, 37 Optional: true, 38 Computed: true, 39 ForceNew: true, 40 }, 41 42 "address": &schema.Schema{ 43 Type: schema.TypeString, 44 Required: true, 45 ForceNew: true, 46 }, 47 48 "protocol_port": &schema.Schema{ 49 Type: schema.TypeInt, 50 Required: true, 51 ForceNew: true, 52 }, 53 54 "weight": &schema.Schema{ 55 Type: schema.TypeInt, 56 Optional: true, 57 Computed: true, 58 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 59 value := v.(int) 60 if value < 1 { 61 errors = append(errors, fmt.Errorf( 62 "Only numbers greater than 0 are supported values for 'weight'")) 63 } 64 return 65 }, 66 }, 67 68 "subnet_id": &schema.Schema{ 69 Type: schema.TypeString, 70 Required: true, 71 ForceNew: true, 72 }, 73 74 "admin_state_up": &schema.Schema{ 75 Type: schema.TypeBool, 76 Default: true, 77 Optional: true, 78 }, 79 80 "pool_id": &schema.Schema{ 81 Type: schema.TypeString, 82 Required: true, 83 ForceNew: true, 84 }, 85 86 "id": &schema.Schema{ 87 Type: schema.TypeString, 88 Optional: true, 89 Computed: true, 90 }, 91 }, 92 } 93 } 94 95 func resourceMemberV2Create(d *schema.ResourceData, meta interface{}) error { 96 config := meta.(*Config) 97 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 98 if err != nil { 99 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 100 } 101 102 adminStateUp := d.Get("admin_state_up").(bool) 103 createOpts := pools.CreateMemberOpts{ 104 Name: d.Get("name").(string), 105 TenantID: d.Get("tenant_id").(string), 106 Address: d.Get("address").(string), 107 ProtocolPort: d.Get("protocol_port").(int), 108 Weight: d.Get("weight").(int), 109 AdminStateUp: &adminStateUp, 110 } 111 112 // Must omit if not set 113 if v, ok := d.GetOk("subnet_id"); ok { 114 createOpts.SubnetID = v.(string) 115 } 116 117 poolID := d.Get("pool_id").(string) 118 119 log.Printf("[DEBUG] Create Options: %#v", createOpts) 120 member, err := pools.CreateMember(networkingClient, poolID, createOpts).Extract() 121 if err != nil { 122 return fmt.Errorf("Error creating OpenStack LBaaSV2 member: %s", err) 123 } 124 log.Printf("[INFO] member ID: %s", member.ID) 125 126 log.Printf("[DEBUG] Waiting for Openstack LBaaSV2 member (%s) to become available.", member.ID) 127 128 stateConf := &resource.StateChangeConf{ 129 Pending: []string{"PENDING_CREATE"}, 130 Target: []string{"ACTIVE"}, 131 Refresh: waitForMemberActive(networkingClient, poolID, member.ID), 132 Timeout: 2 * time.Minute, 133 Delay: 5 * time.Second, 134 MinTimeout: 3 * time.Second, 135 } 136 137 _, err = stateConf.WaitForState() 138 if err != nil { 139 return err 140 } 141 142 d.SetId(member.ID) 143 144 return resourceMemberV2Read(d, meta) 145 } 146 147 func resourceMemberV2Read(d *schema.ResourceData, meta interface{}) error { 148 config := meta.(*Config) 149 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 150 if err != nil { 151 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 152 } 153 154 member, err := pools.GetMember(networkingClient, d.Get("pool_id").(string), d.Id()).Extract() 155 if err != nil { 156 return CheckDeleted(d, err, "LBV2 Member") 157 } 158 159 log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 Member %s: %+v", d.Id(), member) 160 161 d.Set("name", member.Name) 162 d.Set("weight", member.Weight) 163 d.Set("admin_state_up", member.AdminStateUp) 164 d.Set("tenant_id", member.TenantID) 165 d.Set("subnet_id", member.SubnetID) 166 d.Set("address", member.Address) 167 d.Set("protocol_port", member.ProtocolPort) 168 d.Set("id", member.ID) 169 170 return nil 171 } 172 173 func resourceMemberV2Update(d *schema.ResourceData, meta interface{}) error { 174 config := meta.(*Config) 175 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 176 if err != nil { 177 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 178 } 179 180 var updateOpts pools.UpdateMemberOpts 181 if d.HasChange("name") { 182 updateOpts.Name = d.Get("name").(string) 183 } 184 if d.HasChange("weight") { 185 updateOpts.Weight = d.Get("weight").(int) 186 } 187 if d.HasChange("admin_state_up") { 188 asu := d.Get("admin_state_up").(bool) 189 updateOpts.AdminStateUp = &asu 190 } 191 192 log.Printf("[DEBUG] Updating OpenStack LBaaSV2 Member %s with options: %+v", d.Id(), updateOpts) 193 194 _, err = pools.UpdateMember(networkingClient, d.Get("pool_id").(string), d.Id(), updateOpts).Extract() 195 if err != nil { 196 return fmt.Errorf("Error updating OpenStack LBaaSV2 Member: %s", err) 197 } 198 199 return resourceMemberV2Read(d, meta) 200 } 201 202 func resourceMemberV2Delete(d *schema.ResourceData, meta interface{}) error { 203 config := meta.(*Config) 204 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 205 if err != nil { 206 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 207 } 208 209 stateConf := &resource.StateChangeConf{ 210 Pending: []string{"ACTIVE", "PENDING_DELETE"}, 211 Target: []string{"DELETED"}, 212 Refresh: waitForMemberDelete(networkingClient, d.Get("pool_id").(string), d.Id()), 213 Timeout: 2 * time.Minute, 214 Delay: 5 * time.Second, 215 MinTimeout: 3 * time.Second, 216 } 217 218 _, err = stateConf.WaitForState() 219 if err != nil { 220 return fmt.Errorf("Error deleting OpenStack LBaaSV2 Member: %s", err) 221 } 222 223 d.SetId("") 224 return nil 225 } 226 227 func waitForMemberActive(networkingClient *gophercloud.ServiceClient, poolID string, memberID string) resource.StateRefreshFunc { 228 return func() (interface{}, string, error) { 229 member, err := pools.GetMember(networkingClient, poolID, memberID).Extract() 230 if err != nil { 231 return nil, "", err 232 } 233 234 // The member resource has no Status attribute, so a successful Get is the best we can do 235 log.Printf("[DEBUG] OpenStack LBaaSV2 Member: %+v", member) 236 return member, "ACTIVE", nil 237 } 238 } 239 240 func waitForMemberDelete(networkingClient *gophercloud.ServiceClient, poolID string, memberID string) resource.StateRefreshFunc { 241 return func() (interface{}, string, error) { 242 log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 Member %s", memberID) 243 244 member, err := pools.GetMember(networkingClient, poolID, memberID).Extract() 245 if err != nil { 246 if _, ok := err.(gophercloud.ErrDefault404); ok { 247 log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID) 248 return member, "DELETED", nil 249 } 250 return member, "ACTIVE", err 251 } 252 253 log.Printf("[DEBUG] Openstack LBaaSV2 Member: %+v", member) 254 err = pools.DeleteMember(networkingClient, poolID, memberID).ExtractErr() 255 if err != nil { 256 if _, ok := err.(gophercloud.ErrDefault404); ok { 257 log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID) 258 return member, "DELETED", nil 259 } 260 261 if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok { 262 if errCode.Actual == 409 { 263 log.Printf("[DEBUG] OpenStack LBaaSV2 Member (%s) is still in use.", memberID) 264 return member, "ACTIVE", nil 265 } 266 } 267 268 return member, "ACTIVE", err 269 } 270 271 log.Printf("[DEBUG] OpenStack LBaaSV2 Member %s still active.", memberID) 272 return member, "ACTIVE", nil 273 } 274 }