FWaaS Router Insertion (#220)
* Implement fwaasrouterinsertion FWaaS extension
This commit adds the FWaaS router insertion extension which enables
the firewall to be associated with one or more routers.
* FWaaS Router Insertion Acceptance Tests
diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go
index 976eb79..0b021d3 100644
--- a/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go
+++ b/acceptance/openstack/networking/v2/extensions/fwaas/firewall_test.go
@@ -6,7 +6,9 @@
"testing"
"github.com/gophercloud/gophercloud/acceptance/clients"
+ layer3 "github.com/gophercloud/gophercloud/acceptance/openstack/networking/v2/extensions/layer3"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
+ "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion"
)
func TestFirewallList(t *testing.T) {
@@ -36,6 +38,12 @@
t.Fatalf("Unable to create a network client: %v", err)
}
+ router, err := layer3.CreateExternalRouter(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create router: %v", err)
+ }
+ defer layer3.DeleteRouter(t, client, router.ID)
+
rule, err := CreateRule(t, client)
if err != nil {
t.Fatalf("Unable to create rule: %v", err)
@@ -52,7 +60,7 @@
PrintPolicy(t, policy)
- firewall, err := CreateFirewall(t, client, policy.ID)
+ firewall, err := CreateFirewallOnRouter(t, client, policy.ID, router.ID)
if err != nil {
t.Fatalf("Unable to create firewall: %v", err)
}
@@ -60,11 +68,22 @@
PrintFirewall(t, firewall)
- updateOpts := firewalls.UpdateOpts{
+ router2, err := layer3.CreateExternalRouter(t, client)
+ if err != nil {
+ t.Fatalf("Unable to create router: %v", err)
+ }
+ defer layer3.DeleteRouter(t, client, router2.ID)
+
+ firewallUpdateOpts := firewalls.UpdateOpts{
PolicyID: policy.ID,
Description: "Some firewall description",
}
+ updateOpts := routerinsertion.UpdateOptsExt{
+ firewallUpdateOpts,
+ []string{router2.ID},
+ }
+
_, err = firewalls.Update(client, firewall.ID, updateOpts).Extract()
if err != nil {
t.Fatalf("Unable to update firewall: %v", err)
diff --git a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go
index 6533a51..dd361f9 100644
--- a/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go
+++ b/acceptance/openstack/networking/v2/extensions/fwaas/fwaas.go
@@ -9,6 +9,7 @@
"github.com/gophercloud/gophercloud/acceptance/tools"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
+ "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
)
@@ -39,6 +40,39 @@
return firewall, nil
}
+// CreateFirewallOnRouter will create a Firewall with a random name and a
+// specified policy ID attached to a specified Router. An error will be
+// returned if the firewall could not be created.
+func CreateFirewallOnRouter(t *testing.T, client *gophercloud.ServiceClient, policyID string, routerID string) (*firewalls.Firewall, error) {
+ firewallName := tools.RandomString("TESTACC-", 8)
+
+ t.Logf("Attempting to create firewall %s", firewallName)
+
+ firewallCreateOpts := firewalls.CreateOpts{
+ Name: firewallName,
+ PolicyID: policyID,
+ }
+
+ createOpts := routerinsertion.CreateOptsExt{
+ firewallCreateOpts,
+ []string{routerID},
+ }
+
+ firewall, err := firewalls.Create(client, createOpts).Extract()
+ if err != nil {
+ return firewall, err
+ }
+
+ t.Logf("Waiting for firewall to become active.")
+ if err := WaitForFirewallState(client, firewall.ID, "ACTIVE", 60); err != nil {
+ return firewall, err
+ }
+
+ t.Logf("Successfully created firewall %s", firewallName)
+
+ return firewall, nil
+}
+
// CreatePolicy will create a Firewall Policy with a random name and given
// rule. An error will be returned if the rule could not be created.
func CreatePolicy(t *testing.T, client *gophercloud.ServiceClient, ruleID string) (*policies.Policy, error) {
diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/doc.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/doc.go
new file mode 100644
index 0000000..9b847e2
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/routerinsertion/doc.go
@@ -0,0 +1,2 @@
+// Package routerinsertion implements the fwaasrouterinsertion FWaaS extension.
+package routerinsertion
diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go
new file mode 100644
index 0000000..3a5942e
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/routerinsertion/requests.go
@@ -0,0 +1,51 @@
+package routerinsertion
+
+import (
+ "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
+)
+
+// CreateOptsExt adds a RouterIDs option to the base CreateOpts.
+type CreateOptsExt struct {
+ firewalls.CreateOptsBuilder
+ RouterIDs []string `json:"router_ids"`
+}
+
+// ToFirewallCreateMap adds router_ids to the base firewall creation options.
+func (opts CreateOptsExt) ToFirewallCreateMap() (map[string]interface{}, error) {
+ base, err := opts.CreateOptsBuilder.ToFirewallCreateMap()
+ if err != nil {
+ return nil, err
+ }
+
+ if len(opts.RouterIDs) == 0 {
+ return base, nil
+ }
+
+ firewallMap := base["firewall"].(map[string]interface{})
+ firewallMap["router_ids"] = opts.RouterIDs
+
+ return base, nil
+}
+
+// UpdateOptsExt updates a RouterIDs option to the base UpdateOpts.
+type UpdateOptsExt struct {
+ firewalls.UpdateOptsBuilder
+ RouterIDs []string `json:"router_ids"`
+}
+
+// ToFirewallUpdateMap adds router_ids to the base firewall update options.
+func (opts UpdateOptsExt) ToFirewallUpdateMap() (map[string]interface{}, error) {
+ base, err := opts.UpdateOptsBuilder.ToFirewallUpdateMap()
+ if err != nil {
+ return nil, err
+ }
+
+ if len(opts.RouterIDs) == 0 {
+ return base, nil
+ }
+
+ firewallMap := base["firewall"].(map[string]interface{})
+ firewallMap["router_ids"] = opts.RouterIDs
+
+ return base, nil
+}
diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/doc.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/doc.go
new file mode 100644
index 0000000..36a6c1c
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/doc.go
@@ -0,0 +1,2 @@
+// networking_extensions_fwaas_extensions_routerinsertion_v2
+package testing
diff --git a/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go
new file mode 100644
index 0000000..a4890ee
--- /dev/null
+++ b/openstack/networking/v2/extensions/fwaas/routerinsertion/testing/requests_test.go
@@ -0,0 +1,126 @@
+package testing
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ "github.com/gophercloud/gophercloud"
+ fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
+ "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
+ "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion"
+ th "github.com/gophercloud/gophercloud/testhelper"
+)
+
+func TestCreate(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/v2.0/fw/firewalls", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "POST")
+ 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, `
+{
+ "firewall":{
+ "name": "fw",
+ "description": "OpenStack firewall",
+ "admin_state_up": true,
+ "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
+ "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
+ "router_ids": [
+ "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8"
+ ]
+ }
+}
+ `)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+
+ fmt.Fprintf(w, `
+{
+ "firewall":{
+ "status": "PENDING_CREATE",
+ "name": "fw",
+ "description": "OpenStack firewall",
+ "admin_state_up": true,
+ "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
+ "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c"
+ }
+}
+ `)
+ })
+
+ firewallCreateOpts := firewalls.CreateOpts{
+ TenantID: "b4eedccc6fb74fa8a7ad6b08382b852b",
+ Name: "fw",
+ Description: "OpenStack firewall",
+ AdminStateUp: gophercloud.Enabled,
+ PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
+ }
+ createOpts := routerinsertion.CreateOptsExt{
+ firewallCreateOpts,
+ []string{"8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8"},
+ }
+
+ _, err := firewalls.Create(fake.ServiceClient(), createOpts).Extract()
+ th.AssertNoErr(t, err)
+}
+
+func TestUpdate(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/v2.0/fw/firewalls/ea5b5315-64f6-4ea3-8e58-981cc37c6576", 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, `
+{
+ "firewall":{
+ "name": "fw",
+ "description": "updated fw",
+ "admin_state_up":false,
+ "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
+ "router_ids": [
+ "8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8"
+ ]
+ }
+}
+ `)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+
+ fmt.Fprintf(w, `
+{
+ "firewall": {
+ "status": "ACTIVE",
+ "name": "fw",
+ "admin_state_up": false,
+ "tenant_id": "b4eedccc6fb74fa8a7ad6b08382b852b",
+ "firewall_policy_id": "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
+ "id": "ea5b5315-64f6-4ea3-8e58-981cc37c6576",
+ "description": "OpenStack firewall"
+ }
+}
+ `)
+ })
+
+ firewallUpdateOpts := firewalls.UpdateOpts{
+ Name: "fw",
+ Description: "updated fw",
+ AdminStateUp: gophercloud.Disabled,
+ PolicyID: "19ab8c87-4a32-4e6a-a74e-b77fffb89a0c",
+ }
+ updateOpts := routerinsertion.UpdateOptsExt{
+ firewallUpdateOpts,
+ []string{"8a3a0d6a-34b5-4a92-b65d-6375a4c1e9e8"},
+ }
+
+ _, err := firewalls.Update(fake.ServiceClient(), "ea5b5315-64f6-4ea3-8e58-981cc37c6576", updateOpts).Extract()
+ th.AssertNoErr(t, err)
+}