ManageIQ/manageiq-providers-vmware

View on GitHub
app/models/manageiq/providers/vmware/infra_manager.rb

Summary

Maintainability
B
6 hrs
Test Coverage
F
48%
module ManageIQ::Providers
  class Vmware::InfraManager < InfraManager
    include VimConnectMixin
    include CisConnectMixin

    before_save :stop_event_monitor_queue_on_change, :stop_refresh_worker_queue_on_change
    before_destroy :stop_event_monitor, :stop_refresh_worker

    supports :catalog
    supports :create
    supports :label_mapping
    supports :metrics
    supports :native_console
    supports :provisioning
    supports :smartstate_analysis
    supports :streaming_refresh do
      _("Streaming refresh not enabled") unless streaming_refresh_enabled?
    end

    def self.ems_type
      @ems_type ||= "vmwarews".freeze
    end

    def self.description
      @description ||= "VMware vCenter".freeze
    end

    def self.params_for_create
      {
        :fields => [
          {
            :component => 'sub-form',
            :id        => 'endpoints-subform',
            :name      => 'endpoints-subform',
            :title     => _("Endpoints"),
            :fields    => [
              :component => 'tabs',
              :name      => 'tabs',
              :fields    => [
                {
                  :component => 'tab-item',
                  :id        => 'default-tab',
                  :name      => 'default-tab',
                  :title     => _('Default'),
                  :fields    => [
                    {
                      :component              => 'validate-provider-credentials',
                      :id                     => 'endpoints.default.valid',
                      :name                   => 'endpoints.default.valid',
                      :skipSubmit             => true,
                      :isRequired             => true,
                      :validationDependencies => %w[type zone_id],
                      :fields                 => [
                        {
                          :component  => "text-field",
                          :id         => "endpoints.default.hostname",
                          :name       => "endpoints.default.hostname",
                          :label      => _("Hostname (or IPv4 or IPv6 address)"),
                          :isRequired => true,
                          :validate   => [{:type => "required"}]
                        },
                        {
                          :component    => 'text-field',
                          :id           => 'endpoints.default.port',
                          :name         => 'endpoints.default.port',
                          :label        => _('API Port'),
                          :initialValue => 443,
                          :type         => 'number',
                          :validate     => [
                            {
                              :type  => 'max-number-value',
                              :value => 65_535,
                            }
                          ]
                        },
                        {
                          :component    => "select",
                          :id           => "endpoints.default.verify_ssl",
                          :name         => "endpoints.default.verify_ssl",
                          :label        => _("SSL verification"),
                          :dataType     => "integer",
                          :isRequired   => true,
                          :initialValue => OpenSSL::SSL::VERIFY_PEER,
                          :options      => [
                            {
                              :label => _('Do not verify'),
                              :value => OpenSSL::SSL::VERIFY_NONE,
                            },
                            {
                              :label => _('Verify'),
                              :value => OpenSSL::SSL::VERIFY_PEER,
                            },
                          ]
                        },
                        {
                          :component  => "textarea",
                          :name       => "endpoints.default.certificate_authority",
                          :id         => "endpoints.default.certificate_authority",
                          :label      => _("Trusted CA Certificates"),
                          :rows       => 10,
                          :isRequired => false,
                          :helperText => _('Paste here the trusted CA certificates, in PEM format.'),
                          :condition  => {
                            :when => 'endpoints.default.verify_ssl',
                            :is   => OpenSSL::SSL::VERIFY_PEER,
                          },
                        },
                        {
                          :component  => "text-field",
                          :id         => "authentications.default.userid",
                          :name       => "authentications.default.userid",
                          :label      => _("Username"),
                          :isRequired => true,
                          :validate   => [{:type => "required"}]
                        },
                        {
                          :component  => "password-field",
                          :id         => "authentications.default.password",
                          :name       => "authentications.default.password",
                          :label      => _("Password"),
                          :type       => "password",
                          :isRequired => true,
                          :validate   => [{:type => "required"}]
                        }
                      ],
                    },
                  ],
                },
                {
                  :component => 'tab-item',
                  :id        => 'console-tab',
                  :name      => 'console-tab',
                  :title     => _('VMRC Console'),
                  :fields    => [
                    {
                      :component    => 'protocol-selector',
                      :id           => 'vmrc_console',
                      :name         => 'vmrc_console',
                      :skipSubmit   => true,
                      :label        => _('Access'),
                      :initialValue => 'none',
                      :options      => [
                        {
                          :label => _("Disabled"),
                          :value => 'none',
                        },
                        {
                          :label => _("Enabled"),
                          :value => "enabled",
                          :pivot => 'authentications.console.userid',
                        },
                      ],
                    },
                    {
                      :component              => 'validate-provider-credentials',
                      :id                     => 'endpoints.console.valid',
                      :name                   => 'endpoints.console.valid',
                      :skipSubmit             => true,
                      :isRequired             => true,
                      :validationDependencies => %w[type zone_id endpoints.default.hostname],
                      :condition              => {
                        :when => 'vmrc_console',
                        :is   => 'enabled',
                      },
                      :fields                 => [
                        {
                          :component  => "text-field",
                          :id         => "authentications.console.userid",
                          :name       => "authentications.console.userid",
                          :label      => _("Username"),
                          :isRequired => true,
                          :validate   => [{:type => "required"}],
                        },
                        {
                          :component  => "password-field",
                          :id         => "authentications.console.password",
                          :name       => "authentications.console.password",
                          :label      => _("Password"),
                          :type       => "password",
                          :isRequired => true,
                          :validate   => [{:type => "required"}],
                        },
                      ],
                    },
                  ],
                },
                {
                  :component => 'tab-item',
                  :id        => 'vnc-console-tab',
                  :name      => 'vnc-console-tab',
                  :title     => _('VNC Console Ports'),
                  :fields    => [
                    {
                      :component    => 'protocol-selector',
                      :id           => 'vnc-console',
                      :name         => 'vnc-console',
                      :skipSubmit   => true,
                      :label        => _('VNC Console Ports'),
                      :initialValue => 'none',
                      :options      => [
                        {
                          :label => _("Disabled"),
                          :value => 'none',
                        },
                        {
                          :label => _("Enabled"),
                          :value => "enabled",
                          :pivot => "host_default_vnc_port_start",
                        },
                      ]
                    },
                    {
                      :component => 'text-field',
                      :id        => 'host_default_vnc_port_start',
                      :name      => 'host_default_vnc_port_start',
                      :label     => _('Host Default VNC Start Port'),
                      :type      => 'number',
                      :condition => {
                        :when => 'vnc-console',
                        :is   => 'enabled',
                      },
                      :validate  => [{
                        :type  => 'max-number-value',
                        :value => 65_535,
                      }]
                    },
                    {
                      :component => 'text-field',
                      :id        => 'host_default_vnc_port_end',
                      :name      => 'host_default_vnc_port_end',
                      :label     => _('Host Default VNC End Port'),
                      :type      => 'number',
                      :condition => {
                        :when => 'vnc-console',
                        :is   => 'enabled',
                      },
                      :validate  => [{
                        :type  => 'max-number-value',
                        :value => 65_535,
                      }]
                    }
                  ]
                }
              ]
            ]
          },
        ]
      }.freeze
    end

    def self.verify_credentials(args)
      default_endpoint = args.dig("endpoints", "default")
      hostname, port, verify_ssl, certificate_authority = default_endpoint&.values_at("hostname", "port", "verify_ssl", "certificate_authority")

      authtype = args.dig("authentications").keys.first
      authentication = args.dig("authentications", authtype)
      userid, password = authentication&.values_at('userid', 'password')

      password = ManageIQ::Password.try_decrypt(password)
      password ||= find(args["id"]).authentication_password(authtype) if args['id']

      !!raw_connect(:ip => hostname, :port => port, :user => userid, :pass => password, :verify_ssl => verify_ssl, :certificate_authority => certificate_authority)
    end

    def supported_auth_types
      %w(default console)
    end

    def self.catalog_types
      {"vmware" => N_("VMware"), "generic_ovf_template" => N_("VMware Content Library OVF Template")}
    end

    def streaming_refresh_enabled?
      true
    end

    def allow_targeted_refresh?
      true
    end

    def queue_name_for_ems_operations
      queue_name
    end

    def console_url
      Gem::Version.new(api_version) >= Gem::Version.new("6.5") ? "https://#{hostname}/ui" : "https://#{hostname}/vsphere-client"
    end

    def remote_console_vmrc_acquire_ticket
      vim = connect(:auth_type => :console)
      ticket = vim.acquireCloneTicket

      # The ticket received is valid for 30 seconds, but we can't disconnect the
      #   session until it is used.  So, in a separate thread we will disconnect
      #   after 30 seconds.
      Thread.new(vim) do |handle|
        begin
          sleep 30
        ensure
          handle.disconnect if handle rescue nil
        end
      end

      ticket
    end

    def remote_console_vmrc_support_known?
      !api_version.blank? && !hostname.blank? && !uid_ems.blank?
    end

    def validate_remote_console_vmrc_support
      raise(MiqException::RemoteConsoleNotSupportedError, "vCenter needs to be refreshed to determine VMRC remote console support.")   unless self.remote_console_vmrc_support_known?
      true
    end

    def validate_remote_console_webmks_support
      true
    end

    def after_update_authentication
      super
      stop_refresh_worker_queue_on_credential_change
    end

    def self.event_monitor_class
      self::EventCatcher
    end

    def self.refresh_worker_class
      self::RefreshWorker
    end

    def self.provision_class(via)
      case via
      when "pxe" then self::ProvisionViaPxe
      else            self::Provision
      end
    end

    def self.default_blacklisted_event_names
      %w(
        AlarmActionTriggeredEvent
        AlarmCreatedEvent
        AlarmEmailCompletedEvent
        AlarmEmailFailedEvent
        AlarmReconfiguredEvent
        AlarmRemovedEvent
        AlarmScriptCompleteEvent
        AlarmScriptFailedEvent
        AlarmSnmpCompletedEvent
        AlarmSnmpFailedEvent
        AlarmStatusChangedEvent
        AlreadyAuthenticatedSessionEvent
        EventEx
        UserLoginSessionEvent
        UserLogoutSessionEvent
      )
    end

    def verify_credentials(auth_type = nil, _options = {})
      user, pwd = auth_user_pwd(auth_type)
      self.class.raw_connect(:ip => hostname, :port => port, :user => user, :pass => pwd, :verify_ssl => verify_ssl, :certificate_authority => certificate_authority)
    end

    def get_alarms
      with_provider_connection do |vim|
        miqAm = vim.getVimAlarmManager
        miqAm.getAlarm
      end
    end

    def vm_start(vm, options = {})
      invoke_vim_ws(:start, vm, options[:user_event])
    end

    def vm_stop(vm, options = {})
      invoke_vim_ws(:stop, vm, options[:user_event])
    end

    def vm_poweroff(vm, options = {})
      vm_stop(vm, options)
    end

    def vm_suspend(vm, options = {})
      invoke_vim_ws(:suspend, vm, options[:user_event])
    end

    def vm_shutdown_guest(vm, options = {})
      invoke_vim_ws(:shutdownGuest, vm, options[:user_event])
    end

    def vm_reboot_guest(vm, options = {})
      invoke_vim_ws(:rebootGuest, vm, options[:user_event])
    end

    def vm_reset(vm, options = {})
      invoke_vim_ws(:reset, vm, options[:user_event])
    end

    def vm_standby_guest(vm, options = {})
      invoke_vim_ws(:standbyGuest, vm, options[:user_event])
    end

    def vm_unregister(vm, options = {})
      invoke_vim_ws(:unregister, vm, options[:user_event])
    end

    def vm_mark_as_template(vm, options = {})
      invoke_vim_ws(:markAsTemplate, vm, options[:user_event])
    end

    def vm_mark_as_vm(vm, options = {})
      defaults = {
        :host     => nil,
      }
      options = defaults.merge(options)
      invoke_vim_ws(:markAsVm, vm, options[:user_event], options[:pool], options[:host])
    end

    def vm_migrate(vm, options = {})
      defaults = {
        :pool     => nil,
        :priority => "defaultPriority",
        :state    => nil
      }
      options = defaults.merge(options)

      # Convert host to its MOR, if host is an ActiveRecord Host
      host     = options[:host]
      host_mor = host.kind_of?(Host) ? host.ems_ref_obj : host

      # If pool is nil, use the host's default resource pool, if possible
      # Convert pool to its MOR, if pool is an ActiveRecord ResourcePool
      pool     = options[:pool]
      pool ||= (host.default_resource_pool || (host.ems_cluster && host.ems_cluster.default_resource_pool)) if host.kind_of?(Host)
      pool_mor = pool.kind_of?(ResourcePool) ? pool.ems_ref_obj : pool

      invoke_vim_ws(:migrate, vm, options[:user_event], host_mor, pool_mor, options[:priority], options[:state])
    end

    def vm_relocate(vm, options = {})
      defaults = {
        :host           => nil,
        :pool           => nil,
        :datastore      => nil,
        :disk_move_type => nil,
        :transform      => nil,
        :priority       => "defaultPriority",
        :disk           => nil
      }
      options = defaults.merge(options)
      invoke_vim_ws(:relocateVM, vm, options[:user_event], options[:host], options[:pool], options[:datastore], options[:disk_move_type], options[:transform], options[:priority], options[:disk])
    end

    def vm_move_into_folder(vm, options = {})
      invoke_vim_ws(:moveIntoFolder, options[:folder], options[:user_event], vm.ems_ref_obj)
    end

    def vm_clone(vm, options = {})
      defaults = {
        :pool          => nil,
        :host          => nil,
        :datastore     => nil,
        :powerOn       => false,
        :template      => false,
        :transform     => nil,
        :config        => nil,
        :customization => nil,
        :disk          => nil
      }
      options = defaults.merge(options)
      invoke_vim_ws(:cloneVM, vm, options[:user_event], options[:name], options[:folder], options[:pool], options[:host], options[:datastore], options[:powerOn], options[:template], options[:transform], options[:config], options[:customization], options[:disk])
    end

    def vm_rename(vm, options = {})
      invoke_vim_ws(:renameVM, vm, options[:user_event], options[:new_name])
    end

    def vm_connect_all(vm, options = {})
      defaults = {:onStartup => false}
      options  = defaults.merge(options)
      vm_connect_disconnect_all_connectable_devices(vm, true, options[:onStartup], options[:user_event])
    end

    def vm_disconnect_all(vm, options = {})
      defaults = {:onStartup => false}
      options  = defaults.merge(options)
      vm_connect_disconnect_all_connectable_devices(vm, false, options[:onStartup], options[:user_event])
    end

    def vm_connect_cdrom(vm, options = {})
      defaults = {:onStartup => false}
      options  = defaults.merge(options)
      vm_connect_disconnect_cdrom(vm, true, options[:onStartup], options[:user_event])
    end

    def vm_disconnect_cdrom(vm, options = {})
      defaults = {:onStartup => false}
      options  = defaults.merge(options)
      vm_connect_disconnect_cdrom(vm, false, options[:onStartup], options[:user_event])
    end

    def vm_connect_floppy(vm, options = {})
      defaults = {:onStartup => false}
      options  = defaults.merge(options)
      vm_connect_disconnect_floppy(vm, true, options[:onStartup], options[:user_event])
    end

    def vm_disconnect_floppy(vm, options = {})
      defaults = {:onStartup => false}
      options  = defaults.merge(options)
      vm_connect_disconnect_floppy(vm, false, options[:onStartup], options[:user_event])
    end

    def vm_connect_disconnect_cdrom(vm, connect, onStartup = false, user_event = nil)
      vm_connect_disconnect_specified_connectable_devices(vm, "CD/DVD Drive", connect, onStartup, user_event)
    end

    def vm_connect_disconnect_floppy(vm, connect, onStartup = false, user_event = nil)
      vm_connect_disconnect_specified_connectable_devices(vm, "Floppy Drive", connect, onStartup, user_event)
    end

    def vm_connect_disconnect_all_connectable_devices(vm, connect, onStartup = false, user_event = nil)
      vm_connect_disconnect_specified_connectable_devices(vm, "*", connect, onStartup, user_event)
    end

    def vm_connect_disconnect_specified_connectable_devices(vm, deviceLabel, connect, onStartup = false, user_event = nil)
      vm.with_provider_object do |vim_vm|
        vim_vm.logUserEvent(user_event) if user_event

        _log.info("EMS: [#{name}] VM path [#{vm.path}] Invoking [devicesByFilter]...")
        devs = vim_vm.devicesByFilter("connectable.connected" => /(false|true)/)
        devs.each do |dev|
          currentLabel = dev['deviceInfo']['label']
          next if  (deviceLabel != "*") && (currentLabel.index(deviceLabel) != 0)
          _log.info("EMS: [#{name}] VM path [#{vm.path}] Invoking [connectDevice] for device [#{currentLabel}]...")
          result = vim_vm.connectDevice(dev, connect, onStartup)
          _log.info("EMS: [#{name}] VM path [#{vm.path}] Returned with result [#{result}]...")
        end

        _log.info("EMS: [#{name}] VM path [#{vm.path}] Invoking [refresh]...")
        vim_vm.refresh
      end
    end

    def vm_create_snapshot(vm, options = {})
      defaults = {
        :memory             => false,
        :quiesce            => "false",
        :wait               => true,
        :free_space_percent => ::Settings.snapshots.create_free_percent
      }
      options = defaults.merge(options)
      invoke_vim_ws(:createSnapshot, vm, options[:user_event], options[:name], options[:desc], options[:memory], options[:quiesce], options[:wait], options[:free_space_percent])
    end

    def vm_create_evm_snapshot(vm, options = {})
      defaults = {
        :quiesce            => "false",
        :wait               => true,
        :free_space_percent => ::Settings.snapshots.create_free_percent
      }
      options = defaults.merge(options)
      invoke_vim_ws(:createEvmSnapshot, vm, options[:user_event], options[:desc], options[:quiesce], options[:wait], options[:free_space_percent])
    end

    def vm_remove_snapshot(vm, options = {})
      defaults = {
        :subTree            => "false",
        :wait               => true,
        :free_space_percent => ::Settings.snapshots.remove_free_percent
      }
      options = defaults.merge(options)
      invoke_vim_ws(:removeSnapshot, vm, options[:user_event], options[:snMor], options[:subTree], options[:wait], options[:free_space_percent])
    end

    def vm_remove_snapshot_by_description(vm, options = {})
      defaults = {
        :subTree            => "false",
        :refresh            => false,
        :wait               => true,
        :free_space_percent => ::Settings.snapshots.remove_free_percent
      }
      options.reverse_merge!(defaults)
      invoke_vim_ws(:removeSnapshotByDescription, vm, options[:user_event], options[:description], options[:refresh], options[:subTree], options[:wait], options[:free_space_percent])
    end

    def vm_remove_all_snapshots(vm, options = {})
      defaults = {
        :free_space_percent => ::Settings.snapshots.remove_free_percent
      }
      options.reverse_merge!(defaults)
      invoke_vim_ws(:removeAllSnapshots, vm, options[:user_event], options[:free_space_percent])
    end

    def vm_revert_to_snapshot(vm, options = {})
      invoke_vim_ws(:revertToSnapshot, vm, options[:user_event], options[:snMor])
    end

    def vm_add_disk(vm, options = {})
      invoke_vim_ws(:addDisk, vm, options[:user_event], options[:diskName], options[:diskSize], nil, nil,
                    :thin_provisioned => options[:thinProvisioned], :dependent => options[:dependent], :persistent => options[:persistent])
    end

    def vm_remove_disk_by_file(vm, options = {})
      options[:delete_backing] = true if options[:delete_backing].nil?
      invoke_vim_ws(:removeDiskByFile, vm, options[:user_event], options[:diskName], options[:delete_backing])
    end
    alias vm_remove_disk vm_remove_disk_by_file

    def vm_resize_disk(vm, options = {})
      invoke_vim_ws(:resizeDisk, vm, options[:user_event], options[:diskName], options[:newSizeInKb])
    end

    def vm_acquire_ticket(vm, options = {})
      invoke_vim_ws(:acquireTicket, vm, options[:user_event], options[:ticket_type])
    end
    alias_method :vm_remote_console_acquire_ticket, :vm_acquire_ticket

    def vm_acquire_webmks_ticket(vm, options = {})
      vm_acquire_ticket(vm, options.merge(:ticket_type => 'webmks'))
    end
    alias_method :vm_remote_console_webmks_acquire_ticket, :vm_acquire_webmks_ticket

    def vm_add_miq_alarm(vm, _options = {})
      result = nil
      vm.with_provider_object do |vim_vm|
        vim_vm.removeMiqAlarm
        result = vim_vm.addMiqAlarm
      end
      result
    end

    def vm_set_memory(vm, options = {})
      invoke_vim_ws(:setMemory, vm, options[:user_event], options[:value])
    end

    def vm_set_num_cpus(vm, options = {})
      invoke_vim_ws(:setNumCPUs, vm, options[:user_event], options[:value])
    end

    def vm_set_custom_field(vm, options = {})
      invoke_vim_ws(:setCustomField, vm, options[:user_event], options[:attribute], options[:value])
    end

    def vm_reconfigure(vm, options = {})
      invoke_vim_ws(:reconfig, vm, options[:user_event], options[:spec])
    end

    def vm_destroy(vm, options = {})
      invoke_vim_ws(:destroy, vm, options[:user_event])
    end

    def vm_quick_stats(obj, options = {})
      invoke_vim_ws(:quickStats, obj, options[:user_event])
    end
    alias_method :host_quick_stats, :vm_quick_stats

    def vm_set_description(vm, new_description, options = {})
      options[:spec] = VimHash.new("VirtualMachineConfigSpec") do |spec|
        spec.annotation = new_description
      end

      vm_reconfigure(vm, options)
    end

    def invoke_vim_ws(cmd, obj, user_event = nil, *opts)
      log_header = "EMS: [#{name}] #{obj.class.name}: id [#{obj.id}], name [#{obj.name}], ems_ref [#{obj.ems_ref}]"
      result = nil

      if obj.kind_of?(self.class::Vm) || obj.kind_of?(self.class::Template) || obj.kind_of?(self.class::Host) || obj.kind_of?(EmsCluster) || obj.kind_of?(EmsFolder)
        obj.with_provider_object do |vim_obj|
          vim_obj.logUserEvent(user_event) if user_event && obj.kind_of?(Vm)

          _log.info("#{log_header} Invoking [#{cmd}]...")
          result = vim_obj.send(cmd, *opts)
          _log.info("#{log_header} Returned with result [#{result}]")
        end
      else
        _log.warn("#{log_header} VIM calls not supported, invocation skipped")
      end

      result
    end

    # Find the VmCreated events for a list of VMs and return the time
    def find_vm_create_events(vms_list)
      # Create a hash of VM uuids for lookup
      vm_guids = {}
      vms_list.each { |v| vm_guids[v[:uid_ems]] = v }

      found = []
      event_array = ['VmCreatedEvent']

      with_provider_connection do |vim|
        eventSpec = VimHash.new("EventFilterSpec") do |efs|
          efs.time = VimHash.new("EventFilterSpecByTime") { |eft| eft.endTime = vim.currentServerTime.to_s }
          efs.disableFullMessage = 'false'
          if vim.v4
            efs.eventTypeId = event_array
          else
            efs['type'] = event_array
          end
        end

        miqEh = vim.getVimEventHistory(eventSpec)
        begin
          miqEh.events do |event|
            # Check to see if the VM is still in the inventory.
            # Match by MOR and VM name, just in case the MOR was reused.
            vm = vim.virtualMachinesByFilter('summary.vm' => event.vm.vm, 'config.name' => event.vm.name).first unless event.vm.nil?
            next if vm.nil?
            current_uid = vm.config.uuid
            if vm_guids.key?(current_uid)
              item  = vm_guids.delete(current_uid)
              item[:created_time] = event.createdTime
              found << item
            end
            break if vm_guids.empty?
          end
        ensure
          miqEh.release unless miqEh.nil? rescue nil
        end
      end

      # Return list of VMs that we found create events for
      found
    end

    def assign_ems_created_on_queue(vm_ids)
      MiqQueue.submit_job(
        :class_name  => self.class.name,
        :instance_id => id,
        :method_name => 'assign_ems_created_on',
        :queue_name  => queue_name_for_ems_operations,
        :role        => 'ems_operations',
        :args        => [vm_ids],
        :priority    => MiqQueue::MIN_PRIORITY
      )
    end

    def assign_ems_created_on(vm_ids)
      vms_to_update = vms_and_templates.where(:id => vm_ids, :ems_created_on => nil)
      return if vms_to_update.empty?

      # Of the VMs without a VM create time, filter out the ones for which we
      #   already have a VM create event
      vms_to_update = vms_to_update.reject do |v|
        event = v.ems_events.find_by(:event_type => ["VmCreatedEvent", "VmDeployedEvent"])
        v.update_attribute(:ems_created_on, event.timestamp) if event && v.ems_created_on != event.timestamp
        event
      end
      return if vms_to_update.empty?

      # Of the VMs still without an VM create time, use historical events, if
      #   available, to determine the VM create time

      vms_list = vms_to_update.collect { |v| {:id => v.id, :name => v.name, :uid_ems => v.uid_ems} }
      found = find_vm_create_events(vms_list)

      # Loop through the found VMs and set their create times
      found.each do |vmh|
        v = vms_to_update.detect { |vm| vm.id == vmh[:id] }
        v.update_attribute(:ems_created_on, vmh[:created_time])
      end
    end

    def get_files_on_datastore(datastore)
      with_provider_connection do |vim|
        begin
          vim_ds = vim.getVimDataStore(datastore.name)
          return vim_ds.dsFolderFileList
        rescue Handsoap::Fault, StandardError, Timeout::Error, DRb::DRbConnError => err
          _log.log_backtrace(err)
          raise MiqException::MiqStorageError, "Error communicating with Host: [#{name}]"
        ensure
          begin
            vim_ds.release if vim_ds
          rescue
            # TODO: specify what to rescue
            # TODO: log it
            nil
          end
        end
      end

      nil
    end

    def refresh_files_on_datastore(datastore)
      hashes = self.class::RefreshParser.datastore_file_inv_to_hashes(
        get_files_on_datastore(datastore), datastore.vm_ids_by_path)
      EmsRefresh.save_storage_files_inventory(datastore, hashes)
    end

    def connect(options = {})
      service = options[:service] || 'vim'
      send("#{service}_connect", options)
    end

    def self.display_name(number = 1)
      n_('Infrastructure Provider (VMware)', 'Infrastructure Providers (VMware)', number)
    end

    LABEL_MAPPING_ENTITIES = {
      "VmVmware" => "ManageIQ::Providers::Vmware::InfraManager::Vm"
    }.freeze

    def self.entities_for_label_mapping
      LABEL_MAPPING_ENTITIES
    end

    def self.label_mapping_prefix
      "vmware"
    end
  end
end