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

     1  // Package v2 contains common functions for creating compute-based resources
     2  // for use in acceptance tests. See the `*_test.go` files for example usages.
     3  package v2
     4  
     5  import (
     6  	"crypto/rand"
     7  	"crypto/rsa"
     8  	"fmt"
     9  	"testing"
    10  
    11  	"github.com/gophercloud/gophercloud"
    12  	"github.com/gophercloud/gophercloud/internal/acceptance/clients"
    13  	"github.com/gophercloud/gophercloud/internal/acceptance/tools"
    14  	"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
    15  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/aggregates"
    16  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces"
    17  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
    18  	dsr "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/defsecrules"
    19  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
    20  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
    21  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/networks"
    22  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/quotasets"
    23  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/remoteconsoles"
    24  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/rescueunrescue"
    25  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints"
    26  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
    27  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
    28  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
    29  	"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
    30  	"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
    31  	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
    32  	neutron "github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
    33  	th "github.com/gophercloud/gophercloud/testhelper"
    34  
    35  	"golang.org/x/crypto/ssh"
    36  )
    37  
    38  // AssociateFloatingIP will associate a floating IP with an instance. An error
    39  // will be returned if the floating IP was unable to be associated.
    40  func AssociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) error {
    41  	associateOpts := floatingips.AssociateOpts{
    42  		FloatingIP: floatingIP.IP,
    43  	}
    44  
    45  	t.Logf("Attempting to associate floating IP %s to instance %s", floatingIP.IP, server.ID)
    46  	err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr()
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	return nil
    52  }
    53  
    54  // AssociateFloatingIPWithFixedIP will associate a floating IP with an
    55  // instance's specific fixed IP. An error will be returend if the floating IP
    56  // was unable to be associated.
    57  func AssociateFloatingIPWithFixedIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server, fixedIP string) error {
    58  	associateOpts := floatingips.AssociateOpts{
    59  		FloatingIP: floatingIP.IP,
    60  		FixedIP:    fixedIP,
    61  	}
    62  
    63  	t.Logf("Attempting to associate floating IP %s to fixed IP %s on instance %s", floatingIP.IP, fixedIP, server.ID)
    64  	err := floatingips.AssociateInstance(client, server.ID, associateOpts).ExtractErr()
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	return nil
    70  }
    71  
    72  // AttachInterface will create and attach an interface on a given server.
    73  // An error will returned if the interface could not be created.
    74  func AttachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID string) (*attachinterfaces.Interface, error) {
    75  	t.Logf("Attempting to attach interface to server %s", serverID)
    76  
    77  	choices, err := clients.AcceptanceTestChoicesFromEnv()
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  
    82  	networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	createOpts := attachinterfaces.CreateOpts{
    88  		NetworkID: networkID,
    89  	}
    90  
    91  	iface, err := attachinterfaces.Create(client, serverID, createOpts).Extract()
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	t.Logf("Successfully created interface %s on server %s", iface.PortID, serverID)
    97  
    98  	return iface, nil
    99  }
   100  
   101  // CreateAggregate will create an aggregate with random name and available zone.
   102  // An error will be returned if the aggregate could not be created.
   103  func CreateAggregate(t *testing.T, client *gophercloud.ServiceClient) (*aggregates.Aggregate, error) {
   104  	aggregateName := tools.RandomString("aggregate_", 5)
   105  	availabilityZone := tools.RandomString("zone_", 5)
   106  	t.Logf("Attempting to create aggregate %s", aggregateName)
   107  
   108  	createOpts := aggregates.CreateOpts{
   109  		Name:             aggregateName,
   110  		AvailabilityZone: availabilityZone,
   111  	}
   112  
   113  	aggregate, err := aggregates.Create(client, createOpts).Extract()
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	t.Logf("Successfully created aggregate %d", aggregate.ID)
   119  
   120  	aggregate, err = aggregates.Get(client, aggregate.ID).Extract()
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	th.AssertEquals(t, aggregate.Name, aggregateName)
   126  	th.AssertEquals(t, aggregate.AvailabilityZone, availabilityZone)
   127  
   128  	return aggregate, nil
   129  }
   130  
   131  // CreateBootableVolumeServer works like CreateServer but is configured with
   132  // one or more block devices defined by passing in []bootfromvolume.BlockDevice.
   133  // An error will be returned if a server was unable to be created.
   134  func CreateBootableVolumeServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) {
   135  	var server *servers.Server
   136  
   137  	choices, err := clients.AcceptanceTestChoicesFromEnv()
   138  	if err != nil {
   139  		t.Fatal(err)
   140  	}
   141  
   142  	networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName)
   143  	if err != nil {
   144  		return server, err
   145  	}
   146  
   147  	name := tools.RandomString("ACPTTEST", 16)
   148  	t.Logf("Attempting to create bootable volume server: %s", name)
   149  
   150  	serverCreateOpts := servers.CreateOpts{
   151  		Name:      name,
   152  		FlavorRef: choices.FlavorID,
   153  		Networks: []servers.Network{
   154  			{UUID: networkID},
   155  		},
   156  	}
   157  
   158  	if blockDevices[0].SourceType == bootfromvolume.SourceImage && blockDevices[0].DestinationType == bootfromvolume.DestinationLocal {
   159  		serverCreateOpts.ImageRef = blockDevices[0].UUID
   160  	}
   161  
   162  	server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
   163  		CreateOptsBuilder: serverCreateOpts,
   164  		BlockDevice:       blockDevices,
   165  	}).Extract()
   166  
   167  	if err != nil {
   168  		return server, err
   169  	}
   170  
   171  	if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
   172  		return server, err
   173  	}
   174  
   175  	newServer, err := servers.Get(client, server.ID).Extract()
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  
   180  	th.AssertEquals(t, newServer.Name, name)
   181  
   182  	return newServer, nil
   183  }
   184  
   185  // CreateDefaultRule will create a default security group rule with a
   186  // random port range between 80 and 90. An error will be returned if
   187  // a default rule was unable to be created.
   188  func CreateDefaultRule(t *testing.T, client *gophercloud.ServiceClient) (dsr.DefaultRule, error) {
   189  	createOpts := dsr.CreateOpts{
   190  		FromPort:   tools.RandomInt(80, 89),
   191  		ToPort:     tools.RandomInt(90, 99),
   192  		IPProtocol: "TCP",
   193  		CIDR:       "0.0.0.0/0",
   194  	}
   195  
   196  	defaultRule, err := dsr.Create(client, createOpts).Extract()
   197  	if err != nil {
   198  		return *defaultRule, err
   199  	}
   200  
   201  	t.Logf("Created default rule: %s", defaultRule.ID)
   202  
   203  	return *defaultRule, nil
   204  }
   205  
   206  // CreateFlavor will create a flavor with a random name.
   207  // An error will be returned if the flavor could not be created.
   208  func CreateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) {
   209  	flavorName := tools.RandomString("flavor_", 5)
   210  	flavorDescription := fmt.Sprintf("I am %s and i am a yummy flavor", flavorName)
   211  
   212  	// Microversion 2.55 is required to add description to flavor
   213  	client.Microversion = "2.55"
   214  	t.Logf("Attempting to create flavor %s", flavorName)
   215  
   216  	isPublic := true
   217  	createOpts := flavors.CreateOpts{
   218  		Name:        flavorName,
   219  		RAM:         1,
   220  		VCPUs:       1,
   221  		Disk:        gophercloud.IntToPointer(1),
   222  		IsPublic:    &isPublic,
   223  		Description: flavorDescription,
   224  	}
   225  
   226  	flavor, err := flavors.Create(client, createOpts).Extract()
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	t.Logf("Successfully created flavor %s", flavor.ID)
   232  
   233  	th.AssertEquals(t, flavor.Name, flavorName)
   234  	th.AssertEquals(t, flavor.RAM, 1)
   235  	th.AssertEquals(t, flavor.Disk, 1)
   236  	th.AssertEquals(t, flavor.VCPUs, 1)
   237  	th.AssertEquals(t, flavor.IsPublic, true)
   238  	th.AssertEquals(t, flavor.Description, flavorDescription)
   239  
   240  	return flavor, nil
   241  }
   242  
   243  // CreateFloatingIP will allocate a floating IP.
   244  // An error will be returend if one was unable to be allocated.
   245  func CreateFloatingIP(t *testing.T, client *gophercloud.ServiceClient) (*floatingips.FloatingIP, error) {
   246  	choices, err := clients.AcceptanceTestChoicesFromEnv()
   247  	if err != nil {
   248  		t.Fatal(err)
   249  	}
   250  
   251  	createOpts := floatingips.CreateOpts{
   252  		Pool: choices.FloatingIPPoolName,
   253  	}
   254  	floatingIP, err := floatingips.Create(client, createOpts).Extract()
   255  	if err != nil {
   256  		return floatingIP, err
   257  	}
   258  
   259  	t.Logf("Created floating IP: %s", floatingIP.ID)
   260  	return floatingIP, nil
   261  }
   262  
   263  func createKey() (string, error) {
   264  	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
   265  	if err != nil {
   266  		return "", err
   267  	}
   268  
   269  	publicKey := privateKey.PublicKey
   270  	pub, err := ssh.NewPublicKey(&publicKey)
   271  	if err != nil {
   272  		return "", err
   273  	}
   274  
   275  	pubBytes := ssh.MarshalAuthorizedKey(pub)
   276  	pk := string(pubBytes)
   277  	return pk, nil
   278  }
   279  
   280  // CreateKeyPair will create a KeyPair with a random name. An error will occur
   281  // if the keypair failed to be created. An error will be returned if the
   282  // keypair was unable to be created.
   283  func CreateKeyPair(t *testing.T, client *gophercloud.ServiceClient) (*keypairs.KeyPair, error) {
   284  	keyPairName := tools.RandomString("keypair_", 5)
   285  
   286  	t.Logf("Attempting to create keypair: %s", keyPairName)
   287  	createOpts := keypairs.CreateOpts{
   288  		Name: keyPairName,
   289  	}
   290  	keyPair, err := keypairs.Create(client, createOpts).Extract()
   291  	if err != nil {
   292  		return keyPair, err
   293  	}
   294  
   295  	t.Logf("Created keypair: %s", keyPairName)
   296  
   297  	th.AssertEquals(t, keyPair.Name, keyPairName)
   298  
   299  	return keyPair, nil
   300  }
   301  
   302  // CreateMultiEphemeralServer works like CreateServer but is configured with
   303  // one or more block devices defined by passing in []bootfromvolume.BlockDevice.
   304  // These block devices act like block devices when booting from a volume but
   305  // are actually local ephemeral disks.
   306  // An error will be returned if a server was unable to be created.
   307  func CreateMultiEphemeralServer(t *testing.T, client *gophercloud.ServiceClient, blockDevices []bootfromvolume.BlockDevice) (*servers.Server, error) {
   308  	var server *servers.Server
   309  
   310  	choices, err := clients.AcceptanceTestChoicesFromEnv()
   311  	if err != nil {
   312  		t.Fatal(err)
   313  	}
   314  
   315  	networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName)
   316  	if err != nil {
   317  		return server, err
   318  	}
   319  
   320  	name := tools.RandomString("ACPTTEST", 16)
   321  	t.Logf("Attempting to create bootable volume server: %s", name)
   322  
   323  	serverCreateOpts := servers.CreateOpts{
   324  		Name:      name,
   325  		FlavorRef: choices.FlavorID,
   326  		ImageRef:  choices.ImageID,
   327  		Networks: []servers.Network{
   328  			{UUID: networkID},
   329  		},
   330  	}
   331  
   332  	server, err = bootfromvolume.Create(client, bootfromvolume.CreateOptsExt{
   333  		CreateOptsBuilder: serverCreateOpts,
   334  		BlockDevice:       blockDevices,
   335  	}).Extract()
   336  
   337  	if err != nil {
   338  		return server, err
   339  	}
   340  
   341  	if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
   342  		return server, err
   343  	}
   344  
   345  	newServer, err := servers.Get(client, server.ID).Extract()
   346  	if err != nil {
   347  		return server, err
   348  	}
   349  	th.AssertEquals(t, newServer.Name, name)
   350  	th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID)
   351  	th.AssertEquals(t, newServer.Image["id"], choices.ImageID)
   352  
   353  	return newServer, nil
   354  }
   355  
   356  // CreatePrivateFlavor will create a private flavor with a random name.
   357  // An error will be returned if the flavor could not be created.
   358  func CreatePrivateFlavor(t *testing.T, client *gophercloud.ServiceClient) (*flavors.Flavor, error) {
   359  	flavorName := tools.RandomString("flavor_", 5)
   360  	t.Logf("Attempting to create flavor %s", flavorName)
   361  
   362  	isPublic := false
   363  	createOpts := flavors.CreateOpts{
   364  		Name:     flavorName,
   365  		RAM:      1,
   366  		VCPUs:    1,
   367  		Disk:     gophercloud.IntToPointer(1),
   368  		IsPublic: &isPublic,
   369  	}
   370  
   371  	flavor, err := flavors.Create(client, createOpts).Extract()
   372  	if err != nil {
   373  		return nil, err
   374  	}
   375  
   376  	t.Logf("Successfully created flavor %s", flavor.ID)
   377  
   378  	th.AssertEquals(t, flavor.Name, flavorName)
   379  	th.AssertEquals(t, flavor.RAM, 1)
   380  	th.AssertEquals(t, flavor.Disk, 1)
   381  	th.AssertEquals(t, flavor.VCPUs, 1)
   382  	th.AssertEquals(t, flavor.IsPublic, false)
   383  
   384  	return flavor, nil
   385  }
   386  
   387  // CreateSecurityGroup will create a security group with a random name.
   388  // An error will be returned if one was failed to be created.
   389  func CreateSecurityGroup(t *testing.T, client *gophercloud.ServiceClient) (*secgroups.SecurityGroup, error) {
   390  	name := tools.RandomString("secgroup_", 5)
   391  
   392  	createOpts := secgroups.CreateOpts{
   393  		Name:        name,
   394  		Description: "something",
   395  	}
   396  
   397  	securityGroup, err := secgroups.Create(client, createOpts).Extract()
   398  	if err != nil {
   399  		return nil, err
   400  	}
   401  
   402  	t.Logf("Created security group: %s", securityGroup.ID)
   403  
   404  	th.AssertEquals(t, securityGroup.Name, name)
   405  
   406  	return securityGroup, nil
   407  }
   408  
   409  // CreateSecurityGroupRule will create a security group rule with a random name
   410  // and a random TCP port range between port 80 and 99. An error will be
   411  // returned if the rule failed to be created.
   412  func CreateSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) (*secgroups.Rule, error) {
   413  	fromPort := tools.RandomInt(80, 89)
   414  	toPort := tools.RandomInt(90, 99)
   415  	createOpts := secgroups.CreateRuleOpts{
   416  		ParentGroupID: securityGroupID,
   417  		FromPort:      fromPort,
   418  		ToPort:        toPort,
   419  		IPProtocol:    "TCP",
   420  		CIDR:          "0.0.0.0/0",
   421  	}
   422  
   423  	rule, err := secgroups.CreateRule(client, createOpts).Extract()
   424  	if err != nil {
   425  		return nil, err
   426  	}
   427  
   428  	t.Logf("Created security group rule: %s", rule.ID)
   429  
   430  	th.AssertEquals(t, rule.FromPort, fromPort)
   431  	th.AssertEquals(t, rule.ToPort, toPort)
   432  	th.AssertEquals(t, rule.ParentGroupID, securityGroupID)
   433  
   434  	return rule, nil
   435  }
   436  
   437  // CreateServer creates a basic instance with a randomly generated name.
   438  // The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable.
   439  // The image will be the value of the OS_IMAGE_ID environment variable.
   440  // The instance will be launched on the network specified in OS_NETWORK_NAME.
   441  // An error will be returned if the instance was unable to be created.
   442  func CreateServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) {
   443  	choices, err := clients.AcceptanceTestChoicesFromEnv()
   444  	if err != nil {
   445  		t.Fatal(err)
   446  	}
   447  
   448  	networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName)
   449  	if err != nil {
   450  		return nil, err
   451  	}
   452  
   453  	name := tools.RandomString("ACPTTEST", 16)
   454  	t.Logf("Attempting to create server: %s", name)
   455  
   456  	pwd := tools.MakeNewPassword("")
   457  
   458  	server, err := servers.Create(client, servers.CreateOpts{
   459  		Name:      name,
   460  		FlavorRef: choices.FlavorID,
   461  		ImageRef:  choices.ImageID,
   462  		AdminPass: pwd,
   463  		Networks: []servers.Network{
   464  			{UUID: networkID},
   465  		},
   466  		Metadata: map[string]string{
   467  			"abc": "def",
   468  		},
   469  		Personality: servers.Personality{
   470  			&servers.File{
   471  				Path:     "/etc/test",
   472  				Contents: []byte("hello world"),
   473  			},
   474  		},
   475  	}).Extract()
   476  	if err != nil {
   477  		return server, err
   478  	}
   479  
   480  	if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
   481  		return nil, err
   482  	}
   483  
   484  	newServer, err := servers.Get(client, server.ID).Extract()
   485  	if err != nil {
   486  		return nil, err
   487  	}
   488  
   489  	th.AssertEquals(t, newServer.Name, name)
   490  	th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID)
   491  	th.AssertEquals(t, newServer.Image["id"], choices.ImageID)
   492  
   493  	return newServer, nil
   494  }
   495  
   496  // CreateMicroversionServer creates a basic instance compatible with
   497  // newer microversions with a randomly generated name.
   498  // The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable.
   499  // The image will be the value of the OS_IMAGE_ID environment variable.
   500  // The instance will be launched on the network specified in OS_NETWORK_NAME.
   501  // An error will be returned if the instance was unable to be created.
   502  func CreateMicroversionServer(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) {
   503  	choices, err := clients.AcceptanceTestChoicesFromEnv()
   504  	if err != nil {
   505  		t.Fatal(err)
   506  	}
   507  
   508  	networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName)
   509  	if err != nil {
   510  		return nil, err
   511  	}
   512  
   513  	name := tools.RandomString("ACPTTEST", 16)
   514  	t.Logf("Attempting to create server: %s", name)
   515  
   516  	pwd := tools.MakeNewPassword("")
   517  
   518  	server, err := servers.Create(client, servers.CreateOpts{
   519  		Name:      name,
   520  		FlavorRef: choices.FlavorID,
   521  		ImageRef:  choices.ImageID,
   522  		AdminPass: pwd,
   523  		Networks: []servers.Network{
   524  			{UUID: networkID},
   525  		},
   526  		Metadata: map[string]string{
   527  			"abc": "def",
   528  		},
   529  	}).Extract()
   530  	if err != nil {
   531  		return server, err
   532  	}
   533  
   534  	if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
   535  		return nil, err
   536  	}
   537  
   538  	newServer, err := servers.Get(client, server.ID).Extract()
   539  	if err != nil {
   540  		return nil, err
   541  	}
   542  
   543  	th.AssertEquals(t, newServer.Name, name)
   544  	th.AssertEquals(t, newServer.Image["id"], choices.ImageID)
   545  
   546  	return newServer, nil
   547  }
   548  
   549  // CreateServerWithoutImageRef creates a basic instance with a randomly generated name.
   550  // The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable.
   551  // The image is intentionally missing to trigger an error.
   552  // The instance will be launched on the network specified in OS_NETWORK_NAME.
   553  // An error will be returned if the instance was unable to be created.
   554  func CreateServerWithoutImageRef(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) {
   555  	choices, err := clients.AcceptanceTestChoicesFromEnv()
   556  	if err != nil {
   557  		t.Fatal(err)
   558  	}
   559  
   560  	networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName)
   561  	if err != nil {
   562  		return nil, err
   563  	}
   564  
   565  	name := tools.RandomString("ACPTTEST", 16)
   566  	t.Logf("Attempting to create server: %s", name)
   567  
   568  	pwd := tools.MakeNewPassword("")
   569  
   570  	server, err := servers.Create(client, servers.CreateOpts{
   571  		Name:      name,
   572  		FlavorRef: choices.FlavorID,
   573  		AdminPass: pwd,
   574  		Networks: []servers.Network{
   575  			{UUID: networkID},
   576  		},
   577  		Personality: servers.Personality{
   578  			&servers.File{
   579  				Path:     "/etc/test",
   580  				Contents: []byte("hello world"),
   581  			},
   582  		},
   583  	}).Extract()
   584  	if err != nil {
   585  		return nil, err
   586  	}
   587  
   588  	if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
   589  		return nil, err
   590  	}
   591  
   592  	return server, nil
   593  }
   594  
   595  // CreateServerWithTags creates a basic instance with a randomly generated name.
   596  // The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable.
   597  // The image will be the value of the OS_IMAGE_ID environment variable.
   598  // The instance will be launched on the network specified in OS_NETWORK_NAME.
   599  // Two tags will be assigned to the server.
   600  // An error will be returned if the instance was unable to be created.
   601  func CreateServerWithTags(t *testing.T, client *gophercloud.ServiceClient, networkID string) (*servers.Server, error) {
   602  	choices, err := clients.AcceptanceTestChoicesFromEnv()
   603  	if err != nil {
   604  		t.Fatal(err)
   605  	}
   606  
   607  	name := tools.RandomString("ACPTTEST", 16)
   608  	t.Logf("Attempting to create server: %s", name)
   609  
   610  	pwd := tools.MakeNewPassword("")
   611  
   612  	server, err := servers.Create(client, servers.CreateOpts{
   613  		Name:      name,
   614  		FlavorRef: choices.FlavorID,
   615  		ImageRef:  choices.ImageID,
   616  		AdminPass: pwd,
   617  		Networks: []servers.Network{
   618  			{UUID: networkID},
   619  		},
   620  		Metadata: map[string]string{
   621  			"abc": "def",
   622  		},
   623  		Personality: servers.Personality{
   624  			&servers.File{
   625  				Path:     "/etc/test",
   626  				Contents: []byte("hello world"),
   627  			},
   628  		},
   629  		Tags: []string{"tag1", "tag2"},
   630  	}).Extract()
   631  	if err != nil {
   632  		return server, err
   633  	}
   634  
   635  	if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
   636  		return nil, err
   637  	}
   638  
   639  	res := servers.Get(client, server.ID)
   640  	if res.Err != nil {
   641  		return nil, res.Err
   642  	}
   643  
   644  	newServer, err := res.Extract()
   645  	th.AssertNoErr(t, err)
   646  	th.AssertEquals(t, newServer.Name, name)
   647  	th.AssertDeepEquals(t, *newServer.Tags, []string{"tag1", "tag2"})
   648  
   649  	return newServer, nil
   650  }
   651  
   652  // CreateServerGroup will create a server with a random name. An error will be
   653  // returned if the server group failed to be created.
   654  func CreateServerGroup(t *testing.T, client *gophercloud.ServiceClient, policy string) (*servergroups.ServerGroup, error) {
   655  	name := tools.RandomString("ACPTTEST", 16)
   656  
   657  	t.Logf("Attempting to create server group %s", name)
   658  
   659  	sg, err := servergroups.Create(client, &servergroups.CreateOpts{
   660  		Name:     name,
   661  		Policies: []string{policy},
   662  	}).Extract()
   663  
   664  	if err != nil {
   665  		return nil, err
   666  	}
   667  
   668  	t.Logf("Successfully created server group %s", name)
   669  
   670  	th.AssertEquals(t, sg.Name, name)
   671  
   672  	return sg, nil
   673  }
   674  
   675  // CreateServerGroupMicroversion will create a server with a random name using 2.64 microversion. An error will be
   676  // returned if the server group failed to be created.
   677  func CreateServerGroupMicroversion(t *testing.T, client *gophercloud.ServiceClient) (*servergroups.ServerGroup, error) {
   678  	name := tools.RandomString("ACPTTEST", 16)
   679  	policy := "anti-affinity"
   680  	maxServerPerHost := 3
   681  
   682  	t.Logf("Attempting to create %s server group with max server per host = %d: %s", policy, maxServerPerHost, name)
   683  
   684  	sg, err := servergroups.Create(client, &servergroups.CreateOpts{
   685  		Name:   name,
   686  		Policy: policy,
   687  		Rules: &servergroups.Rules{
   688  			MaxServerPerHost: maxServerPerHost,
   689  		},
   690  	}).Extract()
   691  
   692  	if err != nil {
   693  		return nil, err
   694  	}
   695  
   696  	t.Logf("Successfully created server group %s", name)
   697  
   698  	th.AssertEquals(t, sg.Name, name)
   699  
   700  	return sg, nil
   701  }
   702  
   703  // CreateServerInServerGroup works like CreateServer but places the instance in
   704  // a specified Server Group.
   705  func CreateServerInServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) (*servers.Server, error) {
   706  	choices, err := clients.AcceptanceTestChoicesFromEnv()
   707  	if err != nil {
   708  		t.Fatal(err)
   709  	}
   710  
   711  	networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName)
   712  	if err != nil {
   713  		return nil, err
   714  	}
   715  
   716  	name := tools.RandomString("ACPTTEST", 16)
   717  	t.Logf("Attempting to create server: %s", name)
   718  
   719  	pwd := tools.MakeNewPassword("")
   720  
   721  	serverCreateOpts := servers.CreateOpts{
   722  		Name:      name,
   723  		FlavorRef: choices.FlavorID,
   724  		ImageRef:  choices.ImageID,
   725  		AdminPass: pwd,
   726  		Networks: []servers.Network{
   727  			{UUID: networkID},
   728  		},
   729  	}
   730  
   731  	schedulerHintsOpts := schedulerhints.CreateOptsExt{
   732  		CreateOptsBuilder: serverCreateOpts,
   733  		SchedulerHints: schedulerhints.SchedulerHints{
   734  			Group: serverGroup.ID,
   735  		},
   736  	}
   737  	server, err := servers.Create(client, schedulerHintsOpts).Extract()
   738  	if err != nil {
   739  		return nil, err
   740  	}
   741  
   742  	if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
   743  		return nil, err
   744  	}
   745  
   746  	newServer, err := servers.Get(client, server.ID).Extract()
   747  	if err != nil {
   748  		return nil, err
   749  	}
   750  
   751  	th.AssertEquals(t, newServer.Name, name)
   752  	th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID)
   753  	th.AssertEquals(t, newServer.Image["id"], choices.ImageID)
   754  
   755  	return newServer, nil
   756  }
   757  
   758  // CreateServerWithPublicKey works the same as CreateServer, but additionally
   759  // configures the server with a specified Key Pair name.
   760  func CreateServerWithPublicKey(t *testing.T, client *gophercloud.ServiceClient, keyPairName string) (*servers.Server, error) {
   761  	choices, err := clients.AcceptanceTestChoicesFromEnv()
   762  	if err != nil {
   763  		t.Fatal(err)
   764  	}
   765  
   766  	networkID, err := GetNetworkIDFromNetworks(t, client, choices.NetworkName)
   767  	if err != nil {
   768  		return nil, err
   769  	}
   770  
   771  	name := tools.RandomString("ACPTTEST", 16)
   772  	t.Logf("Attempting to create server: %s", name)
   773  
   774  	serverCreateOpts := servers.CreateOpts{
   775  		Name:      name,
   776  		FlavorRef: choices.FlavorID,
   777  		ImageRef:  choices.ImageID,
   778  		Networks: []servers.Network{
   779  			{UUID: networkID},
   780  		},
   781  	}
   782  
   783  	server, err := servers.Create(client, keypairs.CreateOptsExt{
   784  		CreateOptsBuilder: serverCreateOpts,
   785  		KeyName:           keyPairName,
   786  	}).Extract()
   787  	if err != nil {
   788  		return nil, err
   789  	}
   790  
   791  	if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
   792  		return nil, err
   793  	}
   794  
   795  	newServer, err := servers.Get(client, server.ID).Extract()
   796  	if err != nil {
   797  		return nil, err
   798  	}
   799  
   800  	th.AssertEquals(t, newServer.Name, name)
   801  	th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID)
   802  	th.AssertEquals(t, newServer.Image["id"], choices.ImageID)
   803  
   804  	return newServer, nil
   805  }
   806  
   807  // CreateVolumeAttachment will attach a volume to a server. An error will be
   808  // returned if the volume failed to attach.
   809  func CreateVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volume *volumes.Volume) (*volumeattach.VolumeAttachment, error) {
   810  	tag := tools.RandomString("ACPTTEST", 16)
   811  	dot := false
   812  
   813  	volumeAttachOptions := volumeattach.CreateOpts{
   814  		VolumeID:            volume.ID,
   815  		Tag:                 tag,
   816  		DeleteOnTermination: dot,
   817  	}
   818  
   819  	t.Logf("Attempting to attach volume %s to server %s", volume.ID, server.ID)
   820  	volumeAttachment, err := volumeattach.Create(client, server.ID, volumeAttachOptions).Extract()
   821  	if err != nil {
   822  		return volumeAttachment, err
   823  	}
   824  
   825  	if err := volumes.WaitForStatus(blockClient, volume.ID, "in-use", 60); err != nil {
   826  		return volumeAttachment, err
   827  	}
   828  
   829  	return volumeAttachment, nil
   830  }
   831  
   832  // DeleteAggregate will delete a given host aggregate. A fatal error will occur if
   833  // the aggregate deleting is failed. This works best when using it as a
   834  // deferred function.
   835  func DeleteAggregate(t *testing.T, client *gophercloud.ServiceClient, aggregate *aggregates.Aggregate) {
   836  	err := aggregates.Delete(client, aggregate.ID).ExtractErr()
   837  	if err != nil {
   838  		t.Fatalf("Unable to delete aggregate %d", aggregate.ID)
   839  	}
   840  
   841  	t.Logf("Deleted aggregate: %d", aggregate.ID)
   842  }
   843  
   844  // DeleteDefaultRule deletes a default security group rule.
   845  // A fatal error will occur if the rule failed to delete. This works best when
   846  // using it as a deferred function.
   847  func DeleteDefaultRule(t *testing.T, client *gophercloud.ServiceClient, defaultRule dsr.DefaultRule) {
   848  	err := dsr.Delete(client, defaultRule.ID).ExtractErr()
   849  	if err != nil {
   850  		t.Fatalf("Unable to delete default rule %s: %v", defaultRule.ID, err)
   851  	}
   852  
   853  	t.Logf("Deleted default rule: %s", defaultRule.ID)
   854  }
   855  
   856  // DeleteFlavor will delete a flavor. A fatal error will occur if the flavor
   857  // could not be deleted. This works best when using it as a deferred function.
   858  func DeleteFlavor(t *testing.T, client *gophercloud.ServiceClient, flavor *flavors.Flavor) {
   859  	err := flavors.Delete(client, flavor.ID).ExtractErr()
   860  	if err != nil {
   861  		t.Fatalf("Unable to delete flavor %s", flavor.ID)
   862  	}
   863  
   864  	t.Logf("Deleted flavor: %s", flavor.ID)
   865  }
   866  
   867  // DeleteFloatingIP will de-allocate a floating IP. A fatal error will occur if
   868  // the floating IP failed to de-allocate. This works best when using it as a
   869  // deferred function.
   870  func DeleteFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP) {
   871  	err := floatingips.Delete(client, floatingIP.ID).ExtractErr()
   872  	if err != nil {
   873  		t.Fatalf("Unable to delete floating IP %s: %v", floatingIP.ID, err)
   874  	}
   875  
   876  	t.Logf("Deleted floating IP: %s", floatingIP.ID)
   877  }
   878  
   879  // DeleteKeyPair will delete a specified keypair. A fatal error will occur if
   880  // the keypair failed to be deleted. This works best when used as a deferred
   881  // function.
   882  func DeleteKeyPair(t *testing.T, client *gophercloud.ServiceClient, keyPair *keypairs.KeyPair) {
   883  	err := keypairs.Delete(client, keyPair.Name, nil).ExtractErr()
   884  	if err != nil {
   885  		t.Fatalf("Unable to delete keypair %s: %v", keyPair.Name, err)
   886  	}
   887  
   888  	t.Logf("Deleted keypair: %s", keyPair.Name)
   889  }
   890  
   891  // DeleteSecurityGroup will delete a security group. A fatal error will occur
   892  // if the group failed to be deleted. This works best as a deferred function.
   893  func DeleteSecurityGroup(t *testing.T, client *gophercloud.ServiceClient, securityGroupID string) {
   894  	err := secgroups.Delete(client, securityGroupID).ExtractErr()
   895  	if err != nil {
   896  		t.Fatalf("Unable to delete security group %s: %s", securityGroupID, err)
   897  	}
   898  
   899  	t.Logf("Deleted security group: %s", securityGroupID)
   900  }
   901  
   902  // DeleteSecurityGroupRule will delete a security group rule. A fatal error
   903  // will occur if the rule failed to be deleted. This works best when used
   904  // as a deferred function.
   905  func DeleteSecurityGroupRule(t *testing.T, client *gophercloud.ServiceClient, ruleID string) {
   906  	err := secgroups.DeleteRule(client, ruleID).ExtractErr()
   907  	if err != nil {
   908  		t.Fatalf("Unable to delete rule: %v", err)
   909  	}
   910  
   911  	t.Logf("Deleted security group rule: %s", ruleID)
   912  }
   913  
   914  // DeleteServer deletes an instance via its UUID.
   915  // A fatal error will occur if the instance failed to be destroyed. This works
   916  // best when using it as a deferred function.
   917  func DeleteServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) {
   918  	err := servers.Delete(client, server.ID).ExtractErr()
   919  	if err != nil {
   920  		t.Fatalf("Unable to delete server %s: %s", server.ID, err)
   921  	}
   922  
   923  	if err := WaitForComputeStatus(client, server, "DELETED"); err != nil {
   924  		if _, ok := err.(gophercloud.ErrDefault404); ok {
   925  			t.Logf("Deleted server: %s", server.ID)
   926  			return
   927  		}
   928  		t.Fatalf("Error deleting server %s: %s", server.ID, err)
   929  	}
   930  
   931  	// If we reach this point, the API returned an actual DELETED status
   932  	// which is a very short window of time, but happens occasionally.
   933  	t.Logf("Deleted server: %s", server.ID)
   934  }
   935  
   936  // DeleteServerGroup will delete a server group. A fatal error will occur if
   937  // the server group failed to be deleted. This works best when used as a
   938  // deferred function.
   939  func DeleteServerGroup(t *testing.T, client *gophercloud.ServiceClient, serverGroup *servergroups.ServerGroup) {
   940  	err := servergroups.Delete(client, serverGroup.ID).ExtractErr()
   941  	if err != nil {
   942  		t.Fatalf("Unable to delete server group %s: %v", serverGroup.ID, err)
   943  	}
   944  
   945  	t.Logf("Deleted server group %s", serverGroup.ID)
   946  }
   947  
   948  // DeleteVolumeAttachment will disconnect a volume from an instance. A fatal
   949  // error will occur if the volume failed to detach. This works best when used
   950  // as a deferred function.
   951  func DeleteVolumeAttachment(t *testing.T, client *gophercloud.ServiceClient, blockClient *gophercloud.ServiceClient, server *servers.Server, volumeAttachment *volumeattach.VolumeAttachment) {
   952  
   953  	err := volumeattach.Delete(client, server.ID, volumeAttachment.VolumeID).ExtractErr()
   954  	if err != nil {
   955  		t.Fatalf("Unable to detach volume: %v", err)
   956  	}
   957  
   958  	if err := volumes.WaitForStatus(blockClient, volumeAttachment.ID, "available", 60); err != nil {
   959  		t.Fatalf("Unable to wait for volume: %v", err)
   960  	}
   961  	t.Logf("Deleted volume: %s", volumeAttachment.VolumeID)
   962  }
   963  
   964  // DetachInterface will detach an interface from a server. A fatal
   965  // error will occur if the interface could not be detached. This works best
   966  // when used as a deferred function.
   967  func DetachInterface(t *testing.T, client *gophercloud.ServiceClient, serverID, portID string) {
   968  	t.Logf("Attempting to detach interface %s from server %s", portID, serverID)
   969  
   970  	err := attachinterfaces.Delete(client, serverID, portID).ExtractErr()
   971  	if err != nil {
   972  		t.Fatalf("Unable to detach interface %s from server %s", portID, serverID)
   973  	}
   974  
   975  	t.Logf("Detached interface %s from server %s", portID, serverID)
   976  }
   977  
   978  // DisassociateFloatingIP will disassociate a floating IP from an instance. A
   979  // fatal error will occur if the floating IP failed to disassociate. This works
   980  // best when using it as a deferred function.
   981  func DisassociateFloatingIP(t *testing.T, client *gophercloud.ServiceClient, floatingIP *floatingips.FloatingIP, server *servers.Server) {
   982  	disassociateOpts := floatingips.DisassociateOpts{
   983  		FloatingIP: floatingIP.IP,
   984  	}
   985  
   986  	err := floatingips.DisassociateInstance(client, server.ID, disassociateOpts).ExtractErr()
   987  	if err != nil {
   988  		t.Fatalf("Unable to disassociate floating IP %s from server %s: %v", floatingIP.IP, server.ID, err)
   989  	}
   990  
   991  	t.Logf("Disassociated floating IP %s from server %s", floatingIP.IP, server.ID)
   992  }
   993  
   994  // GetNetworkIDFromOSNetworks will return the network ID from a specified network
   995  // UUID using the os-networks API extension. An error will be returned if the
   996  // network could not be retrieved.
   997  func GetNetworkIDFromOSNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
   998  	allPages, err := networks.List(client).AllPages()
   999  	if err != nil {
  1000  		t.Fatalf("Unable to list networks: %v", err)
  1001  	}
  1002  
  1003  	networkList, err := networks.ExtractNetworks(allPages)
  1004  	if err != nil {
  1005  		t.Fatalf("Unable to list networks: %v", err)
  1006  	}
  1007  
  1008  	networkID := ""
  1009  	for _, network := range networkList {
  1010  		t.Logf("Network: %v", network)
  1011  		if network.Label == networkName {
  1012  			networkID = network.ID
  1013  		}
  1014  	}
  1015  
  1016  	t.Logf("Found network ID for %s: %s", networkName, networkID)
  1017  
  1018  	return networkID, nil
  1019  }
  1020  
  1021  // GetNetworkIDFromTenantNetworks will return the network UUID for a given
  1022  // network name using the os-tenant-networks API extension. An error will be
  1023  // returned if the network could not be retrieved.
  1024  func GetNetworkIDFromTenantNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
  1025  	allPages, err := tenantnetworks.List(client).AllPages()
  1026  	if err != nil {
  1027  		return "", err
  1028  	}
  1029  
  1030  	allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages)
  1031  	if err != nil {
  1032  		return "", err
  1033  	}
  1034  
  1035  	for _, network := range allTenantNetworks {
  1036  		if network.Name == networkName {
  1037  			return network.ID, nil
  1038  		}
  1039  	}
  1040  
  1041  	return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName)
  1042  }
  1043  
  1044  // GetNetworkIDFromNetworks will return the network UUID for a given network
  1045  // name using either the os-tenant-networks API extension or Neutron API.
  1046  // An error will be returned if the network could not be retrieved.
  1047  func GetNetworkIDFromNetworks(t *testing.T, client *gophercloud.ServiceClient, networkName string) (string, error) {
  1048  	allPages, err := tenantnetworks.List(client).AllPages()
  1049  	if err == nil {
  1050  		allTenantNetworks, err := tenantnetworks.ExtractNetworks(allPages)
  1051  		if err != nil {
  1052  			return "", err
  1053  		}
  1054  
  1055  		for _, network := range allTenantNetworks {
  1056  			if network.Name == networkName {
  1057  				return network.ID, nil
  1058  			}
  1059  		}
  1060  	}
  1061  
  1062  	networkClient, err := clients.NewNetworkV2Client()
  1063  	th.AssertNoErr(t, err)
  1064  
  1065  	allPages2, err := neutron.List(networkClient, nil).AllPages()
  1066  	th.AssertNoErr(t, err)
  1067  
  1068  	allNetworks, err := neutron.ExtractNetworks(allPages2)
  1069  	th.AssertNoErr(t, err)
  1070  
  1071  	for _, network := range allNetworks {
  1072  		if network.Name == networkName {
  1073  			return network.ID, nil
  1074  		}
  1075  	}
  1076  
  1077  	return "", fmt.Errorf("Failed to obtain network ID for network %s", networkName)
  1078  }
  1079  
  1080  // ImportPublicKey will create a KeyPair with a random name and a specified
  1081  // public key. An error will be returned if the keypair failed to be created.
  1082  func ImportPublicKey(t *testing.T, client *gophercloud.ServiceClient, publicKey string) (*keypairs.KeyPair, error) {
  1083  	keyPairName := tools.RandomString("keypair_", 5)
  1084  
  1085  	t.Logf("Attempting to create keypair: %s", keyPairName)
  1086  	createOpts := keypairs.CreateOpts{
  1087  		Name:      keyPairName,
  1088  		PublicKey: publicKey,
  1089  	}
  1090  	keyPair, err := keypairs.Create(client, createOpts).Extract()
  1091  	if err != nil {
  1092  		return keyPair, err
  1093  	}
  1094  
  1095  	t.Logf("Created keypair: %s", keyPairName)
  1096  
  1097  	th.AssertEquals(t, keyPair.Name, keyPairName)
  1098  	th.AssertEquals(t, keyPair.PublicKey, publicKey)
  1099  
  1100  	return keyPair, nil
  1101  }
  1102  
  1103  // ResizeServer performs a resize action on an instance. An error will be
  1104  // returned if the instance failed to resize.
  1105  // The new flavor that the instance will be resized to is specified in OS_FLAVOR_ID_RESIZE.
  1106  func ResizeServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error {
  1107  	choices, err := clients.AcceptanceTestChoicesFromEnv()
  1108  	if err != nil {
  1109  		t.Fatal(err)
  1110  	}
  1111  
  1112  	opts := &servers.ResizeOpts{
  1113  		FlavorRef: choices.FlavorIDResize,
  1114  	}
  1115  	if res := servers.Resize(client, server.ID, opts); res.Err != nil {
  1116  		return res.Err
  1117  	}
  1118  
  1119  	if err := WaitForComputeStatus(client, server, "VERIFY_RESIZE"); err != nil {
  1120  		return err
  1121  	}
  1122  
  1123  	return nil
  1124  }
  1125  
  1126  // WaitForComputeStatus will poll an instance's status until it either matches
  1127  // the specified status or the status becomes ERROR.
  1128  func WaitForComputeStatus(client *gophercloud.ServiceClient, server *servers.Server, status string) error {
  1129  	return tools.WaitFor(func() (bool, error) {
  1130  		latest, err := servers.Get(client, server.ID).Extract()
  1131  		if err != nil {
  1132  			return false, err
  1133  		}
  1134  
  1135  		if latest.Status == status {
  1136  			// Success!
  1137  			return true, nil
  1138  		}
  1139  
  1140  		if latest.Status == "ERROR" {
  1141  			return false, fmt.Errorf("Instance in ERROR state")
  1142  		}
  1143  
  1144  		return false, nil
  1145  	})
  1146  }
  1147  
  1148  // Convenience method to fill an QuotaSet-UpdateOpts-struct from a QuotaSet-struct
  1149  func FillUpdateOptsFromQuotaSet(src quotasets.QuotaSet, dest *quotasets.UpdateOpts) {
  1150  	dest.FixedIPs = &src.FixedIPs
  1151  	dest.FloatingIPs = &src.FloatingIPs
  1152  	dest.InjectedFileContentBytes = &src.InjectedFileContentBytes
  1153  	dest.InjectedFilePathBytes = &src.InjectedFilePathBytes
  1154  	dest.InjectedFiles = &src.InjectedFiles
  1155  	dest.KeyPairs = &src.KeyPairs
  1156  	dest.RAM = &src.RAM
  1157  	dest.SecurityGroupRules = &src.SecurityGroupRules
  1158  	dest.SecurityGroups = &src.SecurityGroups
  1159  	dest.Cores = &src.Cores
  1160  	dest.Instances = &src.Instances
  1161  	dest.ServerGroups = &src.ServerGroups
  1162  	dest.ServerGroupMembers = &src.ServerGroupMembers
  1163  	dest.MetadataItems = &src.MetadataItems
  1164  }
  1165  
  1166  // RescueServer will place the specified server into rescue mode.
  1167  func RescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error {
  1168  	t.Logf("Attempting to put server %s into rescue mode", server.ID)
  1169  	_, err := rescueunrescue.Rescue(client, server.ID, rescueunrescue.RescueOpts{}).Extract()
  1170  	if err != nil {
  1171  		return err
  1172  	}
  1173  
  1174  	if err := WaitForComputeStatus(client, server, "RESCUE"); err != nil {
  1175  		return err
  1176  	}
  1177  
  1178  	return nil
  1179  }
  1180  
  1181  // UnrescueServer will return server from rescue mode.
  1182  func UnrescueServer(t *testing.T, client *gophercloud.ServiceClient, server *servers.Server) error {
  1183  	t.Logf("Attempting to return server %s from rescue mode", server.ID)
  1184  	if err := rescueunrescue.Unrescue(client, server.ID).ExtractErr(); err != nil {
  1185  		return err
  1186  	}
  1187  
  1188  	if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
  1189  		return err
  1190  	}
  1191  
  1192  	return nil
  1193  }
  1194  
  1195  // CreateRemoteConsole will create a remote noVNC console for the specified server.
  1196  func CreateRemoteConsole(t *testing.T, client *gophercloud.ServiceClient, serverID string) (*remoteconsoles.RemoteConsole, error) {
  1197  	createOpts := remoteconsoles.CreateOpts{
  1198  		Protocol: remoteconsoles.ConsoleProtocolVNC,
  1199  		Type:     remoteconsoles.ConsoleTypeNoVNC,
  1200  	}
  1201  
  1202  	t.Logf("Attempting to create a %s console for the server %s", createOpts.Type, serverID)
  1203  	remoteConsole, err := remoteconsoles.Create(client, serverID, createOpts).Extract()
  1204  	if err != nil {
  1205  		return nil, err
  1206  	}
  1207  
  1208  	t.Logf("Successfully created console: %s", remoteConsole.URL)
  1209  	return remoteConsole, nil
  1210  }
  1211  
  1212  // CreateNoNetworkServer creates a basic instance with a randomly generated name.
  1213  // The flavor of the instance will be the value of the OS_FLAVOR_ID environment variable.
  1214  // The image will be the value of the OS_IMAGE_ID environment variable.
  1215  // The instance will be launched without network interfaces attached.
  1216  // An error will be returned if the instance was unable to be created.
  1217  func CreateServerNoNetwork(t *testing.T, client *gophercloud.ServiceClient) (*servers.Server, error) {
  1218  	choices, err := clients.AcceptanceTestChoicesFromEnv()
  1219  	if err != nil {
  1220  		t.Fatal(err)
  1221  	}
  1222  
  1223  	name := tools.RandomString("ACPTTEST", 16)
  1224  	t.Logf("Attempting to create server: %s", name)
  1225  
  1226  	pwd := tools.MakeNewPassword("")
  1227  
  1228  	server, err := servers.Create(client, servers.CreateOpts{
  1229  		Name:      name,
  1230  		FlavorRef: choices.FlavorID,
  1231  		ImageRef:  choices.ImageID,
  1232  		AdminPass: pwd,
  1233  		Networks:  "none",
  1234  		Metadata: map[string]string{
  1235  			"abc": "def",
  1236  		},
  1237  		Personality: servers.Personality{
  1238  			&servers.File{
  1239  				Path:     "/etc/test",
  1240  				Contents: []byte("hello world"),
  1241  			},
  1242  		},
  1243  	}).Extract()
  1244  	if err != nil {
  1245  		return server, err
  1246  	}
  1247  
  1248  	if err := WaitForComputeStatus(client, server, "ACTIVE"); err != nil {
  1249  		return nil, err
  1250  	}
  1251  
  1252  	newServer, err := servers.Get(client, server.ID).Extract()
  1253  	if err != nil {
  1254  		return nil, err
  1255  	}
  1256  
  1257  	th.AssertEquals(t, newServer.Name, name)
  1258  	th.AssertEquals(t, newServer.Flavor["id"], choices.FlavorID)
  1259  	th.AssertEquals(t, newServer.Image["id"], choices.ImageID)
  1260  
  1261  	return newServer, nil
  1262  }