You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by mf...@redhat.com on 2011/07/19 16:19:35 UTC
VSphere: Added user_data feature and remote mappings
Hi,
This 'final' patchset will add support for mounting an gzipped cdrom images
in instance. This image could be passed to the instance via 'user_data' parameter.
The 4/4. patch will switch local mappings between Task->Instance that unfortunatelly broke
our 'stateless' specification to use 'remote' datastore to store YAML files. So this driver
is stateless again.
PS: Great thanks to fvollero for time and blood spended in debugging file operations ;-)
-- Michal
Re: [PATCH core 3/4] Added information about current API provider to
layout (near driver)
Posted by Francesco Vollero <fv...@redhat.com>.
Ackd, the TODO's messages are removed on 4th patch, that is a subsequent update of this patch.
Cheers,
Francesco
On Tue, Jul 19, 2011 at 04:19:38PM +0200, mfojtik@redhat.com wrote:
> From: Michal Fojtik <mf...@redhat.com>
>
>
> Signed-off-by: Michal fojtik <mf...@redhat.com>
> ---
> .../deltacloud/drivers/vsphere/vsphere_client.rb | 2 +-
> .../deltacloud/drivers/vsphere/vsphere_driver.rb | 18 +++++++++++-------
> server/views/layout.html.haml | 2 +-
> 3 files changed, 13 insertions(+), 9 deletions(-)
>
> diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
> index f2cc486..7e5cb4a 100644
> --- a/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
> +++ b/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
> @@ -68,7 +68,7 @@ module Deltacloud::Drivers::VSphere
> rootFolder = vsphere.serviceInstance.content.rootFolder
> rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
> dc.datastoreFolder.childEntity.collect do |datastore|
> - vms += datastore.vm.collect { |vm| { :instance => vm, :datastore => datastore.name } }
> + vms += datastore.vm.collect { |vm| { :instance => vm, :datastore => datastore.name } unless vm.nil? }
> end
> end
> vms.flatten.compact
> diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
> index 3a502e0..971bbbb 100644
> --- a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
> +++ b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
> @@ -27,6 +27,7 @@ module Deltacloud::Drivers::VSphere
> include Deltacloud::Drivers::VSphere::Helper
>
> feature :instances, :user_data
> + feature :instances, :user_name
>
> def hardware_profiles(credentials, opts={})
> vsphere = new_client(credentials)
> @@ -71,7 +72,6 @@ module Deltacloud::Drivers::VSphere
> else
> template_vms = list_virtual_machines(credentials).select { |vm| vm[:instance].summary.config[:template] }
> end
> -
> img_arr = template_vms.collect do |image_hash|
> # Since all calls to vm are threaten as SOAP calls, reduce them using
> # local variable.
> @@ -94,7 +94,6 @@ module Deltacloud::Drivers::VSphere
> )
> end
> end
> -
> img_arr = filter_on( img_arr, :architecture, opts )
> img_arr.sort_by{|e| [e.owner_id, e.name]}
> end
> @@ -145,10 +144,8 @@ module Deltacloud::Drivers::VSphere
> # Since all calls to vm are threaten as SOAP calls, reduce them using
> # local variable.
> vm, realm_id = vm_hash[:instance], vm_hash[:datastore]
> - next unless vm
> config = vm.summary.config
> - next unless config
> - next unless vm.summary.storage
> + next if not config
> template_id = vm.config[:extraConfig].select { |k| k.key == 'template_id' }
> template_id = template_id.first.value unless template_id.empty?
> properties = {
> @@ -209,7 +206,7 @@ module Deltacloud::Drivers::VSphere
> :memoryMB => opts[:hwp_memory],
> :numCPUs => opts[:hwp_cpu],
> :extraConfig => [
> - { :key => 'template_id', :value => image_id }
> + { :key => 'template_id', :value => image_id },
> ]
> }
> # If user wants to inject data into instance he need to submit a Base64
> @@ -220,7 +217,10 @@ module Deltacloud::Drivers::VSphere
> device = vm[:instance].config.hardware.device.select { |hw| hw.class == RbVmomi::VIM::VirtualCdrom }.first
> if device
> # TODO: Upload baked ISO image to the Datastore
> - device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] test.iso")
> + machine_config[:extraConfig] << {
> + :key => 'user_data_file', :value => "#{opts[:name]}.iso"
> + }
> + device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] #{opts[:name].iso}")
> machine_config.merge!({
> :deviceChange => [{
> :operation => :edit,
> @@ -290,6 +290,10 @@ module Deltacloud::Drivers::VSphere
> status 502
> end
>
> + on /Failed to inject data/ do
> + status 502
> + end
> +
> end
>
> def valid_credentials?(credentials)
> diff --git a/server/views/layout.html.haml b/server/views/layout.html.haml
> index f6d3010..9deaaf3 100644
> --- a/server/views/layout.html.haml
> +++ b/server/views/layout.html.haml
> @@ -24,7 +24,7 @@
> |
> =link_to_format(:json)
> #driver_info
> - Driver: #{driver_symbol} | API version: #{settings.version}
> + Driver: #{driver_symbol} | Provider: #{Thread::current[:provider] || ENV['API_PROVIDER']} | API version: #{settings.version}
> #copyright
> Copyright 2009-2011
> %a{:href => 'http://incubator.apache.org/deltacloud/'} The Apache Software Foundation
> --
> 1.7.4.1
>
[PATCH core 3/4] Added information about current API provider to layout (near driver)
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
.../deltacloud/drivers/vsphere/vsphere_client.rb | 2 +-
.../deltacloud/drivers/vsphere/vsphere_driver.rb | 18 +++++++++++-------
server/views/layout.html.haml | 2 +-
3 files changed, 13 insertions(+), 9 deletions(-)
diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
index f2cc486..7e5cb4a 100644
--- a/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
+++ b/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
@@ -68,7 +68,7 @@ module Deltacloud::Drivers::VSphere
rootFolder = vsphere.serviceInstance.content.rootFolder
rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
dc.datastoreFolder.childEntity.collect do |datastore|
- vms += datastore.vm.collect { |vm| { :instance => vm, :datastore => datastore.name } }
+ vms += datastore.vm.collect { |vm| { :instance => vm, :datastore => datastore.name } unless vm.nil? }
end
end
vms.flatten.compact
diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
index 3a502e0..971bbbb 100644
--- a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
+++ b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
@@ -27,6 +27,7 @@ module Deltacloud::Drivers::VSphere
include Deltacloud::Drivers::VSphere::Helper
feature :instances, :user_data
+ feature :instances, :user_name
def hardware_profiles(credentials, opts={})
vsphere = new_client(credentials)
@@ -71,7 +72,6 @@ module Deltacloud::Drivers::VSphere
else
template_vms = list_virtual_machines(credentials).select { |vm| vm[:instance].summary.config[:template] }
end
-
img_arr = template_vms.collect do |image_hash|
# Since all calls to vm are threaten as SOAP calls, reduce them using
# local variable.
@@ -94,7 +94,6 @@ module Deltacloud::Drivers::VSphere
)
end
end
-
img_arr = filter_on( img_arr, :architecture, opts )
img_arr.sort_by{|e| [e.owner_id, e.name]}
end
@@ -145,10 +144,8 @@ module Deltacloud::Drivers::VSphere
# Since all calls to vm are threaten as SOAP calls, reduce them using
# local variable.
vm, realm_id = vm_hash[:instance], vm_hash[:datastore]
- next unless vm
config = vm.summary.config
- next unless config
- next unless vm.summary.storage
+ next if not config
template_id = vm.config[:extraConfig].select { |k| k.key == 'template_id' }
template_id = template_id.first.value unless template_id.empty?
properties = {
@@ -209,7 +206,7 @@ module Deltacloud::Drivers::VSphere
:memoryMB => opts[:hwp_memory],
:numCPUs => opts[:hwp_cpu],
:extraConfig => [
- { :key => 'template_id', :value => image_id }
+ { :key => 'template_id', :value => image_id },
]
}
# If user wants to inject data into instance he need to submit a Base64
@@ -220,7 +217,10 @@ module Deltacloud::Drivers::VSphere
device = vm[:instance].config.hardware.device.select { |hw| hw.class == RbVmomi::VIM::VirtualCdrom }.first
if device
# TODO: Upload baked ISO image to the Datastore
- device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] test.iso")
+ machine_config[:extraConfig] << {
+ :key => 'user_data_file', :value => "#{opts[:name]}.iso"
+ }
+ device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] #{opts[:name].iso}")
machine_config.merge!({
:deviceChange => [{
:operation => :edit,
@@ -290,6 +290,10 @@ module Deltacloud::Drivers::VSphere
status 502
end
+ on /Failed to inject data/ do
+ status 502
+ end
+
end
def valid_credentials?(credentials)
diff --git a/server/views/layout.html.haml b/server/views/layout.html.haml
index f6d3010..9deaaf3 100644
--- a/server/views/layout.html.haml
+++ b/server/views/layout.html.haml
@@ -24,7 +24,7 @@
|
=link_to_format(:json)
#driver_info
- Driver: #{driver_symbol} | API version: #{settings.version}
+ Driver: #{driver_symbol} | Provider: #{Thread::current[:provider] || ENV['API_PROVIDER']} | API version: #{settings.version}
#copyright
Copyright 2009-2011
%a{:href => 'http://incubator.apache.org/deltacloud/'} The Apache Software Foundation
--
1.7.4.1
Re: [PATCH core 2/4] Added new text filed to input Base64 encoded
field in new instance UI
Posted by Francesco Vollero <fv...@redhat.com>.
ACK'd. Work for me, but I suggest to add a text in order to explain that the Base64 content have to be pasted *as is* else the Zlib::Gzip library throw an error because they look for the well formatted file.
Francesco
On Tue, Jul 19, 2011 at 04:19:37PM +0200, mfojtik@redhat.com wrote:
> From: Michal Fojtik <mf...@redhat.com>
>
>
> Signed-off-by: Michal fojtik <mf...@redhat.com>
> ---
> server/views/instances/new.html.haml | 5 +++++
> 1 files changed, 5 insertions(+), 0 deletions(-)
>
> diff --git a/server/views/instances/new.html.haml b/server/views/instances/new.html.haml
> index 70bd9d2..6f8a086 100644
> --- a/server/views/instances/new.html.haml
> +++ b/server/views/instances/new.html.haml
> @@ -27,6 +27,11 @@
> %option
> - @load_balancers.each do |load_balancer|
> %option{:value => load_balancer.id} #{load_balancer.id}
> + -if driver_has_feature?(:user_data)
> + %p
> + %label
> + User data (Base64):
> + %textarea{:name => :user_data, :cols => 60, :rows => 10, :placeholder => "Copy&Paste a Base64 string here..."}
> -if driver_has_feature?(:authentication_key)
> %p
> %label
> --
> 1.7.4.1
>
[PATCH core 2/4] Added new text filed to input Base64 encoded field in new instance UI
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
server/views/instances/new.html.haml | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/server/views/instances/new.html.haml b/server/views/instances/new.html.haml
index 70bd9d2..6f8a086 100644
--- a/server/views/instances/new.html.haml
+++ b/server/views/instances/new.html.haml
@@ -27,6 +27,11 @@
%option
- @load_balancers.each do |load_balancer|
%option{:value => load_balancer.id} #{load_balancer.id}
+ -if driver_has_feature?(:user_data)
+ %p
+ %label
+ User data (Base64):
+ %textarea{:name => :user_data, :cols => 60, :rows => 10, :placeholder => "Copy&Paste a Base64 string here..."}
-if driver_has_feature?(:authentication_key)
%p
%label
--
1.7.4.1
Re: [PATCH core 1/4] VSphere: Switched from floppy image to CD-ROM
ISO Vsphere: Replaced hardware profiles with one highly customizable profile
Vsphere: Moved all VSphere helpers to separate file
Posted by Francesco Vollero <fv...@redhat.com>.
Ack'd. Work for me.
On Tue, Jul 19, 2011 at 04:19:36PM +0200, mfojtik@redhat.com wrote:
> From: Michal Fojtik <mf...@redhat.com>
>
>
> Signed-off-by: Michal fojtik <mf...@redhat.com>
> ---
> .../deltacloud/drivers/vsphere/vsphere_client.rb | 113 +++++++++++
> .../deltacloud/drivers/vsphere/vsphere_driver.rb | 205 ++++++--------------
> 2 files changed, 170 insertions(+), 148 deletions(-)
> create mode 100644 server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
>
> diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
> new file mode 100644
> index 0000000..f2cc486
> --- /dev/null
> +++ b/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
> @@ -0,0 +1,113 @@
> +module Deltacloud::Drivers::VSphere
> +
> + module Helper
> +
> + # Find a VirtualMachine traversing through all Datastores and Datacenters
> + #
> + # This helper will return a Hash: { :datastore => NAME_OF_DS, :instance => VM }
> + # Returning datastore is necesarry for constructing a correct realm for an
> + # instance
> + #
> + def find_vm(credentials, name)
> + vsphere = new_client(credentials)
> + safely do
> + rootFolder = vsphere.serviceInstance.content.rootFolder
> + vm = {}
> + rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
> + dc.datastoreFolder.childEntity.collect do |datastore|
> + vm[:instance] = datastore.vm.find { |x| x.name == name }
> + if vm[:instance]
> + vm[:datastore] = datastore.name
> + break
> + end
> + end
> + break if [:datastore]
> + end
> + vm
> + end
> + end
> +
> + # Find a ResourcePool[1] object associated by given Datastore
> + # ResourcePool is defined for Datacenter and is used for launching a new
> + # instance
> + #
> + # [1] http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.ResourcePool.html
> + #
> + def find_resource_pool(credentials, name)
> + vsphere = new_client(credentials)
> + safely do
> + rootFolder = vsphere.serviceInstance.content.rootFolder
> + dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).select do |dc|
> + dc.datastoreFolder.childEntity.find { |d| d.name == name }.nil? == false
> + end.flatten.compact.first
> + dc.hostFolder.childEntity.collect.first.resourcePool
> + end
> + end
> +
> + # This helper will try to find a Datastore[1] object in all Datacenters.
> + # Datastore is used to place instance on create to correct place
> + #
> + # [1] http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.Datastore.html
> + #
> + def find_datastore(credentials, name)
> + vsphere = new_client(credentials)
> + safely do
> + rootFolder = vsphere.serviceInstance.content.rootFolder
> + rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect do |dc|
> + dc.datastoreFolder.childEntity.find { |d| d.name == name }
> + end.flatten.compact.first
> + end
> + end
> +
> + # This helper will traverse across all datacenters and datastores and gather
> + # all virtual machines available on vSphere
> + #
> + def list_virtual_machines(credentials)
> + vsphere = new_client(credentials)
> + vms = []
> + rootFolder = vsphere.serviceInstance.content.rootFolder
> + rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
> + dc.datastoreFolder.childEntity.collect do |datastore|
> + vms += datastore.vm.collect { |vm| { :instance => vm, :datastore => datastore.name } }
> + end
> + end
> + vms.flatten.compact
> + end
> +
> + def map_task_to_instance(task_key, new_instance)
> + FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
> + File::open(File::join(MAPPER_STORAGE_ROOT, task_key), "w") do |f|
> + f.puts(YAML::dump(new_instance))
> + end
> + new_instance
> + end
> +
> + def load_serialized_instance(task_key)
> + FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
> + YAML::load(File::read(File::join(MAPPER_STORAGE_ROOT, task_key))) rescue nil
> + end
> +
> + # Yield all tasks if they are included in mapper storage directory.
> + def stored_tasks(vsphere)
> + FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
> + tasks = Dir[File::join(MAPPER_STORAGE_ROOT, '*')].collect { |file| File::basename(file) }
> + vsphere.serviceInstance.content.taskManager.recentTask.each do |task|
> + if tasks.include?(task.info.key)
> + yield task
> + else
> + # If given task is not longer listed in 'recentTasks' delete the
> + # mapper file
> + FileUtils::rm_rf(File::join(MAPPER_STORAGE_ROOT, task.info.key))
> + end
> + end
> + end
> +
> + def extract_architecture(text)
> + 'x86_64' if text.include?('64-bit')
> + 'i386' if text.include?('32-bit')
> + end
> +
> +
> + end
> +
> +end
> diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
> index 8c3c3e0..3a502e0 100644
> --- a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
> +++ b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
> @@ -15,6 +15,7 @@
> #
>
> require 'deltacloud/base_driver'
> +require 'deltacloud/drivers/vsphere/vsphere_client'
> require 'rbvmomi'
>
> module Deltacloud::Drivers::VSphere
> @@ -23,35 +24,25 @@ module Deltacloud::Drivers::VSphere
>
> class VSphereDriver < Deltacloud::BaseDriver
>
> - # Set of predefined hardware profiles
> - define_hardware_profile('small') do
> - cpu 1
> - memory 256
> - architecture ['x86_64', 'i386']
> - end
> -
> - define_hardware_profile('medium') do
> - cpu 1
> - memory 512
> - architecture ['x86_64', 'i386']
> - end
> + include Deltacloud::Drivers::VSphere::Helper
>
> - define_hardware_profile('large') do
> - cpu 2
> - memory 1024
> - architecture ['x86_64', 'i386']
> - end
> + feature :instances, :user_data
>
> - define_hardware_profile('x-large') do
> - cpu 4
> - memory 2048
> - architecture ['x86_64', 'i386']
> - end
> -
> - # Since user can launch own instance using vSphere tools
> - # with customized properties, threat this hardware profile as
> - # unknown
> - define_hardware_profile('custom') do
> + def hardware_profiles(credentials, opts={})
> + vsphere = new_client(credentials)
> + safely do
> + service = vsphere.serviceInstance.content
> + max_memory, max_cpu_cores = 0, 0
> + service.rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
> + max_memory += dc.hostFolder.childEntity.first.summary.effectiveMemory
> + max_cpu_cores += dc.hostFolder.childEntity.first.summary.numCpuCores
> + end
> + [Deltacloud::HardwareProfile::new('default') do
> + cpu (1..max_cpu_cores)
> + memory (128..max_memory)
> + architecture ['x86_64', 'i386']
> + end]
> + end
> end
>
> # Configure instance state machine
> @@ -92,10 +83,11 @@ module Deltacloud::Drivers::VSphere
> :full_name => config[:guestFullName]
> }
> image_state = convert_state(:image, image.summary.runtime[:powerState])
> + image_architecture = extract_architecture(properties[:full_name]) || 'x86_64'
> Image.new(
> :id => properties[:name],
> :name => properties[:name],
> - :architecture => 'x86_64', # FIXME: I'm not sure if all templates/VM's in vSphere are x86_64
> + :architecture => image_architecture,
> :owner_id => credentials.user,
> :description => properties[:full_name],
> :state => image_state
> @@ -167,7 +159,7 @@ module Deltacloud::Drivers::VSphere
> :full_name => config[:guestFullName],
> }
> instance_state = convert_state(:instance, vm.summary.runtime[:powerState])
> - instance_profile = InstanceProfile::new(match_hwp_id(:memory => properties[:memory].to_s, :cpus => properties[:cpus].to_s),
> + instance_profile = InstanceProfile::new('default',
> :hwp_cpu => properties[:cpus],
> :hwp_memory => properties[:memory],
> :hwp_storage => properties[:storage])
> @@ -212,28 +204,54 @@ module Deltacloud::Drivers::VSphere
> end
> relocate = { :pool => resourcePool, :datastore => datastore }
> relocateSpec = RbVmomi::VIM.VirtualMachineRelocateSpec(relocate)
> - instance_profile = hardware_profiles(credentials, :id => opts[:hwp_id]).first
> + # Set extra configuration for VM, like template_id
> + machine_config = {
> + :memoryMB => opts[:hwp_memory],
> + :numCPUs => opts[:hwp_cpu],
> + :extraConfig => [
> + { :key => 'template_id', :value => image_id }
> + ]
> + }
> + # If user wants to inject data into instance he need to submit a Base64
> + # encoded gzipped ISO image.
> + # This image will be uplaoded to the Datastore given in 'realm_id'
> + # parameter and them attached to instance.
> + if opts[:user_data] and not opts[:user_data].empty?
> + device = vm[:instance].config.hardware.device.select { |hw| hw.class == RbVmomi::VIM::VirtualCdrom }.first
> + if device
> + # TODO: Upload baked ISO image to the Datastore
> + device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] test.iso")
> + machine_config.merge!({
> + :deviceChange => [{
> + :operation => :edit,
> + :device => device
> + }]
> + })
> + else
> + raise "Failed to inject data to device because there is no CD-ROM drive defined in given template"
> + end
> + end
> spec = RbVmomi::VIM.VirtualMachineCloneSpec(
> :location => relocateSpec,
> :powerOn => true,
> :template => false,
> - :config => RbVmomi::VIM.VirtualMachineConfigSpec(
> - :memoryMB => instance_profile.memory.value,
> - :numCPUs => instance_profile.cpu.value,
> - :extraConfig => [
> - { :key => 'template_id', :value => image_id }
> - ]
> - )
> + :config => RbVmomi::VIM.VirtualMachineConfigSpec(machine_config)
> )
> + instance_profile = InstanceProfile::new('default', :hwp_memory => opts[:hwp_memory], :hwp_cpu => opts[:hwp_cpu])
> task = vm[:instance].CloneVM_Task(:folder => vm[:instance].parent, :name => opts[:name], :spec => spec)
> new_instance = Instance::new(
> :id => opts[:name],
> :name => opts[:name],
> + :description => opts[:name],
> :owner_id => credentials.user,
> + :image_id => opts[:image_id],
> :realm_id => opts[:realm_id] || vm[:datastore],
> :state => 'PENDING',
> - :instance_profile => InstanceProfile::new(instance_profile.name),
> - :actions => instance_actions_for( 'PENDING' )
> + :public_addresses => [],
> + :private_addresses => [],
> + :instance_profile => instance_profile,
> + :actions => instance_actions_for( 'PENDING' ),
> + :create_image => false
> )
> map_task_to_instance(task.info.key, new_instance)
> end
> @@ -298,105 +316,6 @@ module Deltacloud::Drivers::VSphere
> endpoint || Deltacloud::Drivers::driver_config[:vsphere][:entrypoints]['default']['default']
> end
>
> - def map_task_to_instance(task_key, new_instance)
> - FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
> - File::open(File::join(MAPPER_STORAGE_ROOT, task_key), "w") do |f|
> - f.puts(YAML::dump(new_instance))
> - end
> - new_instance
> - end
> -
> - def load_serialized_instance(task_key)
> - YAML::load(File::read(File::join(MAPPER_STORAGE_ROOT, task_key)))
> - end
> -
> - # Yield all tasks if they are included in mapper storage directory.
> - def stored_tasks(vsphere)
> - FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
> - tasks = Dir[File::join(MAPPER_STORAGE_ROOT, '*')].collect { |file| File::basename(file) }
> - vsphere.serviceInstance.content.taskManager.recentTask.each do |task|
> - if tasks.include?(task.info.key)
> - yield task
> - else
> - # If given task is not longer listed in 'recentTasks' delete the
> - # mapper file
> - FileUtils::rm_rf(File::join(MAPPER_STORAGE_ROOT, task.info.key))
> - end
> - end
> - end
> -
> - # This helper will traverse across all datacenters and datastores and gather
> - # all virtual machines available on vSphere
> - #
> - def list_virtual_machines(credentials)
> - vsphere = new_client(credentials)
> - vms = []
> - rootFolder = vsphere.serviceInstance.content.rootFolder
> - rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
> - dc.datastoreFolder.childEntity.collect do |datastore|
> - vms += datastore.vm.collect { |vm| { :instance => vm, :datastore => datastore.name } }
> - end
> - end
> - vms.flatten.compact
> - end
> -
> - # This helper will try to find a Datastore[1] object in all Datacenters.
> - # Datastore is used to place instance on create to correct place
> - #
> - # [1] http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.Datastore.html
> - #
> - def find_datastore(credentials, name)
> - vsphere = new_client(credentials)
> - safely do
> - rootFolder = vsphere.serviceInstance.content.rootFolder
> - rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect do |dc|
> - dc.datastoreFolder.childEntity.find { |d| d.name == name }
> - end.flatten.compact.first
> - end
> - end
> -
> - # Find a ResourcePool[1] object associated by given Datastore
> - # ResourcePool is defined for Datacenter and is used for launching a new
> - # instance
> - #
> - # [1] http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.ResourcePool.html
> - #
> - def find_resource_pool(credentials, name)
> - vsphere = new_client(credentials)
> - safely do
> - rootFolder = vsphere.serviceInstance.content.rootFolder
> - dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).select do |dc|
> - dc.datastoreFolder.childEntity.find { |d| d.name == name }.nil? == false
> - end.flatten.compact.first
> - dc.hostFolder.childEntity.collect.first.resourcePool
> - end
> - end
> -
> - # Find a VirtualMachine traversing through all Datastores and Datacenters
> - #
> - # This helper will return a Hash: { :datastore => NAME_OF_DS, :instance => VM }
> - # Returning datastore is necesarry for constructing a correct realm for an
> - # instance
> - #
> - def find_vm(credentials, name)
> - vsphere = new_client(credentials)
> - safely do
> - rootFolder = vsphere.serviceInstance.content.rootFolder
> - vm = {}
> - rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
> - dc.datastoreFolder.childEntity.collect do |datastore|
> - vm[:instance] = datastore.vm.find { |x| x.name == name }
> - if vm[:instance]
> - vm[:datastore] = datastore.name
> - break
> - end
> - end
> - break if [:datastore]
> - end
> - vm
> - end
> - end
> -
> def convert_realm(datastore)
> Realm::new(
> :id => datastore.name,
> @@ -424,16 +343,6 @@ module Deltacloud::Drivers::VSphere
> new_state
> end
>
> - # Match hardware profile ID against given properties
> - def match_hwp_id(prop)
> - return 'small' if prop[:memory] == '256' and prop[:cpus] == '1'
> - return 'medium' if prop[:memory] == '512' and prop[:cpus] == '1'
> - return 'large' if prop[:memory] == '1024' and prop[:cpus] == '2'
> - return 'x-large' if prop[:memory] == '2048' and prop[:cpus] == '4'
> - 'unknown'
> - end
> -
> -
> end
>
> end
> --
> 1.7.4.1
>
[PATCH core 1/4] VSphere: Switched from floppy image to CD-ROM ISO Vsphere: Replaced hardware profiles with one highly customizable profile Vsphere: Moved all VSphere helpers to separate file
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
.../deltacloud/drivers/vsphere/vsphere_client.rb | 113 +++++++++++
.../deltacloud/drivers/vsphere/vsphere_driver.rb | 205 ++++++--------------
2 files changed, 170 insertions(+), 148 deletions(-)
create mode 100644 server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
new file mode 100644
index 0000000..f2cc486
--- /dev/null
+++ b/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
@@ -0,0 +1,113 @@
+module Deltacloud::Drivers::VSphere
+
+ module Helper
+
+ # Find a VirtualMachine traversing through all Datastores and Datacenters
+ #
+ # This helper will return a Hash: { :datastore => NAME_OF_DS, :instance => VM }
+ # Returning datastore is necesarry for constructing a correct realm for an
+ # instance
+ #
+ def find_vm(credentials, name)
+ vsphere = new_client(credentials)
+ safely do
+ rootFolder = vsphere.serviceInstance.content.rootFolder
+ vm = {}
+ rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
+ dc.datastoreFolder.childEntity.collect do |datastore|
+ vm[:instance] = datastore.vm.find { |x| x.name == name }
+ if vm[:instance]
+ vm[:datastore] = datastore.name
+ break
+ end
+ end
+ break if [:datastore]
+ end
+ vm
+ end
+ end
+
+ # Find a ResourcePool[1] object associated by given Datastore
+ # ResourcePool is defined for Datacenter and is used for launching a new
+ # instance
+ #
+ # [1] http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.ResourcePool.html
+ #
+ def find_resource_pool(credentials, name)
+ vsphere = new_client(credentials)
+ safely do
+ rootFolder = vsphere.serviceInstance.content.rootFolder
+ dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).select do |dc|
+ dc.datastoreFolder.childEntity.find { |d| d.name == name }.nil? == false
+ end.flatten.compact.first
+ dc.hostFolder.childEntity.collect.first.resourcePool
+ end
+ end
+
+ # This helper will try to find a Datastore[1] object in all Datacenters.
+ # Datastore is used to place instance on create to correct place
+ #
+ # [1] http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.Datastore.html
+ #
+ def find_datastore(credentials, name)
+ vsphere = new_client(credentials)
+ safely do
+ rootFolder = vsphere.serviceInstance.content.rootFolder
+ rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect do |dc|
+ dc.datastoreFolder.childEntity.find { |d| d.name == name }
+ end.flatten.compact.first
+ end
+ end
+
+ # This helper will traverse across all datacenters and datastores and gather
+ # all virtual machines available on vSphere
+ #
+ def list_virtual_machines(credentials)
+ vsphere = new_client(credentials)
+ vms = []
+ rootFolder = vsphere.serviceInstance.content.rootFolder
+ rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
+ dc.datastoreFolder.childEntity.collect do |datastore|
+ vms += datastore.vm.collect { |vm| { :instance => vm, :datastore => datastore.name } }
+ end
+ end
+ vms.flatten.compact
+ end
+
+ def map_task_to_instance(task_key, new_instance)
+ FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
+ File::open(File::join(MAPPER_STORAGE_ROOT, task_key), "w") do |f|
+ f.puts(YAML::dump(new_instance))
+ end
+ new_instance
+ end
+
+ def load_serialized_instance(task_key)
+ FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
+ YAML::load(File::read(File::join(MAPPER_STORAGE_ROOT, task_key))) rescue nil
+ end
+
+ # Yield all tasks if they are included in mapper storage directory.
+ def stored_tasks(vsphere)
+ FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
+ tasks = Dir[File::join(MAPPER_STORAGE_ROOT, '*')].collect { |file| File::basename(file) }
+ vsphere.serviceInstance.content.taskManager.recentTask.each do |task|
+ if tasks.include?(task.info.key)
+ yield task
+ else
+ # If given task is not longer listed in 'recentTasks' delete the
+ # mapper file
+ FileUtils::rm_rf(File::join(MAPPER_STORAGE_ROOT, task.info.key))
+ end
+ end
+ end
+
+ def extract_architecture(text)
+ 'x86_64' if text.include?('64-bit')
+ 'i386' if text.include?('32-bit')
+ end
+
+
+ end
+
+end
diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
index 8c3c3e0..3a502e0 100644
--- a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
+++ b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
@@ -15,6 +15,7 @@
#
require 'deltacloud/base_driver'
+require 'deltacloud/drivers/vsphere/vsphere_client'
require 'rbvmomi'
module Deltacloud::Drivers::VSphere
@@ -23,35 +24,25 @@ module Deltacloud::Drivers::VSphere
class VSphereDriver < Deltacloud::BaseDriver
- # Set of predefined hardware profiles
- define_hardware_profile('small') do
- cpu 1
- memory 256
- architecture ['x86_64', 'i386']
- end
-
- define_hardware_profile('medium') do
- cpu 1
- memory 512
- architecture ['x86_64', 'i386']
- end
+ include Deltacloud::Drivers::VSphere::Helper
- define_hardware_profile('large') do
- cpu 2
- memory 1024
- architecture ['x86_64', 'i386']
- end
+ feature :instances, :user_data
- define_hardware_profile('x-large') do
- cpu 4
- memory 2048
- architecture ['x86_64', 'i386']
- end
-
- # Since user can launch own instance using vSphere tools
- # with customized properties, threat this hardware profile as
- # unknown
- define_hardware_profile('custom') do
+ def hardware_profiles(credentials, opts={})
+ vsphere = new_client(credentials)
+ safely do
+ service = vsphere.serviceInstance.content
+ max_memory, max_cpu_cores = 0, 0
+ service.rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
+ max_memory += dc.hostFolder.childEntity.first.summary.effectiveMemory
+ max_cpu_cores += dc.hostFolder.childEntity.first.summary.numCpuCores
+ end
+ [Deltacloud::HardwareProfile::new('default') do
+ cpu (1..max_cpu_cores)
+ memory (128..max_memory)
+ architecture ['x86_64', 'i386']
+ end]
+ end
end
# Configure instance state machine
@@ -92,10 +83,11 @@ module Deltacloud::Drivers::VSphere
:full_name => config[:guestFullName]
}
image_state = convert_state(:image, image.summary.runtime[:powerState])
+ image_architecture = extract_architecture(properties[:full_name]) || 'x86_64'
Image.new(
:id => properties[:name],
:name => properties[:name],
- :architecture => 'x86_64', # FIXME: I'm not sure if all templates/VM's in vSphere are x86_64
+ :architecture => image_architecture,
:owner_id => credentials.user,
:description => properties[:full_name],
:state => image_state
@@ -167,7 +159,7 @@ module Deltacloud::Drivers::VSphere
:full_name => config[:guestFullName],
}
instance_state = convert_state(:instance, vm.summary.runtime[:powerState])
- instance_profile = InstanceProfile::new(match_hwp_id(:memory => properties[:memory].to_s, :cpus => properties[:cpus].to_s),
+ instance_profile = InstanceProfile::new('default',
:hwp_cpu => properties[:cpus],
:hwp_memory => properties[:memory],
:hwp_storage => properties[:storage])
@@ -212,28 +204,54 @@ module Deltacloud::Drivers::VSphere
end
relocate = { :pool => resourcePool, :datastore => datastore }
relocateSpec = RbVmomi::VIM.VirtualMachineRelocateSpec(relocate)
- instance_profile = hardware_profiles(credentials, :id => opts[:hwp_id]).first
+ # Set extra configuration for VM, like template_id
+ machine_config = {
+ :memoryMB => opts[:hwp_memory],
+ :numCPUs => opts[:hwp_cpu],
+ :extraConfig => [
+ { :key => 'template_id', :value => image_id }
+ ]
+ }
+ # If user wants to inject data into instance he need to submit a Base64
+ # encoded gzipped ISO image.
+ # This image will be uplaoded to the Datastore given in 'realm_id'
+ # parameter and them attached to instance.
+ if opts[:user_data] and not opts[:user_data].empty?
+ device = vm[:instance].config.hardware.device.select { |hw| hw.class == RbVmomi::VIM::VirtualCdrom }.first
+ if device
+ # TODO: Upload baked ISO image to the Datastore
+ device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] test.iso")
+ machine_config.merge!({
+ :deviceChange => [{
+ :operation => :edit,
+ :device => device
+ }]
+ })
+ else
+ raise "Failed to inject data to device because there is no CD-ROM drive defined in given template"
+ end
+ end
spec = RbVmomi::VIM.VirtualMachineCloneSpec(
:location => relocateSpec,
:powerOn => true,
:template => false,
- :config => RbVmomi::VIM.VirtualMachineConfigSpec(
- :memoryMB => instance_profile.memory.value,
- :numCPUs => instance_profile.cpu.value,
- :extraConfig => [
- { :key => 'template_id', :value => image_id }
- ]
- )
+ :config => RbVmomi::VIM.VirtualMachineConfigSpec(machine_config)
)
+ instance_profile = InstanceProfile::new('default', :hwp_memory => opts[:hwp_memory], :hwp_cpu => opts[:hwp_cpu])
task = vm[:instance].CloneVM_Task(:folder => vm[:instance].parent, :name => opts[:name], :spec => spec)
new_instance = Instance::new(
:id => opts[:name],
:name => opts[:name],
+ :description => opts[:name],
:owner_id => credentials.user,
+ :image_id => opts[:image_id],
:realm_id => opts[:realm_id] || vm[:datastore],
:state => 'PENDING',
- :instance_profile => InstanceProfile::new(instance_profile.name),
- :actions => instance_actions_for( 'PENDING' )
+ :public_addresses => [],
+ :private_addresses => [],
+ :instance_profile => instance_profile,
+ :actions => instance_actions_for( 'PENDING' ),
+ :create_image => false
)
map_task_to_instance(task.info.key, new_instance)
end
@@ -298,105 +316,6 @@ module Deltacloud::Drivers::VSphere
endpoint || Deltacloud::Drivers::driver_config[:vsphere][:entrypoints]['default']['default']
end
- def map_task_to_instance(task_key, new_instance)
- FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
- File::open(File::join(MAPPER_STORAGE_ROOT, task_key), "w") do |f|
- f.puts(YAML::dump(new_instance))
- end
- new_instance
- end
-
- def load_serialized_instance(task_key)
- YAML::load(File::read(File::join(MAPPER_STORAGE_ROOT, task_key)))
- end
-
- # Yield all tasks if they are included in mapper storage directory.
- def stored_tasks(vsphere)
- FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
- tasks = Dir[File::join(MAPPER_STORAGE_ROOT, '*')].collect { |file| File::basename(file) }
- vsphere.serviceInstance.content.taskManager.recentTask.each do |task|
- if tasks.include?(task.info.key)
- yield task
- else
- # If given task is not longer listed in 'recentTasks' delete the
- # mapper file
- FileUtils::rm_rf(File::join(MAPPER_STORAGE_ROOT, task.info.key))
- end
- end
- end
-
- # This helper will traverse across all datacenters and datastores and gather
- # all virtual machines available on vSphere
- #
- def list_virtual_machines(credentials)
- vsphere = new_client(credentials)
- vms = []
- rootFolder = vsphere.serviceInstance.content.rootFolder
- rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
- dc.datastoreFolder.childEntity.collect do |datastore|
- vms += datastore.vm.collect { |vm| { :instance => vm, :datastore => datastore.name } }
- end
- end
- vms.flatten.compact
- end
-
- # This helper will try to find a Datastore[1] object in all Datacenters.
- # Datastore is used to place instance on create to correct place
- #
- # [1] http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.Datastore.html
- #
- def find_datastore(credentials, name)
- vsphere = new_client(credentials)
- safely do
- rootFolder = vsphere.serviceInstance.content.rootFolder
- rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect do |dc|
- dc.datastoreFolder.childEntity.find { |d| d.name == name }
- end.flatten.compact.first
- end
- end
-
- # Find a ResourcePool[1] object associated by given Datastore
- # ResourcePool is defined for Datacenter and is used for launching a new
- # instance
- #
- # [1] http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.ResourcePool.html
- #
- def find_resource_pool(credentials, name)
- vsphere = new_client(credentials)
- safely do
- rootFolder = vsphere.serviceInstance.content.rootFolder
- dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).select do |dc|
- dc.datastoreFolder.childEntity.find { |d| d.name == name }.nil? == false
- end.flatten.compact.first
- dc.hostFolder.childEntity.collect.first.resourcePool
- end
- end
-
- # Find a VirtualMachine traversing through all Datastores and Datacenters
- #
- # This helper will return a Hash: { :datastore => NAME_OF_DS, :instance => VM }
- # Returning datastore is necesarry for constructing a correct realm for an
- # instance
- #
- def find_vm(credentials, name)
- vsphere = new_client(credentials)
- safely do
- rootFolder = vsphere.serviceInstance.content.rootFolder
- vm = {}
- rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
- dc.datastoreFolder.childEntity.collect do |datastore|
- vm[:instance] = datastore.vm.find { |x| x.name == name }
- if vm[:instance]
- vm[:datastore] = datastore.name
- break
- end
- end
- break if [:datastore]
- end
- vm
- end
- end
-
def convert_realm(datastore)
Realm::new(
:id => datastore.name,
@@ -424,16 +343,6 @@ module Deltacloud::Drivers::VSphere
new_state
end
- # Match hardware profile ID against given properties
- def match_hwp_id(prop)
- return 'small' if prop[:memory] == '256' and prop[:cpus] == '1'
- return 'medium' if prop[:memory] == '512' and prop[:cpus] == '1'
- return 'large' if prop[:memory] == '1024' and prop[:cpus] == '2'
- return 'x-large' if prop[:memory] == '2048' and prop[:cpus] == '4'
- 'unknown'
- end
-
-
end
end
--
1.7.4.1
Re: [PATCH core 4/4] Removed local mappings and switched to storing
mappings in remote datastore.
Posted by Francesco Vollero <ra...@gmail.com>.
On Thu, Jul 21, 2011 at 1:06 PM, Michal Fojtik <mf...@redhat.com> wrote:
>
> On Jul 21, 2011, at 12:44 AM, Francesco Vollero wrote:
>
>> On Thu, Jul 21, 2011 at 12:29 AM, David Lutterkort <lu...@redhat.com> wrote:
>>> On Tue, 2011-07-19 at 16:19 +0200, mfojtik@redhat.com wrote:
>>>> From: Michal Fojtik <mf...@redhat.com>
>>>
>>> I have one nit with this patch series:
>>>
>>>> + # You can use 'user_data' feature to set 'user_data' parameter when creating
>>>> + # a new instance where this parameter can hold gzipped CDROM iso which will
>>>> + # be mounted into created instance after boot
>>>> feature :instances, :user_data
>>>
>>> We can't call this ISO upload 'user_data' in the API - there are very
>>> different expectations around what that data is for vSphere than for
>>> EC2/Euca.
>>>
>>> Instead, we should call this feature something else, say 'user_iso', and
>>> accept a different parameter for it.
>>>
>>
>> Tomorrow with Michal we gonna analyze the whole suggestion.
>>
>>> Ideally, we could also support the feature user_data straightup, with
>>> the effect that we will create an ISO from it, place the user-supplied
>>> data inside that ISO with a fixed file name, and attach that as the
>>> CD-ROM.
>>>
>>
>> Would be a bit tricky, because we need to deal with temporary iso file
>> and this would break our choice to not store anything on deltacloud
>> system, but tomorrow we start doing a brainstorming on that too :)
>
> + I think we will need to call 'mkisofs' or something similar in order
> to build an ISO image, which will not work on Windows (except user will
> install this utility via cygwin)
>
Yep. Exactly. Or use genisoimage that is a simple interface to
mkisofs, linux-compatible but not windows compatible.
> Instead of this I would prefer to use 'user_data' feature to set a 'extraConfig'
> parameter for VM. Then script inside instance can eventually connect to the VSphere
> SOAP API and fetch this data. I think in EC2 they use same behavior.
>
> -- Michal
>
> ------------------------------------------------------
> Michal Fojtik, mfojtik@redhat.com
> Deltacloud API: http://deltacloud.org
>
>
Re: [PATCH core 4/4] Removed local mappings and switched to storing
mappings in remote datastore.
Posted by David Lutterkort <lu...@redhat.com>.
On Thu, 2011-07-21 at 13:06 +0200, Michal Fojtik wrote:
> > Would be a bit tricky, because we need to deal with temporary iso file
> > and this would break our choice to not store anything on deltacloud
> > system, but tomorrow we start doing a brainstorming on that too :)
>
> + I think we will need to call 'mkisofs' or something similar in order
> to build an ISO image, which will not work on Windows (except user will
> install this utility via cygwin)
Yes, but for now, having this work only on Linux would be fine by me;
wecan sort out what needs to happen in Windows later. The important
thing is that we want to offer the same user_data interface across as
many clouds as possible.
> Instead of this I would prefer to use 'user_data' feature to set a 'extraConfig'
> parameter for VM. Then script inside instance can eventually connect to the VSphere
> SOAP API and fetch this data. I think in EC2 they use same behavior.
I've spent quite a bit of time trying to get that to work, but haven't
been able to. The only extraConfig that you can access within the guest
are properties called 'guestinfo.*', but it seems they can only be set
on running instances. A beer to anybody who proves me wrong on this.
David
Re: [PATCH core 4/4] Removed local mappings and switched to storing mappings in remote datastore.
Posted by Michal Fojtik <mf...@redhat.com>.
On Jul 21, 2011, at 12:44 AM, Francesco Vollero wrote:
> On Thu, Jul 21, 2011 at 12:29 AM, David Lutterkort <lu...@redhat.com> wrote:
>> On Tue, 2011-07-19 at 16:19 +0200, mfojtik@redhat.com wrote:
>>> From: Michal Fojtik <mf...@redhat.com>
>>
>> I have one nit with this patch series:
>>
>>> + # You can use 'user_data' feature to set 'user_data' parameter when creating
>>> + # a new instance where this parameter can hold gzipped CDROM iso which will
>>> + # be mounted into created instance after boot
>>> feature :instances, :user_data
>>
>> We can't call this ISO upload 'user_data' in the API - there are very
>> different expectations around what that data is for vSphere than for
>> EC2/Euca.
>>
>> Instead, we should call this feature something else, say 'user_iso', and
>> accept a different parameter for it.
>>
>
> Tomorrow with Michal we gonna analyze the whole suggestion.
>
>> Ideally, we could also support the feature user_data straightup, with
>> the effect that we will create an ISO from it, place the user-supplied
>> data inside that ISO with a fixed file name, and attach that as the
>> CD-ROM.
>>
>
> Would be a bit tricky, because we need to deal with temporary iso file
> and this would break our choice to not store anything on deltacloud
> system, but tomorrow we start doing a brainstorming on that too :)
+ I think we will need to call 'mkisofs' or something similar in order
to build an ISO image, which will not work on Windows (except user will
install this utility via cygwin)
Instead of this I would prefer to use 'user_data' feature to set a 'extraConfig'
parameter for VM. Then script inside instance can eventually connect to the VSphere
SOAP API and fetch this data. I think in EC2 they use same behavior.
-- Michal
------------------------------------------------------
Michal Fojtik, mfojtik@redhat.com
Deltacloud API: http://deltacloud.org
Re: [PATCH core 4/4] Removed local mappings and switched to storing
mappings in remote datastore.
Posted by Francesco Vollero <ra...@gmail.com>.
On Thu, Jul 21, 2011 at 12:29 AM, David Lutterkort <lu...@redhat.com> wrote:
> On Tue, 2011-07-19 at 16:19 +0200, mfojtik@redhat.com wrote:
>> From: Michal Fojtik <mf...@redhat.com>
>
> I have one nit with this patch series:
>
>> + # You can use 'user_data' feature to set 'user_data' parameter when creating
>> + # a new instance where this parameter can hold gzipped CDROM iso which will
>> + # be mounted into created instance after boot
>> feature :instances, :user_data
>
> We can't call this ISO upload 'user_data' in the API - there are very
> different expectations around what that data is for vSphere than for
> EC2/Euca.
>
> Instead, we should call this feature something else, say 'user_iso', and
> accept a different parameter for it.
>
Tomorrow with Michal we gonna analyze the whole suggestion.
> Ideally, we could also support the feature user_data straightup, with
> the effect that we will create an ISO from it, place the user-supplied
> data inside that ISO with a fixed file name, and attach that as the
> CD-ROM.
>
Would be a bit tricky, because we need to deal with temporary iso file
and this would break our choice to not store anything on deltacloud
system, but tomorrow we start doing a brainstorming on that too :)
Francesco
Re: [PATCH core 4/4] Removed local mappings and switched to storing
mappings in remote datastore.
Posted by David Lutterkort <lu...@redhat.com>.
On Tue, 2011-07-19 at 16:19 +0200, mfojtik@redhat.com wrote:
> From: Michal Fojtik <mf...@redhat.com>
I have one nit with this patch series:
> + # You can use 'user_data' feature to set 'user_data' parameter when creating
> + # a new instance where this parameter can hold gzipped CDROM iso which will
> + # be mounted into created instance after boot
> feature :instances, :user_data
We can't call this ISO upload 'user_data' in the API - there are very
different expectations around what that data is for vSphere than for
EC2/Euca.
Instead, we should call this feature something else, say 'user_iso', and
accept a different parameter for it.
Ideally, we could also support the feature user_data straightup, with
the effect that we will create an ISO from it, place the user-supplied
data inside that ISO with a fixed file name, and attach that as the
CD-ROM.
David
[PATCH core 4/4] Removed local mappings and switched to storing mappings in remote datastore.
Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>
Signed-off-by: Michal fojtik <mf...@redhat.com>
---
.../deltacloud/drivers/vsphere/vsphere_client.rb | 45 ++++---
.../deltacloud/drivers/vsphere/vsphere_driver.rb | 76 +++++++----
.../drivers/vsphere/vsphere_filemanager.rb | 146 ++++++++++++++++++++
3 files changed, 225 insertions(+), 42 deletions(-)
create mode 100644 server/lib/deltacloud/drivers/vsphere/vsphere_filemanager.rb
diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
index 7e5cb4a..3eadbd9 100644
--- a/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
+++ b/server/lib/deltacloud/drivers/vsphere/vsphere_client.rb
@@ -1,5 +1,7 @@
module Deltacloud::Drivers::VSphere
+ require 'deltacloud/drivers/vsphere/vsphere_filemanager'
+
module Helper
# Find a VirtualMachine traversing through all Datastores and Datacenters
@@ -20,6 +22,11 @@ module Deltacloud::Drivers::VSphere
vm[:datastore] = datastore.name
break
end
+ stored_tasks(datastore, vsphere) do |task|
+ if task.info.entity.class == RbVmomi::VIM::VirtualMachine and ['queued', 'running'].member? task.info.state
+ vm = { :stored_instance => load_serialized_instance(datastore,task.info.key), :datastore => datastore.name }
+ end
+ end
end
break if [:datastore]
end
@@ -67,39 +74,43 @@ module Deltacloud::Drivers::VSphere
vms = []
rootFolder = vsphere.serviceInstance.content.rootFolder
rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
- dc.datastoreFolder.childEntity.collect do |datastore|
+ dc.datastoreFolder.childEntity.each do |datastore|
vms += datastore.vm.collect { |vm| { :instance => vm, :datastore => datastore.name } unless vm.nil? }
+ stored_tasks(datastore, vsphere) do |task|
+ if task.info.entity.class == RbVmomi::VIM::VirtualMachine
+ vms << { :stored_instance => load_serialized_instance(datastore, task.info.key), :datastore => datastore.name }
+ end
+ end
end
end
vms.flatten.compact
end
- def map_task_to_instance(task_key, new_instance)
- FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
- File::open(File::join(MAPPER_STORAGE_ROOT, task_key), "w") do |f|
- f.puts(YAML::dump(new_instance))
- end
+ # Map given instance to task. Task name is used as a filename.
+ #
+ def map_task_to_instance(datastore, task_key, new_instance)
+ VSphere::FileManager::store_mapping!(datastore, YAML::dump(new_instance).to_s, task_key)
new_instance
end
- def load_serialized_instance(task_key)
- FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
- YAML::load(File::read(File::join(MAPPER_STORAGE_ROOT, task_key))) rescue nil
+ def load_serialized_instance(datastore, task_key)
+ VSphere::FileManager::load_mapping(datastore, task_key)
end
# Yield all tasks if they are included in mapper storage directory.
- def stored_tasks(vsphere)
- FileUtils::mkdir_p(MAPPER_STORAGE_ROOT) unless File::directory?(MAPPER_STORAGE_ROOT)
- tasks = Dir[File::join(MAPPER_STORAGE_ROOT, '*')].collect { |file| File::basename(file) }
+ def stored_tasks(datastore, vsphere)
+ tasks = VSphere::FileManager::list_mappings(datastore)
+ return [] if tasks.empty?
vsphere.serviceInstance.content.taskManager.recentTask.each do |task|
- if tasks.include?(task.info.key)
+ if tasks.include?(task.info.key) and ['queued', 'running'].member?(task.info.state)
yield task
- else
- # If given task is not longer listed in 'recentTasks' delete the
- # mapper file
- FileUtils::rm_rf(File::join(MAPPER_STORAGE_ROOT, task.info.key))
+ tasks.delete(task.info.key)
end
end
+ # Delete old left tasks
+ tasks.select { |f| f =~ /task-(\d+)/ }.each do |task|
+ VSphere::FileManager::delete_mapping!(datastore, task)
+ end
end
def extract_architecture(text)
diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
index 971bbbb..20cf483 100644
--- a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
+++ b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
@@ -15,8 +15,8 @@
#
require 'deltacloud/base_driver'
-require 'deltacloud/drivers/vsphere/vsphere_client'
require 'rbvmomi'
+require 'deltacloud/drivers/vsphere/vsphere_client'
module Deltacloud::Drivers::VSphere
@@ -25,10 +25,17 @@ module Deltacloud::Drivers::VSphere
class VSphereDriver < Deltacloud::BaseDriver
include Deltacloud::Drivers::VSphere::Helper
+ include Deltacloud::Drivers::VSphere::FileManager
+ # You can use 'user_data' feature to set 'user_data' parameter when creating
+ # a new instance where this parameter can hold gzipped CDROM iso which will
+ # be mounted into created instance after boot
feature :instances, :user_data
feature :instances, :user_name
+ # There is just one hardware profile where memory is measured using maximum
+ # memory available on ESX for virtual machines and CPU using maximum free
+ # CPU cores in ESX.
def hardware_profiles(credentials, opts={})
vsphere = new_client(credentials)
safely do
@@ -58,8 +65,8 @@ module Deltacloud::Drivers::VSphere
end
- # List all images, across all datacenters. Note: Deltacloud API does not
- # yet support filtering images by realm.
+ # Images are virtual machines with 'template' flag set to be true.
+ # Thus we're getting them using find_vm and list_virtual_machines
def images(credentials, opts=nil)
cloud = new_client(credentials)
img_arr = []
@@ -68,21 +75,23 @@ module Deltacloud::Drivers::VSphere
# attribute is set
safely do
if opts[:id]
- template_vms = [ find_vm(credentials, opts[:id]) ].compact
+ template_vms = [ find_vm(credentials, opts[:id]) ].select { |vm| vm[:instance] }.compact
else
- template_vms = list_virtual_machines(credentials).select { |vm| vm[:instance].summary.config[:template] }
+ template_vms = list_virtual_machines(credentials).select { |vm| vm[:instance] && vm[:instance].summary.config[:template] }
end
img_arr = template_vms.collect do |image_hash|
- # Since all calls to vm are threaten as SOAP calls, reduce them using
- # local variable.
image, realm = image_hash[:instance], image_hash[:datastore]
config = image.summary.config
instance_state = convert_state(:instance, image.summary.runtime[:powerState])
+ # Preload all properties to save multiple SOAP calls to vSphere
properties = {
:name => config[:name],
:full_name => config[:guestFullName]
}
image_state = convert_state(:image, image.summary.runtime[:powerState])
+ # This will determine image architecture using image description.
+ # Ussualy description include '64-bit' or '32-bit'. In case you used
+ # some weird template/OS it will fallback to 64 bit
image_architecture = extract_architecture(properties[:full_name]) || 'x86_64'
Image.new(
:id => properties[:name],
@@ -126,18 +135,17 @@ module Deltacloud::Drivers::VSphere
# not yet support filtering instances by realm.
def instances(credentials, opts=nil)
cloud = new_client(credentials)
- inst_arr, machine_vms, stored_vms = [], [], []
+ inst_arr, machine_vms, pending_vms = [], [], []
safely do
+ # Using find_vm is a way faster than listing all virtual machines
if opts[:id]
- machine_vms = [ find_vm(credentials, opts[:id]) ].compact
+ list_vms = [ find_vm(credentials, opts[:id]) ].compact
else
- machine_vms = list_virtual_machines(credentials).select { |vm| !vm[:instance].summary.config[:template] }
- end
- stored_tasks(cloud) do |task|
- if task.info.entity.class == RbVmomi::VIM::VirtualMachine and ['queued', 'running'].member? task.info.state
- stored_vms << load_serialized_instance(task.info.key)
- end
+ list_vms = list_virtual_machines(credentials)
end
+ # Split machines to the 'real' one and PENDING one.
+ machine_vms = list_vms.select { |vm| vm[:instance] && !vm[:instance].summary.config[:template] }
+ pending_vms = list_vms.select { |vm| vm[:stored_instance] }.collect { |vm| vm[:stored_instance]}
end
safely do
inst_arr = machine_vms.collect do |vm_hash|
@@ -146,6 +154,8 @@ module Deltacloud::Drivers::VSphere
vm, realm_id = vm_hash[:instance], vm_hash[:datastore]
config = vm.summary.config
next if not config
+ # Template (image_id) is beeing stored as 'extraConfig' parameter in
+ # instance.
template_id = vm.config[:extraConfig].select { |k| k.key == 'template_id' }
template_id = template_id.first.value unless template_id.empty?
properties = {
@@ -160,6 +170,10 @@ module Deltacloud::Drivers::VSphere
:hwp_cpu => properties[:cpus],
:hwp_memory => properties[:memory],
:hwp_storage => properties[:storage])
+
+ # We're getting IP address from 'vmware guest tools'.
+ # If guest tools are not installed, we return list of MAC addresses
+ # assigned to this instance.
instance_address = vm.guest[:net].empty? ? vm.macs.values.first : vm.guest[:net].first[:ipAddress].first
Instance.new(
:id => properties[:name],
@@ -177,10 +191,12 @@ module Deltacloud::Drivers::VSphere
)
end
end
-
- # Append 'PENDING' instances
- inst_arr += stored_vms
inst_arr.compact!
+ # Append 'temporary' instances to real instances.
+ # 'Temporary' or 'stored' instance are used to speed up instance creation
+ # process by serializing instances to datastore and map instance to task.
+ #
+ inst_arr += pending_vms
filter_on( inst_arr, :state, opts )
end
@@ -190,8 +206,8 @@ module Deltacloud::Drivers::VSphere
safely do
rootFolder = vsphere.serviceInstance.content.rootFolder
vm = find_vm(credentials, opts[:image_id])
- # Find correct ResourcePool and Datastore where a new VM will be
- # located
+ # New instance need valid resource pool and datastore to be placed.
+ # For this reason, realm_id **needs** to be set.
if opts and opts[:realm_id]
resourcePool = find_resource_pool(credentials, opts[:realm_id])
datastore = find_datastore(credentials, opts[:realm_id])
@@ -216,11 +232,12 @@ module Deltacloud::Drivers::VSphere
if opts[:user_data] and not opts[:user_data].empty?
device = vm[:instance].config.hardware.device.select { |hw| hw.class == RbVmomi::VIM::VirtualCdrom }.first
if device
- # TODO: Upload baked ISO image to the Datastore
+ VSphere::FileManager::store_iso!(datastore, opts[:user_data], "#{opts[:name]}.iso")
machine_config[:extraConfig] << {
:key => 'user_data_file', :value => "#{opts[:name]}.iso"
}
- device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] #{opts[:name].iso}")
+ device.backing = RbVmomi::VIM.VirtualCdromIsoBackingInfo(:fileName => "[#{opts[:realm_id] || vm[:datastore]}] "+
+ "/#{VSphere::FileManager::DIRECTORY_PATH}/#{opts[:name]}.iso")
machine_config.merge!({
:deviceChange => [{
:operation => :edit,
@@ -253,7 +270,10 @@ module Deltacloud::Drivers::VSphere
:actions => instance_actions_for( 'PENDING' ),
:create_image => false
)
- map_task_to_instance(task.info.key, new_instance)
+ # This will 'serialize' instance to YAML file and map it to the task.
+ # Ussualy it takes like 2-3 minutes (depending on storage size) to
+ # complete instance cloning process.
+ map_task_to_instance(datastore, task.info.key, new_instance)
end
end
@@ -272,10 +292,16 @@ module Deltacloud::Drivers::VSphere
find_vm(credentials, id)[:instance].PowerOffVM_Task
end
- # Destroy an instance, given its id. Note that this will destory all
+ # Destroy an instance, given its id. Note that this will destroy all
# instance data.
+ #
+ # If there is user-data dile asocciated with instance, remove this file as
+ # well.
def destroy_instance(credentials, instance_id)
- find_vm(credentials, instance_id)[:instance].Destroy_Task.wait_for_completion
+ vm = find_vm(credentials, instance_id)
+ user_file = vm[:instance].config[:extraConfig].select { |k| k.key == 'user_data_file' }.first
+ VSphere::FileManager::delete_iso!(vm[:instance].send(:datastore).first, user_file.value) if user_file
+ vm[:instance].Destroy_Task.wait_for_completion
end
alias :destroy_image :destroy_instance
diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_filemanager.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_filemanager.rb
new file mode 100644
index 0000000..cd6460d
--- /dev/null
+++ b/server/lib/deltacloud/drivers/vsphere/vsphere_filemanager.rb
@@ -0,0 +1,146 @@
+require 'nokogiri'
+
+module VSphere
+ module FileManager
+
+
+ DIRECTORY_PATH="deltacloud"
+
+ RbVmomi::VIM::Datastore::class_eval do
+ def soap
+ @soap
+ end
+ end
+
+ class << self
+
+ def store_iso!(datastore,base64_iso, file_name)
+ file = StringIO.new(get_plain_iso(base64_iso).read)
+ uploadFile(datastore, file, file_name)
+ end
+
+ def delete_iso!(datastore,file_name)
+ deleteFile(datastore, file_name)
+ end
+
+ def store_mapping!(datastore, yaml_object, file_name)
+ file = StringIO::new(yaml_object)
+ uploadFile(datastore, file, file_name)
+ end
+
+ def delete_mapping!(datastore, file_name)
+ deleteFile(datastore, file_name)
+ end
+
+ def load_mapping(datastore, file_name)
+ YAML::load(downloadFile(datastore, file_name))
+ end
+
+ def list_mappings(datastore)
+ listFolder(datastore)
+ end
+
+ private
+
+ def make_directory!(datastore,directory)
+ dc=datastore.send(:datacenter)
+ dc._connection.serviceContent.fileManager.MakeDirectory :name => "[#{datastore.name}] #{directory}",
+ :datacenter => dc,
+ :createParentDirectories => false
+ end
+
+ def _exist?(datastore,file=nil)
+ uri = buildUrl(datastore,file) if file
+ uri = buildUrl(datastore) unless file
+ http = Net::HTTP.new(uri.host,uri.port)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ headers = {
+ 'cookie' => datastore.send(:soap).cookie,
+ }
+ request = Net::HTTP::Head.new(uri.request_uri,headers)
+ res = http.request(request)
+ if res.kind_of?(Net::HTTPSuccess)
+ return true
+ else
+ return false
+ end
+ end
+
+ def downloadFile(datastore,file_name)
+ @uri = buildUrl(datastore,file_name)
+ http=Net::HTTP.new(@uri.host, @uri.port)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ headers = { 'cookie' => datastore.send(:soap).cookie }
+ request = Net::HTTP::Get.new(@uri.request_uri, headers)
+ res = http.request(request)
+ raise "download failed: #{res.message}" unless res.kind_of?(Net::HTTPSuccess)
+ res.body
+ end
+
+ def deleteFile(datastore, file)
+ @uri = buildUrl(datastore, file)
+ http=Net::HTTP.new(@uri.host, @uri.port)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ headers = { 'cookie' => datastore.send(:soap).cookie }
+ request = Net::HTTP::Delete.new(@uri.request_uri, headers)
+ res = http.request(request)
+ unless res.kind_of?(Net::HTTPSuccess) or res.kind_of?(Net::HTTPServiceUnavailable) or res.kind_of?(Net::HTTPNotFound)
+ raise "delete failed: #{res.message} #{file}"
+ end
+ end
+
+ def listFolder(datastore, folder="")
+ @uri = buildUrl(datastore, folder)
+ http=Net::HTTP.new(@uri.host, @uri.port)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ headers = { 'cookie' => datastore.send(:soap).cookie }
+ request = Net::HTTP::Get.new(@uri.request_uri, headers)
+ begin
+ res = http.request(request)
+ Nokogiri::HTML(res.body).css("table tr a").map { |f| f.text.strip }.reject { |f| f == 'Parent Directory'}
+ rescue
+ puts "[ERROR]: Unable to list deltacloud folder"
+ []
+ end
+ end
+
+ def uploadFile(datastore,file,file_name)
+ make_directory!(datastore,DIRECTORY_PATH) unless _exist?(datastore)
+ @uri = buildUrl(datastore,file_name)
+ http=Net::HTTP.new(@uri.host, @uri.port)
+ http.use_ssl = true
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
+ headers = {
+ 'cookie' => datastore.send(:soap).cookie,
+ 'content-length' => file.size.to_s,
+ 'Content-Type' => 'application/octet-stream',
+ }
+ request = Net::HTTP::Put.new(@uri.request_uri, headers)
+ request.body_stream = file
+ res = http.request(request)
+ raise "upload failed: #{res.message}" unless res.kind_of?(Net::HTTPSuccess)
+ end
+
+ # return the url like https://<server_address>/folder/<path>/<file_name>?<query_infos>
+
+ def buildUrl(datastore,file="")
+ uri=URI::HTTPS::build(:host=>datastore._connection.http.address)
+ uri.path= ["/folder",DIRECTORY_PATH,file].join("/") if file
+ query={:dcPath => datastore.send(:datacenter).name, :dsName => datastore.name }
+ uri.query=query.collect{|name, value| "#{name}=#{URI.escape value}"}.join("&")
+ uri
+ end
+
+ def get_plain_iso(stream)
+ unbase64file=stream.unpack('m').to_s
+ Zlib::GzipReader.new(StringIO.new(unbase64file))
+ end
+
+ end
+
+ end
+end
--
1.7.4.1