class HPCStatsApp(object):

    """Abstract HPCStats application class, parent of all real application
       classes (Importer, Reporter).
    """

    def __init__(self, conf, cluster_name):

        self.conf = conf
        self.cluster_name = cluster_name
        self.cluster = None # Cluster object created later in apps run()

    def new_db(self):
        """Returns a new HPCStatsDB object."""

        # Instantiate connexion to db
        db = HPCStatsDB(self.conf)
        db.bind()
        return db

    def run_check(self):
        """Pre-run checks"""

        # dump configuration in debug mode
        for section in self.conf.sections():
            logger.debug("conf: %s", section)
            for option in self.conf.options(section):
                logger.debug("conf:  %s=%s",
                             option,
                             self.conf.get(section, option))

        if self.cluster_name != 'all':
            # check presence of cluster in configuration
            self.conf.check_cluster()

    def run(self):
        """Run method that should be implemented by all real App classes.
           For this abstract class, it raises NotImplementedError.
        """
        raise NotImplementedError class JobImporterFactory(object):

    """This class simply delivers the factory() static method, there is not
       point to instanciate it with an object.
    """

    def __init__(self):
        pass

    @staticmethod
    def factory(app, db, config, cluster):
        """This method returns the appropriate JobImporter object depending
           on what is specified in configuration. In case of configuration
           error, HPCStatsConfigurationException is raised.
        """

        implem = config.get(cluster.name, 'jobs')

        if implem == "slurm":
            return JobImporterSlurm(app, db, config, cluster)
        else:
            raise HPCStatsConfigurationException( \
                    "JobImporter %s is not implemented" \
                      % (implem)) class UserImporterFactory(object):

    """This class simply delivers the factory() static method, there is not
       point to instanciate it with an object.
    """

    def __init__(self):
        pass

    @staticmethod
    def factory(app, db, config, cluster):
        """This method returns the appropriate UserImporter object depending on
           what is specified in configuration. In case of configuration error,
           HPCStatsConfigurationException is raised.
        """
        implem = config.get(cluster.name, 'users')

        if implem == 'ldap':
            return UserImporterLdap(app, db, config, cluster)
        if implem == 'ldap+slurm':
            return UserImporterLdapSlurm(app, db, config, cluster)
        else:
            raise HPCStatsConfigurationException( \
                    "UserImporter %s is not implemented" \
                      % (implem)) class ArchitectureImporterFactory(object):

    """This class simply delivers the factory() static method, there is not
       point to instanciate it with an object.
    """

    def __init__(self):
        pass

    @staticmethod
    def factory(app, db, config, cluster_name):
        """This method returns the appropriate ArchitectureImporter object
           depending on what is specified in configuration. In case of
           configuration error, HPCStatsConfigurationException is raised.
        """

        implem = config.get(cluster_name, 'architecture')

        if implem == "archfile":
            return ArchitectureImporterArchfile(app, db, config, cluster_name)
        else:
            raise HPCStatsConfigurationException( \
                    "ArchitectureImporter %s is not implemented" \
                      % (implem)) class EventImporterFactory(object):

    """This class simply delivers the factory() static method, there is not
       point to instanciate it with an object.
    """

    def __init__(self):
        pass

    @staticmethod
    def factory(app, db, config, cluster):
        """This method returns the appropriate EventImporter object depending
           on what is specified in configuration. In case of configuration
           error, HPCStatsConfigurationException is raised.
        """

        implem = config.get(cluster.name, 'events')

        if implem == "slurm":
            return EventImporterSlurm(app, db, config, cluster)
        else:
            raise HPCStatsConfigurationException( \
                    "EventImporter %s is not implemented" \
                      % (implem)) class FSQuotaImporterFactory(object):

    """This class simply delivers the factory() static method, there is no
       point in instanciating it with an object.
    """

    def __init__(self):
        pass

    @staticmethod
    def factory(app, db, config, cluster):
        """This method returns the appropriate FSQuotaImporter object
           depending on what is specified in the configuration. In case of a
           configuration error, an HPCStatsConfigurationException is raised.
        """

        implem = config.get(cluster.name, 'fsquota')

        if implem == 'dummy':
            return FSQuotaImporterDummy(app, db, config, cluster)
        elif implem == "ssh":
            return FSQuotaImporterSSH(app, db, config, cluster)
        else:
            raise HPCStatsConfigurationException( \
                    "FSQuotaImporter %s is not implemented" \
                      % (implem)) class FSUsageImporterFactory(object):

    """This class simply delivers the factory() static method, there is not
       point to instanciate it with an object.
    """

    def __init__(self):
        pass

    @staticmethod
    def factory(app, db, config, cluster):
        """This method returns the appropriate FSUsageImporter object depending
           on what is specified in configuration. In case of configuration
           error, HPCStatsConfigurationException is raised.
        """

        implem = config.get(cluster.name, 'fsusage')

        if implem == 'dummy':
            return FSUsageImporterDummy(app, db, config, cluster)
        elif implem == "ssh":
            return FSUsageImporterSSH(app, db, config, cluster)
        else:
            raise HPCStatsConfigurationException( \
                    "FSUsageImporter %s is not implemented" \
                      % (implem)) class BusinessCodeImporterFactory(object):

    """This class simply delivers the factory() static method, there is not
       point to instanciate it with an object.
    """

    def __init__(self):
        pass

    @staticmethod
    def factory(app, db, config):
        """This method returns the appropriate BusinessCodeImporter object
           depending on what is specified in configuration. In case of
           configuration error, HPCStatsConfigurationException is raised.
        """

        implem = config.get_default('globals', 'business', 'dummy')

        if implem == 'dummy':
            return BusinessCodeImporterDummy(app, db, config)
        elif implem == 'slurm':
            return BusinessCodeImporterSlurm(app, db, config)
        elif implem == 'csv':
            return BusinessCodeImporterCSV(app, db, config)
        else:
            raise HPCStatsConfigurationException( \
                    "BusinessCodeImporter %s is not implemented" \
                      % (implem)) class ProjectImporterFactory(object):

    """This class simply delivers the factory() static method, there is not
       point to instanciate it with an object.
    """

    def __init__(self):
        pass

    @staticmethod
    def factory(app, db, config):
        """This method returns the appropriate ProjectImporter object depending
           on what is specified in configuration. In case of configuration
           error, HPCStatsConfigurationException is raised.
        """

        implem = config.get_default('globals', 'projects', 'dummy')

        if implem == 'dummy':
            return ProjectImporterDummy(app, db, config)
        elif implem == 'slurm':
            return ProjectImporterSlurm(app, db, config)
        elif implem == 'csv':
            return ProjectImporterCSV(app, db, config)
        else:
            raise HPCStatsConfigurationException( \
                    "ProjectImporter %s is not implemented" \
                      % (implem)) class Cluster(object):
    """Model class for Cluster table"""

    def __init__(self, name, cluster_id=None):

        self.cluster_id = cluster_id
        self.name = name
    
    def __str__(self):

        return self.name

    def __eq__(self, other):

        return self.name == other.name

    def find(self, db):
        """Search the Cluster in the database based on its name. If exactly
           one cluster matches in database, set cluster_id attribute properly
           and returns its value. If more than one cluster matches, raises
           HPCStatsDBIntegrityError. If no cluster is found, returns None.
        """

        req = """
                SELECT cluster_id
                  FROM Cluster
                 WHERE cluster_name = %s
              """
        params = ( self.name, )
        db.execute(req, params)
        nb_rows = db.cur.rowcount
        if nb_rows == 0:
            logger.debug("cluster %s not found in DB", str(self))
            return None
        elif nb_rows > 1:
            raise HPCStatsDBIntegrityError(
                    "several cluster_id found in DB for cluster %s" \
                      % (str(self)))
        else:
            self.cluster_id = db.cur.fetchone()[0]
            logger.debug("cluster %s found in DB with id %d",
                         str(self),
                         self.cluster_id )
            return self.cluster_id

    def save(self, db):
        """Insert Cluster in database. You must make sure that the Cluster does
           not already exist in database yet (typically using Cluster.find()
           method else there is a risk of future integrity errors because of
           duplicated clusters. If cluster_id attribute is set, it raises
           HPCStatsRuntimeError.
        """

        if self.cluster_id is not None:
            raise HPCStatsRuntimeError(
                    "could not insert cluster %s since already existing in "\
                    "database" \
                      % (str(self)))

        req = """
                INSERT INTO Cluster ( cluster_name )
                VALUES ( %s )
                RETURNING cluster_id
              """
        params = ( self.name, )

        #print db.cur.mogrify(req, params)
        db.execute(req, params)
        self.cluster_id = db.cur.fetchone()[0]

    def get_nb_cpus(self, db):
        """Returns the total number of CPUs available on the cluster"""

        if self.cluster_id is None:
            raise HPCStatsRuntimeError(
                    "could not search for data with cluster %s since not " \
                    "found in database" \
                      % (str(self)))

        req = """
                SELECT SUM(node_nbCpu)
                  FROM Node
                 WHERE cluster_id = %s
              """
        params = ( self.cluster_id, )

        #print db.cur.mogrify(req, params)
        db.execute(req, params)
        return db.cur.fetchone()[0]

    def get_min_datetime(self, db):
        """Returns the start datetime of the oldest started and unfinished
           job on the cluster.
        """

        if self.cluster_id is None:
            raise HPCStatsRuntimeError(
                    "could not search for data with cluster %s since not " \
                    "found in database" \
                      % (str(self)))

        req = """
                SELECT MIN(job_start)
                  FROM Job
                 WHERE cluster_id = %s
                   AND job_state NOT IN ('CANCELLED', 'NODE_FAIL', 'PENDING')
              """
        params = ( self.cluster_id, )

        #print db.cur.mogrify(req, params)
        db.execute(req, params)
        return db.cur.fetchone()[0]

   
    def get_nb_accounts(self, db, creation_date):
        """Returns the total of users on the cluster whose account have been
           created defore date given in parameter.
        """

        if self.cluster_id is None:
            raise HPCStatsRuntimeError(
                    "could not search for data with cluster %s since not " \
                    "found in database" \
                      % (str(self)))

        req = """
                SELECT COUNT (userhpc_id)
                  FROM Userhpc,
                       Account
                 WHERE Account.userhpc_id = Userhpc.userhpc_id
                   AND Account.account_creation < %s
                   AND Account.cluster_id = %s
              """
        params = (creation_date, self.cluster_id )

        #print db.cur.mogrify(req, params)
        db.execute(req, params)
        
        return db.cur.fetchone()[0]

    def get_nb_active_users(self, db, start, end):
        """Returns the total number of users who have run job(s) on the cluster
           between start and end datetimes in parameters.
        """

        if self.cluster_id is None:
            raise HPCStatsRuntimeError(
                    "could not search for data with cluster %s since not " \
                    "found in database" \
                      % (str(self)))

        req = """
                SELECT COUNT(DISTINCT userhpc_id)
                  FROM Job
                 WHERE Job.cluster_id = %s,
                   AND ((job_start BETWEEN %s AND %s)
                        OR (job_end BETWEEN %s AND %s)
                        OR (job_start <= %s AND job_end >= %s))
              """
        params = (self.cluster_id, start, end, start, end, start, end)
        #print db.cur.mogrify(req, params)
        db.execute(req, params)
        
        return db.cur.fetchone()[0]
