Project

General

Profile

« Previous | Next » 

Revision 52398a6b

Added by Jim Pingle about 11 years ago

Bring in proper gmirror support for the GUI and notifications.
Made a general gmirror library to perform various gmirror tasks and get information, using some of the former widget logic to start. Updated widget to use this new code.
Added a Diag > GEOM Mirrors page that displays information about existing mirrors and perform various management tasks. Current actions include rebuilding a drive, forgetting disconnected mirror drives, insert/remove, deactivate/activate, clearing medatada. It's now possible to use the GUI to rebuild a failed mirror by performing a forget, then insert action to replace a missing/dead drive.
Also included is a notification setup. Mirror status is polled every 60 seconds, and if any aspect of the mirror changes, notifications are issues that alert in the GUI and by SMTP, etc.

View differences:

etc/inc/gmirror.inc
1
<?php
2
/*
3
	gmirror.inc
4
	Copyright (C) 2009-2014 Jim Pingle
5

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

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

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

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

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

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

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

  
58
/* Get only status word for a single mirror. */
59
function gmirror_get_status_single($mirror) {
60
	$status = "";
61
	$mirror_status = gmirror_get_status();
62
	var_dump($mirror_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
	/* Get a list of consumers, exclude existing mirrors and diskid entries */
111
	exec("/sbin/geom part status -s | /usr/bin/egrep -v '(mirror|diskid)' | /usr/bin/awk '{print $1, $3;}'", $consumerlist);
112
	$all_consumers = array();
113
	foreach ($consumerlist as $cl) {
114
		$parts = explode(" ", $cl);
115
		foreach ($parts as $part)
116
			$all_consumers[] = $part;
117
	}
118
	return $all_consumers;
119
}
120

  
121
/* List all existing geom mirrors */
122
function gmirror_get_mirrors() {
123
	$mirrorlist = "";
124
	exec("/sbin/gmirror list | /usr/bin/grep '^Geom name:' | /usr/bin/awk '{print $3;}'", $mirrorlist);
125
	return $mirrorlist;
126
}
127

  
128

  
129
/* List all consumers for a given mirror */
130
function gmirror_get_consumers_in_mirror($mirror) {
131
	if (!is_valid_mirror($mirror))
132
		return array();
133

  
134
	$consumers = array();
135
	exec("/sbin/gmirror status -s " . escapeshellarg($mirror) . " | /usr/bin/awk '{print $3;}'", $consumers);
136
	return $consumers;
137
}
138

  
139
/* Test if a given consumer is a member of an existing mirror */
140
function is_consumer_in_mirror($consumer, $mirror) {
141
	if (!is_valid_consumer($consumer) || !is_valid_mirror($mirror))
142
		return false;
143

  
144
	$mirrorconsumers = gmirror_get_consumers_in_mirror($mirror);
145
	return in_array(basename($consumer), $mirrorconsumers);
146
}
147

  
148
/* Test if a mirror exists */
149
function is_valid_mirror($mirror) {
150
	$mirrors = gmirror_get_mirrors();
151
	return in_array($mirror, $mirrors);
152
}
153

  
154
/* Test if a disk is valid/exists */
155
function is_valid_disk($disk) {
156
	$adisks = gmirror_get_disks();
157
	return in_array(basename($disk), $adisks);
158
}
159

  
160
/* Test if a consumer is valid and in use in a mirror */
161
function is_consumer_used($consumer) {
162
	$found = false;
163
	$mirrors = gmirror_get_mirrors();
164
	foreach ($mirrors as $mirror) {
165
		$consumers = gmirror_get_consumers_in_mirror($mirror);
166
		if (in_array($consumer, $consumers))
167
			return true;
168
	}
169
	return false;
170
}
171

  
172
/* Test if a consumer is valid and not in use */
173
function is_consumer_unused($consumer) {
174
	$consumers = gmirror_get_unused_consumers();
175
	return in_array($consumer, $consumers);
176
}
177

  
178
/* Test if a consumer is valid (either a disk or partition) */
179
function is_valid_consumer($consumer) {
180
	return (is_consumer_unused($consumer) || is_consumer_used($consumer));
181
}
182

  
183
/* Remove all disconnected drives from a mirror */
184
function gmirror_forget_disconnected($mirror) {
185
	if (!is_valid_mirror($mirror))
186
		return false;
187
	return mwexec("/sbin/gmirror forget " . escapeshellarg($mirror));
188
}
189

  
190
/* Insert another consumer into a mirror */
191
function gmirror_insert_consumer($mirror, $consumer) {
192
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer))
193
		return false;
