Project

General

Profile

Download (11.6 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	gmirror.inc
4
	Copyright (C) 2009-2014 Jim Pingle
5
	Copyright (C) 2013-2014 Electric Sheep Fencing, LP
6

    
7
	Redistribution and use in source and binary forms, with or without
8
	modification, are permitted provided that the following conditions are met:
9

    
10
	1. Redistributions of source code must retain the above copyright notice,
11
	this list of conditions and the following disclaimer.
12

    
13
	2. Redistributions in binary form must reproduce the above copyright
14
	notice, this list of conditions and the following disclaimer in the
15
	documentation and/or other materials provided with the distribution.
16

    
17
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
18
	INClUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
21
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
	POSSIBILITY OF SUCH DAMAGE.
27
*/
28

    
29
global $balance_methods;
30
$balance_methods = array("load", "prefer", "round-robin", "split");
31

    
32
/* Create a status array for each mirror and its disk components. */
33
function gmirror_get_status() {
34
	$status = "";
35
	exec("/sbin/gmirror status -s", $status);
36
	$mirrors = array();
37

    
38
	/* Empty output = no mirrors found */
39
	if (count($status) > 0) {
40
		/* Loop through gmirror status output. */
41
		foreach ($status as $line) {
42
			/* Split the line by whitespace */
43
			$all = preg_split("/[\s\t]+/", trim($line), 3);
44
			if (count($all) == 3) {
45
				/* If there are three items on a line, it is mirror name, status, and component */
46
				$currentmirror = basename($all[0]);
47
				$mirrors[$currentmirror]['name'] = basename($all[0]);
48
				$mirrors[$currentmirror]['status'] = $all[1];
49
				if (!is_array($mirrors[$currentmirror]['components']))
50
					$mirrors[$currentmirror]['components'] = array();
51
				$mirrors[$currentmirror]['components'][] = $all[2];
52
			}
53
		}
54
	}
55
	/* Return an hash of mirrors and components */	
56
	return $mirrors;
57
}
58

    
59
/* Get only status word for a single mirror. */
60
function gmirror_get_status_single($mirror) {
61
	$status = "";
62
	$mirror_status = gmirror_get_status();
63
	return $mirror_status[$mirror]['status'];
64
}
65

    
66
/* Generate an HTML formatted status for mirrors and disks in a small format for the widget */
67
function gmirror_html_status() {
68
	$mirrors = gmirror_get_status();
69
	$output = "";
70
	if (count($mirrors) > 0) {
71
		$output .= "<tr>\n";
72
		$output .= "<td width=\"40%\" class=\"vncellt\">Name</td>\n";
73
		$output .= "<td width=\"40%\" class=\"vncellt\">Status</td>\n";
74
		$output .= "<td width=\"20%\" class=\"vncellt\">Component</td>\n";
75
		$output .= "</tr>\n";
76
		foreach ($mirrors as $mirror => $name) {
77
			$components = count($name["components"]);
78
			$output .= "<tr>\n";
79
			$output .= "<td width=\"40%\" rowspan=\"{$components}\" class=\"listr\">{$name['name']}</td>\n";
80
			$output .= "<td width=\"40%\" rowspan=\"{$components}\" class=\"listr\">{$name['status']}</td>\n";
81
			$output .= "<td width=\"20%\" class=\"listr\">{$name['components'][0]}</td>\n";
82
			$output .= "</tr>\n";
83
			if (count($name["components"]) > 1) {
84
				$morecomponents = array_slice($name["components"], 1);
85
				foreach ($morecomponents as $component) {
86
					$output .= "<tr>\n";
87
					$output .= "<td width=\"20%\" class=\"listr\">{$component}</td>\n";
88
					$output .= "</tr>\n";
89
				}
90
			}
91
		}
92
	} else {
93
		$output .= "<tr><td colspan=\"3\" class=\"listr\">No Mirrors Found</td></tr>\n";
94
	}
95
	// $output .= "<tr><td colspan=\"3\" class=\"listr\">Updated at " . date("F j, Y, g:i:s a") . "</td></tr>\n";
96
	return $output;
97
}
98

    
99
/* List all disks in the system (potential gmirror targets) */
100
function gmirror_get_disks() {
101
	$disklist = "";
102
	/* Get a list of disks in a scriptable way, exclude optical drives */
103
	exec("/sbin/geom disk status -s | /usr/bin/grep -v '[[:blank:]]*cd[[:digit:]]*' | /usr/bin/awk '{print $1;}'", $disklist);
104
	return $disklist;
105
}
106

    
107
/* List all potential gmirror consumers */
108
function gmirror_get_unused_consumers() {
109
	$consumerlist = "";
110
	$disklist = gmirror_get_disks();
111
	/* Get a list of consumers, exclude existing mirrors and diskid entries */
112
	exec("/sbin/geom part status -s | /usr/bin/egrep -v '(mirror|diskid)' | /usr/bin/awk '{print $1, $3;}'", $consumerlist);
113
	$all_consumers = array();
114
	foreach ($consumerlist as $cl) {
115
		$parts = explode(" ", $cl);
116
		foreach ($parts as $part)
117
			$all_consumers[] = $part;
118
	}
119
	foreach ($disklist as $d) {
120
		if (!is_consumer_used($d) && !in_array($d, $all_consumers)) {
121
			$all_consumers[] = $d;
122
		}
123
	}
124
	return $all_consumers;
125
}
126

    
127
/* List all existing geom mirrors */
128
function gmirror_get_mirrors() {
129
	$mirrorlist = "";
130
	exec("/sbin/gmirror list | /usr/bin/grep '^Geom name:' | /usr/bin/awk '{print $3;}'", $mirrorlist);
131
	return $mirrorlist;
132
}
133

    
134

    
135
/* List all consumers for a given mirror */
136
function gmirror_get_consumers_in_mirror($mirror) {
137
	if (!is_valid_mirror($mirror))
138
		return array();
139

    
140
	$consumers = array();
141
	exec("/sbin/gmirror status -s " . escapeshellarg($mirror) . " | /usr/bin/awk '{print $3;}'", $consumers);
142
	return $consumers;
143
}
144

    
145
/* Test if a given consumer is a member of an existing mirror */
146
function is_consumer_in_mirror($consumer, $mirror) {
147
	if (!is_valid_consumer($consumer) || !is_valid_mirror($mirror))
148
		return false;
149

    
150
	$mirrorconsumers = gmirror_get_consumers_in_mirror($mirror);
151
	return in_array(basename($consumer), $mirrorconsumers);
152
}
153

    
154
/* Test if a mirror exists */
155
function is_valid_mirror($mirror) {
156
	$mirrors = gmirror_get_mirrors();
157
	return in_array($mirror, $mirrors);
158
}
159

    
160
/* Test if a disk is valid/exists */
161
function is_valid_disk($disk) {
162
	$adisks = gmirror_get_disks();
163
	return in_array(basename($disk), $adisks);
164
}
165

    
166
/* Test if a consumer is valid and in use in a mirror */
167
function is_consumer_used($consumer) {
168
	$found = false;
169
	$mirrors = gmirror_get_mirrors();
170
	foreach ($mirrors as $mirror) {
171
		$consumers = gmirror_get_consumers_in_mirror($mirror);
172
		if (in_array($consumer, $consumers))
173
			return true;
174
	}
175
	return false;
176
}
177

    
178
/* Test if a consumer is valid and not in use */
179
function is_consumer_unused($consumer) {
180
	$consumers = gmirror_get_unused_consumers();
181
	return in_array($consumer, $consumers);
182
}
183

    
184
/* Test if a consumer is valid (either a disk or partition) */
185
function is_valid_consumer($consumer) {
186
	return (is_consumer_unused($consumer) || is_consumer_used($consumer));
187
}
188

    
189
/* Remove all disconnected drives from a mirror */
190
function gmirror_forget_disconnected($mirror) {
191
	if (!is_valid_mirror($mirror))
192
		return false;
193
	return mwexec("/sbin/gmirror forget " . escapeshellarg($mirror));
194
}
195

    
196
/* Insert another consumer into a mirror */
197
function gmirror_insert_consumer($mirror, $consumer) {
198
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer))
199
		return false;
