#!/usr/bin/python # # determine which LVM2 logical volume spans a hard disk block. # useful when you get messages about defective hard disk blocks from # SMART and you want to find out which volume is affected. # Usage: # # Option 1: # # badblocklocate disk # Queries smartctl for bad blocks and locates them. # # Option 2: # # badblocklocate disk blockno [blockno blockno ...] # Find the volums corresponding to the blockno given on the command line # # # Mail me at florian@void.s.bawue.de for questions and suggestions. # # Florian Laws # LICENSE: # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import sys import os from subprocess import * disk = sys.argv[1] def find_partition(sector): output = Popen(["fdisk","-lu",disk],stdout=PIPE).communicate()[0] for line in output.split('\n'): if line.startswith('/'): parts = line.strip().split() partdev = parts[0] if parts[1] == '*': # Partition has boot flag, delete this field, so numbering matches non-boot partitions del parts[1] start = int(parts[1]) end = int(parts[2]) parttype = parts[4] if sector >= start and sector <= end: # found the right partition return { 'dev' : partdev, 'start' : start, 'end' : end, 'type' : parttype, 'offset' : sector - start } # didn't find anything return None def find_lv(partition, offset): #print "Warning: assuming LINEAR allocation policy" # determine PE size output = Popen(["pvdisplay","-c",partition],stdout=PIPE).communicate()[0] parts = output.split(":") # PE size is given in KB by pvdisplay - multiply by 2 to get size in terms of LBA blocks PE_size = int(parts[7]) * 2 #print "PE size for %s is %d blocks" % (partition, PE_size) PE_rank = offset / PE_size #print "PE rank" , PE_rank # get LV-to-PE mappings output = Popen(["lvs","--noheadings","-o","seg_pe_ranges,vg_name,lv_name","-O","seg_start_pe"],stdout=PIPE).communicate()[0] # print output for line in output.strip().split('\n'): pv_range, vg, lvname = line.split() pv, pe_range = pv_range.split(':') if pv != partition: continue pe_range_parts = pe_range.split('-') pe_start = int(pe_range_parts[0]) pe_end = int(pe_range_parts[1]) if pe_start <= PE_rank and PE_rank <= pe_end: return (vg, lvname) # No LV found return None bad_sectors = [] if len(sys.argv) > 2: # get sector numbers from command line. for a in sys.argv[2:]: bad_sectors.append( int(a) ) else: print "Getting list of defective sectors from %s" % ( disk ) output = Popen(["smartctl", "-l","selftest", disk],stdout=PIPE,universal_newlines=True).communicate()[0] #print repr(output) for line in output.split('\n'): if line.startswith('#'): parts = line.strip().split() bad_sectors.append( int(parts[9]) ) #print bad_sectors for s in bad_sectors: partition = find_partition(s) if partition['type'] == '8e': #print "%(dev)s is LVM partition" % partition lvinfo = find_lv(partition['dev'], partition['offset']) if not lvinfo: print "%d: LV not found - probably unallocated area." % (s) else: print s, lvinfo[0], lvinfo[1] else: print "%d: %s is not an LVM partition - partition type %s is not supported. skipping." % (s, partition['dev'], partition['type'])