class VmodlHelper

Public Class Methods

generate!() click to toggle source
# File lib/tasks/vmodl_helper.rb, line 17
def generate!
  run!('generate')
end
new(vmodl_path:, wsdl_path:) click to toggle source
# File lib/tasks/vmodl_helper.rb, line 42
def initialize(vmodl_path:, wsdl_path:)
  @vmodl_path = Pathname.new(vmodl_path)
  @wsdl_path  = Pathname.new(wsdl_path)

  @vmodl = load_vmodl(@vmodl_path)
  @wsdl  = load_wsdl(@wsdl_path)
end
verify!() click to toggle source
# File lib/tasks/vmodl_helper.rb, line 13
def verify!
  run!('verify')
end

Private Class Methods

options(rake_task) click to toggle source
# File lib/tasks/vmodl_helper.rb, line 28
    def options(rake_task)
      argv = ARGV.slice_after('--').to_a.last
      Optimist.options(argv) do
        educate_on_error
        opt :wsdl, 'Path to the vsphere-ws wsdl file', type: :string, required: true
        opt :vmodl, 'Path to the vmodl.db', type: :string, default: 'vmodl.db'
        banner <<~EOS
          Usage:
          rake vmodl:#{rake_task} -- --wsdl=path/to/wsdl
        EOS
      end
    end
run!(mode) click to toggle source
# File lib/tasks/vmodl_helper.rb, line 23
def run!(mode)
  vmodl, wsdl = options(mode).values_at(:vmodl, :wsdl)
  new(vmodl_path: vmodl, wsdl_path: wsdl).send("#{mode}!")
end

Public Instance Methods

generate!() click to toggle source
# File lib/tasks/vmodl_helper.rb, line 87
def generate!
  wsdl_types_by_name.each_value do |type|
    type_name  = type.name.name
    vmodl_data = @vmodl[type_name]

    if vmodl_data.nil?
      base_class           = wsdl_types_by_name[type.complexcontent.extension.base.name]
      inherited_properties = base_class.elements.map { |element| element.name.name }
      properties           = type.elements.reject { |e| inherited_properties.include?(e.name.name) }

      vmodl_data = {
        'kind'      => 'data',
        'props'     => properties.map do |element|
          {
            'name'           => element.name.name,
            'is-optional'    => element.minoccurs == 0,
            'is-array'       => element.maxoccurs != 1,
            'version-id-ref' => nil,
            'wsdl_type'      => wsdl_to_vmodl_type(element.type)
          }
        end,
        'wsdl_base' => type.complexcontent.extension.base.name
      }

      @vmodl[type_name] = vmodl_data
      @vmodl['_typenames']['_typenames'] << type_name

      puts "Adding #{type_name} to vmodl"

      wsdl_to_rbvmomi_namespace(type).loader.add_types type_name => vmodl_data
    end
  end

  wsdl_types_by_name.each_value do |type|
    type_name = type.name.name
    vmodl_data = @vmodl[type_name]

    elements_by_name = type.elements.index_by { |e| e.name.name }

    # Loop through the properties defined in the vmodl.db for this type and
    # compare the type to that property as defined in the wsdl.
    vmodl_data['props'].each do |vmodl_prop|
      wsdl_prop = elements_by_name[vmodl_prop['name']]
      next if wsdl_prop.nil?

      vmodl_klass = wsdl_constantize(vmodl_prop['wsdl_type'])
      wsdl_klass  = wsdl_constantize(wsdl_prop.type.source)

      vmodl_prop['wsdl_type'] = wsdl_klass.wsdl_name unless vmodl_klass <= wsdl_klass
    end
  end

  dump_vmodl!