194
	return mwexec("/sbin/gmirror insert " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
195
}
196

  
197
/* Remove consumer from a mirror and clear its metadata */
198
function gmirror_remove_consumer($mirror, $consumer) {
199
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer))
200
		return false;
201
	return mwexec("/sbin/gmirror remove " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
202
}
203

  
204
/* Wipe geom info from drive (if mirror is not running) */
205
function gmirror_clear_consumer($consumer) {
206
	if (!is_valid_consumer($consumer))
207
		return false;
208
	return mwexec("/sbin/gmirror clear " . escapeshellarg($consumer));
209
}
210

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

  
220
/* Change balance algorithm of the mirror */
221
function gmirror_configure_balance($mirror, $balancemethod) {
222
	if (!is_valid_mirror($mirror) || !in_array($balancemethod, $balancemethods))
223
		return false;
224
	return mwexec("/sbin/gmirror configure -b " . escapeshellarg($balancemethod) . " " . escapeshellarg($mirror));
225
}
226

  
227
/* Force a mirror member to rebuild */
228
function gmirror_force_rebuild($mirror, $consumer) {
229
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer))
230
		return false;
231
	return mwexec("/sbin/gmirror rebuild " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
232
}
233

  
234
/* Show all metadata on the physical consumer */
235
function gmirror_get_consumer_metadata($consumer) {
236
	if (!is_valid_consumer($consumer))
237
		return array();
238
	$output = "";
239
	exec("/sbin/gmirror dump " . escapeshellarg($consumer), $output);
240
	return array_map('trim', $output);
241
}
242

  
243
/* Test if a consumer has metadata, indicating it is a member of a mirror (active or inactive) */
244
function gmirror_consumer_has_metadata($consumer) {
245
	return (count(gmirror_get_consumer_metadata($consumer)) > 0);
246
}
247

  
248
/* Find the mirror to which this consumer belongs */
249
function gmirror_get_consumer_metadata_mirror($consumer) {
250
	if (!is_valid_consumer($consumer))
251
		return array();
252
	$metadata = gmirror_get_consumer_metadata($consumer);
253
	foreach ($metadata as $line) {
254
		if (substr($line, 0, 5) == "name:") {
255
			list ($key, $value) = explode(":", $line, 2);
256
			return trim($value);
257
		}
258
	}
259
}
260

  
261
/* Deactivate consumer, removing it from service in the mirror, but leave metadata intact */
262
function gmirror_deactivate_consumer($mirror, $consumer) {
263
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer))
264
		return false;
265
	return mwexec("/sbin/gmirror deactivate " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
266
}
267

  
268
/* Reactivate a deactivated consumer */
269
function gmirror_activate_consumer($mirror, $consumer) {
270
	if (!is_valid_mirror($mirror) || !is_valid_consumer($consumer))
271
		return false;
272
	return mwexec("/sbin/gmirror activate " . escapeshellarg($mirror) . " " . escapeshellarg($consumer));
273
}
274

  
275
/* Find the size of the given mirror */
276
function gmirror_get_mirror_size($mirror) {
277
	if (!is_valid_mirror($mirror))
278
		return false;
279
	$mirrorsize = "";
280
	exec("/sbin/gmirror list " . escapeshellarg($mirror) . " | /usr/bin/grep 'Mediasize:' | /usr/bin/head -n 1 | /usr/bin/awk '{print $2;}'", $mirrorsize);
281
	return $mirrorsize[0];
282
}
283

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

  
304
/* Get only the size for one specific potential consumer. */
305
function gmirror_get_unused_consumer_size($consumer) {
306
	$consumersizes = gmirror_get_all_unused_consumer_sizes_on_disk($consumer);
307
	foreach ($consumersizes as $csize) {
308
		if ($csize['name'] == $consumer)
309
			return $csize['size'];
310
	}
311
	return -1;
312
}
313
?>
etc/rc
443 443

  
444 444
/bin/chmod a+rw /tmp/.
445 445

  
446
# Check for GEOM mirrors
447
GMIRROR_STATUS=`/sbin/gmirror status`
448
if [ "${GMIRROR_STATUS}" != "" ]; then
449
	# Using a flag file at bootup saves an expensive exec/check on each page load.
