Last Updated on December 16, 2019 by Paresh Gupta
I had to add more than a 1000 disks to multiple Linux servers in my lab. Here are some quick steps to do that on CentOS 7 using bash shell within minutes instead of days.
- Find the attached disks using
fdisk -l
command. - Create partition on the disks using
fdisk
command. I am using single partition for the entire disk. (printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdc)
- Create filesystem on the partition using
mkfs
command. (mkfs -t ext4 /dev/sdc1
) - Create a mount directory and mount the disk/partition to the directory. (
mkdir /mnt/sdc && mount /dev/sdc1 /mnt/sdc
) - Finally, verify using
df -Th
In the initial version of this post, I manually collected the devices from ‘fdisk -l’ and executed the commands back-to-back on bash shell. That process is described below. Few days later, I automated the complete process using Python and pexpect. Scroll to the bottom of the page or click here for details, essentially reducing the effort from minutes to seconds.
Step 1: List the attached disks using fdisk -l
This command will list all the attached disks. In the below output, the new disks that I want to add have names from /dev/sdc, /dev/sdd,…. till /dev/sdl
[root@stg-tme-lnx-b230-2 ~]# fdisk -l
Disk /dev/sda: 99.0 GB, 98999205888 bytes, 193357824 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: dos
Disk identifier: 0x000d2e8c
Device Boot Start End Blocks Id System
/dev/sda1 * 2048 1026047 512000 83 Linux
/dev/sda2 1026048 193357823 96165888 8e Linux LVM
Disk /dev/sdb: 31.0 GB, 30999052288 bytes, 60545024 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk /dev/mapper/centos-root: 53.7 GB, 53687091200 bytes, 104857600 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk /dev/mapper/centos-swap: 4294 MB, 4294967296 bytes, 8388608 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk /dev/sdc: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 524288 bytes
Disk /dev/sdd: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 524288 bytes
Disk /dev/sde: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 524288 bytes
Disk /dev/sdf: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 524288 bytes
Disk /dev/sdg: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 524288 bytes
Disk /dev/sdh: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 524288 bytes
Disk /dev/sdi: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 524288 bytes
Disk /dev/sdj: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 524288 bytes
Disk /dev/sdk: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 524288 bytes
Disk /dev/sdl: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 524288 bytes
Step 2: Create partition using fdisk
For a few disks, fdisk <disk> command offers an intuitive and interactive way of creating partition. But repeating the process 1000 times is better served by an automated, scripted and non-interactive approach. The interactive workflow of fdisk command offers multiple options. The most important of them are n (for adding a new partition) and w (for writing the config to disk and exit). The other option are safe to accept in default format. That means, I need n, w and many <enter> or <return> which will be simulated by \n. Here is a sample command to create partition on /dev/sdc
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdc
To create partition on all 10 disks, I am throwing the commands for all disks in a single file, give it a look of bash script, make it executable and run.
[root@stg-tme-lnx-b230-2 ~]# vim partition.sh
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdb &&
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdb &&
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdc &&
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdd &&
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sde &&
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdf &&
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdg &&
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdh &&
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdi &&
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdj &&
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdk &&
printf '\n\n\n\nn\n\n\n\n\n\n\n\nw\n' | fdisk /dev/sdl
"partition.sh" [New] 11L, 578C written
[root@stg-tme-lnx-b230-2 ~]# chmod 733 partition.sh
[root@stg-tme-lnx-b230-2 ~]# ./partition.sh
The device presents a logical sector size that is smaller than
the physical sector size. Aligning to a physical sector (or optimal
I/O) size boundary is recommended, or performance may be impacted.
Welcome to fdisk (util-linux 2.23.2).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Command (m for help): Command (m for help): Command (m for help): Command (m for help): Command (m for help): Partition type:
p primary (1 primary, 0 extended, 3 free)
e extended
Select (default p): Using default response p
Partition number (2-4, default 2): No free sectors available
Command (m for help): Command (m for help): Command (m for help): Command (m for help): Command (m for help): Command (m for help): The partition table has been altered!
Calling ioctl() to re-read partition table.
Syncing disks.
(Above output will be repeated as many time as the number of disk, 10 in this case)
You can verify that the partition has been created
[root@stg-tme-lnx-b230-2 ~]# fdisk -l /dev/sdc
Disk /dev/sdc: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 524288 bytes
Disk label type: dos
Disk identifier: 0x56d7160d
Device Boot Start End Blocks Id System
/dev/sdc1 2048 20971519 10484736 83 Linux
[root@stg-tme-lnx-b230-2 ~]#
Step 3: Create filesystem on new partition using mkfs
The partition name appends numbers to the disk name. Hence, the mkfs commands will take an argument in the form of /dev/sdc1. Do not forget the number 1 at the end. The command to create filesystem is
mkfs -t ext4 /dev/sdc1
To repeat the command for 10 partitions, add the command 10 times in a file, give it a look of shell script, make it executable and run. This step is same as before while creating the partition (as described in step 2).
[root@stg-tme-lnx-b230-2 ~]# cat mkfs.sh
mkfs -t ext4 /dev/sdc1 &&
mkfs -t ext4 /dev/sdd1 &&
mkfs -t ext4 /dev/sde1 &&
mkfs -t ext4 /dev/sdf1 &&
mkfs -t ext4 /dev/sdg1 &&
mkfs -t ext4 /dev/sdh1 &&
mkfs -t ext4 /dev/sdi1 &&
mkfs -t ext4 /dev/sdj1 &&
mkfs -t ext4 /dev/sdk1 &&
mkfs -t ext4 /dev/sdl1
[root@stg-tme-lnx-b230-2 ~]# chmod 733 mkfs.sh
[root@stg-tme-lnx-b230-2 ~]# ./mkfs.sh
mke2fs 1.42.9 (28-Dec-2013)
Discarding device blocks: done
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=128 blocks
655360 inodes, 2621184 blocks
131059 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=2151677952
80 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632
Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done
(Above output will repeat as many times as the number of disks, 10 in this example)
Step 4: Create mount directory and mount
[root@stg-tme-lnx-b230-2 ~]# cat mkdir.sh
mkdir /mnt/sdc &&
mkdir /mnt/sdd &&
mkdir /mnt/sde &&
mkdir /mnt/sdf &&
mkdir /mnt/sdg &&
mkdir /mnt/sdh &&
mkdir /mnt/sdi &&
mkdir /mnt/sdj &&
mkdir /mnt/sdk &&
mkdir /mnt/sdl
[root@stg-tme-lnx-b230-2 ~]# chmod 733 mkdir.sh
[root@stg-tme-lnx-b230-2 ~]# ./mkdir.sh
[root@stg-tme-lnx-b230-2 ~]# cat mount.sh
mount /dev/sdc1 /mnt/sdc &&
mount /dev/sdd1 /mnt/sdd &&
mount /dev/sde1 /mnt/sde &&
mount /dev/sdf1 /mnt/sdf &&
mount /dev/sdg1 /mnt/sdg &&
mount /dev/sdh1 /mnt/sdh &&
mount /dev/sdi1 /mnt/sdi &&
mount /dev/sdj1 /mnt/sdj &&
mount /dev/sdk1 /mnt/sdk &&
mount /dev/sdl1 /mnt/sdl
[root@stg-tme-lnx-b230-2 ~]# chmod 733 mount.sh
[root@stg-tme-lnx-b230-2 ~]# ./mount.sh
Finally, verify using df -Th
[root@stg-tme-lnx-b230-2 ~]# df -Th
Filesystem Type Size Used Avail Use% Mounted on
/dev/sdc1 ext4 9.8G 37M 9.2G 1% /mnt/sdc
/dev/sdd1 ext4 9.8G 37M 9.2G 1% /mnt/sdd
/dev/sde1 ext4 9.8G 37M 9.2G 1% /mnt/sde
/dev/sdf1 ext4 9.8G 37M 9.2G 1% /mnt/sdf
/dev/sdg1 ext4 9.8G 37M 9.2G 1% /mnt/sdg
/dev/sdh1 ext4 9.8G 37M 9.2G 1% /mnt/sdh
/dev/sdi1 ext4 9.8G 37M 9.2G 1% /mnt/sdi
/dev/sdj1 ext4 9.8G 37M 9.2G 1% /mnt/sdj
/dev/sdk1 ext4 9.8G 37M 9.2G 1% /mnt/sdk
/dev/sdl1 ext4 9.8G 37M 9.2G 1% /mnt/sdl
Automating with Python and pexpect
See below Python code which
- Executes fdisk -l command and makes a list of all devices
- Skips all the devices which already have partitions or have system partition
- Goes in a loop for one device at a time to creates partition using fdisk command
- Creates filesystem using mkfs command
- Creates mount directory under /mnt and mounts the device
The code using pexpect to handle the interactive fdisk command
from __future__ import print_function
import subprocess
import pexpect
import getpass
import sys
import time
def fdisk_device_list():
p1 = subprocess.Popen(['fdisk', '-l'], stdout=subprocess.PIPE)
fdisk_output = p1.communicate()[0]
fdisk_lstrip = fdisk_output.lstrip('\nDisk ')
fdisk_items = fdisk_lstrip.split('\n\nDisk ')
disk_list = []
for disks in fdisk_items:
if 'mapper' in disks:
# Matches system created like /dev/mapper/centos-root or
# /dev/mapper/centos-swap or /dev/mapper/centos-home
continue
# Comment below -if- block to get all disks
if 'Device Boot' in disks:
if 'dev' in disks[disks.find('Device Boot'):]:
continue
disk_location = disks.split(':')
disk_list.append(disk_location[0])
return disk_list
def main():
disk_list = fdisk_device_list()
print('Creating and mounting partition from {} disks...'.format(len(disk_list)))
i = 0
for disks in disk_list:
# Step 1: Create partition
print('{}:{}'.format(i,disks))
i = i + 1
child = pexpect.spawn('fdisk ' + disks)
child.logfile = sys.stdout
child.expect('\):')
child.sendline('n')
child.expect('\):') # p
child.sendline()
child.expect('\):') # 1
child.sendline()
child.expect('\):') # 2048
child.sendline()
child.expect('\):') # last sector
child.sendline()
child.expect('\):')
child.sendline('w')
child.expect(pexpect.EOF)
time.sleep(1)
# Step 2: Create filesystem on partition
pexpect.run('mkfs -t ext4 ' + disks + '1', logfile=sys.stdout)
time.sleep(1)
# Step 3: Create directory in /mnt and mount
pexpect.run('mkdir /mnt/' + disks[5:], logfile=sys.stdout)
pexpect.run('mount ' + disks + '1' + ' /mnt/' + disks[5:], logfile=sys.stdout)
time.sleep(1)
if __name__ == '__main__':
main()
Hello Paresh,
I want to thank you for this easy to follow article on adding disks to Linux. I am new to linux and overwhelmed by the amount of commands and different way of accomplishing tasks in linux. I wanted to add one disk to Ubuntu server; and although your article is tailored towards adding multiple disks, I found it very easy to follow. Searching the net for help on adding disks, you would come across people mentioning editing /etc/fstab; and I wonder if you find it necessary to do so or it is just an option.