require 'awesome_spawn' require 'MiqVm/MiqVm' class MiqRhevmVm < MiqVm RHEV_NFS_UID = 36 def openDisks(diskFiles) mount_storage if @ost.nfs_storage_mounted $log.debug "MiqRhevmVm#openDisks: setting euid = #{RHEV_NFS_UID}" orig_uid = Process::UID.eid Process::UID.grant_privilege(RHEV_NFS_UID) end rv = super if @ost.nfs_storage_mounted $log.debug "MiqRhevmVm#openDisks: resetting euid = #{orig_uid}" Process::UID.grant_privilege(orig_uid) end rv end def unmount super ensure unmount_storage end def getCfg(_snap = nil) cfg_props = @rhevmVm.as_json.with_indifferent_access raise MiqException::MiqVimError, "Failed to retrieve configuration information for VM" if cfg_props.nil? storage_domains = @rhevm.collect_storagedomains $log.debug "MiqRhevmVm#getCfg: storage_domains = #{storage_domains.inspect}" cfg_hash = {} cfg_hash['displayname'] = cfg_props[:name] cfg_hash['guestos'] = cfg_props.fetch_path(:os, :type) cfg_hash['memsize'] = cfg_props[:memory] / 1_048_576 # in MB cfg_hash['numvcpu'] = cfg_props.fetch_path(:cpu, :sockets) # Collect disk information disks = @rhevm.collect_vm_disks(@rhevmVm) disks.each_with_index do |disk, idx| $log.debug "MiqRhevmVm#getCfg: disk = #{disk.inspect}" storage_domain = disk.storage_domains&.first if storage_domain.nil? $log.info("Disk <#{disk.name}> is skipped due to unassigned storage domain") next end storage_id = storage_domain.id storage_obj = storage_domains_by_id[storage_id] file_path = file_path_for_storage_type(storage_obj, disk) file_path_s = file_path.to_s tag = "scsi0:#{idx}" cfg_hash["#{tag}.present"] = "true" cfg_hash["#{tag}.devicetype"] = "disk" cfg_hash["#{tag}.filename"] = file_path_s cfg_hash["#{tag}.format"] = disk.format if storage_type_block?(storage_obj.storage.type) && !lv_active?(file_path_s) AwesomeSpawn.run!("sudo lvchange", :params => [:activate, "y", file_path_s]) end end cfg_hash end def file_path_for_storage_type(storage_obj, disk) storage_type = storage_obj&.storage&.type # TODO: account for other storage types here. if storage_type_file?(storage_type) add_fs_mount(storage_obj) fs_file_path(storage_obj, disk) else lun_file_path(storage_obj, disk) end end def nfs_mount_root @nfs_mount_root ||= @ost.nfs_mount_root || "/mnt/#{@rhevmVm.id}" end def fs_file_path(storage_obj, disk) storage_id = storage_obj.id disk_id = disk.id image_id = disk.image_id mount_point = nfs_mounts[storage_id][:mount_point] ::File.join(mount_point, storage_id, 'images', disk_id, image_id) end def lun_file_path(storage_obj, disk) storage_id = storage_obj.id disk_id = disk.image_id || disk.id ::File.join('/dev', storage_id, disk_id) end def storage_domains_by_id @storage_domains_by_id ||= @rhevm.collect_storagedomains.each_with_object({}) { |sd, sdh| sdh[sd.id] = sd } end # # Returns uri and mount points, hashed by storage ID. # def add_fs_mount(storage_obj) storage_id = storage_obj.id return if nfs_mounts[storage_id] mount_point = ::File.join(nfs_mount_root, nfs_mount_dir(storage_obj)) type = storage_obj.storage.type nfs_mounts[storage_id] = { :uri => "#{type}://#{nfs_uri(storage_obj)}", :mount_point => mount_point, :read_only => true, :type => type } end def nfs_uri(storage_obj) storage = storage_obj.storage "#{storage.address}:#{storage.path}" end def nfs_mounts @nfs_mounts ||= {} end def nfs_mount_dir(storage_obj) nfs_uri(storage_obj).gsub("_", "__").tr("/", "_") end def mount_storage require 'manageiq/gems/pending' require 'util/mount/miq_nfs_session' require 'util/mount/miq_glusterfs_session' log_header = "MIQ(MiqRhevmVm.mount_storage)" $log.info "#{log_header} called" @ost.nfs_storage_mounted = false if nfs_mounts.empty? $log.info "#{log_header} storage mount not needed." return end begin FileUtils.mkdir_p(nfs_mount_root) unless File.directory?(nfs_mount_root) nfs_mounts.each do |storage_id, mount_info| $log.info "#{log_header} Mounting #{mount_info[:uri]} on #{mount_info[:mount_point]} for #{storage_id}" case mount_info[:type] when "nfs" MiqNfsSession.new(mount_info).connect when "glusterfs" MiqGlusterfsSession.new(mount_info).connect end @ost.nfs_storage_mounted = true end $log.info "#{log_header} - mount:\n#{`mount`}" rescue $log.error "#{log_header} Unable to mount all items from <#{nfs_mount_root}>" unmount_storage raise $! end end # Moved from MIQExtract.rb def unmount_storage log_header = "MIQ(MiqRhevmVm.unmount_storage)" return if nfs_mounts.empty? begin $log.warn "#{log_header} Unmount all items from <#{nfs_mount_root}>" nfs_mounts.each_value { |mnt| MiqNfsSession.disconnect(mnt[:mount_point]) } FileUtils.rm_rf(nfs_mount_root) @ost.nfs_storage_mounted = false rescue $log.warn "#{log_header} Failed to unmount all items from <#{nfs_mount_root}>. Reason: <#{$!}>" end end private # Output attributes of LV in column format and parse to retrieve active status def lv_active?(lv_path) AwesomeSpawn.run!("sudo lvs", :params => [:noheadings, :o, "lv_active", lv_path]).output.strip == "active" end def storage_type_block?(storage_type) ["ISCSI", "FCP"].include?(storage_type.upcase) end def storage_type_file?(storage_type) ["NFS", "GLUSTERFS"].include?(storage_type.upcase) end end