]>
Commit | Line | Data |
---|---|---|
76e64919 MR |
1 | <?php |
2 | /* | |
3 | * OMVZModulePool.php | |
4 | * | |
5 | * Copyright 2013 Michael Rasmussen <mir@datanom.net> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation; either version 3 of the License, or | |
10 | * (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
20 | * MA 02110-1301, USA. | |
21 | */ | |
22 | ||
23 | /** | |
24 | * @class OMVModuleZPool. | |
25 | */ | |
26 | ||
c6268644 MR |
27 | require ('OMVModulePoolType.php'); |
28 | require ('OMVModulePoolAction.php'); | |
76e64919 MR |
29 | |
30 | class OMVModuleZPool { | |
31 | ||
32 | private $params; | |
33 | private $log; | |
34 | private $mirror_log; | |
35 | private $cache; | |
36 | private $spare; | |
c6268644 MR |
37 | private $alloc; |
38 | private $status; // one of null, ok, degraded, faulted, scrub, resilver | |
39 | private $debug; | |
76e64919 MR |
40 | |
41 | /** | |
42 | * Constructor of class OMVModuleZPool. | |
43 | * @param params Array which can contain all or a subset of | |
44 | * the following fields: | |
c6268644 MR |
45 | * <ul> |
46 | * <li> atime Either true or false. <b>Default is true</b>. | |
47 | * <li> compress Either true or false. <b>Default is false</b>. | |
48 | * <li> dedub Either true or false. <b>Default is false</b>. | |
49 | * <li> name Name for pool | |
50 | * <li> size Size of pool in either MB or GB. 100M or 100G. | |
51 | * <li> sync Either true or false. <b>Default is false</b>. | |
52 | * <li> type See OMVModulePoolType. | |
53 | * <li> vdevs Number of vdevs in pool. <b>Default is 1</b>. | |
54 | * </ul> | |
55 | * @param options Array which can contain all or a subset of | |
56 | * the following fields: | |
57 | * <ul> | |
58 | * <li> log Array which contains the following fields: | |
59 | * <ul> | |
60 | * <li> disks Array of disks. Format: /dev/disk/by-id/..... | |
61 | * <li> mirror Either true or false. <b>Default is false</b>. | |
62 | * </ul> | |
63 | * <li> cache Array which contains the following fields: | |
64 | * <ul> | |
65 | * <li> disks Array of disks. Format: /dev/disk/by-id/.... <b>Default use internal</b>. | |
66 | * </ul> | |
67 | * <li> spare Array of disks. Format: /dev/disk/by-id/... | |
68 | * </ul> | |
76e64919 MR |
69 | * @param debug Print debug information to STDERR. |
70 | * @throws Exception. | |
71 | */ | |
c6268644 | 72 | public function __construct(array $params = array(), array $options = array(), $debug = false) { |
76e64919 MR |
73 | $this->params = array( |
74 | 'atime' => 'on', | |
75 | 'compress' => 'off', | |
76 | 'dedub' => 'off', | |
77 | 'name' => '', | |
78 | 'size' => '0M', | |
79 | 'sync' => 'off', | |
80 | 'type' => OMVModulePoolType::OMVModulePoolType_TYPE_NONE, | |
81 | 'vdevs' => 1 | |
82 | ); | |
c6268644 | 83 | $this->debug = $debug; |
76e64919 MR |
84 | |
85 | foreach ($params as $key => $value) { | |
86 | if (array_key_exists($key, $this->params)) { | |
87 | $this->validate($key, $value); | |
88 | $this->params[$key] = $value; | |
89 | } else { | |
90 | throw new Exception("$key: Not valid parameter"); | |
91 | } | |
92 | } | |
93 | ||
c6268644 MR |
94 | foreach ($options as $key => $value) { |
95 | $this->validate($key, $value, true); | |
96 | } | |
97 | ||
98 | $this->status = null; | |
99 | ||
100 | if ($this->debug) { | |
76e64919 MR |
101 | fprintf(STDERR, "OMVModuleZPool instantiate with the following params\n"); |
102 | foreach ($this->params as $key => $value) { | |
103 | $param = ($key == 'type') ? OMVModulePoolType::toString($value) : $value; | |
104 | fprintf(STDERR, " %-8s => %s\n", $key, $param); | |
105 | } | |
106 | } | |
107 | } | |
108 | ||
109 | /** | |
110 | * Create pool. | |
c6268644 MR |
111 | * @param disks Array of disks. Format: /dev/disk/by-id/... |
112 | * @param name Name for the pool. | |
113 | * @param options Array which can contain all or a subset of | |
114 | * the following fields: | |
115 | * <ul> | |
116 | * <li> log Array which contains the following fields: | |
117 | * <ul> | |
118 | * <li> disks Array of disks. Format: /dev/disk/by-id/... | |
119 | * <li> mirror Either true or false. <b>Default is false</b>. | |
120 | * </ul> | |
121 | * <li> cache Array which contains the following fields: | |
122 | * <ul> | |
123 | * <li> disks Array of disks. Format: /dev/disk/by-id/... <b>Default use internal</b>. | |
124 | * </ul> | |
125 | * <li> spare Array of disks. Format: /dev/disk/by-id/... | |
126 | * </ul> | |
127 | * @throws Exception. | |
128 | */ | |
129 | public function createPool(array $disks, $name = null, array $options = array()) { | |
130 | return $this->updatePool(OMVModulePoolAction::OMVModulePoolAction_TYPE_CREATE, | |
131 | $disks, count($disks), $name, $options); | |
132 | } | |
133 | ||
134 | /** | |
135 | * Add pool. | |
136 | * @param disks Array of disks. Format: /dev/disk/by-id/... | |
137 | * @param name Name for the pool. | |
76e64919 MR |
138 | * @param options Array which can contain all or a subset of |
139 | * the following fields: | |
c6268644 MR |
140 | * <ul> |
141 | * <li> log Array which contains the following fields: | |
142 | * <ul> | |
143 | * <li> disks Array of disks. Format: /dev/disk/by-id/... | |
144 | * <li> mirror Either true or false. <b>Default is false</b>. | |
145 | * </ul> | |
146 | * <li> cache Array which contains the following fields: | |
147 | * <ul> | |
148 | * <li> disks Array of disks. Format: /dev/disk/by-id/... <b>Default use internal</b>. | |
149 | * </ul> | |
150 | * <li> spare Array of disks. Format: /dev/disk/by-id/... | |
151 | * </ul> | |
76e64919 MR |
152 | * @throws Exception. |
153 | */ | |
c6268644 MR |
154 | public function addPool(array $disks, $name = null, array $options = array()) { |
155 | $this->initPool($name); | |
156 | return $this->updatePool(OMVModulePoolAction::OMVModulePoolAction_TYPE_ADD, | |
157 | $disks, count($disks), $name, $options); | |
158 | } | |
159 | ||
160 | public function removeDevice(array $disks, $name = null) { | |
161 | } | |
162 | ||
163 | public function destroyPool($name = null) { | |
164 | } | |
165 | ||
166 | public function scrubPool($name = null) { | |
167 | } | |
168 | ||
169 | public function exportPool($name = null){ | |
170 | } | |
171 | ||
172 | public function importPool($name = null){ | |
173 | } | |
174 | ||
175 | /* | |
176 | * public function attachDevice($extingDisk, $newDisk, $name = null) | |
177 | * public function detachDevice($disk, $name = null) | |
178 | * public function onlineDevice($disk, $name = null) | |
179 | * public function offlineDevice($disk, $name = null) | |
180 | * public function replaceDevice($extingDisk, $newDisk, $name = null) | |
181 | * public function upgradePool($name = null) | |
182 | */ | |
183 | ||
184 | private function initPool($name = null) { | |
185 | // Replace with OMVUtil::exec | |
186 | $this->validateName($name); | |
187 | } | |
188 | ||
189 | private function validateName($name = null) { | |
190 | $oldname = null; | |
191 | if ($name != null) { | |
192 | $oldname = $this->params['name']; | |
193 | $this->params['name'] = $name; | |
194 | } | |
195 | if (! $this->params['name'] || ! preg_match('/^\w+$/', $this->params['name'])) { | |
196 | if ($oldname) { | |
197 | $this->params['name'] = $oldname; | |
198 | } | |
199 | throw new Exception("Name of pool cannot be null or empty"); | |
200 | } | |
201 | } | |
202 | ||
203 | /** | |
204 | * Update pool. | |
205 | * @param action Action to be taken. create or add. | |
206 | * @param disks Array of disks. Format: /dev/disk/by-id/... | |
207 | * @param disk_num Number of disk(s) in the pool. | |
208 | * @param name Name for the pool. | |
209 | * @param options Array which can contain all or a subset of | |
210 | * the following fields: | |
211 | * <ul> | |
212 | * <li> log Array which contains the following fields: | |
213 | * <ul> | |
214 | * <li> disks Array of disks. Format: /dev/disk/by-id/... | |
215 | * <li> mirror Either true or false. <b>Default is false</b>. | |
216 | * </ul> | |
217 | * <li> cache Array which contains the following fields: | |
218 | * <ul> | |
219 | * <li> disks Array of disks. Format: /dev/disk/by-id/... <b>Default use internal</b>. | |
220 | * </ul> | |
221 | * <li> spare Array of disks. Format: /dev/disk/by-id/... | |
222 | * </ul> | |
223 | * @throws Exception. | |
224 | */ | |
225 | private function updatePool($action, array $disks, $disk_num, $name = null, array $options = array()) { | |
226 | OMVModulePoolAction::toString($action); | |
227 | $cmd = 'zpool ' . OMVModulePoolAction::getAction($action); | |
76e64919 MR |
228 | if ($disk_num < 1) { |
229 | throw new Exception("Pool must have at least 1 disk"); | |
230 | }; | |
c6268644 MR |
231 | $this->validateName($name); |
232 | foreach ($options as $key => $value) { | |
233 | $this->validate($key, $value, true); | |
234 | } | |
235 | switch ($action) { | |
236 | case OMVModulePoolAction::OMVModulePoolAction_TYPE_CREATE: | |
237 | $cmd .= ' ' . $this->params['name']; | |
238 | break; | |
239 | case OMVModulePoolAction::OMVModulePoolAction_TYPE_ADD: | |
240 | $cmd .= ' ' . $this->params['name']; | |
241 | break; | |
242 | } | |
76e64919 MR |
243 | switch ($this->params['type']) { |
244 | case OMVModulePoolType::OMVModulePoolType_TYPE_NONE: | |
245 | print "Create basic pool\n"; | |
c6268644 MR |
246 | if ($this->params['vdevs'] != 1) { |
247 | throw new Exception("A basic zpool can only have 1 vdev"); | |
76e64919 | 248 | } |
c6268644 MR |
249 | $vdev = ''; |
250 | foreach ($disks as $disk) { | |
251 | $vdev .= ($vdev) ? " $disk" : "$disk"; | |
76e64919 | 252 | } |
c6268644 | 253 | $cmd .= " $vdev"; |
76e64919 MR |
254 | break; |
255 | case OMVModulePoolType::OMVModulePoolType_TYPE_MIRROR: | |
256 | print "Create mirrored pool\n"; | |
257 | if ($disk_num / $this->params['vdevs'] < 2 || | |
258 | ($disk_num / $this->params['vdevs']) % $this->params['vdevs']) { | |
259 | throw new Exception("$disk_num disk(s) cannot be evenly distributed to ". | |
c6268644 MR |
260 | $this->params['vdevs']." vdev(s) and form a proper stripe or mirror"); |
261 | } | |
262 | $vdevs = array(); | |
263 | $disk_sum = $disk_num / $this->params['vdevs']; | |
264 | $s = ''; | |
265 | for ($i = 1, $num = 0; $i <= $disk_num; $i++) { | |
266 | $s .= ($s) ? " ${disks[$i-1]}" : "${disks[$i-1]}"; | |
267 | if ($i % $disk_sum == 0) { | |
268 | $vdevs[$num++] = $s; | |
269 | $s = ''; | |
270 | } | |
271 | } | |
272 | foreach ($vdevs as $vdev) { | |
273 | $cmd .= " mirror $vdev"; | |
76e64919 MR |
274 | } |
275 | break; | |
276 | case OMVModulePoolType::OMVModulePoolType_TYPE_RAIDZ1: | |
277 | print "Create raidz1 pool\n"; | |
278 | if ($disk_num / $this->params['vdevs'] < 3 || | |
279 | ($disk_num / $this->params['vdevs']) % $this->params['vdevs']) { | |
280 | throw new Exception("$disk_num disk and ".$this->params['vdevs']. | |
281 | " vdev(s) cannot provide mininum 3 disks per vdev for raidz1"); | |
282 | } | |
c6268644 MR |
283 | $vdevs = array(); |
284 | $disk_sum = $disk_num / $this->params['vdevs']; | |
285 | $s = ''; | |
286 | for ($i = 1, $num = 0; $i <= $disk_num; $i++) { | |
287 | $s .= ($s) ? " ${disks[$i-1]}" : "${disks[$i-1]}"; | |
288 | if ($i % $disk_sum == 0) { | |
289 | $vdevs[$num++] = $s; | |
290 | $s = ''; | |
291 | } | |
292 | } | |
293 | foreach ($vdevs as $vdev) { | |
294 | $cmd .= " raidz1 $vdev"; | |
295 | } | |
76e64919 MR |
296 | break; |
297 | case OMVModulePoolType::OMVModulePoolType_TYPE_RAIDZ2: | |
298 | print "Create raidz2 pool\n"; | |
c6268644 | 299 | if ($disk_num / $this->params['vdevs'] < 4 || |
76e64919 MR |
300 | ($disk_num / $this->params['vdevs']) % $this->params['vdevs']) { |
301 | throw new Exception("$disk_num disk and ".$this->params['vdevs']. | |
c6268644 MR |
302 | " vdev(s) cannot provide mininum 4 disks per vdev for raidz2"); |
303 | } | |
304 | $vdevs = array(); | |
305 | $disk_sum = $disk_num / $this->params['vdevs']; | |
306 | $s = ''; | |
307 | for ($i = 1, $num = 0; $i <= $disk_num; $i++) { | |
308 | $s .= ($s) ? " ${disks[$i-1]}" : "${disks[$i-1]}"; | |
309 | if ($i % $disk_sum == 0) { | |
310 | $vdevs[$num++] = $s; | |
311 | $s = ''; | |
312 | } | |
313 | } | |
314 | foreach ($vdevs as $vdev) { | |
315 | $cmd .= " raidz2 $vdev"; | |
76e64919 MR |
316 | } |
317 | break; | |
318 | case OMVModulePoolType::OMVModulePoolType_TYPE_RAIDZ3: | |
319 | print "Create raidz3 pool\n"; | |
c6268644 | 320 | if ($disk_num / $this->params['vdevs'] < 5 || |
76e64919 MR |
321 | ($disk_num / $this->params['vdevs']) % $this->params['vdevs']) { |
322 | throw new Exception("$disk_num disk and ".$this->params['vdevs']. | |
c6268644 MR |
323 | " vdev(s) cannot provide mininum 5 disks per vdev for raidz3"); |
324 | } | |
325 | $vdevs = array(); | |
326 | $disk_sum = $disk_num / $this->params['vdevs']; | |
327 | $s = ''; | |
328 | for ($i = 1, $num = 0; $i <= $disk_num; $i++) { | |
329 | $s .= ($s) ? " ${disks[$i-1]}" : "${disks[$i-1]}"; | |
330 | if ($i % $disk_sum == 0) { | |
331 | $vdevs[$num++] = $s; | |
332 | $s = ''; | |
333 | } | |
334 | } | |
335 | foreach ($vdevs as $vdev) { | |
336 | $cmd .= " raidz3 $vdev"; | |
76e64919 MR |
337 | } |
338 | break; | |
339 | } | |
c6268644 MR |
340 | foreach ($options as $key => $value) { |
341 | switch ($key) { | |
342 | case 'log': | |
343 | $cmd .= " log"; | |
344 | if ($value['mirror'] === true) { | |
345 | $cmd .= " mirror"; | |
346 | } | |
347 | foreach ($value['disks'] as $disk) { | |
348 | $cmd .= " $disk"; | |
349 | } | |
76e64919 | 350 | break; |
c6268644 MR |
351 | case 'cache': |
352 | $cmd .= " cache"; | |
353 | foreach ($value as $disk) { | |
354 | $cmd .= " $disk"; | |
355 | } | |
76e64919 | 356 | break; |
c6268644 MR |
357 | case 'spare': |
358 | $cmd .= " spare"; | |
359 | foreach ($value as $disk) { | |
360 | $cmd .= " $disk"; | |
361 | } | |
76e64919 | 362 | break; |
c6268644 MR |
363 | } |
364 | } | |
365 | if ($this->debug) { | |
366 | fprintf(STDERR, "%s\n", $cmd); | |
367 | } | |
368 | ||
369 | return $cmd; | |
370 | } | |
371 | ||
372 | /** | |
373 | * Validate options. | |
374 | * @param key Option to validate. | |
375 | * @param value Value for option to validate. | |
376 | * @param type See OMVModulePoolType. | |
377 | * @throws Exception if option is invalid. | |
378 | */ | |
379 | private function validate($key, $value, $options = false) { | |
380 | if ($options) { | |
381 | switch ($key) { | |
382 | case 'log': | |
383 | if (! is_array($value) || ! array_key_exists('mirror', $value) || | |
384 | ! array_key_exists('disks', $value)) { | |
385 | throw new Exception("log option missing disks array or mirror value"); | |
386 | } | |
387 | if ($value['mirror'] === true && count($value['disks']) != 2) { | |
388 | throw new Exception("Two disks is required to form a log mirror"); | |
389 | } | |
390 | $this->log = $value['disks']; | |
391 | $this->mirror_log = $value['mirror']; | |
392 | break; | |
393 | case 'cache': | |
394 | if (! is_array($value)) { | |
395 | throw new Exception("log option missing disks array or mirror value"); | |
396 | } | |
397 | $this->cache = $value; | |
76e64919 | 398 | break; |
c6268644 MR |
399 | case 'spare': |
400 | if (! is_array($value)) { | |
401 | throw new Exception("log option missing disks array or mirror value"); | |
402 | } | |
403 | $this->spare = $value; | |
76e64919 MR |
404 | break; |
405 | default: | |
c6268644 | 406 | throw new Exception("$key: Not valid option"); |
76e64919 MR |
407 | } |
408 | } else { | |
409 | switch ($key) { | |
410 | case 'atime': | |
411 | case 'compress': | |
412 | case 'dedub': | |
413 | case 'sync': | |
414 | if ($value != 'on' && $value != 'off') { | |
415 | throw new Exception("$key: Value must be 'on' or 'off' found '$value'"); | |
416 | } | |
417 | break; | |
418 | case 'size': | |
419 | if (! preg_match('/^\d+(M|G)$/', $value)) { | |
420 | throw new Exception("$key: Value must have format '100M' or '100G' found '$value'"); | |
421 | } | |
422 | break; | |
423 | case 'type': | |
424 | if (OMVModulePoolType::OMVModulePoolType_TYPE_NONE > $value || | |
c6268644 | 425 | OMVModulePoolType::OMVModulePoolType_TYPE_RAIDZ3 < $value) { |
76e64919 MR |
426 | throw new Exception("$key: Value must be type of OMVPoolType found '$value'"); |
427 | } | |
428 | break; | |
429 | case 'vdevs': | |
430 | if (! preg_match('/^\d+$/', $value) || $value < 1) { | |
431 | throw new Exception("$key: Value must be positive int found '$value'"); | |
432 | } | |
433 | } | |
434 | } | |
435 | } | |
436 | } |