summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Pouzenc <lpouzenc@gmail.com>2015-06-28 01:05:47 +0200
committerLudovic Pouzenc <lpouzenc@gmail.com>2015-06-28 01:05:47 +0200
commit45e7b53b013999e4cc2f72c080651cc2ece5a346 (patch)
tree8f965f0a436bdd2bfcb960128bf4c683b7a2e8b0
parent2e121ce4cd7e9a5ec856351459e5c683a33c9a80 (diff)
downloadraidguessfs-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.py122
-rwxr-xr-xraidguessfs.py14
2 files changed, 91 insertions, 45 deletions
diff --git a/myraid.py b/myraid.py
index 5b44fc4..3706c36 100644
--- a/myraid.py
+++ b/myraid.py
@@ -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':