450
	/usr/bin/touch /var/run/gmirror_active
451
	# Setup monitoring/notifications
452
	/usr/local/bin/minicron 60 /var/run/gmirror_status_check.pid /usr/local/sbin/gmirror_status_check.php
453
fi
454

  
446 455
echo "Bootup complete"
447 456

  
448 457
/usr/local/bin/beep.sh start 2>&1 >/dev/null
usr/local/sbin/gmirror_status_check.php
1
#!/usr/local/bin/php -f
2
<?php
3
/*
4
	diag_gmirror.php
5
	Copyright (C) 2014 Jim Pingle
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
/*
30
	pfSense_BUILDER_BINARIES:	/sbin/gmirror	/sbin/geom	/usr/bin/grep	/usr/bin/egrep	/usr/bin/cut	/usr/bin/head
31
	pfSense_BUILDER_BINARIES:	/sbin/mount	/usr/bin/awk	/usr/bin/sed
32
	pfSense_MODULE:	gmirror
33
*/
34

  
35
require_once("config.inc");
36
require_once("notices.inc");
37
require_once("globals.inc");
38
require_once("gmirror.inc");
39

  
40
global $g;
41
$status_file = "{$g['varrun_path']}/gmirror.status";
42

  
43
$mirror_status = gmirror_get_status();
44
$mirror_list = array_keys($mirror_status);
45
sort($mirror_list);
46
$notices = array();
47

  
48
// Check for gmirror.status
49
if (file_exists($status_file)) {
50
	// If it exists, read status in
51
	$previous_mirror_status = unserialize(file_get_contents($status_file));
52
	$previous_mirror_list = array_keys($previous_mirror_status);
53
	sort($previous_mirror_list);
54
	if (count($previous_mirror_status) > 0) {
55
		// Check list of current mirrors vs old mirrors, notify if one has appeared/disappeared
56
		if ($mirror_list != $previous_mirror_list)
57
			$notices[] = sprintf(gettext("List of mirrors changed. Old: (%s) New: (%s)"), implode(", ", $previous_mirror_list), implode(", ", $mirror_list));
58

  
59
		// For each mirror, check the mirror status, notify if changed
60
		foreach ($mirror_list as $mirror) {
61
			if (is_array($previous_mirror_status[$mirror])) {
62
				// Notify if the status changed
63
				if ($mirror_status[$mirror]['status'] != $previous_mirror_status[$mirror]['status']) {
64
					$notices[] = sprintf(gettext("Mirror %s status changed from %s to %s."), $mirror, $previous_mirror_status[$mirror]['status'], $mirror_status[$mirror]['status']);
65
				}
66
				// Notify if the drive count changed
67
				if (count($mirror_status[$mirror]['components']) != count($previous_mirror_status[$mirror]['components'])) {
68
					// Notify if the consumer count changed.
69
					$notices[] = sprintf(gettext("Mirror %s consumer count changed from %d to %d."), $mirror, count($previous_mirror_status[$mirror]['components']), count($mirror_status[$mirror]['components']));
70
				}
71
				if (strtoupper($mirror_status[$mirror]['status']) == "DEGRADED") {
72
					// Check the drive status as it may be different.
73
					asort($mirror_status[$mirror]['components']);
74
					asort($previous_mirror_status[$mirror]['components']);
75
					if ($mirror_status[$mirror]['components'] != $previous_mirror_status[$mirror]['components'])
76
						$notices[] = sprintf(gettext("Mirror %s drive status changed. Old: (%s) New: (%s)"),
77
								$mirror,
78
								implode(", ", $previous_mirror_status[$mirror]['components']),
79
								implode(", ", $mirror_status[$mirror]['components']));
80
				}
81
			}
82
		}
83
	}
84
}
85
if (count($notices)) {
86
	file_notice("gmirror", implode("\n ", $notices), "GEOM Mirror Status Change", 1);
87
}
88
// Write out current status if changed
89
if ($mirror_status != $previous_mirror_status)
90
	file_put_contents($status_file, serialize($mirror_status));
