#!/usr/bin/python

# Copyright (c) 2009-2011 by Bjoern Kolbeck, Zuse Institute Berlin
# Licensed under the BSD License, see LICENSE file for details.

from time import sleep
from optparse import OptionParser
import os
import traceback
import subprocess
import sys
import signal


class Server:
    def __init__(self,
                 start_stop_retries,
                 config_file_path,
                 run_dir_path,
                 xtreemfs_dir,
                 service_name):
        self._start_stop_retries = start_stop_retries
        self._config_file_path = config_file_path
        self._run_dir_path = run_dir_path
        self._xtreemfs_dir = xtreemfs_dir
        self._service_name = service_name

    def _get_pid_file_path(self):
        return self._run_dir_path

    def get_config_file_path(self):
        return self._config_file_path

    def is_running(self):
        pid_file_path = self._get_pid_file_path()
        if os.path.exists(pid_file_path):
            pid = open(pid_file_path).read().strip()

            try:
                pid = int(pid)
            except ValueError:
                return False

            print "xtestenv: checking if server is running with pid", pid

            # Use 'waitpid' to touch any zombies and ensure that these are
            # cleaned up first.
            try:
                # We dont care about the actual result of waitpid.
                os.waitpid(int(pid), os.WNOHANG)
            except OSError:
                pass

            return os.path.exists("/proc/" + str(pid))
        else:
            return False

    def start(self,
              log_file_path=None):

        if sys.platform == "win32" or not self.is_running():
            pid_file_path = self._get_pid_file_path()

            java_args = [os.path.join(os.environ["JAVA_HOME"], "bin", "java")]

            # Enable assertions.
            java_args.append("-ea")

            # Construct the -cp classpath
            XtreemFS_jar_file_path = os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "servers", "dist", "XtreemFS.jar"))
            if os.path.exists(XtreemFS_jar_file_path):
                classpath = (
                             XtreemFS_jar_file_path,
                             os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "lib", "BabuDB.jar")),
                             os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "lib", "protobuf-java-2.5.0.jar")),
                             os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "flease", "dist", "Flease.jar")),
                             os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "foundation", "dist", "Foundation.jar")),
                             os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "lib", "jdmkrt.jar")),
                             os.path.abspath(os.path.join(self._xtreemfs_dir, "java", "lib", "commons-codec-1.3.jar")),
                             )
                if sys.platform.startswith("win"):
                    classpath = ";".join(classpath)
                else:
                    classpath = ":".join(classpath)
                java_args.extend(("-cp", classpath))

            # Name of the class to start
            java_args.append("org.xtreemfs." + self._service_name.lower() + "." + self._service_name.upper())

            # .config file
            java_args.append(self.get_config_file_path())

            # Don't .join java_args, since Popen wants a sequence when shell=False

            if log_file_path is None:
                stderr = sys.stderr
                stdout = sys.stdout
            else:
                # Redirect stderr and stdout to a log file
                stderr = stdout = open(log_file_path, "a")

            #print "xctl: starting", self.__class__.__name__, "server with UUID", self.get_uuid(), "on port", self.get_rpc_port(), "with", " ".join(java_args)

            p = subprocess.Popen(java_args, stdout=stdout, stderr=stderr) # No shell=True: we only want one process (java), not two (/bin/sh and java)
            if p.returncode is not None:
                raise RuntimeError(self.get_uuid() + " failed to start: " + str(p.returncode))
            pidfile = open(pid_file_path, "w+")
            pidfile.write(str(p.pid))
            pidfile.close()

            print "xtestenv: started", self._service_name, "server with pid", p.pid

            sleep(2.0)

            if not self.is_running():
                raise RuntimeError, "server failed to start"
        else:
            print "xtestenv:", self._service_name, "server is already running"

    def stop(self):
        pid_file_path = self._get_pid_file_path()
        if os.path.exists(pid_file_path):
            pid = int(open(pid_file_path).read().strip())

            if sys.platform.startswith("win"):
                subprocess.call("TASKKILL /PID %(pid)u /F /T" % locals())
                killed = True
            else:
                killed = False
                for signo in (signal.SIGTERM, signal.SIGKILL):
                    for try_i in xrange(self._start_stop_retries):
                        print "xtestenv: stopping", self._service_name, "server with pid", pid, "with signal", str(signo) + ", try", try_i

                        try: os.kill(pid, signo)
                        except: pass

                        sleep(1)

                        # Use 'waitpid' to touch any zombies and ensure that these are
                        # cleaned up first.
                        try:
                            # We dont care about the actual result of waitpid.
                            os.waitpid(int(pid), os.WNOHANG)
                        except OSError:
                            pass

                        killed = not os.path.exists("/proc/" + str(pid))
                        if killed:
                            break

                    if killed:
                        break

            if killed:
                os.unlink(pid_file_path)
            else:
                print "FAILED to stop", self._service_name, "server with pid", pid

        else:
            print "xtestenv: no pid file for", self._service_name, "server found"


