#!/usr/bin/python

import os
import re
import subprocess
import sys
import time
import getopt
import isi.misc

def usage():
    print >> sys.stderr, '''\
    usage: fulldump -f -d devname
    -f: force create fulldump partition, even if one exists already
    -d devname: drive to use
    -s size: size of fulldump partition in bytes. If given size is
    less than RAM size, fulldump partition size is size of RAM
    If the disk is in use by ufs mounts, it cannot be used for fulldump.
    This implies using an unused drive for imdd
    example: fulldump -d /dev/da3
    '''

def gpart_drive_format(dev, force, size):

    import isi.sys.bootdisk as bd
    import isi.sys.disk as d
    import isi.sys.geom as g

    mesh = g.Mesh()
    mesh.refresh()
    dp = mesh.disk_provider(dev.split('/')[2])

    for pp in mesh.partition_providers:
        if pp.label == 'kerneldump' and not force:
	    print 'kerneldump partition already exists.\n\
to create new kerneldump partition use -f'
	    return

    if dp is not None:
        # IDE disks with partitions cannot be used for fulldump
        if d.is_bootdisk(dp):
            pg = dp.partition_geom
            if pg != None:
                if len(pg.providers) != 0:
                    print "cannot use a bootdisk for fulldump\n"
                    return
        if d.is_storage_disk(dp):
            # Smart fail this drive
            smart_fail(dev)
        d.set_fulldump_device(dev.split('/')[2], size=size)
	return

def bsdlabel_drive_format(dev, force, size):
    import isi.hw.imdd.devices as devices

    dumpparts = []
    disks = isi.misc.sysctlbyname('kern.disks').split()
    for d in disks:
        parts = devices.drive2parts(d)
        if parts is None or len(parts) != 1:
            continue
        if parts[0].find("s1f") != -1:
            dumpparts.append(parts[0])
    # Check if we already have fulldump device
    if len(dumpparts) != 0 and not force:
        print "fulldump device already exists. \n\
Use -f to create new fulldump disk"
        return

    # Does this drive have imdd partitions?
    p1=subprocess.Popen(['disklabel', '%ss1'%dev],stdout=subprocess.PIPE)
    p1.wait()
    [op,err]=subprocess.Popen(['grep',
	'imdd'],stdin=p1.stdout,stdout=subprocess.PIPE).communicate()

    if op.find('imdd') != -1:
        # Create dump partition on an imdd unused drive only
        devices.device_lists_build()
        if devices.drive_in_use_by_imdd(dev):
            print "%s in use by imdd"%dev
            newdev = get_new_dev()
            if newdev == None:
	        devices.print_drives_not_in_use()
            else:
                print "Use %s"%newdev
	    return

        smart_fail(dev)
        subprocess.check_call(['/usr/bin/isi_imdd_setfail','-d' ,dev])
    devices.format_dump_drive(dev, size)

    if len(dumpparts) != 0:
        for olddev in dumpparts:
            subprocess.call(['/sbin/savecore', '-v', '/var/crash', "/dev/%s"%olddev])
            if olddev != '%ss1f'%dev:
                devices.delete_fulldump_partition(olddev)
    subprocess.call(['/sbin/dumpon', '-v', '%ss1f'%dev])

def main():
    dev = None
    force = False
    size = 0

    opts, args = getopt.getopt(sys.argv[1:], 'fd:s:')
    for opt, val in opts:
        if opt == '-f':
            force = True
	elif opt == '-d':
            dev = val
	elif opt == '-s':
	    size = int(val)

    if dev == None or not os.path.exists(dev):
        usage()
        return

    try:
        import isi.sys.bootdisk as bootdisk
        if (bootdisk.current_system_is_geom() or
            bootdisk.future_system_is_geom()):
            gpart_drive_format(dev, force, size)
        else:
            bsdlabel_drive_format(dev, force, size)
    except:
        bsdlabel_drive_format(dev, force, size)

def get_new_dev():
    child = subprocess.Popen(['isi', 'devices'], stdout=subprocess.PIPE)
    child.wait()
    ln = child.stdout.readline()

    while ln != "":
        if ln.find('NEW') != -1:
            ls = ln.split()
            sz = len(ls)
            return ls[sz-1]
        ln = child.stdout.readline()
    return None

def smart_fail(dev):
    child = subprocess.Popen(['isi', 'devices'], stdout=subprocess.PIPE)
    child.wait()
    ln = child.stdout.readline()

    bay_re = re.compile('Bay \d*')
    nodenum_re = re.compile('Node \d*')

    while ln != "":
        if ln.find('Node') != -1:
	    node = int(nodenum_re.search(ln).group().strip('Node'))
        if ln.find(dev) != -1:
	    bay = int(bay_re.search(ln).group().strip('Bay'))
            break
        ln = child.stdout.readline()
    child.stdout.close()

    isi_dev = "%s:%s"%(node,bay)
    try:
        subprocess.check_call(['isi', 'devices', '-a', 'smartfail', '-d', isi_dev])
    except:
        pass

    done = False
    while not done:
        ch = subprocess.Popen(['isi', 'devices', '-d', isi_dev], stdout=subprocess.PIPE)
        ln = ch.stdout.read(256)
	if ln.find('NEW') != -1:
	    return
        if ln.find('REPLACE') != -1:
	    return
        time.sleep(1)

if __name__ == '__main__':
    main()