91

  
92
?>
usr/local/www/diag_gmirror.php
1
<?php
2
/*
3
	diag_gmirror.php
4
	Copyright (C) 2014 Jim Pingle
5

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

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

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

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

  
28
/*
29
	pfSense_BUILDER_BINARIES:	/sbin/gmirror	/sbin/geom	/usr/bin/grep	/usr/bin/egrep	/usr/bin/cut	/usr/bin/head
30
	pfSense_BUILDER_BINARIES:	/sbin/mount	/usr/bin/awk	/usr/bin/sed
31
	pfSense_MODULE:	gmirror
32
*/
33

  
34
##|+PRIV
35
##|*IDENT=page-diagnostics-gmirror
36
##|*NAME=Diagnostics: GEOM Mirrors
37
##|*DESCR=Allow access to the 'Diagnostics: GEOM Mirrors' page.
38
##|*MATCH=diag_gmirror.php*
39
##|-PRIV
40

  
41
require_once("guiconfig.inc");
42
require_once("config.inc");
43
require_once("gmirror.inc");
44

  
45
$pgtitle = array(gettext("Diagnostics"), gettext("GEOM Mirrors"));
46

  
47
include("head.inc");
48

  
49
?>
50

  
51
<body link="#0000CC" vlink="#0000CC" alink="#0000CC" onload="<?=$jsevents["body"]["onload"];?>">
52

  
53
<?php include("fbegin.inc"); ?>
54

  
55
<?PHP
56
$action_list = array(
57
	"forget" => gettext("Forget all formerly connected consumers"),
58
	"clear" => gettext("Remove metadata from disk"),
59
	"insert" => gettext("Insert consumer into mirror"),
60
	"remove" => gettext("Remove consumer from mirror"),
61
	"activate" => gettext("Reactivate consumer on mirror"),
62
	"deactivate" => gettext("Deactivate consumer from mirror"),
63
	"rebuild" => gettext("Force rebuild of mirror consumer"),
64
);
65

  
66
/* User tried to pass a bogus action */
67
if (!empty($_REQUEST['action']) && !array_key_exists($_REQUEST['action'], $action_list)) {
68
	header("Location: diag_gmirror.php");
69
	return;
70
}
71

  
72
if ($_POST) {
73
	if (!isset($_POST['confirm']) || ($_POST['confirm'] != gettext("Confirm"))) {
74
		header("Location: diag_gmirror.php");
75
		return;
76
	}
77
	$input_errors = "";
78

  
79
	if (($_POST['action'] != "clear") && !is_valid_mirror($_POST['mirror']))
80
		$input_errors[] = gettext("You must supply a valid mirror name.");
81

  
82
	if (!empty($_POST['consumer']) && !is_valid_consumer($_POST['consumer']))
83
		$input_errors[] = gettext("You must supply a valid consumer name");
84

  
85
	/* Additional action-specific validation that hasn't already been tested */
86
	switch ($_POST['action']) {
87
		case "insert":
88
			if (!is_consumer_unused($_POST['consumer']))
89
				$input_errors[] = gettext("Consumer is already in use and cannot be inserted. Remove consumer from existing mirror first.");
90
			if (gmirror_consumer_has_metadata($_POST['consumer']))
91
				$input_errors[] = gettext("Consumer has metadata from an existing mirror. Clear metadata before inserting consumer.");
92
			$mstat = gmirror_get_status_single($_POST['mirror']);
93
			if (strtoupper($mstat) != "COMPLETE")
94
				$input_errors[] = gettext("Mirror is not in a COMPLETE state, cannot insert consumer. Forget disconnected disks or wait for rebuild to finish.");
95
			break;
96
		case "clear":
97
			if (!is_consumer_unused($_POST['consumer']))
98
				$input_errors[] = gettext("Consumer is in use and cannot be cleared. Deactivate disk first.");
99
			if (!gmirror_consumer_has_metadata($_POST['consumer']))
100
				$input_errors[] = gettext("Consumer has no metadata to clear.");
101
			break;
102
		case "activate":
103
			if (is_consumer_in_mirror($_POST['consumer'], $_POST['mirror']))
104
				$input_errors[] = gettext("Consumer is already present on specified mirror.");
105
			if (!gmirror_consumer_has_metadata($_POST['consumer']))
106
				$input_errors[] = gettext("Consumer has no metadata and cannot be reactivated.");
107
			
108
			break;
109
		case "remove":
110
		case "deactivate":
111
		case "rebuild":
112
			if (!is_consumer_in_mirror($_POST['consumer'], $_POST['mirror']))
113
				$input_errors[] = gettext("Consumer must be present on the specified mirror.");
114
			break;
115
	}
116

  
117
$result = 0;
118
	if (empty($input_errors)) {
119
		switch ($_POST['action']) {
120
			case "forget":
121
				$result = gmirror_forget_disconnected($_POST['mirror']);
122
				break;
123
			case "clear":
124
				$result = gmirror_clear_consumer($_POST['consumer']);
125
				break;
126
			case "insert":
127
				$result = gmirror_insert_consumer($_POST['mirror'], $_POST['consumer']);
128
				break;
129
			case "remove":
130
				$result = gmirror_remove_consumer($_POST['mirror'], $_POST['consumer']);
131
				break;
132
			case "activate":
133
				$result = gmirror_activate_consumer($_POST['mirror'], $_POST['consumer']);
134
				break;
135
			case "deactivate":
136
				$result = gmirror_deactivate_consumer($_POST['mirror'], $_POST['consumer']);
137
				break;
138
			case "rebuild":
139
				$result = gmirror_force_rebuild($_POST['mirror'], $_POST['consumer']);
140
				break;
141
		}
142
		$redir = "Location: diag_gmirror.php";
143
		if ($result != 0) {
144
			$redir .= "?error=" . urlencode($result);
145
		}
146
		/* If we reload the page too fast, the gmirror information may be missing or not up-to-date. */
147
		sleep(3);
148
		header($redir);
149
		return;
150
	}
151
}
152

  
153
$mirror_status = gmirror_get_status();
154
$mirror_list = gmirror_get_mirrors();
155
$unused_disks = gmirror_get_disks();
156
$unused_consumers = array();
157
foreach ($unused_disks as $disk) {
158
	if (is_consumer_unused($disk))
159
		$unused_consumers = array_merge($unused_consumers, gmirror_get_all_unused_consumer_sizes_on_disk($disk));
160
}
161

  
162
if ($input_errors)
163
	print_input_errors($input_errors);
