#!/usr/bin/env /opt/opensvc/bin/python
#
# Copyright (c) 2009 Christophe Varoqui <christophe.varoqui@free.fr>'
# Copyright (c) 2009 Cyril Galibern <cyril.galibern@free.fr>'
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# 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 this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import sys
import os
import optparse
import string
import platform

#
# add project lib to path
#
pathsvc = os.path.realpath(os.path.join(os.path.dirname(__file__), '..'))
pathetc = os.path.join(pathsvc, 'etc')
sys.path.append(os.path.join(pathsvc, 'lib'))
prog = os.path.basename(__file__)

import svcBuilder
import rcExceptions as ex
from rcUtilities import *
from lock import *
import node
from rcStatus import colorize

sysname, nodename, x, x, machine, x = platform.uname()

if sysname == "Windows":                                                                                                                              
    mp = False                                                                                                                                        
else:                                                                                                                                                 
    try:                                                                                                                                              
        from multiprocessing import Process, Queue, Lock                                                                                              
        mp = True                                                                                                                                     
    except:                                                                                                                                           
        mp = False                                                                                                                                    

try:
    from version import version
except:
    version = "dev"

def svcmon_verbose(svcs):
    for svc in svcs:
        try:
            svc.print_status()
        except:
            pass

def max_len(svcs):
    _len = 7
    for svc in svcs:
        l = len(getattr(svc, "svcname"))
        if _len < l:
            _len = l
        for container in svc.get_resources('container'):
            l = len(getattr(container, "name")) + 2
            if _len < l:
                _len = l
    return _len

def svcmon_normal1(svc,upddb=False, fmt=None, queue=None, lock=None):
    # don't schedule svcmon updates for encap services.
    # those are triggered by the master node
    status = svc.group_status()
    l = []
    buff = fmt % (
              svc.svcname,
              svc.svctype,
              svc.svcmode,
              colorize(status["container"]),
              colorize(status["ip"]),
              colorize(status["disk"]),
              colorize(status["fs"]),
              colorize(status.get("share", "n/a")),
              colorize(status["app"]),
              colorize(status["hb"]),
              colorize(status["sync"]),
              colorize(status["avail"]),
              colorize(status["overall"]),
              svc.frozen())
    l.append(buff)
    containers = svc.get_resources("container")
    if len(containers) > 0 and svc.has_encap_resources:
        for container in containers:
            try:
                s = svc.encap_json_status(container)
            except ex.excNotAvailable as e:
                s = {'resources': [],
                     'ip': 'n/a',
                     'disk': 'n/a',
                     'sync': 'n/a',
                     'hb': 'n/a',
                     'container': 'n/a',
                     'fs': 'n/a',
                     'share': 'n/a',
                     'app': 'n/a',
                     'avail': 'n/a',
                     'overall': 'n/a'}

            buff = fmt % (
                      ' @'+container.name,
                      '',
                      container.type.replace('container.', ''),
                      colorize(s["container"]),
                      colorize(s["ip"]),
                      colorize(s["disk"]),
                      colorize(s["fs"]),
                      colorize(s.get("share", "n/a")),
                      colorize(s["app"]),
                      colorize(s["hb"]),
                      colorize(s["sync"]),
                      colorize(s["avail"]),
                      colorize(s["overall"]),
                      '')
            l.append(buff)

    if lock is not None:
        lock.acquire()
    print('\n'.join(l))
    if lock is not None:
        lock.release()

    if upddb:
        o = svc.svcmon_push_lists(status)
        _size = len(str(o))
        if queue is None or _size > 30000:
            # multiprocess Queue not supported, can't combine results
            g_vars, g_vals, r_vars, r_vals = svc.svcmon_push_lists(status)
            svc.node.collector.call('svcmon_update_combo', g_vars, g_vals, r_vars, r_vals)
        else:
            queue.put(svc.svcmon_push_lists(status))

def svcmon_normal(svcs, upddb=False):
    fmt = '%-' + str(max_len(svcs)) + 's'
    fmt += ' %-7s %-9s %-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s %-10s %-6s'

    print(fmt % ("service", "service", "container", "container", "ip    ", "disk  ", "fs    ", "share ", "app   ", "hb    ", "sync  ", "avail ", "overall", "      "))
    print(fmt % ("name   ", "type   ", "type     ", "status   ", "status", "status", "status", "status", "status", "status", "status", "status", "status ", "frozen"))
    print(fmt % ("-------", "-------", "---------", "---------", "------", "------", "------", "------", "------", "------", "------", "------", "-------", "------"))
    ps = []
    queues = {}

    for svc in svcs:
        if svc.encap and upddb:
            continue
        if not mp:
            svcmon_normal1(svc, upddb, fmt, None)
            continue
        try:
            queues[svc.svcname] = Queue(maxsize=32000)
            lock = Lock()
        except:
            # some platform don't support Queue's synchronize (bug 3770)
            queues[svc.svcname] = None
        p = Process(target=svcmon_normal1, args=(svc, upddb, fmt, queues[svc.svcname], lock))
        p.start()
        ps.append(p)
    for p in ps:
        p.join()

    if upddb and mp:
        g_vals = []
        r_vals = []
        if options.delay > 0:
            import random
            import time
            delay = int(random.random()*options.delay)
            time.sleep(delay)

        for svc in svcs:
            if svc.svcname not in queues or queues[svc.svcname] is None:
                continue
            if queues[svc.svcname].empty():
                continue
            g_vars, _g_vals, r_vars, _r_vals = queues[svc.svcname].get()
            g_vals.append(_g_vals)
            r_vals.append(_r_vals)
        if len(g_vals) > 0:
            svc.node.collector.call('svcmon_update_combo', g_vars, g_vals, r_vars, r_vals)

__ver = prog + " version " + version
__usage = "%prog [options]\n\nDisplay services status"
parser = optparse.OptionParser(version=__ver, usage=__usage)
parser.add_option("--service", default="", action="store", dest="parm_svcs",
                  help="comma-separated list of service to display status of")
parser.add_option("--updatedb", default=False, action="store_true", dest="upddb",
                  help="update resource status in central database")
parser.add_option("--verbose", default=False, action="store_true", dest="verbose",
                  help="display per-resource status for each selected service")
parser.add_option("--maxdelaydb", default=0, action="store", type="int", dest="delay",
                  help="introduce a random delay before pushing to database to level the load on the collector")
parser.add_option("--debug", default=False, action="store_true", dest="debug",
                  help="debug mode")

(options, args) = parser.parse_args()

if options.upddb:
    lockf = 'svcmon.lock'
    try:
        lockfd = monlock(fname=lockf)
    except ex.excError:
        sys.exit(1)
    except:
        import traceback
        traceback.print_exc()
        sys.exit(1)
 
node = node.Node()

if len(options.parm_svcs) > 0:
    node.build_services(svcnames=options.parm_svcs.split(','))
else:
    node.build_services()

for s in node.svcs:
    s.options.debug = options.debug
    s.options.refresh = options.upddb

if options.verbose:
    svcmon_verbose(node.svcs)
else:
    svcmon_normal(node.svcs, options.upddb)

node.close()

if options.upddb:
    try:
        monunlock(lockfd)
    except ex.excError:
        sys.exit(1)
    except:
        import traceback
        traceback.print_exc()
        sys.exit(1)

sys.exit(0)
