Project

General

Profile

Download (12.4 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * gmirror.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2016 Electric Sheep Fencing, LLC
7
 * All rights reserved.
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions are met:
11
 *
12
 * 1. Redistributions of source code must retain the above copyright notice,
13
 *    this list of conditions and the following disclaimer.
14
 *
15
 * 2. Redistributions in binary form must reproduce the above copyright
16
 *    notice, this list of conditions and the following disclaimer in
17
 *    the documentation and/or other materials provided with the
18
 *    distribution.
19
 *
20
 * 3. All advertising materials mentioning features or use of this software
21
 *    must display the following acknowledgment:
22
 *    "This product includes software developed by the pfSense Project
23
 *    for use in the pfSense® software distribution. (http://www.pfsense.org/).
24
 *
25
 * 4. The names "pfSense" and "pfSense Project" must not be used to
26
 *    endorse or promote products derived from this software without
27
 *    prior written permission. For written permission, please contact
28
 *    coreteam@pfsense.org.
29
 *
30
 * 5. Products derived from this software may not be called "pfSense"
31
 *    nor may "pfSense" appear in their names without prior written
32
 *    permission of the Electric Sheep Fencing, LLC.
33
 *
34
 * 6. Redistributions of any form whatsoever must retain the following
35
 *    acknowledgment:
36
 *
37
 * "This product includes software developed by the pfSense Project
38
 * for use in the pfSense software distribution (http://www.pfsense.org/).
39
 *
40
 * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
41
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
44
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51
 * OF THE POSSIBILITY OF SUCH DAMAGE.
52
 */
53

    
54
global $balance_methods;
55
$balance_methods = array("load", "prefer", "round-robin", "split");
56

    
57
/* Create a status array for each mirror and its disk components. */
58
function gmirror_get_status() {
59
	$status = "";
60
	exec("/sbin/gmirror status -s", $status);
61
	$mirrors = array();
62

    
63
	/* Empty output = no mirrors found */
64
	if (count($status) > 0) {
65
		/* Loop through gmirror status output. */
66
		foreach ($status as $line) {
67
			/* Split the line by whitespace */
68
			$all = preg_split("/[\s\t]+/", trim($line), 3);
69
			if (count($all) == 3) {
70
				/* If there are three items on a line, it is mirror name, status, and component */
71
				$currentmirror = basename($all[0]);
72
				$mirrors[$currentmirror]['name'] = basename($all[0]);
73
				$mirrors[$currentmirror]['status'] = $all[1];
74
				if (!is_array($mirrors[$currentmirror]['components'])) {
75
					$mirrors[$currentmirror]['components'] = array();
76
				}
77
				$mirrors[$currentmirror]['components'][] = $all[2];
78
			}
79
		}
80
	}
81
	/* Return an hash of mirrors and components */
82
	return $mirrors;
83
}
84

    
85
/* Get only status word for a single mirror. */
86
function gmirror_get_status_single($mirror) {
87
	$status = "";
88
	$mirror_status = gmirror_get_status();
89
	return $mirror_status[$mirror]['status'];
90
}
91

    
92
/* Generate an HTML formatted status for mirrors and disks in a small format for the widget */
93
function gmirror_html_status() {
94
	$mirrors = gmirror_get_status();
95
	$output = "";
96
	if (count($mirrors) < 1) {
97
		print_info_box(gettext("No mirrors found."), 'warning', false);
98
		return;
99
	}
100

    
101
?>
102
<table class="table table-striped table-hover">
103
	<thead>
104
		<tr>
105
			<th><?=gettext("Name")?></td>
106
			<th><?=gettext("Status")?></td>
107
			<th><?=gettext("Component")?></td>
108
		</tr>
109
	</thead>
110
	<tbody>
111
<?php foreach ($mirrors as $mirror => $name): ?>
112
		<tr>
113
			<td rowspan="<?=count($name["components"])?>"><?=$name['name']?></td>
114
			<td rowspan="<?=count($name["components"])?>"><?=$name['status']?></td>
115
			<td><?=$name['components'][0]?></td>
116
		</tr>
117
<?php if (count($name["components"]) > 1): ?>
118
		<?php foreach (array_slice($name["components"], 1) as $component): ?>
119
			<tr>
120
				<td><?=$component?></td>
121
			</tr>
122
		<?php endforeach; ?>
123
	<?php endif; ?>
124
<?php endforeach; ?>
125
	</tbody>
126
</table>
127
<?php
128
}
129

    
130
/* List all disks in the system (potential gmirror targets) */
131
function gmirror_get_disks() {
132
	$disklist = "";
133
	/* Get a list of disks in a scriptable way, exclude optical drives */
134
	exec("/sbin/geom disk status -s | /usr/bin/grep -v '[[:blank:]]*cd[[:digit:]]*' | /usr/bin/awk '{print $1;}'", $disklist);
135
	return $disklist;
136
}
137

    
138
/* List all potential gmirror consumers */
139
function gmirror_get_unused_consumers() {
140
	$consumerlist = "";
141
	$disklist = gmirror_get_disks();
142
	/* Get a list of consumers, exclude existing mirrors and diskid entries */
143
	exec("/sbin/geom part status -s | /usr/bin/egrep -v '(mirror|diskid)' | /usr/bin/awk '{print $1, $3;}'", $consumerlist);
144
	$all_consumers = array();
145
	foreach ($consumerlist as $cl) {
146
		$parts = explode(" ", $cl);
147
		foreach ($parts as $part) {
148
			$all_consumers[] = $part;
149
		}
150
	}
151
	foreach ($disklist as $d) {
152
		if (!is_consumer_used($d) && !in_array($d, $all_consumers)) {
153
			$all_consumers[] = $d;
154
		}
155
	}
156
	return $all_consumers;
157
}
158

    
159
/* List all existing geom mirrors */
160
function gmirror_get_mirrors() {
161
	$mirrorlist = "";
162
	exec("/sbin/gmirror list | /usr/bin/grep '^Geom name:' | /usr/bin/awk '{print $3;}'", $mirrorlist);
163
	return $mirrorlist;
164
}
165

    
166

    
167
/* List all consumers for a given mirror */
168
function gmirror_get_consumers_in_mirror($mirror) {
169
	if (!is_valid_mirror($mirror)) {
170
		return array();
171
	}
172

    
173
	$consumers = array();
174
	exec("/sbin/gmirror status -s " . escapeshellarg($mirror) . " | /usr/bin/awk '{print $3;}'", $consumers);
175
	return $consumers;
176
}
177

    
178
/* Test if a given consumer is a member of an existing mirror */
179
function is_consumer_in_mirror($consumer, $mirror) {
180
	if (!is_valid_consumer($consumer) || !is_valid_mirror($mirror)) {
181
		return false;
182
	}
183

    
184
	$mirrorconsumers = gmirror_get_consumers_in_mirror($mirror);
185
	return in_array(basename($consumer), $mirrorconsumers);
186
}
187

    
188
/* Test if a mirror exists */
189
function is_valid_mirror($mirror) {
190
	$mirrors = gmirror_get_mirrors();
191
	return in_array($mirror, $mirrors);
192
}
193

    
194
/* Test if a disk is valid/exists */
195
function is_valid_disk($disk) {
196
	$adisks = gmirror_get_disks();
197
	return in_array(basename($disk), $adisks);
198
}
199

    
200
/* Test if a consumer is valid and in use in a mirror */
201
function is_consumer_used($consumer) {
202
	$found = false;
203
	$mirrors = gmirror_get_mirrors();
204
	foreach ($mirrors as $mirror) {
205
		$consumers = gmirror_get_consumers_in_mirror($mirror);
206
		if (in_array($consumer, $consumers)) {
207
			return true;
208
		}
209
	}
210
	return false;
211
}
212

    
213
/* Test if a consumer is valid and not in use */
214
function is_consumer_unused($consumer) {
215
	$consumers = gmirror_get_unused_consumers();
216
	return in_array($consumer, $consumers);
217
}
218

    
219
/* Test if a consumer is valid (either a disk or partition) */
220
function is_valid_consumer($consumer) {
221
	return (is_consumer_unused($consumer) || is_consumer_used($consumer));
222
}
223

    
224
/* Remove all disconnected drives from a mirror */
225
function gmirror_forget_disconnected($mirror) {
226
	if (!is_valid_mirror($mirror)) {
227
		return false;
228
	}
229
	return mwexec("/sbin/gmirror forget " . escapeshellarg($mirror));
230
}
231

    
232
/* Insert another consumer into a mirror */
233
function gmirror_insert_consumer($mirror, $consumer) {
234
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer)) {
235
		return false;
236
	}
237
	return mwexec("/sbin/gmirror insert " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
238
}
239

    
240
/* Remove consumer from a mirror and clear its metadata */
241
function gmirror_remove_consumer($mirror, $consumer) {
242
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer)) {
243
		return false;
244
	}
245
	return mwexec("/sbin/gmirror remove " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
246
}
247

    
248
/* Wipe geom info from drive (if mirror is not running) */
249
function gmirror_clear_consumer($consumer) {
250
	if (!is_valid_consumer($consumer)) {
251
		return false;
252
	}
253
	return mwexec("/sbin/gmirror clear " . escapeshellarg($consumer));
254
}
255

    
256
/* Find the balance method used by a given mirror */
257
function gmirror_get_mirror_balance($mirror) {
258
	if (!is_valid_mirror($mirror)) {
259
		return false;
260
	}
261
	$balancemethod = "";
262
	exec("/sbin/gmirror list " . escapeshellarg($mirror) . " | /usr/bin/grep '^Balance:' | /usr/bin/awk '{print $2;}'", $balancemethod);
263
	return $balancemethod[0];
264
}
265

    
266
/* Change balance algorithm of the mirror */
267
function gmirror_configure_balance($mirror, $balancemethod) {
268
	global $balance_methods;
269
	if (!is_valid_mirror($mirror) || !in_array($balancemethod, $balance_methods)) {
270
		return false;
271
	}
272
	return mwexec("/sbin/gmirror configure -b " . escapeshellarg($balancemethod) . " " . escapeshellarg($mirror));
273
}
274

    
275
/* Force a mirror member to rebuild */
276
function gmirror_force_rebuild($mirror, $consumer) {
277
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer)) {
278
		return false;
279
	}
