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