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

     1  package testing
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/url"
     7  	"testing"
     8  	"time"
     9  
    10  	fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
    11  	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/extradhcpopts"
    12  	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/portsecurity"
    13  	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
    14  	"github.com/gophercloud/gophercloud/pagination"
    15  	th "github.com/gophercloud/gophercloud/testhelper"
    16  )
    17  
    18  func TestList(t *testing.T) {
    19  	th.SetupHTTP()
    20  	defer th.TeardownHTTP()
    21  
    22  	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
    23  		th.TestMethod(t, r, "GET")
    24  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
    25  
    26  		w.Header().Add("Content-Type", "application/json")
    27  		w.WriteHeader(http.StatusOK)
    28  
    29  		fmt.Fprintf(w, ListResponse)
    30  	})
    31  
    32  	count := 0
    33  
    34  	err := ports.List(fake.ServiceClient(), ports.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
    35  		count++
    36  		actual, err := ports.ExtractPorts(page)
    37  		if err != nil {
    38  			t.Errorf("Failed to extract subnets: %v", err)
    39  			return false, nil
    40  		}
    41  
    42  		expected := []ports.Port{
    43  			{
    44  				Status:       "ACTIVE",
    45  				Name:         "",
    46  				AdminStateUp: true,
    47  				NetworkID:    "70c1db1f-b701-45bd-96e0-a313ee3430b3",
    48  				TenantID:     "",
    49  				DeviceOwner:  "network:router_gateway",
    50  				MACAddress:   "fa:16:3e:58:42:ed",
    51  				FixedIPs: []ports.IP{
    52  					{
    53  						SubnetID:  "008ba151-0b8c-4a67-98b5-0d2b87666062",
    54  						IPAddress: "172.24.4.2",
    55  					},
    56  				},
    57  				ID:             "d80b1a3b-4fc1-49f3-952e-1e2ab7081d8b",
    58  				SecurityGroups: []string{},
    59  				DeviceID:       "9ae135f4-b6e0-4dad-9e91-3c223e385824",
    60  				CreatedAt:      time.Date(2019, time.June, 30, 4, 15, 37, 0, time.UTC),
    61  				UpdatedAt:      time.Date(2019, time.June, 30, 5, 18, 49, 0, time.UTC),
    62  			},
    63  		}
    64  
    65  		th.CheckDeepEquals(t, expected, actual)
    66  
    67  		return true, nil
    68  	})
    69  
    70  	th.AssertNoErr(t, err)
    71  
    72  	if count != 1 {
    73  		t.Errorf("Expected 1 page, got %d", count)
    74  	}
    75  }
    76  
    77  func TestListWithExtensions(t *testing.T) {
    78  	th.SetupHTTP()
    79  	defer th.TeardownHTTP()
    80  
    81  	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
    82  		th.TestMethod(t, r, "GET")
    83  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
    84  
    85  		w.Header().Add("Content-Type", "application/json")
    86  		w.WriteHeader(http.StatusOK)
    87  
    88  		fmt.Fprintf(w, ListResponse)
    89  	})
    90  
    91  	type portWithExt struct {
    92  		ports.Port
    93  		portsecurity.PortSecurityExt
    94  	}
    95  
    96  	var allPorts []portWithExt
    97  
    98  	allPages, err := ports.List(fake.ServiceClient(), ports.ListOpts{}).AllPages()
    99  	th.AssertNoErr(t, err)
   100  
   101  	err = ports.ExtractPortsInto(allPages, &allPorts)
   102  	th.AssertNoErr(t, err)
   103  
   104  	th.AssertEquals(t, allPorts[0].Status, "ACTIVE")
   105  	th.AssertEquals(t, allPorts[0].PortSecurityEnabled, false)
   106  }
   107  
   108  func TestGet(t *testing.T) {
   109  	th.SetupHTTP()
   110  	defer th.TeardownHTTP()
   111  
   112  	th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) {
   113  		th.TestMethod(t, r, "GET")
   114  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   115  
   116  		w.Header().Add("Content-Type", "application/json")
   117  		w.WriteHeader(http.StatusOK)
   118  
   119  		fmt.Fprintf(w, GetResponse)
   120  	})
   121  
   122  	n, err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").Extract()
   123  	th.AssertNoErr(t, err)
   124  
   125  	th.AssertEquals(t, n.Status, "ACTIVE")
   126  	th.AssertEquals(t, n.Name, "")
   127  	th.AssertEquals(t, n.AdminStateUp, true)
   128  	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
   129  	th.AssertEquals(t, n.TenantID, "7e02058126cc4950b75f9970368ba177")
   130  	th.AssertEquals(t, n.DeviceOwner, "network:router_interface")
   131  	th.AssertEquals(t, n.MACAddress, "fa:16:3e:23:fd:d7")
   132  	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
   133  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.1"},
   134  	})
   135  	th.AssertEquals(t, n.ID, "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2")
   136  	th.AssertDeepEquals(t, n.SecurityGroups, []string{})
   137  	th.AssertEquals(t, n.Status, "ACTIVE")
   138  	th.AssertEquals(t, n.DeviceID, "5e3898d7-11be-483e-9732-b2f5eccd2b2e")
   139  	th.AssertEquals(t, n.CreatedAt, time.Date(2019, time.June, 30, 4, 15, 37, 0, time.UTC))
   140  	th.AssertEquals(t, n.UpdatedAt, time.Date(2019, time.June, 30, 5, 18, 49, 0, time.UTC))
   141  }
   142  
   143  func TestGetWithExtensions(t *testing.T) {
   144  	th.SetupHTTP()
   145  	defer th.TeardownHTTP()
   146  
   147  	th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) {
   148  		th.TestMethod(t, r, "GET")
   149  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   150  
   151  		w.Header().Add("Content-Type", "application/json")
   152  		w.WriteHeader(http.StatusOK)
   153  
   154  		fmt.Fprintf(w, GetResponse)
   155  	})
   156  
   157  	var portWithExtensions struct {
   158  		ports.Port
   159  		portsecurity.PortSecurityExt
   160  	}
   161  
   162  	err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&portWithExtensions)
   163  	th.AssertNoErr(t, err)
   164  
   165  	th.AssertEquals(t, portWithExtensions.Status, "ACTIVE")
   166  	th.AssertEquals(t, portWithExtensions.PortSecurityEnabled, false)
   167  }
   168  
   169  func TestCreate(t *testing.T) {
   170  	th.SetupHTTP()
   171  	defer th.TeardownHTTP()
   172  
   173  	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
   174  		th.TestMethod(t, r, "POST")
   175  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   176  		th.TestHeader(t, r, "Content-Type", "application/json")
   177  		th.TestHeader(t, r, "Accept", "application/json")
   178  		th.TestJSONRequest(t, r, CreateRequest)
   179  
   180  		w.Header().Add("Content-Type", "application/json")
   181  		w.WriteHeader(http.StatusCreated)
   182  
   183  		fmt.Fprintf(w, CreateResponse)
   184  	})
   185  
   186  	asu := true
   187  	options := ports.CreateOpts{
   188  		Name:         "private-port",
   189  		AdminStateUp: &asu,
   190  		NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
   191  		FixedIPs: []ports.IP{
   192  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   193  		},
   194  		SecurityGroups: &[]string{"foo"},
   195  		AllowedAddressPairs: []ports.AddressPair{
   196  			{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   197  		},
   198  	}
   199  	n, err := ports.Create(fake.ServiceClient(), options).Extract()
   200  	th.AssertNoErr(t, err)
   201  
   202  	th.AssertEquals(t, n.Status, "DOWN")
   203  	th.AssertEquals(t, n.Name, "private-port")
   204  	th.AssertEquals(t, n.AdminStateUp, true)
   205  	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
   206  	th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
   207  	th.AssertEquals(t, n.DeviceOwner, "")
   208  	th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0")
   209  	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
   210  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   211  	})
   212  	th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
   213  	th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
   214  	th.AssertDeepEquals(t, n.AllowedAddressPairs, []ports.AddressPair{
   215  		{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   216  	})
   217  }
   218  
   219  func TestCreateOmitSecurityGroups(t *testing.T) {
   220  	th.SetupHTTP()
   221  	defer th.TeardownHTTP()
   222  
   223  	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
   224  		th.TestMethod(t, r, "POST")
   225  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   226  		th.TestHeader(t, r, "Content-Type", "application/json")
   227  		th.TestHeader(t, r, "Accept", "application/json")
   228  		th.TestJSONRequest(t, r, CreateOmitSecurityGroupsRequest)
   229  
   230  		w.Header().Add("Content-Type", "application/json")
   231  		w.WriteHeader(http.StatusCreated)
   232  
   233  		fmt.Fprintf(w, CreateOmitSecurityGroupsResponse)
   234  	})
   235  
   236  	asu := true
   237  	options := ports.CreateOpts{
   238  		Name:         "private-port",
   239  		AdminStateUp: &asu,
   240  		NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
   241  		FixedIPs: []ports.IP{
   242  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   243  		},
   244  		AllowedAddressPairs: []ports.AddressPair{
   245  			{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   246  		},
   247  	}
   248  	n, err := ports.Create(fake.ServiceClient(), options).Extract()
   249  	th.AssertNoErr(t, err)
   250  
   251  	th.AssertEquals(t, n.Status, "DOWN")
   252  	th.AssertEquals(t, n.Name, "private-port")
   253  	th.AssertEquals(t, n.AdminStateUp, true)
   254  	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
   255  	th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
   256  	th.AssertEquals(t, n.DeviceOwner, "")
   257  	th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0")
   258  	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
   259  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   260  	})
   261  	th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
   262  	th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
   263  	th.AssertDeepEquals(t, n.AllowedAddressPairs, []ports.AddressPair{
   264  		{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   265  	})
   266  }
   267  
   268  func TestCreateWithNoSecurityGroup(t *testing.T) {
   269  	th.SetupHTTP()
   270  	defer th.TeardownHTTP()
   271  
   272  	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
   273  		th.TestMethod(t, r, "POST")
   274  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   275  		th.TestHeader(t, r, "Content-Type", "application/json")
   276  		th.TestHeader(t, r, "Accept", "application/json")
   277  		th.TestJSONRequest(t, r, CreateWithNoSecurityGroupsRequest)
   278  
   279  		w.Header().Add("Content-Type", "application/json")
   280  		w.WriteHeader(http.StatusCreated)
   281  
   282  		fmt.Fprintf(w, CreateWithNoSecurityGroupsResponse)
   283  	})
   284  
   285  	asu := true
   286  	options := ports.CreateOpts{
   287  		Name:         "private-port",
   288  		AdminStateUp: &asu,
   289  		NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
   290  		FixedIPs: []ports.IP{
   291  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   292  		},
   293  		SecurityGroups: &[]string{},
   294  		AllowedAddressPairs: []ports.AddressPair{
   295  			{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   296  		},
   297  	}
   298  	n, err := ports.Create(fake.ServiceClient(), options).Extract()
   299  	th.AssertNoErr(t, err)
   300  
   301  	th.AssertEquals(t, n.Status, "DOWN")
   302  	th.AssertEquals(t, n.Name, "private-port")
   303  	th.AssertEquals(t, n.AdminStateUp, true)
   304  	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
   305  	th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
   306  	th.AssertEquals(t, n.DeviceOwner, "")
   307  	th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0")
   308  	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
   309  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   310  	})
   311  	th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
   312  	th.AssertDeepEquals(t, n.AllowedAddressPairs, []ports.AddressPair{
   313  		{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   314  	})
   315  }
   316  
   317  func TestCreateWithPropagateUplinkStatus(t *testing.T) {
   318  	th.SetupHTTP()
   319  	defer th.TeardownHTTP()
   320  
   321  	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
   322  		th.TestMethod(t, r, "POST")
   323  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   324  		th.TestHeader(t, r, "Content-Type", "application/json")
   325  		th.TestHeader(t, r, "Accept", "application/json")
   326  		th.TestJSONRequest(t, r, CreatePropagateUplinkStatusRequest)
   327  
   328  		w.Header().Add("Content-Type", "application/json")
   329  		w.WriteHeader(http.StatusCreated)
   330  
   331  		fmt.Fprintf(w, CreatePropagateUplinkStatusResponse)
   332  	})
   333  
   334  	asu := true
   335  	propagateUplinkStatus := true
   336  	options := ports.CreateOpts{
   337  		Name:         "private-port",
   338  		AdminStateUp: &asu,
   339  		NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
   340  		FixedIPs: []ports.IP{
   341  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   342  		},
   343  		PropagateUplinkStatus: &propagateUplinkStatus,
   344  	}
   345  	n, err := ports.Create(fake.ServiceClient(), options).Extract()
   346  	th.AssertNoErr(t, err)
   347  
   348  	th.AssertEquals(t, n.Status, "DOWN")
   349  	th.AssertEquals(t, n.Name, "private-port")
   350  	th.AssertEquals(t, n.AdminStateUp, true)
   351  	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
   352  	th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
   353  	th.AssertEquals(t, n.DeviceOwner, "")
   354  	th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0")
   355  	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
   356  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   357  	})
   358  	th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
   359  	th.AssertEquals(t, n.PropagateUplinkStatus, propagateUplinkStatus)
   360  }
   361  
   362  func TestCreateWithValueSpecs(t *testing.T) {
   363  	th.SetupHTTP()
   364  	defer th.TeardownHTTP()
   365  
   366  	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
   367  		th.TestMethod(t, r, "POST")
   368  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   369  		th.TestHeader(t, r, "Content-Type", "application/json")
   370  		th.TestHeader(t, r, "Accept", "application/json")
   371  		th.TestJSONRequest(t, r, CreateValueSpecRequest)
   372  
   373  		w.Header().Add("Content-Type", "application/json")
   374  		w.WriteHeader(http.StatusCreated)
   375  
   376  		fmt.Fprintf(w, CreateValueSpecResponse)
   377  	})
   378  
   379  	asu := true
   380  	options := ports.CreateOpts{
   381  		Name:         "private-port",
   382  		AdminStateUp: &asu,
   383  		NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
   384  		FixedIPs: []ports.IP{
   385  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   386  		},
   387  		SecurityGroups: &[]string{"foo"},
   388  		AllowedAddressPairs: []ports.AddressPair{
   389  			{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   390  		},
   391  		ValueSpecs: &map[string]string{
   392  			"key": "value",
   393  		},
   394  	}
   395  	n, err := ports.Create(fake.ServiceClient(), options).Extract()
   396  	th.AssertNoErr(t, err)
   397  
   398  	th.AssertEquals(t, n.Status, "DOWN")
   399  	th.AssertEquals(t, n.Name, "private-port")
   400  	th.AssertEquals(t, n.AdminStateUp, true)
   401  	th.AssertEquals(t, n.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
   402  	th.AssertEquals(t, n.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
   403  	th.AssertEquals(t, n.DeviceOwner, "")
   404  	th.AssertEquals(t, n.MACAddress, "fa:16:3e:c9:cb:f0")
   405  	th.AssertDeepEquals(t, n.FixedIPs, []ports.IP{
   406  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   407  	})
   408  	th.AssertEquals(t, n.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
   409  	th.AssertDeepEquals(t, n.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
   410  	th.AssertDeepEquals(t, n.AllowedAddressPairs, []ports.AddressPair{
   411  		{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   412  	})
   413  	th.AssertDeepEquals(t, n.ValueSpecs, map[string]string{"key": "value"})
   414  }
   415  
   416  func TestRequiredCreateOpts(t *testing.T) {
   417  	res := ports.Create(fake.ServiceClient(), ports.CreateOpts{})
   418  	if res.Err == nil {
   419  		t.Fatalf("Expected error, got none")
   420  	}
   421  }
   422  
   423  func TestCreatePortSecurity(t *testing.T) {
   424  	th.SetupHTTP()
   425  	defer th.TeardownHTTP()
   426  
   427  	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
   428  		th.TestMethod(t, r, "POST")
   429  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   430  		th.TestHeader(t, r, "Content-Type", "application/json")
   431  		th.TestHeader(t, r, "Accept", "application/json")
   432  		th.TestJSONRequest(t, r, CreatePortSecurityRequest)
   433  
   434  		w.Header().Add("Content-Type", "application/json")
   435  		w.WriteHeader(http.StatusCreated)
   436  
   437  		fmt.Fprintf(w, CreatePortSecurityResponse)
   438  	})
   439  
   440  	var portWithExt struct {
   441  		ports.Port
   442  		portsecurity.PortSecurityExt
   443  	}
   444  
   445  	asu := true
   446  	iFalse := false
   447  	portCreateOpts := ports.CreateOpts{
   448  		Name:         "private-port",
   449  		AdminStateUp: &asu,
   450  		NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
   451  		FixedIPs: []ports.IP{
   452  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   453  		},
   454  		SecurityGroups: &[]string{"foo"},
   455  		AllowedAddressPairs: []ports.AddressPair{
   456  			{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   457  		},
   458  	}
   459  	createOpts := portsecurity.PortCreateOptsExt{
   460  		CreateOptsBuilder:   portCreateOpts,
   461  		PortSecurityEnabled: &iFalse,
   462  	}
   463  
   464  	err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&portWithExt)
   465  	th.AssertNoErr(t, err)
   466  
   467  	th.AssertEquals(t, portWithExt.Status, "DOWN")
   468  	th.AssertEquals(t, portWithExt.PortSecurityEnabled, false)
   469  }
   470  
   471  func TestUpdate(t *testing.T) {
   472  	th.SetupHTTP()
   473  	defer th.TeardownHTTP()
   474  
   475  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
   476  		th.TestMethod(t, r, "PUT")
   477  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   478  		th.TestHeader(t, r, "Content-Type", "application/json")
   479  		th.TestHeader(t, r, "Accept", "application/json")
   480  		th.TestJSONRequest(t, r, UpdateRequest)
   481  
   482  		w.Header().Add("Content-Type", "application/json")
   483  		w.WriteHeader(http.StatusOK)
   484  
   485  		fmt.Fprintf(w, UpdateResponse)
   486  	})
   487  
   488  	name := "new_port_name"
   489  	options := ports.UpdateOpts{
   490  		Name: &name,
   491  		FixedIPs: []ports.IP{
   492  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   493  		},
   494  		SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"},
   495  		AllowedAddressPairs: &[]ports.AddressPair{
   496  			{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   497  		},
   498  	}
   499  
   500  	s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
   501  	th.AssertNoErr(t, err)
   502  
   503  	th.AssertEquals(t, s.Name, "new_port_name")
   504  	th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
   505  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   506  	})
   507  	th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{
   508  		{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   509  	})
   510  	th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
   511  }
   512  
   513  func TestUpdateOmitSecurityGroups(t *testing.T) {
   514  	th.SetupHTTP()
   515  	defer th.TeardownHTTP()
   516  
   517  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
   518  		th.TestMethod(t, r, "PUT")
   519  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   520  		th.TestHeader(t, r, "Content-Type", "application/json")
   521  		th.TestHeader(t, r, "Accept", "application/json")
   522  		th.TestJSONRequest(t, r, UpdateOmitSecurityGroupsRequest)
   523  
   524  		w.Header().Add("Content-Type", "application/json")
   525  		w.WriteHeader(http.StatusOK)
   526  
   527  		fmt.Fprintf(w, UpdateOmitSecurityGroupsResponse)
   528  	})
   529  
   530  	name := "new_port_name"
   531  	options := ports.UpdateOpts{
   532  		Name: &name,
   533  		FixedIPs: []ports.IP{
   534  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   535  		},
   536  		AllowedAddressPairs: &[]ports.AddressPair{
   537  			{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   538  		},
   539  	}
   540  
   541  	s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
   542  	th.AssertNoErr(t, err)
   543  
   544  	th.AssertEquals(t, s.Name, "new_port_name")
   545  	th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
   546  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   547  	})
   548  	th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{
   549  		{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   550  	})
   551  	th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
   552  }
   553  
   554  func TestUpdatePropagateUplinkStatus(t *testing.T) {
   555  	th.SetupHTTP()
   556  	defer th.TeardownHTTP()
   557  
   558  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
   559  		th.TestMethod(t, r, "PUT")
   560  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   561  		th.TestHeader(t, r, "Content-Type", "application/json")
   562  		th.TestHeader(t, r, "Accept", "application/json")
   563  		th.TestJSONRequest(t, r, UpdatePropagateUplinkStatusRequest)
   564  
   565  		w.Header().Add("Content-Type", "application/json")
   566  		w.WriteHeader(http.StatusOK)
   567  
   568  		fmt.Fprintf(w, UpdatePropagateUplinkStatusResponse)
   569  	})
   570  
   571  	propagateUplinkStatus := true
   572  	options := ports.UpdateOpts{
   573  		PropagateUplinkStatus: &propagateUplinkStatus,
   574  	}
   575  
   576  	s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
   577  	th.AssertNoErr(t, err)
   578  
   579  	th.AssertDeepEquals(t, s.PropagateUplinkStatus, propagateUplinkStatus)
   580  }
   581  
   582  func TestUpdateValueSpecs(t *testing.T) {
   583  	th.SetupHTTP()
   584  	defer th.TeardownHTTP()
   585  
   586  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
   587  		th.TestMethod(t, r, "PUT")
   588  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   589  		th.TestHeader(t, r, "Content-Type", "application/json")
   590  		th.TestHeader(t, r, "Accept", "application/json")
   591  		th.TestJSONRequest(t, r, UpdateValueSpecsRequest)
   592  
   593  		w.Header().Add("Content-Type", "application/json")
   594  		w.WriteHeader(http.StatusOK)
   595  
   596  		fmt.Fprintf(w, UpdateValueSpecsResponse)
   597  	})
   598  
   599  	options := ports.UpdateOpts{
   600  		ValueSpecs: &map[string]string{
   601  			"key": "value",
   602  		},
   603  	}
   604  
   605  	s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
   606  	th.AssertNoErr(t, err)
   607  
   608  	th.AssertDeepEquals(t, s.ValueSpecs, map[string]string{"key": "value"})
   609  }
   610  
   611  func TestUpdatePortSecurity(t *testing.T) {
   612  	th.SetupHTTP()
   613  	defer th.TeardownHTTP()
   614  
   615  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
   616  		th.TestMethod(t, r, "PUT")
   617  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   618  		th.TestHeader(t, r, "Content-Type", "application/json")
   619  		th.TestHeader(t, r, "Accept", "application/json")
   620  		th.TestJSONRequest(t, r, UpdatePortSecurityRequest)
   621  
   622  		w.Header().Add("Content-Type", "application/json")
   623  		w.WriteHeader(http.StatusOK)
   624  
   625  		fmt.Fprintf(w, UpdatePortSecurityResponse)
   626  	})
   627  
   628  	var portWithExt struct {
   629  		ports.Port
   630  		portsecurity.PortSecurityExt
   631  	}
   632  
   633  	iFalse := false
   634  	portUpdateOpts := ports.UpdateOpts{}
   635  	updateOpts := portsecurity.PortUpdateOptsExt{
   636  		UpdateOptsBuilder:   portUpdateOpts,
   637  		PortSecurityEnabled: &iFalse,
   638  	}
   639  
   640  	err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&portWithExt)
   641  	th.AssertNoErr(t, err)
   642  
   643  	th.AssertEquals(t, portWithExt.Status, "DOWN")
   644  	th.AssertEquals(t, portWithExt.Name, "private-port")
   645  	th.AssertEquals(t, portWithExt.PortSecurityEnabled, false)
   646  }
   647  
   648  func TestUpdateRevision(t *testing.T) {
   649  	th.SetupHTTP()
   650  	defer th.TeardownHTTP()
   651  
   652  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
   653  		th.TestMethod(t, r, "PUT")
   654  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   655  		th.TestHeader(t, r, "Content-Type", "application/json")
   656  		th.TestHeader(t, r, "Accept", "application/json")
   657  		th.TestHeaderUnset(t, r, "If-Match")
   658  		th.TestJSONRequest(t, r, UpdateRequest)
   659  
   660  		w.Header().Add("Content-Type", "application/json")
   661  		w.WriteHeader(http.StatusOK)
   662  
   663  		fmt.Fprintf(w, UpdateResponse)
   664  	})
   665  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0e", func(w http.ResponseWriter, r *http.Request) {
   666  		th.TestMethod(t, r, "PUT")
   667  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   668  		th.TestHeader(t, r, "Content-Type", "application/json")
   669  		th.TestHeader(t, r, "Accept", "application/json")
   670  		th.TestHeader(t, r, "If-Match", "revision_number=42")
   671  		th.TestJSONRequest(t, r, UpdateRequest)
   672  
   673  		w.Header().Add("Content-Type", "application/json")
   674  		w.WriteHeader(http.StatusOK)
   675  
   676  		fmt.Fprintf(w, UpdateResponse)
   677  	})
   678  
   679  	name := "new_port_name"
   680  	options := ports.UpdateOpts{
   681  		Name: &name,
   682  		FixedIPs: []ports.IP{
   683  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   684  		},
   685  		SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"},
   686  		AllowedAddressPairs: &[]ports.AddressPair{
   687  			{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   688  		},
   689  	}
   690  	_, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
   691  	th.AssertNoErr(t, err)
   692  
   693  	revisionNumber := 42
   694  	options.RevisionNumber = &revisionNumber
   695  	_, err = ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0e", options).Extract()
   696  	th.AssertNoErr(t, err)
   697  }
   698  
   699  func TestRemoveSecurityGroups(t *testing.T) {
   700  	th.SetupHTTP()
   701  	defer th.TeardownHTTP()
   702  
   703  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
   704  		th.TestMethod(t, r, "PUT")
   705  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   706  		th.TestHeader(t, r, "Content-Type", "application/json")
   707  		th.TestHeader(t, r, "Accept", "application/json")
   708  		th.TestJSONRequest(t, r, RemoveSecurityGroupRequest)
   709  
   710  		w.Header().Add("Content-Type", "application/json")
   711  		w.WriteHeader(http.StatusOK)
   712  
   713  		fmt.Fprintf(w, RemoveSecurityGroupResponse)
   714  	})
   715  
   716  	name := "new_port_name"
   717  	options := ports.UpdateOpts{
   718  		Name: &name,
   719  		FixedIPs: []ports.IP{
   720  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   721  		},
   722  		SecurityGroups: &[]string{},
   723  		AllowedAddressPairs: &[]ports.AddressPair{
   724  			{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   725  		},
   726  	}
   727  
   728  	s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
   729  	th.AssertNoErr(t, err)
   730  
   731  	th.AssertEquals(t, s.Name, "new_port_name")
   732  	th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
   733  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   734  	})
   735  	th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{
   736  		{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   737  	})
   738  	th.AssertDeepEquals(t, s.SecurityGroups, []string(nil))
   739  }
   740  
   741  func TestRemoveAllowedAddressPairs(t *testing.T) {
   742  	th.SetupHTTP()
   743  	defer th.TeardownHTTP()
   744  
   745  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
   746  		th.TestMethod(t, r, "PUT")
   747  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   748  		th.TestHeader(t, r, "Content-Type", "application/json")
   749  		th.TestHeader(t, r, "Accept", "application/json")
   750  		th.TestJSONRequest(t, r, RemoveAllowedAddressPairsRequest)
   751  
   752  		w.Header().Add("Content-Type", "application/json")
   753  		w.WriteHeader(http.StatusOK)
   754  
   755  		fmt.Fprintf(w, RemoveAllowedAddressPairsResponse)
   756  	})
   757  
   758  	name := "new_port_name"
   759  	options := ports.UpdateOpts{
   760  		Name: &name,
   761  		FixedIPs: []ports.IP{
   762  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   763  		},
   764  		SecurityGroups:      &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"},
   765  		AllowedAddressPairs: &[]ports.AddressPair{},
   766  	}
   767  
   768  	s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
   769  	th.AssertNoErr(t, err)
   770  
   771  	th.AssertEquals(t, s.Name, "new_port_name")
   772  	th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
   773  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   774  	})
   775  	th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair(nil))
   776  	th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
   777  }
   778  
   779  func TestDontUpdateAllowedAddressPairs(t *testing.T) {
   780  	th.SetupHTTP()
   781  	defer th.TeardownHTTP()
   782  
   783  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
   784  		th.TestMethod(t, r, "PUT")
   785  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   786  		th.TestHeader(t, r, "Content-Type", "application/json")
   787  		th.TestHeader(t, r, "Accept", "application/json")
   788  		th.TestJSONRequest(t, r, DontUpdateAllowedAddressPairsRequest)
   789  
   790  		w.Header().Add("Content-Type", "application/json")
   791  		w.WriteHeader(http.StatusOK)
   792  
   793  		fmt.Fprintf(w, DontUpdateAllowedAddressPairsResponse)
   794  	})
   795  
   796  	name := "new_port_name"
   797  	options := ports.UpdateOpts{
   798  		Name: &name,
   799  		FixedIPs: []ports.IP{
   800  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   801  		},
   802  		SecurityGroups: &[]string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"},
   803  	}
   804  
   805  	s, err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", options).Extract()
   806  	th.AssertNoErr(t, err)
   807  
   808  	th.AssertEquals(t, s.Name, "new_port_name")
   809  	th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
   810  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   811  	})
   812  	th.AssertDeepEquals(t, s.AllowedAddressPairs, []ports.AddressPair{
   813  		{IPAddress: "10.0.0.4", MACAddress: "fa:16:3e:c9:cb:f0"},
   814  	})
   815  	th.AssertDeepEquals(t, s.SecurityGroups, []string{"f0ac4394-7e4a-4409-9701-ba8be283dbc3"})
   816  }
   817  
   818  func TestDelete(t *testing.T) {
   819  	th.SetupHTTP()
   820  	defer th.TeardownHTTP()
   821  
   822  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
   823  		th.TestMethod(t, r, "DELETE")
   824  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   825  		w.WriteHeader(http.StatusNoContent)
   826  	})
   827  
   828  	res := ports.Delete(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d")
   829  	th.AssertNoErr(t, res.Err)
   830  }
   831  
   832  func TestGetWithExtraDHCPOpts(t *testing.T) {
   833  	th.SetupHTTP()
   834  	defer th.TeardownHTTP()
   835  
   836  	th.Mux.HandleFunc("/v2.0/ports/46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2", func(w http.ResponseWriter, r *http.Request) {
   837  		th.TestMethod(t, r, "GET")
   838  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   839  
   840  		w.Header().Add("Content-Type", "application/json")
   841  		w.WriteHeader(http.StatusOK)
   842  
   843  		fmt.Fprintf(w, GetWithExtraDHCPOptsResponse)
   844  	})
   845  
   846  	var s struct {
   847  		ports.Port
   848  		extradhcpopts.ExtraDHCPOptsExt
   849  	}
   850  
   851  	err := ports.Get(fake.ServiceClient(), "46d4bfb9-b26e-41f3-bd2e-e6dcc1ccedb2").ExtractInto(&s)
   852  	th.AssertNoErr(t, err)
   853  
   854  	th.AssertEquals(t, s.Status, "ACTIVE")
   855  	th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
   856  	th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
   857  	th.AssertEquals(t, s.AdminStateUp, true)
   858  	th.AssertEquals(t, s.Name, "port-with-extra-dhcp-opts")
   859  	th.AssertEquals(t, s.DeviceOwner, "")
   860  	th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0")
   861  	th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
   862  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.4"},
   863  	})
   864  	th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
   865  	th.AssertEquals(t, s.DeviceID, "")
   866  
   867  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptName, "option1")
   868  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptValue, "value1")
   869  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].IPVersion, 4)
   870  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[1].OptName, "option2")
   871  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[1].OptValue, "value2")
   872  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[1].IPVersion, 4)
   873  }
   874  
   875  func TestCreateWithExtraDHCPOpts(t *testing.T) {
   876  	th.SetupHTTP()
   877  	defer th.TeardownHTTP()
   878  
   879  	th.Mux.HandleFunc("/v2.0/ports", func(w http.ResponseWriter, r *http.Request) {
   880  		th.TestMethod(t, r, "POST")
   881  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   882  		th.TestHeader(t, r, "Content-Type", "application/json")
   883  		th.TestHeader(t, r, "Accept", "application/json")
   884  		th.TestJSONRequest(t, r, CreateWithExtraDHCPOptsRequest)
   885  
   886  		w.Header().Add("Content-Type", "application/json")
   887  		w.WriteHeader(http.StatusCreated)
   888  
   889  		fmt.Fprintf(w, CreateWithExtraDHCPOptsResponse)
   890  	})
   891  
   892  	adminStateUp := true
   893  	portCreateOpts := ports.CreateOpts{
   894  		Name:         "port-with-extra-dhcp-opts",
   895  		AdminStateUp: &adminStateUp,
   896  		NetworkID:    "a87cc70a-3e15-4acf-8205-9b711a3531b7",
   897  		FixedIPs: []ports.IP{
   898  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   899  		},
   900  	}
   901  
   902  	createOpts := extradhcpopts.CreateOptsExt{
   903  		CreateOptsBuilder: portCreateOpts,
   904  		ExtraDHCPOpts: []extradhcpopts.CreateExtraDHCPOpt{
   905  			{
   906  				OptName:  "option1",
   907  				OptValue: "value1",
   908  			},
   909  		},
   910  	}
   911  
   912  	var s struct {
   913  		ports.Port
   914  		extradhcpopts.ExtraDHCPOptsExt
   915  	}
   916  
   917  	err := ports.Create(fake.ServiceClient(), createOpts).ExtractInto(&s)
   918  	th.AssertNoErr(t, err)
   919  
   920  	th.AssertEquals(t, s.Status, "DOWN")
   921  	th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
   922  	th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
   923  	th.AssertEquals(t, s.AdminStateUp, true)
   924  	th.AssertEquals(t, s.Name, "port-with-extra-dhcp-opts")
   925  	th.AssertEquals(t, s.DeviceOwner, "")
   926  	th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0")
   927  	th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
   928  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.2"},
   929  	})
   930  	th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
   931  	th.AssertEquals(t, s.DeviceID, "")
   932  
   933  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptName, "option1")
   934  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptValue, "value1")
   935  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].IPVersion, 4)
   936  }
   937  
   938  func TestUpdateWithExtraDHCPOpts(t *testing.T) {
   939  	th.SetupHTTP()
   940  	defer th.TeardownHTTP()
   941  
   942  	th.Mux.HandleFunc("/v2.0/ports/65c0ee9f-d634-4522-8954-51021b570b0d", func(w http.ResponseWriter, r *http.Request) {
   943  		th.TestMethod(t, r, "PUT")
   944  		th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
   945  		th.TestHeader(t, r, "Content-Type", "application/json")
   946  		th.TestHeader(t, r, "Accept", "application/json")
   947  		th.TestJSONRequest(t, r, UpdateWithExtraDHCPOptsRequest)
   948  
   949  		w.Header().Add("Content-Type", "application/json")
   950  		w.WriteHeader(http.StatusOK)
   951  
   952  		fmt.Fprintf(w, UpdateWithExtraDHCPOptsResponse)
   953  	})
   954  
   955  	name := "updated-port-with-dhcp-opts"
   956  	portUpdateOpts := ports.UpdateOpts{
   957  		Name: &name,
   958  		FixedIPs: []ports.IP{
   959  			{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   960  		},
   961  	}
   962  
   963  	edoValue2 := "value2"
   964  	updateOpts := extradhcpopts.UpdateOptsExt{
   965  		UpdateOptsBuilder: portUpdateOpts,
   966  		ExtraDHCPOpts: []extradhcpopts.UpdateExtraDHCPOpt{
   967  			{
   968  				OptName: "option1",
   969  			},
   970  			{
   971  				OptName:  "option2",
   972  				OptValue: &edoValue2,
   973  			},
   974  		},
   975  	}
   976  
   977  	var s struct {
   978  		ports.Port
   979  		extradhcpopts.ExtraDHCPOptsExt
   980  	}
   981  
   982  	err := ports.Update(fake.ServiceClient(), "65c0ee9f-d634-4522-8954-51021b570b0d", updateOpts).ExtractInto(&s)
   983  	th.AssertNoErr(t, err)
   984  
   985  	th.AssertEquals(t, s.Status, "DOWN")
   986  	th.AssertEquals(t, s.NetworkID, "a87cc70a-3e15-4acf-8205-9b711a3531b7")
   987  	th.AssertEquals(t, s.TenantID, "d6700c0c9ffa4f1cb322cd4a1f3906fa")
   988  	th.AssertEquals(t, s.AdminStateUp, true)
   989  	th.AssertEquals(t, s.Name, "updated-port-with-dhcp-opts")
   990  	th.AssertEquals(t, s.DeviceOwner, "")
   991  	th.AssertEquals(t, s.MACAddress, "fa:16:3e:c9:cb:f0")
   992  	th.AssertDeepEquals(t, s.FixedIPs, []ports.IP{
   993  		{SubnetID: "a0304c3a-4f08-4c43-88af-d796509c97d2", IPAddress: "10.0.0.3"},
   994  	})
   995  	th.AssertEquals(t, s.ID, "65c0ee9f-d634-4522-8954-51021b570b0d")
   996  	th.AssertEquals(t, s.DeviceID, "")
   997  
   998  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptName, "option2")
   999  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].OptValue, "value2")
  1000  	th.AssertDeepEquals(t, s.ExtraDHCPOpts[0].IPVersion, 4)
  1001  }
  1002  
  1003  func TestPortsListOpts(t *testing.T) {
  1004  	for expected, opts := range map[string]ports.ListOpts{
  1005  		newValue("fixed_ips", `ip_address=1.2.3.4,subnet_id=42`): {
  1006  			FixedIPs: []ports.FixedIPOpts{{IPAddress: "1.2.3.4", SubnetID: "42"}},
  1007  		},
  1008  	} {
  1009  		actual, _ := opts.ToPortListQuery()
  1010  		th.AssertEquals(t, expected, actual)
  1011  	}
  1012  }
  1013  func newValue(param, value string) string {
  1014  	v := url.Values{}
  1015  	v.Add(param, value)
  1016  	return "?" + v.Encode()
  1017  }