280
	return mwexec("/sbin/gmirror rebuild " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
281
}
282

    
283
/* Show all metadata on the physical consumer */
284
function gmirror_get_consumer_metadata($consumer) {
285
	if (!is_valid_consumer($consumer)) {
286
		return array();
287
	}
288
	$output = array();
289
	exec("/sbin/gmirror dump " . escapeshellarg($consumer), $output);
290
	return array_map('trim', $output);
291
}
292

    
293
/* Test if a consumer has metadata, indicating it is a member of a mirror (active or inactive) */
294
function gmirror_consumer_has_metadata($consumer) {
295
	return (count(gmirror_get_consumer_metadata($consumer)) > 0);
296
}
297

    
298
/* Find the mirror to which this consumer belongs */
299
function gmirror_get_consumer_metadata_mirror($consumer) {
300
	if (!is_valid_consumer($consumer)) {
301
		return array();
302
	}
303
	$metadata = gmirror_get_consumer_metadata($consumer);
304
	foreach ($metadata as $line) {
305
		if (substr($line, 0, 5) == "name:") {
306
			list ($key, $value) = explode(":", $line, 2);
307
			return trim($value);
308
		}
309
	}
310
}
311

    
312
/* Deactivate consumer, removing it from service in the mirror, but leave metadata intact */
313
function gmirror_deactivate_consumer($mirror, $consumer) {
314
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer)) {
315
		return false;
316
	}
