Adds Fixed IP support to os-floating-ips

This commit enables the ability to specify a fixed IP when associating a
floating IP to an instance. If a fixed IP is not specified, Nova will
attempt to associate the floating IP to the first detected fixed IP, as it
did prior to this patch.
diff --git a/acceptance/openstack/compute/v2/floatingip_test.go b/acceptance/openstack/compute/v2/floatingip_test.go
index ab7554b..32f9a04 100644
--- a/acceptance/openstack/compute/v2/floatingip_test.go
+++ b/acceptance/openstack/compute/v2/floatingip_test.go
@@ -49,7 +49,9 @@
 	return fip, err
 }
 
-func associateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
+func associateFloatingIPDeprecated(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
+	// This form works, but is considered deprecated.
+	// See associateFloatingIP or associateFloatingIPFixed
 	err := floatingip.Associate(client, serverId, fip.IP).ExtractErr()
 	th.AssertNoErr(t, err)
 	t.Logf("Associated floating IP %v from instance %v", fip.IP, serverId)
@@ -63,6 +65,63 @@
 	t.Logf("Floating IP %v is associated with Fixed IP %v", fip.IP, floatingIp.FixedIP)
 }
 
+func associateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
+	associateOpts := floatingip.AssociateOpts{
+		ServerID:   serverId,
+		FloatingIP: fip.IP,
+	}
+
+	err := floatingip.AssociateFloatingIP(client, associateOpts).ExtractErr()
+	th.AssertNoErr(t, err)
+	t.Logf("Associated floating IP %v from instance %v", fip.IP, serverId)
+	defer func() {
+		err = floatingip.DisassociateFloatingIP(client, associateOpts).ExtractErr()
+		th.AssertNoErr(t, err)
+		t.Logf("Disassociated floating IP %v from instance %v", fip.IP, serverId)
+	}()
+	floatingIp, err := floatingip.Get(client, fip.ID).Extract()
+	th.AssertNoErr(t, err)
+	t.Logf("Floating IP %v is associated with Fixed IP %v", fip.IP, floatingIp.FixedIP)
+}
+
+func associateFloatingIPFixed(t *testing.T, client *gophercloud.ServiceClient, serverId string, fip *floatingip.FloatingIP) {
+
+	network := os.Getenv("OS_NETWORK_NAME")
+	server, err := servers.Get(client, serverId).Extract()
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+
+	var fixedIP string
+	for _, networkAddresses := range server.Addresses[network].([]interface{}) {
+		address := networkAddresses.(map[string]interface{})
+		if address["OS-EXT-IPS:type"] == "fixed" {
+			if address["version"].(float64) == 4 {
+				fixedIP = address["addr"].(string)
+			}
+		}
+	}
+
+	associateOpts := floatingip.AssociateOpts{
+		ServerID:   serverId,
+		FloatingIP: fip.IP,
+		FixedIP:    fixedIP,
+	}
+
+	err = floatingip.AssociateFloatingIP(client, associateOpts).ExtractErr()
+	th.AssertNoErr(t, err)
+	t.Logf("Associated floating IP %v from instance %v with Fixed IP %v", fip.IP, serverId, fixedIP)
+	defer func() {
+		err = floatingip.DisassociateFloatingIP(client, associateOpts).ExtractErr()
+		th.AssertNoErr(t, err)
+		t.Logf("Disassociated floating IP %v from instance %v with Fixed IP %v", fip.IP, serverId, fixedIP)
+	}()
+	floatingIp, err := floatingip.Get(client, fip.ID).Extract()
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, floatingIp.FixedIP, fixedIP)
+	t.Logf("Floating IP %v is associated with Fixed IP %v", fip.IP, floatingIp.FixedIP)
+}
+
 func TestFloatingIP(t *testing.T) {
 	pool := os.Getenv("OS_POOL_NAME")
 	if pool == "" {
@@ -102,6 +161,8 @@
 		t.Logf("Floating IP deleted.")
 	}()
 
+	associateFloatingIPDeprecated(t, client, server.ID, fip)
 	associateFloatingIP(t, client, server.ID, fip)
+	associateFloatingIPFixed(t, client, server.ID, fip)
 
 }