From 1835c920726e5ce4f012ba1fc4cba0d33c9b316a Mon Sep 17 00:00:00 2001 From: rjones Date: Mon, 10 Sep 2007 17:45:20 +0100 Subject: [PATCH] Basic device detection. --- Makefile | 8 +- inittab | 36 +++++++ livecd-post.sh.in | 26 +++-- p2v.init | 5 +- virt-p2v.sh | 315 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 363 insertions(+), 27 deletions(-) create mode 100644 inittab diff --git a/Makefile b/Makefile index f098f29..0124250 100644 --- a/Makefile +++ b/Makefile @@ -29,9 +29,7 @@ all: build: livecd.ks rm -f $(LABEL).iso - livecd-creator \ - --config=$< \ - --fslabel=$(LABEL) + livecd-creator --config=$< --fslabel=$(LABEL) ls -lhtr *.iso ISO = $(LABEL).iso @@ -47,12 +45,14 @@ livecd.ks: livecd.ks.in livecd-post.sh Makefile -e 's|@TIMEZONE@|$(TIMEZONE)|g' \ < $< | cat - livecd-post.sh > $@ -livecd-post.sh: livecd-post.sh.in p2v.init virt-p2v.sh Makefile +livecd-post.sh: livecd-post.sh.in p2v.init virt-p2v.sh inittab Makefile sed \ -e '/@P2V.INIT@/ r p2v.init' \ -e '/@P2V.INIT@/ d' \ -e '/@VIRT-P2V.SH@/ r virt-p2v.sh' \ -e '/@VIRT-P2V.SH@/ d' \ + -e '/@INITTAB@/ r inittab' \ + -e '/@INITTAB@/ d' \ < $< > $@ clean: diff --git a/inittab b/inittab new file mode 100644 index 0000000..ac0616f --- /dev/null +++ b/inittab @@ -0,0 +1,36 @@ +# Custom inittab for p2v livecd. + +id:5:initdefault: + +# System initialization. +si::sysinit:/etc/rc.d/rc.sysinit + +l0:0:wait:/etc/rc.d/rc 0 +l1:1:wait:/etc/rc.d/rc 1 +l2:2:wait:/etc/rc.d/rc 2 +l3:3:wait:/etc/rc.d/rc 3 +l4:4:wait:/etc/rc.d/rc 4 +l5:5:wait:/etc/rc.d/rc 5 +l6:6:wait:/etc/rc.d/rc 6 + +# Trap CTRL-ALT-DELETE +ca::ctrlaltdel:/sbin/shutdown -t3 -r now + +# When our UPS tells us power has failed, assume we have a few minutes +# of power left. Schedule a shutdown for 2 minutes from now. +# This does, of course, assume you have powerd installed and your +# UPS connected and working correctly. +pf::powerfail:/sbin/shutdown -f -h +2 "Power Failure; System Shutting Down" + +# If power was restored before the shutdown kicked in, cancel it. +pr:12345:powerokwait:/sbin/shutdown -c "Power Restored; Shutdown Cancelled" + +# Run gettys but not on tty1 which is where virt-p2v runs. +2:2345:respawn:/sbin/mingetty tty2 +3:2345:respawn:/sbin/mingetty tty3 +4:2345:respawn:/sbin/mingetty tty4 +5:2345:respawn:/sbin/mingetty tty5 +6:2345:respawn:/sbin/mingetty tty6 + +# This file must end with a newline. + diff --git a/livecd-post.sh.in b/livecd-post.sh.in index 82427ae..2f8d7e3 100644 --- a/livecd-post.sh.in +++ b/livecd-post.sh.in @@ -6,35 +6,41 @@ # Create a 'p2v' init script which runs last thing during the # boot. -cat > /etc/init.d/p2v << '__EOF__' +cat > /etc/init.d/p2v << '__EOF4321__' @P2V.INIT@ -__EOF__ +__EOF4321__ # Make the 'p2v' script run when the live CD boots. chmod 0755 /etc/init.d/p2v /sbin/restorecon /etc/init.d/p2v /sbin/chkconfig --add p2v -cat > /usr/bin/virt-p2v.sh << '__EOF__' +cat > /usr/bin/virt-p2v.sh << '__EOF1234__' @VIRT-P2V.SH@ -__EOF__ +__EOF1234__ chmod 0755 /usr/bin/virt-p2v.sh /sbin/restorecon /usr/bin/virt-p2v.sh +# Install custom inittab. +cat > /etc/inittab << '__EOF4123__' +@INITTAB@ +__EOF4123__ + + # Turn off firstboot for livecd boots. echo "RUN_FIRSTBOOT=NO" > /etc/sysconfig/firstboot # Don't start yum-updatesd for livecd boots. -chkconfig --level 345 yum-updatesd off +/sbin/chkconfig --level 345 yum-updatesd off # Don't start cron/at as they tend to spawn things which are # disk intensive that are painful on a live image. -chkconfig --level 345 crond off -chkconfig --level 345 atd off -chkconfig --level 345 anacron off -chkconfig --level 345 readahead_early off -chkconfig --level 345 readahead_later off +/sbin/chkconfig --level 345 crond off +/sbin/chkconfig --level 345 atd off +/sbin/chkconfig --level 345 anacron off +/sbin/chkconfig --level 345 readahead_early off +/sbin/chkconfig --level 345 readahead_later off # Stopgap fix for RH #217966; should be fixed in HAL instead touch /media/.hal-mtab diff --git a/p2v.init b/p2v.init index cb54b24..153b148 100644 --- a/p2v.init +++ b/p2v.init @@ -15,9 +15,12 @@ start) /sbin/modprobe dm_snapshot # Start the P2V script. - /usr/bin/virt-p2v.sh + /usr/bin/virt-p2v.sh & ;; *) ;; esac + +# This file must end with a newline + diff --git a/virt-p2v.sh b/virt-p2v.sh index 44184c7..fa2e705 100755 --- a/virt-p2v.sh +++ b/virt-p2v.sh @@ -6,6 +6,7 @@ # By Richard W.M. Jones # # Copyright (C) 2007 Red Hat Inc. +# $Id$ export PATH=/usr/sbin:/sbin:/usr/local/bin:/usr/kerberos/bin:/usr/bin:/bin @@ -14,7 +15,7 @@ export PATH=/usr/sbin:/sbin:/usr/local/bin:/usr/kerberos/bin:/usr/bin:/bin # these variables to something, and the script won't ask the user for # input. -# greeting=no +# Use 'greeting=no' to suppress greeting and final verification screens. greeting= override_remote_host= @@ -28,8 +29,46 @@ override_remote_transport= override_remote_directory= # list of devices to send, separated by spaces, if empty ask user +# this is usually a list of whole disk devices (eg. "sda") override_devices_to_send="" +# list of root filesystems to examine (eg. "sda3" or +# "VolGroup00/LogVol00") +override_root_filesystems="" + +#---------------------------------------------------------------------- +# Logging. + +function log { + echo "$@" >> /tmp/virt-p2v.log +} + +#---------------------------------------------------------------------- +# Helper functions. + +# 'matches_regexp regexp string' returns true if string matches regexp. +# It uses egrep for extended regexps. +function matches_regexp { + local re="$1"; shift + echo "$@" | grep -Esq "$re" +} + +# 'string_contains needle haystack' returns true if needle in haystack. +function string_contains { + echo "$2" | grep -Fsq "$1" +} + +# 'word_in_list word ...'. '...' (list of parameters) is a list of +# words. Returns true if 'word' is in the list. +function word_in_list { + local word="$1"; shift + local w + for w in "$@"; do + [ "$w" = "$word" ] && return 0 + done + return 1 +} + #---------------------------------------------------------------------- # Device mapper snapshotting. @@ -41,11 +80,10 @@ override_devices_to_send="" # XXX Error checking. function snapshot { - dev=$1 - name=$2 + local dev=$1 name=$2 # Get size of the device in sectors. - sectors=`blockdev --getsize $dev` + local sectors=`blockdev --getsize $dev` dmsetup create ${name}_org \ --table="0 $sectors snapshot-origin $dev" @@ -62,6 +100,110 @@ function drop_snapshot { dmsetup remove /dev/mapper/${name}_org } +#---------------------------------------------------------------------- +# Searching for devices, partitions and LVs. + +# Convert "/dev/foo" into "foo". Returns $device. +function strip_slash_dev { + device=$(echo "$1" | sed 's|^/dev/||') +} + +# The 'lvs' utility returns devices like '/dev/sda2(0)'. I've no +# idea what the number in parentheses is, but remove /dev/ and the +# strange number. +function device_of_lvs_device { + strip_slash_dev "$1" + device=$(echo "$device" | sed 's|(.*)$||') +} + +# All functions in this section assume that physical block devices +# (things corresponding to hard disks in the system) are called +# hd[a-z]+ or sd[a-z]+ + +# Get list of physical block devices. Sets variable $devices +# to something like "sda sdb". +function search_devices { + devices=$(cd /sys/block && /bin/ls -d [hs]d*) +} + +# Get list of partitions from a physical block device. Sets +# variable $partitions to something like "sda1 sda2 sda3". +# See also: search_parts +function get_partitions { + partitions=$(cd /sys/block/"$1" && /bin/ls -d "$1"*) +} + +# Given a partition (eg. "sda1" or "VolGroup00/LogVol00") return +# the physical block device which contains it. In the case where +# we are given an LV, this is supposed to do the right thing and +# go back through the layers until it gets to the physical block +# device. Returns $device set to something like "sda". +function block_device_of_part { + local part="$1" vg_name lv_name pv + + if matches_regexp '^[hs]d[a-z]+[0-9]*$' "$part"; then + device=$(echo "$part" | sed 's|[0-9]*$||') + return 0 + fi + + # Not a partition name, so it's a LV name. Ask lvs to + # do the hard work. + lvs --noheadings -o vg_name,lv_name,devices > lvs + while read vg_name lv_name pv; do + if [ "$vg_name/$lv_name" = "$part" \ + -o "mapper/$vg_name-$lv_name" = "$part" ]; then + device_of_lvs_device "$pv" ;# sets $device to (eg.) "sda1" + block_device_of_part "$device" + return 0 + fi + done < lvs + + # Help ... block device not found. + log block_device_of_part: block device cannot be resolved: $part + device="$part" +} + +# search_parts $devices examines the list of devices and looks for +# partitions or LVs on just those devices. Sets variable $parts to +# something like "sda1 VolGroup00/LogVol00". +function search_parts { + local vg_name lv_name pv pvs="" device partition partitions + + parts="" + + lvs --noheadings -o vg_name,lv_name,devices > lvs + while read vg_name lv_name pv; do + # Get just the partition name (eg. "sda2"). + device_of_lvs_device "$pv"; pv="$device" + # Get just the block device name (eg. "sda"). + block_device_of_part "$pv" + + log search_parts: pv $pv device $device lv $vg_name/$lv_name + + # A device in our list of devices to consider? + if word_in_list $device "$@"; then + log search_parts: adding $vg_name/$lv_name + parts="$parts $vg_name/$lv_name" + pvs="$pvs $pv" + fi + done < lvs + + log search_parts: after lvs, parts $parts pvs $pvs + + # Look for non-LVM partitions, but ignore any which are PVs + # as identified above. + for device in "$@"; do + get_partitions "$device" + for partition in $partitions; do + if ! word_in_list $partition $pvs; then + log search_parts: adding $partition + parts="$parts $partition" + fi + done + done + + # $parts is returned. +} #---------------------------------------------------------------------- # General script setup. @@ -72,6 +214,9 @@ cd /tmp #---------------------------------------------------------------------- # Dialog with the user. +log +log virt-p2v starting up at `date` + if [ "$greeting" != "no" ]; then dialog \ --title "virt-p2v" \ @@ -85,24 +230,53 @@ fi # (unless [Back] is pressed, in which case it jumps back to the previous # state). Finally the 'exit' state causes us to quit the loop. -state=hostname +remote_port=22 +remote_directory=/var/lib/xen/images +state=transport + while [ "$state" != "exit" ]; do + log next state = $state case "$state" in + transport) + if [ -n "$override_remote_transport" ]; then + remote_transport="$override_remote_transport" + state=hostname + else + case "$remote_transport" in + tcp) ssh_on=off; tcp_on=on;; + *) ssh_on=on; tcp_on=off;; + esac + dialog \ + --nocancel \ + --radiolist "Connection type" 10 50 2 \ + "ssh" "SSH (secure shell - recommended)" $ssh_on \ + "tcp" "TCP socket" $tcp_on \ + 2> line + remote_transport=`cat line` + state=hostname + fi + ;; hostname) if [ -n "$override_remote_host" ]; then remote_host="$override_remote_host" + state=port else dialog \ - --nocancel \ + --extra-button --extra-label "Back" --nocancel \ --inputbox "Remote host" 10 50 "$remote_host" \ 2> line - remote_host=`cat line` - state=port + if [ $? -eq 3 ]; then state=transport + else + remote_host=`cat line` + if [ -n "$remote_host" ]; then state=port; fi + # else stay in same state and demand a hostname! + fi fi ;; port) if [ -n "$override_remote_port" ]; then remote_port="$override_remote_port" + state=directory else dialog \ --extra-button --extra-label "Back" --nocancel \ @@ -111,18 +285,135 @@ while [ "$state" != "exit" ]; do if [ $? -eq 3 ]; then state=hostname else remote_port=`cat line` - state=exit + state=directory + fi + fi + ;; + directory) + if [ "$remote_transport" = "tcp" ]; then + state=devices + elif [ -n "$override_remote_directory" ]; then + remote_directory="$override_remote_directory" + state=devices + else + dialog \ + --extra-button --extra-label "Back" --nocancel \ + --inputbox "Remote directory containing guest images" \ + 10 50 \ + "$remote_directory" \ + 2> line + if [ $? -eq 3 ]; then state=port + else + remote_directory=`cat line` + state=devices + fi + fi + ;; + devices) + if [ -n "$override_devices_to_send" ]; then + devices_to_send="$override_devices_to_send" + state=roots + else + # Returns the list of possible devices in $devices + search_devices + + log devices returned by search_devices: $devices + + deviceslist="" + for d in $devices; do + if word_in_list $d $devices_to_send; then + stat=on + else + stat=off + fi + deviceslist="$deviceslist $d /dev/$d $stat" + done + + dialog \ + --extra-button --extra-label "Back" --nocancel \ + --single-quoted \ + --checklist "Pick which local disk images to send" 10 50 5 \ + $deviceslist \ + 2> line + if [ $? -eq 3 ]; then state=directory + else + devices_to_send=`cat line` + state=roots + log user selected devices_to_send = $devices_to_send fi fi ;; + roots) + if [ -n "$override_root_filesystems" ]; then + root_filesystems="$override_root_filesystems" + state=verify + else + # Returns the list of possible partitions / LVs in $parts + search_parts $devices_to_send + + log filesystems returned by search_parts: $parts + + if [ -z "$parts" ]; then + root_filesystems="" + state=verify + else + partslist="" + for r in $parts; do + if word_in_list $r $root_filesystems; then + stat=on + else + stat=off + fi + partslist="$partslist $r /dev/$r $stat" + done + dialog \ + --extra-button --extra-label "Back" --nocancel \ + --single-quoted \ + --checklist "Pick partition(s) containing root (/) filesystem" 10 70 5 \ + $partslist \ + 2> line + if [ $? -eq 3 ]; then state=devices + else + root_filesystems=`cat line` + state=verify + log user selected root_filesystems = $root_filesystems + fi + fi + fi + ;; + verify) + if [ "$greeting" = "no" ]; then + state=exit + else + dialog \ + --title "Summary" \ + --yesno "Transport: $remote_transport\nRemote host: $remote_host\nRemote port: $remote_port\nRemote directory (ssh only): $remote_directory\nDisks to send: $devices\nRoot filesystem(s): $root_filesystems\n\nProceed with these settings?" \ + 23 70 + if [ $? -eq 1 ]; then + state=transport + else + state=exit + fi + fi + ;; *) echo "Invalid state: $state" - state=hostname + state=transport ;; esac done clear -echo remote_host $remote_host -echo remote_port $remote_port \ No newline at end of file + +# Mount the root filesystem(s) using device-mapper snapshots. + + + + + + + + +# This file must end with a newline + -- 1.8.3.1