317
	return mwexec("/sbin/gmirror deactivate " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
318
}
319

    
320
/* Reactivate a deactivated consumer */
321
function gmirror_activate_consumer($mirror, $consumer) {
322
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer)) {
323
		return false;
324
	}
325
	return mwexec("/sbin/gmirror activate " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
326
}
327

    
328
/* Find the size of the given mirror */
329
function gmirror_get_mirror_size($mirror) {
330
	if (!is_valid_mirror($mirror)) {
331
		return false;
332
	}
333
	$mirrorsize = "";
334
	exec("/sbin/gmirror list " . escapeshellarg($mirror) . " | /usr/bin/grep 'Mediasize:' | /usr/bin/head -n 1 | /usr/bin/awk '{print $2;}'", $mirrorsize);
335
	return $mirrorsize[0];
336
}
337

    
338
/* Return a list of all potential consumers on a disk with sizes. The geom part
339
	list output is a little odd, we can't get the output for just the disk, if the disk contains
340
	slices those get output also. */
341
function gmirror_get_all_unused_consumer_sizes_on_disk($disk) {
342
	if (!is_valid_disk($disk) || !is_consumer_unused($disk)) {
343
		return array();
344
	}
345
	$output = array();
346
	exec("/sbin/geom part list " . escapeshellarg($disk) . " | /usr/bin/egrep '(Name:|Mediasize:)' | /usr/bin/cut -c4- | /usr/bin/sed -l -e 'N;s/\\nMediasize://;P;D;' | /usr/bin/cut -c7-", $output);
347
	if (empty($output)) {
348
		exec("/sbin/geom disk list " . escapeshellarg($disk) . " | /usr/bin/egrep '(Name:|Mediasize:)' | /usr/bin/cut -c4- | /usr/bin/sed -l -e 'N;s/\\nMediasize://;P;D;' | /usr/bin/cut -c7-", $output);
349
	}
350
	$disk_contents = array();
351
	foreach ($output as $line) {
352
		list($name, $size, $humansize) = explode(" ", $line, 3);
353
		$consumer = array();
354
		$consumer['name'] = $name;
355
		$consumer['size'] = $size;
356
		$consumer['humansize'] = $humansize;
357
		$disk_contents[] = $consumer;
358
	}
359
	return $disk_contents;
360
}
361

    
362
/* Get only the size for one specific potential consumer. */
363
function gmirror_get_unused_consumer_size($consumer) {
364
	$consumersizes = gmirror_get_all_unused_consumer_sizes_on_disk($consumer);
365
	foreach ($consumersizes as $csize) {
366
		if ($csize['name'] == $consumer) {
367
			return $csize['size'];
368
		}
369
	}
370
	return -1;
371
}
372
?>
(22-22/65)