首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >RAID 4仿真(用于学习目的)

RAID 4仿真(用于学习目的)
EN

Code Review用户
提问于 2017-05-20 06:44:09
回答 2查看 445关注 0票数 8

我已经创建了一个模拟RAID 4配置的Python3程序,使用列表作为模拟的

.

  • a = RAID4()创建类RAID4的一个变量。
  • a.convert_to("string to make raided", 4)convert_to需要一个字符串输入和许多HDD来创建。然后,它将字符串转换为字节,并在HDD之间均匀地分割比特,使奇偶HDD保持为空。然后生成奇偶校验并存储HDD。返回一个RAID4类对象。

一旦完成了这个任务,您可以:

  • 模拟被a.remove_hdd(2)损坏或破坏的硬盘。
  • 通过运行a.repair()修复RAID配置。
  • 通过print(a)打印HDD表。
  • 通过运行str = a.convert_from()从HDD获取字符串。

我要问的是:

  • 有什么可以简化的吗?我有额外的代码不做任何事情吗?
  • 在我的测试中,有没有什么but我还没有发现(我不希望会有,但你永远不会知道)?
  • 有什么Python约定是我不小心忽略的吗?

奖励问题(对于了解RAID的人):

  • 我是否正确地模拟了RAID 4(这实际上有点像RAID 4所做的)?我用的是比特而不是块。

如有任何其他意见,敬请见谅。

代码语言:javascript
复制
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import types


