summaryrefslogtreecommitdiffstats
path: root/steeltoe.sh
blob: 31919c8657133e152ab30c0e54d0ad4b2258048b (plain)
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
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
#!/bin/bash
#
# Steeltoe command processor

fail()
{
	# Print error message on $1 to stderr
	echo "$1" >&2
	# then exit with error code $2 or 1
	exit ${2:-1}
}

# require: make sure specified config options are set
require()
{
	for vn in $*; do case $vn in
		RSH) RSH=`gxpp "/steeltoe/config/remoteshell" $STXML` || fail "Config option remoteshell not found";;
		RCP) RCP=`gxpp "/steeltoe/config/remotecopy" $STXML` || fail "Config option remotecopy not found";;
		TFTPROOT) TFTPROOT=`gxpp "/steeltoe/config/tftproot" $STXML` || fail "Config option tftproot not found"
			  [ -d "$TFTPROOT" -a -w "$TFTPROOT" ] || fail "Directory $TFTPROOT does not exist or is not writeable";;
		TREESERVER) TREESERVER=`gxpp "/steeltoe/config/treeserver" $STXML` || fail "Config option treeserver not found";;
		KSBASE) KSBASE=`gxpp "/steeltoe/config/kickstartbase" $STXML` || fail "Config option kickstartbase not found";;
		YABOOTETC) YABOOTETC=`gxpp "/steeltoe/config/yabootetc" $STXML` || fail "Config option yabootetc not found"
			   [ -d "$YABOOTETC" -a -w "$YABOOTETC" ] || fail "Directory $YABOOTETC does not exist or is not writeable";;
		*) fail "Unknown config requirement '$vn'"
	esac; done
}

expand_groups() {
	for target in $*; do
		grouplist=$(gxpp "//group[@name='$target']" $STXML)
		if [ -z "$grouplist" ]; then
			echo $target
		else 
			for g in $grouplist; do echo $g; done
		fi
	done
}

# Set global config variables
if [ -z "$STEELTOE_ETC" ]; then
	STEELTOE_ETC="/etc/steeltoe"
fi
STXML="$STEELTOE_ETC/steeltoe.xml";
if [ ! -f $STXML ]; then
	fail "Steel Toe config file '$STXML' not found"
fi

XSLTOPTS="--xinclude"
# Make the default install method http
instmethod="http"

check_instmethod()
{
	instmethods="http ftp nfs"
	if ! echo "$instmethods" | grep -q $1; then
		echo "Install method '$1' unknown.  Supported methods: $instmethods" >&2
		exit 1
	fi
}

# Make the default kickstart template /etc/steeltoe/kickstart.xsl
kstemplate="$STEELTOE_ETC/kickstart.xsl"

check_kstemplate()
{
  if [ ! -f $1 ]; then
    echo "Kickstart template '$1' does not exist" >&2
    exit 1
  fi
}

global_usage()
{
	cat >&2 << __EOT__ 
Usage: $0 <cmd> <options>
__EOT__
	for i in $COMMANDS; do
		${i}_usage >&2
	done
}

COMMANDS="$COMMANDS gendhcpd"
gendhcpd_usage()
{
	cat << __EOT__
	gendhcpd [-o outputfile ]
		Generate ISC DHCPD configuration snippet
__EOT__
}

gendhcpd_do()
{
	if [ -n "$outputfile" ]; then
		XSLTOPTS="$XSLTOPTS -o $outputfile"
	fi
	xsltproc $XSLTOPTS $STEELTOE_ETC/dhcpd.xsl $STXML
}

COMMANDS="$COMMANDS genks"
genks_usage()
{
	cat << __EOT__
	genks [-k kickstart.xsl][-m http|ftp|nfs][-o outputfile ] host tree
		Generate a Kickstart config file for host to install tree
__EOT__
}

genks_do()
{
	if [ -z "$1" -o -z "$2" ]; then
		genks_usage >&2
		exit 1
	fi
	if [ -n "$outputfile" ]; then
		XSLTOPTS="$XSLTOPTS -o $outputfile"
	fi
	if [ -n "$instmethod" ]; then
		METHOD="--stringparam method $instmethod"
	fi
	xsltproc $XSLTOPTS $METHOD --stringparam hostname "$1" --stringparam treename "$2" $kstemplate $STXML
}

