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

Merge pull request #1272 from ohadlevy/vmware

VMWare vsphere provider refactor
This commit is contained in:
Ohad Levy 2012-11-14 10:09:02 -08:00
commit f9b729eb69
50 changed files with 1526 additions and 462 deletions

View file

@ -11,19 +11,50 @@ module Fog
model_path 'fog/vsphere/models/compute'
model :server
collection :servers
model :datacenter
collection :datacenters
model :interface
collection :interfaces
model :volume
collection :volumes
model :template
collection :templates
model :cluster
collection :clusters
model :resource_pool
collection :resource_pools
model :network
collection :networks
model :datastore
collection :datastores
model :folder
collection :folders
request_path 'fog/vsphere/requests/compute'
request :current_time
request :find_vm_by_ref
request :list_virtual_machines
request :vm_power_off
request :vm_power_on
request :vm_reboot
request :vm_clone
request :vm_create
request :vm_destroy
request :vm_migrate
request :datacenters
request :list_datacenters
request :get_datacenter
request :list_clusters
request :get_cluster
request :list_resource_pools
request :get_resource_pool
request :list_networks
request :get_network
request :list_datastores
request :get_datastore
request :get_folder
request :list_folders
request :create_vm
request :list_vm_interfaces
request :list_vm_volumes
request :get_virtual_machine
request :vm_reconfig_hardware
request :vm_reconfig_memory
request :vm_reconfig_cpus
@ -36,11 +67,12 @@ module Fog
attr_reader :vsphere_server
attr_reader :vsphere_username
protected
ATTR_TO_PROP = {
:id => 'config.instanceUuid',
:name => 'name',
:uuid => 'config.uuid',
:instance_uuid => 'config.instanceUuid',
:hostname => 'summary.guest.hostName',
:operatingsystem => 'summary.guest.guestFullName',
:ipaddress => 'guest.ipAddress',
@ -49,9 +81,10 @@ module Fog
:hypervisor => 'runtime.host',
:tools_state => 'guest.toolsStatus',
:tools_version => 'guest.toolsVersionStatus',
:is_a_template => 'config.template',
:memory_mb => 'config.hardware.memoryMB',
:cpus => 'config.hardware.numCPU',
:overall_status => 'overallStatus',
:guest_id => 'summary.guest.guestId',
}
# Utility method to convert a VMware managed object into an attribute hash.
@ -75,9 +108,17 @@ module Fog
attrs['mo_ref'] = vm_mob_ref._ref
# The name method "magically" appears after a VM is ready and
# finished cloning.
if attrs['hypervisor'].kind_of?(RbVmomi::VIM::HostSystem) then
# If it's not ready, set the hypervisor to nil
attrs['hypervisor'] = attrs['hypervisor'].name rescue nil
if attrs['hypervisor'].kind_of?(RbVmomi::VIM::HostSystem)
begin
host = attrs['hypervisor']
attrs['datacenter'] = parent_attribute(host.path, :datacenter)[1]
attrs['cluster'] = parent_attribute(host.path, :cluster)[1]
attrs['hypervisor'] = host.name
attrs['resource_pool'] = (vm_mob_ref.resourcePool || host.resourcePool).name rescue nil
rescue
# If it's not ready, set the hypervisor to nil
attrs['hypervisor'] = nil
end
end
# This inline rescue catches any standard error. While a VM is
# cloning, a call to the macs method will throw and NoMethodError
@ -86,6 +127,31 @@ module Fog
end
end
# returns the parent object based on a type
# provides both real RbVmomi object and its name.
# e.g.
#[Datacenter("datacenter-2"), "dc-name"]
def parent_attribute path, type
element = case type
when :datacenter
RbVmomi::VIM::Datacenter
when :cluster
RbVmomi::VIM::ClusterComputeResource
when :host
RbVmomi::VIM::HostSystem
else
raise "Unknown type"
end
path.select {|x| x[0].is_a? element}.flatten
rescue
nil
end
# returns vmware managed obj id string
def managed_obj_id obj
obj.to_s.match(/\("(\S+)"\)/)[1]
end
end
class Mock

View file

@ -0,0 +1,31 @@
module Fog
module Compute
class Vsphere
class Cluster < Fog::Model
identity :id
attribute :name
attribute :datacenter
attribute :num_host
attribute :num_cpu_cores
attribute :overall_status
def resource_pools(filters = { })
self.attributes[:resource_pools] ||= id.nil? ? [] : connection.resource_pools({
:connection => connection,
:cluster => name,
:datacenter => datacenter
}.merge(filters))
end
def to_s
name
end
end
end
end
end

View file

@ -0,0 +1,26 @@
require 'fog/core/collection'
require 'fog/vsphere/models/compute/cluster'
module Fog
module Compute
class Vsphere
class Clusters < Fog::Collection
model Fog::Compute::Vsphere::Cluster
attr_accessor :datacenter
def all(filters = {})
requires :datacenter
load connection.list_clusters(filters.merge(:datacenter => datacenter))
end
def get(id)
requires :datacenter
new connection.get_cluster(id, datacenter)
end
end
end
end
end

View file

@ -0,0 +1,35 @@
module Fog
module Compute
class Vsphere
class Datacenter < Fog::Model
identity :id
attribute :name
attribute :status
def clusters filters = { }
connection.clusters({ :datacenter => name }.merge(filters))
end
def networks filters = { }
connection.networks({ :datacenter => name }.merge(filters))
end
def datastores filters = { }
connection.datastores({ :datacenter => name }.merge(filters))
end
def vm_folders filters = { }
connection.folders({ :datacenter => name, :type => :vm }.merge(filters))
end
def to_s
name
end
end
end
end
end

View file

@ -0,0 +1,23 @@
require 'fog/core/collection'
require 'fog/vsphere/models/compute/datacenter'
module Fog
module Compute
class Vsphere
class Datacenters < Fog::Collection
model Fog::Compute::Vsphere::Datacenter
def all(filters = {})
load connection.list_datacenters(filters)
end
def get(name)
new connection.get_datacenter(name)
end
end
end
end
end

View file

@ -0,0 +1,24 @@
module Fog
module Compute
class Vsphere
class Datastore < Fog::Model
identity :id
attribute :name
attribute :datacenter
attribute :type
attribute :freespace
attribute :accessible # reachable by at least one hypervisor
attribute :capacity
def to_s
name
end
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'fog/core/collection'
require 'fog/vsphere/models/compute/datastore'
module Fog
module Compute
class Vsphere
class Datastores < Fog::Collection
model Fog::Compute::Vsphere::Datastore
attr_accessor :datacenter
def all(filters = {})
load connection.list_datastores(filters.merge(:datacenter => datacenter))
end
def get(id)
requires :datacenter
new connection.get_datastore(id, datacenter)
end
end
end
end
end

View file

@ -0,0 +1,28 @@
module Fog
module Compute
class Vsphere
class Folder < Fog::Model
identity :id
attribute :name
attribute :parent
attribute :datacenter
attribute :path
attribute :type
def vms
return [] if type.to_s != 'vm'
connection.servers(:folder => path, :datacenter => datacenter)
end
def to_s
name
end
end
end
end
end

View file

@ -0,0 +1,27 @@
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, :type, :path
def all(filters = { })
requires :datacenter
requires :type
load connection.list_folders(filters.merge(:datacenter => datacenter, :type => type, :path => path))
end
def get(id)
requires :datacenter
new connection.get_folder(id, datacenter, type)
end
end
end
end
end

