diff options
Diffstat (limited to 'myraid.py')
-rw-r--r-- | myraid.py | 122 |
1 files changed, 82 insertions, 40 deletions
@@ -22,6 +22,8 @@ import logging, numpy class MyRaid(): """Auxiliary class, managing RAID layer""" + RAID_TYPES = [ '0', '1', '5', '5+0' ] + RAID5_LAYOUTS = [ 'la', 'ra', 'ls', 'rs' ] def __init__(self, *args, **kwargs): self.disks = [] @@ -31,7 +33,7 @@ class MyRaid(): self.raid_chunk_size = 65536 self.raid_disk_order = [] self.raid_disk_count = 0 - self.raid_types = [ '0', '1', '5', '5+0' ] + self.raid_layout = 'ls' def get_raid_start(self): return self.raid_start @@ -48,6 +50,9 @@ class MyRaid(): def get_raid_disk_order_str(self): return ' '.join(map(str,self.raid_disk_order)) + def get_raid_layout(self): + return self.raid_layout + def set_disks(self, disks): self.disks = disks @@ -79,6 +84,12 @@ class MyRaid(): self.raid_disk_order = new_raid_disk_order self.raid_disk_count = len(new_raid_disk_order) + def set_raid_layout(self, new_raid_layout): + if new_raid_layout in MyRaid.RAID5_LAYOUTS: + self.raid_layout = new_raid_layout + else: + raise ValueError('raid_layout has to be one of %s'%' '.join(RAID_LAYOUTS)) + def sizeof_raid_result(self, raid_type): size = self.raid_end - self.raid_start if size <= 0 : @@ -86,14 +97,11 @@ class MyRaid(): else: return { '0' : size * self.raid_disk_count, - '1' : size, + '1' : size if self.raid_disk_count == 2 else 0, '5' : size * (self.raid_disk_count - 1) if self.raid_disk_count >= 3 else 0, '5+0': size * (self.raid_disk_count - 2) if self.raid_disk_count >= 6 and self.raid_disk_count % 2 == 0 else 0, }[raid_type] - def sizeof_disk_parity(self, raid_type): - return self.sizeof_disk_xor(raid_type) / self.raid_sector_size * 16 - def sizeof_disk_xor(self, raid_type): size = self.raid_end - self.raid_start if size <= 0: @@ -101,16 +109,20 @@ class MyRaid(): else: return { '0' : 0, # TODO Could contain some plain text error message - '1' : size, + '1' : size if self.raid_disk_count == 2 else 0, '5' : size if self.raid_disk_count >= 3 else 0, '5+0': size if self.raid_disk_count >= 6 and self.raid_disk_count % 2 == 0 else 0, }[raid_type] + def sizeof_disk_parity(self, raid_type): + return self.sizeof_disk_xor(raid_type) / self.raid_sector_size * 16 + def xor_blocks(self,fd_list, offset, size): - """TODO""" + """Compute bitwise XOR against a bunch of disks slice""" logging.info("Enter xor_blocks(fd_list,%d,%d)"%(offset, size)) - assert(size % 8 == 0), "Size must be multiple of 8" + if size % 8 != 0: + raise ValueError('xor_blocks : size must be multiple of 8') dt = numpy.dtype('<Q8') fd_list[0].seek(offset) @@ -146,53 +158,83 @@ class MyRaid(): #logging.warn(binascii.hexlify(numpy_b1)) return (result,numpy_b1) + def read_disk_xor(self,raid_type,offset,size): + raid_disks = [ self.disks[i] for i in self.raid_disk_order ] + return self.xor_blocks(raid_disks,offset,size)[1].tostring() + def read_disk_parity(self,raid_type,offset,size): - """TODO""" - logging.warn("Enter check_data(%s,disks,%d,%d)"%(raid_type,offset,size)) + """Returns textual information about parity status of each sector""" + logging.warn("Enter read_disk_parity(%s,%d,%d)"%(raid_type,offset,size)) raid_disks = [ self.disks[i] for i in self.raid_disk_order ] start = self.raid_start + offset * self.raid_sector_size / 16 end = start + size * self.raid_sector_size / 16 - result = ''.join( - [ '0x%011x %c\n'%( addr, self.xor_blocks(raid_disks, addr, self.raid_sector_size)[0]) - for addr in range(start, end, self.raid_sector_size) - ]) - - logging.warn("Exit. check_data(%s,disks,%d,%d)"%(raid_type,offset,size)) - return result + #TODO : improove for nested levels + if raid_type in ['1','5', '5+0']: + result = ''.join( + [ '0x%011x %c\n'%( addr, self.xor_blocks(raid_disks, addr, self.raid_sector_size)[0]) + for addr in range(start, end, self.raid_sector_size) + ]) + else: + result = None - def read_disk_xor(self,raid_type,offset,size): - raid_disks = [ self.disks[i] for i in self.raid_disk_order ] - return self.xor_blocks(raid_disks,offset,size)[1].tostring() + logging.warn("Exit. read_disk_parity(%s,%d,%d)"%(raid_type,offset,size)) + return result def read_raid_result(self,raid_type,offset,size): - """TODO""" + """Returns actual RAID data""" + raid_disks = [ self.disks[i] for i in self.raid_disk_order ] # TODO A garder en attribut ? disk_count = len(self.raid_disk_order) # TODO doublon ? - raid_disks = [ self.disks[i] for i in self.raid_disk_order ] # A garder en attribut ? - - # This code is RAID 5 only (left-assymetric) - if disk_count < 3: - return None - - slice_no = offset / self.raid_chunk_size - slice_off = offset % self.raid_chunk_size - segment=slice_no/(disk_count-1) - par_disk=(disk_count-1) - (segment % disk_count) # TODO : equivalent a : segment-1 % disk_count ? - data_disk=( par_disk + 1 + (slice_no % (disk_count-1)) ) % disk_count - off_disk = self.raid_start + segment * self.raid_chunk_size + slice_off - - size2 = min(size, (slice_no+1) * self.raid_chunk_size - offset) - - logging.info("raid.read_result(%s): offset=%d,slice_no=%d,slice_off=%d,segment=%d,par_disk=%d,data_disk=%d,off_disk=%d,size2=%d,slice_off+size2=%d" - % (raid_type,offset,slice_no,slice_off,segment,par_disk,data_disk,off_disk,size2,slice_off+size2) ) + if raid_type == '0': + segment_no = offset / self.raid_chunk_size + segment_off = offset % self.raid_chunk_size + stripe_no = segment_no / disk_count + par_disk = -1 + data_disk = segment_no % disk_count + off_disk = self.raid_start + stripe_no * self.raid_chunk_size + segment_off + size2 = min(size, (segment_no+1) * self.raid_chunk_size - offset) + + elif raid_type == '1': + segment_no = -1 + segment_off = -1 + stripe_no = -1 + par_disk = 1 + data_disk = 0 + off_disk = self.raid_start + offset + size2 = size + + elif raid_type == '5': + segment_no = offset / self.raid_chunk_size + segment_off = offset % self.raid_chunk_size + stripe_no = segment_no / (disk_count-1) + + if self.raid_layout in ['ls','la']: + par_disk = (disk_count-1) - (stripe_no % disk_count) + else: # self.raid_layout in ['rs','ra']: + par_disk = stripe_no % disk_count + + if self.raid_layout in ['ls','rs']: + data_disk = (par_disk+1 + (segment_no % (disk_count-1)) ) % disk_count + else: # self.raid_layout in ['la','ra']: + data_disk = segment_no % (disk_count-1) + if data_disk >= par_disk: + data_disk = data_disk + 1 + + off_disk = self.raid_start + stripe_no * self.raid_chunk_size + segment_off + size2 = min(size, (segment_no+1) * self.raid_chunk_size - offset) + + logging.info("raid.read_result(%s): offset=%d,segment_no=%d,segment_off=%d,stripe_no=%d,par_disk=%d,data_disk=%d,off_disk=%d,size2=%d,segment_off+size2=%d" + % (raid_type,offset,segment_no,segment_off,stripe_no,par_disk,data_disk,off_disk,size2,segment_off+size2) ) + + #TODO recorver from parity if damaged sectors in data_disk data_fd = raid_disks[data_disk] data_fd.seek(off_disk) data = data_fd.read(size2) - + # This kills performance but don't make short reads before EOF - #if size2 < size: + #if size2 > 0 and size2 < size: # data += self.read_result(self,raid_type,offset+size2,size-size2) return data |