ManageIQ/manageiq-smartstate

View on GitHub
lib/VolumeManager/LVM/lvm2disk.rb

Summary

Maintainability
A
2 hrs
Test Coverage
F
22%
#
# MiqDisk support module for LVM2 logical volumes.
#
module Lvm2DiskIO
  def d_init
    @lvObj = dInfo.lvObj
    raise "Logical volume object not present in disk info." unless @lvObj
    @vgObj = @lvObj.vgObj
    self.diskType = "#{@vgObj.lvmType} Logical Volume"
    self.blockSize = 512

    @extentSize = @vgObj.extentSize * blockSize    # extent size in bytes

    @lvSize = 0
    @segments = []
    @lvObj.segments.each do |lvSeg|
      seg = Segment.new(lvSeg.startExtent * @extentSize, ((lvSeg.startExtent + lvSeg.extentCount) * @extentSize) - 1, lvSeg.type)
      seg.lvSeg = lvSeg
      @lvSize += (seg.segSize / blockSize)

      #
      # Each slice is defined by a physical volume name and the extent
      # number of where the stripe starts on that physical volume.
      #
      lvSeg.stripes.each_slice(2) do |pvn, ext|
        pvObj = @vgObj.physicalVolumes[pvn]
        raise "Physical volume object (name=<#{pvn}>) not found in volume group (id=<#{@vgObj.vgId}> name=<#{@vgObj.vgName}>) of logical volume (id=<#{@lvObj.lvId}> name=<#{@lvObj.lvName}>)" if pvObj.nil?
        #
        # Compute the byte address of the start of the stripe on the physical volume.
        #
        ba = (pvObj.peStart * blockSize) + (ext * @extentSize)
        seg.stripes << Stripe.new(pvObj.diskObj, ba)
      end
      @segments << seg
    end
  end # def d_init

  def d_read(pos, len)
    retStr = ''
    return retStr if len == 0

    if logicalVolume.thin?
      device_id = logicalVolume.thin_segment.device_id
      thin_pool = logicalVolume.thin_pool_volume
      data_blks = thin_pool.metadata_volume.superblock.device_to_data(device_id, pos, len)

      data_blks.each do |_device_blk, data_blk, blk_offset, blk_len|
        if data_blk.nil?
          # fill in unallactored data
          retStr << Array.new(blk_len, 0).pack("C*")

        else
          thin_pool.data_volume.disk.seek(blk_offset)
          retStr << thin_pool.data_volume.disk.read(blk_len)
        end
      end

      return retStr
    end

    endPos = pos + len - 1
    startSeg, endSeg = getSegs(pos, endPos)

    (startSeg..endSeg).each do |si|
      seg = @segments[si]
      srs = seg.startByteAddr     # segment read start
      srl = seg.segSize           # segment read length

      if si == startSeg
        srs = pos
        srl = seg.segSize - (pos - seg.startByteAddr)
      end

      if si == endSeg
        srl = endPos - srs + 1
      end

      retStr << readSeg(seg, srs, srl)
    end

    retStr
  end # def d_read

  def d_write(_pos, _buf, _len)
    raise "Write operation not yet supported for logical volumes"
  end # def d_write

  def d_close
  end # def d_close

  def d_size
    @lvSize
  end # def d_size

  def logicalVolume
    @lvObj
  end

  def volumeGroup
    @vgObj
  end

  private

  def getSegs(startPos, endPos, segments=@segments)
    startSeg = nil
    endSeg = nil

    segments.each_with_index do |seg, i|
      startSeg = i if seg.byteRange === startPos
      if seg.byteRange === endPos
        raise "Segment sequence error" unless startSeg
        endSeg = i
        break
      end
    end
    raise "Segment range error: LV = #{@lvObj.lvName}, startPos = #{startPos}, endPos = #{endPos}" if !startSeg || !endSeg

    return startSeg, endSeg
  end

  def readSeg(seg, sba, len)
    #
    # For now, we only support linear segments (stripeCount = 1)
    # TODO: support other segment types.
    #
    stripe = seg.stripes[0]
    pvReadPos = stripe.pvStartByteAddr + (sba - seg.startByteAddr)     # byte address on the physical volume

    stripe.pvDiskObj.seek(pvReadPos, IO::SEEK_SET)
    stripe.pvDiskObj.read(len)
  end

  #
  # Like LvSegment but optimized for logical volume IO
  #
  class Segment
    attr_accessor :type, :stripes, :segSize, :byteRange
    attr_accessor :lvSeg

    def initialize(startByte, endByte, type = nil)
      @byteRange = Range.new(startByte, endByte, false)
      @type = type
      @segSize = endByte - startByte + 1
      @stripes = []
    end

    def startByteAddr
      @byteRange.begin
    end

    def endByteAddr
      @byteRange.end
    end
  end

  class Stripe
    attr_accessor :pvDiskObj, :pvStartByteAddr

    def initialize(pvDisk, pvStart)
      @pvDiskObj = pvDisk
      @pvStartByteAddr = pvStart
    end
  end
end # module Lvm2DiskIO