"""Laten BuildSlave for Disribute Resource Management Application API

Author: Lital Natan <procscsi@gmail.com>

"""

from twisted.internet import threads
from buildbot.buildslave import AbstractLatentBuildSlave
import drmaa

class DRMAALatentBuildSlave(AbstractLatentBuildSlave):
    """Latent BuildSlave for Disribute Resource Management Application API
    
    BuildSlave managed by a Job Distribution System
    When a build is requested, the slave is started on a compute node
    When all builds are done and the slave is idle, it shuts down and the
    job is terminated.

    """

    _drmaa_session = None
    drmaa_session_contact = None
    last_job_id = None
    job_template = None

    def __init__(self, buildslave_setup_command, *args, **kwargs):
        """Default Constructor

        buildslave_setup_command -- the o/s command starting the BuildBot slave
                                    its recommended to use a command starting a 
                                    non-daemon slave instance.
        *args -- non-keyword arguments for the BuildBot slave
        **kwargs -- keyword arguments for the BuildBot slave

        """

        AbstractLatentBuildSlave.__init__(self, *args, **kwargs)
        self.job_template = self._get_blank_job_template()
        self.job_template.remoteCommand = buildslave_setup_command
        
    def _get_blank_job_template(self):
        """Creates a new, default job template from the DRMAA session """

        drmaa_session = self._get_persistent_drmaa_session()
        return drmaa_session.createJobTemplate()

    def _get_persistent_drmaa_session(self):
        """Obtains a singleton of the DRMAA session """

        if not DRMAALatentBuildSlave._drmaa_session:
            session = None
            if self.drmaa_session_contact:
                session = drmaa.Session(self.drmaa_session_contact)
            else:
                session = drmaa.Session()
                self.drmaa_session_contact = session.contact
            DRMAALatentBuildSlave._drmaa_session = session
        return DRMAALatentBuildSlave._drmaa_session

    def _run_job(self):
        """Submits the BuildBot slave job using """

        drmaa_session = self._get_persistent_drmaa_session()
        self.last_job_id = drmaa_session.runJob(self.job_template)

    def _terminate_job(self):
        """Terminates the current running BuildBot slave """

        drmaa_session = self._get_persistent_drmaa_session()
        drmaa_session.control(self.last_job_id, drmaa.JobControlAction.TERMINATE)
        self.last_job_id = None

    def start_instance(self):
        """Starts the BuildBot slave -> Deferred thread

        Implements AbstractLatentBuildSlave

        """

        print "Starting remote slave instance"
        return threads.deferToThread(self._start_instance)

    def stop_instance(self, fast=False):
        """Stops the BuildBot slave -> Deferred thread

        Implements AbstractLatentBuildSlave

        """

        print "Stopping remote slave instance"
        return threads.deferToThread(self._stop_instance)

    def _start_instance(self):
        """Starts the BuildBot slave -> Deferred thread

        This method exists for extending sub-classes to overwrite if needed
        """

        self._run_job()

    def _stop_instance(self):
        """Stops the BuildBot slave -> Deferred thread

        This method exists for extending sub-classes to overwrite if needed
        """

        self._terminate_job()

    def buildFinished(self, sb):
        """Stops the BuildSlave job when idle"""

        self.building.remove(sb.builder_name)
        if not self.building:
            self._setBuildWaitTimer()
            self.insubstantiate()