164
if ($_GET["error"] && ($_GET["error"] != 0))
165
	print_info_box(gettext("There was an error performing the chosen mirror operation. Check the System Log for details."));
166

  
167
?>
168
<form action="diag_gmirror.php" method="POST" id="gmirror_form" name="gmirror_form">
169
<table width="100%" border="0" cellpadding="0" cellspacing="0">
170
	<tr>
171
		<td id="mainarea">
172
			<div class="tabcont">
173
				<span class="vexpl">
174
					<span class="red">
175
						<strong><?=gettext("NOTE:")?>&nbsp;</strong>
176
					</span>
177
					<?=gettext("The options on this page are intended for use by advanced users only. This page is for managing existing mirrors, not creating new mirrors.")?>
178
					<br />&nbsp;
179
				</span>
180
				<p/>
181
				<table width="100%" border="0" cellpadding="6" cellspacing="0">
182

  
183
<?PHP if ($_GET["action"]): ?>
184
					<tr>
185
						<td colspan="2" valign="top" class="listtopic"><?PHP echo gettext("Confirm Action"); ?></td>
186
					</tr>
187
					<tr>
188
						<td width="22%" valign="top" class="vncell">&nbsp;</td>
189
						<td width="78%" class="vtable">
190
							<strong><?PHP echo gettext("Please confirm the selected action"); ?></strong>:
191
							<br />
192
							<br /><strong><?PHP echo gettext("Action"); ?>:</strong> <?PHP echo $action_list[$_GET["action"]]; ?>
193
							<input type="hidden" name="action" value="<?PHP echo htmlspecialchars($_GET["action"]); ?>" />
194
						<?PHP if (!empty($_GET["mirror"])): ?>
195
							<br /><strong><?PHP echo gettext("Mirror"); ?>:</strong> <?PHP echo htmlspecialchars($_GET["mirror"]); ?>
196
							<input type="hidden" name="mirror" value="<?PHP echo htmlspecialchars($_GET["mirror"]); ?>" />
197
						<?PHP endif; ?>
198
						<?PHP if (!empty($_GET["consumer"])): ?>
