~ubuntu-branches/ubuntu/quantal/mdcfg/quantal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
#!/bin/sh

. /usr/share/debconf/confmodule

# Translate device from /proc/mdstat (mdX) to device node
md_devnode() {
	local num=$(echo $1 | sed -e "s/^md//")

	# Also handle the case where the first does not exist
	if [ -b /dev/md/$num ]; then
		echo /dev/md/$num
	elif [ -b /dev/md$num ]; then
		echo /dev/md$num
	else
		return 1
	fi
}

md_get_level() {
	echo $(mdadm -Q --detail $1 | grep "Raid Level" | sed "s/.*: //")
}

md_get_devices() {
	DEVICES=""
	for DEVICE in $(grep ^md /proc/mdstat | \
		   sed -e 's/^\(md.*\) : .*/\1/'); do
		MDDEV=$(md_devnode $DEVICE) || return 1
		TYPE=$(md_get_level $MDDEV)
		DEVICES="${DEVICES:+$DEVICES, }${DEVICE}_$TYPE"
	done
}

md_delete_verify() {
	DEVICE=$(echo "$1" | sed -e "s/^\(md.*\)_.*/\1/")
	DEVICES=$(grep "^$DEVICE[ :]" /proc/mdstat | \
		sed -e "s/^.*active \(.*\)/\1/; s/raid[0-9]* //")
	MDDEV=$(md_devnode $DEVICE) || return 1
	TYPE=$(md_get_level $MDDEV)

	db_set mdcfg/deleteverify false
	db_subst mdcfg/deleteverify DEVICE "/dev/$DEVICE"
	db_subst mdcfg/deleteverify TYPE "$TYPE"
	db_subst mdcfg/deleteverify DEVICES "$DEVICES"
	db_input critical mdcfg/deleteverify
	db_go
	db_get mdcfg/deleteverify

	case $RET in
	    true)
		# Stop the MD device and zero the superblock
		# of all the component devices
		DEVICES=$(mdadm -Q --detail $MDDEV | \
			  grep -E "^[[:space:]]*[0-9].*(active|spare)" | \
			  sed -e 's/.* //')
		logger -t mdcfg "Removing $MDDEV ($DEVICES)"
		log-output -t mdcfg mdadm --stop $MDDEV || return 1
		for DEV in "$DEVICES"; do
			log-output -t mdcfg \
				mdadm --zero-superblock --force $DEV || return 1
		done
		;;
	esac
	return 0
}

md_delete() {
	md_get_devices
	if [ -z "$DEVICES" ]; then
		db_set mdcfg/delete_no_md false
		db_input high mdcfg/delete_no_md
		db_go
		return
	fi
	db_set mdcfg/deletemenu false
	db_subst mdcfg/deletemenu DEVICES "$DEVICES"
	db_input critical mdcfg/deletemenu
	db_go
	db_get mdcfg/deletemenu

	case $RET in
	    md*)
		if ! md_delete_verify $RET; then
			db_input critical mdcfg/deletefailed
			db_go
		fi
		;;
	esac
}

md_createmain() {
	db_set mdcfg/createmain false
	db_input critical mdcfg/createmain
	db_go
	if [ $? -ne 30 ]; then
		db_get mdcfg/createmain
		if [ "$RET" = Cancel ]; then
			return
		fi
		RAID_SEL="$RET"

		if ! get_partitions; then
			return
		fi

		case "$RAID_SEL" in
		    RAID10|RAID6|RAID5|RAID1)
			md_create_array "$RAID_SEL" ;;
		    RAID0)
			md_create_raid0 ;;
		    *)
			return 1 ;;
		esac
	fi
}

# This will set PARTITIONS and NUM_PART global variables
get_partitions() {
	PARTITIONS=""

	# Get a list of RAID partitions. This only works if there is no
	# filesystem on the partitions, which is fine by us.
	RAW_PARTITIONS=$(/usr/lib/partconf/find-partitions --ignore-fstype 2>/dev/null | \
		grep "[[:space:]]RAID[[:space:]]" | cut -f1)

	# Convert it into a proper list form for a select question
	# (comma separated)
	NUM_PART=0
	for i in $RAW_PARTITIONS; do
		DEV=$(echo $i | sed -e "s/\/dev\///")
		REALDEV=$(mapdevfs "$i")
		MAPPEDDEV=$(echo "$REALDEV" | sed -e "s/\/dev\///")

		if grep -Eq "($DEV|$MAPPEDDEV)" /proc/mdstat; then
			continue
		fi

		PARTITIONS="${PARTITIONS:+$PARTITIONS, }$REALDEV"
		NUM_PART=$(($NUM_PART + 1))
	done

	if [ -z "$PARTITIONS" ]; then
		db_input critical mdcfg/noparts
		db_go
		return 1
	fi
	return 0
}

