github.com/gophercloud/gophercloud@v1.11.0/openstack/networking/v2/extensions/layer3/routers/testing/requests_test.go (about)

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