F #3045: vCenter support for limiting VM traffic

rbvmomi updated to v1.9.3
This commit is contained in:
Tino Vazquez committed 2016-09-23 11:44:35 +02:00
1 parent fa05b5dc34
commit f1915898a2
16 files changed
+148 -88

No files matched your search

+19 -2
View File
@@ -1,17 +1,34 @@
= RbVmomi
{<img src="https://travis-ci.org/vmware/rbvmomi.svg?branch=master" alt="travis-ci">}[http://travis-ci.org/vmware/rbvmomi]
This is an Open Source community supported project at VMware. It is built and maintained by programmers like you!
== Introduction
RbVmomi is a Ruby interface to the vSphere API. Like the Perl and Java SDKs,
you can use it to manage ESX and VirtualCenter servers. The current release
supports the vSphere 5.0 API. RbVmomi specific documentation is
online[http://rdoc.info/github/rlane/rbvmomi/master/frames] and is meant to
be used alongside the official documentation[http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/index.html].
be used alongside the official documentation[http://pubs.vmware.com/vsphere-60/topic/com.vmware.sdk.doc/GUID-19793BCA-9EAB-42E2-8B9F-F9F2129E7741.html].
== Installation
gem install rbvmomi
=== Support for older Ruby versions
RbVmomi supports Ruby 1.8.7 and higher, but certain dependencies may need
pinning to older versions to get a compatible set of gems.
On Ruby 1.8.7:
* use +nokogiri+ 1.5.x (Gemfile: <code>gem 'nokogiri', '< 1.6'</code>)
On both Ruby 1.9 and 1.8.7:
* use +json+ 1.x (Gemfile: <code>gem 'json', '< 2'</code>)
== Usage
A simple example of turning on a VM:
@@ -56,7 +73,7 @@ in the first example uses the SearchIndex for fast lookups.
A few important points:
* All class, method, parameter, and property names match the official documentation[http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/index.html].
* All class, method, parameter, and property names match the official documentation[http://pubs.vmware.com/vsphere-60/topic/com.vmware.sdk.doc/GUID-19793BCA-9EAB-42E2-8B9F-F9F2129E7741.html].
* Properties are exposed as accessor methods.
* Data object types can usually be inferred from context, so you may use a hash instead.
* Enumeration values are simply strings.
+2 -34
View File
@@ -1,26 +1,8 @@
require 'bundler/gem_tasks'
require 'rake/testtask'
require 'rdoc/task'
require 'yard'
begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
gem.name = "rbvmomi"
gem.summary = "Ruby interface to the VMware vSphere API"
#gem.description = ""
gem.email = "rlane@vmware.com"
gem.homepage = "https://github.com/vmware/rbvmomi"
gem.authors = ["Rich Lane", "Christian Dickmann"]
gem.add_dependency 'nokogiri', '>= 1.4.1'
gem.add_dependency 'builder'
gem.add_dependency 'trollop'
gem.required_ruby_version = '>= 1.8.7'
gem.files.include 'vmodl.db'
gem.files.include '.yardopts'
end
rescue LoadError
puts "Jeweler not available. Install it with: gem install jeweler"
end
task(:default => :test)
Rake::TestTask.new do |t|
t.libs << "test"
@@ -29,17 +11,3 @@ Rake::TestTask.new do |t|
end
YARD::Rake::YardocTask.new
begin
require 'rcov/rcovtask'
desc 'Measures test coverage using rcov'
Rcov::RcovTask.new do |rcov|
rcov.pattern = 'test/test_*.rb'
rcov.output_dir = 'coverage'
rcov.verbose = true
rcov.libs << "test"
rcov.rcov_opts << '--exclude "gems/*"'
end
rescue LoadError
puts "Rcov not available. Install it with: gem install rcov"
end
+1 -1
View File
@@ -1 +1 @@
1.8.2
1.9.3
@@ -158,6 +158,12 @@ class DataObject < ObjectWithProperties
q.text ')'
end
def to_json(*args)
h = self.props
m = h.merge({ JSON.create_id => self.class.name })
m.to_json(*args)
end
init
end
+2 -2
View File
@@ -48,8 +48,8 @@ class RbVmomi::TrivialSoap
@http.key = OpenSSL::PKey::RSA.new(@opts[:key]) if @opts[:key]
end
@http.set_debug_output(STDERR) if $DEBUG
@http.read_timeout = 1000000
@http.open_timeout = 60
@http.read_timeout = @opts[:read_timeout] || 1000000
@http.open_timeout = @opts[:open_timeout] || 60
def @http.on_connect
@socket.io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
end
@@ -77,11 +77,11 @@ class AdmissionControlledResourceScheduler
# Returns the used VM folder. If not set yet, uses the vm_folder_path to
# lookup the folder. If it doesn't exist, it is created. Collisions between
# multiple clients concurrently creating the same folder are handled.
# @return [VIM::Folder] The VM folder
# @return [RbVmomi::VIM::Folder] The VM folder
def vm_folder
retries = 1
begin
@vm_folder ||= datacenter.vmFolder.traverse!(@vm_folder_path, VIM::Folder)
@vm_folder ||= datacenter.vmFolder.traverse!(@vm_folder_path, RbVmomi::VIM::Folder)
if !@vm_folder
fail "VM folder #{@vm_folder_path} not found"
end
@@ -98,10 +98,10 @@ class AdmissionControlledResourceScheduler
# Returns the used Datacenter. If not set yet, uses the datacenter_path to
# lookup the datacenter.
# @return [VIM::Datacenter] The datacenter
# @return [RbVmomi::VIM::Datacenter] The datacenter
def datacenter
if !@datacenter
@datacenter = @root_folder.traverse(@datacenter_path, VIM::Datacenter)
@datacenter = @root_folder.traverse(@datacenter_path, RbVmomi::VIM::Datacenter)
if !@datacenter
fail "datacenter #{@datacenter_path} not found"
end
@@ -112,11 +112,11 @@ class AdmissionControlledResourceScheduler
# Returns the candidate datastores. If not set yet, uses the datastore_paths
# to lookup the datastores under the datacenter.
# As a side effect, also looks up properties about all the datastores
# @return [Array] List of VIM::Datastore
# @return [Array] List of RbVmomi::VIM::Datastore
def datastores
if !@datastores
@datastores = @datastore_paths.map do |path|
ds = datacenter.datastoreFolder.traverse(path, VIM::Datastore)
ds = datacenter.datastoreFolder.traverse(path, RbVmomi::VIM::Datastore)
if !ds
fail "datastore #{path} not found"
end
@@ -131,7 +131,7 @@ class AdmissionControlledResourceScheduler
# Returns the candidate computers (aka clusters). If not set yet, uses the
# computer_names to look them up.
# @return [Array] List of [VIM::ClusterComputeResource, Hash] tuples, where
# @return [Array] List of [RbVmomi::VIM::ClusterComputeResource, Hash] tuples, where
# the Hash is a list of stats about the computer
def computers
if !@computers
@@ -145,7 +145,7 @@ class AdmissionControlledResourceScheduler
# Returns the candidate pods. If not set, automatically computes the pods
# based on the list of computers (aka clusters) and datastores.
# @return [Array] List of pods, where a pod is a list of VIM::ClusterComputeResource
# @return [Array] List of pods, where a pod is a list of RbVmomi::VIM::ClusterComputeResource
def pods
if !@pods
# A pod is defined as a set of clusters (aka computers) that share the same
@@ -168,22 +168,22 @@ class AdmissionControlledResourceScheduler
# @return [Hash] Hash of VMs as keys and their properties as values.
def pod_vms pod
# This function retrieves all VMs residing inside a pod
filterSpec = VIM.PropertyFilterSpec(
filterSpec = RbVmomi::VIM.PropertyFilterSpec(
objectSet: pod.map do |computer, stats|
{
obj: computer.resourcePool,
selectSet: [
VIM.TraversalSpec(
RbVmomi::VIM.TraversalSpec(
name: 'tsFolder',
type: 'ResourcePool',
path: 'resourcePool',
skip: false,
selectSet: [
VIM.SelectionSpec(name: 'tsFolder'),
VIM.SelectionSpec(name: 'tsVM'),
RbVmomi::VIM.SelectionSpec(name: 'tsFolder'),
RbVmomi::VIM.SelectionSpec(name: 'tsVM'),
]
),
VIM.TraversalSpec(
RbVmomi::VIM.TraversalSpec(
name: 'tsVM',
type: 'ResourcePool',
path: 'vm',
@@ -202,11 +202,11 @@ class AdmissionControlledResourceScheduler
result = @vim.propertyCollector.RetrieveProperties(specSet: [filterSpec])
out = result.map { |x| [x.obj, Hash[x.propSet.map { |y| [y.name, y.val] }]] }
out.select{|obj, props| obj.is_a?(VIM::VirtualMachine)}
out.select{|obj, props| obj.is_a?(RbVmomi::VIM::VirtualMachine)}
end
# Returns all candidate datastores for a given pod.
# @return [Array] List of VIM::Datastore
# @return [Array] List of RbVmomi::VIM::Datastore
def pod_datastores pod
pod.first.datastore & self.datastores
end
@@ -214,7 +214,7 @@ class AdmissionControlledResourceScheduler
# Returns the list of pods that pass admission control. If not set yet, performs
# admission control to compute the list. If no pods passed the admission
# control, an exception is thrown.
# @return [Array] List of pods, where a pod is a list of VIM::ClusterComputeResource
# @return [Array] List of pods, where a pod is a list of RbVmomi::VIM::ClusterComputeResource
def filtered_pods
# This function applies admission control and returns those pods that have
# passed admission control. An exception is thrown if access was denied to
@@ -300,7 +300,7 @@ class AdmissionControlledResourceScheduler
# Returns the computer (aka cluster) to be used for placement. If not set yet,
# computs the least loaded cluster (using a metric that combines CPU and Memory
# load) that passes admission control.
# @return [VIM::ClusterComputeResource] Chosen computer (aka cluster)
# @return [RbVmomi::VIM::ClusterComputeResource] Chosen computer (aka cluster)
def pick_computer placementhint = nil
if !@computer
# Out of the pods to which we have been granted access, pick the cluster
@@ -330,7 +330,7 @@ class AdmissionControlledResourceScheduler
# Returns the datastore to be used for placement. If not set yet, picks a
# datastore without much intelligence, as long as it passes admission control.
# @return [VIM::Datastore] Chosen datastore
# @return [RbVmomi::VIM::Datastore] Chosen datastore
def datastore placementHint = nil
if @datastore
return @datastore
@@ -395,4 +395,4 @@ class AdmissionControlledResourceScheduler
datastore = self.datastore @placement_hint
log "Datastore: #{datastore.name}"
end
end
end
@@ -336,7 +336,7 @@ class PerfAggregator
vms_props, inventory = all_inventory_flat root_folder, prop_names
vms = vms_props.keys
hosts_props = inventory.select{|k, v| k.is_a?(VIM::HostSystem)}
hosts_props = inventory.select{|k, v| k.is_a?(RbVmomi::VIM::HostSystem)}
conn = root_folder._connection
sc = conn.serviceContent
+3
View File
@@ -0,0 +1,3 @@
module RbVmomi
VERSION = '1.9.3'.freeze
end
+25 -4
View File
@@ -1,6 +1,13 @@
# Copyright (c) 2011 VMware, Inc. All Rights Reserved.
require 'rbvmomi'
# Win32::SSPI is part of core on Windows
begin
require 'win32/sspi'
rescue LoadError
end
WIN32 = (defined? Win32::SSPI)
module RbVmomi
# A connection to one vSphere SDK endpoint.
@@ -22,7 +29,7 @@ class VIM < Connection
fail unless opts.is_a? Hash
fail "host option required" unless opts[:host]
opts[:cookie] ||= nil
opts[:user] ||= 'root'
opts[:user] ||= (WIN32 ? ENV['USERNAME'].dup : 'root')
opts[:password] ||= ''
opts[:ssl] = true unless opts.member? :ssl or opts[:"no-ssl"]
opts[:insecure] ||= false
@@ -30,16 +37,30 @@ class VIM < Connection
opts[:path] ||= '/sdk'
opts[:ns] ||= 'urn:vim25'
rev_given = opts[:rev] != nil
opts[:rev] = '4.0' unless rev_given
opts[:rev] = '6.0' unless rev_given
opts[:debug] = (!ENV['RBVMOMI_DEBUG'].empty? rescue false) unless opts.member? :debug
new(opts).tap do |vim|
unless opts[:cookie]
vim.serviceContent.sessionManager.Login :userName => opts[:user], :password => opts[:password]
if WIN32 && opts[:password] == ''
# Attempt login by SSPI if no password specified on Windows
negotiation = Win32::SSPI::NegotiateAuth.new opts[:user], ENV['USERDOMAIN'].dup
begin
vim.serviceContent.sessionManager.LoginBySSPI :base64Token => negotiation.get_initial_token
rescue RbVmomi::Fault => fault
if !fault.fault.is_a?(RbVmomi::VIM::SSPIChallenge)
raise
else
vim.serviceContent.sessionManager.LoginBySSPI :base64Token => negotiation.complete_authentication(fault.base64Token)
end
end
else
vim.serviceContent.sessionManager.Login :userName => opts[:user], :password => opts[:password]
end
end
unless rev_given
rev = vim.serviceContent.about.apiVersion
vim.rev = [rev, '5.5'].min
vim.rev = [rev, '6.0'].min
end
end
end
@@ -13,5 +13,10 @@ class RbVmomi::VIM::Datacenter
def find_vm path
vmFolder.traverse path, RbVmomi::VIM::VirtualMachine
end
# Traverse the given inventory +path+ to find a Folder.
def find_folder path
vmFolder.traverse path, RbVmomi::VIM::Folder
end
end
@@ -63,6 +63,7 @@ class RbVmomi::VIM::Datastore
end
def mkuripath path
"/folder/#{URI.escape path}?dcPath=#{URI.escape datacenter.name}&dsName=#{URI.escape name}"
datacenter_path_str = datacenter.path[1..-1].map{|elem| elem[1]}.join('/')
"/folder/#{URI.escape path}?dcPath=#{URI.escape datacenter_path_str }&dsName=#{URI.escape name}"
end
end
@@ -29,13 +29,18 @@ class RbVmomi::VIM::DynamicTypeMgrManagedTypeInfo
'version-id-ref' => param.version,
}
end,
'result' => {
'name' => result.name,
'type-id-ref' => result.type.gsub("[]", ""),
'is-array' => (result.type =~ /\[\]$/) ? true : false,
'is-optional' => result.annotation.find{|a| a.name == "optional"} ? true : false,
'version-id-ref' => result.version,
}
'result' => (
if result.nil? then
nil
else
{
'name' => result.name,
'type-id-ref' => result.type.gsub("[]", ""),
'is-array' => (result.type =~ /\[\]$/) ? true : false,
'is-optional' => result.annotation.find{|a| a.name == "optional"} ? true : false,
'version-id-ref' => result.version,
}
end)
}
]
end
@@ -16,6 +16,7 @@ class RbVmomi::VIM::OvfManager
# @option opts [String] :diskProvisioning (thin) Disk provisioning mode.
# @option opts [Hash] :networkMappings Network mappings.
# @option opts [Hash] :propertyMappings Property mappings.
# @option opts [String] :deploymentOption Deployment option key.
def deployOVF opts
opts = { :networkMappings => {},
:propertyMappings => {},
@@ -29,7 +30,7 @@ class RbVmomi::VIM::OvfManager
:hostSystem => opts[:host],
:locale => "US",
:entityName => opts[:vmName],
:deploymentOption => "",
:deploymentOption => opts[:deploymentOption] || "",
:networkMapping => opts[:networkMappings].map{|from, to| RbVmomi::VIM::OvfNetworkMapping(:name => from, :network => to)},
:propertyMapping => opts[:propertyMappings].to_a,
:diskProvisioning => opts[:diskProvisioning]
@@ -2,7 +2,7 @@ module RbVmomi
class VIM::ReflectManagedMethodExecuter
def fetch moid, prop
result = FetchSoap(:moid => moid, :version => 'urn:vim25/5.0', :prop => prop)
result = FetchSoap(:moid => moid, :version => 'urn:vim25/6.0', :prop => prop)
xml = Nokogiri(result.response)
_connection.deserializer.deserialize xml.root, nil
end
@@ -16,7 +16,7 @@ class VIM::ReflectManagedMethodExecuter
soap_arg.val = xml.target!
end
end
result = ExecuteSoap(:moid => moid, :version => 'urn:vim25/5.0',
result = ExecuteSoap(:moid => moid, :version => 'urn:vim25/6.0',
:method => method, :argument => soap_args)
if result
_connection.deserializer.deserialize Nokogiri(result.response).root, nil
Binary file not shown.
+47 -14
View File
@@ -2015,7 +2015,7 @@ private
########################################################################
# Returns the spec to reconfig a VM and add a NIC
########################################################################
def self.calculate_addnic_spec(vm, mac, bridge, model)
def self.calculate_addnic_spec(vm, mac, bridge, model, limit=nil, rsrv=nil)
model = model.nil? ? nil : model.downcase
network = vm.runtime.host.network.select{|n| n.name==bridge}
backing = nil
@@ -2065,17 +2065,33 @@ private
:port => port)
end
return {:operation => :add,
:device => nic_card.new(
:key => 0,
:deviceInfo => {
:label => "net" + card_num.to_s,
:summary => bridge
},
:backing => backing,
:addressType => mac ? 'manual' : 'generated',
:macAddress => mac
)
card_spec = {
:key => 0,
:deviceInfo => {
:label => "net" + card_num.to_s,
:summary => bridge
},
:backing => backing,
:addressType => mac ? 'manual' : 'generated',
:macAddress => mac
}
if (limit or rsrv) and (limit > 0)
ra_spec = Hash.new
rsrv = limit if rsrv > limit
ra_spec[:limit] = limit if limit
ra_spec[:reservation] = rsrv if rsrv
ra_spec[:share] = RbVmomi::VIM.SharesInfo({
:level => RbVmomi::VIM.SharesLevel("normal"),
:shares => 0
})
card_spec[:resourceAllocation] =
RbVmomi::VIM.VirtualEthernetCardResourceAllocation(ra_spec)
end
return {
:operation => :add,
:device => nic_card.new(card_spec)
}
end
@@ -2410,7 +2426,24 @@ private
mac = nic.elements["MAC"].text
bridge = nic.elements["BRIDGE"].text
model = nic.elements["MODEL"] ? nic.elements["MODEL"].text : nil
nic_array << calculate_addnic_spec(vm, mac, bridge, model)
limit_in = nic.elements["INBOUND_PEAK_BW"] ? nic.elements["INBOUND_PEAK_BW"].text : ""
limit_out = nic.elements["OUTBOUND_PEAK_BW"] ? nic.elements["OUTBOUND_PEAK_BW"].text : ""
limit = nil
if !limit_in.empty? or !limit_out.empty?
limit=([limit_in.to_i, limit_out.to_i].min / 1024) * 8
end
rsrv_in = nic.elements["INBOUND_AVG_BW"] ? nic.elements["INBOUND_AVG_BW"].text : ""
rsrv_out = nic.elements["OUTBOUND_AVG_BW"] ? nic.elements["OUTBOUND_AVG_BW"].text : ""
rsrv = nil
if !rsrv_in.empty? or !rsrv_out.empty?
rsrv=([rsrv_in.to_i, rsrv_out.to_i].min / 1024) * 8
end
nic_array << calculate_addnic_spec(vm,
mac,
bridge,
model,
limit,
rsrv)
}
device_change += nic_array
@@ -2421,7 +2454,7 @@ private
disks = xml.root.get_elements("/VM/TEMPLATE/DISK")
disk_spec = {}
# If the VM is not new, avoid readding DISKS
# If the VM is not new, avoid reading DISKS
if !newvm
vm.config.hardware.device.select { |d|
if is_disk?(d)