github.com/leeclow-ops/gophercloud@v1.2.1/acceptance/openstack/loadbalancer/v2/loadbalancer.go (about)

     1  package v2
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/leeclow-ops/gophercloud"
     9  	"github.com/leeclow-ops/gophercloud/acceptance/clients"
    10  	"github.com/leeclow-ops/gophercloud/acceptance/tools"
    11  	"github.com/leeclow-ops/gophercloud/openstack/loadbalancer/v2/l7policies"
    12  	"github.com/leeclow-ops/gophercloud/openstack/loadbalancer/v2/listeners"
    13  	"github.com/leeclow-ops/gophercloud/openstack/loadbalancer/v2/loadbalancers"
    14  	"github.com/leeclow-ops/gophercloud/openstack/loadbalancer/v2/monitors"
    15  	"github.com/leeclow-ops/gophercloud/openstack/loadbalancer/v2/pools"
    16  	th "github.com/leeclow-ops/gophercloud/testhelper"
    17  )
    18  
    19  // CreateListener will create a listener for a given load balancer on a random
    20  // port with a random name. An error will be returned if the listener could not
    21  // be created.
    22  func CreateListener(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) {
    23  	listenerName := tools.RandomString("TESTACCT-", 8)
    24  	listenerDescription := tools.RandomString("TESTACCT-DESC-", 8)
    25  	listenerPort := tools.RandomInt(1, 100)
    26  
    27  	t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort)
    28  
    29  	createOpts := listeners.CreateOpts{
    30  		Name:           listenerName,
    31  		Description:    listenerDescription,
    32  		LoadbalancerID: lb.ID,
    33  		Protocol:       listeners.ProtocolTCP,
    34  		ProtocolPort:   listenerPort,
    35  	}
    36  
    37  	listener, err := listeners.Create(client, createOpts).Extract()
    38  	if err != nil {
    39  		return listener, err
    40  	}
    41  
    42  	t.Logf("Successfully created listener %s", listenerName)
    43  
    44  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
    45  		return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
    46  	}
    47  
    48  	th.AssertEquals(t, listener.Name, listenerName)
    49  	th.AssertEquals(t, listener.Description, listenerDescription)
    50  	th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID)
    51  	th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolTCP))
    52  	th.AssertEquals(t, listener.ProtocolPort, listenerPort)
    53  
    54  	return listener, nil
    55  }
    56  
    57  // CreateListenerHTTP will create an HTTP-based listener for a given load
    58  // balancer on a random port with a random name. An error will be returned
    59  // if the listener could not be created.
    60  func CreateListenerHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*listeners.Listener, error) {
    61  	tlsVersions := []listeners.TLSVersion{}
    62  	tlsVersionsExp := []string(nil)
    63  	listenerName := tools.RandomString("TESTACCT-", 8)
    64  	listenerDescription := tools.RandomString("TESTACCT-DESC-", 8)
    65  	listenerPort := tools.RandomInt(1, 100)
    66  
    67  	t.Logf("Attempting to create listener %s on port %d", listenerName, listenerPort)
    68  
    69  	headers := map[string]string{
    70  		"X-Forwarded-For": "true",
    71  	}
    72  
    73  	// tls_version is only supported in microversion v2.17 introduced in victoria
    74  	if clients.IsReleasesAbove(t, "stable/ussuri") {
    75  		tlsVersions = []listeners.TLSVersion{"TLSv1.2", "TLSv1.3"}
    76  		tlsVersionsExp = []string{"TLSv1.2", "TLSv1.3"}
    77  	}
    78  
    79  	createOpts := listeners.CreateOpts{
    80  		Name:           listenerName,
    81  		Description:    listenerDescription,
    82  		LoadbalancerID: lb.ID,
    83  		InsertHeaders:  headers,
    84  		Protocol:       listeners.ProtocolHTTP,
    85  		ProtocolPort:   listenerPort,
    86  		TLSVersions:    tlsVersions,
    87  	}
    88  
    89  	listener, err := listeners.Create(client, createOpts).Extract()
    90  	if err != nil {
    91  		return listener, err
    92  	}
    93  
    94  	t.Logf("Successfully created listener %s", listenerName)
    95  
    96  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
    97  		return listener, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
    98  	}
    99  
   100  	th.AssertEquals(t, listener.Name, listenerName)
   101  	th.AssertEquals(t, listener.Description, listenerDescription)
   102  	th.AssertEquals(t, listener.Loadbalancers[0].ID, lb.ID)
   103  	th.AssertEquals(t, listener.Protocol, string(listeners.ProtocolHTTP))
   104  	th.AssertEquals(t, listener.ProtocolPort, listenerPort)
   105  	th.AssertDeepEquals(t, listener.InsertHeaders, headers)
   106  	th.AssertDeepEquals(t, listener.TLSVersions, tlsVersionsExp)
   107  
   108  	return listener, nil
   109  }
   110  
   111  // CreateLoadBalancer will create a load balancer with a random name on a given
   112  // subnet. An error will be returned if the loadbalancer could not be created.
   113  func CreateLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, subnetID string, tags []string, policyID string) (*loadbalancers.LoadBalancer, error) {
   114  	lbName := tools.RandomString("TESTACCT-", 8)
   115  	lbDescription := tools.RandomString("TESTACCT-DESC-", 8)
   116  
   117  	t.Logf("Attempting to create loadbalancer %s on subnet %s", lbName, subnetID)
   118  
   119  	createOpts := loadbalancers.CreateOpts{
   120  		Name:         lbName,
   121  		Description:  lbDescription,
   122  		VipSubnetID:  subnetID,
   123  		AdminStateUp: gophercloud.Enabled,
   124  	}
   125  	if len(tags) > 0 {
   126  		createOpts.Tags = tags
   127  	}
   128  
   129  	if len(policyID) > 0 {
   130  		createOpts.VipQosPolicyID = policyID
   131  	}
   132  
   133  	lb, err := loadbalancers.Create(client, createOpts).Extract()
   134  	if err != nil {
   135  		return lb, err
   136  	}
   137  
   138  	t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID)
   139  	t.Logf("Waiting for loadbalancer %s to become active", lbName)
   140  
   141  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   142  		return lb, err
   143  	}
   144  
   145  	t.Logf("LoadBalancer %s is active", lbName)
   146  
   147  	th.AssertEquals(t, lb.Name, lbName)
   148  	th.AssertEquals(t, lb.Description, lbDescription)
   149  	th.AssertEquals(t, lb.VipSubnetID, subnetID)
   150  	th.AssertEquals(t, lb.AdminStateUp, true)
   151  
   152  	if len(tags) > 0 {
   153  		th.AssertDeepEquals(t, lb.Tags, tags)
   154  	}
   155  
   156  	if len(policyID) > 0 {
   157  		th.AssertEquals(t, lb.VipQosPolicyID, policyID)
   158  	}
   159  
   160  	return lb, nil
   161  }
   162  
   163  // CreateLoadBalancerFullyPopulated will create a  fully populated load balancer with a random name on a given
   164  // subnet. It will contain a listener, l7policy, l7rule, pool, member and health monitor.
   165  // An error will be returned if the loadbalancer could not be created.
   166  func CreateLoadBalancerFullyPopulated(t *testing.T, client *gophercloud.ServiceClient, subnetID string, tags []string) (*loadbalancers.LoadBalancer, error) {
   167  	lbName := tools.RandomString("TESTACCT-", 8)
   168  	lbDescription := tools.RandomString("TESTACCT-DESC-", 8)
   169  	listenerName := tools.RandomString("TESTACCT-", 8)
   170  	listenerDescription := tools.RandomString("TESTACCT-DESC-", 8)
   171  	listenerPort := tools.RandomInt(1, 100)
   172  	policyName := tools.RandomString("TESTACCT-", 8)
   173  	policyDescription := tools.RandomString("TESTACCT-DESC-", 8)
   174  	poolName := tools.RandomString("TESTACCT-", 8)
   175  	poolDescription := tools.RandomString("TESTACCT-DESC-", 8)
   176  	memberName := tools.RandomString("TESTACCT-", 8)
   177  	memberPort := tools.RandomInt(100, 1000)
   178  	memberWeight := tools.RandomInt(1, 10)
   179  
   180  	t.Logf("Attempting to create fully populated loadbalancer %s on subnet %s which contains listener: %s, l7Policy: %s, pool %s, member %s",
   181  		lbName, subnetID, listenerName, policyName, poolName, memberName)
   182  
   183  	createOpts := loadbalancers.CreateOpts{
   184  		Name:         lbName,
   185  		Description:  lbDescription,
   186  		VipSubnetID:  subnetID,
   187  		AdminStateUp: gophercloud.Enabled,
   188  		Listeners: []listeners.CreateOpts{{
   189  			Name:         listenerName,
   190  			Description:  listenerDescription,
   191  			Protocol:     listeners.ProtocolHTTP,
   192  			ProtocolPort: listenerPort,
   193  			DefaultPool: &pools.CreateOpts{
   194  				Name:        poolName,
   195  				Description: poolDescription,
   196  				Protocol:    pools.ProtocolHTTP,
   197  				LBMethod:    pools.LBMethodLeastConnections,
   198  				Members: []pools.BatchUpdateMemberOpts{{
   199  					Name:         &memberName,
   200  					ProtocolPort: memberPort,
   201  					Weight:       &memberWeight,
   202  					Address:      "1.2.3.4",
   203  					SubnetID:     &subnetID,
   204  				}},
   205  				Monitor: &monitors.CreateOpts{
   206  					Delay:          10,
   207  					Timeout:        5,
   208  					MaxRetries:     5,
   209  					MaxRetriesDown: 4,
   210  					Type:           monitors.TypeHTTP,
   211  				},
   212  			},
   213  			L7Policies: []l7policies.CreateOpts{{
   214  				Name:        policyName,
   215  				Description: policyDescription,
   216  				Action:      l7policies.ActionRedirectToURL,
   217  				RedirectURL: "http://www.example.com",
   218  				Rules: []l7policies.CreateRuleOpts{{
   219  					RuleType:    l7policies.TypePath,
   220  					CompareType: l7policies.CompareTypeStartWith,
   221  					Value:       "/api",
   222  				}},
   223  			}},
   224  		}},
   225  	}
   226  	if len(tags) > 0 {
   227  		createOpts.Tags = tags
   228  	}
   229  
   230  	lb, err := loadbalancers.Create(client, createOpts).Extract()
   231  	if err != nil {
   232  		return lb, err
   233  	}
   234  
   235  	t.Logf("Successfully created loadbalancer %s on subnet %s", lbName, subnetID)
   236  	t.Logf("Waiting for loadbalancer %s to become active", lbName)
   237  
   238  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   239  		return lb, err
   240  	}
   241  
   242  	t.Logf("LoadBalancer %s is active", lbName)
   243  
   244  	th.AssertEquals(t, lb.Name, lbName)
   245  	th.AssertEquals(t, lb.Description, lbDescription)
   246  	th.AssertEquals(t, lb.VipSubnetID, subnetID)
   247  	th.AssertEquals(t, lb.AdminStateUp, true)
   248  
   249  	th.AssertEquals(t, len(lb.Listeners), 1)
   250  	th.AssertEquals(t, lb.Listeners[0].Name, listenerName)
   251  	th.AssertEquals(t, lb.Listeners[0].Description, listenerDescription)
   252  	th.AssertEquals(t, lb.Listeners[0].ProtocolPort, listenerPort)
   253  
   254  	th.AssertEquals(t, len(lb.Listeners[0].L7Policies), 1)
   255  	th.AssertEquals(t, lb.Listeners[0].L7Policies[0].Name, policyName)
   256  	th.AssertEquals(t, lb.Listeners[0].L7Policies[0].Description, policyDescription)
   257  	th.AssertEquals(t, lb.Listeners[0].L7Policies[0].Description, policyDescription)
   258  	th.AssertEquals(t, len(lb.Listeners[0].L7Policies[0].Rules), 1)
   259  
   260  	th.AssertEquals(t, len(lb.Pools), 1)
   261  	th.AssertEquals(t, lb.Pools[0].Name, poolName)
   262  	th.AssertEquals(t, lb.Pools[0].Description, poolDescription)
   263  
   264  	th.AssertEquals(t, len(lb.Pools[0].Members), 1)
   265  	th.AssertEquals(t, lb.Pools[0].Members[0].Name, memberName)
   266  	th.AssertEquals(t, lb.Pools[0].Members[0].ProtocolPort, memberPort)
   267  	th.AssertEquals(t, lb.Pools[0].Members[0].Weight, memberWeight)
   268  
   269  	if len(tags) > 0 {
   270  		th.AssertDeepEquals(t, lb.Tags, tags)
   271  	}
   272  
   273  	return lb, nil
   274  }
   275  
   276  // CreateMember will create a member with a random name, port, address, and
   277  // weight. An error will be returned if the member could not be created.
   278  func CreateMember(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool, subnetID, subnetCIDR string) (*pools.Member, error) {
   279  	memberName := tools.RandomString("TESTACCT-", 8)
   280  	memberPort := tools.RandomInt(100, 1000)
   281  	memberWeight := tools.RandomInt(1, 10)
   282  
   283  	cidrParts := strings.Split(subnetCIDR, "/")
   284  	subnetParts := strings.Split(cidrParts[0], ".")
   285  	memberAddress := fmt.Sprintf("%s.%s.%s.%d", subnetParts[0], subnetParts[1], subnetParts[2], tools.RandomInt(10, 100))
   286  
   287  	t.Logf("Attempting to create member %s", memberName)
   288  
   289  	createOpts := pools.CreateMemberOpts{
   290  		Name:         memberName,
   291  		ProtocolPort: memberPort,
   292  		Weight:       &memberWeight,
   293  		Address:      memberAddress,
   294  		SubnetID:     subnetID,
   295  	}
   296  
   297  	t.Logf("Member create opts: %#v", createOpts)
   298  
   299  	member, err := pools.CreateMember(client, pool.ID, createOpts).Extract()
   300  	if err != nil {
   301  		return member, err
   302  	}
   303  
   304  	t.Logf("Successfully created member %s", memberName)
   305  
   306  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   307  		return member, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   308  	}
   309  
   310  	th.AssertEquals(t, member.Name, memberName)
   311  
   312  	return member, nil
   313  }
   314  
   315  // CreateMonitor will create a monitor with a random name for a specific pool.
   316  // An error will be returned if the monitor could not be created.
   317  func CreateMonitor(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer, pool *pools.Pool) (*monitors.Monitor, error) {
   318  	monitorName := tools.RandomString("TESTACCT-", 8)
   319  
   320  	t.Logf("Attempting to create monitor %s", monitorName)
   321  
   322  	createOpts := monitors.CreateOpts{
   323  		PoolID:         pool.ID,
   324  		Name:           monitorName,
   325  		Delay:          10,
   326  		Timeout:        5,
   327  		MaxRetries:     5,
   328  		MaxRetriesDown: 4,
   329  		Type:           monitors.TypePING,
   330  	}
   331  
   332  	monitor, err := monitors.Create(client, createOpts).Extract()
   333  	if err != nil {
   334  		return monitor, err
   335  	}
   336  
   337  	t.Logf("Successfully created monitor: %s", monitorName)
   338  
   339  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   340  		return monitor, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   341  	}
   342  
   343  	th.AssertEquals(t, monitor.Name, monitorName)
   344  	th.AssertEquals(t, monitor.Type, monitors.TypePING)
   345  	th.AssertEquals(t, monitor.Delay, 10)
   346  	th.AssertEquals(t, monitor.Timeout, 5)
   347  	th.AssertEquals(t, monitor.MaxRetries, 5)
   348  	th.AssertEquals(t, monitor.MaxRetriesDown, 4)
   349  
   350  	return monitor, nil
   351  }
   352  
   353  // CreatePool will create a pool with a random name with a specified listener
   354  // and loadbalancer. An error will be returned if the pool could not be
   355  // created.
   356  func CreatePool(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) {
   357  	poolName := tools.RandomString("TESTACCT-", 8)
   358  	poolDescription := tools.RandomString("TESTACCT-DESC-", 8)
   359  
   360  	t.Logf("Attempting to create pool %s", poolName)
   361  
   362  	createOpts := pools.CreateOpts{
   363  		Name:           poolName,
   364  		Description:    poolDescription,
   365  		Protocol:       pools.ProtocolTCP,
   366  		LoadbalancerID: lb.ID,
   367  		LBMethod:       pools.LBMethodLeastConnections,
   368  	}
   369  
   370  	pool, err := pools.Create(client, createOpts).Extract()
   371  	if err != nil {
   372  		return pool, err
   373  	}
   374  
   375  	t.Logf("Successfully created pool %s", poolName)
   376  
   377  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   378  		return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   379  	}
   380  
   381  	th.AssertEquals(t, pool.Name, poolName)
   382  	th.AssertEquals(t, pool.Description, poolDescription)
   383  	th.AssertEquals(t, pool.Protocol, string(pools.ProtocolTCP))
   384  	th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID)
   385  	th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections))
   386  
   387  	return pool, nil
   388  }
   389  
   390  // CreatePoolHTTP will create an HTTP-based pool with a random name with a
   391  // specified listener and loadbalancer. An error will be returned if the pool
   392  // could not be created.
   393  func CreatePoolHTTP(t *testing.T, client *gophercloud.ServiceClient, lb *loadbalancers.LoadBalancer) (*pools.Pool, error) {
   394  	poolName := tools.RandomString("TESTACCT-", 8)
   395  	poolDescription := tools.RandomString("TESTACCT-DESC-", 8)
   396  
   397  	t.Logf("Attempting to create pool %s", poolName)
   398  
   399  	createOpts := pools.CreateOpts{
   400  		Name:           poolName,
   401  		Description:    poolDescription,
   402  		Protocol:       pools.ProtocolHTTP,
   403  		LoadbalancerID: lb.ID,
   404  		LBMethod:       pools.LBMethodLeastConnections,
   405  	}
   406  
   407  	pool, err := pools.Create(client, createOpts).Extract()
   408  	if err != nil {
   409  		return pool, err
   410  	}
   411  
   412  	t.Logf("Successfully created pool %s", poolName)
   413  
   414  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   415  		return pool, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   416  	}
   417  
   418  	th.AssertEquals(t, pool.Name, poolName)
   419  	th.AssertEquals(t, pool.Description, poolDescription)
   420  	th.AssertEquals(t, pool.Protocol, string(pools.ProtocolHTTP))
   421  	th.AssertEquals(t, pool.Loadbalancers[0].ID, lb.ID)
   422  	th.AssertEquals(t, pool.LBMethod, string(pools.LBMethodLeastConnections))
   423  
   424  	return pool, nil
   425  }
   426  
   427  // CreateL7Policy will create a l7 policy with a random name with a specified listener
   428  // and loadbalancer. An error will be returned if the l7 policy could not be
   429  // created.
   430  func CreateL7Policy(t *testing.T, client *gophercloud.ServiceClient, listener *listeners.Listener, lb *loadbalancers.LoadBalancer) (*l7policies.L7Policy, error) {
   431  	policyName := tools.RandomString("TESTACCT-", 8)
   432  	policyDescription := tools.RandomString("TESTACCT-DESC-", 8)
   433  
   434  	t.Logf("Attempting to create l7 policy %s", policyName)
   435  
   436  	createOpts := l7policies.CreateOpts{
   437  		Name:        policyName,
   438  		Description: policyDescription,
   439  		ListenerID:  listener.ID,
   440  		Action:      l7policies.ActionRedirectToURL,
   441  		RedirectURL: "http://www.example.com",
   442  	}
   443  
   444  	policy, err := l7policies.Create(client, createOpts).Extract()
   445  	if err != nil {
   446  		return policy, err
   447  	}
   448  
   449  	t.Logf("Successfully created l7 policy %s", policyName)
   450  
   451  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   452  		return policy, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   453  	}
   454  
   455  	th.AssertEquals(t, policy.Name, policyName)
   456  	th.AssertEquals(t, policy.Description, policyDescription)
   457  	th.AssertEquals(t, policy.ListenerID, listener.ID)
   458  	th.AssertEquals(t, policy.Action, string(l7policies.ActionRedirectToURL))
   459  	th.AssertEquals(t, policy.RedirectURL, "http://www.example.com")
   460  
   461  	return policy, nil
   462  }
   463  
   464  // CreateL7Rule creates a l7 rule for specified l7 policy.
   465  func CreateL7Rule(t *testing.T, client *gophercloud.ServiceClient, policyID string, lb *loadbalancers.LoadBalancer) (*l7policies.Rule, error) {
   466  	t.Logf("Attempting to create l7 rule for policy %s", policyID)
   467  
   468  	createOpts := l7policies.CreateRuleOpts{
   469  		RuleType:    l7policies.TypePath,
   470  		CompareType: l7policies.CompareTypeStartWith,
   471  		Value:       "/api",
   472  	}
   473  
   474  	rule, err := l7policies.CreateRule(client, policyID, createOpts).Extract()
   475  	if err != nil {
   476  		return rule, err
   477  	}
   478  
   479  	t.Logf("Successfully created l7 rule for policy %s", policyID)
   480  
   481  	if err := WaitForLoadBalancerState(client, lb.ID, "ACTIVE"); err != nil {
   482  		return rule, fmt.Errorf("Timed out waiting for loadbalancer to become active: %s", err)
   483  	}
   484  
   485  	th.AssertEquals(t, rule.RuleType, string(l7policies.TypePath))
   486  	th.AssertEquals(t, rule.CompareType, string(l7policies.CompareTypeStartWith))
   487  	th.AssertEquals(t, rule.Value, "/api")
   488  
   489  	return rule, nil
   490  }
   491  
   492  // DeleteL7Policy will delete a specified l7 policy. A fatal error will occur if
   493  // the l7 policy could not be deleted. This works best when used as a deferred
   494  // function.
   495  func DeleteL7Policy(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID string) {
   496  	t.Logf("Attempting to delete l7 policy %s", policyID)
   497  
   498  	if err := l7policies.Delete(client, policyID).ExtractErr(); err != nil {
   499  		if _, ok := err.(gophercloud.ErrDefault404); !ok {
   500  			t.Fatalf("Unable to delete l7 policy: %v", err)
   501  		}
   502  	}
   503  
   504  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   505  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   506  	}
   507  
   508  	t.Logf("Successfully deleted l7 policy %s", policyID)
   509  }
   510  
   511  // DeleteL7Rule will delete a specified l7 rule. A fatal error will occur if
   512  // the l7 rule could not be deleted. This works best when used as a deferred
   513  // function.
   514  func DeleteL7Rule(t *testing.T, client *gophercloud.ServiceClient, lbID, policyID, ruleID string) {
   515  	t.Logf("Attempting to delete l7 rule %s", ruleID)
   516  
   517  	if err := l7policies.DeleteRule(client, policyID, ruleID).ExtractErr(); err != nil {
   518  		if _, ok := err.(gophercloud.ErrDefault404); !ok {
   519  			t.Fatalf("Unable to delete l7 rule: %v", err)
   520  		}
   521  	}
   522  
   523  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   524  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   525  	}
   526  
   527  	t.Logf("Successfully deleted l7 rule %s", ruleID)
   528  }
   529  
   530  // DeleteListener will delete a specified listener. A fatal error will occur if
   531  // the listener could not be deleted. This works best when used as a deferred
   532  // function.
   533  func DeleteListener(t *testing.T, client *gophercloud.ServiceClient, lbID, listenerID string) {
   534  	t.Logf("Attempting to delete listener %s", listenerID)
   535  
   536  	if err := listeners.Delete(client, listenerID).ExtractErr(); err != nil {
   537  		if _, ok := err.(gophercloud.ErrDefault404); !ok {
   538  			t.Fatalf("Unable to delete listener: %v", err)
   539  		}
   540  	}
   541  
   542  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   543  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   544  	}
   545  
   546  	t.Logf("Successfully deleted listener %s", listenerID)
   547  }
   548  
   549  // DeleteMember will delete a specified member. A fatal error will occur if the
   550  // member could not be deleted. This works best when used as a deferred
   551  // function.
   552  func DeleteMember(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID, memberID string) {
   553  	t.Logf("Attempting to delete member %s", memberID)
   554  
   555  	if err := pools.DeleteMember(client, poolID, memberID).ExtractErr(); err != nil {
   556  		if _, ok := err.(gophercloud.ErrDefault404); !ok {
   557  			t.Fatalf("Unable to delete member: %s", memberID)
   558  		}
   559  	}
   560  
   561  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   562  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   563  	}
   564  
   565  	t.Logf("Successfully deleted member %s", memberID)
   566  }
   567  
   568  // DeleteLoadBalancer will delete a specified loadbalancer. A fatal error will
   569  // occur if the loadbalancer could not be deleted. This works best when used
   570  // as a deferred function.
   571  func DeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) {
   572  	t.Logf("Attempting to delete loadbalancer %s", lbID)
   573  
   574  	deleteOpts := loadbalancers.DeleteOpts{
   575  		Cascade: false,
   576  	}
   577  
   578  	if err := loadbalancers.Delete(client, lbID, deleteOpts).ExtractErr(); err != nil {
   579  		if _, ok := err.(gophercloud.ErrDefault404); !ok {
   580  			t.Fatalf("Unable to delete loadbalancer: %v", err)
   581  		}
   582  	}
   583  
   584  	t.Logf("Waiting for loadbalancer %s to delete", lbID)
   585  
   586  	if err := WaitForLoadBalancerState(client, lbID, "DELETED"); err != nil {
   587  		t.Fatalf("Loadbalancer did not delete in time: %s", err)
   588  	}
   589  
   590  	t.Logf("Successfully deleted loadbalancer %s", lbID)
   591  }
   592  
   593  // CascadeDeleteLoadBalancer will perform a cascading delete on a loadbalancer.
   594  // A fatal error will occur if the loadbalancer could not be deleted. This works
   595  // best when used as a deferred function.
   596  func CascadeDeleteLoadBalancer(t *testing.T, client *gophercloud.ServiceClient, lbID string) {
   597  	t.Logf("Attempting to cascade delete loadbalancer %s", lbID)
   598  
   599  	deleteOpts := loadbalancers.DeleteOpts{
   600  		Cascade: true,
   601  	}
   602  
   603  	if err := loadbalancers.Delete(client, lbID, deleteOpts).ExtractErr(); err != nil {
   604  		t.Fatalf("Unable to cascade delete loadbalancer: %v", err)
   605  	}
   606  
   607  	t.Logf("Waiting for loadbalancer %s to cascade delete", lbID)
   608  
   609  	if err := WaitForLoadBalancerState(client, lbID, "DELETED"); err != nil {
   610  		t.Fatalf("Loadbalancer did not delete in time.")
   611  	}
   612  
   613  	t.Logf("Successfully deleted loadbalancer %s", lbID)
   614  }
   615  
   616  // DeleteMonitor will delete a specified monitor. A fatal error will occur if
   617  // the monitor could not be deleted. This works best when used as a deferred
   618  // function.
   619  func DeleteMonitor(t *testing.T, client *gophercloud.ServiceClient, lbID, monitorID string) {
   620  	t.Logf("Attempting to delete monitor %s", monitorID)
   621  
   622  	if err := monitors.Delete(client, monitorID).ExtractErr(); err != nil {
   623  		if _, ok := err.(gophercloud.ErrDefault404); !ok {
   624  			t.Fatalf("Unable to delete monitor: %v", err)
   625  		}
   626  	}
   627  
   628  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   629  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   630  	}
   631  
   632  	t.Logf("Successfully deleted monitor %s", monitorID)
   633  }
   634  
   635  // DeletePool will delete a specified pool. A fatal error will occur if the
   636  // pool could not be deleted. This works best when used as a deferred function.
   637  func DeletePool(t *testing.T, client *gophercloud.ServiceClient, lbID, poolID string) {
   638  	t.Logf("Attempting to delete pool %s", poolID)
   639  
   640  	if err := pools.Delete(client, poolID).ExtractErr(); err != nil {
   641  		if _, ok := err.(gophercloud.ErrDefault404); !ok {
   642  			t.Fatalf("Unable to delete pool: %v", err)
   643  		}
   644  	}
   645  
   646  	if err := WaitForLoadBalancerState(client, lbID, "ACTIVE"); err != nil {
   647  		t.Fatalf("Timed out waiting for loadbalancer to become active: %s", err)
   648  	}
   649  
   650  	t.Logf("Successfully deleted pool %s", poolID)
   651  }
   652  
   653  // WaitForLoadBalancerState will wait until a loadbalancer reaches a given state.
   654  func WaitForLoadBalancerState(client *gophercloud.ServiceClient, lbID, status string) error {
   655  	return tools.WaitFor(func() (bool, error) {
   656  		current, err := loadbalancers.Get(client, lbID).Extract()
   657  		if err != nil {
   658  			if httpStatus, ok := err.(gophercloud.ErrDefault404); ok {
   659  				if httpStatus.Actual == 404 {
   660  					if status == "DELETED" {
   661  						return true, nil
   662  					}
   663  				}
   664  			}
   665  			return false, err
   666  		}
   667  
   668  		if current.ProvisioningStatus == status {
   669  			return true, nil
   670  		}
   671  
   672  		if current.ProvisioningStatus == "ERROR" {
   673  			return false, fmt.Errorf("Load balancer is in ERROR state")
   674  		}
   675  
   676  		return false, nil
   677  	})
   678  }