rbvmomi 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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