#!/usr/bin/env python

#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.
#

import qmf.console, optparse, sys, time, os
from qpid.management import managementChannel, managementClient
from qpid.messaging import Connection
from qpid.messaging import Message as QpidMessage
from qpidtoollibs.broker import BrokerAgent
try:
    from uuid import uuid4
except ImportError:
    from qpid.datatypes import uuid4

# QMF address for the HA broker object.
HA_BROKER = "org.apache.qpid.ha:habroker:ha-broker"

class Command:
    commands = {}

    def __init__(self, name, help, arg_names=[]):
        Command.commands[name] = self
        self.name = name
        self.arg_names = arg_names
        usage="%s [options] %s\n\n%s"%(name, " ".join(arg_names), help)
        self.help = help
        self.op=optparse.OptionParser(usage)
        self.op.add_option("-b", "--broker", metavar="<url>", help="Connect to broker at <url>")

    def execute(self, args):
        opts, args = self.op.parse_args(args)
        if len(args) != len(self.arg_names)+1:
            self.op.print_help()
            raise Exception("Wrong number of arguments")
        broker = opts.broker or "localhost:5672"
        connection = Connection.establish(broker, client_properties={"qpid.ha-admin":1})
        qmf_broker = BrokerAgent(connection)
        ha_broker = qmf_broker.getHaBroker()
        if not ha_broker: raise Exception("HA module is not loaded on broker at %s"%broker)
        try: return self.do_execute(qmf_broker, ha_broker, opts, args)
        finally: connection.close()

    def do_execute(self, qmf_broker, opts, args):
        raise Exception("Command '%s' is not yet implemented"%self.name)

class PromoteCmd(Command):
    def __init__(self):
        Command.__init__(self, "promote","Promote broker from backup to primary")
    def do_execute(self, qmf_broker, ha_broker, opts, args):
        qmf_broker._method("promote", {}, HA_BROKER)
PromoteCmd()

class StatusCmd(Command):
    def __init__(self):
        Command.__init__(self, "status", "Print HA status")
        self.op.add_option(
            "--expect", type="string", metavar="<status>",
            help="Don't print status but return 0 if it matches <status>, 1 otherwise")
    def do_execute(self, qmf_broker, ha_broker, opts, args):
        if opts.expect:
            if opts.expect != ha_broker.status: return 1
        else:
            print ha_broker.status
        return 0
StatusCmd()

class ReplicateCmd(Command):
    def __init__(self):
        Command.__init__(self, "replicate", "Set up replication from <queue> on <remote-broker> to <queue> on the current broker.", ["<queue>", "<remote-broker>"])
    def do_execute(self, qmf_broker, ha_broker, opts, args):
        qmf_broker._method("replicate", {"broker":args[1], "queue":args[2]}, HA_BROKER)
ReplicateCmd()

class SetCmd(Command):
    def __init__(self):
        Command.__init__(self, "set", "Set HA configuration settings")
        def add(optname, metavar, type, help):
            self.op.add_option(optname, metavar=metavar, type=type, help=help, action="store")
        add("--brokers-url", "<url>", "string", "URL with address of each broker in the cluster. Used by brokers to connect to each other.")
        add("--public-url", "<url>", "string", "URL advertised to clients to connect to the cluster. May be a list or a VIP.")
        add("--backups", "<n>", "int", "Expect <n> backups to be running"),

    def do_execute(self, qmf_broker, ha_broker, opts, args):
        if (opts.brokers_url): qmf_broker._method("setBrokersUrl", {"url":opts.brokers_url}, HA_BROKER)
        if (opts.public_url): qmf_broker._method("setPublicUrl", {"url":opts.public_url}, HA_BROKER)
        if (opts.backups): qmf_broker._method("setExpectedBackups", {"expectedBackups":opts.backups}, HA_BROKER)

SetCmd()

class QueryCmd(Command):
    def __init__(self):
        Command.__init__(self, "query", "Print HA configuration and status")

    def do_execute(self, qmf_broker, ha_broker, opts, args):
        hb = ha_broker
        for x in [("Status:", hb.status),
                  ("Brokers URL:", hb.brokersUrl),
                  ("Public URL:", hb.publicUrl),
                  ("Expected Backups:", hb.expectedBackups),
                  ("Replicate: ", hb.replicateDefault)
                  ]:
            print "%-20s %s"%(x[0], x[1])

QueryCmd()

def print_usage(prog):
    print "usage: %s <command> [<arguments>]\n\nCommands are:\n"%prog
    for name, command in Command.commands.iteritems():
        help = command.help
        print "  %-12s %s."%(name, help.split(".")[0])
    print "\nFor help with a command type: %s <command> --help\n"%prog

def find_command(args):
    """Find a command among the arguments and options"""
    for arg in args:
        if arg in Command.commands:
            return Command.commands[arg]
    return None

def main(argv):
    try:
        args=argv[1:]
        if args and args[0] == "--help-all":
            for c in Command.commands.itervalues():
                c.op.print_help(); print
            return 1
        command = find_command(args)
        if not command:
            print_usage(os.path.basename(argv[0]));
            return 1;
        if command.execute(args): return 1
    except Exception, e:
        print e
        return 1

if __name__ == "__main__":
    sys.exit(main(sys.argv))
