#!/usr/bin/python
# Copyright (c) 2010 OpenStack, LLC.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import with_statement
import errno
import glob
import os
import resource
import signal
import sys
import time

ALL_SERVERS = ['account-auditor', 'account-server', 'container-auditor',
    'container-replicator', 'container-server', 'container-updater',
    'object-auditor', 'object-server', 'object-replicator', 'object-updater',
    'proxy-server', 'account-replicator', 'auth-server', 'account-reaper']
GRACEFUL_SHUTDOWN_SERVERS = ['account-server', 'container-server',
    'object-server', 'proxy-server', 'auth-server']
MAX_DESCRIPTORS = 32768
MAX_MEMORY = (1024 * 1024 * 1024) * 2 # 2 GB

_, server, command = sys.argv
if server == 'all':
    servers = ALL_SERVERS
else:
    if '-' not in server:
        server = '%s-server' % server
    servers = [server]
command = command.lower()

def pid_files(server):
    if os.path.exists('/var/run/swift/%s.pid' % server):
        pid_files = ['/var/run/swift/%s.pid' % server]
    else:
        pid_files = glob.glob('/var/run/swift/%s/*.pid' % server)
    for pid_file in pid_files:
        pid = int(open(pid_file).read().strip())
        yield pid_file, pid

def do_start(server, once=False):
    server_type = '-'.join(server.split('-')[:-1])

    for pid_file, pid in pid_files(server):
        if os.path.exists('/proc/%s' % pid):
            print "%s appears to already be running: %s" % (server, pid_file)
            return
        else:
            print "Removing stale pid file %s" % pid_file
            os.unlink(pid_file)

    try:
        resource.setrlimit(resource.RLIMIT_NOFILE,
                (MAX_DESCRIPTORS, MAX_DESCRIPTORS))
        resource.setrlimit(resource.RLIMIT_DATA,
                (MAX_MEMORY, MAX_MEMORY))
    except ValueError:
        print "Unable to increase file descriptor limit.  Running as non-root?"
    os.environ['PYTHON_EGG_CACHE'] = '/tmp'

    def write_pid_file(pid_file, pid):
        dir, file = os.path.split(pid_file)
        if not os.path.exists(dir):
            try:
                os.makedirs(dir)
            except OSError, err:
                if err.errno == errno.EACCES:
                    sys.exit('Unable to create %s.  Running as non-root?' % dir)
        fp = open(pid_file, 'w')
        fp.write('%d\n' % pid)
        fp.close()

    def launch(ini_file, pid_file):
        cmd = 'swift-%s' % server
        args = [server, ini_file]
        if once:
            print 'Running %s once' % server
            args.append('once')
        else:
            print 'Starting %s' % server

        pid = os.fork()
        if pid == 0:
            os.setsid()
            with open(os.devnull, 'r+b') as nullfile:
                for desc in (0, 1, 2): # close stdio
                    try:
                        os.dup2(nullfile.fileno(), desc)
                    except OSError:
                        pass
            try:
                if once:
                    os.execlp('swift-%s' % server, server,
                        ini_file, 'once')
                else:
                    os.execlp('swift-%s' % server, server, ini_file)
            except OSError:
                print 'unable to launch %s' % server
            sys.exit(0)
        else:
            write_pid_file(pid_file, pid)

    ini_file = '/etc/swift/%s-server.conf' % server_type
    if os.path.exists(ini_file):
        # single config file over-rides config dirs
        pid_file = '/var/run/swift/%s.pid' % server
        launch_args = [(ini_file, pid_file)]
    elif os.path.exists('/etc/swift/%s-server/' % server_type):
        # found config directory, searching for config file(s)
        launch_args = []
        for num, ini_file in enumerate(glob.glob('/etc/swift/%s-server/*.conf' % server_type)):
            pid_file = '/var/run/swift/%s/%d.pid' % (server, num)
            # start a server for each ini_file found
            launch_args.append((ini_file, pid_file))
    else:
        # maybe there's a config file(s) out there, but I couldn't find it!
        sys.exit('Unable to locate config file for %s. %s does not exist?' % (server, ini_file))

    # start all servers
    for ini_file, pid_file in launch_args:
        launch(ini_file, pid_file)

def do_stop(server, graceful=False):
    if graceful and server in GRACEFUL_SHUTDOWN_SERVERS:
        sig = signal.SIGHUP
    else:
        sig = signal.SIGTERM

    did_anything = False
    pfiles = pid_files(server)
    for pid_file, pid in pfiles:
        did_anything = True
        try:
            print 'Stopping %s  pid: %s  signal: %s' % (server, pid, sig)
            os.kill(pid, sig)
        except OSError:
            print "Process %d not running" % pid
        try:
            os.unlink(pid_file)
        except OSError:
            pass
    for pid_file, pid in pfiles:
        for _ in xrange(150): # 15 seconds
            if not os.path.exists('/proc/%s' % pid):
                break
            time.sleep(0.1)
        else:
            print 'Waited 15 seconds for pid %s (%s) to die; giving up' % \
                  (pid, pid_file)
    if not did_anything:
        print 'No %s running' % server

if command == 'start':
    for server in servers:
        do_start(server)

if command == 'stop':
    for server in servers:
        do_stop(server)

if command == 'shutdown':
    for server in servers:
        do_stop(server, graceful=True)

if command == 'restart':
    for server in servers:
        do_stop(server)
    for server in servers:
        do_start(server)

if command == 'reload':
    for server in servers:
        do_stop(server, graceful=True)
        do_start(server)

if command == 'once':
    for server in servers:
        do_start(server, once=True)
