Skip to content
This repository has been archived by the owner on Aug 1, 2023. It is now read-only.

[rfr] Adds Fixed IP support to os-floating-ips #403

Merged
merged 2 commits into from
Jan 5, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 62 additions & 1 deletion acceptance/openstack/compute/v2/floatingip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ func createFloatingIP(t *testing.T, client *gophercloud.ServiceClient) (*floatin
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)
Expand All @@ -63,6 +65,63 @@ func associateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, server
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.AssociateInstance(client, associateOpts).ExtractErr()
th.AssertNoErr(t, err)
t.Logf("Associated floating IP %v from instance %v", fip.IP, serverId)
defer func() {
err = floatingip.DisassociateInstance(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.AssociateInstance(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.DisassociateInstance(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 == "" {
Expand Down Expand Up @@ -102,6 +161,8 @@ func TestFloatingIP(t *testing.T) {
t.Logf("Floating IP deleted.")
}()

associateFloatingIPDeprecated(t, client, server.ID, fip)
associateFloatingIP(t, client, server.ID, fip)
associateFloatingIPFixed(t, client, server.ID, fip)

}
19 changes: 19 additions & 0 deletions openstack/compute/v2/extensions/floatingip/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,25 @@ func HandleAssociateSuccessfully(t *testing.T) {
})
}

// HandleFixedAssociateSucessfully configures the test server to respond to a Post request
// to associate an allocated floating IP with a specific fixed IP address
func HandleAssociateFixedSuccessfully(t *testing.T) {
th.Mux.HandleFunc("/servers/4d8c3732-a248-40ed-bebc-539a6ffd25c0/action", func(w http.ResponseWriter, r *http.Request) {
th.TestMethod(t, r, "POST")
th.TestHeader(t, r, "X-Auth-Token", client.TokenID)
th.TestJSONRequest(t, r, `
{
"addFloatingIp": {
"address": "10.10.10.2",
"fixed_address": "166.78.185.201"
}
}
`)

w.WriteHeader(http.StatusAccepted)
})
}

// HandleDisassociateSuccessfully configures the test server to respond to a Post request
// to disassociate an allocated floating IP
func HandleDisassociateSuccessfully(t *testing.T) {
Expand Down
79 changes: 79 additions & 0 deletions openstack/compute/v2/extensions/floatingip/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ type CreateOpts struct {
Pool string
}

// AssociateOpts specifies the required information to associate or disassociate a floating IP to an instance
type AssociateOpts struct {
// ServerID is the UUID of the server
ServerID string

// FixedIP is an optional fixed IP address of the server
FixedIP string

// FloatingIP is the floating IP to associate with an instance
FloatingIP string
}

// ToFloatingIPCreateMap constructs a request body from CreateOpts.
func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
if opts.Pool == "" {
Expand All @@ -35,6 +47,26 @@ func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
return map[string]interface{}{"pool": opts.Pool}, nil
}

// ToAssociateMap constructs a request body from AssociateOpts.
func (opts AssociateOpts) ToAssociateMap() (map[string]interface{}, error) {
if opts.ServerID == "" {
return nil, errors.New("Required field missing for floating IP association: ServerID")
}

if opts.FloatingIP == "" {
return nil, errors.New("Required field missing for floating IP association: FloatingIP")
}

associateInfo := map[string]interface{}{
"serverId": opts.ServerID,
"floatingIp": opts.FloatingIP,
"fixedIp": opts.FixedIP,
}

return associateInfo, nil

}

// Create requests the creation of a new floating IP
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
var res CreateResult
Expand Down Expand Up @@ -68,6 +100,7 @@ func Delete(client *gophercloud.ServiceClient, id string) DeleteResult {
// association / disassociation

// Associate pairs an allocated floating IP with an instance
// Deprecated. Use AssociateInstance.
func Associate(client *gophercloud.ServiceClient, serverId, fip string) AssociateResult {
var res AssociateResult

Expand All @@ -79,7 +112,33 @@ func Associate(client *gophercloud.ServiceClient, serverId, fip string) Associat
return res
}

// AssociateInstance pairs an allocated floating IP with an instance.
func AssociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) AssociateResult {
var res AssociateResult

associateInfo, err := opts.ToAssociateMap()
if err != nil {
res.Err = err
return res
}

addFloatingIp := make(map[string]interface{})
addFloatingIp["address"] = associateInfo["floatingIp"].(string)

// fixedIp is not required
if associateInfo["fixedIp"] != "" {
addFloatingIp["fixed_address"] = associateInfo["fixedIp"].(string)
}

serverId := associateInfo["serverId"].(string)

reqBody := map[string]interface{}{"addFloatingIp": addFloatingIp}
_, res.Err = client.Post(associateURL(client, serverId), reqBody, nil, nil)
return res
}

// Disassociate decouples an allocated floating IP from an instance
// Deprecated. Use DisassociateInstance.
func Disassociate(client *gophercloud.ServiceClient, serverId, fip string) DisassociateResult {
var res DisassociateResult

Expand All @@ -90,3 +149,23 @@ func Disassociate(client *gophercloud.ServiceClient, serverId, fip string) Disas
_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
return res
}

// DisassociateInstance decouples an allocated floating IP from an instance
func DisassociateInstance(client *gophercloud.ServiceClient, opts AssociateOpts) DisassociateResult {
var res DisassociateResult

associateInfo, err := opts.ToAssociateMap()
if err != nil {
res.Err = err
return res
}

removeFloatingIp := make(map[string]interface{})
removeFloatingIp["address"] = associateInfo["floatingIp"].(string)
reqBody := map[string]interface{}{"removeFloatingIp": removeFloatingIp}

serverId := associateInfo["serverId"].(string)

_, res.Err = client.Post(disassociateURL(client, serverId), reqBody, nil, nil)
return res
}
47 changes: 45 additions & 2 deletions openstack/compute/v2/extensions/floatingip/requests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func TestDelete(t *testing.T) {
th.AssertNoErr(t, err)
}

func TestAssociate(t *testing.T) {
func TestAssociateDeprecated(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleAssociateSuccessfully(t)
Expand All @@ -68,7 +68,36 @@ func TestAssociate(t *testing.T) {
th.AssertNoErr(t, err)
}

func TestDisassociate(t *testing.T) {
func TestAssociate(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleAssociateSuccessfully(t)

associateOpts := AssociateOpts{
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
FloatingIP: "10.10.10.2",
}

err := AssociateInstance(client.ServiceClient(), associateOpts).ExtractErr()
th.AssertNoErr(t, err)
}

func TestAssociateFixed(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleAssociateFixedSuccessfully(t)

associateOpts := AssociateOpts{
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
FloatingIP: "10.10.10.2",
FixedIP: "166.78.185.201",
}

err := AssociateInstance(client.ServiceClient(), associateOpts).ExtractErr()
th.AssertNoErr(t, err)
}

func TestDisassociateDeprecated(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleDisassociateSuccessfully(t)
Expand All @@ -78,3 +107,17 @@ func TestDisassociate(t *testing.T) {
err := Disassociate(client.ServiceClient(), serverId, fip).ExtractErr()
th.AssertNoErr(t, err)
}

func TestDisassociateInstance(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()
HandleDisassociateSuccessfully(t)

associateOpts := AssociateOpts{
ServerID: "4d8c3732-a248-40ed-bebc-539a6ffd25c0",
FloatingIP: "10.10.10.2",
}

err := DisassociateInstance(client.ServiceClient(), associateOpts).ExtractErr()
th.AssertNoErr(t, err)
}