from multiprocessing import Process
import datetime
import os
import subprocess
import sys

devices = {'/dev/sdb1': {'mount': '/mnt/smc0', 'size': 64},
           '/dev/sdc1': {'mount': '/mnt/smc1', 'size': 256},
           '/dev/sdd1': {'mount': '/mnt/smc2', 'size': 1024}}

MIN_FILE_SIZE =  10240 # in megabytes
MAX_FILE_SIZE = 102400 # in megabytes
DRIVE_CAPACITY = int(1.8 * (2**20)) # in megabytes

class Tester(Process):
    """
    """
    
    def __init__(self, device, mount_path, chunk_size, fs_name, output_path):
        Process.__init__(self)
        self.device = device
        self.mount_path = mount_path
        self.chunk_size = chunk_size
        self.mkfs_cmd = '/sbin/mkfs.{:s}'.format(fs_name)
        self.output_path = output_path
                
        # Build a logfile name for the stdout and stderr of the subprocess calls
        logname = 'subproc_{:d}.log'.format(self.chunk_size)
        self.subproclog = open(os.path.join(self.output_path, logname), 'w')
        
    def out(self, msg):
        print '[{:4d}] {:s}'.format(self.chunk_size, msg)
        sys.stdout.flush()
        
    def cmd(self, cmd):
        return subprocess.call(cmd, shell=True, stdout=self.subproclog, 
                               stderr=self.subproclog)
        
    def run(self):
        self.out('Started')
        
        # Unmount mount path
        cmd = 'umount {:s}'.format(self.mount_path)
        rc = self.cmd(cmd)
        if rc == 0:
            self.out('Unmounted {:s}'.format(self.mount_path))
        elif rc == 2:
            self.out('{:s} was not mounted'.format(self.mount_path))
        else:
            self.out('Error unmounting {:s}'.format(self.mount_path))
            
        # Format device partition
        cmd = '{:s} -f {:s}'.format(self.mkfs_cmd, self.device)
        rc = self.cmd(cmd)
        if rc == 0:
            self.out('Formatted {:s}'.format(self.device))
        else:
            self.out('Error while formatting {:s}'.format(self.device))
            
        # Mount path
        cmd = 'mount {:s} {:s}'.format(self.device, self.mount_path)
        rc = self.cmd(cmd)
        if rc == 0:
            self.out('Mounted {:s} on {:s}'.format(self.device, self.mount_path))
        else:
            self.out('Error mounting {:s} on {:s}'.format(self.device, self.mount_path))
            
        # Run filler script        
        output_file = os.path.join(self.output_path, 
                                   'extent_test_{:d}MB.log'.format(self.chunk_size))
                                   
        min_chunks = MIN_FILE_SIZE / self.chunk_size
        max_chunks = MAX_FILE_SIZE / self.chunk_size
        
        cmd_args = (self.mount_path, min_chunks, max_chunks, self.chunk_size, 
                    DRIVE_CAPACITY,  output_file)        
        cmd = 'python filler.py {:s} {:d} {:d} {:d} {:d} > {:s}'.format(*cmd_args)
        
        self.out('Started filler.py on {:s}'.format(self.mount_path))
        start = datetime.datetime.now()
        rc = self.cmd(cmd)
        if rc == 0:
            self.out('filler.py complete on {:s}'.format(self.mount_path))
        end = datetime.datetime.now()
        
        # Display elapsed time for filler.py script
        elpased = end - start
        hours, remainder = divmod(elpased.seconds, 3600)
        minutes, seconds = divmod(remainder, 60)
        time = '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds)
        self.out('Filler took: {:s}'.format(time))
        
        # Run analyze.py script
        output_file = os.path.join(self.output_path, 
                                   'extent_analysis_{:d}MB.log'.format(self.chunk_size))
        cmd_args = (self.mount_path, output_file)
        cmd = 'python analyze.py {:s} > {:s}'.format(*cmd_args)
        rc = self.cmd(cmd)
        self.out('Ran analyze.py on {:s}'.format(self.output_path))
        
        # Run raw_data.py script
        output_file = os.path.join(self.output_path, 
                                   'raw_extents_{:d}MB.log'.format(self.chunk_size))
        cmd_args = (self.mount_path, output_file)
        cmd = 'python raw_data.py {:s} > {:s}'.format(*cmd_args)
        rc = self.cmd(cmd)
        self.out('Ran raw_data.py on {:s}'.format(self.output_path))

        # Close the subprocess output log for good measure
        self.subproclog.close()

def usage():
    """ TODO: write usage blurb to be printed here
    """
    pass

def main():
    """
    arg1 - file system name (e.g. xfs)
    arg2 - path to location to store outputs
    """
        
    if len(sys.argv) < 3:
        print 'ERROR: not enough arguments provided'
        return -1
        
    fs_name = sys.argv[1]
    output_path = sys.argv[2]
    
    # Make sure file system name exists (i.e. /sbin/mkfs.[FSNAME] exists)
    if not os.path.exists('/sbin/mkfs.{:s}'.format(fs_name)):
        print 'ERROR: "{:s}" is not a known file system on this system!'.format(fs_name)
        return -1
    
    # Make sure the output path exists
    if not os.path.exists(output_path):
        print 'ERROR: output path "{:s}" does not exist!'.format(output_path)
        return -1
        
    # Create output folder if it does not already exist
    output_path = os.path.join(output_path, fs_name)
    if os.path.exists(output_path):
        print 'ERROR: output folder "{:s}" already exists!'.format(output_path)
        return -1
    os.makedirs(output_path)
    
    # Create Tester instances for each of the devices
    tester_workers = []
    for device, dev_attrs in devices.iteritems():
        tester_workers.append(Tester(device, dev_attrs['mount'],
                              dev_attrs['size'], fs_name, output_path))
    
    # Start all Tester processes
    for worker in tester_workers:
        worker.start()
    
    # Join each of the processes
    for worker in tester_workers:
        worker.join()
    
    print 'Test complete'
    
if __name__ == '__main__':
    sys.exit(main())