end
verify!() click to toggle source
# File lib/tasks/vmodl_helper.rb, line 50
def verify!
  # Loop through the ComplexTypes in the WSDL and compare their types
  # to the types which are defined in the vmodl.db
  wsdl_types_by_name.each_value do |type|
    type_name  = type.name.name
    vmodl_data = @vmodl[type_name]

    # If a type exists in the WSDL but not in the vmodl.db this usually
    # indicates that it was added in a newer version than the current
    # vmodl.db supports.
    #
    # Print a warning that the type is missing and skip it.
    if vmodl_data.nil?
      puts " #{type_name} is missing"
      next
    end

    # Index the properties by name to make it simpler to find later
    elements_by_name = type.elements.index_by { |e| e.name.name }

    # Loop through the properties defined in the vmodl.db for this type and
    # compare the type to that property as defined in the wsdl.
    vmodl_data['props'].each do |vmodl_prop|
      wsdl_prop = elements_by_name[vmodl_prop['name']]
      next if wsdl_prop.nil?

      vmodl_klass = wsdl_constantize(vmodl_prop['wsdl_type'])
      wsdl_klass  = wsdl_constantize(wsdl_prop.type.source)

      # The vmodl class should be equal to or a subclass of the one in the wsdl.
      # 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.
      puts "#{type_name}.#{vmodl_prop["name"]} #{wsdl_klass.wsdl_name} doesn't match #{vmodl_klass.wsdl_name}" unless vmodl_klass <= wsdl_klass
    end
  end
end

Protected Instance Methods

dump_vmodl!() click to toggle source
# File lib/tasks/vmodl_helper.rb, line 156
def dump_vmodl!
  File.write(@vmodl_path, Marshal.dump(@vmodl))
end
load_vmodl(path) click to toggle source
# File lib/tasks/vmodl_helper.rb, line 152
def load_vmodl(path)
  Marshal.load(path.read)
end
load_wsdl(path) click to toggle source
# File lib/tasks/vmodl_helper.rb, line 144
def load_wsdl(path)
  # WSDL includes have to resolve in the local directory so we have to
  # change working directories to where the wsdl is
  Dir.chdir(path.dirname) do
    WSDL::Parser.new.parse(path.read)
  end
end

Private Instance Methods

wsdl_constantize(type) click to toggle source

Normalize the type, some of these don’t have RbVmomi equivalents such as xsd:long and RbVmomi uses ManagedObjects not ManagedObjectReferences as parameters

# File lib/tasks/vmodl_helper.rb, line 196
def wsdl_constantize(type)
  type = type.split(':').last
  type = 'int'           if %w[long short byte].include?(type)
  type = 'float'         if type == 'double'
  type = 'binary'        if type == 'base64Binary'
  type = 'ManagedObject' if type == 'ManagedObjectReference'

  type = type.camelcase
  type.safe_constantize || "RbVmomi::BasicTypes::#{type}".safe_constantize || "RbVmomi::VIM::#{type}".safe_constantize
end
wsdl_to_rbvmomi_namespace(type) click to toggle source
# File lib/tasks/vmodl_helper.rb, line 181
def wsdl_to_rbvmomi_namespace(type)
  case type.targetnamespace
  when 'urb:vim25'
    RbVmomi::VIM
  when 'urn:pbm'
    RbVmomi::PBM
  when 'urn:sms'
    RbVmomi::SMS
  else
    raise ArgumentError, "Unrecognized namespace [#{type}]"
  end
end
wsdl_to_vmodl_type(type) click to toggle source
# File lib/tasks/vmodl_helper.rb, line 168
def wsdl_to_vmodl_type(type)
  case type.source
  when /vim25:/, /pbm:/, /sms:/
    vmodl_type = type.name == 'ManagedObjectReference' ? 'ManagedObject' : type.name
  when /xsd:/
    vmodl_type = type.source
  else
    raise ArgumentError, "Unrecognized wsdl type: [#{type}]"
  end

  vmodl_type
end
wsdl_types_by_name() click to toggle source
# File lib/tasks/vmodl_helper.rb, line 162
def wsdl_types_by_name
  @wsdl_types_by_name ||= @wsdl.collect_complextypes
    .reject   { |type| type.name.name.match?(/^ArrayOf|RequestType$/) }
    .index_by { |type| type.name.name }
end