View file

@ -0,0 +1,39 @@
module Fog
module Compute
class Vsphere
class Interface < Fog::Model
identity :mac
attribute :network
attribute :name
attribute :status
attribute :summary
attribute :type
def initialize(attributes={} )
super defaults.merge(attributes)
end
def to_s
name
end
private
def defaults
{
:name=>"Network adapter",
:network=>"VM Network",
:summary=>"VM Network",
:type=> RbVmomi::VIM::VirtualE1000,
}
end
end
end
end
end

View file

@ -0,0 +1,33 @@
require 'fog/core/collection'
require 'fog/vsphere/models/compute/interface'
module Fog
module Compute
class Vsphere
class Interfaces < Fog::Collection
model Fog::Compute::Vsphere::Interface
attr_accessor :vm
def all(filters = {})
requires :vm
case vm
when Fog::Compute::Vsphere::Server
load connection.list_vm_interfaces(vm.id)
when Fog::Compute::Vsphere::Template
load connection.list_template_interfaces(vm.id)
else
raise 'interfaces should have vm or template'
end
end
def get(id)
new connection.get_interface(id)
end
end
end
end
end

View file

@ -0,0 +1,21 @@
module Fog
module Compute
class Vsphere
class Network < Fog::Model
identity :id
attribute :name
attribute :datacenter
attribute :accessible # reachable by at least one hypervisor
def to_s
name
end
end
end
end
end

View file

@ -0,0 +1,25 @@
require 'fog/core/collection'
require 'fog/vsphere/models/compute/network'
module Fog
module Compute
class Vsphere
class Networks < Fog::Collection
model Fog::Compute::Vsphere::Network
attr_accessor :datacenter
def all(filters = {})
load connection.list_networks(filters.merge(:datacenter => datacenter))
end
def get(id)
requires :datacenter
new connection.get_network(id, datacenter)
end
end
end
end
end

View file

@ -0,0 +1,23 @@
module Fog
module Compute
class Vsphere
class ResourcePool < Fog::Model
identity :id
attribute :name
attribute :cluster
attribute :datacenter
attribute :configured_memory_mb
attribute :overall_status
def to_s
name
end
end
end
end
end

View file

@ -0,0 +1,26 @@
require 'fog/core/collection'
require 'fog/vsphere/models/compute/resource_pool'
module Fog
module Compute
class Vsphere
class ResourcePools < Fog::Collection
model Fog::Compute::Vsphere::ResourcePool
attr_accessor :datacenter, :cluster
def all(filters = {})
load connection.list_resource_pools(filters.merge(:datacenter => datacenter, :cluster => cluster))
end
def get(id)
requires :datacenter
requires :cluster
new connection.get_resource_pool(id, cluster, datacenter)
end
end
end
end
end

View file

@ -20,8 +20,6 @@ module Fog
attribute :name
# UUID may be the same from VM to VM if the user does not select (I copied it)
attribute :uuid
# Instance UUID should be unique across a vCenter deployment.
attribute :instance_uuid
attribute :hostname
attribute :operatingsystem
attribute :ipaddress, :aliases => 'public_ip_address'
@ -30,12 +28,26 @@ module Fog
attribute :tools_version
attribute :mac_addresses, :aliases => 'macs'
attribute :hypervisor, :aliases => 'host'
attribute :is_a_template
attribute :connection_state
attribute :mo_ref
attribute :path
attribute :memory_mb
attribute :cpus
attribute :interfaces
attribute :volumes
attribute :overall_status, :aliases => 'status'
attribute :cluster
attribute :datacenter
attribute :resource_pool
attribute :instance_uuid # move this --> id
attribute :guest_id
def initialize(attributes={} )
super defaults.merge(attributes)
self.instance_uuid ||= id # TODO: remvoe instance_uuid as it can be replaced with simple id
initialize_interfaces
initialize_volumes
end
def vm_reconfig_memory(options = {})
requires :instance_uuid, :memory
@ -81,16 +93,8 @@ module Fog
connection.vm_migrate('instance_uuid' => instance_uuid, 'priority' => options[:priority])
end
def create(options ={})
requires :name, :path
new_vm = self.class.new(create_results['vm_attributes'])
new_vm.collection = self.collection
new_vm.connection = self.connection
new_vm
end
def clone(options = {})
requires :name, :path
requires :name, :datacenter
# Convert symbols to strings
req_options = options.inject({}) { |hsh, (k,v)| hsh[k.to_s] = v; hsh }
# Give our path to the request
@ -131,6 +135,60 @@ module Fog
memory_mb * 1024 * 1024
end
def mac
interfaces.first.mac unless interfaces.empty?
end
def interfaces
attributes[:interfaces] ||= id.nil? ? [] : connection.interfaces( :vm => self )
end
def volumes
attributes[:volumes] ||= id.nil? ? [] : connection.volumes( :vm => self )
end
def folder
return nil unless datacenter and path
attributes[:folder] ||= connection.folders(:datacenter => datacenter, :type => :vm).get(path)
end
def save
requires :name, :cluster, :datacenter
if identity
raise "update is not supported yet"
# connection.update_vm(attributes)
else
self.id = connection.create_vm(attributes)
end
reload
end
def new?
id.nil?
end
private
def defaults
{
:cpus => 1,
:memory_mb => 512,
:guest_id => 'otherGuest',
:path => '/'
}
end
def initialize_interfaces
if attributes[:interfaces] and attributes[:interfaces].is_a?(Array)
self.attributes[:interfaces].map! { |nic| nic.is_a?(Hash) ? connection.interfaces.new(nic) : nic }
end
end
def initialize_volumes
if attributes[:volumes] and attributes[:volumes].is_a?(Array)
self.attributes[:volumes].map! { |vol| vol.is_a?(Hash) ? connection.volumes.new(vol) : vol }
end
end
end
end

View file

@ -8,30 +8,26 @@ module Fog
class Servers < Fog::Collection
model Fog::Compute::Vsphere::Server
attr_accessor :datacenter
attr_accessor :network
attr_accessor :cluster
attr_accessor :resource_pool
attr_accessor :folder
# 'path' => '/Datacenters/vm/Jeff/Templates' will be MUCH faster.
# 'folder' => '/Datacenters/vm/Jeff/Templates' will be MUCH faster.
# than simply listing everything.
def all(filters = {})
# REVISIT: I'm not sure if this is the best way to implement search
# filters on a collection but it does work. I need to study the AWS
# code more to make sure this matches up.
filters['folder'] ||= attributes['folder']
response = connection.list_virtual_machines(filters)
load(response['virtual_machines'])
def all(filters = { })
load connection.list_virtual_machines(filters.merge(
:datacenter => datacenter,
:cluster => cluster,
:network => network,
:resource_pool => resource_pool,
:folder => folder
))
end
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-/
response = connection.find_vm_by_ref('vm_ref' => id)
server_attributes = response['virtual_machine']
else
response = connection.list_virtual_machines('instance_uuid' => id)
server_attributes = response['virtual_machines'].first
end
new(server_attributes)
def get(id, datacenter = nil)
new connection.get_virtual_machine id, datacenter
rescue Fog::Compute::Vsphere::NotFound
nil
end

View file