class RAID4(object):
    def __init__(self, input_str=None, hdd_num=3):
        if input_str:
            self.convert_to(input_str, hdd_num)

    class HDDNotExist(Exception):
        pass

    class NotEnoughHDDs(Exception):
        pass

    def __str__(self):
        """Returns the HDDs in a visual format."""
        # top border
        print_str = "====" * len(self.hdds) + "=\n"
        # columns headings
        for i in range(len(self.hdds) - 1):
            print_str += "|{:^3}".format(i)
        print_str += "|XOR|\n"
        print_str += "|" + "---+" * (len(self.hdds) - 1) + "---|\n"

        # for every row
        for i in range(len(max(self.hdds, key=len))):
            # print each column
            for j in range(len(self.hdds)):
                print_str += "| " + str(self.hdds[j][i]) + " "
            print_str += ("|\n")

        # bottom border
        print_str += "====" * len(self.hdds) + "="

        return print_str

    def xor(self, *to_xor):
        """Performs XOR on parameters, from left to right."""
        # if passed a list as it's only argument, xor everything in it
        if len(to_xor) == 1 and \
                isinstance(to_xor[0], (list, tuple, types.GeneratorType)):
            to_xor = to_xor[0]

        x = 0
        for i in to_xor:
            x ^= i
        return x

    def convert_to(self, input_str, hdd_num=3):
        """Converts a string into a set number of HDDs."""
        if hdd_num < 3:
            raise NotEnoughHDDs(
                "RAID 5 requires a minimum of three hard drives to operate.")

        # convert every character into a byte (8x bits)
        input_bin = ''.join(format(ord(x), 'b').zfill(8) for x in input_str)
        # add a 1, this, and every 0 after it will be removed when
        #   converting back into the string
        input_bin += "1"

        # number of bits required for each HDD to have a full byte
        bits_per_hdd_byte = 8 * (hdd_num - 1)

        # next lowest multiple of bits for each
        #   HDD to have a whole number of bytes
        next_lowest_multiple = len(input_bin) // bits_per_hdd_byte + 1

        # make each HDD have a whole number of bytes when input is evenly split
        input_bin += "0" * (
            next_lowest_multiple * bits_per_hdd_byte - len(input_bin))

        # make blank hdds
        self.hdds = [[] for _ in range(hdd_num)]

        # split data into hdds, with one hdd left for parity
        for i, x in enumerate(input_bin):
            self.hdds[i % (hdd_num - 1)].append(int(x))

        # xor every row
        for i in range(max(len(x) for x in self.hdds)):
            # append the row's xor
            self.hdds[-1].append(self.xor(
                self.hdds[j][i] for j in range(hdd_num - 1)
                ))

    def convert_from(self):
        """Converts HDDs into the string."""
        self.repair()

        # combine HDDs into a single list
        # zip(hdds). for i in zip. for j in i. str(j)
        output_bin = (str(j) for i in zip(*self.hdds[:-1]) for j in i)
        output_bin = ''.join(output_bin)

        # remove the last 1, and following 0s (padding)
        output_bin = output_bin.rsplit("1", 1)[0]

        # split into bytes (8x bits), one for each character
        output_bytes = [output_bin[i:i+8] for i in range(0, len(output_bin), 8)]

        # decode bytes into characters, join and return
        return ''.join(chr(int(x, 2)) for x in output_bytes)

    def remove_hdd(self, index_to_remove):
        """Remove a HDD, simulating a destroyed HDD in a RAID system."""
        try:
            # sets HDD to empty list so we know theres supposed to be a HDD here
            # (instead of deleting the HDD)
            self.hdds[index_to_remove] = []
        except IndexError:
            raise HDDNotExist(
                "The HDD to remove does not exists. HDDs are 0-indexed.")

    def repair(self):
        """Creates a new HDD to replace the missing one."""
        # if a HDD is missing, repair it
        if [] in self.hdds:
            # get index (so we XOR in the right order)
            empty_hdd_index = self.hdds.index([])

            # delete the empty HDD and create the replacement
            del self.hdds[empty_hdd_index]
            new_hdd = []

            # for every data row in the HDD
            for i in range(len(self.hdds[0])):
                # get the row (one bit from each HDD)
                row = [self.hdds[j][i] for j in range(len(self.hdds) - 1)]
                # insert a 0 into the missing HDD's spot
                row.insert(empty_hdd_index, 0)

                # add a 1 or 0 to the replacement HDD depending on whether
                #   having a 0 created a matching XOR result
                new_hdd.append(0 if self.xor(row) == self.hdds[-1][i] else 1)

            # insert the replacement HDD where it belongs
            self.hdds.insert(empty_hdd_index, new_hdd)

        # if there are no HDDs missing, check that there are no corruptions
        else:
            # for every data row in the HDD 
            for i in range(len(self.hdds[0])):
                # get the row (one bit from each HDD)
                row = [self.hdds[j][i] for j in range(len(self.hdds) - 1)]

                # if the row XORed is different to what it should be
                if self.xor(row) != self.hdds[-1][i]:
                    # send a warning, saying where the error will be
                    print(
                        "WARNING: data point {} corrupted, XOR didn't match. "
                        "This will be character {} if it's a string."
                        .format(i, ((i + 1) * (len(self.hdds) - 1) // 8) + 1))


if __name__ == "__main__":
    print("hey Bob!")
    a = RAID4("hey Bob!", 4)
    print(a)
    # uncomment one of the lines below to "screw with" the HDDs
    #a.remove_hdd(2)    # this line deletes a HDD
    #a.hdds[2][5] = 0   # this line manually changes a bit
    #a.hdds[2][5] = 0 if a.hdds[2][5] else 1  # this line manually toggles a bit
    print(a)
    a.repair()
    print(a)
    print(a.convert_from())
EN

回答 2

Code Review用户

回答已采纳

发布于 2018-01-17 04:08:01

代码语言:javascript
复制
def __init__(self, input_str=None, hdd_num=3):
    if input_str:
        self.convert_to(input_str, hdd_num)

这是有点模糊的一面。其效果似乎是将一个值赋给self.hdds。但令人惊讶的是,这种情况并不总是发生。另外,看到self.hdds = []None这样的文本提示也很好,convert_to()会覆盖这些提示。在读取构造函数时,我们试图学习对象属性的集合,以及代码允许它们包含的那种“好值”。

此外,在引入构造函数之前,请定义嵌套的异常类。

代码语言:javascript
复制
    for i in range(len(self.hdds) - 1):
        print_str += "|{:^3}".format(i)

这可能是二次型的,除非在扩展字符串的某些版本的cPython解释器上。通常的python成语是.append()到循环中的一个列表,然后用''.join()返回一个字符串。你的范围是有限的,所以这没什么大不了的,只是一个值得警惕的编码习惯。

您的字符串连接表达式非常好,但是可以考虑使用.format()

代码语言:javascript
复制
    if len(to_xor) == 1 and \
            isinstance(to_xor[0], (list, tuple, types.GeneratorType)):

这很好。考虑以这种方式编写它,而不使用反斜杠:

代码语言:javascript
复制
    if (len(to_xor) == 1
        and isinstance(to_xor[0], (list, tuple, types.GeneratorType))):

与一群and's在左边边沿排成一排,这比右派and's在右边的边缘更容易读懂。

代码语言:javascript
复制
        self.hdds[-1].append(self.xor(
            self.hdds[j][i] for j in range(hdd_num - 1)
            ))

在这种情况下,这对悬在一起的近亲似乎并不能提高清晰度。有时候,在它自己的行上的一个paren可能非常有用,就像定义一个长列表常量一样。在这里,考虑让for j开始一个新的行来提高清晰度。

代码语言:javascript
复制
            "The HDD to remove does not exists. HDDs are 0-indexed.")

错误:存在

代码语言:javascript
复制
print(a.convert_from())

对不起,我没有发现这是一个非常清楚的公共API。“从”表示我们将通过一个arg。考虑重命名该方法。

票数 2
EN

Code Review用户

发布于 2018-01-17 04:25:35

在Python3.x中,您不需要指定编码类型,因为默认的是UTF-8。这样您就可以安全地删除指令:# -*- coding: utf-8 -*-

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/163814

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档