Mindtwist.de

...let your mind twist!

How to quickly restore lvm2 volumes from saved layout

~/lvmrestore.py.html

Sometimes you need to quickly reconstruct a Logical Volume Manager 2 layout. For example if you need to do a bare metal restore and just kickstarted your Linux Server and you want to recreate the lvm volumes before you restore your backup. Or if you need to transfer a system to another server and you want to quickly copy the lvm layout so that you can start rsyncing your data. The following script allows you to restore a lvm2 layout from the saved command output of 'pvs' and 'lvs'. Of course you may also modify that layout and pipe it to this script to create all the necessary lvm commands to construct it from scratch.

The Script

#!/usr/bin/python
"""
Creates commands to restore physical and logical volume layout from saved pvs and lvs command output.
Copyright (c) 2010 by Reiner Rottmann. Released under The BSD License.
"""
import sys
import subprocess
import re
import os
version=1.0
revision='$Rev $'
pvs='/sbin/pvs'
lvs='/sbin/lvs'
lvcreate='/sbin/lvcreate'
vgcreate='/sbin/vgcreate'
pvcreate='/sbin/pvcreate'

def main():
"""Main function
"""
import os
import sys
import getopt
global debug
"""Enable debug mode
"""
debug=0
global input_file
"""File with saved pvs and lvs command output.
"""
input_file=None
global read_from_stdin
"""Read pvs and lvs command output from stdin?
"""
read_from_stdin=False
#if not uid == 0:
# print 'ERROR: Detected user id is', uid, '. This script needs to be run as root (uid 0)!'
# sys.exit(1)
try:
opts, args = getopt.getopt(sys.argv[1:], "dhf:s", ["debug", "help", "file=", "stdin"])
except getopt.GetoptError, err:
usage()
sys.exit(2)
for o, a in opts:
if o in ("-d", "--debug"):
debug=True
if debug: print "DEBUG: Enabled debug mode."
elif o in ("-h", "--help"):
usage()
sys.exit(0)
elif o in ("-f", "--file"):
if os.path.exists(a):
if debug:
print "DEBUG: Using input file", input_file
input_file = a
else:
print "ERROR: Input file",a , "could not be found. Exiting."
sys.exit(1)
elif o in ("-s", "--stdin"):
if debug: print "DEBUG: Using input from stdin."
read_from_stdin = True
if not input_file:
if not read_from_stdin:
print "ERROR: No input file specified. Exiting."
usage()
sys.exit(1)
if read_from_stdin:
data=read_config_stdin()
else:
data=read_config(input_file)
if data:
volumes=parse_config(data)
create_lv_cmds(volumes)

def read_config(file):
"""Reads pvs and lvm command output from file.
"""
try:
f = open(file, 'r')
config_data=f.read()
except IOError, e:
print e
f.close()
sys.exit(1)
return config_data

def read_config_stdin():
"""Reads pvs and lvm command output from standard input.
"""
import sys
config_data = sys.stdin.readlines()
return "".join(config_data)