COMMANDS="$COMMANDS genpxe"
genpxe_usage()
{
	cat << __EOT__
	genpxe [-o outputfile ] host tree
		Generate a PXE config file for host to install tree
__EOT__
}

genpxe_do()
{
	if [ -z "$1" -o -z "$2" ]; then
		genpxe_usage >&2
		exit 1
	fi
	if [ -n "$outputfile" ]; then
		XSLTOPTS="$XSLTOPTS -o $outputfile"
	fi
	xsltproc $XSLTOPTS --stringparam hostname "$1" --stringparam treename "$2" $STEELTOE_ETC/pxeconf.xsl $STXML
}

COMMANDS="$COMMANDS genelilo"
genelilo_usage()
{
	cat << __EOT__
	genelilo [-o outputfile ] host tree
		Generate a ELILO config file for host to install tree
__EOT__
}

genelilo_do()
{
	if [ -z "$1" -o -z "$2" ]; then
		genelilo_usage >&2
		exit 1
	fi
	if [ -n "$outputfile" ]; then
		XSLTOPTS="$XSLTOPTS -o $outputfile"
	fi
	xsltproc $XSLTOPTS --stringparam hostname "$1" --stringparam treename "$2" $STEELTOE_ETC/eliloconf.xsl $STXML
}

COMMANDS="$COMMANDS genyaboot"
genyaboot_usage()
{
	cat << __EOT__
	genyaboot [-o outputfile ] host tree
		Generate a yaboot config file for host to install tree
__EOT__
}

genyaboot_do()
{
	if [ -z "$1" -o -z "$2" ]; then
		genyaboot_usage >&2
		exit 1
	fi
	if [ -n "$outputfile" ]; then
		XSLTOPTS="$XSLTOPTS -o $outputfile"
	fi
	xsltproc $XSLTOPTS --stringparam hostname "$1" --stringparam treename "$2" $STEELTOE_ETC/yaboot.xsl $STXML
}

COMMANDS="install $COMMANDS"
install_usage()
{
	cat << __EOT__
	install [options] tree host|group [host|group ...]
		Install tree on hosts
		-a ARCH	override the architecture of a host
		-k FILE	specify an alternate kickstart template to use.
		-m meth	install method (http, ftp, or nfs; default http)
		-r	reboot now
__EOT__
}

install_do()
{
	if [ -z "$1" -o -z "$2" ]; then
		install_usage >&2
		exit 1
	fi
	require TFTPROOT RSH
	treename="$1"
	shift
	hostlist=`expand_groups $*`

	for host in $hostlist; do
		echo "Configuring $host to install $treename"
		tftp_do $host $treename
		if ping -q -c 1 -w 3 $host > /dev/null && $RSH -l root $host /bin/true; then
			autoks_do $host $treename
		else
			echo "$host needs to be manually PXE booted to install $treename"
		fi
	done
}

COMMANDS="$COMMANDS tftp"
tftp_usage()
{
	cat << __EOT__
	tftp [-m http|ftp|nfs][-a ARCH] host tree
		Prepare host's TFTP directory for PXE installing tree.
		-a ARCH	override the architecture of a host
		-m	install method (default http)
__EOT__
}

