github.com/gophercloud/gophercloud@v1.11.0/internal/acceptance/openstack/blockstorage/v3/blockstorage.go (about)

     1  // Package v3 contains common functions for creating block storage based
     2  // resources for use in acceptance tests. See the `*_test.go` files for
     3  // example usages.
     4  package v3
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/gophercloud/gophercloud"
    10  	"github.com/gophercloud/gophercloud/internal/acceptance/tools"
    11  	"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/qos"
    12  	"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/snapshots"
    13  	"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
    14  	"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumetypes"
    15  	th "github.com/gophercloud/gophercloud/testhelper"
    16  )
    17  
    18  // CreateSnapshot will create a snapshot of the specified volume.
    19  // Snapshot will be assigned a random name and description.
    20  func CreateSnapshot(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) (*snapshots.Snapshot, error) {
    21  	snapshotName := tools.RandomString("ACPTTEST", 16)
    22  	snapshotDescription := tools.RandomString("ACPTTEST", 16)
    23  	t.Logf("Attempting to create snapshot: %s", snapshotName)
    24  
    25  	createOpts := snapshots.CreateOpts{
    26  		VolumeID:    volume.ID,
    27  		Name:        snapshotName,
    28  		Description: snapshotDescription,
    29  	}
    30  
    31  	snapshot, err := snapshots.Create(client, createOpts).Extract()
    32  	if err != nil {
    33  		return snapshot, err
    34  	}
    35  
    36  	err = snapshots.WaitForStatus(client, snapshot.ID, "available", 60)
    37  	if err != nil {
    38  		return snapshot, err
    39  	}
    40  
    41  	snapshot, err = snapshots.Get(client, snapshot.ID).Extract()
    42  	if err != nil {
    43  		return snapshot, err
    44  	}
    45  
    46  	tools.PrintResource(t, snapshot)
    47  	th.AssertEquals(t, snapshot.Name, snapshotName)
    48  	th.AssertEquals(t, snapshot.VolumeID, volume.ID)
    49  
    50  	t.Logf("Successfully created snapshot: %s", snapshot.ID)
    51  
    52  	return snapshot, nil
    53  }
    54  
    55  // CreateVolume will create a volume with a random name and size of 1GB. An
    56  // error will be returned if the volume was unable to be created.
    57  func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) {
    58  	volumeName := tools.RandomString("ACPTTEST", 16)
    59  	volumeDescription := tools.RandomString("ACPTTEST-DESC", 16)
    60  	t.Logf("Attempting to create volume: %s", volumeName)
    61  
    62  	createOpts := volumes.CreateOpts{
    63  		Size:        1,
    64  		Name:        volumeName,
    65  		Description: volumeDescription,
    66  	}
    67  
    68  	volume, err := volumes.Create(client, createOpts).Extract()
    69  	if err != nil {
    70  		return volume, err
    71  	}
    72  
    73  	err = volumes.WaitForStatus(client, volume.ID, "available", 60)
    74  	if err != nil {
    75  		return volume, err
    76  	}
    77  
    78  	volume, err = volumes.Get(client, volume.ID).Extract()
    79  	if err != nil {
    80  		return volume, err
    81  	}
    82  
    83  	tools.PrintResource(t, volume)
    84  	th.AssertEquals(t, volume.Name, volumeName)
    85  	th.AssertEquals(t, volume.Description, volumeDescription)
    86  	th.AssertEquals(t, volume.Size, 1)
    87  
    88  	t.Logf("Successfully created volume: %s", volume.ID)
    89  
    90  	return volume, nil
    91  }
    92  
    93  // CreateVolumeWithType will create a volume of the given volume type
    94  // with a random name and size of 1GB. An error will be returned if
    95  // the volume was unable to be created.
    96  func CreateVolumeWithType(t *testing.T, client *gophercloud.ServiceClient, vt *volumetypes.VolumeType) (*volumes.Volume, error) {
    97  	volumeName := tools.RandomString("ACPTTEST", 16)
    98  	volumeDescription := tools.RandomString("ACPTTEST-DESC", 16)
    99  	t.Logf("Attempting to create volume: %s", volumeName)
   100  
   101  	createOpts := volumes.CreateOpts{
   102  		Size:        1,
   103  		Name:        volumeName,
   104  		Description: volumeDescription,
   105  		VolumeType:  vt.Name,
   106  	}
   107  
   108  	volume, err := volumes.Create(client, createOpts).Extract()
   109  	if err != nil {
   110  		return volume, err
   111  	}
   112  
   113  	err = volumes.WaitForStatus(client, volume.ID, "available", 60)
   114  	if err != nil {
   115  		return volume, err
   116  	}
   117  
   118  	volume, err = volumes.Get(client, volume.ID).Extract()
   119  	if err != nil {
   120  		return volume, err
   121  	}
   122  
   123  	tools.PrintResource(t, volume)
   124  	th.AssertEquals(t, volume.Name, volumeName)
   125  	th.AssertEquals(t, volume.Description, volumeDescription)
   126  	th.AssertEquals(t, volume.Size, 1)
   127  	th.AssertEquals(t, volume.VolumeType, vt.Name)
   128  
   129  	t.Logf("Successfully created volume: %s", volume.ID)
   130  
   131  	return volume, nil
   132  }
   133  
   134  // CreateVolumeType will create a volume type with a random name. An
   135  // error will be returned if the volume was unable to be created.
   136  func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) {
   137  	name := tools.RandomString("ACPTTEST", 16)
   138  	description := "create_from_gophercloud"
   139  	t.Logf("Attempting to create volume type: %s", name)
   140  
   141  	createOpts := volumetypes.CreateOpts{
   142  		Name:        name,
   143  		ExtraSpecs:  map[string]string{"volume_backend_name": "fake_backend_name"},
   144  		Description: description,
   145  	}
   146  
   147  	vt, err := volumetypes.Create(client, createOpts).Extract()
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	tools.PrintResource(t, vt)
   153  	th.AssertEquals(t, vt.IsPublic, true)
   154  	th.AssertEquals(t, vt.Name, name)
   155  	th.AssertEquals(t, vt.Description, description)
   156  	// TODO: For some reason returned extra_specs are empty even in API reference: https://developer.openstack.org/api-ref/block-storage/v3/?expanded=create-a-volume-type-detail#volume-types-types
   157  	// "extra_specs": {}
   158  	// th.AssertEquals(t, vt.ExtraSpecs, createOpts.ExtraSpecs)
   159  
   160  	t.Logf("Successfully created volume type: %s", vt.ID)
   161  
   162  	return vt, nil
   163  }
   164  
   165  // CreateVolumeTypeNoExtraSpecs will create a volume type with a random name and
   166  // no extra specs. This is required to bypass cinder-scheduler filters and be able
   167  // to create a volume with this volumeType. An error will be returned if the volume
   168  // type was unable to be created.
   169  func CreateVolumeTypeNoExtraSpecs(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) {
   170  	name := tools.RandomString("ACPTTEST", 16)
   171  	description := "create_from_gophercloud"
   172  	t.Logf("Attempting to create volume type: %s", name)
   173  
   174  	createOpts := volumetypes.CreateOpts{
   175  		Name:        name,
   176  		ExtraSpecs:  map[string]string{},
   177  		Description: description,
   178  	}
   179  
   180  	vt, err := volumetypes.Create(client, createOpts).Extract()
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	tools.PrintResource(t, vt)
   186  	th.AssertEquals(t, vt.IsPublic, true)
   187  	th.AssertEquals(t, vt.Name, name)
   188  	th.AssertEquals(t, vt.Description, description)
   189  
   190  	t.Logf("Successfully created volume type: %s", vt.ID)
   191  
   192  	return vt, nil
   193  }
   194  
   195  // CreateVolumeTypeMultiAttach will create a volume type with a random name and
   196  // extra specs for multi-attach. An error will be returned if the volume type was
   197  // unable to be created.
   198  func CreateVolumeTypeMultiAttach(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) {
   199  	name := tools.RandomString("ACPTTEST", 16)
   200  	description := "create_from_gophercloud"
   201  	t.Logf("Attempting to create volume type: %s", name)
   202  
   203  	createOpts := volumetypes.CreateOpts{
   204  		Name:        name,
   205  		ExtraSpecs:  map[string]string{"multiattach": "<is> True"},
   206  		Description: description,
   207  	}
   208  
   209  	vt, err := volumetypes.Create(client, createOpts).Extract()
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	tools.PrintResource(t, vt)
   215  	th.AssertEquals(t, vt.IsPublic, true)
   216  	th.AssertEquals(t, vt.Name, name)
   217  	th.AssertEquals(t, vt.Description, description)
   218  	th.AssertEquals(t, vt.ExtraSpecs["multiattach"], "<is> True")
   219  
   220  	t.Logf("Successfully created volume type: %s", vt.ID)
   221  
   222  	return vt, nil
   223  }
   224  
   225  // CreatePrivateVolumeType will create a private volume type with a random
   226  // name and no extra specs. An error will be returned if the volume type was
   227  // unable to be created.
   228  func CreatePrivateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) {
   229  	name := tools.RandomString("ACPTTEST", 16)
   230  	description := "create_from_gophercloud"
   231  	isPublic := false
   232  	t.Logf("Attempting to create volume type: %s", name)
   233  
   234  	createOpts := volumetypes.CreateOpts{
   235  		Name:        name,
   236  		ExtraSpecs:  map[string]string{},
   237  		Description: description,
   238  		IsPublic:    &isPublic,
   239  	}
   240  
   241  	vt, err := volumetypes.Create(client, createOpts).Extract()
   242  	if err != nil {
   243  		return nil, err
   244  	}
   245  
   246  	tools.PrintResource(t, vt)
   247  	th.AssertEquals(t, vt.IsPublic, false)
   248  	th.AssertEquals(t, vt.Name, name)
   249  	th.AssertEquals(t, vt.Description, description)
   250  
   251  	t.Logf("Successfully created volume type: %s", vt.ID)
   252  
   253  	return vt, nil
   254  }
   255  
   256  // DeleteSnapshot will delete a snapshot. A fatal error will occur if the
   257  // snapshot failed to be deleted.
   258  func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) {
   259  	err := snapshots.Delete(client, snapshot.ID).ExtractErr()
   260  	if err != nil {
   261  		if _, ok := err.(gophercloud.ErrDefault404); ok {
   262  			return
   263  		}
   264  		t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err)
   265  	}
   266  
   267  	// Volumes can't be deleted until their snapshots have been,
   268  	// so block until the snapshoth as been deleted.
   269  	err = tools.WaitFor(func() (bool, error) {
   270  		_, err := snapshots.Get(client, snapshot.ID).Extract()
   271  		if err != nil {
   272  			return true, nil
   273  		}
   274  
   275  		return false, nil
   276  	})
   277  	if err != nil {
   278  		t.Fatalf("Error waiting for snapshot to delete: %v", err)
   279  	}
   280  
   281  	t.Logf("Deleted snapshot: %s", snapshot.ID)
   282  }
   283  
   284  // DeleteVolume will delete a volume. A fatal error will occur if the volume
   285  // failed to be deleted. This works best when used as a deferred function.
   286  func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) {
   287  	t.Logf("Attempting to delete volume: %s", volume.ID)
   288  
   289  	err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr()
   290  	if err != nil {
   291  		if _, ok := err.(gophercloud.ErrDefault404); ok {
   292  			return
   293  		}
   294  		t.Fatalf("Unable to delete volume %s: %v", volume.ID, err)
   295  	}
   296  
   297  	// VolumeTypes can't be deleted until their volumes have been,
   298  	// so block until the volume is deleted.
   299  	err = tools.WaitFor(func() (bool, error) {
   300  		_, err := volumes.Get(client, volume.ID).Extract()
   301  		if err != nil {
   302  			return true, nil
   303  		}
   304  
   305  		return false, nil
   306  	})
   307  	if err != nil {
   308  		t.Fatalf("Error waiting for volume to delete: %v", err)
   309  	}
   310  
   311  	t.Logf("Successfully deleted volume: %s", volume.ID)
   312  }
   313  
   314  // DeleteVolumeType will delete a volume type. A fatal error will occur if the
   315  // volume type failed to be deleted. This works best when used as a deferred
   316  // function.
   317  func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, vt *volumetypes.VolumeType) {
   318  	t.Logf("Attempting to delete volume type: %s", vt.ID)
   319  
   320  	err := volumetypes.Delete(client, vt.ID).ExtractErr()
   321  	if err != nil {
   322  		t.Fatalf("Unable to delete volume type %s: %v", vt.ID, err)
   323  	}
   324  
   325  	t.Logf("Successfully deleted volume type: %s", vt.ID)
   326  }
   327  
   328  // CreateQoS will create a QoS with one spec and a random name. An
   329  // error will be returned if the volume was unable to be created.
   330  func CreateQoS(t *testing.T, client *gophercloud.ServiceClient) (*qos.QoS, error) {
   331  	name := tools.RandomString("ACPTTEST", 16)
   332  	t.Logf("Attempting to create QoS: %s", name)
   333  
   334  	createOpts := qos.CreateOpts{
   335  		Name:     name,
   336  		Consumer: qos.ConsumerFront,
   337  		Specs: map[string]string{
   338  			"read_iops_sec": "20000",
   339  		},
   340  	}
   341  
   342  	qs, err := qos.Create(client, createOpts).Extract()
   343  	if err != nil {
   344  		return nil, err
   345  	}
   346  
   347  	tools.PrintResource(t, qs)
   348  	th.AssertEquals(t, qs.Consumer, "front-end")
   349  	th.AssertEquals(t, qs.Name, name)
   350  	th.AssertDeepEquals(t, qs.Specs, createOpts.Specs)
   351  
   352  	t.Logf("Successfully created QoS: %s", qs.ID)
   353  
   354  	return qs, nil
   355  }
   356  
   357  // DeleteQoS will delete a QoS. A fatal error will occur if the QoS
   358  // failed to be deleted. This works best when used as a deferred function.
   359  func DeleteQoS(t *testing.T, client *gophercloud.ServiceClient, qs *qos.QoS) {
   360  	t.Logf("Attempting to delete QoS: %s", qs.ID)
   361  
   362  	deleteOpts := qos.DeleteOpts{
   363  		Force: true,
   364  	}
   365  
   366  	err := qos.Delete(client, qs.ID, deleteOpts).ExtractErr()
   367  	if err != nil {
   368  		t.Fatalf("Unable to delete QoS %s: %v", qs.ID, err)
   369  	}
   370  
   371  	t.Logf("Successfully deleted QoS: %s", qs.ID)
   372  }