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 }