Ports: Remove Security Groups and Address Pairs (#236)
This commit enables security groups and address pairs to be removed
from a port. This is done by allowing an empty value to be passed
for either attribute in the port update request.
diff --git a/acceptance/openstack/networking/v2/ports_test.go b/acceptance/openstack/networking/v2/ports_test.go
index fa1b4a4..a11ff55 100644
--- a/acceptance/openstack/networking/v2/ports_test.go
+++ b/acceptance/openstack/networking/v2/ports_test.go
@@ -6,6 +6,7 @@
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
+ extensions "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions"
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
)
@@ -72,3 +73,120 @@
tools.PrintResource(t, newPort)
}
+
+func TestPortsRemoveSecurityGroups(t *testing.T) {
+ client, err := clients.NewNetworkV2Client()
+ if err != nil {
+ t.Fatalf("Unable to create a network client: %v", err)
+ }
+
+ // Create Network
+ network, err := CreateNetwork(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create network: %v", err)
+ }
+ defer DeleteNetwork(t, client, network.ID)
+
+ // Create Subnet
+ subnet, err := CreateSubnet(t, client, network.ID)
+ if err != nil {
+ t.Fatalf("Unable to create subnet: %v", err)
+ }
+ defer DeleteSubnet(t, client, subnet.ID)
+
+ // Create port
+ port, err := CreatePort(t, client, network.ID, subnet.ID)
+ if err != nil {
+ t.Fatalf("Unable to create port: %v", err)
+ }
+ defer DeletePort(t, client, port.ID)
+
+ PrintPort(t, port)
+
+ // Create a Security Group
+ group, err := extensions.CreateSecurityGroup(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create security group: %v", err)
+ }
+ defer extensions.DeleteSecurityGroup(t, client, group.ID)
+
+ // Add the group to the port
+ updateOpts := ports.UpdateOpts{
+ SecurityGroups: []string{group.ID},
+ }
+ newPort, err := ports.Update(client, port.ID, updateOpts).Extract()
+ if err != nil {
+ t.Fatalf("Could not update port: %v", err)
+ }
+
+ // Remove the group
+ updateOpts = ports.UpdateOpts{
+ SecurityGroups: []string{},
+ }
+ newPort, err = ports.Update(client, port.ID, updateOpts).Extract()
+ if err != nil {
+ t.Fatalf("Could not update port: %v", err)
+ }
+
+ PrintPort(t, newPort)
+
+ if len(newPort.SecurityGroups) > 0 {
+ t.Fatalf("Unable to remove security group from port")
+ }
+}
+
+func TestPortsRemoveAddressPair(t *testing.T) {
+ client, err := clients.NewNetworkV2Client()
+ if err != nil {
+ t.Fatalf("Unable to create a network client: %v", err)
+ }
+
+ // Create Network
+ network, err := CreateNetwork(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create network: %v", err)
+ }
+ defer DeleteNetwork(t, client, network.ID)
+
+ // Create Subnet
+ subnet, err := CreateSubnet(t, client, network.ID)
+ if err != nil {
+ t.Fatalf("Unable to create subnet: %v", err)
+ }
+ defer DeleteSubnet(t, client, subnet.ID)
+
+ // Create port
+ port, err := CreatePort(t, client, network.ID, subnet.ID)
+ if err != nil {
+ t.Fatalf("Unable to create port: %v", err)
+ }
+ defer DeletePort(t, client, port.ID)
+
+ PrintPort(t, port)
+
+ // Add an address pair to the port
+ updateOpts := ports.UpdateOpts{
+ AllowedAddressPairs: []ports.AddressPair{
+ ports.AddressPair{IPAddress: "192.168.255.10", MACAddress: "aa:bb:cc:dd:ee:ff"},
+ },
+ }
+ newPort, err := ports.Update(client, port.ID, updateOpts).Extract()
+ if err != nil {
+ t.Fatalf("Could not update port: %v", err)
+ }
+
+ // Remove the address pair
+ updateOpts = ports.UpdateOpts{
+ AllowedAddressPairs: []ports.AddressPair{},
+ }
+ newPort, err = ports.Update(client, port.ID, updateOpts).Extract()
+ if err != nil {
+ t.Fatalf("Could not update port: %v", err)
+ }
+
+ PrintPort(t, newPort)
+
+ if len(newPort.AllowedAddressPairs) > 0 {
+ t.Fatalf("Unable to remove the address pair")
+ }
+}
diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go
index 2a53202..d353b7e 100644
--- a/openstack/networking/v2/ports/requests.go
+++ b/openstack/networking/v2/ports/requests.go
@@ -119,8 +119,8 @@
FixedIPs interface{} `json:"fixed_ips,omitempty"`
DeviceID string `json:"device_id,omitempty"`
DeviceOwner string `json:"device_owner,omitempty"`
- SecurityGroups []string `json:"security_groups,omitempty"`
- AllowedAddressPairs []AddressPair `json:"allowed_address_pairs,omitempty"`
+ SecurityGroups []string `json:"security_groups"`
+ AllowedAddressPairs []AddressPair `json:"allowed_address_pairs"`
}
// ToPortUpdateMap casts an UpdateOpts struct to a map.
diff --git a/openstack/networking/v2/ports/testing/requests_test.go b/openstack/networking/v2/ports/testing/requests_test.go
index 007dc5a..1da6ad3 100644
--- a/openstack/networking/v2/ports/testing/requests_test.go
+++ b/openstack/networking/v2/ports/testing/requests_test.go
@@ -342,6 +342,168 @@
th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
}
+func TestRemoveSecurityGroups(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "PUT")
+ th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+ th.TestHeader(t, r, "Content-Type", "application/json")
+ th.TestHeader(t, r, "Accept", "application/json")
+ th.TestJSONRequest(t, r, `
+{
+ "port": {
+ "name": "new_port_name",
+ "fixed_ips": [
+ {
+ "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+ "ip_address": "10.0.0.3"
+ }
+ ],
+ "allowed_address_pairs": [
+ {
+ "ip_address": "10.0.0.4",
+ "mac_address": "fa:16:3e:c9:cb:f0"
+ }
+ ],
+ "security_groups": []
+ }
+}
+ `)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+
+ fmt.Fprintf(w, `
+{
+ "port": {
+ "status": "DOWN",
+ "name": "new_port_name",
+ "admin_state_up": true,
+ "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+ "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
+ "device_owner": "",
+ "mac_address": "fa:16:3e:c9:cb:f0",
+ "fixed_ips": [
+ {
+ "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+ "ip_address": "10.0.0.3"
+ }
+ ],
+ "allowed_address_pairs": [
+ {
+ "ip_address": "10.0.0.4",
+ "mac_address": "fa:16:3e:c9:cb:f0"
+ }
+ ],
+ "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
+ "device_id": ""
+ }
+}
+ `)
+ })
+
+ options := ports.UpdateOpts{
+ Name: "new_port_name",
+ FixedIPs: []ports.IP{
+ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
+ },
+ SecurityGroups: []string{},
+ AllowedAddressPairs: []ports.AddressPair{
+ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
+ },
+ }
+
+ s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
+ th.AssertNoErr(t, err)
+
+ th.AssertEquals(t, s.Name, "new_port_name")
+ th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
+ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
+ })
+ th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{
+ {IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
+ })
+ th.AssertDeepEquals(t, s.SecurityGroups, []string(nil))
+}
+
+func TestRemoveAllowedAddressPairs(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "PUT")
+ th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+ th.TestHeader(t, r, "Content-Type", "application/json")
+ th.TestHeader(t, r, "Accept", "application/json")
+ th.TestJSONRequest(t, r, `
+{
+ "port": {
+ "name": "new_port_name",
+ "fixed_ips": [
+ {
+ "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+ "ip_address": "10.0.0.3"
+ }
+ ],
+ "allowed_address_pairs": [],
+ "security_groups": [
+ "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
+ ]
+ }
+}
+ `)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+
+ fmt.Fprintf(w, `
+{
+ "port": {
+ "status": "DOWN",
+ "name": "new_port_name",
+ "admin_state_up": true,
+ "network_id": "a87cc70a-3e15-4acf-8205-9b711a3531b7",
+ "tenant_id": "d6700c0c9ffa4f1cb322cd4a1f3906fa",
+ "device_owner": "",
+ "mac_address": "fa:16:3e:c9:cb:f0",
+ "fixed_ips": [
+ {
+ "subnet_id": "a0304c3a-4f08-4c43-88af-d796509c97d2",
+ "ip_address": "10.0.0.3"
+ }
+ ],
+ "id": "65c0ee9f-d634-4522-8954-51021b570b0d",
+ "security_groups": [
+ "f0ac4394-7e4a-4409-9701-ba8be283dbc3"
+ ],
+ "device_id": ""
+ }
+}
+ `)
+ })
+
+ options := ports.UpdateOpts{
+ Name: "new_port_name",
+ FixedIPs: []ports.IP{
+ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
+ },
+ SecurityGroups: []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"},
+ AllowedAddressPairs: []ports.AddressPair{},
+ }
+
+ s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
+ th.AssertNoErr(t, err)
+
+ th.AssertEquals(t, s.Name, "new_port_name")
+ th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
+ {SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
+ })
+ th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair(nil))
+ th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
+}
+
func TestDelete(t *testing.T) {
th.SetupHTTP()
defer th.TeardownHTTP()