From 8e341ad8313766d77b58d9b78b419b24d731322d Mon Sep 17 00:00:00 2001 From: Eric Stonfer Date: Tue, 17 Jan 2012 11:52:08 -0500 Subject: [PATCH 1/7] Add the ability to create linked clones in vsphere --- lib/fog/vsphere/requests/compute/vm_clone.rb | 37 ++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/lib/fog/vsphere/requests/compute/vm_clone.rb b/lib/fog/vsphere/requests/compute/vm_clone.rb index 8ea9f6577..a60e9046d 100644 --- a/lib/fog/vsphere/requests/compute/vm_clone.rb +++ b/lib/fog/vsphere/requests/compute/vm_clone.rb @@ -70,10 +70,41 @@ module Fog esx_host = vm_mob_ref.collect!('runtime.host')['runtime.host'] # The parent of the ESX host itself is a ComputeResource which has a resourcePool resource_pool = esx_host.parent.resourcePool - - # Next, create a Relocation Spec instance - relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool, + relocation_spec=nil + if ( options['linked_clone'] ) + # cribbed heavily from the rbvmomi clone_vm.rb + # this chunk of code reconfigures the disk of the clone source to be read only, + # and then creates a delta disk on top of that, this is required by the API in order to create + # linked clondes + disks=vm_mob_ref.config.hardware.device.select{|vm_device| vm_device.class == RbVmomi::VIM::VirtualDisk} + disks.select{|vm_device| vm_device.backing.parent == nil}.each do |disk| + disk_spec = { + :deviceChange => [ + { + :operation => :remove, + :device => disk + }, + { + :operation => :add, + :fileOperation => :create, + :device => disk.dup.tap{|disk_backing| + disk_backing.backing = disk_backing.backing.dup; + disk_backing.backing.fileName = "[#{disk.backing.datastore.name}]"; + disk_backing.backing.parent = disk.backing + } + }, + ] + } + vm_mob_ref.ReconfigVM_Task(:spec => disk_spec).wait_for_completion + end + # Next, create a Relocation Spec instance + relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool, + :diskMoveType => :moveChildMostDiskBacking) + + else + relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool, :transform => options['transform'] || 'sparse') + end # And the clone specification clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(:location => relocation_spec, :powerOn => options['power_on'] || true, From 8542632a67d82c5fe7fea6152516eb16fa5aff47 Mon Sep 17 00:00:00 2001 From: Eric Stonfer Date: Tue, 17 Jan 2012 13:52:49 -0500 Subject: [PATCH 2/7] whitespace fix --- lib/fog/vsphere/requests/compute/vm_clone.rb | 52 ++++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/lib/fog/vsphere/requests/compute/vm_clone.rb b/lib/fog/vsphere/requests/compute/vm_clone.rb index a60e9046d..a9d313652 100644 --- a/lib/fog/vsphere/requests/compute/vm_clone.rb +++ b/lib/fog/vsphere/requests/compute/vm_clone.rb @@ -70,47 +70,45 @@ module Fog esx_host = vm_mob_ref.collect!('runtime.host')['runtime.host'] # The parent of the ESX host itself is a ComputeResource which has a resourcePool resource_pool = esx_host.parent.resourcePool - relocation_spec=nil + relocation_spec=nil if ( options['linked_clone'] ) - # cribbed heavily from the rbvmomi clone_vm.rb - # this chunk of code reconfigures the disk of the clone source to be read only, - # and then creates a delta disk on top of that, this is required by the API in order to create - # linked clondes - disks=vm_mob_ref.config.hardware.device.select{|vm_device| vm_device.class == RbVmomi::VIM::VirtualDisk} - disks.select{|vm_device| vm_device.backing.parent == nil}.each do |disk| - disk_spec = { - :deviceChange => [ - { - :operation => :remove, - :device => disk - }, - { - :operation => :add, - :fileOperation => :create, - :device => disk.dup.tap{|disk_backing| - disk_backing.backing = disk_backing.backing.dup; - disk_backing.backing.fileName = "[#{disk.backing.datastore.name}]"; - disk_backing.backing.parent = disk.backing - } + # cribbed heavily from the rbvmomi clone_vm.rb + # this chunk of code reconfigures the disk of the clone source to be read only, + # and then creates a delta disk on top of that, this is required by the API in order to create + # linked clondes + disks=vm_mob_ref.config.hardware.device.select{|vm_device| vm_device.class == RbVmomi::VIM::VirtualDisk} + disks.select{|vm_device| vm_device.backing.parent == nil}.each do |disk| + disk_spec = { + :deviceChange => [ + { + :operation => :remove, + :device => disk }, - ] + { + :operation => :add, + :fileOperation => :create, + :device => disk.dup.tap{|disk_backing| + disk_backing.backing = disk_backing.backing.dup; + disk_backing.backing.fileName = "[#{disk.backing.datastore.name}]"; + disk_backing.backing.parent = disk.backing + } + }, + ] } - vm_mob_ref.ReconfigVM_Task(:spec => disk_spec).wait_for_completion + vm_mob_ref.ReconfigVM_Task(:spec => disk_spec).wait_for_completion end # Next, create a Relocation Spec instance relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool, - :diskMoveType => :moveChildMostDiskBacking) - + :diskMoveType => :moveChildMostDiskBacking) else relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool, - :transform => options['transform'] || 'sparse') + :transform => options['transform'] || 'sparse') end # And the clone specification clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(:location => relocation_spec, :powerOn => options['power_on'] || true, :template => false) task = vm_mob_ref.CloneVM_Task(:folder => vm_mob_ref.parent, :name => options['name'], :spec => clone_spec) - # Waiting for the VM to complete allows us to get the VirtulMachine # object of the new machine when it's done. It is HIGHLY recommended # to set 'wait' => true if your app wants to wait. Otherwise, you're From 4bcfed0099a10bb6ceb5c1fb1eb2f7f638542293 Mon Sep 17 00:00:00 2001 From: Eric Stonfer Date: Tue, 17 Jan 2012 14:16:22 -0500 Subject: [PATCH 3/7] whitespace fix --- lib/fog/vsphere/requests/compute/vm_clone.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/fog/vsphere/requests/compute/vm_clone.rb b/lib/fog/vsphere/requests/compute/vm_clone.rb index a9d313652..ac1f0cbce 100644 --- a/lib/fog/vsphere/requests/compute/vm_clone.rb +++ b/lib/fog/vsphere/requests/compute/vm_clone.rb @@ -81,17 +81,17 @@ module Fog disk_spec = { :deviceChange => [ { - :operation => :remove, - :device => disk + :operation => :remove, + :device => disk }, { - :operation => :add, - :fileOperation => :create, - :device => disk.dup.tap{|disk_backing| - disk_backing.backing = disk_backing.backing.dup; - disk_backing.backing.fileName = "[#{disk.backing.datastore.name}]"; - disk_backing.backing.parent = disk.backing - } + :operation => :add, + :fileOperation => :create, + :device => disk.dup.tap{|disk_backing| + disk_backing.backing = disk_backing.backing.dup; + disk_backing.backing.fileName = "[#{disk.backing.datastore.name}]"; + disk_backing.backing.parent = disk.backing + } }, ] } From cd682ba89c3e1234019fe2c8b54a467017ce73a7 Mon Sep 17 00:00:00 2001 From: Eric Stonfer Date: Tue, 17 Jan 2012 22:42:53 -0500 Subject: [PATCH 4/7] add a linked clone test scenario, set the vm_clone test to wait, and clean up old servers after the VM clone test --- .../vsphere/requests/compute/vm_clone_tests.rb | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/vsphere/requests/compute/vm_clone_tests.rb b/tests/vsphere/requests/compute/vm_clone_tests.rb index 28cc73d0e..e698df723 100644 --- a/tests/vsphere/requests/compute/vm_clone_tests.rb +++ b/tests/vsphere/requests/compute/vm_clone_tests.rb @@ -3,13 +3,27 @@ Shindo.tests("Fog::Compute[:vsphere] | vm_clone request", 'vsphere') do template = "/Datacenters/Solutions/vm/Jeff/Templates/centos56gm2" compute = Fog::Compute[:vsphere] - tests("The return value should") do - response = compute.vm_clone('path' => template, 'name' => 'cloning_vm') + tests("Standard Clone | The return value should") do + response = compute.vm_clone('path' => template, 'name' => 'cloning_vm', 'wait' => 1) test("be a kind of Hash") { response.kind_of? Hash } %w{ vm_ref task_ref }.each do |key| test("have a #{key} key") { response.has_key? key } end end + template = "/Datacenters/Solutions/vm/Jeff/Templates/cloning_vm" + tests("Linked Clone | The return value should") do + response_linked = compute.vm_clone('path' => template, 'name' => 'cloning_vm', 'wait' => 1) + test("be a kind of Hash") { response_linked.kind_of? Hash } + %w{ vm_ref task_ref }.each do |key| + test("have a #{key} key") { response_linked.has_key? key } + end + end + ## clean up afterward as in the aws tests. remove the source first and then the linked clone + compute.vm_power_off('uuid' => response_linked['vm_attributes']['uuid'], 'instance_uuid' => response_linked['vm_attributes']['instance_uuid'], 'force' => 1) + compute.vm_destroy('instance_uuid' => response_linked['vm_attributes']['instance_uuid']) + compute.vm_power_off('uuid' => response['vm_attributes']['uuid'], 'instance_uuid' => response['vm_attributes']['instance_uuid'], 'force' => 1) + compute.vm_destroy('instance_uuid' => response['vm_attributes']['instance_uuid']) + tests("When invalid input is presented") do raises(ArgumentError, 'it should raise ArgumentError') { compute.vm_clone(:foo => 1) } raises(Fog::Compute::Vsphere::NotFound, 'it should raise Fog::Compute::Vsphere::NotFound when the UUID is not a string') do From a318c9a239ba8b464f25a8da4c726408c6c30dae Mon Sep 17 00:00:00 2001 From: Eric Stonfer Date: Tue, 17 Jan 2012 22:58:08 -0500 Subject: [PATCH 5/7] linked clone tests --- tests/vsphere/requests/compute/vm_clone_tests.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/vsphere/requests/compute/vm_clone_tests.rb b/tests/vsphere/requests/compute/vm_clone_tests.rb index e698df723..895f65762 100644 --- a/tests/vsphere/requests/compute/vm_clone_tests.rb +++ b/tests/vsphere/requests/compute/vm_clone_tests.rb @@ -1,8 +1,11 @@ Shindo.tests("Fog::Compute[:vsphere] | vm_clone request", 'vsphere') do #require 'guid' - template = "/Datacenters/Solutions/vm/Jeff/Templates/centos56gm2" + compute = Fog::Compute[:vsphere] + response = nil + response_linked = nil + template = "/Datacenters/Solutions/vm/Jeff/Templates/centos56gm2" tests("Standard Clone | The return value should") do response = compute.vm_clone('path' => template, 'name' => 'cloning_vm', 'wait' => 1) test("be a kind of Hash") { response.kind_of? Hash } @@ -12,13 +15,13 @@ Shindo.tests("Fog::Compute[:vsphere] | vm_clone request", 'vsphere') do end template = "/Datacenters/Solutions/vm/Jeff/Templates/cloning_vm" tests("Linked Clone | The return value should") do - response_linked = compute.vm_clone('path' => template, 'name' => 'cloning_vm', 'wait' => 1) + response_linked = compute.vm_clone('path' => template, 'name' => 'cloning_vm_linked', 'wait' => 1) test("be a kind of Hash") { response_linked.kind_of? Hash } %w{ vm_ref task_ref }.each do |key| test("have a #{key} key") { response_linked.has_key? key } end end - ## clean up afterward as in the aws tests. remove the source first and then the linked clone + ## clean up afterward as in the aws tests. compute.vm_power_off('uuid' => response_linked['vm_attributes']['uuid'], 'instance_uuid' => response_linked['vm_attributes']['instance_uuid'], 'force' => 1) compute.vm_destroy('instance_uuid' => response_linked['vm_attributes']['instance_uuid']) compute.vm_power_off('uuid' => response['vm_attributes']['uuid'], 'instance_uuid' => response['vm_attributes']['instance_uuid'], 'force' => 1) From 67cede19a5e262c0fc6481be7f1fd927f0e5d7f3 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Thu, 19 Jan 2012 11:38:25 -0800 Subject: [PATCH 6/7] Fix linked clone mocked test unhandled exception Without this patch, the test for the vSphere linked clone option to the vm_clone request throws an exception. For the full exception please see GH-697 discussion comments on Github at [1] This patch fixes the issue by removing the cleanup code which is not necessary for a Mocked test. The cleanup code was calling a method on the response_linked object which is not actually in scope. [1] https://github.com/fog/fog/pull/697 --- lib/fog/vsphere/requests/compute/vm_clone.rb | 6 +++++- tests/vsphere/requests/compute/vm_clone_tests.rb | 16 ++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/fog/vsphere/requests/compute/vm_clone.rb b/lib/fog/vsphere/requests/compute/vm_clone.rb index ac1f0cbce..fe7acf59c 100644 --- a/lib/fog/vsphere/requests/compute/vm_clone.rb +++ b/lib/fog/vsphere/requests/compute/vm_clone.rb @@ -5,7 +5,11 @@ module Fog module Shared private def vm_clone_check_options(options) - options = { 'force' => false }.merge(options) + default_options = { + 'force' => false, + 'linked_clone' => false, + } + options = default_options.merge(options) required_options = %w{ path name } required_options.each do |param| raise ArgumentError, "#{required_options.join(', ')} are required" unless options.has_key? param diff --git a/tests/vsphere/requests/compute/vm_clone_tests.rb b/tests/vsphere/requests/compute/vm_clone_tests.rb index 895f65762..2e4fe470a 100644 --- a/tests/vsphere/requests/compute/vm_clone_tests.rb +++ b/tests/vsphere/requests/compute/vm_clone_tests.rb @@ -13,20 +13,16 @@ Shindo.tests("Fog::Compute[:vsphere] | vm_clone request", 'vsphere') do test("have a #{key} key") { response.has_key? key } end end - template = "/Datacenters/Solutions/vm/Jeff/Templates/cloning_vm" + + template = "/Datacenters/Solutions/vm/Jeff/Templates/centos56gm2" tests("Linked Clone | The return value should") do - response_linked = compute.vm_clone('path' => template, 'name' => 'cloning_vm_linked', 'wait' => 1) - test("be a kind of Hash") { response_linked.kind_of? Hash } + response = compute.vm_clone('path' => template, 'name' => 'cloning_vm_linked', 'wait' => 1, 'linked_clone' => true) + test("be a kind of Hash") { response.kind_of? Hash } %w{ vm_ref task_ref }.each do |key| - test("have a #{key} key") { response_linked.has_key? key } + test("have a #{key} key") { response.has_key? key } end end - ## clean up afterward as in the aws tests. - compute.vm_power_off('uuid' => response_linked['vm_attributes']['uuid'], 'instance_uuid' => response_linked['vm_attributes']['instance_uuid'], 'force' => 1) - compute.vm_destroy('instance_uuid' => response_linked['vm_attributes']['instance_uuid']) - compute.vm_power_off('uuid' => response['vm_attributes']['uuid'], 'instance_uuid' => response['vm_attributes']['instance_uuid'], 'force' => 1) - compute.vm_destroy('instance_uuid' => response['vm_attributes']['instance_uuid']) - + tests("When invalid input is presented") do raises(ArgumentError, 'it should raise ArgumentError') { compute.vm_clone(:foo => 1) } raises(Fog::Compute::Vsphere::NotFound, 'it should raise Fog::Compute::Vsphere::NotFound when the UUID is not a string') do From 246c91fc67ea7bb3403f833cb56dd2093c5fcf00 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Thu, 19 Jan 2012 11:42:39 -0800 Subject: [PATCH 7/7] (maint) Whitespace and format only clean up Without this patch there are some niggling whitespace and formatting issues introduced by this pull request and change set. This patch cleans those up and makes git log --check look nice again. --- lib/fog/vsphere/requests/compute/vm_clone.rb | 20 ++++++++++--------- .../requests/compute/vm_clone_tests.rb | 3 +-- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/fog/vsphere/requests/compute/vm_clone.rb b/lib/fog/vsphere/requests/compute/vm_clone.rb index fe7acf59c..bd75147e5 100644 --- a/lib/fog/vsphere/requests/compute/vm_clone.rb +++ b/lib/fog/vsphere/requests/compute/vm_clone.rb @@ -80,13 +80,15 @@ module Fog # this chunk of code reconfigures the disk of the clone source to be read only, # and then creates a delta disk on top of that, this is required by the API in order to create # linked clondes - disks=vm_mob_ref.config.hardware.device.select{|vm_device| vm_device.class == RbVmomi::VIM::VirtualDisk} + disks = vm_mob_ref.config.hardware.device.select do |vm_device| + vm_device.class == RbVmomi::VIM::VirtualDisk + end disks.select{|vm_device| vm_device.backing.parent == nil}.each do |disk| - disk_spec = { - :deviceChange => [ + disk_spec = { + :deviceChange => [ { - :operation => :remove, - :device => disk + :operation => :remove, + :device => disk }, { :operation => :add, @@ -97,14 +99,14 @@ module Fog disk_backing.backing.parent = disk.backing } }, - ] + ] } vm_mob_ref.ReconfigVM_Task(:spec => disk_spec).wait_for_completion end # Next, create a Relocation Spec instance relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool, - :diskMoveType => :moveChildMostDiskBacking) - else + :diskMoveType => :moveChildMostDiskBacking) + else relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool, :transform => options['transform'] || 'sparse') end @@ -159,7 +161,7 @@ module Fog end { 'vm_ref' => 'vm-123', - 'task_ref' => 'task-1234' + 'task_ref' => 'task-1234', } end diff --git a/tests/vsphere/requests/compute/vm_clone_tests.rb b/tests/vsphere/requests/compute/vm_clone_tests.rb index 2e4fe470a..14b3fcb94 100644 --- a/tests/vsphere/requests/compute/vm_clone_tests.rb +++ b/tests/vsphere/requests/compute/vm_clone_tests.rb @@ -1,6 +1,5 @@ Shindo.tests("Fog::Compute[:vsphere] | vm_clone request", 'vsphere') do - #require 'guid' - + # require 'guid' compute = Fog::Compute[:vsphere] response = nil response_linked = nil