Moving or copying Logical Volumes

LVM is a wonderful feature of Linux which I use all over the place.

It has always struck me as somewhat odd, however, that there is no command to copy or move a Logical Volume from one Volume Group to another.

Therefore I came up with a Bash script to do the job for me, and whilst I was at it, I made it work between servers as well.

lvmove
#!/bin/bash

# Copy or move a Logical Volume from one Volume Group to another
# possibly on another machine

if [ -z "$4" ]
then
  echo "Usage: $0 LV fromVG [host:]toVG <copy|keep|move> [nocheck]"
  exit 1
fi

# 1. Make sure LV exists on source VG
# 2. Parse destination VG - if it contains a colon, it means hostname:VG
# 3. Make sure LV does not exist on destination VG
# 4. Get size of source LV
# 5. Create destination LV
# 6. Copy content
# 7. Compare source vs. destination if required
# 8. If match, delete source and any log files

host=
LV=$1
fromVG=$2
toVG=$3
task=$4
check=$5

[ "$toVG" = "${toVG/:}" ] || host="${toVG%%:*}"
[ -n "$host" ] && toVG="${toVG##*:}"

needed="ddrescue lvdisplay diff"
for need in $needed
do
  if ! which $need &>/dev/null
  then
    echo "Sorry, I need $need installed before this can work."
    exit 1
  fi
done

if lvdisplay $fromVG/$LV &>/dev/null
then
  if [ -n "$host" ]
  then
    if ssh $host "lvdisplay $toVG/$LV 2>/dev/null" | grep -q $LV
    then
      echo "Logical Volume $LV already exists in Volume Group $toVG on $host"
      exit 1
    fi
  else
    if lvdisplay $toVG/$LV &>/dev/null
    then
      echo "Logical Volume $LV already exists in Volume Group $toVG"
      exit 1
    fi
  fi


  lvsize=`lvdisplay $fromVG/$LV | grep "Current LE" | tr -s ' ' | cut -d' ' -f4`

  if [ -n "$host" ]
  then
    echo "Creating new LV $LV on destination VG $toVG on $host"
    ssh $host "lvcreate -y -l $lvsize -n $LV $toVG"
    echo "Copying content (be patient)..."
    dd if=/dev/$fromVG/$LV bs=1M | ssh $host dd bs=1M of=/dev/$toVG/$LV
  else
    echo "Creating new LV $LV on destination VG $toVG"
    lvcreate -y -l $lvsize -n $LV $toVG
    echo "Copying content..."
    ddrescue -f /dev/$fromVG/$LV /dev/$toVG/$LV /tmp/$LV.log
  fi
  OK=no
  if [ "$check" = "nocheck" ]
  then
    OK=yes
  else
    if [ -n "$host" ]
    then
      echo "Checking the copy..."
      diff -q <(md5sum /dev/$fromVG/$LV | cut -d' ' -f1) <(ssh $host md5sum /dev/$toVG/$LV | cut -d' ' -f1) && OK=yes
    else
      echo "Checking the copy (this will take approximately as long as the copy did)..."
      diff -q /dev/$fromVG/$LV /dev/$toVG/$LV && OK=yes
    fi
  fi
  if [ "$OK" = "yes" ]
  then
    if [ "$task" = "keep" -o "$task" = "copy" ]
    then
      rm -f /tmp/$LV.log
      echo "$LV successfully copied from $fromVG to $toVG"
      exit 0
    elif [ "$task" = "move" ]
    then
      lvremove -y $fromVG/$LV
      rm -f /tmp/$LV.log
      echo "$LV successfully moved from $fromVG to $toVG"
      exit 0
    else
      echo "$LV successfully copied from $fromVG to $toVG"
      echo "but not sure whether you wanted to delete the original, therefore keeping both."
      exit 0
    fi
  else
    echo "Something went wrong."
    exit 1
  fi
else
  echo "Logical Volume $LV not found in Volume Group $fromVG"
  exit 1
fi

The script uses ddrescue if you're working on a single server, but has to use dd (which is just as effective, but produces no progress output) if you're working between two machines.

Between two machines, the script works best if you have passwordless public-key login access between the machines (but will still prompt for a password (several times) if this is not in place).


Go up
Return to main index.