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