github.com/gabrielperezs/terraform@v0.7.0-rc2.0.20160715084931-f7da2612946f/builtin/providers/openstack/resource_openstack_networking_router_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/rackspace/gophercloud" 12 "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers" 13 ) 14 15 func resourceNetworkingRouterV2() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceNetworkingRouterV2Create, 18 Read: resourceNetworkingRouterV2Read, 19 Update: resourceNetworkingRouterV2Update, 20 Delete: resourceNetworkingRouterV2Delete, 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 "name": &schema.Schema{ 30 Type: schema.TypeString, 31 Optional: true, 32 ForceNew: false, 33 }, 34 "admin_state_up": &schema.Schema{ 35 Type: schema.TypeBool, 36 Optional: true, 37 ForceNew: false, 38 Computed: true, 39 }, 40 "distributed": &schema.Schema{ 41 Type: schema.TypeBool, 42 Optional: true, 43 ForceNew: true, 44 Computed: true, 45 }, 46 "external_gateway": &schema.Schema{ 47 Type: schema.TypeString, 48 Optional: true, 49 ForceNew: false, 50 }, 51 "tenant_id": &schema.Schema{ 52 Type: schema.TypeString, 53 Optional: true, 54 ForceNew: true, 55 Computed: true, 56 }, 57 "value_specs": &schema.Schema{ 58 Type: schema.TypeMap, 59 Optional: true, 60 ForceNew: true, 61 }, 62 }, 63 } 64 } 65 66 // routerCreateOpts contains all the values needed to create a new router. There are 67 // no required values. 68 type RouterCreateOpts struct { 69 Name string 70 AdminStateUp *bool 71 Distributed *bool 72 TenantID string 73 GatewayInfo *routers.GatewayInfo 74 ValueSpecs map[string]string 75 } 76 77 // ToRouterCreateMap casts a routerCreateOpts struct to a map. 78 func (opts RouterCreateOpts) ToRouterCreateMap() (map[string]interface{}, error) { 79 r := make(map[string]interface{}) 80 81 if gophercloud.MaybeString(opts.Name) != nil { 82 r["name"] = opts.Name 83 } 84 85 if opts.AdminStateUp != nil { 86 r["admin_state_up"] = opts.AdminStateUp 87 } 88 89 if opts.Distributed != nil { 90 r["distributed"] = opts.Distributed 91 } 92 93 if gophercloud.MaybeString(opts.TenantID) != nil { 94 r["tenant_id"] = opts.TenantID 95 } 96 97 if opts.GatewayInfo != nil { 98 r["external_gateway_info"] = opts.GatewayInfo 99 } 100 101 if opts.ValueSpecs != nil { 102 for k, v := range opts.ValueSpecs { 103 r[k] = v 104 } 105 } 106 107 return map[string]interface{}{"router": r}, nil 108 } 109 110 func resourceNetworkingRouterV2Create(d *schema.ResourceData, meta interface{}) error { 111 config := meta.(*Config) 112 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 113 if err != nil { 114 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 115 } 116 117 createOpts := RouterCreateOpts{ 118 Name: d.Get("name").(string), 119 TenantID: d.Get("tenant_id").(string), 120 ValueSpecs: routerValueSpecs(d), 121 } 122 123 if asuRaw, ok := d.GetOk("admin_state_up"); ok { 124 asu := asuRaw.(bool) 125 createOpts.AdminStateUp = &asu 126 } 127 128 if dRaw, ok := d.GetOk("distributed"); ok { 129 d := dRaw.(bool) 130 createOpts.Distributed = &d 131 } 132 133 externalGateway := d.Get("external_gateway").(string) 134 if externalGateway != "" { 135 gatewayInfo := routers.GatewayInfo{ 136 NetworkID: externalGateway, 137 } 138 createOpts.GatewayInfo = &gatewayInfo 139 } 140 141 log.Printf("[DEBUG] Create Options: %#v", createOpts) 142 n, err := routers.Create(networkingClient, createOpts).Extract() 143 if err != nil { 144 return fmt.Errorf("Error creating OpenStack Neutron router: %s", err) 145 } 146 log.Printf("[INFO] Router ID: %s", n.ID) 147 148 log.Printf("[DEBUG] Waiting for OpenStack Neutron Router (%s) to become available", n.ID) 149 stateConf := &resource.StateChangeConf{ 150 Pending: []string{"BUILD", "PENDING_CREATE", "PENDING_UPDATE"}, 151 Target: []string{"ACTIVE"}, 152 Refresh: waitForRouterActive(networkingClient, n.ID), 153 Timeout: 10 * time.Minute, 154 Delay: 5 * time.Second, 155 MinTimeout: 3 * time.Second, 156 } 157 158 _, err = stateConf.WaitForState() 159 160 d.SetId(n.ID) 161 162 return resourceNetworkingRouterV2Read(d, meta) 163 } 164 165 func resourceNetworkingRouterV2Read(d *schema.ResourceData, meta interface{}) error { 166 config := meta.(*Config) 167 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 168 if err != nil { 169 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 170 } 171 172 n, err := routers.Get(networkingClient, d.Id()).Extract() 173 if err != nil { 174 httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError) 175 if !ok { 176 return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err) 177 } 178 179 if httpError.Actual == 404 { 180 d.SetId("") 181 return nil 182 } 183 return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err) 184 } 185 186 log.Printf("[DEBUG] Retreived Router %s: %+v", d.Id(), n) 187 188 d.Set("name", n.Name) 189 d.Set("admin_state_up", n.AdminStateUp) 190 d.Set("distributed", n.Distributed) 191 d.Set("tenant_id", n.TenantID) 192 d.Set("external_gateway", n.GatewayInfo.NetworkID) 193 194 return nil 195 } 196 197 func resourceNetworkingRouterV2Update(d *schema.ResourceData, meta interface{}) error { 198 routerId := d.Id() 199 osMutexKV.Lock(routerId) 200 defer osMutexKV.Unlock(routerId) 201 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 routers.UpdateOpts 209 if d.HasChange("name") { 210 updateOpts.Name = d.Get("name").(string) 211 } 212 if d.HasChange("admin_state_up") { 213 asu := d.Get("admin_state_up").(bool) 214 updateOpts.AdminStateUp = &asu 215 } 216 217 log.Printf("[DEBUG] Updating Router %s with options: %+v", d.Id(), updateOpts) 218 219 _, err = routers.Update(networkingClient, d.Id(), updateOpts).Extract() 220 if err != nil { 221 return fmt.Errorf("Error updating OpenStack Neutron Router: %s", err) 222 } 223 224 return resourceNetworkingRouterV2Read(d, meta) 225 } 226 227 func resourceNetworkingRouterV2Delete(d *schema.ResourceData, meta interface{}) error { 228 config := meta.(*Config) 229 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 230 if err != nil { 231 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 232 } 233 234 stateConf := &resource.StateChangeConf{ 235 Pending: []string{"ACTIVE"}, 236 Target: []string{"DELETED"}, 237 Refresh: waitForRouterDelete(networkingClient, d.Id()), 238 Timeout: 10 * time.Minute, 239 Delay: 5 * time.Second, 240 MinTimeout: 3 * time.Second, 241 } 242 243 _, err = stateConf.WaitForState() 244 if err != nil { 245 return fmt.Errorf("Error deleting OpenStack Neutron Router: %s", err) 246 } 247 248 d.SetId("") 249 return nil 250 } 251 252 func waitForRouterActive(networkingClient *gophercloud.ServiceClient, routerId string) resource.StateRefreshFunc { 253 return func() (interface{}, string, error) { 254 r, err := routers.Get(networkingClient, routerId).Extract() 255 if err != nil { 256 return nil, r.Status, err 257 } 258 259 log.Printf("[DEBUG] OpenStack Neutron Router: %+v", r) 260 return r, r.Status, nil 261 } 262 } 263 264 func waitForRouterDelete(networkingClient *gophercloud.ServiceClient, routerId string) resource.StateRefreshFunc { 265 return func() (interface{}, string, error) { 266 log.Printf("[DEBUG] Attempting to delete OpenStack Router %s.\n", routerId) 267 268 r, err := routers.Get(networkingClient, routerId).Extract() 269 if err != nil { 270 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 271 if !ok { 272 return r, "ACTIVE", err 273 } 274 if errCode.Actual == 404 { 275 log.Printf("[DEBUG] Successfully deleted OpenStack Router %s", routerId) 276 return r, "DELETED", nil 277 } 278 } 279 280 err = routers.Delete(networkingClient, routerId).ExtractErr() 281 if err != nil { 282 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 283 if !ok { 284 return r, "ACTIVE", err 285 } 286 if errCode.Actual == 404 { 287 log.Printf("[DEBUG] Successfully deleted OpenStack Router %s", routerId) 288 return r, "DELETED", nil 289 } 290 } 291 292 log.Printf("[DEBUG] OpenStack Router %s still active.\n", routerId) 293 return r, "ACTIVE", nil 294 } 295 } 296 297 func routerValueSpecs(d *schema.ResourceData) map[string]string { 298 m := make(map[string]string) 299 for key, val := range d.Get("value_specs").(map[string]interface{}) { 300 m[key] = val.(string) 301 } 302 return m 303 }