prune_partitions() {
	CHOSEN="$1"
	OLDIFS="$IFS"
	IFS=,
	NEW_PARTITIONS=""
	for i in $PARTITIONS; do
		found=0
		for j in $CHOSEN; do
			if [ "$i" = "$j" ]; then
				found=1
			fi
		done
		if [ $found -eq 0 ]; then
			NEW_PARTITIONS="${NEW_PARTITIONS:+$NEW_PARTITIONS,}$i"
		fi
	done
	IFS=$OLDIFS
	PARTITIONS=$NEW_PARTITIONS
}

md_create_raid0() {
	db_subst mdcfg/raid0devs PARTITIONS "$PARTITIONS"
	db_set mdcfg/raid0devs ""
	db_input critical mdcfg/raid0devs
	db_go

	if [ $? -eq 30 ]; then return; fi

	db_get mdcfg/raid0devs
	SELECTED=0
	for i in $RET; do
		let SELECTED++
	done

	prune_partitions "$RET"

	MD_NUM=$(grep ^md /proc/mdstat | \
		 sed -e 's/^md\(.*\) : active .*/\1/' | sort | tail -n1)

	if [ -z "$MD_NUM" ]; then
		MD_NUM=0
	else
		let MD_NUM++
	fi

	logger -t mdcfg "Number of devices in the RAID0 array md$MD_NUM: $SELECTED"

	RAID_DEVICES="$(echo $RET | sed -e 's/,//g')"
	log-output -t mdcfg \
		mdadm --create /dev/md$MD_NUM --auto=yes --force -R -l raid0 \
		      -n $SELECTED $RAID_DEVICES
}