@ -0,0 +1,13 @@
module Fog
module Compute
class Vsphere
class Template < Fog::Model
identity :id
end
end
end
end

View file

@ -0,0 +1,23 @@
require 'fog/core/collection'
require 'fog/vsphere/models/compute/template'
module Fog
module Compute
class Vsphere
class Templates < Fog::Collection
model Fog::Compute::Vsphere::Template
def all(filters = {})
load connection.list_templates(filters)
end
def get(id)
new connection.get_template(id)
end
end
end
end
end

View file

@ -0,0 +1,45 @@
module Fog
module Compute
class Vsphere
class Volume < Fog::Model
DISK_SIZE_TO_GB = 1048576
identity :id
attribute :datastore
attribute :mode
attribute :size
attribute :thin
attribute :name
attribute :filename
attribute :size_gb
def initialize(attributes={} )
super defaults.merge(attributes)
end
def size_gb
attributes[:size_gb] ||= attributes[:size].to_i / DISK_SIZE_TO_GB if attributes[:size]
end
def size_gb= s
attributes[:size] = s.to_i * DISK_SIZE_TO_GB if s
end
def to_s
name
end
private
def defaults
{
:thin=>true,
:name=>"Hard disk",
:mode=>"persistent"
}
end
end
end
end
end

View file

@ -0,0 +1,33 @@
require 'fog/core/collection'
require 'fog/vsphere/models/compute/volume'
module Fog
module Compute
class Vsphere
class Volumes < Fog::Collection
model Fog::Compute::Vsphere::Volume
attr_accessor :vm
def all(filters = {})
requires :vm
case vm
when Fog::Compute::Vsphere::Server
load connection.list_vm_volumes(vm.id)
when Fog::Compute::Vsphere::Template
load connection.list_template_volumes(vm.id)
else
raise 'volumes should have vm or template'
end
end
def get(id)
new connection.get_volume(id)
end
end
end
end
end

View file

@ -0,0 +1,114 @@
module Fog
module Compute
class Vsphere
class Real
def create_vm attributes = { }
# build up vm configuration
vm_cfg = {
:name => attributes[:name],
:guestId => attributes[:guest_id],
:files => { :vmPathName => vm_path_name(attributes) },
:numCPUs => attributes[:cpus],
:memoryMB => attributes[:memory_mb],
:deviceChange => device_change(attributes),
:extraConfig => extra_config(attributes),
}
resource_pool = if attributes[:resource_pool]
get_raw_resource_pool(attributes[:resource_pool], attributes[:cluster], attributes[:datacenter])
else
get_raw_cluster(attributes[:cluster], attributes[:datacenter]).resourcePool
end
vmFolder = get_raw_vmfolder(attributes[:path], attributes[:datacenter])
vm = vmFolder.CreateVM_Task(:config => vm_cfg, :pool => resource_pool).wait_for_completion
vm.config.instanceUuid
rescue => e
raise e, "failed to create vm: #{e}"
end
private
# this methods defines where the vm config files would be located,
# by default we prefer to keep it at the same place the (first) vmdk is located
def vm_path_name attributes
datastore = attributes[:volumes].first.datastore unless attributes[:volumes].empty?
datastore ||= 'datastore1'
"[#{datastore}]"
end
def device_change attributes
devices = []
if (nics = attributes[:interfaces])
devices << nics.map { |nic| create_interface(nic, nics.index(nic)) }
end
if (disks = attributes[:volumes])
devices << create_controller
devices << disks.map { |disk| create_disk(disk, disks.index(disk)) }
end
devices.flatten
end
def create_interface nic, index = 0, operation = :add
{
:operation => operation,
:device => nic.type.new(
:key => index,
:deviceInfo =>
{
:label => nic.name,
:summary => nic.summary,
},
:backing => RbVmomi::VIM.VirtualEthernetCardNetworkBackingInfo(:deviceName => nic.network),
:addressType => 'generated')
}
end
def create_controller operation = :add, controller_key = 1000, bus_id = 0
{
:operation => operation,
:device => RbVmomi::VIM.VirtualLsiLogicController(
:key => controller_key,
:busNumber => bus_id,
:sharedBus => :noSharing
)
}
end
def create_disk disk, index = 0, operation = :add, controller_key = 1000, unit_id = 0
{
:operation => operation,
:fileOperation => :create,
:device => RbVmomi::VIM.VirtualDisk(
:key => index,
:backing => RbVmomi::VIM.VirtualDiskFlatVer2BackingInfo(
:fileName => "[#{disk.datastore}]",
:diskMode => disk.mode.to_sym,
:thinProvisioned => disk.thin
),
:controllerKey => controller_key,
:unitNumber => unit_id,
:capacityInKB => disk.size
)
}
end
def extra_config attributes
[
{
:key => 'bios.bootOrder',
:value => 'ethernet0'
}
]
end
end
class Mock
def create_vm attributes = { }
end
end
end
end
end

View file

@ -1,34 +0,0 @@
module Fog
module Compute
class Vsphere
class Real
def datacenters
@datacenters ||= datacenters_reload
# Hide the values which are the RbVmomi instances
@datacenters.keys
end
private
def datacenters_reload
@rootfolder ||= @connection.rootFolder
inventory = @rootfolder.inventory(:Datacenter => [ 'name' ])[@rootfolder]
# Convert the inventory into a Hash of the form: We remove the
# property selectors. { "<dc_name>" => #<RbVmomi::VIM::Datacenter> }
# The Datacenter instance itself is at index 0 and the properties we
# collected are at index 1.
inventory.inject({}) do |memo, (name,dc_ary)|
memo[name] = dc_ary[0]
memo
end
end
end
class Mock
def datacenters
[ "Solutions", "Solutions2", "Solutions3" ]
end
end
end
end
end

View file

@ -1,41 +0,0 @@
module Fog
module Compute
class Vsphere
module Shared
# 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(options = {})
raise ArgumentError, "Must pass a vm_ref option" unless options['vm_ref']
# This is the inefficient call
all_vm_attributes = list_virtual_machines['virtual_machines']
# Find the VM attributes of the reference
if vm_attributes = all_vm_attributes.find { |vm| vm['mo_ref'] == options['vm_ref'] }
response = { 'virtual_machine' => vm_attributes }
else
raise Fog::Compute::Vsphere::NotFound, "VirtualMachine with Managed Object Reference #{options['vm_ref']} could not be found."
end
response
end
end
# The Real and Mock classes share the same method
# because list_virtual_machines will be properly mocked for us
class Real
include Shared
end
class Mock
include Shared
end
end
end
end

View file

@ -0,0 +1,25 @@
module Fog
module Compute
class Vsphere
class Real
def get_cluster(name, datacenter_name)
cluster = get_raw_cluster(name, datacenter_name)
raise(Fog::Compute::Vsphere::NotFound) unless cluster
cluster_attributes(cluster, datacenter_name)
end
protected
def get_raw_cluster(name, datacenter_name)
dc = find_raw_datacenter(datacenter_name)
dc.find_compute_resource(name)
end
end
class Mock
def get_cluster(id)
end
end
end
end
end

View file

