diff --git a/lib/fog/vsphere/compute.rb b/lib/fog/vsphere/compute.rb index 168916877..555fd84a3 100644 --- a/lib/fog/vsphere/compute.rb +++ b/lib/fog/vsphere/compute.rb @@ -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 diff --git a/lib/fog/vsphere/models/compute/server.rb b/lib/fog/vsphere/models/compute/server.rb index dfbd2c183..f7a48d085 100644 --- a/lib/fog/vsphere/models/compute/server.rb +++ b/lib/fog/vsphere/models/compute/server.rb @@ -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, diff --git a/lib/fog/vsphere/models/compute/servers.rb b/lib/fog/vsphere/models/compute/servers.rb index 885aa6022..c54d4e9a5 100644 --- a/lib/fog/vsphere/models/compute/servers.rb +++ b/lib/fog/vsphere/models/compute/servers.rb @@ -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 diff --git a/lib/fog/vsphere/requests/compute/find_vm_by_ref.rb b/lib/fog/vsphere/requests/compute/find_vm_by_ref.rb new file mode 100644 index 000000000..21999e681 --- /dev/null +++ b/lib/fog/vsphere/requests/compute/find_vm_by_ref.rb @@ -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 diff --git a/lib/fog/vsphere/requests/compute/vm_clone.rb b/lib/fog/vsphere/requests/compute/vm_clone.rb index 71ef13784..4f255dc5a 100644 --- a/lib/fog/vsphere/requests/compute/vm_clone.rb +++ b/lib/fog/vsphere/requests/compute/vm_clone.rb @@ -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