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/06/17 11:33:32 UTC

Initial VSphere driver support for Deltacloud API (rev 4)

Hi,

Changelog for -4:

 - Support for Datastore folders (all new instances could be placed to datastore
   given as 'realm_id')

 - Create instance is still incedibely slow, we should really consider some
   mapper for this.

 -- Michal



Re: [PATCH core 1/2] Initial VSphere driver support for Deltacloud API

Posted by Francesco Vollero <fv...@redhat.com>.
Ack'd but I had to add the storage field from nil to a bogus value, since seems that conductor does not manage it well.


On Fri, Jun 17, 2011 at 11:33:33AM +0200, mfojtik@redhat.com wrote:
> From: David Lutterkort <lu...@redhat.com>
> 
> 
> Signed-off-by: Michal fojtik <mf...@redhat.com>
> ---
>  server/config/drivers.yaml                         |    7 +
>  .../deltacloud/drivers/vsphere/vsphere_driver.rb   |  340 ++++++++++++++++++++
>  2 files changed, 347 insertions(+), 0 deletions(-)
>  create mode 100644 server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
> 
> diff --git a/server/config/drivers.yaml b/server/config/drivers.yaml
> index 20b2857..4d06d93 100644
> --- a/server/config/drivers.yaml
> +++ b/server/config/drivers.yaml
> @@ -46,3 +46,10 @@
>        eu-west-1: ec2.eu-west-1.amazonaws.com
>        us-east-1: ec2.us-east-1.amazonaws.com
>    :name: EC2
> +:vsphere:
> +  :name: VSphere
> +  :username: Login Name
> +  :password: Password
> +  :entrypoints:
> +    default:
> +      default: "https://vsphere.provider.com/sdk"
> diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
> new file mode 100644
> index 0000000..52835c9
> --- /dev/null
> +++ b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
> @@ -0,0 +1,340 @@
> +# Licensed to the Apache Software Foundation (ASF) under one or more
> +# contributor license agreements.  See the NOTICE file distributed with
> +# this work for additional information regarding copyright ownership.  The
> +# ASF licenses this file to you under the Apache License, Version 2.0 (the
> +# "License"); you may not use this file except in compliance with the
> +# License.  You may obtain a copy of the License at
> +#
> +#       http://www.apache.org/licenses/LICENSE-2.0
> +#
> +# Unless required by applicable law or agreed to in writing, software
> +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
> +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
> +# License for the specific language governing permissions and limitations
> +# under the License.
> +#
> +
> +require 'deltacloud/base_driver'
> +require 'rbvmomi'
> +
> +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'
> +    end
> +
> +    define_hardware_profile('medium') do
> +      cpu                1
> +      memory             512
> +      architecture       'x86_64'
> +    end
> +
> +    define_hardware_profile('large') do
> +      cpu                2
> +      memory             1024
> +      architecture       'x86_64'
> +    end
> +
> +    define_hardware_profile('x-large') do
> +      cpu                4
> +      memory             2048
> +      architecture       'x86_64'
> +    end
> +
> +    # Since user can launch own instance using vSphere tools 
> +    # with customized properties, threat this hardware profile as
> +    # unknown
> +    define_hardware_profile('unknown') do
> +      # NOTE: Memory and CPU should be set properly
> +      architecture       'x86_64'
> +    end
> +
> +    # Configure instance state machine
> +    define_instance_states do
> +      start.to(:pending)          .on( :create )
> +      pending.to(:stopped)        .automatically
> +      stopped.to(:running)        .on( :start )
> +      running.to(:running)        .on( :reboot )
> +      running.to(:shutting_down)  .on( :stop )
> +      shutting_down.to(:stopped)  .automatically
> +      stopped.to(:finish)         .on( :destroy )
> +    end
> +
> +
> +    # List all images, across all datacenters. Note: Deltacloud API does not
> +    # yet support filtering images by realm.
> +    def images(credentials, opts=nil)
> +      cloud = new_client(credentials)
> +      img_arr = []
> +
> +      # Skip traversing through all instances in all datacenters when ID
> +      # attribute is set
> +      safely do
> +        if opts[:id]
> +          template_vms = [ find_vm(credentials, opts[:id]) ].compact
> +        else
> +          template_vms = list_virtual_machines(credentials).select { |vm| vm.summary.config[:template] }
> +        end
> +
> +        img_arr = template_vms.collect do |image|
> +          # Since all calls to vm are threaten as SOAP calls, reduce them using
> +          # local variable.
> +          config = image.summary.config
> +          instance_state = convert_state(:instance, image.summary.runtime[:powerState])
> +          properties = {
> +            :name => config[:name],
> +            :full_name => config[:guestFullName]
> +          }
> +          image_state = convert_state(:image, image.summary.runtime[:powerState])
> +          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
> +            :owner_id => credentials.user,
> +            :description => properties[:full_name],
> +            :state => image_state
> +          )
> +        end
> +      end
> +
> +      img_arr = filter_on( img_arr, :architecture, opts )
> +      img_arr.sort_by{|e| [e.owner_id, e.name]}
> +    end
> +
> +    def create_image(credentials, opts={})
> +      vsphere = new_client(credentials)
> +      safely do
> +        find_vm(credentials, opts[:id]).MarkAsTemplate
> +      end
> +      image(credentials, :id => opts[:id])
> +    end
> +
> +    # List all datacenters managed by the vSphere or vCenter entrypoint.
> +    def realms(credentials, opts=nil)
> +      vsphere = new_client(credentials)
> +      safely do
> +        rootFolder = vsphere.serviceInstance.content.rootFolder
> +        rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect do |dc|
> +          Realm.new(
> +            :id => dc.name,
> +            :name => dc.name, 
> +            :limit => :unlimited,
> +            :state => convert_state(:datacenter, dc.configStatus)
> +          )
> +        end
> +      end
> +    end
> +
> +    # List all running instances, across all datacenters. DeltaCloud API does
> +    # not yet support filtering instances by realm.
> +    def instances(credentials, opts=nil)
> +      cloud = new_client(credentials)
> +      inst_arr, machine_vms = [], []
> +      safely do
> +        if opts[:id]
> +          machine_vms = [ find_vm(credentials, opts[:id]) ].compact
> +        else
> +          machine_vms = list_virtual_machines(credentials).select { |vm| !vm.summary.config[:template] }
> +        end
> +      end
> +      realm_id = realms(credentials).first.id
> +      safely do
> +        inst_arr = machine_vms.collect do |vm|
> +          # Since all calls to vm are threaten as SOAP calls, reduce them using
> +          # local variable.
> +          config = vm.summary.config
> +          next unless config
> +          next unless vm.summary.storage
> +          properties = {
> +            :memory => config[:memorySizeMB],
> +            :cpus => config[:numCpu],
> +            :storage => vm.summary.storage[:unshared],
> +            :name => config[:name],
> +            :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),
> +                                                  :hwp_cpu => properties[:cpus],
> +                                                  :hwp_memory => properties[:memory],
> +                                                  :hwp_storage => properties[:storage])
> +          instance_address = vm.guest[:net].empty? ? vm.macs.values.first : vm.guest[:net].first[:ipAddress].first
> +          Instance.new(
> +            :id => properties[:name],
> +            :name => properties[:name],
> +            :owner_id => credentials.user,
> +            :description => properties[:full_name],
> +            :realm_id => realm_id,
> +            :state => instance_state,
> +            :public_addresses => instance_address,
> +            :private_addresses => [],
> +            :instance_profile => instance_profile,
> +            :actions => instance_actions_for( instance_state ),
> +            :create_image => true
> +          )
> +        end
> +      end
> +      filter_on( inst_arr, :state, opts )
> +    end
> +
> +
> +    def create_instance(credentials, image_id, opts)
> +      vsphere = new_client(credentials)
> +      safely do
> +        rootFolder = vsphere.serviceInstance.content.rootFolder
> +        # FIXME: This will consume first datacenter and ignore 'realm' property
> +        dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect.first
> +        vm = dc.find_vm(image_id)
> +        resource_pool = dc.hostFolder.childEntity.collect.first.resourcePool
> +        relocateSpec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool)
> +        # NOTE: 'powerOn' attribute will force machine to start after clone operation
> +        #       'template' attribute will mark VM as a template if set to true
> +        spec = RbVmomi::VIM.VirtualMachineCloneSpec(
> +          :location => relocateSpec,
> +          :powerOn => true,
> +          :template => false,
> +          :config => RbVmomi::VIM.VirtualMachineConfigSpec(
> +            :memoryMB => 256,
> +            :numCPUs => 1
> +          )
> +        )
> +        # NOTE: This operation may take a very long time (about 1m) to complete
> +        puts "Cloning template #{image_id} to #{opts[:name]}..."
> +        vm.CloneVM_Task(:folder => vm.parent, :name => opts[:name], :spec => spec).wait_for_completion
> +        puts "Cloning complete!"
> +        # Since task itself is not returning anything, construct Instance from things we already have
> +        Instance::new(
> +          :id => opts[:name],
> +          :name => opts[:name],
> +          :owner_id => credentials.user,
> +          :realm_id => dc.name,
> +          :state => 'PENDING',
> +          :instance_profile => InstanceProfile::new('default'),
> +          :actions => instance_actions_for( 'PENDING' )
> +        )
> +      end
> +    end
> +
> +    # Reboot an instance, given its id.
> +    def reboot_instance(credentials, id)
> +      find_vm(credentials, id).ResetVM_Task
> +    end
> +
> +    # Start an instance, given its id.
> +    def start_instance(credentials, id)
> +      find_vm(credentials, id).PowerOnVM_Task
> +    end
> +
> +    # Stop an instance, given its id.
> +    def stop_instance(credentials, id)
> +      find_vm(credentials, id).PowerOffVM_Task
> +    end
> +
> +    # Destroy an instance, given its id. Note that this will destory all
> +    # instance data.
> +    def destroy_instance(credentials, id)
> +      find_vm(credentials, id).Destroy_Task.wait_for_completion
> +    end
> +
> +    exceptions do
> +
> +      on /InvalidLogin/ do
> +        status 401
> +      end
> +
> +      on /RbVmomi::Fault/ do
> +        status 502
> +      end
> +
> +    end
> +
> +    def valid_credentials?(credentials)
> +      begin
> +        RbVmomi::VIM.connect(:host => host_endpoint, :user => credentials.user, :password => credentials.password, :insecure => true)
> +        return true
> +      rescue
> +        return false
> +      end
> +    end
> +
> +    #######
> +    private
> +    #######
> +
> +    def new_client(credentials)
> +      safely do
> +        RbVmomi::VIM.connect(:host => host_endpoint, :user => credentials.user, :password => credentials.password, :insecure => true)
> +      end
> +    end
> +
> +    def host_endpoint
> +      endpoint = (Thread.current[:provider] || ENV['API_PROVIDER'])
> +      endpoint || Deltacloud::Drivers::driver_config[:vsphere][:entrypoints]['default']['default']
> +    end
> +
> +    def list_virtual_machines(credentials)
> +      vsphere = new_client(credentials)
> +      vms = []
> +      rootFolder = vsphere.serviceInstance.content.rootFolder
> +      rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
> +        vms += dc.vmFolder.childEntity.collect do |ent|
> +          if ent.class.name == 'RbVmomi::VIM::Folder'
> +            ent.childEntity.grep(RbVmomi::VIM::VirtualMachine).collect
> +          else
> +            ent
> +          end
> +        end
> +      end
> +      vms.flatten.compact
> +    end
> +
> +    def find_vm(credentials, name)
> +      vsphere = new_client(credentials)
> +      safely do
> +        rootFolder = vsphere.serviceInstance.content.rootFolder
> +        dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect.first
> +        dc.find_vm(name)
> +      end
> +    end
> +
> +    def convert_state(object, state)
> +      new_state = ''
> +      if object == :image
> +        new_state = case state
> +          when 'poweredOff' then 'AVAILABLE'
> +          when 'poweredOn' then 'UNAVAILABLE'
> +        end
> +      end
> +      if object == :instance
> +        new_state = case state
> +          when 'poweredOff' then 'STOPPED'
> +          when 'poweredOn' then 'RUNNING'
> +          else 'PENDING'
> +        end
> +      end
> +      if object == :datacenter
> +        new_state = case state
> +          when 'gray', 'green' then 'AVAILABLE'
> +          else 'UNAVAILABLE'
> +        end
> +      end
> +      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/2] Initial VSphere driver support for Deltacloud API

