#!/usr/bin/env python

"""Utility class for Condor IPP software deployent
"""

import os
import subprocess
import tempfile
import shutil
import logging

class Deployment:
    """Utility class for Condor IPP software deployent
    """
    logger = logging.getLogger('ippdor')

    CLASSES_FILENAMES = [ '__init__.py',
                          'ChipManager.py', 
                          'CamManager.py', 
                          'FakeManager.py', 
                          'WarpManager.py',
                          'StackManager.py',
                          'Gpc1Manager.py', 
                          'helpers.py', 
                          'constants/__init__.py',
                          'constants/Globals.py',
                          'constants/Chip.py',
                          'constants/Camera.py',
                          'constants/Fake.py',
                          'constants/Warp.py',
                          'constants/Stack.py',
                          'exceptions/__init__.py',
                          'exceptions/IppException.py',
                          'exceptions/UninstantiableIppException.py',
                          ]
    EXECUTABLES_FILENAMES = [ 'generate_chip_jobs.py', 
                              'generate_cam_jobs.py', 
                              'generate_fake_jobs.py',
                              'generate_warp_jobs.py', 
                              'generate_stack_jobs.py', 
                              'generate_exposure_dag.py',
                              'ippdor_chip_to_warp.py', 
                              # 'ippdor_chip_to_stack.py', 
                              'undo_dag_exposure.py',
                              'send_email.py', 
                              'encapsulate_chip.sh', 
                              'encapsulate_camera.sh', 
                              'encapsulate_fake.sh',
                              'encapsulate_warp.sh', 
                              'encapsulate_stack.sh',
                              ]

    def __init__(self, 
                 svn_source_url, 
                 target_directory, 
                 target_binary_directory, 
                 logger = None):
        """
        Constructor:
        
        svn_source_url: SVN Source URL
        target_directory: Target Python Directory
        target_binary_directory: Target Executables Directory
        logger: optional argument (if None: logging to logging.getLogger('ippdor'))
        """
        self.svn_source_url = svn_source_url
        self.target_directory  = target_directory
        self.target_binary_directory = target_binary_directory
        self.temporary_directory = None # Set when necessary
        if logger is not None:
            Deployment.logger = logger

    def copy_to_targetdir(self, path_to_file, filename):
        """
        Copy a file from the SVN temporary directory to the target
        directory.
        """
        Deployment.logger.info('...... Installing %s' % filename)
        shutil.copyfile('%s/%s/%s' % (self.temporary_directory, 
                                      path_to_file, 
                                      filename), 
                        '%s/%s' % (self.target_directory, filename))

    def copy_to_bindir(self, path_to_file, filename):
        """
        Copy a file from the SVN temporary directory to the target
        binary directory.
        """
        Deployment.logger.info('...... Installing %s' % filename)
        shutil.copyfile('%s/%s/%s' % (self.temporary_directory, 
                                      path_to_file, 
                                      filename), 
                        '%s/%s' % (self.target_binary_directory, filename))

    def check(self):
        """
        Check different elements
        """
        Deployment.logger.info('Checking Condor-IPP Softwares Configuration')
        # Configuration
        from IppSite import IppSite, Gpc1
        Deployment.logger.info('Checking definition of IppSite.SVN_SOURCE_URL: %s' 
                               % IppSite.SVN_SOURCE_URL)
        Deployment.logger.info('Checking definition of IppSite.TARGET_DIRECTORY: %s' 
                               % IppSite.TARGET_DIRECTORY)
        Deployment.logger.info('Checking definition of IppSite.TARGET_BINARY_DIRECTORY: %s' 
                               % IppSite.TARGET_BINARY_DIRECTORY)
        Deployment.logger.info('Checking definition of IppSite.LOCAL_TMP_DIR: %s' 
                               % IppSite.LOCAL_TMP_DIR)
        Deployment.logger.info('Checking definition of IppSite.NFS_TMP_DIR: %s' 
                               % IppSite.NFS_TMP_DIR)
        Deployment.logger.info('Checking definition of IppSite.IPP_BIN_PATH: %s' 
                               % IppSite.IPP_BIN_PATH)
        Deployment.logger.info('Checking definition of Gpc1.HOST: %s' 
                               % Gpc1.HOST)
        Deployment.logger.info('Checking definition of Gpc1.NAME: %s' 
                               % Gpc1.NAME)
        Deployment.logger.info('Checking definition of Gpc1.USER: %s' 
                               % Gpc1.USER)
        Deployment.logger.info('Checking definition of Gpc1.PASSWORD: %s[Hidden]' 
                               % Gpc1.PASSWORD[:0])
        # target_directory
        Deployment.logger.info('... Checking write permission to %s' % self.target_directory)
        extra = ''
        if not os.path.exists(self.target_directory):
            os.makedirs(self.target_directory)
            extra = ' (directory was created)'
        if not os.access(self.target_directory, os.W_OK):
            raise IOError('Directory [%s] is not writable' % self.target_directory)
        Deployment.logger.info('...... OK%s' % extra)
        # target_binary_directory
        Deployment.logger.info('... Checking write permission to %s' % self.target_binary_directory)
        extra = ''
        if not os.path.exists(self.target_binary_directory):
            os.makedirs(self.target_binary_directory)
            extra = ' (directory was created)'
        if not os.access(self.target_binary_directory, os.W_OK):
            raise IOError('Directory [%s] is not writable' % self.target_binary_directory)
        Deployment.logger.info('...... OK%s' % extra)
        # Check deployment software version?
        # Anything else?
        Deployment.logger.info('... Check complete')

    def svn_export(self):
        """
        Export the software under configuration into the SVN temporary directory
        """
        Deployment.logger.info('Updating Condor-IPP Softwares SVN temporary directory')
        Deployment.logger.info('... Creating temporary directory:')
        self.temporary_directory = tempfile.mkdtemp()
        Deployment.logger.info('...... OK [%s]' % self.temporary_directory)
        Deployment.logger.info('... Exporting Condor source code to temporary directory')
        process = subprocess.Popen( ('svn export --force %s %s' % (self.svn_source_url, 
                                                                   self.temporary_directory)).split(' '),
                                    stdout = open('/dev/null', 'w'), 
                                    stderr = open('/dev/null', 'w') )
        status = process.wait()
        if status != 0:
            Deployment.logger.error('Error: svn export failed')
            raise Exception('svn export failed')
        Deployment.logger.info('...... OK')
        Deployment.logger.info('... SVN Update complete')
        
    def generate(self):
        """
        Generate any file depending on the installation target
        """
        Deployment.logger.info('Generating Condor-IPP Softwares')
        Deployment.logger.debug('--- Nothing to generate yet from %s' % self.svn_source_url)
        Deployment.logger.info('... Generation complete')

    def deploy(self):
        """
        Deploy files on the target
        """
        Deployment.logger.info('Deploying Condor-IPP Softwares')
        Deployment.logger.info('... Installing Site definition class to [%s]' 
                               % self.target_directory)
        shutil.copyfile('.tmp_site/IppSite.py',
                        '%s/IppSite.py' % (self.target_directory))
        Deployment.logger.info('... Installing Python classes to [%s]' % self.target_directory)
        for filename in Deployment.CLASSES_FILENAMES:
            path_elements = filename.split('/')
            if len(path_elements) > 1:
                try:
                    os.makedirs('%s/%s' % (self.target_directory,
                                           path_elements[0]))
                except OSError, exc:
                    Deployment.logger.warn(exc)
                    Deployment.logger.info('Ignored')
        for filename in Deployment.CLASSES_FILENAMES:
            self.copy_to_targetdir('ipp', filename)
        Deployment.logger.info('... Installing executables to [%s]' % self.target_binary_directory)
        for filename in Deployment.EXECUTABLES_FILENAMES:
            self.copy_to_bindir('executables', filename)
        Deployment.logger.info('... Deployment complete')
        
    def clean(self, doit = False):
        """
        Clean the installation, i.e. remove files and subdirectories
        if doit is True (default is False). Otherwise, doesn't do
        anything...
        """
        Deployment.logger.info('Cleaning')
        if not doit:
            Deployment.logger.warn('!!! NO DELETION WILL BE PERFORMED SINCE doit IS False')
        try:
            Deployment.logger.info('... Deleting temporary directory [%s]' % self.temporary_directory)
            if doit:
                for root, dirs, files in os.walk(self.temporary_directory, topdown=False):
                    Deployment.logger.debug('...... Deleting [%s]' % root)
                    for name in files:
                        Deployment.logger.debug('...... Deleting [%s] (in files)' % name)
                        os.remove(os.path.join(root, name))
                    for name in dirs:
                        Deployment.logger.debug('...... Deleting [%s] (in dirs)' % name)
                        os.rmdir(os.path.join(root, name))
                os.rmdir(self.temporary_directory)
        except AttributeError:
            Deployment.logger.info('... Nothing to be cleaned')
        Deployment.logger.info('... Cleaning complete')

if __name__ == '__main__':
    print "Running doctest"
    import doctest
    doctest.testmod()
