1
0
Fork 0
mirror of https://github.com/fog/fog.git synced 2022-11-09 13:51:43 -05:00

Merge pull request #3189 from smashwilson/boot-from-volume

[rackspace] Boot from volume
This commit is contained in:
Wesley Beary 2014-10-02 09:05:19 -05:00
commit 1bc78346c0
7 changed files with 130 additions and 8 deletions

View file

@ -48,6 +48,10 @@ module Fog
# @return [String] region of the volume
attribute :availability_zone
# @!attribute [rw] image_id
# @return [String] The ID of an image used to create a bootable volume.
attribute :image_id, :aliases => ['image', 'imageRef'], :squash => 'id'
# Returns true if the volume is in a ready state
# @return [Boolean] returns true if volume is in a ready state
def ready?
@ -104,7 +108,8 @@ module Fog
:display_description => display_description,
:volume_type => volume_type,
:availability_zone => availability_zone,
:snapshot_id => attributes[:snapshot_id]
:snapshot_id => attributes[:snapshot_id],
:image_id => attributes[:image_id]
})
merge_attributes(data.body['volume'])
true

View file

@ -148,6 +148,16 @@ module Fog
# @see http://docs.rackspace.com/servers/api/v2/cs-devguide/content/List_Images-d1e4435.html
attribute :image_id, :aliases => 'image', :squash => 'id'
# @!attribute [w] boot_volume_id
# @return [String] The ID of a bootable volume from the BlockStorage service.
# @see http://developer.openstack.org/api-ref-compute-v2-ext.html#ext-os-block-device-mapping-v2-boot
attribute :boot_volume_id
# @!attribute [w] boot_image_id
# @return [String] The ID of an image to create a bootable volume from.
# @see http://developer.openstack.org/api-ref-compute-v2-ext.html#ext-os-block-device-mapping-v2-boot
attribute :boot_image_id
# @!attribute [rw] password
# @return [String] Password for system adminstrator account.
# @see http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Server_Passwords-d1e2510.html
@ -221,7 +231,8 @@ module Fog
# Creates server
# * requires attributes: service:, :name, :image_id, and :flavor_id
# * optional attributes :disk_config, :metadata, :personality, :config_drive
# * optional attributes :disk_config, :metadata, :personality, :config_drive, :boot_volume_id, :boot_image_id
# * :image_id should be "" if :boot_volume_id or :boot_image_id are provided.
# @return [Boolean] returns true if server is being created
# @raise [Fog::Compute::RackspaceV2::NotFound] - HTTP 404
# @raise [Fog::Compute::RackspaceV2::BadRequest] - HTTP 400
@ -251,10 +262,13 @@ module Fog
modified_options[:config_drive] = config_drive unless config_drive.nil?
modified_options[:user_data] = user_data_encoded unless user_data_encoded.nil?
modified_options[:key_name] ||= attributes[:key_name]
modified_options[:boot_volume_id] ||= attributes[:boot_volume_id]
modified_options[:boot_image_id] ||= attributes[:boot_image_id]
if modified_options[:networks]
modified_options[:networks].map! { |id| { :uuid => id } }
end
data = service.create_server(name, image_id, flavor_id, 1, 1, modified_options)
merge_attributes(data.body['server'])
true

View file

