Created
November 11, 2012 06:42
-
-
Save endzyme/4053977 to your computer and use it in GitHub Desktop.
new changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/.project b/.project | |
new file mode 100644 | |
index 0000000..414c3ac | |
--- /dev/null | |
+++ b/.project | |
@@ -0,0 +1,12 @@ | |
+<?xml version="1.0" encoding="UTF-8"?> | |
+<projectDescription> | |
+ <name>fog</name> | |
+ <comment></comment> | |
+ <projects> | |
+ </projects> | |
+ <buildSpec> | |
+ </buildSpec> | |
+ <natures> | |
+ <nature>com.aptana.ruby.core.rubynature</nature> | |
+ </natures> | |
+</projectDescription> | |
diff --git a/lib/fog/vsphere/compute.rb b/lib/fog/vsphere/compute.rb | |
index afdb5ec..a154b73 100644 | |
--- a/lib/fog/vsphere/compute.rb | |
+++ b/lib/fog/vsphere/compute.rb | |
@@ -27,6 +27,8 @@ module Fog | |
collection :networks | |
model :datastore | |
collection :datastores | |
+ model :folder | |
+ collection :folders | |
request_path 'fog/vsphere/requests/compute' | |
request :current_time | |
@@ -47,6 +49,7 @@ module Fog | |
request :get_network | |
request :list_datastores | |
request :get_datastore | |
+ request :get_folder | |
request :create_vm | |
request :list_vm_interfaces | |
request :list_vm_volumes | |
diff --git a/lib/fog/vsphere/models/compute/datacenter.rb b/lib/fog/vsphere/models/compute/datacenter.rb | |
index 1443ae9..a2e3cf5 100644 | |
--- a/lib/fog/vsphere/models/compute/datacenter.rb | |
+++ b/lib/fog/vsphere/models/compute/datacenter.rb | |
@@ -19,6 +19,10 @@ module Fog | |
def datastores | |
connection.datastores(:datacenter => name) | |
end | |
+ | |
+ def folders | |
+ connection.folders(:datacenter => name) | |
+ end | |
def to_s | |
name | |
diff --git a/lib/fog/vsphere/models/compute/folder.rb b/lib/fog/vsphere/models/compute/folder.rb | |
new file mode 100644 | |
index 0000000..5da1f4c | |
--- /dev/null | |
+++ b/lib/fog/vsphere/models/compute/folder.rb | |
@@ -0,0 +1,21 @@ | |
+module Fog | |
+ module Compute | |
+ class Vsphere | |
+ | |
+ class Folder < Fog::Model | |
+ | |
+ identity :id | |
+ | |
+ attribute :name | |
+ attribute :parent | |
+ attribute :datacenter | |
+ | |
+ def to_s | |
+ name | |
+ end | |
+ | |
+ end | |
+ | |
+ end | |
+ end | |
+end | |
diff --git a/lib/fog/vsphere/models/compute/folders.rb b/lib/fog/vsphere/models/compute/folders.rb | |
new file mode 100644 | |
index 0000000..eac9626 | |
--- /dev/null | |
+++ b/lib/fog/vsphere/models/compute/folders.rb | |
@@ -0,0 +1,21 @@ | |
+require 'fog/core/collection' | |
+require 'fog/vsphere/models/compute/folder' | |
+ | |
+module Fog | |
+ module Compute | |
+ class Vsphere | |
+ | |
+ class Folders < Fog::Collection | |
+ | |
+ model Fog::Compute::Vsphere::Folder | |
+ attr_accessor :datacenter | |
+ | |
+ def get(id) | |
+ requires :datacenter | |
+ new connection.get_folder(id, datacenter) | |
+ end | |
+ | |
+ end | |
+ end | |
+ end | |
+end | |
diff --git a/lib/fog/vsphere/requests/compute/get_folder.rb b/lib/fog/vsphere/requests/compute/get_folder.rb | |
new file mode 100644 | |
index 0000000..132f24c | |
--- /dev/null | |
+++ b/lib/fog/vsphere/requests/compute/get_folder.rb | |
@@ -0,0 +1,43 @@ | |
+module Fog | |
+ module Compute | |
+ class Vsphere | |
+ class Real | |
+ def get_folder(path, datacenter_name) | |
+ folder = get_raw_folder(path, datacenter_name) | |
+ raise(Fog::Compute::Vsphere::NotFound) unless folder | |
+ folder_attributes(folder, datacenter_name) | |
+ end | |
+ | |
+ protected | |
+ | |
+ def get_raw_folder(path, datacenter_name) | |
+ # The required path syntax - 'topfolder/subfolder/anotherfolder' | |
+ path_ary = path.split('/') | |
+ dc = get_raw_datacenter(datacenter_name) | |
+ dc_root_folder = dc.vmFolder | |
+ | |
+ # Walk the tree resetting the folder pointer as we go | |
+ folder = path_ary.inject(dc_root_folder) do |last_returned_folder, sub_folder| | |
+ # JJM VIM::Folder#find appears to be quite efficient as it uses the | |
+ # searchIndex It certainly appears to be faster than | |
+ # VIM::Folder#inventory since that returns _all_ managed objects of | |
+ # a certain type _and_ their properties. | |
+ # NH: renamed some vars. | |
+ sub = last_returned_folder.find(sub_folder, RbVmomi::VIM::Folder) | |
+ raise ArgumentError, "Could not descend into #{sub_folder}. Please check your path. #{path}" unless sub | |
+ sub | |
+ end | |
+ end | |
+ | |
+ def folder_attributes(folder, datacenter_name) | |
+ { | |
+ :id => managed_obj_id(folder), | |
+ :name => folder.name, | |
+ :parent => folder.parent.name, | |
+ :datacenter => datacenter_name, | |
+ } | |
+ end | |
+ end | |
+ end | |
+ end | |
+end | |
\ No newline at end of file | |
diff --git a/lib/fog/vsphere/requests/compute/get_virtual_machine.rb b/lib/fog/vsphere/requests/compute/get_virtual_machine.rb | |
index 37dfb09..880e3a1 100644 | |
--- a/lib/fog/vsphere/requests/compute/get_virtual_machine.rb | |
+++ b/lib/fog/vsphere/requests/compute/get_virtual_machine.rb | |
@@ -2,25 +2,36 @@ module Fog | |
module Compute | |
class Vsphere | |
class Real | |
- def get_virtual_machine(id, dc = nil) | |
- convert_vm_mob_ref_to_attr_hash(get_vm_ref(id, dc)) | |
+ def get_virtual_machine(id, datacenter_name = nil) | |
+ convert_vm_mob_ref_to_attr_hash(get_vm_ref(id, datacenter_name)) | |
end | |
protected | |
- def get_vm_ref(id, dc = nil) | |
- vm = case id | |
- # UUID based | |
- when /[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}/ | |
- @connection.searchIndex.FindByUuid :uuid => id, :vmSearch => true, :instanceUuid => true, :datacenter => dc | |
- else | |
- # try to find based on VM name | |
- if dc | |
- get_datacenter(dc).find_vm(id) | |
- else | |
- raw_datacenters.map { |d| d.find_vm(id) }.compact.first | |
- end | |
- end | |
+ def get_vm_ref(id, datacenter_name = nil) | |
+ # The required id syntax - 'topfolder/subfolder/anotherfolder' | |
+ # or uuid | |
+ if datacenter_name | |
+ # The required path syntax - 'topfolder/subfolder/anotherfolder' | |
+ path_ary = id.split('/') | |
+ vm_name = path_ary.pop | |
+ dc = get_raw_datacenter(datacenter_name) | |
+ dc_root_folder = dc.vmFolder | |
+ # try to find based on VM name if datacenter is set | |
+ # Walk the tree resetting the folder pointer as we go | |
+ folder = get_raw_folder(path_ary.join('/'), datacenter_name) | |
+ vm = folder.find(vm_name, RbVmomi::VIM::VirtualMachine) | |
+ raise Fog::Compute::Vsphere::NotFound, "#{id} was not found or returned as a folder" unless vm | |
+ else | |
+ vm = case id | |
+ # UUID based search (needs test) | |
+ when /[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}/ | |
+ @connection.searchIndex.FindByUuid :uuid => id, :vmSearch => true, :instanceUuid => true, :datacenter => datacenter_name | |
+ else | |
+ # Don't believe this works needs test | |
+ raw_datacenters.map { |d| d.find_vm(id) }.compact.first | |
+ end | |
+ end | |
vm ? vm : raise(Fog::Compute::Vsphere::NotFound, "#{id} was not found") | |
end | |
end | |
@@ -31,4 +42,4 @@ module Fog | |
end | |
end | |
end | |
-end | |
+end | |
\ No newline at end of file | |
diff --git a/lib/fog/vsphere/requests/compute/vm_clone.rb b/lib/fog/vsphere/requests/compute/vm_clone.rb | |
index 7b92380..ef7b8ba 100644 | |
--- a/lib/fog/vsphere/requests/compute/vm_clone.rb | |
+++ b/lib/fog/vsphere/requests/compute/vm_clone.rb | |
@@ -10,12 +10,16 @@ module Fog | |
'linked_clone' => false, | |
} | |
options = default_options.merge(options) | |
- required_options = %w{ path name } | |
+ # Backwards compatibility settings | |
+ if ( options.has_key? 'path' ) | |
+ options['template_path'] ||= options['path'] | |
+ end | |
+ required_options = %w{ template_path name } | |
required_options.each do |param| | |
raise ArgumentError, "#{required_options.join(', ')} are required" unless options.has_key? param | |
end | |
# The tap removes the leading empty string | |
- path_elements = options['path'].split('/').tap { |o| o.shift } | |
+ path_elements = options['template_path'].split('/').tap { |o| o.shift } | |
first_folder = path_elements.shift | |
if first_folder != 'Datacenters' then | |
raise ArgumentError, "vm_clone path option must start with /Datacenters. Got: #{options['path']}" | |
@@ -24,6 +28,7 @@ module Fog | |
if not self.datacenters.include? dc_name then | |
raise ArgumentError, "Datacenter #{dc_name} does not exist, only datacenters #{self.datacenters.join(",")} are accessible." | |
end | |
+ | |
options | |
end | |
end | |
@@ -32,18 +37,19 @@ module Fog | |
include Shared | |
# Clones a VM from a template or existing machine on your vSphere | |
- # Server. | |
+ # Server. Needs to be augmented to select network label and set | |
+ # guest hostname. | |
# | |
# ==== Parameters | |
# * options<~Hash>: | |
+ # * 'datacenter'<~String> - *REQUIRED* Your datacenter where you're | |
+ # cloning. Example: 'my-datacenter-name' | |
# * 'template_path'<~String> - *REQUIRED* The path to the machine you | |
- # want to clone FROM. (Example: | |
- # "/Datacenter/DataCenterNameHere/FolderNameHere/VMNameHere") | |
+ # want to clone FROM. Example: 'aFolderNameHere/sub folder/VMNameHere'. | |
# * 'name'<~String> - *REQUIRED* The VMName of the Destination | |
- # * 'resource_pool'<~String> - The resource pool on your datacenter | |
- # cluster you want to use. | |
# * 'dest_folder'<~String> - Destination Folder of where 'name' will | |
- # be placed on your cluster. *NOT TESTED OR VALIDATED* | |
+ # be placed on your cluster. Example: 'myfolder/some/subfolder'. | |
+ # Only clones to the same datacenter. | |
# * 'power_on'<~Boolean> - Whether to power on machine after clone. | |
# Defaults to true. | |
# * 'wait'<~Boolean> - Whether the method should wait for the virtual | |
@@ -51,64 +57,129 @@ module Fog | |
# vSphere. Returns the value of the machine if it finishes cloning | |
# in 150 seconds (1m30s) else it returns nil. 'wait' Defaults to nil. | |
# Saves a little time. | |
+ # * 'resource_pool'<~Array> - The resource pool on your datacenter | |
+ # cluster you want to use. Only works with clusters within same | |
+ # same datacenter as where you're cloning from. Datacenter grabbed | |
+ # from template_path option. | |
+ # Example: ['cluster_name_here','resource_pool_name_here'] | |
+ # * 'datastore'<~String> - The datastore name you'd like to use. Must | |
+ # be in the same datacenter as the dest_folder. | |
# * 'transform'<~String> - Not documented - see http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.vm.RelocateSpec.html | |
+ # * customization_spec<~Hash>: Options are marked as required if you | |
+ # use this customization_spec. Static IP Settings not configured. | |
+ # This only support cloning and setting DHCP on the first interface | |
+ # and also only supports Linux. | |
+ # * 'domain'<~String> - *REQUIRED* This is put into | |
+ # /etc/resolve.conf | |
+ # * 'hostname'<~String> - Hostname of the Guest OS - default is | |
+ # options['name'] | |
+ # * 'hw_utc_clock'<~Boolean> - *REQUIRED* Is hardware clock UTC? | |
+ # Default true | |
+ # * 'time_zone'<~String> - *REQUIRED* Only valid linux options | |
+ # are valid - example: 'America/Denver' | |
# | |
def vm_clone(options = {}) | |
# Option handling | |
options = vm_clone_check_options(options) | |
- # Added for people still using options['path'] | |
- template_path = options['path'] || options['template_path'] | |
- | |
+ # Comment needed | |
notfound = lambda { raise Fog::Compute::Vsphere::NotFound, "Could not find VM template" } | |
- # Find the template in the folder. This is more efficient than | |
- # searching ALL VM's looking for the template. | |
- # Tap gets rid of the leading empty string and "Datacenters" element | |
- # and returns the array. | |
- path_elements = template_path.split('/').tap { |ary| ary.shift 2 } | |
- # The DC name itself. | |
- template_dc = path_elements.shift | |
- # If the first path element contains "vm" this denotes the vmFolder | |
- # and needs to be shifted out | |
- path_elements.shift if path_elements[0] == 'vm' | |
- # The template name. The remaining elements are the folders in the | |
- # datacenter. | |
- template_name = path_elements.pop | |
+ # Options['template_path'] && Options['datacenter'] | |
+ # Grab the template or Node you're cloning from | |
+ vm_mob_ref = get_raw_virtual_machine(options['template_path'], options['datacenter']) | |
- dc = find_raw_datacenter(template_dc) | |
- # Get the VM Folder (Group) efficiently | |
- vm_folder = dc.vmFolder | |
- # Walk the tree resetting the folder pointer as we go | |
- folder = path_elements.inject(vm_folder) do |current_folder, sub_folder_name| | |
- # JJM VIM::Folder#find appears to be quite efficient as it uses the | |
- # searchIndex It certainly appears to be faster than | |
- # VIM::Folder#inventory since that returns _all_ managed objects of | |
- # a certain type _and_ their properties. | |
- sub_folder = current_folder.find(sub_folder_name, RbVmomi::VIM::Folder) | |
- raise ArgumentError, "Could not descend into #{sub_folder_name}. Please check your path." unless sub_folder | |
- sub_folder | |
- end | |
- | |
- # Now find the template itself using the efficient find method | |
- vm_mob_ref = folder.find(template_name, RbVmomi::VIM::VirtualMachine) | |
+ # Options['dest_folder']<~String> | |
+ # Grab the destination folder object if it exists else use cloned mach | |
+ dest_folder = get_raw_folder(options['dest_folder']) if options.has_key?('dest_folder') | |
+ dest_folder ||= vm_mob_ref.parent | |
+ # Options['resource_pool']<~Array> | |
# Now find _a_ resource pool to use for the clone if one is not specified | |
- if ( options.has_key?('resource_pool') ) | |
- resource_pool = options['resource_pool'] | |
+ if ( options.has_key?('resource_pool') && options['resource_pool'].is_a?(Array) && options['resource_pool'].length == 2 ) | |
+ cluster_name = options['resource_pool'][0] | |
+ pool_name = options['resource_pool'][1] | |
+ resource_pool = get_resource_pool(pool_name, cluster_name, options['datacenter']) | |
elsif ( vm_mob_ref.resourcePool == nil ) | |
# If the template is really a template then there is no associated resource pool, | |
# so we need to find one using the template's parent host or cluster | |
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 | |
- else | |
- # If the vm given did return a valid resource pool, default to using it for the clone. | |
- # Even if specific pools aren't implemented in this environment, we will still get back | |
- # at least the cluster or host we can pass on to the clone task | |
- resource_pool = vm_mob_ref.resourcePool | |
end | |
+ # If the vm given did return a valid resource pool, default to using it for the clone. | |
+ # Even if specific pools aren't implemented in this environment, we will still get back | |
+ # at least the cluster or host we can pass on to the clone task | |
+ # This catches if resource_pool option is set but comes back nil and if resourcePool is | |
+ # already set. | |
+ resource_pool ||= vm_mob_ref.resourcePool.nil? ? esx_host.parent.resourcePool : vm_mob_ref.resourcePool | |
+ | |
+ # Options['datastore']<~String> | |
+ # Grab the datastore object if option is set | |
+ datastore_obj = get_raw_datastore(options['datastore'], options['datacenter']) if options.has_key?('datastore') | |
+ # confirm nil if nil or option is not set | |
+ datastore_obj ||= nil | |
+ | |
+ # Options['network'] ## REVISE ME | |
+ # Build up the config spec - should be based on original vm | |
+ # by finding first network device number. | |
+ if ( options.has_key?('network_label') ) | |
+ network_obj = datacenter_obj.networkFolder.find(options['network_label']) | |
+ config_spec_operation = RbVmomi::VIM::VirtualDeviceConfigSpecOperation('edit') | |
+ nic_backing_info = RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo(:deviceName => options['network_label']) | |
+ #:deviceName => "Network adapter 1", | |
+ #:network => network_obj) | |
+ connectable = RbVmomi::VIM::VirtualDeviceConnectInfo( | |
+ :allowGuestControl => true, | |
+ :connected => true, | |
+ :startConnected => true) | |
+ device = RbVmomi::VIM::VirtualE1000( | |
+ :backing => nic_backing_info, | |
+ :deviceInfo => RbVmomi::VIM::Description(:label => "Network adapter 1", :summary => options['network_label']), | |
+ :key => options['network_adapter_device_key'], | |
+ :connectable => connectable) | |
+ device_spec = RbVmomi::VIM::VirtualDeviceConfigSpec( | |
+ :operation => config_spec_operation, | |
+ :device => device) | |
+ virtual_machine_config_spec = RbVmomi::VIM::VirtualMachineConfigSpec( | |
+ :deviceChange => [device_spec]) | |
+ end | |
+ | |
+ # Options['customization_spec'] | |
+ # Build up all the crappy tiered objects like the perl method | |
+ # Collect your variables ifset (writing at 11pm revist me) | |
+ if ( options.has_key?('customization_spec') ) | |
+ cust_options = options['customization_spec'] | |
+ cust_domain = cust_options['domain'] | |
+ cust_hostname = RbVmomi::VIM::CustomizationFixedName.new(:name => cust_options['hostname']) if cust_options.has_key?('hostname') | |
+ cust_hostname ||= RbVmomi::VIM::CustomizationFixedName.new(:name => options['name']) | |
+ cust_hwclockutc = cust_options['hw_clock_utc'] | |
+ cust_timezone = cust_options['time_zone'] | |
+ # Start Building objects | |
+ # Build the CustomizationLinuxPrep Object | |
+ cust_prep = RbVmomi::VIM::CustomizationLinuxPrep.new( | |
+ :domain => cust_domain, | |
+ :hostName => cust_hostname, | |
+ :hwClockUTC => cust_hwclockutc, | |
+ :timeZone => cust_timezone,) | |
+ # Build the Dhcp Generator Object | |
+ cust_fixed_ip = RbVmomi::VIM::CustomizationDhcpIpGenerator.new() | |
+ # Build the custom_ip_settings Object | |
+ cust_ip_setting = RbVmomi::VIM::CustomizationIPSettings.new(:ip => cust_fixed_ip) | |
+ # Build the Custom Adapter Mapping Supports only one eth right now | |
+ cust_adapter_mapping = [RbVmomi::VIM::CustomizationAdapterMapping.new(:adapter => cust_ip_setting)] | |
+ # Build the customization Spec | |
+ customization_spec = RbVmomi::VIM::CustomizationSpec.new( | |
+ :identity => cust_prep, | |
+ :globalIPSettings => RbVmomi::VIM::CustomizationGlobalIPSettings.new(), | |
+ :nicSettingMap => cust_adapter_mapping) | |
+ end | |
+ customization_spec ||= nil | |
+ | |
+ # Begin Building Objects to CloneVM_Task - Below here is all action | |
+ # on built parameters. | |
+ # Build VirtualMachineRelocateSpec | |
relocation_spec=nil | |
if ( options['linked_clone'] ) | |
# cribbed heavily from the rbvmomi clone_vm.rb | |
@@ -139,17 +210,21 @@ module Fog | |
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, | |
+ relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:datastore => datastore_obj, | |
+ :pool => resource_pool, | |
:diskMoveType => :moveChildMostDiskBacking) | |
else | |
- relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:pool => resource_pool, | |
+ relocation_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(:datastore => datastore_obj, | |
+ :pool => resource_pool, | |
:transform => options['transform'] || 'sparse') | |
end | |
# And the clone specification | |
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(:location => relocation_spec, | |
+ :config => virtual_machine_config_spec, | |
+ :customization => customization_spec, | |
:powerOn => options.has_key?('power_on') ? options['power_on'] : true, | |
:template => false) | |
- task = vm_mob_ref.CloneVM_Task(:folder => options.has_key?('dest_folder') ? options['dest_folder'] : vm_mob_ref.parent, | |
+ task = vm_mob_ref.CloneVM_Task(:folder => dest_folder, | |
:name => options['name'], | |
:spec => clone_spec) | |
# Waiting for the VM to complete allows us to get the VirtulMachine | |
@@ -166,7 +241,7 @@ module Fog | |
tries = 0 | |
new_vm = begin | |
# Try and find the new VM (folder.find is quite efficient) | |
- folder.find(options['name'], RbVmomi::VIM::VirtualMachine) or raise Fog::Vsphere::Errors::NotFound | |
+ dest_folder.find(options['name'], RbVmomi::VIM::VirtualMachine) or raise Fog::Vsphere::Errors::NotFound | |
rescue Fog::Vsphere::Errors::NotFound | |
tries += 1 | |
if tries <= 10 then |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment