github.com/vnpaycloud-console/gophercloud/v2@v2.0.5/internal/acceptance/openstack/networking/v2/networking.go (about)

     1  package v2
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/vnpaycloud-console/gophercloud/v2"
     9  	"github.com/vnpaycloud-console/gophercloud/v2/internal/acceptance/tools"
    10  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/networking/v2/extensions/extradhcpopts"
    11  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/networking/v2/extensions/portsecurity"
    12  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/networking/v2/networks"
    13  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/networking/v2/ports"
    14  	"github.com/vnpaycloud-console/gophercloud/v2/openstack/networking/v2/subnets"
    15  	th "github.com/vnpaycloud-console/gophercloud/v2/testhelper"
    16  )
    17  
    18  // PortWithExtraDHCPOpts represents a port with extra DHCP options configuration.
    19  type PortWithExtraDHCPOpts struct {
    20  	ports.Port
    21  	extradhcpopts.ExtraDHCPOptsExt
    22  }
    23  
    24  // CreateNetwork will create basic network. An error will be returned if the
    25  // network could not be created.
    26  func CreateNetwork(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) {
    27  	networkName := tools.RandomString("TESTACC-", 8)
    28  	networkDescription := tools.RandomString("TESTACC-DESC-", 8)
    29  	createOpts := networks.CreateOpts{
    30  		Name:         networkName,
    31  		Description:  networkDescription,
    32  		AdminStateUp: gophercloud.Enabled,
    33  	}
    34  
    35  	t.Logf("Attempting to create network: %s", networkName)
    36  
    37  	network, err := networks.Create(context.TODO(), client, createOpts).Extract()
    38  	if err != nil {
    39  		return network, err
    40  	}
    41  
    42  	t.Logf("Successfully created network.")
    43  
    44  	th.AssertEquals(t, network.Name, networkName)
    45  	th.AssertEquals(t, network.Description, networkDescription)
    46  
    47  	return network, nil
    48  }
    49  
    50  // CreateNetworkWithoutPortSecurity will create a network without port security.
    51  // An error will be returned if the network could not be created.
    52  func CreateNetworkWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceClient) (*networks.Network, error) {
    53  	networkName := tools.RandomString("TESTACC-", 8)
    54  	networkCreateOpts := networks.CreateOpts{
    55  		Name:         networkName,
    56  		AdminStateUp: gophercloud.Enabled,
    57  	}
    58  
    59  	iFalse := false
    60  	createOpts := portsecurity.NetworkCreateOptsExt{
    61  		CreateOptsBuilder:   networkCreateOpts,
    62  		PortSecurityEnabled: &iFalse,
    63  	}
    64  
    65  	t.Logf("Attempting to create network: %s", networkName)
    66  
    67  	network, err := networks.Create(context.TODO(), client, createOpts).Extract()
    68  	if err != nil {
    69  		return network, err
    70  	}
    71  
    72  	t.Logf("Successfully created network.")
    73  
    74  	th.AssertEquals(t, network.Name, networkName)
    75  
    76  	return network, nil
    77  }
    78  
    79  // CreatePort will create a port on the specified subnet. An error will be
    80  // returned if the port could not be created.
    81  func CreatePort(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) {
    82  	portName := tools.RandomString("TESTACC-", 8)
    83  	portDescription := tools.RandomString("TESTACC-DESC-", 8)
    84  
    85  	t.Logf("Attempting to create port: %s", portName)
    86  
    87  	createOpts := ports.CreateOpts{
    88  		NetworkID:    networkID,
    89  		Name:         portName,
    90  		Description:  portDescription,
    91  		AdminStateUp: gophercloud.Enabled,
    92  		FixedIPs:     []ports.IP{{SubnetID: subnetID}},
    93  	}
    94  
    95  	port, err := ports.Create(context.TODO(), client, createOpts).Extract()
    96  	if err != nil {
    97  		return port, err
    98  	}
    99  
   100  	if err := WaitForPortToCreate(client, port.ID); err != nil {
   101  		return port, err
   102  	}
   103  
   104  	newPort, err := ports.Get(context.TODO(), client, port.ID).Extract()
   105  	if err != nil {
   106  		return newPort, err
   107  	}
   108  
   109  	t.Logf("Successfully created port: %s", portName)
   110  
   111  	th.AssertEquals(t, port.Name, portName)
   112  	th.AssertEquals(t, port.Description, portDescription)
   113  
   114  	return newPort, nil
   115  }
   116  
   117  // CreatePortWithNoSecurityGroup will create a port with no security group
   118  // attached. An error will be returned if the port could not be created.
   119  func CreatePortWithNoSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) {
   120  	portName := tools.RandomString("TESTACC-", 8)
   121  	iFalse := false
   122  
   123  	t.Logf("Attempting to create port: %s", portName)
   124  
   125  	createOpts := ports.CreateOpts{
   126  		NetworkID:      networkID,
   127  		Name:           portName,
   128  		AdminStateUp:   &iFalse,
   129  		FixedIPs:       []ports.IP{{SubnetID: subnetID}},
   130  		SecurityGroups: &[]string{},
   131  	}
   132  
   133  	port, err := ports.Create(context.TODO(), client, createOpts).Extract()
   134  	if err != nil {
   135  		return port, err
   136  	}
   137  
   138  	if err := WaitForPortToCreate(client, port.ID); err != nil {
   139  		return port, err
   140  	}
   141  
   142  	newPort, err := ports.Get(context.TODO(), client, port.ID).Extract()
   143  	if err != nil {
   144  		return newPort, err
   145  	}
   146  
   147  	t.Logf("Successfully created port: %s", portName)
   148  
   149  	th.AssertEquals(t, port.Name, portName)
   150  
   151  	return newPort, nil
   152  }
   153  
   154  // CreatePortWithoutPortSecurity will create a port without port security on the
   155  // specified subnet. An error will be returned if the port could not be created.
   156  func CreatePortWithoutPortSecurity(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) {
   157  	portName := tools.RandomString("TESTACC-", 8)
   158  
   159  	t.Logf("Attempting to create port: %s", portName)
   160  
   161  	portCreateOpts := ports.CreateOpts{
   162  		NetworkID:    networkID,
   163  		Name:         portName,
   164  		AdminStateUp: gophercloud.Enabled,
   165  		FixedIPs:     []ports.IP{{SubnetID: subnetID}},
   166  	}
   167  
   168  	iFalse := false
   169  	createOpts := portsecurity.PortCreateOptsExt{
   170  		CreateOptsBuilder:   portCreateOpts,
   171  		PortSecurityEnabled: &iFalse,
   172  	}
   173  
   174  	port, err := ports.Create(context.TODO(), client, createOpts).Extract()
   175  	if err != nil {
   176  		return port, err
   177  	}
   178  
   179  	if err := WaitForPortToCreate(client, port.ID); err != nil {
   180  		return port, err
   181  	}
   182  
   183  	newPort, err := ports.Get(context.TODO(), client, port.ID).Extract()
   184  	if err != nil {
   185  		return newPort, err
   186  	}
   187  
   188  	t.Logf("Successfully created port: %s", portName)
   189  
   190  	th.AssertEquals(t, port.Name, portName)
   191  
   192  	return newPort, nil
   193  }
   194  
   195  // CreatePortWithExtraDHCPOpts will create a port with DHCP options on the
   196  // specified subnet. An error will be returned if the port could not be created.
   197  func CreatePortWithExtraDHCPOpts(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*PortWithExtraDHCPOpts, error) {
   198  	portName := tools.RandomString("TESTACC-", 8)
   199  
   200  	t.Logf("Attempting to create port: %s", portName)
   201  
   202  	portCreateOpts := ports.CreateOpts{
   203  		NetworkID:    networkID,
   204  		Name:         portName,
   205  		AdminStateUp: gophercloud.Enabled,
   206  		FixedIPs:     []ports.IP{{SubnetID: subnetID}},
   207  	}
   208  
   209  	createOpts := extradhcpopts.CreateOptsExt{
   210  		CreateOptsBuilder: portCreateOpts,
   211  		ExtraDHCPOpts: []extradhcpopts.CreateExtraDHCPOpt{
   212  			{
   213  				OptName:  "test_option_1",
   214  				OptValue: "test_value_1",
   215  			},
   216  		},
   217  	}
   218  	port := &PortWithExtraDHCPOpts{}
   219  
   220  	err := ports.Create(context.TODO(), client, createOpts).ExtractInto(port)
   221  	if err != nil {
   222  		return nil, err
   223  	}
   224  
   225  	if err := WaitForPortToCreate(client, port.ID); err != nil {
   226  		return nil, err
   227  	}
   228  
   229  	err = ports.Get(context.TODO(), client, port.ID).ExtractInto(port)
   230  	if err != nil {
   231  		return port, err
   232  	}
   233  
   234  	t.Logf("Successfully created port: %s", portName)
   235  
   236  	return port, nil
   237  }
   238  
   239  // CreatePortWithMultipleFixedIPs will create a port with two FixedIPs on the
   240  // specified subnet. An error will be returned if the port could not be created.
   241  func CreatePortWithMultipleFixedIPs(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetID string) (*ports.Port, error) {
   242  	portName := tools.RandomString("TESTACC-", 8)
   243  	portDescription := tools.RandomString("TESTACC-DESC-", 8)
   244  
   245  	t.Logf("Attempting to create port with two fixed IPs: %s", portName)
   246  
   247  	createOpts := ports.CreateOpts{
   248  		NetworkID:    networkID,
   249  		Name:         portName,
   250  		Description:  portDescription,
   251  		AdminStateUp: gophercloud.Enabled,
   252  		FixedIPs:     []ports.IP{{SubnetID: subnetID}, {SubnetID: subnetID}},
   253  	}
   254  
   255  	port, err := ports.Create(context.TODO(), client, createOpts).Extract()
   256  	if err != nil {
   257  		return port, err
   258  	}
   259  
   260  	if err := WaitForPortToCreate(client, port.ID); err != nil {
   261  		return port, err
   262  	}
   263  
   264  	newPort, err := ports.Get(context.TODO(), client, port.ID).Extract()
   265  	if err != nil {
   266  		return newPort, err
   267  	}
   268  
   269  	t.Logf("Successfully created port: %s", portName)
   270  
   271  	th.AssertEquals(t, port.Name, portName)
   272  	th.AssertEquals(t, port.Description, portDescription)
   273  
   274  	if len(port.FixedIPs) != 2 {
   275  		t.Fatalf("Failed to create a port with two fixed IPs: %s", portName)
   276  	}
   277  
   278  	return newPort, nil
   279  }
   280  
   281  // CreateSubnet will create a subnet on the specified Network ID. An error
   282  // will be returned if the subnet could not be created.
   283  func CreateSubnet(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) {
   284  	subnetOctet := tools.RandomInt(1, 250)
   285  	subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet)
   286  	subnetGateway := fmt.Sprintf("192.168.%d.1", subnetOctet)
   287  	return CreateSubnetWithCIDR(t, client, networkID, subnetCIDR, subnetGateway)
   288  }
   289  
   290  // CreateSubnetWithCIDR will create a subnet on the specified Network ID and CIDR. An error
   291  // will be returned if the subnet could not be created.
   292  func CreateSubnetWithCIDR(t *testing.T, client *gophercloud.ServiceClient, networkID, subnetCIDR, subnetGateway string) (*subnets.Subnet, error) {
   293  	subnetName := tools.RandomString("TESTACC-", 8)
   294  	subnetDescription := tools.RandomString("TESTACC-DESC-", 8)
   295  	createOpts := subnets.CreateOpts{
   296  		NetworkID:   networkID,
   297  		CIDR:        subnetCIDR,
   298  		IPVersion:   4,
   299  		Name:        subnetName,
   300  		Description: subnetDescription,
   301  		EnableDHCP:  gophercloud.Disabled,
   302  		GatewayIP:   &subnetGateway,
   303  	}
   304  
   305  	t.Logf("Attempting to create subnet: %s", subnetName)
   306  
   307  	subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract()
   308  	if err != nil {
   309  		return subnet, err
   310  	}
   311  
   312  	t.Logf("Successfully created subnet.")
   313  
   314  	th.AssertEquals(t, subnet.Name, subnetName)
   315  	th.AssertEquals(t, subnet.Description, subnetDescription)
   316  	th.AssertEquals(t, subnet.GatewayIP, subnetGateway)
   317  	th.AssertEquals(t, subnet.CIDR, subnetCIDR)
   318  
   319  	return subnet, nil
   320  }
   321  
   322  // CreateSubnet will create a subnet on the specified Network ID and service types.
   323  //
   324  //	An error will be returned if the subnet could not be created.
   325  func CreateSubnetWithServiceTypes(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) {
   326  	subnetName := tools.RandomString("TESTACC-", 8)
   327  	subnetDescription := tools.RandomString("TESTACC-DESC-", 8)
   328  	subnetOctet := tools.RandomInt(1, 250)
   329  	subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet)
   330  	subnetGateway := fmt.Sprintf("192.168.%d.1", subnetOctet)
   331  	serviceTypes := []string{"network:routed"}
   332  	createOpts := subnets.CreateOpts{
   333  		NetworkID:    networkID,
   334  		CIDR:         subnetCIDR,
   335  		IPVersion:    4,
   336  		Name:         subnetName,
   337  		Description:  subnetDescription,
   338  		EnableDHCP:   gophercloud.Disabled,
   339  		GatewayIP:    &subnetGateway,
   340  		ServiceTypes: serviceTypes,
   341  	}
   342  
   343  	t.Logf("Attempting to create subnet: %s", subnetName)
   344  
   345  	subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract()
   346  	if err != nil {
   347  		return subnet, err
   348  	}
   349  
   350  	t.Logf("Successfully created subnet.")
   351  
   352  	th.AssertEquals(t, subnet.Name, subnetName)
   353  	th.AssertEquals(t, subnet.Description, subnetDescription)
   354  	th.AssertEquals(t, subnet.GatewayIP, subnetGateway)
   355  	th.AssertEquals(t, subnet.CIDR, subnetCIDR)
   356  	th.AssertDeepEquals(t, subnet.ServiceTypes, serviceTypes)
   357  
   358  	return subnet, nil
   359  }
   360  
   361  // CreateSubnetWithDefaultGateway will create a subnet on the specified Network
   362  // ID and have Neutron set the gateway by default An error will be returned if
   363  // the subnet could not be created.
   364  func CreateSubnetWithDefaultGateway(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) {
   365  	subnetName := tools.RandomString("TESTACC-", 8)
   366  	subnetOctet := tools.RandomInt(1, 250)
   367  	subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet)
   368  	defaultGateway := fmt.Sprintf("192.168.%d.1", subnetOctet)
   369  
   370  	createOpts := subnets.CreateOpts{
   371  		NetworkID:  networkID,
   372  		CIDR:       subnetCIDR,
   373  		IPVersion:  4,
   374  		Name:       subnetName,
   375  		EnableDHCP: gophercloud.Disabled,
   376  	}
   377  
   378  	t.Logf("Attempting to create subnet: %s", subnetName)
   379  
   380  	subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract()
   381  	if err != nil {
   382  		return subnet, err
   383  	}
   384  
   385  	t.Logf("Successfully created subnet.")
   386  
   387  	th.AssertEquals(t, subnet.Name, subnetName)
   388  	th.AssertEquals(t, subnet.GatewayIP, defaultGateway)
   389  	th.AssertEquals(t, subnet.CIDR, subnetCIDR)
   390  
   391  	return subnet, nil
   392  }
   393  
   394  // CreateSubnetWithNoGateway will create a subnet with no gateway on the
   395  // specified Network ID.  An error will be returned if the subnet could not be
   396  // created.
   397  func CreateSubnetWithNoGateway(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*subnets.Subnet, error) {
   398  	noGateway := ""
   399  	subnetName := tools.RandomString("TESTACC-", 8)
   400  	subnetOctet := tools.RandomInt(1, 250)
   401  	subnetCIDR := fmt.Sprintf("192.168.%d.0/24", subnetOctet)
   402  	dhcpStart := fmt.Sprintf("192.168.%d.10", subnetOctet)
   403  	dhcpEnd := fmt.Sprintf("192.168.%d.200", subnetOctet)
   404  	createOpts := subnets.CreateOpts{
   405  		NetworkID:  networkID,
   406  		CIDR:       subnetCIDR,
   407  		IPVersion:  4,
   408  		Name:       subnetName,
   409  		EnableDHCP: gophercloud.Disabled,
   410  		GatewayIP:  &noGateway,
   411  		AllocationPools: []subnets.AllocationPool{
   412  			{
   413  				Start: dhcpStart,
   414  				End:   dhcpEnd,
   415  			},
   416  		},
   417  	}
   418  
   419  	t.Logf("Attempting to create subnet: %s", subnetName)
   420  
   421  	subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract()
   422  	if err != nil {
   423  		return subnet, err
   424  	}
   425  
   426  	t.Logf("Successfully created subnet.")
   427  
   428  	th.AssertEquals(t, subnet.Name, subnetName)
   429  	th.AssertEquals(t, subnet.GatewayIP, "")
   430  	th.AssertEquals(t, subnet.CIDR, subnetCIDR)
   431  
   432  	return subnet, nil
   433  }
   434  
   435  // CreateSubnetWithSubnetPool will create a subnet associated with the provided subnetpool on the specified Network ID.
   436  // An error will be returned if the subnet or the subnetpool could not be created.
   437  func CreateSubnetWithSubnetPool(t *testing.T, client *gophercloud.ServiceClient, networkID string, subnetPoolID string) (*subnets.Subnet, error) {
   438  	subnetName := tools.RandomString("TESTACC-", 8)
   439  	subnetOctet := tools.RandomInt(1, 250)
   440  	subnetCIDR := fmt.Sprintf("10.%d.0.0/24", subnetOctet)
   441  	createOpts := subnets.CreateOpts{
   442  		NetworkID:    networkID,
   443  		CIDR:         subnetCIDR,
   444  		IPVersion:    4,
   445  		Name:         subnetName,
   446  		EnableDHCP:   gophercloud.Disabled,
   447  		SubnetPoolID: subnetPoolID,
   448  	}
   449  
   450  	t.Logf("Attempting to create subnet: %s", subnetName)
   451  
   452  	subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract()
   453  	if err != nil {
   454  		return subnet, err
   455  	}
   456  
   457  	t.Logf("Successfully created subnet.")
   458  
   459  	th.AssertEquals(t, subnet.Name, subnetName)
   460  	th.AssertEquals(t, subnet.CIDR, subnetCIDR)
   461  
   462  	return subnet, nil
   463  }
   464  
   465  // CreateSubnetWithSubnetPoolNoCIDR will create a subnet associated with the
   466  // provided subnetpool on the specified Network ID.
   467  // An error will be returned if the subnet or the subnetpool could not be created.
   468  func CreateSubnetWithSubnetPoolNoCIDR(t *testing.T, client *gophercloud.ServiceClient, networkID string, subnetPoolID string) (*subnets.Subnet, error) {
   469  	subnetName := tools.RandomString("TESTACC-", 8)
   470  	createOpts := subnets.CreateOpts{
   471  		NetworkID:    networkID,
   472  		IPVersion:    4,
   473  		Name:         subnetName,
   474  		EnableDHCP:   gophercloud.Disabled,
   475  		SubnetPoolID: subnetPoolID,
   476  	}
   477  
   478  	t.Logf("Attempting to create subnet: %s", subnetName)
   479  
   480  	subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract()
   481  	if err != nil {
   482  		return subnet, err
   483  	}
   484  
   485  	t.Logf("Successfully created subnet.")
   486  
   487  	th.AssertEquals(t, subnet.Name, subnetName)
   488  
   489  	return subnet, nil
   490  }
   491  
   492  // CreateSubnetWithSubnetPoolPrefixlen will create a subnet associated with the
   493  // provided subnetpool on the specified Network ID and with overwritten
   494  // prefixlen instead of the default subnetpool prefixlen.
   495  // An error will be returned if the subnet or the subnetpool could not be created.
   496  func CreateSubnetWithSubnetPoolPrefixlen(t *testing.T, client *gophercloud.ServiceClient, networkID string, subnetPoolID string) (*subnets.Subnet, error) {
   497  	subnetName := tools.RandomString("TESTACC-", 8)
   498  	createOpts := subnets.CreateOpts{
   499  		NetworkID:    networkID,
   500  		IPVersion:    4,
   501  		Name:         subnetName,
   502  		EnableDHCP:   gophercloud.Disabled,
   503  		SubnetPoolID: subnetPoolID,
   504  		Prefixlen:    12,
   505  	}
   506  
   507  	t.Logf("Attempting to create subnet: %s", subnetName)
   508  
   509  	subnet, err := subnets.Create(context.TODO(), client, createOpts).Extract()
   510  	if err != nil {
   511  		return subnet, err
   512  	}
   513  
   514  	t.Logf("Successfully created subnet.")
   515  
   516  	th.AssertEquals(t, subnet.Name, subnetName)
   517  
   518  	return subnet, nil
   519  }
   520  
   521  // DeleteNetwork will delete a network with a specified ID. A fatal error will
   522  // occur if the delete was not successful. This works best when used as a
   523  // deferred function.
   524  func DeleteNetwork(t *testing.T, client *gophercloud.ServiceClient, networkID string) {
   525  	t.Logf("Attempting to delete network: %s", networkID)
   526  
   527  	err := networks.Delete(context.TODO(), client, networkID).ExtractErr()
   528  	if err != nil {
   529  		t.Fatalf("Unable to delete network %s: %v", networkID, err)
   530  	}
   531  
   532  	t.Logf("Deleted network: %s", networkID)
   533  }
   534  
   535  // DeletePort will delete a port with a specified ID. A fatal error will
   536  // occur if the delete was not successful. This works best when used as a
   537  // deferred function.
   538  func DeletePort(t *testing.T, client *gophercloud.ServiceClient, portID string) {
   539  	t.Logf("Attempting to delete port: %s", portID)
   540  
   541  	err := ports.Delete(context.TODO(), client, portID).ExtractErr()
   542  	if err != nil {
   543  		t.Fatalf("Unable to delete port %s: %v", portID, err)
   544  	}
   545  
   546  	t.Logf("Deleted port: %s", portID)
   547  }
   548  
   549  // DeleteSubnet will delete a subnet with a specified ID. A fatal error will
   550  // occur if the delete was not successful. This works best when used as a
   551  // deferred function.
   552  func DeleteSubnet(t *testing.T, client *gophercloud.ServiceClient, subnetID string) {
   553  	t.Logf("Attempting to delete subnet: %s", subnetID)
   554  
   555  	err := subnets.Delete(context.TODO(), client, subnetID).ExtractErr()
   556  	if err != nil {
   557  		t.Fatalf("Unable to delete subnet %s: %v", subnetID, err)
   558  	}
   559  
   560  	t.Logf("Deleted subnet: %s", subnetID)
   561  }
   562  
   563  func WaitForPortToCreate(client *gophercloud.ServiceClient, portID string) error {
   564  	return tools.WaitFor(func(ctx context.Context) (bool, error) {
   565  		p, err := ports.Get(ctx, client, portID).Extract()
   566  		if err != nil {
   567  			return false, err
   568  		}
   569  
   570  		if p.Status == "ACTIVE" || p.Status == "DOWN" {
   571  			return true, nil
   572  		}
   573  
   574  		return false, nil
   575  	})
   576  }
   577  
   578  // This is duplicated from https://github.com/gophercloud/utils
   579  // so that Gophercloud "core" doesn't have a dependency on the
   580  // complementary utils repository.
   581  func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
   582  	count := 0
   583  	id := ""
   584  
   585  	listOpts := networks.ListOpts{
   586  		Name: name,
   587  	}
   588  
   589  	pages, err := networks.List(client, listOpts).AllPages(context.TODO())
   590  	if err != nil {
   591  		return "", err
   592  	}
   593  
   594  	all, err := networks.ExtractNetworks(pages)
   595  	if err != nil {
   596  		return "", err
   597  	}
   598  
   599  	for _, s := range all {
   600  		if s.Name == name {
   601  			count++
   602  			id = s.ID
   603  		}
   604  	}
   605  
   606  	switch count {
   607  	case 0:
   608  		return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "network"}
   609  	case 1:
   610  		return id, nil
   611  	default:
   612  		return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "network"}
   613  	}
   614  }