Extending Linux LVM partitions script

Here’s a script I wrote a while a go to extend LVM partitions on Linux machines.

The script assumes that you have extended the existing underlying physical (or “virtual” if it’s a VM) storage device prior to execution. It will rescan the disks (skip with -f), resize the existing partition (basically just setting a different end sector), reboot, and run scripts to extend the actual file system after the reboot. There are other ways to extend the disk space including creating a new partition on the additional disk space, but I’ve decided against that approach in favor of a single-partition scheme for management/simplicity’s sake.

This script will work with VMs and physical servers alike. I’ve tested it with RHEL 6/7 and CentOS 6/7, but it should generally work with other Linux distributions as well.

You can get the most recent version of this script on Github here. If you have any suggestions or improvements (which I’m sure there is plenty of room for), feel free to drop a comment or an issue or a pull-request on Github.

 

#!/bin/bash
##########
# Script to resize a LVM Partition after extending the underlying disk device. Can be used on physical or virtual machines alike.
# Tested with CentOS6, RHEL6, CentOS7, RHEL7. This script is only intended for MBR partitioned disks and not for GPT.
#
# The script will first resize the partition by changing the partition end sector of the selected partition, and then after a reboot resize the filesystem.
# By default it rescans the SCSI bus to check a change in disk size if the disk was hot-extended, which is easy with VMs, and only then proceeds.
# If the extended disk size is recognized by the OS already, you can force resizing with the -f flag.
#
# Github: https://github.com/alpacacode/Homebrewn-Scripts
########

usage() {
  echo "Usage:
$0 [-p <LVM physical volume>] [-l <LVM logical volume>] [-f]
 
Options:
 -p physical LVM volume device to extend (check pvdisplay)
 -l logical LVM volume to extend (check lvdisplay)
 -f force extending without a disk rescan. Use this if the OS has detected the enlarged disk already, otherwise we first check whether the underlying disk is larger after a SCSI rescan
    
Example:
./lvmresize.sh -p /dev/sda2 -l /dev/VolGroup/lv_root -f
 
It is highly recommended you backup the boot sector of your disk before as a safety measure with something like the following:
# dd if=/dev/sda of=sda_mbr_backup.mbr bs=512 count=1 
# sfdisk -d /dev/sda > sda_mbr_backup.bak
 
You can restore the partition table then like this:
# dd if=sda_mbr_backup.mbr of=/dev/sda bs=512 count=1
# sfdisk /dev/sda < sda_mbr_backup.bak --force" 1>&2
  exit 1
} 

extenddisk_parted() {
  # Use parted because fdisk behavior can vary between OSes and scripting fdisk is non-deterministic.
  # Using parted resizepart would be easer, but RHEL/CentOS6 parted doesn't support resizepart
  echo -e "\nThis will now extend partition number $partitionnum on disk $disk using start sector $startsector.\nWARNING: Make sure you backup your boot sector prior to this."
  read -r -p "Are you sure? [y/N] " response
  response=${response,,} # tolower 
  if [[ $response =~ ^(yes|y)$ ]]
  then
    echo -e "\n+++Current partition layout of $disk:+++"
    parted $disk --script unit s print
    if [ $logical == 1 ]
    then
      parted $disk --script rm $ext_partitionnum
      parted $disk --script "mkpart extended ${ext_startsector}s -1s"
      parted $disk --script "set $ext_partitionnum lba off"
      parted $disk --script "mkpart logical ext2 ${startsector}s -1s"
    else
      parted $disk --script rm $partitionnum
      parted $disk --script "mkpart primary ext2 ${startsector}s -1s"
    fi
    parted $disk --script set $partitionnum lvm on
    echo -e "\n\n+++New partition layout of $disk:+++"
    parted $disk --script unit s print
    # The 2nd script to expand the filesystem will be automatically executed on the next reboot.
    echo "#!/bin/bash
#Extend Physical Volume first
pvresize $p

#Extend LVM, using 100% of the free allocation units and resize filesystem
lvextend --extents +100%FREE $l --resizefs
chmod -x \$0" > /root/fsresize.sh
    chmod +x /root/fsresize.sh
    # Use a temporary systemd service or a rc.local script for extending the filesystem during next reboot, depending on what the OS is running.
    if(pidof systemd)
    then
      resizefs_systemd
    else
      resizefs_rclocal
    fi
    
    echo -e "Done. The system will reboot automatically in 15 seconds and resize the filesystem during reboot.\n"
    sleep 15
    # Reboot is necessary in most cases for the kernel to read the new partition table.
    reboot
  else
    echo -e "Aborted by user.\n"
    exit 1
  fi
}

resizefs_rclocal() {
  # Resize the filesystem using a script in rc.local if the OS run with sysvinit.
  echo "#Cleanup rc.local again
sed -i /etc/rc.local -e '/\/root\/fsresize\.sh/d' --follow-symlinks
sed -i /etc/rc.local -re 's/^#(exit 0)$/\1/' --follow-symlinks" >> /root/fsresize.sh
  
  sed -i /etc/rc.local -re 's/^(exit 0)$/#\1/' --follow-symlinks
  echo "/root/fsresize.sh" >> /etc/rc.local
}