Posted by mf...@redhat.com.
From: David Lutterkort <lu...@redhat.com>


Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 server/config/drivers.yaml                         |    7 +
 .../deltacloud/drivers/vsphere/vsphere_driver.rb   |  340 ++++++++++++++++++++
 2 files changed, 347 insertions(+), 0 deletions(-)
 create mode 100644 server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb

diff --git a/server/config/drivers.yaml b/server/config/drivers.yaml
index 20b2857..4d06d93 100644
--- a/server/config/drivers.yaml
+++ b/server/config/drivers.yaml
@@ -46,3 +46,10 @@
       eu-west-1: ec2.eu-west-1.amazonaws.com
       us-east-1: ec2.us-east-1.amazonaws.com
   :name: EC2
+:vsphere:
+  :name: VSphere
+  :username: Login Name
+  :password: Password
+  :entrypoints:
+    default:
+      default: "https://vsphere.provider.com/sdk"
diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
new file mode 100644
index 0000000..52835c9
--- /dev/null
+++ b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
@@ -0,0 +1,340 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.  The
+# ASF licenses this file to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance with the
+# License.  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+require 'deltacloud/base_driver'
+require 'rbvmomi'
+
+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'
+    end
+
+    define_hardware_profile('medium') do
+      cpu                1
+      memory             512
+      architecture       'x86_64'
+    end
+
+    define_hardware_profile('large') do
+      cpu                2
+      memory             1024
+      architecture       'x86_64'
+    end
+
+    define_hardware_profile('x-large') do
+      cpu                4
+      memory             2048
+      architecture       'x86_64'
+    end
+
+    # Since user can launch own instance using vSphere tools 
+    # with customized properties, threat this hardware profile as
+    # unknown
+    define_hardware_profile('unknown') do
+      # NOTE: Memory and CPU should be set properly
+      architecture       'x86_64'
+    end
+
+    # Configure instance state machine
+    define_instance_states do
+      start.to(:pending)          .on( :create )
+      pending.to(:stopped)        .automatically
+      stopped.to(:running)        .on( :start )
+      running.to(:running)        .on( :reboot )
+      running.to(:shutting_down)  .on( :stop )
+      shutting_down.to(:stopped)  .automatically
+      stopped.to(:finish)         .on( :destroy )
+    end
+
+
+    # List all images, across all datacenters. Note: Deltacloud API does not
+    # yet support filtering images by realm.
+    def images(credentials, opts=nil)
+      cloud = new_client(credentials)
+      img_arr = []
+
+      # Skip traversing through all instances in all datacenters when ID
+      # attribute is set
+      safely do
+        if opts[:id]
+          template_vms = [ find_vm(credentials, opts[:id]) ].compact
+        else
+          template_vms = list_virtual_machines(credentials).select { |vm| vm.summary.config[:template] }
+        end
+
+        img_arr = template_vms.collect do |image|
+          # Since all calls to vm are threaten as SOAP calls, reduce them using
+          # local variable.
+          config = image.summary.config
+          instance_state = convert_state(:instance, image.summary.runtime[:powerState])
+          properties = {
+            :name => config[:name],
+            :full_name => config[:guestFullName]
+          }
+          image_state = convert_state(:image, image.summary.runtime[:powerState])
+          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
+            :owner_id => credentials.user,
+            :description => properties[:full_name],
+            :state => image_state
+          )
+        end
+      end
+
+      img_arr = filter_on( img_arr, :architecture, opts )
+      img_arr.sort_by{|e| [e.owner_id, e.name]}
+    end
+
+    def create_image(credentials, opts={})
+      vsphere = new_client(credentials)
+      safely do
+        find_vm(credentials, opts[:id]).MarkAsTemplate
+      end
+      image(credentials, :id => opts[:id])
+    end
+
+    # List all datacenters managed by the vSphere or vCenter entrypoint.
+    def realms(credentials, opts=nil)
+      vsphere = new_client(credentials)
+      safely do
+        rootFolder = vsphere.serviceInstance.content.rootFolder
+        rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect do |dc|
+          Realm.new(
+            :id => dc.name,
+            :name => dc.name, 
+            :limit => :unlimited,
+            :state => convert_state(:datacenter, dc.configStatus)
+          )
+        end
+      end
+    end
+
+    # List all running instances, across all datacenters. DeltaCloud API does
+    # not yet support filtering instances by realm.
+    def instances(credentials, opts=nil)
+      cloud = new_client(credentials)
+      inst_arr, machine_vms = [], []
+      safely do
+        if opts[:id]
+          machine_vms = [ find_vm(credentials, opts[:id]) ].compact
+        else
+          machine_vms = list_virtual_machines(credentials).select { |vm| !vm.summary.config[:template] }
+        end
+      end
+      realm_id = realms(credentials).first.id
+      safely do
+        inst_arr = machine_vms.collect do |vm|
+          # Since all calls to vm are threaten as SOAP calls, reduce them using
+          # local variable.
+          config = vm.summary.config
+          next unless config
+          next unless vm.summary.storage
+          properties = {
+            :memory => config[:memorySizeMB],
+            :cpus => config[:numCpu],
+            :storage => vm.summary.storage[:unshared],
+            :name => config[:name],
+            :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),
+                                                  :hwp_cpu => properties[:cpus],
+                                                  :hwp_memory => properties[:memory],
+                                                  :hwp_storage => properties[:storage])
+          instance_address = vm.guest[:net].empty? ? vm.macs.values.first : vm.guest[:net].first[:ipAddress].first
+          Instance.new(
+            :id => properties[:name],
+            :name => properties[:name],
+            :owner_id => credentials.user,
+            :description => properties[:full_name],
+            :realm_id => realm_id,
+            :state => instance_state,
+            :public_addresses => instance_address,
+            :private_addresses => [],
+            :instance_profile => instance_profile,
+            :actions => instance_actions_for( instance_state ),
+            :create_image => true
+          )
+        end
+      end
+      filter_on( inst_arr, :state, opts )
+    end
+
+
+    def create_instance(credentials, image_id, opts)
+      vsphere = new_client(credentials)
+      safely do
+        rootFolder = vsphere.serviceInstance.content.rootFolder
+        # FIXME: This will consume first datacenter and ignore 'realm' property
+        dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect.first
+        vm = dc.find_vm(image_id)
+        resource_pool = dc.hostFolder.childEntity.collect.first.resourcePool
+        relocateSpec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool)
+        # NOTE: 'powerOn' attribute will force machine to start after clone operation
+        #       'template' attribute will mark VM as a template if set to true
+        spec = RbVmomi::VIM.VirtualMachineCloneSpec(
+          :location => relocateSpec,
+          :powerOn => true,
+          :template => false,
+          :config => RbVmomi::VIM.VirtualMachineConfigSpec(
+            :memoryMB => 256,
+            :numCPUs => 1
+          )
+        )
+        # NOTE: This operation may take a very long time (about 1m) to complete
+        puts "Cloning template #{image_id} to #{opts[:name]}..."
+        vm.CloneVM_Task(:folder => vm.parent, :name => opts[:name], :spec => spec).wait_for_completion
+        puts "Cloning complete!"
+        # Since task itself is not returning anything, construct Instance from things we already have
+        Instance::new(
+          :id => opts[:name],
+          :name => opts[:name],
+          :owner_id => credentials.user,
+          :realm_id => dc.name,
+          :state => 'PENDING',
+          :instance_profile => InstanceProfile::new('default'),
+          :actions => instance_actions_for( 'PENDING' )
+        )
+      end
+    end
+
+    # Reboot an instance, given its id.
+    def reboot_instance(credentials, id)
+      find_vm(credentials, id).ResetVM_Task
+    end
+
+    # Start an instance, given its id.
+    def start_instance(credentials, id)
+      find_vm(credentials, id).PowerOnVM_Task
+    end
+
+    # Stop an instance, given its id.
+    def stop_instance(credentials, id)
+      find_vm(credentials, id).PowerOffVM_Task
+    end
+
+    # Destroy an instance, given its id. Note that this will destory all
+    # instance data.
+    def destroy_instance(credentials, id)
+      find_vm(credentials, id).Destroy_Task.wait_for_completion
+    end
+
+    exceptions do
+
+      on /InvalidLogin/ do
+        status 401
+      end
+
+      on /RbVmomi::Fault/ do
+        status 502
+      end
+
+    end
+
+    def valid_credentials?(credentials)
+      begin
+        RbVmomi::VIM.connect(:host => host_endpoint, :user => credentials.user, :password => credentials.password, :insecure => true)
+        return true
+      rescue
+        return false
+      end
+    end
+
+    #######
+    private
+    #######
+
+    def new_client(credentials)
+      safely do
+        RbVmomi::VIM.connect(:host => host_endpoint, :user => credentials.user, :password => credentials.password, :insecure => true)
+      end
+    end
+
+    def host_endpoint
+      endpoint = (Thread.current[:provider] || ENV['API_PROVIDER'])
+      endpoint || Deltacloud::Drivers::driver_config[:vsphere][:entrypoints]['default']['default']
+    end
+
+    def list_virtual_machines(credentials)
+      vsphere = new_client(credentials)
+      vms = []
+      rootFolder = vsphere.serviceInstance.content.rootFolder
+      rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).each do |dc|
+        vms += dc.vmFolder.childEntity.collect do |ent|
+          if ent.class.name == 'RbVmomi::VIM::Folder'
+            ent.childEntity.grep(RbVmomi::VIM::VirtualMachine).collect
+          else
+            ent
+          end
+        end
+      end
+      vms.flatten.compact
+    end
+
+    def find_vm(credentials, name)
+      vsphere = new_client(credentials)
+      safely do
+        rootFolder = vsphere.serviceInstance.content.rootFolder
+        dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect.first
+        dc.find_vm(name)
+      end
+    end
+
+    def convert_state(object, state)
+      new_state = ''
+      if object == :image
+        new_state = case state
+          when 'poweredOff' then 'AVAILABLE'
+          when 'poweredOn' then 'UNAVAILABLE'
+        end
+      end
+      if object == :instance
+        new_state = case state
+          when 'poweredOff' then 'STOPPED'
+          when 'poweredOn' then 'RUNNING'
+          else 'PENDING'
+        end
+      end
+      if object == :datacenter
+        new_state = case state
+          when 'gray', 'green' then 'AVAILABLE'
+          else 'UNAVAILABLE'
+        end
+      end
+      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 2/2] Added Datastore object as Realm so Instances could be now placed in different locations inside Datacenter.

