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 }