github.com/leeclow-ops/gophercloud@v1.2.1/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/leeclow-ops/gophercloud"
    10  	"github.com/leeclow-ops/gophercloud/acceptance/tools"
    11  	"github.com/leeclow-ops/gophercloud/openstack/blockstorage/v3/qos"
    12  	"github.com/leeclow-ops/gophercloud/openstack/blockstorage/v3/snapshots"
    13  	"github.com/leeclow-ops/gophercloud/openstack/blockstorage/v3/volumes"
    14  	"github.com/leeclow-ops/gophercloud/openstack/blockstorage/v3/volumetypes"
    15  	th "github.com/leeclow-ops/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  	tools.PrintResource(t, snapshot)
    42  	th.AssertEquals(t, snapshot.Name, snapshotName)
    43  	th.AssertEquals(t, snapshot.VolumeID, volume.ID)
    44  
    45  	t.Logf("Successfully created snapshot: %s", snapshot.ID)
    46  
    47  	return snapshot, nil
    48  }
    49  
    50  // CreateVolume will create a volume with a random name and size of 1GB. An
    51  // error will be returned if the volume was unable to be created.
    52  func CreateVolume(t *testing.T, client *gophercloud.ServiceClient) (*volumes.Volume, error) {
    53  	volumeName := tools.RandomString("ACPTTEST", 16)
    54  	volumeDescription := tools.RandomString("ACPTTEST-DESC", 16)
    55  	t.Logf("Attempting to create volume: %s", volumeName)
    56  
    57  	createOpts := volumes.CreateOpts{
    58  		Size:        1,
    59  		Name:        volumeName,
    60  		Description: volumeDescription,
    61  	}
    62  
    63  	volume, err := volumes.Create(client, createOpts).Extract()
    64  	if err != nil {
    65  		return volume, err
    66  	}
    67  
    68  	err = volumes.WaitForStatus(client, volume.ID, "available", 60)
    69  	if err != nil {
    70  		return volume, err
    71  	}
    72  
    73  	tools.PrintResource(t, volume)
    74  	th.AssertEquals(t, volume.Name, volumeName)
    75  	th.AssertEquals(t, volume.Description, volumeDescription)
    76  	th.AssertEquals(t, volume.Size, 1)
    77  
    78  	t.Logf("Successfully created volume: %s", volume.ID)
    79  
    80  	return volume, nil
    81  }
    82  
    83  // CreateVolumeWithType will create a volume of the given volume type
    84  // with a random name and size of 1GB. An error will be returned if
    85  // the volume was unable to be created.
    86  func CreateVolumeWithType(t *testing.T, client *gophercloud.ServiceClient, vt *volumetypes.VolumeType) (*volumes.Volume, error) {
    87  	volumeName := tools.RandomString("ACPTTEST", 16)
    88  	volumeDescription := tools.RandomString("ACPTTEST-DESC", 16)
    89  	t.Logf("Attempting to create volume: %s", volumeName)
    90  
    91  	createOpts := volumes.CreateOpts{
    92  		Size:        1,
    93  		Name:        volumeName,
    94  		Description: volumeDescription,
    95  		VolumeType:  vt.Name,
    96  	}
    97  
    98  	volume, err := volumes.Create(client, createOpts).Extract()
    99  	if err != nil {
   100  		return volume, err
   101  	}
   102  
   103  	err = volumes.WaitForStatus(client, volume.ID, "available", 60)
   104  	if err != nil {
   105  		return volume, err
   106  	}
   107  
   108  	tools.PrintResource(t, volume)
   109  	th.AssertEquals(t, volume.Name, volumeName)
   110  	th.AssertEquals(t, volume.Description, volumeDescription)
   111  	th.AssertEquals(t, volume.Size, 1)
   112  	th.AssertEquals(t, volume.VolumeType, vt.Name)
   113  
   114  	t.Logf("Successfully created volume: %s", volume.ID)
   115  
   116  	return volume, nil
   117  }
   118  
   119  // CreateVolumeType will create a volume type with a random name. An
   120  // error will be returned if the volume was unable to be created.
   121  func CreateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) {
   122  	name := tools.RandomString("ACPTTEST", 16)
   123  	description := "create_from_gophercloud"
   124  	t.Logf("Attempting to create volume type: %s", name)
   125  
   126  	createOpts := volumetypes.CreateOpts{
   127  		Name:        name,
   128  		ExtraSpecs:  map[string]string{"volume_backend_name": "fake_backend_name"},
   129  		Description: description,
   130  	}
   131  
   132  	vt, err := volumetypes.Create(client, createOpts).Extract()
   133  	if err != nil {
   134  		return nil, err
   135  	}
   136  
   137  	tools.PrintResource(t, vt)
   138  	th.AssertEquals(t, vt.IsPublic, true)
   139  	th.AssertEquals(t, vt.Name, name)
   140  	th.AssertEquals(t, vt.Description, description)
   141  	// 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
   142  	// "extra_specs": {}
   143  	// th.AssertEquals(t, vt.ExtraSpecs, createOpts.ExtraSpecs)
   144  
   145  	t.Logf("Successfully created volume type: %s", vt.ID)
   146  
   147  	return vt, nil
   148  }
   149  
   150  // CreateVolumeTypeNoExtraSpecs will create a volume type with a random name and
   151  // no extra specs. This is required to bypass cinder-scheduler filters and be able
   152  // to create a volume with this volumeType. An error will be returned if the volume
   153  // type was unable to be created.
   154  func CreateVolumeTypeNoExtraSpecs(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) {
   155  	name := tools.RandomString("ACPTTEST", 16)
   156  	description := "create_from_gophercloud"
   157  	t.Logf("Attempting to create volume type: %s", name)
   158  
   159  	createOpts := volumetypes.CreateOpts{
   160  		Name:        name,
   161  		ExtraSpecs:  map[string]string{},
   162  		Description: description,
   163  	}
   164  
   165  	vt, err := volumetypes.Create(client, createOpts).Extract()
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	tools.PrintResource(t, vt)
   171  	th.AssertEquals(t, vt.IsPublic, true)
   172  	th.AssertEquals(t, vt.Name, name)
   173  	th.AssertEquals(t, vt.Description, description)
   174  
   175  	t.Logf("Successfully created volume type: %s", vt.ID)
   176  
   177  	return vt, nil
   178  }
   179  
   180  // CreatePrivateVolumeType will create a private volume type with a random
   181  // name and no extra specs. An error will be returned if the volume type was
   182  // unable to be created.
   183  func CreatePrivateVolumeType(t *testing.T, client *gophercloud.ServiceClient) (*volumetypes.VolumeType, error) {
   184  	name := tools.RandomString("ACPTTEST", 16)
   185  	description := "create_from_gophercloud"
   186  	isPublic := false
   187  	t.Logf("Attempting to create volume type: %s", name)
   188  
   189  	createOpts := volumetypes.CreateOpts{
   190  		Name:        name,
   191  		ExtraSpecs:  map[string]string{},
   192  		Description: description,
   193  		IsPublic:    &isPublic,
   194  	}
   195  
   196  	vt, err := volumetypes.Create(client, createOpts).Extract()
   197  	if err != nil {
   198  		return nil, err
   199  	}
   200  
   201  	tools.PrintResource(t, vt)
   202  	th.AssertEquals(t, vt.IsPublic, false)
   203  	th.AssertEquals(t, vt.Name, name)
   204  	th.AssertEquals(t, vt.Description, description)
   205  
   206  	t.Logf("Successfully created volume type: %s", vt.ID)
   207  
   208  	return vt, nil
   209  }
   210  
   211  // DeleteSnapshot will delete a snapshot. A fatal error will occur if the
   212  // snapshot failed to be deleted.
   213  func DeleteSnapshot(t *testing.T, client *gophercloud.ServiceClient, snapshot *snapshots.Snapshot) {
   214  	err := snapshots.Delete(client, snapshot.ID).ExtractErr()
   215  	if err != nil {
   216  		t.Fatalf("Unable to delete snapshot %s: %+v", snapshot.ID, err)
   217  	}
   218  
   219  	// Volumes can't be deleted until their snapshots have been,
   220  	// so block until the snapshoth as been deleted.
   221  	err = tools.WaitFor(func() (bool, error) {
   222  		_, err := snapshots.Get(client, snapshot.ID).Extract()
   223  		if err != nil {
   224  			return true, nil
   225  		}
   226  
   227  		return false, nil
   228  	})
   229  	if err != nil {
   230  		t.Fatalf("Error waiting for snapshot to delete: %v", err)
   231  	}
   232  
   233  	t.Logf("Deleted snapshot: %s", snapshot.ID)
   234  }
   235  
   236  // DeleteVolume will delete a volume. A fatal error will occur if the volume
   237  // failed to be deleted. This works best when used as a deferred function.
   238  func DeleteVolume(t *testing.T, client *gophercloud.ServiceClient, volume *volumes.Volume) {
   239  	t.Logf("Attempting to delete volume: %s", volume.ID)
   240  
   241  	err := volumes.Delete(client, volume.ID, volumes.DeleteOpts{}).ExtractErr()
   242  	if err != nil {
   243  		t.Fatalf("Unable to delete volume %s: %v", volume.ID, err)
   244  	}
   245  
   246  	// VolumeTypes can't be deleted until their volumes have been,
   247  	// so block until the volume is deleted.
   248  	err = tools.WaitFor(func() (bool, error) {
   249  		_, err := volumes.Get(client, volume.ID).Extract()
   250  		if err != nil {
   251  			return true, nil
   252  		}
   253  
   254  		return false, nil
   255  	})
   256  	if err != nil {
   257  		t.Fatalf("Error waiting for volume to delete: %v", err)
   258  	}
   259  
   260  	t.Logf("Successfully deleted volume: %s", volume.ID)
   261  }
   262  
   263  // DeleteVolumeType will delete a volume type. A fatal error will occur if the
   264  // volume type failed to be deleted. This works best when used as a deferred
   265  // function.
   266  func DeleteVolumeType(t *testing.T, client *gophercloud.ServiceClient, vt *volumetypes.VolumeType) {
   267  	t.Logf("Attempting to delete volume type: %s", vt.ID)
   268  
   269  	err := volumetypes.Delete(client, vt.ID).ExtractErr()
   270  	if err != nil {
   271  		t.Fatalf("Unable to delete volume %s: %v", vt.ID, err)
   272  	}
   273  
   274  	t.Logf("Successfully deleted volume type: %s", vt.ID)
   275  }
   276  
   277  // CreateQoS will create a QoS with one spec and a random name. An
   278  // error will be returned if the volume was unable to be created.
   279  func CreateQoS(t *testing.T, client *gophercloud.ServiceClient) (*qos.QoS, error) {
   280  	name := tools.RandomString("ACPTTEST", 16)
   281  	t.Logf("Attempting to create QoS: %s", name)
   282  
   283  	createOpts := qos.CreateOpts{
   284  		Name:     name,
   285  		Consumer: qos.ConsumerFront,
   286  		Specs: map[string]string{
   287  			"read_iops_sec": "20000",
   288  		},
   289  	}
   290  
   291  	qs, err := qos.Create(client, createOpts).Extract()
   292  	if err != nil {
   293  		return nil, err
   294  	}
   295  
   296  	tools.PrintResource(t, qs)
   297  	th.AssertEquals(t, qs.Consumer, "front-end")
   298  	th.AssertEquals(t, qs.Name, name)
   299  	th.AssertDeepEquals(t, qs.Specs, createOpts.Specs)
   300  
   301  	t.Logf("Successfully created QoS: %s", qs.ID)
   302  
   303  	return qs, nil
   304  }
   305  
   306  // DeleteQoS will delete a QoS. A fatal error will occur if the QoS
   307  // failed to be deleted. This works best when used as a deferred function.
   308  func DeleteQoS(t *testing.T, client *gophercloud.ServiceClient, qs *qos.QoS) {
   309  	t.Logf("Attempting to delete QoS: %s", qs.ID)
   310  
   311  	deleteOpts := qos.DeleteOpts{
   312  		Force: true,
   313  	}
   314  
   315  	err := qos.Delete(client, qs.ID, deleteOpts).ExtractErr()
   316  	if err != nil {
   317  		t.Fatalf("Unable to delete QoS %s: %v", qs.ID, err)
   318  	}
   319  
   320  	t.Logf("Successfully deleted QoS: %s", qs.ID)
   321  }