Posted by mf...@redhat.com.
From: Michal Fojtik <mf...@redhat.com>


Signed-off-by: Michal fojtik <mf...@redhat.com>
---
 .../deltacloud/drivers/vsphere/vsphere_driver.rb   |  154 ++++++++++++++------
 1 files changed, 107 insertions(+), 47 deletions(-)

diff --git a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
index 52835c9..3a59ee2 100644
--- a/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
+++ b/server/lib/deltacloud/drivers/vsphere/vsphere_driver.rb
@@ -78,12 +78,13 @@ module Deltacloud::Drivers::VSphere
         if opts[:id]
           template_vms = [ find_vm(credentials, opts[:id]) ].compact
         else
-          template_vms = list_virtual_machines(credentials).select { |vm| vm.summary.config[:template] }
+          template_vms = list_virtual_machines(credentials).select { |vm| vm[:instance].summary.config[:template] }
         end
 
-        img_arr = template_vms.collect do |image|
+        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])
           properties = {
@@ -109,23 +110,23 @@ module Deltacloud::Drivers::VSphere
     def create_image(credentials, opts={})
       vsphere = new_client(credentials)
       safely do
-        find_vm(credentials, opts[:id]).MarkAsTemplate
+        find_vm(credentials, opts[:id])[:instance].MarkAsTemplate
       end
-      image(credentials, :id => opts[:id])
+      images(credentials, :id => opts[:id])
     end
 
     # List all datacenters managed by the vSphere or vCenter entrypoint.
     def realms(credentials, opts=nil)
       vsphere = new_client(credentials)
       safely do
-        rootFolder = vsphere.serviceInstance.content.rootFolder
-        rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect do |dc|
-          Realm.new(
-            :id => dc.name,
-            :name => dc.name, 
-            :limit => :unlimited,
-            :state => convert_state(:datacenter, dc.configStatus)
-          )
+        if opts and opts[:id]
+          datastore = find_datastore(credentials, opts[:id])
+          [convert_realm(datastore)]
+        else
+          rootFolder = vsphere.serviceInstance.content.rootFolder
+          rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect do |dc|
+            dc.datastoreFolder.childEntity.collect { |datastore| convert_realm(datastore) }
+          end.flatten
         end
       end
     end
@@ -139,14 +140,14 @@ module Deltacloud::Drivers::VSphere
         if opts[:id]
           machine_vms = [ find_vm(credentials, opts[:id]) ].compact
         else
-          machine_vms = list_virtual_machines(credentials).select { |vm| !vm.summary.config[:template] }
+          machine_vms = list_virtual_machines(credentials).select { |vm| !vm[:instance].summary.config[:template] }
         end
       end
-      realm_id = realms(credentials).first.id
       safely do
-        inst_arr = machine_vms.collect do |vm|
+        inst_arr = machine_vms.collect do |vm_hash|
           # Since all calls to vm are threaten as SOAP calls, reduce them using
           # local variable.
+          vm, realm_id = vm_hash[:instance], vm_hash[:datastore]
           config = vm.summary.config
           next unless config
           next unless vm.summary.storage
@@ -186,34 +187,43 @@ module Deltacloud::Drivers::VSphere
       vsphere = new_client(credentials)
       safely do
         rootFolder = vsphere.serviceInstance.content.rootFolder
-        # FIXME: This will consume first datacenter and ignore 'realm' property
-        dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect.first
-        vm = dc.find_vm(image_id)
-        resource_pool = dc.hostFolder.childEntity.collect.first.resourcePool
-        relocateSpec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool)
-        # NOTE: 'powerOn' attribute will force machine to start after clone operation
-        #       'template' attribute will mark VM as a template if set to true
+        vm = find_vm(credentials, opts[:image_id])
+        # Find correct ResourcePool and Datastore where a new VM will be
+        # located
+        if opts and opts[:realm_id]
+          resourcePool = find_resource_pool(credentials, opts[:realm_id])
+          datastore = find_datastore(credentials, opts[:realm_id])
+        else
+          resourcePool = find_resource_pool(credentials, vm[:datastore])
+          datastore = find_datastore(credentials, opts[:datastore])
+        end
+        relocate = { :pool => resourcePool, :datastore => datastore }
+        relocateSpec = RbVmomi::VIM.VirtualMachineRelocateSpec(relocate)
+        instance_profile = hardware_profiles(credentials, :id => opts[:hwp_id]).first
         spec = RbVmomi::VIM.VirtualMachineCloneSpec(
           :location => relocateSpec,
           :powerOn => true,
           :template => false,
           :config => RbVmomi::VIM.VirtualMachineConfigSpec(
-            :memoryMB => 256,
-            :numCPUs => 1
+            :memoryMB => instance_profile.memory.value,
+            :numCPUs => instance_profile.cpu.value
           )
         )
