lib/VolumeManager/LVM/lvm2disk.rb
#
# 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