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 devices to send, separated by spaces, if empty ask user
32 # this is usually a list of whole disk devices (eg. "sda")
33 override_devices_to_send=""
35 # list of root filesystems to examine (eg. "sda3" or
36 # "VolGroup00/LogVol00")
37 override_root_filesystems=""
39 #----------------------------------------------------------------------
43 echo "$@" >> /tmp/virt-p2v.log
46 #----------------------------------------------------------------------
49 # 'matches_regexp regexp string' returns true if string matches regexp.
50 # It uses egrep for extended regexps.
51 function matches_regexp {
53 echo "$@" | grep -Esq "$re"
56 # 'string_contains needle haystack' returns true if needle in haystack.
57 function string_contains {
58 echo "$2" | grep -Fsq "$1"
61 # 'word_in_list word ...'. '...' (list of parameters) is a list of
62 # words. Returns true if 'word' is in the list.
63 function word_in_list {
64 local word="$1"; shift
67 [ "$w" = "$word" ] && return 0
72 #----------------------------------------------------------------------
73 # Device mapper snapshotting.
75 # Create a device-mapper snapshot of a device with ramdisk overlay.
78 # creates a snapshot of /dev/sda1 called /dev/mapper/snap
85 # Get size of the device in sectors.
86 local sectors=`blockdev --getsize /dev/$dev`
88 dmsetup create ${name}_org \
89 --table="0 $sectors snapshot-origin /dev/$dev"
90 dmsetup create $name \
91 --table="0 $sectors snapshot /dev/mapper/${name}_org /dev/ram1 n 64"
94 # Drop an existing snapshot created by snapshot function.
97 # drops a snapshot called /dev/mapper/snap
98 function drop_snapshot {
99 dmsetup remove /dev/mapper/$name
100 dmsetup remove /dev/mapper/${name}_org
103 #----------------------------------------------------------------------
104 # Searching for devices, partitions and LVs.
106 # Convert "/dev/foo" into "foo". Returns $device.
107 function strip_slash_dev {
108 device=$(echo "$1" | sed 's|^/dev/||')
111 # The 'lvs' utility returns devices like '/dev/sda2(0)'. I've no
112 # idea what the number in parentheses is, but remove /dev/ and the
114 function device_of_lvs_device {
116 device=$(echo "$device" | sed 's|(.*)$||')
119 # All functions in this section assume that physical block devices
120 # (things corresponding to hard disks in the system) are called
121 # hd[a-z]+ or sd[a-z]+
123 # Get list of physical block devices. Sets variable $devices
124 # to something like "sda sdb".
125 function search_devices {
126 devices=$(cd /sys/block && /bin/ls -d [hs]d*)
129 # Get list of partitions from a physical block device. Sets
130 # variable $partitions to something like "sda1 sda2 sda3".
131 # See also: search_parts
132 function get_partitions {
133 partitions=$(cd /sys/block/"$1" && /bin/ls -d "$1"*)
136 # Given a partition (eg. "sda1" or "VolGroup00/LogVol00") return
137 # the physical block device which contains it. In the case where
138 # we are given an LV, this is supposed to do the right thing and
139 # go back through the layers until it gets to the physical block
140 # device. Returns $device set to something like "sda".
141 function block_device_of_part {
142 local part="$1" vg_name lv_name pv
144 if matches_regexp '^[hs]d[a-z]+[0-9]*$' "$part"; then
145 device=$(echo "$part" | sed 's|[0-9]*$||')
149 # Not a partition name, so it's a LV name. Ask lvs to
151 lvs --noheadings -o vg_name,lv_name,devices > lvs
152 while read vg_name lv_name pv; do
153 if [ "$vg_name/$lv_name" = "$part" \
154 -o "mapper/$vg_name-$lv_name" = "$part" ]; then
155 device_of_lvs_device "$pv" ;# sets $device to (eg.) "sda1"
156 block_device_of_part "$device"
161 # Help ... block device not found.
162 log block_device_of_part: block device cannot be resolved: $part
166 # search_parts $devices examines the list of devices and looks for
167 # partitions or LVs on just those devices. Sets variable $parts to
168 # something like "sda1 VolGroup00/LogVol00".
169 function search_parts {
170 local vg_name lv_name pv pvs="" device partition partitions
174 lvs --noheadings -o vg_name,lv_name,devices > lvs
175 while read vg_name lv_name pv; do
176 # Get just the partition name (eg. "sda2").
177 device_of_lvs_device "$pv"; pv="$device"
178 # Get just the block device name (eg. "sda").
179 block_device_of_part "$pv"
181 log search_parts: pv $pv device $device lv $vg_name/$lv_name
183 # A device in our list of devices to consider?
184 if word_in_list $device "$@"; then
185 log search_parts: adding $vg_name/$lv_name
186 parts="$parts $vg_name/$lv_name"
191 log search_parts: after lvs, parts $parts pvs $pvs
193 # Look for non-LVM partitions, but ignore any which are PVs
194 # as identified above.
195 for device in "$@"; do
196 get_partitions "$device"
197 for partition in $partitions; do
198 if ! word_in_list $partition $pvs; then
199 log search_parts: adding $partition
200 parts="$parts $partition"
205 # $parts is returned.
208 #----------------------------------------------------------------------
209 # General script setup.
211 # We can safely write files into /tmp without modifying anything.
214 #----------------------------------------------------------------------
215 # Dialog with the user.
218 log virt-p2v starting up at `date`
220 if [ "$greeting" != "no" ]; then
223 --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
226 # Get configuration from the user.
228 # To make the [Back] button work, we make this into a looping state
229 # machine. Each state asks a question and jumps to the next state
230 # (unless [Back] is pressed, in which case it jumps back to the previous
231 # state). Finally the 'exit' state causes us to quit the loop.
234 remote_directory=/var/lib/xen/images
237 while [ "$state" != "exit" ]; do
238 log next state = $state
241 if [ -n "$override_remote_transport" ]; then
242 remote_transport="$override_remote_transport"
245 case "$remote_transport" in
246 tcp) ssh_on=off; tcp_on=on;;
247 *) ssh_on=on; tcp_on=off;;
251 --radiolist "Connection type" 10 50 2 \
252 "ssh" "SSH (secure shell - recommended)" $ssh_on \
253 "tcp" "TCP socket" $tcp_on \
255 remote_transport=`cat line`
260 if [ -n "$override_remote_host" ]; then
261 remote_host="$override_remote_host"
265 --extra-button --extra-label "Back" --nocancel \
266 --inputbox "Remote host" 10 50 "$remote_host" \
268 if [ $? -eq 3 ]; then state=transport
270 remote_host=`cat line`
271 if [ -n "$remote_host" ]; then state=port; fi
272 # else stay in same state and demand a hostname!
277 if [ -n "$override_remote_port" ]; then
278 remote_port="$override_remote_port"
282 --extra-button --extra-label "Back" --nocancel \
283 --inputbox "Remote port" 10 50 "$remote_port" \
285 if [ $? -eq 3 ]; then state=hostname
287 remote_port=`cat line`
293 if [ "$remote_transport" = "tcp" ]; then
295 elif [ -n "$override_remote_directory" ]; then
296 remote_directory="$override_remote_directory"
300 --extra-button --extra-label "Back" --nocancel \
301 --inputbox "Remote directory containing guest images" \
303 "$remote_directory" \
305 if [ $? -eq 3 ]; then state=port
307 remote_directory=`cat line`
313 if [ -n "$override_devices_to_send" ]; then
314 devices_to_send="$override_devices_to_send"
317 # Returns the list of possible devices in $devices
320 log devices returned by search_devices: $devices
323 for d in $devices; do
324 if word_in_list $d $devices_to_send; then
329 deviceslist="$deviceslist $d /dev/$d $stat"
333 --extra-button --extra-label "Back" --nocancel \
335 --checklist "Pick which local disk images to send" 10 50 5 \
338 if [ $? -eq 3 ]; then state=directory
340 devices_to_send=`cat line`
342 log user selected devices_to_send = $devices_to_send
347 if [ -n "$override_root_filesystems" ]; then
348 root_filesystems="$override_root_filesystems"
351 # Returns the list of possible partitions / LVs in $parts
352 search_parts $devices_to_send
354 log filesystems returned by search_parts: $parts
356 if [ -z "$parts" ]; then
362 if word_in_list $r $root_filesystems; then
367 partslist="$partslist $r /dev/$r $stat"
371 --extra-button --extra-label "Back" --nocancel \
373 --checklist "Pick partition(s) containing root (/) filesystem" 10 70 5 \
376 if [ $? -eq 3 ]; then state=devices
378 root_filesystems=`cat line`
380 log user selected root_filesystems = $root_filesystems
386 if [ "$greeting" = "no" ]; then
391 --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?" \
393 if [ $? -eq 1 ]; then
401 echo "Invalid state: $state"
409 # Mount the root filesystem(s) using device-mapper snapshots.
410 # For example, if asked to examine VolGroup00/LogVol00, then
411 # we might find that it is located on physical device sda, and
412 # the snapshot in that case will be called sda_snap.
420 # This file must end with a newline