@ -0,0 +1,29 @@
module Fog
module Compute
class Vsphere
class Real
def get_datacenter name
dc = find_raw_datacenter(name)
raise(Fog::Compute::Vsphere::NotFound) unless dc
{:name => dc.name, :status => dc.overallStatus}
end
protected
def find_raw_datacenter name
raw_datacenters.find {|d| d.name == name} || get_raw_datacenter(name)
end
def get_raw_datacenter name
@connection.serviceInstance.find_datacenter(name)
end
end
class Mock
def get_datacenter name
{:name => "Solutions", :status => "grey"}
end
end
end
end
end

View file

@ -0,0 +1,25 @@
module Fog
module Compute
class Vsphere
class Real
def get_datastore(name, datacenter_name)
datastore = get_raw_datastore(name, datacenter_name)
raise(Fog::Compute::Vsphere::NotFound) unless datastore
datastore_attributes(datastore, datacenter_name)
end
protected
def get_raw_datastore(name, datacenter_name)
dc = find_raw_datacenter(datacenter_name)
dc.datastoreFolder.find(name)
end
end
class Mock
def get_datastore(name, datacenter_name)
end
end
end
end
end

View file

@ -0,0 +1,73 @@
module Fog
module Compute
class Vsphere
class Real
def get_folder(path, datacenter_name, type = nil)
type ||= 'vm'
# Cycle through all types of folders.
case type
when 'vm', :vm
# if you're a vm then grab the VM.
folder = get_raw_vmfolder(path, datacenter_name)
raise(Fog::Compute::Vsphere::NotFound) unless folder
folder_attributes(folder, datacenter_name)
when 'network', :network
raise "not implemented"
when 'datastore', :datastore
raise "not implemented"
else
raise ArgumentError, "#{type} is unknown"
end
end
protected
def get_raw_vmfolder(path, datacenter_name)
# The required path syntax - 'topfolder/subfolder
# Clean up path to be relative since we're providing datacenter name
paths = path.sub(/^\/?Datacenters\/#{datacenter_name}\/vm\/?/, '').split('/')
dc = find_raw_datacenter(datacenter_name)
dc_root_folder = dc.vmFolder
return dc_root_folder if paths.empty?
# Walk the tree resetting the folder pointer as we go
paths.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.
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,
:type => folder_type(folder),
:path => folder.path.map(&:last).join('/'),
}
end
def folder_type(folder)
types = folder.childType
return :vm if types.include?('VirtualMachine')
return :network if types.include?('Network')
return :datastore if types.include?('Datastore')
end
end
class Mock
def get_folder(path, filters = { })
end
end
end
end
end

View file

@ -0,0 +1,25 @@
module Fog
module Compute
class Vsphere
class Real
def get_network(name, datacenter_name)
network = get_raw_network(name, datacenter_name)
raise(Fog::Compute::Vsphere::NotFound) unless network
network_attributes(network, datacenter_name)
end
protected
def get_raw_network(name, datacenter_name)
dc = find_raw_datacenter(datacenter_name)
dc.networkFolder.find(name)
end
end
class Mock
def get_network(id)
end
end
end
end
end

View file

@ -0,0 +1,26 @@
module Fog
module Compute
class Vsphere
class Real
def get_resource_pool(name, cluster_name, datacenter_name)
resource_pool = get_raw_resource_pool(name, cluster_name, datacenter_name)
raise(Fog::Compute::Vsphere::NotFound) unless resource_pool
resource_pool_attributes(resource_pool, cluster_name, datacenter_name)
end
protected
def get_raw_resource_pool(name, cluster_name, datacenter_name)
dc = get_raw_datacenter(datacenter_name)
cluster = dc.find_compute_resource(cluster_name)
cluster.resourcePool.find name
end
end
class Mock
def get_resource_pool(name, cluster_name, datacenter_name)
end
end
end
end
end

View file

@ -0,0 +1,62 @@
module Fog
module Compute
class Vsphere
class Real
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
vm ? vm : raise(Fog::Compute::Vsphere::NotFound, "#{id} was not found")
end
end
class Mock
def get_virtual_machine(id, datacenter_name = nil)
case id
when "5032c8a5-9c5e-ba7a-3804-832a03e16381", 'vm-715'
{ :resource_pool => "Resources",
:memory_mb => 2196,
:power_state => "poweredOn",
:mac_addresses => { "Network adapter 1" => "00:50:56:b2:00:af" },
:hostname => "centos56gm.localdomain",
:cpus => 1,
:connection_state => "connected",
:mo_ref => "vm-715",
:overall_status => "green",
:datacenter => "Solutions",
:instance_uuid => "5029c440-85ee-c2a1-e9dd-b63e39364603",
:hypervisor => "gunab.puppetlabs.lan",
:guest_id => "rhel6_64Guest",
:cluster => "virtlabcluster",
:tools_state => "toolsOk",
:name => "jefftest",
:operatingsystem => "Red Hat Enterprise Linux 6 (64-bit)",
:path => "/",
:tools_version => "guestToolsUnmanaged",
:ipaddress => "192.168.100.187",
:id => "5032c8a5-9c5e-ba7a-3804-832a03e16381",
:uuid => "42329da7-e8ab-29ec-1892-d6a4a964912a"
}
end
end
end
end
end
end

View file

@ -0,0 +1,37 @@
module Fog
module Compute
class Vsphere
class Real
def list_clusters(filters = { })
datacenter_name = filters[:datacenter]
raw_clusters(datacenter_name).map do |cluster|
cluster_attributes(cluster, datacenter_name)
end
end
def raw_clusters(datacenter)
find_raw_datacenter(datacenter).hostFolder.childEntity.grep(RbVmomi::VIM::ClusterComputeResource)
end
protected
def cluster_attributes cluster, datacenter_name
{
:id => managed_obj_id(cluster),
:name => cluster.name,
:num_host => cluster.summary.numHosts,
:num_cpu_cores => cluster.summary.numCpuCores,
:overall_status => cluster.summary.overallStatus,
:datacenter => datacenter_name || parent_attribute(cluster.path, :datacenter)[1],
}
end
end
class Mock
def list_clusters(filters = { })
end
end
end
end
end

View file

@ -0,0 +1,34 @@
module Fog
module Compute
class Vsphere
class Real
def list_datacenters filters = {}
raw_datacenters.map do |dc|
{
:id => managed_obj_id(dc),
:name => dc.name,
:status => dc.overallStatus
}
end
end
protected
def raw_datacenters
@raw_datacenters ||= @connection.rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter)
end
def find_datacenters name=nil
name ? [get_raw_datacenter(name)] : raw_datacenters
end
end
class Mock
def list_datacenters filters = {}
[ {:name => "Solutions", :status => "grey"}, {:name => "Solutions2", :status => "green" }]
end
end
end
end
end

View file

@ -0,0 +1,40 @@
module Fog
module Compute
class Vsphere
class Real
def list_datastores(filters = { })
datacenter_name = filters[:datacenter]
# default to show all datastores
only_active = filters[:accessible] || false
raw_datastores(datacenter_name).map do |datastore|
next if only_active and !datastore.summary.accessible
datastore_attributes(datastore, datacenter_name)
end.compact
end
def raw_datastores(datacenter_name)
find_raw_datacenter(datacenter_name).datastore
end
protected
def datastore_attributes datastore, datacenter
{
:id => managed_obj_id(datastore),
:name => datastore.name,
:accessible => datastore.summary.accessible,
:type => datastore.summary.type,
:freespace => datastore.summary.freeSpace,
:capacity => datastore.summary.capacity,
:datacenter => datacenter,
}
end
end
class Mock
def list_datastores(datacenter_name)
[]
end
end
end
end
end

