Basic device detection.
authorrjones <devnull@localhost>
Mon, 10 Sep 2007 16:45:20 +0000 (17:45 +0100)
committerrjones <devnull@localhost>
Mon, 10 Sep 2007 16:45:20 +0000 (17:45 +0100)
Makefile
inittab [new file with mode: 0644]
livecd-post.sh.in
p2v.init
virt-p2v.sh

index f098f29..0124250 100644 (file)
--- 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 (file)
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.
+
index 82427ae..2f8d7e3 100644 (file)
@@ -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
index cb54b24..153b148 100644 (file)
--- 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
+
index 44184c7..fa2e705 100755 (executable)
@@ -6,6 +6,7 @@
 # By Richard W.M. Jones <rjones@redhat.com>
 #
 # 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
+