resizefs_systemd() {
  # Resize the filesystem using a script called by a temporary systemd service file if the OS runs with systemd.
  echo "#Cleanup systemd autostart script again.
systemctl disable fsresize.service
rm -f /etc/systemd/system/fsresize.service" >> /root/fsresize.sh
  
  echo "[Unit]
Description=Filesystem resize script for LVM volume $l

[Service]
ExecStart=/root/fsresize.sh

[Install]
WantedBy=multi-user.target" > /etc/systemd/system/fsresize.service
  systemctl enable fsresize.service
}

# Get options passed to the script.
while getopts ":p:l:f" o; do
  case "${o}" in
    p)
      p=${OPTARG}
      ;;
    l)
      l=${OPTARG}
      ;;
    f)
      f=1
      ;;
    *)
      usage
      ;;
  esac
done
shift $((OPTIND-1))

if [ -z "${p}" ] || [ -z "${l}" ]
then
  usage
fi

command -v fdisk >/dev/null 2>&1 && command -v parted >/dev/null 2>&1 && command -v pvresize >/dev/null 2>&1 || {
  echo -e "Error: Some of the required utilities (fdisk, parted, lvm tools etc) don't seem to be installed on this system.  Aborting.\n" >&2
  exit 1
}

# Check if a valid LVM physical volume was supplied by verifying the pvdisplay exit code ($?).
pvdisplay $p > /dev/null
if [ $? != 0 ] || ( ! (file $p | grep -q "block special"))
then
  echo -e "Error: $p does not look like a block device or LVM physical volume. Aborting.\n"
  usage
fi

# Check if a valid LVM logical volume was supplied by verifying the lvdisplay exit code ($?).
lvdisplay $l > /dev/null
if [ $? != 0 ]
then
  echo -e "Error: $l does not look like a LVM logical volume. Aborting.\n"
  usage
fi

# Fill variables for later use.
disk=$(echo $p | rev | cut -c 2- | rev) # /dev/sda
diskshort=$(echo $disk | grep -Po '[^\/]+$') # sda
partitionnum=$(echo $p | grep -Po '\d$') # 2
startsector=$(fdisk -u -l $disk | grep $p | awk '{print $2}')

# Detect LVM on logical/extended partition
layout=$(parted $disk --script unit s print)
if grep -Pq "^\s$partitionnum\s+.+?logical.+$" <<< "$layout"
then
  echo -e "Detected LVM residing on a logical partition.\n"
  logical=1
  ext_partitionnum=$(parted $disk --script unit s print | grep extended | grep -Po '^\s\d\s' | tr -d ' ')
  ext_startsector=$(parted $disk --script unit s print | grep extended | awk '{print $2}' | tr -d 's')
else
  logical=0
fi

parted $disk --script unit s print | if ! grep -Pq "^\s$partitionnum\s+.+?[^,]+?lvm$"
then
  echo -e "Error: $p seems to have some flags other than the lvm flag set. Other flags are not supported."
  usage
fi

if ! (fdisk -u -l $disk | grep $disk | tail -1 | grep $p | grep -q "Linux LVM")
then
  echo -e "Error: $p is not the last LVM volume on disk $disk. Cannot expand.\n"
  usage
fi

if [ "$f" != 1 ]
then
  oldsize=$(cat /sys/block/${diskshort}/size)
  # Rescan the SCSI bus to detect the grown disk.
  ls /sys/class/scsi_device/*/device/rescan | while read path; do echo 1 > $path; done
  ls /sys/class/scsi_host/host*/scan | while read path; do echo "- - -" > $path; done
  newsize=$(cat /sys/block/${diskshort}/size)

  # Check if the disk is larger now and proceed with the partition expansion if it is. 
  if [ $oldsize -lt $newsize ]
  then
    echo -e "Underlying disk $disk is larger now.\n"
    extenddisk_parted
  else
    echo -e "Disk Size not changed after rescan, already rescanned previously? Force extension with -f. Quitting.\n"
  fi
# When -f (force) flag is set, proceed to extend the disk without checking if it has grown.
else
    extenddisk_parted
fi
0 (0)
Article Rating (No Votes)
Rate this article
Attachments
There are no attachments for this article.
Comments
There are no comments for this article. Be the first to post a comment.
Full Name
Email Address
Security Code Security Code
Related Articles RSS Feed
LVM: Move allocated PE between Physical Volumes
Viewed 3806 times since Sat, Jun 2, 2018
stunnel Howto A Guide to create SSL access to a HTTP-only webserver with stunnel
Viewed 2591 times since Fri, Sep 28, 2018
Top 10 darmowych i publicznych serwerów DNS
Viewed 2276 times since Tue, May 22, 2018
How to deal with dmesg timestamps
Viewed 3315 times since Wed, Oct 3, 2018
20 Practical Examples of RPM Commands in Linux rpm
Viewed 7911 times since Mon, Feb 18, 2019
FIO (Flexible I/O) – a benchmark tool for any operating system
Viewed 19312 times since Wed, Jul 25, 2018
What UUIDs can do for you
Viewed 1716 times since Tue, Jul 17, 2018
RHEL7: Create and configure LUKS-encrypted partitions and logical volumes to prompt for password and mount a decrypted file system at boot.
Viewed 11838 times since Mon, Aug 6, 2018
Extending Linux LVM partitions - scripts
Viewed 6456 times since Sun, May 20, 2018
RHEL: Scan and configure new SAN (fibre channel) LUNs
Viewed 7906 times since Sun, May 27, 2018