md_create_array(){
	OK=0

	case "$1" in
	    RAID1)
		MIN_SIZE=2 ;;
	    RAID5)
		MIN_SIZE=3 ;;
	    RAID6)
		MIN_SIZE=4 ;;
	    RAID10)
		MIN_SIZE=2 ;;
	    *)
		return ;;
	esac

	LEVEL=${1#RAID}

	for i in devcount devs sparecount sparedevs; do
		db_subst mdcfg/raid$i LEVEL "$LEVEL"
	done
	db_subst mdcfg/raiddevcount MINIMUM "$MIN_SIZE"

	db_set mdcfg/raiddevcount "$MIN_SIZE"

	# Get the count of active devices
	while [ $OK -eq 0 ]; do
		db_input critical mdcfg/raiddevcount
		db_go
		if [ $? -eq 30 ]; then
			return
		fi

		# Figure out, if the user entered a number
		db_get mdcfg/raiddevcount
		RET=$(echo $RET | sed -e "s/[[:space:]]//g")
		if [ "$RET" ]; then
			let "OK=${RET}>0 && ${RET}<99"
		fi
	done


	db_set mdcfg/raidsparecount "0"
	OK=0

	# Same procedure as above, but get the number of spare partitions
	# this time.
	# TODO: Make a general function for this kind of stuff
	while [ $OK -eq 0 ]; do
		db_input critical mdcfg/raidsparecount
		db_go
		if [ $? -eq 30 ]; then
			return
		fi
		db_get mdcfg/raidsparecount
		RET=$(echo $RET | sed -e "s/[[:space:]]//g")
		if [ "$RET" ]; then
			let "OK=${RET}>=0 && ${RET}<99"
		fi
	done

	db_get mdcfg/raiddevcount
	DEV_COUNT="$RET"
	if [ $LEVEL -ne 1 ]; then
		if [ $DEV_COUNT -lt $MIN_SIZE ]; then
			DEV_COUNT=$MIN_SIZE # Minimum number for the selected RAID level
		fi
	fi
	db_get mdcfg/raidsparecount
	SPARE_COUNT="$RET"
	REQUIRED=$(($DEV_COUNT + $SPARE_COUNT))
	if [ $LEVEL -ne 1 ]; then
		if [ $REQUIRED -gt $NUM_PART ]; then
			db_subst mdcfg/notenoughparts NUM_PART "$NUM_PART"
			db_subst mdcfg/notenoughparts REQUIRED "$REQUIRED"
			db_input critical mdcfg/notenoughparts
			db_go mdcfg/notenoughparts
			return
		fi
	fi

	db_set mdcfg/raiddevs ""
	SELECTED=0

	# Loop until the correct number of active devices has been selected for RAID 5, 6, and 10
	# Loop until at least one device has been selected for RAID 1
	until ([ $LEVEL -ne 1 ] && [ $SELECTED -eq $DEV_COUNT ]) || \
	      ([ $LEVEL -eq 1 ] && [ $SELECTED -gt 0 ] && [ $SELECTED -le $DEV_COUNT ]); do
		db_subst mdcfg/raiddevs COUNT "$DEV_COUNT"
		db_subst mdcfg/raiddevs PARTITIONS "$PARTITIONS"
		db_input critical mdcfg/raiddevs
		db_go
		if [ $? -eq 30 ]; then
			return
		fi

		db_get mdcfg/raiddevs
		SELECTED=0
		for i in $RET; do
			DEVICE=$(echo $i | sed -e "s/,//")
			let SELECTED++
		done
	done

	MISSING_DEVICES=""
	if [ $LEVEL -eq 1 ]; then
		# Add "missing" for as many devices as weren't selected
		while [ $SELECTED -lt $DEV_COUNT ]; do
			MISSING_DEVICES="$MISSING_DEVICES missing"
			let SELECTED++
		done
	fi

	# Remove partitions selected in raiddevs from the PARTITION list
	db_get mdcfg/raiddevs

	prune_partitions "$RET"

	db_set mdcfg/raidsparedevs ""
	SELECTED=0
	if [ $SPARE_COUNT -gt 0 ] && [ -n "$PARTITIONS" ]; then
		FIRST=1
		# Loop until the correct number of devices has been selected.
		# That means any number less than or equal to the spare count.
		while [ $SELECTED -gt $SPARE_COUNT ] || [ $FIRST -eq 1 ]; do
			FIRST=0
			db_subst mdcfg/raidsparedevs COUNT "$SPARE_COUNT"
			db_subst mdcfg/raidsparedevs PARTITIONS "$PARTITIONS"
			db_input critical mdcfg/raidsparedevs
			db_go
			if [ $? -eq 30 ]; then
				return
			fi

			db_get mdcfg/raidsparedevs
			SELECTED=0
			for i in $RET; do
				DEVICE=$(echo $i | sed -e "s/,//")
				let SELECTED++
			done
		done
	fi

	LAYOUT=""
	if [ $LEVEL -eq 10 ]; then
		OK=0
		db_set mdcfg/raid10layout "n2"
		while [ $OK -eq 0 ]; do
			db_input low mdcfg/raid10layout
			db_go
			if [ $? -eq 30 ]; then return; fi
			db_get mdcfg/raid10layout
			if echo $RET | grep -Eq "^[nfo][0-9]{1,2}$" && \
			    [ $(echo $RET | sed s/.//) -le $DEV_COUNT ]; then
				OK=1
			fi
		done
		LAYOUT="--layout=$RET"
	fi

	# The number of spares the user has selected
	NAMED_SPARES=$SELECTED

	db_get mdcfg/raiddevs
	RAID_DEVICES=$(echo $RET | sed -e "s/,//g")

	db_get mdcfg/raidsparedevs
	SPARE_DEVICES=$(echo $RET | sed -e "s/,//g")

	MISSING_SPARES=""

	COUNT=$NAMED_SPARES
	while [ $COUNT -lt $SPARE_COUNT ]; do
		MISSING_SPARES="$MISSING_SPARES missing"
		let COUNT++
	done

	# Find the next available md-number
	MD_NUM=$(grep ^md /proc/mdstat | \
		 sed -e 's/^md\(.*\) : active .*/\1/' | sort | tail -n1)
	if [ -z "$MD_NUM" ]; then
		MD_NUM=0
	else
		let MD_NUM++
	fi

	logger -t mdcfg "Selected spare count: $NAMED_SPARES"
	logger -t mdcfg "Raid devices count: $DEV_COUNT"
	logger -t mdcfg "Spare devices count: $SPARE_COUNT"
	log-output -t mdcfg \
		mdadm --create /dev/md$MD_NUM --auto=yes --force -R -l raid${LEVEL} $LAYOUT \
		      -n $DEV_COUNT -x $SPARE_COUNT $RAID_DEVICES \
		      $MISSING_DEVICES $SPARE_DEVICES $MISSING_SPARES
}

md_mainmenu() {
	while true; do
		db_set mdcfg/mainmenu false
		db_input critical mdcfg/mainmenu
		db_go
		if [ $? -eq 30 ]; then
			exit 30
		fi
		db_get mdcfg/mainmenu
		case $RET in
		    "Create MD device")
			md_createmain ;;
		    "Delete MD device")
			md_delete ;;
		    "Finish")
			break ;;
		esac
	done
}

### Main of script ###

# Load the modules and scan for MD devices if needed
if ! [ -e /proc/mdstat ] || ! [ -d /dev/md ]; then
	# Try to load the necesarry modules.
	depmod -a >/dev/null 2>&1
	modprobe md-mod >/dev/null 2>&1

	# Make sure that we have md-support
	if [ ! -e /proc/mdstat ]; then
		db_set mdcfg/nomd false
		db_input high mdcfg/nomd
		db_go
		exit 0
	fi

	# Try to detect MD devices, and start them
	# mdadm will fail if /dev/md does not already exist
	mkdir -p /dev/md

	log-output -t mdcfg --pass-stdout \
		mdadm --examine --scan --config=partitions >/tmp/mdadm.conf

	log-output -t mdcfg \
		mdadm --assemble --scan --run \
		--config=/tmp/mdadm.conf --auto=yes
fi

# Force mdadm to be installed on the target system
apt-install mdadm

# We want the "go back" button
#db_capb backup

md_mainmenu

exit 0