View file

@ -0,0 +1,45 @@
module Fog
module Compute
class Vsphere
class Real
# Grabs all sub folders within a given path folder.
#
# ==== Parameters
# * filters<~Hash>:
# * :datacenter<~String> - *REQUIRED* Your datacenter where you're
# looking for folders. Example: 'my-datacenter-name' (passed if you
# are using the models/collections)
# eg: vspconn.datacenters.first.vm_folders('mypath')
# * :path<~String> - Your path where you're looking for
# more folders, if return = none you will get an error. If you don't
# define it will look in the main datacenter folder for any folders
# in that datacenter.
#
# Example Usage Testing Only:
# vspconn = Fog::Compute[:vsphere]
# mydc = vspconn.datacenters.first
# folders = mydc.vm_folders
#
def list_folders(filters = { })
path = filters[:path] || filters['path'] || ''
datacenter_name = filters[:datacenter]
get_raw_vmfolders(path, datacenter_name).map do |folder|
folder_attributes(folder, datacenter_name)
end
end
protected
def get_raw_vmfolders(path, datacenter_name)
folder = get_raw_vmfolder(path, datacenter_name)
child_folders(folder).flatten.compact
end
def child_folders folder
[folder, folder.childEntity.grep(RbVmomi::VIM::Folder).map(&method(:child_folders)).flatten]
end
end
end
end
end

View file

@ -0,0 +1,38 @@
module Fog
module Compute
class Vsphere
class Real
def list_networks(filters = { })
datacenter_name = filters[:datacenter]
# default to show all networks
only_active = filters[:accessible] || false
raw_networks(datacenter_name).map do |network|
next if only_active and !network.summary.accessible
network_attributes(network, datacenter_name)
end.compact
end
def raw_networks(datacenter_name)
find_raw_datacenter(datacenter_name).network
end
protected
def network_attributes network, datacenter
{
:id => managed_obj_id(network),
:name => network.name,
:accessible => network.summary.accessible,
:datacenter => datacenter,
}
end
end
class Mock
def list_networks(datacenter_name)
[]
end
end
end
end
end

View file

@ -0,0 +1,39 @@
module Fog
module Compute
class Vsphere
class Real
def list_resource_pools(filters = { })
datacenter_name = filters[:datacenter]
cluster_name = filters[:cluster]
cluster = get_raw_cluster(cluster_name, datacenter_name)
list_raw_resource_pools(cluster).map do |resource_pool|
resource_pool_attributes(resource_pool, cluster_name, datacenter_name)
end
end
protected
# root ResourcePool + Children if they exists
def list_raw_resource_pools(cluster)
[cluster.resourcePool, cluster.resourcePool.resourcePool].flatten
end
def resource_pool_attributes resource_pool, cluster, datacenter
{
:id => managed_obj_id(resource_pool),
:name => resource_pool.name,
:configured_memory_mb => resource_pool.summary.configuredMemoryMB,
:overall_status => resource_pool.overallStatus,
:cluster => cluster,
:datacenter => datacenter
}
end
end
class Mock
def list_resource_pools(filters = { })
end
end
end
end
end

View file