-        # NOTE: This operation may take a very long time (about 1m) to complete
+        #
+        # WARNING: This operation may take a very long time (about 1m) to complete
+        #
+        # TODO: Use mapper?
+        #
         puts "Cloning template #{image_id} to #{opts[:name]}..."
-        vm.CloneVM_Task(:folder => vm.parent, :name => opts[:name], :spec => spec).wait_for_completion
+        vm[:instance].CloneVM_Task(:folder => vm[:instance].parent, :name => opts[:name], :spec => spec).wait_for_completion
         puts "Cloning complete!"
-        # Since task itself is not returning anything, construct Instance from things we already have
         Instance::new(
           :id => opts[:name],
           :name => opts[:name],
           :owner_id => credentials.user,
-          :realm_id => dc.name,
+          :realm_id => opts[:realm_id] || vm[:datastore],
           :state => 'PENDING',
-          :instance_profile => InstanceProfile::new('default'),
+          :instance_profile => InstanceProfile::new(instance_profile.name),
           :actions => instance_actions_for( 'PENDING' )
         )
       end
@@ -221,23 +231,23 @@ module Deltacloud::Drivers::VSphere
 
     # Reboot an instance, given its id.
     def reboot_instance(credentials, id)
-      find_vm(credentials, id).ResetVM_Task
+      find_vm(credentials, id)[:instance].ResetVM_Task
     end
 
     # Start an instance, given its id.
     def start_instance(credentials, id)