import logging
from HPCStats.CLI.HPCStatsApp import HPCStatsApp
from HPCStats.Importer.Jobs.JobImporterFactory import JobImporterFactory
from HPCStats.Importer.Users.UserImporterFactory import UserImporterFactory
from HPCStats.Importer.Architectures.ArchitectureImporterFactory import ArchitectureImporterFactory
from HPCStats.Importer.Events.EventImporterFactory import EventImporterFactory
from HPCStats.Importer.FSQuota.FSQuotaImporterFactory import FSQuotaImporterFactory
from HPCStats.Importer.FSUsage.FSUsageImporterFactory import FSUsageImporterFactory
from HPCStats.Importer.BusinessCodes.BusinessCodeImporterFactory import BusinessCodeImporterFactory
from HPCStats.Importer.Projects.ProjectImporterFactory import ProjectImporterFactory
from HPCStats.Model.Cluster import Cluster
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2011-2015 EDF SA
# Contact:
#       CCN - HPC <dsp-cspit-ccn-hpc@edf.fr>
#       1, Avenue du General de Gaulle
#       92140 Clamart
#
# Authors: CCN - HPC <dsp-cspit-ccn-hpc@edf.fr>
#
# This file is part of HPCStats.
#
# HPCStats is free software: you can redistribute in and/or
# modify it under the terms of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# 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 HPCStats. If not, see
# <http://www.gnu.org/licenses/>.
#
# On Calibre systems, the complete text of the GNU General
# Public License can be found in `/usr/share/common-licenses/GPL'.