@ -2,97 +2,43 @@ module Fog
module Compute
class Vsphere
class Real
def list_virtual_machines(options = {})
def list_virtual_machines(options = { })
# Listing all VM's can be quite slow and expensive. Try and optimize
# based on the available options we have. These conditions are in
# ascending order of time to complete for large deployments.
# ascending order of time to complete for large deployments.
options[:folder] ||= options['folder']
if options['instance_uuid'] then
list_all_virtual_machines_by_instance_uuid(options)
elsif options['folder'] then
list_all_virtual_machines_in_folder(options)
[connection.get_virtual_machine(options['instance_uuid'])]
elsif options[:folder] && options[:datacenter] then
list_all_virtual_machines_in_folder(options[:folder], options[:datacenter])
else
list_all_virtual_machines
list_all_virtual_machines(options)
end
end
private
def list_all_virtual_machines_in_folder(options = {})
# Tap gets rid of the leading empty string and "Datacenters" element
# and returns the array.
path_elements = options['folder'].split('/').tap { |ary| ary.shift 2 }
# The DC name itself.
dc_name = path_elements.shift
# If the first path element contains "vm" this denotes the vmFolder
# and needs to be shifted out since each DC contains only one
# vmFolder
path_elements.shift if path_elements[0] == 'vm'
# Make sure @datacenters is populated (the keys are DataCenter instances)
self.datacenters.include? dc_name or raise ArgumentError, "Could not find a Datacenter named #{dc_name}"
# Get the datacenter managed object
dc = @datacenters[dc_name]
def list_all_virtual_machines_in_folder(path, datacenter_name)
folder = get_raw_vmfolder(path, datacenter_name)
# 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
# This should be efficient since we're not obtaining properties
virtual_machines = folder.children.inject([]) do |ary, child|
if child.is_a? RbVmomi::VIM::VirtualMachine then
ary << convert_vm_mob_ref_to_attr_hash(child)
end
ary
end
# Return the managed objects themselves as an array. These may be converted
# to an attribute has using convert_vm_mob_ref_to_attr_hash
{ 'virtual_machines' => virtual_machines }
folder.children.grep(RbVmomi::VIM::VirtualMachine).map(&method(:convert_vm_mob_ref_to_attr_hash))
end
def list_all_virtual_machines_by_instance_uuid(options = {})
uuid = options['instance_uuid']
search_filter = { :uuid => uuid, 'vmSearch' => true, 'instanceUuid' => true }
vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first
if vm_attribute_hash = convert_vm_mob_ref_to_attr_hash(vm_mob_ref) then
virtual_machines = [ vm_attribute_hash ]
else
virtual_machines = [ ]
end
{ 'virtual_machines' => virtual_machines }
end
def list_all_virtual_machines(options = { })
datacenters = find_datacenters(options[:datacenter])
# TODO: go though nested folders
vms = datacenters.map { |dc| dc.vmFolder.childEntity.grep(RbVmomi::VIM::VirtualMachine) }.flatten
# remove all template based virtual machines
vms.delete_if { |v| v.config.template }
def list_all_virtual_machines
virtual_machines = list_all_virtual_machine_mobs.collect do |vm_mob|
vms.map do |vm_mob|
convert_vm_mob_ref_to_attr_hash(vm_mob)
end
{ 'virtual_machines' => virtual_machines }
end
# NOTE: This is a private instance method required by the vm_clone
# request. It's very hard to get the Managed Object Reference
# of a Template because we can't search for it by instance_uuid
# As a result, we need a list of all virtual machines, and we
# need them in "native" format, not filter attribute format.
def list_all_virtual_machine_mobs
# Find each datacenter
datacenters = @connection.rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter)
# Next, search the "vmFolder" inventory of each data center:
datacenters.map {|dc| dc.vmFolder.childEntity.grep(RbVmomi::VIM::VirtualMachine) }.flatten
end
def get_folder_path(folder, root = nil)
if ( not folder.methods.include?('parent') ) or ( folder == root )
if (not folder.methods.include?('parent')) or (folder == root)
return
end
"#{get_folder_path(folder.parent)}/#{folder.name}"
@ -105,100 +51,120 @@ module Fog
nil
end
def list_virtual_machines(options = {})
def list_virtual_machines(options = { })
case options['instance_uuid']
when nil
rval = YAML.load <<-'ENDvmLISTING'
---
virtual_machines:
- name: centos56gm
hypervisor: gunab.puppetlabs.lan
tools_version: guestToolsCurrent
ipaddress:
mo_ref: vm-698
power_state: poweredOff
uuid: 42322347-d791-cd34-80b9-e25fe28ad37c
is_a_template: true
id: 50323f93-6835-1178-8b8f-9e2109890e1a
tools_state: toolsNotRunning
connection_state: connected
instance_uuid: 50323f93-6835-1178-8b8f-9e2109890e1a
hostname:
mac_addresses:
Network adapter 1: 00:50:56:b2:00:a1
operatingsystem:
- name: centos56gm2
hypervisor: gunab.puppetlabs.lan
tools_version: guestToolsCurrent
ipaddress:
mo_ref: vm-640
power_state: poweredOff
uuid: 564ddcbe-853a-d29a-b329-a0a3693a004d
is_a_template: true
id: 5257dee8-050c-cbcd-ae25-db0e582ab530
tools_state: toolsNotRunning
connection_state: connected
instance_uuid: 5257dee8-050c-cbcd-ae25-db0e582ab530
hostname:
mac_addresses:
Network adapter 1: 00:0c:29:3a:00:4d
operatingsystem:
- name: dashboard_gm
hypervisor: gunab.puppetlabs.lan
tools_version: guestToolsCurrent
ipaddress: 192.168.100.184
mo_ref: vm-669
power_state: poweredOn
uuid: 564d3f91-3452-a509-a678-1246f7897979
is_a_template: false
id: 5032739c-c871-c0d2-034f-9700a0b5383e
tools_state: toolsOk
connection_state: connected
instance_uuid: 5032739c-c871-c0d2-034f-9700a0b5383e
hostname: compliance.puppetlabs.vm
mac_addresses:
Network adapter 1: 00:50:56:b2:00:96
operatingsystem: Red Hat Enterprise Linux 6 (64-bit)
- name: jefftest
hypervisor: gunab.puppetlabs.lan
tools_version: guestToolsCurrent
ipaddress: 192.168.100.187
mo_ref: vm-715
power_state: poweredOn
uuid: 42329da7-e8ab-29ec-1892-d6a4a964912a
is_a_template: false
id: 5032c8a5-9c5e-ba7a-3804-832a03e16381
tools_state: toolsOk
connection_state: connected
instance_uuid: 5032c8a5-9c5e-ba7a-3804-832a03e16381
hostname: centos56gm.localdomain
mac_addresses:
Network adapter 1: 00:50:56:b2:00:af
operatingsystem: CentOS 4/5 (32-bit)
ENDvmLISTING
when '5032c8a5-9c5e-ba7a-3804-832a03e16381'
YAML.load <<-'5032c8a5-9c5e-ba7a-3804-832a03e16381'
---
virtual_machines:
- name: jefftest
hypervisor: gunab.puppetlabs.lan
tools_version: guestToolsCurrent
ipaddress: 192.168.100.187
mo_ref: vm-715
power_state: poweredOn
uuid: 42329da7-e8ab-29ec-1892-d6a4a964912a
is_a_template: false
id: 5032c8a5-9c5e-ba7a-3804-832a03e16381
tools_state: toolsOk
connection_state: connected
instance_uuid: 5032c8a5-9c5e-ba7a-3804-832a03e16381
hostname: centos56gm.localdomain
mac_addresses:
Network adapter 1: 00:50:56:b2:00:af
operatingsystem: CentOS 4/5 (32-bit)
5032c8a5-9c5e-ba7a-3804-832a03e16381
when 'does-not-exist-and-is-not-a-uuid', '50323f93-6835-1178-8b8f-9e2109890e1a'
{ 'virtual_machines' => [] }
when nil
[
{ :resource_pool => "Resources",
:memory_mb => 2196,
:mac_addresses => { "Network adapter 1" => "00:50:56:a9:00:28" },
:power_state => "poweredOn",
:cpus => 1,
:hostname => "dhcp75-197.virt.bos.redhat.com",
:mo_ref => "vm-562",
:connection_state => "connected",
:overall_status => "green",
:datacenter => "Solutions",
:volumes =>
[{
:id => "6000C29c-a47d-4cd9-5249-c371de775f06",
:datastore => "Storage1",
:mode => "persistent",
:size => 8388608,
:thin => true,
:name => "Hard disk 1",
:filename => "[Storage1] rhel6-mfojtik/rhel6-mfojtik.vmdk",
:size_gb => 8
}],
:interfaces =>
[{ :mac => "00:50:56:a9:00:28",
:network => "VM Network",
:name => "Network adapter 1",
:status => "ok",
:summary => "VM Network",
}],
:hypervisor => "gunab.puppetlabs.lan",
:guest_id => "rhel6_64Guest",
:tools_state => "toolsOk",
:cluster => "Solutionscluster",
:name => "rhel64",
:operatingsystem => "Red Hat Enterprise Linux 6 (64-bit)",
:uuid => "4229f0e9-bfdc-d9a7-7bac-12070772e6dc",
:path => "/Datacenters/Solutions/vm",
:id => "5029c440-85ee-c2a1-e9dd-b63e39364603",
:tools_version => "guestToolsUnmanaged",
:ipaddress => "192.168.100.184",
},
{ :resource_pool => "Resources",
:memory_mb => 512,
:power_state => "poweredOn",
:mac_addresses => { "Network adapter 1" => "00:50:56:a9:00:00" },
:hostname => nil,
:cpus => 1,
:connection_state => "connected",
:mo_ref => "vm-621",
:overall_status => "green",
:datacenter => "Solutions",
:volumes =>
[{ :thin => false,
:size_gb => 10,
:datastore => "datastore1",
:filename => "[datastore1] i-1342439683/i-1342439683.vmdk",
:size => 10485762,
:name => "Hard disk 1",
:mode => "persistent",
:id => "6000C29b-f364-d073-8316-8e98ac0a0eae" }],
:interfaces =>
[{ :summary => "VM Network",
:mac => "00:50:56:a9:00:00",
:status => "ok",
:network => "VM Network",
:name => "Network adapter 1" }],
:instance_uuid => "502916a3-b42e-17c7-43ce-b3206e9524dc",
:hypervisor => "gunab.puppetlabs.lan",
:guest_id => nil,
:cluster => "Solutionscluster",
:tools_state => "toolsNotInstalled",
:uuid => "4229e0de-30cb-ceb2-21f9-4d8d8beabb52",
:name => "i-1342439683",
:operatingsystem => nil,
:path => "/",
:tools_version => "guestToolsNotInstalled",
:id => "502916a3-b42e-17c7-43ce-b3206e9524dc",
:ipaddress => nil }
]
when '5032c8a5-9c5e-ba7a-3804-832a03e16381'
[{ :resource_pool => "Resources",
:memory_mb => 2196,
:power_state => "poweredOn",
:mac_addresses => { "Network adapter 1" => "00:50:56:b2:00:af" },
:hostname => "centos56gm.localdomain",
:cpus => 1,
:connection_state => "connected",
:mo_ref => "vm-715",
:overall_status => "green",
:datacenter => "Solutions",
:instance_uuid => "5029c440-85ee-c2a1-e9dd-b63e39364603",
:hypervisor => "gunab.puppetlabs.lan",
:guest_id => "rhel6_64Guest",
:cluster => "Solutionscluster",
:tools_state => "toolsOk",
:name => "jefftest",
:operatingsystem => "Red Hat Enterprise Linux 6 (64-bit)",
:path => "/",
:tools_version => "guestToolsUnmanaged",
:ipaddress => "192.168.100.187",
:id => "5032c8a5-9c5e-ba7a-3804-832a03e16381",
:uuid => "42329da7-e8ab-29ec-1892-d6a4a964912a"
}
]
when 'does-not-exist-and-is-not-a-uuid', '50323f93-6835-1178-8b8f-9e2109890e1a'
[]
end
end
end