@ -10,6 +10,8 @@ module Fog
# @option options [String] :display_description display description for volume
# @option options [String] :volume_type type of volume
# @option options [String] :snapshot_id The optional snapshot from which to create a volume.
# @option options [String] :image_id The ID of an image from the compute service. If provided, a bootable volume will be
# created.
# @return [Excon::Response] response:
# * body [Hash]:
# * 'volume' [Hash]:
@ -41,6 +43,7 @@ module Fog
data['volume']['volume_type'] = options[:volume_type] unless options[:volume_type].nil?
data['volume']['availability_zone'] = options[:availability_zone] unless options[:availability_zone].nil?
data['volume']['snapshot_id'] = options[:snapshot_id] unless options[:snapshot_id].nil?
data['volume']['imageRef'] = options[:image_id] unless options[:image_id].nil?
request(
:body => Fog::JSON.encode(data),
@ -80,6 +83,7 @@ module Fog
snapshot = self.data[:snapshots][snapshot_id]
volume.merge!("size" => snapshot["size"])
end
volume["image_id"] = options[:image_id] if options[:image_id]
self.data[:volumes][volume_id] = volume

View file

@ -14,6 +14,12 @@ module Fog
# @option options [Hash] personality Hash containing data to inject into the file system of the cloud server instance during server creation.
# @option options [Boolean] config_drive whether to attach a read-only configuration drive
# @option options [String] keypair Name of the kay-pair to associate with this server.
# @option options [Array<Hash>] block_device_mapping A manually specified block device mapping to fully control the creation and
# attachment of volumes to this server. Mutually exclusive with :volume_id or :volume_image_id. If provided, leave image_id
# as "". See http://developer.openstack.org/api-ref-compute-v2-ext.html#ext-os-block-device-mapping-v2-boot for details.
# @option options [String] boot_volume_id Id of a pre-created bootable volume to use for this server. If provided, leave image_id as "".
# @option options [String] boot_image_id Id of an image to create a bootable volume from and attach to this server. If provided,
# leave image_id as "".
# @return [Excon::Response] response:
# * body [Hash]:
# * server [Hash]:
@ -40,6 +46,7 @@ module Fog
# @see http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Server_Metadata-d1e2529.html
# @see http://docs.rackspace.com/servers/api/v2/cs-devguide/content/Server_Personality-d1e2543.html
# @see http://docs.rackspace.com/servers/api/v2/cs-devguide/content/ch_extensions.html#diskconfig_attribute
# @see http://developer.openstack.org/api-ref-compute-v2-ext.html#ext-os-block-device-mapping-v2-boot
#
# * State Transitions
# * BUILD -> ACTIVE
@ -73,6 +80,42 @@ module Fog
data['server']['key_name'] = options[:key_name] unless options[:key_name].nil?
if options[:block_device_mapping]
if options[:boot_volume_id]
Fog::Logger.warning("Manual :block_device_mapping overrides :boot_volume_id in #create_server!")
end
if options[:boot_image_id]
Fog::Logger.warning("Manual :block_device_mapping overrides :boot_image_id in #create_server!")
end
data['server']['block_device_mapping_v2'] = options[:block_device_mapping]
else
if options[:boot_volume_id]
if options[:boot_image_id]
Fog::Logger.warning(":boot_volume_id overrides :boot_image_id!")
end
data['server']['block_device_mapping_v2'] = [{
'boot_index' => '0',
'uuid' => options[:boot_volume_id],
'source_type' => 'volume',
'destination_type' => 'volume',
'volume_size' => 100
}]
end
if options[:boot_image_id]
data['server']['block_device_mapping_v2'] = [{
'boot_index' => '0',
'uuid' => options[:boot_image_id],
'source_type' => 'image',
'destination_type' => 'volume',
'volume_size' => 100
}]
end
end
request(
:body => Fog::JSON.encode(data),
:expects => [202],

View file

@ -60,6 +60,19 @@ module Shindo
sleep 30 unless Fog.mocking?
end
def wait_for_volume_state(service, volume_id, state)
current_state = nil
until current_state == state
current_state = service.get_volume(volume_id).body['volume']['status']
if current_state == 'error'
Fog::Logger.warning caller
Fog::Logger.warning "Volume is in an error state!"
return
end
sleep 10 unless Fog.mocking?
end
end
def rackspace_test_image_id(service)
image_id = Fog.credentials[:rackspace_image_id]
# I chose to use the first Ubuntu because it will work with the smallest flavor and it doesn't require a license

View file

@ -10,6 +10,7 @@ Shindo.tests('Fog::Rackspace::BlockStorage | volume_tests', ['rackspace']) do
'volume_type' => String,
'availability_zone' => String,
'snapshot_id' => Fog::Nullable::String,
'image_id' => Fog::Nullable::String,
'attachments' => Array,
'metadata' => Hash
}
@ -25,12 +26,23 @@ Shindo.tests('Fog::Rackspace::BlockStorage | volume_tests', ['rackspace']) do
service = Fog::Rackspace::BlockStorage.new
tests('success') do
id = nil
ids = []
size = 100
tests("#create_volume(#{size})").formats(get_volume_format) do
data = service.create_volume(size).body
id = data['volume']['id']
ids << data['volume']['id']
data
end
tests("#create_volume for a bootable volume").formats(get_volume_format) do
# Find a suitable image.
image_id = rackspace_test_image_id(Fog::Compute.new(:provider => 'rackspace'))
data = service.create_volume(size, :image_id => image_id).body
tests("assigned an image id").returns(image_id) do
data['volume']['image_id']
end
ids << data['volume']['id']
data
end
@ -38,12 +50,16 @@ Shindo.tests('Fog::Rackspace::BlockStorage | volume_tests', ['rackspace']) do
service.list_volumes.body
end
tests("#get_volume(#{id})").formats(get_volume_format) do
service.get_volume(id).body
tests("#get_volume(#{ids.first})").formats(get_volume_format) do
service.get_volume(ids.first).body
end
tests("#delete_volume(#{id})").succeeds do
service.delete_volume(id)
ids.each do |id|
tests("#delete_volume(#{id})").succeeds do
wait_for_volume_state(service, id, 'available')
service.delete_volume(id)
end
end
end

View file

@ -61,6 +61,11 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server_tests', ['rackspace']) do
server_name = "fog#{Time.now.to_i.to_s}"
image_id = rackspace_test_image_id(service)
flavor_id = rackspace_test_flavor_id(service)
bootable_flavor_id = if Fog.mocking?
flavor_id
else
service.flavors.find { |f| f.name =~ /Performance/ }.id
end
tests("#create_server(#{server_name}, #{image_id}, #{flavor_id}, 1, 1)").formats(create_server_format) do
body = service.create_server(server_name, image_id, flavor_id, 1, 1).body
@ -69,6 +74,28 @@ Shindo.tests('Fog::Compute::RackspaceV2 | server_tests', ['rackspace']) do
end
wait_for_server_state(service, server_id, 'ACTIVE', 'ERROR')
tests("#create_server(#{server_name}_bfv_1, '', #{flavor_id}, 1, 1, :boot_volume_id => bootable_volume_id)").succeeds do
# First, create a bootable volume.
volume_service = Fog::Rackspace::BlockStorage.new
bootable_volume_id = volume_service.create_volume(100, :image_id => image_id).body['volume']['id']
wait_for_volume_state(volume_service, bootable_volume_id, 'available')
body = service.create_server(server_name + "_bfv_1", '', bootable_flavor_id, 1, 1, :boot_volume_id => bootable_volume_id).body
bfv_server_id = body['server']['id']
wait_for_server_state(service, bfv_server_id, 'ACTIVE', 'ERROR')
service.delete_server(bfv_server_id)
wait_for_volume_state(volume_service, bootable_volume_id, 'available')
volume_service.delete_volume(bootable_volume_id)
end
tests("#create_server(#{server_name}_bfv_2, '', #{flavor_id}, 1, 1, :boot_image_id => #{image_id})").succeeds do
body = service.create_server(server_name + "_bfv_2", '', bootable_flavor_id, 1, 1, :boot_image_id => image_id).body
bfv_server_id = body['server']['id']
wait_for_server_state(service, bfv_server_id, 'ACTIVE', 'ERROR')
service.delete_server(bfv_server_id)
end
tests('#list_servers').formats(list_servers_format, false) do
service.list_servers.body
end