package openstack import ( "fmt" "log" "time" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers" "github.com/gophercloud/gophercloud/openstack/networking/v2/ports" ) func resourceNetworkingRouterInterfaceV2() *schema.Resource { return &schema.Resource{ Create: resourceNetworkingRouterInterfaceV2Create, Read: resourceNetworkingRouterInterfaceV2Read, Delete: resourceNetworkingRouterInterfaceV2Delete, Timeouts: &schema.ResourceTimeout{ Create: schema.DefaultTimeout(10 * time.Minute), Delete: schema.DefaultTimeout(10 * time.Minute), }, Schema: map[string]*schema.Schema{ "region": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), }, "router_id": &schema.Schema{ Type: schema.TypeString, Required: true, ForceNew: true, }, "subnet_id": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, }, "port_id": &schema.Schema{ Type: schema.TypeString, Optional: true, ForceNew: true, }, }, } } func resourceNetworkingRouterInterfaceV2Create(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := config.networkingV2Client(GetRegion(d)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } createOpts := routers.AddInterfaceOpts{ SubnetID: d.Get("subnet_id").(string), PortID: d.Get("port_id").(string), } log.Printf("[DEBUG] Create Options: %#v", createOpts) n, err := routers.AddInterface(networkingClient, d.Get("router_id").(string), createOpts).Extract() if err != nil { return fmt.Errorf("Error creating OpenStack Neutron router interface: %s", err) } log.Printf("[INFO] Router interface Port ID: %s", n.PortID) log.Printf("[DEBUG] Waiting for Router Interface (%s) to become available", n.PortID) stateConf := &resource.StateChangeConf{ Pending: []string{"BUILD", "PENDING_CREATE", "PENDING_UPDATE"}, Target: []string{"ACTIVE"}, Refresh: waitForRouterInterfaceActive(networkingClient, n.PortID), Timeout: d.Timeout(schema.TimeoutCreate), Delay: 5 * time.Second, MinTimeout: 3 * time.Second, } _, err = stateConf.WaitForState() d.SetId(n.PortID) return resourceNetworkingRouterInterfaceV2Read(d, meta) } func resourceNetworkingRouterInterfaceV2Read(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := config.networkingV2Client(GetRegion(d)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } n, err := ports.Get(networkingClient, d.Id()).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { d.SetId("") return nil } return fmt.Errorf("Error retrieving OpenStack Neutron Router Interface: %s", err) } log.Printf("[DEBUG] Retrieved Router Interface %s: %+v", d.Id(), n) return nil } func resourceNetworkingRouterInterfaceV2Delete(d *schema.ResourceData, meta interface{}) error { config := meta.(*Config) networkingClient, err := config.networkingV2Client(GetRegion(d)) if err != nil { return fmt.Errorf("Error creating OpenStack networking client: %s", err) } stateConf := &resource.StateChangeConf{ Pending: []string{"ACTIVE"}, Target: []string{"DELETED"}, Refresh: waitForRouterInterfaceDelete(networkingClient, d), Timeout: d.Timeout(schema.TimeoutDelete), Delay: 5 * time.Second, MinTimeout: 3 * time.Second, } _, err = stateConf.WaitForState() if err != nil { return fmt.Errorf("Error deleting OpenStack Neutron Router Interface: %s", err) } d.SetId("") return nil } func waitForRouterInterfaceActive(networkingClient *gophercloud.ServiceClient, rId string) resource.StateRefreshFunc { return func() (interface{}, string, error) { r, err := ports.Get(networkingClient, rId).Extract() if err != nil { return nil, "", err } log.Printf("[DEBUG] OpenStack Neutron Router Interface: %+v", r) return r, r.Status, nil } } func waitForRouterInterfaceDelete(networkingClient *gophercloud.ServiceClient, d *schema.ResourceData) resource.StateRefreshFunc { return func() (interface{}, string, error) { routerId := d.Get("router_id").(string) routerInterfaceId := d.Id() log.Printf("[DEBUG] Attempting to delete OpenStack Router Interface %s.", routerInterfaceId) removeOpts := routers.RemoveInterfaceOpts{ SubnetID: d.Get("subnet_id").(string), PortID: d.Get("port_id").(string), } r, err := ports.Get(networkingClient, routerInterfaceId).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { log.Printf("[DEBUG] Successfully deleted OpenStack Router Interface %s", routerInterfaceId) return r, "DELETED", nil } return r, "ACTIVE", err } _, err = routers.RemoveInterface(networkingClient, routerId, removeOpts).Extract() if err != nil { if _, ok := err.(gophercloud.ErrDefault404); ok { log.Printf("[DEBUG] Successfully deleted OpenStack Router Interface %s.", routerInterfaceId) return r, "DELETED", nil } if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok { if errCode.Actual == 409 { log.Printf("[DEBUG] Router Interface %s is still in use.", routerInterfaceId) return r, "ACTIVE", nil } } return r, "ACTIVE", err } log.Printf("[DEBUG] OpenStack Router Interface %s is still active.", routerInterfaceId) return r, "ACTIVE", nil } }