-      find_vm(credentials, id).PowerOnVM_Task
+      find_vm(credentials, id)[:instance].PowerOnVM_Task
     end
 
     # Stop an instance, given its id.
     def stop_instance(credentials, id)
-      find_vm(credentials, id).PowerOffVM_Task
+      find_vm(credentials, id)[:instance].PowerOffVM_Task
     end
 
     # Destroy an instance, given its id. Note that this will destory all
     # instance data.
     def destroy_instance(credentials, id)
-      find_vm(credentials, id).Destroy_Task.wait_for_completion
+      find_vm(credentials, id)[:instance].Destroy_Task.wait_for_completion
     end
 
     exceptions do
@@ -276,31 +286,87 @@ module Deltacloud::Drivers::VSphere
       endpoint || Deltacloud::Drivers::driver_config[:vsphere][:entrypoints]['default']['default']
     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|
-        vms += dc.vmFolder.childEntity.collect do |ent|
-          if ent.class.name == 'RbVmomi::VIM::Folder'
-            ent.childEntity.grep(RbVmomi::VIM::VirtualMachine).collect
-          else
-            ent
-          end
+        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
-        dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).collect.first
-        dc.find_vm(name)
+        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,
+        :name => datastore.name, 
+        :limit => datastore.summary.freeSpace,
+        :state => datastore.summary.accessible ? 'AVAILABLE' : 'UNAVAILABLE'
+      )
+    end
+
     def convert_state(object, state)
       new_state = ''
       if object == :image
@@ -316,12 +382,6 @@ module Deltacloud::Drivers::VSphere
           else 'PENDING'
         end
       end
-      if object == :datacenter
-        new_state = case state
-          when 'gray', 'green' then 'AVAILABLE'
-          else 'UNAVAILABLE'
-        end
-      end
       new_state
     end
 
-- 
1.7.4.1