diff --git a/Main.php b/Main.php new file mode 100644 index 0000000..5d28a96 --- /dev/null +++ b/Main.php @@ -0,0 +1,48 @@ + 0.9) { + $size /= $step; + $i++; + } + return number_format(round($size, $precision), $precision, ',', '.') . " " . $units[$i]; +} + +$RAIDTypes = array(0, 1, 5, 6, 10, 'SHR', 'SHR2'); +$drives = [ + new Drive('1TB', 'hdd', 1), + new Drive('1TB', 'hdd', 2), + new Drive('250GB', 'hdd', 3), + new Drive('250GB', 'hdd', 4), + new Drive('160GB', 'hdd', 5), + new Drive('160GB', 'hdd', 6), +]; +$factory = new RaidFactory(); +$numberOfDecimalsToPrint = 2; + +foreach ($RAIDTypes as $RAIDType) { + $raid[$RAIDType] = $factory->create($RAIDType, $drives); + echo "RAID level : " . $raid[$RAIDType]->getLevel() . "\n\n"; + echo "RAID " . $raid[$RAIDType]->getLevel() . " total capacity : " . toHumanReadableSize($raid[$RAIDType]->getTotalCapacity(), $numberOfDecimalsToPrint) . "\n"; + echo "RAID " . $raid[$RAIDType]->getLevel() . " usable space : " . toHumanReadableSize($raid[$RAIDType]->getCapacity(), $numberOfDecimalsToPrint) . "\n"; + echo "RAID " . $raid[$RAIDType]->getLevel() . " parity size : " . toHumanReadableSize($raid[$RAIDType]->getParitySize(), $numberOfDecimalsToPrint) . "\n"; + echo "RAID " . $raid[$RAIDType]->getLevel() . " lossed space : " . toHumanReadableSize($raid[$RAIDType]->getLossedSpace(), $numberOfDecimalsToPrint) . "\n"; + echo "RAID " . $raid[$RAIDType]->getLevel() . " data protected : "; + if ($raid[$RAIDType]->getDrivesFailureSupported() === 0) { + echo "no"; + } else if ($raid[$RAIDType]->getDrivesFailureSupported() === 1) { + echo "yes, once"; + } else if ($raid[$RAIDType]->getDrivesFailureSupported() === 2) { + echo "yes, twice"; + } else { + echo "yes, " . $raid[$RAIDType]->getDrivesFailureSupported() . " times"; + } + echo "\n\n"; +} diff --git a/src/AbstractRaid.php b/src/AbstractRaid.php index 93a3db7..039076e 100644 --- a/src/AbstractRaid.php +++ b/src/AbstractRaid.php @@ -2,6 +2,7 @@ namespace kevinquinnyo\Raid; use Cake\I18n\Number; +use Cake\Utility\Text; use kevinquinnyo\Raid\Drive; use RuntimeException; @@ -12,6 +13,7 @@ abstract class AbstractRaid protected $parity = false; protected $striped = false; protected $minimumDrives = 1; + protected $drivesFailureSupported = 0; /** * Is Mirrored @@ -33,6 +35,16 @@ public function getMinimumDrives() return $this->minimumDrives; } + /** + * Get Drives Failure Supported Count + * + * @return int The drives failure supported count of this RAID. + */ + public function getDrivesFailureSupported() + { + return $this->drivesFailureSupported; + } + /** * Is Striped * @@ -96,43 +108,93 @@ public function getLevel() /** * Get Drives * - * @param $options Options - add 'withHotSpares' to include hot spare drives. + * Options: + * + * ``` + * - sortBy - Whether to sort the drives by capacity + * - sortOrder - If sorted, the order (ascending ('ASC') or descending ('DESC')), ascending by default + * - withHotSpares - Whether to include the hot spares in the drives. + * ``` + * + * @param array $options Additional options to scope the results. * @return array The RAID's Drive objects. */ - public function getDrives($options = []) + public function getDrives(array $options = []) { - $options += ['withHotSpares' => false]; - $drives = $this->drives; + $options += [ + 'sortBy' => null, + 'sortOrder' => 'ASC', + 'withHotSpares' => false, + ]; if ($options['withHotSpares'] === false) { $drives = []; + foreach ($this->drives as $drive) { if ($drive->isHotSpare() === false) { - $drives[] = $drive; + array_push($drives, $drive); } } + } else { + $drives = $this->drives; + } + + if ($options['sortBy'] === 'capacity') { + if ($options['sortOrder'] === 'ASC') { + usort($drives, function ($a, $b) { + return $a->getCapacity() - $b->getCapacity(); // sorted by ASC order + }); + } else { + usort($drives, function ($a, $b) { + return $b->getCapacity() - $a->getCapacity(); // sorted by DESC order + }); + } } return $drives; } /** - * Get Hot Spares + * Get Hot Spare Drives * - * @return array The RAID's Drive objects listed as hot spares. + * Options: + * + * ``` + * - sortBy - Whether to sort the drives by capacity + * - sortOrder - If sorted, the order (ascending ('ASC') or descending ('DESC')), ascending by default + * ``` + * + * @param array $options Additional options to scope the results. + * @return array The RAID's Drive objects listed as hot spare drives. */ - public function getHotSpares() + public function getHotSpareDrives(array $options = []) { - $hotSpares = []; - $drives = $this->getDrives(['withHotSpares' => true]); + $options += [ + 'sortBy' => null, + 'sortOrder' => 'ASC', + ]; - foreach ($drives as $drive) { + $hotSpareDrives = []; + + foreach ($this->drives as $drive) { if ($drive->isHotSpare() === true) { - $hotSpares[] = $drive; + array_push($hotSpareDrives, $drive); } } - return $hotSpares; + if ($options['sortBy'] === 'capacity') { + if ($options['sortOrder'] === 'ASC') { + usort($drives, function ($a, $b) { + return $a->getCapacity() - $b->getCapacity(); // sorted by ASC order + }); + } else { + usort($hotSpareDrives, function ($a, $b) { + return $b->getCapacity() - $a->getCapacity(); // sorted by DESC order + }); + } + } + + return $hotSpareDrives; } /** @@ -141,7 +203,7 @@ public function getHotSpares() * @param array $drives An array of \kevinquinnyo\Raid\Drive Drive objects to set on the RAID. * @return self */ - public function setDrives($drives) + public function setDrives(array $drives) { $this->validate($drives); $this->drives = $drives; @@ -166,7 +228,7 @@ public function addDrive(Drive $drive) /** * Add Hot Spare * - * @param \kevinquinnyo\Raid\Drive $drive A Drive to add to the list of hot spares. + * @param \kevinquinnyo\Raid\Drive $drive A Drive to add to the list of hot spares drives. * @return self */ public function addHotSpare(Drive $drive) @@ -184,35 +246,88 @@ public function addHotSpare(Drive $drive) * Get the usable capacity of the RAID in its current state. * This method differs slightly per RAID level implementation. * - * @param array $options Additional options that the Raid objects methods allow. - * @return int The usable capacity of the RAID. + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * ``` + * + * @param array $options Additional options to pass. + * @return int|string Usable capacity of the RAID in bytes or human readable format. */ abstract public function getCapacity(array $options = []); /** * Get Minimum Drive Size * - * Return the size in capacity of the smallest drive in the array. + * Return the capacity of the smallest drive in the array. * - * @return int Capacity of smallest drive in array. + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * - withHotSpares - Whether to include the hot spares in the search. + * ``` + * + * @param array $options Additional options to pass. + * @return int|string Capacity of the smallest drive in the array. */ - public function getMinimumDriveSize($options = []) + public function getMinimumDriveSize(array $options = []) { - $options += ['withHotSpares' => false]; - $floor = null; + $options += [ + 'human' => false, + 'withHotSpares' => false, + ]; + $minimumDriveSize = $this->getDrives($options)[0]->getCapacity(); $drives = $this->getDrives($options); foreach ($drives as $drive) { - if (isset($floor) === false) { - $floor = $drive->getCapacity(); + if ($drive->getCapacity() < $minimumDriveSize) { + $minimumDriveSize = $drive->getCapacity(); } + } - if ($drive->getCapacity() < $floor) { - $floor = $drive->getCapacity(); + if ($options['human'] === true) { + $minimumDriveSize = Number::toReadableSize($minimumDriveSize); + } + + return $minimumDriveSize; + } + + /** + * Get Maximum Drive Size + * + * Return the capacity of the biggest drive in the array. + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * - withHotSpares - Whether to include the hot spares in the search. + * ``` + * + * @param array $options Additional options to pass. + * @return int|string Capacity of the biggest drive in the array. + */ + public function getMaximumDriveSize(array $options = []) + { + $options += [ + 'human' => false, + 'withHotSpares' => false, + ]; + $maximumDriveSize = $this->drives[0]->getCapacity(); + + foreach ($this->getDrives($options) as $drive) { + if ($drive->getCapacity() > $maximumDriveSize) { + $maximumDriveSize = $drive->getCapacity(); } } - return $floor; + if ($options['human'] === true) { + $maximumDriveSize = Number::toReadableSize($maximumDriveSize); + } + + return $maximumDriveSize; } /** @@ -221,9 +336,11 @@ public function getMinimumDriveSize($options = []) * @param array $options Add 'withHotSpares' if you wish to include hot spares in the count. * @return int The drive count for the RAID. */ - public function getDriveCount($options = []) + public function getDriveCount(array $options = []) { - $options += ['withHotSpares' => false]; + $options += [ + 'withHotSpares' => false, + ]; $drives = $this->getDrives($options); return count($drives); @@ -249,22 +366,19 @@ public function getDriveCount($options = []) * ``` * * @param array $options Additional options to scope the results. - * @return int The total capacity for the RAID. + * @return int|string The total capacity for the RAID. */ - public function getTotalCapacity($options = []) + public function getTotalCapacity(array $options = []) { $options += [ 'human' => false, 'withHotSpares' => false, - 'floor' => true, + 'floor' => false, ]; $total = 0; - $min = $this->getMinimumDriveSize(); - - $drives = $this->getDrives($options); - - foreach ($drives as $drive) { - $total += $options['floor'] === true ? $min : $drive->getCapacity(); + $minimumDriveSize = $this->getMinimumDriveSize(); + foreach ($this->getDrives($options) as $drive) { + $total += $options['floor'] === true ? $minimumDriveSize : $drive->getCapacity(); } if ($options['human'] === true) { @@ -302,4 +416,119 @@ public function validate(array $drives) return true; } + + /** + * Get parity total size + * + * Get the total size reserved for parity (unusable by data but not lossed). + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * ``` + * + * @param array $options Additional options to scope the results. + * @return int|string The total size reserved for parity of the RAID. + */ + abstract public function getParitySize(array $options = []); + + /** + * Get lossed space + * + * Get lossed space size (unusable and unused space, neither by parity nor by data). + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * - withHotSpares - Whether to include the hot spares in the sum. + * ``` + * + * @param array $options Additional options to scope the results. + * @return int|string The lossed space size of the RAID. + */ + public function getLossedSpace(array $options = []) + { + $options += [ + 'human' => false, + 'withHotSpares' => false, + ]; + $lossedSpace = $this->getTotalCapacity($options) - $this->getCapacity() - $this->getParitySize(); + + if ($options['human'] === true) { + $lossedSpace = Number::toReadableSize($lossedSpace); + } + + return $lossedSpace; + } + + /** + * Get number of drives of the given capacity + * + * @param mixed $capacity The capacity of the drives you're looking for, in bytes or human readable format, e.g. - '500GB', '5T', etc. + * @param array $options Add 'withHotSpares' if you wish to include hot spares in the count. + * @return int The lossed capacity of the RAID. + */ + public function getNumberOfDrivesOfThisCapacity($capacity, array $options = []) + { + $options += [ + 'withHotSpares' => false, + ]; + if (ctype_digit($capacity)) { + $capacity = (int)$capacity; + } else { + $capacity = (int)Text::parseFileSize($capacity); + } + + $numberOfDrivesOfThisCapacity = 0; + $temp = $this->getDrives($options); + foreach ($temp as $drive) { + if ($drive->getCapacity() === $capacity) { + ++$numberOfDrivesOfThisCapacity; + } + } + + return $numberOfDrivesOfThisCapacity; + } + + /** + * Get next maximum drive capacity + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * - withHotSpares - Whether to include the hot spares in the sum. + * ``` + * + * @param mixed $maximumDriveCapacity The capacity of the biggest drive you know, in bytes or human readable format, e.g. - '500GB', '5T', etc. + * @param array $options Additional options to scope the results. + * @return int|string The next bigger drive's capacity of the drives array, return 0 if no smaller drive than the capacity provided. + */ + public function getNextMaximumDriveCapacity($maximumDriveCapacity, array $options = []) + { + $options += [ + 'human' => false, + 'withHotSpares' => false, + ]; + if (ctype_digit($maximumDriveCapacity)) { + $maximumDriveCapacity = (int)$maximumDriveCapacity; + } else { + $maximumDriveCapacity = (int)Text::parseFileSize($maximumDriveCapacity); + } + + $capacityOfNextLargerDrive = 0; + foreach ($this->getDrives($options) as $drive) { + if ($drive->getCapacity() > $capacityOfNextLargerDrive && $drive->getCapacity() < $maximumDriveCapacity) { + $capacityOfNextLargerDrive = $drive->getCapacity(); + } + } + + if ($options['human'] === true) { + $capacityOfNextLargerDrive = Number::toReadableSize($capacityOfNextLargerDrive); + } + + return $capacityOfNextLargerDrive; + } } diff --git a/src/Drive.php b/src/Drive.php index 51142f5..7acb347 100644 --- a/src/Drive.php +++ b/src/Drive.php @@ -11,6 +11,7 @@ class Drive protected $type = null; protected $hotSpare = false; protected $identifier = null; + protected static $types = ['ssd', 'hdd']; /** * Constructor. @@ -34,15 +35,26 @@ public function __construct($capacity, string $type, string $identifier, $option 'hotSpare' => false, ]; if (ctype_digit($capacity)) { - $bytes = (int)$capacity; + $this->capacity = (int)$capacity; + } else { + $this->capacity = (int)Text::parseFileSize($capacity); } - $this->capacity = isset($bytes) === true ? $bytes : Text::parseFileSize($capacity); $this->validate($type); $this->type = $type; $this->identifier = $identifier; $this->hotSpare = $options['hotSpare']; } + /** + * Get Types + * + * @return string All drives Types. + */ + public static function getTypes() + { + return self::$types; + } + /** * Get Identifier * @@ -77,9 +89,7 @@ public function setIdentifier(string $identifier) */ protected function validate($type) { - $types = ['ssd', 'hdd']; - - if (in_array($type, $types) === false) { + if (!in_array($type, $this->getTypes())) { throw new InvalidArgumentException('This is not a valid drive type.'); } } diff --git a/src/Raid/RaidFive.php b/src/Raid/RaidFive.php index fc9fba4..9162517 100644 --- a/src/Raid/RaidFive.php +++ b/src/Raid/RaidFive.php @@ -9,11 +9,11 @@ class RaidFive extends AbstractRaid { const LEVEL = 5; protected $drives = []; - protected $hotSpares = []; - protected $minimumDrives = 3; protected $mirrored = false; protected $parity = true; protected $striped = true; + protected $minimumDrives = 3; + protected $drivesFailureSupported = 1; /** * Constructor. @@ -45,14 +45,40 @@ public function getCapacity(array $options = []) $options += [ 'human' => false, ]; - $total = $this->getTotalCapacity(); - $min = $this->getMinimumDriveSize(); - $result = $total === 0 ? $total : ($total - $min); + $capacity = $this->getMinimumDriveSize() * ($this->getDriveCount() - 1); + + if ($options['human'] === true) { + return Number::toReadableSize($capacity); + } + + return $capacity; + } + + /** + * Get parity total size + * + * Get the total size reserved for parity (unusable by data but not lossed). + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * ``` + * + * @param array $options Additional options to scope the results. + * @return int|string The total size reserved for parity of the RAID. + */ + public function getParitySize(array $options = []) + { + $options += [ + 'human' => false, + ]; + $paritySize = $this->getMinimumDriveSize(); if ($options['human'] === true) { - return Number::toReadableSize($result); + return Number::toReadableSize($paritySize); } - return $result; + return $paritySize; } } diff --git a/src/Raid/RaidOne.php b/src/Raid/RaidOne.php index f8b2dc4..3867c0c 100644 --- a/src/Raid/RaidOne.php +++ b/src/Raid/RaidOne.php @@ -9,10 +9,11 @@ class RaidOne extends AbstractRaid { const LEVEL = 1; protected $drives = []; - protected $hotSpares = []; - protected $minimumDrives = 2; protected $mirrored = true; + protected $parity = false; protected $striped = false; + protected $minimumDrives = 2; + protected $drivesFailureSupported = 1; /** * Constructor. @@ -25,6 +26,7 @@ public function __construct(array $drives = []) $this->validate($drives); } + $this->drivesFailureSupported = count($drives) - 1; $this->setDrives($drives); } @@ -44,10 +46,40 @@ public function getCapacity(array $options = []) $options += [ 'human' => false, ]; + $capacity = $this->getMinimumDriveSize(); + + if ($options['human'] === true) { + return Number::toReadableSize($capacity); + } + + return $capacity; + } + + /** + * Get parity total size + * + * Get the total size reserved for parity (unusable by data but not lossed). + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * ``` + * + * @param array $options Additional options to scope the results. + * @return int|string The total size reserved for parity of the RAID. + */ + public function getParitySize(array $options = []) + { + $options += [ + 'human' => false, + ]; + $capacity = $this->getMinimumDriveSize() * ($this->getDriveCount() - 1); + if ($options['human'] === true) { - return Number::toReadableSize($this->getMinimumDriveSize()); + return Number::toReadableSize($capacity); } - return $this->getMinimumDriveSize(); + return $capacity; } } diff --git a/src/Raid/RaidSHR.php b/src/Raid/RaidSHR.php new file mode 100644 index 0000000..d7fd1c8 --- /dev/null +++ b/src/Raid/RaidSHR.php @@ -0,0 +1,99 @@ +validate($drives); + } + + $this->setDrives($drives); + } + + /** + * Get Capacity + * + * Get the usable capacity of the RAID in its current state. + * This method differs slightly per RAID level implementation. + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * ``` + * + * @param array $options Additional options to pass. + * @return int|string Usable capacity of the RAID in bytes or human readable format. + */ + public function getCapacity(array $options = []) + { + $options += [ + 'human' => false, + ]; + $capacity = $this->getTotalCapacity() - $this->getMaximumDriveSize(); + + if ($options['human'] === true) { + return Number::toReadableSize($capacity); + } + + return $capacity; + } + + /** + * Get parity total size + * + * Get the total size reserved for parity (unusable by data but not lossed). + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * ``` + * + * @param array $options Additional options to scope the results. + * @return int|string The total size reserved for parity of the RAID. + */ + public function getParitySize(array $options = []) + { + $options += [ + 'human' => false, + ]; + $maximumDriveSizeOfRAID = $this->getMaximumDriveSize(); + if ($this->getNumberOfDrivesOfThisCapacity($maximumDriveSizeOfRAID) >= $this->minimumDrives) { + $paritySize = $maximumDriveSizeOfRAID; + } else { + /* We are looking for the second disk with the highest capacity present in the drives array, the use of the first will be the same as the capacity of the second */ + $paritySize = 0; + foreach ($this->drives as $drive) { + if ($drive->getCapacity() > $paritySize && $drive->getCapacity() < $maximumDriveSizeOfRAID) { + $paritySize = $drive->getCapacity(); + } + } + } + + if ($options['human'] === true) { + return Number::toReadableSize($paritySize); + } + + return $paritySize; + } +} diff --git a/src/Raid/RaidSHR2.php b/src/Raid/RaidSHR2.php new file mode 100644 index 0000000..6e21a07 --- /dev/null +++ b/src/Raid/RaidSHR2.php @@ -0,0 +1,131 @@ +validate($drives); + } + + $this->setDrives($drives); + } + + /** + * Get Capacity + * + * Get the usable capacity of the RAID in its current state. + * This method differs slightly per RAID level implementation. + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * ``` + * + * @param array $options Additional options to pass. + * @return int|string Usable capacity of the RAID in bytes or human readable format. + */ + public function getCapacity(array $options = []) + { + $options += [ + 'human' => false, + ]; + $numberOfParityDrives = $this->minimumDrives - 2; /* the different blocks of parities are distributed among all drives */ + $maximumDriveSizeOfRAID = $this->getMaximumDriveSize(); + if ($this->getNumberOfDrivesOfThisCapacity($maximumDriveSizeOfRAID) >= $this->minimumDrives) { + $capacity = $this->getTotalCapacity() - ($maximumDriveSizeOfRAID * $numberOfParityDrives); + } else { + /* We are looking for the second drive with the highest capacity present in the drives array, the use of the first will be the same as the capacity of the second */ + $capacity = 0; + $capacityOfSecondLargestDrive = $this->getNextMaximumDriveCapacity($maximumDriveSizeOfRAID); + if ($this->getNumberOfDrivesOfThisCapacity($capacityOfSecondLargestDrive) >= $numberOfParityDrives) { + foreach ($this->drives as $drive) { + if ($drive->getCapacity() < $capacityOfSecondLargestDrive) { + $capacity += $drive->getCapacity(); + } else { + $capacity += $capacityOfSecondLargestDrive; + } + } + $capacity -= $capacityOfSecondLargestDrive * $numberOfParityDrives; + } else { + /* We are looking for the third drive with the highest capacity present in the drives array, the use of the two firsts will be the same as the capacity of the third */ + $capacityOfThirdLargestDrive = $this->getNextMaximumDriveCapacity($capacityOfSecondLargestDrive); + foreach ($this->drives as $drive) { + if ($drive->getCapacity() < $capacityOfThirdLargestDrive) { + $capacity += $drive->getCapacity(); + } else { + $capacity += $capacityOfThirdLargestDrive; + } + } + $capacity -= $capacityOfThirdLargestDrive * $numberOfParityDrives; + } + } + + if ($options['human'] === true) { + return Number::toReadableSize($capacity); + } + + return $capacity; + } + + /** + * Get parity total size + * + * Get the total size reserved for parity (unusable by data but not lossed). + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * ``` + * + * @param array $options Additional options to scope the results. + * @return int|string The total size reserved for parity of the RAID. + */ + public function getParitySize(array $options = []) + { + $options += [ + 'human' => false, + ]; + $numberOfParityDrives = $this->minimumDrives - 2; /* the different blocks of parities are distributed among all drives */ + $maximumDriveSizeOfRAID = $this->getMaximumDriveSize(); + if ($this->getNumberOfDrivesOfThisCapacity($maximumDriveSizeOfRAID) >= $this->minimumDrives) { + $paritySize = $maximumDriveSizeOfRAID * $numberOfParityDrives; + } else { + /* We are looking for the second drive with the highest capacity present in the drives array, the use of the first will be the same as the capacity of the second */ + $capacityOfSecondLargestDrive = $this->getNextMaximumDriveCapacity($maximumDriveSizeOfRAID); + if ($this->getNumberOfDrivesOfThisCapacity($capacityOfSecondLargestDrive) >= $numberOfParityDrives) { + $paritySize = $capacityOfSecondLargestDrive * $numberOfParityDrives; + } else { + /* We are looking for the third drive with the highest capacity present in the drives array, the use of the two firsts will be the same as the capacity of the third */ + $capacityOfThirdLargestDrive = $this->getNextMaximumDriveCapacity($capacityOfSecondLargestDrive); + $paritySize = $capacityOfThirdLargestDrive * $numberOfParityDrives; + } + } + + if ($options['human'] === true) { + return Number::toReadableSize($paritySize); + } + + return $paritySize; + } +} diff --git a/src/Raid/RaidSix.php b/src/Raid/RaidSix.php index 6ffb8f1..354384f 100644 --- a/src/Raid/RaidSix.php +++ b/src/Raid/RaidSix.php @@ -9,10 +9,11 @@ class RaidSix extends AbstractRaid { const LEVEL = 6; protected $drives = []; - protected $hotSpares = []; - protected $minimumDrives = 4; protected $mirrored = false; protected $parity = true; + protected $striped = true; + protected $minimumDrives = 4; + protected $drivesFailureSupported = 2; /** * Constructor. @@ -44,13 +45,40 @@ public function getCapacity(array $options = []) $options += [ 'human' => false, ]; - $total = $this->getTotalCapacity(); - $count = $this->getDriveCount(); - $result = $total === 0 ? $total : $total / 2; + $capacity = $this->getMinimumDriveSize() * ($this->getDriveCount() - 2); + + if ($options['human'] === true) { + return Number::toReadableSize($capacity); + } + + return $capacity; + } + + /** + * Get parity total size + * + * Get the total size reserved for parity (unusable by data but not lossed). + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * ``` + * + * @param array $options Additional options to scope the results. + * @return int|string The total size reserved for parity of the RAID. + */ + public function getParitySize(array $options = []) + { + $options += [ + 'human' => false, + ]; + $paritySize = $this->getMinimumDriveSize() * 2; + if ($options['human'] === true) { - return Number::toReadableSize($result); + return Number::toReadableSize($paritySize); } - return $result; + return $paritySize; } } diff --git a/src/Raid/RaidTen.php b/src/Raid/RaidTen.php index 5436fcf..6f57e42 100644 --- a/src/Raid/RaidTen.php +++ b/src/Raid/RaidTen.php @@ -9,9 +9,11 @@ class RaidTen extends AbstractRaid { const LEVEL = 10; protected $drives = []; - protected $hotSpares = []; - protected $minimumDrives = 4; protected $mirrored = true; + protected $parity = false; + protected $striped = true; + protected $minimumDrives = 4; + protected $drivesFailureSupported = 2; /** * Constructor. @@ -24,6 +26,7 @@ public function __construct(array $drives = []) $this->validate($drives); } + $this->drivesFailureSupported = count($drives) / 2; $this->setDrives($drives); } @@ -43,11 +46,54 @@ public function getCapacity(array $options = []) $options += [ 'human' => false, ]; - $result = $this->getTotalCapacity() / 2; + $drivesOrderedByCapacity = $this->getDrives(['orderBy' => 'capacity', 'sortOrder' => 'DESC']); + $numberOfDrives = count($drivesOrderedByCapacity); + $capacity = 0; + for ($i = 0; $i + 1 < $numberOfDrives; $i += 2) { + $firstDrive = $drivesOrderedByCapacity[$i]; + $secondDrive = $drivesOrderedByCapacity[$i + 1]; + $capacity += min($firstDrive->getCapacity(), $secondDrive->getCapacity()); + } + + if ($options['human'] === true) { + return Number::toReadableSize($capacity); + } + + return $capacity; + } + + /** + * Get parity total size + * + * Get the total size reserved for parity (unusable by data but not lossed). + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * ``` + * + * @param array $options Additional options to scope the results. + * @return int|string The total size reserved for parity of the RAID. + */ + public function getParitySize(array $options = []) + { + $options += [ + 'human' => false, + ]; + $drivesOrderedByCapacity = $this->getDrives(['orderBy' => 'capacity', 'sortOrder' => 'DESC']); + $numberOfDrives = count($drivesOrderedByCapacity); + $paritySize = 0; + for ($i = 0; $i + 1 < $numberOfDrives; $i += 2) { + $firstDrive = $drivesOrderedByCapacity[$i]; + $secondDrive = $drivesOrderedByCapacity[$i + 1]; + $paritySize += min($firstDrive->getCapacity(), $secondDrive->getCapacity()); + } + if ($options['human'] === true) { - return Number::toReadableSize($result); + return Number::toReadableSize($paritySize); } - return $result; + return $paritySize; } } diff --git a/src/Raid/RaidZero.php b/src/Raid/RaidZero.php index 8bc76b8..bf5ab6b 100644 --- a/src/Raid/RaidZero.php +++ b/src/Raid/RaidZero.php @@ -9,10 +9,11 @@ class RaidZero extends AbstractRaid { const LEVEL = 0; protected $drives = []; - protected $hotSpares = []; - protected $minimumDrives = 2; protected $mirrored = false; + protected $parity = false; protected $striped = true; + protected $minimumDrives = 2; + protected $drivesFailureSupported = 0; /** * Constructor. @@ -44,12 +45,40 @@ public function getCapacity(array $options = []) $options += [ 'human' => false, ]; - $result = $this->getTotalCapacity(); + $capacity = $this->getTotalCapacity(); + + if ($options['human'] === true) { + return Number::toReadableSize($capacity); + } + + return $capacity; + } + + /** + * Get parity total size + * + * Get the total size reserved for parity (unusable by data but not lossed). + * + * Options: + * + * ``` + * - human - Whether to convert the result into human readable units, e.g. - 4 TB, 500 GB, etc + * ``` + * + * @param array $options Additional options to scope the results. + * @return int|string The total size reserved for parity of the RAID. + */ + public function getParitySize(array $options = []) + { + $options += [ + 'human' => false, + ]; + $paritySize = 0; if ($options['human'] === true) { - return Number::toReadableSize($result); + return Number::toReadableSize($paritySize); } - return $result; + return $paritySize; } } diff --git a/src/RaidFactory.php b/src/RaidFactory.php index 41603d9..733e944 100644 --- a/src/RaidFactory.php +++ b/src/RaidFactory.php @@ -4,6 +4,8 @@ use InvalidArgumentException; use kevinquinnyo\Raid\Raid\RaidFive; use kevinquinnyo\Raid\Raid\RaidOne; +use kevinquinnyo\Raid\Raid\RaidSHR; +use kevinquinnyo\Raid\Raid\RaidSHR2; use kevinquinnyo\Raid\Raid\RaidSix; use kevinquinnyo\Raid\Raid\RaidTen; use kevinquinnyo\Raid\Raid\RaidZero; @@ -18,31 +20,37 @@ class RaidFactory * @throws \InvalidArgumentException If the level provided is not supported by this library. * @return \kevinquinnyo\Raid\AbstractRaid Initialized Raid object. */ - public function create(int $level, $drives) + public function create($level, $drives) { $drives = (array)$drives; $raid = null; - switch ($level) { - case 0: + switch (true) { + case $level === 0: $raid = new RaidZero($drives); break; - case 1: + case $level === 1: $raid = new RaidOne($drives); break; - case 5: + case $level === 5: $raid = new RaidFive($drives); break; - case 6: + case $level === 6: $raid = new RaidSix($drives); break; - case 10: + case $level === 10: $raid = new RaidTen($drives); break; + case $level === 'SHR': + $raid = new RaidSHR($drives); + break; + case $level === 'SHR2': + $raid = new RaidSHR2($drives); + break; } if ($raid === null) { - throw new InvalidArgumentException('Unsupported RAID level provided. (Supported levels: 0, 1, 5, 6, 10)'); + throw new InvalidArgumentException('Unsupported RAID level provided. (Supported levels: 0, 1, 5, 6, 10, SHR)'); } return $raid; diff --git a/tests/AbstractRaidTest.php b/tests/AbstractRaidTest.php index d2d7f82..e944fc3 100644 --- a/tests/AbstractRaidTest.php +++ b/tests/AbstractRaidTest.php @@ -4,7 +4,6 @@ use \PHPUnit\Framework\TestCase; use \kevinquinnyo\Raid\Drive; use \kevinquinnyo\Raid\AbstractRaid; -use \kevinquinnyo\Raid\Raid\RaidFive; use ReflectionClass; use RuntimeException; @@ -52,11 +51,11 @@ public function testValidateThrowsExceptionWhenDriveIdentifierAlreadyPresent() $raidClass->validate($existingDrive); } - public function testGetHotSpares() + public function testGetHotSpareDrives() { $concreteRaid = $this->getMockForAbstractClass(AbstractRaid::class); $raidClass = new $concreteRaid(); - $this->assertEquals([], $raidClass->getHotSpares()); + $this->assertEquals([], $raidClass->getHotSpareDrives()); } public function testAddHotSpare() @@ -65,12 +64,12 @@ public function testAddHotSpare() $raidClass = new $concreteRaid(); // ensure there are no hot spares to begin with. - $this->assertEquals([], $raidClass->getHotSpares()); + $this->assertEquals([], $raidClass->getHotSpareDrives()); $newDrive = new Drive(1024, 'ssd', 1); $raidClass = $raidClass->addHotSpare($newDrive); - $this->assertEquals([$newDrive], $raidClass->getHotSpares()); + $this->assertEquals([$newDrive], $raidClass->getHotSpareDrives()); } public function testGetDriveCount() @@ -257,8 +256,33 @@ public function testGetMinimumDriveSizeWithHotSpare() $concreteRaid = $this->getMockForAbstractClass(AbstractRaid::class); $raidClass = new $concreteRaid(); $raidClass->setDrives($drives); + $this->assertSame(1024, $raidClass->getMinimumDriveSize(['withHotSpares' => true])); + $this->assertSame(1024, $raidClass->getMinimumDriveSize(['withHotSpares' => false])); + $raidClass->addHotSpare($hotSpare); $this->assertSame(512, $raidClass->getMinimumDriveSize(['withHotSpares' => true])); + $this->assertSame(1024, $raidClass->getMinimumDriveSize(['withHotSpares' => false])); + } + + public function testGetMaximumDriveSizeWithHotSpare() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(2048, 'ssd', 2), + new Drive(2048, 'ssd', 3), + ]; + + $hotSpare = new Drive(4096, 'ssd', 4); + + $concreteRaid = $this->getMockForAbstractClass(AbstractRaid::class); + $raidClass = new $concreteRaid(); + $raidClass->setDrives($drives); + $this->assertSame(2048, $raidClass->getMaximumDriveSize(['withHotSpares' => false])); + $this->assertSame(2048, $raidClass->getMaximumDriveSize(['withHotSpares' => true])); + + $raidClass->addHotSpare($hotSpare); + $this->assertSame(2048, $raidClass->getMaximumDriveSize(['withHotSpares' => false])); + $this->assertSame(4096, $raidClass->getMaximumDriveSize(['withHotSpares' => true])); } public function testAddDrive() @@ -332,4 +356,53 @@ public function testValidDriveCountWithEvenDrivesPlusSpare() $raidClass->setDrives($drives); $this->assertTrue($raidClass->validDriveCount()); } + + public function testGetNumberOfDrivesOfThisCapacity() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(2048, 'ssd', 2), + new Drive(2048, 'ssd', 3), + new Drive(2048, 'ssd', 4), + new Drive(2048, 'ssd', 5, ['hotSpare' => true]), + ]; + + $concreteRaid = $this->getMockForAbstractClass(AbstractRaid::class); + $raidClass = new $concreteRaid(); + /* Basically this is a Raid 10 with 4 drives and one hot spare */ + $this->setProtectedProperty($raidClass, 'minimumDrives', 4); + $this->setProtectedProperty($raidClass, 'mirrored', true); + + $raidClass->setDrives($drives); + $this->assertSame(1, $raidClass->getNumberOfDrivesOfThisCapacity(1024, array('withHotSpares' => true))); + $this->assertSame(1, $raidClass->getNumberOfDrivesOfThisCapacity(1024, array('withHotSpares' => false))); + $this->assertSame(3, $raidClass->getNumberOfDrivesOfThisCapacity(2048, array('withHotSpares' => false))); + $this->assertSame(4, $raidClass->getNumberOfDrivesOfThisCapacity(2048, array('withHotSpares' => true))); + } + + public function testGetNextMaximumDriveCapacity() + { + $drives = [ + new Drive(1024, 'ssd', 1, ['hotSpare' => true]), + new Drive(2048, 'ssd', 2), + new Drive(2048, 'ssd', 3), + new Drive(4096, 'ssd', 4, ['hotSpare' => true]), + ]; + + $concreteRaid = $this->getMockForAbstractClass(AbstractRaid::class); + $raidClass = new $concreteRaid(); + /* Basically this is a Raid 10 with 4 drives and one hot spare */ + $this->setProtectedProperty($raidClass, 'minimumDrives', 4); + $this->setProtectedProperty($raidClass, 'mirrored', true); + + $raidClass->setDrives($drives); + $this->assertSame(0, $raidClass->getNextMaximumDriveCapacity(1024, array('withHotSpares' => true))); + $this->assertSame(0, $raidClass->getNextMaximumDriveCapacity(1024, array('withHotSpares' => false))); + $this->assertSame(1024, $raidClass->getNextMaximumDriveCapacity(2048, array('withHotSpares' => true))); + $this->assertSame(0, $raidClass->getNextMaximumDriveCapacity(2048, array('withHotSpares' => false))); + $this->assertSame(2048, $raidClass->getNextMaximumDriveCapacity(4096, array('withHotSpares' => true))); + $this->assertSame(2048, $raidClass->getNextMaximumDriveCapacity(4096, array('withHotSpares' => false))); + $this->assertSame(4096, $raidClass->getNextMaximumDriveCapacity(8192, array('withHotSpares' => true))); + $this->assertSame(2048, $raidClass->getNextMaximumDriveCapacity(8192, array('withHotSpares' => false))); + } } diff --git a/tests/DriveTest.php b/tests/DriveTest.php index e91391d..af57dc0 100644 --- a/tests/DriveTest.php +++ b/tests/DriveTest.php @@ -28,7 +28,7 @@ public function testSetIdentifier() public function testGetCapacity() { $drive = new Drive('1k', 'ssd', 1); - $this->assertSame(1024.0, $drive->getCapacity()); + $this->assertSame(1024, $drive->getCapacity()); } public function testGetCapacityWithHuman() diff --git a/tests/Raid/RaidFiveTest.php b/tests/Raid/RaidFiveTest.php index dec9c0d..8520255 100644 --- a/tests/Raid/RaidFiveTest.php +++ b/tests/Raid/RaidFiveTest.php @@ -30,10 +30,39 @@ public function testGetCapacityWithHotSparesWithoutHotSparesOption() ]; $raidFive = new RaidFive($drives); $this->assertSame(2048, $raidFive->getCapacity()); + $this->assertSame("2 KB", $raidFive->getCapacity(['human' => true])); } + public function testGetLevel() { $raidFive = new RaidFive(); $this->assertSame(5, $raidFive->getLevel()); } + + public function testGetParitySize() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + ]; + $raidFive = new RaidFive($drives); + $this->assertSame(1024, $raidFive->getParitySize()); + $this->assertSame("1 KB", $raidFive->getParitySize(['human' => true])); + } + + public function testGetParitySizeWithHotSpares() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(1024, 'ssd', 4), + new Drive(1024, 'ssd', 5, ['hotSpare' => true]), + new Drive(1024, 'ssd', 6, ['hotSpare' => true]), + ]; + $raidFive = new RaidFive($drives); + $this->assertSame(1024, $raidFive->getParitySize()); + $this->assertSame("1 KB", $raidFive->getParitySize(['human' => true])); + } } diff --git a/tests/Raid/RaidOneTest.php b/tests/Raid/RaidOneTest.php index 6b1beb8..45292ae 100644 --- a/tests/Raid/RaidOneTest.php +++ b/tests/Raid/RaidOneTest.php @@ -17,6 +17,7 @@ public function testGetCapacity() $this->assertSame(1024, $raidOne->getCapacity()); $this->assertSame('1 KB', $raidOne->getCapacity(['human' => true])); } + public function testGetCapacityWithHotSpares() { $drives = [ @@ -28,10 +29,39 @@ public function testGetCapacityWithHotSpares() ]; $raidOne = new RaidOne($drives); $this->assertSame(1024, $raidOne->getCapacity()); + $this->assertSame('1 KB', $raidOne->getCapacity(['human' => true])); } + public function testGetLevel() { $raidOne = new RaidOne(); $this->assertSame(1, $raidOne->getLevel()); } + + public function testGetParitySize() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + ]; + $raidOne = new RaidOne($drives); + $this->assertSame(2048, $raidOne->getParitySize()); + $this->assertSame("2 KB", $raidOne->getParitySize(['human' => true])); + } + + public function testGetParitySizeWithHotSpares() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(1024, 'ssd', 4), + new Drive(1024, 'ssd', 5, ['hotSpare' => true]), + new Drive(1024, 'ssd', 6, ['hotSpare' => true]), + ]; + $raidOne = new RaidOne($drives); + $this->assertSame(3072, $raidOne->getParitySize()); + $this->assertSame("3 KB", $raidOne->getParitySize(['human' => true])); + } } diff --git a/tests/Raid/RaidSHR2Test.php b/tests/Raid/RaidSHR2Test.php new file mode 100644 index 0000000..a5b9e62 --- /dev/null +++ b/tests/Raid/RaidSHR2Test.php @@ -0,0 +1,71 @@ +assertSame(2048, $raidSHR2->getCapacity()); + $this->assertSame('2 KB', $raidSHR2->getCapacity(['human' => true])); + } + + public function testGetCapacityWithHotSpares() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(1024, 'ssd', 4), + new Drive(1024, 'ssd', 5, ['hotSpare' => true]), + new Drive(1024, 'ssd', 6, ['hotSpare' => true]), + ]; + $raidSHR2 = new RaidSHR2($drives); + $this->assertSame(2048, $raidSHR2->getCapacity()); + $this->assertSame("2 KB", $raidSHR2->getCapacity(['human' => true])); + } + + public function testGetLevel() + { + $raidSHR2 = new RaidSHR2(); + $this->assertSame('SHR2', $raidSHR2->getLevel()); + } + + public function testGetParitySize() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(1024, 'ssd', 4), + ]; + $raidSHR2 = new RaidSHR2($drives); + $this->assertSame(2048, $raidSHR2->getParitySize()); + $this->assertSame("2 KB", $raidSHR2->getParitySize(['human' => true])); + } + + public function testGetParitySizeWithHotSpares() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(1024, 'ssd', 4), + new Drive(1024, 'ssd', 5, ['hotSpare' => true]), + new Drive(1024, 'ssd', 6, ['hotSpare' => true]), + ]; + $raidSHR2 = new RaidSHR2($drives); + $this->assertSame(2048, $raidSHR2->getParitySize()); + $this->assertSame("2 KB", $raidSHR2->getParitySize(['human' => true])); + } +} diff --git a/tests/Raid/RaidSHRTest.php b/tests/Raid/RaidSHRTest.php new file mode 100644 index 0000000..bfddbec --- /dev/null +++ b/tests/Raid/RaidSHRTest.php @@ -0,0 +1,68 @@ +assertSame(2048, $raidSHR->getCapacity()); + $this->assertSame('2 KB', $raidSHR->getCapacity(['human' => true])); + } + + public function testGetCapacityWithHotSpares() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(1024, 'ssd', 4, ['hotSpare' => true]), + new Drive(1024, 'ssd', 5, ['hotSpare' => true]), + ]; + $raidSHR = new RaidSHR($drives); + $this->assertSame(2048, $raidSHR->getCapacity()); + $this->assertSame("2 KB", $raidSHR->getCapacity(['human' => true])); + } + + public function testGetLevel() + { + $raidSHR = new RaidSHR(); + $this->assertSame('SHR', $raidSHR->getLevel()); + } + + public function testGetParitySize() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + ]; + $raidSHR = new RaidSHR($drives); + $this->assertSame(1024, $raidSHR->getParitySize()); + $this->assertSame("1 KB", $raidSHR->getParitySize(['human' => true])); + } + + public function testGetParitySizeWithHotSpares() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(1024, 'ssd', 4), + new Drive(1024, 'ssd', 5, ['hotSpare' => true]), + new Drive(1024, 'ssd', 6, ['hotSpare' => true]), + ]; + $raidSHR = new RaidSHR($drives); + $this->assertSame(1024, $raidSHR->getParitySize()); + $this->assertSame("1 KB", $raidSHR->getParitySize(['human' => true])); + } +} diff --git a/tests/Raid/RaidSixTest.php b/tests/Raid/RaidSixTest.php index 9be8900..390fc8e 100644 --- a/tests/Raid/RaidSixTest.php +++ b/tests/Raid/RaidSixTest.php @@ -31,10 +31,38 @@ public function testGetCapacityWithHotSpares() ]; $raidSix = new RaidSix($drives); $this->assertSame(2048, $raidSix->getCapacity()); + $this->assertSame("2 KB", $raidSix->getCapacity(['human' => true])); } public function testGetLevel() { $raidSix = new RaidSix(); $this->assertSame(6, $raidSix->getLevel()); } + + public function testGetParitySize() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + ]; + $raidSix = new RaidSix($drives); + $this->assertSame(2048, $raidSix->getParitySize()); + $this->assertSame("2 KB", $raidSix->getParitySize(['human' => true])); + } + + public function testGetParitySizeWithHotSpares() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(1024, 'ssd', 4), + new Drive(1024, 'ssd', 5, ['hotSpare' => true]), + new Drive(1024, 'ssd', 6, ['hotSpare' => true]), + ]; + $raidSix = new RaidSix($drives); + $this->assertSame(2048, $raidSix->getParitySize()); + $this->assertSame("2 KB", $raidSix->getParitySize(['human' => true])); + } } diff --git a/tests/Raid/RaidTenTest.php b/tests/Raid/RaidTenTest.php index 73f5d81..3b84d0e 100644 --- a/tests/Raid/RaidTenTest.php +++ b/tests/Raid/RaidTenTest.php @@ -13,15 +13,59 @@ public function testGetCapacity() new Drive(1024, 'ssd', 1), new Drive(1024, 'ssd', 2), new Drive(1024, 'ssd', 3), - new Drive(1024, 'ssd', 4), + new Drive(2048, 'ssd', 4), ]; $raidTen = new RaidTen($drives); $this->assertSame(2048, $raidTen->getCapacity()); $this->assertSame('2 KB', $raidTen->getCapacity(['human' => true])); } + + public function testGetCapacityWithHotSpares() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(2048, 'ssd', 4), + new Drive(1024, 'ssd', 5, ['hotSpare' => true]), + new Drive(2048, 'ssd', 6, ['hotSpare' => true]), + ]; + $raidTen = new RaidTen($drives); + $this->assertSame(2048, $raidTen->getCapacity()); + $this->assertSame('2 KB', $raidTen->getCapacity(['human' => true])); + } + public function testGetLevel() { $raidTen = new RaidTen(); $this->assertSame(10, $raidTen->getLevel()); } + + public function testGetParitySize() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(2048, 'ssd', 4), + ]; + $raidTen = new RaidTen($drives); + $this->assertSame(2048, $raidTen->getParitySize()); + $this->assertSame("2 KB", $raidTen->getParitySize(['human' => true])); + } + + public function testGetParitySizeWithHotSpares() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(2048, 'ssd', 4), + new Drive(1024, 'ssd', 5, ['hotSpare' => true]), + new Drive(2048, 'ssd', 6, ['hotSpare' => true]), + ]; + $raidTen = new RaidTen($drives); + $this->assertSame(2048, $raidTen->getParitySize()); + $this->assertSame("2 KB", $raidTen->getParitySize(['human' => true])); + } } diff --git a/tests/Raid/RaidZeroTest.php b/tests/Raid/RaidZeroTest.php index 55fa23f..141cd42 100644 --- a/tests/Raid/RaidZeroTest.php +++ b/tests/Raid/RaidZeroTest.php @@ -29,6 +29,7 @@ public function testGetCapacityWithHotSpares() ]; $raidZero = new RaidZero($drives); $this->assertSame(3072, $raidZero->getCapacity()); + $this->assertSame("3 KB", $raidZero->getCapacity(['human' => true])); } public function testGetLevel() @@ -36,4 +37,31 @@ public function testGetLevel() $raidZero = new RaidZero(); $this->assertSame(0, $raidZero->getLevel()); } + + public function testGetParitySize() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + ]; + $raidZero = new RaidZero($drives); + $this->assertSame(0, $raidZero->getParitySize()); + $this->assertSame("0 Byte", $raidZero->getParitySize(['human' => true])); + } + + public function testGetParitySizeWithHotSpares() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(1024, 'ssd', 4), + new Drive(1024, 'ssd', 5, ['hotSpare' => true]), + new Drive(1024, 'ssd', 6, ['hotSpare' => true]), + ]; + $raidZero = new RaidZero($drives); + $this->assertSame(0, $raidZero->getParitySize()); + $this->assertSame("0 Byte", $raidZero->getParitySize(['human' => true])); + } } diff --git a/tests/RaidFactoryTest.php b/tests/RaidFactoryTest.php index 098756a..9a7af31 100644 --- a/tests/RaidFactoryTest.php +++ b/tests/RaidFactoryTest.php @@ -7,6 +7,8 @@ use \kevinquinnyo\Raid\Raid\RaidZero; use \kevinquinnyo\Raid\Raid\RaidOne; use \kevinquinnyo\Raid\Raid\RaidFive; +use \kevinquinnyo\Raid\Raid\RaidSHR; +use \kevinquinnyo\Raid\Raid\RaidSHR2; use \kevinquinnyo\Raid\Raid\RaidSix; use \kevinquinnyo\Raid\Raid\RaidTen; use InvalidArgumentException; @@ -73,4 +75,26 @@ public function testCreateRaidTen() $raid = $factory->create(10, $drives); $this->assertInstanceOf(RaidTen::class, $raid); } + public function testCreateSHRRaid() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + ]; + $factory = new RaidFactory(); + $raid = $factory->create('SHR', $drives); + $this->assertInstanceOf(RaidSHR::class, $raid); + } + public function testCreateSHR2Raid() + { + $drives = [ + new Drive(1024, 'ssd', 1), + new Drive(1024, 'ssd', 2), + new Drive(1024, 'ssd', 3), + new Drive(1024, 'ssd', 4), + ]; + $factory = new RaidFactory(); + $raid = $factory->create('SHR2', $drives); + $this->assertInstanceOf(RaidSHR2::class, $raid); + } }