tftp_do()
{
	if [ -z "$1" -o -z "$2" ]; then
		tftp_usage >&2
		exit 1
	fi
	require TFTPROOT RSH
	host="$1"
	treename="$2"
	hostdir="$TFTPROOT/$host"

	# Try to lookup something under the host so we catch a non-existant host early.
	hostarch=`gxpp "//host[@name='$host']/arch" $STXML` || fail "Could not look up architecture of host '$host'"

	# Check to see if we're overriding the arch on the command line
	if [ -n "$forcearch" ]; then
		hostarch=$forcearch
	fi

	# First lookup the tree information to make sure it exists.
	treebase=`gxpp "//tree[@name='$treename' and meta/arch='$hostarch']/path" $STXML` || fail "Could not find tree '$treename' for host '$host'"
	# Next figure out which bootloader we need to configure
	loadername=`gxpp "//host[@name='$host']/bootloader" $STXML` || fail "Boot loader not specified for host '$host'"
	# Now find the source for the bootloader link
	loadersrc=`gxpp "/steeltoe/config/bootloader/$loadername" $STXML` || fail "Boot loader source is not set for '$loadername'"
	# Make sure the host's TFTP directory exists
	[ -d "$hostdir" ] || mkdir -p $hostdir
	# Link in the bootloader specified in the config file
	[ -f "$hostdir/$loadername" ] || ln -f $loadersrc $hostdir/$loadername
	# Generate the kickstart config
	genks_do $host $treename > $hostdir/ks.cfg
	# Handle bootloader specific configuration needs and link in the kernel and initrd
	case $loadername in
	  pxelinux.0) genpxe_do $host $treename > $hostdir/default
			[ -e "$hostdir/pxelinux.cfg" ] || ln -s . $hostdir/pxelinux.cfg
			ln -sf $treebase/images/pxeboot/vmlinuz $hostdir/vmlinuz
			ln -sf $treebase/images/pxeboot/initrd.img $hostdir/initrd.img
			;;
	   elilo.efi) genelilo_do $host $treename > $hostdir/elilo.conf
			ln -sf $treebase/images/pxeboot/vmlinuz $hostdir/vmlinuz
			ln -sf $treebase/images/pxeboot/initrd.img $hostdir/initrd.img
			;;
	      yaboot) genyaboot_do $host $treename > $hostdir/yaboot.conf
			require YABOOTETC
			etcfn="01-`gxpp "//host[@name='$host']/macaddr" $STXML | tr [A-Z]: [a-z]-`"
			ln -f $hostdir/yaboot.conf $YABOOTETC/$etcfn
			# kernel and ramdisk locations differ between RHEL4 and RHEL5
			for subdir in ppc/chrp ppc/ppc64; do if [ -f $treebase/$subdir/vmlinuz ]; then
				ln -sf $treebase/$subdir/vmlinuz $hostdir/vmlinuz
				ln -sf $treebase/$subdir/ramdisk.image.gz $hostdir/initrd.img
				break
			fi; done;;
	esac
}

COMMANDS="$COMMANDS autoks"
autoks_usage()
{
	cat << __EOT__
	autoks [-k kickstart.xsl][-r] host tree
		Copy installer to host and configure bootloader to boot it.
		-k FILE	Specify alternate kickstart template
		-r	reboot now
__EOT__
}

autoks_do()
{
	require RSH RCP TFTPROOT
	if [ -z "$1" -o -z "$2" ]; then
		autoks_usage >&2
		exit 1
	fi
	host="$1"
	treename="$2"

	$RSH -l root $host /bin/true || fail "Host $host must be up to auto-kickstart"

	# Try to lookup something under the host so we catch a non-existant host early.
	hostarch=`gxpp "//host[@name='$host']/arch" $STXML` || fail "Could not look up architecture of host '$host'"

	# Figure out where to copy the vmlinuz and initrd on the host
	if [ "$hostarch" = "ia64" ]; then
		bootdir="/boot/efi/EFI/redhat"
	else
		bootdir="/boot"
	fi

	# First lookup the tree information to make sure it exists.
	ksargs=`xsltproc $XSLTOPTS --stringparam hostname "$host" --stringparam treename "$treename" $STEELTOE_ETC/autoks-args.xsl $STXML` || fail "Could not generate kickstart arguments for $host and $treename"
	hostdir="$TFTPROOT/$host"
	# Make sure the host's TFTP directory is populated
	[ -d "$hostdir" ] || fail "No TFTP directory for $host, run tftp command first or install"
	[ -e "$hostdir/vmlinuz" ] || fail "No kernel $host, run tftp command first or install"
	[ -e "$hostdir/initrd.img" ] || fail "No ramdisk for $host, run tftp command first or install"

	# Everything is in place, copy it out to host
	echo "Copying files to $host for auto-kickstart of $treename"
	$RCP $hostdir/vmlinuz root@$host:$bootdir/vmlinuz-steeltoe || fail "Failed to copy vmlinuz"
	$RCP $hostdir/initrd.img root@$host:$bootdir/initrd.img-steeltoe || fail "Failed to copy initrd"

	# Check to see if there is a steeltoe kernel and config on host already so we don't duplicate it.
	$RSH -l root $host "/sbin/grubby --set-default $bootdir/vmlinuz-steeltoe"
	defkernel=`$RSH -l root $host "/sbin/grubby --default-kernel --bad-image-okay"`
	if [ "$defkernel" != "$bootdir/vmlinuz-steeltoe" ]; then
		# There wasn't a bootloader entry already, make one
		$RSH -l root $host "/sbin/grubby --make-default --title='steeltoe' --add-kernel=$bootdir/vmlinuz-steeltoe --initrd=$bootdir/initrd.img-steeltoe --args='$ksargs'" || fail "Failed to configure bootloader"
	fi
	$RSH -l root $host sync
	if [ $reboot ]; then
		echo "Rebooting $host"
		$RSH -l root $host "reboot -fi" > /dev/null 2>&1 &
	fi
}