View file

@ -0,0 +1,52 @@
module Fog
module Compute
class Vsphere
class Real
# => VirtualE1000(
#addressType: "assigned",
#backing: VirtualEthernetCardNetworkBackingInfo(
# deviceName: "VM Network",
# dynamicProperty: [],
# network: Network("network-163"),
# useAutoDetect: false
#),
#connectable: VirtualDeviceConnectInfo(
# allowGuestControl: true,
# connected: true,
# dynamicProperty: [],
# startConnected: true,
# status: "ok"
#),
#controllerKey: 100,
#deviceInfo: Description(
# dynamicProperty: [],
# label: "Network adapter 1",
# summary: "VM Network"
#),
#dynamicProperty: [],
#key: 4000,
#macAddress: "00:50:56:a9:00:28",
#unitNumber: 7,
#
def list_vm_interfaces(vm_id)
get_vm_ref(vm_id).config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).map do |nic|
{
:name => nic.deviceInfo.label,
:mac => nic.macAddress,
:network => nic.backing.network.name,
:status => nic.connectable.status,
:summary => nic.deviceInfo.summary,
:type => nic.class,
}
end
end
end
class Mock
def list_vm_interfaces(vm_id)
end
end
end
end
end

View file

@ -0,0 +1,51 @@
module Fog
module Compute
class Vsphere
class Real
# [VirtualDisk(
# backing: VirtualDiskFlatVer2BackingInfo(
# contentId: "a172d19487e878e17d6b16ff2505d7eb",
# datastore: Datastore("datastore-162"),
# diskMode: "persistent",
# dynamicProperty: [],
# fileName: "[Storage1] rhel6-mfojtik/rhel6-mfojtik.vmdk",
# split: false,
# thinProvisioned: true,
# uuid: "6000C29c-a47d-4cd9-5249-c371de775f06",
# writeThrough: false
# ),
# capacityInKB: 8388608,
# controllerKey: 1000,
# deviceInfo: Description(
# dynamicProperty: [],
# label: "Hard disk 1",
# summary: "8,388,608 KB"
# ),
# dynamicProperty: [],
# key: 2001,
# shares: SharesInfo( dynamicProperty: [], level: "normal", shares: 1000 ),
# unitNumber: 1
#)]
def list_vm_volumes(vm_id)
get_vm_ref(vm_id).disks.map do |vol|
{
:id => vol.backing.uuid,
:thin => vol.backing.thinProvisioned,
:mode => vol.backing.diskMode,
:filename => vol.backing.fileName,
:datastore => (vol.backing.datastore.name rescue(nil)),
:size => vol.capacityInKB,
:name => vol.deviceInfo.label
}
end
end
end
class Mock
def list_vm_volumes(vm_id)
end
end
end
end
end

View file

@ -21,8 +21,8 @@ module Fog
raise ArgumentError, "vm_clone path option must start with /Datacenters. Got: #{options['path']}"
end
dc_name = path_elements.shift
if not self.datacenters.include? dc_name then
raise ArgumentError, "Datacenter #{dc_name} does not exist, only datacenters #{self.datacenters.join(",")} are accessible."
if not datacenters.get dc_name then
raise ArgumentError, "Datacenter #{dc_name} does not exist, only datacenters #{datacenters.all.map(:name).join(",")} are accessible."
end
options
end
@ -75,10 +75,8 @@ module Fog
# The template name. The remaining elements are the folders in the
# datacenter.
template_name = path_elements.pop
# Make sure @datacenters is populated. We need the instances from the Hash keys.
self.datacenters
# Get the datacenter managed object from the hash
dc = @datacenters[template_dc]
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
@ -195,8 +193,8 @@ module Fog
# Option handling
options = vm_clone_check_options(options)
notfound = lambda { raise Fog::Compute::Vsphere::NotFound, "Could not find VM template" }
vm_mob_ref = list_virtual_machines['virtual_machines'].find(notfound) do |vm|
vm['name'] == options['path'].split("/")[-1]
list_virtual_machines.find(notfound) do |vm|
vm[:name] == options['path'].split("/")[-1]
end
{
'vm_ref' => 'vm-123',

View file

@ -1,97 +0,0 @@
module Fog
module Compute
class Vsphere
module Shared
private
def vm_create_check_options(options)
options = { 'force' => false }.merge(options)
required_options = %w{ path name cluster }
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 }
first_folder = path_elements.shift
if first_folder != 'Datacenters' then
raise ArgumentError, "vm_create path option must start with /Datacenters. Got: #{options['path']}"
end
dc_name = path_elements.shift
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
class Real
include Shared
def vm_create(options = {})
# Option handling
options = vm_create_check_options(options)
path_elements = options['path'].split('/').tap { |ary| ary.shift 2 }
dc_name = path_elements.shift
vm_cfg = {
:name => options['name'],
:guestId => options['guest_id'] ? options['guest_id'] : 'otherGuest',
:files => { :vmPathName => "[#{options['datastore']}]" },
:numCPUs => options['num_cpus'] ? options['num_cpus'] : 1 ,
:memoryMB => options['memory'] ? options['memory'] : 512,
:memoryHotAddEnabled => options['memory_hot_add_enabled'] ? options['memory_hot_add_enabled'] : 0,
:cpuHotAddEnabled => options['cpu_hot_add_enabled'] ? options['cpu_hot_add_enabled'] : 0,
:deviceChange => options['device_array'].class == Array ? options['device_array'] : nil,
:extraConfig => options['extra_config'].class == Array ? options['extra_config'] : nil,
}
self.datacenters
dc = @datacenters[dc_name]
vm_folder = dc.vmFolder
folder = path_elements.inject(vm_folder) do |current_folder, sub_folder_name|
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
clusters = dc.hostFolder.children
build_cluster=''
clusters.each { |my_cluster|
if "#{my_cluster.name}" == "#{options['cluster']}"
build_cluster=my_cluster
end
}
resource_pool = build_cluster.resourcePool
task=folder.CreateVM_Task(:config => vm_cfg, :pool => resource_pool)
if options['wait'] then
new_vm = task.wait_for_completion
else
tries = 0
new_vm = begin
folder.find(options['name'], RbVmomi::VIM::VirtualMachine) or raise Fog::Vsphere::Errors::NotFound
rescue Fog::Vsphere::Errors::NotFound
tries += 1
if tries <= 10 then
sleep 15
retry
end
nil
end
end
{
'vm_ref' => new_vm ? new_vm._ref : nil,
'vm_attributes' => new_vm ? convert_vm_mob_ref_to_attr_hash(new_vm) : {},
'task_ref' => task._ref
}
end
end
class Mock
include Shared
def vm_create(options = {})
# Option handling
options = vm_create_check_options(options)
{
'vm_ref' => 'vm-123',
'task_ref' => 'task-1234'
}
end
end
end
end
end