199
							<br /><strong><?PHP echo gettext("Consumer"); ?>:</strong> <?PHP echo htmlspecialchars($_GET["consumer"]); ?>
200
							<input type="hidden" name="consumer" value="<?PHP echo htmlspecialchars($_GET["consumer"]); ?>" />
201
						<?PHP endif; ?>
202
							<br />
203
							<br /><input type="submit" name="confirm" value="<?PHP echo gettext("Confirm"); ?>" />
204
						</td>
205
					</tr>
206
<?PHP else: ?>
207
					<tr>
208
						<td colspan="2" valign="top" class="listtopic"><?PHP echo gettext("GEOM Mirror information"); ?></td>
209
					</tr>
210

  
211
					<tr>
212
						<td width="22%" valign="top" class="vncell"><?PHP echo gettext("Mirror Status"); ?></td>
213
						<td width="78%" class="vtable">
214

  
215
						<table width="100%" border="0" cellspacing="0" cellpadding="0" summary="gmirror status">
216
							<tbody id="gmirror_status_table">
217
					<?PHP	if (count($mirror_status) > 0): ?>
218
							<tr>
219
							<td width="30%" class="vncellt"><?PHP echo gettext("Name"); ?></td>
220
							<td width="30%" class="vncellt"><?PHP echo gettext("Status"); ?></td>
221
							<td width="40%" class="vncellt"><?PHP echo gettext("Component"); ?></td>
222
							</tr>
223
						<?PHP	foreach ($mirror_status as $mirror => $name): 
224
								$components = count($name["components"]); ?>
225
								<tr>
226
								<td width="30%" rowspan="<?PHP echo $components; ?>" class="listr">
227
									<?PHP echo htmlspecialchars($name['name']); ?>
228
									<br />Size: <?PHP echo gmirror_get_mirror_size($name['name']); ?>
229
								</td>
230
								<td width="30%" rowspan="<?PHP echo $components; ?>" class="listr">
231
									<?PHP echo htmlspecialchars($name['status']); ?>
232
								<?PHP	if (strtoupper($name['status']) == "DEGRADED"): ?>
233
									<br /><a href="diag_gmirror.php?action=forget&amp;mirror=<?PHP echo htmlspecialchars($name['name']); ?>">[<?PHP echo gettext("Forget Disconnected Disks"); ?>]</a>
234
								<?PHP	endif; ?>
235
								</td>
236
								<td width="40%" class="listr">
237
									<?PHP echo $name['components'][0]; ?>
238
									<?PHP list($cname, $cstatus) = explode(" ", $name['components'][0], 2); ?>
239
									<br />
240
								<?PHP	if ((strtoupper($name['status']) == "COMPLETE") && (count($name["components"]) > 1)): ?>
241
									<a href="diag_gmirror.php?action=rebuild&amp;consumer=<?PHP echo htmlspecialchars($cname); ?>&amp;mirror=<?PHP echo htmlspecialchars($name['name']); ?>">[<?PHP echo gettext("Rebuild"); ?>]</a>
242
									<a href="diag_gmirror.php?action=deactivate&amp;consumer=<?PHP echo htmlspecialchars($cname); ?>&amp;mirror=<?PHP echo htmlspecialchars($name['name']); ?>">[<?PHP echo gettext("Deactivate"); ?>]</a>
243
									<a href="diag_gmirror.php?action=remove&amp;consumer=<?PHP echo htmlspecialchars($cname); ?>&amp;mirror=<?PHP echo htmlspecialchars($name['name']); ?>">[<?PHP echo gettext("Remove"); ?>]</a>
244
								<?PHP	endif; ?>
245
								</td>
246
								</tr>
247
							<?PHP	if (count($name["components"]) > 1):
248
									$morecomponents = array_slice($name["components"], 1); ?>
249
								<?PHP	foreach ($morecomponents as $component): ?>
250
										<tr>
251
										<td width="40%" class="listr">
252
											<?PHP echo $component; ?>
253
											<?PHP list($cname, $cstatus) = explode(" ", $component, 2); ?>
254
											<br />
255
										<?PHP	if ((strtoupper($name['status']) == "COMPLETE") && (count($name["components"]) > 1)): ?>
256
											<a href="diag_gmirror.php?action=rebuild&amp;consumer=<?PHP echo htmlspecialchars($cname); ?>&amp;mirror=<?PHP echo htmlspecialchars($name['name']); ?>">[<?PHP echo gettext("Rebuild"); ?>]</a>
