diff options
author | Ludovic Pouzenc <lpouzenc@gmail.com> | 2015-06-28 01:05:47 +0200 |
---|---|---|
committer | Ludovic Pouzenc <lpouzenc@gmail.com> | 2015-06-28 01:05:47 +0200 |
commit | 45e7b53b013999e4cc2f72c080651cc2ece5a346 (patch) | |
tree | 8f965f0a436bdd2bfcb960128bf4c683b7a2e8b0 | |
parent | 2e121ce4cd7e9a5ec856351459e5c683a33c9a80 (diff) | |
download | raidguessfs-45e7b53b013999e4cc2f72c080651cc2ece5a346.tar.gz raidguessfs-45e7b53b013999e4cc2f72c080651cc2ece5a346.tar.bz2 raidguessfs-45e7b53b013999e4cc2f72c080651cc2ece5a346.zip |
RAID 5 layout implementation (left/right (as)symmetric and RAID 0 impl.
-rw-r--r-- | myraid.py | 122 | ||||
-rwxr-xr-x | raidguessfs.py | 14 |
2 files changed, 91 insertions, 45 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 diff --git a/raidguessfs.py b/raidguessfs.py index fba2187..807a2be 100755 --- a/raidguessfs.py +++ b/raidguessfs.py @@ -62,30 +62,33 @@ class RaidGuessFS(fuse.Fuse): self.settings = [ 'disk_count', 'raid_start', 'raid_end', 'raid_chunk_size', 'raid_disk_order', + 'raid_layout', 'bmp_height', 'bmp_width', 'bmp_start_offset' ] self.settings_getters = [ self.d.get_disk_count, self.raid.get_raid_start, self.raid.get_raid_end, self.raid.get_raid_chunk_size, self.raid.get_raid_disk_order_str, + self.raid.get_raid_layout, self.bmp.get_bmp_height, self.bmp.get_bmp_width, self.bmp.get_bmp_start_offset ] self.settings_updaters = [ self.update_disk_count, self.update_raid_start, self.update_raid_end, self.update_raid_chunk_size, self.update_raid_disk_order, + self.raid.set_raid_layout, self.update_bmp_height, self.update_bmp_width, self.update_bmp_start_offset ] self.dentries = { '/' : [ fuse.Direntry(name) for name in ['config','disk','raid','visual'] ], '/config': [ fuse.Direntry(name) for name in self.settings ], - '/raid' : [ fuse.Direntry(name) for name in self.raid.raid_types ], + '/raid' : [ fuse.Direntry(name) for name in myraid.MyRaid.RAID_TYPES ], '/disk' : [ ], # Filled in _refresh_disk_dentries() '/visual': [ ], # Filled in _refresh_disk_dentries() } - for raid_type in self.raid.raid_types: + for raid_type in myraid.MyRaid.RAID_TYPES: self.dentries.update( { '/raid/%s'%raid_type: [ fuse.Direntry(name) for name in ['disk_parity','disk_xor','raid_result'] ], } @@ -116,7 +119,7 @@ class RaidGuessFS(fuse.Fuse): """Update the raid computed attributes after a config change""" logging.debug("Enter _refresh_raid_fattr()") - for raid_type in self.raid.raid_types: + for raid_type in myraid.MyRaid.RAID_TYPES: self.fattr['/raid/%s/disk_parity'%raid_type].st_size = self.raid.sizeof_disk_parity(raid_type) self.fattr['/raid/%s/disk_xor' %raid_type].st_size = self.raid.sizeof_disk_xor (raid_type) self.fattr['/raid/%s/raid_result'%raid_type].st_size = self.raid.sizeof_raid_result(raid_type) @@ -133,6 +136,7 @@ class RaidGuessFS(fuse.Fuse): def update_disk_count(self,arg): i = int(arg) + #TODO : raise exception instead of using assertions assert (i > 0), "Negative value make no sense here" self.d.set_disk_count(i) self.d.open_disks() @@ -214,7 +218,7 @@ class RaidGuessFS(fuse.Fuse): self.fattr = { '/config/%s'%s: self.st.make_fake_file(64,0666) for s in self.settings } - for raid_type in self.raid.raid_types: + for raid_type in myraid.MyRaid.RAID_TYPES: self.fattr.update( { '/raid/%s/disk_parity'%raid_type: self.st.make_fake_file(0), '/raid/%s/disk_xor'%raid_type: self.st.make_fake_file(0), @@ -285,7 +289,7 @@ class RaidGuessFS(fuse.Fuse): if path_chuncks[0] == 'raid': raid_type=path_chuncks[2] - if raid_type in self.raid.raid_types: + if raid_type in myraid.MyRaid.RAID_TYPES: if path_chuncks[4] == 'disk_parity': return self.raid.read_disk_parity(raid_type,offset,size) if path_chuncks[4] == 'disk_xor': |