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

(#9124) Add ability to reload the model of a cloning VM

Without this patch it is very difficult to reload the model of a VM in
the process of being cloned.  All we have is the vmware managed object
reference ID string and the name of the VM.

This patch adds a number of improvements:

First, the model of a VM being cloned can reload itself after the VM
finishes cloning, even though we don't have an instance_uuid until the
clone completes.

Because the model can reload itself, it's now possible to do something
like:

    c = Fog::Compute[:vsphere]
    new_vm_name = "test"
    c.vm_clone(:instance_uuid => "abc123", :name => new_vm_name)
    my_new_vm = c.servers.find { |vm| vm.name == new_vm_name }
    my_new_vm.wait_for { uuid }
    puts "New VM is ready! (It has a UUID)"
    my_new_vm.wait_for { ipaddress }
    puts "New VM is on the network!"

Without this patch, a VM model could not reload itself with an id of
'vm-123', reloading the model only works if the ID is a UUID.

In addition, a number of the attributes of the server model have been
adjusted to be nil values when the VM is in the process of cloning.
This makes it easier to use wait_for conditionals in blocks.
This commit is contained in:
Jeff McCune 2011-09-06 20:09:57 -07:00
parent 96ada81149
commit dc9a2e4808
5 changed files with 85 additions and 10 deletions

View file

@ -23,6 +23,7 @@ module Fog
request :find_all_by_uuid
request :find_all_by_instance_uuid
request :find_template_by_instance_uuid
request :find_vm_by_ref
class Mock

View file

@ -46,14 +46,14 @@ module Fog
{
:id => is_ready ? vm.config.instanceUuid : vm._ref,
:name => vm.name,
:uuid => is_ready ? vm.config.uuid : 'unavailable',
:instance_uuid => is_ready ? vm.config.instanceUuid : 'unavailable',
:uuid => is_ready ? vm.config.uuid : nil,
:instance_uuid => is_ready ? vm.config.instanceUuid : nil,
:hostname => vm.summary.guest.hostName,
:operatingsystem => vm.summary.guest.guestFullName,
:ipaddress => vm.summary.guest.ipAddress,
:power_state => vm.runtime.powerState,
:connection_state => vm.runtime.connectionState,
:hypervisor => vm.runtime.host ? vm.runtime.host.name : 'unknown',
:hypervisor => vm.runtime.host ? vm.runtime.host.name : nil,
:tools_state => vm.summary.guest.toolsStatus,
:tools_version => vm.summary.guest.toolsVersionStatus,
:mac_addresses => is_ready ? vm.macs : nil,

View file

@ -19,8 +19,15 @@ module Fog
load(vm_attributes)
end
def get(instance_uuid)
vm_mob = connection.find_all_by_instance_uuid(instance_uuid).first
def get(id)
# Is the id a managed_object_reference? This may be the case if we're reloading
# a model of a VM in the process of being cloned, since it
# will not have a instance_uuid yet.
if id =~ /^vm-/
vm_mob = connection.find_vm_by_ref(:vm_ref => id)
else
vm_mob = connection.find_all_by_instance_uuid(id).first
end
if server_attributes = model.attribute_hash_from_mob(vm_mob)
new(server_attributes)
end

View file

@ -0,0 +1,28 @@
module Fog
module Compute
class Vsphere
class Real
# REVISIT: This is a naive implementation and not very efficient since
# we find ALL VM's and then iterate over them looking for the managed object
# reference id... There should be an easier way to obtain a reference to a
# VM using only the name or the _ref. This request is primarily intended to
# reload the attributes of a cloning VM which does not yet have an instance_uuid
def find_vm_by_ref(params = {})
list_virtual_machines.detect(lambda { raise Fog::Vsphere::Errors::NotFound }) do |vm|
vm._ref == params[:vm_ref]
end
end
end
class Mock
def find_vm_by_ref(params = {})
Fog::Mock.not_implmented
end
end
end
end
end

View file

@ -4,8 +4,16 @@ module Fog
class Real
def vm_clone(params = {})
raise ArgumentError, ":instance_uuid and :name are required" if params.empty?
# First, find the Managed Object of the template VM
params = { :force => false }.merge(params)
required_params = %w{ instance_uuid name }
required_params.each do |param|
raise ArgumentError, "#{required_params.join(', ')} are required" unless params.has_key? param.to_sym
end
# First, figure out if there's already a VM of the same name.
if not params[:force] and list_virtual_machines.detect { |vm| vm.name == params[:name] } then
raise Fog::Vsphere::Errors::ServiceError, "A VM already exists with name #{params[:name]}"
end
# Find the Managed Object reference of the template VM
vm = find_template_by_instance_uuid(params[:instance_uuid])
# We need to locate the datacenter object to find the
# default resource pool.
@ -24,8 +32,36 @@ module Fog
:powerOn => true,
:template => false)
task = vm.CloneVM_Task(:folder => vm.parent, :name => params[:name], :spec => clone_spec)
# REVISIT: We may want to return an identifier for the asyncronous task
task.info.state
# REVISIT: The task object contains a reference to the template but does
# not appear to contain a reference to the newly created VM.
# This is a really naive way to find the managed object reference
# of the newly created VM.
tries = 0
new_vm = begin
list_virtual_machines.detect(lambda { raise Fog::Vsphere::Errors::NotFound }) do |vm|
next false if vm.name != params[:name]
begin
vm.config ? true : false
rescue RuntimeError
# This rescue is here because we want to make sure we find
# a VM _without_ a config, which indicates the VM is still cloning.
true
end
end
rescue Fog::Vsphere::Errors::NotFound
tries += 1
if tries <= 10 then
sleep 2
retry
end
nil
end
# Taking a hint from wait_for we return a hash to indicate this is a
# managed object reference
{
:vm_ref => new_vm ? new_vm._ref : nil,
:task_ref => task._ref
}
end
end
@ -33,7 +69,10 @@ module Fog
class Mock
def vm_clone(params = {})
"running"
{
:vm_ref => 'vm-123',
:task_ref => 'task-1234'
}
end
end