rbvmomi 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. data/.yardopts +5 -0
  2. data/README.rdoc +71 -0
  3. data/Rakefile +11 -0
  4. data/VERSION +1 -1
  5. data/devel/analyze-xml.rb +29 -29
  6. data/{test/test.rb → examples/create_vm.rb} +40 -15
  7. data/examples/extraConfig.rb +54 -0
  8. data/examples/power.rb +57 -0
  9. data/examples/readme-1.rb +35 -0
  10. data/examples/readme-2.rb +51 -0
  11. data/lib/rbvmomi.rb +3 -282
  12. data/lib/rbvmomi/{types.rb → basic_types.rb} +37 -97
  13. data/lib/rbvmomi/connection.rb +239 -0
  14. data/lib/rbvmomi/fault.rb +17 -0
  15. data/lib/{trivial_soap.rb → rbvmomi/trivial_soap.rb} +31 -13
  16. data/lib/rbvmomi/trollop.rb +6 -2
  17. data/lib/rbvmomi/type_loader.rb +72 -0
  18. data/lib/rbvmomi/vim.rb +76 -0
  19. data/lib/rbvmomi/vim/ComputeResource.rb +51 -0
  20. data/lib/rbvmomi/vim/Datacenter.rb +17 -0
  21. data/lib/rbvmomi/vim/Datastore.rb +68 -0
  22. data/lib/rbvmomi/vim/Folder.rb +112 -0
  23. data/lib/rbvmomi/vim/ManagedEntity.rb +46 -0
  24. data/lib/rbvmomi/vim/ManagedObject.rb +55 -0
  25. data/lib/rbvmomi/vim/ObjectContent.rb +23 -0
  26. data/lib/rbvmomi/vim/ObjectUpdate.rb +23 -0
  27. data/lib/rbvmomi/vim/OvfManager.rb +93 -0
  28. data/lib/rbvmomi/vim/ResourcePool.rb +18 -0
  29. data/lib/rbvmomi/vim/ServiceInstance.rb +53 -0
  30. data/lib/rbvmomi/vim/Task.rb +31 -0
  31. data/lib/rbvmomi/vim/VirtualMachine.rb +7 -0
  32. data/test/test_deserialization.rb +11 -55
  33. data/test/test_emit_request.rb +13 -10
  34. data/test/test_exceptions.rb +16 -0
  35. data/test/test_parse_response.rb +2 -10
  36. data/test/test_serialization.rb +14 -11
  37. metadata +41 -25
  38. data/.gitignore +0 -4
  39. data/README.md +0 -12
  40. data/lib/rbvmomi/extensions.rb +0 -491
  41. data/lib/rbvmomi/profile.rb +0 -22
  42. data/test/runner.rb +0 -3