257
											<a href="diag_gmirror.php?action=deactivate&amp;consumer=<?PHP echo htmlspecialchars($cname); ?>&amp;mirror=<?PHP echo htmlspecialchars($name['name']); ?>">[<?PHP echo gettext("Deactivate"); ?>]</a>
258
											<a href="diag_gmirror.php?action=remove&amp;consumer=<?PHP echo htmlspecialchars($cname); ?>&amp;mirror=<?PHP echo htmlspecialchars($name['name']); ?>">[<?PHP echo gettext("Remove"); ?>]</a>
259
										<?PHP	endif; ?>
260
										</td>
261
										</tr>
262
								<?PHP	endforeach; ?>
263
							<?PHP	endif; ?>
264
						<?PHP	endforeach; ?>
265
					<?PHP	else: ?>
266
							<tr><td colspan="3" class="listr"><?PHP echo gettext("No Mirrors Found"); ?></td></tr>
267
					<?PHP	endif; ?>
268
							</tbody>
269
						</table>
270
						<br /><?PHP echo gettext("Some disk operations may only be performed when there are multiple consumers present in a mirror."); ?>
271
						</td>
272
					</tr>
273

  
274
					<tr>
275
						<td colspan="2" valign="top" class="listtopic"><?PHP echo gettext("Consumer information"); ?></td>
276
					</tr>
277

  
278
					<tr>
279
						<td width="22%" valign="top" class="vncell"><?PHP echo gettext("Available Consumers"); ?></td>
280
						<td width="78%" class="vtable">
281

  
282
						<table width="100%" border="0" cellspacing="0" cellpadding="0" summary="consumer list">
283
							<tbody id="consumer_list">
284
					<?PHP	if (count($unused_consumers) > 0): ?>
285
							<tr>
286
							<td width="30%" class="vncellt"><?PHP echo gettext("Name"); ?></td>
287
							<td width="30%" class="vncellt"><?PHP echo gettext("Size"); ?></td>
288
							<td width="40%" class="vncellt"><?PHP echo gettext("Add to Mirror"); ?></td>
289
							</tr>
290
						<?PHP	foreach ($unused_consumers as $consumer): ?>
291
								<tr>
292
								<td width="30%" class="listr">
293
									<?PHP echo htmlspecialchars($consumer['name']); ?>
294
								</td>
295
								<td width="30%" class="listr"><?PHP echo htmlspecialchars($consumer['size']); ?> <?PHP echo htmlspecialchars($consumer['humansize']); ?></td>
296
								<td width="40%" class="listr">
297
							<?PHP	$oldmirror = gmirror_get_consumer_metadata_mirror($consumer['name']);
298
								if ($oldmirror): ?>
299
									<a href="diag_gmirror.php?action=activate&amp;consumer=<?PHP echo htmlspecialchars($consumer['name']); ?>&amp;mirror=<?PHP echo htmlspecialchars($oldmirror); ?>">[<?PHP echo gettext("Reactivate on:"); ?> <?PHP echo htmlspecialchars($oldmirror); ?>]</a>
300
									<br /><a href="diag_gmirror.php?action=clear&amp;consumer=<?PHP echo htmlspecialchars($consumer['name']); ?>">[<?PHP echo gettext("Remove metadata from disk"); ?>]</a>
301
							<?PHP	else: ?>
302
							<?PHP	foreach ($mirror_list as $mirror):
303
									$mirror_size = gmirror_get_mirror_size($mirror);
304
									$consumer_size = gmirror_get_unused_consumer_size($consumer['name']);
305
								?>
306
								<?PHP	if ($consumer_size > $mirror_size): ?>
307
									<a href="diag_gmirror.php?action=insert&amp;consumer=<?PHP echo htmlspecialchars($consumer['name']); ?>&amp;mirror=<?PHP echo htmlspecialchars($mirror); ?>"><?PHP echo htmlspecialchars($mirror); ?></a>
308
								<?PHP	endif; ?>
309
							<?PHP	endforeach; ?>
310
							<?PHP	endif; ?>
311
								</td>
312
								</tr>
313
						<?PHP	endforeach; ?>
314
					<?PHP	else: ?>
