app/models/hardware.rb
class Hardware < ApplicationRecord
belongs_to :vm_or_template
belongs_to :vm, :foreign_key => :vm_or_template_id
belongs_to :miq_template, :foreign_key => :vm_or_template_id
belongs_to :host
belongs_to :computer_system
belongs_to :physical_switch, :foreign_key => :switch_id, :inverse_of => :hardware
has_many :networks, :dependent => :destroy
has_many :firmwares, :as => :resource, :dependent => :destroy
has_many :disks, -> { order(:location) }, :dependent => :destroy
has_many :hard_disks, -> { where.not(:device_type => 'floppy').where.not(Disk.arel_table[:device_type].lower.matches('%cdrom%')).order(:location) }, :class_name => "Disk", :foreign_key => :hardware_id
has_many :floppies, -> { where(:device_type => 'floppy').order(:location) }, :class_name => "Disk", :foreign_key => :hardware_id
has_many :cdroms, -> { where(Disk.arel_table[:device_type].lower.matches('%cdrom%')).order(:location) }, :class_name => "Disk", :foreign_key => :hardware_id
has_many :partitions, :dependent => :destroy
has_many :volumes, :dependent => :destroy
has_many :guest_devices, :dependent => :destroy
has_many :storage_adapters, -> { where(:device_type => 'storage') }, :class_name => "GuestDevice", :foreign_key => :hardware_id
has_many :nics, -> { where(:device_type => 'ethernet') }, :class_name => "GuestDevice", :foreign_key => :hardware_id
has_many :ports, -> { where.not(:device_type => 'storage') }, :class_name => "GuestDevice", :foreign_key => :hardware_id
has_many :physical_ports, -> { where(:device_type => 'physical_port') }, :class_name => "GuestDevice", :foreign_key => :hardware_id
has_many :connected_physical_switches, :through => :guest_devices
has_many :management_devices, -> { where(:device_type => 'management') }, :class_name => "GuestDevice", :foreign_key => :hardware_id
virtual_column :ipaddresses, :type => :string_set, :uses => :networks
virtual_column :hostnames, :type => :string_set, :uses => :networks
virtual_column :mac_addresses, :type => :string_set, :uses => :nics
virtual_sum :used_disk_storage, :disks, :used_disk_storage
virtual_sum :allocated_disk_storage, :disks, :size
virtual_total :num_disks, :disks
virtual_total :num_hard_disks, :hard_disks
def ipaddresses
@ipaddresses ||= if networks.loaded?
networks.collect(&:ipaddress).compact.uniq + networks.collect(&:ipv6address).compact.uniq
else
networks.pluck(:ipaddress, :ipv6address).flatten.tap(&:compact!).tap(&:uniq!)
end
end
def hostnames
@hostnames ||= networks.collect(&:hostname).compact.uniq
end
def mac_addresses
@mac_addresses ||= nics.collect(&:address).compact.uniq
end
def ram_size_in_bytes
memory_mb.to_i * 1.megabyte
end
virtual_attribute :ram_size_in_bytes, :integer, :arel => (lambda do |t|
t.grouping(Arel::Nodes::Multiplication.new([t[:memory_mb]], 1.megabyte))
end)
@@dh = {"type" => "device_name", "devicetype" => "device_type", "id" => "location", "present" => "present",
"filename" => "filename", "startconnected" => "start_connected", "autodetect" => "auto_detect", "mode" => "mode",
"connectiontype" => "mode", "size" => "size", "free_space" => "free_space", "size_on_disk" => "size_on_disk",
"generatedaddress" => "address", "disk_type" => "disk_type"}
def self.add_elements(parent, xmlNode)
_log.info("Adding Hardware XML elements for VM[id]=[#{parent.id}] from XML doc [#{xmlNode.root.name}]")
parent.hardware = Hardware.new if parent.hardware.nil?
# Record guest_devices so we can delete any removed items.
deletes = {:gd => [], :disk => []}
# Excluding ethernet devices from deletes because the refresh is the master of the data and it will handle the deletes.
deletes[:gd] = parent.hardware.guest_devices
.where.not(:device_type => "ethernet")
.select(:id, :device_type, :location, :address)
.collect { |rec| [rec.id, [rec.device_type, rec.location, rec.address]] }
deletes[:disk] = parent.hardware.disks.select(:id, :device_type, :location)
.collect { |rec| [rec.id, [rec.device_type, rec.location]] }
xmlNode.root.each_recursive do |e|
begin
parent.hardware.send(:"m_#{e.name}", parent, e, deletes) if parent.hardware.respond_to?(:"m_#{e.name}")
rescue => err
_log.warn(err.to_s)
end
end
GuestDevice.delete(deletes[:gd].transpose[0])
Disk.delete(deletes[:disk].transpose[0])
# Count the count of ethernet devices
parent.hardware.number_of_nics = parent.hardware.nics.length
parent.hardware.save
end
def aggregate_cpu_speed
if has_attribute?("aggregate_cpu_speed")
self["aggregate_cpu_speed"]
elsif try(:cpu_total_cores) && try(:cpu_speed)
cpu_total_cores * cpu_speed
end
end
virtual_attribute :aggregate_cpu_speed, :integer, :arel => (lambda do |t|
t.grouping(t[:cpu_total_cores] * t[:cpu_speed])
end)
def v_pct_free_disk_space
return nil if disk_free_space.nil? || disk_capacity.nil? || disk_capacity.zero?
(disk_free_space.to_f / disk_capacity * 100).round(2)
end
# resulting sql: "(cast(disk_free_space as float) / (disk_capacity * 100))"
virtual_attribute :v_pct_free_disk_space, :float, :arel => (lambda do |t|
t.grouping(Arel::Nodes::Division.new(
Arel::Nodes::NamedFunction.new("CAST", [t[:disk_free_space].as("float")]),
t[:disk_capacity]) * 100)
end)
def v_pct_used_disk_space
percent_free = v_pct_free_disk_space
100 - percent_free if percent_free
end
# resulting sql: "(cast(disk_free_space as float) / (disk_capacity * -100) + 100)"
# to work with arel better, put the 100 at the end
virtual_attribute :v_pct_used_disk_space, :float, :arel => (lambda do |t|
t.grouping(Arel::Nodes::Division.new(
Arel::Nodes::NamedFunction.new("CAST", [t[:disk_free_space].as("float")]),
t[:disk_capacity]) * -100 + 100)
end)
def provisioned_storage
if has_attribute?("provisioned_storage")
self["provisioned_storage"]
else
allocated_disk_storage.to_i + ram_size_in_bytes
end
end
virtual_attribute :provisioned_storage, :integer, :arel => (lambda do |t|
t.grouping(
t.grouping(t.coalesce([t[:allocated_disk_storage], 0])) +
(t.grouping(t.coalesce([t[:memory_mb], 0])) * 1.megabyte)
)
end)
def connect_lans(lans)
return if lans.blank?
nics.each do |n|
# TODO: Use a different field here
# model is temporarily being used here to transfer the name of the
# lan to which this nic is connected. If model ends up being an
# otherwise used field, this will need to change
n.lan = lans.find { |l| l.name == n.model }
n.model = nil
n.save
end
end
def disconnect_lans
nics.each do |n|
n.lan = nil
n.save
end
end
def m_controller(_parent, xmlNode, deletes)
# $log.info("Adding controller XML elements for [#{xmlNode.attributes["type"]}]")
xmlNode.each_element do |e|
next if e.attributes['present'].to_s.downcase == "false"
da = {"device_type" => xmlNode.attributes["type"].to_s.downcase, "controller_type" => xmlNode.attributes["type"]}
# Loop over the device mapping table and add attributes
@@dh.each_pair { |k, v| da.merge!(v => e.attributes[k]) if e.attributes[k] }
if da["device_name"] == 'disk'
target = disks
target_type = :disk
else
target = guest_devices
target_type = :gd
end
# Try to find the existing row
found = target.find_by(:device_type => da["device_type"], :location => da["location"])
found ||= da["address"] && target.find_by(:device_type => da["device_type"], :address => da["address"])
# Add or update the device
if found.nil?
target.create(da)
else
da.delete('device_name') if target_type == :disk
found.update(da)
end
# Remove the devices from the delete list if it matches on device_type and either location or address
deletes[target_type].delete_if { |ele| (ele[1][0] == da["device_type"]) && (ele[1][1] == da["location"] || (!da["address"].nil? && ele[1][2] == da["address"])) }
end
end
def m_memory(_parent, xmlNode, _deletes)
self.memory_mb = xmlNode.attributes["memsize"]
end
def m_bios(_parent, xmlNode, _deletes)
new_bios = Digest::UUID.clean(xmlNode.attributes["bios"])
self.bios = new_bios.nil? ? xmlNode.attributes["bios"] : new_bios
new_bios = Digest::UUID.clean(xmlNode.attributes["location"])
self.bios_location = new_bios.nil? ? xmlNode.attributes["location"] : new_bios
end
def m_vm(parent, xmlNode, _deletes)
xmlNode.each_element do |e|
self.guest_os = e.attributes["guestos"] if e.name == "guestos"
self.config_version = e.attributes["version"] if e.name == "config"
self.virtual_hw_version = e.attributes["version"] if e.name == "virtualhw"
self.time_sync = e.attributes["synctime"] if e.name == "tools"
self.annotation = e.attributes["annotation"] if e.name == "annotation"
self.cpu_speed = e.attributes["cpuspeed"] if e.name == "cpuspeed"
self.cpu_type = e.attributes["cputype"] if e.name == "cputype"
parent.autostart = e.attributes["autostart"] if e.name == "autostart"
self.cpu_sockets = e.attributes["numvcpus"] if e.name == "numvcpus"
end
end
def m_files(_parent, xmlNode, _deletes)
self.size_on_disk = xmlNode.attributes["size_on_disk"]
self.disk_free_space = xmlNode.attributes["disk_free_space"]
self.disk_capacity = xmlNode.attributes["disk_capacity"]
end
def m_snapshots(parent, xmlNode, _deletes)
Snapshot.add_elements(parent, xmlNode)
end
def m_volumes(parent, xmlNode, _deletes)
Volume.add_elements(parent, xmlNode)
end
end