3 # virt-p2v.sh is a shell script which performs a physical to
4 # virtual conversion of local disks.
6 # Copyright (C) 2007 Red Hat Inc.
7 # Written by Richard W.M. Jones <rjones@redhat.com>
11 export PATH=/usr/sbin:/sbin:/usr/local/bin:/usr/kerberos/bin:/usr/bin:/bin
13 # The defaults here make a generic virt-p2v.sh script, but if you want
14 # to build a partially-/fully-automatic P2V solution, then you can set
15 # these variables to something, and the script won't ask the user for
18 # Use 'greeting=no' to suppress greeting and final verification screens.
24 # can be 'ssh' or 'tcp'
25 override_remote_transport=
27 # eg. override_remote_directory=/var/lib/xen/images
28 # (only if override_remote_transport is 'ssh')
29 override_remote_directory=
31 # list of local physical devices to examine, separated by spaces,
33 # this is usually a list of whole disk devices (eg. "sda")
34 override_physical_devices=""
36 # list of filesystems to send (eg. "sda1 sda2 VolGroup00/LogVol00")
37 override_filesystems_to_send=""
39 # list of root filesystems to examine (eg. "sda3" or
40 # "VolGroup00/LogVol00")
41 override_root_filesystems=""
43 # network configuration
44 # - if empty, ask user
45 # - if contains a filesystem name (eg. "VolGroup00/LogVol00") try to
46 # auto-configure network from that
47 # (XXX needs to contain more ways to override in future)
50 #----------------------------------------------------------------------
54 echo "$@" >> /tmp/virt-p2v.log
57 #----------------------------------------------------------------------
60 # 'matches_regexp regexp string' returns true if string matches regexp.
61 # It uses egrep for extended regexps.
62 function matches_regexp {
64 echo "$@" | grep -Esq "$re"
67 # 'string_contains needle haystack' returns true if needle in haystack.
68 function string_contains {
69 echo "$2" | grep -Fsq "$1"
72 # 'word_in_list word ...'. '...' (list of parameters) is a list of
73 # words. Returns true if 'word' is in the list.
74 function word_in_list {
75 local word="$1"; shift
78 [ "$w" = "$word" ] && return 0
83 #----------------------------------------------------------------------
84 # Device mapper snapshotting.
86 # Create a device-mapper snapshot of a device with ramdisk overlay.
89 # creates a snapshot of /dev/sda1 called /dev/mapper/snap
93 # Get size of the device in sectors.
94 local sectors=`blockdev --getsize /dev/$dev`
96 dmsetup create ${name}_org \
97 --table="0 $sectors snapshot-origin /dev/$dev"
98 if [ $? -ne 0 ]; then exit 1; fi
99 dmsetup create $name \
100 --table="0 $sectors snapshot /dev/mapper/${name}_org /dev/ram1 n 64"
101 if [ $? -ne 0 ]; then exit 1; fi
104 # Drop an existing snapshot created by snapshot function.
107 # drops a snapshot called /dev/mapper/snap
108 function drop_snapshot {
109 dmsetup remove /dev/mapper/$name
110 dmsetup remove /dev/mapper/${name}_org
113 #----------------------------------------------------------------------
114 # Searching for devices, partitions and LVs.
116 # Convert "/dev/foo" into "foo". Returns $device.
117 function strip_slash_dev {
118 device=$(echo "$1" | sed 's|^/dev/||')
121 # The 'lvs' utility returns devices like '/dev/sda2(0)'. I've no
122 # idea what the number in parentheses is, but remove /dev/ and the
124 function device_of_lvs_device {
126 device=$(echo "$device" | sed 's|(.*)$||')
129 # All functions in this section assume that physical block devices
130 # (things corresponding to hard disks in the system) are called
131 # hd[a-z]+ or sd[a-z]+
133 # Get list of physical block devices. Sets variable $devices
134 # to something like "sda sdb".
135 function search_devices {
136 devices=$(cd /sys/block && /bin/ls -d [hs]d*)
139 # Get list of partitions from a physical block device. Sets
140 # variable $partitions to something like "sda1 sda2 sda3".
141 # See also: search_parts
142 function get_partitions {
143 partitions=$(cd /sys/block/"$1" && /bin/ls -d "$1"*)
146 # Given a partition (eg. "sda1" or "VolGroup00/LogVol00") return
147 # the physical block device which contains it. In the case where
148 # we are given an LV, this is supposed to do the right thing and
149 # go back through the layers until it gets to the physical block
150 # device. Returns $device set to something like "sda".
151 function block_device_of_part {
152 local part="$1" vg_name lv_name pv
154 if matches_regexp '^[hs]d[a-z]+[0-9]*$' "$part"; then
155 device=$(echo "$part" | sed 's|[0-9]*$||')
159 # Not a partition name, so it's a LV name. Ask lvs to
161 lvs --noheadings -o vg_name,lv_name,devices > lvs
162 while read vg_name lv_name pv; do
163 if [ "$vg_name/$lv_name" = "$part" \
164 -o "mapper/$vg_name-$lv_name" = "$part" ]; then
165 device_of_lvs_device "$pv" ;# sets $device to (eg.) "sda1"
166 block_device_of_part "$device"
171 # Help ... block device not found.
172 log block_device_of_part: block device cannot be resolved: $part
176 # search_parts $devices examines the list of devices and looks for
177 # partitions or LVs on just those devices. Sets variable $parts to
178 # something like "sda1 VolGroup00/LogVol00".
179 function search_parts {
180 local vg_name lv_name pv pvs="" device partition partitions
184 lvs --noheadings -o vg_name,lv_name,devices > lvs
185 while read vg_name lv_name pv; do
186 # Get just the partition name (eg. "sda2").
187 device_of_lvs_device "$pv"; pv="$device"
188 # Get just the block device name (eg. "sda").
189 block_device_of_part "$pv"
191 log search_parts: pv $pv device $device lv $vg_name/$lv_name
193 # A device in our list of devices to consider?
194 if word_in_list $device "$@"; then
195 log search_parts: adding $vg_name/$lv_name
196 parts="$parts $vg_name/$lv_name"
201 log search_parts: after lvs, parts $parts pvs $pvs
203 # Look for non-LVM partitions, but ignore any which are PVs
204 # as identified above.
205 for device in "$@"; do
206 get_partitions "$device"
207 for partition in $partitions; do
208 if ! word_in_list $partition $pvs; then
209 log search_parts: adding $partition
210 parts="$parts $partition"
215 # $parts is returned.
218 #----------------------------------------------------------------------
219 # Network configuration functions.
221 # `auto_network $fs' mounts /dev/$fs read-only on /mnt/root and then
222 # tries to configure the network from it. Returns true or false.
223 function auto_network {
224 if ! mount -o ro /dev/$1 /mnt; then return 1; fi
228 mv network network.saved
229 mv networking networking.saved
230 mv network-scripts network-scripts.saved
232 ln -s /mnt/etc/sysconfig/network
233 ln -s /mnt/etc/sysconfig/networking
234 ln -s /mnt/etc/sysconfig/network-scripts
236 /etc/init.d/network start
239 rm network networking network-scripts
240 mv network.saved network
241 mv networking.saved networking
242 mv network-scripts.saved network-scripts
245 if [ "$greeting" != "no" ]; then
246 echo "Did automatic network configuration work?"
247 echo "(Hint: if not sure, there is a shell on console [ALT] [F2])"
251 if [ "$line" = "y" -o "$line" = "yes" ]; then return 0; fi
255 # In non-interactive mode, return the status of /etc/init.d/network.
260 #----------------------------------------------------------------------
261 # General script setup.
263 # We can safely write files into /tmp without modifying anything.
266 #----------------------------------------------------------------------
267 # Dialog with the user.
270 log virt-p2v starting up at `date`
272 if [ "$greeting" != "no" ]; then
275 --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
278 # Get configuration from the user.
280 # To make the [Back] button work, we make this into a looping state
281 # machine. Each state asks a question and jumps to the next state
282 # (unless [Back] is pressed, in which case it jumps back to the previous
283 # state). Finally the 'exit' state causes us to quit the loop.
286 remote_directory=/var/lib/xen/images
289 while [ "$state" != "exit" ]; do
290 log next state = $state
293 if [ -n "$override_remote_transport" ]; then
294 remote_transport="$override_remote_transport"
297 case "$remote_transport" in
298 tcp) ssh_on=off; tcp_on=on;;
299 *) ssh_on=on; tcp_on=off;;
303 --radiolist "Connection type" 10 50 2 \
304 "ssh" "SSH (secure shell - recommended)" $ssh_on \
305 "tcp" "TCP socket" $tcp_on \
307 remote_transport=`cat line`
312 if [ -n "$override_remote_host" ]; then
313 remote_host="$override_remote_host"
317 --extra-button --extra-label "Back" --nocancel \
318 --inputbox "Remote host" 10 50 "$remote_host" \
320 if [ $? -eq 3 ]; then state=transport
322 remote_host=`cat line`
323 if [ -n "$remote_host" ]; then state=port; fi
324 # else stay in same state and demand a hostname!
329 if [ -n "$override_remote_port" ]; then
330 remote_port="$override_remote_port"
334 --extra-button --extra-label "Back" --nocancel \
335 --inputbox "Remote port" 10 50 "$remote_port" \
337 if [ $? -eq 3 ]; then state=hostname
339 remote_port=`cat line`
345 if [ "$remote_transport" = "tcp" ]; then
347 elif [ -n "$override_remote_directory" ]; then
348 remote_directory="$override_remote_directory"
352 --extra-button --extra-label "Back" --nocancel \
353 --inputbox "Remote directory containing guest images" \
355 "$remote_directory" \
357 if [ $? -eq 3 ]; then state=port
359 remote_directory=`cat line`
365 # Block devices configuration.
367 if [ -n "$override_physical_devices" ]; then
368 physical_devices="$override_physical_devices"
371 # Returns the list of physical devices in $devices
374 log devices returned by search_devices: $devices
377 for d in $devices; do
378 if word_in_list $d $physical_devices; then
383 gigs=$(($(blockdev --getsize /dev/$d)/2/1024/1024))
384 deviceslist="$deviceslist $d /dev/$d(${gigs}GB) $stat"
388 --extra-button --extra-label "Back" --nocancel \
390 --checklist "Pick local disks to examine" 15 50 8 \
393 if [ $? -eq 3 ]; then state=directory
395 physical_devices=`cat line`
401 if [ -n "$override_filesystems_to_send" ]; then
402 filesystems_to_send="$override_filesystems_to_send"
405 # Returns the list of possible partitions / LVs in $parts
406 search_parts $physical_devices
408 log partitions returned by search_parts: $parts
412 if word_in_list $p $filesystems_to_send; then
417 gigs=$(($(blockdev --getsize /dev/$p)/2/1024/1024))
418 partslist="$partslist $p /dev/$p(${gigs}GB) $stat"
422 --extra-button --extra-label "Back" --nocancel \
424 --checklist "Pick filesystems to send" 15 70 8 \
427 if [ $? -eq 3 ]; then state=devices
429 filesystems_to_send=`cat line`
435 if [ -n "$override_root_filesystems" ]; then
436 root_filesystems="$override_root_filesystems"
440 for r in $filesystems_to_send; do
441 if word_in_list $r $root_filesystems; then
446 partslist="$partslist $r /dev/$r $stat"
450 --extra-button --extra-label "Back" --nocancel \
452 --checklist "Pick partition(s) containing a root (/) filesystem" 10 70 5 \
455 if [ $? -eq 3 ]; then state=filesystems
457 root_filesystems=`cat line`
463 # Network configuration.
465 if [ -n "$override_network" ]; then
466 network="$override_network"
470 for r in $root_filesystems; do
471 if [ "$r" = "$network" ]; then
476 partslist="$partslist $r Auto-configure $stat"
480 --extra-button --extra-label "Back" --nocancel \
481 --radiolist "Network configuration" 10 70 5 \
483 "ask" "Manual configuration" off \
484 "sh" "Configure from the shell" off \
486 if [ $? -eq 3 ]; then state=roots
494 # Verify configuration.
496 if [ "$greeting" = "no" ]; then
501 --yesno "Transport: $remote_transport\nRemote host: $remote_host\nRemote port: $remote_port\nRemote directory (ssh only): $remote_directory\nFilesystems to send: $filesystems_to_send\nRoot filesystem(s): $root_filesystems\nNetwork configuration: $network\n\nProceed with these settings?" \
503 if [ $? -eq 1 ]; then
511 echo "Invalid state: $state"
519 # Now see if we can get a network configuration.
520 log network configuration $network
524 echo "Network configuration"
526 echo "Please configure the network from this shell."
528 echo "You can modify any file under /etc (especially /etc/sysconfig)."
529 echo "No changes are made to the system disks unless you mount them"
530 echo "and explicitly modify them."
532 echo "When finished, exit with ^D or exit"
538 # XXX Not implemented
539 echo "Sorry, we didn't implement this one yet."
544 echo "Trying to auto-configure network from /dev/$network ..."
546 if ! auto_network "$network"; then
547 echo "Auto-configuration failed. Starting a shell."
553 # Mount the root filesystem(s) using device-mapper snapshots.
554 # The snapshots are called snap0, snap1, etc. with the number
555 # corresponding to its index in $root_filesystems array.
568 # This file must end with a newline