#!/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

#
# 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__)
action = None

import svcBuilder
import rcStatus
import rcOptParser
import node
try:
    from version import version
except:
    version = "dev"

node = node.Node()

__ver = prog + " version " + version
__usage = "%prog [options] command\n\n" + rcOptParser.format_desc()
parser = optparse.OptionParser(version=__ver, usage=__usage)
parser.add_option("--debug", default=False,
		  action="store_true", dest="debug",
                  help="debug mode")
parser.add_option("-p", "--parallel", default=False, action="store_true", dest="parallel",
                  help="start actions on specified services in parallel")
parser.add_option("-f", "--force", default=False, action="store_true", dest="force",
                  help="force action, ignore sanity check warnings")
parser.add_option("--cron", default=False, action="store_true", dest="cron",
                  help="used by cron'ed action to tell the collector to treat the log entries as such")
parser.add_option("-c", "--cluster", default=False, action="store_true", dest="cluster",
                  help="option to set when excuting from a clusterware to disable safety net")
parser.add_option("-i", "--interactive", default=False, action="store_true", dest="interactive",
                  help="prompt user for a choice instead of going for defaults or failing")
parser.add_option("--rid", default=None, action="store", dest="parm_rid",
                  help="comma-separated list of resource to limit action to")
parser.add_option("--tags", default=None, action="store", dest="parm_tags",
                  help="comma-separated list of resource tags to limit action to")
parser.add_option("--resource", default=[], action="append",
                  help="a resource definition in json dictionary format fed to create or update")
parser.add_option("--provision", default=False, action="store_true", dest="provision",
                  help="provision the service in addition to env file creation. defaults to False.")
parser.add_option("--waitlock", default=60, action="store", dest="parm_waitlock", type="int",
                  help="comma-separated list of resource tags to limit action to")
parser.add_option("--to", default=None, action="store", dest="parm_destination_node",
                  help="remote node to start or migrate the service to")


cluster = False
cmd = os.path.basename(__file__)
if cmd == 'allservices':
    node.build_services()
elif cmd == 'allupservices':
    node.build_services(status=rcStatus.UP)
    node.build_services(status=rcStatus.WARN)
elif cmd == 'alldownservices':
    node.build_services(status=rcStatus.DOWN)
elif cmd == 'allprimaryservices':
    node.build_services(onlyprimary=True)
elif cmd == 'allsecondaryservices':
    node.build_services(onlysecondary=True)
elif cmd == 'svcmgr':
    parser.add_option("-s", "--service", default=None, action="store", dest="parm_svcs",
              help="comma-separated list of service to operate on")
    parser.add_option("--status", default=None, action="store", dest="parm_status",
              help="operate only on service in the specified status (up/down/warn)")
    parser.add_option("--onlyprimary", default=None, action="store_true", dest="parm_primary",
              help="operate only on service flagged for autostart on this node")
    parser.add_option("--onlysecondary", default=None, action="store_true", dest="parm_secondary",
              help="operate only on service not flagged for autostart on this node")
else:
    svcname = cmd.split('.')
    if len(svcname) == 2:
        if svcname[1] == 'cluster':
            cluster = True
            svcname = svcname[0]
        elif svcname[1] == 'stonith':
            cluster = True
            svcname = svcname[0]
            action = "stonith"
    else:
        svcname = cmd
    node.build_services(svcnames=[svcname])

(options, args) = parser.parse_args()
if action is not None:
    args = ['stonith']

if node.svcs is None:
    parm_primary = False
    parm_secondary = False
    parm_status = None

    if hasattr(options, "parm_svcs") and options.parm_svcs is not None:
        svcnames = options.parm_svcs.split(',')
    else:
        svcnames = []

    if hasattr(options, "parm_status") and options.parm_status is not None:
        status = rcStatus.status_value(options.parm_status)
    else:
        status = None

    if hasattr(options, "parm_primary") and options.parm_primary is not None and \
       hasattr(options, "parm_secondary") and options.parm_secondary is not None:
        parser.error("--onlyprimary and --onlysecondary are exclusive")

    if hasattr(options, "parm_primary") and options.parm_primary is not None:
        onlyprimary = options.parm_primary
    else:
        onlyprimary = False

    if hasattr(options, "parm_secondary") and options.parm_secondary is not None:
        onlysecondary = options.parm_secondary
    else:
        onlysecondary = False

    node.build_services(svcnames=svcnames,
                        status=status,
                        onlyprimary=onlyprimary,
                        onlysecondary=onlysecondary)

if len(args) > 1:
    parser.error("More than one action: %s"%str(args))
if len(args) is 0:
    parser.error("Missing action")
if action is None:
    action = args[0]
if not action in rcOptParser.action_desc.keys():
    parser.error("unsupported action")

if hasattr(options, "parm_rid") and options.parm_rid is not None:
   rid = options.parm_rid.split(',')
else:
   rid = []

if hasattr(options, "parm_tags") and options.parm_tags is not None:
   tags = set(options.parm_tags.split(','))
else:
   tags = set([])

if options.parallel:
    from multiprocessing import Process
    p = {}

def _exit(r):
    node.close()
    sys.exit(r)

if action in ['create', 'update']:
    r = getattr(svcBuilder, action)(svcnames, options.resource, interactive=options.interactive, provision=options.provision)
    if options.provision:
        if action == 'create':
            # don't push to the collector yet
            node.build_services(svcnames=svcnames, autopush=False)
            if len(node.svcs) == 1:
                node.svcs[0].action("provision")
    _exit(r)
elif action in ['disable', 'enable', 'delete']:
    r = getattr(svcBuilder, action)(svcnames, rid)
    _exit(r)

err = 0
for s in node.svcs:
    s.force = options.force
    s.cron = options.cron
    if cluster:
        s.cluster = cluster
    else:
        s.cluster = options.cluster
    s.destination_node = options.parm_destination_node
    if options.parallel:
        d = {'action':action, 'rid':rid, 'tags':tags, 'waitlock':options.parm_waitlock}
        p[s.svcname] = Process(target=node.service_action_worker,
                               name='worker_'+s.svcname,
                               args=[s],
                               kwargs=d)
        p[s.svcname].start()
    else:
        try:
            err += s.action(action, rid=rid, tags=tags, waitlock=options.parm_waitlock)
        except s.exMonitorAction:
            s.cluster = True
            s.action('toc')
            s.action(s.monitor_action)

if options.parallel:
    for svcname in p:
        p[svcname].join()
        r = p[svcname].exitcode
        if r > 0:
           # r is negative when p[svcname] is killed by signal.
           # in this case, we don't want to decrement the err counter.
           err += r

try:
    import logging
    logging.shutdown()
except:
    pass

_exit(err)