315
							<tr><td colspan="3" class="listr"><?PHP echo gettext("No unused consumers found"); ?></td></tr>
316
					<?PHP	endif; ?>
317
							</tbody>
318
						</table>
319
						<br /><?PHP echo gettext("Consumers may only be added to a mirror if they are larger than the size of the mirror."); ?>
320
						</td>
321
					</tr>
322

  
323
					<tr>
324
						<td colspan="2" valign="top" class="">&nbsp;</td>
325
					</tr>
326
<?PHP endif;?>
327
				</table>
328
			</div>
329
		</td>
330
	</tr>
331
</table>
332
</form>
333
<?php require("fend.inc"); ?>
334
</body>
335
</html>
336

  
337
<?php
338

  
339
// Clear the loading indicator
340
echo "<script type=\"text/javascript\">";
341
echo "jQuery('#loading').html('');";
342
echo "</script>";
343

  
344
?>
usr/local/www/fbegin.inc
210 210
$diagnostics_menu[] = array(gettext("DNS Lookup"), "/diag_dns.php");
211 211
$diagnostics_menu[] = array(gettext("Edit File"), "/edit.php");
212 212
$diagnostics_menu[] = array(gettext("Factory Defaults"), "/diag_defaults.php");
213

  
214
if(file_exists("/var/run/gmirror_active"))
215
	$diagnostics_menu[] = array(gettext("GEOM Mirrors"), "/diag_gmirror.php" );
216

  
213 217
$diagnostics_menu[] = array(gettext("Halt System"), "/halt.php" );
214 218
$diagnostics_menu[] = array(gettext("Limiter Info"), "/diag_limiter_info.php" );
215 219
$diagnostics_menu[] = array(gettext("NDP Table"), "/diag_ndp.php" );
usr/local/www/widgets/include/gmirror_status.inc
1
<?php
2
/*
3
    gmirror_status.widget.php
4
    Copyright (C) 2009-2010 Jim Pingle
5

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

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

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

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

  
28
function gmirror_get_status() {
29
	$status = "";
30
	exec("/sbin/gmirror status -s", $status);
31
	$mirrors = array();
32

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

  
54
function gmirror_html_status() {
55
	$mirrors = gmirror_get_status();
56
	$output = "";
57
	if (count($mirrors) > 0) {
58
		$output .= "<tr>\n";
59
		$output .= "<td width=\"40%\" class=\"vncellt\">Name</td>\n";
60
		$output .= "<td width=\"40%\" class=\"vncellt\">Status</td>\n";
61
		$output .= "<td width=\"20%\" class=\"vncellt\">Component</td>\n";
62
		$output .= "</tr>\n";
63
		foreach ($mirrors as $mirror => $name) {
64
			$components = count($name["components"]);
65
			$output .= "<tr>\n";
66
			$output .= "<td width=\"40%\" rowspan=\"{$components}\" class=\"listr\">{$name['name']}</td>\n";
67
			$output .= "<td width=\"40%\" rowspan=\"{$components}\" class=\"listr\">{$name['status']}</td>\n";
68
			$output .= "<td width=\"20%\" class=\"listr\">{$name['components'][0]}</td>\n";
69
			$output .= "</tr>\n";
70
			if (count($name["components"]) > 1) {
71
				$morecomponents = array_slice($name["components"], 1);
72
				foreach ($morecomponents as $component) {
73
					$output .= "<tr>\n";
74
					$output .= "<td width=\"20%\" class=\"listr\">{$component}</td>\n";
75
					$output .= "</tr>\n";
76
				}
77
			}
78
		}
79
	} else {
80
		$output .= "<tr><td colspan=\"3\" class=\"listr\">No Mirrors Found</td></tr>\n";
81
	}
82
	// $output .= "<tr><td colspan=\"3\" class=\"listr\">Updated at " . date("F j, Y, g:i:s a") . "</td></tr>\n";
83
	return $output;
84
}
85
?>
usr/local/www/widgets/widgets/gmirror_status.widget.php
28 28
$nocsrf = true;
29 29

  
30 30
require_once("guiconfig.inc");
31
require_once("/usr/local/www/widgets/include/gmirror_status.inc");
31
require_once("gmirror.inc");
32 32

  
33 33
if ($_GET['textonly'] == "true") {
34 34
	header("Cache-Control: no-cache");

Also available in: Unified diff