View file

@ -6,14 +6,7 @@ module Fog
def vm_destroy(options = {})
raise ArgumentError, "instance_uuid is a required parameter" unless options.has_key? 'instance_uuid'
# Find the VM Object
search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first
unless vm_mob_ref.kind_of? RbVmomi::VIM::VirtualMachine
raise Fog::Vsphere::Errors::NotFound,
"Could not find VirtualMachine with instance uuid #{options['instance_uuid']}"
end
vm_mob_ref = get_vm_ref(options['instance_uuid'])
task = vm_mob_ref.Destroy_Task
task.wait_for_completion
{ 'task_state' => task.info.state }

View file

@ -5,8 +5,7 @@ module Fog
def vm_reconfig_hardware(options = {})
raise ArgumentError, "hardware_spec is a required parameter" unless options.has_key? 'hardware_spec'
raise ArgumentError, "instance_uuid is a required parameter" unless options.has_key? 'instance_uuid'
search_filter = { :uuid => options['instance_uuid'], 'vmSearch' => true, 'instanceUuid' => true }
vm_mob_ref = @connection.searchIndex.FindAllByUuid(search_filter).first
vm_mob_ref = get_vm_by_ref(options['instance_uuid'])
task = vm_mob_ref.ReconfigVM_Task(:spec => RbVmomi::VIM.VirtualMachineConfigSpec(options['hardware_spec']))
task.wait_for_completion
{ 'task_state' => task.info.state }

View file

@ -21,10 +21,10 @@ Shindo.tests('Fog::Compute[:vsphere]', ['vsphere']) do
tests("When converting an incomplete vm object") do
test("it should return a Hash") do
compute.convert_vm_mob_ref_to_attr_hash(fake_vm_mob_ref).kind_of? Hash
compute.send(:convert_vm_mob_ref_to_attr_hash, fake_vm_mob_ref).kind_of? Hash
end
tests("The converted Hash should") do
attr_hash = compute.convert_vm_mob_ref_to_attr_hash(fake_vm_mob_ref)
attr_hash = compute.send(:convert_vm_mob_ref_to_attr_hash, fake_vm_mob_ref)
test("have a name") { attr_hash['name'] == 'fakevm' }
test("have a mo_ref") {attr_hash['mo_ref'] == 'vm-123' }
test("have an id") { attr_hash['id'] == 'vm-123' }
@ -33,7 +33,7 @@ Shindo.tests('Fog::Compute[:vsphere]', ['vsphere']) do
end
tests("When passed a nil object") do
attr_hash = compute.convert_vm_mob_ref_to_attr_hash(nil)
attr_hash = compute.send :convert_vm_mob_ref_to_attr_hash, nil
test("it should return a nil object") do
attr_hash.nil?
end

View file

@ -26,8 +26,7 @@ Shindo.tests('Fog::Compute[:vsphere] | server model', ['vsphere']) do
:connection_state,
:hypervisor,
:name,
:ipaddress,
:is_a_template]
:ipaddress]
tests("The server model should respond to") do
attributes.each do |attribute|
test("#{attribute}") { server.respond_to? attribute }

View file

@ -1,26 +0,0 @@
Shindo.tests('Fog::Compute[:vsphere] | find_vm_by_ref request', ['vsphere']) do
compute = Fog::Compute[:vsphere]
tests("When missing arguments") do
raises(ArgumentError, "Should raise ArgumentError when missing :vm_ref") do
compute.find_vm_by_ref
end
raises(Fog::Compute::Vsphere::NotFound, "Should raise Fog::Compute::Vsphere::NotFound when the vm does not exist") do
compute.find_vm_by_ref('vm_ref' => 'vm-000')
end
end
# centos56gm is a template
existing_vms = { 'vm-715' => 'jefftest', 'vm-698' => 'centos56gm' }
tests("When looking for existing VM's the response") do
existing_vms.each do |ref,name|
response = compute.find_vm_by_ref('vm_ref' => ref)
test("should be a kind of Hash") { response.kind_of? Hash }
test("should have virtual_machine key") { response.has_key? 'virtual_machine' }
returns(name, "#{ref} should return #{name}") { response['virtual_machine']['name'] }
end
end
end

View file

@ -1,19 +1,11 @@
Shindo.tests('Fog::Compute[:vsphere] | list_virtual_machines request', ['vsphere']) do
key = 'virtual_machines'
tests("When listing all machines") do
response = Fog::Compute[:vsphere].list_virtual_machines
tests("The response data format ...") do
test("be a kind of Hash") { response.kind_of? Hash }
test("have a #{key} key") do
response.has_key? key
end
test("it should be a kind of Array") do
response[key].kind_of? Array
end
test("be a kind of Hash") { response.kind_of? Array }
end
end
@ -26,9 +18,9 @@ Shindo.tests('Fog::Compute[:vsphere] | list_virtual_machines request', ['vsphere
response = Fog::Compute[:vsphere].list_virtual_machines({'instance_uuid' => uuid})
tests("The response should") do
test("contain one vm") { response[key].length == 1 }
test("contain that is an attribute hash") { response[key][0].kind_of? Hash }
test("find jefftest") { response[key].first['name'] == 'jefftest' }
test("contain one vm") { response.length == 1 }
test("contain that is an attribute hash") { response[0].kind_of? Hash }
test("find jefftest") { response.first[:name] == 'jefftest' }
end
end
@ -37,7 +29,7 @@ Shindo.tests('Fog::Compute[:vsphere] | list_virtual_machines request', ['vsphere
response = Fog::Compute[:vsphere].list_virtual_machines({'instance_uuid' => uuid})
tests("The response should") do
test("be empty") { response[key].empty? }
test("be empty") { response.empty? }
end
end
end

View file

@ -4,7 +4,7 @@ Shindo.tests("Fog::Compute[:vsphere] | vm_clone request", 'vsphere') do
response = nil
response_linked = nil
template = "/Datacenters/Solutions/vm/Jeff/Templates/centos56gm2"
template = "/Datacenters/Solutions/vm/rhel64"
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 }
@ -13,7 +13,7 @@ Shindo.tests("Fog::Compute[:vsphere] | vm_clone request", 'vsphere') do
end
end
template = "/Datacenters/Solutions/vm/Jeff/Templates/centos56gm2"
template = "/Datacenters/Solutions/vm/rhel64"
tests("Linked Clone | The return value should") do
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 }

View file

@ -1,20 +0,0 @@
Shindo.tests("Fog::Compute[:vsphere] | vm_create request", 'vsphere') do
#require 'guid'
path = "/Datacenters/Solutions"
compute = Fog::Compute[:vsphere]
tests("The return value should") do
response = compute.vm_create('path' => path, 'name' => 'fog_test_vm', 'cluster' => 'cluster01')
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
tests("When invalid input is presented") do
raises(ArgumentError, 'it should raise ArgumentError') { compute.vm_create(:foo => 1) }
raises(Fog::Compute::Vsphere::NotFound, 'it should raise Fog::Compute::Vsphere::NotFound when the UUID is not a string') do
pending # require 'guid'
compute.vm_create('instance_uuid' => Guid.from_s(template), 'name' => 'jefftestfoo')
end
end
end