COMMANDS="$COMMANDS virtinstall"
virtinstall_usage()
{
	cat << __EOT__
	virtinstall tree virthost installhost virtroot_device [virtshare_device [virtshare_device] [...]]
		Install tree on virthost from installhost 
		onto virtroot_device, and configure virt guest 
		profile to use virtshare_device(s).
__EOT__
}

virtinstall_do()
{
	if [[ $# < 4 ]]; then
		virtinstall_usage >&2
		exit 2
	fi

	treename="$1"
	virthost="$2"
	installhost="$3"
	virtroot="$4"
	shift 4
	#virtshare is all the rest...

	# Don't even bother if the virt host is up and running
	if ping -c 1 -W 1 $virthost > /dev/null ; then
		fail "$virthost seems to be running, cannot install on running virt guest"
	fi

	require RSH TFTPROOT TREESERVER KSBASE
	# Let install_do do all the hard work and set up the tftp area.
	tftp_do $virthost $treename 

	virtmac=$(gxpp "//host[@name='$virthost']/macaddr" $STXML) || fail "Unable to find $virthost mac address in $STXML"
	DEFAULT_RAM=1024
	virtram=$(gxpp "//host[@name='$virthost']/ram" $STXML)
	virtram=${virtram:-$DEFAULT_RAM}

	ks="$KSBASE/$virthost/ks.cfg"

	hostarch=$(gxpp "//host[@name='$virthost']/arch" $STXML) || fail "Could not look up architecture of virthost '$virthost'"
	treebase=$(gxpp "//tree[@name='$treename' and meta/arch='$hostarch']/path" $STXML) || fail "Could not find tree '$treename' for virthost '$virthost'"
	location="http://$TREESERVER/$treebase"

	extra="'ks=$ks ro selinux=0'"

	# Rebuild the kickstart file, setting the virtnstall param on the xsltproc cmdline.
	xsltproc --xinclude --stringparam virtinstall true --stringparam method http --stringparam hostname $virthost --stringparam treename $treename $kstemplate $STXML > $TFTPROOT/$virthost/ks.cfg

	# Kick off the virtinstall on the installhost
	$RSH -l root $installhost "virt-install -n $virthost -f $virtroot -r $virtram -m $virtmac -p -x $extra --nographics -l $location --noautoconsole"

	# Add to the Xen config file created to add the share devices
	# Blech, Xen specific stuff follows.  Right now it seems we
	# can only have xvda-xvdg as disks, so limit shared to b-g
	XEN_CONFIGROOT=/etc/xen
	SHARED_DEVICES=(xvdb xvdc xvdd xvde xvdf xvdg)
	i=0
	for vshare in $*; do
		vshareentry="disk.append(\"phy:${vshare},${SHARED_DEVICES[$i]},w!\")"
		echo "Adding $vshare to $virthost"
		$RSH -l root $installhost "echo '$vshareentry' >> $XEN_CONFIGROOT/$virthost"
		(( i = i + 1 ))
		if [[ $i > 6 ]]; then
			echo "Only support 5 shared devices, ignoring the rest"
		fi
	done
}


pxe_reboot() 
{
	host="$1"

	set -o pipefail
	rootdev=`gxpp "//host[@name='$host']/kickstart/rootdisk" $STXML | head -1` || fail "Root disk is not specified"
	echo "Making /dev/$rootdev on $host unbootable"
	$RSH -l root $host dd if=/dev/zero of=/dev/$rootdev bs=1 seek=510 count=2 conv=sync > /dev/null 2>&1
	# Sync
	$RSH -l root $host "sync"
	# reboot
	echo "Rebooting $host"
	$RSH -l root $host "reboot -fin" > /dev/null 2>&1 &
}


efi_reboot()
{
	host="$1"

	# Grab the EFI boot menu entry for Steeltoe
	steeltoe=`$RSH -l root $host "efibootmgr" | awk -F\* '($1 ~ /^Boot/) && ($2 ~ /[Ss]teeltoe/) {print $1}'`

	if [ -z "$steeltoe" ]; then
		fail "Unable to find Steeltoe EFI boot entry on $host"
	fi

	# Set the NextBoot option to the found entry
	echo "Updating EFI on $host to boot to Steeltoe"
	$RSH -l root $host "efibootmgr -n ${steeltoe/Boot/}" || fail "Unable to set NextBoot to Steeltoe on $host"
	# reboot
	echo "Rebooting $host"
	$RSH -l root $host "reboot -fin" > /dev/null 2>&1 &
}

COMMANDS="$COMMANDS list"
list_usage()
{
	cat << __EOT__
	list [-v] <host|group|tree> [substring]
		List out hosts or trees
__EOT__
}

list_do()
{
	class="$1"
	search="$2"
	if [ "$class" == "host" -o "$class" == "tree" -o "$class" == "group" ]; then
		XSLTOPTS="$XSLTOPTS --stringparam class $class"
	elif [ "$class" == "trees" ]; then
		XSLTOPTS="$XSLTOPTS --stringparam class tree"
	elif [ "$class" == "hosts" ]; then
		XSLTOPTS="$XSLTOPTS --stringparam class host"
	elif [ "$class" == "groups" ]; then
		XSLTOPTS="$XSLTOPTS --stringparam class group"
	else
		list_usage >&2
		exit 1
	fi
	if [ -n "$verbose" ]; then
		XSLTOPTS="$XSLTOPTS --param verbose 1"
	fi
	xsltproc $XSLTOPTS $STEELTOE_ETC/list.xsl $STXML
}


# Make sure the first option is a command
cmd="$1"
if [ -z "$cmd" ]; then
	global_usage
	exit 1
fi
if ! echo $COMMANDS | grep -q -- "$cmd"; then
	echo "Unknown command '$cmd'" >&2
	global_usage
	exit 1
fi
shift

# Generate the getopts string based on command
opts="h"
case $cmd in
	gendhcpd) opts="o:$opts";;
	genpxe) opts="o:$opts";;
	genelilo) opts="o:$opts";;
	genyaboot) opts="o:$opts";;
	genks) opts="m:o:k:$opts";;
	install) opts="a:rm:k:$opts";;
	autoks) opts="a:rm:k:$opts";;
	virtinstall) opts="k:$opts";;
	list) opts="v$opts";;
esac

# parse command line options for all subcommands
while getopts "$opts" opt; do
	case $opt in
		a) forcearch="$OPTARG";;
		o) outputfile="$OPTARG";;
		r) reboot=1;;
		v) verbose=1;;
		m) instmethod="$OPTARG"; check_instmethod "$OPTARG";;
		k) kstemplate="$OPTARG"; check_kstemplate "$OPTARG";;
		*) global_usage;;
	esac
done
shift $(($OPTIND - 1))
# Run the command
${cmd}_do $*