github.com/vnpaycloud-console/gophercloud/v2@v2.0.5/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go (about)

     1  package testing
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net/http"
     7  	"testing"
     8  	"time"
     9  
    10  	fake "github.com/vnpaycloud-console/gophercloud/v2/openstack/networking/v2/common"
    11  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/networking/v2/extensions/layer3/routers"
    12  	"github.com/vnpaycloud-console/gophercloud/v2/pagination"
    13  	th "github.com/vnpaycloud-console/gophercloud/v2/testhelper"
    14  )
    15  
    16  func TestList(t *testing.T) {
    17  	th.SetupHTTP()
    18  	defer th.TeardownHTTP()
    19  
    20  	th.Mux.HandleFunc("/v2.0/routers", func(w http.ResponseWriter, r *http.Request) {
    21  		th.TestMethod(t, r, "GET")
    22  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
    23  
    24  		w.Header().Add("Content-Type", "application/json")
    25  		w.WriteHeader(http.StatusOK)
    26  
    27  		fmt.Fprint(w, `
    28  {
    29      "routers": [
    30          {
    31              "status": "ACTIVE",
    32              "external_gateway_info": null,
    33              "name": "second_routers",
    34              "admin_state_up": true,
    35              "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
    36              "distributed": false,
    37              "id": "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b"
    38          },
    39          {
    40              "status": "ACTIVE",
    41              "external_gateway_info": {
    42                  "network_id": "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8"
    43              },
    44              "name": "router1",
    45              "admin_state_up": true,
    46              "tenant_id": "33a40233088643acb66ff6eb0ebea679",
    47              "distributed": false,
    48              "id": "a9254bdb-2613-4a13-ac4c-adc581fba50d"
    49          },
    50          {
    51              "status": "ACTIVE",
    52              "external_gateway_info": {
    53                  "network_id": "2b37576e-b050-4891-8b20-e1e37a93942a",
    54                  "external_fixed_ips": [
    55                      {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"},
    56                      {"ip_address": "198.51.100.33", "subnet_id": "1d699529-bdfd-43f8-bcaa-bff00c547af2"}
    57                  ],
    58                  "qos_policy_id": "6601bae5-f15a-4687-8be9-ddec9a2f8a8b"
    59              },
    60              "name": "gateway",
    61              "admin_state_up": true,
    62              "tenant_id": "a3e881e0a6534880c5473d95b9442099",
    63              "distributed": false,
    64              "id": "308a035c-005d-4452-a9fe-6f8f2f0c28d8"
    65          }
    66      ]
    67  }
    68  			`)
    69  	})
    70  
    71  	count := 0
    72  
    73  	err := routers.List(fake.ServiceClient(), routers.ListOpts{}).EachPage(context.TODO(), func(_ context.Context, page pagination.Page) (bool, error) {
    74  		count++
    75  		actual, err := routers.ExtractRouters(page)
    76  		if err != nil {
    77  			t.Errorf("Failed to extract routers: %v", err)
    78  			return false, err
    79  		}
    80  
    81  		expected := []routers.Router{
    82  			{
    83  				Status:       "ACTIVE",
    84  				GatewayInfo:  routers.GatewayInfo{NetworkID: ""},
    85  				AdminStateUp: true,
    86  				Distributed:  false,
    87  				Name:         "second_routers",
    88  				ID:           "7177abc4-5ae9-4bb7-b0d4-89e94a4abf3b",
    89  				TenantID:     "6b96ff0cb17a4b859e1e575d221683d3",
    90  			},
    91  			{
    92  				Status:       "ACTIVE",
    93  				GatewayInfo:  routers.GatewayInfo{NetworkID: "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8"},
    94  				AdminStateUp: true,
    95  				Distributed:  false,
    96  				Name:         "router1",
    97  				ID:           "a9254bdb-2613-4a13-ac4c-adc581fba50d",
    98  				TenantID:     "33a40233088643acb66ff6eb0ebea679",
    99  			},
   100  			{
   101  				Status: "ACTIVE",
   102  				GatewayInfo: routers.GatewayInfo{
   103  					NetworkID: "2b37576e-b050-4891-8b20-e1e37a93942a",
   104  					ExternalFixedIPs: []routers.ExternalFixedIP{
   105  						{IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"},
   106  						{IPAddress: "198.51.100.33", SubnetID: "1d699529-bdfd-43f8-bcaa-bff00c547af2"},
   107  					},
   108  					QoSPolicyID: "6601bae5-f15a-4687-8be9-ddec9a2f8a8b",
   109  				},
   110  				AdminStateUp: true,
   111  				Distributed:  false,
   112  				Name:         "gateway",
   113  				ID:           "308a035c-005d-4452-a9fe-6f8f2f0c28d8",
   114  				TenantID:     "a3e881e0a6534880c5473d95b9442099",
   115  			},
   116  		}
   117  
   118  		th.CheckDeepEquals(t, expected, actual)
   119  
   120  		return true, nil
   121  	})
   122  	th.AssertNoErr(t, err)
   123  
   124  	if count != 1 {
   125  		t.Errorf("Expected 1 page, got %d", count)
   126  	}
   127  }
   128  
   129  func TestCreate(t *testing.T) {
   130  	th.SetupHTTP()
   131  	defer th.TeardownHTTP()
   132  
   133  	th.Mux.HandleFunc("/v2.0/routers", func(w http.ResponseWriter, r *http.Request) {
   134  		th.TestMethod(t, r, "POST")
   135  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   136  		th.TestHeader(t, r, "Content-Type", "application/json")
   137  		th.TestHeader(t, r, "Accept", "application/json")
   138  		th.TestJSONRequest(t, r, `
   139  {
   140     "router":{
   141        "name": "foo_router",
   142        "admin_state_up": false,
   143        "external_gateway_info":{
   144           "enable_snat": false,
   145           "network_id":"8ca37218-28ff-41cb-9b10-039601ea7e6b",
   146           "external_fixed_ips": [
   147               {"subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}
   148           ],
   149           "qos_policy_id": "6601bae5-f15a-4687-8be9-ddec9a2f8a8b"
   150  	  },
   151  	  "availability_zone_hints": ["zone1", "zone2"]
   152     }
   153  }
   154  			`)
   155  
   156  		w.Header().Add("Content-Type", "application/json")
   157  		w.WriteHeader(http.StatusCreated)
   158  
   159  		fmt.Fprint(w, `
   160  {
   161      "router": {
   162          "status": "ACTIVE",
   163          "external_gateway_info": {
   164              "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b",
   165              "enable_snat": false,
   166              "external_fixed_ips": [
   167                  {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}
   168              ],
   169              "qos_policy_id": "6601bae5-f15a-4687-8be9-ddec9a2f8a8b"
   170          },
   171          "name": "foo_router",
   172          "admin_state_up": false,
   173          "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
   174  		"distributed": false,
   175  		"availability_zone_hints": ["zone1", "zone2"],
   176          "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e"
   177      }
   178  }
   179  		`)
   180  	})
   181  
   182  	asu := false
   183  	enableSNAT := false
   184  	qosID := "6601bae5-f15a-4687-8be9-ddec9a2f8a8b"
   185  	efi := []routers.ExternalFixedIP{
   186  		{
   187  			SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def",
   188  		},
   189  	}
   190  	gwi := routers.GatewayInfo{
   191  		NetworkID:        "8ca37218-28ff-41cb-9b10-039601ea7e6b",
   192  		EnableSNAT:       &enableSNAT,
   193  		ExternalFixedIPs: efi,
   194  		QoSPolicyID:      qosID,
   195  	}
   196  	options := routers.CreateOpts{
   197  		Name:                  "foo_router",
   198  		AdminStateUp:          &asu,
   199  		GatewayInfo:           &gwi,
   200  		AvailabilityZoneHints: []string{"zone1", "zone2"},
   201  	}
   202  	r, err := routers.Create(context.TODO(), fake.ServiceClient(), options).Extract()
   203  	th.AssertNoErr(t, err)
   204  
   205  	gwi.ExternalFixedIPs = []routers.ExternalFixedIP{{
   206  		IPAddress: "192.0.2.17",
   207  		SubnetID:  "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def",
   208  	}}
   209  
   210  	th.AssertEquals(t, "foo_router", r.Name)
   211  	th.AssertEquals(t, false, r.AdminStateUp)
   212  	th.AssertDeepEquals(t, gwi, r.GatewayInfo)
   213  	th.AssertDeepEquals(t, []string{"zone1", "zone2"}, r.AvailabilityZoneHints)
   214  }
   215  
   216  func TestGet(t *testing.T) {
   217  	th.SetupHTTP()
   218  	defer th.TeardownHTTP()
   219  
   220  	th.Mux.HandleFunc("/v2.0/routers/a07eea83-7710-4860-931b-5fe220fae533", func(w http.ResponseWriter, r *http.Request) {
   221  		th.TestMethod(t, r, "GET")
   222  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   223  
   224  		w.Header().Add("Content-Type", "application/json")
   225  		w.WriteHeader(http.StatusOK)
   226  
   227  		fmt.Fprint(w, `
   228  {
   229      "router": {
   230          "status": "ACTIVE",
   231          "external_gateway_info": {
   232              "network_id": "85d76829-6415-48ff-9c63-5c5ca8c61ac6",
   233              "external_fixed_ips": [
   234                  {"ip_address": "198.51.100.33", "subnet_id": "1d699529-bdfd-43f8-bcaa-bff00c547af2"}
   235              ],
   236              "qos_policy_id": "6601bae5-f15a-4687-8be9-ddec9a2f8a8b"
   237          },
   238          "routes": [
   239              {
   240                  "nexthop": "10.1.0.10",
   241                  "destination": "40.0.1.0/24"
   242              }
   243          ],
   244          "name": "router1",
   245          "admin_state_up": true,
   246          "tenant_id": "d6554fe62e2f41efbb6e026fad5c1542",
   247  		"distributed": false,
   248  		"availability_zone_hints": ["zone1", "zone2"],
   249          "id": "a07eea83-7710-4860-931b-5fe220fae533"
   250      }
   251  }
   252  			`)
   253  	})
   254  
   255  	n, err := routers.Get(context.TODO(), fake.ServiceClient(), "a07eea83-7710-4860-931b-5fe220fae533").Extract()
   256  	th.AssertNoErr(t, err)
   257  
   258  	th.AssertEquals(t, n.Status, "ACTIVE")
   259  	th.AssertDeepEquals(t, n.GatewayInfo, routers.GatewayInfo{
   260  		NetworkID: "85d76829-6415-48ff-9c63-5c5ca8c61ac6",
   261  		ExternalFixedIPs: []routers.ExternalFixedIP{
   262  			{IPAddress: "198.51.100.33", SubnetID: "1d699529-bdfd-43f8-bcaa-bff00c547af2"},
   263  		},
   264  		QoSPolicyID: "6601bae5-f15a-4687-8be9-ddec9a2f8a8b",
   265  	})
   266  	th.AssertEquals(t, n.Name, "router1")
   267  	th.AssertEquals(t, n.AdminStateUp, true)
   268  	th.AssertEquals(t, n.TenantID, "d6554fe62e2f41efbb6e026fad5c1542")
   269  	th.AssertEquals(t, n.ID, "a07eea83-7710-4860-931b-5fe220fae533")
   270  	th.AssertDeepEquals(t, n.Routes, []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}})
   271  	th.AssertDeepEquals(t, n.AvailabilityZoneHints, []string{"zone1", "zone2"})
   272  }
   273  
   274  func TestUpdate(t *testing.T) {
   275  	th.SetupHTTP()
   276  	defer th.TeardownHTTP()
   277  
   278  	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
   279  		th.TestMethod(t, r, "PUT")
   280  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   281  		th.TestHeader(t, r, "Content-Type", "application/json")
   282  		th.TestHeader(t, r, "Accept", "application/json")
   283  		th.TestJSONRequest(t, r, `
   284  {
   285      "router": {
   286  			"name": "new_name",
   287          "external_gateway_info": {
   288              "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b",
   289              "qos_policy_id": "01ba32e5-f15a-4687-8be9-ddec92a2f8a8"
   290  		},
   291          "routes": [
   292              {
   293                  "nexthop": "10.1.0.10",
   294                  "destination": "40.0.1.0/24"
   295              }
   296          ]
   297      }
   298  }
   299  			`)
   300  
   301  		w.Header().Add("Content-Type", "application/json")
   302  		w.WriteHeader(http.StatusOK)
   303  
   304  		fmt.Fprint(w, `
   305  {
   306      "router": {
   307          "status": "ACTIVE",
   308          "external_gateway_info": {
   309              "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b",
   310              "external_fixed_ips": [
   311                  {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}
   312              ],
   313              "qos_policy_id": "01ba32e5-f15a-4687-8be9-ddec92a2f8a8"
   314          },
   315          "name": "new_name",
   316          "admin_state_up": true,
   317          "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
   318          "distributed": false,
   319          "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e",
   320          "routes": [
   321              {
   322                  "nexthop": "10.1.0.10",
   323                  "destination": "40.0.1.0/24"
   324              }
   325          ]
   326      }
   327  }
   328  		`)
   329  	})
   330  
   331  	gwi := routers.GatewayInfo{
   332  		NetworkID:   "8ca37218-28ff-41cb-9b10-039601ea7e6b",
   333  		QoSPolicyID: "01ba32e5-f15a-4687-8be9-ddec92a2f8a8",
   334  	}
   335  	r := []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}}
   336  	options := routers.UpdateOpts{Name: "new_name", GatewayInfo: &gwi, Routes: &r}
   337  
   338  	n, err := routers.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
   339  	th.AssertNoErr(t, err)
   340  
   341  	gwi.ExternalFixedIPs = []routers.ExternalFixedIP{
   342  		{IPAddress: "192.0.2.17", SubnetID: "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"},
   343  	}
   344  
   345  	th.AssertEquals(t, n.Name, "new_name")
   346  	th.AssertDeepEquals(t, n.GatewayInfo, gwi)
   347  	th.AssertDeepEquals(t, n.Routes, []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}})
   348  }
   349  
   350  func TestUpdateWithoutRoutes(t *testing.T) {
   351  	th.SetupHTTP()
   352  	defer th.TeardownHTTP()
   353  
   354  	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
   355  		th.TestMethod(t, r, "PUT")
   356  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   357  		th.TestHeader(t, r, "Content-Type", "application/json")
   358  		th.TestHeader(t, r, "Accept", "application/json")
   359  		th.TestJSONRequest(t, r, `
   360  {
   361      "router": {
   362          "name": "new_name"
   363      }
   364  }
   365  		`)
   366  
   367  		w.Header().Add("Content-Type", "application/json")
   368  		w.WriteHeader(http.StatusOK)
   369  
   370  		fmt.Fprint(w, `
   371  {
   372      "router": {
   373          "status": "ACTIVE",
   374          "external_gateway_info": {
   375              "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b",
   376              "external_fixed_ips": [
   377                  {"ip_address": "192.0.2.17", "subnet_id": "ab561bc4-1a8e-48f2-9fbd-376fcb1a1def"}
   378              ]
   379          },
   380          "name": "new_name",
   381          "admin_state_up": true,
   382          "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
   383          "distributed": false,
   384          "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e",
   385          "routes": [
   386              {
   387                  "nexthop": "10.1.0.10",
   388                  "destination": "40.0.1.0/24"
   389              }
   390          ]
   391      }
   392  }
   393  		`)
   394  	})
   395  
   396  	options := routers.UpdateOpts{Name: "new_name"}
   397  
   398  	n, err := routers.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
   399  	th.AssertNoErr(t, err)
   400  
   401  	th.AssertEquals(t, n.Name, "new_name")
   402  	th.AssertDeepEquals(t, n.Routes, []routers.Route{{DestinationCIDR: "40.0.1.0/24", NextHop: "10.1.0.10"}})
   403  }
   404  
   405  func TestAllRoutesRemoved(t *testing.T) {
   406  	th.SetupHTTP()
   407  	defer th.TeardownHTTP()
   408  
   409  	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
   410  		th.TestMethod(t, r, "PUT")
   411  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   412  		th.TestHeader(t, r, "Content-Type", "application/json")
   413  		th.TestHeader(t, r, "Accept", "application/json")
   414  		th.TestJSONRequest(t, r, `
   415  {
   416      "router": {
   417          "routes": []
   418      }
   419  }
   420  			`)
   421  
   422  		w.Header().Add("Content-Type", "application/json")
   423  		w.WriteHeader(http.StatusOK)
   424  
   425  		fmt.Fprint(w, `
   426  {
   427      "router": {
   428          "status": "ACTIVE",
   429          "external_gateway_info": {
   430              "network_id": "8ca37218-28ff-41cb-9b10-039601ea7e6b"
   431          },
   432          "name": "name",
   433          "admin_state_up": true,
   434          "tenant_id": "6b96ff0cb17a4b859e1e575d221683d3",
   435          "distributed": false,
   436          "id": "8604a0de-7f6b-409a-a47c-a1cc7bc77b2e",
   437          "routes": []
   438      }
   439  }
   440  		`)
   441  	})
   442  
   443  	r := []routers.Route{}
   444  	options := routers.UpdateOpts{Routes: &r}
   445  
   446  	n, err := routers.Update(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", options).Extract()
   447  	th.AssertNoErr(t, err)
   448  
   449  	th.AssertDeepEquals(t, n.Routes, []routers.Route{})
   450  }
   451  
   452  func TestDelete(t *testing.T) {
   453  	th.SetupHTTP()
   454  	defer th.TeardownHTTP()
   455  
   456  	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c", func(w http.ResponseWriter, r *http.Request) {
   457  		th.TestMethod(t, r, "DELETE")
   458  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   459  		w.WriteHeader(http.StatusNoContent)
   460  	})
   461  
   462  	res := routers.Delete(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c")
   463  	th.AssertNoErr(t, res.Err)
   464  }
   465  
   466  func TestAddInterface(t *testing.T) {
   467  	th.SetupHTTP()
   468  	defer th.TeardownHTTP()
   469  
   470  	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/add_router_interface", func(w http.ResponseWriter, r *http.Request) {
   471  		th.TestMethod(t, r, "PUT")
   472  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   473  		th.TestHeader(t, r, "Content-Type", "application/json")
   474  		th.TestHeader(t, r, "Accept", "application/json")
   475  		th.TestJSONRequest(t, r, `
   476  {
   477      "subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1"
   478  }
   479  	`)
   480  
   481  		w.Header().Add("Content-Type", "application/json")
   482  		w.WriteHeader(http.StatusOK)
   483  
   484  		fmt.Fprint(w, `
   485  {
   486      "subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188",
   487      "tenant_id": "017d8de156df4177889f31a9bd6edc00",
   488      "port_id": "3f990102-4485-4df1-97a0-2c35bdb85b31",
   489      "id": "9a83fa11-8da5-436e-9afe-3d3ac5ce7770"
   490  }
   491  `)
   492  	})
   493  
   494  	opts := routers.AddInterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"}
   495  	res, err := routers.AddInterface(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract()
   496  	th.AssertNoErr(t, err)
   497  
   498  	th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID)
   499  	th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", res.TenantID)
   500  	th.AssertEquals(t, "3f990102-4485-4df1-97a0-2c35bdb85b31", res.PortID)
   501  	th.AssertEquals(t, "9a83fa11-8da5-436e-9afe-3d3ac5ce7770", res.ID)
   502  }
   503  
   504  func TestAddInterfaceRequiredOpts(t *testing.T) {
   505  	_, err := routers.AddInterface(context.TODO(), fake.ServiceClient(), "foo", routers.AddInterfaceOpts{}).Extract()
   506  	if err == nil {
   507  		t.Fatalf("Expected error, got none")
   508  	}
   509  	_, err = routers.AddInterface(context.TODO(), fake.ServiceClient(), "foo", routers.AddInterfaceOpts{SubnetID: "bar", PortID: "baz"}).Extract()
   510  	if err == nil {
   511  		t.Fatalf("Expected error, got none")
   512  	}
   513  }
   514  
   515  func TestRemoveInterface(t *testing.T) {
   516  	th.SetupHTTP()
   517  	defer th.TeardownHTTP()
   518  
   519  	th.Mux.HandleFunc("/v2.0/routers/4e8e5957-649f-477b-9e5b-f1f75b21c03c/remove_router_interface", func(w http.ResponseWriter, r *http.Request) {
   520  		th.TestMethod(t, r, "PUT")
   521  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   522  		th.TestHeader(t, r, "Content-Type", "application/json")
   523  		th.TestHeader(t, r, "Accept", "application/json")
   524  		th.TestJSONRequest(t, r, `
   525  {
   526  		"subnet_id": "a2f1f29d-571b-4533-907f-5803ab96ead1"
   527  }
   528  	`)
   529  
   530  		w.Header().Add("Content-Type", "application/json")
   531  		w.WriteHeader(http.StatusOK)
   532  
   533  		fmt.Fprint(w, `
   534  {
   535  		"subnet_id": "0d32a837-8069-4ec3-84c4-3eef3e10b188",
   536  		"tenant_id": "017d8de156df4177889f31a9bd6edc00",
   537  		"port_id": "3f990102-4485-4df1-97a0-2c35bdb85b31",
   538  		"id": "9a83fa11-8da5-436e-9afe-3d3ac5ce7770"
   539  }
   540  `)
   541  	})
   542  
   543  	opts := routers.RemoveInterfaceOpts{SubnetID: "a2f1f29d-571b-4533-907f-5803ab96ead1"}
   544  	res, err := routers.RemoveInterface(context.TODO(), fake.ServiceClient(), "4e8e5957-649f-477b-9e5b-f1f75b21c03c", opts).Extract()
   545  	th.AssertNoErr(t, err)
   546  
   547  	th.AssertEquals(t, "0d32a837-8069-4ec3-84c4-3eef3e10b188", res.SubnetID)
   548  	th.AssertEquals(t, "017d8de156df4177889f31a9bd6edc00", res.TenantID)
   549  	th.AssertEquals(t, "3f990102-4485-4df1-97a0-2c35bdb85b31", res.PortID)
   550  	th.AssertEquals(t, "9a83fa11-8da5-436e-9afe-3d3ac5ce7770", res.ID)
   551  }
   552  
   553  func TestListL3Agents(t *testing.T) {
   554  	th.SetupHTTP()
   555  	defer th.TeardownHTTP()
   556  
   557  	th.Mux.HandleFunc("/v2.0/routers/fa3a4aaa-c73f-48aa-a603-8c8bf642b7c0/l3-agents", func(w http.ResponseWriter, r *http.Request) {
   558  		th.TestMethod(t, r, "GET")
   559  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   560  
   561  		w.Header().Add("Content-Type", "application/json")
   562  		w.WriteHeader(http.StatusOK)
   563  
   564  		fmt.Fprint(w, `
   565  {
   566      "agents": [
   567          {
   568              "id": "ddbf087c-e38f-4a73-bcb3-c38f2a719a03",
   569              "agent_type": "L3 agent",
   570              "binary": "neutron-l3-agent",
   571              "topic": "l3_agent",
   572              "host": "os-ctrl-02",
   573              "admin_state_up": true,
   574              "created_at": "2017-07-26 23:15:44",
   575              "started_at": "2018-06-26 21:46:19",
   576              "heartbeat_timestamp": "2019-01-09 10:28:53",
   577              "description": "My L3 agent for OpenStack",
   578              "resources_synced": true,
   579              "availability_zone": "nova",
   580              "alive": true,
   581              "configurations": {
   582                  "agent_mode": "legacy",
   583                  "ex_gw_ports": 2,
   584                  "floating_ips": 2,
   585                  "handle_internal_only_routers": true,
   586                  "interface_driver": "linuxbridge",
   587                  "interfaces": 1,
   588                  "log_agent_heartbeats": false,
   589                  "routers": 2
   590              },
   591              "resource_versions": {},
   592              "ha_state": "standby"
   593          },
   594          {
   595              "id": "4541cc6c-87bc-4cee-bad2-36ca78836c91",
   596              "agent_type": "L3 agent",
   597              "binary": "neutron-l3-agent",
   598              "topic": "l3_agent",
   599              "host": "os-ctrl-03",
   600              "admin_state_up": true,
   601              "created_at": "2017-01-22 14:00:50",
   602              "started_at": "2018-11-06 12:09:17",
   603              "heartbeat_timestamp": "2019-01-09 10:28:50",
   604              "description": "My L3 agent for OpenStack",
   605              "resources_synced": true,
   606              "availability_zone": "nova",
   607              "alive": true,
   608              "configurations": {
   609                  "agent_mode": "legacy",
   610                  "ex_gw_ports": 2,
   611                  "floating_ips": 2,
   612                  "handle_internal_only_routers": true,
   613                  "interface_driver": "linuxbridge",
   614                  "interfaces": 1,
   615                  "log_agent_heartbeats": false,
   616                  "routers": 2
   617              },
   618              "resource_versions": {},
   619              "ha_state": "active"
   620          }
   621      ]
   622  }
   623  			`)
   624  	})
   625  
   626  	l3AgentsPages, err := routers.ListL3Agents(fake.ServiceClient(), "fa3a4aaa-c73f-48aa-a603-8c8bf642b7c0").AllPages(context.TODO())
   627  	th.AssertNoErr(t, err)
   628  	actual, err := routers.ExtractL3Agents(l3AgentsPages)
   629  	th.AssertNoErr(t, err)
   630  
   631  	expected := []routers.L3Agent{
   632  		{
   633  			ID:               "ddbf087c-e38f-4a73-bcb3-c38f2a719a03",
   634  			AdminStateUp:     true,
   635  			AgentType:        "L3 agent",
   636  			Description:      "My L3 agent for OpenStack",
   637  			Alive:            true,
   638  			ResourcesSynced:  true,
   639  			Binary:           "neutron-l3-agent",
   640  			AvailabilityZone: "nova",
   641  			Configurations: map[string]any{
   642  				"agent_mode":                   "legacy",
   643  				"ex_gw_ports":                  float64(2),
   644  				"floating_ips":                 float64(2),
   645  				"handle_internal_only_routers": true,
   646  				"interface_driver":             "linuxbridge",
   647  				"interfaces":                   float64(1),
   648  				"log_agent_heartbeats":         false,
   649  				"routers":                      float64(2),
   650  			},
   651  			CreatedAt:          time.Date(2017, 7, 26, 23, 15, 44, 0, time.UTC),
   652  			StartedAt:          time.Date(2018, 6, 26, 21, 46, 19, 0, time.UTC),
   653  			HeartbeatTimestamp: time.Date(2019, 1, 9, 10, 28, 53, 0, time.UTC),
   654  			Host:               "os-ctrl-02",
   655  			Topic:              "l3_agent",
   656  			HAState:            "standby",
   657  			ResourceVersions:   map[string]any{},
   658  		},
   659  		{
   660  			ID:               "4541cc6c-87bc-4cee-bad2-36ca78836c91",
   661  			AdminStateUp:     true,
   662  			AgentType:        "L3 agent",
   663  			Description:      "My L3 agent for OpenStack",
   664  			Alive:            true,
   665  			ResourcesSynced:  true,
   666  			Binary:           "neutron-l3-agent",
   667  			AvailabilityZone: "nova",
   668  			Configurations: map[string]any{
   669  				"agent_mode":                   "legacy",
   670  				"ex_gw_ports":                  float64(2),
   671  				"floating_ips":                 float64(2),
   672  				"handle_internal_only_routers": true,
   673  				"interface_driver":             "linuxbridge",
   674  				"interfaces":                   float64(1),
   675  				"log_agent_heartbeats":         false,
   676  				"routers":                      float64(2),
   677  			},
   678  			CreatedAt:          time.Date(2017, 1, 22, 14, 00, 50, 0, time.UTC),
   679  			StartedAt:          time.Date(2018, 11, 6, 12, 9, 17, 0, time.UTC),
   680  			HeartbeatTimestamp: time.Date(2019, 1, 9, 10, 28, 50, 0, time.UTC),
   681  			Host:               "os-ctrl-03",
   682  			Topic:              "l3_agent",
   683  			HAState:            "active",
   684  			ResourceVersions:   map[string]any{},
   685  		},
   686  	}
   687  	th.CheckDeepEquals(t, expected, actual)
   688  }