+++ /dev/null
-#!/bin/bash
-#
-# virt-p2v.sh is a shell script which performs a physical to
-# virtual conversion of local disks.
-#
-# Copyright (C) 2007 Red Hat Inc.
-# Written by Richard W.M. Jones <rjones@redhat.com>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-# $Id$
-
-# Because we're running from a start-up script, we don't have much
-# of a login environment, so set one up.
-export PATH=/usr/sbin:/sbin:/usr/local/bin:/usr/kerberos/bin:/usr/bin:/bin
-export HOME=/root
-export LOGNAME=root
-
-# The defaults here make a generic virt-p2v.sh script, but if you want
-# to build a partially-/fully-automatic P2V solution, then you can set
-# these variables to something, and the script won't ask the user for
-# input.
-
-# Use 'greeting=no' to suppress greeting and final verification screens.
-greeting=
-
-override_remote_host=
-override_remote_port=
-
-# can be 'ssh' or 'tcp'
-override_remote_transport=
-
-# eg. override_remote_directory=/var/lib/xen/images
-# (only if override_remote_transport is 'ssh')
-override_remote_directory=
-
-# list of local physical 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=""
-
-# the root filesystem containing /etc/fstab (eg. "sda3" or
-# "VolGroup00/LogVol00")
-override_root_filesystem=""
-
-# network configuration
-# - if empty, ask user
-# - "auto" means try to autoconfigure from root filesystem
-# (XXX needs to contain more ways to override in future)
-override_network=""
-
-#----------------------------------------------------------------------
-# General script setup and logging.
-
-exec 2>> /tmp/virt-p2v.log
-
-function log {
- echo "$@" 1>&2
-}
-
-log
-log virt-p2v starting up at `date`
-
-# The first and only parameter must be the tty. Connect
-# stdin/stdout to this device.
-if [ -n "$1" ]; then
- log connecting to /dev/$1
- exec </dev/$1 >/dev/$1
-fi
-
-# We can safely write files into /tmp without modifying anything.
-cd /tmp
-
-#----------------------------------------------------------------------
-# 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
-}
-
-#----------------------------------------------------------------------
-# I/O functions
-
-# Use the function read_line instead of the shell built-in read.
-# It reads from the console.
-function read_line {
- read "$*"
-}
-
-# Launch a bash subshell connected to the console.
-function shell {
- PS1='\u@\h:\w\$ ' bash
-}
-
-#----------------------------------------------------------------------
-# Device mapper snapshotting.
-
-next_ramdisk=1
-
-# Create a device-mapper snapshot of a device with ramdisk overlay.
-# Example:
-# snapshot sda1 snap
-# creates a snapshot of /dev/sda1 called /dev/mapper/snap
-function snapshot {
- local dev=$1 name=$2
-
- # Next free ramdisk (/dev/ram$i)
- local i=$next_ramdisk
- next_ramdisk=$(($next_ramdisk+1))
-
- # Get size of the device in sectors.
- local sectors=`blockdev --getsize /dev/$dev`
-
- dmsetup create ${name}_org \
- --table="0 $sectors snapshot-origin /dev/$dev"
- if [ $? -ne 0 ]; then exit 1; fi
- dmsetup create $name \
- --table="0 $sectors snapshot /dev/mapper/${name}_org /dev/ram$i n 64"
- if [ $? -ne 0 ]; then exit 1; fi
-}
-
-# Drop an existing snapshot created by snapshot function.
-# Example:
-# drop_snapshot snap
-# drops a snapshot called /dev/mapper/snap
-function drop_snapshot {
- local name=$1
-
- dmsetup remove /dev/mapper/$name
- 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 {
- devices1=$(cd /sys/block && /bin/ls -d [hs]d*)
- log search_devices: devices1: $devices1
- # Ignore devices which fail 'blockdev --getsize' - probably
- # removable devices or other strange ones.
- devices=""
- for d in $devices1; do
- if blockdev --getsize /dev/$d > /dev/null; then
- devices="$devices${devices:+ }$d"
- fi
- done
- log search_devices: devices: $devices
-}
-
-# 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.
-}
-
-# This generates a snapshot device name from a device name.
-# eg. sda -> snap_sda
-# Sets $dname.
-function snap_name {
- dname=`echo -n snap_"$1" | tr -cs '[:alnum:]' _`
-}
-
-#----------------------------------------------------------------------
-# Network configuration functions.
-
-# `auto_network' tries to configure the network from the
-# root filesystem. Returns true or false.
-function auto_network {
- # Make sure this file exists, otherwise Fedora gives a warning.
- touch /etc/resolv.conf
-
- pushd /etc/sysconfig
-
- mv network network.saved
- mv networking networking.saved
- mv network-scripts network-scripts.saved
-
- # Originally I symlinked these, but that causes dhclient to
- # keep open /mnt/root (as its cwd is in network-scripts subdir).
- # So now we will copy them recursively instead.
- cp -r /mnt/root/etc/sysconfig/network .
- cp -r /mnt/root/etc/sysconfig/networking .
- cp -r /mnt/root/etc/sysconfig/network-scripts .
-
- /etc/init.d/network start
- local status=$?
-
- rm -rf network networking network-scripts
- mv network.saved network
- mv networking.saved networking
- mv network-scripts.saved network-scripts
-
- popd
-
- ping -c 3 $remote_host
-
- if [ "$greeting" != "no" ]; then
- echo "Did automatic network configuration work?"
- echo "(Hint: if not sure, there is a shell on console [ALT] [F2])"
- echo -n " (y/n) "
- local line
- read_line line
- if [ "$line" = "y" -o "$line" = "yes" ]; then return 0; fi
- return 1
- fi
-
- # In non-interactive mode, return the status of /etc/init.d/network.
- return $status
-}
-
-#----------------------------------------------------------------------
-# Dialog with the user.
-
-if [ "$greeting" != "no" ]; then
- dialog \
- --title "virt-p2v" \
- --msgbox "\nWelcome to virt-p2v, a live CD for migrating a physical machine to a virtualized host.\n\nTo continue press the Return key.\n\nTo get a shell you can use [ALT] [F2] and log in as root with no password." 17 50
-fi
-
-# Get configuration from the user.
-
-# To make the [Back] button work, we make this into a looping state
-# machine. Each state asks a question and jumps to the next state
-# (unless [Back] is pressed, in which case it jumps back to the previous
-# state). Finally the 'exit' state causes us to quit the loop.
-
-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 \
- --extra-button --extra-label "Back" --nocancel \
- --inputbox "Remote host" 10 50 "$remote_host" \
- 2> line
- 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 \
- --inputbox "Remote port" 10 50 "$remote_port" \
- 2> line
- if [ $? -eq 3 ]; then state=hostname
- else
- remote_port=`cat line`
- 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
- ;;
-
- # Block devices configuration.
- devices)
- if [ -n "$override_devices_to_send" ]; then
- devices_to_send="$override_devices_to_send"
- state=root
- else
- # Returns the list of physical 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
- log running blockdev --getsize /dev/$d
- gigs=$(($(blockdev --getsize /dev/$d)/2/1024/1024))
- log /dev/$d has size $gigs
- deviceslist="$deviceslist $d /dev/$d(${gigs}GB) $stat"
- done
- log deviceslist="$deviceslist"
-
- dialog \
- --extra-button --extra-label "Back" --nocancel \
- --single-quoted \
- --checklist "Pick disks to send" 15 50 8 \
- $deviceslist \
- 2> line
- if [ $? -eq 3 ]; then state=directory
- else
- devices_to_send=`cat line`
- state=root
- fi
- fi
- ;;
-
- # Root filesystem.
- root)
- if [ -n "$override_root_filesystem" ]; then
- root_filesystem="$override_root_filesystem"
- state=network
- else
- # Returns the list of possible partitions / LVs in $parts
- search_parts $devices_to_send
-
- log partitions returned by search_parts: $parts
-
- partslist=""
- for r in $parts; do
- if word_in_list $r $root_filesystem; then
- stat=on
- else
- stat=off
- fi
- partslist="$partslist $r /dev/$r $stat"
- done
-
- dialog \
- --extra-button --extra-label "Back" --nocancel \
- --single-quoted \
- --radiolist "Pick partition containing the root (/) filesystem" 10 70 5 \
- $partslist \
- 2> line
- if [ $? -eq 3 ]; then state=devices
- else
- root_filesystem=`cat line`
- state=network
- fi
- fi
- ;;
-
- # Network configuration.
- network)
- if [ -n "$override_network" ]; then
- network="$override_network"
- state=verify
- else
- dialog \
- --extra-button --extra-label "Back" --nocancel \
- --radiolist "Network configuration" 10 70 5 \
- "auto" "Auto-configure from root filesystem" on \
- "ask" "Manual configuration" off \
- "sh" "Configure from the shell" off \
- 2> line
- if [ $? -eq 3 ]; then state=root
- else
- network=`cat line`
- state=verify
- fi
- fi
- ;;
-
- # Verify configuration.
- 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_to_send\nRoot filesystem: $root_filesystem\nNetwork configuration: $network\n\nProceed with these settings?" \
- 18 70
- if [ $? -eq 1 ]; then
- state=transport
- else
- state=exit
- fi
- fi
- ;;
- *)
- echo "Invalid state: $state"
- state=transport
- ;;
- esac
-done
-
-clear
-
-#----------------------------------------------------------------------
-# De-activate all volume groups and switch to new dm-only LVM config.
-log deactivating volume groups
-
-vgchange -a n
-mv /etc/lvm/lvm.conf /etc/lvm/lvm.conf.old
-mv /etc/lvm/lvm.conf.new /etc/lvm/lvm.conf
-rm -f /etc/lvm/cache/.cache
-
-# Snapshot the block devices.
-for d in $devices_to_send; do
- snap_name $d
- log snapshotting block device /dev/$d to $dname ...
-
- snapshot $d $dname
-
- # The block devices are whole disks. Use kpartx to repartition them.
- log running kpartx -a /dev/mapper/$dname ...
- kpartx -a /dev/mapper/$dname
-done
-
-# Rescan for LVs.
-log running vgscan
-vgscan
-vgchange -a y
-
-# Mount the root filesystem on /mnt/root. If it's a physical
-# device then we want to mount (eg) /dev/mapper/snap_sda2.
-# If it's a LVM device then we can just mount the LVM partition.
-
-log mount $root_filesystem as /mnt/root
-
-snap_name $root_filesystem
-root_filesystem_dname="$dname"
-
-if [ -b /dev/mapper/$root_filesystem_dname ]; then
- mount /dev/mapper/$root_filesystem_dname /mnt/root
-else
- mount /dev/$root_filesystem /mnt/root
-fi
-
-#----------------------------------------------------------------------
-# Now see if we can get a network configuration.
-log network configuration $network
-
-case "$network" in
- sh)
- echo "Network configuration"
- echo
- echo "Please configure the network from this shell."
- echo
- echo "When finished, exit with ^D or exit"
- echo
- shell
- ;;
-
- ask)
- # XXX Not implemented
- echo "Sorry, we didn't implement this one yet."
- shell
- ;;
-
- auto)
- echo "Trying to auto-configure network from root filesystem ..."
- echo
- if ! auto_network; then
- echo "Auto-configuration failed. Starting a shell."
- echo
- shell
- fi
-esac
-
-#----------------------------------------------------------------------
-# Rewrite /mnt/root/etc/fstab
-
-log rewriting /etc/fstab
-
-cp /mnt/root/etc/fstab /mnt/root/etc/fstab.p2vsaved
-while read dev mountpoint fstype options freq passno; do
- # If device is /dev/sd* then on the target fullvirt machine
- # it will be /dev/hd*
- if matches_regexp "^/dev/sd[[:alpha:]]+[[:digit:]]+$" "$dev"; then
- dev=`echo $dev | sed 's|^/dev/sd|/dev/hd|'`
- fi
-
- # Print out again with all the fields nicely lined up.
- printf "%-23s %-23s %-7s %-15s %d %d\n" \
- "$dev" "$mountpoint" "$fstype" "$options" "$freq" "$passno"
-done < /mnt/root/etc/fstab.p2vsaved > /mnt/root/etc/fstab
-
-#----------------------------------------------------------------------
-# XXX Look for other files which might need to be changed here ...
-
-
-
-# We've now finished with /mnt/root (the real root filesystem),
-# so unmount it and synch everything.
-umount /mnt/root
-sync
-
-#----------------------------------------------------------------------
-# Send the device snapshots (underlying device + changes in ramdisk)
-# to the remote server.
-
-log sending disks
-
-# XXX Effectively this is using the hostname derived from network
-# configuration, but we might want to ask the user instead.
-# XXX How do we ensure that we won't overwrite target files? Currently
-# tries to use the current date as a uniquifier.
-
-# Names will be something like
-# p2v-oirase-200709011249-hda.img
-basename=p2v-`hostname -s|tr -cd '[0-9a-zA-Z]'`-`date +'%Y%m%d%H%M'`
-
-for dev in $devices_to_send; do
- rdev=`echo $dev | sed 's|^sd|hd|'`
- name="$basename-$rdev.img"
- log sending $dev to $name
-
- snap_name $dev
-
- sectors=`blockdev --getsize /dev/mapper/$dname`
-
- gigs=$(($sectors/2/1024/1024))
- echo "Sending /dev/$dev (${gigs} GB) to remote machine"
-
- dd if=/dev/mapper/$dname | gzip --best |
- case "$remote_transport" in
- ssh)
- ssh -p "$remote_port" "$remote_host" \
- "zcat > $remote_directory/$name"
- ;;
- tcp)
- echo "p2v $name $sectors" > header
- echo > newline
- cat header - newline | nc "$remote_host" "$remote_port"
- ;;
- esac
-done
-
-
-#----------------------------------------------------------------------
-# Clean up.
-
-#for d in $devices_to_send; do
-# snap_name $d
-# kpartx -d /dev/mapper/$dname
-# drop_snapshot $dname
-#done
-
-# This file must end with a newline
-