def parse_config(config_data=""):
"""Parse given config data. Returns a dict with lvm config.
"""
pvblock=False
lvblock=False
volumes={}
lines=[]
# PV VG Fmt Attr PSize PFree
regex_pvblock=re.compile("^\s*PV\s+VG\s+Fmt\s+Attr\s+PSize\s+PFree\s*$")
# LV VG Attr LSize Origin Snap% Move Log Copy% Convert
regex_lvblock=re.compile("^\s*LV\s+VG\s+Attr+\s+LSize.*$")
# lvhome vg00 -wi-ao 5.00g
regex_lvconfig=re.compile("^\s+(?P<lv>.*?)\s(?P<vg>.*?)\s[-\w]{6}\s+(?P<size>[.\d\w]+?)\s*$")
# /dev/sda2 vg00 lvm2 a- 49.00g 12.80
regex_pvconfig=re.compile("^\s*(?P<pv>[\/\w]+?)\s+(?P<vg>\w+?)\s+\w+\s+[\w-]+\s+(?P<size>[\w.]+?)\s+[\w.]+.*")
lines=config_data.splitlines()
for line in lines:
m=regex_pvblock.findall(line)
if m:
if debug:
print "DEBUG: Found physical volume configuration block."
pvblock=True
lvblock=False
continue
m=regex_lvblock.findall(line)
if m:
if debug:
print "DEBUG: Found logical volume configuration block."
pvblock=False
lvblock=True
continue
if lvblock:
m=regex_lvconfig.search(line)
if m:
lv=m.group("lv").strip()
vg=m.group("vg").strip()
size=m.group("size").strip()
if debug: print "DEBUG: Found logical volume",lv,"in", vg, "with", size
if not volumes.has_key(vg):
volumes[vg]={}
if not volumes[vg].has_key(lv):
volumes[vg][lv]={}
if not volumes[vg][lv]:
volumes[vg][lv]=size
continue
if pvblock:
m=regex_pvconfig.search(line)
if m:
pv=m.group("pv").strip()
vg=m.group("vg").strip()
size=m.group("size").strip()
if debug: print "DEBUG: Found physical volume", pv, "for", vg, "with", size
if not volumes.has_key("-pv"):
volumes["-pv"]={}
if not volumes["-pv"].has_key(vg):
volumes["-pv"][vg]={}
if not volumes["-pv"][vg].has_key(pv):
volumes["-pv"][vg][pv]=size
return volumes

def create_lv_cmds(volumes):
"""Creates commands t o create given volumes.
"""
if debug:
print "DEBUG: Use the following commands to restore the given lvm layout:"
print "#-------8<"+ "-" * 70
for vg in volumes.keys():
if vg=="-pv":
continue
if debug: print "######[ Volume Group", vg, "]#####"
for pv in volumes["-pv"][vg].keys():
print pvcreate, pv
print vgcreate, vg, " ".join(volumes["-pv"][vg].keys())
for lv in volumes[vg].keys():
print lvcreate, "--size", volumes[vg][lv], "--name", lv, vg
if debug: print "#-------8<"+ "-" * 70
def usage():
"""Prints usage"""
import os
data="""
[root@moria ~]# lvs
LV VG Attr LSize Origin Snap% Move Log Copy% Convert
lvhome vg00 -wi-ao 5.00g
lvopt vg00 -wi-ao 1.00g
lvroot vg00 -wi-ao 8.00g
lvswap vg00 -wi-ao 4.00g
lvtest vg00 -wi-ao 100.00m
lvtmp vg00 owi-ao 2.00g
lvusr vg00 -wi-ao 5.00g
lvvar vg00 -wi-ao 9.00g
[root@moria ~]# pvs
PV VG Fmt Attr PSize PFree
/dev/sda2 vg00 lvm2 a- 49.00g 12.80g
"""
print ""
print "Creates commands to restore physical and logical volume layout from saved pvs and lvs command output."
print "Copyright (c) 2010 by Reiner Rottmann. Released under The BSD License."
print ""
print "Usage: ", os.path.basename(sys.argv[0]), "[options]"
print ""
print "-d| --debug\tDebug mode on"
print "-h| --help\tShow this help"
print "-f| --file\tFile with saved pvs and cvs command output"
print "-s| --stdin\tRead pvs and cvs command output from stdin"
print ""
print "Examples:"
print ""
print "$ cat /tmp/pvs-lvs-output.txt | ./lvmrestore.py -d -s"
print "$ ./lvmrestore.py -d -f /tmp/pvs-lvs-output.txt"
print "$ ./lvmrestore.py -h | grep -A 14 'Example input data' | ./lvmrestore.py -d -s"
print ""
print "Example input data:"
print data
print ""
return 0

main()
 

Linux Magazine

Linux Magazine News (path: lmi_news)