From c67a3d5c85bcfcc948954b0ef92b84f44f1428cf Mon Sep 17 00:00:00 2001 From: Artem Starovoitov Date: Thu, 1 Oct 2020 19:01:10 +0300 Subject: [PATCH] Add rubocop --- .gitignore | 1 + .rubocop.yml | 272 +++++++ Gemfile | 3 +- README.md | 4 +- Rakefile | 11 +- bin/console | 7 +- devel/analyze-vim-declarations.rb | 229 +++--- devel/analyze-xml.rb | 32 +- devel/benchmark.rb | 35 +- devel/collisions.rb | 12 +- devel/merge-internal-vmodl.rb | 22 +- devel/merge-manual-vmodl.rb | 23 +- devel/verify-vim-wsdl.rb | 17 +- examples/annotate.rb | 57 +- examples/cached_ovf_deploy.rb | 106 ++- examples/clone_vm.rb | 71 +- examples/create_vm-1.9.rb | 69 +- examples/create_vm.rb | 113 +-- examples/customAttributes.rb | 65 +- examples/delete_disk_from_vm.rb | 55 +- examples/extraConfig.rb | 59 +- examples/lease_tool.rb | 95 ++- examples/logbundle.rb | 51 +- examples/logtail.rb | 45 +- examples/nfs_datastore.rb | 77 +- examples/power.rb | 59 +- examples/readme-1.rb | 43 +- examples/readme-2.rb | 63 +- examples/screenshot.rb | 53 +- examples/vdf.rb | 80 +-- examples/vm_drs_behavior.rb | 84 ++- exe/rbvmomish | 124 ++-- lib/rbvmomi.rb | 9 +- lib/rbvmomi/basic_types.rb | 659 ++++++++--------- lib/rbvmomi/connection.rb | 451 ++++++------ lib/rbvmomi/deserialization.rb | 414 ++++++----- lib/rbvmomi/fault.rb | 21 +- lib/rbvmomi/optimist.rb | 114 +-- lib/rbvmomi/pbm.rb | 110 +-- lib/rbvmomi/sms.rb | 105 +-- lib/rbvmomi/sms/SmsStorageManager.rb | 13 +- lib/rbvmomi/sso.rb | 141 ++-- lib/rbvmomi/trivial_soap.rb | 203 +++--- lib/rbvmomi/type_loader.rb | 207 +++--- lib/rbvmomi/utils/admission_control.rb | 308 ++++---- lib/rbvmomi/utils/deploy.rb | 213 +++--- lib/rbvmomi/utils/leases.rb | 94 ++- lib/rbvmomi/utils/perfdump.rb | 661 +++++++++--------- lib/rbvmomi/version.rb | 3 +- lib/rbvmomi/vim.rb | 232 +++--- lib/rbvmomi/vim/ComputeResource.rb | 96 +-- lib/rbvmomi/vim/Datacenter.rb | 38 +- lib/rbvmomi/vim/Datastore.rb | 121 ++-- lib/rbvmomi/vim/DynamicTypeMgrAllTypeInfo.rb | 139 ++-- lib/rbvmomi/vim/DynamicTypeMgrDataTypeInfo.rb | 39 +- .../vim/DynamicTypeMgrManagedTypeInfo.rb | 96 +-- lib/rbvmomi/vim/Folder.rb | 396 +++++------ lib/rbvmomi/vim/HostSystem.rb | 287 ++++---- lib/rbvmomi/vim/ManagedEntity.rb | 101 +-- lib/rbvmomi/vim/ManagedObject.rb | 112 +-- lib/rbvmomi/vim/ObjectContent.rb | 40 +- lib/rbvmomi/vim/ObjectUpdate.rb | 40 +- lib/rbvmomi/vim/OvfManager.rb | 365 +++++----- lib/rbvmomi/vim/PerfCounterInfo.rb | 18 +- lib/rbvmomi/vim/PerformanceManager.rb | 175 +++-- lib/rbvmomi/vim/PropertyCollector.rb | 47 +- .../vim/ReflectManagedMethodExecuter.rb | 44 +- lib/rbvmomi/vim/ResourcePool.rb | 101 +-- lib/rbvmomi/vim/ServiceInstance.rb | 99 +-- lib/rbvmomi/vim/Task.rb | 125 ++-- lib/rbvmomi/vim/VirtualMachine.rb | 131 ++-- rbvmomi.gemspec | 56 +- test/test_deserialization.rb | 487 ++++++------- test/test_emit_request.rb | 108 +-- test/test_exceptions.rb | 15 +- test/test_helper.rb | 9 +- test/test_misc.rb | 45 +- test/test_parse_response.rb | 66 +- test/test_serialization.rb | 412 +++++------ 79 files changed, 5090 insertions(+), 4713 deletions(-) create mode 100644 .rubocop.yml mode change 100644 => 100755 devel/analyze-vim-declarations.rb mode change 100644 => 100755 devel/benchmark.rb mode change 100644 => 100755 devel/collisions.rb mode change 100644 => 100755 devel/merge-internal-vmodl.rb mode change 100644 => 100755 devel/merge-manual-vmodl.rb mode change 100644 => 100755 examples/cached_ovf_deploy.rb mode change 100644 => 100755 examples/clone_vm.rb mode change 100644 => 100755 examples/create_vm-1.9.rb mode change 100644 => 100755 examples/create_vm.rb mode change 100644 => 100755 examples/delete_disk_from_vm.rb mode change 100644 => 100755 examples/lease_tool.rb mode change 100644 => 100755 examples/nfs_datastore.rb mode change 100644 => 100755 examples/vm_drs_behavior.rb diff --git a/.gitignore b/.gitignore index ad50f828..bd0a2211 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ Gemfile.lock /vendor/bundle/ /vmodl/ *.gem +.idea diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 00000000..bd990615 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,272 @@ +AllCops: + UseCache: true + DisabledByDefault: false + Exclude: + - 'exe/*' + +################## STYLE ################################# + +Style/NumericPredicate: + Enabled: false + +Style/TernaryParentheses: + Enabled: false + +Naming/AccessorMethodName: + Description: Check the naming of accessor methods for get_/set_. + Enabled: false + +Layout/ArrayAlignment: + Description: >- + Align the elements of an array literal if they span more than + one line. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays' + Enabled: false + +Layout/HashAlignment: + Description: >- + Align the elements of a hash literal if they span more than + one line. + Enabled: false + +Layout/ParameterAlignment: + Description: >- + Align the parameters of a method call if they span more + than one line. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent' + Enabled: false + +Style/CaseEquality: + Description: 'Avoid explicit use of the case equality operator(===).' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality' + Enabled: false + +Style/Documentation: + Description: 'Document classes and non-namespace modules.' + Enabled: false + +Layout/EmptyLines: + Description: "Don't use several empty lines in a row." + Enabled: false + +Layout/ExtraSpacing: + Description: 'Do not use unnecessary spacing.' + Enabled: false + +Naming/MethodName: + Enabled: false + +Naming/VariableName: + Enabled: false + +Naming/BlockParameterName: + Enabled: false + +Style/MultilineTernaryOperator: + Description: >- + Avoid multi-line ?: (the ternary operator); + use if/unless instead. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary' + Enabled: false + +Style/SingleLineBlockParams: + Description: 'Enforces the names of some block params.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks' + Enabled: false + +Style/StringLiterals: + EnforcedStyle: double_quotes + +Layout/EmptyLineAfterMagicComment: + Enabled: false + +Style/PercentLiteralDelimiters: + PreferredDelimiters: + 'default': '()' + '%i': '()' + '%w': '()' + +Naming/MemoizedInstanceVariableName: + Enabled: false + +Style/InverseMethods: + Enabled: false + +Layout/MultilineMethodCallIndentation: + Enabled: false + +Naming/MethodParameterName: + Enabled: false + +Style/FormatStringToken: + Enabled: false + +Style/SymbolArray: + Enabled: false + +Style/FormatString: + Enabled: false + +Style/DateTime: + Enabled: false + +Style/AccessModifierDeclarations: + Enabled: false + +Style/ExpandPathArguments: + Enabled: false + +Naming/PredicateName: + Enabled: true + +Style/MixinUsage: + Enabled: true + Exclude: + - 'bin/*' + +Style/MultilineBlockChain: + Enabled: false + +Style/MissingRespondToMissing: + Enabled: false + +Style/ExponentialNotation: + Enabled: true + +Style/HashEachMethods: + Enabled: true + +Style/HashTransformKeys: + Enabled: true + +Style/HashTransformValues: + Enabled: true + +Style/IfUnlessModifier: + Enabled: true + +Style/GuardClause: + Enabled: false + +Style/SafeNavigation: + Enabled: false + +Style/GlobalVars: + Enabled: false + +Style/IfInsideElse: + Enabled: false + +Style/MultipleComparison: + Enabled: false + +################## METRICS ################################# + +Metrics/AbcSize: + Enabled: false + +Metrics/BlockNesting: + Enabled: true + Max: 4 + +Metrics/ClassLength: + Enabled: false + +Metrics/ModuleLength: + Enabled: true + Max: 150 + +Metrics/BlockLength: + Enabled: false + +Metrics/ParameterLists: + Enabled: false + +Metrics/MethodLength: + Enabled: false + +Metrics/CyclomaticComplexity: + Enabled: false + +Metrics/PerceivedComplexity: + Enabled: false + +################## LAYOUT ################################# + +Layout/LineLength: + Enabled: false + +Layout/SpaceAroundMethodCallOperator: + Enabled: true + +################## LINT ################################# + +Lint/AmbiguousBlockAssociation: + Enabled: false + +Lint/Void: + Description: 'Possible use of operator/literal/variable in void context.' + Enabled: true + +Lint/SendWithMixinArgument: + Enabled: true + +Lint/Debugger: + Enabled: true + +Lint/RaiseException: + Enabled: true + +Lint/StructNewOverride: + Enabled: true + +Lint/ShadowingOuterLocalVariable: + Enabled: false + +Lint/LiteralAsCondition: + Enabled: false + +Lint/UriEscapeUnescape: + Enabled: false + +Lint/RescueException: + Enabled: false + +Lint/DuplicateMethods: + Enabled: false + +Lint/Void: + Enabled: false + +Lint/UselessAssignment: + Enabled: false + +Lint/AssignmentInCondition: + Enabled: false + +Lint/NestedMethodDefinition: + Enabled: false + +Naming/HeredocDelimiterNaming: + Enabled: false + +Lint/NonLocalExitFromIterator: + Enabled: false + +Naming/VariableNumber: + Enabled: false + +Naming/FileName: + Enabled: false + +Naming/BinaryOperatorParameterName: + Enabled: false + +Security/Open: + Enabled: false + +Security/MarshalLoad: + Enabled: false + +Security/JSONLoad: + Enabled: false diff --git a/Gemfile b/Gemfile index e9fece90..5309e187 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,7 @@ +# frozen_string_literal: true # Copyright (c) 2016-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -source 'https://rubygems.org' +source "https://rubygems.org" gemspec diff --git a/README.md b/README.md index da3d367f..65e137e0 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ A simple example of turning on a VM: require 'rbvmomi' vim = RbVmomi::VIM.connect(host: 'foo', user: 'bar', password: 'baz') -dc = vim.serviceInstance.find_datacenter('my_datacenter') || fail('datacenter not found') +dc = vim.service_instance.find_datacenter('my_datacenter') || fail('datacenter not found') vm = dc.find_vm('my_vm') || fail('VM not found') vm.PowerOnVM_Task.wait_for_completion ``` @@ -40,7 +40,7 @@ to users of the Java SDK: require 'rbvmomi' vim = RbVmomi::VIM.connect(host: 'foo', user: 'bar', password: 'baz') -root_folder = vim.serviceInstance.content.rootFolder +root_folder = vim.service_instance.content.rootFolder dc = root_folder.childEntity.grep(RbVmomi::VIM::Datacenter).find { |x| x.name == 'mydatacenter' } || fail('datacenter not found') vm = dc.vmFolder.childEntity.grep(RbVmomi::VIM::VirtualMachine).find { |x| x.name == 'my_vm' } || fail('VM not found') task = vm.PowerOnVM_Task diff --git a/Rakefile b/Rakefile index 67e61923..8bb5de52 100644 --- a/Rakefile +++ b/Rakefile @@ -1,15 +1,16 @@ +# frozen_string_literal: true # Copyright (c) 2010-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'bundler/gem_tasks' -require 'rake/testtask' -require 'yard' +require "bundler/gem_tasks" +require "rake/testtask" +require "yard" -task(:default => :test) +task(default: :test) Rake::TestTask.new do |t| t.libs << "test" - t.test_files = FileList['test/test_*.rb'] + t.test_files = FileList["test/test_*.rb"] t.verbose = true t.warning = true end diff --git a/bin/console b/bin/console index dfdc18e1..d093f586 100755 --- a/bin/console +++ b/bin/console @@ -1,7 +1,8 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -require 'bundler/setup' -require 'rbvmomi' +require "bundler/setup" +require "rbvmomi" -require 'pry' +require "pry" Pry.start diff --git a/devel/analyze-vim-declarations.rb b/devel/analyze-vim-declarations.rb old mode 100644 new mode 100755 index 66bb7be1..cd1cda78 --- a/devel/analyze-vim-declarations.rb +++ b/devel/analyze-vim-declarations.rb @@ -1,10 +1,11 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2010-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'nokogiri' -require 'pp' +require "nokogiri" +require "pp" # :usage => analyze-vim-declarations.rb vim-declarations.xml foo-declarations.xml vmodl.db XML_FNS = ARGV[0...-1] @@ -12,203 +13,203 @@ OUT_FN = ARGV[-1] or abort "must specify path to output database" XML_FNS.each do |x| - abort "XML file #{x} does not exist" unless File.exists? x + abort "XML file #{x} does not exist" unless File.exist? x end -TYPES = {} -VERSIONS = [] +TYPES = {}.freeze +VERSIONS = [].freeze -ID2NAME = Hash.new { |h,k| fail "unknown type-id #{k.inspect}" } +ID2NAME = Hash.new { |_h, k| raise "unknown type-id #{k.inspect}" } ID2NAME.merge!({ - 'java.lang.String' => 'xsd:string', - 'BOOLEAN' => 'xsd:boolean', - 'BYTE' => 'xsd:byte', - 'SHORT' => 'xsd:short', - 'INT' => 'xsd:int', - 'LONG' => 'xsd:long', - 'FLOAT' => 'xsd:float', - 'DOUBLE' => 'xsd:double', - 'vmodl.DateTime' => 'xsd:dateTime', - 'vmodl.Binary' => 'xsd:base64Binary', - 'vmodl.Any' => 'xsd:anyType', - 'vmodl.URI' => 'xsd:anyURI', - 'void' => nil, -}) + "java.lang.String" => "xsd:string", + "BOOLEAN" => "xsd:boolean", + "BYTE" => "xsd:byte", + "SHORT" => "xsd:short", + "INT" => "xsd:int", + "LONG" => "xsd:long", + "FLOAT" => "xsd:float", + "DOUBLE" => "xsd:double", + "vmodl.DateTime" => "xsd:dateTime", + "vmodl.Binary" => "xsd:base64Binary", + "vmodl.Any" => "xsd:anyType", + "vmodl.URI" => "xsd:anyURI", + "void" => nil + }) %w(DataObject ManagedObject MethodFault MethodName PropertyPath RuntimeFault TypeName).each do |x| - ID2NAME['vmodl.' + x] = x + ID2NAME["vmodl." + x] = x end -def handle_data_object node - if TYPES[node['name']] +def handle_data_object(node) + if TYPES[node["name"]] puts "Type #{node['name']} already exists" return end - ID2NAME[node['type-id']] = node['name'] - TYPES[node['name']] = { - 'kind' => 'data', - 'base-type-id' => node['base-type-id'], - 'props' => node.children.select { |x| x.name == 'property' }.map do |property| + ID2NAME[node["type-id"]] = node["name"] + TYPES[node["name"]] = { + "kind" => "data", + "base-type-id" => node["base-type-id"], + "props" => node.children.select { |x| x.name == "property" }.map do |property| { - 'name' => property['name'], - 'type-id-ref' => property['type-id-ref'], - 'is-optional' => property['is-optional'] ? true : false, - 'is-array' => property['is-array'] ? true : false, - 'version-id-ref' => property['version-id-ref'], + "name" => property["name"], + "type-id-ref" => property["type-id-ref"], + "is-optional" => property["is-optional"] ? true : false, + "is-array" => property["is-array"] ? true : false, + "version-id-ref" => property["version-id-ref"] } end } end -def handle_managed_object node - if TYPES[node['name']] +def handle_managed_object(node) + if TYPES[node["name"]] puts "Type #{node['name']} already exists" return end - ID2NAME[node['type-id']] = node['name'] - TYPES[node['name']] = { - 'kind' => 'managed', - 'base-type-id' => node['base-type-id'], - 'props' => node.children.select { |x| x.name == 'property' }.map do |property| + ID2NAME[node["type-id"]] = node["name"] + TYPES[node["name"]] = { + "kind" => "managed", + "base-type-id" => node["base-type-id"], + "props" => node.children.select { |x| x.name == "property" }.map do |property| { - 'name' => property['name'], - 'type-id-ref' => property['type-id-ref'], - 'is-optional' => property['is-optional'] ? true : false, - 'is-array' => property['is-array'] ? true : false, - 'version-id-ref' => property['version-id-ref'], + "name" => property["name"], + "type-id-ref" => property["type-id-ref"], + "is-optional" => property["is-optional"] ? true : false, + "is-array" => property["is-array"] ? true : false, + "version-id-ref" => property["version-id-ref"] } end, - 'methods' => Hash[ - node.children.select { |x| x.name == 'method' }.map do |method| - [method['is-task'] ? "#{method['name']}_Task" : method['name'], + "methods" => Hash[ + node.children.select { |x| x.name == "method" }.map do |method| + [method["is-task"] ? "#{method['name']}_Task" : method["name"], { - 'params' => method.children.select { |x| x.name == 'parameter' }.map do |param| + "params" => method.children.select { |x| x.name == "parameter" }.map do |param| { - 'name' => param['name'], - 'type-id-ref' => param['type-id-ref'], - 'is-array' => param['is-array'] ? true : false, - 'is-optional' => param['is-optional'] ? true : false, - 'version-id-ref' => param['version-id-ref'], + "name" => param["name"], + "type-id-ref" => param["type-id-ref"], + "is-array" => param["is-array"] ? true : false, + "is-optional" => param["is-optional"] ? true : false, + "version-id-ref" => param["version-id-ref"] } end, - 'result' => { - 'type-id-ref' => method['type-id-ref'], - 'is-array' => method['is-array'] ? true : false, - 'is-optional' => method['is-optional'] ? true : false, - 'is-task' => method['is-task'] ? true : false, - 'version-id-ref' => method['version-id-ref'], + "result" => { + "type-id-ref" => method["type-id-ref"], + "is-array" => method["is-array"] ? true : false, + "is-optional" => method["is-optional"] ? true : false, + "is-task" => method["is-task"] ? true : false, + "version-id-ref" => method["version-id-ref"] } - } - ] + }] end ] } end -def handle_enum node - if TYPES[node['name']] +def handle_enum(node) + if TYPES[node["name"]] puts "Type #{node['name']} already exists" return end - ID2NAME[node['type-id']] = node['name'] - TYPES[node['name']] = { - 'kind' => 'enum', - 'values' => node.children.map { |child| child['name'] }, + ID2NAME[node["type-id"]] = node["name"] + TYPES[node["name"]] = { + "kind" => "enum", + "values" => node.children.map { |child| child["name"] } } end -def handle_fault node +def handle_fault(node) handle_data_object node end -def handle_version x +def handle_version(x) attrs = %w(display-name name service-namespace type-id version-id vmodl-name) h = Hash[attrs.map { |k| [k, x[k]] }] - h['compatible'] = x.children.select(&:element?).map { |y| y.text } + h["compatible"] = x.children.select(&:element?).map(&:text) VERSIONS << h end XML_FNS.each do |fn| puts "parsing #{fn} ..." xml_str = File.read(fn) - xml_str = xml_str.gsub(/\(.*?)\<\/description-html\>/m, "") + xml_str = xml_str.gsub(%r{\(.*?)\}m, "") xml = Nokogiri.parse(xml_str, nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS) - xml.root.at('enums').children.each { |x| handle_enum x } - xml.root.at('managed-objects').children.each { |x| handle_managed_object x } - xml.root.at('data-objects').children.each { |x| handle_data_object x } - xml.root.at('faults').children.each { |x| handle_fault x } - #xml.root.at('definitions').at('version-types').children.each { |x| handle_version x } + xml.root.at("enums").children.each { |x| handle_enum x } + xml.root.at("managed-objects").children.each { |x| handle_managed_object x } + xml.root.at("data-objects").children.each { |x| handle_data_object x } + xml.root.at("faults").children.each { |x| handle_fault x } + # xml.root.at('definitions').at('version-types').children.each { |x| handle_version x } end -#pp ID2NAME +# pp ID2NAME -munge_fault = lambda { |x| true } +munge_fault = ->(_x) { true } -TYPES.each do |k,t| - case t['kind'] - when 'data' - t['wsdl_base'] = t['base-type-id'] ? ID2NAME[t['base-type-id']] : 'DataObject' - t.delete 'base-type-id' - t['props'].each do |x| - x['wsdl_type'] = ID2NAME[x['type-id-ref']] - x.delete 'type-id-ref' +TYPES.each do |_k, t| + case t["kind"] + when "data" + t["wsdl_base"] = t["base-type-id"] ? ID2NAME[t["base-type-id"]] : "DataObject" + t.delete "base-type-id" + t["props"].each do |x| + x["wsdl_type"] = ID2NAME[x["type-id-ref"]] + x.delete "type-id-ref" munge_fault[x] end - when 'managed' - t['wsdl_base'] = t['base-type-id'] ? ID2NAME[t['base-type-id']] : 'ManagedObject' - t.delete 'base-type-id' - t['props'].each do |x| - x['wsdl_type'] = ID2NAME[x['type-id-ref']] - x.delete 'type-id-ref' + when "managed" + t["wsdl_base"] = t["base-type-id"] ? ID2NAME[t["base-type-id"]] : "ManagedObject" + t.delete "base-type-id" + t["props"].each do |x| + x["wsdl_type"] = ID2NAME[x["type-id-ref"]] + x.delete "type-id-ref" munge_fault[x] end - t['methods'].each do |mName,x| - if y = x['result'] + t["methods"].each do |_mName, x| + if y = x["result"] begin - y['wsdl_type'] = ID2NAME[y['type-id-ref']] - rescue Exception => ex - pp ex + y["wsdl_type"] = ID2NAME[y["type-id-ref"]] + rescue Exception => e + pp e end - y.delete 'type-id-ref' + y.delete "type-id-ref" munge_fault[y] end - x['params'].each do |r| + x["params"].each do |r| begin - r['wsdl_type'] = ID2NAME[r['type-id-ref']] - rescue Exception => ex - pp ex + r["wsdl_type"] = ID2NAME[r["type-id-ref"]] + rescue Exception => e + pp e end - r.delete 'type-id-ref' + r.delete "type-id-ref" munge_fault[r] end end - when 'enum' - else fail + when "enum" + "" + else raise end end db = {} -TYPES.each do |k,t| +TYPES.each do |k, t| db[k] = t end -db['_typenames'] = TYPES.keys -db['_versions'] = VERSIONS +db["_typenames"] = TYPES.keys +db["_versions"] = VERSIONS -File.open(OUT_FN, 'w') { |io| Marshal.dump db, io } +File.open(OUT_FN, "w") { |io| Marshal.dump db, io } -if filename = ENV['VERSION_GRAPH'] - File.open(filename, 'w') do |io| +if filename = ENV["VERSION_GRAPH"] + File.open(filename, "w") do |io| io.puts "digraph versions\n{" VERSIONS.each do |h| io.puts "\"#{h['vmodl-name']}\" [label=\"#{h['vmodl-name']} (#{h['version-id']})\"]" - h['compatible'].each do |x| - x =~ /^interface / or fail x + h["compatible"].each do |x| + x =~ /^interface / or raise x io.puts "\"#{h['vmodl-name']}\" -> \"#{$'}\"" end end diff --git a/devel/analyze-xml.rb b/devel/analyze-xml.rb index 7a68dabb..f1ab7e61 100644 --- a/devel/analyze-xml.rb +++ b/devel/analyze-xml.rb @@ -1,7 +1,8 @@ +# frozen_string_literal: true # Copyright (c) 2010-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'nokogiri' +require "nokogiri" # removes line breaks and whitespace between xml nodes. def prepare_xml(xml) @@ -9,8 +10,8 @@ def prepare_xml(xml) xml = xml.gsub(/(>)\s*(<)/, '\1\2') end -def analyze_xml x, tree - subtree = (tree[x.name] ||= { :attributes => [], :min_occur => nil, :max_occur => nil }) +def analyze_xml(x, tree) + subtree = (tree[x.name] ||= { attributes: [], min_occur: nil, max_occur: nil }) attrs = x.attributes.keys.sort subtree[:attributes] << attrs unless subtree[:attributes].member? attrs @@ -20,30 +21,31 @@ def analyze_xml x, tree analyze_xml c, subtree end - subtree.select { |k,v| k.is_a? String }.each do |k,v| + subtree.select { |k, _v| k.is_a? String }.each do |k, v| v[:min_occur] = [v[:min_occur], child_occurs[k]].compact.min v[:max_occur] = [v[:max_occur], child_occurs[k]].compact.max end end -def print_tree tree, indent=0 - tree.select { |k,v| k.is_a? String }.sort.each do |k,v| +def print_tree(tree, indent = 0) + tree.select { |k, _v| k.is_a? String }.sort.each do |k, v| attrs = v[:attributes] || [] - min, max = v[:min_occur], v[:max_occur] - numsym = if min == 0 and max == 0 then fail - elsif min == 0 and max == 1 then '?' - elsif min == 0 then '*' - elsif min == 1 and max == 1 then '' - else '+' + min = v[:min_occur] + max = v[:max_occur] + numsym = if (min == 0) && (max == 0) then raise + elsif (min == 0) && (max == 1) then "?" + elsif min == 0 then "*" + elsif (min == 1) && (max == 1) then "" + else "+" end - puts "#{' '*indent}#{k}#{numsym}: #{attrs.sort.map { |a| "[#{a * ' '}]"} * ', '} {#{min},#{max}}" - print_tree v, (indent+1) + puts "#{' ' * indent}#{k}#{numsym}: #{attrs.sort.map { |a| "[#{a * ' '}]" } * ', '} {#{min},#{max}}" + print_tree v, (indent + 1) end end tree = {} ARGV.each do |fn| - nk = Nokogiri(prepare_xml(File.read fn)) + nk = Nokogiri(prepare_xml(File.read(fn))) analyze_xml nk.root, tree end print_tree tree diff --git a/devel/benchmark.rb b/devel/benchmark.rb old mode 100644 new mode 100755 index e2b3ad9e..a15dcfc6 --- a/devel/benchmark.rb +++ b/devel/benchmark.rb @@ -1,29 +1,30 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'tempfile' +require "tempfile" -if ENV['RBVMOMI_COVERAGE'] == '1' - require 'simplecov' +if ENV["RBVMOMI_COVERAGE"] == "1" + require "simplecov" SimpleCov.start end -require 'rbvmomi' -require 'rbvmomi/deserialization' -require 'benchmark' -require 'libxml' +require "rbvmomi" +require "rbvmomi/deserialization" +require "benchmark" +require "libxml" -NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance' +NS_XSI = "http://www.w3.org/2001/XMLSchema-instance" VIM = RbVmomi::VIM -$conn = VIM.new(:ns => 'urn:vim25', :rev => '4.0') +$conn = VIM.new(ns: "urn:vim25", rev: "4.0") raw = File.read(ARGV[0]) -def diff a, b - a_io = Tempfile.new 'rbvmomi-diff-a' - b_io = Tempfile.new 'rbvmomi-diff-b' +def diff(a, b) + a_io = Tempfile.new "rbvmomi-diff-a" + b_io = Tempfile.new "rbvmomi-diff-b" PP.pp a, a_io PP.pp b, b_io a_io.close @@ -37,11 +38,11 @@ def diff a, b deserializer = RbVmomi::OldDeserializer.new($conn) end_time = Time.now + 1 n = 0 - while n == 0 or end_time > Time.now + while (n == 0) || (end_time > Time.now) deserializer.deserialize Nokogiri::XML(raw).root n += 1 end - N = n*10 + N = n * 10 end puts "iterations: #{N}" @@ -82,19 +83,19 @@ def diff a, b puts "all results match" end -Benchmark.bmbm do|b| +Benchmark.bmbm do |b| GC.start b.report("nokogiri parsing") do N.times { Nokogiri::XML(raw) } end - + GC.start b.report("libxml parsing") do N.times do LibXML::XML::Parser.string(raw).parse end end - + GC.start b.report("old deserialization (nokogiri)") do deserializer = RbVmomi::OldDeserializer.new($conn) diff --git a/devel/collisions.rb b/devel/collisions.rb old mode 100644 new mode 100755 index 6a9adbd4..6aae10f5 --- a/devel/collisions.rb +++ b/devel/collisions.rb @@ -1,22 +1,24 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT # Find collisions between VMODL property names and Ruby methods -require 'rbvmomi' +require "rbvmomi" VIM = RbVmomi::VIM -conn = VIM.new(:ns => 'urn:vim25', :rev => '4.0') +conn = VIM.new(ns: "urn:vim25", rev: "4.0") VIM.loader.typenames.each do |name| klass = VIM.loader.get name - next unless klass.respond_to? :kind and [:managed, :data].member? klass.kind + next unless klass.respond_to?(:kind) && [:managed, :data].member?(klass.kind) + methods = klass.kind == :managed ? - RbVmomi::BasicTypes::ObjectWithMethods.instance_methods : + RbVmomi::BasicTypes::ObjectWithMethods.instance_methods : RbVmomi::BasicTypes::ObjectWithProperties.instance_methods klass.props_desc.each do |x| - name = x['name'] + name = x["name"] puts "collision: #{klass}##{name}" if methods.member? name.to_sym end end diff --git a/devel/merge-internal-vmodl.rb b/devel/merge-internal-vmodl.rb old mode 100644 new mode 100755 index 1f3f5b84..237d9682 --- a/devel/merge-internal-vmodl.rb +++ b/devel/merge-internal-vmodl.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT @@ -11,7 +12,7 @@ output_vmodl_filename = ARGV[2] or abort "output vmodl filename required" TYPES = %w( -DVSKeyedOpaqueData + DVSKeyedOpaqueData DVSOpaqueDataConfigSpec DVPortgroupSelection DVPortSelection @@ -37,27 +38,28 @@ ReflectManagedMethodExecuterSoapFault ReflectManagedMethodExecuterSoapResult SelectionSet -) +).freeze METHODS = %w( -DistributedVirtualSwitchManager.UpdateDvsOpaqueData_Task + DistributedVirtualSwitchManager.UpdateDvsOpaqueData_Task HostSystem.RetrieveDynamicTypeManager HostSystem.RetrieveManagedMethodExecuter -) +).freeze -public_vmodl = File.open(public_vmodl_filename, 'r') { |io| Marshal.load io } -internal_vmodl = File.open(internal_vmodl_filename, 'r') { |io| Marshal.load io } +public_vmodl = File.open(public_vmodl_filename, "r") { |io| Marshal.load io } +internal_vmodl = File.open(internal_vmodl_filename, "r") { |io| Marshal.load io } TYPES.each do |k| puts "Merging in #{k}" - fail "Couldn't find type #{k} in internal VMODL" unless internal_vmodl.member? k + raise "Couldn't find type #{k} in internal VMODL" unless internal_vmodl.member? k + public_vmodl[k] = internal_vmodl[k] end METHODS.each do |x| puts "Merging in #{x}" - type, method = x.split '.' - public_vmodl[type]['methods'][method] = internal_vmodl[type]['methods'][method] or fail + type, method = x.split "." + public_vmodl[type]["methods"][method] = internal_vmodl[type]["methods"][method] or raise end -File.open(output_vmodl_filename, 'w') { |io| Marshal.dump public_vmodl, io } +File.open(output_vmodl_filename, "w") { |io| Marshal.dump public_vmodl, io } diff --git a/devel/merge-manual-vmodl.rb b/devel/merge-manual-vmodl.rb old mode 100644 new mode 100755 index 04f02939..fe06dce1 --- a/devel/merge-manual-vmodl.rb +++ b/devel/merge-manual-vmodl.rb @@ -1,4 +1,5 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2013-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT @@ -9,28 +10,28 @@ internal_vmodl_filename = ARGV[1] or abort "internal vmodl filename required" output_vmodl_filename = ARGV[2] or abort "output vmodl filename required" -public_vmodl = File.open(public_vmodl_filename, 'r') { |io| Marshal.load io } -internal_vmodl = File.open(internal_vmodl_filename, 'r') { |io| Marshal.load io } +public_vmodl = File.open(public_vmodl_filename, "r") { |io| Marshal.load io } +internal_vmodl = File.open(internal_vmodl_filename, "r") { |io| Marshal.load io } db = {} tn = {} -public_vmodl.each do |k,v| - unless k == '_typenames' - db[k] = v +public_vmodl.each do |k, v| + if k == "_typenames" + tn["_typenames"] = v else - tn['_typenames'] = v + db[k] = v end end internal_vmodl.each do |k, v| - unless k == '_typenames' - db[k] = v unless db[k] + if k == "_typenames" + tn["_typenames"] = tn["_typenames"] + v else - tn['_typenames'] = tn['_typenames'] + v + db[k] = v unless db[k] end end -db['_typenames'] = tn +db["_typenames"] = tn -File.open(output_vmodl_filename, 'w') { |io| Marshal.dump db, io } +File.open(output_vmodl_filename, "w") { |io| Marshal.dump db, io } diff --git a/devel/verify-vim-wsdl.rb b/devel/verify-vim-wsdl.rb index abebb488..7a504773 100755 --- a/devel/verify-vim-wsdl.rb +++ b/devel/verify-vim-wsdl.rb @@ -1,7 +1,8 @@ #!/usr/bin/env ruby +# frozen_string_literal: true -require 'active_support/core_ext/enumerable' -require 'active_support/inflector' +require "active_support/core_ext/enumerable" +require "active_support/inflector" require "optimist" require "pathname" require "rbvmomi" @@ -14,18 +15,18 @@ def parse_args(args) verify-vim-wsdl.rb [path to wsdl] [path to vmodl.db] HELP - opt :fix, "Optionally fix the wsdl types in the vmodl.db", :type => :boolean, :default => false + opt :fix, "Optionally fix the wsdl types in the vmodl.db", type: :boolean, default: false end Optimist.die("You must provide a wsdl file and a vmodl file") if args.count < 2 wsdl_path = Pathname.new(args.shift).expand_path - Optimist.die("You must pass a path to a wsdl file") if !wsdl_path.exist? + Optimist.die("You must pass a path to a wsdl file") unless wsdl_path.exist? vmodl_path = Pathname.new(args.shift).expand_path - Optimist.die("You must pass a path to the vmodl.db file") if !vmodl_path.exist? + Optimist.die("You must pass a path to the vmodl.db file") unless vmodl_path.exist? - return wsdl_path, vmodl_path, opts + [wsdl_path, vmodl_path, opts] end def load_wsdl(path) @@ -48,7 +49,7 @@ def dump_vmodl(vmodl, path) # and RbVmomi uses ManagedObjects not ManagedObjectReferences as parameters def wsdl_constantize(type) type = type.split(":").last - type = "int" if %w[long short byte].include?(type) + type = "int" if %w(long short byte).include?(type) type = "float" if type == "double" type = "binary" if type == "base64Binary" type = "ManagedObject" if type == "ManagedObjectReference" @@ -90,7 +91,7 @@ def wsdl_constantize(type) # Example of a subclass is e.g. VirtualMachine.host is defined as a HostSystem # in the vmodl.db but it is a ManagedObjectReference in the wsdl. unless vmodl_klass <= wsdl_klass - puts "#{type_name}.#{vmodl_prop["name"]} #{wsdl_klass.wsdl_name} doesn't match #{vmodl_klass.wsdl_name}" + puts "#{type_name}.#{vmodl_prop['name']} #{wsdl_klass.wsdl_name} doesn't match #{vmodl_klass.wsdl_name}" vmodl_prop["wsdl_type"] = wsdl_klass.wsdl_name if options[:fix] end end diff --git a/examples/annotate.rb b/examples/annotate.rb index 222d32c3..308cf446 100644 --- a/examples/annotate.rb +++ b/examples/annotate.rb @@ -1,38 +1,39 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM -CMDS = %w(get set) +CMDS = %w(get set).freeze opts = Optimist.options do - banner <<-EOS -Annotate a VM. - -Usage: - annotate.rb [options] VM get - annotate.rb [options] VM set annotation - -Commands: #{CMDS * ' '} - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Annotate a VM. + + Usage: + annotate.rb [options] VM get + annotate.rb [options] VM set annotation + + Commands: #{CMDS * ' '} + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS stop_on CMDS @@ -45,13 +46,13 @@ vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" vm = dc.find_vm(vm_name) or abort "VM not found" case cmd -when 'get' +when "get" puts vm.config.annotation -when 'set' +when "set" value = ARGV[2] or Optimist.die("no annotation given") - vm.ReconfigVM_Task(:spec => VIM.VirtualMachineConfigSpec(:annotation => value)).wait_for_completion + vm.ReconfigVM_Task(spec: VIM.VirtualMachineConfigSpec(annotation: value)).wait_for_completion end diff --git a/examples/cached_ovf_deploy.rb b/examples/cached_ovf_deploy.rb old mode 100644 new mode 100755 index bb40569f..1c5d3c54 --- a/examples/cached_ovf_deploy.rb +++ b/examples/cached_ovf_deploy.rb @@ -1,48 +1,49 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2012-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' -require 'rbvmomi/utils/deploy' -require 'rbvmomi/utils/admission_control' -require 'yaml' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" +require "rbvmomi/utils/deploy" +require "rbvmomi/utils/admission_control" +require "yaml" VIM = RbVmomi::VIM opts = Optimist.options do - banner <<-EOS -Deploy an OVF to a cluster, using a cached template if available. - -Usage: - cached_ovf_deploy.rb [options] - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Deploy an OVF to a cluster, using a cached template if available. + + Usage: + cached_ovf_deploy.rb [options] + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt - rbvmomi_datastore_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt + rbvmomi_datastore_opt -Other options: + text <<~EOS + + Other options: EOS - opt :template_name, "Name to give to the (cached) template", :type => :string - opt :template_path, "Path where templates are stored", :default => 'templates', :type => :string - opt :computer_path, "Path to the cluster to deploy into", :type => :string - opt :network, "Name of the network to attach template to", :type => :string - opt :vm_folder_path, "Path to VM folder to deploy VM into", :type => :string - opt :lease, "Lease in days", :type => :int, :default => 3 + opt :template_name, "Name to give to the (cached) template", type: :string + opt :template_path, "Path where templates are stored", default: "templates", type: :string + opt :computer_path, "Path to the cluster to deploy into", type: :string + opt :network, "Name of the network to attach template to", type: :string + opt :vm_folder_path, "Path to VM folder to deploy VM into", type: :string + opt :lease, "Lease in days", type: :int, default: 3 end Optimist.die("must specify host") unless opts[:host] @@ -53,35 +54,33 @@ ovf_url = ARGV[1] or Optimist.die("No OVF URL given") vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" root_vm_folder = dc.vmFolder vm_folder = root_vm_folder -if opts[:vm_folder_path] - vm_folder = root_vm_folder.traverse(opts[:vm_folder_path], VIM::Folder) -end +vm_folder = root_vm_folder.traverse(opts[:vm_folder_path], VIM::Folder) if opts[:vm_folder_path] template_folder = root_vm_folder.traverse!(template_folder_path, VIM::Folder) scheduler = AdmissionControlledResourceScheduler.new( vim, - :datacenter => dc, - :computer_names => [opts[:computer_path]], - :vm_folder => vm_folder, - :rp_path => '/', - :datastore_paths => [opts[:datastore]], - :max_vms_per_pod => nil, # No limits - :min_ds_free => nil, # No limits + datacenter: dc, + computer_names: [opts[:computer_path]], + vm_folder: vm_folder, + rp_path: "/", + datastore_paths: [opts[:datastore]], + max_vms_per_pod: nil, # No limits + min_ds_free: nil # No limits ) scheduler.make_placement_decision datastore = scheduler.datastore computer = scheduler.pick_computer # XXX: Do this properly -if opts[:network] - network = computer.network.find{|x| x.name == opts[:network]} -else - network = computer.network[0] -end +network = if opts[:network] + computer.network.find { |x| x.name == opts[:network] } + else + computer.network[0] + end lease_tool = LeaseTool.new lease = opts[:lease] * 24 * 60 * 60 @@ -90,20 +89,20 @@ ) template = deployer.lookup_template template_name -if !template +unless template puts "#{Time.now}: Uploading/Preparing OVF template ..." - + template = deployer.upload_ovf_as_template( ovf_url, template_name, - :run_without_interruptions => true, - :config => lease_tool.set_lease_in_vm_config({}, lease) + run_without_interruptions: true, + config: lease_tool.set_lease_in_vm_config({}, lease) ) end puts "#{Time.now}: Cloning template ..." config = { - :numCPUs => opts[:cpus], - :memoryMB => opts[:memory], + numCPUs: opts[:cpus], + memoryMB: opts[:memory] } config = lease_tool.set_lease_in_vm_config(config, lease) vm = deployer.linked_clone template, vm_name, config @@ -114,11 +113,10 @@ puts "#{Time.now}: Waiting for VM to be up ..." ip = nil -while !(ip = vm.guest_ip) +until (ip = vm.guest_ip) sleep 5 end puts "#{Time.now}: VM got IP: #{ip}" puts "#{Time.now}: Done" - diff --git a/examples/clone_vm.rb b/examples/clone_vm.rb old mode 100644 new mode 100755 index f1a326e2..4f4afdcf --- a/examples/clone_vm.rb +++ b/examples/clone_vm.rb @@ -1,36 +1,37 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM opts = Optimist.options do - banner <<-EOS -Clone a VM. - -Usage: - clone_vm.rb [options] source_vm dest_vm - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Clone a VM. + + Usage: + clone_vm.rb [options] source_vm dest_vm + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS opt :linked_clone, "Use a linked clone instead of a full clone" @@ -42,7 +43,7 @@ vm_target = ARGV[1] vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" vm = dc.find_vm(vm_source) or abort "VM not found" if opts[:linked_clone] @@ -55,34 +56,34 @@ # Thus, this code first create a delta disk on top of the base disk for # the to-be-cloned VM, if delta disks aren't used already. disks = vm.config.hardware.device.grep(VIM::VirtualDisk) - disks.select { |x| x.backing.parent == nil }.each do |disk| + disks.select { |x| x.backing.parent.nil? }.each do |disk| spec = { - :deviceChange => [ + deviceChange: [ { - :operation => :remove, - :device => disk + operation: :remove, + device: disk }, { - :operation => :add, - :fileOperation => :create, - :device => disk.dup.tap { |x| + operation: :add, + fileOperation: :create, + device: disk.dup.tap do |x| x.backing = x.backing.dup x.backing.fileName = "[#{disk.backing.datastore.name}]" x.backing.parent = disk.backing - }, + end } ] } - vm.ReconfigVM_Task(:spec => spec).wait_for_completion + vm.ReconfigVM_Task(spec: spec).wait_for_completion end - relocateSpec = VIM.VirtualMachineRelocateSpec(:diskMoveType => :moveChildMostDiskBacking) + relocateSpec = VIM.VirtualMachineRelocateSpec(diskMoveType: :moveChildMostDiskBacking) else relocateSpec = VIM.VirtualMachineRelocateSpec end -spec = VIM.VirtualMachineCloneSpec(:location => relocateSpec, - :powerOn => false, - :template => false) +spec = VIM.VirtualMachineCloneSpec(location: relocateSpec, + powerOn: false, + template: false) -vm.CloneVM_Task(:folder => vm.parent, :name => vm_target, :spec => spec).wait_for_completion +vm.CloneVM_Task(folder: vm.parent, name: vm_target, spec: spec).wait_for_completion diff --git a/examples/create_vm-1.9.rb b/examples/create_vm-1.9.rb old mode 100644 new mode 100755 index c6e9f6bf..e2ea7f2a --- a/examples/create_vm-1.9.rb +++ b/examples/create_vm-1.9.rb @@ -1,36 +1,37 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM opts = Optimist.options do - banner <<-EOS -Create a VM. - -Usage: - create_vm-1.9.rb [options] - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Create a VM. + + Usage: + create_vm-1.9.rb [options] + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS end @@ -38,15 +39,15 @@ vm_name = ARGV[0] or abort "must specify VM name" vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" vmFolder = dc.vmFolder hosts = dc.hostFolder.children rp = hosts.first.resourcePool vm_cfg = { name: vm_name, - guestId: 'otherGuest', - files: { vmPathName: '[datastore1]' }, + guestId: "otherGuest", + files: { vmPathName: "[datastore1]" }, numCPUs: 1, memoryMB: 128, deviceChange: [ @@ -55,7 +56,7 @@ device: VIM.VirtualLsiLogicController( key: 1000, busNumber: 0, - sharedBus: :noSharing, + sharedBus: :noSharing ) }, { operation: :add, @@ -63,35 +64,35 @@ device: VIM.VirtualDisk( key: 0, backing: VIM.VirtualDiskFlatVer2BackingInfo( - fileName: '[datastore1]', + fileName: "[datastore1]", diskMode: :persistent, - thinProvisioned: true, + thinProvisioned: true ), controllerKey: 1000, unitNumber: 0, - capacityInKB: 4000000, + capacityInKB: 4_000_000 ) }, { operation: :add, device: VIM.VirtualE1000( key: 0, deviceInfo: { - label: 'Network Adapter 1', - summary: 'VM Network', + label: "Network Adapter 1", + summary: "VM Network" }, backing: VIM.VirtualEthernetCardNetworkBackingInfo( - deviceName: 'VM Network', + deviceName: "VM Network" ), - addressType: 'generated' + addressType: "generated" ) } ], extraConfig: [ { - key: 'bios.bootOrder', - value: 'ethernet0' + key: "bios.bootOrder", + value: "ethernet0" } ] } -vmFolder.CreateVM_Task(:config => vm_cfg, :pool => rp).wait_for_completion +vmFolder.CreateVM_Task(config: vm_cfg, pool: rp).wait_for_completion diff --git a/examples/create_vm.rb b/examples/create_vm.rb old mode 100644 new mode 100755 index ef9e4f1e..2049b216 --- a/examples/create_vm.rb +++ b/examples/create_vm.rb @@ -1,36 +1,37 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM opts = Optimist.options do - banner <<-EOS -Create a VM. - -Usage: - create_vm.rb [options] - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Create a VM. + + Usage: + create_vm.rb [options] + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS end @@ -38,60 +39,60 @@ vm_name = ARGV[0] or abort "must specify VM name" vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" vmFolder = dc.vmFolder hosts = dc.hostFolder.children rp = hosts.first.resourcePool vm_cfg = { - :name => vm_name, - :guestId => 'otherGuest', - :files => { :vmPathName => '[datastore1]' }, - :numCPUs => 1, - :memoryMB => 128, - :deviceChange => [ + name: vm_name, + guestId: "otherGuest", + files: { vmPathName: "[datastore1]" }, + numCPUs: 1, + memoryMB: 128, + deviceChange: [ { - :operation => :add, - :device => VIM.VirtualLsiLogicController( - :key => 1000, - :busNumber => 0, - :sharedBus => :noSharing + operation: :add, + device: VIM.VirtualLsiLogicController( + key: 1000, + busNumber: 0, + sharedBus: :noSharing ) }, { - :operation => :add, - :fileOperation => :create, - :device => VIM.VirtualDisk( - :key => 0, - :backing => VIM.VirtualDiskFlatVer2BackingInfo( - :fileName => '[datastore1]', - :diskMode => :persistent, - :thinProvisioned => true + operation: :add, + fileOperation: :create, + device: VIM.VirtualDisk( + key: 0, + backing: VIM.VirtualDiskFlatVer2BackingInfo( + fileName: "[datastore1]", + diskMode: :persistent, + thinProvisioned: true ), - :controllerKey => 1000, - :unitNumber => 0, - :capacityInKB => 4000000 + controllerKey: 1000, + unitNumber: 0, + capacityInKB: 4_000_000 ) }, { - :operation => :add, - :device => VIM.VirtualE1000( - :key => 0, - :deviceInfo => { - :label => 'Network Adapter 1', - :summary => 'VM Network' + operation: :add, + device: VIM.VirtualE1000( + key: 0, + deviceInfo: { + label: "Network Adapter 1", + summary: "VM Network" }, - :backing => VIM.VirtualEthernetCardNetworkBackingInfo( - :deviceName => 'VM Network' + backing: VIM.VirtualEthernetCardNetworkBackingInfo( + deviceName: "VM Network" ), - :addressType => 'generated' + addressType: "generated" ) } ], - :extraConfig => [ + extraConfig: [ { - :key => 'bios.bootOrder', - :value => 'ethernet0' + key: "bios.bootOrder", + value: "ethernet0" } ] } -vmFolder.CreateVM_Task(:config => vm_cfg, :pool => rp).wait_for_completion +vmFolder.CreateVM_Task(config: vm_cfg, pool: rp).wait_for_completion diff --git a/examples/customAttributes.rb b/examples/customAttributes.rb index 5ac5d7ec..43ec49c9 100644 --- a/examples/customAttributes.rb +++ b/examples/customAttributes.rb @@ -1,41 +1,42 @@ +# frozen_string_literal: true # Author: Raul Mahiques - Red Hat 2020 # Based on "annotate.rb" ( https://github.com/vmware/rbvmomi/blob/a5867550bef9535c17f7bedd947fe336151347af/examples/annotate.rb ) # License MIT ( https://mit-license.org/ ) # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' -require 'yaml' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" +require "yaml" VIM = RbVmomi::VIM -CMDS = %w(get set) +CMDS = %w(get set).freeze opts = Optimist.options do - banner <<-EOS -Set a custom value for a VM. - -Usage: - customAttributes.rb [options] get - customAttributes.rb [options] set <"Custom Attribute"> <"Custom Attribute value"> - -Commands: #{CMDS * ' '} - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Set a custom value for a VM. + + Usage: + customAttributes.rb [options] get + customAttributes.rb [options] set <"Custom Attribute"> <"Custom Attribute value"> + + Commands: #{CMDS * ' '} + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS stop_on CMDS @@ -47,22 +48,20 @@ Optimist.die("must specify host") unless opts[:host] vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" vm = dc.find_vm(vm_name) or abort "VM not found" case cmd -when 'get' +when "get" puts "Custom Attributes for \"#{vm_name}\"" vm.value.each do |val| - fname = 'unknown_field' + fname = "unknown_field" vm.availableField.each do |af| - if af.key == val.key - fname = af.name - end + fname = af.name if af.key == val.key end puts "\t#{fname}: \"#{val.value}\"" end -when 'set' +when "set" arrayCustomAttributes = [] customAttribute = ARGV[2] or Optimist.die("no Custom Attribute given") customAttributeValue = ARGV[3] or Optimist.die("no value for the Custom Attribute given") @@ -77,5 +76,5 @@ end end exists == 1 or abort "Field \"#{customAttribute}\" doesn't exists\nPlease use one of the following:\n\t#{arrayCustomAttributes.join("\n\t")}" - vm.setCustomValue({"key" => "#{customAttribute}", :value => "#{customAttributeValue}"}) + vm.setCustomValue({ "key" => customAttribute.to_s, :value => customAttributeValue.to_s }) end diff --git a/examples/delete_disk_from_vm.rb b/examples/delete_disk_from_vm.rb old mode 100644 new mode 100755 index 87b4ef03..64f39f2d --- a/examples/delete_disk_from_vm.rb +++ b/examples/delete_disk_from_vm.rb @@ -1,32 +1,33 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM opts = Optimist.options do - banner <<-EOS -Delete a disk from a VM. - -Usage: - delete_disk_from_vm.rb [options] vm_name disk_unit_number - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS - -VM location options: - EOS - - rbvmomi_datacenter_opt + banner <<~EOS + Delete a disk from a VM. + + Usage: + delete_disk_from_vm.rb [options] vm_name disk_unit_number + + VIM connection options: + EOS + + rbvmomi_connection_opts + + text <<~EOS + + VM location options: + EOS + + rbvmomi_datacenter_opt end Optimist.die("must specify host") unless opts[:host] @@ -35,23 +36,23 @@ disk_unit_number = ARGV[1].to_i vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" vm = dc.find_vm(vm_name) or abort "VM not found" disk = vm.config.hardware.device.detect do |device| - device.kind_of?(VIM::VirtualDisk) && device.unitNumber == disk_unit_number + device.is_a?(VIM::VirtualDisk) && device.unitNumber == disk_unit_number end raise "Disk #{disk_unit_number} not found" if disk.nil? spec = VIM::VirtualMachineConfigSpec( - :deviceChange => [ + deviceChange: [ VIM::VirtualDeviceConfigSpec( - :device => disk, - :fileOperation => VIM.VirtualDeviceConfigSpecFileOperation(:destroy), - :operation => VIM::VirtualDeviceConfigSpecOperation(:remove) + device: disk, + fileOperation: VIM.VirtualDeviceConfigSpecFileOperation(:destroy), + operation: VIM::VirtualDeviceConfigSpecOperation(:remove) ) ] ) -vm.ReconfigVM_Task(:spec => spec).wait_for_completion +vm.ReconfigVM_Task(spec: spec).wait_for_completion diff --git a/examples/extraConfig.rb b/examples/extraConfig.rb index e0d6775c..64e2376e 100644 --- a/examples/extraConfig.rb +++ b/examples/extraConfig.rb @@ -1,38 +1,39 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM -CMDS = %w(list set) +CMDS = %w(list set).freeze opts = Optimist.options do - banner <<-EOS -View and modify VM extraConfig options. - -Usage: - extraConfig.rb [options] VM list - extraConfig.rb [options] VM set key=value [key=value...] - -Commands: #{CMDS * ' '} - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + View and modify VM extraConfig options. + + Usage: + extraConfig.rb [options] VM list + extraConfig.rb [options] VM set key=value [key=value...] + + Commands: #{CMDS * ' '} + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS stop_on CMDS @@ -45,13 +46,13 @@ vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" vm = dc.find_vm(vm_name) or abort "VM not found" case cmd -when 'list' +when "list" vm.config.extraConfig.each { |x| puts "#{x.key}: #{x.value}" } -when 'set' - extraConfig = ARGV[2..-1].map { |x| x.split("=", 2) }.map { |k,v| { :key => k, :value => v } } - vm.ReconfigVM_Task(:spec => VIM.VirtualMachineConfigSpec(:extraConfig => extraConfig)).wait_for_completion +when "set" + extraConfig = ARGV[2..-1].map { |x| x.split("=", 2) }.map { |k, v| { key: k, value: v } } + vm.ReconfigVM_Task(spec: VIM.VirtualMachineConfigSpec(extraConfig: extraConfig)).wait_for_completion end diff --git a/examples/lease_tool.rb b/examples/lease_tool.rb old mode 100644 new mode 100755 index 89ff4bc5..80a3f610 --- a/examples/lease_tool.rb +++ b/examples/lease_tool.rb @@ -1,45 +1,46 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2012-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' -require 'rbvmomi/utils/leases' -require 'yaml' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" +require "rbvmomi/utils/leases" +require "yaml" VIM = RbVmomi::VIM -CMDS = ['set_lease_on_leaseless_vms', 'show_expired_vms', - 'show_soon_expired_vms', 'kill_expired_vms'] +CMDS = %w(set_lease_on_leaseless_vms show_expired_vms + show_soon_expired_vms kill_expired_vms).freeze opts = Optimist.options do - banner <<-EOS -Tool for managing leases on VMs where leases are stored in YAML on VM annotations. - -Usage: - lease_tool.rb [options] - -Commands: #{CMDS * ' '} - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Tool for managing leases on VMs where leases are stored in YAML on VM annotations. + + Usage: + lease_tool.rb [options] + + Commands: #{CMDS * ' '} + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS - opt :vm_folder_path, "Path to VM folder to deploy VM into", :type => :string + opt :vm_folder_path, "Path to VM folder to deploy VM into", type: :string opt :force, "Really perform VMs. Used with kill_expired_vms" stop_on CMDS @@ -50,29 +51,29 @@ Optimist.die("no vm folder path given") unless opts[:vm_folder_path] vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" root_vm_folder = dc.vmFolder vm_folder = root_vm_folder.traverse(opts[:vm_folder_path], VIM::Folder) lease_tool = LeaseTool.new -vms_props_list = (['runtime.powerState'] + lease_tool.vms_props_list).uniq -inventory = vm_folder.inventory_flat('VirtualMachine' => vms_props_list) -inventory = inventory.select{|obj, props| obj.is_a?(VIM::VirtualMachine)} +vms_props_list = (["runtime.powerState"] + lease_tool.vms_props_list).uniq +inventory = vm_folder.inventory_flat("VirtualMachine" => vms_props_list) +inventory = inventory.select { |obj, _props| obj.is_a?(VIM::VirtualMachine) } case cmd -when 'set_lease_on_leaseless_vms' +when "set_lease_on_leaseless_vms" lease_tool.set_lease_on_leaseless_vms( inventory.keys, inventory, - :lease_minutes => 3 * 24 * 60 * 60 # 3 days + lease_minutes: 3 * 24 * 60 * 60 # 3 days ) -when 'show_expired_vms' +when "show_expired_vms" vms = lease_tool.filter_expired_vms inventory.keys, inventory - vms.each do |vm, time_to_expiration| + vms.each do |vm, _time_to_expiration| puts "VM '#{inventory[vm]['name']}' is expired" - end -when 'kill_expired_vms' + end +when "kill_expired_vms" vms = lease_tool.filter_expired_vms inventory.keys, inventory - vms.each do |vm, time_to_expiration| + vms.each do |vm, _time_to_expiration| puts "VM '#{inventory[vm]['name']}' is expired" if !opts[:force] puts "NOT killing VM '#{inventory[vm]['name']}' because --force not set" @@ -80,27 +81,25 @@ puts "Killing expired VM '#{inventory[vm]['name']}'" # Destroying VMs is very stressful for vCenter, and we aren't in a rush # so do one VM at a time - if inventory[vm]['runtime.powerState'] == 'poweredOn' - vm.PowerOffVM_Task.wait_for_completion - end + vm.PowerOffVM_Task.wait_for_completion if inventory[vm]["runtime.powerState"] == "poweredOn" vm.Destroy_Task.wait_for_completion end - end -when 'show_soon_expired_vms' + end +when "show_soon_expired_vms" vms = lease_tool.filter_expired_vms( - inventory.keys, inventory, - :time_delta => 3.5 * 24 * 60 * 60, # 3.5 days + inventory.keys, inventory, + time_delta: 3.5 * 24 * 60 * 60 # 3.5 days ) # We could send the user emails here, but for this example, just print the # VMs that will expire within the next 3.5 days vms.each do |vm, time_to_expiration| if time_to_expiration > 0 - hours_to_expiration = time_to_expiration / (60.0 * 60.0) - puts "VM '%s' expires in %.2fh" % [inventory[vm]['name'], hours_to_expiration] + hours_to_expiration = time_to_expiration / (60.0 * 60.0) + puts "VM '%s' expires in %.2fh" % [inventory[vm]["name"], hours_to_expiration] else puts "VM '#{inventory[vm]['name']}' is expired" end - end + end else abort "invalid command" end diff --git a/examples/logbundle.rb b/examples/logbundle.rb index 1865f1c5..a016289b 100644 --- a/examples/logbundle.rb +++ b/examples/logbundle.rb @@ -1,31 +1,32 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT # @todo Retrieve ESX log bundles when run against VC. -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM -DEFAULT_SERVER_PLACEHOLDER = '0.0.0.0' +DEFAULT_SERVER_PLACEHOLDER = "0.0.0.0" opts = Optimist.options do - banner <<-EOS -Generate and retrieve a log bundle. - -Usage: - logbundle.rb [options] dest - -dest must be a directory. - -VIM connection options: - EOS - - rbvmomi_connection_opts + banner <<~EOS + Generate and retrieve a log bundle. + + Usage: + logbundle.rb [options] dest + + dest must be a directory. + + VIM connection options: + EOS - text <<-EOS + rbvmomi_connection_opts -Other options: + text <<~EOS + + Other options: EOS end @@ -35,32 +36,32 @@ abort "destination is not a directory" unless File.directory? dest vim = VIM.connect opts -is_vc = vim.serviceContent.about.apiType == 'VirtualCenter' -diagMgr = vim.serviceContent.diagnosticManager +is_vc = vim.service_content.about.apiType == "VirtualCenter" +diagMgr = vim.service_content.diagnosticManager bundles = begin diagMgr.GenerateLogBundles_Task(includeDefault: true).wait_for_completion rescue VIM::TaskInProgress - $!.task.wait_for_completion + $ERROR_INFO.task.wait_for_completion end bundles.each do |b| - uri = URI.parse(b.url.sub('*', DEFAULT_SERVER_PLACEHOLDER)) + uri = URI.parse(b.url.sub("*", DEFAULT_SERVER_PLACEHOLDER)) dest_path = File.join(dest, File.basename(uri.path)) puts "downloading bundle #{b.url} to #{dest_path}" if uri.host == DEFAULT_SERVER_PLACEHOLDER vim.http.request_get(uri.path) do |res| - File.open dest_path, 'w' do |io| + File.open dest_path, "w" do |io| res.read_body do |data| io.write data - $stdout.write '.' + $stdout.write "." $stdout.flush end end puts end else - puts 'not supported yet' + puts "not supported yet" end end diff --git a/examples/logtail.rb b/examples/logtail.rb index f38ae81a..3681dfa3 100644 --- a/examples/logtail.rb +++ b/examples/logtail.rb @@ -1,31 +1,32 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT # Translation of example 2-2 from the vSphere SDK for Perl Programming Guide -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM opts = Optimist.options do - banner <<-EOS -Follow a log file. - -Usage: - logtail.rb [options] [logKey] - -If logKey is not provided the list of available log keys will be printed and -the program will exit. - -VIM connection options: - EOS - - rbvmomi_connection_opts + banner <<~EOS + Follow a log file. + + Usage: + logtail.rb [options] [logKey] + + If logKey is not provided the list of available log keys will be printed and + the program will exit. + + VIM connection options: + EOS - text <<-EOS + rbvmomi_connection_opts -Other options: + text <<~EOS + + Other options: EOS end @@ -33,9 +34,9 @@ logKey = ARGV[0] vim = VIM.connect opts -diagMgr = vim.serviceContent.diagnosticManager +diagMgr = vim.service_content.diagnosticManager -if not logKey +unless logKey puts "Available logs:" diagMgr.QueryDescriptions.each do |desc| puts "#{desc.key}: #{desc.info.label}" @@ -45,13 +46,13 @@ # Obtain the last line of the logfile by setting an arbitrarily large # line number as the starting point -log = diagMgr.BrowseDiagnosticLog(key: logKey, start: 999999999) +log = diagMgr.BrowseDiagnosticLog(key: logKey, start: 999_999_999) lineEnd = log.lineEnd # Get the last 5 lines of the log first, and then check every 2 seconds # to see if the log size has increased. start = lineEnd - 5 -while true +loop do log = diagMgr.BrowseDiagnosticLog(key: logKey, start: start) if log.lineStart != 0 log.lineText.each do |l| diff --git a/examples/nfs_datastore.rb b/examples/nfs_datastore.rb old mode 100644 new mode 100755 index 26e7de25..e19895c7 --- a/examples/nfs_datastore.rb +++ b/examples/nfs_datastore.rb @@ -1,40 +1,41 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM -CMDS = %w(mount unmount) +CMDS = %w(mount unmount).freeze opts = Optimist.options do - banner <<-EOS -Mount/Unmount an NFS datastore from a cluster or single host system. - -Usage: - nfs_datastore.rb [options] resource mount nfs-hostname:/remote/path [name] - nfs_datastore.rb [options] resource unmount nfs-hostname:/remote/path [name] - -Commands: #{CMDS * ' '} - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Mount/Unmount an NFS datastore from a cluster or single host system. + + Usage: + nfs_datastore.rb [options] resource mount nfs-hostname:/remote/path [name] + nfs_datastore.rb [options] resource unmount nfs-hostname:/remote/path [name] + + Commands: #{CMDS * ' '} + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS stop_on CMDS @@ -48,10 +49,10 @@ nfs_spec = ARGV[2] or Optimist.die("no nfs path given") remoteHost, remotePath = nfs_spec.split(":") localPath = ARGV[3] || remoteHost -mode = "readOnly" #hardcoded. +mode = "readOnly" # hardcoded. vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" cr = dc.find_compute_resource(cr_path) || dc.hostFolder.children.find(cr_path).first abort "compute resource not found" unless cr @@ -67,28 +68,28 @@ hosts.each do |host| hds = host.configManager.datastoreSystem - ds = hds.datastore.select {|ds| + ds = hds.datastore.select do |ds| ds.info.respond_to?(:nas) and - ds.info.name == localPath and - ds.info.nas.remoteHost == remoteHost and - ds.info.nas.remotePath == remotePath - }.first + ds.info.name == localPath and + ds.info.nas.remoteHost == remoteHost and + ds.info.nas.remotePath == remotePath + end.first case cmd - when 'mount' + when "mount" if ds puts "already mounted on #{host.name} as #{ds.name}" else ds = - hds.CreateNasDatastore(:spec => VIM.HostNasVolumeSpec(:remoteHost => remoteHost, - :remotePath => remotePath, - :localPath => localPath, - :accessMode => mode)) + hds.CreateNasDatastore(spec: VIM.HostNasVolumeSpec(remoteHost: remoteHost, + remotePath: remotePath, + localPath: localPath, + accessMode: mode)) puts "mounted on #{host.name} as #{ds.name}" end - when 'unmount' + when "unmount" if ds - hds.RemoveDatastore(:datastore => ds) + hds.RemoveDatastore(datastore: ds) puts "unmounted from #{host.name}" else puts "not mounted on #{host.name}" diff --git a/examples/power.rb b/examples/power.rb index fa03159b..26053b87 100644 --- a/examples/power.rb +++ b/examples/power.rb @@ -1,37 +1,38 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM -CMDS = %w(on off reset suspend destroy) +CMDS = %w(on off reset suspend destroy).freeze opts = Optimist.options do - banner <<-EOS -Perform VM power operations. - -Usage: - power.rb [options] cmd VM - -Commands: #{CMDS * ' '} - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Perform VM power operations. + + Usage: + power.rb [options] cmd VM + + Commands: #{CMDS * ' '} + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS stop_on CMDS @@ -43,19 +44,19 @@ vim = VIM.connect opts -dc = vim.serviceInstance.content.rootFolder.traverse(opts[:datacenter], VIM::Datacenter) or abort "datacenter not found" +dc = vim.service_instance.content.rootFolder.traverse(opts[:datacenter], VIM::Datacenter) or abort "datacenter not found" vm = dc.vmFolder.traverse(vm_name, VIM::VirtualMachine) or abort "VM not found" case cmd -when 'on' +when "on" vm.PowerOnVM_Task.wait_for_completion -when 'off' +when "off" vm.PowerOffVM_Task.wait_for_completion -when 'reset' +when "reset" vm.ResetVM_Task.wait_for_completion -when 'suspend' +when "suspend" vm.SuspendVM_Task.wait_for_completion -when 'destroy' +when "destroy" vm.Destroy_Task.wait_for_completion else abort "invalid command" diff --git a/examples/readme-1.rb b/examples/readme-1.rb index ea14891d..7c513824 100644 --- a/examples/readme-1.rb +++ b/examples/readme-1.rb @@ -1,31 +1,32 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'rbvmomi' -require 'rbvmomi/optimist' +require "rbvmomi" +require "rbvmomi/optimist" opts = Optimist.options do - banner <<-EOS -Example 1 from the README: Power on a VM. - -Usage: - readme-1.rb [options] VM name - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Example 1 from the README: Power on a VM. + + Usage: + readme-1.rb [options] VM name + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS end @@ -33,6 +34,6 @@ vm_name = ARGV[0] or abort "must specify VM name" vim = RbVmomi::VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or fail "datacenter not found" -vm = dc.find_vm(vm_name) or fail "VM not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or raise "datacenter not found" +vm = dc.find_vm(vm_name) or raise "VM not found" vm.PowerOnVM_Task.wait_for_completion diff --git a/examples/readme-2.rb b/examples/readme-2.rb index 6ae3b747..b6f015a4 100644 --- a/examples/readme-2.rb +++ b/examples/readme-2.rb @@ -1,31 +1,32 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'rbvmomi' -require 'rbvmomi/optimist' +require "rbvmomi" +require "rbvmomi/optimist" opts = Optimist.options do - banner <<-EOS -Example 2 from the README: Power on a VM the hard way. - -Usage: - readme-2.rb [options] VM name - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Example 2 from the README: Power on a VM the hard way. + + Usage: + readme-2.rb [options] VM name + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS end @@ -33,22 +34,22 @@ vm_name = ARGV[0] or abort "must specify VM name" vim = RbVmomi::VIM.connect opts -rootFolder = vim.serviceInstance.content.rootFolder -dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).find { |x| x.name == opts[:datacenter] } or fail "datacenter not found" -vm = dc.vmFolder.childEntity.grep(RbVmomi::VIM::VirtualMachine).find { |x| x.name == vm_name } or fail "VM not found" +rootFolder = vim.service_instance.content.rootFolder +dc = rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).find { |x| x.name == opts[:datacenter] } or raise "datacenter not found" +vm = dc.vmFolder.childEntity.grep(RbVmomi::VIM::VirtualMachine).find { |x| x.name == vm_name } or raise "VM not found" task = vm.PowerOnVM_Task filter = vim.propertyCollector.CreateFilter( - :spec => { - :propSet => [{ :type => 'Task', :all => false, :pathSet => ['info.state']}], - :objectSet => [{ :obj => task }] + spec: { + propSet: [{ type: "Task", all: false, pathSet: ["info.state"] }], + objectSet: [{ obj: task }] }, - :partialUpdates => false + partialUpdates: false ) -ver = '' -while true - result = vim.propertyCollector.WaitForUpdates(:version => ver) +ver = "" +loop do + result = vim.propertyCollector.WaitForUpdates(version: ver) ver = result.version - break if ['success', 'error'].member? task.info.state + break if %w(success error).member? task.info.state end filter.DestroyPropertyFilter -raise task.info.error if task.info.state == 'error' +raise task.info.error if task.info.state == "error" diff --git a/examples/screenshot.rb b/examples/screenshot.rb index c77ef969..e740697c 100644 --- a/examples/screenshot.rb +++ b/examples/screenshot.rb @@ -1,37 +1,38 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT # Based on takeVMScreenshot.pl by William Lam -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM opts = Optimist.options do - banner <<-EOS -Take a screenshot. - -Usage: - screenshot.rb [options] vm filename - -A PNG image will be saved to the given filename. - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Take a screenshot. + + Usage: + screenshot.rb [options] vm filename + + A PNG image will be saved to the given filename. + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS end @@ -40,12 +41,12 @@ output_path = ARGV[1] or abort("must specify output filename") vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) +dc = vim.service_instance.find_datacenter(opts[:datacenter]) vm = dc.find_vm vm_name -abort "VM must be running" unless vm.runtime.powerState == 'poweredOn' +abort "VM must be running" unless vm.runtime.powerState == "poweredOn" remote_path = vm.CreateScreenshot_Task.wait_for_completion -remote_path =~ /^(\/vmfs\/volumes\/[^\/]+)\// or fail -datastore_prefix = $1 +remote_path =~ %r{^(/vmfs/volumes/[^/]+)/} or raise +datastore_prefix = Regexp.last_match(1) datastore_path = $' datastore = vm.datastore.find { |ds| ds.info.url == datastore_prefix } datastore.download datastore_path, output_path diff --git a/examples/vdf.rb b/examples/vdf.rb index b216fde9..ab9b0f36 100644 --- a/examples/vdf.rb +++ b/examples/vdf.rb @@ -1,35 +1,36 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT # Translation of vGhetto vdf, originally by William Lam -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM opts = Optimist.options do - banner <<-EOS -Display utilization of each datastore in the datacenter. - -Usage: - vdf.rb [options] - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Display utilization of each datastore in the datacenter. + + Usage: + vdf.rb [options] + + VIM connection options: + EOS -Datacenter selection: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + Datacenter selection: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS end @@ -37,24 +38,24 @@ vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" -def si n - ['', 'K', 'M', 'G', 'T', 'P'].each_with_index do |x,i| - v = n.to_f/(1000**i) - return v,x if v < 1000 or x == 'P' +def si(n) + ["", "K", "M", "G", "T", "P"].each_with_index do |x, i| + v = n.to_f / (1000**i) + return v, x if (v < 1000) || (x == "P") end end -def unit n, u, p +def unit(n, u, p) "%.*g%s%s" % [p, si(n), u].flatten end -def b n - unit(n,'B',3) +def b(n) + unit(n, "B", 3) end -puts "Filesystem#{' '*53}Size Used Avail Use% Mounted on" +puts "Filesystem#{' ' * 53}Size Used Avail Use% Mounted on" fmt = "%-62s %-8s %-8s %-8s %-8s %s" if false @@ -62,23 +63,24 @@ def b n dc.datastore.sort_by { |ds| ds.info.url }.each do |ds| s = ds.summary next unless s.accessible + size = s.capacity free = s.freeSpace used = size - free - pct_used = used*100.0/size - puts(fmt % [ds.info.url, b(size), b(used), b(free), unit(pct_used,'%',3), ds.name]) + pct_used = used * 100.0 / size + puts(fmt % [ds.info.url, b(size), b(used), b(free), unit(pct_used, "%", 3), ds.name]) end else # fast version paths = %w(name info.url summary.accessible summary.capacity summary.freeSpace) - propSet = [{ :type => 'Datastore', :pathSet => paths }] - filterSpec = { :objectSet => dc.datastore.map { |ds| { :obj => ds } }, :propSet => propSet } - data = vim.propertyCollector.RetrieveProperties(:specSet => [filterSpec]) - data.select { |d| d['summary.accessible'] }.sort_by { |d| d['info.url'] }.each do |d| - size = d['summary.capacity'] - free = d['summary.freeSpace'] + propSet = [{ type: "Datastore", pathSet: paths }] + filterSpec = { objectSet: dc.datastore.map { |ds| { obj: ds } }, propSet: propSet } + data = vim.propertyCollector.RetrieveProperties(specSet: [filterSpec]) + data.select { |d| d["summary.accessible"] }.sort_by { |d| d["info.url"] }.each do |d| + size = d["summary.capacity"] + free = d["summary.freeSpace"] used = size - free - pct_used = used*100.0/size - puts(fmt % [d['info.url'], b(size), b(used), b(free), unit(pct_used,'%',3), d['name']]) + pct_used = used * 100.0 / size + puts(fmt % [d["info.url"], b(size), b(used), b(free), unit(pct_used, "%", 3), d["name"]]) end end diff --git a/examples/vm_drs_behavior.rb b/examples/vm_drs_behavior.rb old mode 100644 new mode 100755 index 2f99025d..362a97b2 --- a/examples/vm_drs_behavior.rb +++ b/examples/vm_drs_behavior.rb @@ -1,41 +1,42 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM -CMDS = %w(get set) -BEHAVIOR = %w(fullyAutomated manual partiallyAutomated default) +CMDS = %w(get set).freeze +BEHAVIOR = %w(fullyAutomated manual partiallyAutomated default).freeze opts = Optimist.options do - banner <<-EOS -Configure VM DRS behavior. - -Usage: - vm_drs_behavior.rb [options] VM get - vm_drs_behavior.rb [options] VM set #{BEHAVIOR.join('|')} - -Commands: #{CMDS * ' '} - -VIM connection options: - EOS - - rbvmomi_connection_opts - - text <<-EOS + banner <<~EOS + Configure VM DRS behavior. + + Usage: + vm_drs_behavior.rb [options] VM get + vm_drs_behavior.rb [options] VM set #{BEHAVIOR.join('|')} + + Commands: #{CMDS * ' '} + + VIM connection options: + EOS -VM location options: - EOS + rbvmomi_connection_opts - rbvmomi_datacenter_opt + text <<~EOS + + VM location options: + EOS - text <<-EOS + rbvmomi_datacenter_opt -Other options: + text <<~EOS + + Other options: EOS stop_on CMDS @@ -48,33 +49,30 @@ abort "invalid command" unless CMDS.member? cmd vim = VIM.connect opts -dc = vim.serviceInstance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" +dc = vim.service_instance.find_datacenter(opts[:datacenter]) or abort "datacenter not found" vm = dc.find_vm(vm_name) or abort "VM not found" cluster = vm.runtime.host.parent -config = cluster.configurationEx.drsVmConfig.select {|c| c.key.name == vm.name }.first +config = cluster.configurationEx.drsVmConfig.select { |c| c.key.name == vm.name }.first default = cluster.configurationEx.drsConfig.defaultVmBehavior case cmd -when 'get' - if config - behavior = config.behavior - else - behavior = "#{default} (default)" - end +when "get" + behavior = if config + config.behavior + else + "#{default} (default)" + end puts "#{vm.name} is #{behavior}" -when 'set' +when "set" behavior = ARGV[2] or Optimist.die("no behavior given") abort "invalid behavior" unless BEHAVIOR.member? behavior - if behavior == "default" - behavior = default - end + behavior = default if behavior == "default" vm_spec = - VIM.ClusterDrsVmConfigSpec(:operation => VIM.ArrayUpdateOperation(config ? "edit" : "add"), - :info => VIM.ClusterDrsVmConfigInfo(:key => vm, - :behavior => VIM.DrsBehavior(behavior))) - spec = VIM.ClusterConfigSpecEx(:drsVmConfigSpec => [vm_spec]) - cluster.ReconfigureComputeResource_Task(:spec => spec, :modify => true).wait_for_completion + VIM.ClusterDrsVmConfigSpec(operation: VIM.ArrayUpdateOperation(config ? "edit" : "add"), + info: VIM.ClusterDrsVmConfigInfo(key: vm, + behavior: VIM.DrsBehavior(behavior))) + spec = VIM.ClusterConfigSpecEx(drsVmConfigSpec: [vm_spec]) + cluster.ReconfigureComputeResource_Task(spec: spec, modify: true).wait_for_completion end - diff --git a/exe/rbvmomish b/exe/rbvmomish index ce4a12d8..4491d639 100755 --- a/exe/rbvmomish +++ b/exe/rbvmomish @@ -1,38 +1,39 @@ #!/usr/bin/env ruby +# frozen_string_literal: true # TODO keepalive # TODO rc file # TODO proxy support? -require 'optimist' -require 'readline' -require 'rbvmomi' -require 'rbvmomi/optimist' +require "optimist" +require "readline" +require "rbvmomi" +require "rbvmomi/optimist" VIM = RbVmomi::VIM opts = Optimist.options do - banner <<-EOS -vSphere API console. - -Usage: - rbvmomish [options] - -Predefined methods: -conn: Returns the VIM connection -si: Returns the ServiceInstance -help: Displays this text. - -Special syntax: -Adding a '#' suffix to an expression displays information about the type of the -result, including its properties and methods, instead of the value. - -VIM connection options: + banner <<~EOS + vSphere API console. + + Usage: + rbvmomish [options] + + Predefined methods: + conn: Returns the VIM connection + si: Returns the ServiceInstance + help: Displays this text. + + Special syntax: + Adding a '#' suffix to an expression displays information about the type of the + result, including its properties and methods, instead of the value. + + VIM connection options: EOS rbvmomi_connection_opts - text <<-EOS - -Other options: + text <<~EOS + + Other options: EOS $optimist = self @@ -41,25 +42,34 @@ end begin $vim = VIM.connect opts rescue Errno::EHOSTUNREACH - abort $!.message + abort $ERROR_INFO.message end typenames = VIM.loader.typenames Readline.completion_append_character = " " Readline.completion_proc = lambda do |word| return unless word + prefix_regex = /^#{Regexp.escape(word)}/ candidates = typenames.sort candidates.find_all { |e| e.match(prefix_regex) } end history_fn = "#{ENV['HOME']}/.rbvmomish-history" -IO.foreach(history_fn) { |l| Readline::HISTORY << l.chomp } rescue nil -history = File.open(history_fn, 'a') +begin + IO.foreach(history_fn) { |l| Readline::HISTORY << l.chomp } +rescue StandardError + nil +end +history = File.open(history_fn, "a") def type name - klass = VIM.type(name) rescue err("invalid type #{name.inspect}") - q = lambda { |x| x =~ /^xsd:/ ? $' : x } + klass = begin + VIM.type(name) + rescue StandardError + err("invalid type #{name.inspect}") + end + q = ->(x) { x =~ /^xsd:/ ? $' : x } if klass < VIM::DataObject puts "Data Object #{klass}" klass.full_props_desc.each do |desc| @@ -74,8 +84,8 @@ def type name end puts puts "Methods:" - klass.full_methods_desc.sort_by(&:first).each do |name,desc| - params = desc['params'] + klass.full_methods_desc.sort_by(&:first).each do |name, desc| + params = desc["params"] puts " #{name}(#{params.map { |x| "#{x['name']} : #{q[x['wsdl_type'] || 'void']}#{x['is-array'] ? '[]' : ''}" } * ', '}) : #{q[desc['result']['wsdl_type'] || 'void']}" end else @@ -86,7 +96,7 @@ end class UserError < RuntimeError; end def err msg - raise UserError.new(msg) + raise UserError, msg end def cookie str @@ -98,7 +108,7 @@ def conn end def si - $vim.serviceInstance + $vim.service_instance end def help @@ -109,30 +119,30 @@ end $binding = $vim.instance_eval { binding } loop do -begin - input = Readline.readline("#{opts[:host]}> ", false) or break - input = input.strip - next if input.empty? - - (history.puts input; Readline::HISTORY << input) unless input == Readline::HISTORY.to_a[-1] - - result = eval(input, $binding) - if input =~ /\#$/ - type result.class.wsdl_name - else - pp result unless result == :no_result + begin + input = Readline.readline("#{opts[:host]}> ", false) or break + input = input.strip + next if input.empty? + + (history.puts input; Readline::HISTORY << input) unless input == Readline::HISTORY.to_a[-1] + + result = eval(input, $binding) + if input =~ /\#$/ + type result.class.wsdl_name + else + pp result unless result == :no_result + end + rescue SystemExit, IOError + raise + rescue RuntimeError, RbVmomi::Fault + puts "#{$ERROR_INFO.class}: #{$ERROR_INFO.message}" + puts $ERROR_INFO.backtrace * "\n" + rescue UserError + puts $ERROR_INFO.message + rescue Interrupt + puts + rescue Exception + puts "#{$ERROR_INFO.class}: #{$ERROR_INFO.message}" + puts $ERROR_INFO.backtrace * "\n" end -rescue SystemExit, IOError - raise -rescue RuntimeError, RbVmomi::Fault - puts "#{$!.class}: #{$!.message}" - puts $!.backtrace * "\n" -rescue UserError - puts $!.message -rescue Interrupt - puts -rescue Exception - puts "#{$!.class}: #{$!.message}" - puts $!.backtrace * "\n" -end end diff --git a/lib/rbvmomi.rb b/lib/rbvmomi.rb index ec0ef2ec..982c365c 100644 --- a/lib/rbvmomi.rb +++ b/lib/rbvmomi.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true # Copyright (c) 2010-2019 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT @@ -10,7 +11,7 @@ def self.connect(opts) end end -require_relative 'rbvmomi/connection' -require_relative 'rbvmomi/sso' -require_relative 'rbvmomi/version' -require_relative 'rbvmomi/vim' +require_relative "rbvmomi/connection" +require_relative "rbvmomi/sso" +require_relative "rbvmomi/version" +require_relative "rbvmomi/vim" diff --git a/lib/rbvmomi/basic_types.rb b/lib/rbvmomi/basic_types.rb index 54f706f7..e315e040 100644 --- a/lib/rbvmomi/basic_types.rb +++ b/lib/rbvmomi/basic_types.rb @@ -1,403 +1,436 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'pp' -require 'set' +require "pp" +require "set" module RbVmomi -module BasicTypes + module BasicTypes + BUILTIN = Set.new %w(ManagedObject DataObject TypeName PropertyPath ManagedObjectReference MethodName MethodFault LocalizedMethodFault KeyValue) -BUILTIN = Set.new %w(ManagedObject DataObject TypeName PropertyPath ManagedObjectReference MethodName MethodFault LocalizedMethodFault KeyValue) + class Base + class << self + attr_accessor :wsdl_name -class Base - class << self - attr_accessor :wsdl_name + def init(wsdl_name = name) + @wsdl_name = wsdl_name + end - def init wsdl_name=self.name - @wsdl_name = wsdl_name - end + def to_s + @wsdl_name + end + end - def to_s - @wsdl_name + init end - end - init -end + class ObjectWithProperties < Base + class << self + attr_accessor :props_desc + + def init(name = self.name, props = []) + super name + @props_desc = props + @props_desc.each do |d| + sym = d["name"].to_sym + define_method(sym) { _get_property sym } + define_method(:"#{sym}=") { |x| _set_property sym, x } + end + end -class ObjectWithProperties < Base - class << self - attr_accessor :props_desc + def full_props_set + @full_props_set ||= Set.new(full_props_desc.map { |x| x["name"] }) + end + + def full_props_desc + @full_props_desc ||= (self == ObjectWithProperties ? [] : superclass.full_props_desc) + props_desc + end - def init name=self.name, props=[] - super name - @props_desc = props - @props_desc.each do |d| - sym = d['name'].to_sym - define_method(sym) { _get_property sym } - define_method(:"#{sym}=") { |x| _set_property sym, x } + def find_prop_desc(name) + full_props_desc.find { |x| x["name"] == name.to_s } + end end - end - def full_props_set - @full_props_set ||= Set.new(full_props_desc.map { |x| x['name'] }) - end + def _get_property(_sym) + raise "unimplemented" + end - def full_props_desc - @full_props_desc ||= (self == ObjectWithProperties ? [] : superclass.full_props_desc) + props_desc - end + def _set_property(_sym, _val) + raise "unimplemented" + end - def find_prop_desc name - full_props_desc.find { |x| x['name'] == name.to_s } + init end - end - - def _get_property sym - fail 'unimplemented' - end - - def _set_property sym, val - fail 'unimplemented' - end - init -end + class ObjectWithMethods < ObjectWithProperties + class << self + attr_accessor :methods_desc -class ObjectWithMethods < ObjectWithProperties - class << self - attr_accessor :methods_desc + def init(name = self.name, props = [], methods = {}) + super name, props + @methods_desc = methods - def init name=self.name, props=[], methods={} - super name, props - @methods_desc = methods + @methods_desc.each do |k, _d| + sym = k.to_sym + define_method(sym) { |*args| _call sym, *args } + define_method(:"#{sym}!") { |*args| _call sym, *args } + end + end - @methods_desc.each do |k,d| - sym = k.to_sym - define_method(sym) { |*args| _call sym, *args } - define_method(:"#{sym}!") { |*args| _call sym, *args } + # XXX cache + def full_methods_desc + (self == ObjectWithMethods ? {} : superclass.full_methods_desc).merge methods_desc + end end - end - # XXX cache - def full_methods_desc - (self == ObjectWithMethods ? {} : superclass.full_methods_desc).merge methods_desc + init end - end - init -end + class DataObject < ObjectWithProperties + attr_reader :props -class DataObject < ObjectWithProperties - attr_reader :props + def self.kind + :data + end - def self.kind; :data end + def initialize(props = {}) + # Deserialization fast path + if props.nil? + @props = {} + return + end - def initialize props={} - # Deserialization fast path - if props == nil - @props = {} - return - end + @props = Hash[props.map { |k, v| [k.to_sym, v] }] + # self.class.full_props_desc.each do |desc| + # fail "missing required property #{desc['name'].inspect} of #{self.class.wsdl_name}" if @props[desc['name'].to_sym].nil? and not desc['is-optional'] + # end + @props.each do |k, _v| + raise "unexpected property name #{k}" unless self.class.find_prop_desc(k) + end + end - @props = Hash[props.map { |k,v| [k.to_sym, v] }] - #self.class.full_props_desc.each do |desc| - #fail "missing required property #{desc['name'].inspect} of #{self.class.wsdl_name}" if @props[desc['name'].to_sym].nil? and not desc['is-optional'] - #end - @props.each do |k,v| - fail "unexpected property name #{k}" unless self.class.find_prop_desc(k) - end - end + def initialize_copy(source) + super + @props = @props.dup + end - def initialize_copy(source) - super - @props = @props.dup - end + def _get_property(sym) + @props[sym] + end - def _get_property sym - @props[sym] - end + def [](sym) + _get_property(sym) + end - def [] sym - _get_property sym - end + def _set_property(sym, val) + @props[sym] = val + end - def _set_property sym, val - @props[sym] = val - end + def []=(sym, val) + _set_property sym, val + end - def []= sym, val - _set_property sym, val - end + def ==(o) + return false unless o.class == self.class - def == o - return false unless o.class == self.class - keys = (props.keys + o.props.keys).uniq - keys.all? { |k| props[k] == o.props[k] } - end + keys = (props.keys + o.props.keys).uniq + keys.all? { |k| props[k] == o.props[k] } + end - alias eql? == + alias eql? == - def hash - props.hash - end + def hash + props.hash + end - def pretty_print q - q.text self.class.wsdl_name - q.group 2 do - q.text '(' - q.breakable - props = @props.sort_by { |k,v| k.to_s } - q.seplist props, nil, :each do |e| - k, v = e - q.group do - q.text k.to_s - q.text ': ' - q.pp v + def pretty_print(q) + q.text self.class.wsdl_name + q.group 2 do + q.text "(" + q.breakable + props = @props.sort_by { |k, _v| k.to_s } + q.seplist props, nil, :each do |e| + k, v = e + q.group do + q.text k.to_s + q.text ": " + q.pp v + end + end end + q.breakable + q.text ")" end - end - q.breakable - q.text ')' - end - def as_hash(val) - if val.kind_of?(Array) - val.map { |v| as_hash(v) } - elsif val.respond_to?(:to_hash) - val.to_hash - else - val - end - end + def as_hash(val) + if val.is_a?(Array) + val.map { |v| as_hash(v) } + elsif val.respond_to?(:to_hash) + val.to_hash + else + val + end + end - def to_hash - props.transform_values { |v| as_hash(v) } - end + def to_hash + props.transform_values { |v| as_hash(v) } + end - def to_json(options = nil) - to_hash.merge(JSON.create_id => self.class.name).to_json - end + def to_json(_options = nil) + to_hash.merge(JSON.create_id => self.class.name).to_json + end - init -end + init + end -class ManagedObject < ObjectWithMethods - def self.kind; :managed end + class ManagedObject < ObjectWithMethods + def self.kind + :managed + end - def initialize connection, ref - super() - @connection = connection - @soap = @connection # XXX deprecated - @ref = ref - end + def initialize(connection, ref) + super() + @connection = connection + @soap = @connection # XXX deprecated + @ref = ref + end - def _connection - @connection - end + def _connection + @connection + end - def _ref - @ref - end + def _ref + @ref + end - def _get_property sym - ret = @connection.propertyCollector.RetrieveProperties(:specSet => [{ - :propSet => [{ :type => self.class.wsdl_name, :pathSet => [sym.to_s] }], - :objectSet => [{ :obj => self }], - }])[0] - - if !ret - return nil - elsif ret.propSet.empty? - return nil if ret.missingSet.empty? - raise ret.missingSet[0].fault - else - ret.propSet[0].val - end - end + def _get_property(sym) + ret = @connection.propertyCollector.RetrieveProperties(specSet: [{ + propSet: [{ type: self.class.wsdl_name, pathSet: [sym.to_s] }], + objectSet: [{ obj: self }] + }])[0] - def _set_property sym, val - fail 'unimplemented' - end + if !ret + nil + elsif ret.propSet.empty? + return nil if ret.missingSet.empty? - def _call method, o={} - fail "parameters must be passed as a hash" unless o.is_a? Hash - desc = self.class.full_methods_desc[method.to_s] or fail "unknown method" - @connection.call method, desc, self, o - end + raise ret.missingSet[0].fault + else + ret.propSet[0].val + end + end - def to_s - "#{self.class.wsdl_name}(#{@ref.inspect})" - end + def _set_property(_sym, _val) + raise "unimplemented" + end - def to_hash - to_s - end + def _call(method, o = {}) + raise "parameters must be passed as a hash" unless o.is_a? Hash - def pretty_print pp - pp.text to_s - end + desc = self.class.full_methods_desc[method.to_s] or raise "unknown method" + @connection.call method, desc, self, o + end - def [] k - _get_property k - end + def to_s + "#{self.class.wsdl_name}(#{@ref.inspect})" + end - def == x - out = (x.class == self.class && x._ref == @ref) - out = (x._connection.instanceUuid == self._connection.instanceUuid) if out && x._connection.host - out - end + def to_hash + to_s + end - alias eql? == + def pretty_print(pp) + pp.text to_s + end - def hash - [self.class, @ref].hash - end + def [](k) + _get_property k + end - init 'ManagedObject' -end + def ==(x) + out = (x.class == self.class && x._ref == @ref) + out = (x._connection.instanceUuid == _connection.instanceUuid) if out && x._connection.host + out + end + + alias eql? == -class Enum < Base - class << self - attr_accessor :values + def hash + [self.class, @ref].hash + end - def init name=self.name, values=[] - super name - @values = values + init "ManagedObject" end - end - def self.kind; :enum end + class Enum < Base + class << self + attr_accessor :values - attr_reader :value + def init(name = self.name, values = []) + super name + @values = values + end + end - def initialize value - @value = value - end + def self.kind + :enum + end - def to_hash - value - end + attr_reader :value - init -end + def initialize(value) + @value = value + end -class MethodFault < DataObject - init 'MethodFault', [ - { - 'name' => 'faultCause', - 'wsdl_type' => 'LocalizedMethodFault', - 'is-array' => false, - 'is-optional' => true, - }, { - 'name' => 'faultMessage', - 'wsdl_type' => 'LocalizableMessage', - 'is-array' => true, - 'is-optional' => true, - }, - ] - - def self.=== exn - exn.class == RbVmomi::Fault and self <= exn.fault.class - end -end + def to_hash + value + end -class LocalizedMethodFault < DataObject - init 'LocalizedMethodFault', [ - { - 'name' => 'fault', - 'wsdl_type' => 'MethodFault', - 'is-array' => false, - 'is-optional' => false, - }, { - 'name' => 'localizedMessage', - 'wsdl_type' => 'xsd:string', - 'is-array' => false, - 'is-optional' => true, - }, - ] - - def exception - RbVmomi::Fault.new(self.localizedMessage, self.fault) - end -end + init + end -class RuntimeFault < MethodFault - init -end + class MethodFault < DataObject + init "MethodFault", [ + { + "name" => "faultCause", + "wsdl_type" => "LocalizedMethodFault", + "is-array" => false, + "is-optional" => true + }, { + "name" => "faultMessage", + "wsdl_type" => "LocalizableMessage", + "is-array" => true, + "is-optional" => true + } + ] + + def self.===(exn) + exn.class == RbVmomi::Fault and self <= exn.fault.class + end + end -class MethodName < String - def self.wsdl_name; 'MethodName' end -end + class LocalizedMethodFault < DataObject + init "LocalizedMethodFault", [ + { + "name" => "fault", + "wsdl_type" => "MethodFault", + "is-array" => false, + "is-optional" => false + }, { + "name" => "localizedMessage", + "wsdl_type" => "xsd:string", + "is-array" => false, + "is-optional" => true + } + ] + + def exception + RbVmomi::Fault.new(localizedMessage, fault) + end + end -class PropertyPath < String - def self.wsdl_name; 'PropertyPath' end -end + class RuntimeFault < MethodFault + init + end -class TypeName < String - def self.wsdl_name; 'TypeName' end -end + class MethodName < String + def self.wsdl_name + "MethodName" + end + end -class ManagedObjectReference - def self.wsdl_name; 'ManagedObjectReference' end -end + class PropertyPath < String + def self.wsdl_name + "PropertyPath" + end + end -class Boolean - def self.wsdl_name; 'xsd:boolean' end -end + class TypeName < String + def self.wsdl_name + "TypeName" + end + end -class AnyType - def self.wsdl_name; 'xsd:anyType' end -end + class ManagedObjectReference + def self.wsdl_name + "ManagedObjectReference" + end + end -class Binary - def self.wsdl_name; 'xsd:base64Binary' end -end + class Boolean + def self.wsdl_name + "xsd:boolean" + end + end -class ::Class - def wsdl_name; self.class.name end -end + class AnyType + def self.wsdl_name + "xsd:anyType" + end + end -class ::String - def self.wsdl_name; 'xsd:string' end -end + class Binary + def self.wsdl_name + "xsd:base64Binary" + end + end -class ::Integer - def self.wsdl_name; 'xsd:int' end -end + class ::Class + def wsdl_name + self.class.name + end + end -class ::Float - def self.wsdl_name; 'xsd:float' end -end + class ::String + def self.wsdl_name + "xsd:string" + end + end -class Int - def self.wsdl_name; 'xsd:int' end - - def initialize x - @val = x - end - - def to_s - @val.to_s - end -end + class ::Integer + def self.wsdl_name + "xsd:int" + end + end -class KeyValue - def self.wsdl_name; 'KeyValue' end - attr_accessor :key, :value + class ::Float + def self.wsdl_name + "xsd:float" + end + end - def initialize k, v - @key = k - @value = v - end + class Int + def self.wsdl_name + "xsd:int" + end + + def initialize(x) + @val = x + end - def [] i - if i == 0 then @key - elsif i == 1 then @value - else fail "invalid index #{i.inspect}" + def to_s + @val.to_s + end end - end -end + class KeyValue + def self.wsdl_name + "KeyValue" + end + attr_accessor :key, :value -end + def initialize(k, v) + @key = k + @value = v + end + + def [](i) + if i == 0 then @key + elsif i == 1 then @value + else raise "invalid index #{i.inspect}" + end + end + end + end end diff --git a/lib/rbvmomi/connection.rb b/lib/rbvmomi/connection.rb index 56c6a915..2c6f5abb 100644 --- a/lib/rbvmomi/connection.rb +++ b/lib/rbvmomi/connection.rb @@ -1,272 +1,273 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'time' -require 'date' -require_relative 'trivial_soap' -require_relative 'basic_types' -require_relative 'fault' -require_relative 'type_loader' -require_relative 'deserialization' +require "time" +require "date" +require_relative "trivial_soap" +require_relative "basic_types" +require_relative "fault" +require_relative "type_loader" +require_relative "deserialization" module RbVmomi + IS_JRUBY = RUBY_PLATFORM == "java" -IS_JRUBY = RUBY_PLATFORM == 'java' + class DeserializationFailed < RuntimeError; end -class DeserializationFailed < Exception; end + class Connection < TrivialSoap + NS_XSI = "http://www.w3.org/2001/XMLSchema-instance" -class Connection < TrivialSoap - NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance' + attr_accessor :rev + attr_reader :profile + attr_reader :profile_summary + attr_accessor :profiling + attr_reader :deserializer - attr_accessor :rev - attr_reader :profile - attr_reader :profile_summary - attr_accessor :profiling - attr_reader :deserializer - - def initialize opts - @ns = opts[:ns] or fail "no namespace specified" - @rev = opts[:rev] or fail "no revision specified" - @deserializer = Deserializer.new self - reset_profiling - @profiling = false - super opts - end + def initialize(opts) + @ns = opts[:ns] or raise "no namespace specified" + @rev = opts[:rev] or raise "no revision specified" + @deserializer = Deserializer.new self + reset_profiling + @profiling = false + super opts + end - def reset_profiling - @profile = {} - @profile_summary = {:network_latency => 0, :request_emit => 0, :response_parse => 0, :num_calls => 0} - end + def reset_profiling + @profile = {} + @profile_summary = { network_latency: 0, request_emit: 0, response_parse: 0, num_calls: 0 } + end - def emit_request xml, method, descs, this, params - xml.tag! method, :xmlns => @ns do - obj2xml xml, '_this', 'ManagedObject', false, this - descs.each do |d| - k = d['name'] - k = k.to_sym if !params.member?(k) && params.member?(k.to_sym) - v = params[k] - if not v == nil - obj2xml xml, d['name'], d['wsdl_type'], d['is-array'], v - else - fail "missing required parameter #{d['name']}" unless d['is-optional'] + def emit_request(xml, method, descs, this, params) + xml.tag! method, xmlns: @ns do + obj2xml xml, "_this", "ManagedObject", false, this + descs.each do |d| + k = d["name"] + k = k.to_sym if !params.member?(k) && params.member?(k.to_sym) + v = params[k] + if !v.nil? + obj2xml xml, d["name"], d["wsdl_type"], d["is-array"], v + else + raise "missing required parameter #{d['name']}" unless d["is-optional"] + end end end end - end - def parse_response resp, desc - if resp.at('faultcode') - detail = resp.at('detail') - fault = detail && @deserializer.deserialize(detail.children.first, 'MethodFault') - msg = resp.at('faultstring').text - if fault - raise RbVmomi::Fault.new(msg, fault) - else - fail "#{resp.at('faultcode').text}: #{msg}" - end - else - if desc - type = desc['is-task'] ? 'Task' : desc['wsdl_type'] - returnvals = resp.children.select(&:element?).map { |c| @deserializer.deserialize c, type } - (desc['is-array'] && !desc['is-task']) ? returnvals : returnvals.first + def parse_response(resp, desc) + if resp.at("faultcode") + detail = resp.at("detail") + fault = detail && @deserializer.deserialize(detail.children.first, "MethodFault") + msg = resp.at("faultstring").text + if fault + raise RbVmomi::Fault.new(msg, fault) + else + raise "#{resp.at('faultcode').text}: #{msg}" + end else - nil + if desc + type = desc["is-task"] ? "Task" : desc["wsdl_type"] + returnvals = resp.children.select(&:element?).map { |c| @deserializer.deserialize c, type } + (desc["is-array"] && !desc["is-task"]) ? returnvals : returnvals.first + end end end - end - def call method, desc, this, params - fail "this is not a managed object" unless this.is_a? BasicTypes::ManagedObject - fail "parameters must be passed as a hash" unless params.is_a? Hash - fail unless desc.is_a? Hash - - t1 = Time.now - body = soap_envelope do |xml| - emit_request xml, method, desc['params'], this, params - end.target! - - t2 = Time.now - resp, resp_size = request "#{@ns}/#{@rev}", body - - t3 = Time.now - out = parse_response resp, desc['result'] - - if @profiling - t4 = Time.now - @profile[method] ||= [] - profile_info = { - :network_latency => (t3 - t2), - :request_emit => t2 - t1, - :response_parse => t4 - t3, - :params => params, - :obj => this, - :backtrace => caller, - :request_size => body.length, - :response_size => resp_size, - } - @profile[method] << profile_info - @profile_summary[:network_latency] += profile_info[:network_latency] - @profile_summary[:response_parse] += profile_info[:response_parse] - @profile_summary[:request_emit] += profile_info[:request_emit] - @profile_summary[:num_calls] += 1 + def call(method, desc, this, params) + raise "this is not a managed object" unless this.is_a? BasicTypes::ManagedObject + raise "parameters must be passed as a hash" unless params.is_a? Hash + raise unless desc.is_a? Hash + + t1 = Time.now + body = soap_envelope do |xml| + emit_request xml, method, desc["params"], this, params + end.target! + + t2 = Time.now + resp, resp_size = request "#{@ns}/#{@rev}", body + + t3 = Time.now + out = parse_response resp, desc["result"] + + if @profiling + t4 = Time.now + @profile[method] ||= [] + profile_info = { + network_latency: (t3 - t2), + request_emit: t2 - t1, + response_parse: t4 - t3, + params: params, + obj: this, + backtrace: caller, + request_size: body.length, + response_size: resp_size + } + @profile[method] << profile_info + @profile_summary[:network_latency] += profile_info[:network_latency] + @profile_summary[:response_parse] += profile_info[:response_parse] + @profile_summary[:request_emit] += profile_info[:request_emit] + @profile_summary[:num_calls] += 1 + end + + out end - out - end + # hic sunt dracones + def obj2xml(xml, name, type, is_array, o, attrs = {}) + expected = type(type) + raise "expected array for '#{name}', got #{o.class.wsdl_name}" if is_array && !(o.is_a?(Array) || (o.is_a?(Hash) && (expected == BasicTypes::KeyValue))) - # hic sunt dracones - def obj2xml xml, name, type, is_array, o, attrs={} - expected = type(type) - fail "expected array for '#{name}', got #{o.class.wsdl_name}" if is_array and not (o.is_a? Array or (o.is_a? Hash and expected == BasicTypes::KeyValue)) - case o - when Array, BasicTypes::KeyValue - if o.is_a? BasicTypes::KeyValue and expected != BasicTypes::KeyValue - fail "expected #{expected.wsdl_name} for '#{name}', got KeyValue" - elsif expected == BasicTypes::KeyValue and not is_array - xml.tag! name, attrs do - xml.tag! 'key', o[0].to_s - xml.tag! 'value', o[1].to_s - end - else - fail "expected #{expected.wsdl_name} for '#{name}', got array" unless is_array - o.each do |e| - obj2xml xml, name, expected.wsdl_name, false, e, attrs + case o + when Array, BasicTypes::KeyValue + if o.is_a?(BasicTypes::KeyValue) && (expected != BasicTypes::KeyValue) + raise "expected #{expected.wsdl_name} for '#{name}', got KeyValue" + elsif (expected == BasicTypes::KeyValue) && !is_array + xml.tag! name, attrs do + xml.tag! "key", o[0].to_s + xml.tag! "value", o[1].to_s + end + else + raise "expected #{expected.wsdl_name} for '#{name}', got array" unless is_array + + o.each do |e| + obj2xml xml, name, expected.wsdl_name, false, e, attrs + end end - end - when BasicTypes::ManagedObject - fail "expected #{expected.wsdl_name} for '#{name}', got #{o.class.wsdl_name} for field #{name.inspect}" if expected and not expected >= o.class and not expected == BasicTypes::AnyType - xml.tag! name, o._ref, :type => o.class.wsdl_name - when BasicTypes::DataObject - if expected and not expected >= o.class and not expected == BasicTypes::AnyType - fail "expected #{expected.wsdl_name} for '#{name}', got #{o.class.wsdl_name} for field #{name.inspect}" - end - xml.tag! name, attrs.merge("xsi:type" => o.class.wsdl_name) do - o.class.full_props_desc.each do |desc| - if o.props.member? desc['name'].to_sym - v = o.props[desc['name'].to_sym] + when BasicTypes::ManagedObject + raise "expected #{expected.wsdl_name} for '#{name}', got #{o.class.wsdl_name} for field #{name.inspect}" if expected && (expected < o.class) && (expected != BasicTypes::AnyType) + + xml.tag! name, o._ref, type: o.class.wsdl_name + when BasicTypes::DataObject + raise "expected #{expected.wsdl_name} for '#{name}', got #{o.class.wsdl_name} for field #{name.inspect}" if expected && (expected < o.class) && (expected != BasicTypes::AnyType) + + xml.tag! name, attrs.merge("xsi:type" => o.class.wsdl_name) do + o.class.full_props_desc.each do |desc| + next unless o.props.member? desc["name"].to_sym + + v = o.props[desc["name"].to_sym] next if v.nil? - obj2xml xml, desc['name'], desc['wsdl_type'], desc['is-array'], v + + obj2xml xml, desc["name"], desc["wsdl_type"], desc["is-array"], v end end - end - when BasicTypes::Enum - xml.tag! name, o.value.to_s, attrs - when Hash - if expected == BasicTypes::KeyValue and is_array - obj2xml xml, name, type, is_array, o.to_a, attrs - else - fail "expected #{expected.wsdl_name} for '#{name}', got a hash" unless expected <= BasicTypes::DataObject - obj2xml xml, name, type, false, expected.new(o), attrs - end - when true, false - fail "expected #{expected.wsdl_name} for '#{name}', got a boolean" unless [BasicTypes::Boolean, BasicTypes::AnyType].member? expected - attrs['xsi:type'] = 'xsd:boolean' if expected == BasicTypes::AnyType - xml.tag! name, (o ? '1' : '0'), attrs - when Symbol, String - if expected == BasicTypes::Binary - attrs['xsi:type'] = 'xsd:base64Binary' if expected == BasicTypes::AnyType - xml.tag! name, [o].pack('m').chomp.gsub("\n", ""), attrs - else - attrs['xsi:type'] = 'xsd:string' if expected == BasicTypes::AnyType + when BasicTypes::Enum + xml.tag! name, o.value.to_s, attrs + when Hash + if (expected == BasicTypes::KeyValue) && is_array + obj2xml xml, name, type, is_array, o.to_a, attrs + else + raise "expected #{expected.wsdl_name} for '#{name}', got a hash" unless expected <= BasicTypes::DataObject + + obj2xml xml, name, type, false, expected.new(o), attrs + end + when true, false + raise "expected #{expected.wsdl_name} for '#{name}', got a boolean" unless [BasicTypes::Boolean, BasicTypes::AnyType].member? expected + + attrs["xsi:type"] = "xsd:boolean" if expected == BasicTypes::AnyType + xml.tag! name, (o ? "1" : "0"), attrs + when Symbol, String + if expected == BasicTypes::Binary + attrs["xsi:type"] = "xsd:base64Binary" if expected == BasicTypes::AnyType + xml.tag! name, [o].pack("m").chomp.gsub("\n", ""), attrs + else + attrs["xsi:type"] = "xsd:string" if expected == BasicTypes::AnyType + xml.tag! name, o.to_s, attrs + end + when Integer + attrs["xsi:type"] = "xsd:long" if expected == BasicTypes::AnyType + xml.tag! name, o.to_s, attrs + when Float + attrs["xsi:type"] = "xsd:double" if expected == BasicTypes::AnyType + xml.tag! name, o.to_s, attrs + when DateTime + attrs["xsi:type"] = "xsd:dateTime" if expected == BasicTypes::AnyType + xml.tag! name, o.strftime("%FT%T%:z"), attrs + when Time + attrs["xsi:type"] = "xsd:dateTime" if expected == BasicTypes::AnyType + xml.tag! name, o.iso8601, attrs + when BasicTypes::Int + attrs["xsi:type"] = "xsd:int" xml.tag! name, o.to_s, attrs + else raise "unexpected object class #{o.class} for '#{name}'" end - when Integer - attrs['xsi:type'] = 'xsd:long' if expected == BasicTypes::AnyType - xml.tag! name, o.to_s, attrs - when Float - attrs['xsi:type'] = 'xsd:double' if expected == BasicTypes::AnyType - xml.tag! name, o.to_s, attrs - when DateTime - attrs['xsi:type'] = 'xsd:dateTime' if expected == BasicTypes::AnyType - xml.tag! name, o.strftime('%FT%T%:z'), attrs - when Time - attrs['xsi:type'] = 'xsd:dateTime' if expected == BasicTypes::AnyType - xml.tag! name, o.iso8601, attrs - when BasicTypes::Int - attrs['xsi:type'] = 'xsd:int' - xml.tag! name, o.to_s, attrs - else fail "unexpected object class #{o.class} for '#{name}'" + xml + rescue StandardError + warn "#{$ERROR_INFO.class} while serializing #{name} (#{type}):" + PP.pp o, $stderr + raise end - xml - rescue - $stderr.puts "#{$!.class} while serializing #{name} (#{type}):" - PP.pp o, $stderr - raise - end - def self.type name - fail unless name and (name.is_a? String or name.is_a? Symbol) - name = $' if name.to_s =~ /^xsd:/ - case name.to_sym - when :anyType then BasicTypes::AnyType - when :boolean then BasicTypes::Boolean - when :string then String - when :int, :long, :short, :byte then Integer - when :float, :double then Float - when :dateTime then Time - when :base64Binary then BasicTypes::Binary - when :KeyValue then BasicTypes::KeyValue - else - first_char = name[0].chr - if first_char.downcase == first_char - name = "%s%s" % [first_char.upcase, name[1..-1]] - end + def self.type(name) + raise unless name && (name.is_a?(String) || name.is_a?(Symbol)) - if @loader.has? name - const_get(name) + name = $' if name.to_s =~ /^xsd:/ + case name.to_sym + when :anyType then BasicTypes::AnyType + when :boolean then BasicTypes::Boolean + when :string then String + when :int, :long, :short, :byte then Integer + when :float, :double then Float + when :dateTime then Time + when :base64Binary then BasicTypes::Binary + when :KeyValue then BasicTypes::KeyValue else - fail "no such type #{name.inspect}" + first_char = name[0].chr + name = "%s%s" % [first_char.upcase, name[1..-1]] if first_char.downcase == first_char + + if @loader.has? name + const_get(name) + else + raise "no such type #{name.inspect}" + end end end - end - - def type name - self.class.type name - end - def instanceUuid - nil - end + def type(name) + self.class.type name + end - def self.extension_dirs - @extension_dirs ||= [] - end + def instanceUuid + nil + end - def self.add_extension_dir dir - extension_dirs << dir - @loader.reload_extensions_dir dir if @loader - end + def self.extension_dirs + @extension_dirs ||= [] + end - def self.reload_extensions - @loader.reload_extensions - end + def self.add_extension_dir(dir) + extension_dirs << dir + @loader.reload_extensions_dir dir if @loader + end - def self.loader; @loader; end + def self.reload_extensions + @loader.reload_extensions + end -protected + class << self + attr_reader :loader + end - def self.const_missing sym - name = sym.to_s - if @loader and @loader.has? name - @loader.get(name) - else - super + def self.const_missing(sym) + name = sym.to_s + if @loader && @loader.has?(name) + @loader.get(name) + else + super + end end - end - def self.method_missing sym, *a - name = sym.to_s - if @loader and @loader.has? name - @loader.get(name).new(*a) - else - super + def self.method_missing(sym, *a) + name = sym.to_s + if @loader && @loader.has?(name) + @loader.get(name).new(*a) + else + super + end end - end - def self.load_vmodl fn - @loader = RbVmomi::TypeLoader.new fn, extension_dirs, self - nil + def self.load_vmodl(fn) + @loader = RbVmomi::TypeLoader.new fn, extension_dirs, self + nil + end end end - -end diff --git a/lib/rbvmomi/deserialization.rb b/lib/rbvmomi/deserialization.rb index 3c87a7f3..7a3549be 100644 --- a/lib/rbvmomi/deserialization.rb +++ b/lib/rbvmomi/deserialization.rb @@ -1,249 +1,245 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'time' +require "time" module RbVmomi - -class NewDeserializer - NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance' - - DEMANGLED_ARRAY_TYPES = { - 'AnyType' => 'xsd:anyType', - 'DateTime' => 'xsd:dateTime', - } - %w(Boolean String Byte Short Int Long Float Double).each do |x| - DEMANGLED_ARRAY_TYPES[x] = "xsd:#{x.downcase}" - end - - BUILTIN_TYPE_ACTIONS = { - 'xsd:string' => :string, - 'xsd:boolean' => :boolean, - 'xsd:byte' => :int, - 'xsd:short' => :int, - 'xsd:int' => :int, - 'xsd:long' => :int, - 'xsd:float' => :float, - 'xsd:dateTime' => :date, - 'PropertyPath' => :string, - 'MethodName' => :string, - 'TypeName' => :string, - 'xsd:base64Binary' => :binary, - 'KeyValue' => :keyvalue, - } - - BUILTIN_TYPE_ACTIONS.dup.each do |k,v| - if k =~ /^xsd:/ - BUILTIN_TYPE_ACTIONS[$'] = v + class NewDeserializer + NS_XSI = "http://www.w3.org/2001/XMLSchema-instance" + + DEMANGLED_ARRAY_TYPES = { + "AnyType" => "xsd:anyType", + "DateTime" => "xsd:dateTime" + }.freeze + %w(Boolean String Byte Short Int Long Float Double).each do |x| + DEMANGLED_ARRAY_TYPES[x] = "xsd:#{x.downcase}" end - end - def initialize conn - @conn = conn - @loader = conn.class.loader - end - - def deserialize node, type=nil - type_attr = node['type'] - - # Work around for 1.5.x which doesn't populate node['type'] - # XXX what changed - if node.attributes['type'] and not type_attr - type_attr = node.attributes['type'].value + BUILTIN_TYPE_ACTIONS = { + "xsd:string" => :string, + "xsd:boolean" => :boolean, + "xsd:byte" => :int, + "xsd:short" => :int, + "xsd:int" => :int, + "xsd:long" => :int, + "xsd:float" => :float, + "xsd:dateTime" => :date, + "PropertyPath" => :string, + "MethodName" => :string, + "TypeName" => :string, + "xsd:base64Binary" => :binary, + "KeyValue" => :keyvalue + }.freeze + + BUILTIN_TYPE_ACTIONS.dup.each do |k, v| + BUILTIN_TYPE_ACTIONS[$'] = v if k =~ /^xsd:/ end - type = type_attr if type_attr - - if action = BUILTIN_TYPE_ACTIONS[type] - case action - when :string - node.content - when :boolean - node.content == '1' || node.content == 'true' - when :int - node.content.to_i - when :float - node.content.to_f - when :date - leaf_date node - when :binary - leaf_binary node - when :keyvalue - leaf_keyvalue node - else fail - end - else - if type =~ /:/ - type = type.split(":", 2)[1] - end - if type =~ /^ArrayOf/ - type = DEMANGLED_ARRAY_TYPES[$'] || $' - return node.children.select(&:element?).map { |c| deserialize c, type } - end - if type =~ /:/ - type = type.split(":", 2)[1] - end + def initialize(conn) + @conn = conn + @loader = conn.class.loader + end - klass = @loader.get(type) or fail "no such type '#{type}'" - case klass.kind - when :data - traverse_data node, klass - when :enum - node.content - when :managed - leaf_managed node, klass - else fail + def deserialize(node, type = nil) + type_attr = node["type"] + + # Work around for 1.5.x which doesn't populate node['type'] + # XXX what changed + type_attr = node.attributes["type"].value if node.attributes["type"] && !type_attr + + type = type_attr if type_attr + + if action = BUILTIN_TYPE_ACTIONS[type] + case action + when :string + node.content + when :boolean + node.content == "1" || node.content == "true" + when :int + node.content.to_i + when :float + node.content.to_f + when :date + leaf_date node + when :binary + leaf_binary node + when :keyvalue + leaf_keyvalue node + else raise + end + else + type = type.split(":", 2)[1] if type =~ /:/ + if type =~ /^ArrayOf/ + type = DEMANGLED_ARRAY_TYPES[$'] || $' + return node.children.select(&:element?).map { |c| deserialize c, type } + end + type = type.split(":", 2)[1] if type =~ /:/ + + klass = @loader.get(type) or raise "no such type '#{type}'" + case klass.kind + when :data + traverse_data(node, klass) + when :enum + node.content + when :managed + leaf_managed(node, klass) + else raise + end end end - end - def traverse_data node, klass - obj = klass.new nil - props = obj.props - children = node.children.select(&:element?) - i = 0 + def traverse_data(node, klass) + obj = klass.new nil + props = obj.props + children = node.children.select(&:element?) + i = 0 - klass.full_props_desc.each do |desc| - name = desc['name'] - child_type = desc['wsdl_type'] + klass.full_props_desc.each do |desc| + name = desc["name"] + child_type = desc["wsdl_type"] - # Ignore unknown fields - while child = children[i] and not klass.full_props_set.member? child.name - i += 1 - end + # Ignore unknown fields + while (child = children[i]) && (!klass.full_props_set.member? child.name) + i += 1 + end - if desc['is-array'] - a = [] - while ((child = children[i]) && (child.name == name)) - child = children[i] - a << deserialize(child, child_type) + if desc["is-array"] + a = [] + while (child = children[i]) && (child.name == name) + child = children[i] + a << deserialize(child, child_type) + i += 1 + end + props[name.to_sym] = a + elsif (child = children[i]) && (child.name == name) + props[name.to_sym] = deserialize(child, child_type) i += 1 end - props[name.to_sym] = a - elsif ((child = children[i]) && (child.name == name)) - props[name.to_sym] = deserialize(child, child_type) - i += 1 end + + obj end - obj - end + def leaf_managed(node, klass) + type_attr = node["type"] + klass = @loader.get(type_attr) if type_attr + klass.new(@conn, node.content) + end - def leaf_managed node, klass - type_attr = node['type'] - klass = @loader.get(type_attr) if type_attr - klass.new(@conn, node.content) - end + def leaf_date(node) + Time.parse node.content + end - def leaf_date node - Time.parse node.content - end + def leaf_binary(node) + node.content.unpack1("m") + end - def leaf_binary node - node.content.unpack('m')[0] - end + # XXX does the value need to be deserialized? + def leaf_keyvalue(node) + h = {} + node.children.each do |child| + next unless child.element? - # XXX does the value need to be deserialized? - def leaf_keyvalue node - h = {} - node.children.each do |child| - next unless child.element? - h[child.name] = child.content + h[child.name] = child.content + end + [h["key"], h["value"]] end - [h['key'], h['value']] end -end -class OldDeserializer - NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance' - - def initialize conn - @conn = conn - end + class OldDeserializer + NS_XSI = "http://www.w3.org/2001/XMLSchema-instance" - def deserialize xml, typename=nil - if IS_JRUBY - type_attr = xml.attribute_nodes.find { |a| a.name == 'type' && - a.namespace && - a.namespace.prefix == 'xsi' } - else - type_attr = xml.attribute_with_ns('type', NS_XSI) + def initialize(conn) + @conn = conn end - typename = (type_attr || typename).to_s - if typename =~ /^ArrayOf/ - typename = demangle_array_type $' - return xml.children.select(&:element?).map { |c| deserialize c, typename } - end + def deserialize(xml, typename = nil) + type_attr = if IS_JRUBY + xml.attribute_nodes.find do |a| + a.name == "type" && + a.namespace && + a.namespace.prefix == "xsi" + end + else + xml.attribute_with_ns("type", NS_XSI) + end + typename = (type_attr || typename).to_s + + if typename =~ /^ArrayOf/ + typename = demangle_array_type $' + return xml.children.select(&:element?).map { |c| deserialize c, typename } + end - t = @conn.type typename - if t <= BasicTypes::DataObject - props_desc = t.full_props_desc - h = {} - props_desc.select { |d| d['is-array'] }.each { |d| h[d['name'].to_sym] = [] } - xml.children.each do |c| - next unless c.element? - field = c.name.to_sym - d = t.find_prop_desc(field.to_s) or next - o = deserialize c, d['wsdl_type'] - if h[field].is_a? Array - h[field] << o - else - h[field] = o + t = @conn.type(typename) + if t <= BasicTypes::DataObject + props_desc = t.full_props_desc + h = {} + props_desc.select { |d| d["is-array"] }.each { |d| h[d["name"].to_sym] = [] } + xml.children.each do |c| + next unless c.element? + + field = c.name.to_sym + d = t.find_prop_desc(field.to_s) or next + o = deserialize c, d["wsdl_type"] + if h[field].is_a? Array + h[field] << o + else + h[field] = o + end end + t.new h + elsif t == BasicTypes::ManagedObjectReference + @conn.type(xml["type"]).new @conn, xml.text + elsif t <= BasicTypes::ManagedObject + @conn.type(xml["type"] || t.wsdl_name).new @conn, xml.text + elsif t <= BasicTypes::Enum + xml.text + elsif t <= BasicTypes::KeyValue + h = {} + xml.children.each do |c| + next unless c.element? + + h[c.name] = c.text + end + [h["key"], h["value"]] + elsif t <= String + xml.text + elsif t <= Symbol + xml.text.to_sym + elsif t <= Integer + xml.text.to_i + elsif t <= Float + xml.text.to_f + elsif t <= Time + Time.parse xml.text + elsif t == BasicTypes::Boolean + xml.text == "true" || xml.text == "1" + elsif t == BasicTypes::Binary + xml.text.unpack1("m") + elsif t == BasicTypes::AnyType + raise "attempted to deserialize an AnyType" + else raise "unexpected type #{t.inspect} (#{t.ancestors * '/'})" end - t.new h - elsif t == BasicTypes::ManagedObjectReference - @conn.type(xml['type']).new @conn, xml.text - elsif t <= BasicTypes::ManagedObject - @conn.type(xml['type'] || t.wsdl_name).new @conn, xml.text - elsif t <= BasicTypes::Enum - xml.text - elsif t <= BasicTypes::KeyValue - h = {} - xml.children.each do |c| - next unless c.element? - h[c.name] = c.text - end - [h['key'], h['value']] - elsif t <= String - xml.text - elsif t <= Symbol - xml.text.to_sym - elsif t <= Integer - xml.text.to_i - elsif t <= Float - xml.text.to_f - elsif t <= Time - Time.parse xml.text - elsif t == BasicTypes::Boolean - xml.text == 'true' || xml.text == '1' - elsif t == BasicTypes::Binary - xml.text.unpack('m')[0] - elsif t == BasicTypes::AnyType - fail "attempted to deserialize an AnyType" - else fail "unexpected type #{t.inspect} (#{t.ancestors * '/'})" + rescue StandardError + warn "#{$ERROR_INFO.class} while deserializing #{xml.name} (#{typename}):" + warn xml.to_s + raise end - rescue - $stderr.puts "#{$!.class} while deserializing #{xml.name} (#{typename}):" - $stderr.puts xml.to_s - raise - end - def demangle_array_type x - case x - when 'AnyType' then 'anyType' - when 'DateTime' then 'dateTime' - when 'Boolean', 'String', 'Byte', 'Short', 'Int', 'Long', 'Float', 'Double' then x.downcase - else x + def demangle_array_type(x) + case x + when "AnyType" then "anyType" + when "DateTime" then "dateTime" + when "Boolean", "String", "Byte", "Short", "Int", "Long", "Float", "Double" then x.downcase + else x + end end end -end - -if ENV['RBVMOMI_NEW_DESERIALIZER'] == '1' || true # Always use new one now - Deserializer = NewDeserializer -else - Deserializer = OldDeserializer -end + Deserializer = if ENV["RBVMOMI_NEW_DESERIALIZER"] == "1" || true # Always use new one now + NewDeserializer + else + OldDeserializer + end end diff --git a/lib/rbvmomi/fault.rb b/lib/rbvmomi/fault.rb index 2a87242b..8452de4b 100644 --- a/lib/rbvmomi/fault.rb +++ b/lib/rbvmomi/fault.rb @@ -1,19 +1,18 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT module RbVmomi + class Fault < StandardError + attr_reader :fault -class Fault < StandardError - attr_reader :fault + def initialize msg, fault + super "#{fault.class.wsdl_name}: #{msg}" + @fault = fault + end - def initialize msg, fault - super "#{fault.class.wsdl_name}: #{msg}" - @fault = fault + def method_missing *a + @fault.send(*a) + end end - - def method_missing *a - @fault.send(*a) - end -end - end diff --git a/lib/rbvmomi/optimist.rb b/lib/rbvmomi/optimist.rb index f3701cf2..94c33bb3 100644 --- a/lib/rbvmomi/optimist.rb +++ b/lib/rbvmomi/optimist.rb @@ -1,72 +1,72 @@ +# frozen_string_literal: true # Copyright (c) 2010-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'optimist' +require "optimist" # Convenience methods for Optimist, Ruby's premier option parser. # @see http://optimist.rubyforge.org/ # @see Optimist::Parser module Optimist - -# Convenience methods for Optimist, Ruby's premier option parser. -# -# See the examples directory for sample code. -# Descriptions are of the form: -# : () -# @see http://optimist.rubyforge.org/ -class Parser - # Options used by VIM.connect + # Convenience methods for Optimist, Ruby's premier option parser. # - # !!!plain - # host: -o --host RBVMOMI_HOST - # port: --port RBVMOMI_PORT (443) - # no-ssl: --no-ssl RBVMOMI_SSL (false) - # insecure: -k --insecure RBVMOMI_INSECURE (false) - # user: -u --user RBVMOMI_USER (root) - # password: -p --password RBVMOMI_PASSWORD () - # path: --path RBVMOMI_PATH (/sdk) - # debug: -d --debug RBVMOMI_DEBUG (false) - def rbvmomi_connection_opts - opt :host, "host", :type => :string, :short => 'o', :default => ENV['RBVMOMI_HOST'] - opt :port, "port", :type => :int, :short => :none, :default => (ENV.member?('RBVMOMI_PORT') ? ENV['RBVMOMI_PORT'].to_i : 443) - opt :"no-ssl", "don't use ssl", :short => :none, :default => (ENV['RBVMOMI_SSL'] == '0') - opt :insecure, "don't verify ssl certificate", :short => 'k', :default => (ENV['RBVMOMI_INSECURE'] == '1') - opt :user, "username", :short => 'u', :default => (ENV['RBVMOMI_USER'] || 'root') - opt :password, "password", :short => 'p', :default => (ENV['RBVMOMI_PASSWORD'] || '') - opt :path, "SOAP endpoint path", :short => :none, :default => (ENV['RBVMOMI_PATH'] || '/sdk') - opt :debug, "Log SOAP messages", :short => 'd', :default => (ENV['RBVMOMI_DEBUG'] || false) - end + # See the examples directory for sample code. + # Descriptions are of the form: + # : () + # @see http://optimist.rubyforge.org/ + class Parser + # Options used by VIM.connect + # + # !!!plain + # host: -o --host RBVMOMI_HOST + # port: --port RBVMOMI_PORT (443) + # no-ssl: --no-ssl RBVMOMI_SSL (false) + # insecure: -k --insecure RBVMOMI_INSECURE (false) + # user: -u --user RBVMOMI_USER (root) + # password: -p --password RBVMOMI_PASSWORD () + # path: --path RBVMOMI_PATH (/sdk) + # debug: -d --debug RBVMOMI_DEBUG (false) + def rbvmomi_connection_opts + opt :host, "host", type: :string, short: "o", default: ENV["RBVMOMI_HOST"] + opt :port, "port", type: :int, short: :none, default: (ENV.member?("RBVMOMI_PORT") ? ENV["RBVMOMI_PORT"].to_i : 443) + opt :"no-ssl", "don't use ssl", short: :none, default: (ENV["RBVMOMI_SSL"] == "0") + opt :insecure, "don't verify ssl certificate", short: "k", default: (ENV["RBVMOMI_INSECURE"] == "1") + opt :user, "username", short: "u", default: (ENV["RBVMOMI_USER"] || "root") + opt :password, "password", short: "p", default: (ENV["RBVMOMI_PASSWORD"] || "") + opt :path, "SOAP endpoint path", short: :none, default: (ENV["RBVMOMI_PATH"] || "/sdk") + opt :debug, "Log SOAP messages", short: "d", default: (ENV["RBVMOMI_DEBUG"] || false) + end - # Select a datacenter - # - # !!!plain - # datacenter: -D --datacenter RBVMOMI_DATACENTER (ha-datacenter) - def rbvmomi_datacenter_opt - opt :datacenter, "datacenter", :type => :string, :short => "D", :default => (ENV['RBVMOMI_DATACENTER'] || 'ha-datacenter') - end + # Select a datacenter + # + # !!!plain + # datacenter: -D --datacenter RBVMOMI_DATACENTER (ha-datacenter) + def rbvmomi_datacenter_opt + opt :datacenter, "datacenter", type: :string, short: "D", default: (ENV["RBVMOMI_DATACENTER"] || "ha-datacenter") + end - # Select a folder - # - # !!!plain - # folder: -F --folder RBVMOMI_FOLDER () - def rbvmomi_folder_opt - opt :folder, "VM folder", :type => :string, :short => "F", :default => (ENV['RBVMOMI_FOLDER'] || '') - end + # Select a folder + # + # !!!plain + # folder: -F --folder RBVMOMI_FOLDER () + def rbvmomi_folder_opt + opt :folder, "VM folder", type: :string, short: "F", default: (ENV["RBVMOMI_FOLDER"] || "") + end - # Select a compute resource - # - # !!!plain - # computer: -R --computer RBVMOMI_COMPUTER - def rbvmomi_computer_opt - opt :computer, "Compute resource", :type => :string, :short => "R", :default => (ENV['RBVMOMI_COMPUTER']||'ha-compute-res') - end + # Select a compute resource + # + # !!!plain + # computer: -R --computer RBVMOMI_COMPUTER + def rbvmomi_computer_opt + opt :computer, "Compute resource", type: :string, short: "R", default: (ENV["RBVMOMI_COMPUTER"] || "ha-compute-res") + end - # Select a datastore - # - # !!!plain - # datastore: -s --datastore RBVMOMI_DATASTORE (datastore1) - def rbvmomi_datastore_opt - opt :datastore, "Datastore", :short => 's', :default => (ENV['RBVMOMI_DATASTORE'] || 'datastore1') + # Select a datastore + # + # !!!plain + # datastore: -s --datastore RBVMOMI_DATASTORE (datastore1) + def rbvmomi_datastore_opt + opt :datastore, "Datastore", short: "s", default: (ENV["RBVMOMI_DATASTORE"] || "datastore1") + end end end -end diff --git a/lib/rbvmomi/pbm.rb b/lib/rbvmomi/pbm.rb index 2b777e18..190fbafd 100644 --- a/lib/rbvmomi/pbm.rb +++ b/lib/rbvmomi/pbm.rb @@ -1,68 +1,72 @@ +# frozen_string_literal: true # Copyright (c) 2012-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require_relative '../rbvmomi' +require_relative "../rbvmomi" module RbVmomi + # A connection to one vSphere ProfileBasedManagement endpoint. + # @see #serviceInstance + class PBM < Connection + # Connect to a vSphere ProfileBasedManagement endpoint + # + # @param [VIM] Connection to main vSphere API endpoint + # @param [Hash] opts The options hash. + # @option opts [String] :host Host to connect to. + # @option opts [Numeric] :port (443) Port to connect to. + # @option opts [Boolean] :ssl (true) Whether to use SSL. + # @option opts [Boolean] :insecure (false) If true, ignore SSL certificate errors. + # @option opts [String] :path (/pbm/sdk) SDK endpoint path. + # @option opts [Boolean] :debug (false) If true, print SOAP traffic to stderr. + def self.connect vim, opts = {} + raise unless opts.is_a? Hash -# A connection to one vSphere ProfileBasedManagement endpoint. -# @see #serviceInstance -class PBM < Connection - # Connect to a vSphere ProfileBasedManagement endpoint - # - # @param [VIM] Connection to main vSphere API endpoint - # @param [Hash] opts The options hash. - # @option opts [String] :host Host to connect to. - # @option opts [Numeric] :port (443) Port to connect to. - # @option opts [Boolean] :ssl (true) Whether to use SSL. - # @option opts [Boolean] :insecure (false) If true, ignore SSL certificate errors. - # @option opts [String] :path (/pbm/sdk) SDK endpoint path. - # @option opts [Boolean] :debug (false) If true, print SOAP traffic to stderr. - def self.connect vim, opts = {} - fail unless opts.is_a? Hash - opts[:host] = vim.host - opts[:ssl] = true unless opts.member? :ssl or opts[:"no-ssl"] - opts[:insecure] ||= false - opts[:port] ||= (opts[:ssl] ? 443 : 80) - opts[:path] ||= '/pbm/sdk' - opts[:ns] ||= 'urn:pbm' - rev_given = opts[:rev] != nil - opts[:rev] = '1.0' unless rev_given - opts[:debug] = (!ENV['RBVMOMI_DEBUG'].empty? rescue false) unless opts.member? :debug + opts[:host] = vim.host + opts[:ssl] = true unless opts.member?(:ssl) || opts[:"no-ssl"] + opts[:insecure] ||= false + opts[:port] ||= (opts[:ssl] ? 443 : 80) + opts[:path] ||= "/pbm/sdk" + opts[:ns] ||= "urn:pbm" + rev_given = !opts[:rev].nil? + opts[:rev] = "1.0" unless rev_given + unless opts.member? :debug + opts[:debug] = begin + !ENV["RBVMOMI_DEBUG"].empty? + rescue StandardError + false + end + end - new(opts).tap do |pbm| - pbm.vcSessionCookie = vim.cookie.split('"')[1] + new(opts).tap do |pbm| + pbm.vcSessionCookie = vim.cookie.split('"')[1] + end end - end - - def vcSessionCookie= cookie - @vcSessionCookie = cookie - end - def rev= x - super - @serviceContent = nil - end + attr_writer :vcSessionCookie - # Return the ServiceInstance - # - # The ServiceInstance is the root of the vSphere inventory. - def serviceInstance - @serviceInstance ||= VIM::PbmServiceInstance self, 'ServiceInstance' - end + def rev= x + super + @serviceContent = nil + end - # Alias to serviceInstance.PbmRetrieveServiceContent - def serviceContent - @serviceContent ||= serviceInstance.PbmRetrieveServiceContent - end + # Return the ServiceInstance + # + # The ServiceInstance is the root of the vSphere inventory. + def service_instance + @serviceInstance ||= VIM::PbmServiceInstance self, "ServiceInstance" + end - # @private - def pretty_print pp - pp.text "PBM(#{@opts[:host]})" - end + # Alias to serviceInstance.PbmRetrieveServiceContent + def service_content + @serviceContent ||= service_instance.PbmRetrieveServiceContent + end - add_extension_dir File.join(File.dirname(__FILE__), "pbm") - load_vmodl(ENV['VMODL'] || File.join(File.dirname(__FILE__), "../../vmodl.db")) -end + # @private + def pretty_print pp + pp.text "PBM(#{@opts[:host]})" + end + add_extension_dir File.join(File.dirname(__FILE__), "pbm") + load_vmodl(ENV["VMODL"] || File.join(File.dirname(__FILE__), "../../vmodl.db")) + end end diff --git a/lib/rbvmomi/sms.rb b/lib/rbvmomi/sms.rb index 2378f819..9e038da9 100644 --- a/lib/rbvmomi/sms.rb +++ b/lib/rbvmomi/sms.rb @@ -1,63 +1,66 @@ +# frozen_string_literal: true # Copyright (c) 2013-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require_relative '../rbvmomi' +require_relative "../rbvmomi" module RbVmomi + # A connection to one vSphere SMS endpoint. + # @see #serviceInstance + class SMS < Connection + # Connect to a vSphere SMS endpoint + # + # @param [VIM] Connection to main vSphere API endpoint + # @param [Hash] opts The options hash. + # @option opts [String] :host Host to connect to. + # @option opts [Numeric] :port (443) Port to connect to. + # @option opts [Boolean] :ssl (true) Whether to use SSL. + # @option opts [Boolean] :insecure (false) If true, ignore SSL certificate errors. + # @option opts [String] :path (/sms/sdk) SDK endpoint path. + # @option opts [Boolean] :debug (false) If true, print SOAP traffic to stderr. + def self.connect vim, opts = {} + raise unless opts.is_a? Hash -# A connection to one vSphere SMS endpoint. -# @see #serviceInstance -class SMS < Connection - # Connect to a vSphere SMS endpoint - # - # @param [VIM] Connection to main vSphere API endpoint - # @param [Hash] opts The options hash. - # @option opts [String] :host Host to connect to. - # @option opts [Numeric] :port (443) Port to connect to. - # @option opts [Boolean] :ssl (true) Whether to use SSL. - # @option opts [Boolean] :insecure (false) If true, ignore SSL certificate errors. - # @option opts [String] :path (/sms/sdk) SDK endpoint path. - # @option opts [Boolean] :debug (false) If true, print SOAP traffic to stderr. - def self.connect vim, opts = {} - fail unless opts.is_a? Hash - opts[:host] = vim.host - opts[:ssl] = true unless opts.member? :ssl or opts[:"no-ssl"] - opts[:insecure] ||= true - opts[:port] ||= (opts[:ssl] ? 443 : 80) - opts[:path] ||= '/sms/sdk' - opts[:ns] ||= 'urn:sms' - rev_given = opts[:rev] != nil - opts[:rev] = '4.0' unless rev_given - opts[:debug] = (!ENV['RBVMOMI_DEBUG'].empty? rescue false) unless opts.member? :debug - - new(opts).tap do |sms| - sms.vcSessionCookie = vim.cookie.split('"')[1] + opts[:host] = vim.host + opts[:ssl] = true unless opts.member?(:ssl) || opts[:"no-ssl"] + opts[:insecure] ||= true + opts[:port] ||= (opts[:ssl] ? 443 : 80) + opts[:path] ||= "/sms/sdk" + opts[:ns] ||= "urn:sms" + rev_given = !opts[:rev].nil? + opts[:rev] = "4.0" unless rev_given + unless opts.member? :debug + opts[:debug] = begin + !ENV["RBVMOMI_DEBUG"].empty? + rescue StandardError + false + end + end + + new(opts).tap do |sms| + sms.vcSessionCookie = vim.cookie.split('"')[1] + end end - end - def vcSessionCookie= cookie - @vcSessionCookie = cookie - end + attr_writer :vcSessionCookie - def rev= x - super - @serviceContent = nil - end - - # Return the ServiceInstance - # - # The ServiceInstance is the root of the vSphere inventory. - def serviceInstance - @serviceInstance ||= VIM::SmsServiceInstance self, 'ServiceInstance' - end + def rev= x + super + @serviceContent = nil + end - # @private - def pretty_print pp - pp.text "SMS(#{@opts[:host]})" - end + # Return the ServiceInstance + # + # The ServiceInstance is the root of the vSphere inventory. + def serviceInstance + @serviceInstance ||= VIM::SmsServiceInstance self, "ServiceInstance" + end - add_extension_dir File.join(File.dirname(__FILE__), "sms") - load_vmodl(ENV['VMODL'] || File.join(File.dirname(__FILE__), "../../vmodl.db")) -end + # @private + def pretty_print pp + pp.text "SMS(#{@opts[:host]})" + end + add_extension_dir File.join(File.dirname(__FILE__), "sms") + load_vmodl(ENV["VMODL"] || File.join(File.dirname(__FILE__), "../../vmodl.db")) + end end - diff --git a/lib/rbvmomi/sms/SmsStorageManager.rb b/lib/rbvmomi/sms/SmsStorageManager.rb index 2c460c56..f8029b14 100644 --- a/lib/rbvmomi/sms/SmsStorageManager.rb +++ b/lib/rbvmomi/sms/SmsStorageManager.rb @@ -1,10 +1,13 @@ +# frozen_string_literal: true # Copyright (c) 2013-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::SMS::SmsStorageManager - - def RegisterProvider_Task2 providerSpec - self.RegisterProvider_Task providerSpec +module RbVmomi + module SMS + class SmsStorageManager + def RegisterProvider_Task2 providerSpec + self.RegisterProvider_Task providerSpec + end + end end - end diff --git a/lib/rbvmomi/sso.rb b/lib/rbvmomi/sso.rb index 14070d85..445a292e 100644 --- a/lib/rbvmomi/sso.rb +++ b/lib/rbvmomi/sso.rb @@ -1,29 +1,30 @@ -require 'base64' -require 'net/https' -require 'nokogiri' -require 'openssl' -require 'securerandom' -require 'time' +# frozen_string_literal: true +require "base64" +require "net/https" +require "nokogiri" +require "openssl" +require "securerandom" +require "time" module RbVmomi # Provides access to vCenter Single Sign-On class SSO - BST_PROFILE = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'.freeze + BST_PROFILE = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" C14N_CLASS = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0 - C14N_METHOD = 'http://www.w3.org/2001/10/xml-exc-c14n#'.freeze - DIGEST_METHOD = 'http://www.w3.org/2001/04/xmlenc#sha512'.freeze - ENCODING_METHOD = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary'.freeze - SIGNATURE_METHOD = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'.freeze - STS_PATH = '/sts/STSService'.freeze - TOKEN_TYPE = 'urn:oasis:names:tc:SAML:2.0:assertion'.freeze - TOKEN_PROFILE = 'http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0'.freeze + C14N_METHOD = "http://www.w3.org/2001/10/xml-exc-c14n#" + DIGEST_METHOD = "http://www.w3.org/2001/04/xmlenc#sha512" + ENCODING_METHOD = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" + SIGNATURE_METHOD = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512" + STS_PATH = "/sts/STSService" + TOKEN_TYPE = "urn:oasis:names:tc:SAML:2.0:assertion" + TOKEN_PROFILE = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0" NAMESPACES = { - :ds => 'http://www.w3.org/2000/09/xmldsig#', - :soap => 'http://schemas.xmlsoap.org/soap/envelope/', - :wsse => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd', - :wsse11 => 'http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd', - :wst => 'http://docs.oasis-open.org/ws-sx/ws-trust/200512', - :wsu => 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' + ds: "http://www.w3.org/2000/09/xmldsig#", + soap: "http://schemas.xmlsoap.org/soap/envelope/", + wsse: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", + wsse11: "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd", + wst: "http://docs.oasis-open.org/ws-sx/ws-trust/200512", + wsu: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" }.freeze attr_reader :assertion, @@ -64,15 +65,15 @@ def request_token unless req.is_a?(Net::HTTPSuccess) resp = Nokogiri::XML(req.body) resp.remove_namespaces! - raise(resp.at_xpath('//Envelope/Body/Fault/faultstring/text()')) + raise(resp.at_xpath("//Envelope/Body/Fault/faultstring/text()")) end extract_assertion(req.body) end def sign_request(request) - raise('Need SAML2 assertion') unless @assertion - raise('No SAML2 assertion ID') unless @assertion_id + raise("Need SAML2 assertion") unless @assertion + raise("No SAML2 assertion ID") unless @assertion_id request_id = generate_id timestamp_id = generate_id @@ -83,10 +84,10 @@ def sign_request(request) xml[:wsse].Security do wsu_timestamp(xml, timestamp_id) ds_signature(xml, request_id, timestamp_id) do |x| - x[:wsse].SecurityTokenReference('wsse11:TokenType' => TOKEN_PROFILE) do + x[:wsse].SecurityTokenReference("wsse11:TokenType" => TOKEN_PROFILE) do x[:wsse].KeyIdentifier( @assertion_id, - 'ValueType' => 'http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID' + "ValueType" => "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLID" ) end end @@ -96,36 +97,36 @@ def sign_request(request) # To avoid Nokogiri mangling the token, we replace it as a string # later on. Figure out a way around this. - builder.doc.at_xpath('//soap:Header/wsse:Security/wsu:Timestamp').add_previous_sibling(Nokogiri::XML::Text.new('SAML_ASSERTION_PLACEHOLDER', builder.doc)) + builder.doc.at_xpath("//soap:Header/wsse:Security/wsu:Timestamp").add_previous_sibling(Nokogiri::XML::Text.new("SAML_ASSERTION_PLACEHOLDER", builder.doc)) - request.at_xpath('//soap:Envelope', NAMESPACES).tap do |e| + request.at_xpath("//soap:Envelope", NAMESPACES).tap do |e| NAMESPACES.each do |ns, uri| e.add_namespace(ns.to_s, uri) end end - request.xpath('//soap:Envelope/soap:Body').each do |body| + request.xpath("//soap:Envelope/soap:Body").each do |body| body.add_previous_sibling(builder.doc.root) - body.add_namespace('wsu', NAMESPACES[:wsu]) - body['wsu:Id'] = request_id + body.add_namespace("wsu", NAMESPACES[:wsu]) + body["wsu:Id"] = request_id end signed = sign(request) - signed.gsub!('SAML_ASSERTION_PLACEHOLDER', @assertion.to_xml(:indent => 0, :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML).strip) + signed.gsub!("SAML_ASSERTION_PLACEHOLDER", @assertion.to_xml(indent: 0, save_with: Nokogiri::XML::Node::SaveOptions::AS_XML).strip) signed end # We default to Issue, since that's all we currently need. def sso_call(body) - sso_url = URI::HTTPS.build(:host => @host, :port => @port, :path => @path) + sso_url = URI::HTTPS.build(host: @host, port: @port, path: @path) http = Net::HTTP.new(sso_url.host, sso_url.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @insecure req = Net::HTTP::Post.new(sso_url.request_uri) - req.add_field('Accept', 'text/xml, multipart/related') - req.add_field('User-Agent', "VMware/RbVmomi #{RbVmomi::VERSION}") - req.add_field('SOAPAction', 'http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue') + req.add_field("Accept", "text/xml, multipart/related") + req.add_field("User-Agent", "VMware/RbVmomi #{RbVmomi::VERSION}") + req.add_field("SOAPAction", "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue") req.content_type = 'text/xml; charset="UTF-8"' req.body = body @@ -154,27 +155,27 @@ def hok_token_request ds_signature(xml, request_id, timestamp_id, signature_id) do |x| x[:wsse].SecurityTokenReference do x[:wsse].Reference( - 'URI' => "##{security_token_id}", - 'ValueType' => BST_PROFILE + "URI" => "##{security_token_id}", + "ValueType" => BST_PROFILE ) end end end end - xml[:soap].Body('wsu:Id' => request_id) do + xml[:soap].Body("wsu:Id" => request_id) do xml[:wst].RequestSecurityToken do xml[:wst].TokenType(TOKEN_TYPE) - xml[:wst].RequestType('http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue') + xml[:wst].RequestType("http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue") xml[:wst].Lifetime do xml[:wsu].Created(created_at) xml[:wsu].Expires(token_expires_at) end - xml[:wst].Renewing('Allow' => 'false', 'OK' => 'false') - xml[:wst].KeyType('http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey') + xml[:wst].Renewing("Allow" => "false", "OK" => "false") + xml[:wst].KeyType("http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey") xml[:wst].SignatureAlgorithm(SIGNATURE_METHOD) - xml[:wst].Delegatable('false') + xml[:wst].Delegatable("false") end - xml[:wst].UseKey('Sig' => signature_id) + xml[:wst].UseKey("Sig" => signature_id) end end end @@ -189,14 +190,14 @@ def extract_assertion(sso_response) # Doesn't matter that usually there's more than one NS with the same # URI - either will work for XPath. We just don't want to hardcode # xmlns:saml2. - token_ns = namespaces.find { |_, uri| uri == TOKEN_TYPE }.first.gsub(/^xmlns:/, '') + token_ns = namespaces.find { |_, uri| uri == TOKEN_TYPE }.first.gsub(/^xmlns:/, "") @assertion = sso_response.at_xpath("//#{token_ns}:Assertion", namespaces) @assertion_id = @assertion.at_xpath("//#{token_ns}:Assertion/@ID", namespaces).value end def sign(doc) - signature_digest_references = doc.xpath('/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignedInfo/ds:Reference/@URI', doc.collect_namespaces).map { |a| a.value.sub(/^#/, '') } + signature_digest_references = doc.xpath("/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignedInfo/ds:Reference/@URI", doc.collect_namespaces).map { |a| a.value.sub(/^#/, "") } signature_digest_references.each do |ref| data = doc.at_xpath("//*[@wsu:Id='#{ref}']", doc.collect_namespaces) digest = Base64.strict_encode64(Digest::SHA2.new(512).digest(data.canonicalize(C14N_CLASS))) @@ -204,28 +205,22 @@ def sign(doc) digest_tag.add_child(Nokogiri::XML::Text.new(digest, doc)) end - signed_info = doc.at_xpath('/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignedInfo', doc.collect_namespaces) + signed_info = doc.at_xpath("/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignedInfo", doc.collect_namespaces) signature = Base64.strict_encode64(@private_key.sign(OpenSSL::Digest::SHA512.new, signed_info.canonicalize(C14N_CLASS))) - signature_value_tag = doc.at_xpath('/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignatureValue', doc.collect_namespaces) + signature_value_tag = doc.at_xpath("/soap:Envelope/soap:Header/wsse:Security/ds:Signature/ds:SignatureValue", doc.collect_namespaces) signature_value_tag.add_child(Nokogiri::XML::Text.new(signature, doc)) - doc.to_xml(:indent => 0, :save_with => Nokogiri::XML::Node::SaveOptions::AS_XML).strip + doc.to_xml(indent: 0, save_with: Nokogiri::XML::Node::SaveOptions::AS_XML).strip end def load_x509(private_key, certificate) - @private_key = private_key ? private_key : OpenSSL::PKey::RSA.new(2048) - if @private_key.is_a? String - @private_key = OpenSSL::PKey::RSA.new(@private_key) - end + @private_key = private_key || OpenSSL::PKey::RSA.new(2048) + @private_key = OpenSSL::PKey::RSA.new(@private_key) if @private_key.is_a? String @certificate = certificate - if @certificate && !private_key - raise(ArgumentError, "Can't generate private key from a certificate") - end + raise(ArgumentError, "Can't generate private key from a certificate") if @certificate && !private_key - if @certificate.is_a? String - @certificate = OpenSSL::X509::Certificate.new(@certificate) - end + @certificate = OpenSSL::X509::Certificate.new(@certificate) if @certificate.is_a? String # If only a private key is specified, we will generate a certificate. unless @certificate timestamp = Time.now.utc @@ -233,8 +228,8 @@ def load_x509(private_key, certificate) @certificate.not_before = timestamp @certificate.not_after = timestamp + 3600 # 3600 is 1 hour @certificate.subject = OpenSSL::X509::Name.new([ - %w[O VMware], - %w[OU RbVmomi], + %w(O VMware), + %w(OU RbVmomi), %W[CN #{@user}] ]) @certificate.issuer = @certificate.subject @@ -248,7 +243,7 @@ def load_x509(private_key, certificate) def ds_signature(xml, request_id, timestamp_id, id = nil) signature_id = {} - signature_id['Id'] = id if id + signature_id["Id"] = id if id xml[:ds].Signature(signature_id) do ds_signed_info(xml, request_id, timestamp_id) xml[:ds].SignatureValue @@ -260,20 +255,20 @@ def ds_signature(xml, request_id, timestamp_id, id = nil) def ds_signed_info(xml, request_id, timestamp_id) xml[:ds].SignedInfo do - xml[:ds].CanonicalizationMethod('Algorithm' => C14N_METHOD) - xml[:ds].SignatureMethod('Algorithm' => SIGNATURE_METHOD) - xml[:ds].Reference('URI' => "##{request_id}") do + xml[:ds].CanonicalizationMethod("Algorithm" => C14N_METHOD) + xml[:ds].SignatureMethod("Algorithm" => SIGNATURE_METHOD) + xml[:ds].Reference("URI" => "##{request_id}") do xml[:ds].Transforms do - xml[:ds].Transform('Algorithm' => C14N_METHOD) + xml[:ds].Transform("Algorithm" => C14N_METHOD) end - xml[:ds].DigestMethod('Algorithm' => DIGEST_METHOD) + xml[:ds].DigestMethod("Algorithm" => DIGEST_METHOD) xml[:ds].DigestValue end - xml[:ds].Reference('URI' => "##{timestamp_id}") do + xml[:ds].Reference("URI" => "##{timestamp_id}") do xml[:ds].Transforms do - xml[:ds].Transform('Algorithm' => C14N_METHOD) + xml[:ds].Transform("Algorithm" => C14N_METHOD) end - xml[:ds].DigestMethod('Algorithm' => DIGEST_METHOD) + xml[:ds].DigestMethod("Algorithm" => DIGEST_METHOD) xml[:ds].DigestValue end end @@ -284,7 +279,7 @@ def wsu_timestamp(xml, id, datum = nil) created_at = datum.iso8601 expires_at = (datum + 600).iso8601 - xml[:wsu].Timestamp('wsu:Id' => id) do + xml[:wsu].Timestamp("wsu:Id" => id) do xml[:wsu].Created(created_at) xml[:wsu].Expires(expires_at) end @@ -300,9 +295,9 @@ def wsse_username_token(xml) def wsse_binary_security_token(xml, id) xml[:wsse].BinarySecurityToken( Base64.strict_encode64(@certificate.to_der), - 'EncodingType' => ENCODING_METHOD, - 'ValueType' => BST_PROFILE, - 'wsu:Id' => id + "EncodingType" => ENCODING_METHOD, + "ValueType" => BST_PROFILE, + "wsu:Id" => id ) end diff --git a/lib/rbvmomi/trivial_soap.rb b/lib/rbvmomi/trivial_soap.rb index 3033e47a..1b42ccf9 100644 --- a/lib/rbvmomi/trivial_soap.rb +++ b/lib/rbvmomi/trivial_soap.rb @@ -1,122 +1,127 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'rubygems' -require 'builder' -require 'nokogiri' -require 'net/http' -require 'pp' - -class RbVmomi::TrivialSoap - attr_accessor :debug, :cookie, :operation_id - attr_reader :http - - def initialize opts - fail unless opts.is_a? Hash - @opts = opts - return unless @opts[:host] # for testcases - @debug = @opts[:debug] - @cookie = @opts[:cookie] - @sso = @opts[:sso] - @operation_id = @opts[:operation_id] - @lock = Mutex.new - @http = nil - restart_http - end - - def host - @opts[:host] - end - - def close - @http.finish rescue IOError - end +require "rubygems" +require "builder" +require "nokogiri" +require "net/http" +require "pp" + +module RbVmomi + class TrivialSoap + attr_accessor :debug, :cookie, :operation_id + attr_reader :http + + def initialize opts + raise unless opts.is_a? Hash + + @opts = opts + return unless @opts[:host] # for testcases + + @debug = @opts[:debug] + @cookie = @opts[:cookie] + @sso = @opts[:sso] + @operation_id = @opts[:operation_id] + @lock = Mutex.new + @http = nil + restart_http + end - def restart_http - begin - @http.finish if @http - rescue Exception => ex - puts "WARNING: Ignoring exception: #{ex.message}" - puts ex.backtrace.join("\n") + def host + @opts[:host] end - @http = Net::HTTP.new(@opts[:host], @opts[:port], @opts[:proxyHost], @opts[:proxyPort]) - if @opts[:ssl] - require 'net/https' - @http.use_ssl = true - @http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @opts[:insecure] - @http.ca_file = @opts[:ca_file] if @opts[:ca_file] - @http.cert = OpenSSL::X509::Certificate.new(@opts[:cert]) if @opts[:cert] - @http.key = OpenSSL::PKey::RSA.new(@opts[:key]) if @opts[:key] + + def close + @http.finish + rescue StandardError + IOError end - @http.set_debug_output(STDERR) if $DEBUG - @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) + + def restart_http + begin + @http.finish if @http + rescue Exception => e + puts "WARNING: Ignoring exception: #{e.message}" + puts e.backtrace.join("\n") + end + @http = Net::HTTP.new(@opts[:host], @opts[:port], @opts[:proxyHost], @opts[:proxyPort]) + if @opts[:ssl] + require "net/https" + @http.use_ssl = true + @http.verify_mode = OpenSSL::SSL::VERIFY_NONE if @opts[:insecure] + @http.ca_file = @opts[:ca_file] if @opts[:ca_file] + @http.cert = OpenSSL::X509::Certificate.new(@opts[:cert]) if @opts[:cert] + @http.key = OpenSSL::PKey::RSA.new(@opts[:key]) if @opts[:key] + end + @http.set_debug_output(STDERR) if $DEBUG + @http.read_timeout = @opts[:read_timeout] || 1_000_000 + @http.open_timeout = @opts[:open_timeout] || 60 + def @http.on_connect + @socket.io.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) + end + @http.start end - @http.start - end - def soap_envelope - xsd = 'http://www.w3.org/2001/XMLSchema' - env = 'http://schemas.xmlsoap.org/soap/envelope/' - xsi = 'http://www.w3.org/2001/XMLSchema-instance' - xml = Builder::XmlMarkup.new :indent => 0 - xml.tag!('env:Envelope', 'xmlns:xsd' => xsd, 'xmlns:env' => env, 'xmlns:xsi' => xsi) do - if @vcSessionCookie || @operation_id - xml.tag!('env:Header') do - xml.tag!('vcSessionCookie', @vcSessionCookie) if @vcSessionCookie - xml.tag!('operationID', @operation_id) if @operation_id + def soap_envelope + xsd = "http://www.w3.org/2001/XMLSchema" + env = "http://schemas.xmlsoap.org/soap/envelope/" + xsi = "http://www.w3.org/2001/XMLSchema-instance" + xml = Builder::XmlMarkup.new indent: 0 + xml.tag!("env:Envelope", "xmlns:xsd" => xsd, "xmlns:env" => env, "xmlns:xsi" => xsi) do + if @vcSessionCookie || @operation_id + xml.tag!("env:Header") do + xml.tag!("vcSessionCookie", @vcSessionCookie) if @vcSessionCookie + xml.tag!("operationID", @operation_id) if @operation_id + end end - end - xml.tag!('env:Body') do - yield xml if block_given? + xml.tag!("env:Body") do + yield xml if block_given? + end end + xml end - xml - end - def request action, body - headers = { 'content-type' => 'text/xml; charset=utf-8', 'SOAPAction' => action } - headers['cookie'] = @cookie if @cookie + def request action, body + headers = { "content-type" => "text/xml; charset=utf-8", "SOAPAction" => action } + headers["cookie"] = @cookie if @cookie - if @debug - $stderr.puts "Request:" - $stderr.puts body - $stderr.puts - end + if @debug + warn "Request:" + warn body + $stderr.puts + end - if @cookie.nil? && @sso - @sso.request_token unless @sso.assertion_id - body = @sso.sign_request(body) - end + if @cookie.nil? && @sso + @sso.request_token unless @sso.assertion_id + body = @sso.sign_request(body) + end - start_time = Time.now - response = @lock.synchronize do - begin - @http.request_post(@opts[:path], body, headers) - rescue Exception - restart_http - raise + start_time = Time.now + response = @lock.synchronize do + begin + @http.request_post(@opts[:path], body, headers) + rescue Exception + restart_http + raise + end end - end - end_time = Time.now - - if response.is_a? Net::HTTPServiceUnavailable - raise "Got HTTP 503: Service unavailable" - end + end_time = Time.now - self.cookie = response['set-cookie'] if response.key? 'set-cookie' + raise "Got HTTP 503: Service unavailable" if response.is_a? Net::HTTPServiceUnavailable - nk = Nokogiri(response.body) + self.cookie = response["set-cookie"] if response.key? "set-cookie" - if @debug - $stderr.puts "Response (in #{'%.3f' % (end_time - start_time)} s)" - $stderr.puts nk - $stderr.puts - end + nk = Nokogiri(response.body) - [nk.xpath('//soapenv:Body/*').select(&:element?).first, response.body.size] + if @debug + warn "Response (in #{'%.3f' % (end_time - start_time)} s)" + warn nk + $stderr.puts + end + + [nk.xpath("//soapenv:Body/*").select(&:element?).first, response.body.size] + end end end diff --git a/lib/rbvmomi/type_loader.rb b/lib/rbvmomi/type_loader.rb index 49648a94..bb34026a 100644 --- a/lib/rbvmomi/type_loader.rb +++ b/lib/rbvmomi/type_loader.rb @@ -1,138 +1,135 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'set' -require 'monitor' +require "set" +require "monitor" module RbVmomi + class TypeLoader + def initialize fn, extension_dirs, namespace + @extension_dirs = extension_dirs + @namespace = namespace + @lock = Monitor.new + @db = {} + @id2wsdl = {} + @loaded = {} + add_types Hash[BasicTypes::BUILTIN.map { |k| [k, nil] }] + vmodl_database = File.open(fn, "r") { |io| Marshal.load io } + vmodl_database.reject! { |k, _v| k =~ /^_/ } + add_types vmodl_database + preload + end -class TypeLoader - def initialize fn, extension_dirs, namespace - @extension_dirs = extension_dirs - @namespace = namespace - @lock = Monitor.new - @db = {} - @id2wsdl = {} - @loaded = {} - add_types Hash[BasicTypes::BUILTIN.map { |k| [k,nil] }] - vmodl_database = File.open(fn, 'r') { |io| Marshal.load io } - vmodl_database.reject! { |k,v| k =~ /^_/ } - add_types vmodl_database - preload - end - - def preload - names = (@namespace.constants + Object.constants).map(&:to_s).uniq. - select { |x| has? x } - names.each { |x| get(x) } - end - - # Reload all extensions for loaded VMODL types - def reload_extensions - @extension_dirs.each do |path| - reload_extensions_dir path + def preload + names = (@namespace.constants + Object.constants).map(&:to_s).uniq + .select { |x| has? x } + names.each { |x| get(x) } end - end - # Reload all extensions for loaded VMODL types from the given directory - def reload_extensions_dir path - loaded = Set.new(typenames.select { |x| @namespace.const_defined? x }) - Dir.open(path) do |dir| - dir.each do |file| - next unless file =~ /\.rb$/ - next unless loaded.member? $` - file_path = File.join(dir, file) - load file_path + # Reload all extensions for loaded VMODL types + def reload_extensions + @extension_dirs.each do |path| + reload_extensions_dir path end end - end - def has? name - fail unless name.is_a? String + # Reload all extensions for loaded VMODL types from the given directory + def reload_extensions_dir path + loaded = Set.new(typenames.select { |x| @namespace.const_defined? x }) + Dir.open(path) do |dir| + dir.each do |file| + next unless file =~ /\.rb$/ + next unless loaded.member? $` - @db.member?(name) or BasicTypes::BUILTIN.member?(name) - end + file_path = File.join(dir, file) + load file_path + end + end + end - def get name - fail "name '#{name}' is #{name.class} expecting String" unless name.is_a? String + def has? name + raise unless name.is_a? String - first_char = name[0].chr - if first_char.downcase == first_char - name = "%s%s" % [first_char.upcase, name[1..-1]] + @db.member?(name) or BasicTypes::BUILTIN.member?(name) end - return @loaded[name] if @loaded.member? name - @lock.synchronize do + def get name + raise "name '#{name}' is #{name.class} expecting String" unless name.is_a? String + + first_char = name[0].chr + name = "%s%s" % [first_char.upcase, name[1..-1]] if first_char.downcase == first_char + return @loaded[name] if @loaded.member? name - klass = make_type(name) - @namespace.const_set name, klass - load_extension name - @loaded[name] = klass + + @lock.synchronize do + return @loaded[name] if @loaded.member? name + + klass = make_type(name) + @namespace.const_set name, klass + load_extension name + @loaded[name] = klass + end end - end - def add_types types - @lock.synchronize do - @db.merge! types - @db = Hash[@db.map do |name, value| - if value - value['wsdl_name'] ||= name - end - first_char = name[0].chr - if first_char.downcase == first_char - name = "%s%s" % [first_char.upcase, name[1..-1]] - end - [name, value] - end] + def add_types types + @lock.synchronize do + @db.merge! types + @db = Hash[@db.map do |name, value| + value["wsdl_name"] ||= name if value + first_char = name[0].chr + name = "%s%s" % [first_char.upcase, name[1..-1]] if first_char.downcase == first_char + [name, value] + end] + end end - end - def typenames - @db.keys - end + def typenames + @db.keys + end - private + private - def load_extension name - @extension_dirs.map { |x| File.join(x, "#{name}.rb") }. - select { |x| File.exist? x }. - each { |x| load x } - end + def load_extension name + @extension_dirs.map { |x| File.join(x, "#{name}.rb") } + .select { |x| File.exist? x } + .each { |x| load x } + end - def make_type name - name = name.to_s - return BasicTypes.const_get(name) if BasicTypes::BUILTIN.member? name - desc = @db[name] or fail "unknown VMODL type #{name}" - case desc['kind'] - when 'data' then make_data_type name, desc - when 'managed' then make_managed_type name, desc - when 'enum' then make_enum_type name, desc - else fail desc.inspect + def make_type name + name = name.to_s + return BasicTypes.const_get(name) if BasicTypes::BUILTIN.member? name + + desc = @db[name] or raise "unknown VMODL type #{name}" + case desc["kind"] + when "data" then make_data_type name, desc + when "managed" then make_managed_type name, desc + when "enum" then make_enum_type name, desc + else raise desc.inspect + end end - end - def make_data_type name, desc - superclass = get desc['wsdl_base'] - Class.new(superclass).tap do |klass| - klass.init name, desc['props'] - klass.wsdl_name = desc['wsdl_name'] + def make_data_type name, desc + superclass = get desc["wsdl_base"] + Class.new(superclass).tap do |klass| + klass.init name, desc["props"] + klass.wsdl_name = desc["wsdl_name"] + end end - end - def make_managed_type name, desc - superclass = get desc['wsdl_base'] - Class.new(superclass).tap do |klass| - klass.init name, desc['props'], desc['methods'] - klass.wsdl_name = desc['wsdl_name'] + def make_managed_type name, desc + superclass = get desc["wsdl_base"] + Class.new(superclass).tap do |klass| + klass.init name, desc["props"], desc["methods"] + klass.wsdl_name = desc["wsdl_name"] + end end - end - def make_enum_type name, desc - Class.new(BasicTypes::Enum).tap do |klass| - klass.init name, desc['values'] - klass.wsdl_name = desc['wsdl_name'] + def make_enum_type name, desc + Class.new(BasicTypes::Enum).tap do |klass| + klass.init name, desc["values"] + klass.wsdl_name = desc["wsdl_name"] + end end end end - -end diff --git a/lib/rbvmomi/utils/admission_control.rb b/lib/rbvmomi/utils/admission_control.rb index 00ffdf8b..7f31d532 100644 --- a/lib/rbvmomi/utils/admission_control.rb +++ b/lib/rbvmomi/utils/admission_control.rb @@ -1,20 +1,21 @@ +# frozen_string_literal: true # Copyright (c) 2012-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT # An admission controlled resource scheduler for large scale vSphere deployments # -# While DRS (Dynamic Resource Scheduler) in vSphere handles CPU and Memory -# allocations within a single vSphere cluster, larger deployments require +# While DRS (Dynamic Resource Scheduler) in vSphere handles CPU and Memory +# allocations within a single vSphere cluster, larger deployments require # another layer of scheduling to make the use of multiple clusters transparent. -# So this class doesn't replace DRS, but in fact works on top of it. +# So this class doesn't replace DRS, but in fact works on top of it. # # The scheduler in this class performs admission control to make sure clusters # don't get overloaded. It does so by adding additional metrics to the already -# existing CPU and Memory reservation system that DRS has. After admission +# existing CPU and Memory reservation system that DRS has. After admission # control it also performs very basic initial placement. Note that in-cluster # placement and load-balancing is left to DRS. Also note that no cross-cluster -# load balancing is done. +# load balancing is done. # # This class uses the concept of a Pod: A set of clusters that share a set of # datastores. From a datastore perspective, we are free to place a VM on any @@ -22,33 +23,33 @@ # are automatically dicovered based on lists of clusters and datastores. # # Admission control covers the following metrics: -# - Host availability: If no hosts are available within a cluster or pod, +# - Host availability: If no hosts are available within a cluster or pod, # admission is denied. # - Minimum free space: If a datastore falls below this free space percentage, -# admission to it will be denied. Admission to a pod is granted as long at +# admission to it will be denied. Admission to a pod is granted as long at # least one datastore passes admission control. # - Maximum number of VMs: If a Pod exceeds a configured number of powered on # VMs, admission is denied. This is a crude but effective catch-all metric -# in case users didn't set proper individual CPU or Memory reservations or +# in case users didn't set proper individual CPU or Memory reservations or # if the scalability limit doesn't originate from CPU or Memory. # # Placement after admission control: # - Cluster selection: A load metric based on a combination of CPU and Memory # load is used to always select the "least loaded" cluster. The metric is very -# crude and only meant to do very rough load balancing. If DRS clusters are -# large enough, this is good enough in most cases though. -# - Datastore selection: Right now NO intelligence is implemented here. +# crude and only meant to do very rough load balancing. If DRS clusters are +# large enough, this is good enough in most cases though. +# - Datastore selection: Right now NO intelligence is implemented here. # # Usage: # Instantiate the class, call make_placement_decision and then use the exposed -# computer (cluster), resource pool, vm_folder and datastore. Currently once +# computer (cluster), resource pool, vm_folder and datastore. Currently once # computed, a new updated placement can't be generated. class AdmissionControlledResourceScheduler attr_reader :rp - - def initialize vim, opts = {} + + def initialize(vim, opts = {}) @vim = vim - + @datacenter = opts[:datacenter] @datacenter_path = opts[:datacenter_path] @vm_folder = opts[:vm_folder] @@ -58,18 +59,18 @@ def initialize vim, opts = {} @computer_names = opts[:computer_names] @datastores = opts[:datastores] @datastore_paths = opts[:datastore_paths] - + @max_vms_per_pod = opts[:max_vms_per_pod] @min_ds_free = opts[:min_ds_free] @service_docs_url = opts[:service_docs_url] - - @pc = @vim.serviceContent.propertyCollector - @root_folder = @vim.serviceContent.rootFolder - + + @pc = @vim.service_content.propertyCollector + @root_folder = @vim.service_content.rootFolder + @logger = opts[:logger] end - - def log x + + def log(x) if @logger @logger.info x else @@ -77,173 +78,162 @@ def log x end end - # Returns the used VM folder. If not set yet, uses the vm_folder_path to + # 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 [RbVmomi::VIM::Folder] The VM folder - def vm_folder + def vm_folder retries = 1 begin @vm_folder ||= datacenter.vmFolder.traverse!(@vm_folder_path, RbVmomi::VIM::Folder) - if !@vm_folder - fail "VM folder #{@vm_folder_path} not found" - end - rescue RbVmomi::Fault => fault - if !fault.fault.is_a?(RbVmomi::VIM::DuplicateName) + raise "VM folder #{@vm_folder_path} not found" unless @vm_folder + rescue RbVmomi::Fault => e + if !e.fault.is_a?(RbVmomi::VIM::DuplicateName) raise else retries -= 1 - retry if retries >= 0 - end + retry if retries >= 0 + end end - @vm_folder + @vm_folder end - # Returns the used Datacenter. If not set yet, uses the datacenter_path to - # lookup the datacenter. + # Returns the used Datacenter. If not set yet, uses the datacenter_path to + # lookup the datacenter. # @return [RbVmomi::VIM::Datacenter] The datacenter def datacenter - if !@datacenter - @datacenter = @root_folder.traverse(@datacenter_path, RbVmomi::VIM::Datacenter) - if !@datacenter - fail "datacenter #{@datacenter_path} not found" - end + unless @datacenter + @datacenter = @root_folder.traverse(@datacenter_path, RbVmomi::VIM::Datacenter) + raise "datacenter #{@datacenter_path} not found" unless @datacenter end @datacenter end - # Returns the candidate datastores. If not set yet, uses the datastore_paths + # 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 + # As a side effect, also looks up properties about all the datastores # @return [Array] List of RbVmomi::VIM::Datastore def datastores - if !@datastores - @datastores = @datastore_paths.map do |path| - ds = datacenter.datastoreFolder.traverse(path, RbVmomi::VIM::Datastore) - if !ds - fail "datastore #{path} not found" - end - ds - end - end - if !@datastore_props - @datastore_props = @pc.collectMultiple(@datastores, 'summary', 'name') + @datastores ||= @datastore_paths.map do |path| + ds = datacenter.datastoreFolder.traverse(path, RbVmomi::VIM::Datastore) + raise "datastore #{path} not found" unless ds + + ds end + @datastore_props ||= @pc.collectMultiple(@datastores, "summary", "name") @datastores end - # Returns the candidate computers (aka clusters). If not set yet, uses the - # computer_names to look them up. + # Returns the candidate computers (aka clusters). If not set yet, uses the + # computer_names to look them up. # @return [Array] List of [RbVmomi::VIM::ClusterComputeResource, Hash] tuples, where # the Hash is a list of stats about the computer def computers - if !@computers - @computers = @computer_names.map do |name| - computer = datacenter.find_compute_resource(name) - [computer, computer.stats] - end + @computers ||= @computer_names.map do |name| + computer = datacenter.find_compute_resource(name) + [computer, computer.stats] end @computers end - # Returns the candidate pods. If not set, automatically computes the pods - # based on the list of computers (aka clusters) and datastores. + # 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 RbVmomi::VIM::ClusterComputeResource def pods - if !@pods + unless @pods # A pod is defined as a set of clusters (aka computers) that share the same # datastore accessibility. Computing pods is done automatically using simple # set theory math. - computersProps = @pc.collectMultiple(computers.map{|x| x[0]}, 'datastore') - @pods = computers.map do |computer, stats| - computersProps[computer]['datastore'] & self.datastores + computersProps = @pc.collectMultiple(computers.map { |x| x[0] }, "datastore") + @pods = computers.map do |computer, _stats| + computersProps[computer]["datastore"] & datastores end.uniq.map do |ds_list| - computers.map{|x| x[0]}.select do |computer| - (computer.datastore & self.datastores) == ds_list + computers.map { |x| x[0] }.select do |computer| + (computer.datastore & datastores) == ds_list end end end - @pods + @pods end - + # Returns all VMs residing with a pod. Doesn't account for templates. Does so # very efficiently using a single API query. # @return [Hash] Hash of VMs as keys and their properties as values. - def pod_vms pod + def pod_vms(pod) # This function retrieves all VMs residing inside a pod filterSpec = RbVmomi::VIM.PropertyFilterSpec( - objectSet: pod.map do |computer, stats| + objectSet: pod.map do |computer, _stats| { obj: computer.resourcePool, selectSet: [ RbVmomi::VIM.TraversalSpec( - name: 'tsFolder', - type: 'ResourcePool', - path: 'resourcePool', + name: "tsFolder", + type: "ResourcePool", + path: "resourcePool", skip: false, selectSet: [ - RbVmomi::VIM.SelectionSpec(name: 'tsFolder'), - RbVmomi::VIM.SelectionSpec(name: 'tsVM'), + RbVmomi::VIM.SelectionSpec(name: "tsFolder"), + RbVmomi::VIM.SelectionSpec(name: "tsVM") ] ), RbVmomi::VIM.TraversalSpec( - name: 'tsVM', - type: 'ResourcePool', - path: 'vm', + name: "tsVM", + type: "ResourcePool", + path: "vm", skip: false, - selectSet: [], + selectSet: [] ) ] } end, propSet: [ - { type: 'ResourcePool', pathSet: ['name'] }, - { type: 'VirtualMachine', pathSet: %w(runtime.powerState) } + { type: "ResourcePool", pathSet: ["name"] }, + { type: "VirtualMachine", pathSet: %w(runtime.powerState) } ] ) - + 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?(RbVmomi::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 RbVmomi::VIM::Datastore - def pod_datastores pod - pod.first.datastore & self.datastores + def pod_datastores(pod) + pod.first.datastore & datastores end - + # 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 + # 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 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 + # passed admission control. An exception is thrown if access was denied to # all pods. - if !@filtered_pods + unless @filtered_pods log "Performing admission control:" - @filtered_pods = self.pods.select do |pod| + @filtered_pods = pods.select do |pod| # Gather some statistics about the pod ... - on_vms = pod_vms(pod).select{|k,v| v['runtime.powerState'] == 'poweredOn'} + on_vms = pod_vms(pod).select { |_k, v| v["runtime.powerState"] == "poweredOn" } num_pod_vms = on_vms.length pod_datastores = self.pod_datastores(pod) - log "Pod: #{pod.map{|x| x.name}.join(', ')}" + log "Pod: #{pod.map(&:name).join(', ')}" log " #{num_pod_vms} VMs" pod_datastores.each do |ds| - ds_sum = @datastore_props[ds]['summary'] - @datastore_props[ds]['free_percent'] = ds_sum.freeSpace.to_f * 100 / ds_sum.capacity + ds_sum = @datastore_props[ds]["summary"] + @datastore_props[ds]["free_percent"] = ds_sum.freeSpace.to_f * 100 / ds_sum.capacity end pod_datastores.each do |ds| ds_props = @datastore_props[ds] - ds_name = ds_props['name'] - free = ds_props['free_percent'] - free_gb = ds_props['summary'].freeSpace.to_f / 1024**3 + ds_name = ds_props["name"] + free = ds_props["free_percent"] + free_gb = ds_props["summary"].freeSpace.to_f / 1024**3 free_str = "%.2f GB (%.2f%%)" % [free_gb, free] log " Datastore #{ds_name}: #{free_str} free" end - + # Admission check: VM limit denied = false max_vms = @max_vms_per_pod @@ -253,49 +243,47 @@ def filtered_pods denied = true end end - + # Admission check: Free space on datastores min_ds_free = @min_ds_free if min_ds_free && min_ds_free > 0 # We need at least one datastore with enough free space low_list = pod_datastores.select do |ds| - @datastore_props[ds]['free_percent'] <= min_ds_free + @datastore_props[ds]["free_percent"] <= min_ds_free end - + if low_list.length == pod_datastores.length - dsNames = low_list.map{|ds| @datastore_props[ds]['name']}.join(", ") + dsNames = low_list.map { |ds| @datastore_props[ds]["name"] }.join(", ") err = "Datastores #{dsNames} below minimum free disk space (#{min_ds_free}%)" denied = true end end - + # Admission check: Hosts are available - if !denied + unless denied hosts_available = pod.any? do |computer| - stats = Hash[self.computers][computer] + stats = Hash[computers][computer] stats[:totalCPU] > 0 && stats[:totalMem] > 0 end - if !hosts_available + unless hosts_available err = "No hosts are current available in this pod" denied = true end end - - if denied + + if denied log " Admission DENIED: #{err}" else log " Admission granted" end - + !denied end end - if @filtered_pods.length == 0 + if @filtered_pods.empty? log "Couldn't find any Pod with enough resources." - if @service_docs_url - log "Check #{@service_docs_url} to see which other Pods you may be able to use" - end - fail "Admission denied" + log "Check #{@service_docs_url} to see which other Pods you may be able to use" if @service_docs_url + raise "Admission denied" end @filtered_pods end @@ -304,28 +292,25 @@ def filtered_pods # computs the least loaded cluster (using a metric that combines CPU and Memory # load) that passes admission control. # @return [RbVmomi::VIM::ClusterComputeResource] Chosen computer (aka cluster) - def pick_computer placementhint = nil - if !@computer + def pick_computer(placementhint = nil) + unless @computer # Out of the pods to which we have been granted access, pick the cluster # (aka computer) with the lowest CPU/Mem utilization for load balancing - available = self.filtered_pods.flatten - eligible = self.computers.select do |computer,stats| + available = filtered_pods.flatten + eligible = computers.select do |computer, stats| available.member?(computer) && stats[:totalCPU] > 0 and stats[:totalMem] > 0 end computer = nil if placementhint - if eligible.length > 0 - computer = eligible.map{|x| x[0]}[placementhint % eligible.length] - end + computer = eligible.map { |x| x[0] }[placementhint % eligible.length] unless eligible.empty? else - computer, = eligible.min_by do |computer,stats| - 2**(stats[:usedCPU].to_f/stats[:totalCPU]) + (stats[:usedMem].to_f/stats[:totalMem]) + computer, = eligible.min_by do |_computer, stats| + 2**(stats[:usedCPU].to_f / stats[:totalCPU]) + (stats[:usedMem].to_f / stats[:totalMem]) end end - - if !computer - fail "No clusters available, should have been prevented by admission control" - end + + raise "No clusters available, should have been prevented by admission control" unless computer + @computer = computer end @computer @@ -335,66 +320,61 @@ def pick_computer placementhint = nil # datastore without much intelligence, as long as it passes admission control. # @return [RbVmomi::VIM::Datastore] Chosen datastore def datastore placementHint = nil - if @datastore - return @datastore - end - + return @datastore if @datastore + pod_datastores = pick_computer.datastore & datastores - + eligible = pod_datastores.select do |ds| min_ds_free = @min_ds_free if min_ds_free && min_ds_free > 0 - ds_sum = @datastore_props[ds]['summary'] - free_percent = ds_sum.freeSpace.to_f * 100 / ds_sum.capacity + ds_sum = @datastore_props[ds]["summary"] + free_percent = ds_sum.freeSpace.to_f * 100 / ds_sum.capacity free_percent > min_ds_free else true end end - - if eligible.length == 0 - fail "Couldn't find any eligible datastore. Admission control should have prevented this" - end - - if placementHint && placementHint > 0 - @datastore = eligible[placementHint % eligible.length] - else - @datastore = eligible.first - end + + raise "Couldn't find any eligible datastore. Admission control should have prevented this" if eligible.empty? + + @datastore = if placementHint && placementHint > 0 + eligible[placementHint % eligible.length] + else + eligible.first + end @datastore end - - # Runs the placement algorithm and populates all the various properties as + + # Runs the placement algorithm and populates all the various properties as # a side effect. Run this first, before using the other functions of this # class. def make_placement_decision opts = {} - self.filtered_pods - self.pick_computer opts[:placementHint] + filtered_pods + pick_computer opts[:placementHint] log "Selected compute resource: #{@computer.name}" - + @rp = @computer.resourcePool.traverse(@rp_path) - if !@rp - fail "Resource pool #{@rp_path} not found" - end + raise "Resource pool #{@rp_path} not found" unless @rp + log "Resource pool: #{@rp.pretty_path}" - + stats = @computer.stats if stats[:totalMem] > 0 && stats[:totalCPU] > 0 - cpu_load = "#{(100*stats[:usedCPU])/stats[:totalCPU]}% cpu" - mem_load = "#{(100*stats[:usedMem])/stats[:totalMem]}% mem" + cpu_load = "#{(100 * stats[:usedCPU]) / stats[:totalCPU]}% cpu" + mem_load = "#{(100 * stats[:usedMem]) / stats[:totalMem]}% mem" log "Cluster utilization: #{cpu_load}, #{mem_load}" end - - user_vms = vm_folder.inventory_flat('VirtualMachine' => %w(name storage)).select do |k, v| + + user_vms = vm_folder.inventory_flat("VirtualMachine" => %w(name storage)).select do |k, _v| k.is_a?(RbVmomi::VIM::VirtualMachine) end numVms = user_vms.length - unshared = user_vms.map do |vm, info| - info['storage'].perDatastoreUsage.map{|x| x.unshared}.inject(0, &:+) + unshared = user_vms.map do |_vm, info| + info["storage"].perDatastoreUsage.map(&:unshared).inject(0, &:+) end.inject(0, &:+) log "User stats: #{numVms} VMs using %.2fGB of storage" % [unshared.to_f / 1024**3] - - @placement_hint = opts[:placement_hint] || (rand(100) + 1) + + @placement_hint = opts[:placement_hint] || rand(1..100) datastore = self.datastore @placement_hint log "Datastore: #{datastore.name}" end diff --git a/lib/rbvmomi/utils/deploy.rb b/lib/rbvmomi/utils/deploy.rb index 1975714b..58297cc5 100644 --- a/lib/rbvmomi/utils/deploy.rb +++ b/lib/rbvmomi/utils/deploy.rb @@ -1,13 +1,14 @@ +# frozen_string_literal: true # Copyright (c) 2012-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'open-uri' -require 'nokogiri' -require_relative '../../rbvmomi' +require "open-uri" +require "nokogiri" +require_relative "../../rbvmomi" # The cached ovf deployer is an optimization on top of regular OVF deployment # as it is offered by the VIM::OVFManager. Creating a VM becomes a multi-stage -# process: First the OVF is uploaded and instead of directly using it, it is +# process: First the OVF is uploaded and instead of directly using it, it is # prepared for linked cloning and marked as a template. It can then be cloned # many times over, without the cost of repeated OVF deploys (network and storage # IO) and the cost of storing the same base VM several times (storage space). @@ -15,21 +16,21 @@ # automatically detected and de-duplicated. One thread will win to create the # OVF template, while the other will wait for the winning thread to finish the # task. So even fully independent, distributed and unsynchronized clients using -# this call with be auto-synchronized just by talking to the same vCenter -# instance and using the name naming scheme for the templates. +# this call with be auto-synchronized just by talking to the same vCenter +# instance and using the name naming scheme for the templates. # -# The caching concept above can be extended to multiple levels. Lets assume +# The caching concept above can be extended to multiple levels. Lets assume # many VMs will share the same base OS, but are running different builds of the -# application running inside the VM. If it is expected that again many (but not -# all) VMs will share the same build of the application, a tree structure of +# application running inside the VM. If it is expected that again many (but not +# all) VMs will share the same build of the application, a tree structure of # templates becomes useful. At the root of the tree is the template with just # the base OS. It is uploaded from an OVF if needed. Then, this base OS image # is cloned, a particular build is installed and the resulting VM is again marked -# as a template. Users can then instantiate that particular build with very +# as a template. Users can then instantiate that particular build with very # little extra overhead. This class supports such multi level templates via the # :is_template parameter of linked_clone(). class CachedOvfDeployer - # Constructor. Gets the VIM connection and important VIM objects + # Constructor. Gets the VIM connection and important VIM objects # @param vim [VIM] VIM Connection # @param network [VIM::Network] Network to attach templates and VMs to # @param computer [VIM::ComputeResource] Host/Cluster to deploy templates/VMs to @@ -37,7 +38,7 @@ class CachedOvfDeployer # @param vm_folder [VIM::Folder] Folder into which to deploy VMs # @param datastore [VIM::Folder] Datastore to store template/VM in # @param opts [Hash] Additional parameters - def initialize vim, network, computer, template_folder, vm_folder, datastore, opts = {} + def initialize(vim, network, computer, template_folder, vm_folder, datastore, opts = {}) @vim = vim @network = network @computer = computer @@ -47,40 +48,40 @@ def initialize vim, network, computer, template_folder, vm_folder, datastore, op @datastore = datastore @logger = opts[:logger] end - - def log x - if @logger + + def log(x) + if @logger @logger.info x else puts "#{Time.now}: #{x}" end end - - # Internal helper method that executes the passed in block while disabling + + # Internal helper method that executes the passed in block while disabling # the handling of SIGINT and SIGTERM signals. Restores their handlers after - # the block is executed. + # the block is executed. # @param enabled [Boolean] If false, this function is a no-op - def _run_without_interruptions enabled + def _run_without_interruptions(enabled) if enabled - int_handler = Signal.trap("SIGINT", 'IGNORE') - term_handler = Signal.trap("SIGTERM", 'IGNORE') + int_handler = Signal.trap("SIGINT", "IGNORE") + term_handler = Signal.trap("SIGTERM", "IGNORE") end - + yield - + if enabled Signal.trap("SIGINT", int_handler) Signal.trap("SIGTERM", term_handler) end end - + # Uploads an OVF, prepares the resulting VM for linked cloning and then marks - # it as a template. If another thread happens to race to do the same task, - # the losing thread will not do the actual work, but instead wait for the + # it as a template. If another thread happens to race to do the same task, + # the losing thread will not do the actual work, but instead wait for the # winning thread to do the work by looking up the template VM and waiting for # it to be marked as a template. This way, the cost of uploading and keeping # the full size of the VM is only paid once. - # @param ovf_url [String] URL to the OVF to be deployed. Currently only http + # @param ovf_url [String] URL to the OVF to be deployed. Currently only http # and https are supported. # @param template_name [String] Name of the template to be used. Should be the # same name for the same URL. A cluster specific post-fix will automatically @@ -95,59 +96,53 @@ def upload_ovf_as_template(ovf_url, template_name, opts = {}) # Optimization: If there happens to be a fully prepared template, then # there is no need to do the complicated OVF upload dance template = lookup_template template_name - if template - return template - end - + return template if template + # The OVFManager expects us to know the names of the networks mentioned - # in the OVF file so we can map them to VIM::Network objects. For - # simplicity this function assumes we need to read the OVF file + # in the OVF file so we can map them to VIM::Network objects. For + # simplicity this function assumes we need to read the OVF file # ourselves to know the names, and we map all of them to the same # VIM::Network. # If we're handling a file:// URI we need to strip the scheme as open-uri # can't handle them. - if URI(ovf_url).scheme == "file" && URI(ovf_url).host.nil? - ovf_url = URI(ovf_url).path - end + ovf_url = URI(ovf_url).path if URI(ovf_url).scheme == "file" && URI(ovf_url).host.nil? - ovf = open(ovf_url, 'r'){|io| Nokogiri::XML(io.read)} + ovf = open(ovf_url, "r") { |io| Nokogiri::XML(io.read) } ovf.remove_namespaces! - networks = ovf.xpath('//NetworkSection/Network').map{|x| x['name']} - network_mappings = Hash[networks.map{|x| [x, @network]}] + networks = ovf.xpath("//NetworkSection/Network").map { |x| x["name"] } + network_mappings = Hash[networks.map { |x| [x, @network] }] - network_mappings_str = network_mappings.map{|k, v| "#{k} = #{v.name}"} + network_mappings_str = network_mappings.map { |k, v| "#{k} = #{v.name}" } log "networks: #{network_mappings_str.join(', ')}" - pc = @vim.serviceContent.propertyCollector - + pc = @vim.service_content.propertyCollector + # OVFs need to be uploaded to a specific host. DRS won't just pick one # for us, so we need to pick one wisely. The host needs to be connected, # not be in maintenance mode and must have the destination datastore # accessible. hosts = @computer.host hosts_props = pc.collectMultiple( - hosts, - 'datastore', 'runtime.connectionState', - 'runtime.inMaintenanceMode', 'name' + hosts, + "datastore", "runtime.connectionState", + "runtime.inMaintenanceMode", "name" ) host = hosts.shuffle.find do |x| - host_props = hosts_props[x] - is_connected = host_props['runtime.connectionState'] == 'connected' - is_ds_accessible = host_props['datastore'].member?(@datastore) - is_connected && is_ds_accessible && !host_props['runtime.inMaintenanceMode'] + host_props = hosts_props[x] + is_connected = host_props["runtime.connectionState"] == "connected" + is_ds_accessible = host_props["datastore"].member?(@datastore) + is_connected && is_ds_accessible && !host_props["runtime.inMaintenanceMode"] end - if !host - fail "No host in the cluster available to upload OVF to" - end - + raise "No host in the cluster available to upload OVF to" unless host + log "Uploading OVF to #{hosts_props[host]['name']}..." property_mappings = {} - # To work around the VMFS 8-host limit (existed until ESX 5.0), as - # well as just for organization purposes, we create one template per - # cluster. This also provides us with additional isolation. - vm_name = template_name+"-#{@computer.name}" + # To work around the VMFS 8-host limit (existed until ESX 5.0), as + # well as just for organization purposes, we create one template per + # cluster. This also provides us with additional isolation. + vm_name = template_name + "-#{@computer.name}" vm = nil wait_for_template = false @@ -156,9 +151,9 @@ def upload_ovf_as_template(ovf_url, template_name, opts = {}) # This is desirable, as other threads depend on this thread finishing # its prepare job and thus interrupting it has impacts beyond this # single thread or process. - _run_without_interruptions(opts[:run_without_interruptions]) do - begin - vm = @vim.serviceContent.ovfManager.deployOVF( + _run_without_interruptions(opts[:run_without_interruptions]) do + begin + vm = @vim.service_content.ovfManager.deployOVF( uri: ovf_url, vmName: vm_name, vmFolder: @template_folder, @@ -166,37 +161,40 @@ def upload_ovf_as_template(ovf_url, template_name, opts = {}) resourcePool: @rp, datastore: @datastore, networkMappings: network_mappings, - propertyMappings: property_mappings) - rescue RbVmomi::Fault => fault + propertyMappings: property_mappings + ) + rescue RbVmomi::Fault => e # If two threads execute this script at the same time to upload # the same template under the same name, one will win and the other - # with be rejected by VC. We catch those cases here, and handle + # with be rejected by VC. We catch those cases here, and handle # them by waiting for the winning thread to finish preparing the # template, see below ... - is_duplicate = fault.fault.is_a?(RbVmomi::VIM::DuplicateName) - is_duplicate ||= (fault.fault.is_a?(RbVmomi::VIM::InvalidState) && - !fault.fault.is_a?(RbVmomi::VIM::InvalidHostState)) + is_duplicate = e.fault.is_a?(RbVmomi::VIM::DuplicateName) + is_duplicate ||= begin + e.fault.is_a?(RbVmomi::VIM::InvalidState) && + !e.fault.is_a?(RbVmomi::VIM::InvalidHostState) + end if is_duplicate wait_for_template = true else - raise fault + raise e end end - + # The winning thread succeeded in uploading the OVF. Now we need to # prepare it for (linked) cloning and mark it as a template to signal # we are done. - if !wait_for_template + unless wait_for_template if opts[:no_delta] != true config = opts[:config] || {} config = vm.update_spec_add_delta_disk_layer_on_all_disks(config) # XXX: Should we add a version that does retries? - vm.ReconfigVM_Task(:spec => config).wait_for_completion + vm.ReconfigVM_Task(spec: config).wait_for_completion end vm.MarkAsTemplate end end - + # The losing thread now needs to wait for the winning thread to finish # uploading and preparing the template if wait_for_template @@ -204,36 +202,34 @@ def upload_ovf_as_template(ovf_url, template_name, opts = {}) vm = _wait_for_template_ready @template_folder, vm_name log "Template fully prepared and ready to be cloned" end - + vm end - + # Looks up a template by name in the configured template_path. Should be used - # before uploading the VM via upload_ovf_as_template, although that is + # before uploading the VM via upload_ovf_as_template, although that is # not strictly required, but a lot more efficient. - # @param template_name [String] Name of the template to be used. A cluster + # @param template_name [String] Name of the template to be used. A cluster # specific post-fix will automatically be added. - # @return [VIM::VirtualMachine] The template as a VIM::VirtualMachine instance + # @return [VIM::VirtualMachine] The template as a VIM::VirtualMachine instance # or nil - def lookup_template template_name + def lookup_template(template_name) template_path = "#{template_name}-#{@computer.name}" template = @template_folder.traverse(template_path, RbVmomi::VIM::VirtualMachine) if template config = template.config is_template = config && config.template - if !is_template - template = nil - end + template = nil unless is_template end template end - + # Creates a linked clone of a template prepared with upload_ovf_as_template. # The function waits for completion on the clone task. Optionally, in case - # two level templates are being used, this function can wait for another + # two level templates are being used, this function can wait for another # thread to finish creating the second level template. See class comments # for the concept of multi level templates. - # @param template_name [String] Name of the template to be used. A cluster + # @param template_name [String] Name of the template to be used. A cluster # specific post-fix will automatically be added. # @param vm_name [String] Name of the new VM that is being created via cloning. # @param config [Hash] VM Config delta to apply after the VM is cloned. @@ -243,76 +239,75 @@ def lookup_template template_name # again and collision and de-duping logic kicks # in. # @return [VIM::VirtualMachine] The VIM::VirtualMachine instance of the clone - def linked_clone template_vm, vm_name, config, opts = {} + def linked_clone(template_vm, vm_name, config, opts = {}) spec = { location: { - pool: @rp, + pool: @rp, datastore: @datastore, - diskMoveType: :moveChildMostDiskBacking, - }, - powerOn: false, + diskMoveType: :moveChildMostDiskBacking + }, + powerOn: false, template: false, - config: config, + config: config } if opts[:is_template] wait_for_template = false template_name = "#{vm_name}-#{@computer.name}" begin vm = template_vm.CloneVM_Task( - folder: @template_folder, - name: template_name, + folder: @template_folder, + name: template_name, spec: spec ).wait_for_completion - rescue RbVmomi::Fault => fault - if fault.fault.is_a?(RbVmomi::VIM::DuplicateName) + rescue RbVmomi::Fault => e + if e.fault.is_a?(RbVmomi::VIM::DuplicateName) wait_for_template = true else raise end end - + if wait_for_template puts "#{Time.now}: Template already exists, waiting for it to be ready" vm = _wait_for_template_ready @template_folder, template_name puts "#{Time.now}: Template ready" - end + end else vm = template_vm.CloneVM_Task( - folder: @vmfolder, - name: vm_name, + folder: @vmfolder, + name: vm_name, spec: spec ).wait_for_completion end vm end - # Internal helper method that waits for a template to be fully created. It + # Internal helper method that waits for a template to be fully created. It # polls until it finds the VM in the inventory, and once it is there, waits # for it to be fully created and marked as a template. This function will - # block for forever if the template never gets created or marked as a + # block for forever if the template never gets created or marked as a # template. # @param vm_folder [VIM::Folder] Folder in which we expect the template to show up # @param vm_name [String] Name of the VM we are waiting for # @return [VIM::VirtualMachine] The VM we were waiting for when it is ready - def _wait_for_template_ready vm_folder, vm_name + def _wait_for_template_ready(vm_folder, vm_name) vm = nil - while !vm + until vm sleep 3 # XXX: Optimize this - vm = vm_folder.children.find{|x| x.name == vm_name} + vm = vm_folder.children.find { |x| x.name == vm_name } end log "Template VM found" sleep 2 - while true - runtime, template = vm.collect 'runtime', 'config.template' + loop do + runtime, template = vm.collect "runtime", "config.template" ready = runtime && runtime.host && runtime.powerState == "poweredOff" - ready = ready && template - if ready - break - end + ready &&= template + break if ready + sleep 5 end - + vm end end diff --git a/lib/rbvmomi/utils/leases.rb b/lib/rbvmomi/utils/leases.rb index a13f47f2..4ebbcf81 100644 --- a/lib/rbvmomi/utils/leases.rb +++ b/lib/rbvmomi/utils/leases.rb @@ -1,64 +1,63 @@ +# frozen_string_literal: true # Copyright (c) 2012-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'yaml' +require "yaml" # A class to manage VM leases -# +# # This class uses YAML encoded VM annotations (config.annotation) to manage a # lease system. It helps add such lease info onto new and existing VMs and to # find VMs that have expired leases or that are about to have expired leases. -# The calling code can use those to generate emails with about-to-expire +# The calling code can use those to generate emails with about-to-expire # notifications, suspend, power off or destroy VMs that have exceeded their -# lease, etc. +# lease, etc. class LeaseTool # Lists of VM properties the LeaseTool needs to do its job. Can be used to # construct larger property collector calls that retrieve more info than just # one subsystem needs. # @return [Array] List of property names - def vms_props_list - ['name', 'config.annotation'] + def vms_props_list + ["name", "config.annotation"] end - - # Fetch all VM properties that the LeaseTool needs on all VMs passed in. + + # Fetch all VM properties that the LeaseTool needs on all VMs passed in. # @param vms [Array] List of VIM::VirtualMachine instances # @return [Hash] Hash of VMs as keys and their properties as values def get_vms_props vms out = {} - if vms.length > 0 - pc = vms.first._connection.serviceContent.propertyCollector - out = pc.collectMultiple(vms, 'name', 'config.annotation') + unless vms.empty? + pc = vms.first._connection.service_content.propertyCollector + out = pc.collectMultiple(vms, "name", "config.annotation") end out end - - # Retrieve the current time as used by the lease tool. + + # Retrieve the current time as used by the lease tool. # @return [Time] Current time as used by the lease tool def current_time # XXX: Should swith to time provided by VC Time.now end - + # Helper function that sets the lease info in a passed in VM config. If there # is no annotation, it is added. If there is an annotation, it is updated to - # include the lease info. Note that if the annotation isn't YAML, it is - # overwritten. + # include the lease info. Note that if the annotation isn't YAML, it is + # overwritten. # @param vmconfig [Hash] Virtual Machine config spec # @param lease_minutes [int] Time to lease expiration from now in minutes # @return [Hash] Updated Virtual Machine config spec def set_lease_in_vm_config vmconfig, lease_minutes annotation = vmconfig[:annotation] annotation ||= "" - note = YAML.load annotation - if !note.is_a?(Hash) - note = {} - end + note = YAML.safe_load annotation + note = {} unless note.is_a?(Hash) lease = current_time + lease_minutes * 60 - note['lease'] = lease + note["lease"] = lease vmconfig[:annotation] = YAML.dump(note) vmconfig end - + # Issue ReconfigVM_Task on the VM to update the lease. User can pass in current # annotation, but if not, it is retrieved on demand. A task is returned, i.e. # function doesn't wait for completion. @@ -67,17 +66,15 @@ def set_lease_in_vm_config vmconfig, lease_minutes # @param annotation [String] 'config.annotation' property of the VM. Optional. # @return [VIM::Task] VM reconfiguration task def set_lease_on_vm_task vm, lease_minutes, annotation = nil - if !annotation - annotation = vm.collect 'config.annotation' - end - vmconfig = {:annotation => annotation} + annotation ||= vm.collect "config.annotation" + vmconfig = { annotation: annotation } vmconfig = set_lease_in_vm_config vmconfig, lease_minutes # XXX: It may be a good idea to cite the VM version here to avoid # concurrent writes to the annotation stepping on each others toes - vm.ReconfigVM_Task(:spec => vmconfig) + vm.ReconfigVM_Task(spec: vmconfig) end - - # Issue ReconfigVM_Task to set the lease on all VMs that currently do not + + # Issue ReconfigVM_Task to set the lease on all VMs that currently do not # have a lease. All VM reconfigurations are done in parallel and the function # waits for all of them to complete # @param vms [Array] List of VIM::VirtualMachine instances, may or may not have leases @@ -86,56 +83,57 @@ def set_lease_on_vm_task vm, lease_minutes, annotation = nil # @return [Array] List of previously leaseless VMs that now have a lease def set_lease_on_leaseless_vms vms, vmprops, opts = {} lease_minutes = opts[:lease_minutes] - if !lease_minutes - raise "Expected lease_minutes to be specified" - end + raise "Expected lease_minutes to be specified" unless lease_minutes + vms = find_leaseless_vms vms, vmprops - if vms.length > 0 + unless vms.empty? tasks = vms.map do |vm| - annotation = vmprops[vm]['config.annotation'] + annotation = vmprops[vm]["config.annotation"] task = set_lease_on_vm_task(vm, lease_minutes, annotation) task end - si = vms.first._connection.serviceInstance + si = vms.first._connection.service_instance si.wait_for_multiple_tasks [], tasks end vms end - + # Filter the list of passed in Virtual Machines and find the ones that currently # do not have a lease. # @param vms [Array] List of VIM::VirtualMachine instances, may or may not have leases # @param vmprops [Hash] Hash of VIM::VirtualMachine instances to their properties - # @return [Array] List of leaseless VMs + # @return [Array] List of leaseless VMs def find_leaseless_vms vms, vmprops vms.reject do |vm| props = vmprops[vm] - annotation = props['config.annotation'] + annotation = props["config.annotation"] if annotation - note = YAML.load annotation - note.is_a?(Hash) && note['lease'] + note = YAML.safe_load annotation + note.is_a?(Hash) && note["lease"] end end end - # Filter the list of passed in Virtul Machines and find the one that are - # expired. A time offset can be used to identify VMs that will expire at - # a certain point in the future. + # Filter the list of passed in Virtul Machines and find the one that are + # expired. A time offset can be used to identify VMs that will expire at + # a certain point in the future. # If a VM doesn't have a lease, it is treated as never expiring. # @param vms [Array] List of VIM::VirtualMachine instances, may or may not have leases # @param vmprops [Hash] Hash of VIM::VirtualMachine instances to their properties # @option opts [int] :time_delta Time delta (seconds) to be added to current time - # @return [Array] List of expired VMs + # @return [Array] List of expired VMs def filter_expired_vms vms, vmprops, opts = {} time_delta = opts[:time_delta] || 0 time = current_time + time_delta - + out = vms.map do |vm| props = vmprops[vm] - next unless annotation = props['config.annotation'] - note = YAML.load annotation - next unless note.is_a?(Hash) && lease = note['lease'] + next unless annotation = props["config.annotation"] + + note = YAML.safe_load annotation + next unless note.is_a?(Hash) && lease = note["lease"] next unless time > lease + time_to_expiration = ((lease - time) + time_delta) [vm, time_to_expiration] end.compact diff --git a/lib/rbvmomi/utils/perfdump.rb b/lib/rbvmomi/utils/perfdump.rb index 2350c977..3ec6c716 100644 --- a/lib/rbvmomi/utils/perfdump.rb +++ b/lib/rbvmomi/utils/perfdump.rb @@ -1,73 +1,72 @@ +# frozen_string_literal: true # Copyright (c) 2012-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'set' -require 'yaml' +require "set" +require "yaml" # PerfAggregator is a class that, given connections to a list of vCenter # Servers, will fetch the entire VM folder and ResourcePool hierarchies, # including all VIM::VirtualMachine objects and aggregate VM stats along -# the tree hierarchies. The PerfAggregator class allows for users to +# the tree hierarchies. The PerfAggregator class allows for users to # perform post processing on the data returned by vCenter, e.g. to augment -# it with addtional data that was obtained using a combination of +# it with addtional data that was obtained using a combination of # VM annotations (or custom values) and an external DB. Post processing -# can also define additional tree structures that may be completely +# can also define additional tree structures that may be completely # independent of the VM folder and ResourcePool hirarchies provided by # vCenter, e.g. one based on VMs used for testing of a set of source code -# branches. +# branches. class PerfAggregator attr_accessor :path_types - - def initialize logger = nil + + def initialize(logger = nil) @logger = logger - @path_types = Set.new - @path_types << 'rp' - @path_types << 'vmfolder' - + @path_types = Set.new + @path_types << "rp" + @path_types << "vmfolder" + # XXX: Rename this variable @perf_metrics = { - 'virtualDisk.read' => :sum, - 'virtualDisk.write' => :sum, - 'virtualDisk.numberReadAveraged' => :sum, - 'virtualDisk.numberWriteAveraged' => :sum, - 'virtualDisk.totalReadLatency.avg' => :avg_ignore_zero, - 'virtualDisk.totalWriteLatency.avg' => :avg_ignore_zero, - 'virtualDisk.totalReadLatency.max' => :max, - 'virtualDisk.totalWriteLatency.max' => :max, - 'num.vm' => :sum, - 'num.poweredonvm' => :sum, - 'summary.quickStats.hostMemoryUsage' => :sum, - 'summary.quickStats.guestMemoryUsage' => :sum, - 'summary.quickStats.overallCpuUsage' => :sum, - 'summary.config.memorySizeMB' => :sum, - 'summary.config.numCpu' => :sum, - 'storage.space.committed' => :sum, - 'storage.space.uncommitted' => :sum, - 'storage.space.unshared' => :sum, + "virtualDisk.read" => :sum, + "virtualDisk.write" => :sum, + "virtualDisk.numberReadAveraged" => :sum, + "virtualDisk.numberWriteAveraged" => :sum, + "virtualDisk.totalReadLatency.avg" => :avg_ignore_zero, + "virtualDisk.totalWriteLatency.avg" => :avg_ignore_zero, + "virtualDisk.totalReadLatency.max" => :max, + "virtualDisk.totalWriteLatency.max" => :max, + "num.vm" => :sum, + "num.poweredonvm" => :sum, + "summary.quickStats.hostMemoryUsage" => :sum, + "summary.quickStats.guestMemoryUsage" => :sum, + "summary.quickStats.overallCpuUsage" => :sum, + "summary.config.memorySizeMB" => :sum, + "summary.config.numCpu" => :sum, + "storage.space.committed" => :sum, + "storage.space.uncommitted" => :sum, + "storage.space.unshared" => :sum } end - - def log text + + def log(text) if @logger - @logger.info text + @logger.info text else puts "#{Time.now}: #{text}" end end - - def set_vm_processing_callback &block + + def set_vm_processing_callback(&block) @vm_processing_callback = block end - - def add_node_unless_exists inventory, id, props - if !inventory[id] - inventory[id] = props.merge({'children' => []}) - end + + def add_node_unless_exists(inventory, id, props) + inventory[id] = props.merge({ "children" => [] }) unless inventory[id] end - + # Method that extracts the entire VM folder and ResourcePool hierarchy - # from vCenter with a single API call. It generates a flat list of - # VIM objects which will include VIM::Folder, VIM::Datacenter, + # from vCenter with a single API call. It generates a flat list of + # VIM objects which will include VIM::Folder, VIM::Datacenter, # VIM::ClusterComputeResource, VIM::ResourcePool and VIM::VirtualMachine. # # Post processing is done (using helper methods) to populate full paths, @@ -79,197 +78,191 @@ def add_node_unless_exists inventory, id, props # # @param rootFolder [VIM::Folder] Expected to be the rootFolder of the VC # @param vm_prop_names [Array] List of VM properties to fetch - def all_inventory_flat rootFolder, vm_prop_names = ['name'] + def all_inventory_flat(rootFolder, vm_prop_names = ["name"]) conn = rootFolder._connection pc = conn.propertyCollector - + filterSpec = RbVmomi::VIM.PropertyFilterSpec( - :objectSet => [ - :obj => rootFolder, - :selectSet => [ + objectSet: [ + obj: rootFolder, + selectSet: [ RbVmomi::VIM.TraversalSpec( - :name => 'tsFolder', - :type => 'Folder', - :path => 'childEntity', - :skip => false, - :selectSet => [ - RbVmomi::VIM.SelectionSpec(:name => 'tsFolder'), - RbVmomi::VIM.SelectionSpec(:name => 'tsDatacenterVmFolder'), - RbVmomi::VIM.SelectionSpec(:name => 'tsDatacenterHostFolder'), - RbVmomi::VIM.SelectionSpec(:name => 'tsClusterRP'), - RbVmomi::VIM.SelectionSpec(:name => 'tsClusterHost'), + name: "tsFolder", + type: "Folder", + path: "childEntity", + skip: false, + selectSet: [ + RbVmomi::VIM.SelectionSpec(name: "tsFolder"), + RbVmomi::VIM.SelectionSpec(name: "tsDatacenterVmFolder"), + RbVmomi::VIM.SelectionSpec(name: "tsDatacenterHostFolder"), + RbVmomi::VIM.SelectionSpec(name: "tsClusterRP"), + RbVmomi::VIM.SelectionSpec(name: "tsClusterHost") ] ), RbVmomi::VIM.TraversalSpec( - :name => 'tsDatacenterVmFolder', - :type => 'Datacenter', - :path => 'vmFolder', - :skip => false, - :selectSet => [ - RbVmomi::VIM.SelectionSpec(:name => 'tsFolder') + name: "tsDatacenterVmFolder", + type: "Datacenter", + path: "vmFolder", + skip: false, + selectSet: [ + RbVmomi::VIM.SelectionSpec(name: "tsFolder") ] ), RbVmomi::VIM.TraversalSpec( - :name => 'tsDatacenterHostFolder', - :type => 'Datacenter', - :path => 'hostFolder', - :skip => false, - :selectSet => [ - RbVmomi::VIM.SelectionSpec(:name => 'tsFolder') + name: "tsDatacenterHostFolder", + type: "Datacenter", + path: "hostFolder", + skip: false, + selectSet: [ + RbVmomi::VIM.SelectionSpec(name: "tsFolder") ] ), RbVmomi::VIM.TraversalSpec( - :name => 'tsClusterRP', - :type => 'ClusterComputeResource', - :path => 'resourcePool', - :skip => false, - :selectSet => [ - RbVmomi::VIM.SelectionSpec(:name => 'tsRP'), + name: "tsClusterRP", + type: "ClusterComputeResource", + path: "resourcePool", + skip: false, + selectSet: [ + RbVmomi::VIM.SelectionSpec(name: "tsRP") ] ), RbVmomi::VIM.TraversalSpec( - :name => 'tsClusterHost', - :type => 'ClusterComputeResource', - :path => 'host', - :skip => false, - :selectSet => [] + name: "tsClusterHost", + type: "ClusterComputeResource", + path: "host", + skip: false, + selectSet: [] ), RbVmomi::VIM.TraversalSpec( - :name => 'tsRP', - :type => 'ResourcePool', - :path => 'resourcePool', - :skip => false, - :selectSet => [ - RbVmomi::VIM.SelectionSpec(:name => 'tsRP'), + name: "tsRP", + type: "ResourcePool", + path: "resourcePool", + skip: false, + selectSet: [ + RbVmomi::VIM.SelectionSpec(name: "tsRP") ] - ), + ) ] ], - :propSet => [ - { :type => 'Folder', :pathSet => ['name', 'parent'] }, - { :type => 'Datacenter', :pathSet => ['name', 'parent'] }, - { :type => 'ClusterComputeResource', - :pathSet => ['name', 'parent', 'summary.effectiveCpu', 'summary.effectiveMemory'] - }, - { :type => 'ResourcePool', :pathSet => ['name', 'parent'] }, - { :type => 'HostSystem', :pathSet => ['name', 'parent', 'runtime.connectionState'] }, - { :type => 'VirtualMachine', :pathSet => vm_prop_names }, + propSet: [ + { type: "Folder", pathSet: %w(name parent) }, + { type: "Datacenter", pathSet: %w(name parent) }, + { type: "ClusterComputeResource", + pathSet: ["name", "parent", "summary.effectiveCpu", "summary.effectiveMemory"] }, + { type: "ResourcePool", pathSet: %w(name parent) }, + { type: "HostSystem", pathSet: ["name", "parent", "runtime.connectionState"] }, + { type: "VirtualMachine", pathSet: vm_prop_names } ] ) - - result = pc.RetrieveProperties(:specSet => [filterSpec]) + + result = pc.RetrieveProperties(specSet: [filterSpec]) inventory = {} vms = {} - result.each do |r| + result.each do |r| if r.obj.is_a?(RbVmomi::VIM::VirtualMachine) - vms[r.obj] = r.to_hash + vms[r.obj] = r.to_hash else inventory[r.obj] = r.to_hash end end - inventory['root'] = { - 'name' => 'root', - 'path' => 'root', - 'parent' => nil, - 'parents' => [], + inventory["root"] = { + "name" => "root", + "path" => "root", + "parent" => nil, + "parents" => [] } inventory[conn.host] = { - 'name' => conn.host, - 'path' => "root/#{conn.host}", - 'parent' => 'root', - 'parents' => ['root'], + "name" => conn.host, + "path" => "root/#{conn.host}", + "parent" => "root", + "parents" => ["root"] } - _compute_vmfolders_and_rp_paths conn.host, inventory + _compute_vmfolders_and_rp_paths conn.host, inventory _compute_parents_and_children inventory [vms, inventory] end - - # Helper method that computes full paths and parent lists out of a + + # Helper method that computes full paths and parent lists out of a # flat list of objects. Operates recursively and doesn't yet split # the paths into different tree types. # @param obj [Hash] Property hash of current element # @param objs [Array] Flat list of tree elements - def _compute_vmfolder_and_rp_path_and_parents vc, obj, objs - if obj['path'] - return - end - if !obj['parent'] - obj['parent'] = vc - obj['path'] = "root/#{vc}/#{obj['name']}" - obj['parents'] = ['root', vc] + def _compute_vmfolder_and_rp_path_and_parents(vc, obj, objs) + return if obj["path"] + + unless obj["parent"] + obj["parent"] = vc + obj["path"] = "root/#{vc}/#{obj['name']}" + obj["parents"] = ["root", vc] return end - parent = objs[obj['parent']] + parent = objs[obj["parent"]] _compute_vmfolder_and_rp_path_and_parents(vc, parent, objs) - obj['path'] = "%s/%s" % [parent['path'], obj['name']] - obj['parents'] = [obj['parent']] + parent['parents'] + obj["path"] = "%s/%s" % [parent["path"], obj["name"]] + obj["parents"] = [obj["parent"]] + parent["parents"] nil end - - # Helper method that computes full paths and parent lists out of a + + # Helper method that computes full paths and parent lists out of a # flat list of objects. Full paths are tracked seperately per type # of tree, i.e. seperately for the ResourcePool tree and the VM folder - # tree. + # tree. # @param objs [Array] Flat list of tree elements - def _compute_vmfolders_and_rp_paths vc, objs - objs.each do |obj, props| + def _compute_vmfolders_and_rp_paths(vc, objs) + objs.each do |obj, props| _compute_vmfolder_and_rp_path_and_parents(vc, props, objs) - - props['paths'] = {} - obj_with_parents = [obj] + props['parents'] - dc = obj_with_parents.find{|x| x.is_a?(RbVmomi::VIM::Datacenter)} + + props["paths"] = {} + obj_with_parents = [obj] + props["parents"] + dc = obj_with_parents.find { |x| x.is_a?(RbVmomi::VIM::Datacenter) } # Everything above and including a VIM::Datacenter is part of # both the rp and vmfolder tree. Anything below depends on the # folder of the datacenter it is under: The hostFolder is called # "host" while the root vm folder is called "vm". if !dc || obj.is_a?(RbVmomi::VIM::Datacenter) - props['paths']['rp'] = props['path'] - props['paths']['vmfolder'] = props['path'] + props["paths"]["rp"] = props["path"] + props["paths"]["vmfolder"] = props["path"] else dc_index = obj_with_parents.index dc folder = obj_with_parents[dc_index - 1] - if objs[folder]['name'] == 'host' - props['paths']['rp'] = props['path'] + if objs[folder]["name"] == "host" + props["paths"]["rp"] = props["path"] else - props['paths']['vmfolder'] = props['path'] + props["paths"]["vmfolder"] = props["path"] end end - - props['children'] = [] + + props["children"] = [] end end - + # Helper method that computes children references and parent paths on # all objects, if not computed yet. Assumes that full paths of each # object have been calculated already. # @param objs [Array] Flat list of tree elements - def _compute_parents_and_children objs + def _compute_parents_and_children(objs) objs.each do |obj, props| - if props['parent_paths'] - next - end - props['parent_paths'] = {} - if !props['parent'] - next - end - parent = objs[props['parent']] - props['paths'].keys.each do |type| - props['parent_paths'][type] = parent['paths'][type] + next if props["parent_paths"] + + props["parent_paths"] = {} + next unless props["parent"] + + parent = objs[props["parent"]] + props["paths"].each_key do |type| + props["parent_paths"][type] = parent["paths"][type] end - parent['children'] << obj + parent["children"] << obj end end - - def _aggregate_metrics vms_stats, perf_metrics - out = Hash[perf_metrics.keys.map{|x| [x, 0]}] - avg_counter = Hash[perf_metrics.keys.map{|x| [x, 0]}] - + + def _aggregate_metrics(vms_stats, perf_metrics) + out = Hash[perf_metrics.keys.map { |x| [x, 0] }] + avg_counter = Hash[perf_metrics.keys.map { |x| [x, 0] }] + vms_stats.each do |vm_stats| perf_metrics.each do |key, type| values = vm_stats[key] - if !values.is_a?(Array) - values = [values] - end + values = [values] unless values.is_a?(Array) values.compact.each do |val| if type == :sum out[key] += val @@ -287,260 +280,231 @@ def _aggregate_metrics vms_stats, perf_metrics end end end - + perf_metrics.each do |key, type| - if type == :avg_ignore_zero || type == :avg - if avg_counter[key] > 0 - out[key] = out[key] / avg_counter[key] - end - end + next unless type == :avg_ignore_zero || type == :avg + + out[key] = out[key] / avg_counter[key] if avg_counter[key] > 0 end - + out end - - def _collect_info_on_all_vms_single root_folder, opts = {} + + def _collect_info_on_all_vms_single(root_folder, opts = {}) prop_names = opts[:prop_names] - if !prop_names - prop_names = [ - 'name', - 'config.template', - 'runtime.powerState', 'datastore', 'config.annotation', - 'parent', 'resourcePool', 'storage.perDatastoreUsage', - 'summary.config.memorySizeMB', - 'summary.config.numCpu', - 'summary.quickStats.hostMemoryUsage', - 'summary.quickStats.guestMemoryUsage', - 'summary.quickStats.overallCpuUsage', - 'runtime.connectionState', - 'config.instanceUuid', - 'customValue', - ] - end + prop_names ||= [ + "name", + "config.template", + "runtime.powerState", "datastore", "config.annotation", + "parent", "resourcePool", "storage.perDatastoreUsage", + "summary.config.memorySizeMB", + "summary.config.numCpu", + "summary.quickStats.hostMemoryUsage", + "summary.quickStats.guestMemoryUsage", + "summary.quickStats.overallCpuUsage", + "runtime.connectionState", + "config.instanceUuid", + "customValue" + ] perf_metrics = opts[:perf_metrics] - if !perf_metrics - perf_metrics = { - 'virtualDisk.read' => :avg, - 'virtualDisk.write' => :avg, - 'virtualDisk.numberReadAveraged' => :avg, - 'virtualDisk.numberWriteAveraged' => :avg, - 'virtualDisk.totalReadLatency' => :avg_ignore_zero, - 'virtualDisk.totalWriteLatency' => :avg_ignore_zero, - } - end + perf_metrics ||= { + "virtualDisk.read" => :avg, + "virtualDisk.write" => :avg, + "virtualDisk.numberReadAveraged" => :avg, + "virtualDisk.numberWriteAveraged" => :avg, + "virtualDisk.totalReadLatency" => :avg_ignore_zero, + "virtualDisk.totalWriteLatency" => :avg_ignore_zero + } host_perf_metrics = opts[:host_perf_metrics] - if !host_perf_metrics - host_perf_metrics = { - 'cpu.usage' => :avg, - 'mem.usage' => :avg, - } - end + host_perf_metrics ||= { + "cpu.usage" => :avg, + "mem.usage" => :avg + } vms_props, inventory = all_inventory_flat root_folder, prop_names vms = vms_props.keys - - hosts_props = inventory.select{|k, v| k.is_a?(RbVmomi::VIM::HostSystem)} + + hosts_props = inventory.select { |k, _v| k.is_a?(RbVmomi::VIM::HostSystem) } conn = root_folder._connection - sc = conn.serviceContent + sc = conn.service_content pc = sc.propertyCollector pm = sc.perfManager vc_uuid = conn.instanceUuid - - connected_vms = vms_props.select do |vm, props| - is_connected = props['runtime.connectionState'] != "disconnected" - is_template = props['config.template'] + + connected_vms = vms_props.select do |_vm, props| + is_connected = props["runtime.connectionState"] != "disconnected" + is_template = props["config.template"] is_connected && !is_template end.keys - + begin # XXX: Need to find a good way to get the "right" samples - if connected_vms.length == 0 + if connected_vms.empty? {} else vms_stats = pm.retrieve_stats( - connected_vms, perf_metrics.keys, - :max_samples => 3 + connected_vms, perf_metrics.keys, + max_samples: 3 ) end - rescue RbVmomi::Fault => ex - if ex.fault.is_a? RbVmomi::VIM::ManagedObjectNotFound - connected_vms -= [ex.fault.obj] + rescue RbVmomi::Fault => e + if e.fault.is_a? RbVmomi::VIM::ManagedObjectNotFound + connected_vms -= [e.fault.obj] retry end raise end - connected_hosts = hosts_props.select do |k,v| - v['runtime.connectionState'] != "disconnected" + connected_hosts = hosts_props.select do |_k, v| + v["runtime.connectionState"] != "disconnected" end - if connected_hosts.length > 0 + unless connected_hosts.empty? hosts_stats = pm.retrieve_stats( - connected_hosts.keys, host_perf_metrics.keys, - :max_samples => 3 + connected_hosts.keys, host_perf_metrics.keys, + max_samples: 3 ) end hosts_props.each do |host, props| - if !connected_hosts[host] - next - end - + next unless connected_hosts[host] + stats = hosts_stats[host] || {} stats = stats[:metrics] || {} stats = _aggregate_metrics [stats], host_perf_metrics props.merge!(stats) end - + vms_props.each do |vm, props| - if !connected_vms.member?(vm) - next - end - props['num.vm'] = 1 - powered_on = (props['runtime.powerState'] == 'poweredOn') - props['num.poweredonvm'] = powered_on ? 1 : 0 - + next unless connected_vms.member?(vm) + + props["num.vm"] = 1 + powered_on = (props["runtime.powerState"] == "poweredOn") + props["num.poweredonvm"] = powered_on ? 1 : 0 + stats = vms_stats[vm] || {} stats = stats[:metrics] || {} stats = _aggregate_metrics [stats], perf_metrics props.merge!(stats) - props['virtualDisk.totalReadLatency.avg'] = props['virtualDisk.totalReadLatency'] - props['virtualDisk.totalWriteLatency.avg'] = props['virtualDisk.totalWriteLatency'] - props['virtualDisk.totalReadLatency.max'] = props['virtualDisk.totalReadLatency'] - props['virtualDisk.totalWriteLatency.max'] = props['virtualDisk.totalWriteLatency'] - props.delete('virtualDisk.totalReadLatency') - props.delete('virtualDisk.totalWriteLatency') - - per_ds_usage = props['storage.perDatastoreUsage'] - props['storage.space.committed'] = per_ds_usage.map{|x| x.committed}.inject(0, &:+) - props['storage.space.uncommitted'] = per_ds_usage.map{|x| x.uncommitted}.inject(0, &:+) - props['storage.space.unshared'] = per_ds_usage.map{|x| x.unshared}.inject(0, &:+) - - props['parent_paths'] = {} - if inventory[props['parent']] - props['parent_paths']['vmfolder'] = inventory[props['parent']]['path'] - end - if !props['config.template'] - rp_props = inventory[props['resourcePool']] - props['parent_paths']['rp'] = rp_props['path'] - end - - props['annotation_yaml'] = YAML.load(props['config.annotation'] || '') - if !props['annotation_yaml'].is_a?(Hash) - props['annotation_yaml'] = {} + props["virtualDisk.totalReadLatency.avg"] = props["virtualDisk.totalReadLatency"] + props["virtualDisk.totalWriteLatency.avg"] = props["virtualDisk.totalWriteLatency"] + props["virtualDisk.totalReadLatency.max"] = props["virtualDisk.totalReadLatency"] + props["virtualDisk.totalWriteLatency.max"] = props["virtualDisk.totalWriteLatency"] + props.delete("virtualDisk.totalReadLatency") + props.delete("virtualDisk.totalWriteLatency") + + per_ds_usage = props["storage.perDatastoreUsage"] + props["storage.space.committed"] = per_ds_usage.map(&:committed).inject(0, &:+) + props["storage.space.uncommitted"] = per_ds_usage.map(&:uncommitted).inject(0, &:+) + props["storage.space.unshared"] = per_ds_usage.map(&:unshared).inject(0, &:+) + + props["parent_paths"] = {} + props["parent_paths"]["vmfolder"] = inventory[props["parent"]]["path"] if inventory[props["parent"]] + unless props["config.template"] + rp_props = inventory[props["resourcePool"]] + props["parent_paths"]["rp"] = rp_props["path"] end - - props['customValue'] = Hash[props['customValue'].map do |x| + + props["annotation_yaml"] = YAML.safe_load(props["config.annotation"] || "") + props["annotation_yaml"] = {} unless props["annotation_yaml"].is_a?(Hash) + + props["customValue"] = Hash[props["customValue"].map do |x| [x.key, x.value] end] - - props['vc_uuid'] = vc_uuid + + props["vc_uuid"] = vc_uuid end - - [vms_props, inventory, hosts_props] + + [vms_props, inventory, hosts_props] end - - def collect_info_on_all_vms root_folders, opts = {} - log "Fetching information from all VCs ..." + + def collect_info_on_all_vms(root_folders, opts = {}) + log "Fetching information from all VCs ..." vms_props = {} hosts_props = {} inventory = {} lock = Mutex.new root_folders.map do |root_folder| - Thread.new do + Thread.new do begin - single_vms_props, single_inventory, single_hosts_props = + single_vms_props, single_inventory, single_hosts_props = _collect_info_on_all_vms_single(root_folder, opts) - - lock.synchronize do + + lock.synchronize do vms_props.merge!(single_vms_props) - if inventory['root'] - single_inventory['root']['children'] += inventory['root']['children'] - end + single_inventory["root"]["children"] += inventory["root"]["children"] if inventory["root"] inventory.merge!(single_inventory) hosts_props.merge!(single_hosts_props) end - rescue Exception => ex - log "#{ex.class}: #{ex.message}" - ex.backtrace.each do |line| + rescue Exception => e + log "#{e.class}: #{e.message}" + e.backtrace.each do |line| log line end raise end end - end.each{|t| t.join} + end.each(&:join) - log "Make data marshal friendly ..." + log "Make data marshal friendly ..." inventory = _make_marshal_friendly(inventory) vms_props = _make_marshal_friendly(vms_props) hosts_props = _make_marshal_friendly(hosts_props) - log "Perform external post processing ..." - if @vm_processing_callback - @vm_processing_callback.call(self, vms_props, inventory) - end - - log "Perform data aggregation ..." - # Processing the annotations may have added new nodes to the + log "Perform external post processing ..." + @vm_processing_callback.call(self, vms_props, inventory) if @vm_processing_callback + + log "Perform data aggregation ..." + # Processing the annotations may have added new nodes to the # inventory list, hence we need to run _compute_parents_and_children # again to calculate the parents and children for the newly # added nodes. _compute_parents_and_children inventory # Now that we have all VMs and a proper inventory tree built, we can - # aggregate the VM stats along all trees and tree nodes. This + # aggregate the VM stats along all trees and tree nodes. This # de-normalizes the data heavily, but thats fine path_types = opts[:path_types] || @path_types inventory = _aggregate_vms path_types, vms_props, inventory - + log "Done collecting and aggregating stats" @inventory = inventory @vms_props = vms_props - + { - 'inventory' => inventory, - 'vms_props' => vms_props, - 'hosts_props' => hosts_props, + "inventory" => inventory, + "vms_props" => vms_props, + "hosts_props" => hosts_props } end - - def _make_marshal_friendly hash + + def _make_marshal_friendly(hash) hash = Hash[hash.map do |k, v| - if v['parent'] - v['parent'] = _mo2str(v['parent']) - end - if v['resourcePool'] - v['resourcePool'] = _mo2str(v['resourcePool']) - end - if v['children'] - v['children'] = v['children'].map{|x| _mo2str(x)} - end - if v['parents'] - v['parents'] = v['parents'].map{|x| _mo2str(x)} - end - if v['datastore'] - v['datastore'] = v['datastore'].map{|x| _mo2str(x)} - end - v['type'] = k.class.name + v["parent"] = _mo2str(v["parent"]) if v["parent"] + v["resourcePool"] = _mo2str(v["resourcePool"]) if v["resourcePool"] + v["children"] = v["children"].map { |x| _mo2str(x) } if v["children"] + v["parents"] = v["parents"].map { |x| _mo2str(x) } if v["parents"] + v["datastore"] = v["datastore"].map { |x| _mo2str(x) } if v["datastore"] + v["type"] = k.class.name [_mo2str(k), v] - end] - # Marhsal hash to JSON and back. This is just debug code to ensure - # that all further processing can be done on a serialized dump of + end] + # Marhsal hash to JSON and back. This is just debug code to ensure + # that all further processing can be done on a serialized dump of # the data. - hash = JSON.load(JSON.dump(hash)) + JSON.load(JSON.dump(hash)) end - - def _mo2str mo + + def _mo2str(mo) if !mo.is_a?(RbVmomi::VIM::ManagedObject) mo else "vim-#{mo._connection.instanceUuid}-#{mo._ref}" end end - - # Helper method that aggregates the VM stats along all trees and + + # Helper method that aggregates the VM stats along all trees and # tree nodes. This de-normalizes the data heavily, but thats fine. - def _aggregate_vms path_types, vms_props, inventory + def _aggregate_vms(path_types, vms_props, inventory) # XXX: Opimtization: # This function is currently quite wasteful. It computes all VMs # at each level and then aggregates the VMs for each node individually @@ -549,23 +513,22 @@ def _aggregate_vms path_types, vms_props, inventory index = {} reverse_index = {} inventory.each do |k, v| - if v['paths'] && v['paths'][path_type] - path = v['paths'][path_type] - index[path] = v - reverse_index[path] = k - end + next unless v["paths"] && v["paths"][path_type] + + path = v["paths"][path_type] + index[path] = v + reverse_index[path] = k end - + paths_vms = {} - + vms_props.each do |vm, props| - if !props['parent_paths'] || !props['parent_paths'][path_type] - next - end - parent_path = props['parent_paths'][path_type] + next if !props["parent_paths"] || !props["parent_paths"][path_type] + + parent_path = props["parent_paths"][path_type] while parent_path parent = index[parent_path] - if !parent + unless parent puts "Parent is nil, so dumping some stuff" puts path_type puts "parent path: #{parent_path}" @@ -574,25 +537,25 @@ def _aggregate_vms path_types, vms_props, inventory end paths_vms[parent_path] ||= [] paths_vms[parent_path] << vm - parent_path = parent['parent_paths'][path_type] + parent_path = parent["parent_paths"][path_type] end end - + paths_vms.each do |k, vms| - inventory[reverse_index[k]]['vms'] ||= {} - inventory[reverse_index[k]]['vms'][path_type] = vms - vms_stats = vms_props.select{|k, v| vms.member?(k)}.values + inventory[reverse_index[k]]["vms"] ||= {} + inventory[reverse_index[k]]["vms"][path_type] = vms + vms_stats = vms_props.select { |k, _v| vms.member?(k) }.values stats = _aggregate_metrics vms_stats, @perf_metrics - inventory[reverse_index[k]]['stats'] ||= {} - inventory[reverse_index[k]]['stats'][path_type] = stats + inventory[reverse_index[k]]["stats"] ||= {} + inventory[reverse_index[k]]["stats"][path_type] = stats end - - #pp paths_vms.map{|k, v| [k, reverse_index[k], v.length, index[k]['stats'][path_type].length]} + + # pp paths_vms.map{|k, v| [k, reverse_index[k], v.length, index[k]['stats'][path_type].length]} end - + inventory end - + def visualize_vm_props path_types_rows = construct_tree_rows_from_vm_props path_types_rows.each do |path_type, rows| @@ -605,25 +568,25 @@ def visualize_vm_props end end - def construct_tree_rows_from_vm_props path_types = nil + def construct_tree_rows_from_vm_props(path_types = nil) path_types ||= @path_types - def visualize_node path_type, node, inventory, indent = 0 + def visualize_node(path_type, node, inventory, indent = 0) rows = [] - if !node || !node['stats'] || !node['stats'][path_type] + if !node || !node["stats"] || !node["stats"][path_type] stats = {} return [] else - stats = node['stats'][path_type] + stats = node["stats"][path_type] end - rows << [indent, node['name'], stats] - node['children'].each do |child| + rows << [indent, node["name"], stats] + node["children"].each do |child| rows += visualize_node path_type, inventory[child], inventory, indent + 1 end rows end - + Hash[path_types.map do |path_type| - key, root = @inventory.find{|k, v| v['paths'][path_type] == 'root'} + key, root = @inventory.find { |_k, v| v["paths"][path_type] == "root" } rows = visualize_node path_type, root, @inventory [path_type, rows] end] diff --git a/lib/rbvmomi/version.rb b/lib/rbvmomi/version.rb index d83aa26d..197a6a59 100644 --- a/lib/rbvmomi/version.rb +++ b/lib/rbvmomi/version.rb @@ -1,6 +1,7 @@ +# frozen_string_literal: true # Copyright (c) 2016-2020 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT module RbVmomi - VERSION = '3.0.0'.freeze + VERSION = "3.0.0" end diff --git a/lib/rbvmomi/vim.rb b/lib/rbvmomi/vim.rb index 60a8a0fc..65e71e04 100644 --- a/lib/rbvmomi/vim.rb +++ b/lib/rbvmomi/vim.rb @@ -1,138 +1,144 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT module RbVmomi + # A connection to one vSphere SDK endpoint. + # @see #serviceInstance + class VIM < Connection + # Connect to a vSphere SDK endpoint + # + # @param [Hash] opts The options hash. + # @option opts [String] :host Host to connect to. + # @option opts [Numeric] :port (443) Port to connect to. + # @option opts [Boolean] :ssl (true) Whether to use SSL. + # @option opts [Boolean] :insecure (false) If true, ignore SSL certificate errors. + # @option opts [String] :cookie If set, use cookie to connect instead of user/password + # @option opts [String] :user (root) Username. + # @option opts [String] :password Password. + # @option opts [String] :path (/sdk) SDK endpoint path. + # @option opts [Boolean] :debug (false) If true, print SOAP traffic to stderr. + # @option opts [String] :operation_id If set, use for operationID + # @option opts [Boolean] :close_on_exit (true) If true, will close connection with at_exit + # @option opts [RbVmomi::SSO] :sso (nil) Use SSO token to login if set + def self.connect opts + raise unless opts.is_a? Hash + raise "host option required" unless opts[:host] + + opts[:cookie] ||= nil + opts[:user] ||= "root" + opts[:password] ||= "" + opts[:ssl] = true unless opts.member?(:ssl) || opts[:"no-ssl"] + opts[:insecure] ||= false + opts[:port] ||= (opts[:ssl] ? 443 : 80) + opts[:path] ||= "/sdk" + opts[:ns] ||= "urn:vim25" + opts[:rev] = "6.7" if opts[:rev].nil? + unless opts.member? :debug + opts[:debug] = begin + !ENV["RBVMOMI_DEBUG"].empty? + rescue StandardError + false + end + end -# A connection to one vSphere SDK endpoint. -# @see #serviceInstance -class VIM < Connection - # Connect to a vSphere SDK endpoint - # - # @param [Hash] opts The options hash. - # @option opts [String] :host Host to connect to. - # @option opts [Numeric] :port (443) Port to connect to. - # @option opts [Boolean] :ssl (true) Whether to use SSL. - # @option opts [Boolean] :insecure (false) If true, ignore SSL certificate errors. - # @option opts [String] :cookie If set, use cookie to connect instead of user/password - # @option opts [String] :user (root) Username. - # @option opts [String] :password Password. - # @option opts [String] :path (/sdk) SDK endpoint path. - # @option opts [Boolean] :debug (false) If true, print SOAP traffic to stderr. - # @option opts [String] :operation_id If set, use for operationID - # @option opts [Boolean] :close_on_exit (true) If true, will close connection with at_exit - # @option opts [RbVmomi::SSO] :sso (nil) Use SSO token to login if set - def self.connect opts - fail unless opts.is_a? Hash - fail "host option required" unless opts[:host] - opts[:cookie] ||= nil - opts[:user] ||= 'root' - opts[:password] ||= '' - opts[:ssl] = true unless opts.member? :ssl or opts[:"no-ssl"] - opts[:insecure] ||= false - opts[:port] ||= (opts[:ssl] ? 443 : 80) - opts[:path] ||= '/sdk' - opts[:ns] ||= 'urn:vim25' - opts[:rev] = '6.7' if opts[:rev].nil? - opts[:debug] = (!ENV['RBVMOMI_DEBUG'].empty? rescue false) unless opts.member? :debug - - conn = new(opts).tap do |vim| - unless opts[:cookie] - if opts[:sso] - vim.serviceContent.sessionManager.LoginByToken - else - vim.serviceContent.sessionManager.Login :userName => opts[:user], :password => opts[:password] + conn = new(opts).tap do |vim| + unless opts[:cookie] + if opts[:sso] + vim.serviceContent.sessionManager.LoginByToken + else + vim.serviceContent.sessionManager.Login userName: opts[:user], password: opts[:password] + end end + rev = vim.serviceContent.about.apiVersion + vim.rev = [rev, opts[:rev]].min { |a, b| Gem::Version.new(a) <=> Gem::Version.new(b) } end - rev = vim.serviceContent.about.apiVersion - vim.rev = [rev, opts[:rev]].min { |a, b| Gem::Version.new(a) <=> Gem::Version.new(b) } - end - - at_exit { conn.close } if opts.fetch(:close_on_exit, true) - conn - end - def close - serviceContent.sessionManager.Logout - rescue RbVmomi::Fault => e - $stderr.puts(e.message) if debug - ensure - self.cookie = nil - super - end - - def rev= x - super - @serviceContent = nil - end + at_exit { conn.close } if opts.fetch(:close_on_exit, true) + conn + end - # Return the ServiceInstance - # - # The ServiceInstance is the root of the vSphere inventory. - # @see http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.ServiceInstance.html - def serviceInstance - VIM::ServiceInstance self, 'ServiceInstance' - end + def close + serviceContent.sessionManager.Logout + rescue RbVmomi::Fault => e + warn(e.message) if debug + ensure + self.cookie = nil + super + end - # Alias to serviceInstance.RetrieveServiceContent - def serviceContent - @serviceContent ||= serviceInstance.RetrieveServiceContent - end + def rev= x + super + @serviceContent = nil + end - # Alias to serviceContent.rootFolder - def rootFolder - serviceContent.rootFolder - end + # Return the ServiceInstance + # + # The ServiceInstance is the root of the vSphere inventory. + # @see http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.ServiceInstance.html + def serviceInstance + VIM::ServiceInstance self, "ServiceInstance" + end - alias root rootFolder + # Alias to serviceInstance.RetrieveServiceContent + def serviceContent + @serviceContent ||= serviceInstance.RetrieveServiceContent + end - # Alias to serviceContent.propertyCollector - def propertyCollector - serviceContent.propertyCollector - end + # Alias to service_content.rootFolder + def rootFolder + serviceContent.rootFolder + end - # Alias to serviceContent.searchIndex - def searchIndex - serviceContent.searchIndex - end + alias root rootFolder - # @private - def pretty_print pp - pp.text "VIM(#{@opts[:host]})" - end + # Alias to service_content.propertyCollector + def propertyCollector + serviceContent.propertyCollector + end - def instanceUuid - serviceContent.about.instanceUuid - end + # Alias to service_content.searchIndex + def searchIndex + serviceContent.searchIndex + end - def get_log_lines logKey, lines=5, start=nil, host=nil - diagMgr = self.serviceContent.diagnosticManager - if !start - log = diagMgr.BrowseDiagnosticLog(:host => host, :key => logKey, :start => 999999999) - lineEnd = log.lineEnd - start = lineEnd - lines + # @private + def pretty_print pp + pp.text "VIM(#{@opts[:host]})" end - start = start < 0 ? 0 : start - log = diagMgr.BrowseDiagnosticLog(:host => host, :key => logKey, :start => start) - if log.lineText.size > 0 - [log.lineText.slice(-lines, log.lineText.size), log.lineEnd] - else - [log.lineText, log.lineEnd] + + def instanceUuid + serviceContent.about.instanceUuid end - end - def get_log_keys host=nil - diagMgr = self.serviceContent.diagnosticManager - keys = [] - diagMgr.QueryDescriptions(:host => host).each do |desc| - keys << "#{desc.key}" + def get_log_lines logKey, lines=5, start=nil, host=nil + diagMgr = serviceContent.diagnosticManager + unless start + log = diagMgr.BrowseDiagnosticLog(host: host, key: logKey, start: 999_999_999) + lineEnd = log.lineEnd + start = lineEnd - lines + end + start = start < 0 ? 0 : start + log = diagMgr.BrowseDiagnosticLog(host: host, key: logKey, start: start) + if !log.lineText.empty? + [log.lineText.slice(-lines, log.lineText.size), log.lineEnd] + else + [log.lineText, log.lineEnd] + end end - keys - end - add_extension_dir File.join(File.dirname(__FILE__), "vim") - (ENV['RBVMOMI_VIM_EXTENSION_PATH']||'').split(':').each { |dir| add_extension_dir dir } + def get_log_keys host=nil + diagMgr = serviceContent.diagnosticManager + keys = [] + diagMgr.QueryDescriptions(host: host).each do |desc| + keys << desc.key.to_s + end + keys + end - load_vmodl(ENV['VMODL'] || File.join(File.dirname(__FILE__), "../../vmodl.db")) -end + add_extension_dir File.join(File.dirname(__FILE__), "vim") + (ENV["RBVMOMI_VIM_EXTENSION_PATH"] || "").split(":").each { |dir| add_extension_dir dir } + load_vmodl(ENV["VMODL"] || File.join(File.dirname(__FILE__), "../../vmodl.db")) + end end diff --git a/lib/rbvmomi/vim/ComputeResource.rb b/lib/rbvmomi/vim/ComputeResource.rb index cc53dc29..03cfbc8d 100644 --- a/lib/rbvmomi/vim/ComputeResource.rb +++ b/lib/rbvmomi/vim/ComputeResource.rb @@ -1,54 +1,60 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::ComputeResource - # Aggregate cluster information. - # - # @note Values are returned in a hash. - # - # @return [Mhz] totalCPU: Sum of the frequencies of each CPU in the cluster. - # @return [Mhz] usedCPU: CPU cycles used across the cluster. - # @return [MB] totalMem: Total RAM. - # @return [MB] usedMem: Used RAM. - def stats - filterSpec = RbVmomi::VIM.PropertyFilterSpec( - :objectSet => [{ - :obj => self, - :selectSet => [ - RbVmomi::VIM.TraversalSpec( - :name => 'tsHosts', - :type => 'ComputeResource', - :path => 'host', - :skip => false - ) - ] - }], - :propSet => [{ - :pathSet => %w(name overallStatus summary.hardware.cpuMhz - summary.hardware.numCpuCores summary.hardware.memorySize - summary.quickStats.overallCpuUsage - summary.quickStats.overallMemoryUsage), - :type => 'HostSystem' - }] - ) +module RbVmomi + module VIM + class ComputeResource + # Aggregate cluster information. + # + # @note Values are returned in a hash. + # + # @return [Mhz] totalCPU: Sum of the frequencies of each CPU in the cluster. + # @return [Mhz] usedCPU: CPU cycles used across the cluster. + # @return [MB] totalMem: Total RAM. + # @return [MB] usedMem: Used RAM. + def stats + filterSpec = RbVmomi::VIM.PropertyFilterSpec( + objectSet: [{ + obj: self, + selectSet: [ + RbVmomi::VIM.TraversalSpec( + name: "tsHosts", + type: "ComputeResource", + path: "host", + skip: false + ) + ] + }], + propSet: [{ + pathSet: %w(name overallStatus summary.hardware.cpuMhz + summary.hardware.numCpuCores summary.hardware.memorySize + summary.quickStats.overallCpuUsage + summary.quickStats.overallMemoryUsage), + type: "HostSystem" + }] + ) - result = _connection.propertyCollector.RetrieveProperties(:specSet => [filterSpec]) + result = _connection.propertyCollector.RetrieveProperties(specSet: [filterSpec]) - stats = { - :totalCPU => 0, - :totalMem => 0, - :usedCPU => 0, - :usedMem => 0, - } + stats = { + totalCPU: 0, + totalMem: 0, + usedCPU: 0, + usedMem: 0 + } - result.each do |x| - next if x['overallStatus'] == 'red' - stats[:totalCPU] += x['summary.hardware.cpuMhz'] * x['summary.hardware.numCpuCores'] - stats[:totalMem] += x['summary.hardware.memorySize'] / (1024*1024) - stats[:usedCPU] += x['summary.quickStats.overallCpuUsage'] || 0 - stats[:usedMem] += x['summary.quickStats.overallMemoryUsage'] || 0 - end + result.each do |x| + next if x["overallStatus"] == "red" + + stats[:totalCPU] += x["summary.hardware.cpuMhz"] * x["summary.hardware.numCpuCores"] + stats[:totalMem] += x["summary.hardware.memorySize"] / (1024 * 1024) + stats[:usedCPU] += x["summary.quickStats.overallCpuUsage"] || 0 + stats[:usedMem] += x["summary.quickStats.overallMemoryUsage"] || 0 + end - stats + stats + end + end end end diff --git a/lib/rbvmomi/vim/Datacenter.rb b/lib/rbvmomi/vim/Datacenter.rb index 29c24993..cfbe746a 100644 --- a/lib/rbvmomi/vim/Datacenter.rb +++ b/lib/rbvmomi/vim/Datacenter.rb @@ -1,25 +1,29 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::Datacenter - # Traverse the given inventory +path+ to find a ComputeResource. - def find_compute_resource path - hostFolder.traverse path, RbVmomi::VIM::ComputeResource - end +module RbVmomi + module VIM + class Datacenter + # Traverse the given inventory +path+ to find a ComputeResource. + def find_compute_resource path + hostFolder.traverse path, RbVmomi::VIM::ComputeResource + end - # Find the Datastore with the given +name+. - def find_datastore name - datastore.find { |x| x.name == name } - end + # Find the Datastore with the given +name+. + def find_datastore name + datastore.find { |x| x.name == name } + end - # Traverse the given inventory +path+ to find a VirtualMachine. - def find_vm path - vmFolder.traverse path, RbVmomi::VIM::VirtualMachine - end + # Traverse the given inventory +path+ to find a VirtualMachine. + 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 + # Traverse the given inventory +path+ to find a Folder. + def find_folder path + vmFolder.traverse path, RbVmomi::VIM::Folder + end + end end end - diff --git a/lib/rbvmomi/vim/Datastore.rb b/lib/rbvmomi/vim/Datastore.rb index 558b155b..6b8a7ca6 100644 --- a/lib/rbvmomi/vim/Datastore.rb +++ b/lib/rbvmomi/vim/Datastore.rb @@ -1,72 +1,77 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT # @note +download+ and +upload+ require +curl+. If +curl+ is not in your +PATH+ # then set the +CURL+ environment variable to point to it. # @todo Use an HTTP library instead of executing +curl+. -class RbVmomi::VIM::Datastore - CURLBIN = ENV['CURL'] || "curl" #@private +module RbVmomi + module VIM + class Datastore + CURLBIN = ENV["CURL"] || "curl" # @private - # Check whether a file exists on this datastore. - # @param path [String] Path on the datastore. - def exists? path - req = Net::HTTP::Head.new mkuripath(path) - req.initialize_http_header 'cookie' => _connection.cookie - resp = _connection.http.request req - case resp - when Net::HTTPSuccess - true - when Net::HTTPNotFound - false - else - fail resp.inspect - end - end + # Check whether a file exists on this datastore. + # @param path [String] Path on the datastore. + def exists? path + req = Net::HTTP::Head.new mkuripath(path) + req.initialize_http_header "cookie" => _connection.cookie + resp = _connection.http.request req + case resp + when Net::HTTPSuccess + true + when Net::HTTPNotFound + false + else + raise resp.inspect + end + end - # Download a file from this datastore. - # @param remote_path [String] Source path on the datastore. - # @param local_path [String] Destination path on the local machine. - # @return [void] - def download remote_path, local_path - url = "http#{_connection.http.use_ssl? ? 's' : ''}://#{_connection.http.address}:#{_connection.http.port}#{mkuripath(remote_path)}" - pid = spawn CURLBIN, "-k", '--noproxy', '*', '-f', - "-o", local_path, - "-b", _connection.cookie, - url, - :out => '/dev/null' - Process.waitpid(pid, 0) - fail "download failed" unless $?.success? - end + # Download a file from this datastore. + # @param remote_path [String] Source path on the datastore. + # @param local_path [String] Destination path on the local machine. + # @return [void] + def download remote_path, local_path + url = "http#{_connection.http.use_ssl? ? 's' : ''}://#{_connection.http.address}:#{_connection.http.port}#{mkuripath(remote_path)}" + pid = spawn CURLBIN, "-k", "--noproxy", "*", "-f", + "-o", local_path, + "-b", _connection.cookie, + url, + out: "/dev/null" + Process.waitpid(pid, 0) + raise "download failed" unless $CHILD_STATUS.success? + end - # Upload a file to this datastore. - # @param remote_path [String] Destination path on the datastore. - # @param local_path [String] Source path on the local machine. - # @return [void] - def upload remote_path, local_path - url = "http#{_connection.http.use_ssl? ? 's' : ''}://#{_connection.http.address}:#{_connection.http.port}#{mkuripath(remote_path)}" - pid = spawn CURLBIN, "-k", '--noproxy', '*', '-f', - "-T", local_path, - "-b", _connection.cookie, - url, - :out => '/dev/null' - Process.waitpid(pid, 0) - fail "upload failed" unless $?.success? - end + # Upload a file to this datastore. + # @param remote_path [String] Destination path on the datastore. + # @param local_path [String] Source path on the local machine. + # @return [void] + def upload remote_path, local_path + url = "http#{_connection.http.use_ssl? ? 's' : ''}://#{_connection.http.address}:#{_connection.http.port}#{mkuripath(remote_path)}" + pid = spawn CURLBIN, "-k", "--noproxy", "*", "-f", + "-T", local_path, + "-b", _connection.cookie, + url, + out: "/dev/null" + Process.waitpid(pid, 0) + raise "upload failed" unless $CHILD_STATUS.success? + end - private + private - def datacenter - return @datacenter if @datacenter - x = parent - while not x.is_a? RbVmomi::VIM::Datacenter - x = x.parent - end - fail unless x.is_a? RbVmomi::VIM::Datacenter - @datacenter = x - end + def datacenter + return @datacenter if @datacenter + + x = parent + x = x.parent until x.is_a? RbVmomi::VIM::Datacenter + raise unless x.is_a? RbVmomi::VIM::Datacenter - def mkuripath path - datacenter_path_str = datacenter.path[1..-1].map{|elem| elem[1]}.join('/') - "/folder/#{URI.encode_www_form_component path}?dcPath=#{URI.encode_www_form_component datacenter_path_str }&dsName=#{URI.encode_www_form_component name}" + @datacenter = x + end + + def mkuripath path + datacenter_path_str = datacenter.path[1..-1].map { |elem| elem[1]}.join("/") + "/folder/#{URI.encode_www_form_component path}?dcPath=#{URI.encode_www_form_component datacenter_path_str}&dsName=#{URI.encode_www_form_component name}" + end + end end end diff --git a/lib/rbvmomi/vim/DynamicTypeMgrAllTypeInfo.rb b/lib/rbvmomi/vim/DynamicTypeMgrAllTypeInfo.rb index 79bff691..4f597e42 100644 --- a/lib/rbvmomi/vim/DynamicTypeMgrAllTypeInfo.rb +++ b/lib/rbvmomi/vim/DynamicTypeMgrAllTypeInfo.rb @@ -1,78 +1,83 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::DynamicTypeMgrAllTypeInfo - def toRbvmomiTypeHash - id2name = {} - id2name.merge!({ - 'string' => 'xsd:string', - 'java.lang.String' => 'xsd:string', - 'BOOLEAN' => 'xsd:boolean', - 'BYTE' => 'xsd:byte', - 'SHORT' => 'xsd:short', - 'INT' => 'xsd:int', - 'LONG' => 'xsd:long', - 'FLOAT' => 'xsd:float', - 'DOUBLE' => 'xsd:double', - 'boolean' => 'xsd:boolean', - 'byte' => 'xsd:byte', - 'short' => 'xsd:short', - 'int' => 'xsd:int', - 'long' => 'xsd:long', - 'float' => 'xsd:float', - 'double' => 'xsd:double', - 'vmodl.DateTime' => 'xsd:dateTime', - 'vmodl.Binary' => 'xsd:base64Binary', - 'vmodl.Any' => 'xsd:anyType', - 'vim.KeyValue' => 'KeyValue', - 'void' => nil, - }) +module RbVmomi + module VIM + class DynamicTypeMgrAllTypeInfo + def toRbvmomiTypeHash + id2name = {} + id2name.merge!({ + "string" => "xsd:string", + "java.lang.String" => "xsd:string", + "BOOLEAN" => "xsd:boolean", + "BYTE" => "xsd:byte", + "SHORT" => "xsd:short", + "INT" => "xsd:int", + "LONG" => "xsd:long", + "FLOAT" => "xsd:float", + "DOUBLE" => "xsd:double", + "boolean" => "xsd:boolean", + "byte" => "xsd:byte", + "short" => "xsd:short", + "int" => "xsd:int", + "long" => "xsd:long", + "float" => "xsd:float", + "double" => "xsd:double", + "vmodl.DateTime" => "xsd:dateTime", + "vmodl.Binary" => "xsd:base64Binary", + "vmodl.Any" => "xsd:anyType", + "vim.KeyValue" => "KeyValue", + "void" => nil + }) - %w(DataObject ManagedObject MethodFault MethodName DynamicData - PropertyPath RuntimeFault TypeName).each do |x| - id2name['vmodl.' + x] = x - end - - types = {} - self.managedTypeInfo.each{|x| types.merge!(x.toRbvmomiTypeHash) } - self.dataTypeInfo.each{|x| types.merge!(x.toRbvmomiTypeHash) } + %w(DataObject ManagedObject MethodFault MethodName DynamicData + PropertyPath RuntimeFault TypeName).each do |x| + id2name["vmodl." + x] = x + end - types.each do |k,t| - id2name[t['type-id']] = k - end + types = {} + managedTypeInfo.each { |x| types.merge!(x.toRbvmomiTypeHash) } + dataTypeInfo.each { |x| types.merge!(x.toRbvmomiTypeHash) } - types = Hash[types.map do |k,t| - case t['kind'] - when 'data' - t['wsdl_base'] = t['base-type-id'] ? id2name[t['base-type-id']] : 'DataObject' - #t.delete 'base-type-id' - t['props'].each do |x| - x['wsdl_type'] = id2name[x['type-id-ref']] - x.delete 'type-id-ref' - end - when 'managed' - t['wsdl_base'] = t['base-type-id'] ? id2name[t['base-type-id']] : 'ManagedObject' - #t.delete 'base-type-id' - t['props'].each do |x| - x['wsdl_type'] = id2name[x['type-id-ref']] - x.delete 'type-id-ref' + types.each do |k, t| + id2name[t["type-id"]] = k end - t['methods'].each do |mName,x| - if y = x['result'] - y['wsdl_type'] = id2name[y['type-id-ref']] - #y.delete 'type-id-ref' - end - x['params'].each do |r| - r['wsdl_type'] = id2name[r['type-id-ref']] - r.delete 'type-id-ref' + + types = Hash[types.map do |k, t| + case t["kind"] + when "data" + t["wsdl_base"] = t["base-type-id"] ? id2name[t["base-type-id"]] : "DataObject" + # t.delete 'base-type-id' + t["props"].each do |x| + x["wsdl_type"] = id2name[x["type-id-ref"]] + x.delete "type-id-ref" + end + when "managed" + t["wsdl_base"] = t["base-type-id"] ? id2name[t["base-type-id"]] : "ManagedObject" + # t.delete 'base-type-id' + t["props"].each do |x| + x["wsdl_type"] = id2name[x["type-id-ref"]] + x.delete "type-id-ref" + end + t["methods"].each do |_mName, x| + if y = x["result"] + y["wsdl_type"] = id2name[y["type-id-ref"]] + # y.delete 'type-id-ref' + end + x["params"].each do |r| + r["wsdl_type"] = id2name[r["type-id-ref"]] + r.delete "type-id-ref" + end + end + when "enum" + else raise end - end - when 'enum' - else fail - end - [k, t] - end] + [k, t] + end] - types + types + end + end end end diff --git a/lib/rbvmomi/vim/DynamicTypeMgrDataTypeInfo.rb b/lib/rbvmomi/vim/DynamicTypeMgrDataTypeInfo.rb index c5be6128..10cf7874 100644 --- a/lib/rbvmomi/vim/DynamicTypeMgrDataTypeInfo.rb +++ b/lib/rbvmomi/vim/DynamicTypeMgrDataTypeInfo.rb @@ -1,23 +1,28 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::DynamicTypeMgrDataTypeInfo - def toRbvmomiTypeHash - { - self.wsdlName => { - 'kind' => 'data', - 'type-id' => self.name, - 'base-type-id' => self.base.first, - 'props' => self.property.map do |prop| - { - 'name' => prop.name, - 'type-id-ref' => prop.type.gsub("[]", ""), - 'is-array' => (prop.type =~ /\[\]$/) ? true : false, - 'is-optional' => prop.annotation.find{|a| a.name == "optional"} ? true : false, - 'version-id-ref' => prop.version, +module RbVmomi + module VIM + class DynamicTypeMgrDataTypeInfo + def toRbvmomiTypeHash + { + wsdlName => { + "kind" => "data", + "type-id" => name, + "base-type-id" => base.first, + "props" => property.map do |prop| + { + "name" => prop.name, + "type-id-ref" => prop.type.gsub("[]", ""), + "is-array" => (prop.type =~ /\[\]$/) ? true : false, + "is-optional" => prop.annotation.find { |a| a.name == "optional"} ? true : false, + "version-id-ref" => prop.version + } + end } - end, - } - } + } + end + end end end diff --git a/lib/rbvmomi/vim/DynamicTypeMgrManagedTypeInfo.rb b/lib/rbvmomi/vim/DynamicTypeMgrManagedTypeInfo.rb index 848208ee..eea062b4 100644 --- a/lib/rbvmomi/vim/DynamicTypeMgrManagedTypeInfo.rb +++ b/lib/rbvmomi/vim/DynamicTypeMgrManagedTypeInfo.rb @@ -1,54 +1,58 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::DynamicTypeMgrManagedTypeInfo - def toRbvmomiTypeHash - { - self.wsdlName => { - 'kind' => 'managed', - 'type-id' => self.name, - 'base-type-id' => self.base.first, - 'props' => self.property.map do |prop| - { - 'name' => prop.name, - 'type-id-ref' => prop.type.gsub("[]", ""), - 'is-array' => (prop.type =~ /\[\]$/) ? true : false, - 'is-optional' => prop.annotation.find{|a| a.name == "optional"} ? true : false, - 'version-id-ref' => prop.version, - } - end, - 'methods' => Hash[ - self.method.map do |method| - result = method.returnTypeInfo +module RbVmomi + module VIM + class DynamicTypeMgrManagedTypeInfo + def toRbvmomiTypeHash + { + wsdlName => { + "kind" => "managed", + "type-id" => name, + "base-type-id" => base.first, + "props" => property.map do |prop| + { + "name" => prop.name, + "type-id-ref" => prop.type.gsub("[]", ""), + "is-array" => (prop.type =~ /\[\]$/) ? true : false, + "is-optional" => prop.annotation.find { |a| a.name == "optional"} ? true : false, + "version-id-ref" => prop.version + } + end, + "methods" => Hash[ + method.map do |method| + result = method.returnTypeInfo - [method.wsdlName, - { - 'params' => method.paramTypeInfo.map do |param| - { - 'name' => param.name, - 'type-id-ref' => param.type.gsub("[]", ""), - 'is-array' => (param.type =~ /\[\]$/) ? true : false, - 'is-optional' => param.annotation.find{|a| a.name == "optional"} ? true : false, - 'version-id-ref' => param.version, - } - end, - 'result' => ( - if result.nil? then - nil - else + [method.wsdlName, { - '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) - } + "params" => method.paramTypeInfo.map do |param| + { + "name" => param.name, + "type-id-ref" => param.type.gsub("[]", ""), + "is-array" => (param.type =~ /\[\]$/) ? true : false, + "is-optional" => param.annotation.find { |a| a.name == "optional"} ? true : false, + "version-id-ref" => param.version + } + end, + "result" => ( + if result.nil? + 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 ] - end - ] - } - } + } + } + end + end end end diff --git a/lib/rbvmomi/vim/Folder.rb b/lib/rbvmomi/vim/Folder.rb index 3da5721c..aefcc41e 100644 --- a/lib/rbvmomi/vim/Folder.rb +++ b/lib/rbvmomi/vim/Folder.rb @@ -1,214 +1,220 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::Folder - # Retrieve a child entity - # @param name [String] Name of the child. - # @param type [Class] Return nil unless the found entity is_a? type. - # @return [VIM::ManagedEntity] - def find name, type=Object - x = _connection.searchIndex.FindChild(:entity => self, :name => name) - x if x.is_a? type - end - - # Retrieve a virtual machine or host by DNS name - # @param name [String] The fully qualified domain name to find. - # @param type [Class] Return nil unless the found entity is_a? type. - # @param dc [RbVmomi::VIM::Datacenter] Restricts the query to entities in the given Datacenter. - # @return [VIM::ManagedEntity] - def findByDnsName name, type=RbVmomi::VIM::VirtualMachine, dc=nil - propSpecs = { - :entity => self, :dnsName => name, - :vmSearch => type == RbVmomi::VIM::VirtualMachine - } - propSpecs[:datacenter] = dc if dc - x = _connection.searchIndex.FindByDnsName(propSpecs) - x if x.is_a? type - end - - # Retrieve a virtual machine or host by IP address - # @param ip [String] The IP address is in dot-decimal notation. - # @param type [Class] Return nil unless the found entity is_a? type. - # @param dc [RbVmomi::VIM::Datacenter] Restricts the query to entities in the given Datacenter. - # @return [VIM::ManagedEntity] - def findByIp ip, type=RbVmomi::VIM::VirtualMachine, dc=nil - propSpecs = { - :entity => self, :ip => ip, - :vmSearch => type == RbVmomi::VIM::VirtualMachine - } - propSpecs[:datacenter] = dc if dc - x = _connection.searchIndex.FindByIp(propSpecs) - x if x.is_a? type - end - - # Finds a virtual machine or host by BIOS or instance UUID - # - # @param uuid [String] UUID to find - # @param type [Class] return nil unless found entity is_a?(type) - # @param dc [RbVmomi::VIM::Datacenter] restricts query to specified datacenter - # - # @return [VIM::ManagedEntity] - def findByUuid(uuid, type = RbVmomi::VIM::VirtualMachine, dc = nil, instance_uuid = false) - prop_specs = { - :entity => self, - :instanceUuid => instance_uuid, - :uuid => uuid, - :vmSearch => type == RbVmomi::VIM::VirtualMachine - } - prop_specs[:datacenter] = dc if dc - x = _connection.searchIndex.FindByUuid(prop_specs) - x if x.is_a?(type) - end - - # Retrieve a managed entity by inventory path. - # @param path [String] A path of the form "My Folder/My Datacenter/vm/Discovered VM/VM1" - # @return [VIM::ManagedEntity] - def findByInventoryPath path - propSpecs = { - :entity => self, :inventoryPath => path - } - _connection.searchIndex.FindByInventoryPath(propSpecs) - end +module RbVmomi + module VIM + class Folder + # Retrieve a child entity + # @param name [String] Name of the child. + # @param type [Class] Return nil unless the found entity is_a? type. + # @return [VIM::ManagedEntity] + def find name, type=Object + x = _connection.searchIndex.FindChild(entity: self, name: name) + x if x.is_a? type + end - # Alias to traverse path, type, true - # @see #traverse - def traverse! path, type=Object - traverse path, type, true - end + # Retrieve a virtual machine or host by DNS name + # @param name [String] The fully qualified domain name to find. + # @param type [Class] Return nil unless the found entity is_a? type. + # @param dc [RbVmomi::VIM::Datacenter] Restricts the query to entities in the given Datacenter. + # @return [VIM::ManagedEntity] + def findByDnsName name, type=RbVmomi::VIM::VirtualMachine, dc=nil + propSpecs = { + entity: self, dnsName: name, + vmSearch: type == RbVmomi::VIM::VirtualMachine + } + propSpecs[:datacenter] = dc if dc + x = _connection.searchIndex.FindByDnsName(propSpecs) + x if x.is_a? type + end - # Retrieve a descendant of this Folder. - # @param path [String] Path delimited by '/', or an array of path elements. - # @param type (see Folder#find) - # @param create [Boolean] If set, create folders that don't exist. - # @return (see Folder#find) - # @todo Move +create+ functionality into another method. - def traverse path, type=Object, create=false - if path.is_a? String - es = path.split('/').reject(&:empty?) - elsif path.is_a? Enumerable - es = path - else - fail "unexpected path class #{path.class}" - end - return self if es.empty? - final = es.pop + # Retrieve a virtual machine or host by IP address + # @param ip [String] The IP address is in dot-decimal notation. + # @param type [Class] Return nil unless the found entity is_a? type. + # @param dc [RbVmomi::VIM::Datacenter] Restricts the query to entities in the given Datacenter. + # @return [VIM::ManagedEntity] + def findByIp ip, type=RbVmomi::VIM::VirtualMachine, dc=nil + propSpecs = { + entity: self, ip: ip, + vmSearch: type == RbVmomi::VIM::VirtualMachine + } + propSpecs[:datacenter] = dc if dc + x = _connection.searchIndex.FindByIp(propSpecs) + x if x.is_a? type + end - p = es.inject(self) do |f,e| - f.find(e, RbVmomi::VIM::Folder) || (create && f.CreateFolder(:name => e)) || return - end + # Finds a virtual machine or host by BIOS or instance UUID + # + # @param uuid [String] UUID to find + # @param type [Class] return nil unless found entity is_a?(type) + # @param dc [RbVmomi::VIM::Datacenter] restricts query to specified datacenter + # + # @return [VIM::ManagedEntity] + def findByUuid(uuid, type = RbVmomi::VIM::VirtualMachine, dc = nil, instance_uuid = false) + prop_specs = { + entity: self, + instanceUuid: instance_uuid, + uuid: uuid, + vmSearch: type == RbVmomi::VIM::VirtualMachine + } + prop_specs[:datacenter] = dc if dc + x = _connection.searchIndex.FindByUuid(prop_specs) + x if x.is_a?(type) + end - if x = p.find(final, type) - x - elsif create and type == RbVmomi::VIM::Folder - p.CreateFolder(:name => final) - elsif create and type == RbVmomi::VIM::Datacenter - p.CreateDatacenter(:name => final) - else - nil - end - end + # Retrieve a managed entity by inventory path. + # @param path [String] A path of the form "My Folder/My Datacenter/vm/Discovered VM/VM1" + # @return [VIM::ManagedEntity] + def findByInventoryPath path + propSpecs = { + entity: self, inventoryPath: path + } + _connection.searchIndex.FindByInventoryPath(propSpecs) + end - # Alias to +childEntity+. - def children - childEntity - end + # Alias to traverse path, type, true + # @see #traverse + def traverse! path, type=Object + traverse path, type, true + end - # Efficiently retrieve properties from descendants of this folder. - # - # @param propSpecs [Hash] Specification of which properties to retrieve from - # which entities. Keys may be symbols, strings, or - # classes identifying ManagedEntity subtypes to be - # included in the results. Values are an array of - # property paths (strings) or the symbol :all. - # - # @return [Hash] Hash of ManagedObjects to properties. - def inventory_flat propSpecs={} - propSet = [{ :type => 'Folder', :pathSet => ['name', 'parent', 'childEntity'] }] - propSpecs.each do |k,v| - case k - when Class - fail "key must be a subclass of ManagedEntity" unless k < RbVmomi::VIM::ManagedEntity - k = k.wsdl_name - when Symbol, String - k = k.to_s - else - fail "invalid key" + # Retrieve a descendant of this Folder. + # @param path [String] Path delimited by '/', or an array of path elements. + # @param type (see Folder#find) + # @param create [Boolean] If set, create folders that don't exist. + # @return (see Folder#find) + # @todo Move +create+ functionality into another method. + def traverse path, type=Object, create=false + if path.is_a? String + es = path.split("/").reject(&:empty?) + elsif path.is_a? Enumerable + es = path + else + raise "unexpected path class #{path.class}" + end + return self if es.empty? + + final = es.pop + + p = es.inject(self) do |f, e| + f.find(e, RbVmomi::VIM::Folder) || (create && f.CreateFolder(name: e)) || return + end + + if x = p.find(final, type) + x + elsif create && (type == RbVmomi::VIM::Folder) + p.CreateFolder(name: final) + elsif create && (type == RbVmomi::VIM::Datacenter) + p.CreateDatacenter(name: final) + end end - h = { :type => k } - if v == :all - h[:all] = true - elsif v.is_a? Array - h[:pathSet] = v + %w(parent) - else - fail "value must be an array of property paths or :all" + # Alias to +childEntity+. + def children + childEntity end - propSet << h - end - filterSpec = RbVmomi::VIM.PropertyFilterSpec( - :objectSet => [ - :obj => self, - :selectSet => [ - RbVmomi::VIM.TraversalSpec( - :name => 'tsFolder', - :type => 'Folder', - :path => 'childEntity', - :skip => false, - :selectSet => [ - RbVmomi::VIM.SelectionSpec(:name => 'tsFolder') + # Efficiently retrieve properties from descendants of this folder. + # + # @param propSpecs [Hash] Specification of which properties to retrieve from + # which entities. Keys may be symbols, strings, or + # classes identifying ManagedEntity subtypes to be + # included in the results. Values are an array of + # property paths (strings) or the symbol :all. + # + # @return [Hash] Hash of ManagedObjects to properties. + def inventory_flat propSpecs={} + propSet = [{ type: "Folder", pathSet: %w(name parent childEntity) }] + propSpecs.each do |k, v| + case k + when Class + raise "key must be a subclass of ManagedEntity" unless k < RbVmomi::VIM::ManagedEntity + + k = k.wsdl_name + when Symbol, String + k = k.to_s + else + raise "invalid key" + end + + h = { type: k } + if v == :all + h[:all] = true + elsif v.is_a? Array + h[:pathSet] = v + %w(parent) + else + raise "value must be an array of property paths or :all" + end + propSet << h + end + + filterSpec = RbVmomi::VIM.PropertyFilterSpec( + objectSet: [ + obj: self, + selectSet: [ + RbVmomi::VIM.TraversalSpec( + name: "tsFolder", + type: "Folder", + path: "childEntity", + skip: false, + selectSet: [ + RbVmomi::VIM.SelectionSpec(name: "tsFolder") + ] + ) ] - ) - ] - ], - :propSet => propSet - ) - - result = _connection.propertyCollector.RetrieveProperties(:specSet => [filterSpec]) - {}.tap do |h| - result.each { |r| h[r.obj] = r } - end - end + ], + propSet: propSet + ) + + result = _connection.propertyCollector.RetrieveProperties(specSet: [filterSpec]) + {}.tap do |h| + result.each { |r| h[r.obj] = r } + end + end - # Efficiently retrieve properties from descendants of this folder. - # - # @param propSpecs [Hash] Specification of which properties to retrieve from - # which entities. Keys may be symbols, strings, or - # classes identifying ManagedEntity subtypes to be - # included in the results. Values are an array of - # property paths (strings) or the symbol :all. - # - # @return [Hash] Tree of inventory items. Each node is a hash from - # VIM::ObjectContent to children. - def inventory_tree propSpecs={} - inv = inventory_flat propSpecs - children = inv.values.group_by { |v| v['parent'] } - rec = lambda { |parent| Hash[(children[parent]||[]).map { |x| [x, rec[x.obj]] }] } - rec[self] - end + # Efficiently retrieve properties from descendants of this folder. + # + # @param propSpecs [Hash] Specification of which properties to retrieve from + # which entities. Keys may be symbols, strings, or + # classes identifying ManagedEntity subtypes to be + # included in the results. Values are an array of + # property paths (strings) or the symbol :all. + # + # @return [Hash] Tree of inventory items. Each node is a hash from + # VIM::ObjectContent to children. + def inventory_tree propSpecs={} + inv = inventory_flat propSpecs + children = inv.values.group_by { |v| v["parent"] } + rec = ->(parent) { Hash[(children[parent] || []).map { |x| [x, rec[x.obj]] }] } + rec[self] + end - # Efficiently retrieve properties from descendants of this folder. - # - # @param propSpecs [Hash] Specification of which properties to retrieve from - # which entities. Keys may be symbols, strings, or - # classes identifying ManagedEntity subtypes to be - # included in the results. Values are an array of - # property paths (strings) or the symbol :all. - # - # @return [Hash] Tree of inventory items. Folders are hashes from child name - # to child result. Objects are hashes from property path to - # value. - # - # @deprecated - def inventory propSpecs={} - inv = inventory_flat propSpecs - tree = { self => {} } - inv.each do |obj,x| - next if obj == self - h = Hash[x.propSet.map { |y| [y.name, y.val] }] - tree[h['parent']][h['name']] = [obj, h] - tree[obj] = {} if obj.is_a? RbVmomi::VIM::Folder + # Efficiently retrieve properties from descendants of this folder. + # + # @param propSpecs [Hash] Specification of which properties to retrieve from + # which entities. Keys may be symbols, strings, or + # classes identifying ManagedEntity subtypes to be + # included in the results. Values are an array of + # property paths (strings) or the symbol :all. + # + # @return [Hash] Tree of inventory items. Folders are hashes from child name + # to child result. Objects are hashes from property path to + # value. + # + # @deprecated + def inventory propSpecs={} + inv = inventory_flat propSpecs + tree = { self => {} } + inv.each do |obj, x| + next if obj == self + + h = Hash[x.propSet.map { |y| [y.name, y.val] }] + tree[h["parent"]][h["name"]] = [obj, h] + tree[obj] = {} if obj.is_a? RbVmomi::VIM::Folder + end + tree + end end - tree end end diff --git a/lib/rbvmomi/vim/HostSystem.rb b/lib/rbvmomi/vim/HostSystem.rb index c6e5d690..3cd0f453 100644 --- a/lib/rbvmomi/vim/HostSystem.rb +++ b/lib/rbvmomi/vim/HostSystem.rb @@ -1,177 +1,184 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT module RbVmomi + module VIM + class HostSystem + def esxcli + @cached_esxcli ||= VIM::EsxcliNamespace.root(self) + end -class VIM::HostSystem - def esxcli - @cached_esxcli ||= VIM::EsxcliNamespace.root(self) - end - - def dtm - @cached_dtm ||= begin - RetrieveDynamicTypeManager() - rescue VIM::MethodNotFound - if summary.config.product.version >= '4.1.0' - if summary.config.product.version < '5.0.0' and direct? - VIM::InternalDynamicTypeManager(_connection, 'ha-dynamic-type-manager') - else - raise "esxcli not supported through VC before 5.0.0" + def dtm + @cached_dtm ||= begin + RetrieveDynamicTypeManager() + rescue VIM::MethodNotFound + if summary.config.product.version >= "4.1.0" + if (summary.config.product.version < "5.0.0") && direct? + VIM::InternalDynamicTypeManager(_connection, "ha-dynamic-type-manager") + else + raise "esxcli not supported through VC before 5.0.0" + end + else + raise "esxcli not supported before 4.1.0" + end end - else - raise "esxcli not supported before 4.1.0" - end end - end - def dti - @cached_dti ||= dtm.DynamicTypeMgrQueryTypeInfo - end - - def create_dynamic_managed_object inst - wsdlName = dti.managedTypeInfo.find { |x| x.name == inst.moType }.wsdlName - _connection.type(wsdlName).new(_connection, inst.id) - end - - def cli_info_fetcher - # XXX there can be more than one - return @cached_cli_info_fetcher if @cached_cli_info_fetcher - inst = dtm.DynamicTypeMgrQueryMoInstances.find { |x| x.moType == 'vim.CLIInfo' } - @cached_cli_info_fetcher = create_dynamic_managed_object inst - end + def dti + @cached_dti ||= dtm.DynamicTypeMgrQueryTypeInfo + end - def mme - @cached_mme ||= RetrieveManagedMethodExecuter() - end + def create_dynamic_managed_object inst + wsdlName = dti.managedTypeInfo.find { |x| x.name == inst.moType }.wsdlName + _connection.type(wsdlName).new(_connection, inst.id) + end - def direct? - @ref == 'ha-host' - end -end + def cli_info_fetcher + # XXX there can be more than one + return @cached_cli_info_fetcher if @cached_cli_info_fetcher -class VIM::EsxcliNamespace - ESXCLI_PREFIX = 'vim.EsxCLI.' + inst = dtm.DynamicTypeMgrQueryMoInstances.find { |x| x.moType == "vim.CLIInfo" } + @cached_cli_info_fetcher = create_dynamic_managed_object inst + end - attr_reader :name, :parent, :host, :type, :instance, :type_info, :namespaces, :commands + def mme + @cached_mme ||= RetrieveManagedMethodExecuter() + end - def self.root host - type_hash = host.dti.toRbvmomiTypeHash - VIM.loader.add_types type_hash - all_instances = host.dtm.DynamicTypeMgrQueryMoInstances - instances = Hash[all_instances.select { |x| x.moType.start_with? ESXCLI_PREFIX }. - map { |x| [x.moType,x.id] }] - type_infos = Hash[host.dti.managedTypeInfo.map { |x| [x.name,x] }] - new('root', nil, host).tap do |root| - instances.each do |type,instance| - path = type.split('.')[2..-1] - ns = path.inject(root) { |b,v| b.namespaces[v] } - ns.realize type, instance, type_infos[type] + def direct? + @ref == "ha-host" end end end - def initialize name, parent, host - @name = name - @parent = parent - @host = host - @type = nil - @instance = nil - @type_info = nil - @namespaces = Hash.new { |h,k| h[k] = self.class.new k, self, host } - @commands = {} - @cached_cli_info = nil - end + module VIM + class EsxcliNamespace + ESXCLI_PREFIX = "vim.EsxCLI." + + attr_reader :name, :parent, :host, :type, :instance, :type_info, :namespaces, :commands + + def self.root host + type_hash = host.dti.toRbvmomiTypeHash + VIM.loader.add_types type_hash + all_instances = host.dtm.DynamicTypeMgrQueryMoInstances + instances = Hash[all_instances.select { |x| x.moType.start_with? ESXCLI_PREFIX } + .map { |x| [x.moType, x.id] }] + type_infos = Hash[host.dti.managedTypeInfo.map { |x| [x.name, x] }] + new("root", nil, host).tap do |root| + instances.each do |type, instance| + path = type.split(".")[2..-1] + ns = path.inject(root) { |b, v| b.namespaces[v] } + ns.realize type, instance, type_infos[type] + end + end + end - def realize type, instance, type_info - fail if @type or @instance - @type = type - @instance = instance - @type_info = type_info - @type_info.method.each do |method_type_info| - name = method_type_info.name - @commands[name] = VIM::EsxcliCommand.new self, method_type_info - end - end + def initialize name, parent, host + @name = name + @parent = parent + @host = host + @type = nil + @instance = nil + @type_info = nil + @namespaces = Hash.new { |h, k| h[k] = self.class.new k, self, host } + @commands = {} + @cached_cli_info = nil + end - def type_name - if @type then @type - elsif @parent then "#{@parent.type_name}.#{@name}" - else 'vim.EsxCLI' - end - end + def realize type, instance, type_info + raise if @type || @instance - def cli_info - @cached_cli_info ||= - if @host.direct? - @host.cli_info_fetcher.VimCLIInfoFetchCLIInfo(:typeName => type_name) - else - @host.mme.execute(@host.cli_info_fetcher._ref, - "vim.CLIInfo.FetchCLIInfo", :typeName => type_name) + @type = type + @instance = instance + @type_info = type_info + @type_info.method.each do |method_type_info| + name = method_type_info.name + @commands[name] = VIM::EsxcliCommand.new self, method_type_info + end end - end - def obj - conn = @host._connection - conn.type(@type_info.wsdlName).new(conn, @instance) - end + def type_name + if @type then @type + elsif @parent then "#{@parent.type_name}.#{@name}" + else "vim.EsxCLI" + end + end - def method_missing(name, *args) - name = name.to_s - if @namespaces.member? name and args.empty? - @namespaces[name] - elsif @commands.member? name - @commands[name].call(*args) - else - raise NoMethodError - end - end + def cli_info + @cached_cli_info ||= + if @host.direct? + @host.cli_info_fetcher.VimCLIInfoFetchCLIInfo(typeName: type_name) + else + @host.mme.execute(@host.cli_info_fetcher._ref, + "vim.CLIInfo.FetchCLIInfo", typeName: type_name) + end + end - def pretty_print q - q.text @name - q.text ' ' - q.group 2 do - q.text '{' - q.breakable - items = (@namespaces.values+@commands.values).sort_by(&:name) - q.seplist items, nil, :each do |v| - if v.is_a? VIM::EsxcliNamespace - q.pp v + def obj + conn = @host._connection + conn.type(@type_info.wsdlName).new(conn, @instance) + end + + def method_missing(name, *args) + name = name.to_s + if @namespaces.member?(name) && args.empty? + @namespaces[name] + elsif @commands.member? name + @commands[name].call(*args) else - q.text v.name + raise NoMethodError + end + end + + def pretty_print q + q.text @name + q.text " " + q.group 2 do + q.text "{" + q.breakable + items = (@namespaces.values + @commands.values).sort_by(&:name) + q.seplist items, nil, :each do |v| + if v.is_a? VIM::EsxcliNamespace + q.pp v + else + q.text v.name + end + end end + q.breakable + q.text "}" end end - q.breakable - q.text '}' end -end -class VIM::EsxcliCommand - attr_reader :ns, :type_info, :cli_info + module VIM + class EsxcliCommand + attr_reader :ns, :type_info, :cli_info - def initialize ns, type_info - @ns = ns - @type_info = type_info - @cached_cli_info = nil - end + def initialize ns, type_info + @ns = ns + @type_info = type_info + @cached_cli_info = nil + end - def name - @type_info.name - end + def name + @type_info.name + end - def cli_info - @cached_cli_info ||= @ns.cli_info.method.find { |x| x.name == @type_info.name } - end + def cli_info + @cached_cli_info ||= @ns.cli_info.method.find { |x| x.name == @type_info.name } + end - def call args={} - if @ns.host.direct? - @ns.obj._call @type_info.wsdlName, args - else - real_args = Set.new(type_info.paramTypeInfo.map(&:name)) - args = args.reject { |k,v| !real_args.member?(k.to_s) } - @ns.host.mme.execute(@ns.obj._ref, "#{@ns.type_name}.#{@type_info.name}", args) + def call args={} + if @ns.host.direct? + @ns.obj._call @type_info.wsdlName, args + else + real_args = Set.new(type_info.paramTypeInfo.map(&:name)) + args = args.reject { |k, _v| !real_args.member?(k.to_s) } + @ns.host.mme.execute(@ns.obj._ref, "#{@ns.type_name}.#{@type_info.name}", args) + end + end end end end - -end diff --git a/lib/rbvmomi/vim/ManagedEntity.rb b/lib/rbvmomi/vim/ManagedEntity.rb index 8809ad3d..fcf7c8a5 100644 --- a/lib/rbvmomi/vim/ManagedEntity.rb +++ b/lib/rbvmomi/vim/ManagedEntity.rb @@ -1,60 +1,65 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::ManagedEntity - # Retrieve the ancestors of the entity. - # @return [Array] Ancestors of this entity, starting with the root. - def path - self.class.paths([self])[self] - end - - # Retrieve the ancestors of a list of entries. - # @return [Hash] Object-indexed hash of ancestors of entities, starting with the root. - def self.paths objs - filterSpec = RbVmomi::VIM.PropertyFilterSpec( - :objectSet => objs.map do |obj| - RbVmomi::VIM.ObjectSpec( - :obj => obj, - :selectSet => [ - RbVmomi::VIM.TraversalSpec( - :name => "tsME", - :type => 'ManagedEntity', - :path => 'parent', - :skip => false, - :selectSet => [ - RbVmomi::VIM.SelectionSpec(:name => "tsME") +module RbVmomi + module VIM + class ManagedEntity + # Retrieve the ancestors of the entity. + # @return [Array] Ancestors of this entity, starting with the root. + def path + self.class.paths([self])[self] + end + + # Retrieve the ancestors of a list of entries. + # @return [Hash] Object-indexed hash of ancestors of entities, starting with the root. + def self.paths objs + filterSpec = RbVmomi::VIM.PropertyFilterSpec( + objectSet: objs.map do |obj| + RbVmomi::VIM.ObjectSpec( + obj: obj, + selectSet: [ + RbVmomi::VIM.TraversalSpec( + name: "tsME", + type: "ManagedEntity", + path: "parent", + skip: false, + selectSet: [ + RbVmomi::VIM.SelectionSpec(name: "tsME") + ] + ) ] ) - ] + end, + propSet: [{ + pathSet: %w(name parent), + type: "ManagedEntity" + }] ) - end, - :propSet => [{ - :pathSet => %w(name parent), - :type => 'ManagedEntity' - }] - ) - propCollector = objs.first._connection.propertyCollector - result = propCollector.RetrieveProperties(:specSet => [filterSpec]) + propCollector = objs.first._connection.propertyCollector + result = propCollector.RetrieveProperties(specSet: [filterSpec]) - Hash[objs.map do |obj| - tree = {} - result.each { |x| tree[x.obj] = [x['parent'], x['name']] } - a = [] - cur = obj - while cur - parent, name = *tree[cur] - a << [cur, name] - cur = parent + Hash[objs.map do |obj| + tree = {} + result.each { |x| tree[x.obj] = [x["parent"], x["name"]] } + a = [] + cur = obj + while cur + parent, name = *tree[cur] + a << [cur, name] + cur = parent + end + [obj, a.reverse] + end] end - [obj, a.reverse] - end] - end - # Return a string representation of +path+ suitable for display. - # @return [String] - # @see #path - def pretty_path - path[1..-1].map { |x| x[1] } * '/' + # Return a string representation of +path+ suitable for display. + # @return [String] + # @see #path + def pretty_path + path[1..-1].map { |x| x[1] } * "/" + end + end end end diff --git a/lib/rbvmomi/vim/ManagedObject.rb b/lib/rbvmomi/vim/ManagedObject.rb index 895d1ac8..f4b5b15a 100644 --- a/lib/rbvmomi/vim/ManagedObject.rb +++ b/lib/rbvmomi/vim/ManagedObject.rb @@ -1,63 +1,67 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::ManagedObject - # Wait for updates on an object until a condition becomes true. - # - # @param pathSet [Array] Property paths to wait for updates to. - # @yield Called when an update to a subscribed property occurs. - # @yieldreturn [Boolean] Whether to stop waiting. - # - # @todo Pass the current property values to the block. - def wait_until *pathSet, &b - all = pathSet.empty? - filter = _connection.propertyCollector.CreateFilter :spec => { - :propSet => [{ :type => self.class.wsdl_name, :all => all, :pathSet => pathSet }], - :objectSet => [{ :obj => self }], - }, :partialUpdates => false - ver = '' - loop do - result = _connection.propertyCollector.WaitForUpdates(:version => ver) - ver = result.version - if x = b.call - return x +module RbVmomi + module VIM + class ManagedObject + # Wait for updates on an object until a condition becomes true. + # + # @param pathSet [Array] Property paths to wait for updates to. + # @yield Called when an update to a subscribed property occurs. + # @yieldreturn [Boolean] Whether to stop waiting. + # + # @todo Pass the current property values to the block. + def wait_until *pathSet, &b + all = pathSet.empty? + filter = _connection.propertyCollector.CreateFilter spec: { + propSet: [{ type: self.class.wsdl_name, all: all, pathSet: pathSet }], + objectSet: [{ obj: self }] + }, partialUpdates: false + ver = "" + loop do + result = _connection.propertyCollector.WaitForUpdates(version: ver) + ver = result.version + x = b.call + return x if x + end + ensure + filter.DestroyPropertyFilter if filter end - end - ensure - filter.DestroyPropertyFilter if filter - end - # Efficiently retrieve multiple properties from an object. - # @param pathSet [Array] Properties to return. - # @return [Hash] Hash from property paths to values. - def collect! *pathSet - spec = { - :objectSet => [{ :obj => self }], - :propSet => [{ - :pathSet => pathSet, - :type => self.class.wsdl_name - }] - } - ret = _connection.propertyCollector.RetrieveProperties(:specSet => [spec]) - if ret && ret.length > 0 - ret[0].to_hash - else - {} - end - end + # Efficiently retrieve multiple properties from an object. + # @param pathSet [Array] Properties to return. + # @return [Hash] Hash from property paths to values. + def collect! *pathSet + spec = { + objectSet: [{ obj: self }], + propSet: [{ + pathSet: pathSet, + type: self.class.wsdl_name + }] + } + ret = _connection.propertyCollector.RetrieveProperties(specSet: [spec]) + if ret && !ret.empty? + ret[0].to_hash + else + {} + end + end - # Efficiently retrieve multiple properties from an object. - # @param pathSet (see #collect!) - # @yield [*values] Property values in same order as +pathSet+. - # @return [Array] Property values in same order as +pathSet+, or the return - # value from the block if it is given. - def collect *pathSet - h = collect!(*pathSet) - a = pathSet.map { |k| h[k.to_s] } - if block_given? - yield a - else - a + # Efficiently retrieve multiple properties from an object. + # @param pathSet (see #collect!) + # @yield [*values] Property values in same order as +pathSet+. + # @return [Array] Property values in same order as +pathSet+, or the return + # value from the block if it is given. + def collect *pathSet + h = collect!(*pathSet) + a = pathSet.map { |k| h[k.to_s] } + if block_given? + yield a + else + a + end + end end end end diff --git a/lib/rbvmomi/vim/ObjectContent.rb b/lib/rbvmomi/vim/ObjectContent.rb index e5360bc3..c3863f68 100644 --- a/lib/rbvmomi/vim/ObjectContent.rb +++ b/lib/rbvmomi/vim/ObjectContent.rb @@ -1,26 +1,32 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::ObjectContent - # Represent this ObjectContent as a hash. - # @return [Hash] A hash from property paths to values. - def to_hash - @cached_hash ||= to_hash_uncached - end +module RbVmomi + module VIM + class ObjectContent + # Represent this ObjectContent as a hash. + # @return [Hash] A hash from property paths to values. + def to_hash + @cached_hash ||= to_hash_uncached + end - # Alias for +to_hash[k]+. - def [](k) - to_hash[k] - end + # Alias for +to_hash[k]+. + def [](k) + to_hash[k] + end + + private - private + def to_hash_uncached + h = {} + propSet.each do |x| + raise if h.member? x.name - def to_hash_uncached - h = {} - propSet.each do |x| - fail if h.member? x.name - h[x.name] = x.val + h[x.name] = x.val + end + h + end end - h end end diff --git a/lib/rbvmomi/vim/ObjectUpdate.rb b/lib/rbvmomi/vim/ObjectUpdate.rb index be25b0d7..ad034bcc 100644 --- a/lib/rbvmomi/vim/ObjectUpdate.rb +++ b/lib/rbvmomi/vim/ObjectUpdate.rb @@ -1,26 +1,32 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::ObjectUpdate - # Represent this ObjectUpdate as a hash. - # @return [Hash] A hash from property paths to values. - def to_hash - @cached_hash ||= to_hash_uncached - end +module RbVmomi + module VIM + class ObjectUpdate + # Represent this ObjectUpdate as a hash. + # @return [Hash] A hash from property paths to values. + def to_hash + @cached_hash ||= to_hash_uncached + end - # Alias for +to_hash[k]+. - def [](k) - to_hash[k] - end + # Alias for +to_hash[k]+. + def [](k) + to_hash[k] + end + + private - private + def to_hash_uncached + h = {} + changeSet.each do |x| + raise if h.member? x.name - def to_hash_uncached - h = {} - changeSet.each do |x| - fail if h.member? x.name - h[x.name] = x.val + h[x.name] = x.val + end + h + end end - h end end diff --git a/lib/rbvmomi/vim/OvfManager.rb b/lib/rbvmomi/vim/OvfManager.rb index dce7e0d4..34d228e6 100644 --- a/lib/rbvmomi/vim/OvfManager.rb +++ b/lib/rbvmomi/vim/OvfManager.rb @@ -1,204 +1,209 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT # @note +deployOVF+ and requires +curl+. If +curl+ is not in your +PATH+ # then set the +CURL+ environment variable to point to it. # @todo Use an HTTP library instead of executing +curl+. -class RbVmomi::VIM::OvfManager - CURLBIN = ENV['CURL'] || "curl" #@private - - # Deploy an OVF. - # - # @param [Hash] opts The options hash. - # @option opts [String] :uri Location of the OVF. - # @option opts [String] :vmName Name of the new VM. - # @option opts [VIM::Folder] :vmFolder Folder to place the VM in. - # @option opts [VIM::HostSystem] :host Host to use. - # @option opts [VIM::ResourcePool] :resourcePool Resource pool to use. - # @option opts [VIM::Datastore] :datastore Datastore to use. - # @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 => {}, - :diskProvisioning => :thin }.merge opts - - %w(uri vmName vmFolder host resourcePool datastore).each do |k| - fail "parameter #{k} required" unless opts[k.to_sym] - end - - ovfImportSpec = RbVmomi::VIM::OvfCreateImportSpecParams( - :hostSystem => opts[:host], - :locale => "US", - :entityName => opts[:vmName], - :deploymentOption => opts[:deploymentOption] || "", - :networkMapping => opts[:networkMappings].map{|from, to| RbVmomi::VIM::OvfNetworkMapping(:name => from, :network => to)}, - :propertyMapping => opts[:propertyMappings].to_a, - :diskProvisioning => opts[:diskProvisioning] - ) - - result = CreateImportSpec( - :ovfDescriptor => open(opts[:uri]).read, - :resourcePool => opts[:resourcePool], - :datastore => opts[:datastore], - :cisp => ovfImportSpec - ) - - raise result.error[0].localizedMessage if result.error && !result.error.empty? - - if result.warning - result.warning.each{|x| puts "OVF Warning: #{x.localizedMessage.chomp}" } - end +module RbVmomi + module VIM + class OvfManager + CURLBIN = ENV["CURL"] || "curl" # @private - importSpec = result.importSpec - if importSpec && importSpec.instantiationOst && importSpec.instantiationOst.child - importSpec.instantiationOst.child.each do |child| - child.section.map do |section| - section.xml = _handle_ost(section.xml, opts) - end - end - end - - nfcLease = opts[:resourcePool].ImportVApp(:spec => importSpec, - :folder => opts[:vmFolder], - :host => opts[:host]) - - nfcLease.wait_until(:state) { nfcLease.state != "initializing" } - raise nfcLease.error if nfcLease.state == "error" - begin - nfcLease.HttpNfcLeaseProgress(:percent => 5) - timeout, = nfcLease.collect 'info.leaseTimeout' - puts "DEBUG: Timeout: #{timeout}" - if timeout < 4 * 60 - puts "WARNING: OVF upload NFC lease timeout less than 4 minutes" - end - progress = 5.0 - result.fileItem.each do |fileItem| - leaseInfo, leaseState, leaseError = nfcLease.collect 'info', 'state', 'error' - # Retry nfcLease.collect because of PR 969599: - # If retrying property collector works, this means there is a network - # or VC overloading problem. - retrynum = 5 - i = 1 - while i <= retrynum && !leaseState - puts "Retrying at iteration #{i}" - sleep 1 - leaseInfo, leaseState, leaseError = nfcLease.collect 'info', 'state', 'error' - i += 1 - end - if leaseState != "ready" - raise "NFC lease is no longer ready: #{leaseState}: #{leaseError}" - end - if leaseInfo == nil - raise "NFC lease disappeared?" + # Deploy an OVF. + # + # @param [Hash] opts The options hash. + # @option opts [String] :uri Location of the OVF. + # @option opts [String] :vmName Name of the new VM. + # @option opts [VIM::Folder] :vmFolder Folder to place the VM in. + # @option opts [VIM::HostSystem] :host Host to use. + # @option opts [VIM::ResourcePool] :resourcePool Resource pool to use. + # @option opts [VIM::Datastore] :datastore Datastore to use. + # @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: {}, + diskProvisioning: :thin }.merge opts + + %w(uri vmName vmFolder host resourcePool datastore).each do |k| + raise "parameter #{k} required" unless opts[k.to_sym] end - deviceUrl = leaseInfo.deviceUrl.find{|x| x.importKey == fileItem.deviceId} - if !deviceUrl - raise "Couldn't find deviceURL for device '#{fileItem.deviceId}'" + + ovfImportSpec = RbVmomi::VIM::OvfCreateImportSpecParams( + hostSystem: opts[:host], + locale: "US", + entityName: opts[:vmName], + deploymentOption: opts[:deploymentOption] || "", + networkMapping: opts[:networkMappings].map { |from, to| RbVmomi::VIM::OvfNetworkMapping(name: from, network: to)}, + propertyMapping: opts[:propertyMappings].to_a, + diskProvisioning: opts[:diskProvisioning] + ) + + result = CreateImportSpec( + ovfDescriptor: open(opts[:uri]).read, + resourcePool: opts[:resourcePool], + datastore: opts[:datastore], + cisp: ovfImportSpec + ) + + raise result.error[0].localizedMessage if result.error && !result.error.empty? + + result.warning.each { |x| puts "OVF Warning: #{x.localizedMessage.chomp}" } if result.warning + + importSpec = result.importSpec + if importSpec && importSpec.instantiationOst && importSpec.instantiationOst.child + importSpec.instantiationOst.child.each do |child| + child.section.map do |section| + section.xml = _handle_ost(section.xml, opts) + end + end end - ovfFilename = opts[:uri].to_s - tmp = ovfFilename.split(/\//) - tmp.pop - tmp << fileItem.path - filename = tmp.join("/") + nfcLease = opts[:resourcePool].ImportVApp(spec: importSpec, + folder: opts[:vmFolder], + host: opts[:host]) - # If filename doesn't have a URI scheme, we're considering it a local file - if URI(filename).scheme.nil? - filename = "file://" + filename - end + nfcLease.wait_until(:state) { nfcLease.state != "initializing" } + raise nfcLease.error if nfcLease.state == "error" - method = fileItem.create ? "PUT" : "POST" + begin + nfcLease.HttpNfcLeaseProgress(percent: 5) + timeout, = nfcLease.collect "info.leaseTimeout" + puts "DEBUG: Timeout: #{timeout}" + puts "WARNING: OVF upload NFC lease timeout less than 4 minutes" if timeout < 4 * 60 + progress = 5.0 + result.fileItem.each do |fileItem| + leaseInfo, leaseState, leaseError = nfcLease.collect "info", "state", "error" + # Retry nfcLease.collect because of PR 969599: + # If retrying property collector works, this means there is a network + # or VC overloading problem. + retrynum = 5 + i = 1 + while i <= retrynum && !leaseState + puts "Retrying at iteration #{i}" + sleep 1 + leaseInfo, leaseState, leaseError = nfcLease.collect "info", "state", "error" + i += 1 + end + raise "NFC lease is no longer ready: #{leaseState}: #{leaseError}" if leaseState != "ready" + raise "NFC lease disappeared?" if leaseInfo.nil? + + deviceUrl = leaseInfo.deviceUrl.find { |x| x.importKey == fileItem.deviceId} + raise "Couldn't find deviceURL for device '#{fileItem.deviceId}'" unless deviceUrl + + ovfFilename = opts[:uri].to_s + tmp = ovfFilename.split(%r{/}) + tmp.pop + tmp << fileItem.path + filename = tmp.join("/") + + # If filename doesn't have a URI scheme, we're considering it a local file + filename = "file://" + filename if URI(filename).scheme.nil? + + method = fileItem.create ? "PUT" : "POST" + + keepAliveThread = Thread.new do + loop do + nfcLease.HttpNfcLeaseProgress(percent: progress.to_i) + sleep 1 * 60 + end + end + + i = 1 + ip = nil + loop do + begin + puts "Iteration #{i}: Trying to get host's IP address ..." + ip = opts[:host].config.network.vnic[0].spec.ip.ipAddress + rescue Exception => e + puts "Iteration #{i}: Couldn't get host's IP address: #{e}" + end + sleep 1 + i += 1 + break unless i <= 5 && !ip + end + raise "Couldn't get host's IP address" unless ip + + href = deviceUrl.url.gsub("*", ip) + downloadCmd = "#{CURLBIN} -L '#{URI.escape(filename)}'" + uploadCmd = "#{CURLBIN} -Ss -X #{method} --insecure -T - -H 'Content-Type: application/x-vnd.vmware-streamVmdk' '#{URI.escape(href)}'" + # Previously we used to append "-H 'Content-Length: #{fileItem.size}'" + # to the uploadCmd. It is not clear to me why, but that leads to + # trucation of the uploaded disk. Without this option curl can't tell + # the progress, but who cares + system("#{downloadCmd} | #{uploadCmd}", out: "/dev/null") + + keepAliveThread.kill + keepAliveThread.join + + progress += (90.0 / result.fileItem.length) + nfcLease.HttpNfcLeaseProgress(percent: progress.to_i) + end - keepAliveThread = Thread.new do - while true - nfcLease.HttpNfcLeaseProgress(:percent => progress.to_i) - sleep 1 * 60 + nfcLease.HttpNfcLeaseProgress(percent: 100) + raise nfcLease.error if nfcLease.state == "error" + + i = 1 + vm = nil + loop do + begin + puts "Iteration #{i}: Trying to access nfcLease.info.entity ..." + vm = nfcLease.info.entity + rescue Exception => e + puts "Iteration #{i}: Couldn't access nfcLease.info.entity: #{e}" + end + sleep 1 + i += 1 + break unless i <= 5 && !vm + end + raise "Couldn't access nfcLease.info.entity" unless vm + + # Ignore sporadic connection errors caused by PR 1019166.. + # Three attempts are made to execute HttpNfcLeaseComplete. + # Not critical if none goes through, as long as vm is obtained + # + # TODO: find the reason why HttpNfcLeaseComplete gets a wrong + # response (RetrievePropertiesResponse) + i = 0 + begin + nfcLease.HttpNfcLeaseComplete + puts "HttpNfcLeaseComplete succeeded" + rescue RbVmomi::VIM::InvalidState + puts "HttpNfcLeaseComplete already finished.." + rescue Exception => e + puts "HttpNfcLeaseComplete failed at iteration #{i} with exception: #{e}" + i += 1 + retry if i < 3 + puts "Giving up HttpNfcLeaseComplete.." end + vm end - - i = 1 - ip = nil - begin + rescue Exception + if nfcLease begin - puts "Iteration #{i}: Trying to get host's IP address ..." - ip = opts[:host].config.network.vnic[0].spec.ip.ipAddress - rescue Exception=>e - puts "Iteration #{i}: Couldn't get host's IP address: #{e}" + nfcLease.HttpNfcLeaseAbort + rescue StandardError + nil end - sleep 1 - i += 1 - end while i <= 5 && !ip - raise "Couldn't get host's IP address" unless ip - href = deviceUrl.url.gsub("*", ip) - downloadCmd = "#{CURLBIN} -L '#{URI::escape(filename)}'" - uploadCmd = "#{CURLBIN} -Ss -X #{method} --insecure -T - -H 'Content-Type: application/x-vnd.vmware-streamVmdk' '#{URI::escape(href)}'" - # Previously we used to append "-H 'Content-Length: #{fileItem.size}'" - # to the uploadCmd. It is not clear to me why, but that leads to - # trucation of the uploaded disk. Without this option curl can't tell - # the progress, but who cares - system("#{downloadCmd} | #{uploadCmd}", :out => "/dev/null") - - keepAliveThread.kill - keepAliveThread.join - - progress += (90.0 / result.fileItem.length) - nfcLease.HttpNfcLeaseProgress(:percent => progress.to_i) + end + raise end - nfcLease.HttpNfcLeaseProgress(:percent => 100) - raise nfcLease.error if nfcLease.state == "error" - i = 1 - vm = nil - begin - begin - puts "Iteration #{i}: Trying to access nfcLease.info.entity ..." - vm = nfcLease.info.entity - rescue Exception=>e - puts "Iteration #{i}: Couldn't access nfcLease.info.entity: #{e}" + def _handle_ost ost, opts = {} + ost = Nokogiri::XML(ost) + if opts[:vservice] == ["com.vmware.vim.vsm:extension_vservice"] + ost.xpath("//vmw:Annotations/vmw:Providers/vmw:Provider").each do |x| + x["vmw:selected"] = "selected" + end + ost.xpath("//vmw:Annotations/vmw:Providers").each do |x| + x["vmw:selected"] = "com.vmware.vim.vsm:extension_vservice" + end end - sleep 1 - i += 1 - end while i <= 5 && !vm - raise "Couldn't access nfcLease.info.entity" unless vm - - # Ignore sporadic connection errors caused by PR 1019166.. - # Three attempts are made to execute HttpNfcLeaseComplete. - # Not critical if none goes through, as long as vm is obtained - # - # TODO: find the reason why HttpNfcLeaseComplete gets a wrong - # response (RetrievePropertiesResponse) - i = 0 - begin - nfcLease.HttpNfcLeaseComplete - puts "HttpNfcLeaseComplete succeeded" - rescue RbVmomi::VIM::InvalidState - puts "HttpNfcLeaseComplete already finished.." - rescue Exception => e - puts "HttpNfcLeaseComplete failed at iteration #{i} with exception: #{e}" - i += 1 - retry if i < 3 - puts "Giving up HttpNfcLeaseComplete.." - end - vm - end - rescue Exception - (nfcLease.HttpNfcLeaseAbort rescue nil) if nfcLease - raise - end - - def _handle_ost ost, opts = {} - ost = Nokogiri::XML(ost) - if opts[:vservice] == ['com.vmware.vim.vsm:extension_vservice'] - ost.xpath('//vmw:Annotations/vmw:Providers/vmw:Provider').each do |x| - x['vmw:selected'] = 'selected' - end - ost.xpath('//vmw:Annotations/vmw:Providers').each do |x| - x['vmw:selected'] = 'com.vmware.vim.vsm:extension_vservice' + ost.to_s end end - ost.to_s end end diff --git a/lib/rbvmomi/vim/PerfCounterInfo.rb b/lib/rbvmomi/vim/PerfCounterInfo.rb index 0583eb2f..f8252caa 100644 --- a/lib/rbvmomi/vim/PerfCounterInfo.rb +++ b/lib/rbvmomi/vim/PerfCounterInfo.rb @@ -1,17 +1,18 @@ +# frozen_string_literal: true # Copyright (c) 2012-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -20,9 +21,12 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -class RbVmomi::VIM::PerfCounterInfo - def name - "#{groupInfo.key}.#{nameInfo.key}" +module RbVmomi + module VIM + class PerfCounterInfo + def name + "#{groupInfo.key}.#{nameInfo.key}" + end + end end end - diff --git a/lib/rbvmomi/vim/PerformanceManager.rb b/lib/rbvmomi/vim/PerformanceManager.rb index ee38bdc6..c42004b0 100644 --- a/lib/rbvmomi/vim/PerformanceManager.rb +++ b/lib/rbvmomi/vim/PerformanceManager.rb @@ -1,7 +1,8 @@ +# frozen_string_literal: true # Copyright (c) 2012-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'date' +require "date" class Time def to_datetime @@ -16,98 +17,96 @@ def to_datetime end RbVmomi::VIM::PerformanceManager -class RbVmomi::VIM::PerformanceManager - def perfcounter_cached - @perfcounter ||= perfCounter - end - - def perfcounter_hash - @perfcounter_hash ||= Hash[perfcounter_cached.map{|x| [x.name, x]}] - end - - def perfcounter_idhash - @perfcounter_idhash ||= Hash[perfcounter_cached.map{|x| [x.key, x]}] - end - - def provider_summary obj - @provider_summary ||= {} - @provider_summary[obj.class] ||= QueryPerfProviderSummary(:entity => obj) - end +module RbVmomi + module VIM + class PerformanceManager + def perfcounter_cached + @perfcounter ||= perfCounter + end - def retrieve_stats objects, metrics, opts = {} - opts = opts.dup - max_samples = opts[:max_samples] || 1 - realtime = false - if not opts[:interval] - provider = provider_summary objects.first - opts[:interval] = provider.refreshRate - realtime = true - else - provider = provider_summary objects.first - if opts[:interval] == provider.refreshRate - realtime = true + def perfcounter_hash + @perfcounter_hash ||= Hash[perfcounter_cached.map { |x| [x.name, x]}] end - end - - instances = opts[:instance] || '*' - if !instances.is_a?(Array) - instances = [instances] - end - metric_ids = [] - metrics.each do |x| - counter = perfcounter_hash[x] - if !counter - pp perfcounter_hash.keys - fail "Counter for #{x} couldn't be found" + + def perfcounter_idhash + @perfcounter_idhash ||= Hash[perfcounter_cached.map { |x| [x.key, x]}] end - instances.each do |instance| - metric_ids << RbVmomi::VIM::PerfMetricId(:counterId => counter.key, - :instance => instance) + + def provider_summary obj + @provider_summary ||= {} + @provider_summary[obj.class] ||= QueryPerfProviderSummary(entity: obj) end - end - query_specs = objects.map do |obj| - RbVmomi::VIM::PerfQuerySpec({ - :maxSample => max_samples, - :entity => obj, - :metricId => metric_ids, - :intervalId => opts[:interval], - :startTime => (realtime == false ? opts[:start_time].to_datetime : nil), - }) - end - stats = QueryPerf(:querySpec => query_specs) - - if !opts[:multi_instance] - Hash[stats.map do |res| - [ - res.entity, - { - :sampleInfo => res.sampleInfo, - :metrics => Hash[res.value.map do |metric| - metric_name = perfcounter_idhash[metric.id.counterId].name - [metric_name, metric.value] - end] - } - ] - end] - else - Hash[stats.map do |res| - [ - res.entity, - { - :sampleInfo => res.sampleInfo, - :metrics => Hash[res.value.map do |metric| - metric_name = perfcounter_idhash[metric.id.counterId].name - [[metric_name, metric.id.instance], metric.value] - end] - } - ] - end] - end - end + def retrieve_stats objects, metrics, opts = {} + opts = opts.dup + max_samples = opts[:max_samples] || 1 + realtime = false + provider = provider_summary objects.first + if !(opts[:interval]) + opts[:interval] = provider.refreshRate + realtime = true + else + realtime = true if opts[:interval] == provider.refreshRate + end + + instances = opts[:instance] || "*" + instances = [instances] unless instances.is_a?(Array) + metric_ids = [] + metrics.each do |x| + counter = perfcounter_hash[x] + unless counter + pp perfcounter_hash.keys + raise "Counter for #{x} couldn't be found" + end + instances.each do |instance| + metric_ids << RbVmomi::VIM::PerfMetricId(counterId: counter.key, + instance: instance) + end + end + query_specs = objects.map do |obj| + RbVmomi::VIM::PerfQuerySpec({ + maxSample: max_samples, + entity: obj, + metricId: metric_ids, + intervalId: opts[:interval], + startTime: (realtime == false ? opts[:start_time].to_datetime : nil) + }) + end + stats = QueryPerf(querySpec: query_specs) - def active_intervals - intervals = historicalInterval - Hash[(1..4).map { |level| [level, intervals.select { |x| x.enabled && x.level >= level }] }] + if !opts[:multi_instance] + Hash[stats.map do |res| + [ + res.entity, + { + sampleInfo: res.sampleInfo, + metrics: Hash[res.value.map do |metric| + metric_name = perfcounter_idhash[metric.id.counterId].name + [metric_name, metric.value] + end] + } + ] + end] + else + Hash[stats.map do |res| + [ + res.entity, + { + sampleInfo: res.sampleInfo, + metrics: Hash[res.value.map do |metric| + metric_name = perfcounter_idhash[metric.id.counterId].name + [[metric_name, metric.id.instance], metric.value] + end] + } + ] + end] + end + end + + def active_intervals + intervals = historicalInterval + Hash[(1..4).map { |level| [level, intervals.select { |x| x.enabled && x.level >= level }] }] + end + end end end diff --git a/lib/rbvmomi/vim/PropertyCollector.rb b/lib/rbvmomi/vim/PropertyCollector.rb index d3f1b27c..51387606 100644 --- a/lib/rbvmomi/vim/PropertyCollector.rb +++ b/lib/rbvmomi/vim/PropertyCollector.rb @@ -1,28 +1,33 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::PropertyCollector - def collectMultiple objs, *pathSet - return {} if objs.empty? +module RbVmomi + module VIM + class PropertyCollector + def collectMultiple objs, *pathSet + return {} if objs.empty? - klasses = objs.map{|x| x.class}.uniq - klass = if klasses.length > 1 - # common superclass - klasses.map(&:ancestors).inject(&:&)[0] - else - klasses.first - end + klasses = objs.map(&:class).uniq + klass = if klasses.length > 1 + # common superclass + klasses.map(&:ancestors).inject(&:&)[0] + else + klasses.first + end - spec = { - :objectSet => objs.map{|x| { :obj => x }}, - :propSet => [{ - :pathSet => pathSet, - :type => klass.wsdl_name - }] - } - res = RetrieveProperties(:specSet => [spec]) - Hash[res.map do |x| - [x.obj, x.to_hash] - end] + spec = { + objectSet: objs.map { |x| { obj: x }}, + propSet: [{ + pathSet: pathSet, + type: klass.wsdl_name + }] + } + res = RetrieveProperties(specSet: [spec]) + Hash[res.map do |x| + [x.obj, x.to_hash] + end] + end + end end end diff --git a/lib/rbvmomi/vim/ReflectManagedMethodExecuter.rb b/lib/rbvmomi/vim/ReflectManagedMethodExecuter.rb index 1c3c70a8..da80d0a1 100644 --- a/lib/rbvmomi/vim/ReflectManagedMethodExecuter.rb +++ b/lib/rbvmomi/vim/ReflectManagedMethodExecuter.rb @@ -1,33 +1,29 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT module RbVmomi + module VIM + class ReflectManagedMethodExecuter + def fetch moid, prop + result = FetchSoap(moid: moid, version: "urn:vim25/6.5", prop: prop) + xml = Nokogiri(result.response) + _connection.deserializer.deserialize xml.root, nil + end -class VIM::ReflectManagedMethodExecuter - def fetch moid, prop - result = FetchSoap(:moid => moid, :version => 'urn:vim25/6.5', :prop => prop) - xml = Nokogiri(result.response) - _connection.deserializer.deserialize xml.root, nil - end - - def execute moid, method, args - soap_args = args.map do |k,v| - VIM::ReflectManagedMethodExecuterSoapArgument.new.tap do |soap_arg| - soap_arg.name = k - xml = Builder::XmlMarkup.new :indent => 0 - _connection.obj2xml xml, k, :anyType, false, v - soap_arg.val = xml.target! + def execute moid, method, args + soap_args = args.map do |k, v| + VIM::ReflectManagedMethodExecuterSoapArgument.new.tap do |soap_arg| + soap_arg.name = k + xml = Builder::XmlMarkup.new indent: 0 + _connection.obj2xml xml, k, :anyType, false, v + soap_arg.val = xml.target! + end + end + result = ExecuteSoap(moid: moid, version: "urn:vim25/6.5", + method: method, argument: soap_args) + _connection.deserializer.deserialize Nokogiri(result.response).root, nil if result end end - result = ExecuteSoap(:moid => moid, :version => 'urn:vim25/6.5', - :method => method, :argument => soap_args) - if result - _connection.deserializer.deserialize Nokogiri(result.response).root, nil - else - nil - end end end - -end - diff --git a/lib/rbvmomi/vim/ResourcePool.rb b/lib/rbvmomi/vim/ResourcePool.rb index 32b6b26a..db7341c6 100644 --- a/lib/rbvmomi/vim/ResourcePool.rb +++ b/lib/rbvmomi/vim/ResourcePool.rb @@ -1,58 +1,63 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::ResourcePool - # Retrieve a child ResourcePool. - # @param name [String] Name of the child. - # @return [VIM::ResourcePool] - def find name - _connection.searchIndex.FindChild(:entity => self, :name => name) - end +module RbVmomi + module VIM + class ResourcePool + # Retrieve a child ResourcePool. + # @param name [String] Name of the child. + # @return [VIM::ResourcePool] + def find(name) + _connection.searchIndex.FindChild(entity: self, name: name) + end - # Retrieve a descendant of this ResourcePool. - # @param path [String] Path delimited by '/'. - # @return [VIM::ResourcePool] - def traverse path - es = path.split('/').reject(&:empty?) - es.inject(self) do |f,e| - f.find(e) || return - end - end + # Retrieve a descendant of this ResourcePool. + # @param path [String] Path delimited by '/'. + # @return [VIM::ResourcePool] + def traverse(path) + es = path.split("/").reject(&:empty?) + es.inject(self) do |f, e| + f.find(e) || next + end + end - def resourcePoolSubTree fields = [] - self.class.resourcePoolSubTree [self], fields - end - - def self.resourcePoolSubTree objs, fields = [] - fields = (fields + ['name', 'resourcePool']).uniq - filterSpec = RbVmomi::VIM.PropertyFilterSpec( - :objectSet => objs.map do |obj| - RbVmomi::VIM.ObjectSpec( - :obj => obj, - :selectSet => [ - RbVmomi::VIM.TraversalSpec( - :name => "tsRP", - :type => 'ResourcePool', - :path => 'resourcePool', - :skip => false, - :selectSet => [ - RbVmomi::VIM.SelectionSpec(:name => "tsRP") + def resourcePoolSubTree(fields = []) + self.class.resourcePoolSubTree [self], fields + end + + def self.resourcePoolSubTree objs, fields = [] + fields = (fields + %w(name resourcePool)).uniq + filterSpec = RbVmomi::VIM.PropertyFilterSpec( + objectSet: objs.map do |obj| + RbVmomi::VIM.ObjectSpec( + obj: obj, + selectSet: [ + RbVmomi::VIM.TraversalSpec( + name: "tsRP", + type: "ResourcePool", + path: "resourcePool", + skip: false, + selectSet: [ + RbVmomi::VIM.SelectionSpec(name: "tsRP") + ] + ) ] ) - ] + end, + propSet: [{ + pathSet: fields, + type: "ResourcePool" + }] ) - end, - :propSet => [{ - :pathSet => fields, - :type => 'ResourcePool' - }] - ) - - propCollector = objs.first._connection.propertyCollector - result = propCollector.RetrieveProperties(:specSet => [filterSpec]) - - Hash[result.map do |x| - [x.obj, x.to_hash] - end] + + propCollector = objs.first._connection.propertyCollector + result = propCollector.RetrieveProperties(specSet: [filterSpec]) + + Hash[result.map do |x| + [x.obj, x.to_hash] + end] + end + end end end diff --git a/lib/rbvmomi/vim/ServiceInstance.rb b/lib/rbvmomi/vim/ServiceInstance.rb index 19861032..8e8c1139 100644 --- a/lib/rbvmomi/vim/ServiceInstance.rb +++ b/lib/rbvmomi/vim/ServiceInstance.rb @@ -1,58 +1,63 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::ServiceInstance - # Retrieve a Datacenter. - # If no path is given the first datacenter will be returned. - # @param path (see Folder#traverse) - # @return [Datacenter] - def find_datacenter path=nil - if path - content.rootFolder.traverse path, RbVmomi::VIM::Datacenter - else - content.rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).first - end - end +module RbVmomi + module VIM + class ServiceInstance + # Retrieve a Datacenter. + # If no path is given the first datacenter will be returned. + # @param path (see Folder#traverse) + # @return [Datacenter] + def find_datacenter path=nil + if path + content.rootFolder.traverse path, RbVmomi::VIM::Datacenter + else + content.rootFolder.childEntity.grep(RbVmomi::VIM::Datacenter).first + end + end - # Wait for several tasks to complete. - # @param interested [Array] Property paths to watch for updates. - # @param tasks [Array] Tasks to wait on. - # @yield [Hash] Called when a property is updated on a task. - # The parameter is a hash from tasks to hashes from - # property path to value. - # @return [void] - def wait_for_multiple_tasks interested, tasks - version = '' - interested = (interested + ['info.state']).uniq - task_props = Hash.new { |h,k| h[k] = {} } - - filter = _connection.propertyCollector.CreateFilter :spec => { - :propSet => [{ :type => 'Task', :all => false, :pathSet => interested }], - :objectSet => tasks.map { |x| { :obj => x } }, - }, :partialUpdates => false - - begin - until task_props.size == tasks.size and task_props.all? { |k,h| %w(success error).member? h['info.state'] } - result = _connection.propertyCollector.WaitForUpdates(:version => version) - version = result.version - os = result.filterSet[0].objectSet - - os.each do |o| - changes = Hash[o.changeSet.map { |x| [x.name, x.val] }] - - interested.each do |k| - task = tasks.find { |x| x._ref == o.obj._ref } - task_props[task][k] = changes[k] if changes.member? k + # Wait for several tasks to complete. + # @param interested [Array] Property paths to watch for updates. + # @param tasks [Array] Tasks to wait on. + # @yield [Hash] Called when a property is updated on a task. + # The parameter is a hash from tasks to hashes from + # property path to value. + # @return [void] + def wait_for_multiple_tasks interested, tasks + version = "" + interested = (interested + ["info.state"]).uniq + task_props = Hash.new { |h, k| h[k] = {} } + + filter = _connection.propertyCollector.CreateFilter spec: { + propSet: [{ type: "Task", all: false, pathSet: interested }], + objectSet: tasks.map { |x| { obj: x } } + }, partialUpdates: false + + begin + until (task_props.size == tasks.size) && task_props.all? { |_k, h| %w(success error).member? h["info.state"] } + result = _connection.propertyCollector.WaitForUpdates(version: version) + version = result.version + os = result.filterSet[0].objectSet + + os.each do |o| + changes = Hash[o.changeSet.map { |x| [x.name, x.val] }] + + interested.each do |k| + task = tasks.find { |x| x._ref == o.obj._ref } + task_props[task][k] = changes[k] if changes.member? k + end + end + + yield task_props if block_given? end + ensure + _connection.propertyCollector.CancelWaitForUpdates + filter.DestroyPropertyFilter end - yield task_props if block_given? + task_props end - ensure - _connection.propertyCollector.CancelWaitForUpdates - filter.DestroyPropertyFilter end - - task_props end end diff --git a/lib/rbvmomi/vim/Task.rb b/lib/rbvmomi/vim/Task.rb index f09d1ab3..bb0433e8 100644 --- a/lib/rbvmomi/vim/Task.rb +++ b/lib/rbvmomi/vim/Task.rb @@ -1,68 +1,73 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::Task - # Wait for a task to finish. - # @return +info.result+ on success. - # @raise +info.error+ on error. - def wait_for_completion - wait_until('info.state') { %w(success error).member? info.state } - case info.state - when 'success' - info.result - when 'error' - raise info.error - end - end - - # Wait for all child tasks to finish. If any one child task failed, - # the exception of the first failing task is thrown. - # @return [Hash] Map of tasks to their +info.result+ on success. - # @raise +info.error+ on error. - def wait_for_childtask_completion - si = _connection.serviceInstance - tasks_props = si.wait_for_multiple_tasks( - ['info.state', 'info.result', 'info.error'], - self.child_tasks - ) - Hash[tasks_props.map do |task, props| - case props['info.state'] - when 'success' - [task, props['info.result']] - when 'error' - raise props['info.error'] +module RbVmomi + module VIM + class Task + # Wait for a task to finish. + # @return +info.result+ on success. + # @raise +info.error+ on error. + def wait_for_completion + wait_until("info.state") { %w(success error).member? info.state } + case info.state + when "success" + info.result + when "error" + raise info.error + end end - end] - end - # Wait for a task to finish, with progress notifications. - # @return (see #wait_for_completion) - # @raise (see #wait_for_completion) - # @yield [info.progress] - def wait_for_progress - wait_until('info.state', 'info.progress') do - yield info.progress if block_given? - %w(success error).member? info.state - end - case info.state - when 'success' - info.result - when 'error' - raise info.error + # Wait for all child tasks to finish. If any one child task failed, + # the exception of the first failing task is thrown. + # @return [Hash] Map of tasks to their +info.result+ on success. + # @raise +info.error+ on error. + def wait_for_childtask_completion + si = _connection.service_instance + tasks_props = si.wait_for_multiple_tasks( + ["info.state", "info.result", "info.error"], + child_tasks + ) + Hash[tasks_props.map do |task, props| + case props["info.state"] + when "success" + [task, props["info.result"]] + when "error" + raise props["info.error"] + end + end] + end + + # Wait for a task to finish, with progress notifications. + # @return (see #wait_for_completion) + # @raise (see #wait_for_completion) + # @yield [info.progress] + def wait_for_progress + wait_until("info.state", "info.progress") do + yield info.progress if block_given? + %w(success error).member? info.state + end + case info.state + when "success" + info.result + when "error" + raise info.error + end + end + + # Get child tasks of this task. + # @return [Array] List of VIM::Task objects + def child_tasks + tm = _connection.service_content.taskManager + col = tm.CreateCollectorForTasks(filter: { + rootTaskKey: [info.key] + }) + # XXX: Likely this is not enough and we need to collect pages other + # than the latest. + tasks = col.latestPage.map(&:task) + col.DestroyCollector() + tasks + end end end - - # Get child tasks of this task. - # @return [Array] List of VIM::Task objects - def child_tasks - tm = _connection.serviceContent.taskManager - col = tm.CreateCollectorForTasks(:filter => { - :rootTaskKey => [self.info.key], - }) - # XXX: Likely this is not enough and we need to collect pages other - # than the latest. - tasks = col.latestPage.map{|x| x.task} - col.DestroyCollector() - tasks - end end diff --git a/lib/rbvmomi/vim/VirtualMachine.rb b/lib/rbvmomi/vim/VirtualMachine.rb index 502fb7ee..afdeae25 100644 --- a/lib/rbvmomi/vim/VirtualMachine.rb +++ b/lib/rbvmomi/vim/VirtualMachine.rb @@ -1,75 +1,76 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -class RbVmomi::VIM::VirtualMachine - # Retrieve the MAC addresses for all virtual NICs. - # @return [Hash] Keyed by device label. - def macs - Hash[self.config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).map { |x| [x.deviceInfo.label, x.macAddress] }] - end +module RbVmomi + module VIM + class VirtualMachine + # Retrieve the MAC addresses for all virtual NICs. + # @return [Hash] Keyed by device label. + def macs + Hash[config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).map { |x| [x.deviceInfo.label, x.macAddress] }] + end - # Retrieve all virtual disk devices. - # @return [Array] Array of virtual disk devices. - def disks - self.config.hardware.device.grep(RbVmomi::VIM::VirtualDisk) - end + # Retrieve all virtual disk devices. + # @return [Array] Array of virtual disk devices. + def disks + config.hardware.device.grep(RbVmomi::VIM::VirtualDisk) + end - # Get the IP of the guest, but only if it is not stale - # @return [String] Current IP reported (as per VMware Tools) or nil - def guest_ip - g = self.guest - if g.ipAddress && (g.toolsStatus == "toolsOk" || g.toolsStatus == "toolsOld") - g.ipAddress - else - nil - end - end + # Get the IP of the guest, but only if it is not stale + # @return [String] Current IP reported (as per VMware Tools) or nil + def guest_ip + g = guest + g.ipAddress if g.ipAddress && (g.toolsStatus == "toolsOk" || g.toolsStatus == "toolsOld") + end - # Add a layer of delta disks (redo logs) in front of every disk on the VM. - # This is similar to taking a snapshot and makes the VM a valid target for - # creating a linked clone. - # - # Background: The API for linked clones is quite strange. We can't create - # a linked straight from any VM. The disks of the VM for which we can create a - # linked clone need to be read-only and thus VC demands that the VM we - # are cloning from uses delta-disks. Only then it will allow us to - # share the base disk. - def add_delta_disk_layer_on_all_disks - spec = update_spec_add_delta_disk_layer_on_all_disks - self.ReconfigVM_Task(:spec => spec).wait_for_completion - end + # Add a layer of delta disks (redo logs) in front of every disk on the VM. + # This is similar to taking a snapshot and makes the VM a valid target for + # creating a linked clone. + # + # Background: The API for linked clones is quite strange. We can't create + # a linked straight from any VM. The disks of the VM for which we can create a + # linked clone need to be read-only and thus VC demands that the VM we + # are cloning from uses delta-disks. Only then it will allow us to + # share the base disk. + def add_delta_disk_layer_on_all_disks + spec = update_spec_add_delta_disk_layer_on_all_disks + self.ReconfigVM_Task(spec: spec).wait_for_completion + end - # Updates a passed in spec to perform the task of adding a delta disk layer - # on top of all disks. Does the same as add_delta_disk_layer_on_all_disks - # but instead of issuing the ReconfigVM_Task, it just constructs the - # spec, so that the caller can batch a couple of updates into one - # ReconfigVM_Task. - def update_spec_add_delta_disk_layer_on_all_disks spec = {} - devices, = self.collect 'config.hardware.device' - disks = devices.grep(RbVmomi::VIM::VirtualDisk) - device_change = [] - disks.each do |disk| - device_change << { - :operation => :remove, - :device => disk - } - device_change << { - :operation => :add, - :fileOperation => :create, - :device => disk.dup.tap { |x| - x.backing = x.backing.dup - x.backing.fileName = "[#{disk.backing.datastore.name}]" - x.backing.parent = disk.backing - }, - } - end - if spec.is_a?(RbVmomi::VIM::VirtualMachineConfigSpec) - spec.deviceChange ||= [] - spec.deviceChange += device_change - else - spec[:deviceChange] ||= [] - spec[:deviceChange] += device_change + # Updates a passed in spec to perform the task of adding a delta disk layer + # on top of all disks. Does the same as add_delta_disk_layer_on_all_disks + # but instead of issuing the ReconfigVM_Task, it just constructs the + # spec, so that the caller can batch a couple of updates into one + # ReconfigVM_Task. + def update_spec_add_delta_disk_layer_on_all_disks spec = {} + devices, = collect "config.hardware.device" + disks = devices.grep(RbVmomi::VIM::VirtualDisk) + device_change = [] + disks.each do |disk| + device_change << { + operation: :remove, + device: disk + } + device_change << { + operation: :add, + fileOperation: :create, + device: disk.dup.tap do |x| + x.backing = x.backing.dup + x.backing.fileName = "[#{disk.backing.datastore.name}]" + x.backing.parent = disk.backing + end + } + end + if spec.is_a?(RbVmomi::VIM::VirtualMachineConfigSpec) + spec.deviceChange ||= [] + spec.deviceChange += device_change + else + spec[:deviceChange] ||= [] + spec[:deviceChange] += device_change + end + spec + end end - spec end end diff --git a/rbvmomi.gemspec b/rbvmomi.gemspec index f189b179..7880e377 100644 --- a/rbvmomi.gemspec +++ b/rbvmomi.gemspec @@ -1,36 +1,38 @@ +# frozen_string_literal: true # Copyright (c) 2016-2020 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -$LOAD_PATH.unshift(File.expand_path('lib', __dir__)) +$LOAD_PATH.unshift(File.expand_path("lib", __dir__)) -require 'rbvmomi/version' +require "rbvmomi/version" Gem::Specification.new do |spec| - spec.name = 'rbvmomi' - spec.summary = 'Ruby interface to the VMware vSphere API' + spec.name = "rbvmomi" + spec.summary = "Ruby interface to the VMware vSphere API" spec.version = RbVmomi::VERSION - spec.authors = ['Rich Lane', 'Christian Dickmann'] - spec.email = 'jrg@vmware.com' - spec.homepage = 'https://github.com/vmware/rbvmomi' - spec.license = 'MIT' - - spec.bindir = 'exe' - spec.files = %w[LICENSE README.md vmodl.db] + Dir.glob('{lib,exe}/**/*') - spec.executables << 'rbvmomish' - - spec.add_runtime_dependency('builder', '~> 3.2') - spec.add_runtime_dependency('json', '~> 2.3') - spec.add_runtime_dependency('nokogiri', '~> 1.10') - spec.add_runtime_dependency('optimist', '~> 3.0') - - spec.add_development_dependency('activesupport') - spec.add_development_dependency('pry', '~> 0.13.1') - spec.add_development_dependency('rake', '~> 13.0') - spec.add_development_dependency('simplecov', '~> 0.19.0') - spec.add_development_dependency('soap4r-ng', '~> 2.0') - spec.add_development_dependency('test-unit', '~> 3.3') - spec.add_development_dependency('yard', '~> 0.9.25') - - spec.required_ruby_version = '>= 2.4.1' + spec.authors = ["Rich Lane", "Christian Dickmann"] + spec.email = "jrg@vmware.com" + spec.homepage = "https://github.com/vmware/rbvmomi" + spec.license = "MIT" + + spec.bindir = "exe" + spec.files = %w(LICENSE README.md vmodl.db) + Dir.glob("{lib,exe}/**/*") + spec.executables << "rbvmomish" + + spec.add_runtime_dependency("builder", "~> 3.2") + spec.add_runtime_dependency("json", "~> 2.3") + spec.add_runtime_dependency("nokogiri", "~> 1.10") + spec.add_runtime_dependency("optimist", "~> 3.0") + + spec.add_development_dependency("activesupport") + spec.add_development_dependency("pry", "~> 0.13.1") + spec.add_development_dependency("rake", "~> 13.0") + spec.add_development_dependency("rubocop", "~> 0.92") + spec.add_development_dependency("simplecov", "~> 0.19.0") + spec.add_development_dependency("soap4r-ng", "~> 2.0") + spec.add_development_dependency("test-unit", "~> 3.3") + spec.add_development_dependency("yard", "~> 0.9.25") + + spec.required_ruby_version = ">= 2.4.1" end diff --git a/test/test_deserialization.rb b/test/test_deserialization.rb index 38a4194e..01cc9459 100644 --- a/test/test_deserialization.rb +++ b/test/test_deserialization.rb @@ -1,11 +1,12 @@ +# frozen_string_literal: true # Copyright (c) 2010-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'test_helper' +require "test_helper" class DeserializationTest < Test::Unit::TestCase def setup - conn = VIM.new(:ns => 'urn:vim25', :rev => '4.0') + conn = VIM.new(ns: "urn:vim25", rev: "4.0") @deserializer = RbVmomi::Deserializer.new conn end @@ -15,157 +16,157 @@ def check str, expected, type end def test_moref - check <<-EOS, VIM.Folder(nil, 'ha-folder-root'), 'Folder' -ha-folder-root + check <<~EOS, VIM.Folder(nil, "ha-folder-root"), "Folder" + ha-folder-root EOS - check <<-EOS, VIM.Datacenter(nil, 'ha-datacenter'), 'ManagedObjectReference' -ha-datacenter + check <<~EOS, VIM.Datacenter(nil, "ha-datacenter"), "ManagedObjectReference" + ha-datacenter EOS end def test_dataobject obj = VIM.DatastoreSummary( - :capacity => 1000, - :accessible => true, - :datastore => VIM.Datastore(nil, "foo"), - :freeSpace => 31, - :multipleHostAccess => false, - :name => "baz", - :type => "VMFS", - :url => "http://foo/" + capacity: 1000, + accessible: true, + datastore: VIM.Datastore(nil, "foo"), + freeSpace: 31, + multipleHostAccess: false, + name: "baz", + type: "VMFS", + url: "http://foo/" ) - check <<-EOS, obj, 'DatastoreSummary' - - foo - baz - http://foo/ - 1000 - 31 - 1 - false - VMFS - + check <<~EOS, obj, "DatastoreSummary" + + foo + baz + http://foo/ + 1000 + 31 + 1 + false + VMFS + EOS end def test_enum - check <<-EOS, 'add', 'ConfigSpecOperation' -add + check <<~EOS, "add", "ConfigSpecOperation" + add EOS end def test_array obj = VIM.ObjectContent( - :obj => VIM.Folder(nil, 'ha-folder-root'), - :missingSet => [], - :propSet => [ + obj: VIM.Folder(nil, "ha-folder-root"), + missingSet: [], + propSet: [ VIM.DynamicProperty( - :name => 'childEntity', - :val => [ - VIM.Datacenter(nil, 'ha-datacenter') + name: "childEntity", + val: [ + VIM.Datacenter(nil, "ha-datacenter") ] ) ] ) - check <<-EOS, obj, 'ObjectContent' - - ha-folder-root - - childEntity - - ha-datacenter - - - + check <<~EOS, obj, "ObjectContent" + + ha-folder-root + + childEntity + + ha-datacenter + + + EOS end -def test_array2 - obj = VIM.DVPortStatus( - :linkUp => true, - :blocked => false, - :vlanIds => [ - VIM::NumericRange(:start => 5, :end => 7), - VIM::NumericRange(:start => 10, :end => 20), - ], - :vmDirectPathGen2InactiveReasonNetwork => [], - :vmDirectPathGen2InactiveReasonOther => [] - ) - - check <<-EOS, obj, 'DVPortStatus' - - 1 - false - - 5 - 7 - - - 10 - 20 - - - EOS -end + def test_array2 + obj = VIM.DVPortStatus( + linkUp: true, + blocked: false, + vlanIds: [ + VIM::NumericRange(start: 5, end: 7), + VIM::NumericRange(start: 10, end: 20) + ], + vmDirectPathGen2InactiveReasonNetwork: [], + vmDirectPathGen2InactiveReasonOther: [] + ) -def test_empty_array - obj = VIM.DVPortStatus( - :linkUp => true, - :blocked => false, - :vlanIds => [], - :vmDirectPathGen2InactiveReasonNetwork => [], - :vmDirectPathGen2InactiveReasonOther => [] - ) - - check <<-EOS, obj, 'DVPortStatus' - - 1 - false - - EOS -end + check <<~EOS, obj, "DVPortStatus" + + 1 + false + + 5 + 7 + + + 10 + 20 + + + EOS + end + + def test_empty_array + obj = VIM.DVPortStatus( + linkUp: true, + blocked: false, + vlanIds: [], + vmDirectPathGen2InactiveReasonNetwork: [], + vmDirectPathGen2InactiveReasonOther: [] + ) + + check <<~EOS, obj, "DVPortStatus" + + 1 + false + + EOS + end def test_fault obj = VIM.LocalizedMethodFault( - :localizedMessage => "The attempted operation cannot be performed in the current state (Powered off).", - :fault => VIM.InvalidPowerState( - :requestedState => 'poweredOn', - :existingState => 'poweredOff', - :faultMessage => [] + localizedMessage: "The attempted operation cannot be performed in the current state (Powered off).", + fault: VIM.InvalidPowerState( + requestedState: "poweredOn", + existingState: "poweredOff", + faultMessage: [] ) ) - check <<-EOS, obj, "LocalizedMethodFault" - - - poweredOn - poweredOff - - The attempted operation cannot be performed in the current state (Powered off). - + check <<~EOS, obj, "LocalizedMethodFault" + + + poweredOn + poweredOff + + The attempted operation cannot be performed in the current state (Powered off). + EOS end def test_wait_for_updates obj = VIM.UpdateSet( - :version => '7', - :filterSet => [ + version: "7", + filterSet: [ VIM.PropertyFilterUpdate( - :filter => VIM.PropertyFilter(nil, "session[528BA5EB-335B-4AF6-B49C-6160CF5E8D5B]71E3AC7E-7927-4D9E-8BC3-522769F22DAF"), - :missingSet => [], - :objectSet => [ + filter: VIM.PropertyFilter(nil, "session[528BA5EB-335B-4AF6-B49C-6160CF5E8D5B]71E3AC7E-7927-4D9E-8BC3-522769F22DAF"), + missingSet: [], + objectSet: [ VIM.ObjectUpdate( - :kind => 'enter', - :obj => VIM.VirtualMachine(nil, 'vm-1106'), - :missingSet => [], - :changeSet => [ + kind: "enter", + obj: VIM.VirtualMachine(nil, "vm-1106"), + missingSet: [], + changeSet: [ VIM.PropertyChange( - :name => 'runtime.powerState', - :op => 'assign', - :val => 'poweredOn' + name: "runtime.powerState", + op: "assign", + val: "poweredOn" ) ] ) @@ -174,201 +175,201 @@ def test_wait_for_updates ] ) - check <<-EOS, obj, "UpdateSet" - - 7 - - session[528BA5EB-335B-4AF6-B49C-6160CF5E8D5B]71E3AC7E-7927-4D9E-8BC3-522769F22DAF - - enter - vm-1106 - - runtime.powerState - assign - poweredOn - - - - + check <<~EOS, obj, "UpdateSet" + + 7 + + session[528BA5EB-335B-4AF6-B49C-6160CF5E8D5B]71E3AC7E-7927-4D9E-8BC3-522769F22DAF + + enter + vm-1106 + + runtime.powerState + assign + poweredOn + + + + EOS end def test_binary obj = "\x00foo\x01bar\x02baz" - check <<-EOS, obj, 'xsd:base64Binary' -AGZvbwFiYXICYmF6 + check <<~EOS, obj, "xsd:base64Binary" + AGZvbwFiYXICYmF6 EOS end def test_hba obj = VIM::HostBlockHba( - :key => 'key-vim.host.BlockHba-vmhba0', - :device => 'vmhba0', - :bus => 0, - :status => 'unknown', - :model => 'Virtual Machine Chipset', - :driver => 'ata_piix', - :pci => '00:07.1') - - check <<-EOS, obj, "HostBlockHba" - - key-vim.host.BlockHba-vmhba0 - vmhba0 - 0 - unknown - Virtual Machine Chipset - ata_piix - 00:07.1 - - EOS - end - -=begin - def test_runtime_state - obj = VIM::VirtualMachineDeviceRuntimeInfoVirtualEthernetCardRuntimeState( - vmDirectPathGen2:Active => false, - vmDirectPathGen2:InactiveReasonOther => ["vmNptIncompatibleHost"], - vmDirectPathGen2:InactiveReasonVm => [] + key: "key-vim.host.BlockHba-vmhba0", + device: "vmhba0", + bus: 0, + status: "unknown", + model: "Virtual Machine Chipset", + driver: "ata_piix", + pci: "00:07.1" ) - check <<-EOS, obj, 'VirtualMachineDeviceRuntimeInfoDeviceRuntimeState' - - false - vmNptIncompatibleHost - + + check <<~EOS, obj, "HostBlockHba" + + key-vim.host.BlockHba-vmhba0 + vmhba0 + 0 + unknown + Virtual Machine Chipset + ata_piix + 00:07.1 + EOS end -=end + + # def test_runtime_state + # obj = VIM::VirtualMachineDeviceRuntimeInfoVirtualEthernetCardRuntimeState( + # vmDirectPathGen2:Active => false, + # vmDirectPathGen2:InactiveReasonOther => ["vmNptIncompatibleHost"], + # vmDirectPathGen2:InactiveReasonVm => [] + # ) + # check <<-EOS, obj, 'VirtualMachineDeviceRuntimeInfoDeviceRuntimeState' + # + # false + # vmNptIncompatibleHost + # + # EOS + # end def test_runtime_info obj = VIM::VirtualMachineRuntimeInfo( - :bootTime => Time.parse('2010-08-20 05:44:35 UTC'), - :connectionState => "connected", - :faultToleranceState => "notConfigured", - :featureMask => [], - :featureRequirement => [], - :host => VIM::HostSystem(nil, "host-32"), - :maxCpuUsage => 5612, - :maxMemoryUsage => 3072, - :memoryOverhead => 128671744, - :numMksConnections => 1, - :offlineFeatureRequirement => [], - :powerState => "poweredOn", - :recordReplayState => "inactive", - :suspendInterval => 0, - :toolsInstallerMounted => false, - :device => [] + bootTime: Time.parse("2010-08-20 05:44:35 UTC"), + connectionState: "connected", + faultToleranceState: "notConfigured", + featureMask: [], + featureRequirement: [], + host: VIM::HostSystem(nil, "host-32"), + maxCpuUsage: 5612, + maxMemoryUsage: 3072, + memoryOverhead: 128_671_744, + numMksConnections: 1, + offlineFeatureRequirement: [], + powerState: "poweredOn", + recordReplayState: "inactive", + suspendInterval: 0, + toolsInstallerMounted: false, + device: [] ) - check <<-EOS, obj, 'VirtualMachineRuntimeInfo' - - host-32 - connected - poweredOn - notConfigured - false - 2010-08-20T05:44:35.0Z - 0 - 128671744 - 5612 - 3072 - 1 - inactive - + check <<~EOS, obj, "VirtualMachineRuntimeInfo" + + host-32 + connected + poweredOn + notConfigured + false + 2010-08-20T05:44:35.0Z + 0 + 128671744 + 5612 + 3072 + 1 + inactive + EOS end def test_keyvalue - obj = ['a', 'b'] - check <<-EOS, obj, 'KeyValue' - - a - b - + obj = %w(a b) + check <<~EOS, obj, "KeyValue" + + a + b + EOS end def test_boolean - check "1", true, 'xsd:boolean' - check "true", true, 'xsd:boolean' - check "0", false, 'xsd:boolean' - check "false", false, 'xsd:boolean' + check "1", true, "xsd:boolean" + check "true", true, "xsd:boolean" + check "0", false, "xsd:boolean" + check "false", false, "xsd:boolean" end def test_int - check "5", 5, 'xsd:byte' - check "5", 5, 'xsd:short' - check "5", 5, 'xsd:int' - check "5", 5, 'xsd:long' + check "5", 5, "xsd:byte" + check "5", 5, "xsd:short" + check "5", 5, "xsd:int" + check "5", 5, "xsd:long" end def test_float obj = 1.2 - check <<-EOS, obj, 'xsd:float' -1.2 + check <<~EOS, obj, "xsd:float" + 1.2 EOS end def test_date - time_str = '2010-08-20T05:44:35.0Z' + time_str = "2010-08-20T05:44:35.0Z" obj = Time.parse(time_str) - check <<-EOS, obj, 'xsd:dateTime' -#{time_str} + check <<~EOS, obj, "xsd:dateTime" + #{time_str} EOS end def test_array_mangling obj = ["foo"] - check <<-EOS, obj, 'ArrayOfString' -foo + check <<~EOS, obj, "ArrayOfString" + foo EOS - time_str = '2010-08-20T05:44:35.0Z' + time_str = "2010-08-20T05:44:35.0Z" obj = [Time.parse(time_str)] - check <<-EOS, obj, 'ArrayOfDateTime' -#{time_str} + check <<~EOS, obj, "ArrayOfDateTime" + #{time_str} EOS obj = [1] - check <<-EOS, obj, 'ArrayOfAnyType' - - 1 - + check <<~EOS, obj, "ArrayOfAnyType" + + 1 + EOS end def test_propertypath - check "foo", "foo", 'PropertyPath' + check "foo", "foo", "PropertyPath" end def test_methodname - check "foo", "foo", 'MethodName' + check "foo", "foo", "MethodName" end def test_typename - check "foo", "foo", 'TypeName' + check "foo", "foo", "TypeName" end def test_new_fields obj = VIM::HostBlockHba( - :key => 'key-vim.host.BlockHba-vmhba0', - :device => 'vmhba0', - :bus => 0, - :status => 'unknown', - :model => 'Virtual Machine Chipset', - :driver => 'ata_piix', - :pci => '00:07.1') - - check <<-EOS, obj, "HostBlockHba" - - key-vim.host.BlockHba-vmhba0 - vmhba0 - 0 - unknown - bar - Virtual Machine Chipset - ata_piix - 00:07.1 - + key: "key-vim.host.BlockHba-vmhba0", + device: "vmhba0", + bus: 0, + status: "unknown", + model: "Virtual Machine Chipset", + driver: "ata_piix", + pci: "00:07.1" + ) + + check <<~EOS, obj, "HostBlockHba" + + key-vim.host.BlockHba-vmhba0 + vmhba0 + 0 + unknown + bar + Virtual Machine Chipset + ata_piix + 00:07.1 + EOS end end diff --git a/test/test_emit_request.rb b/test/test_emit_request.rb index ba56edab..af5f147b 100644 --- a/test/test_emit_request.rb +++ b/test/test_emit_request.rb @@ -1,15 +1,16 @@ +# frozen_string_literal: true # Copyright (c) 2010-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'test_helper' +require "test_helper" class EmitRequestTest < Test::Unit::TestCase MO = VIM::VirtualMachine(nil, "foo") def check desc, str, this, params - soap = VIM.new(:ns => 'urn:vim25', :rev => '4.0') - xml = Builder::XmlMarkup.new :indent => 2 - soap.emit_request xml, 'root', desc, this, params + soap = VIM.new(ns: "urn:vim25", rev: "4.0") + xml = Builder::XmlMarkup.new indent: 2 + soap.emit_request xml, "root", desc, this, params begin assert_equal str, xml.target! @@ -27,45 +28,45 @@ def check desc, str, this, params def test_string_array desc = [ { - 'name' => 'blah', - 'is-array' => true, - 'is-optional' => true, - 'wsdl_type' => 'xsd:string', + "name" => "blah", + "is-array" => true, + "is-optional" => true, + "wsdl_type" => "xsd:string" } ] - check desc, <<-EOS, MO, :blah => ['a', 'b', 'c'] - - <_this type="VirtualMachine">foo - a - b - c - + check desc, <<~EOS, MO, blah: %w(a b c) + + <_this type="VirtualMachine">foo + a + b + c + EOS end def test_required_param desc = [ { - 'name' => 'blah', - 'is-array' => false, - 'is-optional' => false, - 'wsdl_type' => 'xsd:string', + "name" => "blah", + "is-array" => false, + "is-optional" => false, + "wsdl_type" => "xsd:string" } ] - check desc, <<-EOS, MO, :blah => 'a' - - <_this type="VirtualMachine">foo - a - + check desc, <<~EOS, MO, blah: "a" + + <_this type="VirtualMachine">foo + a + EOS assert_raise RuntimeError do - check desc, <<-EOS, MO, {} - - <_this type="VirtualMachine">foo - + check desc, <<~EOS, MO, {} + + <_this type="VirtualMachine">foo + EOS end end @@ -73,52 +74,52 @@ def test_required_param def test_optional_param desc = [ { - 'name' => 'blah', - 'is-array' => false, - 'is-optional' => true, - 'wsdl_type' => 'xsd:string', + "name" => "blah", + "is-array" => false, + "is-optional" => true, + "wsdl_type" => "xsd:string" } ] - check desc, <<-EOS, MO, {} - - <_this type="VirtualMachine">foo - + check desc, <<~EOS, MO, {} + + <_this type="VirtualMachine">foo + EOS end def test_nil_optional_param desc = [ { - 'name' => 'blah', - 'is-array' => false, - 'is-optional' => true, - 'wsdl_type' => 'xsd:string', + "name" => "blah", + "is-array" => false, + "is-optional" => true, + "wsdl_type" => "xsd:string" } ] - check desc, <<-EOS, MO, :blah => nil - - <_this type="VirtualMachine">foo - + check desc, <<~EOS, MO, blah: nil + + <_this type="VirtualMachine">foo + EOS end def test_string_key desc = [ { - 'name' => 'blah', - 'is-array' => false, - 'is-optional' => false, - 'wsdl_type' => 'xsd:string', + "name" => "blah", + "is-array" => false, + "is-optional" => false, + "wsdl_type" => "xsd:string" } ] - check desc, <<-EOS, MO, 'blah' => 'a' - - <_this type="VirtualMachine">foo - a - + check desc, <<~EOS, MO, "blah" => "a" + + <_this type="VirtualMachine">foo + a + EOS end @@ -128,4 +129,3 @@ def disabled_test_required_property end end end - diff --git a/test/test_exceptions.rb b/test/test_exceptions.rb index 950fe781..8f06a140 100644 --- a/test/test_exceptions.rb +++ b/test/test_exceptions.rb @@ -1,17 +1,16 @@ +# frozen_string_literal: true # Copyright (c) 2010-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'test_helper' +require "test_helper" class ExceptionTest < Test::Unit::TestCase def test_fault - begin - fault = VIM::InvalidArgument.new :invalidProperty => 'foo' - assert_raises RbVmomi::Fault do - raise RbVmomi::Fault.new('A specified parameter was not correct.', fault) - end - rescue VIM::InvalidArgument - assert_equal 'foo', $!.invalidProperty + fault = VIM::InvalidArgument.new invalidProperty: "foo" + assert_raises RbVmomi::Fault do + raise RbVmomi::Fault.new("A specified parameter was not correct.", fault) end + rescue VIM::InvalidArgument + assert_equal "foo", $ERROR_INFO.invalidProperty end end diff --git a/test/test_helper.rb b/test/test_helper.rb index e1f41ec9..c6f50d1c 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,10 +1,11 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'simplecov' -SimpleCov.start { add_filter '/test/' } +require "simplecov" +SimpleCov.start { add_filter "/test/" } -require 'rbvmomi' +require "rbvmomi" VIM = RbVmomi::VIM -require 'test/unit' +require "test/unit" diff --git a/test/test_misc.rb b/test/test_misc.rb index 0d1f03af..d33c7657 100644 --- a/test/test_misc.rb +++ b/test/test_misc.rb @@ -1,12 +1,13 @@ +# frozen_string_literal: true # Copyright (c) 2011-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'test_helper' +require "test_helper" class MiscTest < Test::Unit::TestCase def test_overridden_const assert(VIM::SecurityError < RbVmomi::BasicTypes::Base) - assert_equal 'SecurityError', VIM::SecurityError.wsdl_name + assert_equal "SecurityError", VIM::SecurityError.wsdl_name end # XXX @@ -15,11 +16,11 @@ def disabled_test_dynamically_overridden_const Object.const_set :ClusterAttemptedVmInfo, :override assert VIM::ClusterAttemptedVmInfo.is_a?(Class) assert(VIM::ClusterAttemptedVmInfo < RbVmomi::BasicTypes::Base) - assert_equal 'ClusterAttemptedVmInfo', VIM::ClusterAttemptedVmInfo.wsdl_name + assert_equal "ClusterAttemptedVmInfo", VIM::ClusterAttemptedVmInfo.wsdl_name end def test_loader - klass = VIM.loader.get('HostSystem') + klass = VIM.loader.get("HostSystem") klass2 = VIM::HostSystem assert_equal klass, klass2 end @@ -34,46 +35,46 @@ def test_managed_object_to_json def test_data_object_to_hash # With a nested ManagedObject value - assert_equal VIM.VirtualMachineSummary({vm: VIM.VirtualMachine(nil, "vm-123")}).to_hash, {:vm => "VirtualMachine(\"vm-123\")"} + assert_equal VIM.VirtualMachineSummary({ vm: VIM.VirtualMachine(nil, "vm-123") }).to_hash, { vm: "VirtualMachine(\"vm-123\")" } # With an array - assert_equal VIM.VirtualMachineSummary({customValue: [VIM.CustomFieldValue({key: 1})]}).to_hash, {:customValue => [{key: 1}]} + assert_equal VIM.VirtualMachineSummary({ customValue: [VIM.CustomFieldValue({ key: 1 })] }).to_hash, { customValue: [{ key: 1 }] } # With an Enum - assert_equal VIM.VirtualMachineSummary({overallStatus: VIM.ManagedEntityStatus("green")}).to_hash, {:overallStatus => "green"} + assert_equal VIM.VirtualMachineSummary({ overallStatus: VIM.ManagedEntityStatus("green") }).to_hash, { overallStatus: "green" } # Combined assert_equal VIM.VirtualMachineSummary( - :vm => VIM.VirtualMachine(nil, "vm-123"), - :customValue => [VIM.CustomFieldValue(:key => 1)], - :overallStatus => VIM.ManagedEntityStatus("green") + vm: VIM.VirtualMachine(nil, "vm-123"), + customValue: [VIM.CustomFieldValue(key: 1)], + overallStatus: VIM.ManagedEntityStatus("green") ).to_hash, - { - :vm => "VirtualMachine(\"vm-123\")", - :customValue => [{:key => 1}], - :overallStatus => "green" - } + { + vm: "VirtualMachine(\"vm-123\")", + customValue: [{ key: 1 }], + overallStatus: "green" + } end def test_data_object_to_json # With a nested ManagedObject value - assert_equal VIM.VirtualMachineSummary({vm: VIM.VirtualMachine(nil, "vm-123")}).to_json, + assert_equal VIM.VirtualMachineSummary({ vm: VIM.VirtualMachine(nil, "vm-123") }).to_json, "{\"vm\":\"VirtualMachine(\\\"vm-123\\\")\",\"json_class\":\"RbVmomi::VIM::VirtualMachineSummary\"}" # With an array - assert_equal VIM.VirtualMachineSummary({customValue: [VIM.CustomFieldValue({key: 1})]}).to_json, + assert_equal VIM.VirtualMachineSummary({ customValue: [VIM.CustomFieldValue({ key: 1 })] }).to_json, "{\"customValue\":[{\"key\":1}],\"json_class\":\"RbVmomi::VIM::VirtualMachineSummary\"}" # With an Enum - assert_equal VIM.VirtualMachineSummary({overallStatus: VIM.ManagedEntityStatus("green")}).to_json, + assert_equal VIM.VirtualMachineSummary({ overallStatus: VIM.ManagedEntityStatus("green") }).to_json, "{\"overallStatus\":\"green\",\"json_class\":\"RbVmomi::VIM::VirtualMachineSummary\"}" # Combined assert_equal VIM.VirtualMachineSummary( - :vm => VIM.VirtualMachine(nil, "vm-123"), - :customValue => [VIM.CustomFieldValue(:key => 1)], - :overallStatus => VIM.ManagedEntityStatus("green") + vm: VIM.VirtualMachine(nil, "vm-123"), + customValue: [VIM.CustomFieldValue(key: 1)], + overallStatus: VIM.ManagedEntityStatus("green") ).to_json, - "{\"vm\":\"VirtualMachine(\\\"vm-123\\\")\",\"customValue\":[{\"key\":1}],\"overallStatus\":\"green\",\"json_class\":\"RbVmomi::VIM::VirtualMachineSummary\"}" + "{\"vm\":\"VirtualMachine(\\\"vm-123\\\")\",\"customValue\":[{\"key\":1}],\"overallStatus\":\"green\",\"json_class\":\"RbVmomi::VIM::VirtualMachineSummary\"}" end end diff --git a/test/test_parse_response.rb b/test/test_parse_response.rb index e1ba5e6b..34706b21 100644 --- a/test/test_parse_response.rb +++ b/test/test_parse_response.rb @@ -1,72 +1,72 @@ +# frozen_string_literal: true # Copyright (c) 2010-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'test_helper' +require "test_helper" class ParseResponseTest < Test::Unit::TestCase def check desc, str, expected - soap = VIM.new(:ns => 'urn:vim25', :rev => '4.0') + soap = VIM.new(ns: "urn:vim25", rev: "4.0") got = soap.parse_response Nokogiri(str).root, desc assert_equal expected, got end def test_string_array - desc = { 'wsdl_type' => 'xsd:string', 'is-array' => true, 'is-task' => false } + desc = { "wsdl_type" => "xsd:string", "is-array" => true, "is-task" => false } - check desc, <<-EOS, ['a', 'b', 'c'] - - a - b - c - + check desc, <<~EOS, %w(a b c) + + a + b + c + EOS end def test_missing_parameter_fault - desc = { 'wsdl_type' => nil, 'is-array' => false, 'is-task' => false } + desc = { "wsdl_type" => nil, "is-array" => false, "is-task" => false } assert_raise RuntimeError do - check desc, <<-EOS, ['a', 'b', 'c'] - - ClientFaultCode - Required parameter selectionSet is missing - + check desc, <<~EOS, %w(a b c) + + ClientFaultCode + Required parameter selectionSet is missing + EOS end end def test_invalid_argument_fault - desc = { 'wsdl_type' => nil, 'is-array' => false, 'is-task' => false } + desc = { "wsdl_type" => nil, "is-array" => false, "is-task" => false } assert_raise RbVmomi::Fault do begin - check desc, <<-EOS, nil - - ServerFaultCode - A specified parameter was not correct. ticketType - - - ticketType - - - + check desc, <<~EOS, nil + + ServerFaultCode + A specified parameter was not correct. ticketType + + + ticketType + + + EOS rescue VIM::InvalidArgument raise - rescue + rescue StandardError raise "wrong fault" end end end def test_task_array_result - desc = { 'wsdl_type' => 'xsd:string', 'is-array' => true, 'is-task' => true } + desc = { "wsdl_type" => "xsd:string", "is-array" => true, "is-task" => true } - check desc, <<-EOS, VIM.Task(nil, 'haTask-ha-host-vim.DiagnosticManager.generateLogBundles-865') - - haTask-ha-host-vim.DiagnosticManager.generateLogBundles-865 - + check desc, <<~EOS, VIM.Task(nil, "haTask-ha-host-vim.DiagnosticManager.generateLogBundles-865") + + haTask-ha-host-vim.DiagnosticManager.generateLogBundles-865 + EOS end end - diff --git a/test/test_serialization.rb b/test/test_serialization.rb index c17ac9cf..f74a5c15 100644 --- a/test/test_serialization.rb +++ b/test/test_serialization.rb @@ -1,13 +1,14 @@ +# frozen_string_literal: true # Copyright (c) 2010-2017 VMware, Inc. All Rights Reserved. # SPDX-License-Identifier: MIT -require 'test_helper' +require "test_helper" class SerializationTest < Test::Unit::TestCase def check str, obj, type, array=false - conn = VIM.new(:ns => 'urn:vim25', :rev => '4.0') - xml = Builder::XmlMarkup.new :indent => 2 - conn.obj2xml(xml, 'root', type, array, obj) + conn = VIM.new(ns: "urn:vim25", rev: "4.0") + xml = Builder::XmlMarkup.new indent: 2 + conn.obj2xml(xml, "root", type, array, obj) begin assert_equal str, xml.target! @@ -23,292 +24,291 @@ def check str, obj, type, array=false end def test_moref - check <<-EOS, VIM.Folder(nil, "ha-folder-host"), "Folder" -ha-folder-host + check <<~EOS, VIM.Folder(nil, "ha-folder-host"), "Folder" + ha-folder-host EOS end def test_config cfg = VIM.VirtualMachineConfigSpec( - :name => 'vm', - :files => { :vmPathName => '[datastore1]' }, - :guestId => 'otherGuest64', - :numCPUs => 2, - :memoryMB => 3072, - :deviceChange => [ + name: "vm", + files: { vmPathName: "[datastore1]" }, + guestId: "otherGuest64", + numCPUs: 2, + memoryMB: 3072, + deviceChange: [ { - :operation => :add, - :device => VIM.VirtualLsiLogicController( - :key => 1000, - :busNumber => 0, - :sharedBus => :noSharing + operation: :add, + device: VIM.VirtualLsiLogicController( + key: 1000, + busNumber: 0, + sharedBus: :noSharing ) }, VIM.VirtualDeviceConfigSpec( - :operation => VIM.VirtualDeviceConfigSpecOperation(:add), - :fileOperation => VIM.VirtualDeviceConfigSpecFileOperation(:create), - :device => VIM.VirtualDisk( - :key => 0, - :backing => VIM.VirtualDiskFlatVer2BackingInfo( - :fileName => '[datastore1]', - :diskMode => :persistent, - :thinProvisioned => true + operation: VIM.VirtualDeviceConfigSpecOperation(:add), + fileOperation: VIM.VirtualDeviceConfigSpecFileOperation(:create), + device: VIM.VirtualDisk( + key: 0, + backing: VIM.VirtualDiskFlatVer2BackingInfo( + fileName: "[datastore1]", + diskMode: :persistent, + thinProvisioned: true ), - :controllerKey => 1000, - :unitNumber => 0, - :capacityInKB => 4000000 + controllerKey: 1000, + unitNumber: 0, + capacityInKB: 4_000_000 ) ), { - :operation => :add, - :device => VIM.VirtualE1000( - :key => 0, - :deviceInfo => { - :label => 'Network Adapter 1', - :summary => 'VM Network' + operation: :add, + device: VIM.VirtualE1000( + key: 0, + deviceInfo: { + label: "Network Adapter 1", + summary: "VM Network" }, - :backing => VIM.VirtualEthernetCardNetworkBackingInfo( - :deviceName => 'VM Network' + backing: VIM.VirtualEthernetCardNetworkBackingInfo( + deviceName: "VM Network" ), - :addressType => 'generated' + addressType: "generated" ) } ], - :extraConfig => [ + extraConfig: [ { - :key => 'bios.bootOrder', - :value => 'ethernet0' + key: "bios.bootOrder", + value: "ethernet0" } ] ) - check <<-EOS, cfg, "VirtualMachineConfigSpec" - - vm - otherGuest64 - - [datastore1] - - 2 - 3072 - - add - - 1000 - 0 - noSharing - - - - add - create - - 0 - - [datastore1] - persistent - 1 - - 1000 - 0 - 4000000 - - - - add - - 0 - - - VM Network - - - VM Network - - generated - - - - bios.bootOrder - ethernet0 - - + check <<~EOS, cfg, "VirtualMachineConfigSpec" + + vm + otherGuest64 + + [datastore1] + + 2 + 3072 + + add + + 1000 + 0 + noSharing + + + + add + create + + 0 + + [datastore1] + persistent + 1 + + 1000 + 0 + 4000000 + + + + add + + 0 + + + VM Network + + + VM Network + + generated + + + + bios.bootOrder + ethernet0 + + EOS end def test_nil_field - obj = VIM.OptionValue(:key => 'foo', :value => nil) - check <<-EOS, obj, "OptionValue" - - foo - + obj = VIM.OptionValue(key: "foo", value: nil) + check <<~EOS, obj, "OptionValue" + + foo + EOS end def test_string_array - obj = ["foo", "bar", "baz"] - check <<-EOS, obj, "xsd:string", true -foo -bar -baz + obj = %w(foo bar baz) + check <<~EOS, obj, "xsd:string", true + foo + bar + baz EOS end def test_int_array - obj = [1,2,3] - check <<-EOS, obj, "xsd:int", true -1 -2 -3 + obj = [1, 2, 3] + check <<~EOS, obj, "xsd:int", true + 1 + 2 + 3 EOS end def test_boolean_array - obj = [true,false,true] - check <<-EOS, obj, "xsd:boolean", true -1 -0 -1 + obj = [true, false, true] + check <<~EOS, obj, "xsd:boolean", true + 1 + 0 + 1 EOS end def test_float_array - obj = [0.0,1.5,3.14] - check <<-EOS, obj, "xsd:float", true -0.0 -1.5 -3.14 + obj = [0.0, 1.5, 3.14] + check <<~EOS, obj, "xsd:float", true + 0.0 + 1.5 + 3.14 EOS end def test_binary obj = "\x00foo\x01bar\x02baz" - check <<-EOS, obj, 'xsd:base64Binary' -AGZvbwFiYXICYmF6 + check <<~EOS, obj, "xsd:base64Binary" + AGZvbwFiYXICYmF6 EOS end def test_property_spec interested = %w(info.progress info.state info.entityName info.error) - tasks = [VIM::Task.new(nil, 'task-11')] + tasks = [VIM::Task.new(nil, "task-11")] obj = { - :propSet => [{ :type => 'Task', :all => false, :pathSet => interested }], - :objectSet => tasks.map { |x| { :obj => x } }, + propSet: [{ type: "Task", all: false, pathSet: interested }], + objectSet: tasks.map { |x| { obj: x } } } - check <<-EOS, obj, 'PropertyFilterSpec' - - - Task - 0 - info.progress - info.state - info.entityName - info.error - - - task-11 - - + check <<~EOS, obj, "PropertyFilterSpec" + + + Task + 0 + info.progress + info.state + info.entityName + info.error + + + task-11 + + EOS - end def test_keyvalue - obj = RbVmomi::BasicTypes::KeyValue.new('a', 'b') - check <<-EOS, obj, 'KeyValue', false - - a - b - + obj = RbVmomi::BasicTypes::KeyValue.new("a", "b") + check <<~EOS, obj, "KeyValue", false + + a + b + EOS - obj = ['a', 'b'] - check <<-EOS, obj, 'KeyValue', false - - a - b - + obj = %w(a b) + check <<~EOS, obj, "KeyValue", false + + a + b + EOS - obj = [['a', 'b'], ['c', 'd']] - check <<-EOS, obj, 'KeyValue', true - - a - b - - - c - d - + obj = [%w(a b), %w(c d)] + check <<~EOS, obj, "KeyValue", true + + a + b + + + c + d + EOS - obj = { 'a' => 'b', :c => 'd' } - check <<-EOS, obj, 'KeyValue', true - - a - b - - - c - d - + obj = { "a" => "b", :c => "d" } + check <<~EOS, obj, "KeyValue", true + + a + b + + + c + d + EOS end def test_ovf_import_spec_params obj = RbVmomi::VIM::OvfCreateImportSpecParams( - :hostSystem => VIM::HostSystem(nil, "myhost"), - :locale => "US", - :entityName => "myvm", - :deploymentOption => "", - :networkMapping => [], - :propertyMapping => [['a', 'b'], ['c', 'd']], - :diskProvisioning => :thin + hostSystem: VIM::HostSystem(nil, "myhost"), + locale: "US", + entityName: "myvm", + deploymentOption: "", + networkMapping: [], + propertyMapping: [%w(a b), %w(c d)], + diskProvisioning: :thin ) - check <<-EOS, obj, 'OvfCreateImportSpecParams', false - - US - - myvm - myhost - - a - b - - - c - d - - thin - + check <<~EOS, obj, "OvfCreateImportSpecParams", false + + US + + myvm + myhost + + a + b + + + c + d + + thin + EOS end def test_datetime - obj = DateTime.new(2011, 11, 16, 13, 36, 8, Rational(-8,24)) - check <<-EOS, obj, 'xsd:dateTime', false -2011-11-16T13:36:08-08:00 + obj = DateTime.new(2011, 11, 16, 13, 36, 8, Rational(-8, 24)) + check <<~EOS, obj, "xsd:dateTime", false + 2011-11-16T13:36:08-08:00 EOS end def test_time - obj = Time.at(DateTime.new(2011, 11, 16, 13, 36, 8, Rational(-8,24)).strftime("%s").to_i).getgm - check <<-EOS, obj, 'xsd:dateTime', false -2011-11-16T21:36:08Z + obj = Time.at(DateTime.new(2011, 11, 16, 13, 36, 8, Rational(-8, 24)).strftime("%s").to_i).getgm + check <<~EOS, obj, "xsd:dateTime", false + 2011-11-16T21:36:08Z EOS end - # TODO test all types + # TODO: test all types def test_any_type obj = 1 - check <<-EOS, obj, 'xsd:anyType', false -1 + check <<~EOS, obj, "xsd:anyType", false + 1 EOS - obj = VIM::HostAccountSpec(:id => 'root', :password => 'foo') - check <<-EOS, obj, 'xsd:anyType', false - - root - foo - + obj = VIM::HostAccountSpec(id: "root", password: "foo") + check <<~EOS, obj, "xsd:anyType", false + + root + foo + EOS end end