1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
|
require 'spec_helper'
require_relative './shared_context'
[
Fog::OpenStack::Volume,
Fog::OpenStack::Volume::V1,
Fog::OpenStack::Volume::V2
].delete_if { |the_class| ENV['TEST_CLASS'] && ENV['TEST_CLASS'] != the_class.name }.each do |service_class|
describe service_class do
before :all do
vcr_directory = 'spec/fixtures/openstack/volume' if service_class == Fog::OpenStack::Volume
vcr_directory = 'spec/fixtures/openstack/volume_v1' if service_class == Fog::OpenStack::Volume::V1
vcr_directory = 'spec/fixtures/openstack/volume_v2' if service_class == Fog::OpenStack::Volume::V2
openstack_vcr = OpenStackVCR.new(
:vcr_directory => vcr_directory,
:service_class => service_class
)
@service = openstack_vcr.service
@os_auth_url = openstack_vcr.os_auth_url
# Account for the different parameter naming between v1 and v2 services
@name_param = :display_name unless v2?
@name_param = :name if v2?
@description_param = :display_description unless v2?
@description_param = :description if v2?
end
def v2?
@service.kind_of? Fog::OpenStack::Volume::V2::Real
end
def setup_test_object(options)
type = options.delete(:type)
case type
when :volume
puts "Checking for leftovers..." if ENV['DEBUG_VERBOSE']
volume_name = options[@name_param]
# if this fails, cleanup this object (it was left over from a failed test run)
@service.volumes.all(@name_param => volume_name).length.must_equal 0
puts "Creating volume #{volume_name}..." if ENV['DEBUG_VERBOSE']
return @service.volumes.create(options)
when :transfer
puts "Checking for leftovers..." if ENV['DEBUG_VERBOSE']
transfer_name = options[:name]
# if this fails, cleanup this object (it was left over from a failed test run)
@service.transfers.all(:name => transfer_name).length.must_equal 0
puts "Creating transfer #{transfer_name}..." if ENV['DEBUG_VERBOSE']
return @service.transfers.create(options)
when :snapshot
puts "Checking for leftovers..." if ENV['DEBUG_VERBOSE']
snapshot_name = options[@name_param]
# if this fails, cleanup this object (it was left over from a failed test run)
@service.snapshots.all(@name_param => snapshot_name).length.must_equal 0
puts "Creating snapshot #{snapshot_name}..." if ENV['DEBUG_VERBOSE']
return @service.snapshots.create(options)
else
raise ArgumentError, "don't know how to setup a test object of type #{type.inspect}"
end
end
def cleanup_test_object(collection, id)
# wait for the object to be deletable
Fog.wait_for do
begin
object = collection.get(id)
puts "Current status: #{object ? object.status : 'deleted'}" if ENV['DEBUG_VERBOSE']
object.nil? || (%w[available error].include? object.status.downcase)
end
end
object = collection.get(id)
if object && object.status.casecmp('awaiting-transfer').zero?
object.reset_status 'available'
end
puts "Deleting object #{object.class} #{id}..." if ENV['DEBUG_VERBOSE']
object.destroy if object
# wait for the object to be deleted
Fog.wait_for do
begin
object = collection.get(id)
puts "Current status: #{object ? object.status : 'deleted'}" if ENV['DEBUG_VERBOSE']
object.nil?
end
end
end
it 'CRUD volumes' do
VCR.use_cassette('volume_crud') do
begin
volume_name = "fog-testvolume-1"
volume_description = 'This is the volume description.'
volume_new_description = 'This is the updated volume description.'
volume_new_name = "fog-updated-testvolume-1"
volume_size = 1 # in GB
# create volume
volume_id = setup_test_object(:type => :volume,
@name_param => volume_name,
@description_param => volume_description,
:size => volume_size).id
@service.volumes.all(@name_param => volume_name).length.must_equal 1
# check retrieval of volume by ID
puts "Retrieving volume by ID..." if ENV['DEBUG_VERBOSE']
volume = @service.volumes.get(volume_id)
volume.must_be_kind_of Fog::OpenStack::Volume::Volume
volume.id.must_equal volume_id
volume.display_name.must_equal volume_name unless v2?
volume.name.must_equal volume_name if v2?
volume.display_description.must_equal volume_description unless v2?
volume.description.must_equal volume_description if v2?
volume.size.must_equal volume_size
puts "Waiting for volume to be available..." if ENV['DEBUG_VERBOSE']
volume.wait_for { ready? }
# check retrieval of volume by name
puts "Retrieving volume by name..." if ENV['DEBUG_VERBOSE']
volumes = @service.volumes.all(@name_param => volume_name)
volumes.length.must_equal 1
volume = volumes[0]
volume.must_be_kind_of Fog::OpenStack::Volume::Volume
volume.id.must_equal volume_id
volume.display_name.must_equal volume_name unless v2?
volume.name.must_equal volume_name if v2?
volume.display_description.must_equal volume_description unless v2?
volume.description.must_equal volume_description if v2?
volume.size.must_equal volume_size
# Update the volume's name
volume.update(@name_param => volume_new_name)
volumes = @service.volumes.all(@name_param => volume_new_name)
volume = volumes.first
volume.must_be_kind_of Fog::OpenStack::Volume::Volume
volume.display_name.must_equal volume_new_name unless v2?
volume.name.must_equal volume_new_name if v2?
# Check that save does an update
volume.description = volume_new_description if v2?
volume.display_description = volume_new_description unless v2?
volume.save
volume = @service.volumes.get(volume_id)
volume.display_description.must_equal volume_new_description unless v2?
volume.description.must_equal volume_new_description if v2?
ensure
# delete volume
cleanup_test_object(@service.volumes, volume_id)
end
end
end
it 'reads volume types' do
VCR.use_cassette('volume_type_read') do
# list all volume types
puts "Listing volume types..." if ENV['DEBUG_VERBOSE']
types = @service.volume_types.all
types.length.must_be :>, 0
types.each do |type|
type.name.must_be_kind_of String
end
type_id = types[0].id
type_name = types[0].name
# get a single volume type by ID
puts "Retrieving volume type by ID..." if ENV['DEBUG_VERBOSE']
type = @service.volume_types.get(type_id)
type.must_be_kind_of Fog::OpenStack::Volume::VolumeType
type.id.must_equal type_id
type.name.must_equal type_name
# get a single volume type by name
puts "Retrieving volume type by name..." if ENV['DEBUG_VERBOSE']
type = @service.volume_types.all(type_name).first
type.must_be_kind_of Fog::OpenStack::Volume::VolumeType
type.id.must_equal type_id
type.name.must_equal type_name
end
end
it 'can extend volumes' do
VCR.use_cassette('volume_extend') do
begin
volume_size_small = 1 # in GB
volume_size_large = 2 # in GB
volume = setup_test_object(:type => :volume,
@name_param => 'fog-testvolume-1',
:size => volume_size_small)
volume.wait_for { ready? && size == volume_size_small }
# extend volume
puts "Extending volume..." if ENV['DEBUG_VERBOSE']
volume.extend(volume_size_large)
volume.wait_for do
status == 'error_extending' || (ready? && size == volume_size_large)
end
volume.status.wont_equal 'error_extending'
# shrinking is not allowed in OpenStack
puts "Shrinking volume should fail..." if ENV['DEBUG_VERBOSE']
proc do
volume.extend(volume_size_small)
end.must_raise(Excon::Errors::BadRequest,
/Invalid input received: New size for extend must be greater than current size./)
ensure
# delete volume
cleanup_test_object(@service.volumes, volume.nil? ? nil : volume.id)
# check that extending a non-existing volume fails
puts "Extending deleted volume should fail..." if ENV['DEBUG_VERBOSE']
proc { @service.extend_volume(volume.id, volume_size_small) }.must_raise Fog::OpenStack::Volume::NotFound
end
end
end
it 'can create and accept volume transfers' do
VCR.use_cassette('volume_transfer_and_accept') do
begin
transfer_name = 'fog-testtransfer-1'
# create volume object
volume = setup_test_object(:type => :volume,
@name_param => 'fog-testvolume-1',
:size => 1)
volume.wait_for { ready? }
# create transfer object
transfer = setup_test_object(:type => :transfer,
:name => transfer_name,
:volume_id => volume.id)
# we need to save the auth_key NOW, it's only present in the response
# from the create_transfer request
auth_key = transfer.auth_key
transfer_id = transfer.id
# check retrieval of transfer by ID
puts 'Retrieving transfer by ID...' if ENV['DEBUG_VERBOSE']
transfer = @service.transfers.get(transfer_id)
transfer.must_be_kind_of Fog::OpenStack::Volume::Transfer
transfer.id.must_equal transfer_id
transfer.name.must_equal transfer_name
transfer.volume_id.must_equal volume.id
# check retrieval of transfer by name
puts 'Retrieving transfer by name...' if ENV['DEBUG_VERBOSE']
transfers = @service.transfers.all(:name => transfer_name)
transfers.length.must_equal 1
transfer = transfers[0]
transfer.must_be_kind_of Fog::OpenStack::Volume::Transfer
transfer.id.must_equal transfer_id
transfer.name.must_equal transfer_name
transfer.volume_id.must_equal volume.id
# to accept the transfer, we need a second connection to a different project
puts 'Checking object visibility from different projects...' if ENV['DEBUG_VERBOSE']
other_service = service_class.new(
:openstack_auth_url => @os_auth_url,
:openstack_region => ENV['OS_REGION_NAME'] || 'RegionOne',
:openstack_api_key => ENV['OS_PASSWORD_OTHER'] || 'password',
:openstack_username => ENV['OS_USERNAME_OTHER'] || 'demo',
:openstack_domain_name => ENV['OS_USER_DOMAIN_NAME'] || 'Default',
:openstack_project_name => ENV['OS_PROJECT_NAME_OTHER'] || 'demo'
)
# check that recipient cannot see the transfer object
assert_nil other_service.transfers.get(transfer.id)
other_service.transfers.all(:name => transfer_name).length.must_equal 0
# # check that recipient cannot see the volume before transfer
# proc { other_service.volumes.get(volume.id) }.must_raise Fog::OpenStack::Compute::NotFound
# other_service.volumes.all(@name_param => volume_name).length.must_equal 0
# The recipient can inexplicably see the volume even before the
# transfer, so to confirm that the transfer happens, we record its tenant ID.
volume.tenant_id.must_match(/^[0-9a-f-]+$/) # should look like a UUID
source_tenant_id = volume.tenant_id
# check that accept_transfer fails without valid transfer ID and auth key
bogus_uuid = 'ec8ff7e8-81e2-4e12-b9fb-3e8890612c2d' # from Fog::UUID.uuid, but fixed to play nice with VCR
proc { other_service.transfers.accept(bogus_uuid, auth_key) }.must_raise Fog::OpenStack::Volume::NotFound
proc { other_service.transfers.accept(transfer_id, 'invalidauthkey') }.must_raise Excon::Errors::BadRequest
# accept transfer
puts 'Accepting transfer...' if ENV['DEBUG_VERBOSE']
transfer = other_service.transfers.accept(transfer.id, auth_key)
transfer.must_be_kind_of Fog::OpenStack::Volume::Transfer
transfer.id.must_equal transfer_id
transfer.name.must_equal transfer_name
# check that recipient can see the volume
volume = other_service.volumes.get(volume.id)
volume.must_be_kind_of Fog::OpenStack::Volume::Volume
# # check that sender cannot see the volume anymore
# proc { @service.volumes.get(volume.id) }.must_raise Fog::OpenStack::Compute::NotFound
# @service.volumes.all(@name_param => volume_name).length.must_equal 0
# As noted above, both users seem to be able to see the volume at all times.
# Check change of ownership by looking at the tenant_id, instead.
volume.tenant_id.must_match(/^[0-9a-f-]+$/) # should look like a UUID
volume.tenant_id.wont_equal(source_tenant_id)
# check that the transfer object is gone on both sides
[@service, other_service].each do |service|
assert_nil service.transfers.get(transfer.id)
service.transfers.all(:name => transfer_name).length.must_equal 0
end
ensure
# cleanup volume
cleanup_test_object(other_service.volumes, volume.nil? ? nil : volume.id) if other_service
cleanup_test_object(@service.volumes, volume.nil? ? nil : volume.id) unless other_service
end
end
end
it 'can create and delete volume transfers (v2 only)' do
if v2?
VCR.use_cassette('volume_transfer_and_delete') do
begin
# create volume object
volume = setup_test_object(:type => :volume,
@name_param => 'fog-testvolume-1',
:size => 1)
volume.wait_for { ready? }
# create transfer object
transfer = setup_test_object(:type => :transfer,
:name => 'fog-testtransfer-1',
:volume_id => volume.id)
# we need to save the auth_key NOW, it's only present in the response
# from the create_transfer request
auth_key = transfer.auth_key
transfer_id = transfer.id
# to try to accept the transfer, we need a second connection to a different project
other_service = service_class.new(
:openstack_auth_url => @os_auth_url,
:openstack_region => ENV['OS_REGION_NAME'] || 'RegionOne',
:openstack_api_key => ENV['OS_PASSWORD_OTHER'] || 'password',
:openstack_username => ENV['OS_USERNAME_OTHER'] || 'demo',
:openstack_domain_name => ENV['OS_USER_DOMAIN_NAME'] || 'Default',
:openstack_project_name => ENV['OS_PROJECT_NAME_OTHER'] || 'demo'
)
# delete transfer again
transfer.destroy
# check that transfer cannot be accepted when it has been deleted
puts 'Checking that accepting a deleted transfer fails...' if ENV['DEBUG_VERBOSE']
proc { other_service.transfers.accept(transfer_id, auth_key) }.must_raise Fog::OpenStack::Volume::NotFound
ensure
# cleanup volume
cleanup_test_object(@service.volumes, volume.id) if volume
end
end
end
end
it 'can create, update and delete volume snapshots' do
VCR.use_cassette('volume_snapshot_and_delete') do
begin
# create volume object
volume = setup_test_object(:type => :volume,
@name_param => 'fog-testvolume-1',
:size => 1)
volume.wait_for { ready? }
# create snapshot object
snapshot = setup_test_object(:type => :snapshot,
@name_param => 'fog-testsnapshot-1',
@description_param => 'Test snapshot',
:volume_id => volume.id)
snapshot_id = snapshot.id
# wait for the snapshot to be available
Fog.wait_for do
begin
object = @service.snapshots.get(snapshot.id)
object.wont_be_nil
puts "Current status: #{object ? object.status : 'deleted'}" if ENV['DEBUG_VERBOSE']
object.nil? || (%w[available error].include? object.status.downcase)
end
end
# Update snapshot
snapshot.update(@description_param => 'Updated description')
updated_snapshot = @service.snapshots.get(snapshot.id)
updated_snapshot.description.must_equal 'Updated description' if v2?
updated_snapshot.display_description.must_equal 'Updated description' unless v2?
# delete snapshot
snapshot.destroy
# wait for the snapshot to be deleted
Fog.wait_for do
begin
object = @service.snapshots.get(snapshot_id)
puts "Current status: #{object ? object.status : 'deleted'}" if ENV['DEBUG_VERBOSE']
object.nil?
end
end
ensure
# cleanup volume
begin
snapshot.destroy if snapshot
rescue Fog::OpenStack::Volume::NotFound
# Don't care if it doesn't exist
end
cleanup_test_object(@service.volumes, volume.id) if volume
end
end
end
it 'can create, update and delete volume metadata' do
VCR.use_cassette('volume_metadata_crud') do
begin
# create volume object with metadata
volume = setup_test_object(:type => :volume,
@name_param => 'fog-testvolume-1',
:size => 1,
:metadata => {'some_metadata' => 'this is meta',
'more_metadata' => 'even more meta'})
volume.wait_for { ready? }
updated_volume = @service.volumes.get(volume.id)
check_metadata = updated_volume.metadata
check_metadata.size.must_equal 2
check_metadata['some_metadata'].must_equal 'this is meta'
check_metadata['more_metadata'].must_equal 'even more meta'
# update metadata
volume.update_metadata('some_metadata' => 'this is updated',
'new_metadata' => 'this is new')
updated_volume = @service.volumes.get(volume.id)
check_metadata = updated_volume.metadata
check_metadata.size.must_equal 3
check_metadata['some_metadata'].must_equal 'this is updated'
check_metadata['more_metadata'].must_equal 'even more meta'
check_metadata['new_metadata'].must_equal 'this is new'
# replace metadata
volume.replace_metadata('some_metadata' => 'this is updated again',
'newer_metadata' => 'this is newer')
updated_volume = @service.volumes.get(volume.id)
check_metadata = updated_volume.metadata
check_metadata.size.must_equal 2
check_metadata['some_metadata'].must_equal 'this is updated again'
check_metadata['newer_metadata'].must_equal 'this is newer'
# delete metadata
volume.delete_metadata('some_metadata')
updated_volume = @service.volumes.get(volume.id)
check_metadata = updated_volume.metadata
check_metadata.size.must_equal 1
check_metadata['newer_metadata'].must_equal 'this is newer'
ensure
# cleanup volume
cleanup_test_object(@service.volumes, volume.id) if volume
end
end
end
it 'can create, update and delete volume snapshot metadata' do
VCR.use_cassette('volume_snapshot_metadata_crud') do
begin
# create volume object with metadata
volume = setup_test_object(:type => :volume,
@name_param => 'fog-testvolume-1',
:size => 1,
:metadata => {'some_metadata' => 'this is meta',
'more_metadata' => 'even more meta'})
volume.wait_for { ready? }
# create snapshot object
snapshot = setup_test_object(:type => :snapshot,
@name_param => 'fog-testsnapshot-1',
@description_param => 'Test snapshot',
:volume_id => volume.id)
snapshot_id = snapshot.id
# wait for the snapshot to be available
Fog.wait_for do
begin
object = @service.snapshots.get(snapshot_id)
object.wont_be_nil
puts "Current status: #{object ? object.status : 'deleted'}" if ENV['DEBUG_VERBOSE']
object.nil? || (%w[available error].include? object.status.downcase)
end
end
updated_snapshot = @service.snapshots.get(snapshot_id)
check_metadata = updated_snapshot.metadata
check_metadata.size.must_equal 0
# update metadata
snapshot.update_metadata('some_snapshot_metadata' => 'this is data',
'new_snapshot_metadata' => 'this is new')
updated_snapshot = @service.snapshots.get(snapshot_id)
check_metadata = updated_snapshot.metadata
check_metadata.size.must_equal 2
check_metadata['some_snapshot_metadata'].must_equal 'this is data'
check_metadata['new_snapshot_metadata'].must_equal 'this is new'
# delete metadata
snapshot.delete_metadata('some_snapshot_metadata')
updated_snapshot = @service.snapshots.get(snapshot_id)
check_metadata = updated_snapshot.metadata
check_metadata.size.must_equal 1
check_metadata['new_snapshot_metadata'].must_equal 'this is new'
ensure
# cleanup volume
cleanup_test_object(@service.snapshots, snapshot.id) if snapshot
cleanup_test_object(@service.volumes, volume.id) if volume
end
end
end
# TODO: tests for snapshots
it 'responds to list_snapshots_detailed' do
@service.respond_to?(:list_snapshots_detailed).must_equal true
end
# TODO: tests for quotas
end
end
|