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

# Verify managment objects are consistent in a cluster.
# Arguments: url of one broker in the cluster.

import qmf.console, sys, re

class Session(qmf.console.Session):
    """A qmf.console.Session that caches useful values"""

    def __init__(self):
        qmf.console.Session.__init__(self)
        self.classes = None

    def all_classes(self):
        if self.classes is None:
            self.classes = [c for p in self.getPackages() for c in self.getClasses(p)]
        return self.classes

class Broker:
    def __init__(self, url, qmf):
        self.url = url
        self.qmf = qmf
        self.broker = self.qmf.addBroker(url)
        self.broker._waitForStable()
        self.objects = None
        self.ignore_list = [ re.compile("org.apache.qpid.broker:system:") ]

    def get_objects(self):
        def ignore(name):
            for m in self.ignore_list:
                if m.match(name): return True
        if self.objects is None:
            obj_list = []
            ignored=0
            for c in self.qmf.all_classes():
                for o in self.qmf.getObjects(_key=c, _broker=self.broker):
                    name=o.getObjectId().getObject()
                    if not ignore(name): obj_list.append(name)
                    else: ignored += 1
            self.objects = set(obj_list)
            if (len(obj_list) != len(self.objects)):
                raise Exception("Duplicates in object list for %s"%(self.url))
            print "%d objects on %s, ignored %d."%(len(self.objects), self.url, ignored)
        return self.objects

    def compare(self,other):
        def compare1(x,y):
            diff = x.get_objects() - y.get_objects()
            if diff:
                print "ERROR: found on %s but not %s"%(x, y)
                for o in diff: print "    %s"%(o)
                return False
            return True

        so = compare1(self, other)
        os = compare1(other, self)
        return so and os

    def __str__(self): return self.url

    def get_cluster(self):
        """Given one Broker, return list of all brokers in its cluster"""
        clusters = self.qmf.getObjects(_class="cluster")
        if not clusters: raise ("%s is not a cluster member"%(self.url))
        def first_address(url):
            """Python doesn't understand the brokers URL syntax. Extract a simple addres"""
            return re.compile("amqp:tcp:([^,]*)").match(url).group(1)
        return [Broker(first_address(url), self.qmf)
                 for url in clusters[0].members.split(";")]

    def __del__(self): self.qmf.delBroker(self.broker)

def main(argv=None):
    if argv is None: argv = sys.argv
    qmf = Session()
    brokers = Broker(argv[1], qmf).get_cluster()
    print "%d members in cluster."%(len(brokers))
    base = brokers.pop(0)
    try:
        for b in brokers:
            if not base.compare(b): return 1
        print "No differences."
        return 0
    finally:
        del base
        del brokers

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