github.com/gophercloud/gophercloud@v1.11.0/internal/acceptance/openstack/loadbalancer/v2/loadbalancer.go (about)

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