github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/openstack/resource_openstack_compute_floatingip_associate_v2.go (about) 1 package openstack 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 8 "github.com/gophercloud/gophercloud" 9 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips" 10 "github.com/gophercloud/gophercloud/openstack/compute/v2/servers" 11 nfloatingips "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips" 12 "github.com/hashicorp/terraform/helper/schema" 13 ) 14 15 func resourceComputeFloatingIPAssociateV2() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceComputeFloatingIPAssociateV2Create, 18 Read: resourceComputeFloatingIPAssociateV2Read, 19 Delete: resourceComputeFloatingIPAssociateV2Delete, 20 Importer: &schema.ResourceImporter{ 21 State: schema.ImportStatePassthrough, 22 }, 23 24 Schema: map[string]*schema.Schema{ 25 "region": &schema.Schema{ 26 Type: schema.TypeString, 27 Required: true, 28 ForceNew: true, 29 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 30 }, 31 "floating_ip": &schema.Schema{ 32 Type: schema.TypeString, 33 Required: true, 34 ForceNew: true, 35 }, 36 "instance_id": &schema.Schema{ 37 Type: schema.TypeString, 38 Required: true, 39 ForceNew: true, 40 }, 41 "fixed_ip": &schema.Schema{ 42 Type: schema.TypeString, 43 Optional: true, 44 ForceNew: true, 45 }, 46 }, 47 } 48 } 49 50 func resourceComputeFloatingIPAssociateV2Create(d *schema.ResourceData, meta interface{}) error { 51 config := meta.(*Config) 52 computeClient, err := config.computeV2Client(GetRegion(d)) 53 if err != nil { 54 return fmt.Errorf("Error creating OpenStack compute client: %s", err) 55 } 56 57 floatingIP := d.Get("floating_ip").(string) 58 fixedIP := d.Get("fixed_ip").(string) 59 instanceId := d.Get("instance_id").(string) 60 61 associateOpts := floatingips.AssociateOpts{ 62 FloatingIP: floatingIP, 63 FixedIP: fixedIP, 64 } 65 log.Printf("[DEBUG] Associate Options: %#v", associateOpts) 66 67 err = floatingips.AssociateInstance(computeClient, instanceId, associateOpts).ExtractErr() 68 if err != nil { 69 return fmt.Errorf("Error associating Floating IP: %s", err) 70 } 71 72 // There's an API call to get this information, but it has been 73 // deprecated. The Neutron API could be used, but I'm trying not 74 // to mix service APIs. Therefore, a faux ID will be used. 75 id := fmt.Sprintf("%s/%s/%s", floatingIP, instanceId, fixedIP) 76 d.SetId(id) 77 78 // This API call is synchronous, so Create won't return until the IP 79 // is attached. No need to wait for a state. 80 81 return resourceComputeFloatingIPAssociateV2Read(d, meta) 82 } 83 84 func resourceComputeFloatingIPAssociateV2Read(d *schema.ResourceData, meta interface{}) error { 85 config := meta.(*Config) 86 computeClient, err := config.computeV2Client(GetRegion(d)) 87 if err != nil { 88 return fmt.Errorf("Error creating OpenStack compute client: %s", err) 89 } 90 91 // Obtain relevant info from parsing the ID 92 floatingIP, instanceId, fixedIP, err := parseComputeFloatingIPAssociateId(d.Id()) 93 if err != nil { 94 return err 95 } 96 97 // Now check and see whether the floating IP still exists. 98 // First try to do this by querying the Network API. 99 networkEnabled := true 100 networkClient, err := config.networkingV2Client(GetRegion(d)) 101 if err != nil { 102 networkEnabled = false 103 } 104 105 var exists bool 106 if networkEnabled { 107 log.Printf("[DEBUG] Checking for Floating IP existence via Network API") 108 exists, err = resourceComputeFloatingIPAssociateV2NetworkExists(networkClient, floatingIP) 109 } else { 110 log.Printf("[DEBUG] Checking for Floating IP existence via Compute API") 111 exists, err = resourceComputeFloatingIPAssociateV2ComputeExists(computeClient, floatingIP) 112 } 113 114 if err != nil { 115 return err 116 } 117 118 if !exists { 119 d.SetId("") 120 } 121 122 // Next, see if the instance still exists 123 instance, err := servers.Get(computeClient, instanceId).Extract() 124 if err != nil { 125 if CheckDeleted(d, err, "instance") == nil { 126 return nil 127 } 128 } 129 130 // Finally, check and see if the floating ip is still associated with the instance. 131 var associated bool 132 for _, networkAddresses := range instance.Addresses { 133 for _, element := range networkAddresses.([]interface{}) { 134 address := element.(map[string]interface{}) 135 if address["OS-EXT-IPS:type"] == "floating" && address["addr"] == floatingIP { 136 associated = true 137 } 138 } 139 } 140 141 if !associated { 142 d.SetId("") 143 } 144 145 // Set the attributes pulled from the composed resource ID 146 d.Set("floating_ip", floatingIP) 147 d.Set("instance_id", instanceId) 148 d.Set("fixed_ip", fixedIP) 149 d.Set("region", GetRegion(d)) 150 151 return nil 152 } 153 154 func resourceComputeFloatingIPAssociateV2Delete(d *schema.ResourceData, meta interface{}) error { 155 config := meta.(*Config) 156 computeClient, err := config.computeV2Client(GetRegion(d)) 157 if err != nil { 158 return fmt.Errorf("Error creating OpenStack compute client: %s", err) 159 } 160 161 floatingIP := d.Get("floating_ip").(string) 162 instanceId := d.Get("instance_id").(string) 163 164 disassociateOpts := floatingips.DisassociateOpts{ 165 FloatingIP: floatingIP, 166 } 167 log.Printf("[DEBUG] Disssociate Options: %#v", disassociateOpts) 168 169 err = floatingips.DisassociateInstance(computeClient, instanceId, disassociateOpts).ExtractErr() 170 if err != nil { 171 return CheckDeleted(d, err, "floating ip association") 172 } 173 174 return nil 175 } 176 177 func parseComputeFloatingIPAssociateId(id string) (string, string, string, error) { 178 idParts := strings.Split(id, "/") 179 if len(idParts) < 3 { 180 return "", "", "", fmt.Errorf("Unable to determine floating ip association ID") 181 } 182 183 floatingIP := idParts[0] 184 instanceId := idParts[1] 185 fixedIP := idParts[2] 186 187 return floatingIP, instanceId, fixedIP, nil 188 } 189 190 func resourceComputeFloatingIPAssociateV2NetworkExists(networkClient *gophercloud.ServiceClient, floatingIP string) (bool, error) { 191 listOpts := nfloatingips.ListOpts{ 192 FloatingIP: floatingIP, 193 } 194 allPages, err := nfloatingips.List(networkClient, listOpts).AllPages() 195 if err != nil { 196 return false, err 197 } 198 199 allFips, err := nfloatingips.ExtractFloatingIPs(allPages) 200 if err != nil { 201 return false, err 202 } 203 204 if len(allFips) > 1 { 205 return false, fmt.Errorf("There was a problem retrieving the floating IP") 206 } 207 208 if len(allFips) == 0 { 209 return false, nil 210 } 211 212 return true, nil 213 } 214 215 func resourceComputeFloatingIPAssociateV2ComputeExists(computeClient *gophercloud.ServiceClient, floatingIP string) (bool, error) { 216 // If the Network API isn't available, fall back to the deprecated Compute API. 217 allPages, err := floatingips.List(computeClient).AllPages() 218 if err != nil { 219 return false, err 220 } 221 222 allFips, err := floatingips.ExtractFloatingIPs(allPages) 223 if err != nil { 224 return false, err 225 } 226 227 for _, f := range allFips { 228 if f.IP == floatingIP { 229 return true, nil 230 } 231 } 232 233 return false, nil 234 }