"""This module contains the HPCStatsChecker class."""

logger = logging.getLogger(__name__)


class HPCStatsChecker(HPCStatsApp):

    """HPCStats Checker application which checks all data sources availability.
    """

    def __init__(self, conf, cluster_name):

        super(HPCStatsChecker, self).__init__(conf, cluster_name)

        # all importer objects
        self.business = None
        self.projects = None
        self.arch = None
        self.mounts = None
        self.fsusage = None
        self.events = None
        self.users = None
        self.jobs = None

    def run(self):
        """Run HPCStats Checker application."""

        self.run_check()

        db = self.new_db()

        # import projects and business code that are globals and not related
        # to a specific cluster
        logger.info("checking projects sources")
        self.projects = ProjectImporterFactory.factory(self, db, self.conf)
        self.projects.check()

        logger.info("checking business codes sources")
        self.business = BusinessCodeImporterFactory.factory(self, db, self.conf)
        self.business.check()

        if self.cluster_name == 'all':
            clusters = self.conf.get_clusters_list()
        else:
            clusters = [ self.cluster_name ]

        for cluster_name in clusters:
            self.check_cluster_sources(db, cluster_name)

        db.commit()
        db.unbind()

    def check_cluster_sources(self, db, cluster_name):
        """Check data sources for a cluster."""

        cluster = None

        logger.info("checking architecture source for cluster %s",
                    cluster_name)
        self.arch = \
          ArchitectureImporterFactory.factory(self, db, self.conf,
                                              cluster_name)
        self.arch.check()

        cluster = Cluster(cluster_name)

        logger.info("checking users source for cluster %s",
                    cluster.name)
        self.users = \
          UserImporterFactory.factory(self, db, self.conf, cluster)
        self.users.check()

        logger.info("checking filesystem usage source for cluster %s",
                    cluster.name)
        self.fsusage = \

          FSUsageImporterFactory.factory(self, db, self.conf, cluster)