200
	return mwexec("/sbin/gmirror insert " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
201
}
202

    
203
/* Remove consumer from a mirror and clear its metadata */
204
function gmirror_remove_consumer($mirror, $consumer) {
205
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer))
206
		return false;
207
	return mwexec("/sbin/gmirror remove " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
208
}
209

    
210
/* Wipe geom info from drive (if mirror is not running) */
211
function gmirror_clear_consumer($consumer) {
212
	if (!is_valid_consumer($consumer))
213
		return false;
214
	return mwexec("/sbin/gmirror clear " . escapeshellarg($consumer));
215
}
216

    
217
/* Find the balance method used by a given mirror */
218
function gmirror_get_mirror_balance($mirror) {
219
	if (!is_valid_mirror($mirror))
220
		return false;
221
	$balancemethod = "";
222
	exec("/sbin/gmirror list " . escapeshellarg($mirror) . " | /usr/bin/grep '^Balance:' | /usr/bin/awk '{print $2;}'", $balancemethod);
223
	return $balancemethod[0];
224
}
225

    
226
/* Change balance algorithm of the mirror */
227
function gmirror_configure_balance($mirror, $balancemethod) {
228
	global $balance_methods;
229
	if (!is_valid_mirror($mirror) || !in_array($balancemethod, $balance_methods))
230
		return false;
231
	return mwexec("/sbin/gmirror configure -b " . escapeshellarg($balancemethod) . " " . escapeshellarg($mirror));
232
}
233

    
234
/* Force a mirror member to rebuild */
235
function gmirror_force_rebuild($mirror, $consumer) {
236
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer))
237
		return false;
