#!/usr/bin/env python

# Copyright C 2002-2008 by Isilon Systems, Inc. All rights not expressly
# granted herein are reserved.
# Created in the United States of America.
# 
# This digital media is protected by U.S. and international copyright and
# intellectual property laws. Unless otherwise specified, all information
# and screens appearing as part of this digital medium, including
# software, services, documents, text, images, icons, and logos design;
# the selection, assembly, arrangement, and design thereof; and the code
# that enables its presentation, are the sole property of Isilon Systems,
# Inc.
# 
# Except as otherwise required by applicable law, no part of any
# copyrighted material may be reproduced, modified, translated,
# distributed, published, or transmitted by any means, electronic or
# mechanical, for any purpose, without the prior written consent of Isilon
# Systems. 
#  
# Isilon and Isilon Systems are registered trademarks of Isilon Systems,
# Inc.  Legion is a trademark of Isilon Systems, Inc.  All other
# trademarks and registered trademarks are the property of their
# respective holders.

import getopt
import os
import sys

import isi.app.lib.procs as procs
import isi.fs.journal as jlib
import isi.hw.hal as hal
import isi.misc

def usage():
    print >> sys.stderr, "   usage: isi_restore_journal [-f file]"
    print >> sys.stderr, ""
    print >> sys.stderr, " -f file: read the journal from the file."
    print >> sys.stderr, "      -h: help"
    sys.exit(2)

def restore_journal(f, force):
    unmount = False
    lock = None
    userSpecified = f is not None
    try:
        nvram_size = hal.getNvramSize()
    
        if isi.misc.ifs_mounted():
            print ("OneFS is still mounted. Please unmount before attempting "
                   "a journal restore.")
            return -1
        
        lock = jlib.lock()
    
        if not userSpecified:
            journal = jlib.JournalImdd()
            if not journal.mounted:
                if not journal.mount():
                    print 'ERROR: Unable to mount %s' % journal.mountpoint
                    return -1
                unmount = True
            f = journal.mountpoint + "/journal"
    
        gzName = '%s.gz' % f
        md5Name = '%s.md5' % f
        # If the user didn't specify a backup journal, then we're
        # using the standard system backup, and thus need to verify
        # that the journal_backup_serial and journal_backup_valid
        # files match. If they don't, only allow the restore only if
        # the force flag has been specified
        if not userSpecified:
            (backup_serial, valid_serial) = jlib.backup_serials()
            print 'Journal backup serial: %s' % backup_serial
            print ' Journal valid serial: %s' % valid_serial
        
            try:
                jlib.validate_backup(journal, True)
            except OSError, e:
                print "Error while validating journal: %s" % e
                return -1
            except jlib.ValidationError, e:
                print "Error while validating journal: %s" % e
                return -1
            
        else:
            
            try:
                os.stat(gzName)
            except OSError, e:
                print "File %s does not exist!" % gzName
                return -1

            try:
                os.stat(md5Name)
            except OSError, e:
                print "File %s does not exist!" % md5Name
                return -1

            if not jlib.check_md5(f):
                print "Md5 checking of %s md5 failed!" % gzName
                return -1

        def run_cmd(cmd, msg):
            def out(line):
                 print line.rstrip()
                 sys.stdout.flush()

            (error, output) = procs.proc_cmd_output(cmd, func=out)
            if error:
                print 'Error: %s failed!' % msg
                for line in output:
                    print '- %s' % line.rstrip()
                return -1

            return 0

        #
        # Bug 44255: Before restoring the journal to NVRAM, we should blast
        # the NVRAM to clear any ECC errors and the like.
        #
        # Reinitializing the nvram will destroy any data in it, so we mustn't
        # fail after this point if it can be avoided.  We've already verified
        # the journal against the MD5, so it should be correct.
        #
        devFile = hal.getJournalDevicePath()
        print "Initializing NVRAM %s..." % devFile

        # For performance, do as many 1Mb chunks as we can.
        size = hal.getNvramSize()
        bs = 1024 * 1024
        count = size / bs

        cmd = '/bin/dd if=/dev/zero of=%s bs=%d count=%d' % \
                (devFile, bs, count)
        if run_cmd(cmd, 'NVRAM initialize') < 0:
            return -1

        # Now do the rest, if necessary, as 512b chunks.
        done = bs * count
        size -= done
        if size:
            bs = 512
            count = size / bs
            done /= bs

            cmd = '/bin/dd if=/dev/zero of=%s bs=%d count=%d oseek=%d' % \
                    (devFile, bs, count, done)
            if run_cmd(cmd, 'NVRAM tail initialize') < 0:
                return -1

        print "Restoring journal from %s..." % gzName

        cmd = '/usr/bin/gunzip -c %s | /usr/bin/isi_dumpjournal -u -' % gzName
        if run_cmd(cmd, 'restore') < 0:
            return -1

    finally:
        if unmount:
            # Must force unmount due to advisory locks
            journal.umount()

        jlib.unlock(lock)

if __name__ == "__main__":
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hf:", "force")
    except:
        usage()

    if (not len(args) == 0):
        usage()
        
    force = False
    f = None
    for opt in opts:
        if opt[0] == '-h':
            usage()
        elif opt[0] == '-f':
            f = opt[1]
        elif opt[0] == '--force':
            force = True
    exit_code = restore_journal(f, force)
    sys.exit(exit_code)