START_STOP_SERVICE_RETRIES = 3

if __name__ == "__main__":
    xtreemfs_dir = os.path.join(os.path.dirname( os.path.abspath( sys.modules[__name__].__file__ ) ), "..")

    option_parser = OptionParser( "usage: %prog [options]")
    option_parser.add_option( "-b", "--base-dir", action="store", dest="base_dir", default='/tmp/xtreemfs_xstartserv/')
    option_parser.add_option( "-c", "--config-file-path", action="store", dest="config_file_path")
    option_parser.add_option(  "--start-dir", action="store_true", dest="dir_start")
    option_parser.add_option(  "--stop-dir", action="store_true", dest="dir_stop")
    option_parser.add_option(  "--start-mrc", action="store_true", dest="mrc_start")
    option_parser.add_option(  "--stop-mrc", action="store_true", dest="mrc_stop")
    option_parser.add_option(  "--start-osd", action="store_true", dest="osd_start")
    option_parser.add_option(  "--stop-osd", action="store_true", dest="osd_stop")
    option_parser.add_option(  "--dir-suffix", action="store", dest="dir_suffix")
    option_parser.add_option(  "--mrc-suffix", action="store", dest="mrc_suffix")
    option_parser.add_option(  "--osd-suffix", action="store", dest="osd_suffix")

    options, positional_args = option_parser.parse_args()
    
    if options.dir_suffix:
        dir_string = "dir" + options.dir_suffix
    else:
        dir_string = "dir"

    if options.mrc_suffix:
        mrc_string = "mrc" + options.mrc_suffix
    else:
        mrc_string = "mrc"

    if options.osd_suffix:
        osd_string = "osd" + options.osd_suffix
    else:
        osd_string = "osd"

    if options.dir_start or options.mrc_start or options.osd_start:
        try:
            os.mkdir(options.base_dir)
        except:
            pass
        try:
            os.mkdir(os.path.join(options.base_dir, "run"))
        except:
            pass
        try:
            os.mkdir(os.path.join(options.base_dir, "log"))
        except:
            pass
        

    if options.dir_start:
        if not options.config_file_path:
            config_file = os.path.join(xtreemfs_dir,
                                       "etc/xos/xtreemfs/" + dir_string + "config.properties")
        else:
            config_file = options.config_file_path
        print "Starting DIR with config:", config_file
        dir = Server(1,
                     config_file,
                     os.path.join(options.base_dir, "run", "xtreemfs_" + dir_string + ".pid"),
                     xtreemfs_dir,
                     'DIR')
        dir.start(os.path.join(options.base_dir, "log", dir_string + ".log"))

    elif options.dir_stop:
        print "Stopping DIR..."
        dir = Server(1,
                     '',
                     os.path.join(options.base_dir, "run", "xtreemfs_" + dir_string + ".pid"),
                     xtreemfs_dir,
                     'DIR')
        dir.stop()

    if options.mrc_start:
        if not options.config_file_path:
            config_file = os.path.join(xtreemfs_dir,
                                       "etc/xos/xtreemfs/" + mrc_string + "config.properties")
        else:
            config_file = options.config_file_path
        print "Starting MRC with config:", config_file
        mrc = Server(1,
                     config_file,
                     os.path.join(options.base_dir, "run", "xtreemfs_" + mrc_string + ".pid"),
                     xtreemfs_dir,
                     'MRC')
        mrc.start(os.path.join(options.base_dir, "log", mrc_string + ".log"))

    elif options.mrc_stop:
        print "Stopping MRC..."
        mrc = Server(1,
                     '',
                     os.path.join(options.base_dir, "run", "xtreemfs_" + mrc_string + ".pid"),
                     xtreemfs_dir,
                     'MRC')
        mrc.stop()

    if options.osd_start:
        if not options.config_file_path:
            config_file = os.path.join(xtreemfs_dir,
                                       "etc/xos/xtreemfs/" + osd_string + "config.properties")
        else:
            config_file = options.config_file_path
        print "Starting OSD with config:", config_file
        osd = Server(1,
                     config_file,
                     os.path.join(options.base_dir, "run", "xtreemfs_" + osd_string + ".pid"),
                     xtreemfs_dir,
                     'OSD')
        osd.start(os.path.join(options.base_dir, "log", osd_string + ".log"))

    elif options.osd_stop:
        print "Stopping OSD..."
        osd = Server(1,
                     '',
                     os.path.join(options.base_dir, "run", "xtreemfs_" + osd_string + ".pid"),
                     xtreemfs_dir,
                     'OSD')
        osd.stop()
