ManageIQ/ovirt_metrics

View on GitHub
lib/ovirt_metrics.rb

Summary

Maintainability
A
1 hr
Test Coverage
B
81%
require "active_record"
require "ovirt_metrics/version"
require "ovirt_metrics/configurator"
require "ovirt_metrics/column_definitions"
require "ovirt_metrics/nic_metrics"

$:.push File.expand_path(File.dirname(__FILE__))
require 'ovirt_metrics/models/ovirt_history'
Dir.glob(File.expand_path(File.join(File.dirname(__FILE__), "ovirt_metrics", "models", "*.rb"))) { |f| require "ovirt_metrics/models/#{File.basename(f, ".*")}" }

module OvirtMetrics
  DEFAULT_HISTORY_DATABASE_NAME     = "ovirt_engine_history".freeze
  DEFAULT_HISTORY_DATABASE_NAME_3_0 = "rhevm_history".freeze
  VM_NOT_RUNNING                    = 0

  def self.config
    @config ||= Configurator.new
    yield @config if block_given?
    @config
  end

  def self.establish_connection(opts)
    self.connect(opts)
  end

  def self.connect(opts)
    opts            ||= {}
    opts[:port]     ||= 5432
    opts[:database] ||= DEFAULT_HISTORY_DATABASE_NAME
    opts[:adapter] = begin
                       require "active_record/connection_adapters/ovirt_postgresql_adapter"
                       'ovirt_postgresql'
                     end

    # Don't allow accidental connections to localhost.  A blank host will
    # connect to localhost, so don't allow that at all.
    host = opts[:host].to_s.strip
    raise ArgumentError, "host cannot be blank"            if host.empty?
    raise ArgumentError, "host cannot be set to localhost" if ["localhost", "localhost.localdomain", "127.0.0.1", "0.0.0.0"].include?(host)

    OvirtHistory.establish_connection(opts)
  end

  # Note: This method is expected to raise PGErrors and other exceptions
  def self.connected?
    OvirtHistory.connection.active? && OvirtHistory.connected?
  rescue ActiveRecord::ConnectionNotEstablished => e
    if e.message =~ /No connection pool.+found/
      false
    else
      raise
    end
  end

  def self.disconnect
    OvirtHistory.remove_connection
  end

  def self.vm_realtime(vm_id, start_time = nil, end_time = nil)
    metrics      = query_vm_realtime_metrics(vm_id, start_time, end_time)
    disk_metrics = query_vm_disk_realtime_metrics(vm_id, start_time, end_time)
    nic_metrics  = query_vm_nic_realtime_metrics(vm_id, start_time, end_time)
    vm_realtime_metrics_to_hashes(metrics, disk_metrics, nic_metrics)
  end

  def self.host_realtime(host_id, start_time = nil, end_time = nil)
    metrics      = query_host_realtime_metrics(host_id, start_time, end_time).all
    nic_metrics  = query_host_nic_realtime_metrics(host_id, start_time, end_time)
    host_realtime_metrics_to_hashes(metrics, nic_metrics)
  end

  def self.warn(message)
    unless config.suppress_warnings
      puts "#{message}  To hide this warning, use OvirtMetrics.config.suppress_warnings = true"
    end
  end

  # The methods below this line are PRIVATE not meant to be used out of the scope
  # of this class. TODO refactor to force privacy or remove this line.

  def self.query_host_realtime_metrics(host_id, start_time = nil, end_time = nil)
    HostSamplesHistory.where(:host_id => host_id).includes(:host_configuration).with_time_range(start_time, end_time)
  end

  def self.query_host_nic_realtime_metrics(host_id, start_time = nil, end_time = nil)
    nic_ids = HostInterfaceConfiguration.where(:host_id => host_id).collect(&:host_interface_id)
    HostInterfaceSamplesHistory.where(:host_interface_id => nic_ids).with_time_range(start_time, end_time)
  end

  def self.host_realtime_metrics_to_hashes(metrics, nic_metrics)
    related_metrics = { }
    related_metrics[:nic]  = nic_metrics.group_by  { |m| m.history_datetime }

    metrics_to_hashes(metrics, related_metrics, HOST_COLUMN_DEFINITIONS, :host_metric_to_href)
  end

  def self.query_vm_realtime_metrics(vm_id, start_time = nil, end_time = nil)
    VmSamplesHistory.where(:vm_id => vm_id).includes(:host_configuration).with_time_range(start_time, end_time)
  end

  def self.query_vm_disk_realtime_metrics(vm_id, start_time = nil, end_time = nil)
    disk_ids = vms_disk_ids_for(vm_id)
    VmDiskSamplesHistory.where(:vm_disk_id => disk_ids).with_time_range(start_time, end_time)
  end

  def self.vms_disk_ids_for(vm_id)
    VmDeviceHistory.where(:vm_id => vm_id).disks.attached.pluck('DISTINCT device_id')
  end

  def self.vms_nic_ids_for(vm_id)
    VmDeviceHistory.where(:vm_id => vm_id).nics.attached.pluck("DISTINCT device_id")
  end

  def self.query_vm_nic_realtime_metrics(vm_id, start_time = nil, end_time = nil)
    nic_ids = vms_nic_ids_for(vm_id)
    VmInterfaceSamplesHistory.where(:vm_interface_id => nic_ids).with_time_range(start_time, end_time)
  end

  def self.vm_realtime_metrics_to_hashes(metrics, disk_metrics, nic_metrics)
    related_metrics = { }
    related_metrics[:disk] = disk_metrics.group_by { |m| m.history_datetime }
    related_metrics[:nic]  = nic_metrics.group_by  { |m| m.history_datetime }

    metrics_to_hashes(metrics, related_metrics, VM_COLUMN_DEFINITIONS, :vm_metric_to_href)
  end

  def self.vm_metric_to_href(metric)
    "/api/vms/#{metric.vm_id}"
  end

  def self.host_metric_to_href(metric)
    "/api/hosts/#{metric.host_id}"
  end

  def self.record_duplication_required?(metric)
    metric.try(:seconds_in_status) != 20
  end

  def self.metrics_to_hashes(metrics, related_metrics, column_definitions, href_method)
    counters_by_id              = {}
    counter_values_by_id_and_ts = {}

    metrics.each do |metric|
      options = { :metric => metric }
      related_metrics.each { |key, related_metric| options[key] = related_metric[metric.history_datetime] }

      href = self.send(href_method, metric)

      counters_by_id[href] ||= {}
      values = {}

      column_definitions.each do |evm_col, info|
        next if column_definitions == VM_COLUMN_DEFINITIONS && options[:metric].vm_status.to_i == VM_NOT_RUNNING

        counters_by_id[href][info[:ovirt_key]] ||= info[:counter]
        values[info[:ovirt_key]] = info[:ovirt_method].call(options)
      end

      counter_values_by_id_and_ts[href] ||= {}
      counter_values_by_id_and_ts[href][(metric.history_datetime).utc.iso8601] = values
      offsets = record_duplication_required?(metric) ? [0, 20, 40] : [0]
      offsets.each do |t|
        counter_values_by_id_and_ts[href][(metric.history_datetime + t).utc.iso8601] = values
      end
    end
    return counters_by_id, counter_values_by_id_and_ts
  end
end