# 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
# 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=
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.
# 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"
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.
#----------------------------------------------------------------------
# Dialog with the user.
+log
+log virt-p2v starting up at `date`
+
if [ "$greeting" != "no" ]; then
dialog \
--title "virt-p2v" \
# (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 \
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
+