@@ -0,0 +1,51 @@
1
+ class RbVmomi::VIM::ComputeResource
2
+ # Aggregate cluster information.
3
+ #
4
+ # @note Values are returned in a hash.
5
+ #
6
+ # @return [Mhz] totalCPU: Sum of the frequencies of each CPU in the cluster.
7
+ # @return [Mhz] usedCPU: CPU cycles used across the cluster.
8
+ # @return [MB] totalMem: Total RAM.
9
+ # @return [MB] usedMem: Used RAM.
10
+ def stats
11
+ filterSpec = RbVmomi::VIM.PropertyFilterSpec(
12
+ objectSet: [{
13
+ obj: self,
14
+ selectSet: [
15
+ RbVmomi::VIM.TraversalSpec(
16
+ name: 'tsHosts',
17
+ type: 'ComputeResource',
18
+ path: 'host',
19
+ skip: false,
20
+ )
21
+ ]
22
+ }],
23
+ propSet: [{
24
+ pathSet: %w(name overallStatus summary.hardware.cpuMhz
25
+ summary.hardware.numCpuCores summary.hardware.memorySize
26
+ summary.quickStats.overallCpuUsage
27
+ summary.quickStats.overallMemoryUsage),
28
+ type: 'HostSystem'
29
+ }]
30
+ )
31
+
32
+ result = @soap.propertyCollector.RetrieveProperties(specSet: [filterSpec])
33
+
34
+ stats = {
35
+ totalCPU: 0,
36
+ totalMem: 0,
37
+ usedCPU: 0,
38
+ usedMem: 0,
39
+ }
40
+
41
+ result.each do |x|
42
+ next if x['overallStatus'] == 'red'
43
+ stats[:totalCPU] += x['summary.hardware.cpuMhz'] * x['summary.hardware.numCpuCores']
44
+ stats[:totalMem] += x['summary.hardware.memorySize'] / (1024*1024)
45
+ stats[:usedCPU] += x['summary.quickStats.overallCpuUsage'] || 0
46
+ stats[:usedMem] += x['summary.quickStats.overallMemoryUsage'] || 0
47
+ end
48
+
49
+ stats
50
+ end
51
+ end
@@ -0,0 +1,17 @@
1
+ class RbVmomi::VIM::Datacenter
2
+ # Traverse the given inventory +path+ to find a ComputeResource.
3
+ def find_compute_resource path
4
+ hostFolder.traverse path, RbVmomi::VIM::ComputeResource
5
+ end
6
+
7
+ # Find the Datastore with the given +name+.
8
+ def find_datastore name
9
+ datastore.find { |x| x.name == name }
10
+ end
11
+
12
+ # Traverse the given inventory +path+ to find a VirtualMachine.
13
+ def find_vm path
14
+ vmFolder.traverse path, RbVmomi::VIM::VirtualMachine
15
+ end
16
+ end
17
+
@@ -0,0 +1,68 @@
1
+ # @note +download+ and +upload+ require +curl+. If +curl+ is not in your +PATH+
2
+ # then set the +CURL+ environment variable to point to it.
3
+ # @todo Use an HTTP library instead of executing +curl+.
4
+ class RbVmomi::VIM::Datastore
5
+ CURLBIN = ENV['CURL'] || "curl" #@private
6
+
7
+ # Check whether a file exists on this datastore.
8
+ # @param path [String] Path on the datastore.
9
+ def exists? path
10
+ req = Net::HTTP::Head.new mkuripath(path)
11
+ req.initialize_http_header 'cookie' => @soap.cookie
12
+ resp = @soap.http.request req
13
+ case resp
14
+ when Net::HTTPSuccess
15
+ true
16
+ when Net::HTTPNotFound
17
+ false
18
+ else
19
+ fail resp.inspect
20
+ end
21
+ end
22
+
23
+ # Download a file from this datastore.
24
+ # @param remote_path [String] Source path on the datastore.
25
+ # @param local_path [String] Destination path on the local machine.
26
+ # @return [void]
27
+ def download remote_path, local_path
28
+ url = "http#{@soap.http.use_ssl? ? 's' : ''}://#{@soap.http.address}:#{@soap.http.port}#{mkuripath(remote_path)}"
29
+ pid = spawn CURLBIN, "-k", '--noproxy', '*', '-f',
30
+ "-o", local_path,
31
+ "-b", @soap.cookie,
32
+ url,
33
+ out: '/dev/null'
34
+ Process.waitpid(pid, 0)
35
+ fail "download failed" unless $?.success?
36
+ end
37
+
38
+ # Upload a file to this datastore.
39
+ # @param remote_path [String] Destination path on the datastore.
40
+ # @param local_path [String] Source path on the local machine.
41
+ # @return [void]
42
+ def upload remote_path, local_path
43
+ url = "http#{@soap.http.use_ssl? ? 's' : ''}://#{@soap.http.address}:#{@soap.http.port}#{mkuripath(remote_path)}"
44
+ pid = spawn CURLBIN, "-k", '--noproxy', '*', '-f',
45
+ "-T", local_path,
46
+ "-b", @soap.cookie,
47
+ url,
48
+ out: '/dev/null'
49
+ Process.waitpid(pid, 0)
50
+ fail "upload failed" unless $?.success?
51
+ end
52
+
53
+ private
54
+
55
+ def datacenter
56
+ return @datacenter if @datacenter
57
+ x = parent
58
+ while not x.is_a? Datacenter
59
+ x = x.parent
60
+ end
61
+ fail unless x.is_a? Datacenter
62
+ @datacenter = x
63
+ end
64
+
65
+ def mkuripath path
66
+ "/folder/#{URI.escape path}?dcPath=#{URI.escape datacenter.name}&dsName=#{URI.escape name}"
67
+ end
68
+ end
@@ -0,0 +1,112 @@
1
+ class RbVmomi::VIM::Folder
2
+ # Retrieve a child entity
3
+ # @param name [String] Name of the child.
4
+ # @param type [Class] Return nil unless the found entity <tt>is_a? type</tt>.
5
+ # @return [VIM::ManagedEntity]
6
+ def find name, type=Object
7
+ x = @soap.searchIndex.FindChild(entity: self, name: name)
8
+ x if x.is_a? type
9
+ end
10
+
11
+ # Alias to <tt>traverse path, type, true</tt>
12
+ # @see #traverse
13
+ def traverse! path, type=Object
14
+ traverse path, type, true
15
+ end
16
+
17
+ # Retrieve a descendant of this Folder.
18
+ # @param path [String] Path delimited by '/'.
19
+ # @param type (see Folder#find)
20
+ # @param create [Boolean] If set, create folders that don't exist.
21
+ # @return (see Folder#find)
22
+ # @todo Move +create+ functionality into another method.
23
+ def traverse path, type=Object, create=false
24
+ es = path.split('/').reject(&:empty?)
25
+ return self if es.empty?
26
+ final = es.pop
27
+
28
+ p = es.inject(self) do |f,e|
29
+ f.find(e, Folder) || (create && f.CreateFolder(name: e)) || return
30
+ end
31
+
32
+ if x = p.find(final, type)
33
+ x
34
+ elsif create and type == Folder
35
+ p.CreateFolder(name: final)
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ # Alias to +childEntity+.
42
+ def children
43
+ childEntity
44
+ end
45
+
46
+ # Efficiently retrieve properties from descendants of this folder.
47
+ #
48
+ # @param propSpecs [Hash] Specification of which properties to retrieve from
49
+ # which entities. Keys may be symbols, strings, or
50
+ # classes identifying ManagedEntity subtypes to be
51
+ # included in the results. Values are an array of
52
+ # property paths (strings) or the symbol :all.
53
+ #
54
+ # @return [Hash] Tree of inventory items. Folders are hashes from child name
55
+ # to child result. Objects are hashes from property path to
56
+ # value.
57
+ #
58
+ # @todo Return ObjectContent instead of the leaf hash.
59
+ def inventory propSpecs={}
60
+ propSet = [{ type: 'Folder', pathSet: ['name', 'parent'] }]
61
+ propSpecs.each do |k,v|
62
+ case k
63
+ when RbVmomi::VIM::ManagedEntity
64
+ k = k.wsdl_name
65
+ when Symbol, String
66
+ k = k.to_s
67
+ else
68
+ fail "key must be a ManagedEntity"
69
+ end
70
+
71
+ h = { type: k }
72
+ if v == :all
73
+ h[:all] = true
74
+ elsif v.is_a? Array
75
+ h[:pathSet] = v + %w(parent)
76
+ else
77
+ fail "value must be an array of property paths or :all"
78
+ end
79
+ propSet << h
80
+ end
81
+
82
+ filterSpec = RbVmomi::VIM.PropertyFilterSpec(
83
+ objectSet: [
84
+ obj: self,
85
+ selectSet: [
86
+ RbVmomi::VIM.TraversalSpec(
87
+ name: 'tsFolder',
88
+ type: 'Folder',
89
+ path: 'childEntity',
90
+ skip: false,
91
+ selectSet: [
92
+ RbVmomi::VIM.SelectionSpec(name: 'tsFolder')
93
+ ]
94
+ )
95
+ ]
96
+ ],
97
+ propSet: propSet
98
+ )
99
+
100
+ result = @soap.propertyCollector.RetrieveProperties(specSet: [filterSpec])
101
+
102
+ tree = { self => {} }
103
+ result.each do |x|
104
+ obj = x.obj
105
+ next if obj == self
106
+ h = Hash[x.propSet.map { |y| [y.name, y.val] }]
107
+ tree[h['parent']][h['name']] = [obj, h]
108
+ tree[obj] = {} if obj.is_a? RbVmomi::VIM::Folder
109
+ end
110
+ tree
111
+ end
112
+ end
@@ -0,0 +1,46 @@
1
+ class RbVmomi::VIM::ManagedEntity
2
+ # Retrieve the ancestors of the entity.
3
+ # @return [Array] Ancestors of this entity, starting with the root.
4
+ def path
5
+ filterSpec = RbVmomi::VIM.PropertyFilterSpec(
6
+ objectSet: [{
7
+ obj: self,
8
+ selectSet: [
9
+ RbVmomi::VIM.TraversalSpec(
10
+ name: 'tsME',
11
+ type: 'ManagedEntity',
12
+ path: 'parent',
13
+ skip: false,
14
+ selectSet: [
15
+ RbVmomi::VIM.SelectionSpec(name: 'tsME')
16
+ ]
17
+ )
18
+ ]
19
+ }],
20
+ propSet: [{
21
+ pathSet: %w(name parent),
22
+ type: 'ManagedEntity'
23
+ }]
24
+ )
25
+
26
+ result = @soap.propertyCollector.RetrieveProperties(specSet: [filterSpec])
27
+
28
+ tree = {}
29
+ result.each { |x| tree[x.obj] = [x['parent'], x['name']] }
30
+ a = []
31
+ cur = self
32
+ while cur
33
+ parent, name = *tree[cur]
34
+ a << [cur, name]
35
+ cur = parent
36
+ end
37
+ a.reverse
38
+ end
39
+
40
+ # Return a string representation of +path+ suitable for display.
41
+ # @return [String]
42
+ # @see #path
43
+ def pretty_path
44
+ path[1..-1].map { |x| x[1] } * '/'
45
+ end
46
+ end
@@ -0,0 +1,55 @@
1
+ class RbVmomi::VIM::ManagedObject
2
+ # Wait for updates on an object until a condition becomes true.
3
+ #
4
+ # @param pathSet [Array] Property paths to wait for updates to.
5
+ # @yield Called when an update to a subscribed property occurs.
6
+ # @yieldreturn [Boolean] Whether to stop waiting.
7
+ #
8
+ # @todo Pass the current property values to the block.
9
+ def wait_until *pathSet, &b
10
+ all = pathSet.empty?
11
+ filter = @soap.propertyCollector.CreateFilter :spec => {
12
+ :propSet => [{ :type => self.class.wsdl_name, :all => all, :pathSet => pathSet }],
13
+ :objectSet => [{ :obj => self }],
14
+ }, :partialUpdates => false
15
+ ver = ''
16
+ loop do
17
+ result = @soap.propertyCollector.WaitForUpdates(version: ver)
18
+ ver = result.version
19
+ if x = b.call
20
+ return x
21
+ end
22
+ end
23
+ ensure
24
+ filter.DestroyPropertyFilter if filter
25
+ end
26
+
27
+ # Efficiently retrieve multiple properties from an object.
28
+ # @param pathSet [Array] Properties to return.
29
+ # @return [Hash] Hash from property paths to values.
30
+ def collect! *pathSet
31
+ spec = {
32
+ objectSet: [{ obj: self }],
33
+ propSet: [{
34
+ pathSet: pathSet,
35
+ type: self.class.wsdl_name
36
+ }]
37
+ }
38
+ @soap.propertyCollector.RetrieveProperties(specSet: [spec])[0].to_hash
39
+ end
40
+
41
+ # Efficiently retrieve multiple properties from an object.
42
+ # @param pathSet (see #collect!)
43
+ # @yield [*values] Property values in same order as +pathSet+.
44
+ # @return [Array] Property values in same order as +pathSet+, or the return
45
+ # value from the block if it is given.
46
+ def collect *pathSet
47
+ h = collect! *pathSet
48
+ a = pathSet.map { |k| h[k.to_s] }
49
+ if block_given?
50
+ yield a
51
+ else
52
+ a
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,23 @@
1
+ class RbVmomi::VIM::ObjectContent
2
+ # Represent this ObjectContent as a hash.
3
+ # @return [Hash] A hash from property paths to values.
4
+ def to_hash
5
+ @cached_hash ||= to_hash_uncached
6
+ end
7
+
8
+ # Alias for +to_hash[k]+.
9
+ def [](k)
10
+ to_hash[k]
11
+ end
12
+
13
+ private
14
+
15
+ def to_hash_uncached
16
+ h = {}
17
+ propSet.each do |x|
18
+ fail if h.member? x.name
19
+ h[x.name] = x.val
20
+ end
21
+ h
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ class RbVmomi::VIM::ObjectUpdate
2
+ # Represent this ObjectUpdate as a hash.
3
+ # @return [Hash] A hash from property paths to values.
4
+ def to_hash
5
+ @cached_hash ||= to_hash_uncached
6
+ end
7
+
8
+ # Alias for +to_hash[k]+.
9
+ def [](k)
10
+ to_hash[k]
11
+ end
12
+
13
+ private
14
+
15
+ def to_hash_uncached
16
+ h = {}
17
+ changeSet.each do |x|
18
+ fail if h.member? x.name
19
+ h[x.name] = x.val
20
+ end
21
+ h
22
+ end
23
+ end
@@ -0,0 +1,93 @@
1
+ # @note +deployOVF+ and requires +curl+. If +curl+ is not in your +PATH+
2
+ # then set the +CURL+ environment variable to point to it.
3
+ # @todo Use an HTTP library instead of executing +curl+.
4
+ class RbVmomi::VIM::OvfManager
5
+ CURLBIN = ENV['CURL'] || "curl" #@private
6
+
7
+ # Deploy an OVF.
8
+ #
9
+ # @param [Hash] opts The options hash.
10
+ # @option opts [String] :uri Location of the OVF.
11
+ # @option opts [String] :vmName Name of the new VM.
12
+ # @option opts [VIM::Folder] :vmFolder Folder to place the VM in.
13
+ # @option opts [VIM::HostSystem] :host Host to use.
14
+ # @option opts [VIM::ResourcePool] :resourcePool Resource pool to use.
15
+ # @option opts [VIM::Datastore] :datastore Datastore to use.
16
+ # @option opts [String] :diskProvisioning (thin) Disk provisioning mode.
17
+ # @option opts [Hash] :networkMappings Network mappings.
18
+ # @option opts [Hash] :propertyMappings Property mappings.
19
+ def deployOVF opts
20
+ opts = { networkMappings: {},
21
+ propertyMappings: {},
22
+ diskProvisioning: :thin }.merge opts
23
+
24
+ %w(uri vmName vmFolder host resourcePool datastore).each do |k|
25
+ fail "parameter #{k} required" unless opts[k.to_sym]
26
+ end
27
+
28
+ ovfImportSpec = RbVmomi::VIM::OvfCreateImportSpecParams(
29
+ hostSystem: opts[:host],
30
+ locale: "US",
31
+ entityName: opts[:vmName],
32
+ deploymentOption: "",
33
+ networkMapping: opts[:networkMappings].map{|from, to| RbVmomi::VIM::OvfNetworkMapping(name: from, network: to)},
34
+ propertyMapping: opts[:propertyMappings].map{|key, value| RbVmomi::VIM::KeyValue(key: key, value: value)},
35
+ diskProvisioning: opts[:diskProvisioning]
36
+ )
37
+
38
+ result = CreateImportSpec(
39
+ ovfDescriptor: open(opts[:uri]).read,
40
+ resourcePool: opts[:resourcePool],
41
+ datastore: opts[:datastore],
42
+ cisp: ovfImportSpec
43
+ )
44
+
45
+ raise result.error[0].localizedMessage if result.error && !result.error.empty?
46
+
47
+ if result.warning
48
+ result.warning.each{|x| puts "OVF Warning: #{x.localizedMessage.chomp}" }
49
+ end
50
+
51
+ nfcLease = opts[:resourcePool].ImportVApp(spec: result.importSpec,
52
+ folder: opts[:vmFolder],
53
+ host: opts[:host])
54
+
55
+ nfcLease.wait_until(:state) { nfcLease.state != "initializing" }
56
+ raise nfcLease.error if nfcLease.state == "error"
57
+
58
+ begin
59
+ nfcLease.HttpNfcLeaseProgress(percent: 5)
60
+ progress = 0.0
61
+ result.fileItem.each do |fileItem|
62
+ deviceUrl = nfcLease.info.deviceUrl.find{|x| x.importKey == fileItem.deviceId}
63
+ if !deviceUrl
64
+ raise "Couldn't find deviceURL for device '#{fileItem.deviceId}'"
65
+ end
66
+
67
+ # XXX handle file:// URIs
68
+ ovfFilename = opts[:uri].to_s
69
+ tmp = ovfFilename.split(/\//)
70
+ tmp.pop
71
+ tmp << fileItem.path
72
+ filename = tmp.join("/")
73
+
74
+ method = fileItem.create ? "PUT" : "POST"
75
+
76
+ href = deviceUrl.url.gsub("*", opts[:host].config.network.vnic[0].spec.ip.ipAddress)
77
+ downloadCmd = "#{CURLBIN} -L '#{filename}'"
78
+ uploadCmd = "#{CURLBIN} -X #{method} --insecure -T - -H 'Content-Type: application/x-vnd.vmware-streamVmdk' -H 'Content-Length: #{fileItem.size}' '#{href}'"
79
+ system("#{downloadCmd} | #{uploadCmd}")
80
+ progress += (95.0 / result.fileItem.length)
81
+ nfcLease.HttpNfcLeaseProgress(percent: progress.to_i)
82
+ end
83
+
84
+ nfcLease.HttpNfcLeaseProgress(percent: 100)
85
+ vm = nfcLease.info.entity
86
+ nfcLease.HttpNfcLeaseComplete
87
+ vm
88
+ end
89
+ rescue Exception
90
+ nfcLease.HttpNfcLeaseAbort
91
+ raise
92
+ end
93
+ end