Skip to content

Instantly share code, notes, and snippets.

@endzyme
Created November 11, 2012 06:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save endzyme/4053977 to your computer and use it in GitHub Desktop.
Save endzyme/4053977 to your computer and use it in GitHub Desktop.
new changes
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