238
	return mwexec("/sbin/gmirror rebuild " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
239
}
240

    
241
/* Show all metadata on the physical consumer */
242
function gmirror_get_consumer_metadata($consumer) {
243
	if (!is_valid_consumer($consumer))
244
		return array();
245
	$output = "";
246
	exec("/sbin/gmirror dump " . escapeshellarg($consumer), $output);
247
	return array_map('trim', $output);
248
}
249

    
250
/* Test if a consumer has metadata, indicating it is a member of a mirror (active or inactive) */
251
function gmirror_consumer_has_metadata($consumer) {
252
	return (count(gmirror_get_consumer_metadata($consumer)) > 0);
253
}
254

    
255
/* Find the mirror to which this consumer belongs */
256
function gmirror_get_consumer_metadata_mirror($consumer) {
257
	if (!is_valid_consumer($consumer))
258
		return array();
259
	$metadata = gmirror_get_consumer_metadata($consumer);
260
	foreach ($metadata as $line) {
261
		if (substr($line, 0, 5) == "name:") {
262
			list ($key, $value) = explode(":", $line, 2);
263
			return trim($value);
264
		}
265
	}
266
}
267

    
268
/* Deactivate consumer, removing it from service in the mirror, but leave metadata intact */
269
function gmirror_deactivate_consumer($mirror, $consumer) {
270
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer))
271
		return false;
272
	return mwexec("/sbin/gmirror deactivate " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
273
}
274

    
275
/* Reactivate a deactivated consumer */
276
function gmirror_activate_consumer($mirror, $consumer) {
277
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer))
278
		return false;
279
	return mwexec("/sbin/gmirror activate " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
280
}
281

    
282
/* Find the size of the given mirror */
283
function gmirror_get_mirror_size($mirror) {
284
	if (!is_valid_mirror($mirror))
285
		return false;
286
	$mirrorsize = "";
287
	exec("/sbin/gmirror list " . escapeshellarg($mirror) . " | /usr/bin/grep 'Mediasize:' | /usr/bin/head -n 1 | /usr/bin/awk '{print $2;}'", $mirrorsize);
288
	return $mirrorsize[0];
289
}
290

    
291
/* Return a list of all potential consumers on a disk with sizes. The geom part
292
	list output is a little odd, we can't get the output for just the disk, if the disk contains
293
	slices those get output also. */
294
function gmirror_get_all_unused_consumer_sizes_on_disk($disk) {
295
	if (!is_valid_disk($disk) || !is_consumer_unused($disk))
296
		return array();
297
	$output = "";
298
	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);
299
	if (empty($output)) {
300
		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);
301
	}
302
	$disk_contents = array();
303
	foreach ($output as $line) {
304
		list($name, $size, $humansize) = explode(" ", $line, 3);
305
		$consumer = array();
306
		$consumer['name'] = $name;
307
		$consumer['size'] = $size;
308
		$consumer['humansize'] = $humansize;
309
		$disk_contents[] = $consumer;
310
	}
311
	return $disk_contents;
312
}
313

    
314
/* Get only the size for one specific potential consumer. */
315
function gmirror_get_unused_consumer_size($consumer) {
316
	$consumersizes = gmirror_get_all_unused_consumer_sizes_on_disk($consumer);
317
	foreach ($consumersizes as $csize) {
318
		if ($csize['name'] == $consumer)
319
			return $csize['size'];
320
	}
321
	return -1;
322
}
323
?>
(23-23/68)