├── LICENSE ├── README.md └── main.php /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Programster 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ZFS Balancer 2 | ============ 3 | 4 | A PHP script created in order to re-balance a ZFS RAID array that was built up over time by adding drives to an existing pool. 5 | 6 | To check the balance of your current pool, use the command: 7 | 8 | ``` 9 | zpool list -v 10 | ``` 11 | 12 | Here is an example output of my very unbalanced pool soon after I added two 8TB drives to it: 13 | 14 | ``` 15 | NAME SIZE ALLOC FREE EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT 16 | zpool1 13.6T 4.21T 9.39T - 15% 30% 1.00x ONLINE - 17 | mirror 3.62T 2.20T 1.43T - 31% 60% 18 | sda - - - - - - 19 | sdb - - - - - - 20 | mirror 2.72T 1.65T 1.07T - 32% 60% 21 | sdc - - - - - - 22 | sdd - - - - - - 23 | mirror 7.25T 368G 6.89T - 2% 4% 24 | sde - - - - - - 25 | sdf - - - - - - 26 | ``` 27 | 28 | And this is what it is like today much later and balanced: 29 | 30 | ``` 31 | NAME SIZE ALLOC FREE EXPANDSZ FRAG CAP DEDUP HEALTH ALTROOT 32 | zpool1 13.6T 9.84T 3.75T - 25% 72% 1.00x DEGRADED - 33 | mirror 3.62T 2.62T 1.01T - 26% 72% 34 | ata-WDC_WD40EFRX-68WT0N0_WD-WCC4E1984538 - - - - - - 35 | ata-WDC_WD40EFRX-68WT0N0_WD-WCC4E1984290 - - - - - - 36 | mirror 2.72T 1.93T 807G - 29% 71% 37 | ata-WDC_WD30EFRX-68EUZN0_WD-WCC4N1DCX6L4 - - - - - - 38 | ata-WDC_WD30EFRX-68EUZN0_WD-WMC4N1605537 - - - - - - 39 | mirror 7.25T 5.29T 1.96T - 24% 73% 40 | ata-WDC_WD80EFZX-68UW8N0_VLKZKBBV - - - - - - 41 | ata-WDC_WD80EFZX-68UW8N0_R6G68HBY - - - - - - 42 | ``` 43 | 44 | ### Deduplication Off 45 | As this script works by copying files before deleting the original and renaming the copy back to that of the original, so **you probably need deduplication switched off** for the principle to properly work. You can do this in Ubuntu 16.04 with the following command: 46 | 47 | ``` 48 | sudo zfs set dedup=off myZpoolName 49 | ``` 50 | 51 | Note: this script will work on one file at a time rather than all at once, so it's not a disaster if you terminate the script mid-way through. The worst thing that could happen is that you either have the original file and a copy with a number extension, or you just have the copy with a number extension rather than the original, but you never lose/corrupt a file. 52 | 53 | ## Requirements 54 | This script requires PHP 5.5 or higher. You can usually install PHP on Ubuntu 16.04 with 55 | 56 | ``` 57 | sudo apt-get install php7.0-cli 58 | ``` 59 | 60 | On Ubuntu 14.04 I believe the command was: 61 | ``` 62 | sudo apt-get install php-cli 63 | ``` 64 | 65 | ## Usage 66 | ``` 67 | php main.php /path/to/folder/to/balance 68 | ``` 69 | *Note: You can use relative or full paths.* 70 | 71 | ## Testing 72 | Tested on Ubuntu 14.04 64bit with php 5.5.9 73 | -------------------------------------------------------------------------------- /main.php: -------------------------------------------------------------------------------- 1 | $file) 17 | { 18 | if (!is_dir($filename)) 19 | { 20 | $numFiles++; 21 | } 22 | } 23 | 24 | 25 | # Now "rebalance" 26 | $iterator = new RecursiveDirectoryIterator($directory); 27 | $progressCounter = 0; 28 | $lastPercentage = 0; 29 | 30 | foreach (new RecursiveIteratorIterator($iterator) as $filename => $file) 31 | { 32 | if (!is_dir($filename)) 33 | { 34 | $progressCounter++; 35 | 36 | $newPercentage = intval(($progressCounter / $numFiles * 100)); 37 | if ($newPercentage !== $lastPercentage) 38 | { 39 | print $newPercentage . "%" . PHP_EOL; 40 | $lastPercentage = $newPercentage; 41 | } 42 | 43 | 44 | $counter = 1; 45 | 46 | while (is_file($filename . $counter)) 47 | { 48 | $counter++; 49 | } 50 | 51 | $newname = $filename . $counter; 52 | 53 | # We have to use copy first instead of rename because we need 54 | # new data blocks to be rewritten across all drives. Performing 55 | # a rename would just change the metadata. 56 | copy($filename, $newname); 57 | unlink($filename); 58 | 59 | # Rename the file back to what it was originally called. 60 | rename($newname, $filename); 61 | } 62 | } 63 | } 64 | 65 | 66 | 67 | $directory = $argv[1]; 68 | 69 | 70 | if (is_dir($directory)) 71 | { 72 | $directory = realpath($directory); 73 | 74 | if (strtolower(readline("Balance the files within [$directory] (y/n)? ")) == "y") 75 | { 76 | rebalanceDir($directory); 77 | print "complete!" . PHP_EOL; 78 | } 79 | } 80 | else 81 | { 82 | print 'Invalid directory given!' . PHP_EOL; 83 | } 84 | --------------------------------------------------------------------------------